function enableDrag()
{
  var el = document.getElementById('defaultSchedule');
  var dragIcon = document.createElement('img');
  dragIcon.src = '../graphics/pages_lrg.gif';
  ldcCalc.AddEvent(el, 'dragstart', function(e) 
  {
    e = e || window.event;
    var src = e.target || e.srcElement;
    e.dataTransfer.effectAllowed = 'copy'; // only dropEffect='copy' will be droppable
    e.dataTransfer.setData('Text', src.id); // required otherwise doesn't work
    e.dataTransfer.setDragImage(dragIcon, 5, 5);
  });
}

// These functions are common to all the drop zones.
function _dragOver(e)
{
  if (e.preventDefault) e.preventDefault(); // allows us to drop
  this.className = 'over';
  e.dataTransfer.dropEffect = 'copy';
  return false;
}

// to get IE to work
function _dragEnter(e)
{
  if (e.preventDefault) e.preventDefault(); // allows us to drop
  this.className = 'over';
  return false;
}

function _dragLeave()
{
  this.className = '';
}

function _deleteSchedule(t)
{
  var opts;
  
  if(t.indexOf("bgrSchedule") != 0) 
  {
    opts = {title: "Warning 101", 
            message: "You can't delete the default schedule!",
            type: "warning",
            width: 425,
            height: 0,
            autoHide: false,
            atCursor: false,
            modal: true};

    jsDlg.showDialog(opts, new Array(new jsDlg.button('OK', true)));
    return;
  }
    
  // Now move everything down in localStorage.
  var x = t.split('_');
  var i = 0;
  var first = parseInt(x[1]);
  var last = parseInt(localStorage.getItem("bgrScheduleIndex"));
    
  if(first == last)
  {
    localStorage.removeItem("bgrSchedule_"+ (last));
  }
  else
  {
    for(i = first; i < last; i++)
    {
      var src = localStorage.getItem("bgrSchedule_" + (i+1));
      if(src != null)
      {
        localStorage.setItem("bgrSchedule_"+ i, src);
        localStorage.removeItem("bgrSchedule_"+ (i+1));
        ldcCalc.ShowSchedule('U', i);
      }
    }
  }
  var index = last - 1;
  if(index < -1) 
  {
    index = -1;
  }
    
  localStorage.setItem("bgrScheduleIndex", index);    
    
  last = parseInt(localStorage.getItem("bgrScheduleIndex"));
  ldcCalc.RemoveLastCol();
  document.getElementById('compareTab').innerHTML = "Compare ("+ (last + 2) + ")";

  return false;
}

function _printSchedule(t)
{
  if(t.indexOf("defaultSchedule") == -1)
  {
    var x = t.split('_');
    var i = parseInt(x[1]);
    ldcCalc.ShowPrintableSchedule(i);
  }
  else
  {
    ldcCalc.ShowPrintableSchedule(-1);
  }
}

function _swapSchedule(t)
{
  var opts;
  if(t.indexOf("bgrSchedule") != 0) 
  {
    opts = {title: "Warning 102", 
            message: "You can't swap the default schedule with itself!",
            type: "warning",
            width: 425,
            height: 0,
            autoHide: false,
            atCursor: false,
            modal: true};

    jsDlg.showDialog(opts, new Array(new jsDlg.button('OK', true)));
    return;
  }
  var x = t.split('_');
  var i = parseInt(x[1]);    
  var def = localStorage.getItem("bgrFormData");
  var src = localStorage.getItem("bgrSchedule_" + i);
    
  if((def != null) && (src != null))
  {
    localStorage.setItem("bgrFormData", src);
    localStorage.setItem("bgrSchedule_" + i, def);
    ldcCalc.ShowSchedule('U', -1);
    ldcCalc.ShowSchedule('U', i);
    // Update the input form
    var f = document.getElementById('bgrForm');
    ldcCalc.GetData(f, -1);
    last = parseInt(localStorage.getItem("bgrScheduleIndex"));
    document.getElementById('compareTab').innerHTML = "Compare ("+ (last + 2) + ")";
  }
}

function enableDelete()
{
  var bin = document.querySelector('#editDelete');

  ldcCalc.AddEvent(bin, 'dragover', _dragOver);
  ldcCalc.AddEvent(bin, 'dragleave', _dragLeave);
  ldcCalc.AddEvent(bin, 'dragenter', _dragEnter);
  ldcCalc.AddEvent(bin, 'drop', function(e){
    if (e.stopPropagation) e.stopPropagation(); 

    var t = e.dataTransfer.getData('Text');
    _deleteSchedule(t);
    return false;
  });
}

function enablePrint()
{
  var pr = document.querySelector('#editPrint');

  ldcCalc.AddEvent(pr, 'dragover', _dragOver);
  ldcCalc.AddEvent(pr, 'dragleave', _dragLeave);
  ldcCalc.AddEvent(pr, 'dragenter', _dragEnter);

  ldcCalc.AddEvent(pr, 'drop', function(e) 
  {
    if (e.stopPropagation) e.stopPropagation();

    var t = e.dataTransfer.getData('Text');
    _printSchedule(t);
 
    return false;
  });
}

function enableSwap()
{
  var sw = document.querySelector('#editSwap');

  ldcCalc.AddEvent(sw, 'dragover', _dragOver);
  ldcCalc.AddEvent(sw, 'dragleave', _dragLeave);
  ldcCalc.AddEvent(sw, 'dragenter', _dragEnter);

  ldcCalc.AddEvent(sw, 'drop', function(e) 
  {
    if(e.stopPropagation) e.stopPropagation();

    var t = e.dataTransfer.getData('Text');
    _swapSchedule(t);
 
    return false;
  });
}

function enableSend()
{
  var se = document.querySelector('#editSend');

  ldcCalc.AddEvent(se, 'dragover', _dragOver);
  ldcCalc.AddEvent(se, 'dragleave', _dragLeave);
  ldcCalc.AddEvent(se, 'dragenter', _dragEnter);

  ldcCalc.AddEvent(se, 'drop', function(e) 
  {
    if (e.stopPropagation) e.stopPropagation();

    var t = e.dataTransfer.getData('Text');
    var sched = localStorage.getItem(t);
 
    return false;
  });
}
// shared cookie functions

var mycookie = document.cookie;



// read cookie data

function getCookieData(name) 

{

   var label = name + "=";

   var labelLen = label.length;

   var cLen = mycookie.length;

   var i = 0;



   while (i < cLen) 

   {

      var j = i + labelLen;

 

      if (document.cookie.substring(i, j) == label) 

      {

         var cEnd = document.cookie.indexOf(";", j);

         if (cEnd == -1) 

         {

            cEnd = document.cookie.length;

         }

         return unescape(document.cookie.substring(j, cEnd));

      }

      i++;

   }

   return "";

}



// write cookie data

function setCookieData(name, data, expires) 

{

   mycookie = document.cookie = name + "=" + data + "; expires=" + expires;

}





// Reads the stored BGR data back.

//

// If the cookie exists then return the user preference for the direction,

// otherwise return clockwise ( zero ).



function getBgrDirection()

{

  if(typeof(localStorage) != 'undefined')

  {

    var data = localStorage.getItem("bgrFormData");

    if(data != null)

    {

      var d = JSON.parse(data);

      if(d != null)

      {

        return d.direction;

      }

    }

    return 0;

  }



  var data = getCookieData("bgrData");

  if(data)

  {

    var bgrCookieData = data.split('-');

    return bgrCookieData[4];

  }

  else

  {

    return 0;

  }

}



function getBgrSched()

{

  if(typeof(localStorage) != 'undefined')

  {

    var data = localStorage.getItem("bgrFormData");

    if(data != null)

    {

      var d = JSON.parse(data);

      if(d != null)

      {

        return d.schedule/4;

      }

    }

    return 23.5;

  }



  var data = getCookieData("bgrData");

  if(data)

  {

    var bgrCookieData = data.split('-');

    return bgrCookieData[7]/4;

  }

  else

  {

    return 23.5;

  }

}



function getBgrStart()

{

  if(typeof(localStorage) != 'undefined')

  {

    var data = localStorage.getItem("bgrFormData");

    if(data != null)

    {

      var d = JSON.parse(data);

      if(d != null)

      {

        return d.startTime;

      }

    }

    return 0;

  }



  var data = getCookieData("bgrData");

  if(data)

  {

    var bgrCookieData = data.split('-');

    return bgrCookieData[3];

  }

  else

  {

    return 0;

  }

}



///////////////////////////////////////////////////////////////////////////////////////////////////

//

// Creates a date object that is the date at which serious training should begin.

//

///////////////////////////////////////////////////////////////////////////////////////////////////

function getTrainStart()

{

   var oneMinute = 60 * 1000;

   var oneHour = oneMinute * 60;

   var oneDay = oneHour * 24;

   var oneWeek = oneDay * 7;

   

   var trainStart = new Date();

   var attemptStart = getDateOfAttempt();

   var dateInMs = attemptStart.getTime();

   dateInMs -= (oneWeek * 32);

   

   trainStart.setTime(dateInMs);

   return trainStart;

}



/////////////////////////////////////////////////////////////////////////////

//

//

//

////////////////////////////////////////////////////////////////////////////

function storeNameData(index)

{

   // six month expiry date

   var expDate = new Date();

   expDate.setTime( expDate.getTime() + ( 180 * 24 * 60 * 60 * 1000 ) );

   expDate.toGMTString();   



   var cookieData = index ;



   document.cookie = "nameData" + "=" + cookieData + "; expires=" + expDate.toGMTString();   

}



function getNameData()

{

   var data = getCookieData("nameData");

   if(data)

   {

      var bgrCookieData = data.split('-');

      return bgrCookieData[3];

   }

   else

   {

      return 0;

   }

}



function setBgrMenu(opt)

{

  var data = getCookieData("bgrMenu");

  // one year expiry date

  var expDate = new Date();

  expDate.setTime( expDate.getTime() + ( 365 * 24 * 60 * 60 * 1000 ) );

  expDate.toGMTString();   

  if(data)

  {

    var val = parseInt(data, 10);

  }

  else

  {

    var val = 1;

  }

  //alert(data);



  var cookieData = (val === 0) ? 1 : 0 ;



   document.cookie = "bgrMenu" + "=" + cookieData + "; expires=" + expDate.toGMTString();   

}



/*
    http://www.JSON.org/json2.js
    2010-11-17

    Public Domain.

    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

    See http://www.JSON.org/js.html


    This code should be minified before deployment.
    See http://javascript.crockford.com/jsmin.html

    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
    NOT CONTROL.


    This file creates a global JSON object containing two methods: stringify
    and parse.

        JSON.stringify(value, replacer, space)
            value       any JavaScript value, usually an object or array.

            replacer    an optional parameter that determines how object
                        values are stringified for objects. It can be a
                        function or an array of strings.

            space       an optional parameter that specifies the indentation
                        of nested structures. If it is omitted, the text will
                        be packed without extra whitespace. If it is a number,
                        it will specify the number of spaces to indent at each
                        level. If it is a string (such as '\t' or '&nbsp;'),
                        it contains the characters used to indent at each level.

            This method produces a JSON text from a JavaScript value.

            When an object value is found, if the object contains a toJSON
            method, its toJSON method will be called and the result will be
            stringified. A toJSON method does not serialize: it returns the
            value represented by the name/value pair that should be serialized,
            or undefined if nothing should be serialized. The toJSON method
            will be passed the key associated with the value, and this will be
            bound to the value

            For example, this would serialize Dates as ISO strings.

                Date.prototype.toJSON = function (key) {
                    function f(n) {
                        // Format integers to have at least two digits.
                        return n < 10 ? '0' + n : n;
                    }

                    return this.getUTCFullYear()   + '-' +
                         f(this.getUTCMonth() + 1) + '-' +
                         f(this.getUTCDate())      + 'T' +
                         f(this.getUTCHours())     + ':' +
                         f(this.getUTCMinutes())   + ':' +
                         f(this.getUTCSeconds())   + 'Z';
                };

            You can provide an optional replacer method. It will be passed the
            key and value of each member, with this bound to the containing
            object. The value that is returned from your method will be
            serialized. If your method returns undefined, then the member will
            be excluded from the serialization.

            If the replacer parameter is an array of strings, then it will be
            used to select the members to be serialized. It filters the results
            such that only members with keys listed in the replacer array are
            stringified.

            Values that do not have JSON representations, such as undefined or
            functions, will not be serialized. Such values in objects will be
            dropped; in arrays they will be replaced with null. You can use
            a replacer function to replace those with JSON values.
            JSON.stringify(undefined) returns undefined.

            The optional space parameter produces a stringification of the
            value that is filled with line breaks and indentation to make it
            easier to read.

            If the space parameter is a non-empty string, then that string will
            be used for indentation. If the space parameter is a number, then
            the indentation will be that many spaces.

            Example:

            text = JSON.stringify(['e', {pluribus: 'unum'}]);
            // text is '["e",{"pluribus":"unum"}]'


            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

            text = JSON.stringify([new Date()], function (key, value) {
                return this[key] instanceof Date ?
                    'Date(' + this[key] + ')' : value;
            });
            // text is '["Date(---current time---)"]'


        JSON.parse(text, reviver)
            This method parses a JSON text to produce an object or array.
            It can throw a SyntaxError exception.

            The optional reviver parameter is a function that can filter and
            transform the results. It receives each of the keys and values,
            and its return value is used instead of the original value.
            If it returns what it received, then the structure is not modified.
            If it returns undefined then the member is deleted.

            Example:

            // Parse the text. Values that look like ISO date strings will
            // be converted to Date objects.

            myData = JSON.parse(text, function (key, value) {
                var a;
                if (typeof value === 'string') {
                    a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                    if (a) {
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
                            +a[5], +a[6]));
                    }
                }
                return value;
            });

            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
                var d;
                if (typeof value === 'string' &&
                        value.slice(0, 5) === 'Date(' &&
                        value.slice(-1) === ')') {
                    d = new Date(value.slice(5, -1));
                    if (d) {
                        return d;
                    }
                }
                return value;
            });


    This is a reference implementation. You are free to copy, modify, or
    redistribute.
*/

/*jslint evil: true, strict: false, regexp: false */

/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/


// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

if (!this.JSON) {
    this.JSON = {};
}

(function () {
    "use strict";

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return isFinite(this.valueOf()) ?
                   this.getUTCFullYear()   + '-' +
                 f(this.getUTCMonth() + 1) + '-' +
                 f(this.getUTCDate())      + 'T' +
                 f(this.getUTCHours())     + ':' +
                 f(this.getUTCMinutes())   + ':' +
                 f(this.getUTCSeconds())   + 'Z' : null;
        };

        String.prototype.toJSON =
        Number.prototype.toJSON =
        Boolean.prototype.toJSON = function (key) {
            return this.valueOf();
        };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;


    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        escapable.lastIndex = 0;
        return escapable.test(string) ?
            '"' + string.replace(escapable, function (a) {
                var c = meta[a];
                return typeof c === 'string' ? c :
                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"' :
            '"' + string + '"';
    }


    function str(key, holder) {

// Produce a string from holder[key].

        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            mind = gap,
            partial,
            value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
        case 'string':
            return quote(value);

        case 'number':

// JSON numbers must be finite. Encode non-finite numbers as null.

            return isFinite(value) ? String(value) : 'null';

        case 'boolean':
        case 'null':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.

            return String(value);

// If the type is 'object', we might be dealing with an object or an array or
// null.

        case 'object':

// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.

            if (!value) {
                return 'null';
            }

// Make an array to hold the partial results of stringifying this object value.

            gap += indent;
            partial = [];

// Is the value an array?

            if (Object.prototype.toString.apply(value) === '[object Array]') {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                v = partial.length === 0 ? '[]' :
                    gap ? '[\n' + gap +
                            partial.join(',\n' + gap) + '\n' +
                                mind + ']' :
                          '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }

// If the replacer is an array, use it to select the members to be stringified.

            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    k = rep[i];
                    if (typeof k === 'string') {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {

// Otherwise, iterate through all of the keys in the object.

                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

            v = partial.length === 0 ? '{}' :
                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
                        mind + '}' : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = '';
            indent = '';

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === 'string') {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                     typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }

// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.

            return str('', {'': value});
        };
    }


// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }


// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return typeof reviver === 'function' ?
                    walk({'': j}, '') : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('JSON.parse');
        };
    }
}());
//JavaScript modification T. Dacre 1999

//based on a BASIC program MOON EFFECTS by Bradley E. Schaefer



//   This program helps anyone who needs to know the Moon's

//   phase (age) on

//   any date within several thousand years in the past or future.

//   To illustrate its application, Bradley Schaefer applied it

//   to a number of famous events influenced by the Moon in

//   World War II.  His article appeared in Sky & Telescope for

//   April 1994, page 86.



function MP(day)

{

day2=new Date();

day2=day;

Date.UTC(day.getFullYear(),day.getMonth(), day.getDay());

var Y=day2.getFullYear();

var M=day2.getMonth()+1;

var D=day2.getDate();

var YY,MM,K1,K2,K3,J,V,IP,AG;



YY=Y-Math.floor((12-M)/10);

MM=M+9;

if (MM>=12) {MM=MM-12};

K1=Math.floor(365.25*(YY+4712));

K2=Math.floor(30.6*MM+.5);

K3=Math.floor(Math.floor((YY/100)+49)*.75)-38;

J=K1+K2+D+59;

if (J>2299160) {J=J-K3};

V=(J-2451550.1)/29.530588853;

V=V-Math.floor(V);

if (V<0) {V=V+1};

IP=V;

AG=IP*29.53;

IP=IP*Math.PI*2

return IP;

}



function calceclp(calcmonth,calcyear){

//Eclipse's

EC1=0;

EC2=0;

EM1="";

EM2="";

R1=3.14159265/180;

 U=0;

Y=calcyear;

calcmonth++;

G=1;

K0=Math.floor((Y-1900)*12.3685);

T=(Y-1899.5)/100;

T2=T*T;

 T3=T*T*T;

J0=2415020+29*K0;

F0=0.0001178*T2-0.000000155*T3;

F0=F0+0.75933+0.53058868*K0;

F0=F0-0.000837*T-0.000335*T2;

J0=J0+Math.floor(F0);

 F0=F0-Math.floor(F0);

M0=K0*0.08084821133;

M0=360*(M0-Math.floor(M0))+359.2242;

M0=M0-0.0000333*T2;

M0=M0-0.00000347*T3;

M1=K0*0.07171366128;

M1=360*(M1-Math.floor(M1))+306.0253;

M1=M1+0.0107306*T2;

M1=M1+0.00001236*T3;

B1=K0*0.08519585128;

B1=360*(B1-Math.floor(B1))+21.2964;

B1=B1-0.0016528*T2;

B1=B1-0.00000239*T3;

for (K9=1;K9<=27;K9=K9+2){

J=J0+14*K9;

 F=F0+0.765294*K9;

K=K9/2;

M5=(M0+K*29.10535608)*R1;

M6=(M1+K*385.81691806)*R1;

B6=(B1+K*390.67050646)*R1;

F=F-0.4068*Math.sin(M6);

F=F+(0.1734-0.000393*T)*Math.sin(M5);

F=F+0.0161*Math.sin(2*M6);

F=F-0.0104*Math.sin(2*B6);

F=F-0.0074*Math.sin(M5-M6);

F=F-0.0051*Math.sin(M5+M6);

F=F+0.0021*Math.sin(2*M5);

F=F+0.5/1440;

J=J+Math.floor(F);

 F=F-Math.floor(F);





//100 REM  LUNAR ECLIPSE SUBROUTINE

function eclipser(){

estr="";

 D7=0;

 if (Math.abs(Math.sin(B6))>0.36){return 0}

 S=5.19595-0.0048*Math.cos(M5);

 S=S+0.0020*Math.cos(2*M5);

 S=S-0.3283*Math.cos(M6);

 S=S-0.0060*Math.cos(M5+M6);

 S=S+0.0041*Math.cos(M5-M6);

 C1=0.2070*Math.sin(M5);

 C1=C1+0.0024*Math.sin(2*M5);

 C1=C1-0.0390*Math.sin(M6);

 C1=C1+0.0115*Math.sin(2*M6);

 C1=C1-0.0073*Math.sin(M5+M6);

 C1=C1-0.0067*Math.sin(M5-M6);

 C1=C1+0.0117*Math.sin(2*B6);

 D9=Math.abs(S*Math.sin(B6)+C1*Math.cos(B6));

 U=0.0059+0.0046*Math.cos(M5);

 U=U-0.0182*Math.cos(M6);

 U=U+0.0004*Math.cos(2*M6);

 U=U-0.0005*Math.cos(M5+M6);



 RP=1.2847+U;

 RU=0.7404-U;



 MPE=(1.5572+U-D9)/0.545;









 if (MPE<0){return 0}

 MU=(1.0129-U-D9)/0.545;

 D5=1.5572+U;

 D6=1.0129-U;

 D7=0.4679-U;

 N=(0.5458+0.04*Math.cos(M6))/60;

 D5=((D5*D5-D9*D9)*(D5*D5-D9*D9))/N;

 if (!(MU<=0)){ D6=((D6*D6-D9*D9)*(D6*D6-D9*D9))/N};

 if (!(MU<=1)){  D7=((D7*D7-D9*D9)*(D7*D7-D9*D9))/N};





Jq=J;

Fq=F;

G=1;

if ( Y<1583){G=0}



Fq=Fq+0.5;

if (!(Fq<1)){

Fq=Fq-1;

Jq=Jq+1;

}

if (G==1){ A1=Math.floor((Jq/36524.25)-51.12264);

A=Jq+1+A1-Math.floor(A1/4)

}

else {A=Jq};

B=A+1524;

C=Math.floor((B/365.25)-0.3343);

Dq=Math.floor(365.25*C);

E=Math.floor((B-Dq)/30.61);

Dq=B-Dq-Math.floor(30.61*E)+Fq;

Mq=E-1;

Yq=C-4716;

if (E>13.5){Mq=Mq-12};

if (Mq<2.5) {Yq=Yq+1};

D1=Math.floor(Dq);

H=24*(Dq-D1);

H1=Math.floor(H);

M9=Math.floor(60*(H-H1));

if (M9<10){M9="0"+M9};















if (MU<0 ){estr=estr+"Partial Eclipse, "}

else{

D6=Math.floor(D6+0.5);

estr=estr+"Total Eclipse - ";

   }



if (!(MU<0 )){

D6=Math.floor(D6+0.5);

 D7=Math.floor((D7+0.5)*10)/10;

estr=estr+D7+"min duration, ";

   }



estr=estr+"Maximum phase "+H1+":"+M9+"UT";

if ((Mq == calcmonth) && (Yq == calcyear)) {

	if (EC1==0)

		{EC1=D1;EM1=estr}

    else

		{EC2=D1;EM2=estr}

}

}

}

}



function moonCalc(calcday, calcmonth,calcyear)

{

  calceclp(calcmonth,calcyear);

//alert(calcday)

  //10 REM    NEW AND FULL MOONS

  //12 REM

  //14 REM



  var Y,JJ,K1,K2,K3,D,M,MM,FM1,FM2,NM1,NM2;

  FM1=0;

  FM2=0;

  NM1=0;

  NM2=0;

  Tt1="";

  Tt2="";

  Tt3="";

  Tt4="";



  Y=calcyear;  // year

  M=calcmonth;

  M++;

  D=-1;

  YY=Y-Math.floor((12-M)/10);

  MM=M+9;

  if(MM>=12){MM=MM-12}

  K1=Math.floor(365.25*(YY+4712));

  K2=Math.floor(30.6*MM+.5);

  K3=Math.floor(Math.floor((YY/100)+49)*.75)-38;

  JJ=K1+K2+D+59;

  if (JJ>2299160){JJ=JJ-K3}



  var A,B,C,Dq,E,G,D1,H1,M9,Mq,H,Yq,A1,Fq,Jq;

  var R1,U,K0,T,T2,T3,J0,F0,J,M0,M1,B1,M5,M6,K9,F;

  R1=3.14159265/180;

  U=0;

  K0=Math.floor((Y-1900)*12.3685);

  T=(Y-1899.5)/100;

  T2=T*T;

  T3=T*T*T;

  J0=2415020+29*K0;

  F0=0.0001178*T2-0.000000155*T3;

  F0=F0+0.75933+0.53058868*K0;

  F0=F0-0.000837*T-0.000335*T2;

  J=J+Math.floor(F);

  F=F-Math.floor(F);

  M0=K0*0.08084821133;

  M0=360*(M0-Math.floor(M0))+359.2242;

  M0=M0-0.0000333*T2;

  M0=M0-0.00000347*T3;

  M1=K0*0.07171366128;

  M1=360*(M1-Math.floor(M1))+306.0253;

  M1=M1+0.0107306*T2;

  M1=M1+0.00001236*T3;

  B1=K0*0.08519585128;

  B1=360*(B1-Math.floor(B1))+21.2964;

  B1=B1-0.0016528*T2;

  B1=B1-0.00000239*T3;



  for(K9=0;K9<29;K9++)

  {

    J=J0+14*K9;

    F=F0+0.765294*K9;

    K=K9/2;

    M5=(M0+K*29.10535608)*R1;

    M6=(M1+K*385.81691806)*R1;

    B6=(B1+K*390.67050646)*R1;

    F=F-0.4068*Math.sin(M6);

    F=F+(0.1734-0.000393*T)*Math.sin(M5);

    F=F+0.0161*Math.sin(2*M6);

    F=F+0.0104*Math.sin(2*B6);

    F=F-0.0074*Math.sin(M5-M6);

    F=F-0.0051*Math.sin(M5+M6);

    F=F+0.0021*Math.sin(2*M5);

    F=F+0.0010*Math.sin(2*B6-M6);

    J=J+Math.floor(F);

    F=F-Math.floor(F);





    Jq=J;

    Fq=F;

    G=1;

    if ( Y<1583)

    {

      G=0

    }



    Fq=Fq+0.5;

    if (!(Fq<1))

    {

      Fq=Fq-1;

      Jq=Jq+1;

    }

    if (G==1)

    {

      A1=Math.floor((Jq/36524.25)-51.12264);

      A=Jq+1+A1-Math.floor(A1/4)

    }

    else

    {

      A=Jq;

    }

    B=A+1524;

    C=Math.floor((B/365.25)-0.3343);

    Dq=Math.floor(365.25*C);

    E=Math.floor((B-Dq)/30.61);

    Dq=B-Dq-Math.floor(30.61*E)+Fq;

    Mq=E-1;

    Yq=C-4716;

    if (E>13.5)

    {

      Mq=Mq-12;

    }

    if (Mq<2.5)

    {

      Yq=Yq+1;

    }

    D1=Math.floor(Dq);

    H=24*(Dq-D1);

    H1=Math.floor(H);

    M9=Math.floor(60*(H-H1));

    if (M9<10)

    {

      M9="0"+M9;

    }



    if ((U==0) && (Mq==M) && (Yq==Y))

    {

      if (NM1==0)

      {

        NM1=D1;

        Tt1=H1+":"+M9

      }

      else

      {

        if(NM2==0)

        {

          NM2=D1;

          Tt2=H1+":"+M9;

        }

      }

    }

    if ((U==1) && (Mq==M) && (Yq==Y))

    {

      if (FM1==0)

      {

        FM1=D1;

        Tt3=H1+":"+M9;

      }

      else

      {

        if(FM2==0)

        {

          FM2=D1;

          Tt4=H1+":"+M9;

        }

      }

    }



    U=U+1;



    if (U==2)

    {

      U=0;

    }

  }





  var temp,dayoffset,days,i,j,monthnames,todayday;

  today=new Date(Date.UTC(calcyear,calcmonth,10,1,1));



  month=new Date(Date.UTC(calcyear,calcmonth,10,1,1));



  todayday=today.getDate();

  month.setDate(1);

  today.setDate(1);

  today.setMonth(today.getMonth()+1);

  days=(today-month)/1000/60/60/24;

  createMoonTable(month, days, calcday, NM1, NM2, FM1, FM2);

}





function createMoonTable(month, days, curDay, NM1, NM2, FM1, FM2)

{

  var d = document;

  var i, j, day = 0;

  var f = d.createDocumentFragment();

  var t = d.createElement("table");

  var r = d.createElement("tr");

  var dayNames = new Array("Mon", "Tues", "Wed", "Thurs", "Fri", "Sat", "Sun");

  var head = d.createElement("thead");

  var dayoffset=month.getDay()-1;

  var monthnames=["January","February","March","April","May","June","July","August","September","October","November","December"];



  var cap=d.createElement("caption");

  cap.appendChild(d.createTextNode("Phases of the moon for " + monthnames[month.getMonth()] + " " + month.getFullYear()));

  t.appendChild(cap);

  

  if (dayoffset==-1){dayoffset=6;}



  // Create the table Header row

  for(i = 0; i < 7; i++)

  {

    var h = d.createTextNode(dayNames[i]);

    var c = d.createElement("th");

    c.appendChild(h);

    r.appendChild(c);

  }

  head.appendChild(r);

  t.appendChild(head);



  var b=d.createElement("tbody");

  // Now create the rows of images.

  i = 0;

  var s = d.createElement("span");

  s.className="moonSpan";

  s.appendChild(d.createTextNode(" New"));

  var s2 = d.createElement("span");

  s2.className="moonSpan";

  s2.appendChild(d.createTextNode(" Full"));



  while(days > day)

  {

    r = d.createElement("tr");

    var r2 = d.createElement("tr");



    for(j = 1; j < 8; j++)

    {

      var c = d.createElement("td");

      var c2 = d.createElement("td");

      var day = (i*7)+j-dayoffset;

      if ( day > 0 && day <= days)

      {

        var txt = d.createTextNode("" + day);

        c.appendChild(txt);

        if(NM1 == day || NM2 == day){ c.appendChild(s);}

        if(FM1 == day || FM2 == day){c.appendChild(s2);}

        month.setDate(day);



        if(curDay == day)

        {

          c.id="curday";

          c.setAttribute('title', 'This is the day of your attempt');

          c2.id="curimg";

          c2.setAttribute('title', 'This is the day of your attempt');

        }

        c2.className ="moonImage " + "moon_"+(Math.floor(MP(month)*29/2/Math.PI)+1);

      }

      c.className = "moonDay"

      r.appendChild(c);

      r2.appendChild(c2);

    }

    b.appendChild(r);

    b.appendChild(r2);

    i++;

  }

  t.appendChild(b);

  t.className="moonTable";

  f.appendChild(t);

  var loc = d.getElementById("moonPhase");

  if(loc)

  {

    if(loc.firstChild)

    {

      loc.removeChild(loc.firstChild);

    }

    loc.appendChild(f);

  }

}





function doCalc()

{

  var i,month,year,d=document;

  for (i=0;i<d.forms[0].a.options.length;i++)

  {

    if (d.forms[0].a.options[i].selected)

    {

      month=d.forms[0].a.options[i].value;

    }

  }

  for (i=0;i<d.forms[0].b.options.length;i++)

  {

    if (d.forms[0].b.options[i].selected)

    {

      year=d.forms[0].b.options[i].value;

    }

  }



  moonCalc(month,year);

}

// ldcCalc
//
// This is a generic calculator for long distance challenges (hence "ldc"). It began as the
// calculator for the Bob Graham calculator and associated pages. However during refactoring
// it became obvious that it could be made more of an "engine" rather than a specific
// application.
//
// It relies on JSON and AJAX functionality.

// Create our basic object if it doesn't already exist.
var ldcCalc = window.ldcCalc || {};

// Options
// This is an object holding the various values needed for the engine to make an ajax call
// and deal with it. It is populated by the initialise function.
ldcCalc.options = {
  type:       "GET",         // Default to GET since we shouldn't need the space provided by POST
  url:        "",            // A default empty URL will cause the call to fail.
  timeout:    5000,          // Give ourselves 5 seconds to get the data.
  onSuccess:  function(){},  // This function will handle the success state.
  onFailure:  function(){},  // This function will handle the failure state.
  onComplete: function(){}   // This function does the tidying up.
};

// Elements
// Holds references to the various input and result elements on the page. It is filled in by the
// initialisation routine which is passed an array of IDs.
// This allows the actual elements on the page to be named anything sensible but keeps a constant
// name within the code. 

ldcCalc.elements = {
  'name': null,
  'calcForm':{id:""},
  'result': {id:""}
};

// inputs
// Holds references to the various input elements on the page. It is filled in by the
// initialisation routine which is passed an array of IDs.
// This allows the actual input on the page to be named anything sensible but keeps a constant
// name within the code. Also the type of the input may be defined, they default to "select".
/*
ldcCalc.inputs = {
  'year':       {id: null, type:"", obj : null},
  'month':      {id: null, type:"", obj : null},
  'day':        {id: null, type:"", obj : null},
  'startTime':  {id: null, type:"", obj : null},
  'direction':  {id: null, type:"", obj : null},
  'routeOpt':   {id: null, type:"", obj : null},
  'rocky':      {id: null, type:"", obj : null},
  'schedule':   {id: null, type:"", obj : null},
  'peaks_1':    {id: null, type:"", obj : null},
  'schedule_2': {id: null, type:"", obj : null},
  'peaks_2':    {id: null, type:"", obj : null},
  'schedule_3': {id: null, type:"", obj : null},
  'peaks_3':    {id: null, type:"", obj : null},
  'road_1':    {id: null, type:"", obj : null},
  'road_2':    {id: null, type:"", obj : null},
  'road_3':    {id: null, type:"", obj : null},
  'road_4':    {id: null, type:"", obj : null}
};
*/
ldcCalc.inputs = {
  'year':       null,
  'month':      null,
  'day':        null,
  'startTime':  null,
  'startPoint': null,
  'direction':  null,
  'routeOpt':   null,
  'rocky':      null,
  'schedule':   null,
  'peaks_1':    null,
  'schedule_2': null,
  'peaks_2':    null,
  'schedule_3': null,
  'peaks_3':    null,
  'support_1':  null,
  'support_2':  null,
  'support_3':  null,
  'support_4':  null,
  'support_5':  null
};
ldcCalc.layout = {
  fontSize: 20
};
ldcCalc.storage=true;

// AddEvent
//
// Browser independent way to add events to elements.
ldcCalc.AddEvent = function(elm, evType, fn, useCapture)
{
   if(elm.addEventListener)
   {
      elm.addEventListener(evType, fn, useCapture);
      return true;
   }
   else if(elm.attachEvent)
   {
      return elm.attachEvent('on' + evType, fn);
   }
   else
   {
      elm['on' + evType] = fn;
   }
};

ldcCalc.Msieversion = function()
 {
   var ua = window.navigator.userAgent;
   var msie = ua.indexOf ( "MSIE " );

   if ( msie > 0 )      // If Internet Explorer, return version number
   {
     return parseInt (ua.substring (msie+5, ua.indexOf (".", msie )), 10);
   }
   else                 // If another browser, return 0
   {
     return 0;
   }
};



// read cookie data
ldcCalc.GetCookieData = function(name) 
{
   var label = name + "=";
   var labelLen = label.length;
   var cLen = ldcCalc.mycookie.length;
   var i = 0;

   while (i < cLen) 
   {
      var j = i + labelLen;
 
      if (document.cookie.substring(i, j) === label) 
      {
         var cEnd = document.cookie.indexOf(";", j);
         if (cEnd === -1) 
         {
            cEnd = document.cookie.length;
         }
         return unescape(document.cookie.substring(j, cEnd));
      }
      i++;
   }
   return "";
};

// write cookie data
ldcCalc.SetCookieData = function (name, data, expires) 
{
   ldcCalc.mycookie = document.cookie = name + "=" + data + "; expires=" + expires;
};

ldcCalc.storeData = function(f, index)
{
  var f = ldcCalc.form;

  // Use localstorage if it has been saved that way.
  if(ldcCalc.storage === true)
  {
    var jsonObject = {};
    for(el in ldcCalc.inputs)
    {
      if(ldcCalc.inputs[el] !== null)
      {
        jsonObject[el] = ldcCalc.inputs[el].selectedIndex;
      }
    }
    
    var s = JSON.stringify(jsonObject);
    if( -1 === index)
    {
      localStorage.setItem(ldcCalc.elements.name +"FormData", s);
    }
    else
    {
      var schedName = ldcCalc.elements.name +"Schedule_" + index;

      localStorage.setItem(schedName, s);
      return;
    }
    //return;
  }
  
  // Need to set the cookie as well to pass things around for the server.
  // 12 month expiry date
  var expDate = new Date();
  expDate.setTime( expDate.getTime() + ( 360 * 24 * 60 * 60 * 1000 ) );
  expDate.toGMTString();   

  var cd ="";
  for(el in ldcCalc.inputs)
  {
    if(ldcCalc.inputs[el] !== null)
    {
      cd += ldcCalc.inputs[el].selectedIndex +"-";
    }
  }

  document.cookie = ldcCalc.elements.name + "Data" + "=" + cd + "; expires=" + expDate.toGMTString();
};

// Reads the stored BGR data back.
//
// If the cookie exists then the values it held are used to set up the 
// various fields, otherwise use some sensible defaults.

ldcCalc.GetData = function(f, index)
{
  var data;
  var d;
  // Use localstorage if it has been saved that way.
  if(this.storage === true)
  {
    if(-1 === index)
    {
      data = localStorage.getItem(ldcCalc.elements.name +"FormData");
    }
    else
    {
      localStorage.getItem(ldcCalc.elements.name +"Schedule_" + index);
    }
    
    if(data !== null)
    {
      d = JSON.parse(data);
    
      for(el in ldcCalc.inputs)
      {
        if(ldcCalc.inputs[el] !== null)
        {
          ldcCalc.inputs[el].selectedIndex = d[el];
        }
      }

      return;
    }
  }

  data = getCookieData(ldcCalc.elements.name +"Data");  

  if(data)
  {
    d = data.split('-');
    var i = 0;
    for(el in ldcCalc.inputs)
    {
      if(ldcCalc.inputs[el] !== null)
      {
        ldcCalc.inputs[el].selectedIndex = d[i];
      }
      i++;
    }
  }
  else
  {
    var now = new Date();
     
    var y = 0;
    var m = 5;
    var day = 19;
      
    if(now.getMonth() > 5)
    {
      y += 1;
    }
    // Find the closest Saturday to 20th June next year. getDay() returns 0 for 
    // Sunday, ..., 6 for Saturday.
    var td = new Date(y, m, day);
      
    var t = td.getDay();
    if(t !== 6)
    {
      if(t < 3)
      {
        day -= (t + 1);
      }
      else
      {
        day += (6 - t);
      }
    }
    for(el in ldcCalc.inputs)
    {
      if(ldcCalc.inputs[el] !== null)
      {
        ldcCalc.inputs[el].selectedIndex = 0;
      }
    }

    ldcCalc.inputs.year.selectedIndex = y;
    ldcCalc.inputs.month.selectedIndex=m;
    ldcCalc.inputs.day.selectedIndex=day;
    ldcCalc.inputs.peaks_1.selectedIndex=51;
    ldcCalc.inputs.peaks_2.selectedIndex=51;
    ldcCalc.inputs.peaks_3.selectedIndex=51;
  }
};

ldcCalc.ReadStorage = function(index)
{
  var ret= new Array(0,5,20,0,0,0,0,0,51,0,0,0,51,0,0,0,0);
  var data;
   
  if(this.storage === true)
  {
    if(-1 === index)
    {
      data = localStorage.getItem(ldcCalc.elements.name +"FormData");
    }
    else
    {
      data = localStorage.getItem(ldcCalc.elements.name +"Schedule_" + index);
    }
    if(data !== null)
    {
      var dt = JSON.parse(data);
      if(dt !== null)
      {
        var d = JSON.parse(data);
    
        ret[0]  = d.year;
        ret[1]  = d.month;
        ret[2]  = d.day;
        ret[3]  = d.startTime;
        ret[4]  = d.direction;
        ret[5]  = d.routeOpt;
        ret[6]  = d.rocky;
        ret[7]  = d.schedule;
        ret[8]  = d.peaks_1;
        ret[9]  = d.schedule_2;
        ret[10] = d.peaks_2;
        ret[11] = d.schedule_3;
        ret[12] = 51; 
        ret[13] = d.support_1; 
        ret[14] = d.support_2; 
        ret[15] = d.support_3; 
        ret[16] = d.support_4; 
      }
    }
    return ret;
  }

  return ldcCalc.GetCookieData(ldcCalc.elements.name +'Data').split('-');
};

// getAjaxParams
//
// Recovers the form parameters from local storage then creates the
// request part of the URL from those parameters.
ldcCalc.getAjaxParams = function(act, index)
{ 
  // Do some randomisation to stop IE cacheing things.
  var r = parseInt((Math.random() * 99999999), 10);
  var data;
  var t;

  if(this.storage === true)
  {
    if(-1 === index)
    {
      data = localStorage.getItem(ldcCalc.elements.name +"FormData");
    }
    else
    {
      data = localStorage.getItem(ldcCalc.elements.name +"Schedule_" + index);
    }
    if(data === null)
    {
      return null;
    }
    var jd = JSON.parse(data);
  
    t = "?A="+act;
    t += "&Y="+jd.year +"&M="+jd.month + "&D="+jd.day;
    t += "&st="+jd.startTime + "&dir="+jd.direction + "&o=" +jd.routeOpt;
    t += "&r=" +(jd.rocky*0.05) + "&s1=" +jd.schedule + "&p1=" +jd.peaks_1;
    t += "&s2=" +jd.schedule_2 + "&p2=" +jd.peaks_2 + "&s3=" +jd.schedule_3;
    t += "&p3=" +jd.peaks_3 + "&rx1=" +(5*jd.support_1) + "&rx2=" +(5*jd.support_2);
    t += "&rx3=" +(5*jd.support_3) + "&rx4=" +(5*jd.support_4);
    t += "&rx5=" +(5*jd.support_5)+ "&sp=" +jd.startPoint + "&rand="+r;
  }
  else
  {
    data = ldcCalc.GetCookieData(ldcCalc.elements.name +"Data");  

    if(data)
    {
      var d = data.split('-');
      t = "?A="+act;
      t += "&Y="+d[0] +"&M="+d[1] + "&D="+d[2];
      t += "&st="+d[3] + "&dir="+d[4] + "&o=" +d[5];
      t += "&r=" +(d[6]*0.05) + "&s1=" +d[7] + "&p1=" +d[8];
      t += "&s2=" +d[9] + "&p2=" +d[10] + "&s3=" +d[11];
      t += "&p3=" +d[12];
      if(d.length > 13)
      {
        t+="&rx1=" +(5*d[13]) + "&rx2=" +(5*d[14])+ "&rx3=" +(5*d[15]) + "&rx4=" +(5*d[16]);
      }
      t += "&rand="+r;
    }
  }
  return t;
};

// ShowSchedule
//
// This is the common handler to display the data in the table. It's probably
// the main function on the client side.
//
// It takes two parameters, the first is the action to ask the server to do, the
// second is the index of the schedule to display. It calls the getAjaxParams 
// function to retrieve and format the data from storage before appending it to
// the URL. It then makes the AJAX request, the callback function then parses
// the response, determines how to display the data - whether the table has 
// been created and the rows/cells only need to be updated or if the whole 
// structure needs to be created.
ldcCalc.ShowSchedule = function(act, index)
{
  var temp = act;
  var c;
  
  if('U' === act) 
  {
    act = 'S';
  }
  var opts = ldcCalc.getAjaxParams(act, index);
  act = temp;
  if(null === opts) 
  {
    return;
  }
  
  var url = ldcCalc.url + opts;
  var xhr = ldcCalc.XHRequest();
  if(!xhr) 
  {
    return;
  }
  if(index < 0)
  {
    document.getElementById('compareTab').innerHTML = "Compare";
  }
  else
  {
    document.getElementById('compareTab').innerHTML = "Compare ("+ (index + 2) + ")";
  }
  xhr.open('GET', url, true);
  xhr.timeout = 2000;
  xhr.onreadystatechange = function()
    {
      if(xhr.readyState === 4 && xhr.status === 200)
      {
        var resp = xhr.responseText;
        var jd = JSON.parse(resp);
        var d = document;
        var tab = document.getElementById("schedTable");
        if(tab !== null)
        {
          var addRows = 0;
          var tb = tab.tBodies[0];
          if(-1 === index)
          {
            tb.rows[0].cells[1].innerHTML = jd.startDate;
            tb.rows[1].cells[1].innerHTML = jd.start;
            tb.rows[2].cells[1].innerHTML = jd.sched;
            tb.rows[3].cells[1].innerHTML = jd.direction;
          }
          else
          {
            if('U' === act)
            {
              tb.rows[0].cells[index+2].innerHTML = jd.startDate;
              tb.rows[1].cells[index+2].innerHTML = jd.start;
              tb.rows[2].cells[index+2].innerHTML = jd.sched;
              tb.rows[3].cells[index+2].innerHTML = jd.direction;
            }
            else
            {
              var eTh = d.createElement('th');
              eTh.setAttribute('draggable', 'true');
              eTh.className += " rightClickable";
              var newID = ldcCalc.elements.name +"Schedule_"+ index;
              eTh.setAttribute('id', (newID + "_th"));
              var txt = "Schedule (" + (index+2) + ")";
              var newText = d.createTextNode(txt);
              var newA = d.createElement('a');
              newA.appendChild(newText);
              newA.setAttribute('href', '#');
              newA.setAttribute('draggable', 'true');
              newA.setAttribute('id', newID);
              var dragIcon = d.createElement('img');
              dragIcon.src = '../graphics/pages_lrg.gif';
              ldcCalc.AddEvent(newA, 'dragstart', function(e) 
              {
                e.dataTransfer.effectAllowed = 'copy'; // only dropEffect='copy' will be droppable
                e.dataTransfer.setData('Text', (ldcCalc.elements.name +"Schedule_"+ index)); // required otherwise doesn't work
                e.dataTransfer.setDragImage(dragIcon, 5, 5);
              }, false);
              eTh.appendChild(newA);
              tab.tHead.rows[0].appendChild(eTh);
              c = tb.rows[0].insertCell(-1);
              c.innerHTML = jd.startDate;
              c = tb.rows[1].insertCell(-1);
              c.innerHTML = jd.start;
              c = tb.rows[2].insertCell(-1);
              c.innerHTML = jd.sched;
              c = tb.rows[3].insertCell(-1);
              c.innerHTML = jd.direction;
            } // end if('U' == act)
          } // end if(-1 == index)
          
          // Now either add or update the cell data.
          if(tb.rows.length === 4)
          {
            addRows = 1;
          }
          var len = jd.data.length;
          for(i = 0; i < len; i++)
          {
            var j = i+4;
            if(1 === addRows)
            {
              var nr = tb.insertRow(-1);
              nr.insertCell(0).innerHTML=jd.data[i].pt;
              c = nr.insertCell(1);
              c.innerHTML=jd.data[i].t;    
              c.setAttribute('title', "Leg time: " + jd.data[i].leg + " mins");
              c.setAttribute('class', jd.data[i].ls);
            }
            else
            {
              var r = tb.rows[j];
              if(-1 === index)
              {
                r.cells[1].innerHTML=jd.data[i].t;  
                r.cells[1].setAttribute('title', "Leg time: " + jd.data[i].leg + " mins");
                r.cells[1].setAttribute('class', jd.data[i].ls);
              }
              else
              {
                if(act === 'U')
                {
                  c = r.cells[index+2];
                }
                else
                {
                  c = r.insertCell(-1);
                }
                c.innerHTML=jd.data[i].t;  
                c.setAttribute('title', "Leg time: " + jd.data[i].leg + " mins");
                c.setAttribute('class', jd.data[i].ls);
              } // end if(-1 == index)
            } // if(1 == addRows)
          } // end for(...
        }
        ldcCalc.fetchers.push(xhr);
      }
    };
  xhr.send(null);  
};


ldcCalc.CreateForm = function(root, elements)
{
  var d = document;
  var elem = d.getElementById(root);
  var months = new Array("January", "February", "March", "April","May", "June", "July", "August", "September","October", "November", "December");
  var elCount = 0;
  var elSize = this.layout.fontSize;
  var elStep = elSize - 1;
  var leg;
  var val;
  
  if(!elem) 
  {
    return 0;
  }
  var inp;
  
  var df = d.createDocumentFragment();
  var f = d.createElement('form');
  f.setAttribute('id', elements.calcForm.id);

  var fs = d.createElement('fieldset');
  leg = d.createElement('legend');
  leg.appendChild(d.createTextNode('Date'));
  fs.setAttribute('id', 'calcDate');
  fs.appendChild(leg);
  var fs1Elements = {'year':'year', 'month':'month', 'day':'day'};
                     
  var el;
  var inputs = elements.inputs;
  
  for(el in fs1Elements)
  {
    if(inputs[el])
    {
      p = d.createElement("p");
      inp = d.createElement(inputs[el].type);
      inp.setAttribute('id', inputs[el].id);
      inp.className = 'calcInput';
      switch(el)
      {
        case 'year':
          var now = new Date();
          var curYear = now.getYear();
  
          if(curYear < 2000)
          {
            curYear += 1900;
          }
          inp.options.length = 0;
          for(i = 0; i < 5; ++i)
          {
            inp.options[i]=new Option(curYear+i);
          }
          inp.selectedIndex = 0;  
          break;
        case 'month':
          inp.options.length = 0;
          if(inputs[el].type === 'select')
          {
            for(i = 0; i < 12; i++)
            {
              inp.options[i] = new Option(months[i], i);
            }
            inp.selectedIndex = 5;
          }
          break;
        case 'day':
          inp.options.length = 0;
          if(inputs[el].type === 'select')
          {
            for(i = 0; i < 31; i++)
            {
              inp.options[i] = new Option(i+1, i+1);
            }
            inp.selectedIndex = 20;
          }
          break;
      }

      label = d.createElement('label');
      label.htmlFor = inputs[el].id;
      label.appendChild(d.createTextNode(inputs[el].label));
      label.setAttribute('id', inputs[el].id + "_label");
      label.className = 'calcLabel';
      p.appendChild(label);
      p.appendChild(inp);
      p.style.height=elSize+"px";
      fs.appendChild(p);
      elCount++;
    }  
  }
  var fs1Height = ((elCount) * (elSize+2)+5);
  f.appendChild(fs);
  fs.style.height = fs1Height+"px";

  fs = d.createElement('fieldset');
  fs.setAttribute('id', 'calcOpts');
  leg = d.createElement('legend');
  leg.appendChild(d.createTextNode('Options'));
  fs.appendChild(leg);
  elCount = 0;
  var fs2Elements = {'startPoint':'startPoint',
                     'direction':'direction', 
                     'routeOpt':'routeOpt', 
                     'rocky':'rocky',
                     'support_1':'support_1',
                     'support_2':'support_2',
                     'support_3':'support_3',
                     'support_4':'support_4',
                     'support_5':'support_5'
                     };
  
  for(el in fs2Elements)
  {
    if(inputs[el])
    {
      p = d.createElement("p");
      inp = d.createElement(inputs[el].type);
      inp.setAttribute('id', inputs[el].id);
      inp.className = 'calcInput';
      switch(el)
      {
        case 'direction':
          inp.options.length = 0;
          inp.options[0] = new Option('Clockwise', 0);
          inp.options[1] = new Option('Anticlockwise', 1);
          inp.selectedIndex = 0;  
          break;
        case 'routeOpt':
          inp.options.length = 0;
          inp.options[0] = new Option(" ", 0);
          inp.selectedIndex = 0;
          break;
        case 'rocky':
          inp.options.length = 0;
          for(i = 0; i < 5; i++)
          { 
            val = 0.05;
            inp.options[i] = new Option((val * 100* i)+"%", val*i);
          }
          break;
        case 'support_1':
        case 'support_2':
        case 'support_3':
        case 'support_4':
        case 'support_5':
          inp.options.length = 0;
          for(i = 0; i < 5; i++)
          { 
            val = 5;
            var rt = (0 === i) ? ' (auto)' : ' mins';
            inp.options[i] = new Option((val * i)+rt, val*i);
          }
          break;
      }

      label = d.createElement('label');
      label.htmlFor = inputs[el].id;
      label.appendChild(d.createTextNode(inputs[el].label));
      label.setAttribute('id', inputs[el].id + "_label");
      label.className ='calcLabel';
      p.appendChild(label);
      p.appendChild(inp);
      p.style.height=elSize+"px";
      fs.appendChild(p);
      elCount++;
    }  
  }

  var fs2Height = (elCount * (elSize+2) +5);
  fs.style.height = fs2Height+"px";
  fs.style.top = (fs1Height+(this.layout.fontSize *1.5))+"px";
  f.appendChild(fs);

  fs = d.createElement('fieldset');
  fs.setAttribute('id', 'calcSched');
  leg = d.createElement('legend');
  leg.appendChild(d.createTextNode('Schedule'));
  fs.appendChild(leg);
  elCount = 0;
  var fs3Elements = {'startTime':'startTime', 
                     'schedule':'schedule', 
                     'peaks_1':'peaks_1', 
                     'schedule_2':'schedule_2', 
                     'peaks_2':'peaks_2', 
                     'schedule_3':'schedule_3',
                     'peaks_3':'peaks_3'};
  
  for(el in fs3Elements)
  {
    if(inputs[el])
    {
      p = d.createElement("p");
      inp = d.createElement(inputs[el].type);
      inp.setAttribute('id', inputs[el].id);
      inp.className= 'calcInput';
      switch(el)
      {
        case 'startTime':
          if(elements.inputs.startTime.type === 'select')
          {
            for(i = 0; i < 24; i++)
            {
              inp.options[i] = new Option(i, i);
            }
          }
          inp.selectedIndex = 0;  
          break;
        case 'schedule':
        case 'schedule_2':
        case 'schedule_3':
          var max = elements.inputs.schedule.max || 23.5;
          var min = elements.inputs.schedule.min || 18;
          var step = parseFloat(elements.inputs.schedule.step) || 0.25;
          var count = Math.floor((max - min) /step);
          var q = new Array();
          var s = 60 * step;
          var c = Math.ceil(1/step);
          for (i = 0; i < c; i++)
          {
            var x = Math.round(i * s);
            q[i] = (x < 10) ? ":0" + x : ":" + x;
          }
          for(i = 0; i <= count; i++)
          {
            var t = max - (step * i);
            var h = Math.floor(t);
            var j = Math.floor((t - h)/step);
      
            inp.options[i] = new Option(h + q[j], t);
          }
          inp.selectedIndex = 0;
          break;
      }

      label = d.createElement('label');
      label.htmlFor = inputs[el].id;
      label.appendChild(d.createTextNode(inputs[el].label));
      label.setAttribute('id', inputs[el].id + "_label");
      label.className = 'calcLabel';
      p.appendChild(label);
      p.appendChild(inp);
      p.style.height=elSize+"px";
      fs.appendChild(p);
      elCount++;
    }  
  }
  
  fs.style.height = (elCount * (elSize+2)+5)+"px";
  fs.style.top = (parseInt(fs2Height, 10) + (this.layout.fontSize *6.5))+"px";
  var inpBase = (elCount * 20) + parseInt(fs2Height, 10) + (this.layout.fontSize *12);
  f.style.height = inpBase + "px";
  f.appendChild(fs);
  
  // Now add everything to the document.
  df.appendChild(f);
  elem.appendChild(df);
};

// CalcInitialise
//
// This is the general initialisation function. The actual initialisation done is dependent upon
// the options passed to it.
// The wrapper code must provide an implementation for the FinalInit function.
ldcCalc.CalcInitialise = function(opts, root, elements)
{
  this.elements.name = opts.name;
  this.storage = ldcCalc.supports_local_storage();
  this.layout.fontSize = 24;
  var d = document;
  this.options.url = opts.url;
  this.options.timeout = opts.timeout;
  this.options.onSuccess = opts.onSuccess;
  this.options.onFailure = opts.onFailure;
  this.options.onComplete = opts.onComplete;

  var rt = document.getElementById(root);
  if(rt !== null)
  {  
    this.CreateForm(root, elements);
    ldcCalc.form = d.getElementById(ldcCalc.elements.name +'Form');
    // Need to do this before adding the controls
    for(el in ldcCalc.inputs)
    {
      if(elements.inputs[el] && elements.inputs[el] !== null)
      {
        ldcCalc.inputs[el] = document.getElementById(elements.inputs[el].id);
      }
    }
    ldcCalc.AddControls(ldcCalc.form);
    ldcCalc.GetData(ldcCalc.form, -1);
    ldcCalc.FinalInit();
  }
  
  var theTable = document.getElementById("sched");
  if(theTable !== null)
  {
    ldcCalc.ShowCrossings();
  } 
};

// XHR
//
// This section is for the actual AJAX functionality. In order for multiple 
// AJAX requests to be fired off in quick succession an array is needed, if
// there is a spare XMLHttpRequest object available then use that otherwise
// create a new one. 
// It is up to the caller to put the request back when they have finished
// with it.
ldcCalc.fetchers = [];

ldcCalc.XHRequest = function()
{
  if(!!ldcCalc.fetchers.length)
  {
    return ldcCalc.fetchers.pop();
  }
  var xmlHttp;

  try
  {
    // Firefox, Opera 8.0+, Safari
    xmlHttp=new XMLHttpRequest();
  }
  catch (e)
  {
    // Internet Explorer
    try
    {
      xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch (e)
    {
      try
      {
        xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch (e)
      {
        alert("Your browser does not support AJAX!");
        return null;
      }
    }
  }
  return xmlHttp;
};

// supports_local_storage
//
// Called at startup to determine if the browser in which this is running supports
// the new HTML5 localStorage mechanism. If it does then the code will enable the
// "Add" button and allow multiple schedules to be saved.
ldcCalc.supports_local_storage = function() 
{
  try 
  {
    return 'localStorage' in window && window.localStorage !== null;
  } 
  catch(e)
  {
    return false;
  }
};


/////////////////////////////////////////////////////////////////////////////

// shared cookie functions
ldcCalc.mycookie = document.cookie;


ldcCalc.AddRoadLinks = function(restDetails , dir)
{
  var d=document;
  var directionsDisplay = new google.maps.DirectionsRenderer();;
  var directionsService = new google.maps.DirectionsService();
  var theTable = d.getElementById("sched");
  var rm = d.getElementById("road_map");
 
  var mapOpts = {
    mapTypeId: google.maps.MapTypeId.SATELLITE,
    center: new google.maps.LatLng(54.600774, -3.137203),
    zoom: 10,
    mapTypeControlOptions: {
      style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
    },
    navigationControl: true,
    navigationControlOptions: {
      style: google.maps.NavigationControlStyle.SMALL
    }
  };
  var map = new google.maps.Map(rm, mapOpts);
  
  if(theTable == null){ return;}
  ldcCalc.AddEvent(theTable, 'click', function(e){
      if (!e) var e = window.event;
      if (e.target) 
        targ = e.target;
	  else if (e.srcElement) 
	    targ = e.srcElement;
	  if (targ.nodeType == 3) // defeat Safari bug
		targ = targ.parentNode;
	  var tid = targ.nodeType;
	  
	  var pts = targ.parentNode.id.split("_");
	  if(pts.length == 2)
	  {
        var dets = d.getElementById("route_details");
        var id = parseInt(pts[1]);
        
        dets.innerHTML = "";
        dets.style.width = "40%";
        rm.style.width = "60%";

        directionsDisplay.setMap(null);
        directionsDisplay.setMap(map);
        directionsDisplay.setPanel(dets);
        
        var origin;
        var dest;
        var dir = getBgrDirection();
        if(1 == dir) // anticlockwise
        {
          var index = restDetails.length - id;
          if(id == (restDetails.length - 1))
          {
            origin = new google.maps.LatLng(54.600774, -3.137203);
          }
          else
          {
            origin = new google.maps.LatLng(restDetails[id+1].lat,restDetails[id+1].lon)
          }
        }
        else
        {
          if(0 == id)
          {
            origin = new google.maps.LatLng(54.600774, -3.137203);
          }
          else
          {
            origin = new google.maps.LatLng(restDetails[id - 1].lat,restDetails[id -1].lon)
          }
        }
        dest = new google.maps.LatLng(restDetails[id].lat,restDetails[id].lon);
  
        var request = 
        {
          origin: origin,
          destination: dest,
          travelMode: google.maps.DirectionsTravelMode.DRIVING,
          unitSystem: google.maps.DirectionsUnitSystem.METRIC
        };
        directionsService.route(request, function(result, status) 
        {
          if (status == google.maps.DirectionsStatus.OK) 
          {
            directionsDisplay.setDirections(result);
          }
          else
          {
            alert("Google DirectionsStatus returned " + status);
          }
        });
      }
      return false;
    });
};

ldcCalc.ShowCrossings = function()
{
  var opts = ldcCalc.getAjaxParams('S', -1);
  var url = ldcCalc.url + opts;
  var xhr = ldcCalc.XHRequest();
  if(!xhr) return;
  
  xhr.open('GET', url, true);
  xhr.timeout = 2000;
  xhr.onreadystatechange = function()
    {
      if(xhr.readyState == 4 && xhr.status == 200)
      {
        var resp = xhr.responseText;
        var jd = JSON.parse(resp);
        var len = jd.roads.length;
        var t = document.getElementById("sched");  
        var tb = t.tBodies[0];
        tb.deleteRow(0); 
        for(i = 0; i < len; i++)
        {  
          var nr = tb.insertRow(-1);
          c = nr.insertCell(0);
          c.innerHTML=jd.roads[i].name;
          c = nr.insertCell(1);
          c.innerHTML=jd.roads[i].loc;
          c = nr.insertCell(2);
          c.innerHTML=jd.roads[i].desc;
          c = nr.insertCell(3);
          c.innerHTML=jd.roads[i].time;
          nr.id="row_"+i;
        }
        ldcCalc.AddRoadLinks(jd.roads, jd.direction);
        ldcCalc.fetchers.push(xhr);
      } // end if(xhr.readyState ...
    };
   xhr.send(null);
};


// AddInspectors
//
// Any item with a class of "inspector" (actually only the 42 tops) will show the
// relevant information when the user hovers over it. Typically this is the name;
// the default schedule time; the time taken from the previous point on the round;
// the time required to get to the next point and the light status.
ldcCalc.AddInspectors = function()
{
  var d = document;
  var f = d.getElementById('leg_notes');
  if(f != null)
  {
    this.AddEvent(f, 'mouseout', function(x){
      var pu = d.getElementById("infoPopup");
      pu.className = "InfoPopUpHidden";
      pu.innerHTML="";
    }, false);
    
    this.AddEvent(f, 'mouseover', function(x){
      var x = x || window.event;
      el = x.target || x.srcElement;
      var h = d.getElementById(el.id);
      var pu = d.getElementById("infoPopup");
      if(h)
      {  
        if(h.className == "inspector")
        {
          var opts = ldcCalc.getAjaxParams('S', -1);
          var url = ldcCalc.url + opts;
          var xhr = ldcCalc.XHRequest();
          var id = el.id.replace(/_/g, ' ');
          if(!xhr) return;
  
          xhr.open('GET', url, true);
          xhr.timeout = 2000;
          xhr.onreadystatechange = function()
          {
            if(xhr.readyState == 4 && xhr.status == 200)
            {
              var resp = xhr.responseText;
              var jd = JSON.parse(resp);
              var len = jd.data.length;
              for(i = 0; i < len; i++)
              {
                if(jd.data[i].pt == id)
                {
                  var t = "<h4>"+jd.data[i].pt + "</h4>";
                  t +="<p>Using your default schedule of " + jd.sched +"</p>";
                  t += "<p><strong>"+ jd.data[i-1].pt + "</strong> to here will take " + jd.data[i].leg + " minutes</p>";
                  t += "<p>From here to <strong>"+ jd.data[i+1].pt + "</strong> will take " + jd.data[i+1].leg + " minutes</p>";
                  t += "<p>The current light status is " + jd.data[i].ls + "</p>";
                  pu.innerHTML = t;
                  pu.className = "InfoPopUp";
                  pu.style.left = (h.offsetLeft-180)+"px";
                  pu.style.top = (h.offsetTop-100)+"px";
                }
              }
              ldcCalc.fetchers.push(xhr);
            }
          };
          xhr.send(null);
        }
      }
    }, false);
  }
};

// Behaviour
//
// Combines the logic that controls whether particular items on the form are enabled
// or disabled.
// One example would be the routeOpt field which is only valid if there is a single
// schedule.

ldcCalc.Behaviour = function()
{
  // Enable or disable the route option drop down field depending on whether there
  // is more than one column
  if(ldcCalc.storage)
  {
  var data = localStorage.getItem(ldcCalc.elements.name +"ScheduleIndex");
  var index = parseInt(data);
  
  if(index > -1)
  {
    ldcCalc.inputs['routeOpt'].disabled=true;
  }
  else
  {
    ldcCalc.inputs['routeOpt'].disabled=false;
  }
  
  // Handle the "Add" button
  //var tab = document.getElementById("schedTable");
  //var tb = tab.tBodies.length;
  
  //if((index > 2) || (tb.rows.length !== 4)) 
  if((index > 2) ) 
  {
    ldcCalc.form.Add.disabled=true;
  }
  else
  {
    ldcCalc.form.Add.disabled=false;
  }
  }
  
  // Handle the "Show" button
/*  if(tb.rows.length !== 4)
  {
    ldcCalc.form.Show.disabled=true;
  }
  else
  {
    ldcCalc.form.Show.disabled=false;
  }*/
};

// RemoveLastCol
//
// Finds and removes the last column in the table.
// Is called from the event handler of the delete button by the drag and drop library.
ldcCalc.RemoveLastCol = function()
{
  var tab = document.getElementById("schedTable");
  if(null != tab)
  {
    var last = tab.tBodies[0].rows[0].cells.length -1;
    var rowCount = tab.tBodies[0].rows.length;
    var x = tab.tHead.rows[0].cells[last]; 
    x.parentNode.removeChild(x);
    for(i = 0; i < rowCount; i++)
    {
      x = tab.tBodies[0].rows[i].cells[last];
      x.parentNode.removeChild(x);
    }
  }
  var f = document.getElementById(ldcCalc.elements.name + 'Form');
  f.Add.disabled=false;
  
  ldcCalc.Behaviour();
  
};


// ShowLightStatus
//
// 

ldcCalc.ShowLightStatus = function()
{
  var opts = ldcCalc.getAjaxParams('L', -1);
  var url = ldcCalc.url + opts;
  var xhr = ldcCalc.XHRequest();
  if(!xhr) return;
  
  xhr.open('GET', url, true);
  xhr.timeout = 2000;
  xhr.onreadystatechange = function()
    {
      if(xhr.readyState == 4 && xhr.status == 200)
      {
        var resp = xhr.responseText;
        var jd = JSON.parse(resp);
        var len = jd.data.length;
               
        for(i = 0; i < len; i++)
        {   
          // Now need to sort out which element to format
          var p = document.getElementById(jd.data[i].pt);
          if(p)
          {
            switch(jd.data[i].ls)
            {
              case "Dark":
                p.className +=" bgrDark";
                p.setAttribute('title',"This section will be in the dark.");
                break;
              case "Dawn":
                p.className +=" bgrCrepuscular";
                p.setAttribute('title',"This section will be in dawn light and may not require head torches.");
                break;
              case "Dusk":
                p.className+=" bgrCrepuscular";
                p.setAttribute('title',"The light will be fading during this section.");
                break;
              default:
                p.setAttribute('title',"For your schedule, this section will be in daylight.");                
                break;
            }
          }
        }
        ldcCalc.fetchers.push(xhr);
        ldcCalc.AddInspectors();
      }
    };
   xhr.send(null);
};



// DefaultClick
//
// The event handler for the "Default" button
// Store the default data, then call the common ShowSchedule function to
// do the actual display.
ldcCalc.DefaultClick = function()
{
  var i, m, y;
  var now = new Date();
  var curYear = now.getFullYear();

  for(i = 0; i < 12; ++i)
  {
    if(ldcCalc.inputs['month'].options[i].selected)
      m = i;
  }
  for(i = 0; i < ldcCalc.inputs['year'].options.length; ++i)
  {
    if(ldcCalc.inputs['year'].options[i].selected)
    {
      y = curYear + i;
    }
  }

  ldcCalc.storeData(ldcCalc.form, -1);  
  ldcCalc.ShowSchedule('S', -1);
  moonCalc(ldcCalc.inputs['day'].value, m, y);
};

// AddClick
//
// This is the button handler function for the "Add" button.
// It gets the last index used, then uses that to determine if the data exists
// then increments the index and saves the data using this index. Finally it 
// calls the common ShowSchedule function to do the actual display.
ldcCalc.AddClick = function()
{
  var index = 0;
  var data = localStorage.getItem(ldcCalc.elements.name +"ScheduleIndex");
  
  if(data != null)
  {
    var s = localStorage.getItem(ldcCalc.elements.name +"Schedule_"+data);
    if(s != null)
    {
      index = parseInt(data) + 1;
    }
  }
  localStorage.setItem(ldcCalc.elements.name +"ScheduleIndex", index); 
  
  ldcCalc.storeData(ldcCalc.form, index);
  ldcCalc.ShowSchedule('S', index);
  
  ldcCalc.Behaviour();
};

// ResetClick
//
// Resets the default schedule to the nearest Saturday to Midsummer's
// day.
ldcCalc.ResetClick = function()
{
  if(this.storage == true)
  {
    localStorage.removeItem(ldcCalc.elements.name +'FormData');
  }
  else
  {
    var expDate = new Date();
    expDate.setTime( expDate.getTime() + ( 360 * 24 * 60 * 60 * 1000 ) );
    expDate.toGMTString(); 
    var t = new Date();
    var ms = new Date(t.getFullYear(), 5, 20);
    var dow = ms.getDay();
    var d = ms.getDate();
    if(dow != 6)
    {
      if(dow < 3) 
        d -= (dow+1);
      else
        d += (6-dow);
    }    

    document.cookie = ldcCalc.elements.name + "Data=0-5-"+d+"-0-0-0-0-0-51-0-0-0-51-0-0-0-0; expires=" + expDate.toGMTString();
  }
  ldcCalc.GetData(ldcCalc.form, -1);
};

// ShowClick
//
// This is the handler for the "Show" button. It will create a new
// window with a schedule based on the current values in the form.
ldcCalc.ShowClick = function()
{
  ldcCalc.storeData(ldcCalc.form, -1);
  ldcCalc.ShowPrintableSchedule(-1);
};

ldcCalc.fillPeaks = function(r)
{
  var len = r.peaks.length;
  
  if(ldcCalc.inputs['peaks_2'])
  {
    ldcCalc.inputs['peaks_1'].options.length = 0;
    for(i = 0; i < len; i++)
    {
      ldcCalc.inputs['peaks_1'].options[i] = new Option(r.peaks[i].pt,i)
    }
    ldcCalc.inputs['peaks_1'].options.selectedIndex = len-1;
  }
  if(ldcCalc.inputs['peaks_2'])
  {
    ldcCalc.inputs['peaks_2'].options.length = 0;
    for(i = 0; i < len; i++)
    {
      ldcCalc.inputs['peaks_2'].options[i] = new Option(r.peaks[i].pt,i)
    }
    ldcCalc.inputs['peaks_2'].options.selectedIndex = len-1;
  }
  if(ldcCalc.inputs['peaks_3'])
  {
    ldcCalc.inputs['peaks_3'].options.length = 1;
    ldcCalc.inputs['peaks_3'].options[0] = new Option(r.peaks[len-1].pt,i)
  }
};

/////////////////////////////////////////////////////////////////////////////

// AddControls
//
// This adds the controls needed by the main application on my site.
// There are more controls as it is neccessarily more complicated than the
// widget version. 
//
// Default  :  This is the equivalent of "Done" - this schedule is used for the
//             various pages around the section.
// Add      :  This adds a schedule to the table.
// Reset    :  This puts the form back to the initial state, it clears all stored schedules.
// Show     :  This creates the window that may be printed.
// 

ldcCalc.AddControls = function(f)
{
  var d = document;
  ldcCalc.form = f;
  
  var url = this.url +'?A=P&dir=0';
  xhr = this.XHRequest();
  if(!xhr) return;
  
  xhr.open('GET', url, true);
  xhr.timeout = 2000;
  xhr.onreadystatechange = function()
    {
      if(xhr.readyState == 4 && xhr.status == 200)
      {
        var resp = xhr.responseText;
        var jsonResp = JSON.parse(resp);
        ldcCalc.fillPeaks(jsonResp);
        ldcCalc.fetchers.push(xhr);
      }
    };
  xhr.send(null);  
  
  var b = document.createElement('input');
  b.setAttribute('type', 'button');
  b.setAttribute('value', "Default");
  b.setAttribute('id', "Default");
  this.AddEvent(b, 'click', ldcCalc.DefaultClick, false);
  f.appendChild(b);
  
  // This button should be greyed out (disabled = "true") when there are no entries in the comparison
  // table.
  b = document.createElement('input');
  b.setAttribute('type', 'button');
  b.setAttribute('value', "Add");
  b.setAttribute('id', "Add");
  this.AddEvent(b, 'click', ldcCalc.AddClick, false);
  f.appendChild(b);

  b = document.createElement('input');
  b.setAttribute('type', 'button');
  b.setAttribute('value', "Reset");
  b.setAttribute('id', "Reset");
  this.AddEvent(b, 'click', this.ResetClick, false);
  f.appendChild(b);

  b = document.createElement('input');
  b.setAttribute('type', 'button');
  b.setAttribute('value', "Show");
  b.setAttribute('id', "Show");
  this.AddEvent(b, 'click', this.ShowClick, false);
  f.appendChild(b);
     
  this.GetData(f, -1);
  
  ldcCalc.Behaviour();
};

ldcCalc.form=null;
ldcCalc.jsonResponse=null;

/// End of file


// ShowPrintableSchedule
//
// This is essentially the function from the original calculator that
// creates a new window with the schedule that the user may print. It
// will always use the values from the visible form. If the user has
// several schedules and wishes to print one of the others then they
// must swap it with the default.
ldcCalc.ShowPrintableSchedule = function (index)
{
  var opts = ldcCalc.getAjaxParams('S', index);
  var url = ldcCalc.url + opts;
  var xhr = ldcCalc.XHRequest();
  if(!xhr) return;
  xhr.open('GET', url, true);
  xhr.timeout = 2000;
  xhr.onreadystatechange = function()
    {
      if(xhr.readyState == 4 && xhr.status == 200)
      {
        var resp = xhr.responseText;
        var jd = JSON.parse(resp);
        var monthNames = new Array("January", "February", "March", "April","May", "June", "July", "August", "September","October", "November", "December");
        var c = "<html><head><title>Bob Graham Schedule<\/title>";
        c += "<link rel='stylesheet' type='text/css' href='./style/screen.css' title='desktop' media='screen' >\n<\/head>";
        c += "<body><h1 id='bgrHead'>BGR Schedule<\/h1><p class='bgPopup'>This is a schedule for a " + jd.sched + " " +  jd.direction + " round beginning at " + jd.start + " on " + jd.startDate + "<\/p>";
        if(jd.bst == 1)
        {
          c += "<p class='bgPopup'>All times are BST.<\/p>";
        }
        else
        {
          c += "<p class='bgPopup'>All times are GMT.<\/p>";
        }
        c += "<table class='plainBgr' border=2><tr><td>Sunrise<\/td><td>"+jd.sunrise + "<\/td><td colspan=3><\/td><\/tr><tr><td>Sunset<\/td><td>"+jd.sunset+"<\/td><td colspan='3'><\/td><\/tr>";
        c += "<tr><td>Companion name<\/td><td colspan=4><\/td><\/tr>";
        c += "<tr><td>Companion name<\/td><td colspan=4><\/td><\/tr>";
        c += "<tr><td><b>Location<\/b><\/td><td><b>State of light<\/b><\/td><td><b>Leg time<\/b><\/td><td><b>Estimated time<\/b><\/td><td><b>Your time at this location (notes)<\/b><\/td><\/tr>";
        var len = jd.data.length;
        leg = 0;

        for(i = 0; i < len; i++)
        {
          c +="<tr><td>"+jd.data[i].pt+"<\/td><td>"+jd.data[i].ls+"<\/td><td>"+jd.data[i].leg+" " + jd.data[i].r + "<\/td><td>" + jd.data[i].t+"<\/td><td>&nbsp;<\/td><\/tr>\n";
          // If this is the end of a leg then show the time for that leg and set things up for
          // the next leg.
          if(i == jd.legtimes[leg].id)
          {
            c +="<tr><td>Time for leg<\/td><td>"+ jd.legtimes[leg].t+"<\/td><td colspan=3>&nbsp;<\/td><\/tr>\n";
            // Make an exception of the last point in the list.
            if(i != (len - 1))
            {
              c += "<tr><td>Companion name<\/td><td colspan=4><\/td><\/tr>";
              c += "<tr><td>Companion name<\/td><td colspan=4><\/td><\/tr>";
            } // end if
            leg++;
          } // end if
        } // end for
        c += "<\/table><br><p class='bgPopup'>Table created from http:\/\/bobwightman.co.uk\/run\/bgr_schedule_calculator.php (Bob Wightman)<\/body><\/html>";

        newWindow = window.open("BGR Schedule", "aaa","status,resizable,scrollbars,toolbar,height=400,width=800");
        newWindow.focus();
        newWindow.document.write(c);
        newWindow.document.close();
        ldcCalc.fetchers.push(xhr);
      }
    };
  xhr.send(null);  
};

// HomeLink
//
// Puts in a link to the main calculator page
ldcCalc.HomeLink = function(f, link, content)
{
}

//
// CheckParams
//
// Depending on the current state of the storage and/or calculator, certain actions are either
// undesired or don't make sense - you can't compare a BG round that does High Rise first with one
// that does Sergeant Man first for example.
// This function does the checking and returns a non-zero integer for those fault conditions.
ldcCalc.CheckParams = function(d)
{
  var data = localStorage.getItem(ldcCalc.elements.name +"ScheduleIndex"),
      opts;
  
  if(data != null)
  {
    var i = 0;
    var lim = parseInt(data);
    lim++;
    
    for(i = 0; i < lim; i++)
    {
      var s = localStorage.getItem(ldcCalc.elements.name +"Schedule_"+i);
      if(s != null)
      {
        var jd = JSON.parse(s);
        if(jd.routeOpt != d)
        {
          opts = {title: "Warning 110", 
                  message: "You can't add a schedule with a different route option to that already in use in the table",
                  type: "warning",
                  width: 425,
                  height: jsDlg.USE_DEFAULT,
                  autoHide: false,
                  atCursor: false,
                  modal: true};
            jsDlg.showDialog(opts, new Array(new button('OK', true)));
            return 1;
        }
      }
    }
  }
  return 0;
};

// FinalInit
//
// This function does the last bit of initialisation for the application side of things. This
// takes the form of a pair of event handlers for the help system along with setting the state
// of the 'Add' button depending on the quality of the browser. Finally the route options are
// set along with creating the table showing the state of the moon.
ldcCalc.FinalInit = function()
{
  var d = document;
  
  if(ldcCalc.form != null)
  {
    this.AddEvent(ldcCalc.form, 'mouseover', function(x){
        var x = x || window.event;
        el = x.target || x.srcElement;
        var t = el.id+"_help";
        if(el.id.indexOf("bgrrx") == 0)
          t = "bgrrxing_help";
        var h = d.getElementById(t);
        if(h)
        {
          h.className = ' bgrCalcHelp ';
          h.style.position = 'absolute';
          h.style.left = "630px";
          h.style.top = "620px";
          h.style.width = "20%";
        }
       }, false);
    this.AddEvent(ldcCalc.form, 'mouseout', function(x){
        var x = x || window.event;
        el = x.target || x.srcElement;
        var t = el.id+"_help";
        if(el.id.indexOf("bgrrx") == 0)
          t = "bgrrxing_help";
        var h = d.getElementById(t);
        if(h)
        {
          h.className = ' linked ';
        }
      }, false);
      
    ldcCalc.form.Add.disabled=false;
    if(this.storage == false)
    {
      ldcCalc.form.Add.setAttribute('title', "In order to use this button you must upgrade your browser");
      ldcCalc.form.Add.disabled=true;
    }
    ldcCalc.inputs['routeOpt'].options[0] = new Option("Sergeant Man first", 0);
    ldcCalc.inputs['routeOpt'].options[1] = new Option("High Raise first", 1);
    
    setTimeout("ldcCalc.GetData(ldcCalc.form, -1)", 200);
    
    var i, m, y;
    var now = new Date()
    var curYear = now.getFullYear()

    for(i = 0; i < 12; ++i)
    {
      if (ldcCalc.inputs['month'].options[i].selected)
        m = i;
    }
    for(i = 0; i < ldcCalc.inputs['year'].options.length; ++i)
    {
      if (ldcCalc.inputs['year'].options[i].selected)
        y = curYear + i;
    }
    moonCalc(ldcCalc.inputs['day'].value, m, y);
  }
};

///////////////////////////////////////////////////////////////////////////////////
//
// Pretty well everything down from here is what can be considered unique to the
// BGR calculator.

// setHost
// Allows simpler development than having to comment and uncomment lines.
// Also checks if the user has mistakenly used "http://www.bobwightman.co.uk"
ldcCalc.setHost = function()
{
  var opts;
  
  if(window.location.hostname === "bobwightman.co.uk")
  {
    ldcCalc.url = 'http://bobwightman.co.uk/run/api/v3/bgr.php';
  }
  else
  {
    ldcCalc.url = 'http://localhost/~Bob/bobwightman/run/api/v3/bgr.php';
  }

  if(window.location.hostname.indexOf("www") !== -1)
  {
    var errStr = "<p>Unable to use the URL in your address bar! ";
    errStr += "The address (URL) should begin:</p><br>";
    //errStr += "<pre>   " + window.location.hostname + "</pre>";
    errStr += "<pre><b>     http://bobwightman.co.uk</b></pre><br>";
    errStr += "<p>Note that there is no \"www\" at the start of the address. Please change both the address in use and any bookmarks to this site or page.</p>";

    opts = {title: "Error 210 - Invalid URL", 
            message: errStr,
            type: "error",
            width: 425,
            height: jsDlg.USE_DEFAULT,
            autoHide: false,
            atCursor: false,
            modal: true};

    jsDlg.showDialog(opts, new Array(new jsDlg.button('OK', true)));
  }
};

var options = {
  name:"bgr"
};

var elems = {
    calcForm:{id:'bgrForm'},
    inputs:{
      'year': {id:'bgrYear', type: 'select', label:'Year:'},
      'month': {id:'bgrMonth', type: 'select', label:'Month:'},
      'day': {id:'bgrDay', type: 'select', label: 'Day:'},
      'startTime': {id:'bgrStart', type: 'select', label: 'Start time (24hrs):'},
      'schedule': {id:'bgrSched', type: 'select', label: 'Use this schedule:', step: '0.25'},
      'peaks_1': {id:'bgrP1', type: 'select', label: 'As far as:'},
      'schedule_2': {id:'bgrSched2', type: 'select', label: 'Use this schedule:', step: '0.25'},
      'peaks_2': {id:'bgrP2', type: 'select', label: 'As far as:'},
      'schedule_3': {id:'bgrSched3', type: 'select', label: 'Then this schedule:', step: '0.25'},
      'peaks_3': {id:'bgrP3', type: 'select', label: 'To the end:'},
      'direction': {id:'bgrDir', type: 'select', label: 'Direction:'},
      'routeOpt': {id:'bgrOpt', type: 'select', label: 'Route:'},
      'rocky': {id:'bgrRocky', type: 'select', label: 'Rocky'},
      'support_1': {id:'bgrrx1', type: 'select', label: 'Threlkeld stop:'},
      'support_2': {id:'bgrrx2', type: 'select', label: 'Dunmail Raise stop:'},
      'support_3': {id:'bgrrx3', type: 'select', label: 'Wasdale stop:'},
      'support_4': {id:'bgrrx4', type: 'select', label: 'Honister stop:'}
    }
  };

// Kick everything off.
ldcCalc.setHost();
ldcCalc.CalcInitialise(options, 'bgrCalculator', elems);


function bgrMenuSwitcher()
{
  var xmlHttp;
  setBgrMenu();
  try
  {
  // Firefox, Opera 8.0+, Safari
  xmlHttp=new XMLHttpRequest();
  }
  catch (e)
  {
    // Internet Explorer
    try
    {
      xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch (e)
      {
      try
      {
        xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch (e)
      {
        alert("Your browser does not support AJAX!");
        return false;
      }
    }
  }
  xmlHttp.onreadystatechange=function()
  {
    if(xmlHttp.readyState==4)
    {
      document.getElementById("AjaxBgrMenu").innerHTML=xmlHttp.responseText;
      var el = document.getElementById('menuswitcher');

      addEvent(el, 'click', bgrMenuSwitcher, false);
    }
  }
  
  // Do some randomisation to stop IE cacheing things.
  var r = parseInt(Math.random() * 99999999);
  var url = "snippets/bgr-menu.inc.php";
  var modUrl = url + "?rand="+r;
  xmlHttp.open("GET",modUrl,true);
  xmlHttp.send(null);
}

function addEvent(elm, evType, fn, useCapture)
{
   if(elm.addEventListener)
   {
      elm.addEventListener(evType, fn, useCapture);
      return true;
   }
   else if(elm.attachEvent)
   {
      return elm.attachEvent('on' + evType, fn);
   }
   else
   {
      elm['on' + evType] = fn;
   }
}
var el = document.getElementById('menuswitcher');

addEvent(el, 'click', bgrMenuSwitcher, false);


