text: /*! DatePicker v6.3.6 MIT/GPL2 @freqdec */
  var datePickerController = (function datePickerController() {
  
      "use strict";
  
      var debug               = false,
          isOpera             = Object.prototype.toString.call(window.opera) === "[object Opera]",
          describedBy         = "",
          languageInfo        = parseUILanguage(),
          nbsp                = String.fromCharCode(160),
          datePickers         = {},
          weeksInYearCache    = {},
          bespokeTitles       = {},
          uniqueId            = 0,
          finalOpacity        = 100,
          cssAnimations       = null,
          transitionEnd       = "",
          buttonTabIndex      = true,
          mouseWheel          = true,
          deriveLocale        = true,
          localeImport        = false,
          nodrag              = false,
          langFileFolder      = false,
          returnLocaleDate    = false,
          kbEvent             = false,
          dateParseFallback   = true,
          cellFormat          = "%d %F %Y",
          titleFormat         = "%F %d, %Y",
          statusFormat        = "",
          formatParts         = isOpera ? ["%j"] : ["%j", " %F %Y"],
          dPartsRegExp        = /%([d|j])/,
          mPartsRegExp        = /%([M|F|m|n])/,
          yPartsRegExp        = /%[y|Y]/,
          noSelectionRegExp   = /date-picker-unused|out-of-range|day-disabled|not-selectable/,
          formatTestRegExp    = /%([d|j|M|F|m|n|Y|y])/,
          formatSplitRegExp   = /%([d|D|l|j|N|w|S|W|M|F|m|n|t|Y|y])/,
          rangeRegExp         = /^((\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01]))$/,
          wcDateRegExp        = /^(((\d\d\d\d)|(\*\*\*\*))((0[1-9]|1[012])|(\*\*))(0[1-9]|[12][0-9]|3[01]))$/,
          wsCharClass         = "\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029",
          // https://gist.github.com/padolsey/527683
          oldIE               = (function(){var undef,v = 3,div = document.createElement('div'),all = div.getElementsByTagName('i');while (div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',all[0]); return v > 4 ? v : undef;}());
  
      (function() {
          var scriptFiles = document.getElementsByTagName('script'),
              json        = parseJSON(String(scriptFiles[scriptFiles.length - 1].innerHTML).replace(/[\n\r\s\t]+/g, " ").replace(/^\s+/, "").replace(/\s+$/, ""));
  
          if(typeof json === "object" && !("err" in json)) {
              affectJSON(json);
          };
  
          if(deriveLocale && typeof(fdLocale) != "object") {
              var head   = document.getElementsByTagName("head")[0] || document.documentElement,
                  loc    = langFileFolder ? langFileFolder : scriptFiles[scriptFiles.length - 1].src.substr(0, scriptFiles[scriptFiles.length - 1].src.lastIndexOf("/")) + "/lang/",
                  script,
                  i;
  
              for(i = 0; i < languageInfo.length; i++) {
                  script          = document.createElement('script');
                  script.type     = "text/javascript";
                  script.src      = loc + languageInfo[i] + ".js";
                  script.charSet  = "utf-8";
  
                  if(oldIE && oldIE < 8) {
                      var bases = document.getElementsByTagName('base');
                      if (bases.length && bases[0].childNodes.length) {
                              bases[0].appendChild(script);
                      } else {
                              head.appendChild(script);
                      };
                      bases = null;
                  } else {
                      head.appendChild(script);
                  };
              };
  
              script = null;
          } else {
              returnLocaleDate = true;
          };
      })();
  
      function removeChildNodes(elem) {
          while(elem.firstChild) {
              elem.removeChild(elem.firstChild);
          };
      };
  
      function addClass(e, c) {
          if(new RegExp("(^|[" + wsCharClass + "])" + c + "([" + wsCharClass + "]|$)").test(e.className)) {
              return;
          };
          e.className += ( e.className ? " " : "" ) + c;
      };
  
      function removeClass(e, c) {
          e.className = !c ? "" : e.className.replace(new RegExp("(^|[" + wsCharClass + "])" + c + "([" + wsCharClass + "]|$)"), " ").replace(new RegExp("/^[" + wsCharClass + "][" + wsCharClass + "]*/"), '').replace(new RegExp("/[" + wsCharClass + "][" + wsCharClass + "]*$/"), '');
      };
  
      // Attempts to parse the current language from the HTML element. Defaults to "en" if none given
      function parseUILanguage() {
          var languageTag = document.getElementsByTagName('html')[0].getAttribute('lang') || document.getElementsByTagName('html')[0].getAttribute('xml:lang');
          languageTag = !languageTag ? "en" : languageTag.toLowerCase();
          return languageTag.search(/^([a-z]{2,3})-([a-z]{2})$/) != -1 ? [languageTag.match(/^([a-z]{2,3})-([a-z]{2})$/)[1], languageTag] : [languageTag];
      };
  
      // Cross browser split from http://blog.stevenlevithan.com/archives/cross-browser-split
      var cbSplit = function(str, separator, limit) {
          // if `separator` is not a regex, use the native `split`
          if(Object.prototype.toString.call(separator) !== "[object RegExp]") {
                  return cbSplit._nativeSplit.call(str, separator, limit);
          };
  
          var output        = [],
              lastLastIndex = 0,
              flags         = "",
              separator     = RegExp(separator.source, "g"),
              separator2, match, lastIndex, lastLength;
  
          str = str + "";
  
          if(!cbSplit._compliantExecNpcg) {
              separator2 = RegExp("^" + separator.source + "$(?!\\s)", flags);
          };
  
          /* behavior for `limit`: if it's...
          - `undefined`: no limit.
          - `NaN` or zero: return an empty array.
          - a positive number: use `Math.floor(limit)`.
          - a negative number: no limit.
          - other: type-convert, then use the above rules. */
          if(limit === undefined || +limit < 0) {
              limit = Infinity;
          } else {
              limit = Math.floor(+limit);
              if(!limit) {
                  return [];
              };
          };
  
          while(match = separator.exec(str)) {
              lastIndex = match.index + match[0].length; // `separator.lastIndex` is not reliable cross-browser
  
              if (lastIndex > lastLastIndex) {
                  output.push(str.slice(lastLastIndex, match.index));
  
                  // fix browsers whose `exec` methods don't consistently return `undefined` for nonparticipating capturing groups
                  if(!cbSplit._compliantExecNpcg && match.length > 1) {
                      match[0].replace(separator2, function () {
                          for (var i = 1; i < arguments.length - 2; i++) {
                              if(arguments[i] === undefined) {
                                  match[i] = undefined;
                              };
                          };
                      });
                  };
  
                  if(match.length > 1 && match.index < str.length) {
                      Array.prototype.push.apply(output, match.slice(1));
                  };
  
                  lastLength = match[0].length;
                  lastLastIndex = lastIndex;
  
                  if(output.length >= limit) {
                      break;
                  };
              };
  
              if(separator.lastIndex === match.index) {
                  // avoid an infinite loop
                  separator.lastIndex++;
              };
          };
  
          if(lastLastIndex === str.length) {
              if (lastLength || !separator.test("")) {
                  output.push("");
              };
          } else {
              output.push(str.slice(lastLastIndex));
          };
  
          return output.length > limit ? output.slice(0, limit) : output;
      };
      // NPCG: nonparticipating capturing group
      cbSplit._compliantExecNpcg = /()??/.exec("")[1] === undefined;
      cbSplit._nativeSplit = String.prototype.split;
  
      // Affects the JSON passed to the script
      function affectJSON(json) {
          if(!(typeof json === "object")) {
              return;
          };
  
          var key,
              switchObj = {
                  "debug":function(value) {
                      debug = !!value;
                      return true;
                  },
                  "lang":function(value) {
                      if(typeof value === "string" && value.search(/^[a-z]{2,3}(-([a-z]{2}))?$/i) != -1) {
                          languageInfo = [value.toLowerCase()];
                          returnLocaleDate = true;
                          deriveLocale = true;
                      };
                      return true;
                  },
                  "nodrag":function(value) {
                      nodrag = !!value;
                      return true;
                  },
                  "buttontabindex":function(value) {
                      buttonTabIndex = !!value;
                      return true;
                  },
                  "derivelocale":function(value) {
                      deriveLocale = !!value;
                      return true;
                  },
                  "mousewheel":function(value) {
                      mouseWheel = !!value;
                      return true;
                  },
                  "cellformat":function(value) {
                      if(typeof value === "string") {
                          parseCellFormat(value);
                      };
                      return true;
                  },
                  "titleformat":function(value) {
                      if(typeof value === "string") {
                          titleFormat = value;
                      };
                      return true;
                  },
                  "statusformat":function(value) {
                      if(typeof value === "string") {
                          statusFormat = value;
                      };
                      return true;
                  },
                  "describedby":function(value) {
                      if(typeof value === "string") {
                          describedBy = value;
                      };
                      return true;
                  },
                  "finalopacity":function(value) {
                      if(typeof value === 'number' && (+value > 20 && +value <= 100)) {
                          finalOpacity = parseInt(value, 10);
                      };
                      return true;
                  },
                  "bespoketitles":function(value) {
                      if(typeof value === "object") {
                          bespokeTitles = {};
                          for(var dt in value) {
                              if(value.hasOwnProperty(dt) && String(dt).match(wcDateRegExp) != -1) {
                                  bespokeTitles[dt] = String(value[dt]);
                              };
                          };
                      };
                      return true;
                  },
                  "dateparsefallback":function(value) {
                      dateParseFallback = !!value;
                      return true;
                  },
                  "languagefilelocation":function(value) {
                      langFileFolder = value;
                      return true;
                  },
                  "_default":function() {
                      if(debug) {
                          throw "Unknown key located within JSON data: " + key;
                      };
                      return true;
                  }
              };
  
          for(key in json) {
              if(!json.hasOwnProperty(key)) {
                  continue;
              };
              (switchObj.hasOwnProperty(String(key).toLowerCase()) && switchObj[String(key).toLowerCase()] || switchObj._default)(json[key]);
          };
      };
  
      // Parses the JSON passed either between the script tags or by using the
      // setGlobalOptions method
      function parseJSON(str) {
          if(!(typeof str === 'string') || str == "") {
              return {};
          };
          try {
              // Does a JSON (native or not) Object exist
              if(typeof JSON === "object" && JSON.parse) {
                  return window.JSON.parse(str);
              // Genious code taken from: http://kentbrewster.com/badges/
              } else if(/debug|lang|nodrag|buttontabindex|derivelocale|mousewheel|cellformat|titleformat|statusformat|describedby|finalopacity|bespoketitles|dateparsefallback/.test(str.toLowerCase())) {
                  var f = Function(['var document,top,self,window,parent,Number,Date,Object,Function,',
                      'Array,String,Math,RegExp,Image,ActiveXObject;',
                      'return (' , str.replace(/<\!--.+-->/gim,'').replace(/\bfunction\b/g,'function-') , ');'].join(''));
                  return f();
              };
          } catch (e) { };
  
          if(debug) {
              throw "Could not parse the JSON object";
          };
  
          return {"err":1};
      };
  
      // Parses the cell format to use whenever the datepicker has keyboard focus
      function parseCellFormat(value) {
          if(isOpera) {
              // Don't use hidden text for opera due to the default
              // "blue" browser focus outline stretching outside of the viewport
              // and degrading visual accessibility. Harsh & hackish though...
              formatParts = ["%j"];
              cellFormat  = "%j %F %Y";
              return;
          };
  
          // If no day part stipulated then use presets
          if(value.match(/%([d|j])/) == -1) {
              return;
          };
  
          // Basic split on the %j or %d modifiers
          formatParts = cbSplit(value, /%([d|j])/);
          cellFormat  = value;
      };
  
      function pad(value, length) {
          length = Math.min(4, length || 2);
          return "0000".substr(0,length - Math.min(String(value).length, length)) + value;
      };
  
      // Very, very basic event functions
      function addEvent(obj, type, fn) {
          if(obj.addEventListener) {
              obj.addEventListener(type, fn, true);
          } else if(obj.attachEvent) {
              obj.attachEvent("on"+type, fn);
          };
      };
      function removeEvent(obj, type, fn) {
          try {
              if(obj.removeEventListener) {
                  obj.removeEventListener(type, fn, true);
              } else if(obj.detachEvent) {
                  obj.detachEvent("on"+type, fn);
              };
          } catch(err) {};
      };
      function stopEvent(e) {
          e = e || document.parentWindow.event;
          if(e.stopPropagation) {
              e.stopPropagation();
              e.preventDefault();
          };
  
          if(oldIE) {
              e.cancelBubble = true;
              e.returnValue = false;
          };
          
          return false;
      };
  
      function setARIARole(element, role) {
          if(element && element.tagName) {
              element.setAttribute("role", role);
          };
      };
  
      function setARIAProperty(element, property, value) {
          if(element && element.tagName) {
              element.setAttribute("aria-" + property, value);
          };
      };
  
      // Sets a tabindex attribute on an element, bends over for IE.
      function setTabIndex(e, i) {
          e.setAttribute(oldIE ? "tabIndex" : "tabindex", i);
          e.tabIndex = i;
      };
  
      function dateToYYYYMMDD(dt) {
          return dt instanceof Date && !isNaN(dt) ? dt.getFullYear() + pad(dt.getMonth() + 1) + "" + pad(dt.getDate()) : dt;
      };
  
      // The datePicker object itself
      function datePicker(options) {
          this.dateSet             = null;
          this.timerSet            = false;
          this.visible             = false;
          this.fadeTimer           = null;
          this.timer               = null;
          this.yearInc             = 0;
          this.monthInc            = 0;
          this.dayInc              = 0;
          this.mx                  = 0;
          this.my                  = 0;
          this.x                   = 0;
          this.y                   = 0;
          this.created             = false;
          this.disabled            = false;
          this.opacity             = 0;
          this.opacityTo           = 100;
          this.finalOpacity        = 100;
          this.inUpdate            = false;
          this.kbEventsAdded       = false;
          this.fullCreate          = false;
          this.selectedTD          = null;
          this.cursorTD            = null;
          this.cursorDate          = options.cursorDate ? options.cursorDate : "",
          this.date                = options.cursorDate ? new Date(+options.cursorDate.substr(0,4), +options.cursorDate.substr(4,2) - 1, +options.cursorDate.substr(6,2),5,0,0) : new Date();
          this.defaults            = {};
          this.dynDisabledDates    = {};
          this.dateList            = [];
          this.bespokeClass        = options.bespokeClass;
          this.firstDayOfWeek      = localeImport.firstDayOfWeek;
          this.interval            = new Date();
          this.clickActivated      = false;
          this.showCursor          = false;
          this.noFocus             = true;
          this.kbEvent             = false;
          this.delayedUpdate       = false;
          this.bespokeTitles       = {};
          this.bespokeTabIndex     = options.bespokeTabIndex;
  
          for(var thing in options) {
              if(!options.hasOwnProperty(thing) || String(thing).search(/^(callbacks|formElements|enabledDates|disabledDates)$/) != -1) {
                  continue;
              };
              this[thing] = options[thing];
          };
  
          if(oldIE) {
              this.iePopUp = null;
          };
          
          for(var i = 0, prop; prop = ["callbacks", "formElements"][i]; i++) {
              this[prop] = {};
              if(prop in options) {
                  for(thing in options[prop]) {
                      if(options[prop].hasOwnProperty(thing)) {
                              this[prop][thing] = options[prop][thing];
                      };
                  };
              };
          };
  
          // Adjust time to stop daylight savings madness on windows
          this.date.setHours(5);
  
          // Called from an associated form elements onchange event
          this.changeHandler = function() {
              // In a perfect world this shouldn't ever happen
              if(o.disabled) {
                  return;
              };
              o.setDateFromInput();
              o.callback("dateset", o.createCbArgObj());
          };
  
          // Creates the object passed to the callback functions
          this.createCbArgObj = function() {
              return this.dateSet ? {
                  "id"    :this.id,
                  "date"  :this.dateSet,
                  "dd"    :pad(this.date.getDate()),
                  "mm"    :pad(this.date.getMonth() + 1),
                  "yyyy"  :this.date.getFullYear()
                  } : {
                  "id"    :this.id,
                  "date"  :null,
                  "dd"    :null,
                  "mm"    :null,
                  "yyyy"  :null
                  };
          };
  
          // Attempts to grab the window scroll offsets
          this.getScrollOffsets = function() {
              if(typeof(window.pageYOffset) == 'number') {
                  //Netscape compliant
                  return [window.pageXOffset, window.pageYOffset];
              } else if(document.body && (document.body.scrollLeft || document.body.scrollTop)) {
                  //DOM compliant
                  return [document.body.scrollLeft, document.body.scrollTop];
              } else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
                  //IE6 standards compliant mode
                  return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
              };
              return [0,0];
          };
  
          // Calculates the current list of disabled & enabled dates for a specific year/month
          this.getDateExceptions = function(y, m) {
  
              m = pad(m);
  
              var obj     = {},
                  lower   = o.firstDateShown,
                  upper   = o.lastDateShown,
                  rLength = o.dateList.length,
                  rNumber, workingDt, workingY, workingM, dtLower, dtUpper, i, dt, dt1, dt2, rngLower, rngUpper, cDate;
  
              if(!upper || !lower) {
                  lower = o.firstDateShown = y + pad(m) + "01";
                  upper = o.lastDateShown  = y + pad(m) + pad(daysInMonth(m, y));
              };
  
              dtLower = Number(lower.substr(0,6));
              dtUpper = Number(upper.substr(0,6));
  
              workingDt = String(dtLower);
  
              while(+workingDt <= dtUpper) {
                  workingY = workingDt.substr(0,4);
                  workingM = workingDt.substr(4,2);
  
                  for(rNumber = 0; rNumber < rLength; rNumber++) {
                      dt1 = String(o.dateList[rNumber].rLow).replace(/^(\*\*\*\*)/, workingY).replace(/^(\d\d\d\d)(\*\*)/, "$1"+workingM);
                      dt2 = String(o.dateList[rNumber].rHigh).replace(/^(\*\*\*\*)/, workingY).replace(/^(\d\d\d\d)(\*\*)/, "$1"+workingM);
  
                      // Single date
                      if(dt2 == 1) {
                          if(+dt1 >= +o.firstDateShown && +dt1 <= +o.lastDateShown) {
                              obj[dt1] = o.dateList[rNumber].type;
                          };
                          continue;
                      };
  
                      // Date Range
                      if(dt1 <= dt2
                         &&
                         workingDt >= dt1.substr(0,6)
                         &&
                         workingDt <= dt2.substr(0,6)
                         ) {
                          rngLower = Math.max(dt1, Math.max(String(workingDt) + "01", this.firstDateShown));
                          rngUpper = Math.min(dt2, Math.min(String(workingDt) + "31", this.lastDateShown));
                          for(i = rngLower; i <= rngUpper; i++) {
                              obj[i] = o.dateList[rNumber].type;
                          };
                      };
                  };
  
                  // Let the Date Object take care of month overflowss
                  workingDt = new Date(workingY, +workingM, 2);
                  workingDt = workingDt.getFullYear()+""+pad(workingDt.getMonth()+1);
              };
  
              return obj;
          };
  
          // Repositions the datepicker beside the button - to the bottom by
          // preference but to the top if there is not enough room to display the
          // entire U.I. at the bottom (it really should be updated to favour
          // bottom positioning if not enough room to display the entire U.I. at
          // the top in that scenario though)
          this.reposition = function() {
              if(!o.created || o.staticPos) {
                  return;
              };
  
              o.div.style.visibility = "hidden";
              o.div.style.left = o.div.style.top = "0px";
              o.div.style.display = "block";
  
              var osh         = o.div.offsetHeight,
                  osw         = o.div.offsetWidth,
                  elem        = document.getElementById('fd-but-' + o.id),
                  pos         = o.truePosition(elem),
                  trueBody    = (document.compatMode && document.compatMode!="BackCompat") ? document.documentElement : document.body,
                  sOffsets    = o.getScrollOffsets(),
                  scrollTop   = sOffsets[1],
                  scrollLeft  = sOffsets[0],
                  tSpace      = parseInt(pos[1] - 2) - parseInt(scrollTop),
                  bSpace      = parseInt(trueBody.clientHeight + scrollTop) - parseInt(pos[1] + elem.offsetHeight + 2);
  
              o.div.style.visibility = "visible";
  
              o.div.style.left = Number(parseInt(trueBody.clientWidth+scrollLeft) < parseInt(osw+pos[0]) ? Math.abs(parseInt((trueBody.clientWidth+scrollLeft) - osw)) : pos[0]) + "px";
              o.div.style.top  = (bSpace > tSpace) ? Math.abs(parseInt(pos[1] + elem.offsetHeight + 2)) + "px" : Math.abs(parseInt(pos[1] - (osh + 2))) + "px";
              if(oldIE === 6) {
                  o.iePopUp.style.top    = o.div.style.top;
                  o.iePopUp.style.left   = o.div.style.left;
                  o.iePopUp.style.width  = osw + "px";
                  o.iePopUp.style.height = (osh - 2) + "px";
              };
          };
  
          this.removeCursorHighlight = function() {
              var td = document.getElementById(o.id + "-date-picker-hover");
              if(td) {
                  removeClass(td, "date-picker-hover");
              };
          };
  
          this.addCursorHighlight = function() {
              var td = document.getElementById(o.id + "-date-picker-hover");
              if(td) {
                  addClass(td, "date-picker-hover");
              };
          };
          // Resets the tabindex of the previously focused cell
          this.removeOldFocus = function() {
              var td = document.getElementById(o.id + "-date-picker-hover");
              if(td) {
                  try {
                      setTabIndex(td, -1);
                      removeClass(td, "date-picker-hover");
                      td.id = "";
                      td.onblur  = null;
                      td.onfocus = null;
                  } catch(err) {};
              };
          };
  
          // Sets the tabindex & focus on the currently highlighted cell
          this.setNewFocus = function() {
              var td = document.getElementById(o.id + "-date-picker-hover");
              if(td) {
                  try {
                      setTabIndex(td, 0);
                      if(this.showCursor) {
                          addClass(td, "date-picker-hover");
                      };
                      // If opened with the keyboard then add focus & blur events to the cell
                      if(!this.clickActivated) {
                          td.onblur    = o.onblur;
                          td.onfocus   = o.onfocus;
                      };
  
                      // If opened with the keyboard (and not in opera) then add a screen-reader friendly date format
                      if(!isOpera && !this.clickActivated) {
                          o.addAccessibleDate();
                      };
  
                      // Try to programmatically set focus on the cell
                      if(!this.noFocus && !this.clickActivated) {
                          setTimeout(function() { try { td.focus(); } catch(err) {}; }, 0);
                      };
                  } catch(err) { };
              };
          };
  
          // Adds a screen-reader friendly date to the current cell whenever
          // the datepicker has been opened with the keyboard
          this.addAccessibleDate = function() {
              var td   = document.getElementById(o.id + "-date-picker-hover");
  
              if(td && !(td.getElementsByTagName("span").length)) {
                  var ymd = td.className.match(/cd-([\d]{4})([\d]{2})([\d]{2})/),
                      noS = td.className.search(noSelectionRegExp) != -1,
                      spn = document.createElement('span'),
                      spnC;
  
                  spn.className       = "fd-screen-reader";
  
                  removeChildNodes(td);
  
                  if(noS) {
                      spnC = spn.cloneNode(false);
                      spnC.appendChild(document.createTextNode(getTitleTranslation(13)));
                      td.appendChild(spnC);
                  };
  
                  for(var pt = 0, part; part = formatParts[pt]; pt++) {
                      if(part == "%j" || part == "%d") {
                          td.appendChild(document.createTextNode(printFormattedDate(new Date(ymd[1], +ymd[2]-1, ymd[3], 5, 0, 0), part, true)));
                      } else {
                          spnC = spn.cloneNode(false);
                          spnC.appendChild(document.createTextNode(printFormattedDate(new Date(ymd[1], +ymd[2]-1, ymd[3], 5, 0, 0), part, true)));
                          td.appendChild(spnC);
                      };
                  };
              };
          };
  
          // Sets the current cursor to a specific date
          this.setCursorDate = function(yyyymmdd) {
              if(String(yyyymmdd).search(/^([0-9]{8})$/) != -1) {
                  this.date = new Date(+yyyymmdd.substr(0,4), +yyyymmdd.substr(4,2) - 1, +yyyymmdd.substr(6,2), 5, 0, 0);
                  this.cursorDate = yyyymmdd;
  
                  if(this.staticPos) {
                      this.updateTable();
                  };
              };
          };
  
          // Updates the table used to display the datepicker
          this.updateTable = function(noCallback) {
              if(!o || o.inUpdate || !o.created) {
                  return;
              };
  
              // We are currently updating (used to stop public methods from firing)
              o.inUpdate = true;
  
              // Remove the focus from the currently highlighted cell
              o.removeOldFocus();
  
              o.div.dir = localeImport.rtl ? "rtl" : "ltr";
  
              // If the update timer initiated
              if(o.timerSet && !o.delayedUpdate) {
                  // Are we incrementing/decrementing the month
                  if(o.monthInc) {
                      var n = o.date.getDate(),
                          d = new Date(o.date);
  
                      d.setDate(2);
                      d.setMonth(d.getMonth() + o.monthInc * 1);
                      // Don't go over the days in the month
                      d.setDate(Math.min(n, daysInMonth(d.getMonth(),d.getFullYear())));
  
                      o.date = new Date(d);
                  } else {
                      o.date.setDate(Math.min(o.date.getDate()+o.dayInc, daysInMonth(o.date.getMonth()+o.monthInc,o.date.getFullYear()+o.yearInc)));
                      o.date.setMonth(o.date.getMonth() + o.monthInc);
                      o.date.setFullYear(o.date.getFullYear() + o.yearInc);
                  };
              };
  
              // Make sure the internal date is within range
              o.outOfRange();
  
              // Disable/enable the today button
              if(!o.noToday) {
                  o.disableTodayButton();
              };
  
              // Disable/enable the month & year buttons
              o.showHideButtons(o.date);
  
              var cd = o.date.getDate(),
                  cm = o.date.getMonth(),
                  cy = o.date.getFullYear(),
                  cursorDate = (String(cy) + pad(cm+1) + pad(cd)),
                  tmpDate    = new Date(cy, cm, 1, 5, 0, 0);
  
              tmpDate.setHours(5);
  
              var dt, dts, cName, row, td, i, currentDate, cellAdded, col, currentStub, abbr, bespokeRenderClass, spnC, dateSetD, selectable, weekDay,
                  // Weekday of the fist of the month
                  weekDayC            = (tmpDate.getDay() + 6) % 7,
                  // The column index this weekday will occupy
                  firstColIndex       = (((weekDayC - o.firstDayOfWeek) + 7 ) % 7) - 1,
                  // The number of days in the current month
                  dpm                 = daysInMonth(cm, cy),
                  // Today as a Date Object
                  today               = new Date(),
                  // Today as a YYYYMMDD String
                  today               = today.getFullYear() + pad(today.getMonth()+1) + pad(today.getDate()),
                  // A Sring date stub in a YYYYMM format for the current date
                  stub                = String(tmpDate.getFullYear()) + pad(tmpDate.getMonth()+1),
                  //
                  cellAdded           = [4,4,4,4,4,4],
                  // The first day of the previous month as a Date Object
                  lm                  = new Date(cy, cm-1, 1, 5, 0, 0),
                  // The first day of the next month as a Date Object
                  nm                  = new Date(cy, cm+1, 1, 5, 0, 0),
                  // The number of days in the previous month
                  daySub              = daysInMonth(lm.getMonth(), lm.getFullYear()),
                  // YYYYMM String date stub for the next month
                  stubN               = String(nm.getFullYear()) + pad(nm.getMonth()+1),
                  // YYYYMM String date stub for the previous month
                  stubP               = String(lm.getFullYear()) + pad(lm.getMonth()+1),
                  weekDayN            = (nm.getDay() + 6) % 7,
                  weekDayP            = (lm.getDay() + 6) % 7,
                  // A SPAN node to clone when adding dates to individual cells
                  spn                 = document.createElement('span');
  
              // Give the "fd-screen-reader" class to the span in order to hide them in the UI
              // but keep them accessible to screen-readers
              spn.className       = "fd-screen-reader";
  
              // The first & last dates shown on the datepicker UI - could be a date from the previous & next month respectively
              o.firstDateShown    = !o.constrainSelection && o.fillGrid && (0 - firstColIndex < 1) ? String(stubP) + (daySub + (0 - firstColIndex)) : stub + "01";
              o.lastDateShown     = !o.constrainSelection && o.fillGrid ? stubN + pad(41 - firstColIndex - dpm) : stub + String(dpm);
  
              // Store a reference to the current YYYYMM String representation of the current month
              o.currentYYYYMM     = stub;
  
              bespokeRenderClass  = o.callback("redraw", {id:o.id, dd:pad(cd), mm:pad(cm+1), yyyy:cy, firstDateDisplayed:o.firstDateShown, lastDateDisplayed:o.lastDateShown}) || {};
  
              // An Object of dates that have been explicitly disabled (1) or enabled (0)
              dts                 = o.getDateExceptions(cy, cm+1);
  
              // Double check current date within limits etc
              o.checkSelectedDate();
              //
              dateSetD            = (o.dateSet != null) ? o.dateSet.getFullYear() + pad(o.dateSet.getMonth()+1) + pad(o.dateSet.getDate()) : false;
  
              // If we have selected a date then set its ARIA selected property
              // to false. We then set the ARIA selected property to true on the
              // newly selected cell after redrawing the table
              if(this.selectedTD != null) {
                  setARIAProperty(this.selectedTD, "selected", false);
                  this.selectedTD = null;
              };
  
              // Redraw all of the table cells representing the date parts of the UI
              for(var curr = 0; curr < 42; curr++) {
                  // Current row
                  row  = Math.floor(curr / 7);
                  // Current TD node
                  td   = o.tds[curr];
                  // Clone our SPAN node
                  spnC = spn.cloneNode(false);
                  // Remove any previous contents from the cell
                  removeChildNodes(td);
  
                  // If the current cell contains a date
                  if((curr > firstColIndex && curr <= (firstColIndex + dpm)) || o.fillGrid) {
                      currentStub     = stub;
                      weekDay         = weekDayC;
                      dt              = curr - firstColIndex;
                      cName           = [];
                      selectable      = true;
  
                      // Are we drawing last month
                      if(dt < 1) {
                          dt              = daySub + dt;
                          currentStub     = stubP;
                          weekDay         = weekDayP;
                          selectable      = !o.constrainSelection;
                          cName.push("month-out");
                      // Are we drawing next month
                      } else if(dt > dpm) {
                          dt -= dpm;
                          currentStub     = stubN;
                          weekDay         = weekDayN;
                          selectable      = !o.constrainSelection;
                          cName.push("month-out");
                      };
  
                      // Calcuate this cells weekday
                      weekDay = (weekDay + dt + 6) % 7;
  
                      // Push a classname representing the weekday e.g. "day-3"
                      cName.push("day-" + weekDay + " cell-" + curr);
  
                      // A YYYYMMDD String representation of this cells date
                      currentDate = currentStub + String(dt < 10 ? "0" : "") + dt;
  
                      // If this cells date is out of range
                      if(o.rangeLow && +currentDate < +o.rangeLow || o.rangeHigh && +currentDate > +o.rangeHigh) {
                          // Add a classname to style the cell and stop selection
                          td.className = "out-of-range";
                          // Reset this TD nodes title attribute
                          td.title = "";
                          // Append the cells date as a text node to the TD
                          td.appendChild(document.createTextNode(dt));
                          // Jaysus, what the feck does this line do again...
                          if(o.showWeeks) {
                              cellAdded[row] = Math.min(cellAdded[row], 2);
                          };
                      // This cells date is within the lower & upper ranges (or no ranges have been defined)
                      } else {
                          // If it's a date from last or next month and the "constrainSelection" option
                          // is false then give the cell a CD-YYYYMMDD class
                          if(selectable) {
                              td.title = titleFormat ? printFormattedDate(new Date(+String(currentStub).substr(0,4), +String(currentStub).substr(4, 2) - 1, +dt, 5, 0, 0), titleFormat, true) : "";
                              cName.push("cd-" + currentDate + " yyyymmdd-" + currentDate + " yyyymm-" + currentStub + " mmdd-" + currentStub.substr(4,2) + pad(dt));
                          // Otherwise give a "not-selectable" class (which shouldn't be styled in any way, it's for internal use)
                          } else {
                              td.title = titleFormat ? getTitleTranslation(13) + " " + printFormattedDate(new Date(+String(currentStub).substr(0,4), +String(currentStub).substr(4, 2) - 1, +dt, 5, 0, 0), titleFormat, true) : "";
                              cName.push("yyyymmdd-" + currentDate + " yyyymm-" + currentStub + " mmdd-" + currentStub.substr(4,2) + pad(dt) + " not-selectable");
                          };
  
                          // Add a classname if the current cells date is today
                          if(currentDate == today) {
                              cName.push("date-picker-today");
                          };
  
                          // If this cell represents the currently selected date
                          if(dateSetD == currentDate) {
                              // Add a classname (for styling purposes)
                              cName.push("date-picker-selected-date");
                              // Set the ARIA selected property to true
                              setARIAProperty(td, "selected", "true");
                              // And cache a reference to the current cell
                              this.selectedTD = td;
                          };
  
                          // If the current cell has been explicitly disabled
                          if(((currentDate in dts) && dts[currentDate] == 1)
                             // or
                             ||
                             // ... the current weekday has been disabled
                             (o.disabledDays[weekDay]
                              &&
                             // ... and the current date has not been explicitly enabled
                             !((currentDate in dts) && dts[currentDate] == 0)
                             )
                            ) {
                              // Add a classname to style the cell and stop selection
                              cName.push("day-disabled");
                              // Update the current cells title to say "Disabled date: ..." (or whatever the translation says)
                              if(titleFormat && selectable) {
                                  td.title = getTitleTranslation(13) + " " + td.title;
                              };
                          };
  
                          // Has the redraw callback given us a bespoke classname to add to this cell
                          if(currentDate in bespokeRenderClass) {
                              cName.push(bespokeRenderClass[currentDate]);
                          };
  
                          // Do we need to highlight this cells weekday representation
                          if(o.highlightDays[weekDay]) {
                              cName.push("date-picker-highlight");
                          };
  
                          // Is the current onscreen cursor set to this cells date
                          if(cursorDate == currentDate) {
                              td.id = o.id + "-date-picker-hover";
                          };
  
                          // Add the date to the TD cell as a text node. Note: If the datepicker has been given keyboard
                          // events, this textnode is replaced by a more screen-reader friendly date during the focus event
                          td.appendChild(document.createTextNode(dt));
  
                          // Add the classnames to the TD node
                          td.className = cName.join(" ");
  
                          // If the UI displays week numbers then update the celladded
                          if(o.showWeeks) {
                              cellAdded[row] = Math.min(cName[0] == "month-out" ? 3 : 1, cellAdded[row]);
                          };
                      };
                  // The current TD node is empty i.e. represents no date in the UI
                  } else {
                      // Add a classname to style the cell
                      td.className = "date-picker-unused";
                      // Add a non-breaking space to unused TD node (for IEs benefit mostly)
                      td.appendChild(document.createTextNode(nbsp));
                      // Reset the TD nodes title attribute
                      td.title = "";
                  };
                  // Do we update the week number for this row
                  if(o.showWeeks && curr - (row * 7) == 6) {
                      removeChildNodes(o.wkThs[row]);
                      o.wkThs[row].appendChild(document.createTextNode(cellAdded[row] == 4 && !o.fillGrid ? nbsp : getWeekNumber(cy, cm, curr - firstColIndex - 6)));
                      o.wkThs[row].className = "date-picker-week-header" + (["",""," out-of-range"," month-out",""][cellAdded[row]]);
                  };
              };
  
              // Update the UI title bar displaying the year & month
              var span = o.titleBar.getElementsByTagName("span");
              removeChildNodes(span[0]);
              removeChildNodes(span[1]);
              span[0].appendChild(document.createTextNode(getMonthTranslation(cm, false) + nbsp));
              span[1].appendChild(document.createTextNode(cy));
  
              // If we are in an animation
              if(o.timerSet) {
                  // Speed the timer up a little bit to make the pause between updates quicker
                  o.timerInc = 50 + Math.round(((o.timerInc - 50) / 1.8));
                  // Recall this function in a timeout
                  o.timer = window.setTimeout(o.updateTable, o.timerInc);
              };
  
              // We are not currently updating the UI
              o.inUpdate = o.delayedUpdate = false;
              // Focus on the correct TD node
              o.setNewFocus();
          };
  
          // Removes all scaffold from the DOM & events from memory
          this.destroy = function() {
  
              // Remove the button if it exists
              if(document.getElementById("fd-but-" + this.id)) {
                  document.getElementById("fd-but-" + this.id).parentNode.removeChild(document.getElementById("fd-but-" + this.id));
              };
  
              if(!this.created) {
                  return;
              };
  
              // Event cleanup for Internet Explorers benefit
              removeEvent(this.table, "mousedown", o.onmousedown);
              removeEvent(this.table, "mouseover", o.onmouseover);
              removeEvent(this.table, "mouseout", o.onmouseout);
              removeEvent(document, "mousedown", o.onmousedown);
              removeEvent(document, "mouseup",   o.clearTimer);
  
              if (window.addEventListener && !window.devicePixelRatio) {
                  try {
                      window.removeEventListener('DOMMouseScroll', this.onmousewheel, false);
                  } catch(err) {};
              } else {
                  removeEvent(document, "mousewheel", this.onmousewheel);
                  removeEvent(window,   "mousewheel", this.onmousewheel);
              };
              o.removeOnFocusEvents();
              clearTimeout(o.fadeTimer);
              clearTimeout(o.timer);
  
              if(oldIE === 6 && !o.staticPos) {
                  try {
                      o.iePopUp.parentNode.removeChild(o.iePopUp);
                      o.iePopUp = null;
                  } catch(err) {};
              };
              
              if(this.div && this.div.parentNode) {
                  this.div.parentNode.removeChild(this.div);
              };
  
              o = null;
          };
  
          this.resizeInlineDiv = function()  {
              o.div.style.width = o.table.offsetWidth + "px";
              o.div.style.height = o.table.offsetHeight + "px";
          };
  
          this.reset = function() {
              var elemID, elem;
              for(elemID in o.formElements) {
                  elem = document.getElementById(elemID);
                  if(elem) {
                      if(elem.tagName.toLowerCase() == "select") {
                          elem.selectedIndex = o.defaultVals[elemID];
                      } else {
                          elem.value = o.defaultVals[elemID];
                      };
                  };
              };
              o.changeHandler();
          };
  
          // Creates the DOM scaffold
          this.create = function() {
  
              if(document.getElementById("fd-" + this.id)) {
                  return;
              };
  
              var tr, row, col, tableHead, tableBody, tableFoot;
  
              this.noFocus = true;
  
              function createTH(details) {
                  var th = document.createElement('th');
                  if(details.thClassName) {
                      th.className = details.thClassName;
                  };
                  if(details.colspan) {
                      th.setAttribute(oldIE ? 'colSpan' : "colspan", details.colspan);
                  };
                  th.unselectable = "on";
                  return th;
              };
              function createThAndButton(tr, obj) {
                  for(var i = 0, details; details = obj[i]; i++) {
                      var th = createTH(details);
                      tr.appendChild(th);
                      var but = document.createElement('span');
                      but.className = details.className;
                      but.id = o.id + details.id;
                      but.appendChild(document.createTextNode(details.text || o.nbsp));
                      but.title = details.title || "";
                      but.unselectable = "on";
                      th.appendChild(but);
                  };
              };
  
              this.div                     = document.createElement('div');
              this.div.id                  = "fd-" + this.id;
              this.div.className           = "date-picker" + (cssAnimations ? " fd-dp-fade " : "") + this.bespokeClass;
  
              // Attempt to hide the div from screen readers during content creation
              this.div.style.visibility = "hidden";
              this.div.style.display = "none";
  
              // Set the ARIA describedby property if the required block available
              if(this.describedBy && document.getElementById(this.describedBy)) {
                  setARIAProperty(this.div, "describedby", this.describedBy);
              };
  
              // Set the ARIA labelled property if the required label available
              if(this.labelledBy) {
                  setARIAProperty(this.div, "labelledby", this.labelledBy.id);
              };
  
              this.idiv                     = document.createElement('div');
  
              this.table             = document.createElement('table');
              this.table.className   = "date-picker-table";
              this.table.onmouseover = this.onmouseover;
              this.table.onmouseout  = this.onmouseout;
              this.table.onclick     = this.onclick;
  
              if(this.finalOpacity < 100) {
                  this.idiv.style.opacity = Math.min(Math.max(parseInt(this.finalOpacity, 10) / 100, .2), 1);
              };
  
              if(this.staticPos) {
                  this.table.onmousedown  = this.onmousedown;
              };
  
              this.div.appendChild(this.idiv);
              this.idiv.appendChild(this.table);
  
              var dragEnabledCN = !this.dragDisabled ? " drag-enabled" : "";
  
              if(!this.staticPos) {
                  this.div.style.visibility = "hidden";
                  this.div.className += dragEnabledCN;
                  document.getElementsByTagName('body')[0].appendChild(this.div);
  
                  if(oldIE === 6) {
                      this.iePopUp = document.createElement('iframe');
                      this.iePopUp.src = "javascript:'<html></html>';";
                      this.iePopUp.setAttribute('className','iehack');
                      // Remove iFrame from tabIndex
                      this.iePopUp.setAttribute("tabIndex", -1);
                      // Hide it from ARIA aware technologies
                      setARIARole(this.iePopUp, "presentation");
                      setARIAProperty(this.iePopUp, "hidden", "true");
                      this.iePopUp.scrolling = "no";
                      this.iePopUp.frameBorder = "0";
                      this.iePopUp.name = this.iePopUp.id = this.id + "-iePopUpHack";
                      document.body.appendChild(this.iePopUp);
                  };
  
                  // Aria "hidden" property for non active popup datepickers
                  setARIAProperty(this.div, "hidden", "true");
              } else {
                  var elem = document.getElementById(this.positioned ? this.positioned : this.id);
                  if(!elem) {
                      this.div = null;
                      if(debug) {
                          throw this.positioned ? "Could not locate a datePickers associated parent element with an id:" + this.positioned : "Could not locate a datePickers associated input with an id:" + this.id;
                      };
                      return;
                  };
  
                  this.div.className += " static-datepicker";
  
                  if(this.positioned) {
                      elem.appendChild(this.div);
                  } else {
                      elem.parentNode.insertBefore(this.div, elem.nextSibling);
                  };
  
                  if(this.hideInput) {
                      for(var elemID in this.formElements) {
                          elem = document.getElementById(elemID);
                          if(elem) {
                              elem.className += " fd-hidden-input";
                          };
                      };
                  };
  
                  setTimeout(this.resizeInlineDiv, 300);
              };
              // ARIA Application role
              setARIARole(this.div, "application");
              //setARIARole(this.table, "grid");
  
              if(this.statusFormat) {
                  tableFoot = document.createElement('tfoot');
                  this.table.appendChild(tableFoot);
                  tr = document.createElement('tr');
                  tr.className = "date-picker-tfoot";
                  tableFoot.appendChild(tr);
                  this.statusBar = createTH({thClassName:"date-picker-statusbar" + dragEnabledCN, colspan:this.showWeeks ? 8 : 7});
                  tr.appendChild(this.statusBar);
                  this.updateStatus();
              };
  
              tableHead = document.createElement('thead');
              tableHead.className = "date-picker-thead";
              this.table.appendChild(tableHead);
  
              tr  = document.createElement('tr');
              setARIARole(tr, "presentation");
  
              tableHead.appendChild(tr);
  
              // Title Bar
              this.titleBar = createTH({thClassName:"date-picker-title" + dragEnabledCN, colspan:this.showWeeks ? 8 : 7});
  
              tr.appendChild(this.titleBar);
              tr = null;
  
              var span = document.createElement('span');
              span.appendChild(document.createTextNode(nbsp));
              span.className = "month-display" + dragEnabledCN;
              this.titleBar.appendChild(span);
  
              span = document.createElement('span');
              span.appendChild(document.createTextNode(nbsp));
              span.className = "year-display" + dragEnabledCN;
              this.titleBar.appendChild(span);
  
              span = null;
  
              tr  = document.createElement('tr');
              setARIARole(tr, "presentation");
              tableHead.appendChild(tr);
  
              createThAndButton(tr, [
              {className:"prev-but prev-year",  id:"-prev-year-but", text:"\u00AB", title:getTitleTranslation(2) },
              {className:"prev-but prev-month", id:"-prev-month-but", text:"\u2039", title:getTitleTranslation(0) },
              {colspan:this.showWeeks ? 4 : 3, className:"today-but", id:"-today-but", text:getTitleTranslation(4)},
              {className:"next-but next-month", id:"-next-month-but", text:"\u203A", title:getTitleTranslation(1)},
              {className:"next-but next-year",  id:"-next-year-but", text:"\u00BB", title:getTitleTranslation(3) }
              ]);
  
              tableBody = document.createElement('tbody');
              this.table.appendChild(tableBody);
  
              var colspanTotal = this.showWeeks ? 8 : 7,
                  colOffset    = this.showWeeks ? 0 : -1,
                  but, abbr, formElemId, formElem;
  
              for(var rows = 0; rows < 7; rows++) {
                  row = document.createElement('tr');
  
                  if(rows != 0) {
                      // ARIA Grid role
                      setARIARole(row, "row");
                      tableBody.appendChild(row);
                  } else {
                      tableHead.appendChild(row);
                  };
  
                  for(var cols = 0; cols < colspanTotal; cols++) {
                      if(rows === 0 || (this.showWeeks && cols === 0)) {
                          col = document.createElement('th');
                      } else {
                          col = document.createElement('td');
                          setARIAProperty(col, "describedby", this.id + "-col-" + cols + (this.showWeeks ? " " + this.id + "-row-" + rows : ""));
                          setARIAProperty(col, "selected", "false");
                      };
  
                      if(oldIE) {
                          col.unselectable = "on";
                      };
  
                      row.appendChild(col);
                      if((this.showWeeks && cols > 0 && rows > 0) || (!this.showWeeks && rows > 0)) {
                          //setARIARole(col, "gridcell");
                      } else {
                          if(rows === 0 && cols > colOffset) {
                              col.className = "date-picker-day-header";
                              col.scope = "col";
                              //setARIARole(col, "columnheader");
                              col.id = this.id + "-col-" + cols;
                          } else {
                              col.className = "date-picker-week-header";
                              col.scope = "row";
                              //setARIARole(col, "rowheader");
                              col.id = this.id + "-row-" + rows;
                          };
                      };
                  };
              };
  
              col = row = null;
  
              this.ths = this.table.getElementsByTagName('thead')[0].getElementsByTagName('tr')[2].getElementsByTagName('th');
  
              for (var y = 0; y < colspanTotal; y++) {
  
                  if(y == 0 && this.showWeeks) {
                      this.ths[y].appendChild(document.createTextNode(getTitleTranslation(6)));
                      this.ths[y].title = getTitleTranslation(8);
                      continue;
                  };
  
                  if(y > (this.showWeeks ? 0 : -1)) {
                      but = document.createElement("span");
                      but.className = "fd-day-header";
                      if(oldIE) {
                          but.unselectable = "on";
                      };
                      this.ths[y].appendChild(but);
                  };
              };
  
              but = null;
  
              this.trs             = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
              this.tds             = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('td');
              this.butPrevYear     = document.getElementById(this.id + "-prev-year-but");
              this.butPrevMonth    = document.getElementById(this.id + "-prev-month-but");
              this.butToday        = document.getElementById(this.id + "-today-but");
              this.butNextYear     = document.getElementById(this.id + "-next-year-but");
              this.butNextMonth    = document.getElementById(this.id + "-next-month-but");
  
              if(this.noToday) {
                  this.butToday.style.display = "none";
              };
  
              if(this.showWeeks) {
                  this.wkThs = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('th');
                  this.div.className += " weeks-displayed";
              };
  
              tableBody = tableHead = tr = createThAndButton = createTH = null;
  
              this.updateTableHeaders();
              this.created = true;
              this.updateTable();
  
              if(this.staticPos) {
                  this.visible = true;
                  this.opacity = 100;
                  this.div.style.visibility = "visible";
                  this.div.style.display = "block";
                  this.noFocus = true;
                  this.fade();
              } else {
                  this.reposition();
                  this.div.style.visibility = "visible";
                  this.fade();
                  this.noFocus = true;
              };
  
              this.callback("domcreate", { "id":this.id });
          };
  
          this.transEnd = function() {
              o.div.style.display     = "none";
              o.div.style.visibility  = "hidden";
              setARIAProperty(o.div, "hidden", "true");
          };
  
          this.fade = function() {
              window.clearTimeout(o.fadeTimer);
              o.fadeTimer = null;
              if(cssAnimations) {
                  o.opacity = o.opacityTo;
                  if(o.opacityTo == 0) {
                      o.visible = false;
                      addEvent(o.div, transitionEnd, o.transEnd);
                      addClass(o.div, "fd-dp-fade");
                  } else {
                      removeEvent(o.div, transitionEnd, o.transEnd);
                      o.visible               = true;
                      o.div.style.display     = "block";
                      o.div.style.visibility  = "visible";
                      setARIAProperty(o.div, "hidden", "false");
                      removeClass(o.div, "fd-dp-fade");
                  };
                  return;
              };
  
              var diff = Math.round(o.opacity + ((o.opacityTo - o.opacity) / 4));
              o.setOpacity(diff);
              if(Math.abs(o.opacityTo - diff) > 3 && !o.noFadeEffect) {
                  o.fadeTimer = window.setTimeout(o.fade, 50);
              } else {
                  o.setOpacity(o.opacityTo);
                  if(o.opacityTo == 0) {
                      o.div.style.display    = "none";
                      o.div.style.visibility = "hidden";
                      setARIAProperty(o.div, "hidden", "true");
                      o.visible = false;
                  } else {
                      setARIAProperty(o.div, "hidden", "false");
                      o.visible = true;
                  };
              };
          };
          this.trackDrag = function(e) {
              e = e || window.event;
              var diffx = (e.pageX?e.pageX:e.clientX?e.clientX:e.x) - o.mx;
              var diffy = (e.pageY?e.pageY:e.clientY?e.clientY:e.Y) - o.my;
              o.div.style.left = Math.round(o.x + diffx) > 0 ? Math.round(o.x + diffx) + 'px' : "0px";
              o.div.style.top  = Math.round(o.y + diffy) > 0 ? Math.round(o.y + diffy) + 'px' : "0px";
              if(oldIE === 6 && !o.staticPos) {
                  o.iePopUp.style.top    = o.div.style.top;
                  o.iePopUp.style.left   = o.div.style.left;
              };
          };
          this.stopDrag = function(e) {
              var b = document.getElementsByTagName("body")[0];
              removeClass(b, "fd-drag-active");
              removeEvent(document,'mousemove',o.trackDrag, false);
              removeEvent(document,'mouseup',o.stopDrag, false);
              o.div.style.zIndex = 9999;
          };
          this.onmousedown = function(e) {
              e = e || document.parentWindow.event;
              var el     = e.target != null ? e.target : e.srcElement,
                  origEl = el,
                  hideDP = true,
                  reg    = new RegExp("^fd-(but-)?" + o.id + "$");
  
              o.mouseDownElem = null;
  
              // Are we within the wrapper div or the button
              while(el) {
                  if(el.id && el.id.length && el.id.search(reg) != -1) {
                      hideDP = false;
                      break;
                  };
                  try {
                      el = el.parentNode;
                  } catch(err) {
                      break;
                  };
              };
  
              // If not, then ...
              if(hideDP) {
                  hideAll();
                  return true;
              };
  
              if((o.div.className + origEl.className).search('fd-disabled') != -1) {
                  return true;
              };
  
              // We check the mousedown events on the buttons
              if(origEl.id.search(new RegExp("^" + o.id + "(-prev-year-but|-prev-month-but|-next-month-but|-next-year-but)$")) != -1) {
  
                  o.mouseDownElem = origEl;
  
                  addEvent(document, "mouseup", o.clearTimer);
                  addEvent(origEl, "mouseout",  o.clearTimer);
  
                  var incs = {
                          "-prev-year-but":[0,-1,0],
                          "-prev-month-but":[0,0,-1],
                          "-next-year-but":[0,1,0],
                          "-next-month-but":[0,0,1]
                      },
                      check = origEl.id.replace(o.id, ""),
                      dateYYYYMM = Number(o.date.getFullYear() + pad(o.date.getMonth()+1));
  
                  o.timerInc      = 800;
                  o.timerSet      = true;
                  o.dayInc        = incs[check][0];
                  o.yearInc       = incs[check][1];
                  o.monthInc      = incs[check][2];
                  o.accellerator  = 1;
  
                  if(!(o.currentYYYYMM == dateYYYYMM)) {
                      if((o.currentYYYYMM < dateYYYYMM && (o.yearInc == -1 || o.monthInc == -1)) || (o.currentYYYYMM > dateYYYYMM && (o.yearInc == 1 || o.monthInc == 1))) {
                          o.delayedUpdate = false;
                          o.timerInc = 1200;
                      } else {
                          o.delayedUpdate = true;
                      };
                  };
  
                  o.updateTable();
  
                  return stopEvent(e);
  
              } else if(el.className.search("drag-enabled") != -1) {
                      o.mx = e.pageX ? e.pageX : e.clientX ? e.clientX : e.x;
                      o.my = e.pageY ? e.pageY : e.clientY ? e.clientY : e.Y;
                      o.x  = parseInt(o.div.style.left, 10);
                      o.y  = parseInt(o.div.style.top, 10);
                      addEvent(document,'mousemove',o.trackDrag, false);
                      addEvent(document,'mouseup',o.stopDrag, false);
                      addClass(document.getElementsByTagName("body")[0], "fd-drag-active");
                      o.div.style.zIndex = 10000;
  
                      return stopEvent(e);
              };
              return true;
          };
          this.onclick = function(e) {
              if((!cssAnimations && o.opacity != o.opacityTo) || o.disabled) {
                  return stopEvent(e);
              };
  
              e = e || document.parentWindow.event;
              var el = e.target != null ? e.target : e.srcElement;
  
              while(el.parentNode) {
                  // Are we within a valid i.e. clickable TD node
                  if(el.tagName && el.tagName.toLowerCase() == "td") {
  
                      if(el.className.search(/cd-([0-9]{8})/) == -1 || el.className.search(noSelectionRegExp) != -1) {
                          return stopEvent(e);
                      };
  
                      var cellDate = el.className.match(/cd-([0-9]{8})/)[1];
                      o.date       = new Date(cellDate.substr(0,4),cellDate.substr(4,2)-1,cellDate.substr(6,2), 5, 0, 0);
                      o.dateSet    = new Date(o.date);
                      o.noFocus    = true;
                      o.callback("dateset", { "id":o.id, "date":o.dateSet, "dd":o.dateSet.getDate(), "mm":o.dateSet.getMonth() + 1, "yyyy":o.dateSet.getFullYear() });
                      o.returnFormattedDate();
                      o.hide();
                      o.stopTimer();
                      break;
                  } else if(el.id && el.id == o.id + "-today-but") {
                      o.date = new Date();
                      o.updateTable();
                      o.stopTimer();
                      break;
                  } else if(el.className.search(/date-picker-day-header/) != -1) {
                      var cnt = o.showWeeks ? -1 : 0,
                          elem = el;
  
                      while(elem.previousSibling) {
                          elem = elem.previousSibling;
                          if(elem.tagName && elem.tagName.toLowerCase() == "th") {
                              cnt++;
                          };
                      };
  
                      o.firstDayOfWeek = (o.firstDayOfWeek + cnt) % 7;
                      o.updateTableHeaders();
                      break;
                  };
                  try {
                      el = el.parentNode;
                  } catch(err) {
                      break;
                  };
              };
  
              return stopEvent(e);
          };
  
          this.show = function(autoFocus) {
              if(this.staticPos) {
                  return;
              };
  
              var elem, elemID;
              for(elemID in this.formElements) {
                  elem = document.getElementById(this.id);
                  if(!elem || (elem && elem.disabled)) {
                      return;
                  };
              };
  
              this.noFocus = true;
  
              // If the datepicker doesn't exist in the dom
              if(!this.created || !document.getElementById('fd-' + this.id)) {
                  this.created    = false;
                  this.fullCreate = false;
                  this.create();
                  this.fullCreate = true;
              } else {
                  this.setDateFromInput();
                  this.reposition();
              };
  
              this.noFocus = !!!autoFocus;
  
              if(this.noFocus) {
                  this.clickActivated = true;
                  this.showCursor = false;
                  addEvent(document, "mousedown", this.onmousedown);
                  if(mouseWheel) {
                      if (window.addEventListener && !window.devicePixelRatio) {
                          window.addEventListener('DOMMouseScroll', this.onmousewheel, false);
                      } else {
                          addEvent(document, "mousewheel", this.onmousewheel);
                          addEvent(window,   "mousewheel", this.onmousewheel);
                      };
                  };
              } else {
                  this.clickActivated = false;
                  this.showCursor = true;
              };
  
              this.opacityTo = 100;
              this.div.style.display = "block";
  
              if(oldIE === 6) {
                  this.iePopUp.style.width    = this.div.offsetWidth + "px";
                  this.iePopUp.style.height   = this.div.offsetHeight + "px";
                  this.iePopUp.style.display  = "block";
              };
  
              this.setNewFocus();
              this.fade();
              var butt = document.getElementById('fd-but-' + this.id);
              if(butt) {
                    addClass(butt, "date-picker-button-active");
              };
          };
  
          this.hide = function() {
              if(!this.visible || !this.created || !document.getElementById('fd-' + this.id)) {
                  return;
              };
  
              this.kbEvent = false;
  
              removeClass(o.div, "date-picker-focus");
  
              this.stopTimer();
              this.removeOnFocusEvents();
              this.clickActivated = false;
              this.noFocus = true;
              this.showCursor = false;
              this.setNewFocus();
  
              if(this.staticPos) {
                  return;
              };
  
              if(this.statusBar) {
                  this.updateStatus(getTitleTranslation(9));
              };
  
              var butt = document.getElementById('fd-but-' + this.id);
  
              if(butt) {
                  removeClass(butt, "date-picker-button-active");
              };
  
              removeEvent(document, "mousedown", this.onmousedown);
  
              if(mouseWheel) {
                  if (window.addEventListener && !window.devicePixelRatio) {
                      try {
                          window.removeEventListener('DOMMouseScroll', this.onmousewheel, false);
                      } catch(err) {};
                  } else {
                      removeEvent(document, "mousewheel", this.onmousewheel);
                      removeEvent(window,   "mousewheel", this.onmousewheel);
                  };
              };
  
  
              if(oldIE === 6) {
                  this.iePopUp.style.display = "none";
              };
  
              this.opacityTo = 0;
              this.fade();
          };
  
          this.onblur = function(e) {
              o.removeCursorHighlight();
              o.hide();
          };
          // The current cursor cell gains focus
          this.onfocus = function(e) {
              o.noFocus = false;
              addClass(o.div, "date-picker-focus");
              if(o.statusBar) {
                  o.updateStatus(printFormattedDate(o.date, o.statusFormat, true));
              };
              o.showCursor = true;
              o.addCursorHighlight();
              o.addOnFocusEvents();
          };
          this.onmousewheel = function(e) {
              e = e || document.parentWindow.event;
              var delta = 0;
  
              if (e.wheelDelta) {
                  delta = e.wheelDelta/120;
                  if (isOpera && window.opera.version() < 9.2) {
                      delta = -delta;
                  };
              } else if(e.detail) {
                  delta = -e.detail/3;
              };
  
              var n = o.date.getDate(),
                  d = new Date(o.date),
                  inc = delta > 0 ? 1 : -1;
  
              d.setDate(2);
              d.setMonth(d.getMonth() + inc * 1);
              d.setDate(Math.min(n, daysInMonth(d.getMonth(),d.getFullYear())));
  
              if(o.outOfRange(d)) {
                  return stopEvent(e);
              };
  
              o.date = new Date(d);
  
              o.updateTable();
  
              if(o.statusBar) {
                  o.updateStatus(printFormattedDate(o.date, o.statusFormat, true));
              };
  
              return stopEvent(e);
          };
          this.onkeydown = function (e) {
              o.stopTimer();
  
              if(!o.visible) {
                  return false;
              };
  
              e = e || document.parentWindow.event;
  
              var kc = e.keyCode ? e.keyCode : e.charCode;
  
              if(kc == 13) {
                  // RETURN/ENTER: close & select the date
                  var td = document.getElementById(o.id + "-date-picker-hover");
                  if(!td || td.className.search(/cd-([0-9]{8})/) == -1 || td.className.search(/out-of-range|day-disabled/) != -1) {
                      return stopEvent(e);
                  };
                  o.dateSet = new Date(o.date);
                  o.callback("dateset", o.createCbArgObj());
                  o.returnFormattedDate();
                  o.hide();
                  return stopEvent(e);
              } else if(kc == 27) {
                  // ESC: close, no date selection, refocus on popup button
                  if(!o.staticPos) {
                      o.hide();
                      var butt = document.getElementById('fd-but-' + o.id);
                      if(butt) {
                          setTimeout(function(){try{butt.focus()}catch(err){}},0);
                      };
                      return stopEvent(e);
                  };
                  return true;
              } else if(kc == 32 || kc == 0) {
                  // SPACE: goto todays date
                  o.date = new Date();
                  o.updateTable();
  
                  return stopEvent(e);
              } else if(kc == 9) {
                  // TAB: pass focus - non popup datepickers only
                  if(!o.staticPos) {
                      return stopEvent(e);
                  };
                  return true;
              };
              // TODO - test the need for the IE specific stuff in IE9
  
              // Internet Explorer fires the keydown event faster than the JavaScript engine can
              // update the interface. The following attempts to fix this.
  
              if(oldIE) {
                  if(new Date().getTime() - o.interval.getTime() < 50) { return stopEvent(e); };
                  o.interval = new Date();
              };
              
              // A number key has been pressed so change the first day of the week
              if((kc > 49 && kc < 56) || (kc > 97 && kc < 104)) {
                  if(kc > 96) {
                      kc -= (96-48);
                  };
                  kc -= 49;
                  o.firstDayOfWeek = (o.firstDayOfWeek + kc) % 7;
                  o.updateTableHeaders();
                  return stopEvent(e);
              };
  
              // If outside any other tested keycodes then let the keystroke pass
              if(kc < 33 || kc > 40) {
                  return true;
              };
  
              var d = new Date(o.date),
                  cursorYYYYMM = o.date.getFullYear() + pad(o.date.getMonth()+1),
                  tmp;
  
              // HOME: Set date to first day of current month
              if(kc == 36) {
                  d.setDate(1);
              // END: Set date to last day of current month
              } else if(kc == 35) {
                  d.setDate(daysInMonth(d.getMonth(),d.getFullYear()));
              // PAGE UP & DOWN
              } else if ( kc == 33 || kc == 34) {
                  var inc = (kc == 34) ? 1 : -1;
  
                  // CTRL + PAGE UP/DOWN: Moves to the same date in the previous/next year
                  if(e.ctrlKey) {
                      d.setFullYear(d.getFullYear() + inc * 1);
                  // PAGE UP/DOWN: Moves to the same date in the previous/next month
                  } else {
                      var n = o.date.getDate();
  
                      d.setDate(2);
                      d.setMonth(d.getMonth() + inc * 1);
                      d.setDate(Math.min(n, daysInMonth(d.getMonth(),d.getFullYear())));
                  };
              // LEFT ARROW
              } else if ( kc == 37 ) {
                  d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 1, 5, 0, 0);
              // RIGHT ARROW
              } else if ( kc == 39 || kc == 34) {
                  d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 1, 5, 0, 0);
              // UP ARROW
              } else if ( kc == 38 ) {
                  d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 7, 5, 0, 0);
              // DOWN ARROW
              } else if ( kc == 40 ) {
                  d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 7, 5, 0, 0);
              };
  
              // If the new date is out of range then disallow action
              if(o.outOfRange(d)) {
                  return stopEvent(e);
              };
  
              // Otherwise set the new cursor date
              o.date = d;
  
              // Update the status bar if needs be
              if(o.statusBar) {
                  o.updateStatus(o.getBespokeTitle(o.date.getFullYear(),o.date.getMonth() + 1,o.date.getDate()) || printFormattedDate(o.date, o.statusFormat, true));
              };
  
              // YYYYMMDD format String of the current cursor date
              var t = String(o.date.getFullYear()) + pad(o.date.getMonth()+1) + pad(o.date.getDate());
  
              // If we need to redraw the UI completely
              if(e.ctrlKey || (kc == 33 || kc == 34) || t < o.firstDateShown || t > o.lastDateShown) {
                  o.updateTable();
                  if(oldIE) {
                      o.interval = new Date();
                  };
              // Just highlight current cell
              } else {
                  // Do we need to disable the today button for this date
                  if(!o.noToday) {
                      o.disableTodayButton();
                  };
                  // Remove focus from the previous cell
                  o.removeOldFocus();
                  // Show/hide the month & year buttons
                  o.showHideButtons(o.date);
  
                  // Locate this TD
                  for(var i = 0, td; td = o.tds[i]; i++) {
                      if(td.className.search("cd-" + t) == -1) {
                          continue;
                      };
  
                      td.id = o.id + "-date-picker-hover";
                      o.setNewFocus();
                      break;
                  };
              };
  
              return stopEvent(e);
          };
          this.onmouseout = function(e) {
              e = e || document.parentWindow.event;
              var p = e.toElement || e.relatedTarget;
  
              while(p && p != this) {
                  try {
                      p = p.parentNode;
                  } catch(e) {
                      p = this;
                  };
              };
  
              if(p == this) {
                  return false;
              };
  
              if(o.clickActivated || (o.staticPos && !o.kbEventsAdded)) {
                  o.showCursor = false;
                  o.removeCursorHighlight();
              };
  
              if(o.currentTR) {
                  o.currentTR.className = "";
                  o.currentTR = null;
              };
  
              if(o.statusBar) {
                  o.updateStatus(o.dateSet ? o.getBespokeTitle(o.dateSet.getFullYear(),o.dateSet.getMonth() + 1,o.dateSet.getDate()) || printFormattedDate(o.dateSet, o.statusFormat, true) : getTitleTranslation(9));
              };
          };
          this.onmouseover = function(e) {
              e = e || document.parentWindow.event;
              var el = e.target != null ? e.target : e.srcElement;
              while(el.nodeType != 1) {
                  el = el.parentNode;
              };
  
              if(!el || ! el.tagName) {
                  return;
              };
  
              o.noFocus = true;
  
              var statusText = getTitleTranslation(9);
              if(o.clickActivated || (o.staticPos && !o.kbEventsAdded)) {
                  o.showCursor = false;
              };
  
              switch (el.tagName.toLowerCase()) {
                  case "td":
                      if(el.className.search(/date-picker-unused|out-of-range/) != -1) {
                          statusText = getTitleTranslation(9);
                      } if(el.className.search(/cd-([0-9]{8})/) != -1) {
                          o.showCursor = true;
  
                          o.stopTimer();
                          var cellDate = el.className.match(/cd-([0-9]{8})/)[1];
  
                          o.removeOldFocus();
                          el.id = o.id+"-date-picker-hover";
                          o.setNewFocus();
  
                          o.date = new Date(+cellDate.substr(0,4),+cellDate.substr(4,2)-1,+cellDate.substr(6,2), 5, 0, 0);
                          if(!o.noToday) {
                              o.disableTodayButton();
                          };
  
                          statusText = o.getBespokeTitle(+cellDate.substr(0,4),+cellDate.substr(4,2),+cellDate.substr(6,2)) || printFormattedDate(o.date, o.statusFormat, true);
                      };
                      break;
                  case "th":
                      if(!o.statusBar) {
                          break;
                      };
                      if(el.className.search(/drag-enabled/) != -1) {
                          statusText = getTitleTranslation(10);
                      } else if(el.className.search(/date-picker-week-header/) != -1) {
                          var txt = el.firstChild ? el.firstChild.nodeValue : "";
                          statusText = txt.search(/^(\d+)$/) != -1 ? getTitleTranslation(7, [txt, txt < 3 && o.date.getMonth() == 11 ? getWeeksInYear(o.date.getFullYear()) + 1 : getWeeksInYear(o.date.getFullYear())]) : getTitleTranslation(9);
                      };
                      break;
                  case "span":
                      if(!o.statusBar) {
                          break;
                      };
  
                      if(el.className.search(/day-([0-6])/) != -1) {
                          var day = el.className.match(/day-([0-6])/)[1];
                          statusText = getTitleTranslation(11, [getDayTranslation(day, false)]);
                      } else if(el.className.search(/(drag-enabled|today-but|prev-(year|month)|next-(year|month))/) != -1 && el.className.search(/disabled/) == -1) {
                          statusText = getTitleTranslation({"drag-enabled":10,"prev-year":2,"prev-month":0,"next-year":3,"next-month":1,"today-but":12}[el.className.match(/(drag-enabled|today-but|prev-(year|month)|next-(year|month))/)[0]]);
                      };
  
                      break;
                  default:
                      statusText = "";
              };
              while(el.parentNode) {
                  el = el.parentNode;
                  if(el.nodeType == 1 && el.tagName.toLowerCase() == "tr") {
                      if(o.currentTR) {
                          if(el == o.currentTR) {
                              break;
                          };
                          o.currentTR.className = "";
                      };
                      el.className = "dp-row-highlight";
                      o.currentTR = el;
                      break;
                  };
              };
              if(o.statusBar && statusText) {
                  o.updateStatus(statusText);
              };
  
              if(!o.showCursor) {
                  o.removeCursorHighlight();
              };
          };
          this.clearTimer = function() {
              o.stopTimer();
              o.timerInc      = 800;
              o.yearInc       = 0;
              o.monthInc      = 0;
              o.dayInc        = 0;
  
              removeEvent(document, "mouseup", o.clearTimer);
              if(o.mouseDownElem != null) {
                  removeEvent(o.mouseDownElem, "mouseout",  o.clearTimer);
              };
              o.mouseDownElem = null;
          };
  
          var o = this;
  
          this.setDateFromInput();
  
          if(this.staticPos) {
              this.create();
          } else {
              this.createButton();
          };
  
          (function() {
              var elemID,
                  elem,
                  elemCnt = 0;
  
              for(elemID in o.formElements) {
                  elem = document.getElementById(elemID);
                  if(elem && elem.tagName && elem.tagName.search(/select|input/i) != -1) {
                      addEvent(elem, "change", o.changeHandler);
                      if(elemCnt == 0 && elem.form) {
                          addEvent(elem.form, "reset", o.reset);
                      };
                      elemCnt++;
                  };
  
                  if(!elem || elem.disabled == true) {
                      o.disableDatePicker();
                  };
              };
          })();
  
          // We have fully created the datepicker...
          this.fullCreate = true;
      };
      datePicker.prototype.addButtonEvents = function(but) {
          function buttonEvent (e) {
              e = e || window.event;
  
              var inpId     = this.id.replace('fd-but-',''),
                  dpVisible = isVisible(inpId),
                  autoFocus = false,
                  kbEvent   = datePickers[inpId].kbEvent;
  
              if(kbEvent) {
                  datePickers[inpId].kbEvent = false;
                  return;
              };
  
              if(e.type == "keydown") {
                  var kc = e.keyCode != null ? e.keyCode : e.charCode;
                  if(kc != 13) return true;
                  datePickers[inpId].kbEvent = true;
                  if(dpVisible) {
                      removeClass(this, "date-picker-button-active");
                      hideAll();
                      return stopEvent(e);
                  };
                  autoFocus = true;
              } else {
                  datePickers[inpId].kbEvent = false;
              };
  
              if(!dpVisible) {
                  addClass(this, "date-picker-button-active");
                  hideAll(inpId);
                  showDatePicker(inpId, autoFocus);
              } else {
                  removeClass(this, "date-picker-button-active");
                  hideAll();
              };
  
              return stopEvent(e);
          };
  
          but.onclick     = buttonEvent;
          but.onkeydown   = buttonEvent;
  
          if(!buttonTabIndex) {
              setTabIndex(but, -1);
          } else {
              setTabIndex(but, this.bespokeTabIndex);
          };
      };
  
      datePicker.prototype.createButton = function() {
  
          if(this.staticPos || document.getElementById("fd-but-" + this.id)) {
                  return;
          };
  
          var inp         = document.getElementById(this.id),
              span        = document.createElement('span'),
              but         = document.createElement('a');
  
          but.href        = "#" + this.id;
          but.className   = "date-picker-control";
          but.title       = getTitleTranslation(5);
          but.id          = "fd-but-" + this.id;
  
          span.appendChild(document.createTextNode(nbsp));
          but.appendChild(span);
  
          span = document.createElement('span');
          span.className = "fd-screen-reader";
          span.appendChild(document.createTextNode(but.title));
          but.appendChild(span);
  
          // Set the ARIA role to be "button"
          setARIARole(but, "button");
  
          // Set a "haspopup" ARIA property
          setARIAProperty(but, "haspopup", true);
  
          if(this.positioned && document.getElementById(this.positioned)) {
              document.getElementById(this.positioned).appendChild(but);
          } else {
              inp.parentNode.insertBefore(but, inp.nextSibling);
          };
  
          this.addButtonEvents(but);
  
          but = null;
  
          this.callback("dombuttoncreate", {id:this.id});
      };
      datePicker.prototype.setBespokeTitles = function(titles) {
          this.bespokeTitles = {};
          this.addBespokeTitles(titles);
      };
      datePicker.prototype.addBespokeTitles = function(titles) {
          for(var dt in titles) {
              if(titles.hasOwnProperty(dt)) {
                  this.bespokeTitles[dt] = titles[dt];
              };
          };
      };
      datePicker.prototype.getBespokeTitle = function(y,m,d) {
          var dt,
              dtFull,
              yyyymmdd = y + String(pad(m)) + pad(d);
  
          // Try the datepickers bespoke titles
          for(dt in this.bespokeTitles) {
              if(this.bespokeTitles.hasOwnProperty(dt)) {
                  dtFull = String(dt).replace(/^(\*\*\*\*)/, y).replace(/^(\d\d\d\d)(\*\*)/, "$1"+ pad(m));
                  if(dtFull == yyyymmdd) {
                      return this.bespokeTitles[dt];
                  };
              };
          };
  
          // Try the generic bespoke titles
          for(dt in bespokeTitles) {
              if(bespokeTitles.hasOwnProperty(dt)) {
                  dtFull = String(dt).replace(/^(\*\*\*\*)/, y).replace(/^(\d\d\d\d)(\*\*)/, "$1"+ pad(m));
                  if(dtFull == yyyymmdd) {
                      return bespokeTitles[dt];
                  };
              };
          };
  
          return false;
      };
      datePicker.prototype.returnSelectedDate = function() {
          return this.dateSet;
      };
      datePicker.prototype.setRangeLow = function(range) {
          if(String(range).search(rangeRegExp) == -1) {
              if(debug) {
                  throw "Invalid value passed to setRangeLow method: " + range;
              };
              return false;
          };
          this.rangeLow = range;
          if(!this.inUpdate) {
              this.setDateFromInput();
          };
      };
      datePicker.prototype.setRangeHigh = function(range) {
          if(String(range).search(rangeRegExp) == -1) {
              if(debug) {
                  throw "Invalid value passed to setRangeHigh method: " + range;
              };
              return false;
          };
          this.rangeHigh = range;
          if(!this.inUpdate) {
              this.setDateFromInput();
          };
      };
      datePicker.prototype.setDisabledDays = function(dayArray) {
          if(!dayArray.length || dayArray.join("").search(/^([0|1]{7})$/) == -1) {
              if(debug) {
                  throw "Invalid values located when attempting to call setDisabledDays";
              };
              return false;
          };
          this.disabledDays = dayArray;
          if(!this.inUpdate) {
              this.setDateFromInput();
          };
      };
  
      datePicker.prototype.setDisabledDates = function(dateObj) {
          this.filterDateList(dateObj, true);
      };
      datePicker.prototype.setEnabledDates = function(dateObj) {
          this.filterDateList(dateObj, false);
      };
      datePicker.prototype.addDisabledDates = function(dateObj) {
          this.addDatesToList(dateObj, true);
      };
      datePicker.prototype.addEnabledDates = function(dateObj) {
          this.addDatesToList(dateObj, false);
      };
      datePicker.prototype.filterDateList = function(dateObj, type) {
          var tmpDates = [];
          for(var i = 0; i < this.dateList.length; i++) {
              if(this.dateList[i].type != type) {
                  tmpDates.push(this.dateList[i]);
              };
          };
  
          this.dateList = tmpDates.concat();
          this.addDatesToList(dateObj, type);
      };
      datePicker.prototype.addDatesToList = function(dateObj, areDisabled) {
          var startD;
          for(startD in dateObj) {
              if(String(startD).search(wcDateRegExp) != -1 && (dateObj[startD] == 1 || String(dateObj[startD]).search(wcDateRegExp) != -1)) {
  
                  if(dateObj[startD] != 1 && Number(String(startD).replace(/^\*\*\*\*/, 2010).replace(/^(\d\d\d\d)(\*\*)/, "$1"+"22")) > Number(String(dateObj[startD]).replace(/^\*\*\*\*/, 2010).replace(/^(\d\d\d\d)(\*\*)/, "$1"+"22"))) {
                      continue;
                  };
  
                  this.dateList.push({
                      type:!!(areDisabled),
                      rLow:startD,
                      rHigh:dateObj[startD]
                  });
              };
          };
  
          if(!this.inUpdate) {
              this.setDateFromInput();
          };
      };
      datePicker.prototype.setSelectedDate = function(yyyymmdd) {
          if(String(yyyymmdd).search(wcDateRegExp) == -1) {
              return false;
          };
  
          var match = yyyymmdd.match(rangeRegExp),
              dt    = new Date(+match[2],+match[3]-1,+match[4], 5, 0, 0);
  
          if(!dt || isNaN(dt) || !this.canDateBeSelected(dt)) {
              return false;
          };
  
          this.dateSet = new Date(dt);
  
          if(!this.inUpdate) {
              this.updateTable();
          };
  
          this.callback("dateset", this.createCbArgObj());
          this.returnFormattedDate();
      };
      datePicker.prototype.checkSelectedDate = function() {
          if(this.dateSet && !this.canDateBeSelected(this.dateSet)) {
              this.dateSet = null;
          };
          if(!this.inUpdate) {
              this.updateTable();
          };
      };
      datePicker.prototype.addOnFocusEvents = function() {
          if(this.kbEventsAdded || this.noFocus) {
              return;
          };
  
          addEvent(document, "keypress", this.onkeydown);
          addEvent(document, "mousedown", this.onmousedown);
  
          if(oldIE) {
              removeEvent(document, "keypress", this.onkeydown);
              addEvent(document, "keydown", this.onkeydown);
          };
          if(window.devicePixelRatio) {
              removeEvent(document, "keypress", this.onkeydown);
              addEvent(document, "keydown", this.onkeydown);
          };
          this.noFocus = false;
          this.kbEventsAdded = true;
      };
      datePicker.prototype.removeOnFocusEvents = function() {
  
          if(!this.kbEventsAdded) {
              return;
          };
  
          removeEvent(document, "keypress",  this.onkeydown);
          removeEvent(document, "keydown",   this.onkeydown);
          removeEvent(document, "mousedown", this.onmousedown);
  
          this.kbEventsAdded = false;
      };
      datePicker.prototype.stopTimer = function() {
          this.timerSet = false;
          window.clearTimeout(this.timer);
      };
      datePicker.prototype.setOpacity = function(op) {
          this.div.style.opacity = op/100;
          this.div.style.filter = 'alpha(opacity=' + op + ')';
          this.opacity = op;
      };
      datePicker.prototype.truePosition = function(element) {
          var pos = this.cumulativeOffset(element);
          if(isOpera) {
                  return pos;
          };
          var iebody      = (document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body,
              dsocleft    = document.all ? iebody.scrollLeft : window.pageXOffset,
              dsoctop     = document.all ? iebody.scrollTop  : window.pageYOffset,
              posReal     = this.realOffset(element);
          return [pos[0] - posReal[0] + dsocleft, pos[1] - posReal[1] + dsoctop];
      };
      datePicker.prototype.realOffset = function(element) {
          var t = 0, l = 0;
          do {
              t += element.scrollTop  || 0;
              l += element.scrollLeft || 0;
              element = element.parentNode;
          } while(element);
          return [l, t];
      };
      datePicker.prototype.cumulativeOffset = function(element) {
          var t = 0, l = 0;
          do {
              t += element.offsetTop  || 0;
              l += element.offsetLeft || 0;
              element = element.offsetParent;
          } while(element);
          return [l, t];
      };
      datePicker.prototype.outOfRange = function(tmpDate) {
  
          if(!this.rangeLow && !this.rangeHigh) {
              return false;
          };
  
          var level = false;
  
          if(!tmpDate) {
              level   = true;
              tmpDate = this.date;
          };
  
          var d  = pad(tmpDate.getDate()),
              m  = pad(tmpDate.getMonth() + 1),
              y  = tmpDate.getFullYear(),
              dt = String(y)+String(m)+String(d);
  
          if(this.rangeLow && +dt < +this.rangeLow) {
              if(!level) {
                  return true;
              };
              this.date = new Date(this.rangeLow.substr(0,4), this.rangeLow.substr(4,2)-1, this.rangeLow.substr(6,2), 5, 0, 0);
              return false;
          };
          if(this.rangeHigh && +dt > +this.rangeHigh) {
              if(!level) {
                  return true;
              };
              this.date = new Date(this.rangeHigh.substr(0,4), this.rangeHigh.substr(4,2)-1, this.rangeHigh.substr(6,2), 5, 0, 0);
          };
          return false;
      };
      datePicker.prototype.canDateBeSelected = function(tmpDate) {
          if(!tmpDate || isNaN(tmpDate)) {
              return false;
          };
  
          var d  = pad(tmpDate.getDate()),
              m  = pad(tmpDate.getMonth() + 1),
              y  = tmpDate.getFullYear(),
              dt = y + "" + m + "" + d,
              dd = this.getDateExceptions(y, m),
              wd = tmpDate.getDay() == 0 ? 7 : tmpDate.getDay();
  
          // If date out of range
          if((this.rangeLow && +dt < +this.rangeLow)
             ||
             (this.rangeHigh && +dt > +this.rangeHigh)
             ||
             // or the date has been explicitly disabled
             ((dt in dd) && dd[dt] == 1)
             ||
             // or the date lies on a disabled weekday and it hasn't been explicitly enabled
             (this.disabledDays[wd-1] && (!(dt in dd) || ((dt in dd) && dd[dt] == 1)))) {
                  return false;
          };
  
          return true;
      };
      datePicker.prototype.updateStatus = function(msg) {
          removeChildNodes(this.statusBar);
  
          // All this arseing about just for sups in the footer... nice typography and all that...
          if(msg && this.statusFormat.search(/%S/) != -1 && msg.search(/([0-9]{1,2})(st|nd|rd|th)/) != -1) {
              msg = cbSplit(msg.replace(/([0-9]{1,2})(st|nd|rd|th)/, "$1<sup>$2</sup>"), /<sup>|<\/sup>/);
              var dc = document.createDocumentFragment();
              for(var i = 0, nd; nd = msg[i]; i++) {
                  if(/^(st|nd|rd|th)$/.test(nd)) {
                      var sup = document.createElement("sup");
                      sup.appendChild(document.createTextNode(nd));
                      dc.appendChild(sup);
                  } else {
                      dc.appendChild(document.createTextNode(nd));
                  };
              };
              this.statusBar.appendChild(dc);
          } else {
              this.statusBar.appendChild(document.createTextNode(msg ? msg : getTitleTranslation(9)));
          };
      };
  
      /* Still needs work... */
      datePicker.prototype.setDateFromInput = function() {
          var origDateSet = this.dateSet,
              m           = false,
              but         = this.staticPos ? false : document.getElementById("fd-but-" + this.id),
              e           = localeImport.imported ? [].concat(localeDefaults.fullMonths).concat(localeDefaults.monthAbbrs) : [],
              l           = localeImport.imported ? [].concat(localeImport.fullMonths).concat(localeImport.monthAbbrs) : [],
              eosRegExp   = /(3[01]|[12][0-9]|0?[1-9])(st|nd|rd|th)/i,
              elemCnt     = 0,
              dt          = false,
              allFormats, i, elemID, elem, elemFmt, d, y, elemVal, dp, mp, yp;
  
          // Reset the internal dateSet variable
          this.dateSet = null;
  
          // Try and get a year, month and day from the form element values
          for(elemID in this.formElements) {
  
              elem = document.getElementById(elemID);
  
              if(!elem) {
                  return false;
              };
  
              elemCnt++;
  
              elemVal = String(elem.value);
  
              if(!elemVal) {
                  continue;
              };
  
              elemFmt     = this.formElements[elemID];
              allFormats  = [elemFmt];
              dt          = false;
              dp          = elemFmt.search(dPartsRegExp) != -1;
              mp          = elemFmt.search(mPartsRegExp) != -1;
              yp          = elemFmt.search(yPartsRegExp) != -1;
  
              // Try to assign some default date formats to throw at
              // the (simple) regExp parser for single date parts.
              if(!(dp && mp && yp)) {
                  if(yp && !(mp || dp)) {
                      allFormats = allFormats.concat([
                          "%Y",
                          "%y"
                          ]);
                  } else if(mp && !(yp || dp)) {
                      allFormats = allFormats.concat([
                          "%M",
                          "%F",
                          "%m",
                          "%n"
                          ]);
                  } else if(dp && !(yp || mp)) {
                      allFormats = allFormats.concat([
                          "%d%",
                          "%j"
                          ]);
                  };
              };
  
              for(i = 0; i < allFormats.length; i++) {
                  dt = parseDateString(elemVal, allFormats[i]);
  
                  if(dt) {
                      if(!d && dp && dt.d) {
                          d = dt.d;
                      };
                      if(m === false && mp && dt.m) {
                          m = dt.m;
                      };
                      if(!y && yp && dt.y) {
                          y = dt.y;
                      };
                  };
  
                  if(((dp && d) || !dp)
                     &&
                     ((mp && !m === false) || !mp)
                     &&
                     ((yp && y) || !yp)) {
                      break;
                  };
              };
          };
  
          // Last ditch attempt at date parsing for single inputs that
          // represent the day, month and year parts of the date format.
          // I'm - thankfully - passing this responsibility off to the browser.
          // Date parsing in js sucks but the browsers' in-built Date.parse method
          // will inevitably be better than anything I would hazard to write.
          // Date.parse is implementation dependant though so don't expect
          // consistency, rhyme or reason.
          if(dateParseFallback && (!d || m === false || !y) && dp && mp && yp && elemCnt == 1 && elemVal) {
              // If locale imported then replace month names with English
              // counterparts if necessary
              if(localeImport.imported) {
                  for(i = 0; i < l.length; i++) {
                      elemVal = elemVal.replace(new RegExp(l[i], "i"), e[i]);
                  };
              };
  
              // Remove English ordinal suffix
              if(elemVal.search(eosRegExp) != -1) {
                  elemVal = elemVal.replace(eosRegExp, elemVal.match(eosRegExp)[1]);
              };
  
              // Older browsers have problems with dashes so we replace with
              // slashes which appear to be supported by all and then try to use
              // the in-built Date Object to parse a valid date
              dt = new Date(elemVal.replace(new RegExp("\-", "g"), "/"));
  
              if(dt && !isNaN(dt)) {
                  d = dt.getDate();
                  m = dt.getMonth() + 1;
                  y = dt.getFullYear();
              };
          };
  
          dt = false;
  
          if(d && !(m === false) && y) {
              if(+d > daysInMonth(+m - 1, +y)) {
                  d  = daysInMonth(+m - 1, +y);
                  dt = false;
              } else {
                  dt = new Date(+y, +m - 1, +d, 5, 0, 0);
              };
          };
  
          if(but) {
              removeClass(but, "date-picker-dateval");
          };
  
          if(!dt || isNaN(dt)) {
              var newDate = new Date(y || new Date().getFullYear(), !(m === false) ? m - 1 : new Date().getMonth(), 1, 5, 0, 0);
              this.date = this.cursorDate ? new Date(+this.cursorDate.substr(0,4), +this.cursorDate.substr(4,2) - 1, +this.cursorDate.substr(6,2), 5, 0, 0) : new Date(newDate.getFullYear(), newDate.getMonth(), Math.min(+d || new Date().getDate(), daysInMonth(newDate.getMonth(), newDate.getFullYear())), 5, 0, 0);
  
              this.outOfRange();
              if(this.fullCreate) {
                  this.updateTable();
              };
              return;
          };
  
          dt.setHours(5);
          this.date = new Date(dt);
          this.outOfRange();
  
          if(dt.getTime() == this.date.getTime() && this.canDateBeSelected(this.date)) {
              this.dateSet = new Date(this.date);
              if(but) {
                  addClass(but, "date-picker-dateval");
              };
              this.returnFormattedDate(true);
          };
  
          if(this.fullCreate) {
              this.updateTable();
          };
      };
      datePicker.prototype.setSelectIndex = function(elem, indx) {
          for(var opt = elem.options.length-1; opt >= 0; opt--) {
              if(elem.options[opt].value == indx) {
                  elem.selectedIndex = opt;
                  return;
              };
          };
      };
      datePicker.prototype.returnFormattedDate = function(noFocus) {
          var but = this.staticPos ? false : document.getElementById("fd-but-" + this.id);
  
          if(!this.dateSet) {
              if(but) {
                  removeClass(but, "date-picker-dateval");
              };
              return;
          };
  
          var d   = pad(this.dateSet.getDate()),
              m   = pad(this.dateSet.getMonth() + 1),
              y   = this.dateSet.getFullYear(),
              el  = false,
              elemID, elem, elemFmt, fmtDate;
  
          noFocus = !!noFocus;
  
          for(elemID in this.formElements) {
              elem    = document.getElementById(elemID);
  
              if(!elem) {
                  return;
              };
  
              if(!el) {
                  el = elem;
              };
  
              elemFmt = this.formElements[elemID];
  
              fmtDate = printFormattedDate(this.dateSet, elemFmt, returnLocaleDate);
              if(elem.tagName.toLowerCase() == "input") {
                  elem.value = fmtDate;
              } else {
                  this.setSelectIndex(elem, fmtDate);
              };
          };
  
          if(this.staticPos) {
              this.noFocus = true;
              this.updateTable();
              this.noFocus = false;
          } else if(but) {
              addClass(but, "date-picker-dateval");
          };
  
          if(this.fullCreate) {
              if(el.type && el.type != "hidden" && !noFocus) {
                  try{
                      el.focus();
                  } catch(err) {};
              };
          };
  
          if(!noFocus) {
              this.callback("datereturned", this.createCbArgObj());
          };
      };
      datePicker.prototype.disableDatePicker = function() {
          if(this.disabled) {
              return;
          };
  
          if(this.staticPos) {
              this.removeOnFocusEvents();
              this.removeOldFocus();
              this.noFocus = true;
              addClass(this.div, "date-picker-disabled");
              this.table.onmouseover = this.table.onclick = this.table.onmouseout = this.table.onmousedown = null;
              removeEvent(document, "mousedown", this.onmousedown);
              removeEvent(document, "mouseup",   this.clearTimer);
          } else {
              if(this.visible) {
                  this.hide();
              };
              var but = document.getElementById("fd-but-" + this.id);
              if(but) {
                  addClass(but, "date-picker-control-disabled");
                  // Set a "disabled" ARIA state
                  setARIAProperty(but, "disabled", true);
                  but.onkeydown = but.onclick = function() {
                      return false;
                  };
                  setTabIndex(but, -1);
                  but.title = "";
              }
          };
  
          clearTimeout(this.timer);
          this.disabled = true;
      };
      datePicker.prototype.enableDatePicker = function() {
          if(!this.disabled) {
              return;
          };
  
          if(this.staticPos) {
              this.removeOldFocus();
  
              if(this.dateSet != null) {
                  this.date = this.dateSet;
              };
              this.noFocus = true;
              this.updateTable();
              removeClass(this.div, "date-picker-disabled");
              this.disabled = false;
              this.table.onmouseover = this.onmouseover;
              this.table.onmouseout  = this.onmouseout;
              this.table.onclick     = this.onclick;
              this.table.onmousedown = this.onmousedown;
          } else {
              var but = document.getElementById("fd-but-" + this.id);
              if(but) {
                  removeClass(but, "date-picker-control-disabled");
                  // Reset the "disabled" ARIA state
                  setARIAProperty(but, "disabled", false);
                  this.addButtonEvents(but);
                  but.title = getTitleTranslation(5);
              };
          };
  
          this.disabled = false;
      };
      datePicker.prototype.disableTodayButton = function() {
          var today = new Date();
          removeClass(this.butToday, "fd-disabled");
          if(this.outOfRange(today)
             ||
             (this.date.getDate() == today.getDate()
              &&
              this.date.getMonth() == today.getMonth()
              &&
              this.date.getFullYear() == today.getFullYear())
              ) {
              addClass(this.butToday, "fd-disabled");
          };
      };
      datePicker.prototype.updateTableHeaders = function() {
          var colspanTotal = this.showWeeks ? 8 : 7,
              colOffset    = this.showWeeks ? 1 : 0,
              d, but;
  
          for(var col = colOffset; col < colspanTotal; col++ ) {
              d = (this.firstDayOfWeek + (col - colOffset)) % 7;
              this.ths[col].title = getDayTranslation(d, false);
  
              if(col > colOffset) {
                  but = this.ths[col].getElementsByTagName("span")[0];
                  removeChildNodes(but);
  
                  but.appendChild(document.createTextNode(getDayTranslation(d, true)));
                  but.title = this.ths[col].title;
                  but = null;
              } else {
                  removeChildNodes(this.ths[col]);
                  this.ths[col].appendChild(document.createTextNode(getDayTranslation(d, true)));
              };
  
              removeClass(this.ths[col], "date-picker-highlight");
              if(this.highlightDays[d]) {
                  addClass(this.ths[col], "date-picker-highlight");
              };
          };
  
          if(this.created) {
              this.updateTable();
          };
      };
      datePicker.prototype.callback = function(type, args) {
          if(!type || !(type in this.callbacks)) {
              return false;
          };
  
          var ret = false,
              func;
  
          for(func = 0; func < this.callbacks[type].length; func++) {
              ret = this.callbacks[type][func](args || this.id);
          };
  
          return ret;
      };
      datePicker.prototype.showHideButtons = function(tmpDate) {
          if(!this.butPrevYear) {
              return;
          };
  
          var tdm = tmpDate.getMonth(),
              tdy = tmpDate.getFullYear();
  
          if(this.outOfRange(new Date((tdy - 1), tdm, daysInMonth(+tdm, tdy-1), 5, 0, 0))) {
              addClass(this.butPrevYear, "fd-disabled");
              if(this.yearInc == -1) {
                  this.stopTimer();
              };
          } else {
              removeClass(this.butPrevYear, "fd-disabled");
          };
  
          if(this.outOfRange(new Date(tdy, (+tdm - 1), daysInMonth(+tdm-1, tdy), 5, 0, 0))) {
              addClass(this.butPrevMonth, "fd-disabled");
              if(this.monthInc == -1) {
                  this.stopTimer();
              };
          } else {
              removeClass(this.butPrevMonth, "fd-disabled");
          };
  
          if(this.outOfRange(new Date((tdy + 1), +tdm, 1, 5, 0, 0))) {
              addClass(this.butNextYear, "fd-disabled");
              if(this.yearInc == 1) {
                  this.stopTimer();
              };
          } else {
              removeClass(this.butNextYear, "fd-disabled");
          };
  
          if(this.outOfRange(new Date(tdy, +tdm + 1, 1, 5, 0, 0))) {
              addClass(this.butNextMonth, "fd-disabled");
              if(this.monthInc == 1) {
                  this.stopTimer();
              };
          } else {
              removeClass(this.butNextMonth, "fd-disabled");
          };
      };
      var localeDefaults = {
          fullMonths:["January","February","March","April","May","June","July","August","September","October","November","December"],
          monthAbbrs:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
          fullDays:  ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],
          dayAbbrs:  ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],
          titles:    ["Previous month","Next month","Previous year","Next year", "Today", "Show Calendar", "wk", "Week [[%0%]] of [[%1%]]", "Week", "Select a date", "Click \u0026 Drag to move", "Display \u201C[[%0%]]\u201D first", "Go to Today\u2019s date", "Disabled date :"],
          rtl:       false,
          firstDayOfWeek:0,
          imported:  false
      };
      var joinNodeLists = function() {
          if(!arguments.length) {
              return [];
          };
          var nodeList = [];
          for (var i = 0; i < arguments.length; i++) {
              for (var j = 0, item; item = arguments[i][j]; j++) {
                  nodeList[nodeList.length] = item;
              };
          };
          return nodeList;
      };
      var cleanUp = function() {
          var dp, fe;
          for(dp in datePickers) {
              for(fe in datePickers[dp].formElements) {
                  if(!document.getElementById(fe)) {
                      datePickers[dp].destroy();
                      datePickers[dp] = null;
                      delete datePickers[dp];
                      break;
                  };
              };
          };
      };
      var hideAll = function(exception) {
          var dp;
          for(dp in datePickers) {
              if(!datePickers[dp].created || (exception && exception == datePickers[dp].id)) {
                  continue;
              };
              datePickers[dp].hide();
          };
      };
      var hideDatePicker = function(inpID) {
          if(inpID in datePickers) {
              if(!datePickers[inpID].created || datePickers[inpID].staticPos) {
                  return;
              };
              datePickers[inpID].hide();
          };
      };
      var showDatePicker = function(inpID, autoFocus) {
          if(!(inpID in datePickers)) {
              return false;
          };
  
          datePickers[inpID].clickActivated = !!!autoFocus;
          datePickers[inpID].show(autoFocus);
          return true;
      };
      var destroy = function(e) {
          e = e || window.event;
  
          // Don't remove datepickers if it's a pagehide/pagecache event (webkit et al)
          if(e.persisted) {
              return;
          };
  
          var dp;
          for(dp in datePickers) {
              datePickers[dp].destroy();
              datePickers[dp] = null;
              delete datePickers[dp];
          };
          datePickers = null;
  
          removeEvent(window, 'unload', datePickerController.destroy);
      };
      var destroySingleDatePicker = function(id) {
          if(id && (id in datePickers)) {
              datePickers[id].destroy();
              datePickers[id] = null;
              delete datePickers[id];
          };
      };
      var getTitleTranslation = function(num, replacements) {
          replacements = replacements || [];
          if(localeImport.titles.length > num) {
               var txt = localeImport.titles[num];
               if(replacements && replacements.length) {
                  for(var i = 0; i < replacements.length; i++) {
                      txt = txt.replace("[[%" + i + "%]]", replacements[i]);
                  };
               };
               return txt.replace(/[[%(\d)%]]/g,"");
          };
          return "";
      };
      var getDayTranslation = function(day, abbreviation) {
          var titles = localeImport[abbreviation ? "dayAbbrs" : "fullDays"];
          return titles.length && titles.length > day ? titles[day] : "";
      };
      var getMonthTranslation = function(month, abbreviation) {
          var titles = localeImport[abbreviation ? "monthAbbrs" : "fullMonths"];
          return titles.length && titles.length > month ? titles[month] : "";
      };
      var daysInMonth = function(nMonth, nYear) {
          nMonth = (nMonth + 12) % 12;
          return (((0 == (nYear%4)) && ((0 != (nYear%100)) || (0 == (nYear%400)))) && nMonth == 1) ? 29: [31,28,31,30,31,30,31,31,30,31,30,31][nMonth];
      };
      var getWeeksInYear = function(Y) {
          if(Y in weeksInYearCache) {
              return weeksInYearCache[Y];
          };
  
          var X1 = new Date(Y, 0, 4),
              X2 = new Date(Y, 11, 28);
  
          X1.setDate(X1.getDate() - (6 + X1.getDay()) % 7);
          X2.setDate(X2.getDate() + (7 - X2.getDay()) % 7);
  
          weeksInYearCache[Y] = Math.round((X2 - X1) / 604800000);
  
          return weeksInYearCache[Y];
      };
  
      var getWeekNumber = function(y,m,d) {
          var d   = new Date(y, m, d, 0, 0, 0),
              DoW = d.getDay(),
              ms;
  
          d.setDate(d.getDate() - (DoW + 6) % 7 + 3);
          ms = d.valueOf();
          d.setMonth(0);
          d.setDate(4);
          return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
      };
  
      var printFormattedDate = function(date, fmt, useImportedLocale) {
          if(!date || isNaN(date)) {
              return fmt;
          };
  
          var d           = date.getDate(),
              D           = date.getDay(),
              m           = date.getMonth(),
              y           = date.getFullYear(),
              locale      = useImportedLocale ? localeImport : localeDefaults,
              fmtParts    = String(fmt).split(formatSplitRegExp),
              fmtParts    = cbSplit(fmt, formatSplitRegExp),
              fmtNewParts = [],
              flags       = {
                          "d":pad(d),
                          "D":locale.dayAbbrs[D == 0 ? 6 : D - 1],
                          "l":locale.fullDays[D == 0 ? 6 : D - 1],
                          "j":d,
                          "N":D == 0 ? 7 : D,
                          "w":D,
                          "W":getWeekNumber(y,m,d),
                          "M":locale.monthAbbrs[m],
                          "F":locale.fullMonths[m],
                          "m":pad(m + 1),
                          "n":m + 1,
                          "t":daysInMonth(m, y),
                          "y":String(y).substr(2,2),
                          "Y":y,
                          "S":["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
                          },
              len         = fmtParts.length,
              currFlag, f;
  
          for(f = 0; f < len; f++) {
              currFlag = fmtParts[f];
              fmtNewParts.push(currFlag in flags ? flags[currFlag] : currFlag);
          };
  
          return fmtNewParts.join("");
      };
      var parseDateString = function(str, fmt) {
          var d     = false,
              m     = false,
              y     = false,
              dp    = fmt.search(dPartsRegExp) != -1 ? 1 : 0,
              mp    = fmt.search(mPartsRegExp) != -1 ? 1 : 0,
              yp    = fmt.search(yPartsRegExp) != -1 ? 1 : 0,
              now   = new Date(),
              parts = cbSplit(fmt, formatSplitRegExp),
              str   = "" + str,
              len   = parts.length,
              pt, part, l;
  
          loopLabel:
          for(pt = 0; pt < len; pt++) {
              part = parts[pt];
  
              if(part === "") {
                  continue loopLabel;
              };
  
              if(str.length == 0) {
                  break;
              };
  
              switch(part) {
                  // Dividers - be easy on them all i.e. accept them all when parsing...
                  case "/":
                  case ".":
                  case " ":
                  case "-":
                  case ",":
                  case ":":
                      str = str.substr(1);
                      break;
                  // DAY
                  case "d":
                      // Day of the month, 2 digits with leading zeros (01 - 31)
                      if(str.search(/^(3[01]|[12][0-9]|0[1-9])/) != -1) {
                          d = str.substr(0,2);
                          str = str.substr(2);
                          break;
                      } else {
                          return false;
                      };
                  case "j": // Day of the month without leading zeros (1 - 31)
                      if(str.search(/^(3[01]|[12][0-9]|[1-9])/) != -1) {
                          d = +str.match(/^(3[01]|[12][0-9]|[1-9])/)[0];
                          str = str.substr(str.match(/^(3[01]|[12][0-9]|[1-9])/)[0].length);
                          break;
                      } else {
                          return false;
                      };
                  case "D": // A textual representation of a day, three letters (Mon - Sun)
                  case "l": // A full textual representation of the day of the week (Monday - Sunday)
                            // Accept English & imported locales and both modifiers
                      l = localeDefaults.fullDays.concat(localeDefaults.dayAbbrs);
                      if(localeImport.imported) {
                          l = l.concat(localeImport.fullDays).concat(localeImport.dayAbbrs);
                      };
  
                      for(var i = 0; i < l.length; i++) {
                          if(new RegExp("^" + l[i], "i").test(str)) {
                              str = str.substr(l[i].length);
                              continue loopLabel;
                          };
                      };
  
                      break;
                  case "N": // ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0) 1 (for Monday) through 7 (for Sunday)
                  case "w": // Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
                      if(str.search(part == "N" ? /^([1-7])/ : /^([0-6])/) != -1) {
                          str = str.substr(1);
                      };
                      break;
                  case "S": // English ordinal suffix for the day of the month, 2 characters: st, nd, rd or th
                      if(str.search(/^(st|nd|rd|th)/i) != -1) {
                          str = str.substr(2);
                      };
                      break;
                  // WEEK
                  case "W": // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0): 1 - 53
                      if(str.search(/^([1-9]|[1234[0-9]|5[0-3])/) != -1) {
                          str = str.substr(str.match(/^([1-9]|[1234[0-9]|5[0-3])/)[0].length);
                      };
                      break;
                  // MONTH
                  case "M": // A short textual representation of a month, three letters
                  case "F": // A full textual representation of a month, such as January or March
                            // Accept English & imported locales and both modifiers
                      l = localeDefaults.fullMonths.concat(localeDefaults.monthAbbrs);
                      if(localeImport.imported) {
                          l = l.concat(localeImport.fullMonths).concat(localeImport.monthAbbrs);
                      };
                      for(var i = 0; i < l.length; i++) {
                          if(str.search(new RegExp("^" + l[i],"i")) != -1) {
                              str = str.substr(l[i].length);
                              m = ((i + 12) % 12) + 1;
                              continue loopLabel;
                          };
                      };
                      return false;
                  case "m": // Numeric representation of a month, with leading zeros
                      l = /^(1[012]|0[1-9])/;
                      if(str.search(l) != -1) {
                          m = +str.substr(0, 2);
                          str = str.substr(2);
                          break;
                      } else {
                          return false;
                      };
                  case "n": // Numeric representation of a month, without leading zeros
                            // Accept either when parsing
                      l = /^(1[012]|[1-9])/;
                      if(str.search(l) != -1) {
                          m = +str.match(l)[0];
                          str = str.substr(str.match(l)[0].length);
                          break;
                      } else {
                          return false;
                      };
                  case "t": // Number of days in the given month: 28 through 31
                      if(str.search(/2[89]|3[01]/) != -1) {
                          str = str.substr(2);
                          break;
                      } else {
                          return false;
                      };
                  // YEAR
  
                  case "Y": // A full numeric representation of a year, 4 digits
                      if(str.search(/^(\d{4})/) != -1) {
                          y = str.substr(0,4);
                          str = str.substr(4);
                          break;
                      } else {
                          return false;
                      };
                  case "y": // A two digit representation of a year
                      if(str.search(/^(0[0-9]|[1-9][0-9])/) != -1) {
                          y = str.substr(0,2);
                          y = +y < 50 ? '20' + String(y) : '19' + String(y);
                          str = str.substr(2);
                          break;
                      } else {
                          return false;
                      };
                  default:
                     str = str.substr(part.length);
              };
          };
  
          if((dp && d === false) || (mp && m === false) || (yp && y === false)) {
              return false;
          };
  
          if(dp && mp && yp && +d > daysInMonth(+m - 1, +y)) {
              return false;
          };
  
          return {
              "d":dp ? +d : false,
              "m":mp ? +m : false,
              "y":yp ? +y : false
              };
      };
  
      var findLabelForElement = function(element) {
          var label;
          if(element.parentNode && element.parentNode.tagName.toLowerCase() == "label") {
              label = element.parentNode;
          } else {
              var labelList = document.getElementsByTagName('label');
              // loop through label array attempting to match each 'for' attribute to the id of the current element
              for(var lbl = 0; lbl < labelList.length; lbl++) {
                  // Internet Explorer requires the htmlFor test
                  if((labelList[lbl]['htmlFor'] && labelList[lbl]['htmlFor'] == element.id) || (labelList[lbl].getAttribute('for') == element.id)) {
                      label = labelList[lbl];
                      break;
                  };
              };
          };
  
          if(label && !label.id && element.id) {
              label.id = element.id + "_label";
          };
  
          return label;
      };
      var updateLanguage = function() {
          if(typeof(window.fdLocale) == "object" ) {
              localeImport = {
                  titles          : fdLocale.titles,
                  fullMonths      : fdLocale.fullMonths,
                  monthAbbrs      : fdLocale.monthAbbrs,
                  fullDays        : fdLocale.fullDays,
                  dayAbbrs        : fdLocale.dayAbbrs,
                  firstDayOfWeek  : ("firstDayOfWeek" in fdLocale) ? fdLocale.firstDayOfWeek : 0,
                  rtl             : ("rtl" in fdLocale) ? !!(fdLocale.rtl) : false,
                  imported        : true
              };
          } else if(!localeImport) {
              localeImport = localeDefaults;
          };
      };
      var loadLanguage = function() {
          updateLanguage();
          var dp;
          for(dp in datePickers) {
              if(!datePickers[dp].created) {
                  continue;
              };
              datePickers[dp].updateTable();
          };
      };
      var checkElem = function(elem) {
          return !(!elem || !elem.tagName || !((elem.tagName.toLowerCase() == "input" && (elem.type == "text" || elem.type == "hidden")) || elem.tagName.toLowerCase() == "select"));
      };
      var addDatePicker = function(options) {
          updateLanguage();
  
          if(cssAnimations === null) {
              cssAnimations = testCSSAnimationSupport();
          };
  
          if(!options.formElements) {
              if(debug) {
                  throw "No form elements stipulated within initialisation parameters";
              };
              return;
          };
  
          options.id            = (options.id && (options.id in options.formElements)) ? options.id : "";
          options.enabledDates  = false;
          options.disabledDates = false;
  
          var partsFound  = {d:0,m:0,y:0},
              defaultVals = {},
              cursorDate  = false,
              myMin       = 0,
              myMax       = 0,
              fmt, opts, dtPartStr, elemID, elem, dt, i;
  
          for(elemID in options.formElements) {
              elem = document.getElementById(elemID);
  
              if(!checkElem(elem)) {
                  if(debug) {
                      throw "Element '" + elemID + "' is of the wrong type or does not exist within the DOM";
                  };
                  return false;
              };
  
              if(!(options.formElements[elemID].match(formatTestRegExp))) {
                  if(debug) {
                      throw "Element '" + elemID + "' has a date format that does not contain either a day (d|j), month (m|F|n) or year (y|Y) part: " + options.formElements[elemID];
                  };
                  return false;
              };
  
              if(!options.id) {
                  options.id = elemID;
              };
  
              defaultVals[elemID] = elem.tagName == "select" ? elem.selectedIndex || 0 : elem.defaultValue;
  
              fmt             = {
                  "value":options.formElements[elemID]
              };
  
              fmt.d = fmt.value.search(dPartsRegExp) != -1;
              fmt.m = fmt.value.search(mPartsRegExp) != -1;
              fmt.y = fmt.value.search(yPartsRegExp) != -1;
  
              if(fmt.d) {
                  partsFound.d++;
              };
              if(fmt.m) {
                  partsFound.m++;
              };
              if(fmt.y) {
                  partsFound.y++;
              };
  
              if(elem.tagName.toLowerCase() == "select") {
                  // If we have a selectList, then try to parse the higher and lower limits
                  var selOptions = elem.options;
  
                  // Check the yyyymmdd
                  if(fmt.d && fmt.m && fmt.y) {
                      cursorDate = false;
  
                      // Dynamically calculate the available "enabled" dates
                      options.enabledDates = {};
                      options.disabledDates = {};
  
                      for(i = 0; i < selOptions.length; i++) {
                          dt = parseDateString(selOptions[i].value, fmt.value);
  
                          if(dt && dt.y && !(dt.m === false) && dt.d) {
  
                              dtPartStr = dt.y + "" + pad(dt.m) + pad(dt.d);
                              if(!cursorDate) {
                                  cursorDate = dtPartStr;
                              };
  
                              options.enabledDates[dtPartStr] = 1;
  
                              if(!myMin || +dtPartStr < +myMin) {
                                  myMin = dtPartStr;
                              };
  
                              if(!myMax || +dtPartStr > +myMax) {
                                  myMax = dtPartStr;
                              };
                          };
                      };
  
                      // Automatically set cursor to first available date (if no bespoke cursorDate was set);
                      if(!options.cursorDate && cursorDate) {
                          options.cursorDate = cursorDate;
                      };
  
                      options.disabledDates[myMin] = myMax;
  
                  } else if(fmt.m && fmt.y) {
  
                      for(i = 0; i < selOptions.length; i++) {
                          dt = parseDateString(selOptions[i].value, fmt.value);
                          if(dt.y && !(dt.m === false)) {
                              dtPartStr = dt.y + "" + pad(dt.m);
  
                              if(!myMin || +dtPartStr < +myMin) {
                                  myMin = dtPartStr;
                              };
  
                              if(!myMax || +dtPartStr > +myMax) {
                                  myMax = dtPartStr;
                              };
                          };
                      };
  
                      // Round the min & max values to be used as rangeLow & rangeHigh
                      myMin += "" + "01";
                      myMax += "" + daysInMonth(+myMax.substr(4,2) - 1, +myMax.substr(0,4));
  
                  } else if(fmt.y) {
                      for(i = 0; i < selOptions.length; i++) {
                          dt = parseDateString(selOptions[i].value, fmt.value);
                          if(dt.y) {
                              if(!myMin || +dt.y < +myMin) {
                                  myMin = dt.y;
                              };
  
                              if(!myMax || +dt.y > +myMax) {
                                  myMax = dt.y;
                              };
                          };
                      };
  
                      // Round the min & max values to be used as rangeLow & rangeHigh
                      myMin += "" + "0101";
                      myMax += "" + "1231";
                  };
              };
          };
  
          if(!(partsFound.d == 1 && partsFound.m == 1 && partsFound.y == 1)) {
              if(debug) {
                  throw "Could not find all of the required date parts within the date format for element: " + elem.id;
              };
              return false;
          };
  
          options.rangeLow = dateToYYYYMMDD(options.rangeLow || false);
          options.rangeHigh = dateToYYYYMMDD(options.rangeHigh || false);
          options.cursorDate = dateToYYYYMMDD(options.cursorDate || false);
          if(myMin && (!options.rangeLow  || (+options.rangeLow < +myMin))) {
              options.rangeLow = myMin;
          };
          if(myMax && (!options.rangeHigh || (+options.rangeHigh > +myMax))) {
              options.rangeHigh = myMax;
          };
  
          opts = {
              formElements:options.formElements,
              // default values
              defaultVals:defaultVals,
              // Form element id
              id:options.id,
              // Non popup datepicker required
              staticPos:!!(options.staticPos || options.nopopup),
              // Position static datepicker or popup datepicker's button
              positioned:options.positioned && document.getElementById(options.positioned) ? options.positioned : "",
              // Ranges stipulated in YYYYMMDD format
              rangeLow:options.rangeLow && String(options.rangeLow).search(rangeRegExp) != -1 ? options.rangeLow : "",
              rangeHigh:options.rangeHigh && String(options.rangeHigh).search(rangeRegExp) != -1 ? options.rangeHigh : "",
              // Status bar format
              statusFormat:options.statusFormat || statusFormat,
              // No fade in/out effect
              noFadeEffect:!!(options.staticPos) ? true : !!(options.noFadeEffect),
              // No drag functionality
              dragDisabled:nodrag || !!(options.staticPos) ? true : !!(options.dragDisabled),
              // Bespoke tabindex for this datePicker (or its activation button)
              bespokeTabIndex:options.bespokeTabindex && typeof options.bespokeTabindex == 'number' ? parseInt(options.bespokeTabindex, 10) : 0,
              // Bespoke titles
              bespokeTitles:options.bespokeTitles || (bespokeTitles || {}),
              // Final opacity
              finalOpacity:options.finalOpacity && typeof options.finalOpacity == 'number' && (options.finalOpacity > 20 && options.finalOpacity <= 100) ? parseInt(+options.finalOpacity, 10) : (!!(options.staticPos) ? 100 : finalOpacity),
              // Do we hide the form elements on datepicker creation
              hideInput:!!(options.hideInput),
              // Do we hide the "today" button
              noToday:!!(options.noTodayButton),
              // Do we show week numbers
              showWeeks:!!(options.showWeeks),
              // Do we fill the entire grid with dates
              fillGrid:!!(options.fillGrid),
              // Do we constrain selection of dates outside the current month
              constrainSelection:"constrainSelection" in options ? !!(options.constrainSelection) : true,
              // The date to set the initial cursor to
              cursorDate:options.cursorDate && String(options.cursorDate).search(rangeRegExp) != -1 ? options.cursorDate : "",
              // Locate label to set the ARIA labelled-by property
              labelledBy:findLabelForElement(elem),
              // Have we been passed a describedBy to set the ARIA decribed-by property...
              describedBy:(options.describedBy && document.getElementById(options.describedBy)) ? options.describedBy : describedBy && document.getElementById(describedBy) ? describedBy : "",
              // Callback functions
              callbacks:options.callbackFunctions ? options.callbackFunctions : {},
              // Days of the week to highlight (normally the weekend)
              highlightDays:options.highlightDays && options.highlightDays.length && options.highlightDays.length == 7 ? options.highlightDays : [0,0,0,0,0,1,1],
              // Days of the week to disable
              disabledDays:options.disabledDays && options.disabledDays.length && options.disabledDays.length == 7 ? options.disabledDays : [0,0,0,0,0,0,0],
              // A bespoke class to give the datepicker
              bespokeClass:options.bespokeClass ? " " + options.bespokeClass : ""
          };
  
          datePickers[options.id] = new datePicker(opts);
  
          if("disabledDates" in options && !(options.disabledDates === false)) {
              datePickers[options.id].setDisabledDates(options.disabledDates)
          };
  
          if("enabledDates" in options && !(options.enabledDates === false)) {
              datePickers[options.id].setEnabledDates(options.enabledDates)
          };
  
          datePickers[options.id].callback("create", datePickers[options.id].createCbArgObj());
      };
  
      // Used by the button to dictate whether to open or close the datePicker
      var isVisible = function(id) {
          return (!id || !(id in datePickers)) ? false : datePickers[id].visible;
      };
  
      var updateStatic = function() {
          var dp;
          for(dp in datePickers) {
              if(datePickers.hasOwnProperty(dp)) {
                  datePickers[dp].changeHandler();
              };
          };
      };
  
      var testCSSAnimationSupport = function() {
          var domPrefixes     = ["Webkit","Moz", "ms", "O"],
              elm             = document.createElement('div'),
              transitions     = ["WebkitTransition", "transition", "OTransition", "MozTransition", "msTransition"],
              t;
          
          for(t = 0; t < transitions.length; t++) {
              if(transitions[t] in elm.style) {
                  transitionEnd = transitions[t] == "webkitTransition" || transitions[t] == "OTransition" ? transitions[t] + "End" : "transitionend"
                  break;
              }
          }
  
          if(!transitionEnd) {
              return false;
          };
  
          if(elm.style.animationName) { return true; }
  
          for( var i = 0; i < domPrefixes.length; i++ ) {
              if(elm.style[domPrefixes[i] + "AnimationName"] !== undefined) {
                  return true;
              };
          };
  
          return false;
      };
  
      addEvent(window, 'unload', destroy);
      addEvent(window, "load", function() { setTimeout(updateStatic, 0); });
  
      // Add oldie class if needed for IE < 9
      if(oldIE) {
          addClass(document.documentElement, "oldie");
      };
  
      return {
          // General event functions...
          addEvent:               function(obj, type, fn) { return addEvent(obj, type, fn); },
          removeEvent:            function(obj, type, fn) { return removeEvent(obj, type, fn); },
          stopEvent:              function(e) { return stopEvent(e); },
          // Show a single popup datepicker
          show:                   function(inpID) { return showDatePicker(inpID, false); },
          // Hide a popup datepicker
          hide:                   function(inpID) { return hideDatePicker(inpID); },
          // Create a new datepicker
          createDatePicker:       function(options) { addDatePicker(options); },
          // Destroy a datepicker (remove events and DOM nodes)
          destroyDatePicker:      function(inpID) { destroySingleDatePicker(inpID); },
          // Check datePicker form elements exist, if not, destroy the datepicker
          cleanUp:                function() { cleanUp(); },
          // Pretty print a date object according to the format passed in
          printFormattedDate:     function(dt, fmt, useImportedLocale) { return printFormattedDate(dt, fmt, useImportedLocale); },
          // Update the internal date using the form element value
          setDateFromInput:       function(inpID) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setDateFromInput(); },
          // Set low and high date ranges
          setRangeLow:            function(inpID, yyyymmdd) { if(!inpID || !(inpID in datePickers)) { return false; }; datePickers[inpID].setRangeLow(dateToYYYYMMDD(yyyymmdd)); },
          setRangeHigh:           function(inpID, yyyymmdd) { if(!inpID || !(inpID in datePickers)) { return false; }; datePickers[inpID].setRangeHigh(dateToYYYYMMDD(yyyymmdd)); },
          // Set bespoke titles for a datepicker instance
          setBespokeTitles:       function(inpID, titles) {if(!inpID || !(inpID in datePickers)) { return false; }; datePickers[inpID].setBespokeTitles(titles); },
          // Add bespoke titles for a datepicker instance
          addBespokeTitles:       function(inpID, titles) {if(!inpID || !(inpID in datePickers)) { return false; }; datePickers[inpID].addBespokeTitles(titles); },
          // Attempt to parse a valid date from a date string using the passed in format
          parseDateString:        function(str, format) { return parseDateString(str, format); },
          // Change global configuration parameters
          setGlobalOptions:       function(json) { affectJSON(json); },
          // Forces the datepickers "selected" date
          setSelectedDate:        function(inpID, yyyymmdd) { if(!inpID || !(inpID in datePickers)) { return false; }; datePickers[inpID].setSelectedDate(dateToYYYYMMDD(yyyymmdd)); },
          // Is the date valid for selection i.e. not outside ranges etc
          dateValidForSelection:  function(inpID, dt) { if(!inpID || !(inpID in datePickers)) return false; return datePickers[inpID].canDateBeSelected(dt); },
          // Add disabled and enabled dates
          addDisabledDates:       function(inpID, dts) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].addDisabledDates(dts); },
          setDisabledDates:       function(inpID, dts) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setDisabledDates(dts); },
          addEnabledDates:        function(inpID, dts) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].addEnabledDates(dts); },
          setEnabledDates:        function(inpID, dts) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setEnabledDates(dts); },
          // Disable and enable the datepicker
          disable:                function(inpID) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].disableDatePicker(); },
          enable:                 function(inpID) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].enableDatePicker(); },
          // Set the cursor date
          setCursorDate:          function(inpID, yyyymmdd) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setCursorDate(dateToYYYYMMDD(yyyymmdd)); },
          // Whats the currently selected date
          getSelectedDate:        function(inpID) { return (!inpID || !(inpID in datePickers)) ? false : datePickers[inpID].returnSelectedDate(); },
          // Attempt to update the language (causes a redraw of all datepickers on the page)
          loadLanguage:           function() { loadLanguage(); },
          // Set the debug level i.e. throw errors or fail silently
          setDebug:               function(dbg) { debug = !!(dbg); },
          // Converts Date Object to a YYYYMMDD formatted String
          dateToYYYYMMDDStr:      function(dt) { return dateToYYYYMMDD(dt); }
      };
  })();
  