/***************************************************************************** * FILE: anytime.js - The Any+Time(TM) JavaScript Library (source) * * VERSION: 4.1112H * * Copyright 2008-2010 Andrew M. Andrews III (www.AMA3.com). Some Rights * Reserved. This work licensed under the Creative Commons Attribution- * Noncommercial-Share Alike 3.0 Unported License except in jurisdicitons * for which the license has been ported by Creative Commons International, * where the work is licensed under the applicable ported license instead. * For a copy of the unported license, visit * http://creativecommons.org/licenses/by-nc-sa/3.0/ * or send a letter to Creative Commons, 171 Second Street, Suite 300, * San Francisco, California, 94105, USA. For ported versions of the * license, visit http://creativecommons.org/international/ * * Alternative licensing arrangements may be made by contacting the * author at http://www.AMA3.com/contact/ * * The Any+Time(TM) JavaScript Library provides the following ECMAScript * functionality: * * AnyTime.Converter * Converts Dates to/from Strings, allowing a wide range of formats * closely matching those provided by the MySQL DATE_FORMAT() function, * with some noteworthy enhancements. * * AnyTime.pad() * Pads a value with a specific number of leading zeroes. * * AnyTime.noPicker() * Destroys a calendar widget previously added by AnyTime.picker(). * Can also be invoked via jQuery using $(selector).AnyTime_noPicker() * * AnyTime.picker() * Attaches a calendar widget to a text field for selecting date/time * values with fewer mouse movements than most similar pickers. Any * format supported by AnyTime.Converter can be used for the text field. * If JavaScript is disabled, the text field remains editable without * any of the picker features. * Can also be invoked via jQuery using $(selector).AnyTime_picker() * * IMPORTANT NOTICE: This code depends upon the jQuery JavaScript Library * (www.jquery.com), currently version 1.4. * * The Any+Time(TM) code and styles in anytime.css have been tested (but not * extensively) on Windows Vista in Internet Explorer 8.0, Firefox 3.0, Opera * 10.10 and Safari 4.0. Minor variations in IE6+7 are to be expected, due * to their broken box model. Please report any other problems to the author * (URL above). * * Any+Time is a trademark of Andrew M. Andrews III. * Thanks to Chu for help with a setMonth() issue! ****************************************************************************/ var AnyTime = { //============================================================================= // AnyTime.pad() pads a value with a specified number of zeroes and returns // a string containing the padded value. //============================================================================= pad: function( val, len ) { var str = String(Math.abs(val)); while ( str.length < len ) str = '0'+str; if ( val < 0 ) str = '-'+str; return str; } }; (function($) { // private members var __oneDay = (24 * 60 * 60 * 1000); var __daysIn = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; var __iframe = null; var __initialized = false; var __msie6 = (navigator.userAgent.indexOf('MSIE 6') > 0); var __msie7 = (navigator.userAgent.indexOf('MSIE 7') > 0); var __pickers = []; // Add methods to jQuery to create and destroy pickers using // the typical jQuery approach. jQuery.prototype.AnyTime_picker = function(options) { return this.each(function(i) { AnyTime.picker(this.id, options); }); } jQuery.prototype.AnyTime_noPicker = function() { return this.each(function(i) { AnyTime.noPicker(this.id); }); } // Add special methods to jQuery to compute the height and width // of picker components differently for Internet Explorer 6.x // This prevents the pickers from being too tall and wide. jQuery.prototype.AnyTime_height = function(inclusive) { return (__msie6 ? Number(this.css('height').replace(/[^0-9]/g, '')) : this.outerHeight(inclusive)); }; jQuery.prototype.AnyTime_width = function(inclusive) { return (__msie6 ? (1 + Number(this.css('width').replace(/[^0-9]/g, ''))) : this.outerWidth(inclusive)); }; // Add a method to jQuery to change the classes of an element to // indicate whether it's value is current (used by AnyTime.picker), // and another to trigger the click handler for the currently- // selected button under an element. jQuery.prototype.AnyTime_current = function(isCurrent, isLegal) { if (isCurrent) { this.removeClass('AnyTime-out-btn ui-state-default ui-state-disabled ui-state-highlight'); this.addClass('AnyTime-cur-btn ui-state-default ui-state-highlight'); } else { this.removeClass('AnyTime-cur-btn ui-state-highlight'); if (!isLegal) this.addClass('AnyTime-out-btn ui-state-disabled'); else this.removeClass('AnyTime-out-btn ui-state-disabled'); } }; jQuery.prototype.AnyTime_clickCurrent = function() { this.find('.AnyTime-cur-btn').triggerHandler('click'); } $(document).ready( function() { // IE6 doesn't float popups over // // // // The appearance of the picker can be extensively modified using CSS styles. // A default appearance can be achieved by the "anytime.css" stylesheet that // accompanies this script. The default style looks better in browsers other // than Internet Explorer (before IE8) because older versions of IE do not // properly implement the CSS box model standard; however, it is passable in // Internet Explorer as well. // // Method parameters: // // id - the "id" attribute of the textfield to associate with the // AnyTime.picker object. The AnyTime.picker will attach itself // to the textfield and manage its value. // // options - an object (associative array) of optional parameters that // override default behaviors. The supported options are: // // ajaxOptions - options passed to jQuery's $.ajax() method whenever // the user dismisses a popup picker or selects a value in an inline // picker. The input's name (or ID) and value are passed to the // server (appended to ajaxOptions.data, if present), and the // "success" handler sets the input's value to the responseText. // Therefore, the text returned by the server must be valid for the // input'sdate/time format, and the server can approve or veto the // value chosen by the user. For more information, see: // http://docs.jquery.com/Ajax. // If ajaxOptions.success is specified, it is used instead of the // default "success" behavior. // // askEra - if true, buttons to select the era are shown on the year // selector popup, even if format specifier does not include the // era. If false, buttons to select the era are NOT shown, even // if the format specifier includes ther era. Normally, era buttons // are only shown if the format string specifies the era. // // askSecond - if false, buttons for number-of-seconds are not shown // even if the format includes seconds. Normally, the buttons // are shown if the format string includes seconds. // // earliest - String or Date object representing the earliest date/time // that a user can select. For best results if the field is only // used to specify a date, be sure to set the time to 00:00:00. // If a String is used, it will be parsed according to the picker's // format (see AnyTime.Converter.format()). // // firstDOW - a value from 0 (Sunday) to 6 (Saturday) stating which // day should appear at the beginning of the week. The default is 0 // (Sunday). The most common substitution is 1 (Monday). Note that // if custom arrays are specified for AnyTime.Converter's dayAbbreviations // and/or dayNames options, they should nonetheless begin with the // value for Sunday. // // hideInput - if true, the is "hidden" (the picker appears in // its place). This actually sets the border, height, margin, padding // and width of the field as small as possivle, so it can still get focus. // If you try to hide the field using traditional techniques (such as // setting "display:none"), the picker will not behave correctly. // // labelDayOfMonth - the label for the day-of-month "buttons". // Can be any HTML! If not specified, "Day of Month" is assumed. // // labelDismiss - the label for the dismiss "button" (if placement is // "popup"). Can be any HTML! If not specified, "X" is assumed. // // labelHour - the label for the hour "buttons". // Can be any HTML! If not specified, "Hour" is assumed. // // labelMinute - the label for the minute "buttons". // Can be any HTML! If not specified, "Minute" is assumed. // // labelMonth - the label for the month "buttons". // Can be any HTML! If not specified, "Month" is assumed. // // labelTimeZone - the label for the UTC offset (timezone) "buttons". // Can be any HTML! If not specified, "Time Zone" is assumed. // // labelSecond - the label for the second "buttons". // Can be any HTML! If not specified, "Second" is assumed. // This option is ignored if askSecond is false! // // labelTitle - the label for the "title bar". Can be any HTML! // If not specified, then whichever of the following is most // appropriate is used: "Select a Date and Time", "Select a Date" // or "Select a Time", or no label if only one field is present. // // labelYear - the label for the year "buttons". // Can be any HTML! If not specified, "Year" is assumed. // // latest - String or Date object representing the latest date/time // that a user can select. For best results if the field is only // used to specify a date, be sure to set the time to 23:59:59. // If a String is used, it will be parsed according to the picker's // format (see AnyTime.Converter.format()). // // placement - One of the following strings: // // "popup" = the picker appears above its when the input // receives focus, and disappears when it is dismissed. This is // the default behavior. // // "inline" = the picker is placed immediately after the // and remains visible at all times. When choosing this placement, // it is best to make the invisible and use only the // picker to select dates. The value can still be used // during form submission as it will always reflect the current // picker state. // // WARNING: when using "inline" and XHTML and including a day-of- // the-month format field, the input may only appear where a // element is permitted (for example, NOT within a

element). // This is because the picker uses a

element to arrange // the day-of-the-month (calendar) buttons. Failure to follow this // advice may result in an "unknown error" in Internet Explorer. // // The following additional options may be specified; see documentation // for AnyTime.Converter (above) for information about these options: // // baseYear // dayAbbreviations // dayNames // eraAbbreviations // format // monthAbbreviations // monthNames // // Other behavior, such as how to format the values on the display // and which "buttons" to include, is inferred from the format string. //============================================================================= AnyTime.picker = function(id, options) { // Create a new private object instance to manage the picker, // if one does not already exist. if (__pickers[id]) throw 'Cannot create another AnyTime picker for "' + id + '"'; var _this = null; __pickers[id] = { // private members twelveHr: false, ajaxOpts: null, // options for AJAX requests denyTab: true, // set to true to stop Opera from tabbing away askEra: false, // prompt the user for the era in yDiv? cloak: null, // cloak div conv: null, // AnyTime.Converter bMinW: 0, // min width of body div bMinH: 0, // min height of body div dMinW: 0, // min width of date div dMinH: 0, // min height of date div div: null, // picker div dB: null, // body div dD: null, // date div dY: null, // years div dMo: null, // months div dDoM: null, // date-of-month table hDoM: null, // date-of-month heading hMo: null, // month heading hTitle: null, // title heading hY: null, // year heading dT: null, // time div dH: null, // hours div dM: null, // minutes div dS: null, // seconds div dO: null, // offset (time zone) div earliest: null, // earliest selectable date/time fBtn: null, // button with current focus fDOW: 0, // index to use as first day-of-week hBlur: null, // input handler hClick: null, // input handler hFocus: null, // input handler hKeydown: null, // input handler hKeypress: null, // input handler id: null, // picker ID inp: null, // input text field latest: null, // latest selectable date/time lastAjax: null, // last value submitted using AJAX lostFocus: false, // when focus is lost, must redraw lX: 'X', // label for dismiss button lY: 'Year', // label for year lO: 'Time Zone', // label for UTC offset (time zone) oBody: null, // UTC offset selector popup oConv: null, // AnyTime.Converter for offset display oCur: null, // current-UTC-offset button oDiv: null, // UTC offset selector popup oLab: null, // UTC offset label oListMinW: 0, // min width of offset list element oMinW: 0, // min width of UTC offset element oSel: null, // select (plus/minus) UTC-offset button offMin: Number.MIN_VALUE, // current UTC offset in minutes offSI: -1, // current UTC label sub-index (if any) offStr: "", // current UTC offset (time zone) string pop: true, // picker is a popup? time: null, // current date/time tMinW: 0, // min width of time div tMinH: 0, // min height of time div url: null, // URL to submit value using AJAX wMinW: 0, // min width of picker wMinH: 0, // min height of picker yAhead: null, // years-ahead button y0XXX: null, // millenium-digit-zero button (for focus) yCur: null, // current-year button yDiv: null, // year selector popup yLab: null, // year label yNext: null, // next-year button yPast: null, // years-past button yPrior: null, // prior-year button //--------------------------------------------------------------------- // .initialize() initializes the picker instance. //--------------------------------------------------------------------- initialize: function(id) { _this = this; this.id = 'AnyTime--' + id.replace(/[^-_.A-Za-z0-9]/g, '--AnyTime--'); options = jQuery.extend(true, {}, options || {}); options.utcParseOffsetCapture = true; this.conv = new AnyTime.Converter(options); if (options.placement) { if (options.placement == 'inline') this.pop = false; else if (options.placement != 'popup') throw 'unknown placement: ' + options.placement; } if (options.ajaxOptions) { this.ajaxOpts = jQuery.extend({}, options.ajaxOptions); if (!this.ajaxOpts.success) this.ajaxOpts.success = function(data, status) { _this.inp.val(data); }; } if (options.earliest) { if (typeof options.earliest.getTime == 'function') this.earliest = options.earliest.getTime(); else this.earliest = this.conv.parse(options.earliest.toString()); } if (options.firstDOW) { if ((options.firstDOW < 0) || (options.firstDOW > 6)) throw new Exception('illegal firstDOW: ' + options.firstDOW); this.fDOW = options.firstDOW; } if (options.latest) { if (typeof options.latest.getTime == 'function') this.latest = options.latest.getTime(); else this.latest = this.conv.parse(options.latest.toString()); } this.lX = options.labelDismiss || 'X'; this.lY = options.labelYear || 'Year'; this.lO = options.labelTimeZone || 'Time Zone'; // Infer what we can about what to display from the format. var i; var t; var lab; var shownFields = 0; var format = this.conv.fmt; if (typeof options.askEra != 'undefined') this.askEra = options.askEra; else this.askEra = (format.indexOf('%B') >= 0) || (format.indexOf('%C') >= 0) || (format.indexOf('%E') >= 0); var askYear = (format.indexOf('%Y') >= 0) || (format.indexOf('%y') >= 0) || (format.indexOf('%Z') >= 0) || (format.indexOf('%z') >= 0); var askMonth = (format.indexOf('%b') >= 0) || (format.indexOf('%c') >= 0) || (format.indexOf('%M') >= 0) || (format.indexOf('%m') >= 0); var askDoM = (format.indexOf('%D') >= 0) || (format.indexOf('%d') >= 0) || (format.indexOf('%e') >= 0); var askDate = askYear || askMonth || askDoM; this.twelveHr = (format.indexOf('%h') >= 0) || (format.indexOf('%I') >= 0) || (format.indexOf('%l') >= 0) || (format.indexOf('%r') >= 0); var askHour = this.twelveHr || (format.indexOf('%H') >= 0) || (format.indexOf('%k') >= 0) || (format.indexOf('%T') >= 0); var askMinute = (format.indexOf('%i') >= 0) || (format.indexOf('%r') >= 0) || (format.indexOf('%T') >= 0); var askSec = ((format.indexOf('%r') >= 0) || (format.indexOf('%S') >= 0) || (format.indexOf('%s') >= 0) || (format.indexOf('%T') >= 0)); if (askSec && (typeof options.askSecond != 'undefined')) askSec = options.askSecond; var askOff = ((format.indexOf('%#') >= 0) || (format.indexOf('%+') >= 0) || (format.indexOf('%-') >= 0) || (format.indexOf('%:') >= 0) || (format.indexOf('%;') >= 0) || (format.indexOf('%<') >= 0) || (format.indexOf('%>') >= 0) || (format.indexOf('%@') >= 0)); var askTime = askHour || askMinute || askSec || askOff; if (askOff) this.oConv = new AnyTime.Converter({ format: options.formatUtcOffset || format.match(/\S*%[-+:;<>#@]\S*/g).join(' ') }); // Create the picker HTML and add it to the page. // Popup pickers will be moved to the end of the body // once the entire page has loaded. this.inp = $(document.getElementById(id)); // avoids ID-vs-pseudo-selector probs like id="foo:bar" this.div = $('
'); this.inp.after(this.div); this.wMinW = this.div.outerWidth(!$.browser.safari); this.wMinH = this.div.AnyTime_height(true); this.hTitle = $('
'); this.div.append(this.hTitle); this.dB = $('
'); this.div.append(this.dB); this.bMinW = this.dB.outerWidth(true); this.bMinH = this.dB.AnyTime_height(true); if (options.hideInput) this.inp.css({ border: 0, height: '1px', margin: 0, padding: 0, width: '1px' }); // Add dismiss box to title (if popup) t = null; var xDiv = null; if (this.pop) { xDiv = $('
' + this.lX + '
'); this.hTitle.append(xDiv); xDiv.click(function(e) { _this.dismiss(e); }); } // date (calendar) portion lab = ''; if (askDate) { this.dD = $('
'); this.dB.append(this.dD); this.dMinW = this.dD.outerWidth(true); this.dMinH = this.dD.AnyTime_height(true); if (askYear) { this.yLab = $('
' + this.lY + '
'); this.dD.append(this.yLab); this.dY = $('
    '); this.dD.append(this.dY); this.yPast = this.btn(this.dY, '<', this.newYear, ['yrs-past'], '- ' + this.lY); this.yPrior = this.btn(this.dY, '1', this.newYear, ['yr-prior'], '-1 ' + this.lY); this.yCur = this.btn(this.dY, '2', this.newYear, ['yr-cur'], this.lY); this.yCur.removeClass('ui-state-default'); this.yCur.addClass('AnyTime-cur-btn ui-state-default ui-state-highlight'); this.yNext = this.btn(this.dY, '3', this.newYear, ['yr-next'], '+1 ' + this.lY); this.yAhead = this.btn(this.dY, '>', this.newYear, ['yrs-ahead'], '+ ' + this.lY); shownFields++; } // if ( askYear ) if (askMonth) { lab = options.labelMonth || 'Month'; this.hMo = $('
    ' + lab + '
    '); this.dD.append(this.hMo); this.dMo = $('
      '); this.dD.append(this.dMo); for (i = 0; i < 12; i++) { var mBtn = this.btn(this.dMo, this.conv.mAbbr[i], function(event) { var elem = $(event.target); if (elem.hasClass("AnyTime-out-btn")) return; var mo = event.target.AnyTime_month; var t = new Date(this.time.getTime()); if (t.getDate() > __daysIn[mo]) t.setDate(__daysIn[mo]) t.setMonth(mo); this.set(t); this.upd(elem); }, ['mon', 'mon' + String(i + 1)], lab + ' ' + this.conv.mNames[i]); mBtn[0].AnyTime_month = i; } shownFields++; } if (askDoM) { lab = options.labelDayOfMonth || 'Day of Month'; this.hDoM = $('
      ' + lab + '
      '); this.dD.append(this.hDoM); this.dDoM = $('
'); this.dD.append(this.dDoM); t = $(''); this.dDoM.append(t); var tr = $(''); t.append(tr); for (i = 0; i < 7; i++) tr.append(''); var tbody = $(''); this.dDoM.append(tbody); for (var r = 0; r < 6; r++) { tr = $(''); tbody.append(tr); for (i = 0; i < 7; i++) this.btn(tr, 'x', function(event) { var elem = $(event.target); if (elem.hasClass("AnyTime-out-btn")) return; var dom = Number(elem.html()); if (dom) { var t = new Date(this.time.getTime()); t.setDate(dom); this.set(t); this.upd(elem); } }, ['dom'], lab); } shownFields++; } // if ( askDoM ) } // if ( askDate ) // time portion if (askTime) { var tensDiv, onesDiv; this.dT = $('
'); this.dB.append(this.dT); this.tMinW = this.dT.outerWidth(true); this.tMinH = this.dT.AnyTime_height(true); if (askHour) { this.dH = $('
'); this.dT.append(this.dH); lab = options.labelHour || 'Hour'; this.dH.append($('
' + lab + '
')); var amDiv = $('
' + this.conv.dAbbr[(this.fDOW + i) % 7] + '