/***************************************************************************** * FILE: anytime.js - The Any+Time(TM) JavaScript Library (source) * * VERSION: 4.1112K * * Copyright 2008-2011 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 methods to jQuery to change the earliest and latest times using // the typical jQuery approach. jQuery.prototype.AnyTime_setEarliest = function( options ) { return this.each( function(i) { AnyTime.setEarliest( this.id, options ); } ); } jQuery.prototype.AnyTime_setLatest = function( options ) { return this.each( function(i) { AnyTime.setLatest( this.id, options ); } ); } // 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]+'