From 781789864ede2603d5d1cfd2c276a538b30eb134 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Mon, 26 Aug 2013 06:44:20 -0400 Subject: [PATCH 01/53] Datepicker: Initial commit of widget rewrite work from old branch History is on the datepicker-dead branch. --- demos/datepicker/default.html | 5 + external/date.js | 197 +++ external/localization.js | 436 ++++++ tests/unit/date/all.html | 30 + tests/unit/date/date.html | 40 + tests/unit/date/date_core.js | 223 +++ themes/base/datepicker.css | 13 +- ui/datepicker.js | 2455 +++++++-------------------------- 8 files changed, 1436 insertions(+), 1963 deletions(-) create mode 100644 external/date.js create mode 100644 external/localization.js create mode 100644 tests/unit/date/all.html create mode 100644 tests/unit/date/date.html create mode 100644 tests/unit/date/date_core.js diff --git a/demos/datepicker/default.html b/demos/datepicker/default.html index a11d5def7a9..be0c453c2b3 100644 --- a/demos/datepicker/default.html +++ b/demos/datepicker/default.html @@ -5,8 +5,13 @@ jQuery UI Datepicker - Default functionality + + + + + + + + + + + + + + + + +

jQuery UI Datepicker Test Suite

+

+
+

+
    +
    + +
    + + \ No newline at end of file diff --git a/tests/unit/date/date.html b/tests/unit/date/date.html new file mode 100644 index 00000000000..e3454a55b83 --- /dev/null +++ b/tests/unit/date/date.html @@ -0,0 +1,40 @@ + + + + + jQuery UI Date Test Suite + + + + + + + + + + + + + + +

    jQuery UI Date Test Suite

    +

    +
    +

    +
      +
      + +
      +

      + +
      + + \ No newline at end of file diff --git a/tests/unit/date/date_core.js b/tests/unit/date/date_core.js new file mode 100644 index 00000000000..f8f34abfc78 --- /dev/null +++ b/tests/unit/date/date_core.js @@ -0,0 +1,223 @@ + + +var PROP_NAME = 'date'; + + +module('date: core'); + +test('Check Date Setup', 2, function(){ + ok(true,'First Test Always Passes'); + ok($.date(), 'Load JQuery Date'); +}); +test('Check Sets and Gets', 6, function(){ + var date = $.date(); + equal(date.setYear(2012).year(), 2012, 'Set year and retrieve'); + equal(date.setMonth(9).month(), 9, 'Set month and retrieve'); + equal(date.setDay(15).day(), 15, 'Set day and retrieve'); + equal(date.setFullDate(2012,9,15).year(), 2012, 'Set full date and retrieve year'); + equal(date.month(), 9, 'Set full date and retrieve month'); + equal(date.day(), 15, 'Set full date and retrieve day'); +}); +test('Date Adjustments - Normal Use Cases', 10, function(){ + var date = $.date(); + //Use October 15, 2012 + date.setFullDate(2012,9,15); + equal(date.adjust('D',1).day(),16,'Add 1 day'); + equal(date.adjust('D',-1).day(),15,'Subtract 1 day'); + equal(date.adjust('M',1).month(),10,'Add 1 month'); + equal(date.adjust('M',-1).month(),9,'Subtract 1 month'); + equal(date.adjust('Y',1).year(),2013,'Add 1 year'); + equal(date.adjust('Y',-1).year(),2012,'Subtract 1 year'); + //Check changing one value impact another. Ex: Day impacts month + //Use April 30th 2012 + date.setFullDate(2012,3,30); + equal(date.adjust('D',1).month(),4,'Add 1 day to change month from April to May'); + equal(date.adjust('D',-1).month(),3,'Subtract 1 day to change month from May to April'); + //Use December 31st 2012 + date.setFullDate(2012,11,31); + equal(date.adjust('D',1).year(),2013,'Add 1 day to change year from 2012 to 2013'); + equal(date.adjust('D',-1).year(),2012,'Subtract 1 day to change month from 2013 to 2012'); + +}); + +test('Date Adjustments - Month Overflow Edge Cases', 2, function(){ + var date = $.date(); + //Use May 31 2012 + date.setFullDate(2012,4,31); + equal(date.adjust('M',1).day(),30,'Add 1 month from May to June sets days to 30, last day in June (prevent Overflow)'); + equal(date.adjust('M',-1).day(),30,'Subtract 1 month from June to May sets days to 30 in May'); +}); + +test('Date Adjustments - Leap Year Edge Cases', 1, function(){ + var date = $.date(); + //Use February 29 2012 a Leap year + date.setFullDate(2012,1,29); + equal(date.adjust('Y',1).day(),28,'Feb 29 2012, add a year to convert to Feb 28, 2013'); +}); + +test('List days of Week', 2, function(){ + var date = $.date(); + var offset0 = [ + { 'fullname': 'Sunday', 'shortname': 'Su' }, + { 'fullname': 'Monday', 'shortname': 'Mo' }, + { 'fullname': 'Tuesday', 'shortname': 'Tu' }, + { 'fullname': 'Wednesday', 'shortname': 'We' }, + { 'fullname': 'Thursday', 'shortname': 'Th' }, + { 'fullname': 'Friday', 'shortname': 'Fr' }, + { 'fullname': 'Saturday', 'shortname': 'Sa' } ]; + var offset1 = [ + { 'fullname': 'Montag', 'shortname': 'Mo' }, + { 'fullname': 'Dienstag', 'shortname': 'Di' }, + { 'fullname': 'Mittwoch', 'shortname': 'Mi' }, + { 'fullname': 'Donnerstag', 'shortname': 'Do' }, + { 'fullname': 'Freitag', 'shortname': 'Fr' }, + { 'fullname': 'Samstag', 'shortname': 'Sa' }, + { 'fullname': 'Sonntag', 'shortname': 'So' } ]; + deepEqual(date.weekdays(), offset0, 'Get weekdays with start of day on 0 (English)'); + Globalize.culture( 'de-DE' ); + date.refresh(); + deepEqual(date.weekdays(), offset1, 'Get weekdays with start of day on 1 (Germany)'); + //Revert Globalize changes back to English + Globalize.culture('en-EN'); +}); + +test('Leap Year Check', 8, function(){ + var date = $.date(); + ok(date.setYear( 2008 ).isLeapYear(), '2008 is a Leap Year'); + ok(!date.setYear( 2009 ).isLeapYear(), '2009 is not a Leap Year'); + ok(!date.setYear( 2010 ).isLeapYear(), '2010 is not a Leap Year'); + ok(!date.setYear( 2011 ).isLeapYear(), '2011 is not a Leap Year'); + ok(date.isLeapYear( 2012 ), '2012 is a Leap Year'); + ok(!date.isLeapYear( 2013 ), '2013 is not a Leap Year'); + ok(!date.isLeapYear( 2014 ), '2014 is not a Leap year'); + ok(!date.isLeapYear( 2015 ), '2015 is not a Leap year'); +}); + +test('Days in Month', 3, function(){ + var date = $.date(); + date.setFullDate( 2012, 1, 1 ); + equal(date.daysInMonth(), 29, 'Leap Year implicit check for 29 days'); + equal(date.daysInMonth( 2012, 1 ), 29, 'Leap Year explicit check for 29 days'); + equal(date.daysInMonth( 2011, 3 ), 30, 'April has 30 days'); +}); + +test('Month Name', 2, function(){ + var date = $.date(); + equal(date.setMonth(3).monthName(), 'April', 'Month name return April (English)'); + Globalize.culture( 'de-DE' ); + date.refresh(); + equal(date.setMonth(2).monthName(), 'März', 'Month name return March (German)'); + Globalize.culture('en-EN'); + +}); + +test('Clone', 2, function(){ + var date = $.date(); + var date2 = date.clone(); + ok(date2, 'Created cloned object'); + notEqual(date.adjust('Y',1).year(), date2.year(), 'Object manipulated independently'); +}); + +test('Days', 1, function(){ + //TODO needs work + var date = $.date(); + date.eachDay = function( day ) { + if ( day.lead && day.date > 20 ) { + day.selectable = false; + day.render = true; + day.title = "These are the days of last month"; + day.extraClasses = "ui-state-disabled"; + } + if ( day.lead && day.date < 3 ) { + day.selectable = true; + day.render = true; + day.extraClasses = "ui-state-disabled"; + } + if ( day.date == 1 ) { + day.extraClasses = "ui-state-error"; + day.title = "Something bad explaining the error highlight"; + } + if ( day.today ) { + day.title = "A good day!"; + } + }; + ok(date.days(), 'Date days() returns'); +}); + +test( "Months", 5, function(){ + var date = $.date(), + firstMonth = date.months( 1 )[ 0 ], + lastMonth = date.months( 1 )[ 1 ]; + + ok( firstMonth.first ); + ok( !lastMonth.first ); + ok( lastMonth.last ); + ok( !lastMonth.first ); + + ok( firstMonth.month() == ( lastMonth.month() - 1 ) ); +}); + +test('iso8601Week', 2, function(){ + var date = $.date(); + //date.setFullDate(2012, 0, 8); + equal(date.iso8601Week(new Date(2012, 0, 8)), 1, 'Test first week is 1'); + equal(date.iso8601Week(new Date(2012, 11, 31)), 1, 'Test last week is 1 in next year'); +}); + +test('Equal', 4, function(){ + var date = $.date(); + date.setFullDate(2012, 9, 16); + ok(date.equal(new Date(2012, 9, 16)), 'Does date equal provide date'); + ok(!date.equal(new Date(2011, 9, 16)), 'Does date year not equal provide date'); + ok(!date.equal(new Date(2012, 8, 16)), 'Does date month not equal provide date'); + ok(!date.equal(new Date(2012, 9, 15)), 'Does date day not equal provide date'); +}); + +test('Date', 1, function(){ + var date = $.date(); + ok(date.date() instanceof Date, 'Date returned'); +}); + +test('Format', 4, function(){ + var date = $.date(); + date.setFullDate(2012, 9, 16); + equal(date.format(), '10/16/2012', 'Checking default US format'); + equal(date.format('yyyy/MM/dd'), '2012/10/16', 'Checking yyyy/MM/dd format'); + equal(date.format('yy/dd/MM'), '12/16/10', 'Checking yy/dd/MM format'); + equal(date.format('MMMM dd, yyyy'), 'October 16, 2012', 'Checking MMMM dd, yyyy format'); +}); + +test('Calendar', 3, function(){ + var date = $.date(); + ok(date.calendar(), 'Calendar type returned') + var de_cal = {calendars: { + standard: { + "/": ".", + firstDay: 1, + days: { + names: ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"], + namesAbbr: ["So","Mo","Di","Mi","Do","Fr","Sa"], + namesShort: ["So","Mo","Di","Mi","Do","Fr","Sa"] + }, + months: { + names: ["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember",""], + namesAbbr: ["Jan","Feb","Mrz","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez",""] + }, + AM: null, + PM: null, + eras: [{"name":"n. Chr.","start":null,"offset":0}], + patterns: { + d: "dd.MM.yyyy", + D: "dddd, d. MMMM yyyy", + t: "HH:mm", + T: "HH:mm:ss", + f: "dddd, d. MMMM yyyy HH:mm", + F: "dddd, d. MMMM yyyy HH:mm:ss", + M: "dd MMMM", + Y: "MMMM yyyy" + } + } + }}; + ok(date.calendar(de_cal), 'Calendar type changed'); + deepEqual(date.calendar(), de_cal, 'Calendar change verified'); +}); \ No newline at end of file diff --git a/themes/base/datepicker.css b/themes/base/datepicker.css index 0cff00afe96..aa47853507f 100644 --- a/themes/base/datepicker.css +++ b/themes/base/datepicker.css @@ -21,8 +21,13 @@ .ui-datepicker .ui-datepicker-next { position: absolute; top: 2px; - width: 1.8em; - height: 1.8em; + width: 19px; + height: 18px; +} +.ui-datepicker .ui-datepicker-prev:not(.ui-state-hover):not(.ui-state-focus), +.ui-datepicker .ui-datepicker-next:not(.ui-state-hover):not(.ui-state-focus) { + background: none; + border: none; } .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { @@ -40,8 +45,8 @@ .ui-datepicker .ui-datepicker-next-hover { right: 1px; } -.ui-datepicker .ui-datepicker-prev span, -.ui-datepicker .ui-datepicker-next span { +.ui-datepicker .ui-datepicker-prev .ui-icon, +.ui-datepicker .ui-datepicker-next .ui-icon { display: block; position: absolute; left: 50%; diff --git a/ui/datepicker.js b/ui/datepicker.js index 10b7d7eb5f2..d6d5b5e278e 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -14,7 +14,10 @@ // AMD. Register as an anonymous module. define([ "jquery", - "./core" + "./core", + "./widget", + "./button", + "./position" ], factory ); } else { @@ -23,2051 +26,585 @@ } }(function( $ ) { -$.extend($.ui, { datepicker: { version: "@VERSION" } }); - -var datepicker_instActive; - -function datepicker_getZindex( elem ) { - var position, value; - while ( elem.length && elem[ 0 ] !== document ) { - // Ignore z-index if position is set to a value where z-index is ignored by the browser - // This makes behavior of this function consistent across browsers - // WebKit always returns auto if the element is positioned - position = elem.css( "position" ); - if ( position === "absolute" || position === "relative" || position === "fixed" ) { - // IE returns 0 when zIndex is not specified - // other browsers return a string - // we ignore the case of nested elements with an explicit value of 0 - //
      - value = parseInt( elem.css( "zIndex" ), 10 ); - if ( !isNaN( value ) && value !== 0 ) { - return value; - } - } - elem = elem.parent(); - } - - return 0; -} -/* Date picker manager. - Use the singleton instance of this class, $.datepicker, to interact with the date picker. - Settings for (groups of) date pickers are maintained in an instance object, - allowing multiple different settings on the same page. */ - -function Datepicker() { - this._curInst = null; // The current instance in use - this._keyEvent = false; // If the last event was a key event - this._disabledInputs = []; // List of date picker inputs that have been disabled - this._datepickerShowing = false; // True if the popup picker is showing , false if not - this._inDialog = false; // True if showing within a "dialog", false if not - this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division - this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class - this._appendClass = "ui-datepicker-append"; // The name of the append marker class - this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class - this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class - this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class - this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class - this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class - this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class - this.regional = []; // Available regional settings, indexed by language code - this.regional[""] = { // Default regional settings - closeText: "Done", // Display text for close link - prevText: "Prev", // Display text for previous month link - nextText: "Next", // Display text for next month link - currentText: "Today", // Display text for current month link - monthNames: ["January","February","March","April","May","June", - "July","August","September","October","November","December"], // Names of months for drop-down and formatting - monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting - dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting - dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting - dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday - weekHeader: "Wk", // Column header for week of the year - dateFormat: "mm/dd/yy", // See format options on parseDate - firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ... - isRTL: false, // True if right-to-left language, false if left-to-right - showMonthAfterYear: false, // True if the year select precedes month, false for month then year - yearSuffix: "" // Additional text to append to the year in the month headers - }; - this._defaults = { // Global defaults for all the date picker instances - showOn: "focus", // "focus" for popup on focus, - // "button" for trigger button, or "both" for either - showAnim: "fadeIn", // Name of jQuery animation for popup - showOptions: {}, // Options for enhanced animations - defaultDate: null, // Used when field is blank: actual date, - // +/-number for offset from today, null for today - appendText: "", // Display text following the input box, e.g. showing the format - buttonText: "...", // Text for trigger button - buttonImage: "", // URL for trigger button image - buttonImageOnly: false, // True if the image appears alone, false if it appears on a button - hideIfNoPrevNext: false, // True to hide next/previous month links - // if not applicable, false to just disable them - navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links - gotoCurrent: false, // True if today link goes back to current selection instead - changeMonth: false, // True if month can be selected directly, false if only prev/next - changeYear: false, // True if year can be selected directly, false if only prev/next - yearRange: "c-10:c+10", // Range of years to display in drop-down, - // either relative to today's year (-nn:+nn), relative to currently displayed year - // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n) - showOtherMonths: false, // True to show dates in other months, false to leave blank - selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable - showWeek: false, // True to show week of the year, false to not show it - calculateWeek: this.iso8601Week, // How to calculate the week of the year, - // takes a Date and returns the number of the week for it - shortYearCutoff: "+10", // Short year values < this are in the current century, - // > this are in the previous century, - // string value starting with "+" for current year + value - minDate: null, // The earliest selectable date, or null for no limit - maxDate: null, // The latest selectable date, or null for no limit - duration: "fast", // Duration of display/closure - beforeShowDay: null, // Function that takes a date and returns an array with - // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "", - // [2] = cell title (optional), e.g. $.datepicker.noWeekends - beforeShow: null, // Function that takes an input field and - // returns a set of custom settings for the date picker - onSelect: null, // Define a callback function when a date is selected - onChangeMonthYear: null, // Define a callback function when the month or year is changed - onClose: null, // Define a callback function when the datepicker is closed - numberOfMonths: 1, // Number of months to show at a time - showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0) - stepMonths: 1, // Number of months to step back/forward - stepBigMonths: 12, // Number of months to step back/forward for the big links - altField: "", // Selector for an alternate field to store selected dates into - altFormat: "", // The date format to use for the alternate field - constrainInput: true, // The input is constrained by the current date format - showButtonPanel: false, // True to show button panel, false to not show it - autoSize: false, // True to size the input for the date format, false to leave as is - disabled: false // The initial disabled state - }; - $.extend(this._defaults, this.regional[""]); - this.regional.en = $.extend( true, {}, this.regional[ "" ]); - this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en ); - this.dpDiv = datepicker_bindHover($("
      ")); -} - -$.extend(Datepicker.prototype, { - /* Class name added to elements to indicate already configured with a date picker. */ - markerClassName: "hasDatepicker", - - //Keep track of the maximum number of rows displayed (see #7043) - maxRows: 4, - - // TODO rename to "widget" when switching to widget factory - _widgetDatepicker: function() { - return this.dpDiv; - }, - - /* Override the default settings for all instances of the date picker. - * @param settings object - the new settings to use as defaults (anonymous object) - * @return the manager object - */ - setDefaults: function(settings) { - datepicker_extendRemove(this._defaults, settings || {}); - return this; - }, - - /* Attach the date picker to a jQuery selection. - * @param target element - the target input field or division or span - * @param settings object - the new settings to use for this date picker instance (anonymous) - */ - _attachDatepicker: function(target, settings) { - var nodeName, inline, inst; - nodeName = target.nodeName.toLowerCase(); - inline = (nodeName === "div" || nodeName === "span"); - if (!target.id) { - this.uuid += 1; - target.id = "dp" + this.uuid; - } - inst = this._newInst($(target), inline); - inst.settings = $.extend({}, settings || {}); - if (nodeName === "input") { - this._connectDatepicker(target, inst); - } else if (inline) { - this._inlineDatepicker(target, inst); - } - }, - - /* Create a new instance object. */ - _newInst: function(target, inline) { - var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars - return {id: id, input: target, // associated target - selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection - drawMonth: 0, drawYear: 0, // month being drawn - inline: inline, // is datepicker inline or not - dpDiv: (!inline ? this.dpDiv : // presentation div - datepicker_bindHover($("
      ")))}; - }, - - /* Attach the date picker to an input field. */ - _connectDatepicker: function(target, inst) { - var input = $(target); - inst.append = $([]); - inst.trigger = $([]); - if (input.hasClass(this.markerClassName)) { - return; - } - this._attachments(input, inst); - input.addClass(this.markerClassName).keydown(this._doKeyDown). - keypress(this._doKeyPress).keyup(this._doKeyUp); - this._autoSize(inst); - $.data(target, "datepicker", inst); - //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665) - if( inst.settings.disabled ) { - this._disableDatepicker( target ); - } - }, - - /* Make attachments based on settings. */ - _attachments: function(input, inst) { - var showOn, buttonText, buttonImage, - appendText = this._get(inst, "appendText"), - isRTL = this._get(inst, "isRTL"); - - if (inst.append) { - inst.append.remove(); - } - if (appendText) { - inst.append = $("" + appendText + ""); - input[isRTL ? "before" : "after"](inst.append); - } - - input.unbind("focus", this._showDatepicker); - - if (inst.trigger) { - inst.trigger.remove(); - } - - showOn = this._get(inst, "showOn"); - if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field - input.focus(this._showDatepicker); - } - if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked - buttonText = this._get(inst, "buttonText"); - buttonImage = this._get(inst, "buttonImage"); - inst.trigger = $(this._get(inst, "buttonImageOnly") ? - $("").addClass(this._triggerClass). - attr({ src: buttonImage, alt: buttonText, title: buttonText }) : - $("").addClass(this._triggerClass). - html(!buttonImage ? buttonText : $("").attr( - { src:buttonImage, alt:buttonText, title:buttonText }))); - input[isRTL ? "before" : "after"](inst.trigger); - inst.trigger.click(function() { - if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) { - $.datepicker._hideDatepicker(); - } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) { - $.datepicker._hideDatepicker(); - $.datepicker._showDatepicker(input[0]); - } else { - $.datepicker._showDatepicker(input[0]); +// TODO use uniqueId, if possible +var idIncrement = 0, + // TODO move this to the instance + suppressExpandOnFocus = false; + +$.widget( "ui.datepicker", { + options: { + appendTo: null, + // TODO review + eachDay: $.noop, + numberOfMonths: 1, + position: { + my: "left top", + at: "left bottom" + }, + showWeek: false, + show: true, + hide: true, + + // callbacks + close: null, + open: null, + select: null + }, + _create: function() { + this.date = $.date(); + this.date.eachDay = this.options.eachDay; + this.id = "ui-datepicker-" + idIncrement; + idIncrement++; + if ( this.element.is( "input" ) ) { + this._createPicker(); + } else { + this.inline = true; + this.picker = this.element; + } + this._on( this.picker, { + "click .ui-datepicker-prev": function( event ) { + event.preventDefault(); + this.date.adjust( "M", -this.options.numberOfMonths ); + this.refresh(); + }, + "click .ui-datepicker-next": function( event ) { + event.preventDefault(); + this.date.adjust( "M", this.options.numberOfMonths ); + this.refresh(); + }, + "click .ui-datepicker-current": function( event ) { + event.preventDefault(); + this.select( event, new Date().getTime() ); + }, + "click .ui-datepicker-close": function( event ) { + event.preventDefault(); + this.close( event ); + }, + "mousedown .ui-datepicker-calendar a": function( event ) { + event.preventDefault(); + // TODO exclude clicks on lead days or handle them correctly + // TODO store/read more then just date, also required for multi month picker + this.select( event, $( event.currentTarget ).data( "timestamp" ) ); + if ( this.inline ) { + this.grid.focus( 1 ); } - return false; - }); - } - }, - - /* Apply the maximum length for the date format. */ - _autoSize: function(inst) { - if (this._get(inst, "autoSize") && !inst.inline) { - var findMax, max, maxI, i, - date = new Date(2009, 12 - 1, 20), // Ensure double digits - dateFormat = this._get(inst, "dateFormat"); - - if (dateFormat.match(/[DM]/)) { - findMax = function(names) { - max = 0; - maxI = 0; - for (i = 0; i < names.length; i++) { - if (names[i].length > max) { - max = names[i].length; - maxI = i; - } - } - return maxI; - }; - date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ? - "monthNames" : "monthNamesShort")))); - date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ? - "dayNames" : "dayNamesShort"))) + 20 - date.getDay()); - } - inst.input.attr("size", this._formatDate(inst, date).length); - } - }, - - /* Attach an inline date picker to a div. */ - _inlineDatepicker: function(target, inst) { - var divSpan = $(target); - if (divSpan.hasClass(this.markerClassName)) { - return; - } - divSpan.addClass(this.markerClassName).append(inst.dpDiv); - $.data(target, "datepicker", inst); - this._setDate(inst, this._getDefaultDate(inst), true); - this._updateDatepicker(inst); - this._updateAlternate(inst); - //If disabled option is true, disable the datepicker before showing it (see ticket #5665) - if( inst.settings.disabled ) { - this._disableDatepicker( target ); - } - // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements - // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height - inst.dpDiv.css( "display", "block" ); - }, - - /* Pop-up the date picker in a "dialog" box. - * @param input element - ignored - * @param date string or Date - the initial date to display - * @param onSelect function - the function to call when a date is selected - * @param settings object - update the dialog date picker instance's settings (anonymous object) - * @param pos int[2] - coordinates for the dialog's position within the screen or - * event - with x/y coordinates or - * leave empty for default (screen centre) - * @return the manager object - */ - _dialogDatepicker: function(input, date, onSelect, settings, pos) { - var id, browserWidth, browserHeight, scrollX, scrollY, - inst = this._dialogInst; // internal instance - - if (!inst) { - this.uuid += 1; - id = "dp" + this.uuid; - this._dialogInput = $(""); - this._dialogInput.keydown(this._doKeyDown); - $("body").append(this._dialogInput); - inst = this._dialogInst = this._newInst(this._dialogInput, false); - inst.settings = {}; - $.data(this._dialogInput[0], "datepicker", inst); - } - datepicker_extendRemove(inst.settings, settings || {}); - date = (date && date.constructor === Date ? this._formatDate(inst, date) : date); - this._dialogInput.val(date); - - this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null); - if (!this._pos) { - browserWidth = document.documentElement.clientWidth; - browserHeight = document.documentElement.clientHeight; - scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; - scrollY = document.documentElement.scrollTop || document.body.scrollTop; - this._pos = // should use actual width/height below - [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY]; - } - - // move input on screen for focus, but hidden behind dialog - this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px"); - inst.settings.onSelect = onSelect; - this._inDialog = true; - this.dpDiv.addClass(this._dialogClass); - this._showDatepicker(this._dialogInput[0]); - if ($.blockUI) { - $.blockUI(this.dpDiv); - } - $.data(this._dialogInput[0], "datepicker", inst); - return this; - }, - - /* Detach a datepicker from its control. - * @param target element - the target input field or division or span - */ - _destroyDatepicker: function(target) { - var nodeName, - $target = $(target), - inst = $.data(target, "datepicker"); - - if (!$target.hasClass(this.markerClassName)) { - return; - } - - nodeName = target.nodeName.toLowerCase(); - $.removeData(target, "datepicker"); - if (nodeName === "input") { - inst.append.remove(); - inst.trigger.remove(); - $target.removeClass(this.markerClassName). - unbind("focus", this._showDatepicker). - unbind("keydown", this._doKeyDown). - unbind("keypress", this._doKeyPress). - unbind("keyup", this._doKeyUp); - } else if (nodeName === "div" || nodeName === "span") { - $target.removeClass(this.markerClassName).empty(); - } - }, - - /* Enable the date picker to a jQuery selection. - * @param target element - the target input field or division or span - */ - _enableDatepicker: function(target) { - var nodeName, inline, - $target = $(target), - inst = $.data(target, "datepicker"); + }, + "keydown .ui-datepicker-calendar": "_handleKeydown" + }); - if (!$target.hasClass(this.markerClassName)) { - return; - } + // TODO use hoverable (no delegation support)? convert to _on? + this.picker.delegate( ".ui-datepicker-header a, .ui-datepicker-calendar a", "mouseenter.datepicker mouseleave.datepicker", function() { + $( this ).toggleClass( "ui-state-hover" ); + }); - nodeName = target.nodeName.toLowerCase(); - if (nodeName === "input") { - target.disabled = false; - inst.trigger.filter("button"). - each(function() { this.disabled = false; }).end(). - filter("img").css({opacity: "1.0", cursor: ""}); - } else if (nodeName === "div" || nodeName === "span") { - inline = $target.children("." + this._inlineClass); - inline.children().removeClass("ui-state-disabled"); - inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). - prop("disabled", false); - } - this._disabledInputs = $.map(this._disabledInputs, - function(value) { return (value === target ? null : value); }); // delete entry + this._createTmpl(); }, - /* Disable the date picker to a jQuery selection. - * @param target element - the target input field or division or span - */ - _disableDatepicker: function(target) { - var nodeName, inline, - $target = $(target), - inst = $.data(target, "datepicker"); - - if (!$target.hasClass(this.markerClassName)) { + _handleKeydown: function( event ) { + if ( jQuery.inArray( event.keyCode, [ 13, 33, 34, 35, 36, 37, 38, 39, 40 ] ) === -1 ) { + //only interested navigation keys return; } + event.preventDefault(); + + var newId, newCell, + activeCell = $( "#" + this.grid.attr( "aria-activedescendant" ) ), + oldMonth = this.date.month(), + oldYear = this.date.year(); + + // TODO: Handle for pickers with multiple months + switch ( event.keyCode ) { + case $.ui.keyCode.ENTER: + activeCell.children( "a:first" ).mousedown(); + return; + case $.ui.keyCode.PAGE_UP: + this.date.adjust( event.altKey ? "Y" : "M", 1 ); + break; + case $.ui.keyCode.PAGE_DOWN: + this.date.adjust( event.altKey ? "Y" : "M", -1 ); + break; + case $.ui.keyCode.END: + this.date.setDay( this.date.daysInMonth() ); + break; + case $.ui.keyCode.HOME: + this.date.setDay( 1 ); + break; + case $.ui.keyCode.LEFT: + this.date.adjust( "D", -1 ); + break; + case $.ui.keyCode.UP: + this.date.adjust( "D", -7 ); + break; + case $.ui.keyCode.RIGHT: + this.date.adjust( "D", 1 ); + break; + case $.ui.keyCode.DOWN: + this.date.adjust( "D", 7 ); + break; + default: + return; + } + + if ( this.date.month() !== oldMonth || this.date.year() !== oldYear ) { + this.refresh(); + this.grid.focus( 1 ); + } else { + newId = this.id + "-" + this.date.day(); + newCell = $( "#" + newId ); - nodeName = target.nodeName.toLowerCase(); - if (nodeName === "input") { - target.disabled = true; - inst.trigger.filter("button"). - each(function() { this.disabled = true; }).end(). - filter("img").css({opacity: "0.5", cursor: "default"}); - } else if (nodeName === "div" || nodeName === "span") { - inline = $target.children("." + this._inlineClass); - inline.children().addClass("ui-state-disabled"); - inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). - prop("disabled", true); - } - this._disabledInputs = $.map(this._disabledInputs, - function(value) { return (value === target ? null : value); }); // delete entry - this._disabledInputs[this._disabledInputs.length] = target; - }, - - /* Is the first field in a jQuery collection disabled as a datepicker? - * @param target element - the target input field or division or span - * @return boolean - true if disabled, false if enabled - */ - _isDisabledDatepicker: function(target) { - if (!target) { - return false; - } - for (var i = 0; i < this._disabledInputs.length; i++) { - if (this._disabledInputs[i] === target) { - return true; - } - } - return false; - }, - - /* Retrieve the instance data for the target control. - * @param target element - the target input field or division or span - * @return object - the associated instance data - * @throws error if a jQuery problem getting data - */ - _getInst: function(target) { - try { - return $.data(target, "datepicker"); - } - catch (err) { - throw "Missing instance data for this datepicker"; - } - }, - - /* Update or retrieve the settings for a date picker attached to an input field or division. - * @param target element - the target input field or division or span - * @param name object - the new settings to update or - * string - the name of the setting to change or retrieve, - * when retrieving also "all" for all instance settings or - * "defaults" for all global defaults - * @param value any - the new value for the setting - * (omit if above is an object or to retrieve a value) - */ - _optionDatepicker: function(target, name, value) { - var settings, date, minDate, maxDate, - inst = this._getInst(target); - - if (arguments.length === 2 && typeof name === "string") { - return (name === "defaults" ? $.extend({}, $.datepicker._defaults) : - (inst ? (name === "all" ? $.extend({}, inst.settings) : - this._get(inst, name)) : null)); - } - - settings = name || {}; - if (typeof name === "string") { - settings = {}; - settings[name] = value; - } - - if (inst) { - if (this._curInst === inst) { - this._hideDatepicker(); - } - - date = this._getDateDatepicker(target, true); - minDate = this._getMinMaxDate(inst, "min"); - maxDate = this._getMinMaxDate(inst, "max"); - datepicker_extendRemove(inst.settings, settings); - // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided - if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) { - inst.settings.minDate = this._formatDate(inst, minDate); - } - if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) { - inst.settings.maxDate = this._formatDate(inst, maxDate); - } - if ( "disabled" in settings ) { - if ( settings.disabled ) { - this._disableDatepicker(target); - } else { - this._enableDatepicker(target); - } + // TODO unnecessary optimization? is it really needed? + if ( !newCell.length ) { + return; } - this._attachments($(target), inst); - this._autoSize(inst); - this._setDate(inst, date); - this._updateAlternate(inst); - this._updateDatepicker(inst); - } - }, - // change method deprecated - _changeDatepicker: function(target, name, value) { - this._optionDatepicker(target, name, value); - }, - - /* Redraw the date picker attached to an input field or division. - * @param target element - the target input field or division or span - */ - _refreshDatepicker: function(target) { - var inst = this._getInst(target); - if (inst) { - this._updateDatepicker(inst); - } - }, - - /* Set the dates for a jQuery selection. - * @param target element - the target input field or division or span - * @param date Date - the new date - */ - _setDateDatepicker: function(target, date) { - var inst = this._getInst(target); - if (inst) { - this._setDate(inst, date); - this._updateDatepicker(inst); - this._updateAlternate(inst); - } - }, - - /* Get the date(s) for the first entry in a jQuery selection. - * @param target element - the target input field or division or span - * @param noDefault boolean - true if no default date is to be used - * @return Date - the current date - */ - _getDateDatepicker: function(target, noDefault) { - var inst = this._getInst(target); - if (inst && !inst.inline) { - this._setDateFromField(inst, noDefault); - } - return (inst ? this._getDate(inst) : null); - }, - - /* Handle keystrokes. */ - _doKeyDown: function(event) { - var onSelect, dateStr, sel, - inst = $.datepicker._getInst(event.target), - handled = true, - isRTL = inst.dpDiv.is(".ui-datepicker-rtl"); - - inst._keyEvent = true; - if ($.datepicker._datepickerShowing) { - switch (event.keyCode) { - case 9: $.datepicker._hideDatepicker(); - handled = false; - break; // hide on tab out - case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." + - $.datepicker._currentClass + ")", inst.dpDiv); - if (sel[0]) { - $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]); - } - - onSelect = $.datepicker._get(inst, "onSelect"); - if (onSelect) { - dateStr = $.datepicker._formatDate(inst); - - // trigger custom callback - onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); - } else { - $.datepicker._hideDatepicker(); - } - - return false; // don't submit the form - case 27: $.datepicker._hideDatepicker(); - break; // hide on escape - case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ? - -$.datepicker._get(inst, "stepBigMonths") : - -$.datepicker._get(inst, "stepMonths")), "M"); - break; // previous month/year on page up/+ ctrl - case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ? - +$.datepicker._get(inst, "stepBigMonths") : - +$.datepicker._get(inst, "stepMonths")), "M"); - break; // next month/year on page down/+ ctrl - case 35: if (event.ctrlKey || event.metaKey) { - $.datepicker._clearDate(event.target); - } - handled = event.ctrlKey || event.metaKey; - break; // clear on ctrl or command +end - case 36: if (event.ctrlKey || event.metaKey) { - $.datepicker._gotoToday(event.target); - } - handled = event.ctrlKey || event.metaKey; - break; // current on ctrl or command +home - case 37: if (event.ctrlKey || event.metaKey) { - $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D"); - } - handled = event.ctrlKey || event.metaKey; - // -1 day on ctrl or command +left - if (event.originalEvent.altKey) { - $.datepicker._adjustDate(event.target, (event.ctrlKey ? - -$.datepicker._get(inst, "stepBigMonths") : - -$.datepicker._get(inst, "stepMonths")), "M"); - } - // next month/year on alt +left on Mac + this.grid.attr("aria-activedescendant", newId); + + activeCell.children("a").removeClass("ui-state-focus"); + newCell.children("a").addClass("ui-state-focus"); + } + }, + _createPicker: function() { + this.picker = $( "
      " ) + .addClass( "ui-front" ) + // TODO add a ui-datepicker-popup class, move position:absolte to that + .css( "position", "absolute" ) + .uniqueId() + .hide(); + this._setHiddenPicker(); + this.picker.appendTo( this._appendTo() ); + + this.element + .attr( "aria-haspopup", "true" ) + .attr( "aria-owns", this.picker.attr( "id" ) ); + + this._on({ + keydown: function( event ) { + switch ( event.keyCode ) { + case $.ui.keyCode.TAB: + // Waiting for close() will make popup hide too late, which breaks tab key behavior + this.picker.hide(); + this.close( event ); break; - case 38: if (event.ctrlKey || event.metaKey) { - $.datepicker._adjustDate(event.target, -7, "D"); - } - handled = event.ctrlKey || event.metaKey; - break; // -1 week on ctrl or command +up - case 39: if (event.ctrlKey || event.metaKey) { - $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D"); - } - handled = event.ctrlKey || event.metaKey; - // +1 day on ctrl or command +right - if (event.originalEvent.altKey) { - $.datepicker._adjustDate(event.target, (event.ctrlKey ? - +$.datepicker._get(inst, "stepBigMonths") : - +$.datepicker._get(inst, "stepMonths")), "M"); + case $.ui.keyCode.ESCAPE: + if ( this.isOpen ) { + this.close( event ); } - // next month/year on alt +right break; - case 40: if (event.ctrlKey || event.metaKey) { - $.datepicker._adjustDate(event.target, +7, "D"); + case $.ui.keyCode.DOWN: + case $.ui.keyCode.UP: + clearTimeout( this.closeTimer ); + this._delay(function() { + this.open( event ); + this.grid.focus( 1 ); + }, 1); + break; + } + }, + mousedown: function( event ) { + if (this.isOpen) { + suppressExpandOnFocus = true; + this.close(); + return; + } + this.open( event ); + clearTimeout( this.closeTimer ); + }, + focus: function( event ) { + if ( !suppressExpandOnFocus ) { + this._delay( function() { + if ( !this.isOpen ) { + this.open( event ); } - handled = event.ctrlKey || event.metaKey; - break; // +1 week on ctrl or command +down - default: handled = false; - } - } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home - $.datepicker._showDatepicker(this); - } else { - handled = false; - } - - if (handled) { - event.preventDefault(); - event.stopPropagation(); - } - }, - - /* Filter entered characters - based on date format. */ - _doKeyPress: function(event) { - var chars, chr, - inst = $.datepicker._getInst(event.target); - - if ($.datepicker._get(inst, "constrainInput")) { - chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat")); - chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode); - return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1); - } - }, - - /* Synchronise manual entry and field/alternate field. */ - _doKeyUp: function(event) { - var date, - inst = $.datepicker._getInst(event.target); - - if (inst.input.val() !== inst.lastVal) { - try { - date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"), - (inst.input ? inst.input.val() : null), - $.datepicker._getFormatConfig(inst)); - - if (date) { // only if valid - $.datepicker._setDateFromField(inst); - $.datepicker._updateAlternate(inst); - $.datepicker._updateDatepicker(inst); + }, 1); } + this._delay( function() { + suppressExpandOnFocus = false; + }, 100); + }, + blur: function() { + suppressExpandOnFocus = false; } - catch (err) { - } - } - return true; - }, - - /* Pop-up the date picker for a given input field. - * If false returned from beforeShow event handler do not show. - * @param input element - the input field attached to the date picker or - * event - if triggered by focus - */ - _showDatepicker: function(input) { - input = input.target || input; - if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger - input = $("input", input.parentNode)[0]; - } - - if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here - return; - } - - var inst, beforeShow, beforeShowSettings, isFixed, - offset, showAnim, duration; - - inst = $.datepicker._getInst(input); - if ($.datepicker._curInst && $.datepicker._curInst !== inst) { - $.datepicker._curInst.dpDiv.stop(true, true); - if ( inst && $.datepicker._datepickerShowing ) { - $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] ); - } - } - - beforeShow = $.datepicker._get(inst, "beforeShow"); - beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {}; - if(beforeShowSettings === false){ - return; - } - datepicker_extendRemove(inst.settings, beforeShowSettings); - - inst.lastVal = null; - $.datepicker._lastInput = input; - $.datepicker._setDateFromField(inst); - - if ($.datepicker._inDialog) { // hide cursor - input.value = ""; - } - if (!$.datepicker._pos) { // position below input - $.datepicker._pos = $.datepicker._findPos(input); - $.datepicker._pos[1] += input.offsetHeight; // add the height - } - - isFixed = false; - $(input).parents().each(function() { - isFixed |= $(this).css("position") === "fixed"; - return !isFixed; }); - offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]}; - $.datepicker._pos = null; - //to avoid flashes on Firefox - inst.dpDiv.empty(); - // determine sizing offscreen - inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"}); - $.datepicker._updateDatepicker(inst); - // fix width for dynamic number of date pickers - // and adjust position before showing - offset = $.datepicker._checkOffset(inst, offset, isFixed); - inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ? - "static" : (isFixed ? "fixed" : "absolute")), display: "none", - left: offset.left + "px", top: offset.top + "px"}); - - if (!inst.inline) { - showAnim = $.datepicker._get(inst, "showAnim"); - duration = $.datepicker._get(inst, "duration"); - inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 ); - $.datepicker._datepickerShowing = true; - - if ( $.effects && $.effects.effect[ showAnim ] ) { - inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration); - } else { - inst.dpDiv[showAnim || "show"](showAnim ? duration : null); - } - - if ( $.datepicker._shouldFocusInput( inst ) ) { - inst.input.focus(); - } - - $.datepicker._curInst = inst; - } - }, - - /* Generate the date picker content. */ - _updateDatepicker: function(inst) { - this.maxRows = 4; //Reset the max number of rows being displayed (see #7043) - datepicker_instActive = inst; // for delegate hover events - inst.dpDiv.empty().append(this._generateHTML(inst)); - this._attachHandlers(inst); - inst.dpDiv.find("." + this._dayOverClass + " a"); - - var origyearshtml, - numMonths = this._getNumberOfMonths(inst), - cols = numMonths[1], - width = 17; - - inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""); - if (cols > 1) { - inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em"); - } - inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") + - "Class"]("ui-datepicker-multi"); - inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") + - "Class"]("ui-datepicker-rtl"); - - if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) { - inst.input.focus(); - } - - // deffered render of the years select (to avoid flashes on Firefox) - if( inst.yearshtml ){ - origyearshtml = inst.yearshtml; - setTimeout(function(){ - //assure that inst.yearshtml didn't change. - if( origyearshtml === inst.yearshtml && inst.yearshtml ){ - inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml); + this._on( this.picker, { + focusout: function( event ) { + // use a timer to allow click to clear it and letting that + // handle the closing instead of opening again + // also allows tabbing inside the calendar without it closing + this.closeTimer = this._delay( function() { + this.close( event ); + }, 150); + }, + focusin: function() { + clearTimeout( this.closeTimer ); + }, + mouseup: function() { + clearTimeout( this.closeTimer ); + }, + // TODO on TAB (or shift TAB), make sure it ends up on something useful in DOM order + keyup: function( event ) { + if ( event.keyCode === $.ui.keyCode.ESCAPE && this.picker.is( ":visible" ) ) { + this.close( event ); + this._focusTrigger(); } - origyearshtml = inst.yearshtml = null; - }, 0); - } - }, - - // #6694 - don't focus the input if it's already focused - // this breaks the change event in IE - // Support: IE and jQuery <1.9 - _shouldFocusInput: function( inst ) { - return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" ); - }, - - /* Check positioning to remain on screen. */ - _checkOffset: function(inst, offset, isFixed) { - var dpWidth = inst.dpDiv.outerWidth(), - dpHeight = inst.dpDiv.outerHeight(), - inputWidth = inst.input ? inst.input.outerWidth() : 0, - inputHeight = inst.input ? inst.input.outerHeight() : 0, - viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()), - viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop()); - - offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0); - offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0; - offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0; - - // now check if datepicker is showing outside window viewport - move to a better place if so. - offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? - Math.abs(offset.left + dpWidth - viewWidth) : 0); - offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? - Math.abs(dpHeight + inputHeight) : 0); - - return offset; - }, - - /* Find an object's position on the screen. */ - _findPos: function(obj) { - var position, - inst = this._getInst(obj), - isRTL = this._get(inst, "isRTL"); - - while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) { - obj = obj[isRTL ? "previousSibling" : "nextSibling"]; - } - - position = $(obj).offset(); - return [position.left, position.top]; - }, - - /* Hide the date picker from view. - * @param input element - the input field attached to the date picker - */ - _hideDatepicker: function(input) { - var showAnim, duration, postProcess, onClose, - inst = this._curInst; - - if (!inst || (input && inst !== $.data(input, "datepicker"))) { - return; - } - - if (this._datepickerShowing) { - showAnim = this._get(inst, "showAnim"); - duration = this._get(inst, "duration"); - postProcess = function() { - $.datepicker._tidyDialog(inst); - }; - - // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed - if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) { - inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess); - } else { - inst.dpDiv[(showAnim === "slideDown" ? "slideUp" : - (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess); - } - - if (!showAnim) { - postProcess(); - } - this._datepickerShowing = false; - - onClose = this._get(inst, "onClose"); - if (onClose) { - onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]); } + }); - this._lastInput = null; - if (this._inDialog) { - this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" }); - if ($.blockUI) { - $.unblockUI(); - $("body").append(this.dpDiv); + this._on( this.document, { + click: function( event ) { + if ( this.isOpen && !$( event.target ).closest( this.element.add( this.picker ) ).length ) { + this.close( event ); } } - this._inDialog = false; - } - }, - - /* Tidy up after a dialog display. */ - _tidyDialog: function(inst) { - inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar"); + }); }, + _appendTo: function() { + var element = this.options.appendTo; - /* Close date picker if clicked elsewhere. */ - _checkExternalClick: function(event) { - if (!$.datepicker._curInst) { - return; + if ( element ) { + element = element.jquery || element.nodeType ? + $( element ) : + this.document.find( element ).eq( 0 ); } - var $target = $(event.target), - inst = $.datepicker._getInst($target[0]); - - if ( ( ( $target[0].id !== $.datepicker._mainDivId && - $target.parents("#" + $.datepicker._mainDivId).length === 0 && - !$target.hasClass($.datepicker.markerClassName) && - !$target.closest("." + $.datepicker._triggerClass).length && - $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) || - ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) { - $.datepicker._hideDatepicker(); - } - }, - - /* Adjust one of the date sub-fields. */ - _adjustDate: function(id, offset, period) { - var target = $(id), - inst = this._getInst(target[0]); - - if (this._isDisabledDatepicker(target[0])) { - return; + if ( !element ) { + element = this.element.closest( ".ui-front" ); } - this._adjustInstDate(inst, offset + - (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning - period); - this._updateDatepicker(inst); - }, - - /* Action for current link. */ - _gotoToday: function(id) { - var date, - target = $(id), - inst = this._getInst(target[0]); - if (this._get(inst, "gotoCurrent") && inst.currentDay) { - inst.selectedDay = inst.currentDay; - inst.drawMonth = inst.selectedMonth = inst.currentMonth; - inst.drawYear = inst.selectedYear = inst.currentYear; - } else { - date = new Date(); - inst.selectedDay = date.getDate(); - inst.drawMonth = inst.selectedMonth = date.getMonth(); - inst.drawYear = inst.selectedYear = date.getFullYear(); + if ( !element.length ) { + element = this.document[0].body; } - this._notifyChange(inst); - this._adjustDate(target); - }, - /* Action for selecting a new month/year. */ - _selectMonthYear: function(id, select, period) { - var target = $(id), - inst = this._getInst(target[0]); - - inst["selected" + (period === "M" ? "Month" : "Year")] = - inst["draw" + (period === "M" ? "Month" : "Year")] = - parseInt(select.options[select.selectedIndex].value,10); - - this._notifyChange(inst); - this._adjustDate(target); + return element; }, + _createTmpl: function() { + this.date.refresh(); - /* Action for selecting a day. */ - _selectDay: function(id, month, year, td) { - var inst, - target = $(id); + this._createDatepicker(); + this.picker.find( "button" ).button(); - if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) { - return; + if ( this.inline ) { + this.picker.children().addClass( "ui-datepicker-inline" ); } - - inst = this._getInst(target[0]); - inst.selectedDay = inst.currentDay = $("a", td).html(); - inst.selectedMonth = inst.currentMonth = month; - inst.selectedYear = inst.currentYear = year; - this._selectDate(id, this._formatDate(inst, - inst.currentDay, inst.currentMonth, inst.currentYear)); - }, - - /* Erase the input field and hide the date picker. */ - _clearDate: function(id) { - var target = $(id); - this._selectDate(target, ""); + // against display:none in datepicker.css + this.picker.find( ".ui-datepicker" ).css( "display", "block" ); + this.grid = this.picker.find( ".ui-datepicker-calendar" ); }, + _createDatepicker: function() { + var multiClasses = [], + pickerHtml = ""; - /* Update the input field with the selected date. */ - _selectDate: function(id, dateStr) { - var onSelect, - target = $(id), - inst = this._getInst(target[0]); - - dateStr = (dateStr != null ? dateStr : this._formatDate(inst)); - if (inst.input) { - inst.input.val(dateStr); - } - this._updateAlternate(inst); - - onSelect = this._get(inst, "onSelect"); - if (onSelect) { - onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback - } else if (inst.input) { - inst.input.trigger("change"); // fire the change event - } - - if (inst.inline){ - this._updateDatepicker(inst); + if (this.options.numberOfMonths === 1 ) { + pickerHtml = this._buildHeader() + this._buildGrid() + this._buildButtons(); } else { - this._hideDatepicker(); - this._lastInput = inst.input[0]; - if (typeof(inst.input[0]) !== "object") { - inst.input.focus(); // restore focus + pickerHtml = this._buildMultiplePicker(); + multiClasses.push( "ui-datepicker-multi" ); + multiClasses.push( "ui-datepicker-multi-" + this.options.numberOfMonths ); + } + + $( "
      " ) + .addClass( "ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all" ) + .addClass( multiClasses.join( " " ) ) + .attr({ + role: "region", + "aria-labelledby": this.id + "-title" + }) + .html( pickerHtml ) + .appendTo( this.picker ); + }, + _buildMultiplePicker: function() { + var headerClass, + html = "", + currentDate = this.date, + months = this.date.months( this.options.numberOfMonths - 1 ), + i = 0; + + for ( i; i < months.length; i++ ) { + this.date = months[ i ]; + headerClass = months[ i ].first ? "ui-corner-left" : + months[ i ].last ? "ui-corner-right" : ""; + + html += "
      " + + "
      "; + if ( months[i].first ) { + html += this._buildPreviousLink(); + } + if ( months[i].last ) { + html += this._buildNextLink(); } - this._lastInput = null; - } - }, - - /* Update any alternate field to synchronise with the main field. */ - _updateAlternate: function(inst) { - var altFormat, date, dateStr, - altField = this._get(inst, "altField"); - if (altField) { // update alternate field too - altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat"); - date = this._getDate(inst); - dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst)); - $(altField).each(function() { $(this).val(dateStr); }); + html += this._buildTitlebar(); + html += "
      "; + html += this._buildGrid(); + html += "
      "; } - }, - /* Set as beforeShowDay function to prevent selection of weekends. - * @param date Date - the date to customise - * @return [boolean, string] - is this date selectable?, what is its CSS class? - */ - noWeekends: function(date) { - var day = date.getDay(); - return [(day > 0 && day < 6), ""]; - }, - - /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. - * @param date Date - the date to get the week for - * @return number - the number of the week within the year that contains this date - */ - iso8601Week: function(date) { - var time, - checkDate = new Date(date.getTime()); - - // Find Thursday of this week starting on Monday - checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); + html += "
      "; + html += this._buildButtons(); - time = checkDate.getTime(); - checkDate.setMonth(0); // Compare with Jan 1 - checkDate.setDate(1); - return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; + this.date = currentDate; + return html; }, - - /* Parse a string value into a date object. - * See formatDate below for the possible formats. - * - * @param format string - the expected format of the date - * @param value string - the date in the above format - * @param settings Object - attributes include: - * shortYearCutoff number - the cutoff year for determining the century (optional) - * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) - * dayNames string[7] - names of the days from Sunday (optional) - * monthNamesShort string[12] - abbreviated names of the months (optional) - * monthNames string[12] - names of the months (optional) - * @return Date - the extracted date value or null if value is blank - */ - parseDate: function (format, value, settings) { - if (format == null || value == null) { - throw "Invalid arguments"; - } - - value = (typeof value === "object" ? value.toString() : value + ""); - if (value === "") { - return null; - } - - var iFormat, dim, extra, - iValue = 0, - shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff, - shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp : - new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)), - dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort, - dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames, - monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort, - monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames, - year = -1, - month = -1, - day = -1, - doy = -1, - literal = false, - date, - // Check whether a format character is doubled - lookAhead = function(match) { - var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); - if (matches) { - iFormat++; - } - return matches; - }, - // Extract a number from the string value - getNumber = function(match) { - var isDoubled = lookAhead(match), - size = (match === "@" ? 14 : (match === "!" ? 20 : - (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))), - digits = new RegExp("^\\d{1," + size + "}"), - num = value.substring(iValue).match(digits); - if (!num) { - throw "Missing number at position " + iValue; - } - iValue += num[0].length; - return parseInt(num[0], 10); - }, - // Extract a name from the string value and convert to an index - getName = function(match, shortNames, longNames) { - var index = -1, - names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) { - return [ [k, v] ]; - }).sort(function (a, b) { - return -(a[1].length - b[1].length); - }); - - $.each(names, function (i, pair) { - var name = pair[1]; - if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) { - index = pair[0]; - iValue += name.length; - return false; - } - }); - if (index !== -1) { - return index + 1; - } else { - throw "Unknown name at position " + iValue; - } - }, - // Confirm that a literal character matches the string value - checkLiteral = function() { - if (value.charAt(iValue) !== format.charAt(iFormat)) { - throw "Unexpected literal at position " + iValue; - } - iValue++; - }; - - for (iFormat = 0; iFormat < format.length; iFormat++) { - if (literal) { - if (format.charAt(iFormat) === "'" && !lookAhead("'")) { - literal = false; - } else { - checkLiteral(); - } + _buildHeader: function() { + return "
      " + + this._buildPreviousLink() + + this._buildNextLink() + + this._buildTitlebar() + + "
      "; + }, + _buildPreviousLink: function() { + var labels = Globalize.localize( "datepicker" ); + return ""; + }, + _buildNextLink: function() { + var labels = Globalize.localize( "datepicker" ); + return ""; + }, + _buildTitlebar: function() { + var labels = Globalize.localize( "datepicker" ); + return "
      " + + "
      " + + this._buildTitle() + + "
      " + + ", " + labels.datePickerRole + "" + + "
      "; + }, + _buildTitle: function() { + return "" + + this.date.monthName() + + " " + + "" + + this.date.year() + + ""; + }, + _buildGrid: function() { + return "" + + this._buildGridHeading() + + this._buildGridBody() + + "
      "; + }, + _buildGridHeading: function() { + var cells = "", + i = 0, + labels = Globalize.localize( "datepicker" ); + + if ( this.options.showWeek ) { + cells += "" + labels.weekHeader + ""; + } + for ( i; i < this.date.weekdays().length; i++ ) { + cells += this._buildGridHeaderCell( this.date.weekdays()[i] ); + } + return "" + + "" + cells + "" + + ""; + }, + _buildGridHeaderCell: function( day ) { + return "" + + "" + + day.shortname + + "" + + ""; + }, + _buildGridBody: function() { + var rows = "", + i = 0; + for ( i; i < this.date.days().length; i++ ) { + rows += this._buildWeekRow( this.date.days()[i] ); + } + return "" + rows + ""; + }, + _buildWeekRow: function( week ) { + var cells = "", + i = 0; + + if ( this.options.showWeek ) { + cells += "" + week.number + ""; + } + for ( i; i < week.days.length; i++ ) { + cells += this._buildDayCell( week.days[i] ); + } + return "" + cells + ""; + }, + _buildDayCell: function( day ) { + var contents = "", + idAttribute = day.render ? ( "id=" + this.id + "-" + day.date ) : "", + ariaSelectedAttribute = "aria-selected=" + ( day.current ? "true" : "false" ), + ariaDisabledAttribute = day.selectable ? "" : "aria-disabled=true"; + + if ( day.render ) { + if ( day.selectable ) { + contents = this._buildDayLink( day ); } else { - switch (format.charAt(iFormat)) { - case "d": - day = getNumber("d"); - break; - case "D": - getName("D", dayNamesShort, dayNames); - break; - case "o": - doy = getNumber("o"); - break; - case "m": - month = getNumber("m"); - break; - case "M": - month = getName("M", monthNamesShort, monthNames); - break; - case "y": - year = getNumber("y"); - break; - case "@": - date = new Date(getNumber("@")); - year = date.getFullYear(); - month = date.getMonth() + 1; - day = date.getDate(); - break; - case "!": - date = new Date((getNumber("!") - this._ticksTo1970) / 10000); - year = date.getFullYear(); - month = date.getMonth() + 1; - day = date.getDate(); - break; - case "'": - if (lookAhead("'")){ - checkLiteral(); - } else { - literal = true; - } - break; - default: - checkLiteral(); - } + contents = this._buildDayDisplay( day ); } } - if (iValue < value.length){ - extra = value.substr(iValue); - if (!/^\s+/.test(extra)) { - throw "Extra/unparsed characters found in date: " + extra; - } - } + return "" + + contents + + ""; + }, + _buildDayLink: function( day ) { + var link, + classes = [ "ui-state-default" ], + labels = Globalize.localize( "datepicker" ); - if (year === -1) { - year = new Date().getFullYear(); - } else if (year < 100) { - year += new Date().getFullYear() - new Date().getFullYear() % 100 + - (year <= shortYearCutoff ? 0 : -100); + if ( day.date === this.date.day() ) { + classes.push( "ui-state-focus" ); } - - if (doy > -1) { - month = 1; - day = doy; - do { - dim = this._getDaysInMonth(year, month - 1); - if (day <= dim) { - break; - } - month++; - day -= dim; - } while (true); + if ( day.current ) { + classes.push( "ui-state-active" ); } - - date = this._daylightSavingAdjust(new Date(year, month - 1, day)); - if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) { - throw "Invalid date"; // E.g. 31/02/00 + if ( day.today ) { + classes.push( "ui-state-highlight" ); } - return date; - }, - - /* Standard date formats. */ - ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601) - COOKIE: "D, dd M yy", - ISO_8601: "yy-mm-dd", - RFC_822: "D, d M y", - RFC_850: "DD, dd-M-y", - RFC_1036: "D, d M y", - RFC_1123: "D, d M yy", - RFC_2822: "D, d M yy", - RSS: "D, d M y", // RFC 822 - TICKS: "!", - TIMESTAMP: "@", - W3C: "yy-mm-dd", // ISO 8601 - - _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) + - Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000), - - /* Format a date object into a string value. - * The format can be combinations of the following: - * d - day of month (no leading zero) - * dd - day of month (two digit) - * o - day of year (no leading zeros) - * oo - day of year (three digit) - * D - day name short - * DD - day name long - * m - month of year (no leading zero) - * mm - month of year (two digit) - * M - month name short - * MM - month name long - * y - year (two digit) - * yy - year (four digit) - * @ - Unix timestamp (ms since 01/01/1970) - * ! - Windows ticks (100ns since 01/01/0001) - * "..." - literal text - * '' - single quote - * - * @param format string - the desired format of the date - * @param date Date - the date value to format - * @param settings Object - attributes include: - * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) - * dayNames string[7] - names of the days from Sunday (optional) - * monthNamesShort string[12] - abbreviated names of the months (optional) - * monthNames string[12] - names of the months (optional) - * @return string - the date in the above format - */ - formatDate: function (format, date, settings) { - if (!date) { - return ""; + if ( day.extraClasses ) { + classes.push( day.extraClasses.split( "" ) ); } - var iFormat, - dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort, - dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames, - monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort, - monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames, - // Check whether a format character is doubled - lookAhead = function(match) { - var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); - if (matches) { - iFormat++; - } - return matches; - }, - // Format a number, with leading zero if necessary - formatNumber = function(match, value, len) { - var num = "" + value; - if (lookAhead(match)) { - while (num.length < len) { - num = "0" + num; - } - } - return num; - }, - // Format a name, short or long as requested - formatName = function(match, value, shortNames, longNames) { - return (lookAhead(match) ? longNames[value] : shortNames[value]); - }, - output = "", - literal = false; - - if (date) { - for (iFormat = 0; iFormat < format.length; iFormat++) { - if (literal) { - if (format.charAt(iFormat) === "'" && !lookAhead("'")) { - literal = false; - } else { - output += format.charAt(iFormat); - } - } else { - switch (format.charAt(iFormat)) { - case "d": - output += formatNumber("d", date.getDate(), 2); - break; - case "D": - output += formatName("D", date.getDay(), dayNamesShort, dayNames); - break; - case "o": - output += formatNumber("o", - Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3); - break; - case "m": - output += formatNumber("m", date.getMonth() + 1, 2); - break; - case "M": - output += formatName("M", date.getMonth(), monthNamesShort, monthNames); - break; - case "y": - output += (lookAhead("y") ? date.getFullYear() : - (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100); - break; - case "@": - output += date.getTime(); - break; - case "!": - output += date.getTime() * 10000 + this._ticksTo1970; - break; - case "'": - if (lookAhead("'")) { - output += "'"; - } else { - literal = true; - } - break; - default: - output += format.charAt(iFormat); - } - } - } + link = "" + + day.date + ""; + if ( day.today ) { + link += ", " + labels.currentText + ""; } - return output; + return link; }, - - /* Extract all possible characters from the date format. */ - _possibleChars: function (format) { - var iFormat, - chars = "", - literal = false, - // Check whether a format character is doubled - lookAhead = function(match) { - var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); - if (matches) { - iFormat++; - } - return matches; - }; - - for (iFormat = 0; iFormat < format.length; iFormat++) { - if (literal) { - if (format.charAt(iFormat) === "'" && !lookAhead("'")) { - literal = false; - } else { - chars += format.charAt(iFormat); - } - } else { - switch (format.charAt(iFormat)) { - case "d": case "m": case "y": case "@": - chars += "0123456789"; - break; - case "D": case "M": - return null; // Accept anything - case "'": - if (lookAhead("'")) { - chars += "'"; - } else { - literal = true; - } - break; - default: - chars += format.charAt(iFormat); - } - } + _buildDayDisplay: function( day ) { + var classes = []; + if ( day.current ) { + classes.push( "ui-state-active" ); } - return chars; - }, - - /* Get a setting value, defaulting if necessary. */ - _get: function(inst, name) { - return inst.settings[name] !== undefined ? - inst.settings[name] : this._defaults[name]; - }, - - /* Parse existing date and initialise date picker. */ - _setDateFromField: function(inst, noDefault) { - if (inst.input.val() === inst.lastVal) { - return; + if ( day.today ) { + classes.push( "ui-state-highlight" ); } - - var dateFormat = this._get(inst, "dateFormat"), - dates = inst.lastVal = inst.input ? inst.input.val() : null, - defaultDate = this._getDefaultDate(inst), - date = defaultDate, - settings = this._getFormatConfig(inst); - - try { - date = this.parseDate(dateFormat, dates, settings) || defaultDate; - } catch (event) { - dates = (noDefault ? "" : dates); + if ( day.extraClasses ) { + classes.push( day.extraClasses.split( "" ) ); } - inst.selectedDay = date.getDate(); - inst.drawMonth = inst.selectedMonth = date.getMonth(); - inst.drawYear = inst.selectedYear = date.getFullYear(); - inst.currentDay = (dates ? date.getDate() : 0); - inst.currentMonth = (dates ? date.getMonth() : 0); - inst.currentYear = (dates ? date.getFullYear() : 0); - this._adjustInstDate(inst); - }, - /* Retrieve the default date shown on opening. */ - _getDefaultDate: function(inst) { - return this._restrictMinMax(inst, - this._determineDate(inst, this._get(inst, "defaultDate"), new Date())); + return "" + + day.date + ""; }, - - /* A date may be specified as an exact value or a relative one. */ - _determineDate: function(inst, date, defaultDate) { - var offsetNumeric = function(offset) { - var date = new Date(); - date.setDate(date.getDate() + offset); - return date; - }, - offsetString = function(offset) { - try { - return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"), - offset, $.datepicker._getFormatConfig(inst)); - } - catch (e) { - // Ignore - } - - var date = (offset.toLowerCase().match(/^c/) ? - $.datepicker._getDate(inst) : null) || new Date(), - year = date.getFullYear(), - month = date.getMonth(), - day = date.getDate(), - pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g, - matches = pattern.exec(offset); - - while (matches) { - switch (matches[2] || "d") { - case "d" : case "D" : - day += parseInt(matches[1],10); break; - case "w" : case "W" : - day += parseInt(matches[1],10) * 7; break; - case "m" : case "M" : - month += parseInt(matches[1],10); - day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); - break; - case "y": case "Y" : - year += parseInt(matches[1],10); - day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); - break; - } - matches = pattern.exec(offset); - } - return new Date(year, month, day); - }, - newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) : - (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime())))); - - newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate); - if (newDate) { - newDate.setHours(0); - newDate.setMinutes(0); - newDate.setSeconds(0); - newDate.setMilliseconds(0); - } - return this._daylightSavingAdjust(newDate); + _buildButtons: function() { + var labels = Globalize.localize( "datepicker" ); + return "
      " + + "" + + "" + + "
      "; }, - - /* Handle switch to/from daylight saving. - * Hours may be non-zero on daylight saving cut-over: - * > 12 when midnight changeover, but then cannot generate - * midnight datetime, so jump to 1AM, otherwise reset. - * @param date (Date) the date to check - * @return (Date) the corrected date - */ - _daylightSavingAdjust: function(date) { - if (!date) { - return null; - } - date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0); - return date; + _focusTrigger: function() { + suppressExpandOnFocus = true; + this.element.focus(); }, + // Refreshing the entire datepicker during interaction confuses screen readers, specifically + // because the grid heading is marked up as a live region and will often not update if it's + // destroyed and recreated instead of just having its text change. Additionally, interacting + // with the prev and next links would cause loss of focus issues because the links being + // interacted with will disappear while focused. + refresh: function() { + //determine which day gridcell to focus after refresh + //TODO: Prevent disabled cells from being focused + this.date.refresh(); - /* Set the date(s) directly. */ - _setDate: function(inst, date, noChange) { - var clear = !date, - origMonth = inst.selectedMonth, - origYear = inst.selectedYear, - newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date())); - - inst.selectedDay = inst.currentDay = newDate.getDate(); - inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth(); - inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear(); - if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) { - this._notifyChange(inst); - } - this._adjustInstDate(inst); - if (inst.input) { - inst.input.val(clear ? "" : this._formatDate(inst)); + if ( this.options.numberOfMonths === 1 ) { + this.grid = $( this._buildGrid() ); + $( ".ui-datepicker-title", this.picker ).html( this._buildTitle() ); + $( ".ui-datepicker-calendar", this.picker ).replaceWith( this.grid ); + } else { + this._refreshMultiplePicker(); } }, - - /* Retrieve the date(s) directly. */ - _getDate: function(inst) { - var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null : - this._daylightSavingAdjust(new Date( - inst.currentYear, inst.currentMonth, inst.currentDay))); - return startDate; - }, - - /* Attach the onxxx handlers. These are declared statically so - * they work with static code transformers like Caja. - */ - _attachHandlers: function(inst) { - var stepMonths = this._get(inst, "stepMonths"), - id = "#" + inst.id.replace( /\\\\/g, "\\" ); - inst.dpDiv.find("[data-handler]").map(function () { - var handler = { - prev: function () { - $.datepicker._adjustDate(id, -stepMonths, "M"); - }, - next: function () { - $.datepicker._adjustDate(id, +stepMonths, "M"); - }, - hide: function () { - $.datepicker._hideDatepicker(); - }, - today: function () { - $.datepicker._gotoToday(id); - }, - selectDay: function () { - $.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this); - return false; - }, - selectMonth: function () { - $.datepicker._selectMonthYear(id, this, "M"); - return false; - }, - selectYear: function () { - $.datepicker._selectMonthYear(id, this, "Y"); - return false; - } - }; - $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]); - }); - }, - - /* Generate the HTML for the current state of the date picker. */ - _generateHTML: function(inst) { - var maxDraw, prevText, prev, nextText, next, currentText, gotoDate, - controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin, - monthNames, monthNamesShort, beforeShowDay, showOtherMonths, - selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate, - cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows, - printDate, dRow, tbody, daySettings, otherMonth, unselectable, - tempDate = new Date(), - today = this._daylightSavingAdjust( - new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time - isRTL = this._get(inst, "isRTL"), - showButtonPanel = this._get(inst, "showButtonPanel"), - hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"), - navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"), - numMonths = this._getNumberOfMonths(inst), - showCurrentAtPos = this._get(inst, "showCurrentAtPos"), - stepMonths = this._get(inst, "stepMonths"), - isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1), - currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) : - new Date(inst.currentYear, inst.currentMonth, inst.currentDay))), - minDate = this._getMinMaxDate(inst, "min"), - maxDate = this._getMinMaxDate(inst, "max"), - drawMonth = inst.drawMonth - showCurrentAtPos, - drawYear = inst.drawYear; - - if (drawMonth < 0) { - drawMonth += 12; - drawYear--; - } - if (maxDate) { - maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(), - maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate())); - maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw); - while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) { - drawMonth--; - if (drawMonth < 0) { - drawMonth = 11; - drawYear--; - } - } + _refreshMultiplePicker: function() { + for (var i = 0; i < this.options.numberOfMonths; i++ ) { + $( ".ui-datepicker-title", this.picker ).eq( i ).html( this._buildTitle() ); + $( ".ui-datepicker-calendar", this.picker ).eq( i ).html( this._buildGrid() ); + this.date.adjust( "M", 1 ); } - inst.drawMonth = drawMonth; - inst.drawYear = drawYear; - - prevText = this._get(inst, "prevText"); - prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText, - this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)), - this._getFormatConfig(inst))); - - prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ? - "" + prevText + "" : - (hideIfNoPrevNext ? "" : "" + prevText + "")); - nextText = this._get(inst, "nextText"); - nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText, - this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)), - this._getFormatConfig(inst))); - - next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ? - "" + nextText + "" : - (hideIfNoPrevNext ? "" : "" + nextText + "")); - - currentText = this._get(inst, "currentText"); - gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today); - currentText = (!navigationAsDateFormat ? currentText : - this.formatDate(currentText, gotoDate, this._getFormatConfig(inst))); - - controls = (!inst.inline ? "" : ""); - - buttonPanel = (showButtonPanel) ? "
      " + (isRTL ? controls : "") + - (this._isInRange(inst, gotoDate) ? "" : "") + (isRTL ? "" : controls) + "
      " : ""; - - firstDay = parseInt(this._get(inst, "firstDay"),10); - firstDay = (isNaN(firstDay) ? 0 : firstDay); - - showWeek = this._get(inst, "showWeek"); - dayNames = this._get(inst, "dayNames"); - dayNamesMin = this._get(inst, "dayNamesMin"); - monthNames = this._get(inst, "monthNames"); - monthNamesShort = this._get(inst, "monthNamesShort"); - beforeShowDay = this._get(inst, "beforeShowDay"); - showOtherMonths = this._get(inst, "showOtherMonths"); - selectOtherMonths = this._get(inst, "selectOtherMonths"); - defaultDate = this._getDefaultDate(inst); - html = ""; - dow; - for (row = 0; row < numMonths[0]; row++) { - group = ""; - this.maxRows = 4; - for (col = 0; col < numMonths[1]; col++) { - selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay)); - cornerClass = " ui-corner-all"; - calender = ""; - if (isMultiMonth) { - calender += "
      "; - } - calender += "
      " + - (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") + - (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") + - this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate, - row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers - "
      " + - ""; - thead = (showWeek ? "" : ""); - for (dow = 0; dow < 7; dow++) { // days of the week - day = (dow + firstDay) % 7; - thead += ""; - } - calender += thead + ""; - daysInMonth = this._getDaysInMonth(drawYear, drawMonth); - if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) { - inst.selectedDay = Math.min(inst.selectedDay, daysInMonth); - } - leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7; - curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate - numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043) - this.maxRows = numRows; - printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays)); - for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows - calender += ""; - tbody = (!showWeek ? "" : ""); - for (dow = 0; dow < 7; dow++) { // create date picker days - daySettings = (beforeShowDay ? - beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]); - otherMonth = (printDate.getMonth() !== drawMonth); - unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] || - (minDate && printDate < minDate) || (maxDate && printDate > maxDate); - tbody += ""; // display selectable date - printDate.setDate(printDate.getDate() + 1); - printDate = this._daylightSavingAdjust(printDate); - } - calender += tbody + ""; - } - drawMonth++; - if (drawMonth > 11) { - drawMonth = 0; - drawYear++; - } - calender += "
      " + this._get(inst, "weekHeader") + "= 5 ? " class='ui-datepicker-week-end'" : "") + ">" + - "" + dayNamesMin[day] + "
      " + - this._get(inst, "calculateWeek")(printDate) + "" + // actions - (otherMonth && !showOtherMonths ? " " : // display for other months - (unselectable ? "" + printDate.getDate() + "" : "" + printDate.getDate() + "")) + "
      " + (isMultiMonth ? "
      " + - ((numMonths[0] > 0 && col === numMonths[1]-1) ? "
      " : "") : ""); - group += calender; - } - html += group; - } - html += buttonPanel; - inst._keyEvent = false; - return html; + this.date.adjust( "M", -this.options.numberOfMonths ); }, - - /* Generate the month and year header. */ - _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate, - secondary, monthNames, monthNamesShort) { - - var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear, - changeMonth = this._get(inst, "changeMonth"), - changeYear = this._get(inst, "changeYear"), - showMonthAfterYear = this._get(inst, "showMonthAfterYear"), - html = "
      ", - monthHtml = ""; - - // month selection - if (secondary || !changeMonth) { - monthHtml += "" + monthNames[drawMonth] + ""; - } else { - inMinYear = (minDate && minDate.getFullYear() === drawYear); - inMaxYear = (maxDate && maxDate.getFullYear() === drawYear); - monthHtml += ""; + open: function( event ) { + if ( this.inline || this.isOpen ) { + return; } - if (!showMonthAfterYear) { - html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : ""); - } + // TODO explain this + this.date = $.date( this.element.val() ); + this.date.eachDay = this.options.eachDay; + this.date.select(); + this.refresh(); - // year selection - if ( !inst.yearshtml ) { - inst.yearshtml = ""; - if (secondary || !changeYear) { - html += "" + drawYear + ""; - } else { - // determine range of years to display - years = this._get(inst, "yearRange").split(":"); - thisYear = new Date().getFullYear(); - determineYear = function(value) { - var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) : - (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) : - parseInt(value, 10))); - return (isNaN(year) ? thisYear : year); - }; - year = determineYear(years[0]); - endYear = Math.max(year, determineYear(years[1] || "")); - year = (minDate ? Math.max(year, minDate.getFullYear()) : year); - endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear); - inst.yearshtml += ""; + var position = $.extend( {}, { + of: this.element + }, this.options.position ); - html += inst.yearshtml; - inst.yearshtml = null; - } - } + this.picker + .attr( "aria-hidden", "false" ) + .attr( "aria-expanded", "true" ) + .show() + .position( position ) + .hide(); - html += this._get(inst, "yearSuffix"); - if (showMonthAfterYear) { - html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml; - } - html += "
      "; // Close datepicker_header - return html; - }, + this._show( this.picker, this.options.show ); - /* Adjust one of the date sub-fields. */ - _adjustInstDate: function(inst, offset, period) { - var year = inst.drawYear + (period === "Y" ? offset : 0), - month = inst.drawMonth + (period === "M" ? offset : 0), - day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0), - date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day))); + // take trigger out of tab order to allow shift-tab to skip trigger + // TODO does this really make sense? related bug: tab-shift moves focus to last element on page + this.element.attr( "tabindex", -1 ); + this.isOpen = true; - inst.selectedDay = date.getDate(); - inst.drawMonth = inst.selectedMonth = date.getMonth(); - inst.drawYear = inst.selectedYear = date.getFullYear(); - if (period === "M" || period === "Y") { - this._notifyChange(inst); - } + this._trigger( "open", event ); }, - - /* Ensure a date is within any min/max bounds. */ - _restrictMinMax: function(inst, date) { - var minDate = this._getMinMaxDate(inst, "min"), - maxDate = this._getMinMaxDate(inst, "max"), - newDate = (minDate && date < minDate ? minDate : date); - return (maxDate && newDate > maxDate ? maxDate : newDate); - }, - - /* Notify change of month/year. */ - _notifyChange: function(inst) { - var onChange = this._get(inst, "onChangeMonthYear"); - if (onChange) { - onChange.apply((inst.input ? inst.input[0] : null), - [inst.selectedYear, inst.selectedMonth + 1, inst]); + close: function( event ) { + if ( this.inline ) { + return; } - }, - /* Determine the number of months to show. */ - _getNumberOfMonths: function(inst) { - var numMonths = this._get(inst, "numberOfMonths"); - return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths)); - }, + this._setHiddenPicker(); + this._hide( this.picker, this.options.hide ); - /* Determine the current maximum date - ensure no time components are set. */ - _getMinMaxDate: function(inst, minMax) { - return this._determineDate(inst, this._get(inst, minMax + "Date"), null); - }, + this.element.attr( "tabindex" , 0 ); - /* Find the number of days in a given month. */ - _getDaysInMonth: function(year, month) { - return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate(); + this.isOpen = false; + this._trigger( "close", event ); }, - - /* Find the day of the week of the first of a month. */ - _getFirstDayOfMonth: function(year, month) { - return new Date(year, month, 1).getDay(); + _setHiddenPicker: function() { + this.picker + .attr( "aria-hidden", "true" ) + .attr( "aria-expanded", "false" ); }, - - /* Determines if we should allow a "next/prev" month display change. */ - _canAdjustMonth: function(inst, offset, curYear, curMonth) { - var numMonths = this._getNumberOfMonths(inst), - date = this._daylightSavingAdjust(new Date(curYear, - curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1)); - - if (offset < 0) { - date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth())); + select: function( event, time ) { + this.date.setTime( time ).select(); + this.refresh(); + if ( !this.inline ) { + this.element.val( this.date.format() ); + this.close(); + this._focusTrigger(); } - return this._isInRange(inst, date); - }, - - /* Is the given date in the accepted range? */ - _isInRange: function(inst, date) { - var yearSplit, currentYear, - minDate = this._getMinMaxDate(inst, "min"), - maxDate = this._getMinMaxDate(inst, "max"), - minYear = null, - maxYear = null, - years = this._get(inst, "yearRange"); - if (years){ - yearSplit = years.split(":"); - currentYear = new Date().getFullYear(); - minYear = parseInt(yearSplit[0], 10); - maxYear = parseInt(yearSplit[1], 10); - if ( yearSplit[0].match(/[+\-].*/) ) { - minYear += currentYear; - } - if ( yearSplit[1].match(/[+\-].*/) ) { - maxYear += currentYear; - } - } - - return ((!minDate || date.getTime() >= minDate.getTime()) && - (!maxDate || date.getTime() <= maxDate.getTime()) && - (!minYear || date.getFullYear() >= minYear) && - (!maxYear || date.getFullYear() <= maxYear)); - }, - - /* Provide the configuration settings for formatting/parsing. */ - _getFormatConfig: function(inst) { - var shortYearCutoff = this._get(inst, "shortYearCutoff"); - shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff : - new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); - return {shortYearCutoff: shortYearCutoff, - dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"), - monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")}; + this._trigger( "select", event, { + // TODO replace with value option to initialise and read + date: this.date.format() + }); }, - - /* Format the given date for display. */ - _formatDate: function(inst, day, month, year) { - if (!day) { - inst.currentDay = inst.selectedDay; - inst.currentMonth = inst.selectedMonth; - inst.currentYear = inst.selectedYear; + _destroy: function() { + if ( !this.inline ) { + this.picker.remove(); + this.element + .removeAttr( "aria-haspopup" ) + .removeAttr( "aria-owns" ); } - var date = (day ? (typeof day === "object" ? day : - this._daylightSavingAdjust(new Date(year, month, day))) : - this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay))); - return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst)); + }, + widget: function() { + return this.picker; } }); -/* - * Bind hover events for datepicker elements. - * Done via delegate so the binding only occurs once in the lifetime of the parent div. - * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker. - */ -function datepicker_bindHover(dpDiv) { - var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a"; - return dpDiv.delegate(selector, "mouseout", function() { - $(this).removeClass("ui-state-hover"); - if (this.className.indexOf("ui-datepicker-prev") !== -1) { - $(this).removeClass("ui-datepicker-prev-hover"); - } - if (this.className.indexOf("ui-datepicker-next") !== -1) { - $(this).removeClass("ui-datepicker-next-hover"); - } - }) - .delegate(selector, "mouseover", function(){ - if (!$.datepicker._isDisabledDatepicker( datepicker_instActive.inline ? dpDiv.parent()[0] : datepicker_instActive.input[0])) { - $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"); - $(this).addClass("ui-state-hover"); - if (this.className.indexOf("ui-datepicker-prev") !== -1) { - $(this).addClass("ui-datepicker-prev-hover"); - } - if (this.className.indexOf("ui-datepicker-next") !== -1) { - $(this).addClass("ui-datepicker-next-hover"); - } - } - }); -} - -/* jQuery extend now ignores nulls! */ -function datepicker_extendRemove(target, props) { - $.extend(target, props); - for (var name in props) { - if (props[name] == null) { - target[name] = props[name]; - } - } - return target; -} - -/* Invoke the datepicker functionality. - @param options string - a command, optionally followed by additional parameters or - Object - settings for attaching new datepicker functionality - @return jQuery object */ -$.fn.datepicker = function(options){ - - /* Verify an empty collection wasn't passed - Fixes #6976 */ - if ( !this.length ) { - return this; - } - - /* Initialise the date picker. */ - if (!$.datepicker.initialized) { - $(document).mousedown($.datepicker._checkExternalClick); - $.datepicker.initialized = true; - } - - /* Append datepicker main container to body if not exist. */ - if ($("#"+$.datepicker._mainDivId).length === 0) { - $("body").append($.datepicker.dpDiv); - } - - var otherArgs = Array.prototype.slice.call(arguments, 1); - if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) { - return $.datepicker["_" + options + "Datepicker"]. - apply($.datepicker, [this[0]].concat(otherArgs)); - } - if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") { - return $.datepicker["_" + options + "Datepicker"]. - apply($.datepicker, [this[0]].concat(otherArgs)); - } - return this.each(function() { - typeof options === "string" ? - $.datepicker["_" + options + "Datepicker"]. - apply($.datepicker, [this].concat(otherArgs)) : - $.datepicker._attachDatepicker(this, options); - }); -}; - -$.datepicker = new Datepicker(); // singleton instance -$.datepicker.initialized = false; -$.datepicker.uuid = new Date().getTime(); -$.datepicker.version = "@VERSION"; - -return $.datepicker; - })); From f83580cd7db86da8c36a5816e9ebcdf1758ec797 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 30 Aug 2013 08:27:19 -0400 Subject: [PATCH 02/53] Datepicker: Update tests and demos for new structure --- Gruntfile.js | 2 +- demos/datepicker/alt-field.html | 11 +- demos/datepicker/animation.html | 18 +- demos/datepicker/buttonbar.html | 5 + demos/datepicker/date-formats.html | 14 +- demos/datepicker/date-range.html | 5 + demos/datepicker/datepicker-ar.js | 36 --- demos/datepicker/datepicker-fr.js | 38 ---- demos/datepicker/datepicker-he.js | 36 --- demos/datepicker/datepicker-zh-TW.js | 36 --- demos/datepicker/dropdown-month-year.html | 5 + demos/datepicker/icon-trigger.html | 23 +- demos/datepicker/inline.html | 5 + demos/datepicker/localization.html | 26 ++- demos/datepicker/min-max.html | 5 + demos/datepicker/multiple-calendars.html | 5 + demos/datepicker/other-months.html | 5 + demos/datepicker/show-week.html | 5 + tests/unit/date/date_core.js | 259 +++++++++++----------- tests/unit/datepicker/datepicker.html | 11 +- tests/unit/datepicker/datepicker_core.js | 10 - ui/datepicker.js | 15 +- 22 files changed, 252 insertions(+), 323 deletions(-) delete mode 100644 demos/datepicker/datepicker-ar.js delete mode 100644 demos/datepicker/datepicker-fr.js delete mode 100644 demos/datepicker/datepicker-he.js delete mode 100644 demos/datepicker/datepicker-zh-TW.js diff --git a/Gruntfile.js b/Gruntfile.js index 6a711962b82..b8610b6f641 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -174,7 +174,7 @@ grunt.initConfig({ }, qunit: { files: expandFiles( "tests/unit/" + component + "/*.html" ).filter(function( file ) { - return !( /(all|index|test)\.html$/ ).test( file ); + return !( /(all|index|test|datepicker)\.html$/ ).test( file ); }), options: { page: { diff --git a/demos/datepicker/alt-field.html b/demos/datepicker/alt-field.html index 7e3b50a6de9..4fd64bf6f19 100644 --- a/demos/datepicker/alt-field.html +++ b/demos/datepicker/alt-field.html @@ -5,15 +5,22 @@ jQuery UI Datepicker - Populate alternate field + + + + + diff --git a/demos/datepicker/animation.html b/demos/datepicker/animation.html index 990fd6262e3..5fda2046f52 100644 --- a/demos/datepicker/animation.html +++ b/demos/datepicker/animation.html @@ -5,6 +5,9 @@ jQuery UI Datepicker - Animations + + + @@ -14,13 +17,19 @@ + + @@ -31,16 +40,15 @@

      Animations:

      diff --git a/demos/datepicker/buttonbar.html b/demos/datepicker/buttonbar.html index 040ec1112ec..446bc9fe495 100644 --- a/demos/datepicker/buttonbar.html +++ b/demos/datepicker/buttonbar.html @@ -5,8 +5,13 @@ jQuery UI Datepicker - Display button bar + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/datepicker/inline.html b/demos/datepicker/inline.html index 5d120457b77..5d482919020 100644 --- a/demos/datepicker/inline.html +++ b/demos/datepicker/inline.html @@ -5,8 +5,13 @@ jQuery UI Datepicker - Display inline + + + + + + + + + + + + - - - - @@ -27,11 +31,9 @@

      Date:  

      diff --git a/demos/datepicker/min-max.html b/demos/datepicker/min-max.html index 4052c17852b..6dcc16a48bc 100644 --- a/demos/datepicker/min-max.html +++ b/demos/datepicker/min-max.html @@ -5,8 +5,13 @@ jQuery UI Datepicker - Restrict date range + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index 65b07e2f6ca..7b9dd2b9392 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -12,16 +12,6 @@ module( "datepicker: core", { TestHelpers.testJshint( "datepicker" ); -test("initialization - Reinitialization after body had been emptied.", function() { - expect( 1 ); - var bodyContent = $("body").children(), inp = $("#inp"); - $("#inp").datepicker(); - $("body").empty().append(inp); - $("#inp").datepicker(); - ok( $("#"+$.datepicker._mainDivId).length===1, "Datepicker container added" ); - $("body").empty().append(bodyContent); // Returning to initial state for later tests -}); - test( "widget method - empty collection", function() { expect( 1 ); $( "#nonExist" ).datepicker(); // should create nothing diff --git a/ui/datepicker.js b/ui/datepicker.js index d6d5b5e278e..43c40ccf69b 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -34,6 +34,7 @@ var idIncrement = 0, $.widget( "ui.datepicker", { options: { appendTo: null, + dateFormat: null, // TODO review eachDay: $.noop, numberOfMonths: 1, @@ -46,12 +47,13 @@ $.widget( "ui.datepicker", { hide: true, // callbacks + beforeOpen: null, close: null, open: null, select: null }, _create: function() { - this.date = $.date(); + this.date = $.date( null, this.options.dateFormat ); this.date.eachDay = this.options.eachDay; this.id = "ui-datepicker-" + idIncrement; idIncrement++; @@ -506,8 +508,8 @@ $.widget( "ui.datepicker", { this.element.focus(); }, // Refreshing the entire datepicker during interaction confuses screen readers, specifically - // because the grid heading is marked up as a live region and will often not update if it's - // destroyed and recreated instead of just having its text change. Additionally, interacting + // because the grid heading is marked up as a live region and will often not update if it's + // destroyed and recreated instead of just having its text change. Additionally, interacting // with the prev and next links would cause loss of focus issues because the links being // interacted with will disappear while focused. refresh: function() { @@ -524,7 +526,7 @@ $.widget( "ui.datepicker", { } }, _refreshMultiplePicker: function() { - for (var i = 0; i < this.options.numberOfMonths; i++ ) { + for ( var i = 0; i < this.options.numberOfMonths; i++ ) { $( ".ui-datepicker-title", this.picker ).eq( i ).html( this._buildTitle() ); $( ".ui-datepicker-calendar", this.picker ).eq( i ).html( this._buildGrid() ); this.date.adjust( "M", 1 ); @@ -536,9 +538,12 @@ $.widget( "ui.datepicker", { if ( this.inline || this.isOpen ) { return; } + if ( this._trigger( "beforeOpen", event ) === false ) { + return; + } // TODO explain this - this.date = $.date( this.element.val() ); + this.date = $.date( this.element.val(), this.options.dateFormat ); this.date.eachDay = this.options.eachDay; this.date.select(); this.refresh(); From 7a8de824872e5f16f72080b49367d78e769e7353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rn=20Zaefferer?= Date: Wed, 11 Sep 2013 18:19:27 +0200 Subject: [PATCH 03/53] Datepicker: Fix the `eachDay` option Use the `eachDay` option in the other-months demo. Fix handling of `extraClasses` property, split on space. --- demos/datepicker/other-months.html | 9 +++++++-- external/date.js | 5 +++-- ui/datepicker.js | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/demos/datepicker/other-months.html b/demos/datepicker/other-months.html index bad2b1977fa..bfb3f1af36f 100644 --- a/demos/datepicker/other-months.html +++ b/demos/datepicker/other-months.html @@ -17,8 +17,13 @@ diff --git a/external/date.js b/external/date.js index 23efeef708d..fef4dcaeffe 100644 --- a/external/date.js +++ b/external/date.js @@ -130,8 +130,9 @@ $.date = function( datestring, formatstring ) { today: today.equal( printDate ) }; day.render = day.selectable = !day.lead; - // TODO undefined in picker demos, fix it - // this.eachDay( day ); + if ( this.eachDay ) { + this.eachDay( day ); + } // TODO use adjust("D", 1)? printDate.setDate( printDate.getDate() + 1 ); } diff --git a/ui/datepicker.js b/ui/datepicker.js index 43c40ccf69b..1ca2a445875 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -471,7 +471,7 @@ $.widget( "ui.datepicker", { classes.push( "ui-state-highlight" ); } if ( day.extraClasses ) { - classes.push( day.extraClasses.split( "" ) ); + classes.push( day.extraClasses.split( " " ) ); } link = "" + @@ -490,7 +490,7 @@ $.widget( "ui.datepicker", { classes.push( "ui-state-highlight" ); } if ( day.extraClasses ) { - classes.push( day.extraClasses.split( "" ) ); + classes.push( day.extraClasses.split( " " ) ); } return "" + From a6e15865a3f5107e7e6507482e400f3d94233b1a Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Wed, 30 Oct 2013 09:11:06 -0400 Subject: [PATCH 04/53] Datepicker: Only apply the `ui-state-focus` class name to one cell This is specifically for multi month pickers. This makes the assumption that the keyboard is always interacting with the first month in a multi month calendar. The next step is to store which grid currently has focus and to base the focus logic off of that. --- ui/datepicker.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index 1ca2a445875..36d2e8ace89 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -161,8 +161,8 @@ $.widget( "ui.datepicker", { this.grid.attr("aria-activedescendant", newId); - activeCell.children("a").removeClass("ui-state-focus"); - newCell.children("a").addClass("ui-state-focus"); + this.grid.find( ".ui-state-focus" ).removeClass( "ui-state-focus" ); + newCell.children( "a" ).addClass( "ui-state-focus" ); } }, _createPicker: function() { @@ -533,6 +533,10 @@ $.widget( "ui.datepicker", { } this.date.adjust( "M", -this.options.numberOfMonths ); + + // TODO: This assumes focus is on the first grid. For multi pickers, the widget needs + // to maintain the currently focused grid and base queries like this off of it. + $( this.picker ).find( ".ui-state-focus" ).not( ":first" ).removeClass( "ui-state-focus" ); }, open: function( event ) { if ( this.inline || this.isOpen ) { From 3ec7d0ebe8abaf1831296ef075e5c1b4faf19495 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Sat, 2 Nov 2013 09:54:05 -0400 Subject: [PATCH 05/53] Datepicker: Clean up tests --- tests/unit/datepicker/datepicker.html | 6 +- tests/unit/datepicker/datepicker_core.js | 672 +++++++++--------- tests/unit/datepicker/datepicker_events.js | 56 +- tests/unit/datepicker/datepicker_methods.js | 181 ++--- tests/unit/datepicker/datepicker_options.js | 28 +- .../datepicker/datepicker_test_helpers.js | 9 +- 6 files changed, 443 insertions(+), 509 deletions(-) diff --git a/tests/unit/datepicker/datepicker.html b/tests/unit/datepicker/datepicker.html index 290aa735235..f564b32a35d 100644 --- a/tests/unit/datepicker/datepicker.html +++ b/tests/unit/datepicker/datepicker.html @@ -43,8 +43,10 @@
      -
      -

      + + + +
      diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index 7b9dd2b9392..8795b5a7e89 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -12,38 +12,27 @@ module( "datepicker: core", { TestHelpers.testJshint( "datepicker" ); -test( "widget method - empty collection", function() { - expect( 1 ); - $( "#nonExist" ).datepicker(); // should create nothing - ok( !$( "#ui-datepicker-div" ).length, "Non init on empty collection" ); -}); - -test("widget method", function() { - expect( 1 ); - var actual = $("#inp").datepicker().datepicker("widget")[0]; - deepEqual($("body > #ui-datepicker-div:last-child")[0], actual); -}); - asyncTest( "baseStructure", function() { - expect( 58 ); + expect( 42 ); var header, title, table, thead, week, panel, inl, child, inp = TestHelpers.datepicker.initNewInput(), - dp = $( "#ui-datepicker-div" ); + dp = inp.datepicker( "widget" ).find( ".ui-datepicker" ); function step1() { - TestHelpers.datepicker.onFocus( inp, function() { + inp.focus(); + setTimeout(function() { ok( dp.is( ":visible" ), "Structure - datepicker visible" ); ok( !dp.is( ".ui-datepicker-rtl" ), "Structure - not right-to-left" ); ok( !dp.is( ".ui-datepicker-multi" ), "Structure - not multi-month" ); - equal( dp.children().length, 2, "Structure - child count" ); + equal( dp.children().length, 3, "Structure - child count (header, calendar, buttonpane)" ); header = dp.children( ":first" ); ok( header.is( "div.ui-datepicker-header" ), "Structure - header division" ); equal( header.children().length, 3, "Structure - header child count" ); - ok( header.children( ":first" ).is( "a.ui-datepicker-prev" ) && header.children( ":first" ).html() !== "", "Structure - prev link" ); - ok( header.children( ":eq(1)" ).is( "a.ui-datepicker-next" ) && header.children( ":eq(1)" ).html() !== "", "Structure - next link" ); + ok( header.children( ":first" ).is( ".ui-datepicker-prev" ) && header.children( ":first" ).html() !== "", "Structure - prev link" ); + ok( header.children( ":eq(1)" ).is( ".ui-datepicker-next" ) && header.children( ":eq(1)" ).html() !== "", "Structure - next link" ); - title = header.children( ":last" ); + title = header.children( ":last" ).children( ":first" ); ok( title.is( "div.ui-datepicker-title" ) && title.html() !== "","Structure - title division" ); equal( title.children().length, 2, "Structure - title child count" ); ok( title.children( ":first" ).is( "span.ui-datepicker-month" ) && title.children( ":first" ).text() !== "", "Structure - month text" ); @@ -62,10 +51,11 @@ asyncTest( "baseStructure", function() { week = table.children( ":eq(1)" ).children( ":first" ); ok( week.is( "tr" ), "Structure - month table week row" ); equal( week.children().length, 7, "Structure - week child count" ); - ok( week.children( ":first" ).is( "td.ui-datepicker-week-end" ), "Structure - month table first day cell" ); - ok( week.children( ":last" ).is( "td.ui-datepicker-week-end" ), "Structure - month table second day cell" ); + // TODO: Preserve these class names or let the user use :first-child and :last-child? + // ok( week.children( ":first" ).is( "td.ui-datepicker-week-end" ), "Structure - month table first day cell" ); + // ok( week.children( ":last" ).is( "td.ui-datepicker-week-end" ), "Structure - month table second day cell" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); + inp.datepicker( "close" ).datepicker( "destroy" ); step2(); }); } @@ -77,10 +67,13 @@ asyncTest( "baseStructure", function() { changeYear: true, showButtonPanel: true }); - TestHelpers.datepicker.onFocus( inp, function() { + dp = inp.datepicker( "widget" ).find( ".ui-datepicker" ); + inp.focus(); + setTimeout(function() { title = dp.find( "div.ui-datepicker-title" ); - ok( title.children( ":first" ).is( "select.ui-datepicker-month" ), "Structure - month selector" ); - ok( title.children( ":last" ).is( "select.ui-datepicker-year" ), "Structure - year selector" ); + // TODO: Re-add tests when changeMonth and changeYear are re-implemented + //ok( title.children( ":first" ).is( "select.ui-datepicker-month" ), "Structure - month selector" ); + //ok( title.children( ":last" ).is( "select.ui-datepicker-year" ), "Structure - year selector" ); panel = dp.children( ":last" ); ok( panel.is( "div.ui-datepicker-buttonpane" ), "Structure - button panel division" ); @@ -88,7 +81,7 @@ asyncTest( "baseStructure", function() { ok( panel.children( ":first" ).is( "button.ui-datepicker-current" ), "Structure - today button" ); ok( panel.children( ":last" ).is( "button.ui-datepicker-close" ), "Structure - close button" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); + inp.datepicker( "close" ).datepicker( "destroy" ); step3(); }); } @@ -96,21 +89,25 @@ asyncTest( "baseStructure", function() { function step3() { // Multi-month 2 inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: 2 }); - TestHelpers.datepicker.onFocus( inp, function() { + dp = inp.datepicker( "widget" ).find( ".ui-datepicker" ); + inp.focus(); + setTimeout(function() { ok( dp.is( ".ui-datepicker-multi" ), "Structure multi [2] - multi-month" ); - equal( dp.children().length, 3, "Structure multi [2] - child count" ); + equal( dp.children().length, 4, "Structure multi [2] - child count" ); child = dp.children( ":first" ); - ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure multi [2] - first month division" ); + // TODO: Implement ui-datepicker-group-first class name + // ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure multi [2] - first month division" ); child = dp.children( ":eq(1)" ); - ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure multi [2] - second month division" ); + // TODO: Implement ui-datepicker-group-last class name + // ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure multi [2] - second month division" ); child = dp.children( ":eq(2)" ); ok( child.is( "div.ui-datepicker-row-break" ), "Structure multi [2] - row break" ); ok( dp.is( ".ui-datepicker-multi-2" ), "Structure multi [2] - multi-2" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); + inp.datepicker( "close" ).datepicker( "destroy" ); step4(); }); } @@ -118,11 +115,13 @@ asyncTest( "baseStructure", function() { function step4() { // Multi-month 3 inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: 3 }); - TestHelpers.datepicker.onFocus( inp, function() { + dp = inp.datepicker( "widget" ).find( ".ui-datepicker" ); + inp.focus(); + setTimeout(function() { ok( dp.is( ".ui-datepicker-multi-3" ), "Structure multi [3] - multi-3" ); ok( !dp.is( ".ui-datepicker-multi-2" ), "Structure multi [3] - Trac #6704" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); + inp.datepicker( "close" ).datepicker( "destroy" ); step5(); }); } @@ -130,7 +129,11 @@ asyncTest( "baseStructure", function() { function step5() { // Multi-month [2, 2] inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: [ 2, 2 ] }); - TestHelpers.datepicker.onFocus( inp, function() { + dp = inp.datepicker( "widget" ).find( ".ui-datepicker" ); + inp.focus(); + setTimeout(function() { + /* + TODO: Re-add after array form of the numberOfMonths option is implemented. ok( dp.is( ".ui-datepicker-multi" ), "Structure multi - multi-month" ); equal( dp.children().length, 6, "Structure multi [2,2] - child count" ); @@ -151,368 +154,335 @@ asyncTest( "baseStructure", function() { child = dp.children( ":eq(5)" ); ok( child.is( "div.ui-datepicker-row-break" ), "Structure multi [2,2] - row break" ); - - inp.datepicker( "hide" ).datepicker( "destroy" ); - - // Inline - inl = TestHelpers.datepicker.init( "#inl" ); - dp = inl.children(); - - ok( dp.is( ".ui-datepicker-inline" ), "Structure inline - main div" ); - ok( !dp.is( ".ui-datepicker-rtl" ), "Structure inline - not right-to-left" ); - ok( !dp.is( ".ui-datepicker-multi" ), "Structure inline - not multi-month" ); - equal( dp.children().length, 2, "Structure inline - child count" ); - - header = dp.children( ":first" ); - ok( header.is( "div.ui-datepicker-header" ), "Structure inline - header division" ); - equal( header.children().length, 3, "Structure inline - header child count" ); - - table = dp.children( ":eq(1)" ); - ok( table.is( "table.ui-datepicker-calendar" ), "Structure inline - month table" ); - ok( table.children( ":first" ).is( "thead" ), "Structure inline - month table thead" ); - ok( table.children( ":eq(1)" ).is( "tbody" ), "Structure inline - month table body" ); - - inl.datepicker( "destroy" ); - - // Inline multi-month - inl = TestHelpers.datepicker.init( "#inl", { numberOfMonths: 2 } ); - dp = inl.children(); - - ok( dp.is( ".ui-datepicker-inline" ) && dp.is( ".ui-datepicker-multi" ), "Structure inline multi - main div" ); - equal( dp.children().length, 3, "Structure inline multi - child count" ); - - child = dp.children( ":first" ); - ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure inline multi - first month division" ); - - child = dp.children( ":eq(1)" ); - ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure inline multi - second month division" ); - - child = dp.children( ":eq(2)" ); - ok( child.is( "div.ui-datepicker-row-break" ), "Structure inline multi - row break" ); - - inl.datepicker( "destroy" ); - start(); + */ + inp.datepicker( "close" ).datepicker( "destroy" ); + step6(); }); } - step1(); -}); + function step6() { + // Inline + inl = TestHelpers.datepicker.init( "#inline" ); + dp = inl.children(); -asyncTest( "customStructure", function() { - expect( 20 ); - var header, panel, title, thead, - inp = TestHelpers.datepicker.initNewInput( $.datepicker.regional.he ), - dp = $( "#ui-datepicker-div" ); + ok( dp.is( ".ui-datepicker-inline" ), "Structure inline - main div" ); + ok( !dp.is( ".ui-datepicker-rtl" ), "Structure inline - not right-to-left" ); + ok( !dp.is( ".ui-datepicker-multi" ), "Structure inline - not multi-month" ); + equal( dp.children().length, 3, "Structure inline - child count (header, calendar, buttonpane)" ); - function step1() { - inp.datepicker( "option", "showButtonPanel", true ); + header = dp.children( ":first" ); + ok( header.is( "div.ui-datepicker-header" ), "Structure inline - header division" ); + equal( header.children().length, 3, "Structure inline - header child count" ); - TestHelpers.datepicker.onFocus( inp, function() { - ok( dp.is( ".ui-datepicker-rtl" ), "Structure RTL - right-to-left" ); + table = dp.children( ":eq(1)" ); + ok( table.is( "table.ui-datepicker-calendar" ), "Structure inline - month table" ); + ok( table.children( ":first" ).is( "thead" ), "Structure inline - month table thead" ); + ok( table.children( ":eq(1)" ).is( "tbody" ), "Structure inline - month table body" ); - header = dp.children( ":first" ); - ok( header.is( "div.ui-datepicker-header" ), "Structure RTL - header division" ); - equal( header.children().length, 3, "Structure RTL - header child count" ); - ok( header.children( ":first" ).is( "a.ui-datepicker-next" ), "Structure RTL - prev link" ); - ok( header.children( ":eq(1)" ).is( "a.ui-datepicker-prev" ), "Structure RTL - next link" ); + inl.datepicker( "destroy" ); - panel = dp.children( ":last" ); - ok( panel.is( "div.ui-datepicker-buttonpane" ), "Structure RTL - button division" ); - equal( panel.children().length, 2, "Structure RTL - button panel child count" ); - ok( panel.children( ":first" ).is( "button.ui-datepicker-close" ), "Structure RTL - close button" ); - ok( panel.children( ":last" ).is( "button.ui-datepicker-current" ), "Structure RTL - today button" ); + // TODO: Calling destroy() on inline pickers currently does not work. + inl.empty(); - inp.datepicker( "hide" ).datepicker( "destroy" ); - step2(); - }); + step7(); } - // Hide prev/next - function step2() { - inp = TestHelpers.datepicker.initNewInput({ - hideIfNoPrevNext: true, - minDate: new Date( 2008, 2 - 1, 4 ), - maxDate: new Date( 2008, 2 - 1, 14 ) - }); - inp.val( "02/10/2008" ); + function step7() { + // Inline multi-month + inl = TestHelpers.datepicker.init( "#inline", { numberOfMonths: 2 } ); + dp = inl.datepicker( "widget" ).find( ".ui-datepicker" ); - TestHelpers.datepicker.onFocus( inp, function() { - header = dp.children( ":first" ); - ok( header.is( "div.ui-datepicker-header" ), "Structure hide prev/next - header division" ); - equal( header.children().length, 1, "Structure hide prev/next - links child count" ); - ok( header.children( ":first" ).is( "div.ui-datepicker-title" ), "Structure hide prev/next - title division" ); + ok( dp.is( ".ui-datepicker-inline" ) && dp.is( ".ui-datepicker-multi" ), "Structure inline multi - main div" ); + equal( dp.children().length, 4, "Structure inline multi - child count" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); - step3(); - }); - } + child = dp.children( ":first" ); + // TODO: Implement ui-datepicker-group-first class name + // ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure inline multi - first month division" ); - // Changeable Month with read-only year - function step3() { - inp = TestHelpers.datepicker.initNewInput({ changeMonth: true }); + child = dp.children( ":eq(1)" ); + // TODO: Implement ui-datepicker-group-last class name + // ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure inline multi - second month division" ); - TestHelpers.datepicker.onFocus( inp, function() { - title = dp.children( ":first" ).children( ":last" ); - equal( title.children().length, 2, "Structure changeable month - title child count" ); - ok( title.children( ":first" ).is( "select.ui-datepicker-month" ), "Structure changeable month - month selector" ); - ok( title.children( ":last" ).is( "span.ui-datepicker-year" ), "Structure changeable month - read-only year" ); + child = dp.children( ":eq(2)" ); + ok( child.is( "div.ui-datepicker-row-break" ), "Structure inline multi - row break" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); - step4(); - }); + inl.datepicker( "destroy" ); + start(); } - // Changeable year with read-only month - function step4() { - inp = TestHelpers.datepicker.initNewInput({ changeYear: true }); + step1(); +}); - TestHelpers.datepicker.onFocus( inp, function() { - title = dp.children( ":first" ).children( ":last" ); - equal( title.children().length, 2, "Structure changeable year - title child count" ); - ok( title.children( ":first" ).is( "span.ui-datepicker-month" ), "Structure changeable year - read-only month" ); - ok( title.children( ":last" ).is( "select.ui-datepicker-year" ), "Structure changeable year - year selector" ); +test( "Keyboard handling", function() { + // TODO: These tests all rely on having a method to retrieve a Date object. There + // is not only implemented yet so bail. + expect( 0 ); + return; - inp.datepicker( "hide" ).datepicker( "destroy" ); - step5(); - }); - } + expect( 24 ); + var inp = TestHelpers.datepicker.init( "#datepicker" ), + date = new Date(); - // Read-only first day of week - function step5() { - inp = TestHelpers.datepicker.initNewInput({ changeFirstDay: false }); + inp.val( "" ).datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.val(), date, "Keystroke enter" ); - TestHelpers.datepicker.onFocus( inp, function() { - thead = dp.find( ".ui-datepicker-calendar thead tr" ); - equal( thead.children().length, 7, "Structure read-only first day - thead child count" ); - equal( thead.find( "a" ).length, 0, "Structure read-only first day - thead links count" ); + inp.val( "02/04/2008" ).datepicker( "open" ) + .simulate("keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 4 ), + "Keystroke enter - preset" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); - start(); - }); - } + inp.val( "02/04/2008" ).datepicker( "open" ) + .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.HOME }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Keystroke ctrl+home" ); - // TODO: figure out why this setTimeout is needed in IE, - // it only is necessary when the previous baseStructure tests runs first - // Support: IE - setTimeout( step1 ); -}); + inp.val( "02/04/2008" ).datepicker( "open" ) + .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.END }); + ok( inp.datepicker( "getDate" ) == null, "Keystroke ctrl+end" ); -test("keystrokes", function() { - expect( 26 ); - var inp = TestHelpers.datepicker.init("#inp"), - date = new Date(); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke enter"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), - "Keystroke enter - preset"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.HOME}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+home"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.END}); - ok(inp.datepicker("getDate") == null, "Keystroke ctrl+end"); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); + inp.val( "" ).datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); ok(inp.datepicker("getDate") == null, "Keystroke esc"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), - "Keystroke esc - preset"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), - "Keystroke esc - abandoned"); + + inp.val( "02/04/2008" ).datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 4 ), + "Keystroke esc - preset" ); + + inp.val( "02/04/2008" ).datepicker( "open" ) + .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date(2008, 2 - 1, 4), + "Keystroke esc - abandoned" ); + // Moving by day or week - inp.val("").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.LEFT}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() - 1); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+left"); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.LEFT}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() + 1); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke left"); - inp.val("").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.RIGHT}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + inp.val( "" ).datepicker( "open" ) + .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.LEFT }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date.setDate( date.getDate() - 1 ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke ctrl+left" ); + + inp.val( "" ).datepicker( "open" ) + .simulate( "keydown", {keyCode: $.ui.keyCode.LEFT }). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER }); + date.setDate( date.getDate() + 1 ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke left") ; + + inp.val( "" ).datepicker( "open" ) + .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.RIGHT}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() + 1); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+right"); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.RIGHT}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke ctrl+right" ); + + inp.val( "" ).datepicker( "open" ) + .simulate( "keydown", {keyCode: $.ui.keyCode.RIGHT}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() - 1); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke right"); - inp.val("").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke right" ); + + inp.val( "" ).datepicker( "open" ) + .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.UP}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() - 7); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+up"); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke ctrl+up" ); + + inp.val( "" ).datepicker( "open" ) + .simulate( "keydown", {keyCode: $.ui.keyCode.UP}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() + 7); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke up"); - inp.val("").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke up" ); + + inp.val( "" ).datepicker( "open" ) + .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() + 7); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+down"); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() - 7); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Keystroke down"); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke ctrl+down" ); + + inp.val( "" ).datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date.setDate( date.getDate() - 7 ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke down" ); + // Moving by month or year - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.PAGE_UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 1 - 1, 4), - "Keystroke pgup"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 3 - 1, 4), - "Keystroke pgdn"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2007, 2 - 1, 4), - "Keystroke ctrl+pgup"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2009, 2 - 1, 4), - "Keystroke ctrl+pgdn"); + inp.val( "02/04/2008" ).datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 1 - 1, 4 ), + "Keystroke pgup" ); + + inp.val( "02/04/2008" ).datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 3 - 1, 4 ), + "Keystroke pgdn" ); + + inp.val( "02/04/2008" ).datepicker( "open" ) + .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2007, 2 - 1, 4 ), + "Keystroke ctrl+pgup" ); + + inp.val( "02/04/2008" ).datepicker( "open" ) + .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2009, 2 - 1, 4 ), + "Keystroke ctrl+pgdn" ); + // Check for moving to short months - inp.val("03/31/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.PAGE_UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 29), - "Keystroke pgup - Feb"); - inp.val("01/30/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 29), - "Keystroke pgdn - Feb"); - inp.val("02/29/2008").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2007, 2 - 1, 28), - "Keystroke ctrl+pgup - Feb"); - inp.val("02/29/2008").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2009, 2 - 1, 28), - "Keystroke ctrl+pgdn - Feb"); + inp.val( "03/31/2008" ).datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 29 ), + "Keystroke pgup - Feb" ); + + inp.val( "01/30/2008" ).datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 29 ), + "Keystroke pgdn - Feb" ); + + inp.val( "02/29/2008" ).datepicker( "open" ) + .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2007, 2 - 1, 28 ), + "Keystroke ctrl+pgup - Feb" ); + + inp.val( "02/29/2008" ).datepicker( "open" ) + .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2009, 2 - 1, 28 ), + "Keystroke ctrl+pgdn - Feb" ); + // Goto current - inp.datepicker("option", {gotoCurrent: true}). - datepicker("hide").val("02/04/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.HOME}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), - "Keystroke ctrl+home"); + inp.datepicker( "option", { gotoCurrent: true }) + .datepicker( "close" ).val( "02/04/2008" ).datepicker( "open" ) + .late( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) + .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.HOME }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 4 ), + "Keystroke ctrl+home" ); + // Change steps - inp.datepicker("option", {stepMonths: 2, gotoCurrent: false}). - datepicker("hide").val("02/04/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.PAGE_UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2007, 12 - 1, 4), - "Keystroke pgup step 2"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 4 - 1, 4), - "Keystroke pgdn step 2"); + inp.datepicker( "option", { stepMonths: 2, gotoCurrent: false }) + .datepicker( "close" ).val( "02/04/2008" ).datepicker( "open" ) + .late( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2007, 12 - 1, 4 ), + "Keystroke pgup step 2" ); + + inp.val( "02/04/2008" ).datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 4 - 1, 4 ), + "Keystroke pgdn step 2" ); }); -test("mouse", function() { +test( "mouse", function() { + // TODO: These tests use the old getDate() and setDate() methods. Re-activate these + // tests when those methods are available. + expect( 0 ); + return; + expect( 15 ); var inl, - inp = TestHelpers.datepicker.init("#inp"), - dp = $("#ui-datepicker-div"), + inp = TestHelpers.datepicker.init( "#datepicker" ), + dp = inp.datepicker( "widget" ).find( ".ui-datepicker" ), date = new Date(); - inp.val("").datepicker("show"); - $(".ui-datepicker-calendar tbody a:contains(10)", dp).simulate("click", {}); - date.setDate(10); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Mouse click"); - inp.val("02/04/2008").datepicker("show"); - $(".ui-datepicker-calendar tbody a:contains(12)", dp).simulate("click", {}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 12), - "Mouse click - preset"); - inp.val("02/04/2008").datepicker("show"); - inp.val("").datepicker("show"); - $("button.ui-datepicker-close", dp).simulate("click", {}); - ok(inp.datepicker("getDate") == null, "Mouse click - close"); - inp.val("02/04/2008").datepicker("show"); - $("button.ui-datepicker-close", dp).simulate("click", {}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), - "Mouse click - close + preset"); - inp.val("02/04/2008").datepicker("show"); - $("a.ui-datepicker-prev", dp).simulate("click", {}); - $("button.ui-datepicker-close", dp).simulate("click", {}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 4), - "Mouse click - abandoned"); + + inp.val( "" ).datepicker( "open" ); + $( ".ui-datepicker-calendar tbody a:contains(10)", dp ).simulate( "click", {} ); + date.setDate( 10 ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Mouse click" ); + + inp.val( "02/04/2008" ).datepicker( "open" ); + $( ".ui-datepicker-calendar tbody a:contains(12)", dp ).simulate( "click", {} ); + TestHelpers.datepicker.equalsDate( inp.datepicker("getDate"), new Date( 2008, 2 - 1, 12 ), + "Mouse click - preset") ; + + inp.val( "02/04/2008" ).datepicker( "open" ); + inp.val( "").datepicker( "open" ); + $( "button.ui-datepicker-close", dp ).simulate( "click", {} ); + ok( inp.datepicker( "getDate" ) == null, "Mouse click - close" ); + inp.val( "02/04/2008" ).datepicker( "open" ); + $( "button.ui-datepicker-close", dp ).simulate( "click", {} ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 4 ), + "Mouse click - close + preset" ); + + inp.val( "02/04/2008" ).datepicker( "open" ); + $( "a.ui-datepicker-prev", dp ).simulate( "click", {} ); + $( "button.ui-datepicker-close", dp ).simulate( "click", {} ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 4 ), + "Mouse click - abandoned" ); + // Current/previous/next - inp.val("02/04/2008").datepicker("option", {showButtonPanel: true}).datepicker("show"); - $(".ui-datepicker-current", dp).simulate("click", {}); - $(".ui-datepicker-calendar tbody a:contains(14)", dp).simulate("click", {}); - date.setDate(14); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Mouse click - current"); - inp.val("02/04/2008").datepicker("show"); - $(".ui-datepicker-prev", dp).simulate("click"); - $(".ui-datepicker-calendar tbody a:contains(16)", dp).simulate("click"); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 1 - 1, 16), - "Mouse click - previous"); - inp.val("02/04/2008").datepicker("show"); - $(".ui-datepicker-next", dp).simulate("click"); - $(".ui-datepicker-calendar tbody a:contains(18)", dp).simulate("click"); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 3 - 1, 18), - "Mouse click - next"); + inp.val( "02/04/2008" ).datepicker( "option", { showButtonPanel: true }).datepicker( "open" ); + $( ".ui-datepicker-current", dp ).simulate( "click", {} ); + $( ".ui-datepicker-calendar tbody a:contains(14)", dp ).simulate( "click", {} ); + date.setDate( 14 ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Mouse click - current" ); + + inp.val( "02/04/2008" ).datepicker( "open" ); + $( ".ui-datepicker-prev", dp ).simulate( "click" ); + $( ".ui-datepicker-calendar tbody a:contains(16)", dp ).simulate( "click" ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 1 - 1, 16 ), + "Mouse click - previous" ); + + inp.val( "02/04/2008" ).datepicker( "open" ); + $(".ui-datepicker-next", dp ).simulate( "click" ); + $(".ui-datepicker-calendar tbody a:contains(18)", dp ).simulate( "click" ); + TestHelpers.datepicker.equalsDate( inp.datepicker("getDate"), new Date( 2008, 3 - 1, 18 ), + "Mouse click - next" ); + // Previous/next with minimum/maximum - inp.datepicker("option", {minDate: new Date(2008, 2 - 1, 2), - maxDate: new Date(2008, 2 - 1, 26)}).val("02/04/2008").datepicker("show"); - $(".ui-datepicker-prev", dp).simulate("click"); - $(".ui-datepicker-calendar tbody a:contains(16)", dp).simulate("click"); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 16), - "Mouse click - previous + min/max"); - inp.val("02/04/2008").datepicker("show"); - $(".ui-datepicker-next", dp).simulate("click"); - $(".ui-datepicker-calendar tbody a:contains(18)", dp).simulate("click"); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 18), - "Mouse click - next + min/max"); + inp.datepicker("option", { + minDate: new Date( 2008, 2 - 1, 2 ), + maxDate: new Date( 2008, 2 - 1, 26 ) + }).val( "02/04/2008" ).datepicker( "open" ); + $( ".ui-datepicker-prev", dp ).simulate( "click" ); + $( ".ui-datepicker-calendar tbody a:contains(16)", dp ).simulate( "click" ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 16 ), + "Mouse click - previous + min/max" ); + + inp.val( "02/04/2008" ).datepicker( "open" ); + $( ".ui-datepicker-next", dp ).simulate( "click" ); + $( ".ui-datepicker-calendar tbody a:contains(18)", dp ).simulate( "click" ); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 18 ), + "Mouse click - next + min/max" ); + // Inline - inl = TestHelpers.datepicker.init("#inl"); - dp = $(".ui-datepicker-inline", inl); + inl = TestHelpers.datepicker.init( "#inline" ); + dp = $( ".ui-datepicker-inline", inl ); date = new Date(); - inl.datepicker("setDate", date); - $(".ui-datepicker-calendar tbody a:contains(10)", dp).simulate("click", {}); - date.setDate(10); - TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date, "Mouse click inline"); - inl.datepicker("option", {showButtonPanel: true}).datepicker("setDate", new Date(2008, 2 - 1, 4)); - $(".ui-datepicker-calendar tbody a:contains(12)", dp).simulate("click", {}); - TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), new Date(2008, 2 - 1, 12), "Mouse click inline - preset"); - inl.datepicker("option", {showButtonPanel: true}); - $(".ui-datepicker-current", dp).simulate("click", {}); - $(".ui-datepicker-calendar tbody a:contains(14)", dp).simulate("click", {}); - date.setDate(14); - TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date, "Mouse click inline - current"); - inl.datepicker("setDate", new Date(2008, 2 - 1, 4)); - $(".ui-datepicker-prev", dp).simulate("click"); - $(".ui-datepicker-calendar tbody a:contains(16)", dp).simulate("click"); - TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), new Date(2008, 1 - 1, 16), - "Mouse click inline - previous"); - inl.datepicker("setDate", new Date(2008, 2 - 1, 4)); - $(".ui-datepicker-next", dp).simulate("click"); - $(".ui-datepicker-calendar tbody a:contains(18)", dp).simulate("click"); - TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), new Date(2008, 3 - 1, 18), - "Mouse click inline - next"); + inl.datepicker( "setDate", date ); + $( ".ui-datepicker-calendar tbody a:contains(10)", dp ).simulate( "click", {} ); + date.setDate( 10 ); + TestHelpers.datepicker.equalsDate( inl.datepicker( "getDate" ), date, "Mouse click inline" ); + + inl.datepicker( "option", { showButtonPanel: true }) + .datepicker( "setDate", new Date( 2008, 2 - 1, 4 )); + $( ".ui-datepicker-calendar tbody a:contains(12)", dp ).simulate( "click", {} ); + TestHelpers.datepicker.equalsDate( inl.datepicker( "getDate" ), new Date( 2008, 2 - 1, 12 ), + "Mouse click inline - preset" ); + + inl.datepicker("option", { showButtonPanel: true }); + $( ".ui-datepicker-current", dp ).simulate( "click", {} ); + $( ".ui-datepicker-calendar tbody a:contains(14)", dp ).simulate( "click", {} ); + date.setDate( 14 ); + TestHelpers.datepicker.equalsDate( inl.datepicker( "getDate" ), date, "Mouse click inline - current" ); + + inl.datepicker( "setDate", new Date( 2008, 2 - 1, 4) ); + $( ".ui-datepicker-prev", dp ).simulate( "click" ); + $( ".ui-datepicker-calendar tbody a:contains(16)", dp ).simulate( "click" ); + TestHelpers.datepicker.equalsDate( inl.datepicker( "getDate" ), new Date( 2008, 1 - 1, 16 ), + "Mouse click inline - previous" ); + + inl.datepicker( "setDate", new Date( 2008, 2 - 1, 4) ); + $( ".ui-datepicker-next", dp ).simulate( "click" ); + $( ".ui-datepicker-calendar tbody a:contains(18)", dp ).simulate( "click" ); + TestHelpers.datepicker.equalsDate( inl.datepicker( "getDate" ), new Date( 2008, 3 - 1, 18 ), + "Mouse click inline - next" ); }); })(jQuery); diff --git a/tests/unit/datepicker/datepicker_events.js b/tests/unit/datepicker/datepicker_events.js index dfc42ccf911..63e2b0e9cc7 100644 --- a/tests/unit/datepicker/datepicker_events.js +++ b/tests/unit/datepicker/datepicker_events.js @@ -1,10 +1,29 @@ -/* - * datepicker_events.js - */ -(function($) { +(function( $ ) { + +module( "datepicker: events" ); + +test( "beforeOpen", function() { + expect( 0 ); +}); + +test( "close", function() { + expect( 0 ); +}); -module("datepicker: events"); +test( "open", function() { + expect( 0 ); +}); + +test( "select", function() { + expect( 0 ); +}); +// The implement of events is completely changing therefore these tests are no longer directly +// relevant. Leaving them around commented out so we can ensure the functionality is replicated. +// For example: +// TODO: In the old implementation the Enter key select's today's date when the has +// focus and is empty. Do we want to replicate this behavior in the rewrite? +/* var selectedThis = null, selectedDate = null, selectedInst = null; @@ -125,29 +144,6 @@ test("events", function() { inp.datepicker("show"); equal(selectedThis, inp2[0], "Callback close this"); }); +*/ -test("beforeShowDay-getDate", function() { - expect( 3 ); - var inp = TestHelpers.datepicker.init("#inp", {beforeShowDay: function() { inp.datepicker("getDate"); return [true, ""]; }}), - dp = $("#ui-datepicker-div"); - inp.val("01/01/2010").datepicker("show"); - // contains non-breaking space - equal($("div.ui-datepicker-title").text(), - // support: IE <9, jQuery <1.8 - // In IE7/8 with jQuery <1.8, encoded spaces behave in strange ways - $( "January 2010" ).text(), "Initial month"); - $("a.ui-datepicker-next", dp).click(); - $("a.ui-datepicker-next", dp).click(); - // contains non-breaking space - equal($("div.ui-datepicker-title").text(), - $( "March 2010" ).text(), "After next clicks"); - inp.datepicker("hide").datepicker("show"); - $("a.ui-datepicker-prev", dp).click(); - $("a.ui-datepicker-prev", dp).click(); - // contains non-breaking space - equal($("div.ui-datepicker-title").text(), - $( "November 2009" ).text(), "After prev clicks"); - inp.datepicker("hide"); -}); - -})(jQuery); +})( jQuery ); diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index e52e126d2e6..e00f886444e 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -1,125 +1,72 @@ -/* - * datepicker_methods.js - */ -(function($) { +(function( $ ) { -module("datepicker: methods"); +module( "datepicker: methods" ); -test("destroy", function() { - expect( 33 ); +test( "destroy", function() { + expect( 9 ); var inl, - inp = TestHelpers.datepicker.init("#inp"); - ok(inp.is(".hasDatepicker"), "Default - marker class set"); - ok($.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Default - instance present"); - ok(inp.next().is("#alt"), "Default - button absent"); - inp.datepicker("destroy"); - inp = $("#inp"); - ok(!inp.is(".hasDatepicker"), "Default - marker class cleared"); - ok(!$.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Default - instance absent"); - ok(inp.next().is("#alt"), "Default - button absent"); - // With button - inp= TestHelpers.datepicker.init("#inp", {showOn: "both"}); - ok(inp.is(".hasDatepicker"), "Button - marker class set"); - ok($.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Button - instance present"); - ok(inp.next().text() === "...", "Button - button added"); - inp.datepicker("destroy"); - inp = $("#inp"); - ok(!inp.is(".hasDatepicker"), "Button - marker class cleared"); - ok(!$.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Button - instance absent"); - ok(inp.next().is("#alt"), "Button - button removed"); - // With append text - inp = TestHelpers.datepicker.init("#inp", {appendText: "Testing"}); - ok(inp.is(".hasDatepicker"), "Append - marker class set"); - ok($.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Append - instance present"); - ok(inp.next().text() === "Testing", "Append - append text added"); - inp.datepicker("destroy"); - inp = $("#inp"); - ok(!inp.is(".hasDatepicker"), "Append - marker class cleared"); - ok(!$.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Append - instance absent"); - ok(inp.next().is("#alt"), "Append - append text removed"); - // With both - inp= TestHelpers.datepicker.init("#inp", {showOn: "both", buttonImageOnly: true, - buttonImage: "images/calendar.gif", appendText: "Testing"}); - ok(inp.is(".hasDatepicker"), "Both - marker class set"); - ok($.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Both - instance present"); - ok(inp.next()[0].nodeName.toLowerCase() === "img", "Both - button added"); - ok(inp.next().next().text() === "Testing", "Both - append text added"); - inp.datepicker("destroy"); - inp = $("#inp"); - ok(!inp.is(".hasDatepicker"), "Both - marker class cleared"); - ok(!$.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Both - instance absent"); - ok(inp.next().is("#alt"), "Both - button and append text absent"); - // Inline - inl = TestHelpers.datepicker.init("#inl"); - ok(inl.is(".hasDatepicker"), "Inline - marker class set"); - ok(inl.html() !== "", "Inline - datepicker present"); - ok($.data(inl[0], TestHelpers.datepicker.PROP_NAME), "Inline - instance present"); - ok(inl.next().length === 0 || inl.next().is("p"), "Inline - button absent"); - inl.datepicker("destroy"); - inl = $("#inl"); - ok(!inl.is(".hasDatepicker"), "Inline - marker class cleared"); - ok(inl.html() === "", "Inline - datepicker absent"); - ok(!$.data(inl[0], TestHelpers.datepicker.PROP_NAME), "Inline - instance absent"); - ok(inl.next().length === 0 || inl.next().is("p"), "Inline - button absent"); + inp = TestHelpers.datepicker.init( "#datepicker" ); + + ok( inp.datepicker( "instance" ), "instance created" ); + ok( inp.attr( "aria-owns" ), "aria-owns attribute added" ); + ok( inp.attr( "aria-haspopup" ), "aria-haspopup attribute added" ); + inp.datepicker( "destroy" ); + ok( !inp.datepicker( "instance" ), "instance removed" ); + ok( !inp.attr( "aria-owns" ), "aria-owns attribute removed" ); + ok( !inp.attr( "aria-haspopup" ), "aria-haspopup attribute removed" ); + + inl = TestHelpers.datepicker.init( "#inline" ); + ok( inl.datepicker( "instance" ), "instance created" ); + ok( inl.children().length > 0, "inline datepicker has children" ); + inl.datepicker( "destroy" ); + ok( !inl.datepicker( "instance" ), "instance removed" ); + // TODO: Destroying inline datepickers currently does not work. + // ok( inl.children().length === 0, "inline picker no longer has children" ); }); -test("enableDisable", function() { - expect( 33 ); - var inl, dp, - inp = TestHelpers.datepicker.init("#inp"); - ok(!inp.datepicker("isDisabled"), "Enable/disable - initially marked as enabled"); - ok(!inp[0].disabled, "Enable/disable - field initially enabled"); - inp.datepicker("disable"); - ok(inp.datepicker("isDisabled"), "Enable/disable - now marked as disabled"); - ok(inp[0].disabled, "Enable/disable - field now disabled"); - inp.datepicker("enable"); - ok(!inp.datepicker("isDisabled"), "Enable/disable - now marked as enabled"); - ok(!inp[0].disabled, "Enable/disable - field now enabled"); - inp.datepicker("destroy"); - // With a button - inp = TestHelpers.datepicker.init("#inp", {showOn: "button"}); - ok(!inp.datepicker("isDisabled"), "Enable/disable button - initially marked as enabled"); - ok(!inp[0].disabled, "Enable/disable button - field initially enabled"); - ok(!inp.next("button")[0].disabled, "Enable/disable button - button initially enabled"); - inp.datepicker("disable"); - ok(inp.datepicker("isDisabled"), "Enable/disable button - now marked as disabled"); - ok(inp[0].disabled, "Enable/disable button - field now disabled"); - ok(inp.next("button")[0].disabled, "Enable/disable button - button now disabled"); - inp.datepicker("enable"); - ok(!inp.datepicker("isDisabled"), "Enable/disable button - now marked as enabled"); - ok(!inp[0].disabled, "Enable/disable button - field now enabled"); - ok(!inp.next("button")[0].disabled, "Enable/disable button - button now enabled"); - inp.datepicker("destroy"); - // With an image button - inp = TestHelpers.datepicker.init("#inp", {showOn: "button", buttonImageOnly: true, - buttonImage: "images/calendar.gif"}); - ok(!inp.datepicker("isDisabled"), "Enable/disable image - initially marked as enabled"); - ok(!inp[0].disabled, "Enable/disable image - field initially enabled"); - ok(parseFloat(inp.next("img").css("opacity")) === 1, "Enable/disable image - image initially enabled"); - inp.datepicker("disable"); - ok(inp.datepicker("isDisabled"), "Enable/disable image - now marked as disabled"); - ok(inp[0].disabled, "Enable/disable image - field now disabled"); - ok(parseFloat(inp.next("img").css("opacity")) !== 1, "Enable/disable image - image now disabled"); - inp.datepicker("enable"); - ok(!inp.datepicker("isDisabled"), "Enable/disable image - now marked as enabled"); - ok(!inp[0].disabled, "Enable/disable image - field now enabled"); - ok(parseFloat(inp.next("img").css("opacity")) === 1, "Enable/disable image - image now enabled"); - inp.datepicker("destroy"); +test( "enable / disable", function() { + expect( 6 ); + var inl, + inp = TestHelpers.datepicker.init( "#datepicker" ), + dp = inp.datepicker( "widget" ); + + ok( !inp.datepicker( "option", "disabled" ), "initially enabled" ); + ok( !dp.hasClass( "ui-datepicker-disabled" ), "does not have disabled class name" ); + + inp.datepicker( "disable" ); + ok( inp.datepicker( "option", "disabled" ), "disabled option is set" ); + ok( dp.hasClass( "ui-datepicker-disabled" ), "datepicker has disabled class name" ); + + inp.datepicker( "enable" ); + ok( !inp.datepicker( "option", "disabled" ), "enabled after enable() call" ); + ok( !dp.hasClass( "ui-datepicker-disabled" ), "no longer has disabled class name" ); + // Inline - inl = TestHelpers.datepicker.init("#inl", {changeYear: true}); - dp = $(".ui-datepicker-inline", inl); - ok(!inl.datepicker("isDisabled"), "Enable/disable inline - initially marked as enabled"); - ok(!dp.children().is(".ui-state-disabled"), "Enable/disable inline - not visually disabled initially"); - ok(!dp.find("select").prop("disabled"), "Enable/disable inline - form element enabled initially"); - inl.datepicker("disable"); - ok(inl.datepicker("isDisabled"), "Enable/disable inline - now marked as disabled"); - ok(dp.children().is(".ui-state-disabled"), "Enable/disable inline - visually disabled"); - ok(dp.find("select").prop("disabled"), "Enable/disable inline - form element disabled"); - inl.datepicker("enable"); - ok(!inl.datepicker("isDisabled"), "Enable/disable inline - now marked as enabled"); - ok(!dp.children().is(".ui-state-disabled"), "Enable/disable inline - not visiually disabled"); - ok(!dp.find("select").prop("disabled"), "Enable/disable inline - form element enabled"); - inl.datepicker("destroy"); + inl = TestHelpers.datepicker.init( "#inline" ); + dp = inl.datepicker( "instance" ); + + // TODO: Disabling inline pickers does not work. + // TODO: When changeMonth and changeYear options are implemented ensure their dropdowns + // are properly disabled when in an inline picker. +}); + +test( "widget", function() { + expect( 1 ); + var actual = $( "#datepicker" ).datepicker().datepicker( "widget" ); + deepEqual( $("body > .ui-front" )[ 0 ], actual[ 0 ] ); + actual.remove(); +}); + +test( "close", function() { + expect( 0 ); +}); + +test( "open", function() { + expect( 0 ); +}); + +test( "value", function() { + expect( 0 ); }); -})(jQuery); +})( jQuery ); diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index bcb2d28dde5..4e50e634357 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -1,11 +1,28 @@ -/* - * datepicker_options.js - */ +(function( $ ) { + +module( "datepicker: options" ); + +test( "dateFormat", function() { + expect( 0 ); +}); -(function($) { +test( "eachDay", function() { + expect( 0 ); +}); -module("datepicker: options"); +test( "numberOfMonths", function() { + expect( 0 ); +}); +test( "position", function() { + expect( 0 ); +}); + +test( "showWeek", function() { + expect( 0 ); +}); + +/* test("setDefaults", function() { expect( 3 ); TestHelpers.datepicker.init("#inp"); @@ -1119,5 +1136,6 @@ test( "Ticket 7602: Stop datepicker from appearing with beforeShow event handler equal( dp.css( "display" ), "none","beforeShow returns false" ); inp.datepicker( "destroy" ); }); +*/ })(jQuery); diff --git a/tests/unit/datepicker/datepicker_test_helpers.js b/tests/unit/datepicker/datepicker_test_helpers.js index 34b41bbc6a5..060c6bf9505 100644 --- a/tests/unit/datepicker/datepicker_test_helpers.js +++ b/tests/unit/datepicker/datepicker_test_helpers.js @@ -15,12 +15,13 @@ TestHelpers.datepicker = { equal(d1.toString(), d2.toString(), message); }, init: function( id, options ) { - $.datepicker.setDefaults( $.datepicker.regional[ "" ] ); - return $( id ).datepicker( $.extend( { showAnim: "" }, options || {} ) ); + options = $.extend( { show: false }, options || {} ); + return $( id ).datepicker( options ); }, initNewInput: function( options ) { - var id = $( "" ).appendTo( "#qunit-fixture" ); - return TestHelpers.datepicker.init( id, options ); + options = $.extend( { show: false }, options || {} ); + return $( "" ).datepicker( options ) + .appendTo( "#qunit-fixture" ); }, onFocus: TestHelpers.onFocus, PROP_NAME: "datepicker" From 059f27795d020fc31a9608e2f1a769df2c5a70f7 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 15 Nov 2013 08:42:20 -0500 Subject: [PATCH 06/53] Datepicker: Support changing the `appendTo` option after init --- tests/unit/datepicker/datepicker_options.js | 38 +++++++++++++++++++++ ui/datepicker.js | 7 ++++ 2 files changed, 45 insertions(+) diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index 4e50e634357..f2a06a98f3b 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -2,6 +2,44 @@ module( "datepicker: options" ); +test( "appendTo", function() { + expect( 6 ); + var container, + detached = $( "
      " ), + input = $( "#datepicker" ); + + input.datepicker(); + container = input.datepicker( "widget" ).parent()[ 0 ]; + equal( container, document.body, "defaults to body" ); + input.datepicker( "destroy" ); + + input.datepicker({ appendTo: "#qunit-fixture" }); + container = input.datepicker( "widget" ).parent()[ 0 ]; + equal( container, $( "#qunit-fixture" )[ 0 ], "child of specified element" ); + input.datepicker( "destroy" ); + + input.datepicker({ appendTo: "#does-not-exist" }); + container = input.datepicker( "widget" ).parent()[ 0 ]; + equal( container, document.body, "set to body if element does not exist" ); + input.datepicker( "destroy" ); + + input.datepicker() + .datepicker( "option", "appendTo", "#qunit-fixture" ); + container = input.datepicker( "widget" ).parent()[ 0 ]; + equal( container, $( "#qunit-fixture" )[ 0 ], "modified after init" ); + input.datepicker( "destroy" ); + + input.datepicker({ appendTo: detached }); + container = input.datepicker( "widget" ).parent()[ 0 ]; + equal( container, detached[ 0 ], "detached jQuery object" ); + input.datepicker( "destroy" ); + + input.datepicker({ appendTo: detached[ 0 ] }); + container = input.datepicker( "widget" ).parent()[ 0 ]; + equal( container, detached[ 0 ], "detached DOM element" ); + input.datepicker( "destroy" ); +}); + test( "dateFormat", function() { expect( 0 ); }); diff --git a/ui/datepicker.js b/ui/datepicker.js index 36d2e8ace89..5480ae20337 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -613,6 +613,13 @@ $.widget( "ui.datepicker", { }, widget: function() { return this.picker; + }, + _setOption: function( key, value ) { + this._super( key, value ); + + if ( key === "appendTo" ) { + this.picker.appendTo( this._appendTo() ); + } } }); From 0f66c9ad3406aff064e2242f3ff49f442dd4709b Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 15 Nov 2013 09:17:13 -0500 Subject: [PATCH 07/53] Datepicker: Various changes for the `showWeek` option * Re-add `ui-datepicker-week-col` class name currently used. * Add test suite. * Support changing option after initialization. --- tests/unit/datepicker/datepicker_options.js | 24 ++++++++++++++++++++- ui/datepicker.js | 8 +++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index f2a06a98f3b..ab5cd5ed790 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -57,7 +57,29 @@ test( "position", function() { }); test( "showWeek", function() { - expect( 0 ); + expect( 7 ); + var input = $( "#datepicker" ).datepicker(), + container = input.datepicker( "widget" ); + + equal( container.find( "thead th" ).length, 7, "just 7 days, no column cell" ); + equal( container.find( ".ui-datepicker-week-col" ).length, 0, + "no week column cells present" ); + input.datepicker( "destroy" ); + + input = $( "#datepicker" ).datepicker({ showWeek: true }); + container = input.datepicker( "widget" ); + equal( container.find( "thead th" ).length, 8, "7 days + a column cell" ); + ok( container.find( "thead th:first" ).is( ".ui-datepicker-week-col" ), + "first cell should have ui-datepicker-week-col class name" ); + equal( container.find( ".ui-datepicker-week-col" ).length, + container.find( "tr" ).length, "one week cell for each week" ); + input.datepicker( "destroy" ); + + input = $( "#datepicker" ).datepicker(); + container = input.datepicker( "widget" ); + equal( container.find( "thead th" ).length, 7, "no week column" ); + input.datepicker( "option", "showWeek", true ); + equal( container.find( "thead th" ).length, 8, "supports changing option after init" ); }); /* diff --git a/ui/datepicker.js b/ui/datepicker.js index 5480ae20337..d8f0a2c1e67 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -402,7 +402,7 @@ $.widget( "ui.datepicker", { labels = Globalize.localize( "datepicker" ); if ( this.options.showWeek ) { - cells += "" + labels.weekHeader + ""; + cells += "" + labels.weekHeader + ""; } for ( i; i < this.date.weekdays().length; i++ ) { cells += this._buildGridHeaderCell( this.date.weekdays()[i] ); @@ -431,7 +431,7 @@ $.widget( "ui.datepicker", { i = 0; if ( this.options.showWeek ) { - cells += "" + week.number + ""; + cells += "" + week.number + ""; } for ( i; i < week.days.length; i++ ) { cells += this._buildDayCell( week.days[i] ); @@ -620,6 +620,10 @@ $.widget( "ui.datepicker", { if ( key === "appendTo" ) { this.picker.appendTo( this._appendTo() ); } + + if ( key === "showWeek" ) { + this.refresh(); + } } }); From 0b5703e4d424a394f0b513b1cb65dbd4e8a7ab68 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Mon, 18 Nov 2013 09:23:15 -0500 Subject: [PATCH 08/53] Datepicker: Support `position` option changes after init --- tests/unit/datepicker/datepicker_options.js | 28 +++++++++++++++++++-- ui/datepicker.js | 15 +++++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index ab5cd5ed790..b7d9c82d077 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -52,8 +52,32 @@ test( "numberOfMonths", function() { expect( 0 ); }); -test( "position", function() { - expect( 0 ); +asyncTest( "position", function() { + expect( 3 ); + var input = $( "" ).datepicker().appendTo( "body" ).css({ + position: "absolute", + top: 0, + left: 0 + }), + container = input.datepicker( "widget" ); + + input.datepicker( "open" ); + setTimeout(function() { + closeEnough( input.offset().left, container.offset().left, 1, "left sides line up by default" ); + closeEnough( container.offset().top, input.offset().top + input.outerHeight(), 1, + "datepicker directly under input by default" ); + + // Change the position option using option() + input.datepicker( "option", "position", { + my: "left top", + at: "right bottom" + }); + closeEnough( container.offset().left, input.offset().left + input.outerWidth(), 1, + "datepicker on right hand side of input after position change" ); + + input.remove(); + start(); + }); }); test( "showWeek", function() { diff --git a/ui/datepicker.js b/ui/datepicker.js index d8f0a2c1e67..1b56f71ccf5 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -552,15 +552,11 @@ $.widget( "ui.datepicker", { this.date.select(); this.refresh(); - var position = $.extend( {}, { - of: this.element - }, this.options.position ); - this.picker .attr( "aria-hidden", "false" ) .attr( "aria-expanded", "true" ) .show() - .position( position ) + .position( this._buildPosition() ) .hide(); this._show( this.picker, this.options.show ); @@ -590,6 +586,11 @@ $.widget( "ui.datepicker", { .attr( "aria-hidden", "true" ) .attr( "aria-expanded", "false" ); }, + _buildPosition: function() { + return $.extend( {}, { + of: this.element + }, this.options.position ); + }, select: function( event, time ) { this.date.setTime( time ).select(); this.refresh(); @@ -624,6 +625,10 @@ $.widget( "ui.datepicker", { if ( key === "showWeek" ) { this.refresh(); } + + if ( key === "position" ) { + this.picker.position( this._buildPosition() ); + } } }); From 33c48bd2f48b85f255beada347519c2cec189c96 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Tue, 19 Nov 2013 08:35:19 -0500 Subject: [PATCH 09/53] Datepicker: Support changing `eachDay` after initialization --- tests/unit/datepicker/datepicker_options.js | 38 ++++++++++++++++++++- ui/datepicker.js | 5 +++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index b7d9c82d077..da0d1412d66 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -45,7 +45,43 @@ test( "dateFormat", function() { }); test( "eachDay", function() { - expect( 0 ); + expect( 5 ); + var timestamp, + input = $( "#datepicker" ).datepicker(), + picker = input.datepicker( "widget" ); + firstCell = picker.find( "td[id]:first" ); + + equal( firstCell.find( "a" ).length, 1, "days are selectable by default" ); + timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 ); + equal( new Date( timestamp ).getDate(), 1, "first available day is the 1st by default" ); + + // Do not render the 1st of the month + input.datepicker( "option", "eachDay", function( day ) { + if ( day.date === 1 ) { + day.render = false; + } + }); + firstCell = picker.find( "td[id]:first" ); + timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 ); + equal( new Date( timestamp ).getDate(), 2, "first available day is the 2nd" ); + + // Display the 1st of the month but make it not selectable. + input.datepicker( "option", "eachDay", function( day ) { + if ( day.date === 1 ) { + day.selectable = false; + } + }); + firstCell = picker.find( "td[id]:first" ); + equal( firstCell.find( "a" ).length, 0, "the 1st is not selectable" ); + + input.datepicker( "option", "eachDay", function( day ) { + if ( day.date === 1 ) { + day.extraClasses = "ui-custom"; + } + }); + ok( picker.find( "td[id]:first a" ).hasClass( "ui-custom" ), "extraClasses applied" ); + + input.datepicker( "destroy" ); }); test( "numberOfMonths", function() { diff --git a/ui/datepicker.js b/ui/datepicker.js index 1b56f71ccf5..1b9f64c16f1 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -622,6 +622,11 @@ $.widget( "ui.datepicker", { this.picker.appendTo( this._appendTo() ); } + if ( key === "eachDay" ) { + this.date.eachDay = this.options.eachDay; + this.refresh(); + } + if ( key === "showWeek" ) { this.refresh(); } From acf329dfd6b00031251563c3f402c9a865644910 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Wed, 20 Nov 2013 08:37:24 -0500 Subject: [PATCH 10/53] Datepicker: Allow `dateFormat` to be changed after init --- tests/unit/datepicker/datepicker_options.js | 14 +++++++++++++- ui/datepicker.js | 7 +++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index da0d1412d66..9a93d63f30b 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -41,7 +41,19 @@ test( "appendTo", function() { }); test( "dateFormat", function() { - expect( 0 ); + expect( 2 ); + var input = $( "#datepicker" ).val( "1/1/2014" ).datepicker(), + picker = input.datepicker( "widget" ), + firstDayLink = picker.find( "td[id]:first a" ); + + input.datepicker( "open" ); + firstDayLink.trigger( "mousedown" ); + equal( input.val(), "1/1/2014", "default formatting" ); + + input.datepicker( "option", "dateFormat", "D" ); + equal( input.val(), "Wednesday, January 01, 2014", "updated formatting" ); + + input.datepicker( "destroy" ); }); test( "eachDay", function() { diff --git a/ui/datepicker.js b/ui/datepicker.js index 1b9f64c16f1..f81c5fa8779 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -627,6 +627,13 @@ $.widget( "ui.datepicker", { this.refresh(); } + if ( key === "dateFormat" ) { + this.date.setFormat( this.options.dateFormat ); + if ( !this.inline ) { + this.element.val( this.date.format() ); + } + } + if ( key === "showWeek" ) { this.refresh(); } From 6458bab2ad02b2f718b1baa76078168c901dc202 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Wed, 20 Nov 2013 08:53:02 -0500 Subject: [PATCH 11/53] Datepicker: Add test to ensure the ``'s value is preselected --- tests/unit/datepicker/datepicker_core.js | 29 +++++++++++++++--------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index 8795b5a7e89..cd1a64939b2 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -1,17 +1,24 @@ -/* - * datepicker_core.js - */ +(function( $ ) { -(function($) { - -module( "datepicker: core", { - setup: function() { - $( "body" ).focus(); - } -}); +module( "datepicker: core" ); TestHelpers.testJshint( "datepicker" ); +test( "input's value determines starting date", function() { + expect( 3 ); + + var input = $( "#datepicker" ).val( "1/1/2014" ).datepicker(), + picker = input.datepicker( "widget" ); + + input.datepicker( "open" ); + + equal( picker.find( ".ui-datepicker-month" ).html(), "January", "correct month displayed" ); + equal( picker.find( ".ui-datepicker-year" ).html(), "2014", "correct year displayed" ); + equal( picker.find( ".ui-state-focus" ).html(), "1", "correct day highlighted" ); + + input.val( "" ).datepicker( "destroy" ); +}); + asyncTest( "baseStructure", function() { expect( 42 ); var header, title, table, thead, week, panel, inl, child, @@ -485,4 +492,4 @@ test( "mouse", function() { "Mouse click inline - next" ); }); -})(jQuery); +})( jQuery ); From c8c44d4bd1bdf3adad5981a692a7e75fa5a56706 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 22 Nov 2013 08:55:04 -0500 Subject: [PATCH 12/53] Datepicker: Support destroying inline datepickers --- tests/unit/datepicker/datepicker_core.js | 3 -- tests/unit/datepicker/datepicker_methods.js | 32 ++++++++++----------- ui/datepicker.js | 4 ++- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index cd1a64939b2..1e3ec1fb8e5 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -188,9 +188,6 @@ asyncTest( "baseStructure", function() { inl.datepicker( "destroy" ); - // TODO: Calling destroy() on inline pickers currently does not work. - inl.empty(); - step7(); } diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index e00f886444e..e204b804544 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -3,25 +3,23 @@ module( "datepicker: methods" ); test( "destroy", function() { - expect( 9 ); - var inl, - inp = TestHelpers.datepicker.init( "#datepicker" ); + expect( 10 ); + var input = $( "#datepicker" ).datepicker(), + inline = $( "#inline" ).datepicker(); - ok( inp.datepicker( "instance" ), "instance created" ); - ok( inp.attr( "aria-owns" ), "aria-owns attribute added" ); - ok( inp.attr( "aria-haspopup" ), "aria-haspopup attribute added" ); - inp.datepicker( "destroy" ); - ok( !inp.datepicker( "instance" ), "instance removed" ); - ok( !inp.attr( "aria-owns" ), "aria-owns attribute removed" ); - ok( !inp.attr( "aria-haspopup" ), "aria-haspopup attribute removed" ); + ok( input.datepicker( "instance" ), "instance created" ); + ok( input.attr( "aria-owns" ), "aria-owns attribute added" ); + ok( input.attr( "aria-haspopup" ), "aria-haspopup attribute added" ); + input.datepicker( "destroy" ); + ok( !input.datepicker( "instance" ), "instance removed" ); + ok( !input.attr( "aria-owns" ), "aria-owns attribute removed" ); + ok( !input.attr( "aria-haspopup" ), "aria-haspopup attribute removed" ); - inl = TestHelpers.datepicker.init( "#inline" ); - ok( inl.datepicker( "instance" ), "instance created" ); - ok( inl.children().length > 0, "inline datepicker has children" ); - inl.datepicker( "destroy" ); - ok( !inl.datepicker( "instance" ), "instance removed" ); - // TODO: Destroying inline datepickers currently does not work. - // ok( inl.children().length === 0, "inline picker no longer has children" ); + ok( inline.datepicker( "instance" ), "instance created" ); + ok( inline.children().length > 0, "inline datepicker has children" ); + inline.datepicker( "destroy" ); + ok( !inline.datepicker( "instance" ), "instance removed" ); + ok( inline.children().length === 0, "inline picker no longer has children" ); }); test( "enable / disable", function() { diff --git a/ui/datepicker.js b/ui/datepicker.js index f81c5fa8779..13fa173dd70 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -605,7 +605,9 @@ $.widget( "ui.datepicker", { }); }, _destroy: function() { - if ( !this.inline ) { + if ( this.inline ) { + this.picker.empty(); + } else { this.picker.remove(); this.element .removeAttr( "aria-haspopup" ) From ae29574aec8aa28b76e4db28060212f077bc3c54 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Thu, 21 Nov 2013 09:03:08 -0500 Subject: [PATCH 13/53] Datepicker: Add `value()` and `valueAsDate()` methods --- tests/unit/datepicker/datepicker_methods.js | 46 ++++++++++++++++++++- ui/datepicker.js | 21 ++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index e204b804544..dcd6dc2c7aa 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -64,7 +64,51 @@ test( "open", function() { }); test( "value", function() { - expect( 0 ); + expect( 5 ); + var input = $( "#datepicker" ).datepicker(), + picker = input.datepicker( "widget" ), + inline = $( "#inline" ).datepicker(); + + input.datepicker( "value", "1/1/2014" ); + equal( input.val(), "1/1/2014", "input's value set" ); + ok( picker.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), + "first day marked as selected" ); + equal( input.datepicker( "value" ), "1/1/2014", "getter" ); + + inline.datepicker( "value", "1/1/2014" ); + ok( inline.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), + "first day marked as selected" ); + equal( inline.datepicker( "value" ), "1/1/2014", "getter" ); + + // TODO: Handle for invalid values. + + input.datepicker( "destroy" ); + inline.datepicker( "destroy" ); +}); + +test( "valueAsDate", function() { + expect( 5 ); + var input = $( "#datepicker" ).datepicker(), + picker = input.datepicker( "widget" ), + inline = $( "#inline" ).datepicker(); + + input.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); + equal( input.val(), "1/1/2014", "input's value set" ); + ok( picker.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), + "first day marked as selected" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), + new Date( 2014, 0, 1 ), "getter" ); + + inline.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); + ok( inline.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), + "first day marked as selected" ); + TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), + new Date( 2014, 0, 1 ), "getter" ); + + // TODO: Handle for invalid values. + + input.datepicker( "destroy" ); + inline.datepicker( "destroy" ); }); })( jQuery ); diff --git a/ui/datepicker.js b/ui/datepicker.js index 13fa173dd70..cd30e5c71fc 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -604,6 +604,27 @@ $.widget( "ui.datepicker", { date: this.date.format() }); }, + _value: function( value ) { + this.date.setTime( value ).select(); + if ( !this.inline ) { + this.element.val( this.date.format() ); + } + this.refresh(); + }, + value: function( value ) { + if ( arguments.length ) { + this._value( value ); + } else { + return this.date.format(); + } + }, + valueAsDate: function( value ) { + if ( arguments.length ) { + this._value( value ); + } else { + return this.date.date(); + } + }, _destroy: function() { if ( this.inline ) { this.picker.empty(); From e143e5bf049effb484875b2506abb01c9013ce94 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 22 Nov 2013 09:30:11 -0500 Subject: [PATCH 14/53] Datepicker: Fix key handling implementation and tests --- tests/unit/datepicker/datepicker_core.js | 210 +++++++++++++++-------- ui/datepicker.js | 24 +++ 2 files changed, 158 insertions(+), 76 deletions(-) diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index 1e3ec1fb8e5..e32a0bb86a5 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -218,170 +218,228 @@ asyncTest( "baseStructure", function() { }); test( "Keyboard handling", function() { - // TODO: These tests all rely on having a method to retrieve a Date object. There - // is not only implemented yet so bail. - expect( 0 ); - return; - - expect( 24 ); - var inp = TestHelpers.datepicker.init( "#datepicker" ), + expect( 8 ); + var input = $( "#datepicker" ).datepicker(), + instance = input.datepicker( "instance" ), date = new Date(); - inp.val( "" ).datepicker( "open" ) + input.datepicker( "open" ) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.val(), date, "Keystroke enter" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke enter" ); - inp.val( "02/04/2008" ).datepicker( "open" ) + // Enter = Select today's date by default + input.val( "1/1/2014" ).datepicker( "open" ) .simulate("keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 4 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Keystroke enter - preset" ); - inp.val( "02/04/2008" ).datepicker( "open" ) + // Control + Home = Change the calendar to the current month + input.val( "1/1/2014" ).datepicker( "open" ) .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.HOME }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Keystroke ctrl+home" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke ctrl+home" ); - inp.val( "02/04/2008" ).datepicker( "open" ) + // Control + End = Close the calendar and clear the input + input.val( "1/1/2014" ).datepicker( "open" ) .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.END }); - ok( inp.datepicker( "getDate" ) == null, "Keystroke ctrl+end" ); + equal( input.val(), "", "Keystroke ctrl+end" ); - inp.val( "" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); - ok(inp.datepicker("getDate") == null, "Keystroke esc"); + input.val( "" ).datepicker( "open" ); + ok( instance.isOpen, "datepicker is open before escape" ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); + ok( !instance.isOpen, "escape closes the datepicker" ); - inp.val( "02/04/2008" ).datepicker( "open" ) + input.val( "1/1/2014" ).datepicker( "open" ) .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 4 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Keystroke esc - preset" ); - inp.val( "02/04/2008" ).datepicker( "open" ) + input.val( "1/1/2014" ).datepicker( "open" ) .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP }) .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date(2008, 2 - 1, 4), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Keystroke esc - abandoned" ); - // Moving by day or week - inp.val( "" ).datepicker( "open" ) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.LEFT }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - date.setDate( date.getDate() - 1 ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke ctrl+left" ); - - inp.val( "" ).datepicker( "open" ) - .simulate( "keydown", {keyCode: $.ui.keyCode.LEFT }). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER }); - date.setDate( date.getDate() + 1 ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke left") ; - - inp.val( "" ).datepicker( "open" ) - .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.RIGHT}). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + input.datepicker( "destroy" ); +}); + +asyncTest( "keyboard handling - arrow keys", function() { + expect( 6 ); + var picker, + input = $( "#datepicker" ), + date = new Date(); + + function step1() { + input.datepicker(); + picker = input.datepicker( "widget" ); + ok( !picker.is( ":visible" ), "datepicker closed" ); + input.val( "" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + + setTimeout(function() { + ok( picker.is( ":visible" ), "Keystroke down opens datepicker" ); + input.datepicker( "destroy" ); + step2(); + }); + }; + + function step2() { + input.datepicker(); + picker = input.datepicker( "widget" ) + ok( !picker.is( ":visible" ), "datepicker closed" ); + input.val( "" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.UP }); + + setTimeout(function() { + ok( picker.is( ":visible" ), "Keystroke up opens datepicker" ); + input.datepicker( "destroy" ); + step3(); + }); + }; + + function step3() { + input.datepicker() + .val( "" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.LEFT }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date.setDate( date.getDate() - 1 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, + "Keystroke left to switch to previous day" ); + + input.datepicker( "destroy" ); + step4(); + }, 100); + }; + + function step4() { + input.datepicker() + .val( "" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date.setDate( new Date().getDate() + 1 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, + "Keystroke right to switch to next day" ); + + input.datepicker( "destroy" ); + start(); + }, 100); + }; + + step1(); +}); + +/* + input.val( "" ).datepicker( "open" ) + .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.RIGHT }) + .simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() + 1); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke ctrl+right" ); - - inp.val( "" ).datepicker( "open" ) - .simulate( "keydown", {keyCode: $.ui.keyCode.RIGHT}). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() - 1); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke right" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke ctrl+right" ); - inp.val( "" ).datepicker( "open" ) + input.val( "" ).datepicker( "open" ) .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.UP}). simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() - 7); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke ctrl+up" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke ctrl+up" ); - inp.val( "" ).datepicker( "open" ) + input.val( "" ).datepicker( "open" ) .simulate( "keydown", {keyCode: $.ui.keyCode.UP}). simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() + 7); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke up" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke up" ); - inp.val( "" ).datepicker( "open" ) + input.val( "" ).datepicker( "open" ) .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.DOWN}). simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() + 7); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke ctrl+down" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke ctrl+down" ); - inp.val( "" ).datepicker( "open" ) + input.val( "" ).datepicker( "open" ) .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); date.setDate( date.getDate() - 7 ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Keystroke down" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke down" ); // Moving by month or year - inp.val( "02/04/2008" ).datepicker( "open" ) + input.val( "02/04/2008" ).datepicker( "open" ) .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 1 - 1, 4 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 4 ), "Keystroke pgup" ); - inp.val( "02/04/2008" ).datepicker( "open" ) + input.val( "02/04/2008" ).datepicker( "open" ) .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 3 - 1, 4 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 3 - 1, 4 ), "Keystroke pgdn" ); - inp.val( "02/04/2008" ).datepicker( "open" ) + input.val( "02/04/2008" ).datepicker( "open" ) .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2007, 2 - 1, 4 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2007, 2 - 1, 4 ), "Keystroke ctrl+pgup" ); - inp.val( "02/04/2008" ).datepicker( "open" ) + input.val( "02/04/2008" ).datepicker( "open" ) .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2009, 2 - 1, 4 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2009, 2 - 1, 4 ), "Keystroke ctrl+pgdn" ); // Check for moving to short months - inp.val( "03/31/2008" ).datepicker( "open" ) + input.val( "03/31/2008" ).datepicker( "open" ) .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 29 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 29 ), "Keystroke pgup - Feb" ); - inp.val( "01/30/2008" ).datepicker( "open" ) + input.val( "01/30/2008" ).datepicker( "open" ) .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 29 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 29 ), "Keystroke pgdn - Feb" ); - inp.val( "02/29/2008" ).datepicker( "open" ) + input.val( "02/29/2008" ).datepicker( "open" ) .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2007, 2 - 1, 28 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2007, 2 - 1, 28 ), "Keystroke ctrl+pgup - Feb" ); - inp.val( "02/29/2008" ).datepicker( "open" ) + input.val( "02/29/2008" ).datepicker( "open" ) .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2009, 2 - 1, 28 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2009, 2 - 1, 28 ), "Keystroke ctrl+pgdn - Feb" ); // Goto current - inp.datepicker( "option", { gotoCurrent: true }) + input.datepicker( "option", { gotoCurrent: true }) .datepicker( "close" ).val( "02/04/2008" ).datepicker( "open" ) .late( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.HOME }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 4 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 4 ), "Keystroke ctrl+home" ); // Change steps - inp.datepicker( "option", { stepMonths: 2, gotoCurrent: false }) + input.datepicker( "option", { stepMonths: 2, gotoCurrent: false }) .datepicker( "close" ).val( "02/04/2008" ).datepicker( "open" ) .late( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2007, 12 - 1, 4 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2007, 12 - 1, 4 ), "Keystroke pgup step 2" ); - inp.val( "02/04/2008" ).datepicker( "open" ) + input.val( "02/04/2008" ).datepicker( "open" ) .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 4 - 1, 4 ), + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 4 - 1, 4 ), "Keystroke pgdn step 2" ); -}); +*/ test( "mouse", function() { // TODO: These tests use the old getDate() and setDate() methods. Re-activate these diff --git a/ui/datepicker.js b/ui/datepicker.js index cd30e5c71fc..a352e2d6950 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -54,6 +54,7 @@ $.widget( "ui.datepicker", { }, _create: function() { this.date = $.date( null, this.options.dateFormat ); + this.date.eachDay = this.options.eachDay; this.id = "ui-datepicker-" + idIncrement; idIncrement++; @@ -192,6 +193,9 @@ $.widget( "ui.datepicker", { this.close( event ); } break; + case $.ui.keyCode.ENTER: + this._handleKeydown( event ); + break; case $.ui.keyCode.DOWN: case $.ui.keyCode.UP: clearTimeout( this.closeTimer ); @@ -200,6 +204,26 @@ $.widget( "ui.datepicker", { this.grid.focus( 1 ); }, 1); break; + case $.ui.keyCode.HOME: + if ( event.ctrlKey ) { + this.date.setTime( new Date() ); + event.preventDefault(); + if ( this.isOpen ) { + this.refresh(); + } else { + this.open( event ); + } + } + break; + case $.ui.keyCode.END: + if ( event.ctrlKey ) { + this.element.val( "" ); + event.preventDefault(); + if ( this.isOpen ) { + this.close( event ); + } + } + break; } }, mousedown: function( event ) { From 7e39bc1b5520e5cd39b5e2eeb4089f4c9e0e69d5 Mon Sep 17 00:00:00 2001 From: Rafael Xavier de Souza Date: Sat, 30 Nov 2013 10:22:17 -0200 Subject: [PATCH 15/53] Datepicker: Use Globalize 1.0.0 --- demos/datepicker/date-formats.html | 25 +- demos/datepicker/localization.html | 9 +- demos/datepicker/show-week.html | 10 +- external/date.js | 73 +- external/globalize/globalize.culture.de-DE.js | 81 - external/globalize/globalize.culture.ja-JP.js | 100 - external/globalize/globalize.js | 3065 +++++++------- external/localization.js | 3545 +++++++++++++++-- tests/unit/date/date.html | 6 +- tests/unit/date/date_core.js | 90 +- tests/unit/datepicker/datepicker.html | 6 +- tests/unit/datepicker/datepicker_core.js | 12 +- tests/unit/datepicker/datepicker_methods.js | 12 +- tests/unit/datepicker/datepicker_options.js | 8 +- ui/datepicker.js | 28 +- 15 files changed, 4863 insertions(+), 2207 deletions(-) delete mode 100644 external/globalize/globalize.culture.de-DE.js delete mode 100644 external/globalize/globalize.culture.ja-JP.js diff --git a/demos/datepicker/date-formats.html b/demos/datepicker/date-formats.html index 1b8874f8cf6..2c5045b6f33 100644 --- a/demos/datepicker/date-formats.html +++ b/demos/datepicker/date-formats.html @@ -18,7 +18,24 @@ $(function() { $( "#datepicker" ).datepicker(); $( "#format" ).change(function() { - $( "#datepicker" ).datepicker( "option", "dateFormat", $( this ).val() ); + var format; + switch( $( this ).val() ) { + case "short": + $( "#datepicker" ).datepicker( "option", "dateFormat", { + date: "short" + }); + break; + case "long": + $( "#datepicker" ).datepicker( "option", "dateFormat", { + date: "long" + }); + break; + case "iso": + $( "#datepicker" ).datepicker( "option", "dateFormat", { + pattern: "yyyy-MM-dd" + }); + break; + } }); }); @@ -29,9 +46,9 @@

      Format options:

      diff --git a/demos/datepicker/localization.html b/demos/datepicker/localization.html index b8c99547b7b..61aa7c9a2da 100644 --- a/demos/datepicker/localization.html +++ b/demos/datepicker/localization.html @@ -6,8 +6,6 @@ - - @@ -18,11 +16,11 @@ @@ -32,7 +30,6 @@

      Date:  

      diff --git a/demos/datepicker/show-week.html b/demos/datepicker/show-week.html index e9a33d8da16..c7faf102052 100644 --- a/demos/datepicker/show-week.html +++ b/demos/datepicker/show-week.html @@ -17,8 +17,7 @@ @@ -28,10 +27,9 @@

      Date:

      diff --git a/external/date.js b/external/date.js index fef4dcaeffe..b8d1ef30d33 100644 --- a/external/date.js +++ b/external/date.js @@ -6,25 +6,31 @@ */ (function( $, undefined ) { -$.date = function( datestring, formatstring ) { - //TODO: Need to refactor $.date to be a constructor, move the methods to a prototype. - var calendar = Globalize.culture().calendar, - format = formatstring ? formatstring : calendar.patterns.d, - date = datestring ? Globalize.parseDate( datestring, format ) : new Date(); +var weekdays = [ "sun", "mon", "tue", "wed", "thu", "fri", "sat" ], + weekdaysRev = { + "sun": 0, + "mon": 1, + "tue": 2, + "wed": 3, + "thu": 4, + "fri": 5, + "sat": 6 + }; + +Globalize.locale( "en" ); - if ( !date ) { - date = new Date(); +$.date = function( date, globalFormat ) { + //TODO: Need to refactor $.date to be a constructor, move the methods to a prototype. + if ( typeof date === "string" && date.length ) { + date = Globalize.parseDate( date, globalFormat ); } + date = date || new Date(); + return { - refresh: function() { - calendar = Globalize.culture().calendar; - format = formatstring || calendar.patterns.d; - return this; - }, - setFormat: function( formatstring ) { - if ( formatstring ) { - format = formatstring; + setFormat: function( format ) { + if ( format ) { + globalFormat = format; } return this; }, @@ -82,7 +88,7 @@ $.date = function( datestring, formatstring ) { return 32 - new Date( year, month, 32 ).getDate(); }, monthName: function() { - return calendar.months.names[ date.getMonth() ]; + return Globalize.format( date, { pattern: "MMMM" } ); }, day: function() { return date.getDate(); @@ -101,10 +107,10 @@ $.date = function( datestring, formatstring ) { weekdays: function() { var result = []; for ( var dow = 0; dow < 7; dow++ ) { - var day = ( dow + calendar.firstDay ) % 7; + var day = ( dow + weekdaysRev[ Globalize.locale().supplemental.weekData.firstDay() ] ) % 7; result.push({ - shortname: calendar.days.namesShort[ day ], - fullname: calendar.days.names[ day ] + shortname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/abbreviated", weekdays[ day ] ]), + fullname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/wide", weekdays[ day ] ]), }); } return result; @@ -113,12 +119,12 @@ $.date = function( datestring, formatstring ) { var result = [], today = $.date(), firstDayOfMonth = new Date( this.year(), date.getMonth(), 1 ).getDay(), - leadDays = ( firstDayOfMonth - calendar.firstDay + 7 ) % 7, + leadDays = ( firstDayOfMonth - weekdaysRev[ Globalize.locale().supplemental.weekData.firstDay() ] + 7 ) % 7, rows = Math.ceil( ( leadDays + this.daysInMonth() ) / 7 ), printDate = new Date( this.year(), date.getMonth(), 1 - leadDays ); for ( var row = 0; row < rows; row++ ) { var week = result[ result.length ] = { - number: this.iso8601Week( printDate ), + number: Globalize.format( printDate, { pattern: "w" } ), days: [] }; for ( var dayx = 0; dayx < 7; dayx++ ) { @@ -153,16 +159,6 @@ $.date = function( datestring, formatstring ) { result[ result.length - 1 ].last = true; return result; }, - iso8601Week: function(date) { - var checkDate = new Date( date.getTime() ); - // Find Thursday of this week starting on Monday - checkDate.setDate( checkDate.getDate() + 4 - ( checkDate.getDay() || 7 ) ); - var time = checkDate.getTime(); - // Compare with Jan 1 - checkDate.setMonth( 0 ); - checkDate.setDate( 1 ); - return Math.floor( Math.round( ( time - checkDate ) / 86400000) / 7 ) + 1; - }, select: function() { this.selected = this.clone(); return this; @@ -170,27 +166,20 @@ $.date = function( datestring, formatstring ) { clone: function() { return $.date( new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), - date.getMinutes(), date.getSeconds()), formatstring ); + date.getMinutes(), date.getSeconds()), globalFormat ); }, // TODO compare year, month, day each for better performance equal: function( other ) { function format( date ) { - return Globalize.format( date, "d" ); + return Globalize.format( date, { pattern: "yyyyMMdd" } ); } return format( date ) === format( other ); }, date: function() { return date; }, - format: function( formatstring ) { - return Globalize.format( date, formatstring ? formatstring : format ); - }, - calendar: function( newcalendar ) { - if ( newcalendar ) { - calendar = newcalendar; - return this; - } - return calendar; + format: function( format ) { + return Globalize.format( date, format || globalFormat ); } }; }; diff --git a/external/globalize/globalize.culture.de-DE.js b/external/globalize/globalize.culture.de-DE.js deleted file mode 100644 index 5466bd75e3b..00000000000 --- a/external/globalize/globalize.culture.de-DE.js +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Globalize Culture de-DE - * - * http://github.com/jquery/globalize - * - * Copyright Software Freedom Conservancy, Inc. - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * This file was generated by the Globalize Culture Generator - * Translation: bugs found in this file need to be fixed in the generator - */ - -(function( window, undefined ) { - -var Globalize; - -if ( typeof require !== "undefined" - && typeof exports !== "undefined" - && typeof module !== "undefined" ) { - // Assume CommonJS - Globalize = require( "globalize" ); -} else { - // Global variable - Globalize = window.Globalize; -} - -Globalize.addCultureInfo( "de-DE", "default", { - name: "de-DE", - englishName: "German (Germany)", - nativeName: "Deutsch (Deutschland)", - language: "de", - numberFormat: { - ",": ".", - ".": ",", - NaN: "n. def.", - negativeInfinity: "-unendlich", - positiveInfinity: "+unendlich", - percent: { - pattern: ["-n%","n%"], - ",": ".", - ".": "," - }, - currency: { - pattern: ["-n $","n $"], - ",": ".", - ".": ",", - symbol: "€" - } - }, - calendars: { - standard: { - "/": ".", - firstDay: 1, - days: { - names: ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"], - namesAbbr: ["So","Mo","Di","Mi","Do","Fr","Sa"], - namesShort: ["So","Mo","Di","Mi","Do","Fr","Sa"] - }, - months: { - names: ["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember",""], - namesAbbr: ["Jan","Feb","Mrz","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez",""] - }, - AM: null, - PM: null, - eras: [{"name":"n. Chr.","start":null,"offset":0}], - patterns: { - d: "dd.MM.yyyy", - D: "dddd, d. MMMM yyyy", - t: "HH:mm", - T: "HH:mm:ss", - f: "dddd, d. MMMM yyyy HH:mm", - F: "dddd, d. MMMM yyyy HH:mm:ss", - M: "dd MMMM", - Y: "MMMM yyyy" - } - } - } -}); - -}( this )); diff --git a/external/globalize/globalize.culture.ja-JP.js b/external/globalize/globalize.culture.ja-JP.js deleted file mode 100644 index a9469d709f4..00000000000 --- a/external/globalize/globalize.culture.ja-JP.js +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Globalize Culture ja-JP - * - * http://github.com/jquery/globalize - * - * Copyright Software Freedom Conservancy, Inc. - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * This file was generated by the Globalize Culture Generator - * Translation: bugs found in this file need to be fixed in the generator - */ - -(function( window, undefined ) { - -var Globalize; - -if ( typeof require !== "undefined" - && typeof exports !== "undefined" - && typeof module !== "undefined" ) { - // Assume CommonJS - Globalize = require( "globalize" ); -} else { - // Global variable - Globalize = window.Globalize; -} - -Globalize.addCultureInfo( "ja-JP", "default", { - name: "ja-JP", - englishName: "Japanese (Japan)", - nativeName: "日本語 (日本)", - language: "ja", - numberFormat: { - NaN: "NaN (非数値)", - negativeInfinity: "-∞", - positiveInfinity: "+∞", - percent: { - pattern: ["-n%","n%"] - }, - currency: { - pattern: ["-$n","$n"], - decimals: 0, - symbol: "¥" - } - }, - calendars: { - standard: { - days: { - names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"], - namesAbbr: ["日","月","火","水","木","金","土"], - namesShort: ["日","月","火","水","木","金","土"] - }, - months: { - names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""], - namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""] - }, - AM: ["午前","午前","午前"], - PM: ["午後","午後","午後"], - eras: [{"name":"西暦","start":null,"offset":0}], - patterns: { - d: "yyyy/MM/dd", - D: "yyyy'年'M'月'd'日'", - t: "H:mm", - T: "H:mm:ss", - f: "yyyy'年'M'月'd'日' H:mm", - F: "yyyy'年'M'月'd'日' H:mm:ss", - M: "M'月'd'日'", - Y: "yyyy'年'M'月'" - } - }, - Japanese: { - name: "Japanese", - days: { - names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"], - namesAbbr: ["日","月","火","水","木","金","土"], - namesShort: ["日","月","火","水","木","金","土"] - }, - months: { - names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""], - namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""] - }, - AM: ["午前","午前","午前"], - PM: ["午後","午後","午後"], - eras: [{"name":"平成","start":null,"offset":1867},{"name":"昭和","start":-1812153600000,"offset":1911},{"name":"大正","start":-1357603200000,"offset":1925},{"name":"明治","start":60022080000,"offset":1988}], - twoDigitYearMax: 99, - patterns: { - d: "gg y/M/d", - D: "gg y'年'M'月'd'日'", - t: "H:mm", - T: "H:mm:ss", - f: "gg y'年'M'月'd'日' H:mm", - F: "gg y'年'M'月'd'日' H:mm:ss", - M: "M'月'd'日'", - Y: "gg y'年'M'月'" - } - } - } -}); - -}( this )); diff --git a/external/globalize/globalize.js b/external/globalize/globalize.js index a38a32625d3..1086d339db9 100644 --- a/external/globalize/globalize.js +++ b/external/globalize/globalize.js @@ -1,1585 +1,1788 @@ /*! - * Globalize + * Globalize v1.0.0pre * * http://github.com/jquery/globalize * - * Copyright Software Freedom Conservancy, Inc. - * Dual licensed under the MIT or GPL Version 2 licenses. + * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license * http://jquery.org/license + * + * Date: 2013-12-01T12:08Z */ - -(function( window, undefined ) { - -var Globalize, - // private variables - regexHex, - regexInfinity, - regexParseFloat, - regexTrim, - // private JavaScript utility functions - arrayIndexOf, - endsWith, - extend, - isArray, - isFunction, - isObject, - startsWith, - trim, - truncate, - zeroPad, - // private Globalization utility functions - appendPreOrPostMatch, - expandFormat, - formatDate, - formatNumber, - getTokenRegExp, - getEra, - getEraYear, - parseExact, - parseNegativePattern; - -// Global variable (Globalize) or CommonJS module (globalize) -Globalize = function( cultureSelector ) { - return new Globalize.prototype.init( cultureSelector ); -}; - -if ( typeof require !== "undefined" && - typeof exports !== "undefined" && - typeof module !== "undefined" ) { - // Assume CommonJS - module.exports = Globalize; -} else { - // Export as global variable - window.Globalize = Globalize; -} - -Globalize.cultures = {}; - -Globalize.prototype = { - constructor: Globalize, - init: function( cultureSelector ) { - this.cultures = Globalize.cultures; - this.cultureSelector = cultureSelector; - - return this; +(function( root, factory ) { + + if ( typeof define === "function" && define.amd ) { + // AMD. + define( factory ); + } else if ( typeof module === "object" && typeof module.exports === "object" ) { + // Node. CommonJS. + module.exports = factory(); + } else { + // Global + root.Globalize = factory(); } -}; -Globalize.prototype.init.prototype = Globalize.prototype; - -// 1. When defining a culture, all fields are required except the ones stated as optional. -// 2. Each culture should have a ".calendars" object with at least one calendar named "standard" -// which serves as the default calendar in use by that culture. -// 3. Each culture should have a ".calendar" object which is the current calendar being used, -// it may be dynamically changed at any time to one of the calendars in ".calendars". -Globalize.cultures[ "default" ] = { - // A unique name for the culture in the form - - name: "en", - // the name of the culture in the english language - englishName: "English", - // the name of the culture in its own language - nativeName: "English", - // whether the culture uses right-to-left text - isRTL: false, - // "language" is used for so-called "specific" cultures. - // For example, the culture "es-CL" means "Spanish, in Chili". - // It represents the Spanish-speaking culture as it is in Chili, - // which might have different formatting rules or even translations - // than Spanish in Spain. A "neutral" culture is one that is not - // specific to a region. For example, the culture "es" is the generic - // Spanish culture, which may be a more generalized version of the language - // that may or may not be what a specific culture expects. - // For a specific culture like "es-CL", the "language" field refers to the - // neutral, generic culture information for the language it is using. - // This is not always a simple matter of the string before the dash. - // For example, the "zh-Hans" culture is netural (Simplified Chinese). - // And the "zh-SG" culture is Simplified Chinese in Singapore, whose lanugage - // field is "zh-CHS", not "zh". - // This field should be used to navigate from a specific culture to it's - // more general, neutral culture. If a culture is already as general as it - // can get, the language may refer to itself. - language: "en", - // numberFormat defines general number formatting rules, like the digits in - // each grouping, the group separator, and how negative numbers are displayed. - numberFormat: { - // [negativePattern] - // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency, - // but is still defined as an array for consistency with them. - // negativePattern: one of "(n)|-n|- n|n-|n -" - pattern: [ "-n" ], - // number of decimal places normally shown - decimals: 2, - // string that separates number groups, as in 1,000,000 - ",": ",", - // string that separates a number from the fractional portion, as in 1.99 - ".": ".", - // array of numbers indicating the size of each number group. - // TODO: more detailed description and example - groupSizes: [ 3 ], - // symbol used for positive numbers - "+": "+", - // symbol used for negative numbers - "-": "-", - // symbol used for NaN (Not-A-Number) - "NaN": "NaN", - // symbol used for Negative Infinity - negativeInfinity: "-Infinity", - // symbol used for Positive Infinity - positiveInfinity: "Infinity", - percent: { - // [negativePattern, positivePattern] - // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %" - // positivePattern: one of "n %|n%|%n|% n" - pattern: [ "-n %", "n %" ], - // number of decimal places normally shown - decimals: 2, - // array of numbers indicating the size of each number group. - // TODO: more detailed description and example - groupSizes: [ 3 ], - // string that separates number groups, as in 1,000,000 - ",": ",", - // string that separates a number from the fractional portion, as in 1.99 - ".": ".", - // symbol used to represent a percentage - symbol: "%" - }, - currency: { - // [negativePattern, positivePattern] - // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)" - // positivePattern: one of "$n|n$|$ n|n $" - pattern: [ "($n)", "$n" ], - // number of decimal places normally shown - decimals: 2, - // array of numbers indicating the size of each number group. - // TODO: more detailed description and example - groupSizes: [ 3 ], - // string that separates number groups, as in 1,000,000 - ",": ",", - // string that separates a number from the fractional portion, as in 1.99 - ".": ".", - // symbol used to represent currency - symbol: "$" - } - }, - // calendars defines all the possible calendars used by this culture. - // There should be at least one defined with name "standard", and is the default - // calendar used by the culture. - // A calendar contains information about how dates are formatted, information about - // the calendar's eras, a standard set of the date formats, - // translations for day and month names, and if the calendar is not based on the Gregorian - // calendar, conversion functions to and from the Gregorian calendar. - calendars: { - standard: { - // name that identifies the type of calendar this is - name: "Gregorian_USEnglish", - // separator of parts of a date (e.g. "/" in 11/05/1955) - "/": "/", - // separator of parts of a time (e.g. ":" in 05:44 PM) - ":": ":", - // the first day of the week (0 = Sunday, 1 = Monday, etc) - firstDay: 0, - days: { - // full day names - names: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], - // abbreviated day names - namesAbbr: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], - // shortest day names - namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ] - }, - months: { - // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar) - names: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" ], - // abbreviated month names - namesAbbr: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" ] - }, - // AM and PM designators in one of these forms: - // The usual view, and the upper and lower case versions - // [ standard, lowercase, uppercase ] - // The culture does not use AM or PM (likely all standard date formats use 24 hour time) - // null - AM: [ "AM", "am", "AM" ], - PM: [ "PM", "pm", "PM" ], - eras: [ - // eras in reverse chronological order. - // name: the name of the era in this culture (e.g. A.D., C.E.) - // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era. - // offset: offset in years from gregorian calendar - { - "name": "A.D.", - "start": null, - "offset": 0 - } - ], - // when a two digit year is given, it will never be parsed as a four digit - // year greater than this year (in the appropriate era for the culture) - // Set it as a full year (e.g. 2029) or use an offset format starting from - // the current year: "+19" would correspond to 2029 if the current year 2010. - twoDigitYearMax: 2029, - // set of predefined date and time patterns used by the culture - // these represent the format someone in this culture would expect - // to see given the portions of the date that are shown. - patterns: { - // short date pattern - d: "M/d/yyyy", - // long date pattern - D: "dddd, MMMM dd, yyyy", - // short time pattern - t: "h:mm tt", - // long time pattern - T: "h:mm:ss tt", - // long date, short time pattern - f: "dddd, MMMM dd, yyyy h:mm tt", - // long date, long time pattern - F: "dddd, MMMM dd, yyyy h:mm:ss tt", - // month/day pattern - M: "MMMM dd", - // month/year pattern - Y: "yyyy MMMM", - // S is a sortable format that does not vary by culture - S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss" - } - // optional fields for each calendar: - /* - monthsGenitive: - Same as months but used when the day preceeds the month. - Omit if the culture has no genitive distinction in month names. - For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx - convert: - Allows for the support of non-gregorian based calendars. This convert object is used to - to convert a date to and from a gregorian calendar date to handle parsing and formatting. - The two functions: - fromGregorian( date ) - Given the date as a parameter, return an array with parts [ year, month, day ] - corresponding to the non-gregorian based year, month, and day for the calendar. - toGregorian( year, month, day ) - Given the non-gregorian year, month, and day, return a new Date() object - set to the corresponding date in the gregorian calendar. - */ - } - }, - // For localized strings - messages: {} -}; -Globalize.cultures[ "default" ].calendar = Globalize.cultures[ "default" ].calendars.standard; +}( this, function() { -Globalize.cultures.en = Globalize.cultures[ "default" ]; +/** + * CLDR JavaScript Library v0.2.4-pre + * http://jquery.com/ + * + * Copyright 2013 Rafael Xavier de Souza + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-11-30T11:30Z + */ +/*! + * CLDR JavaScript Library v0.2.4-pre 2013-11-30T11:30Z MIT license © Rafael Xavier + * http://git.io/h4lmVg + */ + var Cldr = (function() { -Globalize.cultureSelector = "en"; -// -// private variables -// -regexHex = /^0x[a-f0-9]+$/i; -regexInfinity = /^[+\-]?infinity$/i; -regexParseFloat = /^[+\-]?\d*\.?\d*(e[+\-]?\d+)?$/; -regexTrim = /^\s+|\s+$/g; + var alwaysArray = function( stringOrArray ) { + return typeof stringOrArray === "string" ? [ stringOrArray ] : stringOrArray; + }; -// -// private JavaScript utility functions -// -arrayIndexOf = function( array, item ) { - if ( array.indexOf ) { - return array.indexOf( item ); - } - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[i] === item ) { - return i; - } - } - return -1; -}; - -endsWith = function( value, pattern ) { - return value.substr( value.length - pattern.length ) === pattern; -}; - -extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !isFunction(target) ) { - target = {}; - } - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } + var common = function( Cldr ) { - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( isObject(copy) || (copyIsArray = isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && isArray(src) ? src : []; + Cldr.prototype.main = function( path ) { + path = alwaysArray( path ); + return this.get( [ "main/{languageId}" ].concat( path ) ); + }; - } else { - clone = src && isObject(src) ? src : {}; - } + }; - // Never move original objects, clone them - target[ name ] = extend( deep, clone, copy ); - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - // Return the modified object - return target; -}; -isArray = Array.isArray || function( obj ) { - return Object.prototype.toString.call( obj ) === "[object Array]"; -}; + var arrayIsArray = Array.isArray || function( obj ) { + return Object.prototype.toString.call( obj ) === "[object Array]"; + }; -isFunction = function( obj ) { - return Object.prototype.toString.call( obj ) === "[object Function]"; -}; -isObject = function( obj ) { - return Object.prototype.toString.call( obj ) === "[object Object]"; -}; -startsWith = function( value, pattern ) { - return value.indexOf( pattern ) === 0; -}; -trim = function( value ) { - return ( value + "" ).replace( regexTrim, "" ); -}; + var pathNormalize = function( path, attributes ) { + if ( arrayIsArray( path ) ) { + path = path.join( "/" ); + } + if ( typeof path !== "string" ) { + throw new Error( "invalid path \"" + path + "\"" ); + } + // 1: Ignore leading slash `/` + // 2: Ignore leading `cldr/` + path = path + .replace( /^\// , "" ) /* 1 */ + .replace( /^cldr\// , "" ); /* 2 */ -truncate = function( value ) { - if ( isNaN( value ) ) { - return NaN; - } - return Math[ value < 0 ? "ceil" : "floor" ]( value ); -}; + // Replace {attribute}'s + path = path.replace( /{[a-zA-Z]+}/g, function( name ) { + name = name.replace( /^{([^}]*)}$/, "$1" ); + return attributes[ name ]; + }); -zeroPad = function( str, count, left ) { - var l; - for ( l = str.length; l < count; l += 1 ) { - str = ( left ? ("0" + str) : (str + "0") ); - } - return str; -}; - -// -// private Globalization utility functions -// - -appendPreOrPostMatch = function( preMatch, strings ) { - // appends pre- and post- token match strings while removing escaped characters. - // Returns a single quote count which is used to determine if the token occurs - // in a string literal. - var quoteCount = 0, - escaped = false; - for ( var i = 0, il = preMatch.length; i < il; i++ ) { - var c = preMatch.charAt( i ); - switch ( c ) { - case "\'": - if ( escaped ) { - strings.push( "\'" ); - } - else { - quoteCount++; - } - escaped = false; - break; - case "\\": - if ( escaped ) { - strings.push( "\\" ); - } - escaped = !escaped; - break; - default: - strings.push( c ); - escaped = false; - break; - } - } - return quoteCount; -}; - -expandFormat = function( cal, format ) { - // expands unspecified or single character date formats into the full pattern. - format = format || "F"; - var pattern, - patterns = cal.patterns, - len = format.length; - if ( len === 1 ) { - pattern = patterns[ format ]; - if ( !pattern ) { - throw "Invalid date format string \'" + format + "\'."; + return path.split( "/" ); + }; + + + + + var arraySome = function( array, callback ) { + var i, length; + if ( array.some ) { + return array.some( callback ); } - format = pattern; - } - else if ( len === 2 && format.charAt(0) === "%" ) { - // %X escape format -- intended as a custom format string that is only one character, not a built-in format. - format = format.charAt( 1 ); - } - return format; -}; - -formatDate = function( value, format, culture ) { - var cal = culture.calendar, - convert = cal.convert, - ret; - - if ( !format || !format.length || format === "i" ) { - if ( culture && culture.name.length ) { - if ( convert ) { - // non-gregorian calendar, so we cannot use built-in toLocaleString() - ret = formatDate( value, cal.patterns.F, culture ); - } - else { - var eraDate = new Date( value.getTime() ), - era = getEra( value, cal.eras ); - eraDate.setFullYear( getEraYear(value, cal, era) ); - ret = eraDate.toLocaleString(); + for ( i = 0, length = array.length; i < length; i++ ) { + if ( callback( array[ i ], i, array ) ) { + return true; } } - else { - ret = value.toString(); - } - return ret; - } + return false; + }; - var eras = cal.eras, - sortable = format === "s"; - format = expandFormat( cal, format ); - - // Start with an empty string - ret = []; - var hour, - zeros = [ "0", "00", "000" ], - foundDay, - checkedDay, - dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g, - quoteCount = 0, - tokenRegExp = getTokenRegExp(), - converted; - - function padZeros( num, c ) { - var r, s = num + ""; - if ( c > 1 && s.length < c ) { - r = ( zeros[c - 2] + s); - return r.substr( r.length - c, c ); - } - else { - r = s; - } - return r; - } - function hasDay() { - if ( foundDay || checkedDay ) { - return foundDay; - } - foundDay = dayPartRegExp.test( format ); - checkedDay = true; - return foundDay; - } - function getPart( date, part ) { - if ( converted ) { - return converted[ part ]; - } - switch ( part ) { - case 0: - return date.getFullYear(); - case 1: - return date.getMonth(); - case 2: - return date.getDate(); - default: - throw "Invalid part value " + part; + + // Return the maximized language id as defined in + // http://www.unicode.org/reports/tr35/#Likely_Subtags + // 1. Canonicalize. + // 1.1 Make sure the input locale is in canonical form: uses the right separator, and has the right casing. + // TODO Right casing? What df? It seems languages are lowercase, scripts are Capitalized, territory is uppercase. I am leaving this as an exercise to the user. + + // 1.2 Replace any deprecated subtags with their canonical values using the data in supplemental metadata. Use the first value in the replacement list, if it exists. Language tag replacements may have multiple parts, such as "sh" ➞ "sr_Latn" or mo" ➞ "ro_MD". In such a case, the original script and/or region are retained if there is one. Thus "sh_Arab_AQ" ➞ "sr_Arab_AQ", not "sr_Latn_AQ". + // TODO What data? + + // 1.3 If the tag is grandfathered (see in the supplemental data), then return it. + // TODO grandfathered? + + // 1.4 Remove the script code 'Zzzz' and the region code 'ZZ' if they occur. + // 1.5 Get the components of the cleaned-up source tag (languages, scripts, and regions), plus any variants and extensions. + // 2. Lookup. Lookup each of the following in order, and stop on the first match: + // 2.1 languages_scripts_regions + // 2.2 languages_regions + // 2.3 languages_scripts + // 2.4 languages + // 2.5 und_scripts + // 3. Return + // 3.1 If there is no match, either return an error value, or the match for "und" (in APIs where a valid language tag is required). + // 3.2 Otherwise there is a match = languagem_scriptm_regionm + // 3.3 Let xr = xs if xs is not empty, and xm otherwise. + // 3.4 Return the language tag composed of languager _ scriptr _ regionr + variants + extensions . + + // + // @subtags [Array] normalized language id subtags tuple (see init.js). + var likelySubtags = function( cldr, subtags, options ) { + var match, matchFound, + language = subtags[ 0 ], + script = subtags[ 1 ], + territory = subtags[ 2 ]; + options = options || {}; + + // Skip if (language, script, territory) is not empty [3.3] + if ( language !== "und" && script !== "Zzzz" && territory !== "ZZ" ) { + return [ language, script, territory ]; + } + + // Skip if no supplemental likelySubtags data is present + if ( typeof cldr.get( "supplemental/likelySubtags" ) === "undefined" ) { + return; + } + + // [2] + matchFound = arraySome([ + [ language, script, territory ], + [ language, territory ], + [ language, script ], + [ language ], + [ "und", script ] + ], function( test ) { + return match = !(/\b(Zzzz|ZZ)\b/).test( test.join( "_" ) ) /* [1.4] */ && cldr.get( [ "supplemental/likelySubtags", test.join( "_" ) ] ); + }); + + // [3] + if ( matchFound ) { + // [3.2 .. 3.4] + match = match.split( "_" ); + return [ + language !== "und" ? language : match[ 0 ], + script !== "Zzzz" ? script : match[ 1 ], + territory !== "ZZ" ? territory : match[ 2 ] + ]; + } else if ( options.force ) { + // [3.1.2] + return cldr.get( "supplemental/likelySubtags/und" ).split( "_" ); + } else { + // [3.1.1] + return; } - } + }; + - if ( !sortable && convert ) { - converted = convert.fromGregorian( value ); - } - for ( ; ; ) { - // Save the current index - var index = tokenRegExp.lastIndex, - // Look for the next pattern - ar = tokenRegExp.exec( format ); + // Given a locale, remove any fields that Add Likely Subtags would add. + // http://www.unicode.org/reports/tr35/#Likely_Subtags + // 1. First get max = AddLikelySubtags(inputLocale). If an error is signaled, return it. + // 2. Remove the variants from max. + // 3. Then for trial in {language, language _ region, language _ script}. If AddLikelySubtags(trial) = max, then return trial + variants. + // 4. If you do not get a match, return max + variants. + // + // @maxLanguageId [Array] maxLanguageId tuple (see init.js). + var removeLikelySubtags = function( cldr, maxLanguageId ) { + var match, matchFound, + language = maxLanguageId[ 0 ], + script = maxLanguageId[ 1 ], + territory = maxLanguageId[ 2 ]; + + // [3] + matchFound = arraySome([ + [ [ language, "Zzzz", "ZZ" ], [ language ] ], + [ [ language, "Zzzz", territory ], [ language, territory ] ], + [ [ language, script, "ZZ" ], [ language, script ] ] + ], function( test ) { + var result = likelySubtags( cldr, test[ 0 ] ); + match = test[ 1 ]; + return result && result[ 0 ] === maxLanguageId[ 0 ] && + result[ 1 ] === maxLanguageId[ 1 ] && + result[ 2 ] === maxLanguageId[ 2 ]; + }); + + // [4] + return matchFound ? match : maxLanguageId; + }; - // Append the text before the pattern (or the end of the string if not found) - var preMatch = format.slice( index, ar ? ar.index : format.length ); - quoteCount += appendPreOrPostMatch( preMatch, ret ); - if ( !ar ) { - break; - } - // do not replace any matches that occur inside a string literal. - if ( quoteCount % 2 ) { - ret.push( ar[0] ); - continue; + + var supplemental = function( cldr ) { + + var prepend, supplemental; + + prepend = function( prepend ) { + return function( path ) { + path = alwaysArray( path ); + return cldr.get( [ prepend ].concat( path ) ); + }; + }; + + supplemental = prepend( "supplemental" ); + + // Week Data + // http://www.unicode.org/reports/tr35/tr35-dates.html#Week_Data + supplemental.weekData = prepend( "supplemental/weekData" ); + + supplemental.weekData.firstDay = function() { + return cldr.get( "supplemental/weekData/firstDay/{territory}" ) || + cldr.get( "supplemental/weekData/firstDay/001" ); + }; + + supplemental.weekData.minDays = function() { + var minDays = cldr.get( "supplemental/weekData/minDays/{territory}" ) || + cldr.get( "supplemental/weekData/minDays/001" ); + return parseInt( minDays, 10 ); + }; + + // Time Data + // http://www.unicode.org/reports/tr35/tr35-dates.html#Time_Data + supplemental.timeData = prepend( "supplemental/timeData" ); + + supplemental.timeData.allowed = function() { + return cldr.get( "supplemental/timeData/{territory}/_allowed" ) || + cldr.get( "supplemental/timeData/001/_allowed" ); + }; + + supplemental.timeData.preferred = function() { + return cldr.get( "supplemental/timeData/{territory}/_preferred" ) || + cldr.get( "supplemental/timeData/001/_preferred" ); + }; + + return supplemental; + + }; + + + + + var init = function( locale ) { + var language, languageId, maxLanguageId, script, territory, unicodeLanguageId, variant; + + if ( typeof locale !== "string" ) { + throw new Error( "invalid locale type: \"" + JSON.stringify( locale ) + "\"" ); } - var current = ar[ 0 ], - clength = current.length; + // Normalize locale code. + // Get (or deduce) the "triple subtags": language, territory (also aliased as region), and script subtags. + // Get the variant subtags (calendar, collation, currency, etc). + // refs: + // - http://www.unicode.org/reports/tr35/#Field_Definitions + // - http://www.unicode.org/reports/tr35/#Language_and_Locale_IDs + // - http://www.unicode.org/reports/tr35/#Unicode_locale_identifier - switch ( current ) { - case "ddd": - //Day of the week, as a three-letter abbreviation - case "dddd": - // Day of the week, using the full name - var names = ( clength === 3 ) ? cal.days.namesAbbr : cal.days.names; - ret.push( names[value.getDay()] ); - break; - case "d": - // Day of month, without leading zero for single-digit days - case "dd": - // Day of month, with leading zero for single-digit days - foundDay = true; - ret.push( - padZeros( getPart(value, 2), clength ) - ); - break; - case "MMM": - // Month, as a three-letter abbreviation - case "MMMM": - // Month, using the full name - var part = getPart( value, 1 ); - ret.push( - ( cal.monthsGenitive && hasDay() ) ? - ( cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] ) : - ( cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] ) - ); - break; - case "M": - // Month, as digits, with no leading zero for single-digit months - case "MM": - // Month, as digits, with leading zero for single-digit months - ret.push( - padZeros( getPart(value, 1) + 1, clength ) - ); - break; - case "y": - // Year, as two digits, but with no leading zero for years less than 10 - case "yy": - // Year, as two digits, with leading zero for years less than 10 - case "yyyy": - // Year represented by four full digits - part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra(value, eras), sortable ); - if ( clength < 4 ) { - part = part % 100; - } - ret.push( - padZeros( part, clength ) - ); - break; - case "h": - // Hours with no leading zero for single-digit hours, using 12-hour clock - case "hh": - // Hours with leading zero for single-digit hours, using 12-hour clock - hour = value.getHours() % 12; - if ( hour === 0 ) hour = 12; - ret.push( - padZeros( hour, clength ) - ); - break; - case "H": - // Hours with no leading zero for single-digit hours, using 24-hour clock - case "HH": - // Hours with leading zero for single-digit hours, using 24-hour clock - ret.push( - padZeros( value.getHours(), clength ) - ); - break; - case "m": - // Minutes with no leading zero for single-digit minutes - case "mm": - // Minutes with leading zero for single-digit minutes - ret.push( - padZeros( value.getMinutes(), clength ) - ); - break; - case "s": - // Seconds with no leading zero for single-digit seconds - case "ss": - // Seconds with leading zero for single-digit seconds - ret.push( - padZeros( value.getSeconds(), clength ) - ); - break; - case "t": - // One character am/pm indicator ("a" or "p") - case "tt": - // Multicharacter am/pm indicator - part = value.getHours() < 12 ? ( cal.AM ? cal.AM[0] : " " ) : ( cal.PM ? cal.PM[0] : " " ); - ret.push( clength === 1 ? part.charAt(0) : part ); + locale = locale.replace( /-/, "_" ); + + // TODO normalize unicode locale extensions. Currently, skipped. + // unicodeLocaleExtensions = locale.split( "_u_" )[ 1 ]; + locale = locale.split( "_u_" )[ 0 ]; + + // TODO normalize transformed extensions. Currently, skipped. + // transformedExtensions = locale.split( "_t_" )[ 1 ]; + locale = locale.split( "_t_" )[ 0 ]; + + unicodeLanguageId = locale; + + // unicodeLanguageId = ... + switch ( true ) { + + // language_script_territory.. + case /^[a-z]{2}_[A-Z][a-z]{3}_[A-Z0-9]{2}(\b|_)/.test( unicodeLanguageId ): + language = unicodeLanguageId.split( "_" )[ 0 ]; + script = unicodeLanguageId.split( "_" )[ 1 ]; + territory = unicodeLanguageId.split( "_" )[ 2 ]; + variant = unicodeLanguageId.split( "_" )[ 3 ]; break; - case "f": - // Deciseconds - case "ff": - // Centiseconds - case "fff": - // Milliseconds - ret.push( - padZeros( value.getMilliseconds(), 3 ).substr( 0, clength ) - ); + + // language_script.. + case /^[a-z]{2}_[A-Z][a-z]{3}(\b|_)/.test( unicodeLanguageId ): + language = unicodeLanguageId.split( "_" )[ 0 ]; + script = unicodeLanguageId.split( "_" )[ 1 ]; + territory = "ZZ"; + variant = unicodeLanguageId.split( "_" )[ 2 ]; break; - case "z": - // Time zone offset, no leading zero - case "zz": - // Time zone offset with leading zero - hour = value.getTimezoneOffset() / 60; - ret.push( - ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), clength ) - ); + + // language_territory.. + case /^[a-z]{2}_[A-Z0-9]{2}(\b|_)/.test( unicodeLanguageId ): + language = unicodeLanguageId.split( "_" )[ 0 ]; + script = "Zzzz"; + territory = unicodeLanguageId.split( "_" )[ 1 ]; + variant = unicodeLanguageId.split( "_" )[ 2 ]; break; - case "zzz": - // Time zone offset with leading zero - hour = value.getTimezoneOffset() / 60; - ret.push( - ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), 2 ) + - // Hard coded ":" separator, rather than using cal.TimeSeparator - // Repeated here for consistency, plus ":" was already assumed in date parsing. - ":" + padZeros( Math.abs(value.getTimezoneOffset() % 60), 2 ) - ); + + // language.., or root + case /^([a-z]{2}|root)(\b|_)/.test( unicodeLanguageId ): + language = unicodeLanguageId.split( "_" )[ 0 ]; + script = "Zzzz"; + territory = "ZZ"; + variant = unicodeLanguageId.split( "_" )[ 1 ]; break; - case "g": - case "gg": - if ( cal.eras ) { - ret.push( - cal.eras[ getEra(value, eras) ].name - ); - } + + default: + language = "und"; break; - case "/": - ret.push( cal["/"] ); - break; - default: - throw "Invalid date format pattern \'" + current + "\'."; - } - } - return ret.join( "" ); -}; - -// formatNumber -(function() { - var expandNumber; - - expandNumber = function( number, precision, formatInfo ) { - var groupSizes = formatInfo.groupSizes, - curSize = groupSizes[ 0 ], - curGroupIndex = 1, - factor = Math.pow( 10, precision ), - rounded = Math.round( number * factor ) / factor; - - if ( !isFinite(rounded) ) { - rounded = number; } - number = rounded; - - var numberString = number+"", - right = "", - split = numberString.split( /e/i ), - exponent = split.length > 1 ? parseInt( split[1], 10 ) : 0; - numberString = split[ 0 ]; - split = numberString.split( "." ); - numberString = split[ 0 ]; - right = split.length > 1 ? split[ 1 ] : ""; - - if ( exponent > 0 ) { - right = zeroPad( right, exponent, false ); - numberString += right.slice( 0, exponent ); - right = right.substr( exponent ); + + // When a locale id does not specify a language, or territory (region), or script, they are obtained by Likely Subtags. + maxLanguageId = likelySubtags( this, [ language, script, territory ], { force: true } ) || unicodeLanguageId.split( "_" ); + language = maxLanguageId[ 0 ]; + script = maxLanguageId[ 1 ]; + territory = maxLanguageId[ 2 ]; + + // TODO json content distributed on zip file use languageId with `-` on main.. Why `-` vs. `_` ? + languageId = removeLikelySubtags( this, maxLanguageId ).join( "_" ); + + // Set attributes + this.attributes = { + + // Unicode Language Id + languageId: languageId, + maxLanguageId: maxLanguageId.join( "_" ), + + // Unicode Language Id Subtabs + language: language, + script: script, + territory: territory, + region: territory, /* alias */ + variant: variant + }; + + this.locale = variant ? [ languageId, variant ].join( "_" ) : languageId; + + // Inlcude supplemental helper + this.supplemental = supplemental( this ); + }; + + + + + // @path: normalized path + var resourceGet = function( data, path ) { + var i, + node = data, + length = path.length; + + for ( i = 0; i < length - 1; i++ ) { + node = node[ path[ i ] ]; + if ( !node ) { + return undefined; + } } - else if ( exponent < 0 ) { - exponent = -exponent; - numberString = zeroPad( numberString, exponent + 1, true ); - right = numberString.slice( -exponent, numberString.length ) + right; - numberString = numberString.slice( 0, -exponent ); + return node[ path[ i ] ]; + }; + + + + + var bundleParentLookup = function( Cldr, locale ) { + var parent; + + if ( locale === "root" ) { + return; } - if ( precision > 0 ) { - right = formatInfo[ "." ] + - ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) ); + // First, try to find parent on supplemental data. + parent = resourceGet( Cldr._resolved, pathNormalize( [ "supplemental/parentLocales/parentLocale", locale ] ) ); + if ( parent ) { + return parent; } - else { - right = ""; + + // Or truncate locale. + parent = locale.substr( 0, locale.lastIndexOf( "_" ) ); + if ( !parent ) { + return "root"; } - var stringIndex = numberString.length - 1, - sep = formatInfo[ "," ], - ret = ""; + return parent; + }; + - while ( stringIndex >= 0 ) { - if ( curSize === 0 || curSize > stringIndex ) { - return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? (sep + ret + right) : right ); - } - ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? (sep + ret) : "" ); - stringIndex -= curSize; - if ( curGroupIndex < groupSizes.length ) { - curSize = groupSizes[ curGroupIndex ]; - curGroupIndex++; + // @path: normalized path + var resourceSet = function( data, path, value ) { + var i, + node = data, + length = path.length; + + for ( i = 0; i < length - 1; i++ ) { + if ( !node[ path[ i ] ] ) { + node[ path[ i ] ] = {}; } + node = node[ path[ i ] ]; } - - return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right; + node[ path[ i ] ] = value; }; - formatNumber = function( value, format, culture ) { - if ( !isFinite(value) ) { - if ( value === Infinity ) { - return culture.numberFormat.positiveInfinity; - } - if ( value === -Infinity ) { - return culture.numberFormat.negativeInfinity; - } - return culture.numberFormat.NaN; + + + + var arrayForEach = function( array, callback ) { + var i, length; + if ( array.forEach ) { + return array.forEach( callback ); } - if ( !format || format === "i" ) { - return culture.name.length ? value.toLocaleString() : value.toString(); + for ( i = 0, length = array.length; i < length; i++ ) { + callback( array[ i ], i, array ); } - format = format || "D"; - - var nf = culture.numberFormat, - number = Math.abs( value ), - precision = -1, - pattern; - if ( format.length > 1 ) precision = parseInt( format.slice(1), 10 ); - - var current = format.charAt( 0 ).toUpperCase(), - formatInfo; - - switch ( current ) { - case "D": - pattern = "n"; - number = truncate( number ); - if ( precision !== -1 ) { - number = zeroPad( "" + number, precision, true ); + }; + + + var jsonMerge = (function() { + + // Returns new deeply merged JSON. + // + // Eg. + // merge( { a: { b: 1, c: 2 } }, { a: { b: 3, d: 4 } } ) + // -> { a: { b: 3, c: 2, d: 4 } } + // + // @arguments JSON's + // + var merge = function() { + var destination = {}, + sources = [].slice.call( arguments, 0 ); + arrayForEach( sources, function( source ) { + var prop; + for ( prop in source ) { + if ( prop in destination && arrayIsArray( destination[ prop ] ) ) { + + // Concat Arrays + destination[ prop ] = destination[ prop ].concat( source[ prop ] ); + + } else if ( prop in destination && typeof destination[ prop ] === "object" ) { + + // Merge Objects + destination[ prop ] = merge( destination[ prop ], source[ prop ] ); + + } else { + + // Set new values + destination[ prop ] = source[ prop ]; + } - if ( value < 0 ) number = "-" + number; - break; - case "N": - formatInfo = nf; - /* falls through */ - case "C": - formatInfo = formatInfo || nf.currency; - /* falls through */ - case "P": - formatInfo = formatInfo || nf.percent; - pattern = value < 0 ? formatInfo.pattern[ 0 ] : ( formatInfo.pattern[1] || "n" ); - if ( precision === -1 ) precision = formatInfo.decimals; - number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo ); - break; - default: - throw "Bad number format specifier: " + current; + } + }); + return destination; + }; + + return merge; + +}()); + var itemLookup = (function() { + + var lookup; + + lookup = function( Cldr, locale, path, attributes, childLocale ) { + var normalizedPath, parent, value; + + // 1: Finish recursion + // 2: Avoid infinite loop + if ( typeof locale === "undefined" /* 1 */ || locale === childLocale /* 2 */ ) { + return; } - var patternParts = /n|\$|-|%/g, - ret = ""; - for ( ; ; ) { - var index = patternParts.lastIndex, - ar = patternParts.exec( pattern ); + // Resolve path + normalizedPath = pathNormalize( path, attributes ); - ret += pattern.slice( index, ar ? ar.index : pattern.length ); + // Check resolved (cached) data first + value = resourceGet( Cldr._resolved, normalizedPath ); + if ( value ) { + return value; + } - if ( !ar ) { - break; - } + // Check raw data + value = resourceGet( Cldr._raw, normalizedPath ); - switch ( ar[0] ) { - case "n": - ret += number; - break; - case "$": - ret += nf.currency.symbol; - break; - case "-": - // don't make 0 negative - if ( /[1-9]/.test(number) ) { - ret += nf[ "-" ]; - } - break; - case "%": - ret += nf.percent.symbol; - break; - } + if ( !value ) { + // Or, lookup at parent locale + parent = bundleParentLookup( Cldr, locale ); + value = lookup( Cldr, parent, path, jsonMerge( attributes, { languageId: parent }), locale ); } - return ret; + // Set resolved (cached) + resourceSet( Cldr._resolved, normalizedPath, value ); + + return value; }; + return lookup; + }()); -getTokenRegExp = function() { - // regular expression for matching date and time tokens in format strings. - return (/\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g); -}; - -getEra = function( date, eras ) { - if ( !eras ) return 0; - var start, ticks = date.getTime(); - for ( var i = 0, l = eras.length; i < l; i++ ) { - start = eras[ i ].start; - if ( start === null || ticks >= start ) { - return i; - } - } - return 0; -}; - -getEraYear = function( date, cal, era, sortable ) { - var year = date.getFullYear(); - if ( !sortable && cal.eras ) { - // convert normal gregorian year to era-shifted gregorian - // year by subtracting the era offset - year -= cal.eras[ era ].offset; - } - return year; -}; - -// parseExact -(function() { - var expandYear, - getDayIndex, - getMonthIndex, - getParseRegExp, - outOfRange, - toUpper, - toUpperArray; - - expandYear = function( cal, year ) { - // expands 2-digit year into 4 digits. - if ( year < 100 ) { - var now = new Date(), - era = getEra( now ), - curr = getEraYear( now, cal, era ), - twoDigitYearMax = cal.twoDigitYearMax; - twoDigitYearMax = typeof twoDigitYearMax === "string" ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax; - year += curr - ( curr % 100 ); - if ( year > twoDigitYearMax ) { - year -= 100; - } - } - return year; + + var itemGetResolved = function( Cldr, path, attributes ) { + // Resolve path + var normalizedPath = pathNormalize( path, attributes ); + + return resourceGet( Cldr._resolved, normalizedPath ); }; - getDayIndex = function ( cal, value, abbr ) { - var ret, - days = cal.days, - upperDays = cal._upperDays; - if ( !upperDays ) { - cal._upperDays = upperDays = [ - toUpperArray( days.names ), - toUpperArray( days.namesAbbr ), - toUpperArray( days.namesShort ) - ]; - } - value = toUpper( value ); - if ( abbr ) { - ret = arrayIndexOf( upperDays[1], value ); - if ( ret === -1 ) { - ret = arrayIndexOf( upperDays[2], value ); - } - } - else { - ret = arrayIndexOf( upperDays[0], value ); + + + + var Cldr = function() { + init.apply( this, arguments ); + }; + + Cldr._resolved = {}; + Cldr._raw = {}; + + // Load resolved or unresolved cldr data + // @json [JSON] + Cldr.load = function( json ) { + if ( typeof json !== "object" ) { + throw new Error( "invalid json" ); } - return ret; + Cldr._raw = jsonMerge( Cldr._raw, json ); }; - getMonthIndex = function( cal, value, abbr ) { - var months = cal.months, - monthsGen = cal.monthsGenitive || cal.months, - upperMonths = cal._upperMonths, - upperMonthsGen = cal._upperMonthsGen; - if ( !upperMonths ) { - cal._upperMonths = upperMonths = [ - toUpperArray( months.names ), - toUpperArray( months.namesAbbr ) - ]; - cal._upperMonthsGen = upperMonthsGen = [ - toUpperArray( monthsGen.names ), - toUpperArray( monthsGen.namesAbbr ) - ]; + Cldr.prototype.get = function( path ) { + // Simplify locale using languageId (there are no other resource bundles) + // 1: during init(), get is called, but languageId is not defined. Use "" as a workaround in this very specific scenario. + var locale = this.attributes && this.attributes.languageId || "" /* 1 */; + + return itemGetResolved( Cldr, path, this.attributes ) || + itemLookup( Cldr, locale, path, this.attributes ); + }; + + common( Cldr ); + + return Cldr; + + + +}()); + + + var arrayMap = function( array, callback ) { + var clone, i, length; + if ( array.map ) { + return array.map( callback ); } - value = toUpper( value ); - var i = arrayIndexOf( abbr ? upperMonths[1] : upperMonths[0], value ); - if ( i < 0 ) { - i = arrayIndexOf( abbr ? upperMonthsGen[1] : upperMonthsGen[0], value ); + for ( clone = [], i = 0, length = array.length; i < length; i++ ) { + clone[ i ] = callback( array[ i ], i, array ); } - return i; + return clone; }; - getParseRegExp = function( cal, format ) { - // converts a format string into a regular expression with groups that - // can be used to extract date fields from a date string. - // check for a cached parse regex. - var re = cal._parseRegExp; - if ( !re ) { - cal._parseRegExp = re = {}; - } - else { - var reFormat = re[ format ]; - if ( reFormat ) { - return reFormat; - } + + + + var objectValues = function( object ) { + var i, + result = []; + + for ( i in object ) { + result.push( object[ i ] ); } - // expand single digit formats, then escape regular expression characters. - var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ), - regexp = [ "^" ], - groups = [], - index = 0, - quoteCount = 0, - tokenRegExp = getTokenRegExp(), - match; - - // iterate through each date token found. - while ( (match = tokenRegExp.exec(expFormat)) !== null ) { - var preMatch = expFormat.slice( index, match.index ); - index = tokenRegExp.lastIndex; - - // don't replace any matches that occur inside a string literal. - quoteCount += appendPreOrPostMatch( preMatch, regexp ); - if ( quoteCount % 2 ) { - regexp.push( match[0] ); - continue; + return result; + }; + + + + + /** + * allPreset() + * + * @cldr [Cldr instance]. + * + * Return an Array with all (skeleton, date, time, datetime) presets. + */ + var datetimeAllPresets = function( cldr ) { + var result = []; + + // Skeleton + result = objectValues( cldr.main( "dates/calendars/gregorian/dateTimeFormats/availableFormats" ) ); + + // Time + result = result.concat( objectValues( cldr.main( "dates/calendars/gregorian/timeFormats" ) ) ); + + // Date + result = result.concat( objectValues( cldr.main( "dates/calendars/gregorian/dateFormats" ) ) ); + + // Datetime + result = result.concat( arrayMap( objectValues( cldr.main( "dates/calendars/gregorian/dateTimeFormats" ) ), function( datetimeFormat, key ) { + if ( typeof datetimeFormat !== "string" ) { + return datetimeFormat; } + return datetimeFormat + .replace( /\{0\}/, cldr.main([ + "dates/calendars/gregorian/timeFormats", + key + ])) + .replace( /\{1\}/, cldr.main([ + "dates/calendars/gregorian/dateFormats", + key + ])); + })); + + return arrayMap( result, function( pattern ) { + return { pattern: pattern }; + }); + }; - // add a regex group for the token. - var m = match[ 0 ], - len = m.length, - add; - switch ( m ) { - case "dddd": case "ddd": - case "MMMM": case "MMM": - case "gg": case "g": - add = "(\\D+)"; - break; - case "tt": case "t": - add = "(\\D*)"; - break; - case "yyyy": - case "fff": - case "ff": - case "f": - add = "(\\d{" + len + "})"; - break; - case "dd": case "d": - case "MM": case "M": - case "yy": case "y": - case "HH": case "H": - case "hh": case "h": - case "mm": case "m": - case "ss": case "s": - add = "(\\d\\d?)"; + + + + /** + * expandPattern( pattern, cldr ) + * + * @pattern [String or Object] if String, it's considered a skeleton. Object accepts: + * - skeleton: [String] lookup availableFormat; + * - date: [String] ( "full" | "long" | "medium" | "short" ); + * - time: [String] ( "full" | "long" | "medium" | "short" ); + * - datetime: [String] ( "full" | "long" | "medium" | "short" ); + * - pattern: [String] For more info see datetime/format.js. + * + * @cldr [Cldr instance]. + * + * Return the corresponding pattern. + * Eg for "en": + * - "GyMMMd" returns "MMM d, y G"; + * - { skeleton: "GyMMMd" } returns "MMM d, y G"; + * - { date: "full" } returns "EEEE, MMMM d, y"; + * - { time: "full" } returns "h:mm:ss a zzzz"; + * - { datetime: "full" } returns "EEEE, MMMM d, y 'at' h:mm:ss a zzzz"; + * - { pattern: "dd/mm" } returns "dd/mm"; + */ + var datetimeExpandPattern = function( pattern, cldr ) { + var result; + + if ( typeof pattern === "string" ) { + pattern = { skeleton: pattern }; + } + + if ( typeof pattern === "object" ) { + + switch ( true ) { + case "skeleton" in pattern: + result = cldr.main([ + "dates/calendars/gregorian/dateTimeFormats/availableFormats", + pattern.skeleton + ]); break; - case "zzz": - add = "([+-]?\\d\\d?:\\d{2})"; + + case "date" in pattern: + case "time" in pattern: + result = cldr.main([ + "dates/calendars/gregorian", + "date" in pattern ? "dateFormats" : "timeFormats", + ( pattern.date || pattern.time ) + ]); break; - case "zz": case "z": - add = "([+-]?\\d\\d?)"; + + case "datetime" in pattern: + result = cldr.main([ + "dates/calendars/gregorian/dateTimeFormats", + pattern.datetime + ]); + if ( result ) { + result = result + .replace( /\{0\}/, cldr.main([ + "dates/calendars/gregorian/timeFormats", + pattern.datetime + ])) + .replace( /\{1\}/, cldr.main([ + "dates/calendars/gregorian/dateFormats", + pattern.datetime + ])); + } break; - case "/": - add = "(\\/)"; + + case "pattern" in pattern: + result = pattern.pattern; break; + default: - throw "Invalid date format pattern \'" + m + "\'."; + throw new Error( "Invalid pattern" ); } - if ( add ) { - regexp.push( add ); - } - groups.push( match[0] ); + + } else { + throw new Error( "Invalid pattern" ); } - appendPreOrPostMatch( expFormat.slice(index), regexp ); - regexp.push( "$" ); - // allow whitespace to differ when matching formats. - var regexpStr = regexp.join( "" ).replace( /\s+/g, "\\s+" ), - parseRegExp = { "regExp": regexpStr, "groups": groups }; + if ( !result ) { + throw new Error( "Pattern not found" ); + } - // cache the regex for this format. - return re[ format ] = parseRegExp; + return result; }; - outOfRange = function( value, low, high ) { - return value < low || value > high; + + + var datetimeWeekDays = [ "sun", "mon", "tue", "wed", "thu", "fri", "sat" ]; + + + + var arrayIndexOf = function( array, item ) { + if ( array.indexOf ) { + return array.indexOf( item ); + } + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[i] === item ) { + return i; + } + } + return -1; }; - toUpper = function( value ) { - // "he-IL" has non-breaking space in weekday names. - return value.split( "\u00A0" ).join( " " ).toUpperCase(); + + + + /** + * firstDayOfWeek + */ + var datetimeFirstDayOfWeek = function( cldr ) { + return arrayIndexOf( datetimeWeekDays, cldr.supplemental.weekData.firstDay() ); }; - toUpperArray = function( arr ) { - var results = []; - for ( var i = 0, l = arr.length; i < l; i++ ) { - results[ i ] = toUpper( arr[i] ); - } - return results; + + + + /** + * dayOfWeek + * + * Return the day of the week normalized by the territory's firstDay [0-6]. + * Eg for "mon": + * - return 0 if territory is GB, or BR, or DE, or FR (week starts on "mon"); + * - return 1 if territory is US (week starts on "sun"); + * - return 2 if territory is EG (week starts on "sat"); + */ + var datetimeDayOfWeek = function( date, cldr ) { + return ( date.getDay() - datetimeFirstDayOfWeek( cldr ) + 7 ) % 7; }; - parseExact = function( value, format, culture ) { - // try to parse the date string by matching against the format string - // while using the specified culture for date field names. - value = trim( value ); - var cal = culture.calendar, - // convert date formats into regular expressions with groupings. - // use the regexp to determine the input format and extract the date fields. - parseInfo = getParseRegExp( cal, format ), - match = new RegExp( parseInfo.regExp ).exec( value ); - if ( match === null ) { - return null; - } - // found a date format that matches the input. - var groups = parseInfo.groups, - era = null, year = null, month = null, date = null, weekDay = null, - hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null, - pmHour = false; - // iterate the format groups to extract and set the date fields. - for ( var j = 0, jl = groups.length; j < jl; j++ ) { - var matchGroup = match[ j + 1 ]; - if ( matchGroup ) { - var current = groups[ j ], - clength = current.length, - matchInt = parseInt( matchGroup, 10 ); - switch ( current ) { - case "dd": case "d": - // Day of month. - date = matchInt; - // check that date is generally in valid range, also checking overflow below. - if ( outOfRange(date, 1, 31) ) return null; - break; - case "MMM": case "MMMM": - month = getMonthIndex( cal, matchGroup, clength === 3 ); - if ( outOfRange(month, 0, 11) ) return null; - break; - case "M": case "MM": - // Month. - month = matchInt - 1; - if ( outOfRange(month, 0, 11) ) return null; - break; - case "y": case "yy": - case "yyyy": - year = clength < 4 ? expandYear( cal, matchInt ) : matchInt; - if ( outOfRange(year, 0, 9999) ) return null; - break; - case "h": case "hh": - // Hours (12-hour clock). - hour = matchInt; - if ( hour === 12 ) hour = 0; - if ( outOfRange(hour, 0, 11) ) return null; - break; - case "H": case "HH": - // Hours (24-hour clock). - hour = matchInt; - if ( outOfRange(hour, 0, 23) ) return null; - break; - case "m": case "mm": - // Minutes. - min = matchInt; - if ( outOfRange(min, 0, 59) ) return null; - break; - case "s": case "ss": - // Seconds. - sec = matchInt; - if ( outOfRange(sec, 0, 59) ) return null; - break; - case "tt": case "t": - // AM/PM designator. - // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of - // the AM tokens. If not, fail the parse for this format. - pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] ); - if ( - !pmHour && ( - !cal.AM || ( matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2] ) - ) - ) return null; - break; - case "f": - // Deciseconds. - case "ff": - // Centiseconds. - case "fff": - // Milliseconds. - msec = matchInt * Math.pow( 10, 3 - clength ); - if ( outOfRange(msec, 0, 999) ) return null; - break; - case "ddd": - // Day of week. - case "dddd": - // Day of week. - weekDay = getDayIndex( cal, matchGroup, clength === 3 ); - if ( outOfRange(weekDay, 0, 6) ) return null; - break; - case "zzz": - // Time zone offset in +/- hours:min. - var offsets = matchGroup.split( /:/ ); - if ( offsets.length !== 2 ) return null; - hourOffset = parseInt( offsets[0], 10 ); - if ( outOfRange(hourOffset, -12, 13) ) return null; - var minOffset = parseInt( offsets[1], 10 ); - if ( outOfRange(minOffset, 0, 59) ) return null; - tzMinOffset = ( hourOffset * 60 ) + ( startsWith(matchGroup, "-") ? -minOffset : minOffset ); - break; - case "z": case "zz": - // Time zone offset in +/- hours. - hourOffset = matchInt; - if ( outOfRange(hourOffset, -12, 13) ) return null; - tzMinOffset = hourOffset * 60; - break; - case "g": case "gg": - var eraName = matchGroup; - if ( !eraName || !cal.eras ) return null; - eraName = trim( eraName.toLowerCase() ); - for ( var i = 0, l = cal.eras.length; i < l; i++ ) { - if ( eraName === cal.eras[i].name.toLowerCase() ) { - era = i; - break; - } - } - // could not find an era with that name - if ( era === null ) return null; - break; - } - } - } - var result = new Date(), defaultYear, convert = cal.convert; - defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear(); - if ( year === null ) { - year = defaultYear; - } - else if ( cal.eras ) { - // year must be shifted to normal gregorian year - // but not if year was not specified, its already normal gregorian - // per the main if clause above. - year += cal.eras[( era || 0 )].offset; - } - // set default day and month to 1 and January, so if unspecified, these are the defaults - // instead of the current day/month. - if ( month === null ) { - month = 0; - } - if ( date === null ) { - date = 1; - } - // now have year, month, and date, but in the culture's calendar. - // convert to gregorian if necessary - if ( convert ) { - result = convert.toGregorian( year, month, date ); - // conversion failed, must be an invalid match - if ( result === null ) return null; - } - else { - // have to set year, month and date together to avoid overflow based on current date. - result.setFullYear( year, month, date ); - // check to see if date overflowed for specified month (only checked 1-31 above). - if ( result.getDate() !== date ) return null; - // invalid day of week. - if ( weekDay !== null && result.getDay() !== weekDay ) { - return null; - } + + + + /** + * distanceInDays( from, to ) + * + * Return the distance in days between from and to Dates. + */ + var datetimeDistanceInDays = function( from, to ) { + var inDays = 864e5; + return ( to.getTime() - from.getTime() ) / inDays; + }; + + + + + /** + * startOf + * + * Return the + */ + var datetimeStartOf = function( date, unit ) { + date = new Date( date.getTime() ); + switch( unit ) { + case "year": + date.setMonth( 0 ); + /* falls through */ + case "month": + date.setDate( 1 ); + /* falls through */ + case "day": + date.setHours( 0 ); + /* falls through */ + case "hour": + date.setMinutes( 0 ); + /* falls through */ + case "minute": + date.setSeconds( 0 ); + /* falls through */ + case "second": + date.setMilliseconds( 0 ); } - // if pm designator token was found make sure the hours fit the 24-hour clock. - if ( pmHour && hour < 12 ) { - hour += 12; + return date; + }; + + + + + /** + * dayOfYear + * + * Return the distance in days of the date to the begin of the year [0-d]. + */ + var datetimeDayOfYear = function( date ) { + return Math.floor( datetimeDistanceInDays( datetimeStartOf( date, "year" ), date ) ); + }; + + + + + /** + * millisecondsInDay + */ + var datetimeMillisecondsInDay = function( date ) { + // TODO Handle daylight savings discontinuities + return date - datetimeStartOf( date, "day" ); + }; + + + + var datetimePatternRe = (/([a-z])\1*|'[^']+'|''|./ig); + + + + var stringPad = function( str, count, right ) { + var length; + if ( typeof str !== "string" ) { + str = String( str ); } - result.setHours( hour, min, sec, msec ); - if ( tzMinOffset !== null ) { - // adjust timezone to utc before applying local offset. - var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() ); - // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours - // to ensure both these fields will not exceed this range. adjustedMin will range - // somewhere between -1440 and 1500, so we only need to split this into hours. - result.setHours( result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60 ); + for ( length = str.length; length < count; length += 1 ) { + str = ( right ? ( str + "0" ) : ( "0" + str ) ); } - return result; + return str; }; -}()); -parseNegativePattern = function( value, nf, negativePattern ) { - var neg = nf[ "-" ], - pos = nf[ "+" ], - ret; - switch ( negativePattern ) { - case "n -": - neg = " " + neg; - pos = " " + pos; - /* falls through */ - case "n-": - if ( endsWith(value, neg) ) { - ret = [ "-", value.substr(0, value.length - neg.length) ]; - } - else if ( endsWith(value, pos) ) { - ret = [ "+", value.substr(0, value.length - pos.length) ]; - } - break; - case "- n": - neg += " "; - pos += " "; - /* falls through */ - case "-n": - if ( startsWith(value, neg) ) { - ret = [ "-", value.substr(neg.length) ]; - } - else if ( startsWith(value, pos) ) { - ret = [ "+", value.substr(pos.length) ]; - } - break; - case "(n)": - if ( startsWith(value, "(") && endsWith(value, ")") ) { - ret = [ "-", value.substr(1, value.length - 2) ]; - } - break; - } - return ret || [ "", value ]; -}; - -// -// public instance functions -// - -Globalize.prototype.findClosestCulture = function( cultureSelector ) { - return Globalize.findClosestCulture.call( this, cultureSelector ); -}; - -Globalize.prototype.format = function( value, format, cultureSelector ) { - return Globalize.format.call( this, value, format, cultureSelector ); -}; - -Globalize.prototype.localize = function( key, cultureSelector ) { - return Globalize.localize.call( this, key, cultureSelector ); -}; - -Globalize.prototype.parseInt = function( value, radix, cultureSelector ) { - return Globalize.parseInt.call( this, value, radix, cultureSelector ); -}; - -Globalize.prototype.parseFloat = function( value, radix, cultureSelector ) { - return Globalize.parseFloat.call( this, value, radix, cultureSelector ); -}; - -Globalize.prototype.culture = function( cultureSelector ) { - return Globalize.culture.call( this, cultureSelector ); -}; - -// -// public singleton functions -// - -Globalize.addCultureInfo = function( cultureName, baseCultureName, info ) { - - var base = {}, - isNew = false; - - if ( typeof cultureName !== "string" ) { - // cultureName argument is optional string. If not specified, assume info is first - // and only argument. Specified info deep-extends current culture. - info = cultureName; - cultureName = this.culture().name; - base = this.cultures[ cultureName ]; - } else if ( typeof baseCultureName !== "string" ) { - // baseCultureName argument is optional string. If not specified, assume info is second - // argument. Specified info deep-extends specified culture. - // If specified culture does not exist, create by deep-extending default - info = baseCultureName; - isNew = ( this.cultures[ cultureName ] == null ); - base = this.cultures[ cultureName ] || this.cultures[ "default" ]; - } else { - // cultureName and baseCultureName specified. Assume a new culture is being created - // by deep-extending an specified base culture - isNew = true; - base = this.cultures[ baseCultureName ]; - } - this.cultures[ cultureName ] = extend(true, {}, - base, - info - ); - // Make the standard calendar the current culture if it's a new culture - if ( isNew ) { - this.cultures[ cultureName ].calendar = this.cultures[ cultureName ].calendars.standard; - } -}; -Globalize.findClosestCulture = function( name ) { - var match; - if ( !name ) { - return this.findClosestCulture( this.cultureSelector ) || this.cultures[ "default" ]; - } - if ( typeof name === "string" ) { - name = name.split( "," ); - } - if ( isArray(name) ) { - var lang, - cultures = this.cultures, - list = name, - i, l = list.length, - prioritized = []; - for ( i = 0; i < l; i++ ) { - name = trim( list[i] ); - var pri, parts = name.split( ";" ); - lang = trim( parts[0] ); - if ( parts.length === 1 ) { - pri = 1; + + /** + * format( date, pattern, cldr ) + * + * @date [Date instance]. + * + * @pattern [String] raw pattern. + * ref: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns + * + * @cldr [Cldr instance]. + * + * TODO Support other calendar types. + * + * Disclosure: this function borrows excerpts of dojo/date/locale. + */ + var datetimeFormat = function( date, pattern, cldr ) { + var widths = [ "abbreviated", "wide", "narrow" ]; + return pattern.replace( datetimePatternRe, function( current ) { + var pad, ret, + chr = current.charAt( 0 ), + length = current.length; + + if ( chr === "j" ) { + // Locale preferred hHKk. + // http://www.unicode.org/reports/tr35/tr35-dates.html#Time_Data + chr = cldr.supplemental.timeData.preferred(); } - else { - name = trim( parts[1] ); - if ( name.indexOf("q=") === 0 ) { - name = name.substr( 2 ); - pri = parseFloat( name ); - pri = isNaN( pri ) ? 0 : pri; - } - else { - pri = 1; - } + + switch ( chr ) { + + // Era + case "G": + ret = cldr.main([ + "dates/calendars/gregorian/eras", + length <= 3 ? "eraAbbr" : ( length === 4 ? "eraNames" : "eraNarrow" ), + date.getFullYear() < 0 ? 0 : 1 + ]); + break; + + // Year + case "y": + // Plain year. + // The length specifies the padding, but for two letters it also specifies the maximum length. + ret = String( date.getFullYear() ); + pad = true; + if ( length === 2 ) { + ret = ret.substr( ret.length - 2 ); + } + break; + + case "Y": + // Year in "Week of Year" + // The length specifies the padding, but for two letters it also specifies the maximum length. + // yearInWeekofYear = date + DaysInAWeek - (dayOfWeek - firstDay) - minDays + ret = new Date( date.getTime() ); + ret.setDate( ret.getDate() + 7 - ( datetimeDayOfWeek( date, cldr ) - datetimeFirstDayOfWeek( cldr ) ) - cldr.supplemental.weekData.minDays() ); + ret = String( ret.getFullYear() ); + pad = true; + if ( length === 2 ) { + ret = ret.substr( ret.length - 2 ); + } + break; + + case "u": // Extended year. Need to be implemented. + case "U": // Cyclic year name. Need to be implemented. + throw new Error( "Not implemented" ); + + // Quarter + case "Q": + case "q": + ret = Math.ceil( ( date.getMonth() + 1 ) / 3 ); + if ( length <= 2 ) { + pad = true; + } else { + // http://unicode.org/cldr/trac/ticket/6788 + ret = cldr.main([ + "dates/calendars/gregorian/quarters", + chr === "Q" ? "format" : "stand-alone", + widths[ length - 3 ], + ret + ]); + } + break; + + // Month + case "M": + case "L": + ret = date.getMonth() + 1; + if ( length <= 2 ) { + pad = true; + } else { + ret = cldr.main([ + "dates/calendars/gregorian/months", + chr === "M" ? "format" : "stand-alone", + widths[ length - 3 ], + ret + ]); + } + break; + + // Week + case "w": + // Week of Year. + // woy = ceil( ( doy + dow of 1/1 ) / 7 ) - minDaysStuff ? 1 : 0. + // TODO should pad on ww? Not documented, but I guess so. + ret = datetimeDayOfWeek( datetimeStartOf( date, "year" ), cldr ); + ret = Math.ceil( ( datetimeDayOfYear( date ) + ret ) / 7 ) - ( 7 - ret >= cldr.supplemental.weekData.minDays() ? 0 : 1 ); + pad = true; + break; + + case "W": + // Week of Month. + // wom = ceil( ( dom + dow of `1/month` ) / 7 ) - minDaysStuff ? 1 : 0. + ret = datetimeDayOfWeek( datetimeStartOf( date, "month" ), cldr ); + ret = Math.ceil( ( date.getDate() + ret ) / 7 ) - ( 7 - ret >= cldr.supplemental.weekData.minDays() ? 0 : 1 ); + break; + + // Day + case "d": + ret = date.getDate(); + pad = true; + break; + + case "D": + ret = datetimeDayOfYear( date ) + 1; + pad = true; + break; + + case "F": + // Day of Week in month. eg. 2nd Wed in July. + ret = Math.floor( date.getDate() / 7 ) + 1; + break; + + case "g+": + // Modified Julian day. Need to be implemented. + throw new Error( "Not implemented" ); + + // Week day + case "e": + case "c": + if ( length <= 2 ) { + // Range is [1-7] (deduced by example provided on documentation) + // TODO Should pad with zeros (not specified in the docs)? + ret = datetimeDayOfWeek( date, cldr ) + 1; + pad = true; + break; + } + + /* falls through */ + case "E": + ret = datetimeWeekDays[ date.getDay() ]; + if ( length === 6 ) { + // If short day names are not explicitly specified, abbreviated day names are used instead. + // http://www.unicode.org/reports/tr35/tr35-dates.html#months_days_quarters_eras + // http://unicode.org/cldr/trac/ticket/6790 + ret = cldr.main([ + "dates/calendars/gregorian/days", + [ chr === "c" ? "stand-alone" : "format" ], + "short", + ret + ]) || cldr.main([ + "dates/calendars/gregorian/days", + [ chr === "c" ? "stand-alone" : "format" ], + "abbreviated", + ret + ]); + } else { + ret = cldr.main([ + "dates/calendars/gregorian/days", + [ chr === "c" ? "stand-alone" : "format" ], + widths[ length < 3 ? 0 : length - 3 ], + ret + ]); + } + break; + + // Period (AM or PM) + case "a": + ret = cldr.main([ + "dates/calendars/gregorian/dayPeriods/format/wide", + date.getHours() < 12 ? "am" : "pm" + ]); + break; + + // Hour + case "h": // 1-12 + ret = ( date.getHours() % 12 ) || 12; + pad = true; + break; + + case "H": // 0-23 + ret = date.getHours(); + pad = true; + break; + + case "K": // 0-11 + ret = date.getHours() % 12; + pad = true; + break; + + case "k": // 1-24 + ret = date.getHours() || 24; + pad = true; + break; + + // Minute + case "m": + ret = date.getMinutes(); + pad = true; + break; + + // Second + case "s": + ret = date.getSeconds(); + pad = true; + break; + + case "S": + ret = Math.round( date.getMilliseconds() * Math.pow( 10, length - 3 ) ); + pad = true; + break; + + case "A": + ret = Math.round( datetimeMillisecondsInDay( date ) * Math.pow( 10, length - 3 ) ); + pad = true; + break; + + // Zone + // see http://www.unicode.org/reports/tr35/tr35-dates.html#Using_Time_Zone_Names ? + // Need to be implemented. + case "z": + case "Z": + case "O": + case "v": + case "V": + case "X": + case "x": + throw new Error( "Not implemented" ); + + // Anything else is considered a literal, including [ ,:/.'@#], chinese, japonese, and arabic characters. + default: + return current; } - prioritized.push({ lang: lang, pri: pri }); - } - prioritized.sort(function( a, b ) { - if ( a.pri < b.pri ) { - return 1; - } else if ( a.pri > b.pri ) { - return -1; + if ( pad ) { + ret = stringPad( ret, length ); } - return 0; + return ret; }); - // exact match - for ( i = 0; i < l; i++ ) { - lang = prioritized[ i ].lang; - match = cultures[ lang ]; - if ( match ) { - return match; + }; + + + + + var arrayEvery = function( array, callback ) { + var i, length; + if ( array.every ) { + return array.every( callback ); + } + for ( i = 0, length = array.length; i < length; i++ ) { + if ( !callback( array[ i ], i, array ) ) { + return false; } } + return true; + }; - // neutral language match - for ( i = 0; i < l; i++ ) { - lang = prioritized[ i ].lang; - do { - var index = lang.lastIndexOf( "-" ); - if ( index === -1 ) { - break; + + + + /** + * tokenizer( value, pattern ) + * + * Returns an Array of tokens, eg. value "5 o'clock PM", pattern "h 'o''clock' a": + * [{ + * type: "h", + * lexeme: "5" + * }, { + * type: "literal", + * lexeme: " " + * }, { + * type: "literal", + * lexeme: "o'clock" + * }, { + * type: "literal", + * lexeme: " " + * }, { + * type: "a", + * lexeme: "PM", + * value: "pm" + * }] + * + * OBS: lexeme's are always String and may return invalid ranges depending of the token type. Eg. "99" for month number. + * + * Return an empty Array when not successfully parsed. + */ + var datetimeTokenizer = function( value, pattern, cldr ) { + var valid, + tokens = [], + widths = [ "abbreviated", "wide", "narrow" ]; + + valid = arrayEvery( pattern.match( datetimePatternRe ), function( current ) { + var chr, length, tokenRe, + token = {}; + + function oneDigitIfLengthOne() { + if ( length === 1 ) { + return tokenRe = /\d/; } - // strip off the last part. e.g. en-US => en - lang = lang.substr( 0, index ); - match = cultures[ lang ]; - if ( match ) { - return match; + } + + function oneOrTwoDigitsIfLengthOne() { + if ( length === 1 ) { + return tokenRe = /\d\d?/; } } - while ( 1 ); - } - // last resort: match first culture using that language - for ( i = 0; i < l; i++ ) { - lang = prioritized[ i ].lang; - for ( var cultureKey in cultures ) { - var culture = cultures[ cultureKey ]; - if ( culture.language === lang ) { - return culture; + function twoDigitsIfLengthTwo() { + if ( length === 2 ) { + return tokenRe = /\d\d/; } } - } - } - else if ( typeof name === "object" ) { - return name; - } - return match || null; -}; -Globalize.format = function( value, format, cultureSelector ) { - var culture = this.findClosestCulture( cultureSelector ); - if ( value instanceof Date ) { - value = formatDate( value, format, culture ); - } - else if ( typeof value === "number" ) { - value = formatNumber( value, format, culture ); - } - return value; -}; + // Brute-force test every locale entry in an attempt to match the given value. + // Return the first found one (and set token accordingly), or null. + function lookup( path ) { + var i, re, + data = cldr.main( path ); + for ( i in data ) { + re = new RegExp( "^" + data[ i ] ); + if ( re.test( value ) ) { + token.value = i; + return tokenRe = new RegExp( data[ i ] ); + } + } + return null; + } -Globalize.localize = function( key, cultureSelector ) { - return this.findClosestCulture( cultureSelector ).messages[ key ] || - this.cultures[ "default" ].messages[ key ]; -}; + token.type = current; + chr = current.charAt( 0 ), + length = current.length; -Globalize.parseDate = function( value, formats, culture ) { - culture = this.findClosestCulture( culture ); + switch ( chr ) { - var date, prop, patterns; - if ( formats ) { - if ( typeof formats === "string" ) { - formats = [ formats ]; - } - if ( formats.length ) { - for ( var i = 0, l = formats.length; i < l; i++ ) { - var format = formats[ i ]; - if ( format ) { - date = parseExact( value, format, culture ); - if ( date ) { + // Era + case "G": + lookup([ + "dates/calendars/gregorian/eras", + length <= 3 ? "eraAbbr" : ( length === 4 ? "eraNames" : "eraNarrow" ) + ]); + break; + + // Year + case "y": + case "Y": + // number l=1:+, l=2:{2}, l=3:{3,}, l=4:{4,}, ... + if ( length === 1 ) { + tokenRe = /\d+/; + } else if ( length === 2 ) { + tokenRe = /\d\d/; + } else { + tokenRe = new RegExp( "\\d{" + length + ",}" ); + } + break; + + case "u": // Extended year. Need to be implemented. + case "U": // Cyclic year name. Need to be implemented. + throw new Error( "Not implemented" ); + + // Quarter + case "Q": + case "q": + // number l=1:{1}, l=2:{2}. + // lookup l=3... + oneDigitIfLengthOne() || twoDigitsIfLengthTwo() || lookup([ + "dates/calendars/gregorian/quarters", + chr === "Q" ? "format" : "stand-alone", + widths[ length - 3 ] + ]); + break; + + // Month + case "M": + case "L": + // number l=1:{1,2}, l=2:{2}. + // lookup l=3... + oneOrTwoDigitsIfLengthOne() || twoDigitsIfLengthTwo() || lookup([ + "dates/calendars/gregorian/months", + chr === "M" ? "format" : "stand-alone", + widths[ length - 3 ] + ]); + break; + + // Day (see d below) + case "D": + // number {l,3}. + if ( length <= 3 ) { + tokenRe = new RegExp( "\\d{" + length + ",3}" ); + } + break; + + case "W": + case "F": + // number l=1:{1}. + oneDigitIfLengthOne(); + break; + + case "g+": + // Modified Julian day. Need to be implemented. + throw new Error( "Not implemented" ); + + // Week day + case "e": + case "c": + // number l=1:{1}, l=2:{2}. + // lookup for length >=3. + if( length <= 2 ) { + oneDigitIfLengthOne() || twoDigitsIfLengthTwo(); break; } - } - } - } - } else { - patterns = culture.calendar.patterns; - for ( prop in patterns ) { - date = parseExact( value, patterns[prop], culture ); - if ( date ) { - break; + + /* falls through */ + case "E": + if ( length === 6 ) { + // Note: if short day names are not explicitly specified, abbreviated day names are used instead http://www.unicode.org/reports/tr35/tr35-dates.html#months_days_quarters_eras + lookup([ + "dates/calendars/gregorian/days", + [ chr === "c" ? "stand-alone" : "format" ], + "short" + ]) || lookup([ + "dates/calendars/gregorian/days", + [ chr === "c" ? "stand-alone" : "format" ], + "abbreviated" + ]); + } else { + lookup([ + "dates/calendars/gregorian/days", + [ chr === "c" ? "stand-alone" : "format" ], + widths[ length < 3 ? 0 : length - 3 ] + ]); + } + break; + + // Period (AM or PM) + case "a": + lookup([ + "dates/calendars/gregorian/dayPeriods/format/wide" + ]); + break; + + // Week, Day, Hour, Minute, or Second + case "w": + case "d": + case "h": + case "H": + case "K": + case "k": + case "j": + case "m": + case "s": + // number l1:{1,2}, l2:{2}. + oneOrTwoDigitsIfLengthOne() || twoDigitsIfLengthTwo(); + break; + + case "S": + // number {l}. + tokenRe = new RegExp( "\\d{" + length + "}" ); + break; + + case "A": + // number {l+5}. + tokenRe = new RegExp( "\\d{" + ( length + 5 ) + "}" ); + break; + + // Zone + // see http://www.unicode.org/reports/tr35/tr35-dates.html#Using_Time_Zone_Names ? + // Need to be implemented. + case "z": + case "Z": + case "O": + case "v": + case "V": + case "X": + case "x": + throw new Error( "Not implemented" ); + + case "'": + token.type = "literal"; + if ( current.charAt( 1 ) === "'" ) { + tokenRe = /'/; + } else { + tokenRe = /'[^']+'/; + } + break; + + default: + token.type = "literal"; + tokenRe = /./; } - } - } - return date || null; -}; + if ( !tokenRe ) { + return false; + } -Globalize.parseInt = function( value, radix, cultureSelector ) { - return truncate( Globalize.parseFloat(value, radix, cultureSelector) ); -}; + // Get lexeme and consume it. + value = value.replace( new RegExp( "^" + tokenRe.source ), function( lexeme ) { + token.lexeme = lexeme; + return ""; + }); -Globalize.parseFloat = function( value, radix, cultureSelector ) { - // radix argument is optional - if ( typeof radix !== "number" ) { - cultureSelector = radix; - radix = 10; - } + if ( !token.lexeme ) { + return false; + } - var culture = this.findClosestCulture( cultureSelector ); - var ret = NaN, - nf = culture.numberFormat; + tokens.push( token ); + return true; + }); - if ( value.indexOf(culture.numberFormat.currency.symbol) > -1 ) { - // remove currency symbol - value = value.replace( culture.numberFormat.currency.symbol, "" ); - // replace decimal seperator - value = value.replace( culture.numberFormat.currency["."], culture.numberFormat["."] ); - } + return valid ? tokens : []; + }; - //Remove percentage character from number string before parsing - if ( value.indexOf(culture.numberFormat.percent.symbol) > -1){ - value = value.replace( culture.numberFormat.percent.symbol, "" ); - } - // remove spaces: leading, trailing and between - and number. Used for negative currency pt-BR - value = value.replace( / /g, "" ); + var datetimeParse = (function() { - // allow infinity or hexidecimal - if ( regexInfinity.test(value) ) { - ret = parseFloat( value ); - } - else if ( !radix && regexHex.test(value) ) { - ret = parseInt( value, 16 ); + function outOfRange( value, low, high ) { + return value < low || value > high; } - else { - - // determine sign and number - var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ), - sign = signInfo[ 0 ], - num = signInfo[ 1 ]; - - // #44 - try parsing as "(n)" - if ( sign === "" && nf.pattern[0] !== "(n)" ) { - signInfo = parseNegativePattern( value, nf, "(n)" ); - sign = signInfo[ 0 ]; - num = signInfo[ 1 ]; - } - // try parsing as "-n" - if ( sign === "" && nf.pattern[0] !== "-n" ) { - signInfo = parseNegativePattern( value, nf, "-n" ); - sign = signInfo[ 0 ]; - num = signInfo[ 1 ]; + /** + * parse + * + * ref: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns + */ + return function( value, pattern, cldr ) { + var amPm, era, hour24, valid, + YEAR = 0, + MONTH = 1, + DAY = 2, + HOUR = 3, + MINUTE = 4, + SECOND = 5, + MILLISECONDS = 6, + date = new Date(), + tokens = datetimeTokenizer( value, pattern, cldr ), + truncateAt = [], + units = [ "year", "month", "day", "hour", "minute", "second", "milliseconds" ]; + + if ( !tokens.length ) { + return null; } - sign = sign || "+"; + valid = arrayEvery( tokens, function( token ) { + var century, chr, value, length; - // determine exponent and number - var exponent, - intAndFraction, - exponentPos = num.indexOf( "e" ); - if ( exponentPos < 0 ) exponentPos = num.indexOf( "E" ); - if ( exponentPos < 0 ) { - intAndFraction = num; - exponent = null; - } - else { - intAndFraction = num.substr( 0, exponentPos ); - exponent = num.substr( exponentPos + 1 ); - } - // determine decimal position - var integer, - fraction, - decSep = nf[ "." ], - decimalPos = intAndFraction.indexOf( decSep ); - if ( decimalPos < 0 ) { - integer = intAndFraction; - fraction = null; - } - else { - integer = intAndFraction.substr( 0, decimalPos ); - fraction = intAndFraction.substr( decimalPos + decSep.length ); + if ( token.type === "literal" ) { + // continue + return true; + } + + chr = token.type.charAt( 0 ); + length = token.type.length; + + if ( chr === "j" ) { + // Locale preferred hHKk. + // http://www.unicode.org/reports/tr35/tr35-dates.html#Time_Data + chr = cldr.supplemental.timeData.preferred(); + } + + switch ( chr ) { + + // Era + case "G": + truncateAt.push( YEAR ); + era = +token.value; + break; + + // Year + case "y": + value = +token.lexeme; + if ( length === 2 ) { + if ( outOfRange( value, 0, 99 ) ) { + return false; + } + // mimic dojo/date/locale: choose century to apply, according to a sliding window of 80 years before and 20 years after present year. + century = Math.floor( date.getFullYear() / 100 ) * 100; + value += century; + if ( value > date.getFullYear() + 20 ) { + value -= 100; + } + } + date.setFullYear( value ); + truncateAt.push( YEAR ); + break; + + case "Y": // Year in "Week of Year" + case "u": // Extended year. Need to be implemented. + case "U": // Cyclic year name. Need to be implemented. + throw new Error( "Not implemented" ); + + // Quarter (skip) + case "Q": + case "q": + break; + + // Month + case "M": + case "L": + if ( length <= 2 ) { + value = +token.lexeme; + } else { + value = +token.value; + } + if( outOfRange( value, 1, 12 ) ) { + return false; + } + date.setMonth( value - 1 ); + truncateAt.push( MONTH ); + break; + + // Week (skip) + case "w": // Week of Year. + case "W": // Week of Month. + break; + + // Day + case "d": + value = +token.lexeme; + if( outOfRange( value, 1, 31 ) ) { + return false; + } + date.setDate( value ); + truncateAt.push( DAY ); + break; + + case "D": + value = +token.lexeme; + if( outOfRange( value, 1, 366 ) ) { + return false; + } + date.setMonth(0); + date.setDate( value ); + truncateAt.push( DAY ); + break; + + case "F": + // Day of Week in month. eg. 2nd Wed in July. + // Skip + break; + + case "g+": + // Modified Julian day. Need to be implemented. + throw new Error( "Not implemented" ); + + // Week day + case "e": + case "c": + case "E": + // Skip. + // value = arrayIndexOf( datetimeWeekDays, token.value ); + break; + + // Period (AM or PM) + case "a": + amPm = token.value; + break; + + // Hour + case "K": // 0-11 + value = +token.lexeme + 1; + + /* falls through */ + case "h": // 1-12 + value = value || +token.lexeme; + if( outOfRange( value, 1, 12 ) ) { + return false; + } + date.setHours( value ); + truncateAt.push( HOUR ); + break; + + case "H": // 0-23 + value = +token.lexeme + 1; + + /* falls through */ + case "k": // 1-24 + hour24 = true; + value = value || +token.lexeme; + if( outOfRange( value, 1, 24 ) ) { + return false; + } + date.setHours( value ); + truncateAt.push( HOUR ); + break; + + // Minute + case "m": + value = +token.lexeme; + if( outOfRange( value, 0, 59 ) ) { + return false; + } + date.setMinutes( value ); + truncateAt.push( MINUTE ); + break; + + // Second + case "s": + value = +token.lexeme; + if( outOfRange( value, 0, 59 ) ) { + return false; + } + date.setSeconds( value ); + truncateAt.push( SECOND ); + break; + + case "A": + date.setHours( 0 ); + date.setMinutes( 0 ); + date.setSeconds( 0 ); + + /* falls through */ + case "S": + value = Math.round( +token.lexeme * Math.pow( 10, 3 - length ) ); + date.setMilliseconds( value ); + truncateAt.push( MILLISECONDS ); + break; + + // Zone + // see http://www.unicode.org/reports/tr35/tr35-dates.html#Using_Time_Zone_Names ? + // Need to be implemented. + case "z": + case "Z": + case "O": + case "v": + case "V": + case "X": + case "x": + throw new Error( "Not implemented" ); + } + + return true; + }); + + if ( !valid || amPm && hour24 ) { + return null; } - // handle groups (e.g. 1,000,000) - var groupSep = nf[ "," ]; - integer = integer.split( groupSep ).join( "" ); - var altGroupSep = groupSep.replace( /\u00A0/g, " " ); - if ( groupSep !== altGroupSep ) { - integer = integer.split( altGroupSep ).join( "" ); + + if ( era === 0 ) { + // 1 BC = year 0 + date.setFullYear( date.getFullYear() * -1 + 1 ); } - // build a natively parsable number string - var p = sign + integer; - if ( fraction !== null ) { - p += "." + fraction; + + if ( amPm === "pm" && date.getHours() !== 12 ) { + date.setHours( date.getHours() + 12 ); } - if ( exponent !== null ) { - // exponent itself may have a number patternd - var expSignInfo = parseNegativePattern( exponent, nf, "-n" ); - p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ]; + + // Truncate date at the most precise unit defined. Eg. + // If value is "12/31", and pattern is "MM/dd": + // => new Date( , 12, 31, 0, 0, 0, 0 ); + truncateAt = Math.max.apply( null, truncateAt ); + date = datetimeStartOf( date, units[ truncateAt ] ); + + return date; + }; + +}()); + + + var arrayIsArray = Array.isArray || function( obj ) { + return Object.prototype.toString.call( obj ) === "[object Array]"; + }; + + + + + var alwaysArray = function( stringOrArray ) { + return arrayIsArray( stringOrArray ) ? stringOrArray : [ stringOrArray ]; + }; + + + + + var arraySome = function( array, callback ) { + var i, length; + if ( array.some ) { + return array.some( callback ); } - if ( regexParseFloat.test(p) ) { - ret = parseFloat( p ); + for ( i = 0, length = array.length; i < length; i++ ) { + if ( callback( array[ i ], i, array ) ) { + return true; + } } - } - return ret; -}; + return false; + }; + -Globalize.culture = function( cultureSelector ) { - // setter - if ( typeof cultureSelector !== "undefined" ) { - this.cultureSelector = cultureSelector; + + + var defaultLocale; + + function getLocale( locale ) { + return locale ? new Cldr( locale ) : defaultLocale; } - // getter - return this.findClosestCulture( cultureSelector ) || this.cultures[ "default" ]; -}; -}( this )); \ No newline at end of file + var Globalize = {}; + + /** + * Globalize.load( json ) + * + * @json [JSON] + * + * Load resolved or unresolved cldr data. + * Somewhat equivalent to previous Globalize.addCultureInfo(...). + */ + Globalize.load = function( json ) { + Cldr.load( json ); + }; + + /** + * Globalize.loadTranslations( locale, json ) + * + * @locale [String] + * + * @json [JSON] + * + * Load translation data per locale. + */ + Globalize.loadTranslations = function( locale, json ) { + var customData = { + "globalize-translation": {} + }; + locale = new Cldr( locale ); + customData[ "globalize-translation" ][ locale.attributes.languageId ] = json; + Cldr.load( customData ); + }; + + /** + * Globalize.locale( locale ) + * + * @locale [String] + * + * Set default locale. + * Somewhat equivalent to previous culture( selector ). + */ + Globalize.locale = function( locale ) { + if ( arguments.length ) { + defaultLocale = new Cldr( locale ); + } + return defaultLocale; + }; + + /** + * Globalize.format( value, pattern, locale ) + * + * @value [Date or Number] + * + * @pattern [String or Object] see datetime/expand_pattern for more info. + * + * @locale [String] + * + * Formats a date or number according to the given pattern string and the given locale (or the default locale if not specified). + */ + Globalize.format = function( value, pattern, locale ) { + locale = getLocale( locale ); + + if ( value instanceof Date ) { + + if ( !pattern ) { + throw new Error( "Missing pattern" ); + } + pattern = datetimeExpandPattern( pattern, locale ); + + value = datetimeFormat( value, pattern, locale ); + + } else if ( typeof value === "number" ) { + // TODO value = numberFormat( value, pattern, locale ); + throw new Error( "Number Format not implemented yet" ); + } + + return value; + }; + + /** + * Globalize.parseDate( value, patterns, locale ) + * + * @value [Date] + * + * @patterns [Array] Optional. See datetime/expand_pattern for more info about each pattern. Defaults to the list of all presets defined in the locale (see datetime/all_presets for more info). + * + * @locale [String] + * + * Return a Date instance or null. + */ + Globalize.parseDate = function( value, patterns, locale ) { + var date; + locale = getLocale( locale ); + + if ( typeof value !== "string" ) { + throw new Error( "invalid value (" + value + "), string expected" ); + } + + if ( !patterns ) { + patterns = datetimeAllPresets( locale ); + } else { + patterns = alwaysArray( patterns ); + } + + arraySome( patterns, function( pattern ) { + pattern = datetimeExpandPattern( pattern, locale ); + date = datetimeParse( value, pattern, locale ); + return !!date; + }); + + return date || null; + }; + + /** + * Globalize.translate( path, locale ) + * + * @path [String or Array] + * + * @locale [String] + * + * Translate item given its path. + */ + Globalize.translate = function( path , locale ) { + locale = getLocale( locale ); + path = alwaysArray( path ); + return locale.get( [ "globalize-translation/{languageId}" ].concat( path ) ); + }; + + return Globalize; + + + +})); diff --git a/external/localization.js b/external/localization.js index d81e997b5e7..98d8f813052 100644 --- a/external/localization.js +++ b/external/localization.js @@ -1,436 +1,3119 @@ -// regional data +/** + * CLDR locale data + */ +Globalize.load({ + "main": { + "en": { + "identity": { + "version": { + "_cldrVersion": "24", + "_number": "$Revision: 9287 $" + }, + "generation": { + "_date": "$Date: 2013-08-28 21:32:04 -0500 (Wed, 28 Aug 2013) $" + }, + "language": "en" + }, + "dates": { + "calendars": { + "gregorian": { + "months": { + "format": { + "abbreviated": { + "1": "Jan", + "2": "Feb", + "3": "Mar", + "4": "Apr", + "5": "May", + "6": "Jun", + "7": "Jul", + "8": "Aug", + "9": "Sep", + "10": "Oct", + "11": "Nov", + "12": "Dec" + }, + "narrow": { + "1": "J", + "2": "F", + "3": "M", + "4": "A", + "5": "M", + "6": "J", + "7": "J", + "8": "A", + "9": "S", + "10": "O", + "11": "N", + "12": "D" + }, + "wide": { + "1": "January", + "2": "February", + "3": "March", + "4": "April", + "5": "May", + "6": "June", + "7": "July", + "8": "August", + "9": "September", + "10": "October", + "11": "November", + "12": "December" + } + }, + "stand-alone": { + "abbreviated": { + "1": "Jan", + "2": "Feb", + "3": "Mar", + "4": "Apr", + "5": "May", + "6": "Jun", + "7": "Jul", + "8": "Aug", + "9": "Sep", + "10": "Oct", + "11": "Nov", + "12": "Dec" + }, + "narrow": { + "1": "J", + "2": "F", + "3": "M", + "4": "A", + "5": "M", + "6": "J", + "7": "J", + "8": "A", + "9": "S", + "10": "O", + "11": "N", + "12": "D" + }, + "wide": { + "1": "January", + "2": "February", + "3": "March", + "4": "April", + "5": "May", + "6": "June", + "7": "July", + "8": "August", + "9": "September", + "10": "October", + "11": "November", + "12": "December" + } + } + }, + "days": { + "format": { + "abbreviated": { + "sun": "Sun", + "mon": "Mon", + "tue": "Tue", + "wed": "Wed", + "thu": "Thu", + "fri": "Fri", + "sat": "Sat" + }, + "narrow": { + "sun": "S", + "mon": "M", + "tue": "T", + "wed": "W", + "thu": "T", + "fri": "F", + "sat": "S" + }, + "short": { + "sun": "Su", + "mon": "Mo", + "tue": "Tu", + "wed": "We", + "thu": "Th", + "fri": "Fr", + "sat": "Sa" + }, + "wide": { + "sun": "Sunday", + "mon": "Monday", + "tue": "Tuesday", + "wed": "Wednesday", + "thu": "Thursday", + "fri": "Friday", + "sat": "Saturday" + } + }, + "stand-alone": { + "abbreviated": { + "sun": "Sun", + "mon": "Mon", + "tue": "Tue", + "wed": "Wed", + "thu": "Thu", + "fri": "Fri", + "sat": "Sat" + }, + "narrow": { + "sun": "S", + "mon": "M", + "tue": "T", + "wed": "W", + "thu": "T", + "fri": "F", + "sat": "S" + }, + "short": { + "sun": "Su", + "mon": "Mo", + "tue": "Tu", + "wed": "We", + "thu": "Th", + "fri": "Fr", + "sat": "Sa" + }, + "wide": { + "sun": "Sunday", + "mon": "Monday", + "tue": "Tuesday", + "wed": "Wednesday", + "thu": "Thursday", + "fri": "Friday", + "sat": "Saturday" + } + } + }, + "quarters": { + "format": { + "abbreviated": { + "1": "Q1", + "2": "Q2", + "3": "Q3", + "4": "Q4" + }, + "narrow": { + "1": "1", + "2": "2", + "3": "3", + "4": "4" + }, + "wide": { + "1": "1st quarter", + "2": "2nd quarter", + "3": "3rd quarter", + "4": "4th quarter" + } + }, + "stand-alone": { + "abbreviated": { + "1": "Q1", + "2": "Q2", + "3": "Q3", + "4": "Q4" + }, + "narrow": { + "1": "1", + "2": "2", + "3": "3", + "4": "4" + }, + "wide": { + "1": "1st quarter", + "2": "2nd quarter", + "3": "3rd quarter", + "4": "4th quarter" + } + } + }, + "dayPeriods": { + "format": { + "abbreviated": { + "am": "AM", + "am-alt-variant": "a.m.", + "noon": "noon", + "pm": "PM", + "pm-alt-variant": "p.m." + }, + "narrow": { + "am": "a", + "am-alt-variant": "a.m.", + "noon": "n", + "pm": "p", + "pm-alt-variant": "p.m." + }, + "wide": { + "am": "AM", + "am-alt-variant": "a.m.", + "noon": "noon", + "pm": "PM", + "pm-alt-variant": "p.m." + } + }, + "stand-alone": { + "abbreviated": { + "am": "AM", + "am-alt-variant": "a.m.", + "noon": "noon", + "pm": "PM", + "pm-alt-variant": "p.m." + }, + "narrow": { + "am": "a", + "am-alt-variant": "a.m.", + "noon": "n", + "pm": "p", + "pm-alt-variant": "p.m." + }, + "wide": { + "am": "AM", + "am-alt-variant": "a.m.", + "noon": "noon", + "pm": "PM", + "pm-alt-variant": "p.m." + } + } + }, + "eras": { + "eraNames": { + "0": "Before Christ", + "0-alt-variant": "Before Common Era", + "1": "Anno Domini", + "1-alt-variant": "Common Era" + }, + "eraAbbr": { + "0": "BC", + "0-alt-variant": "BCE", + "1": "AD", + "1-alt-variant": "CE" + }, + "eraNarrow": { + "0": "B", + "0-alt-variant": "BCE", + "1": "A", + "1-alt-variant": "CE" + } + }, + "dateFormats": { + "full": "EEEE, MMMM d, y", + "long": "MMMM d, y", + "medium": "MMM d, y", + "short": "M/d/yy" + }, + "timeFormats": { + "full": "h:mm:ss a zzzz", + "long": "h:mm:ss a z", + "medium": "h:mm:ss a", + "short": "h:mm a" + }, + "dateTimeFormats": { + "full": "{1} 'at' {0}", + "long": "{1} 'at' {0}", + "medium": "{1}, {0}", + "short": "{1}, {0}", + "availableFormats": { + "d": "d", + "Ed": "d E", + "Ehm": "E h:mm a", + "EHm": "E HH:mm", + "Ehms": "E h:mm:ss a", + "EHms": "E HH:mm:ss", + "Gy": "y G", + "GyMMM": "MMM y G", + "GyMMMd": "MMM d, y G", + "GyMMMEd": "E, MMM d, y G", + "h": "h a", + "H": "HH", + "hm": "h:mm a", + "Hm": "HH:mm", + "hms": "h:mm:ss a", + "Hms": "HH:mm:ss", + "M": "L", + "Md": "M/d", + "MEd": "E, M/d", + "MMM": "LLL", + "MMMd": "MMM d", + "MMMEd": "E, MMM d", + "ms": "mm:ss", + "y": "y", + "yM": "M/y", + "yMd": "M/d/y", + "yMEd": "E, M/d/y", + "yMMM": "MMM y", + "yMMMd": "MMM d, y", + "yMMMEd": "E, MMM d, y", + "yQQQ": "QQQ y", + "yQQQQ": "QQQQ y" + }, + "appendItems": { + "Day": "{0} ({2}: {1})", + "Day-Of-Week": "{0} {1}", + "Era": "{0} {1}", + "Hour": "{0} ({2}: {1})", + "Minute": "{0} ({2}: {1})", + "Month": "{0} ({2}: {1})", + "Quarter": "{0} ({2}: {1})", + "Second": "{0} ({2}: {1})", + "Timezone": "{0} {1}", + "Week": "{0} ({2}: {1})", + "Year": "{0} {1}" + }, + "intervalFormats": { + "intervalFormatFallback": "{0} – {1}", + "d": { + "d": "d – d" + }, + "h": { + "a": "h a – h a", + "h": "h – h a" + }, + "H": { + "H": "HH – HH" + }, + "hm": { + "a": "h:mm a – h:mm a", + "h": "h:mm – h:mm a", + "m": "h:mm – h:mm a" + }, + "Hm": { + "H": "HH:mm – HH:mm", + "m": "HH:mm – HH:mm" + }, + "hmv": { + "a": "h:mm a – h:mm a v", + "h": "h:mm – h:mm a v", + "m": "h:mm – h:mm a v" + }, + "Hmv": { + "H": "HH:mm – HH:mm v", + "m": "HH:mm – HH:mm v" + }, + "hv": { + "a": "h a – h a v", + "h": "h – h a v" + }, + "Hv": { + "H": "HH – HH v" + }, + "M": { + "M": "M – M" + }, + "Md": { + "d": "M/d – M/d", + "M": "M/d – M/d" + }, + "MEd": { + "d": "E, M/d – E, M/d", + "M": "E, M/d – E, M/d" + }, + "MMM": { + "M": "MMM – MMM" + }, + "MMMd": { + "d": "MMM d – d", + "M": "MMM d – MMM d" + }, + "MMMEd": { + "d": "E, MMM d – E, MMM d", + "M": "E, MMM d – E, MMM d" + }, + "y": { + "y": "y – y" + }, + "yM": { + "M": "M/y – M/y", + "y": "M/y – M/y" + }, + "yMd": { + "d": "M/d/y – M/d/y", + "M": "M/d/y – M/d/y", + "y": "M/d/y – M/d/y" + }, + "yMEd": { + "d": "E, M/d/y – E, M/d/y", + "M": "E, M/d/y – E, M/d/y", + "y": "E, M/d/y – E, M/d/y" + }, + "yMMM": { + "M": "MMM – MMM y", + "y": "MMM y – MMM y" + }, + "yMMMd": { + "d": "MMM d – d, y", + "M": "MMM d – MMM d, y", + "y": "MMM d, y – MMM d, y" + }, + "yMMMEd": { + "d": "E, MMM d – E, MMM d, y", + "M": "E, MMM d – E, MMM d, y", + "y": "E, MMM d, y – E, MMM d, y" + }, + "yMMMM": { + "M": "MMMM – MMMM y", + "y": "MMMM y – MMMM y" + } + } + } + } + } + } + } + } +}); + +Globalize.load({ + "main": { + "de": { + "identity": { + "version": { + "_cldrVersion": "24", + "_number": "$Revision: 9287 $" + }, + "generation": { + "_date": "$Date: 2013-08-28 21:32:04 -0500 (Wed, 28 Aug 2013) $" + }, + "language": "de" + }, + "dates": { + "calendars": { + "gregorian": { + "months": { + "format": { + "abbreviated": { + "1": "Jan.", + "2": "Feb.", + "3": "März", + "4": "Apr.", + "5": "Mai", + "6": "Juni", + "7": "Juli", + "8": "Aug.", + "9": "Sep.", + "10": "Okt.", + "11": "Nov.", + "12": "Dez." + }, + "narrow": { + "1": "J", + "2": "F", + "3": "M", + "4": "A", + "5": "M", + "6": "J", + "7": "J", + "8": "A", + "9": "S", + "10": "O", + "11": "N", + "12": "D" + }, + "wide": { + "1": "Januar", + "2": "Februar", + "3": "März", + "4": "April", + "5": "Mai", + "6": "Juni", + "7": "Juli", + "8": "August", + "9": "September", + "10": "Oktober", + "11": "November", + "12": "Dezember" + } + }, + "stand-alone": { + "abbreviated": { + "1": "Jan", + "2": "Feb", + "3": "Mär", + "4": "Apr", + "5": "Mai", + "6": "Jun", + "7": "Jul", + "8": "Aug", + "9": "Sep", + "10": "Okt", + "11": "Nov", + "12": "Dez" + }, + "narrow": { + "1": "J", + "2": "F", + "3": "M", + "4": "A", + "5": "M", + "6": "J", + "7": "J", + "8": "A", + "9": "S", + "10": "O", + "11": "N", + "12": "D" + }, + "wide": { + "1": "Januar", + "2": "Februar", + "3": "März", + "4": "April", + "5": "Mai", + "6": "Juni", + "7": "Juli", + "8": "August", + "9": "September", + "10": "Oktober", + "11": "November", + "12": "Dezember" + } + } + }, + "days": { + "format": { + "abbreviated": { + "sun": "So.", + "mon": "Mo.", + "tue": "Di.", + "wed": "Mi.", + "thu": "Do.", + "fri": "Fr.", + "sat": "Sa." + }, + "narrow": { + "sun": "S", + "mon": "M", + "tue": "D", + "wed": "M", + "thu": "D", + "fri": "F", + "sat": "S" + }, + "short": { + "sun": "So.", + "mon": "Mo.", + "tue": "Di.", + "wed": "Mi.", + "thu": "Do.", + "fri": "Fr.", + "sat": "Sa." + }, + "wide": { + "sun": "Sonntag", + "mon": "Montag", + "tue": "Dienstag", + "wed": "Mittwoch", + "thu": "Donnerstag", + "fri": "Freitag", + "sat": "Samstag" + } + }, + "stand-alone": { + "abbreviated": { + "sun": "So", + "mon": "Mo", + "tue": "Di", + "wed": "Mi", + "thu": "Do", + "fri": "Fr", + "sat": "Sa" + }, + "narrow": { + "sun": "S", + "mon": "M", + "tue": "D", + "wed": "M", + "thu": "D", + "fri": "F", + "sat": "S" + }, + "short": { + "sun": "So.", + "mon": "Mo.", + "tue": "Di.", + "wed": "Mi.", + "thu": "Do.", + "fri": "Fr.", + "sat": "Sa." + }, + "wide": { + "sun": "Sonntag", + "mon": "Montag", + "tue": "Dienstag", + "wed": "Mittwoch", + "thu": "Donnerstag", + "fri": "Freitag", + "sat": "Samstag" + } + } + }, + "quarters": { + "format": { + "abbreviated": { + "1": "Q1", + "2": "Q2", + "3": "Q3", + "4": "Q4" + }, + "narrow": { + "1": "1", + "2": "2", + "3": "3", + "4": "4" + }, + "wide": { + "1": "1. Quartal", + "2": "2. Quartal", + "3": "3. Quartal", + "4": "4. Quartal" + } + }, + "stand-alone": { + "abbreviated": { + "1": "Q1", + "2": "Q2", + "3": "Q3", + "4": "Q4" + }, + "narrow": { + "1": "1", + "2": "2", + "3": "3", + "4": "4" + }, + "wide": { + "1": "1. Quartal", + "2": "2. Quartal", + "3": "3. Quartal", + "4": "4. Quartal" + } + } + }, + "dayPeriods": { + "format": { + "abbreviated": { + "afternoon": "nachmittags", + "am": "vorm.", + "earlyMorning": "morgens", + "evening": "abends", + "morning": "vormittags", + "night": "nachts", + "noon": "Mittag", + "pm": "nachm." + }, + "narrow": { + "afternoon": "nachmittags", + "am": "vorm.", + "earlyMorning": "morgens", + "evening": "abends", + "morning": "vormittags", + "night": "nachts", + "noon": "Mittag", + "pm": "nachm." + }, + "wide": { + "afternoon": "nachmittags", + "am": "vorm.", + "earlyMorning": "morgens", + "evening": "abends", + "morning": "vormittags", + "night": "nachts", + "noon": "Mittag", + "pm": "nachm." + } + }, + "stand-alone": { + "abbreviated": { + "afternoon": "nachmittags", + "am": "vorm.", + "earlyMorning": "morgens", + "evening": "abends", + "morning": "vormittags", + "night": "nachts", + "noon": "Mittag", + "pm": "nachm." + }, + "narrow": { + "afternoon": "nachmittags", + "am": "vorm.", + "earlyMorning": "morgens", + "evening": "abends", + "morning": "vormittags", + "night": "nachts", + "noon": "Mittag", + "pm": "nachm." + }, + "wide": { + "afternoon": "Nachmittag", + "am": "vorm.", + "earlyMorning": "Morgen", + "evening": "Abend", + "morning": "Vormittag", + "night": "Nacht", + "noon": "Mittag", + "pm": "nachm." + } + } + }, + "eras": { + "eraNames": { + "0": "v. Chr.", + "0-alt-variant": "vor der gewöhnlichen Zeitrechnung", + "1": "n. Chr.", + "1-alt-variant": "der gewöhnlichen Zeitrechnung" + }, + "eraAbbr": { + "0": "v. Chr.", + "0-alt-variant": "v. u. Z.", + "1": "n. Chr.", + "1-alt-variant": "u. Z." + }, + "eraNarrow": { + "0": "v. Chr.", + "0-alt-variant": "vdZ", + "1": "n. Chr.", + "1-alt-variant": "dZ" + } + }, + "dateFormats": { + "full": "EEEE, d. MMMM y", + "long": "d. MMMM y", + "medium": "dd.MM.y", + "short": "dd.MM.yy" + }, + "timeFormats": { + "full": "HH:mm:ss zzzz", + "long": "HH:mm:ss z", + "medium": "HH:mm:ss", + "short": "HH:mm" + }, + "dateTimeFormats": { + "full": "{1} {0}", + "long": "{1} {0}", + "medium": "{1} {0}", + "short": "{1} {0}", + "availableFormats": { + "d": "d", + "Ed": "E, d.", + "Ehm": "E h:mm a", + "EHm": "E, HH:mm", + "Ehms": "E, h:mm:ss a", + "EHms": "E, HH:mm:ss", + "Gy": "y G", + "GyMMM": "MMM y G", + "GyMMMd": "d. MMM y G", + "GyMMMEd": "E, d. MMM y G", + "h": "h a", + "H": "HH 'Uhr'", + "hm": "h:mm a", + "Hm": "HH:mm", + "hms": "h:mm:ss a", + "Hms": "HH:mm:ss", + "M": "L", + "Md": "d.M.", + "MEd": "E, d.M.", + "MMd": "d.MM.", + "MMdd": "dd.MM.", + "MMM": "LLL", + "MMMd": "d. MMM", + "MMMEd": "E, d. MMM", + "MMMMdd": "dd. MMMM", + "MMMMEd": "E, d. MMMM", + "ms": "mm:ss", + "y": "y", + "yM": "M.y", + "yMd": "d.M.y", + "yMEd": "E, d.M.y", + "yMM": "MM.y", + "yMMdd": "dd.MM.y", + "yMMM": "MMM y", + "yMMMd": "d. MMM y", + "yMMMEd": "E, d. MMM y", + "yMMMM": "MMMM y", + "yQQQ": "QQQ y", + "yQQQQ": "QQQQ y" + }, + "appendItems": { + "Day": "{0} ({2}: {1})", + "Day-Of-Week": "{0} {1}", + "Era": "{1} {0}", + "Hour": "{0} ({2}: {1})", + "Minute": "{0} ({2}: {1})", + "Month": "{0} ({2}: {1})", + "Quarter": "{0} ({2}: {1})", + "Second": "{0} ({2}: {1})", + "Timezone": "{0} {1}", + "Week": "{0} ({2}: {1})", + "Year": "{1} {0}" + }, + "intervalFormats": { + "intervalFormatFallback": "{0} - {1}", + "d": { + "d": "d.-d." + }, + "h": { + "a": "h a - h a", + "h": "h-h a" + }, + "H": { + "H": "HH-HH 'Uhr'" + }, + "hm": { + "a": "h:mm a - h:mm a", + "h": "h:mm-h:mm a", + "m": "h:mm-h:mm a" + }, + "Hm": { + "H": "HH:mm-HH:mm", + "m": "HH:mm-HH:mm" + }, + "hmv": { + "a": "h:mm a - h:mm a v", + "h": "h:mm-h:mm a v", + "m": "h:mm-h:mm a v" + }, + "Hmv": { + "H": "HH:mm-HH:mm v", + "m": "HH:mm-HH:mm v" + }, + "hv": { + "a": "h a - h a v", + "h": "h-h a v" + }, + "Hv": { + "H": "HH-HH 'Uhr' v" + }, + "M": { + "M": "M.-M." + }, + "Md": { + "d": "dd.MM. - dd.MM.", + "M": "dd.MM. - dd.MM." + }, + "MEd": { + "d": "E, dd.MM. - E, dd.MM.", + "M": "E, dd.MM. - E, dd.MM." + }, + "MMM": { + "M": "MMM-MMM" + }, + "MMMd": { + "d": "d.-d. MMM", + "M": "d. MMM - d. MMM" + }, + "MMMEd": { + "d": "E, d. - E, d. MMM", + "M": "E, d. MMM - E, d. MMM" + }, + "MMMM": { + "M": "LLLL-LLLL" + }, + "y": { + "y": "y-y" + }, + "yM": { + "M": "MM.y - MM.y", + "y": "MM.y - MM.y" + }, + "yMd": { + "d": "dd.MM.y - dd.MM.y", + "M": "dd.MM.y - dd.MM.y", + "y": "dd.MM.y - dd.MM.y" + }, + "yMEd": { + "d": "E, dd.MM.y - E, dd.MM.y", + "M": "E, dd.MM.y - E, dd.MM.y", + "y": "E, dd.MM.y - E, dd.MM.y" + }, + "yMMM": { + "M": "MMM-MMM y", + "y": "MMM y - MMM y" + }, + "yMMMd": { + "d": "d.-d. MMM y", + "M": "d. MMM - d. MMM y", + "y": "d. MMM y - d. MMM y" + }, + "yMMMEd": { + "d": "E, d. - E, d. MMM y", + "M": "E, d. MMM - E, d. MMM y", + "y": "E, d. MMM y - E, d. MMM y" + }, + "yMMMM": { + "M": "MMMM-MMMM y", + "y": "MMMM y - MMMM y" + } + } + } + } + } + } + } + } +}); + +/** + * CLDR supplemental data + */ + +// likelySubtags +Globalize.load({ + "supplemental": { + "version": { + "_cldrVersion": "24", + "_number": "$Revision: 9305 $" + }, + "generation": { + "_date": "$Date: 2013-09-04 09:50:17 -0500 (Wed, 04 Sep 2013) $" + }, + "likelySubtags": { + "aa": "aa_Latn_ET", + "ab": "ab_Cyrl_GE", + "ace": "ace_Latn_ID", + "ady": "ady_Cyrl_RU", + "af": "af_Latn_ZA", + "agq": "agq_Latn_CM", + "ak": "ak_Latn_GH", + "alt": "alt_Cyrl_RU", + "am": "am_Ethi_ET", + "amo": "amo_Latn_NG", + "ar": "ar_Arab_EG", + "as": "as_Beng_IN", + "asa": "asa_Latn_TZ", + "ast": "ast_Latn_ES", + "av": "av_Cyrl_RU", + "awa": "awa_Deva_IN", + "ay": "ay_Latn_BO", + "az": "az_Latn_AZ", + "az_Arab": "az_Arab_IR", + "az_IR": "az_Arab_IR", + "az_RU": "az_Cyrl_RU", + "ba": "ba_Cyrl_RU", + "bal": "bal_Arab_PK", + "ban": "ban_Latn_ID", + "bas": "bas_Latn_CM", + "bax": "bax_Bamu_CM", + "bbc": "bbc_Latn_ID", + "be": "be_Cyrl_BY", + "bem": "bem_Latn_ZM", + "bez": "bez_Latn_TZ", + "bfq": "bfq_Taml_IN", + "bft": "bft_Arab_PK", + "bfy": "bfy_Deva_IN", + "bg": "bg_Cyrl_BG", + "bhb": "bhb_Deva_IN", + "bho": "bho_Deva_IN", + "bi": "bi_Latn_VU", + "bik": "bik_Latn_PH", + "bin": "bin_Latn_NG", + "bjj": "bjj_Deva_IN", + "bku": "bku_Latn_PH", + "bm": "bm_Latn_ML", + "bn": "bn_Beng_BD", + "bo": "bo_Tibt_CN", + "bqv": "bqv_Latn_CI", + "br": "br_Latn_FR", + "bra": "bra_Deva_IN", + "brx": "brx_Deva_IN", + "bs": "bs_Latn_BA", + "bss": "bss_Latn_CM", + "btv": "btv_Deva_PK", + "bua": "bua_Cyrl_RU", + "buc": "buc_Latn_YT", + "bug": "bug_Latn_ID", + "bya": "bya_Latn_ID", + "byn": "byn_Ethi_ER", + "ca": "ca_Latn_ES", + "cch": "cch_Latn_NG", + "ccp": "ccp_Beng_IN", + "ce": "ce_Cyrl_RU", + "ceb": "ceb_Latn_PH", + "cgg": "cgg_Latn_UG", + "ch": "ch_Latn_GU", + "chk": "chk_Latn_FM", + "chm": "chm_Cyrl_RU", + "chp": "chp_Latn_CA", + "chr": "chr_Cher_US", + "cja": "cja_Arab_KH", + "cjm": "cjm_Cham_VN", + "ckb": "ckb_Arab_IQ", + "co": "co_Latn_FR", + "cr": "cr_Cans_CA", + "crk": "crk_Cans_CA", + "cs": "cs_Latn_CZ", + "csb": "csb_Latn_PL", + "cv": "cv_Cyrl_RU", + "cy": "cy_Latn_GB", + "da": "da_Latn_DK", + "dar": "dar_Cyrl_RU", + "dav": "dav_Latn_KE", + "de": "de_Latn_DE", + "den": "den_Latn_CA", + "dgr": "dgr_Latn_CA", + "dje": "dje_Latn_NE", + "doi": "doi_Arab_IN", + "dsb": "dsb_Latn_DE", + "dua": "dua_Latn_CM", + "dv": "dv_Thaa_MV", + "dyo": "dyo_Latn_SN", + "dyu": "dyu_Latn_BF", + "dz": "dz_Tibt_BT", + "ebu": "ebu_Latn_KE", + "ee": "ee_Latn_GH", + "efi": "efi_Latn_NG", + "el": "el_Grek_GR", + "en": "en_Latn_US", + "eo": "eo_Latn_001", + "es": "es_Latn_ES", + "et": "et_Latn_EE", + "eu": "eu_Latn_ES", + "ewo": "ewo_Latn_CM", + "fa": "fa_Arab_IR", + "fan": "fan_Latn_GQ", + "ff": "ff_Latn_SN", + "fi": "fi_Latn_FI", + "fil": "fil_Latn_PH", + "fj": "fj_Latn_FJ", + "fo": "fo_Latn_FO", + "fon": "fon_Latn_BJ", + "fr": "fr_Latn_FR", + "fur": "fur_Latn_IT", + "fy": "fy_Latn_NL", + "ga": "ga_Latn_IE", + "gaa": "gaa_Latn_GH", + "gag": "gag_Latn_MD", + "gbm": "gbm_Deva_IN", + "gcr": "gcr_Latn_GF", + "gd": "gd_Latn_GB", + "gez": "gez_Ethi_ET", + "gil": "gil_Latn_KI", + "gl": "gl_Latn_ES", + "gn": "gn_Latn_PY", + "gon": "gon_Telu_IN", + "gor": "gor_Latn_ID", + "grt": "grt_Beng_IN", + "gsw": "gsw_Latn_CH", + "gu": "gu_Gujr_IN", + "guz": "guz_Latn_KE", + "gv": "gv_Latn_IM", + "gwi": "gwi_Latn_CA", + "ha": "ha_Latn_NG", + "ha_CM": "ha_Arab_CM", + "ha_SD": "ha_Arab_SD", + "haw": "haw_Latn_US", + "he": "he_Hebr_IL", + "hi": "hi_Deva_IN", + "hil": "hil_Latn_PH", + "hne": "hne_Deva_IN", + "hnn": "hnn_Latn_PH", + "ho": "ho_Latn_PG", + "hoc": "hoc_Deva_IN", + "hoj": "hoj_Deva_IN", + "hr": "hr_Latn_HR", + "ht": "ht_Latn_HT", + "hu": "hu_Latn_HU", + "hy": "hy_Armn_AM", + "ia": "ia_Latn_FR", + "ibb": "ibb_Latn_NG", + "id": "id_Latn_ID", + "ig": "ig_Latn_NG", + "ii": "ii_Yiii_CN", + "ik": "ik_Latn_US", + "ilo": "ilo_Latn_PH", + "in": "in_Latn_ID", + "inh": "inh_Cyrl_RU", + "is": "is_Latn_IS", + "it": "it_Latn_IT", + "iu": "iu_Cans_CA", + "iw": "iw_Hebr_IL", + "ja": "ja_Jpan_JP", + "jgo": "jgo_Latn_CM", + "ji": "ji_Hebr_UA", + "jmc": "jmc_Latn_TZ", + "jv": "jv_Latn_ID", + "jw": "jw_Latn_ID", + "ka": "ka_Geor_GE", + "kaa": "kaa_Cyrl_UZ", + "kab": "kab_Latn_DZ", + "kaj": "kaj_Latn_NG", + "kam": "kam_Latn_KE", + "kbd": "kbd_Cyrl_RU", + "kcg": "kcg_Latn_NG", + "kde": "kde_Latn_TZ", + "kdt": "kdt_Thai_TH", + "kea": "kea_Latn_CV", + "ken": "ken_Latn_CM", + "kfo": "kfo_Latn_CI", + "kfr": "kfr_Deva_IN", + "kg": "kg_Latn_CD", + "kha": "kha_Latn_IN", + "khb": "khb_Talu_CN", + "khq": "khq_Latn_ML", + "kht": "kht_Mymr_IN", + "ki": "ki_Latn_KE", + "kj": "kj_Latn_NA", + "kk": "kk_Cyrl_KZ", + "kk_AF": "kk_Arab_AF", + "kk_Arab": "kk_Arab_CN", + "kk_CN": "kk_Arab_CN", + "kk_IR": "kk_Arab_IR", + "kk_MN": "kk_Arab_MN", + "kkj": "kkj_Latn_CM", + "kl": "kl_Latn_GL", + "kln": "kln_Latn_KE", + "km": "km_Khmr_KH", + "kmb": "kmb_Latn_AO", + "kn": "kn_Knda_IN", + "ko": "ko_Kore_KR", + "koi": "koi_Cyrl_RU", + "kok": "kok_Deva_IN", + "kos": "kos_Latn_FM", + "kpe": "kpe_Latn_LR", + "krc": "krc_Cyrl_RU", + "kri": "kri_Latn_SL", + "krl": "krl_Latn_RU", + "kru": "kru_Deva_IN", + "ks": "ks_Arab_IN", + "ksb": "ksb_Latn_TZ", + "ksf": "ksf_Latn_CM", + "ksh": "ksh_Latn_DE", + "ku": "ku_Latn_TR", + "ku_Arab": "ku_Arab_IQ", + "ku_LB": "ku_Arab_LB", + "kum": "kum_Cyrl_RU", + "kv": "kv_Cyrl_RU", + "kw": "kw_Latn_GB", + "ky": "ky_Cyrl_KG", + "ky_Arab": "ky_Arab_CN", + "ky_CN": "ky_Arab_CN", + "ky_Latn": "ky_Latn_TR", + "ky_TR": "ky_Latn_TR", + "la": "la_Latn_VA", + "lag": "lag_Latn_TZ", + "lah": "lah_Arab_PK", + "lb": "lb_Latn_LU", + "lbe": "lbe_Cyrl_RU", + "lcp": "lcp_Thai_CN", + "lep": "lep_Lepc_IN", + "lez": "lez_Cyrl_RU", + "lg": "lg_Latn_UG", + "li": "li_Latn_NL", + "lif": "lif_Deva_NP", + "lis": "lis_Lisu_CN", + "lki": "lki_Arab_IR", + "lkt": "lkt_Latn_US", + "lmn": "lmn_Telu_IN", + "ln": "ln_Latn_CD", + "lo": "lo_Laoo_LA", + "lol": "lol_Latn_CD", + "lt": "lt_Latn_LT", + "lu": "lu_Latn_CD", + "lua": "lua_Latn_CD", + "luo": "luo_Latn_KE", + "luy": "luy_Latn_KE", + "lv": "lv_Latn_LV", + "lwl": "lwl_Thai_TH", + "mad": "mad_Latn_ID", + "mag": "mag_Deva_IN", + "mai": "mai_Deva_IN", + "mak": "mak_Latn_ID", + "man": "man_Latn_GM", + "man_GN": "man_Nkoo_GN", + "man_Nkoo": "man_Nkoo_GN", + "mas": "mas_Latn_KE", + "mdf": "mdf_Cyrl_RU", + "mdh": "mdh_Latn_PH", + "mdr": "mdr_Latn_ID", + "men": "men_Latn_SL", + "mer": "mer_Latn_KE", + "mfe": "mfe_Latn_MU", + "mg": "mg_Latn_MG", + "mgh": "mgh_Latn_MZ", + "mgo": "mgo_Latn_CM", + "mh": "mh_Latn_MH", + "mi": "mi_Latn_NZ", + "min": "min_Latn_ID", + "mk": "mk_Cyrl_MK", + "ml": "ml_Mlym_IN", + "mn": "mn_Cyrl_MN", + "mn_CN": "mn_Mong_CN", + "mn_Mong": "mn_Mong_CN", + "mni": "mni_Beng_IN", + "mnw": "mnw_Mymr_MM", + "mo": "mo_Latn_RO", + "mos": "mos_Latn_BF", + "mr": "mr_Deva_IN", + "ms": "ms_Latn_MY", + "ms_CC": "ms_Arab_CC", + "ms_ID": "ms_Arab_ID", + "mt": "mt_Latn_MT", + "mua": "mua_Latn_CM", + "mwr": "mwr_Deva_IN", + "my": "my_Mymr_MM", + "myv": "myv_Cyrl_RU", + "na": "na_Latn_NR", + "nap": "nap_Latn_IT", + "naq": "naq_Latn_NA", + "nb": "nb_Latn_NO", + "nd": "nd_Latn_ZW", + "nds": "nds_Latn_DE", + "ne": "ne_Deva_NP", + "new": "new_Deva_NP", + "ng": "ng_Latn_NA", + "niu": "niu_Latn_NU", + "nl": "nl_Latn_NL", + "nmg": "nmg_Latn_CM", + "nn": "nn_Latn_NO", + "nnh": "nnh_Latn_CM", + "no": "no_Latn_NO", + "nod": "nod_Lana_TH", + "nr": "nr_Latn_ZA", + "nso": "nso_Latn_ZA", + "nus": "nus_Latn_SD", + "nv": "nv_Latn_US", + "ny": "ny_Latn_MW", + "nym": "nym_Latn_TZ", + "nyn": "nyn_Latn_UG", + "oc": "oc_Latn_FR", + "om": "om_Latn_ET", + "or": "or_Orya_IN", + "os": "os_Cyrl_GE", + "pa": "pa_Guru_IN", + "pa_Arab": "pa_Arab_PK", + "pa_PK": "pa_Arab_PK", + "pag": "pag_Latn_PH", + "pam": "pam_Latn_PH", + "pap": "pap_Latn_AW", + "pau": "pau_Latn_PW", + "pl": "pl_Latn_PL", + "pon": "pon_Latn_FM", + "prd": "prd_Arab_IR", + "ps": "ps_Arab_AF", + "pt": "pt_Latn_BR", + "qu": "qu_Latn_PE", + "raj": "raj_Latn_IN", + "rcf": "rcf_Latn_RE", + "rej": "rej_Latn_ID", + "rjs": "rjs_Deva_NP", + "rkt": "rkt_Beng_BD", + "rm": "rm_Latn_CH", + "rn": "rn_Latn_BI", + "ro": "ro_Latn_RO", + "rof": "rof_Latn_TZ", + "ru": "ru_Cyrl_RU", + "rw": "rw_Latn_RW", + "rwk": "rwk_Latn_TZ", + "sa": "sa_Deva_IN", + "saf": "saf_Latn_GH", + "sah": "sah_Cyrl_RU", + "saq": "saq_Latn_KE", + "sas": "sas_Latn_ID", + "sat": "sat_Latn_IN", + "saz": "saz_Saur_IN", + "sbp": "sbp_Latn_TZ", + "scn": "scn_Latn_IT", + "sco": "sco_Latn_GB", + "sd": "sd_Arab_PK", + "sd_Deva": "sd_Deva_IN", + "sdh": "sdh_Arab_IR", + "se": "se_Latn_NO", + "seh": "seh_Latn_MZ", + "ses": "ses_Latn_ML", + "sg": "sg_Latn_CF", + "shi": "shi_Tfng_MA", + "shn": "shn_Mymr_MM", + "si": "si_Sinh_LK", + "sid": "sid_Latn_ET", + "sk": "sk_Latn_SK", + "sl": "sl_Latn_SI", + "sm": "sm_Latn_WS", + "sma": "sma_Latn_SE", + "smj": "smj_Latn_SE", + "smn": "smn_Latn_FI", + "sms": "sms_Latn_FI", + "sn": "sn_Latn_ZW", + "snk": "snk_Latn_ML", + "so": "so_Latn_SO", + "sq": "sq_Latn_AL", + "sr": "sr_Cyrl_RS", + "sr_ME": "sr_Latn_ME", + "sr_RO": "sr_Latn_RO", + "sr_RU": "sr_Latn_RU", + "sr_TR": "sr_Latn_TR", + "srn": "srn_Latn_SR", + "srr": "srr_Latn_SN", + "ss": "ss_Latn_ZA", + "ssy": "ssy_Latn_ER", + "st": "st_Latn_ZA", + "su": "su_Latn_ID", + "suk": "suk_Latn_TZ", + "sus": "sus_Latn_GN", + "sv": "sv_Latn_SE", + "sw": "sw_Latn_TZ", + "swb": "swb_Arab_YT", + "swc": "swc_Latn_CD", + "syl": "syl_Beng_BD", + "syr": "syr_Syrc_IQ", + "ta": "ta_Taml_IN", + "tbw": "tbw_Latn_PH", + "tcy": "tcy_Knda_IN", + "tdd": "tdd_Tale_CN", + "te": "te_Telu_IN", + "tem": "tem_Latn_SL", + "teo": "teo_Latn_UG", + "tet": "tet_Latn_TL", + "tg": "tg_Cyrl_TJ", + "tg_Arab": "tg_Arab_PK", + "tg_PK": "tg_Arab_PK", + "th": "th_Thai_TH", + "ti": "ti_Ethi_ET", + "tig": "tig_Ethi_ER", + "tiv": "tiv_Latn_NG", + "tk": "tk_Latn_TM", + "tkl": "tkl_Latn_TK", + "tl": "tl_Latn_PH", + "tmh": "tmh_Latn_NE", + "tn": "tn_Latn_ZA", + "to": "to_Latn_TO", + "tpi": "tpi_Latn_PG", + "tr": "tr_Latn_TR", + "trv": "trv_Latn_TW", + "ts": "ts_Latn_ZA", + "tsg": "tsg_Latn_PH", + "tt": "tt_Cyrl_RU", + "tts": "tts_Thai_TH", + "tum": "tum_Latn_MW", + "tvl": "tvl_Latn_TV", + "twq": "twq_Latn_NE", + "ty": "ty_Latn_PF", + "tyv": "tyv_Cyrl_RU", + "tzm": "tzm_Latn_MA", + "udm": "udm_Cyrl_RU", + "ug": "ug_Arab_CN", + "ug_Cyrl": "ug_Cyrl_KZ", + "ug_KZ": "ug_Cyrl_KZ", + "ug_MN": "ug_Cyrl_MN", + "uk": "uk_Cyrl_UA", + "uli": "uli_Latn_FM", + "umb": "umb_Latn_AO", + "und": "en_Latn_US", + "und_AD": "ca_Latn_AD", + "und_AE": "ar_Arab_AE", + "und_AF": "fa_Arab_AF", + "und_AL": "sq_Latn_AL", + "und_AM": "hy_Armn_AM", + "und_AO": "pt_Latn_AO", + "und_AQ": "und_Latn_AQ", + "und_AR": "es_Latn_AR", + "und_Arab": "ar_Arab_EG", + "und_Arab_CC": "ms_Arab_CC", + "und_Arab_CN": "ug_Arab_CN", + "und_Arab_GB": "ks_Arab_GB", + "und_Arab_ID": "ms_Arab_ID", + "und_Arab_IN": "ur_Arab_IN", + "und_Arab_KH": "cja_Arab_KH", + "und_Arab_MN": "kk_Arab_MN", + "und_Arab_MU": "ur_Arab_MU", + "und_Arab_NG": "ha_Arab_NG", + "und_Arab_PK": "ur_Arab_PK", + "und_Arab_TJ": "fa_Arab_TJ", + "und_Arab_TR": "zza_Arab_TR", + "und_Arab_YT": "swb_Arab_YT", + "und_Armi": "arc_Armi_IR", + "und_Armn": "hy_Armn_AM", + "und_AS": "sm_Latn_AS", + "und_AT": "de_Latn_AT", + "und_Avst": "ae_Avst_IR", + "und_AW": "nl_Latn_AW", + "und_AX": "sv_Latn_AX", + "und_AZ": "az_Latn_AZ", + "und_BA": "bs_Latn_BA", + "und_Bali": "ban_Bali_ID", + "und_Bamu": "bax_Bamu_CM", + "und_Batk": "bbc_Batk_ID", + "und_BD": "bn_Beng_BD", + "und_BE": "nl_Latn_BE", + "und_Beng": "bn_Beng_BD", + "und_BF": "fr_Latn_BF", + "und_BG": "bg_Cyrl_BG", + "und_BH": "ar_Arab_BH", + "und_BI": "rn_Latn_BI", + "und_BJ": "fr_Latn_BJ", + "und_BL": "fr_Latn_BL", + "und_BN": "ms_Latn_BN", + "und_BO": "es_Latn_BO", + "und_Bopo": "zh_Bopo_TW", + "und_BQ": "pap_Latn_BQ", + "und_BR": "pt_Latn_BR", + "und_Brah": "pra_Brah_IN", + "und_Brai": "und_Brai_FR", + "und_BT": "dz_Tibt_BT", + "und_Bugi": "bug_Bugi_ID", + "und_Buhd": "bku_Buhd_PH", + "und_BV": "und_Latn_BV", + "und_BY": "be_Cyrl_BY", + "und_Cakm": "ccp_Cakm_BD", + "und_Cans": "cr_Cans_CA", + "und_Cari": "xcr_Cari_TR", + "und_CD": "sw_Latn_CD", + "und_CF": "fr_Latn_CF", + "und_CG": "fr_Latn_CG", + "und_CH": "de_Latn_CH", + "und_Cham": "cjm_Cham_VN", + "und_Cher": "chr_Cher_US", + "und_CI": "fr_Latn_CI", + "und_CL": "es_Latn_CL", + "und_CM": "fr_Latn_CM", + "und_CN": "zh_Hans_CN", + "und_CO": "es_Latn_CO", + "und_Copt": "cop_Copt_EG", + "und_CP": "und_Latn_CP", + "und_Cprt": "grc_Cprt_CY", + "und_CR": "es_Latn_CR", + "und_CU": "es_Latn_CU", + "und_CV": "pt_Latn_CV", + "und_CW": "pap_Latn_CW", + "und_CY": "el_Grek_CY", + "und_Cyrl": "ru_Cyrl_RU", + "und_Cyrl_AL": "mk_Cyrl_AL", + "und_Cyrl_BA": "sr_Cyrl_BA", + "und_Cyrl_GE": "ab_Cyrl_GE", + "und_Cyrl_GR": "mk_Cyrl_GR", + "und_Cyrl_MD": "uk_Cyrl_MD", + "und_Cyrl_PL": "be_Cyrl_PL", + "und_Cyrl_RO": "bg_Cyrl_RO", + "und_Cyrl_SK": "uk_Cyrl_SK", + "und_Cyrl_TR": "kbd_Cyrl_TR", + "und_Cyrl_XK": "sr_Cyrl_XK", + "und_CZ": "cs_Latn_CZ", + "und_DE": "de_Latn_DE", + "und_Deva": "hi_Deva_IN", + "und_Deva_BT": "ne_Deva_BT", + "und_Deva_MU": "bho_Deva_MU", + "und_Deva_PK": "btv_Deva_PK", + "und_DJ": "aa_Latn_DJ", + "und_DK": "da_Latn_DK", + "und_DO": "es_Latn_DO", + "und_DZ": "ar_Arab_DZ", + "und_EA": "es_Latn_EA", + "und_EC": "es_Latn_EC", + "und_EE": "et_Latn_EE", + "und_EG": "ar_Arab_EG", + "und_Egyp": "egy_Egyp_EG", + "und_EH": "ar_Arab_EH", + "und_ER": "ti_Ethi_ER", + "und_ES": "es_Latn_ES", + "und_ET": "am_Ethi_ET", + "und_Ethi": "am_Ethi_ET", + "und_FI": "fi_Latn_FI", + "und_FM": "chk_Latn_FM", + "und_FO": "fo_Latn_FO", + "und_FR": "fr_Latn_FR", + "und_GA": "fr_Latn_GA", + "und_GE": "ka_Geor_GE", + "und_Geor": "ka_Geor_GE", + "und_GF": "fr_Latn_GF", + "und_GH": "ak_Latn_GH", + "und_GL": "kl_Latn_GL", + "und_Glag": "cu_Glag_BG", + "und_GN": "fr_Latn_GN", + "und_Goth": "got_Goth_UA", + "und_GP": "fr_Latn_GP", + "und_GQ": "es_Latn_GQ", + "und_GR": "el_Grek_GR", + "und_Grek": "el_Grek_GR", + "und_GS": "und_Latn_GS", + "und_GT": "es_Latn_GT", + "und_Gujr": "gu_Gujr_IN", + "und_Guru": "pa_Guru_IN", + "und_GW": "pt_Latn_GW", + "und_Hang": "ko_Hang_KR", + "und_Hani": "zh_Hani_CN", + "und_Hano": "hnn_Hano_PH", + "und_Hans": "zh_Hans_CN", + "und_Hant": "zh_Hant_TW", + "und_Hebr": "he_Hebr_IL", + "und_Hebr_CA": "yi_Hebr_CA", + "und_Hebr_GB": "yi_Hebr_GB", + "und_Hebr_SE": "yi_Hebr_SE", + "und_Hebr_UA": "yi_Hebr_UA", + "und_Hebr_US": "yi_Hebr_US", + "und_Hira": "ja_Hira_JP", + "und_HK": "zh_Hant_HK", + "und_HM": "und_Latn_HM", + "und_HN": "es_Latn_HN", + "und_HR": "hr_Latn_HR", + "und_HT": "ht_Latn_HT", + "und_HU": "hu_Latn_HU", + "und_IC": "es_Latn_IC", + "und_ID": "id_Latn_ID", + "und_IL": "he_Hebr_IL", + "und_IN": "hi_Deva_IN", + "und_IQ": "ar_Arab_IQ", + "und_IR": "fa_Arab_IR", + "und_IS": "is_Latn_IS", + "und_IT": "it_Latn_IT", + "und_Ital": "ett_Ital_IT", + "und_Java": "jv_Java_ID", + "und_JO": "ar_Arab_JO", + "und_JP": "ja_Jpan_JP", + "und_Jpan": "ja_Jpan_JP", + "und_Kali": "eky_Kali_MM", + "und_Kana": "ja_Kana_JP", + "und_KG": "ky_Cyrl_KG", + "und_KH": "km_Khmr_KH", + "und_Khar": "pra_Khar_PK", + "und_Khmr": "km_Khmr_KH", + "und_KM": "ar_Arab_KM", + "und_Knda": "kn_Knda_IN", + "und_Kore": "ko_Kore_KR", + "und_KP": "ko_Kore_KP", + "und_KR": "ko_Kore_KR", + "und_Kthi": "bh_Kthi_IN", + "und_KW": "ar_Arab_KW", + "und_KZ": "ru_Cyrl_KZ", + "und_LA": "lo_Laoo_LA", + "und_Lana": "nod_Lana_TH", + "und_Laoo": "lo_Laoo_LA", + "und_Latn_AF": "tk_Latn_AF", + "und_Latn_AM": "az_Latn_AM", + "und_Latn_BG": "tr_Latn_BG", + "und_Latn_CN": "za_Latn_CN", + "und_Latn_CY": "tr_Latn_CY", + "und_Latn_DZ": "fr_Latn_DZ", + "und_Latn_ET": "en_Latn_ET", + "und_Latn_GE": "ku_Latn_GE", + "und_Latn_GR": "tr_Latn_GR", + "und_Latn_IL": "ro_Latn_IL", + "und_Latn_IR": "tk_Latn_IR", + "und_Latn_KM": "fr_Latn_KM", + "und_Latn_KZ": "de_Latn_KZ", + "und_Latn_LB": "fr_Latn_LB", + "und_Latn_MA": "fr_Latn_MA", + "und_Latn_MK": "sq_Latn_MK", + "und_Latn_MO": "pt_Latn_MO", + "und_Latn_MR": "fr_Latn_MR", + "und_Latn_RU": "krl_Latn_RU", + "und_Latn_SY": "fr_Latn_SY", + "und_Latn_TN": "fr_Latn_TN", + "und_Latn_TW": "trv_Latn_TW", + "und_Latn_UA": "pl_Latn_UA", + "und_LB": "ar_Arab_LB", + "und_Lepc": "lep_Lepc_IN", + "und_LI": "de_Latn_LI", + "und_Limb": "lif_Limb_IN", + "und_Linb": "grc_Linb_GR", + "und_Lisu": "lis_Lisu_CN", + "und_LK": "si_Sinh_LK", + "und_LS": "st_Latn_LS", + "und_LT": "lt_Latn_LT", + "und_LU": "fr_Latn_LU", + "und_LV": "lv_Latn_LV", + "und_LY": "ar_Arab_LY", + "und_Lyci": "xlc_Lyci_TR", + "und_Lydi": "xld_Lydi_TR", + "und_MA": "ar_Arab_MA", + "und_Mand": "myz_Mand_IR", + "und_MC": "fr_Latn_MC", + "und_MD": "ro_Latn_MD", + "und_ME": "sr_Latn_ME", + "und_Merc": "xmr_Merc_SD", + "und_Mero": "xmr_Mero_SD", + "und_MF": "fr_Latn_MF", + "und_MG": "mg_Latn_MG", + "und_MK": "mk_Cyrl_MK", + "und_ML": "bm_Latn_ML", + "und_Mlym": "ml_Mlym_IN", + "und_MM": "my_Mymr_MM", + "und_MN": "mn_Cyrl_MN", + "und_MO": "zh_Hant_MO", + "und_Mong": "mn_Mong_CN", + "und_MQ": "fr_Latn_MQ", + "und_MR": "ar_Arab_MR", + "und_MT": "mt_Latn_MT", + "und_Mtei": "mni_Mtei_IN", + "und_MU": "mfe_Latn_MU", + "und_MV": "dv_Thaa_MV", + "und_MX": "es_Latn_MX", + "und_MY": "ms_Latn_MY", + "und_Mymr": "my_Mymr_MM", + "und_Mymr_IN": "kht_Mymr_IN", + "und_Mymr_TH": "mnw_Mymr_TH", + "und_MZ": "pt_Latn_MZ", + "und_NA": "af_Latn_NA", + "und_NC": "fr_Latn_NC", + "und_NE": "ha_Latn_NE", + "und_NI": "es_Latn_NI", + "und_Nkoo": "man_Nkoo_GN", + "und_NL": "nl_Latn_NL", + "und_NO": "nb_Latn_NO", + "und_NP": "ne_Deva_NP", + "und_Ogam": "sga_Ogam_IE", + "und_Olck": "sat_Olck_IN", + "und_OM": "ar_Arab_OM", + "und_Orkh": "otk_Orkh_MN", + "und_Orya": "or_Orya_IN", + "und_Osma": "so_Osma_SO", + "und_PA": "es_Latn_PA", + "und_PE": "es_Latn_PE", + "und_PF": "fr_Latn_PF", + "und_PG": "tpi_Latn_PG", + "und_PH": "fil_Latn_PH", + "und_Phag": "lzh_Phag_CN", + "und_Phli": "pal_Phli_IR", + "und_Phnx": "phn_Phnx_LB", + "und_PK": "ur_Arab_PK", + "und_PL": "pl_Latn_PL", + "und_Plrd": "hmd_Plrd_CN", + "und_PM": "fr_Latn_PM", + "und_PR": "es_Latn_PR", + "und_Prti": "xpr_Prti_IR", + "und_PS": "ar_Arab_PS", + "und_PT": "pt_Latn_PT", + "und_PW": "pau_Latn_PW", + "und_PY": "gn_Latn_PY", + "und_QA": "ar_Arab_QA", + "und_RE": "fr_Latn_RE", + "und_Rjng": "rej_Rjng_ID", + "und_RO": "ro_Latn_RO", + "und_RS": "sr_Cyrl_RS", + "und_RU": "ru_Cyrl_RU", + "und_Runr": "non_Runr_SE", + "und_RW": "rw_Latn_RW", + "und_SA": "ar_Arab_SA", + "und_Samr": "smp_Samr_IL", + "und_Sarb": "xsa_Sarb_YE", + "und_Saur": "saz_Saur_IN", + "und_SC": "fr_Latn_SC", + "und_SD": "ar_Arab_SD", + "und_SE": "sv_Latn_SE", + "und_Shaw": "en_Shaw_GB", + "und_Shrd": "sa_Shrd_IN", + "und_SI": "sl_Latn_SI", + "und_Sinh": "si_Sinh_LK", + "und_SJ": "nb_Latn_SJ", + "und_SK": "sk_Latn_SK", + "und_SM": "it_Latn_SM", + "und_SN": "fr_Latn_SN", + "und_SO": "so_Latn_SO", + "und_Sora": "srb_Sora_IN", + "und_SR": "nl_Latn_SR", + "und_ST": "pt_Latn_ST", + "und_Sund": "su_Sund_ID", + "und_SV": "es_Latn_SV", + "und_SY": "ar_Arab_SY", + "und_Sylo": "syl_Sylo_BD", + "und_Syrc": "syr_Syrc_IQ", + "und_Tagb": "tbw_Tagb_PH", + "und_Takr": "doi_Takr_IN", + "und_Tale": "tdd_Tale_CN", + "und_Talu": "khb_Talu_CN", + "und_Taml": "ta_Taml_IN", + "und_Tavt": "blt_Tavt_VN", + "und_TD": "fr_Latn_TD", + "und_Telu": "te_Telu_IN", + "und_TF": "fr_Latn_TF", + "und_Tfng": "zgh_Tfng_MA", + "und_TG": "fr_Latn_TG", + "und_Tglg": "fil_Tglg_PH", + "und_TH": "th_Thai_TH", + "und_Thaa": "dv_Thaa_MV", + "und_Thai": "th_Thai_TH", + "und_Thai_CN": "lcp_Thai_CN", + "und_Thai_KH": "kdt_Thai_KH", + "und_Thai_LA": "kdt_Thai_LA", + "und_Tibt": "bo_Tibt_CN", + "und_TJ": "tg_Cyrl_TJ", + "und_TK": "tkl_Latn_TK", + "und_TL": "pt_Latn_TL", + "und_TM": "tk_Latn_TM", + "und_TN": "ar_Arab_TN", + "und_TO": "to_Latn_TO", + "und_TR": "tr_Latn_TR", + "und_TV": "tvl_Latn_TV", + "und_TW": "zh_Hant_TW", + "und_TZ": "sw_Latn_TZ", + "und_UA": "uk_Cyrl_UA", + "und_UG": "sw_Latn_UG", + "und_Ugar": "uga_Ugar_SY", + "und_UY": "es_Latn_UY", + "und_UZ": "uz_Latn_UZ", + "und_VA": "la_Latn_VA", + "und_Vaii": "vai_Vaii_LR", + "und_VE": "es_Latn_VE", + "und_VN": "vi_Latn_VN", + "und_VU": "bi_Latn_VU", + "und_WF": "fr_Latn_WF", + "und_WS": "sm_Latn_WS", + "und_XK": "sq_Latn_XK", + "und_Xpeo": "peo_Xpeo_IR", + "und_Xsux": "akk_Xsux_IQ", + "und_YE": "ar_Arab_YE", + "und_Yiii": "ii_Yiii_CN", + "und_YT": "fr_Latn_YT", + "unr": "unr_Beng_IN", + "unr_Deva": "unr_Deva_NP", + "unr_NP": "unr_Deva_NP", + "unx": "unx_Beng_IN", + "ur": "ur_Arab_PK", + "uz": "uz_Latn_UZ", + "uz_AF": "uz_Arab_AF", + "uz_Arab": "uz_Arab_AF", + "uz_CN": "uz_Cyrl_CN", + "vai": "vai_Vaii_LR", + "ve": "ve_Latn_ZA", + "vi": "vi_Latn_VN", + "vo": "vo_Latn_001", + "vun": "vun_Latn_TZ", + "wa": "wa_Latn_BE", + "wae": "wae_Latn_CH", + "wal": "wal_Ethi_ET", + "war": "war_Latn_PH", + "wo": "wo_Latn_SN", + "xh": "xh_Latn_ZA", + "xog": "xog_Latn_UG", + "xsr": "xsr_Deva_NP", + "yao": "yao_Latn_MZ", + "yap": "yap_Latn_FM", + "yav": "yav_Latn_CM", + "yi": "yi_Hebr_UA", + "yo": "yo_Latn_NG", + "za": "za_Latn_CN", + "zgh": "zgh_Tfng_MA", + "zh": "zh_Hans_CN", + "zh_AU": "zh_Hant_AU", + "zh_BN": "zh_Hant_BN", + "zh_GB": "zh_Hant_GB", + "zh_GF": "zh_Hant_GF", + "zh_Hant": "zh_Hant_TW", + "zh_HK": "zh_Hant_HK", + "zh_ID": "zh_Hant_ID", + "zh_MO": "zh_Hant_MO", + "zh_MY": "zh_Hant_MY", + "zh_PA": "zh_Hant_PA", + "zh_PF": "zh_Hant_PF", + "zh_PH": "zh_Hant_PH", + "zh_SR": "zh_Hant_SR", + "zh_TH": "zh_Hant_TH", + "zh_TW": "zh_Hant_TW", + "zh_US": "zh_Hant_US", + "zh_VN": "zh_Hant_VN", + "zu": "zu_Latn_ZA", + "zza": "zza_Arab_TR" + } + } +}); + +// weekData +Globalize.load({ + "supplemental": { + "version": { + "_cldrVersion": "24", + "_number": "$Revision: 9270 $" + }, + "generation": { + "_date": "$Date: 2013-08-25 16:44:03 -0500 (Sun, 25 Aug 2013) $" + }, + "weekData": { + "minDays": { + "001": "1", + "AD": "4", + "AN": "4", + "AT": "4", + "AX": "4", + "BE": "4", + "BG": "4", + "CH": "4", + "CZ": "4", + "DE": "4", + "DK": "4", + "EE": "4", + "ES": "4", + "FI": "4", + "FJ": "4", + "FO": "4", + "FR": "4", + "GB": "4", + "GF": "4", + "GG": "4", + "GI": "4", + "GP": "4", + "GR": "4", + "GU": "1", + "HU": "4", + "IE": "4", + "IM": "4", + "IS": "4", + "IT": "4", + "JE": "4", + "LI": "4", + "LT": "4", + "LU": "4", + "MC": "4", + "MQ": "4", + "NL": "4", + "NO": "4", + "PL": "4", + "PT": "4", + "RE": "4", + "SE": "4", + "SJ": "4", + "SK": "4", + "SM": "4", + "UM": "1", + "US": "1", + "VA": "4", + "VI": "1" + }, + "firstDay": { + "001": "mon", + "AD": "mon", + "AE": "sat", + "AF": "sat", + "AG": "sun", + "AI": "mon", + "AL": "mon", + "AM": "mon", + "AN": "mon", + "AR": "sun", + "AS": "sun", + "AT": "mon", + "AU": "sun", + "AX": "mon", + "AZ": "mon", + "BA": "mon", + "BD": "fri", + "BE": "mon", + "BG": "mon", + "BH": "sat", + "BM": "mon", + "BN": "mon", + "BR": "sun", + "BS": "sun", + "BT": "sun", + "BW": "sun", + "BY": "sun", + "BZ": "sun", + "CA": "sun", + "CH": "mon", + "CL": "mon", + "CM": "mon", + "CN": "sun", + "CO": "sun", + "CR": "mon", + "CY": "mon", + "CZ": "mon", + "DE": "mon", + "DJ": "sat", + "DK": "mon", + "DM": "sun", + "DO": "sun", + "DZ": "sat", + "EC": "mon", + "EE": "mon", + "EG": "sat", + "ES": "mon", + "ET": "sun", + "FI": "mon", + "FJ": "mon", + "FO": "mon", + "FR": "mon", + "GB": "mon", + "GE": "mon", + "GF": "mon", + "GP": "mon", + "GR": "mon", + "GT": "sun", + "GU": "sun", + "HK": "sun", + "HN": "sun", + "HR": "mon", + "HU": "mon", + "ID": "sun", + "IE": "sun", + "IL": "sun", + "IN": "sun", + "IQ": "sat", + "IR": "sat", + "IS": "mon", + "IT": "mon", + "JM": "sun", + "JO": "sat", + "JP": "sun", + "KE": "sun", + "KG": "mon", + "KH": "sun", + "KR": "sun", + "KW": "sat", + "KZ": "mon", + "LA": "sun", + "LB": "mon", + "LI": "mon", + "LK": "mon", + "LT": "mon", + "LU": "mon", + "LV": "mon", + "LY": "sat", + "MA": "sat", + "MC": "mon", + "MD": "mon", + "ME": "mon", + "MH": "sun", + "MK": "mon", + "MM": "sun", + "MN": "mon", + "MO": "sun", + "MQ": "mon", + "MT": "sun", + "MV": "fri", + "MX": "sun", + "MY": "mon", + "MZ": "sun", + "NI": "sun", + "NL": "mon", + "NO": "mon", + "NP": "sun", + "NZ": "sun", + "OM": "sat", + "PA": "sun", + "PE": "sun", + "PH": "sun", + "PK": "sun", + "PL": "mon", + "PR": "sun", + "PT": "mon", + "PY": "sun", + "QA": "sat", + "RE": "mon", + "RO": "mon", + "RS": "mon", + "RU": "mon", + "SA": "sun", + "SD": "sat", + "SE": "mon", + "SG": "sun", + "SI": "mon", + "SK": "mon", + "SM": "mon", + "SV": "sun", + "SY": "sat", + "TH": "sun", + "TJ": "mon", + "TM": "mon", + "TN": "sun", + "TR": "mon", + "TT": "sun", + "TW": "sun", + "UA": "mon", + "UM": "sun", + "US": "sun", + "UY": "mon", + "UZ": "mon", + "VA": "mon", + "VE": "sun", + "VI": "sun", + "VN": "mon", + "WS": "sun", + "XK": "mon", + "YE": "sun", + "ZA": "sun", + "ZW": "sun" + }, + "firstDay-alt-variant": { + "GB": "sun" + }, + "weekendStart": { + "001": "sat", + "AE": "fri", + "AF": "thu", + "BH": "fri", + "DZ": "thu", + "EG": "fri", + "IL": "fri", + "IN": "sun", + "IQ": "fri", + "IR": "thu", + "JO": "fri", + "KW": "fri", + "LY": "fri", + "MA": "fri", + "OM": "thu", + "QA": "fri", + "SA": "fri", + "SD": "fri", + "SY": "fri", + "TN": "fri", + "YE": "fri" + }, + "weekendEnd": { + "001": "sun", + "AE": "sat", + "AF": "fri", + "BH": "sat", + "DZ": "fri", + "EG": "sat", + "IL": "sat", + "IQ": "sat", + "IR": "fri", + "JO": "sat", + "KW": "sat", + "LY": "sat", + "MA": "sat", + "OM": "fri", + "QA": "sat", + "SA": "sat", + "SD": "sat", + "SY": "sat", + "TN": "sat", + "YE": "sat" + } + } + } +}); + +// timeData +Globalize.load({ + "supplemental": { + "version": { + "_cldrVersion": "24", + "_number": "$Revision: 9270 $" + }, + "generation": { + "_date": "$Date: 2013-08-25 16:44:03 -0500 (Sun, 25 Aug 2013) $" + }, + "timeData": { + "001": { + "_allowed": "H h", + "_preferred": "H" + }, + "AD": { + "_allowed": "H", + "_preferred": "H" + }, + "AE": { + "_allowed": "H h", + "_preferred": "h" + }, + "AG": { + "_allowed": "H h", + "_preferred": "h" + }, + "AL": { + "_allowed": "H h", + "_preferred": "h" + }, + "AM": { + "_allowed": "H", + "_preferred": "H" + }, + "AO": { + "_allowed": "H", + "_preferred": "H" + }, + "AS": { + "_allowed": "H h", + "_preferred": "h" + }, + "AT": { + "_allowed": "H", + "_preferred": "H" + }, + "AU": { + "_allowed": "H h", + "_preferred": "h" + }, + "AW": { + "_allowed": "H", + "_preferred": "H" + }, + "AX": { + "_allowed": "H", + "_preferred": "H" + }, + "BB": { + "_allowed": "H h", + "_preferred": "h" + }, + "BD": { + "_allowed": "H h", + "_preferred": "h" + }, + "BE": { + "_allowed": "H", + "_preferred": "H" + }, + "BF": { + "_allowed": "H", + "_preferred": "H" + }, + "BH": { + "_allowed": "H h", + "_preferred": "h" + }, + "BJ": { + "_allowed": "H", + "_preferred": "H" + }, + "BL": { + "_allowed": "H", + "_preferred": "H" + }, + "BM": { + "_allowed": "H h", + "_preferred": "h" + }, + "BN": { + "_allowed": "H h", + "_preferred": "h" + }, + "BR": { + "_allowed": "H", + "_preferred": "H" + }, + "BS": { + "_allowed": "H h", + "_preferred": "h" + }, + "BT": { + "_allowed": "H h", + "_preferred": "h" + }, + "BW": { + "_allowed": "H h", + "_preferred": "h" + }, + "CA": { + "_allowed": "H h", + "_preferred": "h" + }, + "CD": { + "_allowed": "H", + "_preferred": "H" + }, + "CI": { + "_allowed": "H", + "_preferred": "H" + }, + "CN": { + "_allowed": "H h", + "_preferred": "h" + }, + "CO": { + "_allowed": "H h", + "_preferred": "h" + }, + "CP": { + "_allowed": "H", + "_preferred": "H" + }, + "CV": { + "_allowed": "H", + "_preferred": "H" + }, + "CY": { + "_allowed": "H h", + "_preferred": "h" + }, + "CZ": { + "_allowed": "H", + "_preferred": "H" + }, + "DE": { + "_allowed": "H", + "_preferred": "H" + }, + "DJ": { + "_allowed": "H h", + "_preferred": "h" + }, + "DK": { + "_allowed": "H", + "_preferred": "H" + }, + "DM": { + "_allowed": "H h", + "_preferred": "h" + }, + "DZ": { + "_allowed": "H h", + "_preferred": "h" + }, + "EE": { + "_allowed": "H", + "_preferred": "H" + }, + "EG": { + "_allowed": "H h", + "_preferred": "h" + }, + "EH": { + "_allowed": "H h", + "_preferred": "h" + }, + "ER": { + "_allowed": "H h", + "_preferred": "h" + }, + "ET": { + "_allowed": "H h", + "_preferred": "h" + }, + "FI": { + "_allowed": "H", + "_preferred": "H" + }, + "FJ": { + "_allowed": "H h", + "_preferred": "h" + }, + "FM": { + "_allowed": "H h", + "_preferred": "h" + }, + "FR": { + "_allowed": "H", + "_preferred": "H" + }, + "GA": { + "_allowed": "H", + "_preferred": "H" + }, + "GD": { + "_allowed": "H h", + "_preferred": "h" + }, + "GF": { + "_allowed": "H", + "_preferred": "H" + }, + "GH": { + "_allowed": "H h", + "_preferred": "h" + }, + "GL": { + "_allowed": "H h", + "_preferred": "h" + }, + "GM": { + "_allowed": "H h", + "_preferred": "h" + }, + "GN": { + "_allowed": "H", + "_preferred": "H" + }, + "GP": { + "_allowed": "H", + "_preferred": "H" + }, + "GR": { + "_allowed": "H h", + "_preferred": "h" + }, + "GU": { + "_allowed": "H h", + "_preferred": "h" + }, + "GW": { + "_allowed": "H", + "_preferred": "H" + }, + "GY": { + "_allowed": "H h", + "_preferred": "h" + }, + "HK": { + "_allowed": "H h", + "_preferred": "h" + }, + "HR": { + "_allowed": "H", + "_preferred": "H" + }, + "IL": { + "_allowed": "H", + "_preferred": "H" + }, + "IN": { + "_allowed": "H h", + "_preferred": "h" + }, + "IQ": { + "_allowed": "H h", + "_preferred": "h" + }, + "IS": { + "_allowed": "H", + "_preferred": "H" + }, + "IT": { + "_allowed": "H", + "_preferred": "H" + }, + "JM": { + "_allowed": "H h", + "_preferred": "h" + }, + "JO": { + "_allowed": "H h", + "_preferred": "h" + }, + "JP": { + "_allowed": "H K h", + "_preferred": "H" + }, + "KH": { + "_allowed": "H h", + "_preferred": "h" + }, + "KI": { + "_allowed": "H h", + "_preferred": "h" + }, + "KN": { + "_allowed": "H h", + "_preferred": "h" + }, + "KP": { + "_allowed": "H h", + "_preferred": "h" + }, + "KR": { + "_allowed": "H h", + "_preferred": "h" + }, + "KW": { + "_allowed": "H h", + "_preferred": "h" + }, + "KY": { + "_allowed": "H h", + "_preferred": "h" + }, + "LB": { + "_allowed": "H h", + "_preferred": "h" + }, + "LC": { + "_allowed": "H h", + "_preferred": "h" + }, + "LR": { + "_allowed": "H h", + "_preferred": "h" + }, + "LS": { + "_allowed": "H h", + "_preferred": "h" + }, + "LY": { + "_allowed": "H h", + "_preferred": "h" + }, + "MA": { + "_allowed": "H h", + "_preferred": "h" + }, + "MC": { + "_allowed": "H", + "_preferred": "H" + }, + "MD": { + "_allowed": "H", + "_preferred": "H" + }, + "MF": { + "_allowed": "H", + "_preferred": "H" + }, + "MH": { + "_allowed": "H h", + "_preferred": "h" + }, + "ML": { + "_allowed": "H", + "_preferred": "H" + }, + "MO": { + "_allowed": "H h", + "_preferred": "h" + }, + "MP": { + "_allowed": "H h", + "_preferred": "h" + }, + "MQ": { + "_allowed": "H", + "_preferred": "H" + }, + "MR": { + "_allowed": "H h", + "_preferred": "h" + }, + "MW": { + "_allowed": "H h", + "_preferred": "h" + }, + "MY": { + "_allowed": "H h", + "_preferred": "h" + }, + "MZ": { + "_allowed": "H", + "_preferred": "H" + }, + "NA": { + "_allowed": "H h", + "_preferred": "h" + }, + "NC": { + "_allowed": "H", + "_preferred": "H" + }, + "NE": { + "_allowed": "H", + "_preferred": "H" + }, + "NG": { + "_allowed": "H h", + "_preferred": "h" + }, + "NL": { + "_allowed": "H", + "_preferred": "H" + }, + "NZ": { + "_allowed": "H h", + "_preferred": "h" + }, + "OM": { + "_allowed": "H h", + "_preferred": "h" + }, + "PG": { + "_allowed": "H h", + "_preferred": "h" + }, + "PK": { + "_allowed": "H h", + "_preferred": "h" + }, + "PM": { + "_allowed": "H", + "_preferred": "H" + }, + "PR": { + "_allowed": "H h", + "_preferred": "h" + }, + "PS": { + "_allowed": "H h", + "_preferred": "h" + }, + "PT": { + "_allowed": "H", + "_preferred": "H" + }, + "PW": { + "_allowed": "H h", + "_preferred": "h" + }, + "QA": { + "_allowed": "H h", + "_preferred": "h" + }, + "RE": { + "_allowed": "H", + "_preferred": "H" + }, + "RO": { + "_allowed": "H", + "_preferred": "H" + }, + "RU": { + "_allowed": "H", + "_preferred": "H" + }, + "SA": { + "_allowed": "H h", + "_preferred": "h" + }, + "SB": { + "_allowed": "H h", + "_preferred": "h" + }, + "SD": { + "_allowed": "H h", + "_preferred": "h" + }, + "SE": { + "_allowed": "H", + "_preferred": "H" + }, + "SG": { + "_allowed": "H h", + "_preferred": "h" + }, + "SI": { + "_allowed": "H", + "_preferred": "H" + }, + "SJ": { + "_allowed": "H", + "_preferred": "H" + }, + "SK": { + "_allowed": "H", + "_preferred": "H" + }, + "SL": { + "_allowed": "H h", + "_preferred": "h" + }, + "SM": { + "_allowed": "H", + "_preferred": "H" + }, + "SO": { + "_allowed": "H h", + "_preferred": "h" + }, + "SR": { + "_allowed": "H", + "_preferred": "H" + }, + "SS": { + "_allowed": "H h", + "_preferred": "h" + }, + "ST": { + "_allowed": "H", + "_preferred": "H" + }, + "SY": { + "_allowed": "H h", + "_preferred": "h" + }, + "SZ": { + "_allowed": "H h", + "_preferred": "h" + }, + "TC": { + "_allowed": "H h", + "_preferred": "h" + }, + "TD": { + "_allowed": "H h", + "_preferred": "h" + }, + "TG": { + "_allowed": "H", + "_preferred": "H" + }, + "TN": { + "_allowed": "H h", + "_preferred": "h" + }, + "TR": { + "_allowed": "H", + "_preferred": "H" + }, + "TT": { + "_allowed": "H h", + "_preferred": "h" + }, + "TW": { + "_allowed": "H h", + "_preferred": "h" + }, + "UM": { + "_allowed": "H h", + "_preferred": "h" + }, + "US": { + "_allowed": "H h", + "_preferred": "h" + }, + "VC": { + "_allowed": "H h", + "_preferred": "h" + }, + "VG": { + "_allowed": "H h", + "_preferred": "h" + }, + "VI": { + "_allowed": "H h", + "_preferred": "h" + }, + "VU": { + "_allowed": "H h", + "_preferred": "h" + }, + "WF": { + "_allowed": "H", + "_preferred": "H" + }, + "WS": { + "_allowed": "H h", + "_preferred": "h" + }, + "YE": { + "_allowed": "H h", + "_preferred": "h" + }, + "YT": { + "_allowed": "H", + "_preferred": "H" + }, + "ZA": { + "_allowed": "H h", + "_preferred": "h" + }, + "ZM": { + "_allowed": "H h", + "_preferred": "h" + }, + "ZW": { + "_allowed": "H h", + "_preferred": "h" + } + } + } +}); + +/** + * jQuery UI translation data + */ var regions = { - "en": { - "closeText": "Done", - "prevText": "Prev", - "nextText": "Next", - "currentText": "Today", - "weekHeader": "Wk", - "dateFormat": "d", - "datePickerRole": "date picker" - }, - "af": { - "closeText": "Selekteer", - "prevText": "Vorige", - "nextText": "Volgende", - "currentText": "Vandag", - "weekHeader": "Wk", - "dateFormat": "d" - }, - "zh-TW": { - "closeText": "\u95dc\u9589", - "prevText": "<\u4e0a\u6708", - "nextText": "\u4e0b\u6708>", - "currentText": "\u4eca\u5929", - "weekHeader": "\u5468", - "dateFormat": "d" - }, - "ar": { - "closeText": "\u0625\u063a\u0644\u0627\u0642", - "prevText": "<\u0627\u0644\u0633\u0627\u0628\u0642", - "nextText": "\u0627\u0644\u062a\u0627\u0644\u064a>", - "currentText": "\u0627\u0644\u064a\u0648\u0645", - "weekHeader": "\u0623\u0633\u0628\u0648\u0639", - "dateFormat": "d" - }, - "az": { - "closeText": "Ba\u011fla", - "prevText": "<Geri", - "nextText": "\u0130r\u0259li>", - "currentText": "Bug\u00fcn", - "weekHeader": "Hf", - "dateFormat": "d" - }, - "bg": { - "closeText": "\u0437\u0430\u0442\u0432\u043e\u0440\u0438", - "prevText": "<\u043d\u0430\u0437\u0430\u0434", - "nextText": "\u043d\u0430\u043f\u0440\u0435\u0434>", - "currentText": "\u0434\u043d\u0435\u0441", - "weekHeader": "Wk", - "dateFormat": "d" - }, - "bs": { - "closeText": "Zatvori", - "prevText": "<", - "nextText": ">", - "currentText": "Danas", - "weekHeader": "Wk", - "dateFormat": "d" - }, - "ca": { - "closeText": "Tancar", - "prevText": "<Ant", - "nextText": "Seg>", - "currentText": "Avui", - "weekHeader": "Sm", - "dateFormat": "d" - }, - "cs": { - "closeText": "Zav\u0159\u00edt", - "prevText": "<D\u0159\u00edve", - "nextText": "Pozd\u011bji>", - "currentText": "Nyn\u00ed", - "weekHeader": "T\u00fdd", - "dateFormat": "d" - }, - "da": { - "closeText": "Luk", - "prevText": "<Forrige", - "nextText": "N\u00e6ste>", - "currentText": "Idag", - "weekHeader": "Uge", - "dateFormat": "d" - }, - "de": { - "closeText": "schlie\u00dfen", - "prevText": "<zur\u00fcck", - "nextText": "Vor>", - "currentText": "heute", - "weekHeader": "Wo", - "dateFormat": "d" - }, - "el": { - "closeText": "\u039a\u03bb\u03b5\u03af\u03c3\u03b9\u03bc\u03bf", - "prevText": "\u03a0\u03c1\u03bf\u03b7\u03b3\u03bf\u03cd\u03bc\u03b5\u03bd\u03bf\u03c2", - "nextText": "\u0395\u03c0\u03cc\u03bc\u03b5\u03bd\u03bf\u03c2", - "currentText": "\u03a4\u03c1\u03ad\u03c7\u03c9\u03bd \u039c\u03ae\u03bd\u03b1\u03c2", - "weekHeader": "\u0395\u03b2\u03b4", - "dateFormat": "d" - }, - "en-GB": { - "closeText": "Done", - "prevText": "Prev", - "nextText": "Next", - "currentText": "Today", - "weekHeader": "Wk", - "dateFormat": "d" - }, - "eo": { - "closeText": "Fermi", - "prevText": "<Anta", - "nextText": "Sekv>", - "currentText": "Nuna", - "weekHeader": "Sb", - "dateFormat": "dd/MM/yyyy" - }, - "es": { - "closeText": "Cerrar", - "prevText": "<Ant", - "nextText": "Sig>", - "currentText": "Hoy", - "weekHeader": "Sm", - "dateFormat": "d" - }, - "et": { - "closeText": "Sulge", - "prevText": "Eelnev", - "nextText": "J\u00e4rgnev", - "currentText": "T\u00e4na", - "weekHeader": "Sm", - "dateFormat": "d" - }, - "eu": { - "closeText": "Egina", - "prevText": "<Aur", - "nextText": "Hur>", - "currentText": "Gaur", - "weekHeader": "Wk", - "dateFormat": "d" - }, - "fa": { - "closeText": "\u0628\u0633\u062a\u0646", - "prevText": "<\u0642\u0628\u0644\u064a", - "nextText": "\u0628\u0639\u062f\u064a>", - "currentText": "\u0627\u0645\u0631\u0648\u0632", - "weekHeader": "\u0647\u0641", - "dateFormat": "d" - }, - "fi": { - "closeText": "Sulje", - "prevText": "«Edellinen", - "nextText": "Seuraava»", - "currentText": "Tänään", - "weekHeader": "Vk", - "dateFormat": "d" - }, - "fo": { - "closeText": "Lat aftur", - "prevText": "<Fyrra", - "nextText": "N\u00e6sta>", - "currentText": "\u00cd dag", - "weekHeader": "Vk", - "dateFormat": "d" - }, - "fr-CH": { - "closeText": "Fermer", - "prevText": "<Pr\u00e9c", - "nextText": "Suiv>", - "currentText": "Courant", - "weekHeader": "Sm", - "dateFormat": "d" - }, - "fr": { - "closeText": "Fermer", - "prevText": "<Pr\u00e9c", - "nextText": "Suiv>", - "currentText": "Courant", - "weekHeader": "Sm", - "dateFormat": "d" - }, - "he": { - "closeText": "\u05e1\u05d2\u05d5\u05e8", - "prevText": "<\u05d4\u05e7\u05d5\u05d3\u05dd", - "nextText": "\u05d4\u05d1\u05d0>", - "currentText": "\u05d4\u05d9\u05d5\u05dd", - "weekHeader": "Wk", - "dateFormat": "d" - }, - "hr": { - "closeText": "Zatvori", - "prevText": "<", - "nextText": ">", - "currentText": "Danas", - "weekHeader": "Tje", - "dateFormat": "d" - }, - "hu": { - "closeText": "bez\u00c3\u00a1r\u00c3\u00a1s", - "prevText": "« vissza", - "nextText": "el\u00c5\u2018re »", - "currentText": "ma", - "weekHeader": "H\u00c3\u00a9", - "dateFormat": "d" - }, - "hy": { - "closeText": "\u00d5\u201c\u00d5\u00a1\u00d5\u00af\u00d5\u00a5\u00d5\u00ac", - "prevText": "<\u00d5\u2020\u00d5\u00a1\u00d5\u00ad.", - "nextText": "\u00d5\u20ac\u00d5\u00a1\u00d5\u00bb.>", - "currentText": "\u00d4\u00b1\u00d5\u00b5\u00d5\u00bd\u00d6\u2026\u00d6\u20ac", - "weekHeader": "\u00d5\u2021\u00d4\u00b2\u00d5\u008f", - "dateFormat": "d" - }, - "id": { - "closeText": "Tutup", - "prevText": "<mundur", - "nextText": "maju>", - "currentText": "hari ini", - "weekHeader": "Mg", - "dateFormat": "d" - }, - "is": { - "closeText": "Loka", - "prevText": "< Fyrri", - "nextText": "Næsti >", - "currentText": "Í dag", - "weekHeader": "Vika", - "dateFormat": "d" - }, - "it": { - "closeText": "Chiudi", - "prevText": "<Prec", - "nextText": "Succ>", - "currentText": "Oggi", - "weekHeader": "Sm", - "dateFormat": "d" - }, - "ja": { - "closeText": "\u9589\u3058\u308b", - "prevText": "<\u524d", - "nextText": "\u6b21>", - "currentText": "\u4eca\u65e5", - "weekHeader": "\u9031", - "dateFormat": "d" - }, - "ko": { - "closeText": "\u00eb\u2039\u00ab\u00ea\u00b8\u00b0", - "prevText": "\u00ec\u009d\u00b4\u00ec\u00a0\u201e\u00eb\u2039\u00ac", - "nextText": "\u00eb\u2039\u00a4\u00ec\u009d\u0152\u00eb\u2039\u00ac", - "currentText": "\u00ec\u02dc\u00a4\u00eb\u0160\u02dc", - "weekHeader": "Wk", - "dateFormat": "d" - }, - "lt": { - "closeText": "U\u00c5\u00bedaryti", - "prevText": "<Atgal", - "nextText": "Pirmyn>", - "currentText": "\u00c5\u00a0iandien", - "weekHeader": "Wk", - "dateFormat": "d" - }, - "lv": { - "closeText": "Aizv\u00c4\u201crt", - "prevText": "Iepr", - "nextText": "N\u00c4\u0081ka", - "currentText": "\u00c5\u00a0odien", - "weekHeader": "Nav", - "dateFormat": "d" - }, - "ms": { - "closeText": "Tutup", - "prevText": "<Sebelum", - "nextText": "Selepas>", - "currentText": "hari ini", - "weekHeader": "Mg", - "dateFormat": "d" - }, - "nl": { - "closeText": "Sluiten", - "prevText": "\u2190", - "nextText": "\u2192", - "currentText": "Vandaag", - "weekHeader": "Wk", - "dateFormat": "d" - }, - "no": { - "closeText": "Lukk", - "prevText": "«Forrige", - "nextText": "Neste»", - "currentText": "I dag", - "weekHeader": "Uke", - "dateFormat": "d" - }, - "pl": { - "closeText": "Zamknij", - "prevText": "<Poprzedni", - "nextText": "Nast\u00c4\u2122pny>", - "currentText": "Dzi\u00c5\u203a", - "weekHeader": "Tydz", - "dateFormat": "d" - }, - "pt-BR": { - "closeText": "Fechar", - "prevText": "<Anterior", - "nextText": "Próximo>", - "currentText": "Hoje", - "weekHeader": "Sm", - "dateFormat": "d" - }, - "ro": { - "closeText": "\u00cenchide", - "prevText": "« Luna precedent\u0103", - "nextText": "Luna urm\u0103toare »", - "currentText": "Azi", - "weekHeader": "S\u0103pt", - "dateFormat": "d" - }, - "ru": { - "closeText": "\u00d0\u2014\u00d0\u00b0\u00d0\u00ba\u00d1\u20ac\u00d1\u2039\u00d1\u201a\u00d1\u0152", - "prevText": "<\u00d0\u0178\u00d1\u20ac\u00d0\u00b5\u00d0\u00b4", - "nextText": "\u00d0\u00a1\u00d0\u00bb\u00d0\u00b5\u00d0\u00b4>", - "currentText": "\u00d0\u00a1\u00d0\u00b5\u00d0\u00b3\u00d0\u00be\u00d0\u00b4\u00d0\u00bd\u00d1\u008f", - "weekHeader": "\u00d0\u009d\u00d0\u00b5", - "dateFormat": "d" - }, - "sk": { - "closeText": "Zavrie\u00c5\u00a5", - "prevText": "<Predch\u00c3\u00a1dzaj\u00c3\u00baci", - "nextText": "Nasleduj\u00c3\u00baci>", - "currentText": "Dnes", - "weekHeader": "Ty", - "dateFormat": "d" - }, - "sl": { - "closeText": "Zapri", - "prevText": "<Prejšnji", - "nextText": "Naslednji>", - "currentText": "Trenutni", - "weekHeader": "Teden", - "dateFormat": "d" - }, - "sq": { - "closeText": "mbylle", - "prevText": "<mbrapa", - "nextText": "P\u00ebrpara>", - "currentText": "sot", - "weekHeader": "Ja", - "dateFormat": "d" - }, - "sr-SR": { - "closeText": "Zatvori", - "prevText": "<", - "nextText": ">", - "currentText": "Danas", - "weekHeader": "Sed", - "dateFormat": "dd/MM/yyyy" - }, - "sr": { - "closeText": "\u0417\u0430\u0442\u0432\u043e\u0440\u0438", - "prevText": "<", - "nextText": ">", - "currentText": "\u0414\u0430\u043d\u0430\u0441", - "weekHeader": "\u0421\u0435\u0434", - "dateFormat": "d" - }, - "sv": { - "closeText": "St\u00e4ng", - "prevText": "«F\u00f6rra", - "nextText": "N\u00e4sta»", - "currentText": "Idag", - "weekHeader": "Ve", - "dateFormat": "d" - }, - "ta": { - "closeText": "\u0bae\u0bc2\u0b9f\u0bc1", - "prevText": "\u0bae\u0bc1\u0ba9\u0bcd\u0ba9\u0bc8\u0baf\u0ba4\u0bc1", - "nextText": "\u0b85\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0ba4\u0bc1", - "currentText": "\u0b87\u0ba9\u0bcd\u0bb1\u0bc1", - "weekHeader": "\u041d\u0435", - "dateFormat": "d" - }, - "th": { - "closeText": "\u0e1b\u0e34\u0e14", - "prevText": "« \u0e22\u0e49\u0e2d\u0e19", - "nextText": "\u0e16\u0e31\u0e14\u0e44\u0e1b »", - "currentText": "\u0e27\u0e31\u0e19\u0e19\u0e35\u0e49", - "weekHeader": "Wk", - "dateFormat": "d" - }, - "tr": { - "closeText": "kapat", - "prevText": "<geri", - "nextText": "ileri>", - "currentText": "bug\u00c3\u00bcn", - "weekHeader": "Hf", - "dateFormat": "d" - }, - "uk": { - "closeText": "\u00d0\u2014\u00d0\u00b0\u00d0\u00ba\u00d1\u20ac\u00d0\u00b8\u00d1\u201a\u00d0\u00b8", - "prevText": "<", - "nextText": ">", - "currentText": "\u00d0\u00a1\u00d1\u0152\u00d0\u00be\u00d0\u00b3\u00d0\u00be\u00d0\u00b4\u00d0\u00bd\u00d1\u2013", - "weekHeader": "\u00d0\u009d\u00d0\u00b5", - "dateFormat": "d" - }, - "vi": { - "closeText": "\u0110\u00f3ng", - "prevText": "<Tr\u01b0\u1edbc", - "nextText": "Ti\u1ebfp>", - "currentText": "H\u00f4m nay", - "weekHeader": "Tu", - "dateFormat": "d" - }, - "zh-CN": { - "closeText": "\u00e5\u2026\u00b3\u00e9\u2014\u00ad", - "prevText": "<\u00e4\u00b8\u0160\u00e6\u0153\u02c6", - "nextText": "\u00e4\u00b8\u2039\u00e6\u0153\u02c6>", - "currentText": "\u00e4\u00bb\u0160\u00e5\u00a4\u00a9", - "weekHeader": "\u00e5\u2018\u00a8", - "dateFormat": "d" - }, - "zh-HK": { - "closeText": "\u00e9\u2014\u0153\u00e9\u2013\u2030", - "prevText": "<\u00e4\u00b8\u0160\u00e6\u0153\u02c6", - "nextText": "\u00e4\u00b8\u2039\u00e6\u0153\u02c6>", - "currentText": "\u00e4\u00bb\u0160\u00e5\u00a4\u00a9", - "weekHeader": "\u00e5\u2018\u00a8", - "dateFormat": "d" - } + "en": { + "closeText": "Done", + "prevText": "Prev", + "nextText": "Next", + "currentText": "Today", + "weekHeader": "Wk", + "dateFormat": "d", + "datePickerRole": "date picker" + }, + "af": { + "closeText": "Selekteer", + "prevText": "Vorige", + "nextText": "Volgende", + "currentText": "Vandag", + "weekHeader": "Wk", + "dateFormat": "d" + }, + "zh-TW": { + "closeText": "\u95dc\u9589", + "prevText": "<\u4e0a\u6708", + "nextText": "\u4e0b\u6708>", + "currentText": "\u4eca\u5929", + "weekHeader": "\u5468", + "dateFormat": "d" + }, + "ar": { + "closeText": "\u0625\u063a\u0644\u0627\u0642", + "prevText": "<\u0627\u0644\u0633\u0627\u0628\u0642", + "nextText": "\u0627\u0644\u062a\u0627\u0644\u064a>", + "currentText": "\u0627\u0644\u064a\u0648\u0645", + "weekHeader": "\u0623\u0633\u0628\u0648\u0639", + "dateFormat": "d" + }, + "az": { + "closeText": "Ba\u011fla", + "prevText": "<Geri", + "nextText": "\u0130r\u0259li>", + "currentText": "Bug\u00fcn", + "weekHeader": "Hf", + "dateFormat": "d" + }, + "bg": { + "closeText": "\u0437\u0430\u0442\u0432\u043e\u0440\u0438", + "prevText": "<\u043d\u0430\u0437\u0430\u0434", + "nextText": "\u043d\u0430\u043f\u0440\u0435\u0434>", + "currentText": "\u0434\u043d\u0435\u0441", + "weekHeader": "Wk", + "dateFormat": "d" + }, + "bs": { + "closeText": "Zatvori", + "prevText": "<", + "nextText": ">", + "currentText": "Danas", + "weekHeader": "Wk", + "dateFormat": "d" + }, + "ca": { + "closeText": "Tancar", + "prevText": "<Ant", + "nextText": "Seg>", + "currentText": "Avui", + "weekHeader": "Sm", + "dateFormat": "d" + }, + "cs": { + "closeText": "Zav\u0159\u00edt", + "prevText": "<D\u0159\u00edve", + "nextText": "Pozd\u011bji>", + "currentText": "Nyn\u00ed", + "weekHeader": "T\u00fdd", + "dateFormat": "d" + }, + "da": { + "closeText": "Luk", + "prevText": "<Forrige", + "nextText": "N\u00e6ste>", + "currentText": "Idag", + "weekHeader": "Uge", + "dateFormat": "d" + }, + "de": { + "closeText": "schlie\u00dfen", + "prevText": "<zur\u00fcck", + "nextText": "Vor>", + "currentText": "heute", + "weekHeader": "Wo", + "dateFormat": "d" + }, + "el": { + "closeText": "\u039a\u03bb\u03b5\u03af\u03c3\u03b9\u03bc\u03bf", + "prevText": "\u03a0\u03c1\u03bf\u03b7\u03b3\u03bf\u03cd\u03bc\u03b5\u03bd\u03bf\u03c2", + "nextText": "\u0395\u03c0\u03cc\u03bc\u03b5\u03bd\u03bf\u03c2", + "currentText": "\u03a4\u03c1\u03ad\u03c7\u03c9\u03bd \u039c\u03ae\u03bd\u03b1\u03c2", + "weekHeader": "\u0395\u03b2\u03b4", + "dateFormat": "d" + }, + "en-GB": { + "closeText": "Done", + "prevText": "Prev", + "nextText": "Next", + "currentText": "Today", + "weekHeader": "Wk", + "dateFormat": "d" + }, + "eo": { + "closeText": "Fermi", + "prevText": "<Anta", + "nextText": "Sekv>", + "currentText": "Nuna", + "weekHeader": "Sb", + "dateFormat": "dd/MM/yyyy" + }, + "es": { + "closeText": "Cerrar", + "prevText": "<Ant", + "nextText": "Sig>", + "currentText": "Hoy", + "weekHeader": "Sm", + "dateFormat": "d" + }, + "et": { + "closeText": "Sulge", + "prevText": "Eelnev", + "nextText": "J\u00e4rgnev", + "currentText": "T\u00e4na", + "weekHeader": "Sm", + "dateFormat": "d" + }, + "eu": { + "closeText": "Egina", + "prevText": "<Aur", + "nextText": "Hur>", + "currentText": "Gaur", + "weekHeader": "Wk", + "dateFormat": "d" + }, + "fa": { + "closeText": "\u0628\u0633\u062a\u0646", + "prevText": "<\u0642\u0628\u0644\u064a", + "nextText": "\u0628\u0639\u062f\u064a>", + "currentText": "\u0627\u0645\u0631\u0648\u0632", + "weekHeader": "\u0647\u0641", + "dateFormat": "d" + }, + "fi": { + "closeText": "Sulje", + "prevText": "«Edellinen", + "nextText": "Seuraava»", + "currentText": "Tänään", + "weekHeader": "Vk", + "dateFormat": "d" + }, + "fo": { + "closeText": "Lat aftur", + "prevText": "<Fyrra", + "nextText": "N\u00e6sta>", + "currentText": "\u00cd dag", + "weekHeader": "Vk", + "dateFormat": "d" + }, + "fr-CH": { + "closeText": "Fermer", + "prevText": "<Pr\u00e9c", + "nextText": "Suiv>", + "currentText": "Courant", + "weekHeader": "Sm", + "dateFormat": "d" + }, + "fr": { + "closeText": "Fermer", + "prevText": "<Pr\u00e9c", + "nextText": "Suiv>", + "currentText": "Courant", + "weekHeader": "Sm", + "dateFormat": "d" + }, + "he": { + "closeText": "\u05e1\u05d2\u05d5\u05e8", + "prevText": "<\u05d4\u05e7\u05d5\u05d3\u05dd", + "nextText": "\u05d4\u05d1\u05d0>", + "currentText": "\u05d4\u05d9\u05d5\u05dd", + "weekHeader": "Wk", + "dateFormat": "d" + }, + "hr": { + "closeText": "Zatvori", + "prevText": "<", + "nextText": ">", + "currentText": "Danas", + "weekHeader": "Tje", + "dateFormat": "d" + }, + "hu": { + "closeText": "bez\u00c3\u00a1r\u00c3\u00a1s", + "prevText": "« vissza", + "nextText": "el\u00c5\u2018re »", + "currentText": "ma", + "weekHeader": "H\u00c3\u00a9", + "dateFormat": "d" + }, + "hy": { + "closeText": "\u00d5\u201c\u00d5\u00a1\u00d5\u00af\u00d5\u00a5\u00d5\u00ac", + "prevText": "<\u00d5\u2020\u00d5\u00a1\u00d5\u00ad.", + "nextText": "\u00d5\u20ac\u00d5\u00a1\u00d5\u00bb.>", + "currentText": "\u00d4\u00b1\u00d5\u00b5\u00d5\u00bd\u00d6\u2026\u00d6\u20ac", + "weekHeader": "\u00d5\u2021\u00d4\u00b2\u00d5\u008f", + "dateFormat": "d" + }, + "id": { + "closeText": "Tutup", + "prevText": "<mundur", + "nextText": "maju>", + "currentText": "hari ini", + "weekHeader": "Mg", + "dateFormat": "d" + }, + "is": { + "closeText": "Loka", + "prevText": "< Fyrri", + "nextText": "Næsti >", + "currentText": "Í dag", + "weekHeader": "Vika", + "dateFormat": "d" + }, + "it": { + "closeText": "Chiudi", + "prevText": "<Prec", + "nextText": "Succ>", + "currentText": "Oggi", + "weekHeader": "Sm", + "dateFormat": "d" + }, + "ja": { + "closeText": "\u9589\u3058\u308b", + "prevText": "<\u524d", + "nextText": "\u6b21>", + "currentText": "\u4eca\u65e5", + "weekHeader": "\u9031", + "dateFormat": "d" + }, + "ko": { + "closeText": "\u00eb\u2039\u00ab\u00ea\u00b8\u00b0", + "prevText": "\u00ec\u009d\u00b4\u00ec\u00a0\u201e\u00eb\u2039\u00ac", + "nextText": "\u00eb\u2039\u00a4\u00ec\u009d\u0152\u00eb\u2039\u00ac", + "currentText": "\u00ec\u02dc\u00a4\u00eb\u0160\u02dc", + "weekHeader": "Wk", + "dateFormat": "d" + }, + "lt": { + "closeText": "U\u00c5\u00bedaryti", + "prevText": "<Atgal", + "nextText": "Pirmyn>", + "currentText": "\u00c5\u00a0iandien", + "weekHeader": "Wk", + "dateFormat": "d" + }, + "lv": { + "closeText": "Aizv\u00c4\u201crt", + "prevText": "Iepr", + "nextText": "N\u00c4\u0081ka", + "currentText": "\u00c5\u00a0odien", + "weekHeader": "Nav", + "dateFormat": "d" + }, + "ms": { + "closeText": "Tutup", + "prevText": "<Sebelum", + "nextText": "Selepas>", + "currentText": "hari ini", + "weekHeader": "Mg", + "dateFormat": "d" + }, + "nl": { + "closeText": "Sluiten", + "prevText": "\u2190", + "nextText": "\u2192", + "currentText": "Vandaag", + "weekHeader": "Wk", + "dateFormat": "d" + }, + "no": { + "closeText": "Lukk", + "prevText": "«Forrige", + "nextText": "Neste»", + "currentText": "I dag", + "weekHeader": "Uke", + "dateFormat": "d" + }, + "pl": { + "closeText": "Zamknij", + "prevText": "<Poprzedni", + "nextText": "Nast\u00c4\u2122pny>", + "currentText": "Dzi\u00c5\u203a", + "weekHeader": "Tydz", + "dateFormat": "d" + }, + "pt-BR": { + "closeText": "Fechar", + "prevText": "<Anterior", + "nextText": "Próximo>", + "currentText": "Hoje", + "weekHeader": "Sm", + "dateFormat": "d" + }, + "ro": { + "closeText": "\u00cenchide", + "prevText": "« Luna precedent\u0103", + "nextText": "Luna urm\u0103toare »", + "currentText": "Azi", + "weekHeader": "S\u0103pt", + "dateFormat": "d" + }, + "ru": { + "closeText": "\u00d0\u2014\u00d0\u00b0\u00d0\u00ba\u00d1\u20ac\u00d1\u2039\u00d1\u201a\u00d1\u0152", + "prevText": "<\u00d0\u0178\u00d1\u20ac\u00d0\u00b5\u00d0\u00b4", + "nextText": "\u00d0\u00a1\u00d0\u00bb\u00d0\u00b5\u00d0\u00b4>", + "currentText": "\u00d0\u00a1\u00d0\u00b5\u00d0\u00b3\u00d0\u00be\u00d0\u00b4\u00d0\u00bd\u00d1\u008f", + "weekHeader": "\u00d0\u009d\u00d0\u00b5", + "dateFormat": "d" + }, + "sk": { + "closeText": "Zavrie\u00c5\u00a5", + "prevText": "<Predch\u00c3\u00a1dzaj\u00c3\u00baci", + "nextText": "Nasleduj\u00c3\u00baci>", + "currentText": "Dnes", + "weekHeader": "Ty", + "dateFormat": "d" + }, + "sl": { + "closeText": "Zapri", + "prevText": "<Prejšnji", + "nextText": "Naslednji>", + "currentText": "Trenutni", + "weekHeader": "Teden", + "dateFormat": "d" + }, + "sq": { + "closeText": "mbylle", + "prevText": "<mbrapa", + "nextText": "P\u00ebrpara>", + "currentText": "sot", + "weekHeader": "Ja", + "dateFormat": "d" + }, + "sr-SR": { + "closeText": "Zatvori", + "prevText": "<", + "nextText": ">", + "currentText": "Danas", + "weekHeader": "Sed", + "dateFormat": "dd/MM/yyyy" + }, + "sr": { + "closeText": "\u0417\u0430\u0442\u0432\u043e\u0440\u0438", + "prevText": "<", + "nextText": ">", + "currentText": "\u0414\u0430\u043d\u0430\u0441", + "weekHeader": "\u0421\u0435\u0434", + "dateFormat": "d" + }, + "sv": { + "closeText": "St\u00e4ng", + "prevText": "«F\u00f6rra", + "nextText": "N\u00e4sta»", + "currentText": "Idag", + "weekHeader": "Ve", + "dateFormat": "d" + }, + "ta": { + "closeText": "\u0bae\u0bc2\u0b9f\u0bc1", + "prevText": "\u0bae\u0bc1\u0ba9\u0bcd\u0ba9\u0bc8\u0baf\u0ba4\u0bc1", + "nextText": "\u0b85\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0ba4\u0bc1", + "currentText": "\u0b87\u0ba9\u0bcd\u0bb1\u0bc1", + "weekHeader": "\u041d\u0435", + "dateFormat": "d" + }, + "th": { + "closeText": "\u0e1b\u0e34\u0e14", + "prevText": "« \u0e22\u0e49\u0e2d\u0e19", + "nextText": "\u0e16\u0e31\u0e14\u0e44\u0e1b »", + "currentText": "\u0e27\u0e31\u0e19\u0e19\u0e35\u0e49", + "weekHeader": "Wk", + "dateFormat": "d" + }, + "tr": { + "closeText": "kapat", + "prevText": "<geri", + "nextText": "ileri>", + "currentText": "bug\u00c3\u00bcn", + "weekHeader": "Hf", + "dateFormat": "d" + }, + "uk": { + "closeText": "\u00d0\u2014\u00d0\u00b0\u00d0\u00ba\u00d1\u20ac\u00d0\u00b8\u00d1\u201a\u00d0\u00b8", + "prevText": "<", + "nextText": ">", + "currentText": "\u00d0\u00a1\u00d1\u0152\u00d0\u00be\u00d0\u00b3\u00d0\u00be\u00d0\u00b4\u00d0\u00bd\u00d1\u2013", + "weekHeader": "\u00d0\u009d\u00d0\u00b5", + "dateFormat": "d" + }, + "vi": { + "closeText": "\u0110\u00f3ng", + "prevText": "<Tr\u01b0\u1edbc", + "nextText": "Ti\u1ebfp>", + "currentText": "H\u00f4m nay", + "weekHeader": "Tu", + "dateFormat": "d" + }, + "zh-CN": { + "closeText": "\u00e5\u2026\u00b3\u00e9\u2014\u00ad", + "prevText": "<\u00e4\u00b8\u0160\u00e6\u0153\u02c6", + "nextText": "\u00e4\u00b8\u2039\u00e6\u0153\u02c6>", + "currentText": "\u00e4\u00bb\u0160\u00e5\u00a4\u00a9", + "weekHeader": "\u00e5\u2018\u00a8", + "dateFormat": "d" + }, + "zh-HK": { + "closeText": "\u00e9\u2014\u0153\u00e9\u2013\u2030", + "prevText": "<\u00e4\u00b8\u0160\u00e6\u0153\u02c6", + "nextText": "\u00e4\u00b8\u2039\u00e6\u0153\u02c6>", + "currentText": "\u00e4\u00bb\u0160\u00e5\u00a4\u00a9", + "weekHeader": "\u00e5\u2018\u00a8", + "dateFormat": "d" + } }; + $.each( regions, function( name, value ) { - var culture = Globalize.findClosestCulture( name ); - Globalize.addCultureInfo( culture && culture.name || name, { - messages : { - datepicker : value - } + Globalize.loadTranslations( name, { + datepicker : value }); }); diff --git a/tests/unit/date/date.html b/tests/unit/date/date.html index e3454a55b83..d759fdeaee7 100644 --- a/tests/unit/date/date.html +++ b/tests/unit/date/date.html @@ -12,9 +12,9 @@ @@ -37,4 +37,4 @@

      - \ No newline at end of file + diff --git a/tests/unit/date/date_core.js b/tests/unit/date/date_core.js index b75ef8087eb..3452580c840 100644 --- a/tests/unit/date/date_core.js +++ b/tests/unit/date/date_core.js @@ -53,29 +53,28 @@ test("Date Adjustments - Leap Year Edge Cases", 1, function(){ test("List days of Week", 2, function(){ var date = $.date(), offset0 = [ - { "fullname": "Sunday", "shortname": "Su" }, - { "fullname": "Monday", "shortname": "Mo" }, - { "fullname": "Tuesday", "shortname": "Tu" }, - { "fullname": "Wednesday", "shortname": "We" }, - { "fullname": "Thursday", "shortname": "Th" }, - { "fullname": "Friday", "shortname": "Fr" }, - { "fullname": "Saturday", "shortname": "Sa" } + { "fullname": "Sunday", "shortname": "Sun" }, + { "fullname": "Monday", "shortname": "Mon" }, + { "fullname": "Tuesday", "shortname": "Tue" }, + { "fullname": "Wednesday", "shortname": "Wed" }, + { "fullname": "Thursday", "shortname": "Thu" }, + { "fullname": "Friday", "shortname": "Fri" }, + { "fullname": "Saturday", "shortname": "Sat" } ], offset1 = [ - { "fullname": "Montag", "shortname": "Mo" }, - { "fullname": "Dienstag", "shortname": "Di" }, - { "fullname": "Mittwoch", "shortname": "Mi" }, - { "fullname": "Donnerstag", "shortname": "Do" }, - { "fullname": "Freitag", "shortname": "Fr" }, - { "fullname": "Samstag", "shortname": "Sa" }, - { "fullname": "Sonntag", "shortname": "So" } + { "fullname": "Montag", "shortname": "Mo." }, + { "fullname": "Dienstag", "shortname": "Di." }, + { "fullname": "Mittwoch", "shortname": "Mi." }, + { "fullname": "Donnerstag", "shortname": "Do." }, + { "fullname": "Freitag", "shortname": "Fr." }, + { "fullname": "Samstag", "shortname": "Sa." }, + { "fullname": "Sonntag", "shortname": "So." } ]; deepEqual(date.weekdays(), offset0, "Get weekdays with start of day on 0 (English)"); - Globalize.culture( "de-DE" ); - date.refresh(); + Globalize.locale( "de-DE" ); deepEqual(date.weekdays(), offset1, "Get weekdays with start of day on 1 (Germany)"); //Revert Globalize changes back to English - Globalize.culture("en-EN"); + Globalize.locale( "en" ); }); test("Leap Year Check", 8, function(){ @@ -101,10 +100,9 @@ test("Days in Month", 3, function(){ test("Month Name", 2, function(){ var date = $.date(); equal(date.setMonth(3).monthName(), "April", "Month name return April (English)"); - Globalize.culture( "de-DE" ); - date.refresh(); + Globalize.locale( "de-DE" ); equal(date.setMonth(2).monthName(), "März", "Month name return March (German)"); - Globalize.culture("en-EN"); + Globalize.locale( "en" ); }); @@ -154,13 +152,6 @@ test( "Months", 5, function(){ ok( firstMonth.month() === lastMonth.month() - 1 ); }); -test("iso8601Week", 2, function(){ - var date = $.date(); - //date.setFullDate(2012, 0, 8); - equal(date.iso8601Week(new Date(2012, 0, 8)), 1, "Test first week is 1"); - equal(date.iso8601Week(new Date(2012, 11, 31)), 1, "Test last week is 1 in next year"); -}); - test("Equal", 4, function(){ var date = $.date(); date.setFullDate(2012, 9, 16); @@ -178,45 +169,8 @@ test("Date", 1, function(){ test("Format", 4, function(){ var date = $.date(); date.setFullDate(2012, 9, 16); - equal(date.format(), "10/16/2012", "Checking default US format"); - equal(date.format("yyyy/MM/dd"), "2012/10/16", "Checking yyyy/MM/dd format"); - equal(date.format("yy/dd/MM"), "12/16/10", "Checking yy/dd/MM format"); - equal(date.format("MMMM dd, yyyy"), "October 16, 2012", "Checking MMMM dd, yyyy format"); -}); - -test("Calendar", 3, function(){ - var date = $.date(), - de_cal = { - calendars: { - standard: { - "/": ".", - firstDay: 1, - days: { - names: ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"], - namesAbbr: ["So","Mo","Di","Mi","Do","Fr","Sa"], - namesShort: ["So","Mo","Di","Mi","Do","Fr","Sa"] - }, - months: { - names: ["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember",""], - namesAbbr: ["Jan","Feb","Mrz","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez",""] - }, - AM: null, - PM: null, - eras: [{"name":"n. Chr.","start":null,"offset":0}], - patterns: { - d: "dd.MM.yyyy", - D: "dddd, d. MMMM yyyy", - t: "HH:mm", - T: "HH:mm:ss", - f: "dddd, d. MMMM yyyy HH:mm", - F: "dddd, d. MMMM yyyy HH:mm:ss", - M: "dd MMMM", - Y: "MMMM yyyy" - } - } - } - }; - ok(date.calendar(), "Calendar type returned"); - ok(date.calendar(de_cal), "Calendar type changed"); - deepEqual(date.calendar(), de_cal, "Calendar change verified"); + equal(date.format({ date: "short" }), "10/16/12", "Checking default US format"); + equal(date.format({ pattern: "yyyy/MM/dd" }), "2012/10/16", "Checking yyyy/MM/dd format"); + equal(date.format({ pattern: "yy/dd/MM" }), "12/16/10", "Checking yy/dd/MM format"); + equal(date.format({ pattern: "MMMM dd, yyyy" }), "October 16, 2012", "Checking MMMM dd, yyyy format"); }); diff --git a/tests/unit/datepicker/datepicker.html b/tests/unit/datepicker/datepicker.html index f564b32a35d..f55dd97900c 100644 --- a/tests/unit/datepicker/datepicker.html +++ b/tests/unit/datepicker/datepicker.html @@ -7,8 +7,8 @@ - + @@ -21,10 +21,6 @@ "ui/button.js", "ui/position.js", "ui/datepicker.js" - /* TODO: Replace with Globalize cultures - "ui/i18n/datepicker-fr.js", - "ui/i18n/datepicker-he.js", - "ui/i18n/datepicker-zh-CN.js"*/ ] }); diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index e32a0bb86a5..92687178060 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -7,7 +7,7 @@ TestHelpers.testJshint( "datepicker" ); test( "input's value determines starting date", function() { expect( 3 ); - var input = $( "#datepicker" ).val( "1/1/2014" ).datepicker(), + var input = $( "#datepicker" ).val( "1/1/14" ).datepicker(), picker = input.datepicker( "widget" ); input.datepicker( "open" ); @@ -228,19 +228,19 @@ test( "Keyboard handling", function() { TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke enter" ); // Enter = Select today's date by default - input.val( "1/1/2014" ).datepicker( "open" ) + input.val( "1/1/14" ).datepicker( "open" ) .simulate("keydown", { keyCode: $.ui.keyCode.ENTER }); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Keystroke enter - preset" ); // Control + Home = Change the calendar to the current month - input.val( "1/1/2014" ).datepicker( "open" ) + input.val( "1/1/14" ).datepicker( "open" ) .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.HOME }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke ctrl+home" ); // Control + End = Close the calendar and clear the input - input.val( "1/1/2014" ).datepicker( "open" ) + input.val( "1/1/14" ).datepicker( "open" ) .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.END }); equal( input.val(), "", "Keystroke ctrl+end" ); @@ -249,12 +249,12 @@ test( "Keyboard handling", function() { input.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); ok( !instance.isOpen, "escape closes the datepicker" ); - input.val( "1/1/2014" ).datepicker( "open" ) + input.val( "1/1/14" ).datepicker( "open" ) .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Keystroke esc - preset" ); - input.val( "1/1/2014" ).datepicker( "open" ) + input.val( "1/1/14" ).datepicker( "open" ) .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP }) .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index dcd6dc2c7aa..81d1b096ba8 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -69,16 +69,16 @@ test( "value", function() { picker = input.datepicker( "widget" ), inline = $( "#inline" ).datepicker(); - input.datepicker( "value", "1/1/2014" ); - equal( input.val(), "1/1/2014", "input's value set" ); + input.datepicker( "value", "1/1/14" ); + equal( input.val(), "1/1/14", "input's value set" ); ok( picker.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), "first day marked as selected" ); - equal( input.datepicker( "value" ), "1/1/2014", "getter" ); + equal( input.datepicker( "value" ), "1/1/14", "getter" ); - inline.datepicker( "value", "1/1/2014" ); + inline.datepicker( "value", "1/1/14" ); ok( inline.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), "first day marked as selected" ); - equal( inline.datepicker( "value" ), "1/1/2014", "getter" ); + equal( inline.datepicker( "value" ), "1/1/14", "getter" ); // TODO: Handle for invalid values. @@ -93,7 +93,7 @@ test( "valueAsDate", function() { inline = $( "#inline" ).datepicker(); input.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); - equal( input.val(), "1/1/2014", "input's value set" ); + equal( input.val(), "1/1/14", "input's value set" ); ok( picker.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), "first day marked as selected" ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index 9a93d63f30b..c08c370e26f 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -42,16 +42,16 @@ test( "appendTo", function() { test( "dateFormat", function() { expect( 2 ); - var input = $( "#datepicker" ).val( "1/1/2014" ).datepicker(), + var input = $( "#datepicker" ).val( "1/1/14" ).datepicker(), picker = input.datepicker( "widget" ), firstDayLink = picker.find( "td[id]:first a" ); input.datepicker( "open" ); firstDayLink.trigger( "mousedown" ); - equal( input.val(), "1/1/2014", "default formatting" ); + equal( input.val(), "1/1/14", "default formatting" ); - input.datepicker( "option", "dateFormat", "D" ); - equal( input.val(), "Wednesday, January 01, 2014", "updated formatting" ); + input.datepicker( "option", "dateFormat", { date: "full" } ); + equal( input.val(), "Wednesday, January 1, 2014", "updated formatting" ); input.datepicker( "destroy" ); }); diff --git a/ui/datepicker.js b/ui/datepicker.js index a352e2d6950..ef0e3d62852 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -53,6 +53,7 @@ $.widget( "ui.datepicker", { select: null }, _create: function() { + this.options.dateFormat = this.options.dateFormat || { date: "short" }; this.date = $.date( null, this.options.dateFormat ); this.date.eachDay = this.options.eachDay; @@ -304,8 +305,6 @@ $.widget( "ui.datepicker", { return element; }, _createTmpl: function() { - this.date.refresh(); - this._createDatepicker(); this.picker.find( "button" ).button(); @@ -346,6 +345,7 @@ $.widget( "ui.datepicker", { i = 0; for ( i; i < months.length; i++ ) { + // TODO Shouldn't we pass date as a parameter to build* fns instead of setting this.date? this.date = months[ i ]; headerClass = months[ i ].first ? "ui-corner-left" : months[ i ].last ? "ui-corner-right" : ""; @@ -379,7 +379,7 @@ $.widget( "ui.datepicker", { "
      "; }, _buildPreviousLink: function() { - var labels = Globalize.localize( "datepicker" ); + var labels = Globalize.translate( "datepicker" ); return ""; }, _buildNextLink: function() { - var labels = Globalize.localize( "datepicker" ); + var labels = Globalize.translate( "datepicker" ); return ""; }, _buildTitlebar: function() { - var labels = Globalize.localize( "datepicker" ); + var labels = Globalize.translate( "datepicker" ); return "
      " + "
      " + this._buildTitle() + @@ -423,7 +423,7 @@ $.widget( "ui.datepicker", { _buildGridHeading: function() { var cells = "", i = 0, - labels = Globalize.localize( "datepicker" ); + labels = Globalize.translate( "datepicker" ); if ( this.options.showWeek ) { cells += "" + labels.weekHeader + ""; @@ -443,10 +443,12 @@ $.widget( "ui.datepicker", { ""; }, _buildGridBody: function() { - var rows = "", - i = 0; - for ( i; i < this.date.days().length; i++ ) { - rows += this._buildWeekRow( this.date.days()[i] ); + // this.date.days() is not cached, and it has O(n^2) complexity. It is run O(n) times. So, it equals O(n^3). Not good at all. Caching. + var days = this.date.days(), + i = 0, + rows = ""; + for ( i; i < days.length; i++ ) { + rows += this._buildWeekRow( days[i] ); } return "" + rows + ""; }, @@ -483,7 +485,7 @@ $.widget( "ui.datepicker", { _buildDayLink: function( day ) { var link, classes = [ "ui-state-default" ], - labels = Globalize.localize( "datepicker" ); + labels = Globalize.translate( "datepicker" ); if ( day.date === this.date.day() ) { classes.push( "ui-state-focus" ); @@ -521,7 +523,7 @@ $.widget( "ui.datepicker", { day.date + ""; }, _buildButtons: function() { - var labels = Globalize.localize( "datepicker" ); + var labels = Globalize.translate( "datepicker" ); return "
      " + "" + "" + @@ -539,8 +541,6 @@ $.widget( "ui.datepicker", { refresh: function() { //determine which day gridcell to focus after refresh //TODO: Prevent disabled cells from being focused - this.date.refresh(); - if ( this.options.numberOfMonths === 1 ) { this.grid = $( this._buildGrid() ); $( ".ui-datepicker-title", this.picker ).html( this._buildTitle() ); From 4d51220efe024187c2faa9cb7f89c07925d32993 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 27 Dec 2013 08:28:00 -0500 Subject: [PATCH 16/53] Datepicker: Update key handling implementation and tests --- tests/unit/datepicker/datepicker_core.js | 238 ++++++++++++++--------- ui/datepicker.js | 4 +- 2 files changed, 149 insertions(+), 93 deletions(-) diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index 92687178060..1138d7e2eaf 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -263,8 +263,8 @@ test( "Keyboard handling", function() { input.datepicker( "destroy" ); }); -asyncTest( "keyboard handling - arrow keys", function() { - expect( 6 ); +asyncTest( "keyboard handling", function() { + expect( 14 ); var picker, input = $( "#datepicker" ), date = new Date(); @@ -299,134 +299,190 @@ asyncTest( "keyboard handling - arrow keys", function() { function step3() { input.datepicker() - .val( "" ) + .val( "1/1/14" ) .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); setTimeout(function() { $( ":focus" ) .simulate( "keydown", { keyCode: $.ui.keyCode.LEFT }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - date.setDate( date.getDate() - 1 ); + date = new Date( 2013, 12 - 1, 31 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke left to switch to previous day" ); input.datepicker( "destroy" ); step4(); - }, 100); + }, 100 ); }; function step4() { input.datepicker() - .val( "" ) + .val( "1/1/14" ) .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); setTimeout(function() { $( ":focus" ) .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT }) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - date.setDate( new Date().getDate() + 1 ); + date = new Date( 2014, 1 - 1, 2 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke right to switch to next day" ); input.datepicker( "destroy" ); - start(); - }, 100); + step5(); + }, 100 ); }; - step1(); -}); + function step5() { + input.datepicker() + .val( "1/1/14" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); -/* - input.val( "" ).datepicker( "open" ) - .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.RIGHT }) - .simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() + 1); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke ctrl+right" ); - - input.val( "" ).datepicker( "open" ) - .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.UP}). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() - 7); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke ctrl+up" ); - - input.val( "" ).datepicker( "open" ) - .simulate( "keydown", {keyCode: $.ui.keyCode.UP}). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() + 7); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke up" ); - - input.val( "" ).datepicker( "open" ) - .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.DOWN}). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() + 7); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke ctrl+down" ); - - input.val( "" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - date.setDate( date.getDate() - 7 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke down" ); + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.UP }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date = new Date( 2013, 12 - 1, 25 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, + "Keystroke up to move to the previous week" ); - // Moving by month or year - input.val( "02/04/2008" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 4 ), - "Keystroke pgup" ); + input.datepicker( "destroy" ); + step6(); + }, 100 ); + }; - input.val( "02/04/2008" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 3 - 1, 4 ), - "Keystroke pgdn" ); + function step6() { + input.datepicker() + .val( "1/1/14" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); - input.val( "02/04/2008" ).datepicker( "open" ) - .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2007, 2 - 1, 4 ), - "Keystroke ctrl+pgup" ); + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date = new Date( 2014, 1 - 1, 8 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, + "Keystroke down to move to the next week" ); - input.val( "02/04/2008" ).datepicker( "open" ) - .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2009, 2 - 1, 4 ), - "Keystroke ctrl+pgdn" ); + input.datepicker( "destroy" ); + step7(); + }, 100 ); + }; + + function step7() { + input.datepicker() + .val( "1/1/14" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date = new Date( 2013, 12 - 1, 1 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, + "Keystroke Page Up moves date to previous month" ); + + input.datepicker( "destroy" ); + step8(); + }, 100 ); + }; + + function step8() { + input.datepicker() + .val( "1/1/14" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP, altKey: true }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date = new Date( 2013, 1 - 1, 1 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, + "Keystroke Page Up + Ctrl moves date to previous year" ); + + input.datepicker( "destroy" ); + step9(); + }, 100 ); + }; + + function step9() { + input.datepicker() + .val( "1/1/14" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date = new Date( 2014, 2 - 1, 1 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, + "Keystroke Page Down moves date to next month" ); + + input.datepicker( "destroy" ); + step10(); + }, 100 ); + }; + + function step10() { + input.datepicker() + .val( "1/1/14" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date = new Date( 2015, 1 - 1, 1 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, + "Keystroke Page Down + Ctrl moves date to next year" ); + + input.datepicker( "destroy" ); + step11(); + }, 100 ); + }; // Check for moving to short months - input.val( "03/31/2008" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 29 ), - "Keystroke pgup - Feb" ); + function step11() { + input.datepicker() + .val( "3/31/14" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); - input.val( "01/30/2008" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 29 ), - "Keystroke pgdn - Feb" ); + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date = new Date( 2014, 2 - 1, 28 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, + "Keystroke Page Up and short months" ); - input.val( "02/29/2008" ).datepicker( "open" ) - .simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2007, 2 - 1, 28 ), - "Keystroke ctrl+pgup - Feb" ); + input.datepicker( "destroy" ); + step12(); + }, 100 ); + }; - input.val( "02/29/2008" ).datepicker( "open" ) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2009, 2 - 1, 28 ), - "Keystroke ctrl+pgdn - Feb" ); + function step12() { + input.datepicker() + .val( "1/30/16" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); - // Goto current - input.datepicker( "option", { gotoCurrent: true }) - .datepicker( "close" ).val( "02/04/2008" ).datepicker( "open" ) - .late( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.HOME }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 4 ), - "Keystroke ctrl+home" ); + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + date = new Date( 2016, 2 - 1, 29 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, + "Keystroke Page Down and leap years" ); - // Change steps + input.datepicker( "destroy" ); + start(); + }, 100 ); + }; + + step1(); +}); + +/* + // TODO: Re-add tests if we implement a stepMonths option input.datepicker( "option", { stepMonths: 2, gotoCurrent: false }) .datepicker( "close" ).val( "02/04/2008" ).datepicker( "open" ) .late( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) diff --git a/ui/datepicker.js b/ui/datepicker.js index ef0e3d62852..6567d213080 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -122,10 +122,10 @@ $.widget( "ui.datepicker", { activeCell.children( "a:first" ).mousedown(); return; case $.ui.keyCode.PAGE_UP: - this.date.adjust( event.altKey ? "Y" : "M", 1 ); + this.date.adjust( event.altKey ? "Y" : "M", -1 ); break; case $.ui.keyCode.PAGE_DOWN: - this.date.adjust( event.altKey ? "Y" : "M", -1 ); + this.date.adjust( event.altKey ? "Y" : "M", 1 ); break; case $.ui.keyCode.END: this.date.setDay( this.date.daysInMonth() ); From 4db03f4fdfad43e8af6687d63c492e87f6387b80 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Thu, 2 Jan 2014 16:25:38 -0500 Subject: [PATCH 17/53] Datepicker: Re-adding mouse tests --- tests/unit/datepicker/datepicker_core.js | 140 +++++++++++------------ 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index 1138d7e2eaf..c9d0403b240 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -498,109 +498,109 @@ asyncTest( "keyboard handling", function() { */ test( "mouse", function() { - // TODO: These tests use the old getDate() and setDate() methods. Re-activate these - // tests when those methods are available. - expect( 0 ); - return; - - expect( 15 ); - var inl, - inp = TestHelpers.datepicker.init( "#datepicker" ), - dp = inp.datepicker( "widget" ).find( ".ui-datepicker" ), + expect( 13 ); + var input = $( "#datepicker" ).datepicker(), + picker = input.datepicker( "widget" ), + inline = $( "#inline" ).datepicker, date = new Date(); - inp.val( "" ).datepicker( "open" ); - $( ".ui-datepicker-calendar tbody a:contains(10)", dp ).simulate( "click", {} ); + input.val( "" ).datepicker( "open" ); + $( ".ui-datepicker-calendar tbody a:contains(10)", picker ).simulate( "mousedown", {} ); date.setDate( 10 ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Mouse click" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Mouse click" ); - inp.val( "02/04/2008" ).datepicker( "open" ); - $( ".ui-datepicker-calendar tbody a:contains(12)", dp ).simulate( "click", {} ); - TestHelpers.datepicker.equalsDate( inp.datepicker("getDate"), new Date( 2008, 2 - 1, 12 ), + input.val( "2/4/08" ).datepicker( "open" ); + $( ".ui-datepicker-calendar tbody a:contains(12)", picker ).simulate( "mousedown", {} ); + TestHelpers.datepicker.equalsDate( input.datepicker("valueAsDate"), new Date( 2008, 2 - 1, 12 ), "Mouse click - preset") ; - inp.val( "02/04/2008" ).datepicker( "open" ); - inp.val( "").datepicker( "open" ); - $( "button.ui-datepicker-close", dp ).simulate( "click", {} ); - ok( inp.datepicker( "getDate" ) == null, "Mouse click - close" ); - inp.val( "02/04/2008" ).datepicker( "open" ); - $( "button.ui-datepicker-close", dp ).simulate( "click", {} ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 4 ), + input.val( "" ).datepicker( "open" ); + $( "button.ui-datepicker-close", picker ).simulate( "click", {} ); + ok( input.datepicker( "valueAsDate" ) == null, "Mouse click - close" ); + + input.val( "2/4/08" ).datepicker( "open" ); + $( "button.ui-datepicker-close", picker ).simulate( "click", {} ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 4 ), "Mouse click - close + preset" ); - inp.val( "02/04/2008" ).datepicker( "open" ); - $( "a.ui-datepicker-prev", dp ).simulate( "click", {} ); - $( "button.ui-datepicker-close", dp ).simulate( "click", {} ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 4 ), + input.val( "2/4/08" ).datepicker( "open" ); + $( "a.ui-datepicker-prev", picker ).simulate( "click", {} ); + $( "button.ui-datepicker-close", picker ).simulate( "click", {} ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 4 ), "Mouse click - abandoned" ); // Current/previous/next - inp.val( "02/04/2008" ).datepicker( "option", { showButtonPanel: true }).datepicker( "open" ); - $( ".ui-datepicker-current", dp ).simulate( "click", {} ); - $( ".ui-datepicker-calendar tbody a:contains(14)", dp ).simulate( "click", {} ); - date.setDate( 14 ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), date, "Mouse click - current" ); - - inp.val( "02/04/2008" ).datepicker( "open" ); - $( ".ui-datepicker-prev", dp ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(16)", dp ).simulate( "click" ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 1 - 1, 16 ), + input.val( "" ).datepicker( "open" ); + $( ".ui-datepicker-current", picker ).simulate( "click", {} ); + date.setDate( new Date().getDate() ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Mouse click - current" ); + + input.val( "2/4/08" ).datepicker( "open" ); + $( ".ui-datepicker-prev", picker ).simulate( "click" ); + $( ".ui-datepicker-calendar tbody a:contains(16)", picker ).simulate( "mousedown" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 16 ), "Mouse click - previous" ); - inp.val( "02/04/2008" ).datepicker( "open" ); - $(".ui-datepicker-next", dp ).simulate( "click" ); - $(".ui-datepicker-calendar tbody a:contains(18)", dp ).simulate( "click" ); - TestHelpers.datepicker.equalsDate( inp.datepicker("getDate"), new Date( 2008, 3 - 1, 18 ), + input.val( "2/4/08" ).datepicker( "open" ); + $(".ui-datepicker-next", picker ).simulate( "click" ); + $(".ui-datepicker-calendar tbody a:contains(18)", picker ).simulate( "mousedown" ); + TestHelpers.datepicker.equalsDate( input.datepicker("valueAsDate"), new Date( 2008, 3 - 1, 18 ), "Mouse click - next" ); + /* + // TODO: Re-add when min and max options are introduced. // Previous/next with minimum/maximum - inp.datepicker("option", { + input.datepicker("option", { minDate: new Date( 2008, 2 - 1, 2 ), maxDate: new Date( 2008, 2 - 1, 26 ) - }).val( "02/04/2008" ).datepicker( "open" ); - $( ".ui-datepicker-prev", dp ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(16)", dp ).simulate( "click" ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 16 ), + }).val( "2/4/08" ).datepicker( "open" ); + $( ".ui-datepicker-prev", picker ).simulate( "click" ); + $( ".ui-datepicker-calendar tbody a:contains(16)", picker ).simulate( "mousedown" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 16 ), "Mouse click - previous + min/max" ); - inp.val( "02/04/2008" ).datepicker( "open" ); - $( ".ui-datepicker-next", dp ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(18)", dp ).simulate( "click" ); - TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 18 ), + input.val( "2/4/08" ).datepicker( "open" ); + $( ".ui-datepicker-next", picker ).simulate( "click" ); + $( ".ui-datepicker-calendar tbody a:contains(18)", picker ).simulate( "mousedown" ); + TestHelpers.datepicker.equalsDate(input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 18 ), "Mouse click - next + min/max" ); + */ // Inline - inl = TestHelpers.datepicker.init( "#inline" ); - dp = $( ".ui-datepicker-inline", inl ); + inline = TestHelpers.datepicker.init( "#inline" ); + picker = $( ".ui-datepicker-inline", inline ); date = new Date(); - inl.datepicker( "setDate", date ); - $( ".ui-datepicker-calendar tbody a:contains(10)", dp ).simulate( "click", {} ); + inline.datepicker( "valueAsDate", date ); + $( ".ui-datepicker-calendar tbody a:contains(10)", picker ).simulate( "mousedown", {} ); date.setDate( 10 ); - TestHelpers.datepicker.equalsDate( inl.datepicker( "getDate" ), date, "Mouse click inline" ); + TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), date, "Mouse click inline" ); - inl.datepicker( "option", { showButtonPanel: true }) - .datepicker( "setDate", new Date( 2008, 2 - 1, 4 )); - $( ".ui-datepicker-calendar tbody a:contains(12)", dp ).simulate( "click", {} ); - TestHelpers.datepicker.equalsDate( inl.datepicker( "getDate" ), new Date( 2008, 2 - 1, 12 ), + inline.datepicker( "option", { showButtonPanel: true }) + .datepicker( "valueAsDate", new Date( 2008, 2 - 1, 4 )); + $( ".ui-datepicker-calendar tbody a:contains(12)", picker ).simulate( "mousedown", {} ); + TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 12 ), "Mouse click inline - preset" ); - inl.datepicker("option", { showButtonPanel: true }); - $( ".ui-datepicker-current", dp ).simulate( "click", {} ); - $( ".ui-datepicker-calendar tbody a:contains(14)", dp ).simulate( "click", {} ); + inline.datepicker("option", { showButtonPanel: true }); + $( ".ui-datepicker-current", picker ).simulate( "click", {} ); + $( ".ui-datepicker-calendar tbody a:contains(14)", picker ).simulate( "mousedown", {} ); date.setDate( 14 ); - TestHelpers.datepicker.equalsDate( inl.datepicker( "getDate" ), date, "Mouse click inline - current" ); + TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), date, "Mouse click inline - current" ); - inl.datepicker( "setDate", new Date( 2008, 2 - 1, 4) ); - $( ".ui-datepicker-prev", dp ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(16)", dp ).simulate( "click" ); - TestHelpers.datepicker.equalsDate( inl.datepicker( "getDate" ), new Date( 2008, 1 - 1, 16 ), + inline.datepicker( "valueAsDate", new Date( 2008, 2 - 1, 4) ); + $( ".ui-datepicker-prev", picker ).simulate( "click" ); + $( ".ui-datepicker-calendar tbody a:contains(16)", picker ).simulate( "mousedown" ); + TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 16 ), "Mouse click inline - previous" ); - inl.datepicker( "setDate", new Date( 2008, 2 - 1, 4) ); - $( ".ui-datepicker-next", dp ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(18)", dp ).simulate( "click" ); - TestHelpers.datepicker.equalsDate( inl.datepicker( "getDate" ), new Date( 2008, 3 - 1, 18 ), + inline.datepicker( "valueAsDate", new Date( 2008, 2 - 1, 4) ); + $( ".ui-datepicker-next", picker ).simulate( "click" ); + $( ".ui-datepicker-calendar tbody a:contains(18)", picker ).simulate( "mousedown" ); + TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), new Date( 2008, 3 - 1, 18 ), "Mouse click inline - next" ); + + input.datepicker( "destroy" ); + inline.datepicker( "destroy" ); }); })( jQuery ); From 07a58142da75f73cff5c6aa024a42672cff1b62a Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 3 Jan 2014 09:07:38 -0500 Subject: [PATCH 18/53] Datepicker: Add an `isValid()` method --- tests/unit/datepicker/datepicker_methods.js | 13 +++++++++++++ ui/datepicker.js | 3 +++ 2 files changed, 16 insertions(+) diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 81d1b096ba8..98034dacdf1 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -111,4 +111,17 @@ test( "valueAsDate", function() { inline.datepicker( "destroy" ); }); +test( "isValid", function() { + expect( 2 ); + var input = $( "#datepicker" ).datepicker(); + + input.val( "1/1/14" ); + ok( input.datepicker( "isValid" ) ); + + input.val( "1/1/abc" ); + ok( !input.datepicker( "isValid" ) ); + + input.datepicker( "destroy" ); +}); + })( jQuery ); diff --git a/ui/datepicker.js b/ui/datepicker.js index 6567d213080..05351361196 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -649,6 +649,9 @@ $.widget( "ui.datepicker", { return this.date.date(); } }, + isValid: function() { + return Globalize.parseDate( this.element.val(), this.options.dateFormat ) !== null; + }, _destroy: function() { if ( this.inline ) { this.picker.empty(); From 3a38e817f9d1447e06e1a57a7b4c17e2b3372d87 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 3 Jan 2014 09:16:03 -0500 Subject: [PATCH 19/53] Datepicker: Handle invalid values in `value()` and `valueAsDate()` --- tests/unit/datepicker/datepicker_methods.js | 15 +++++++++------ ui/datepicker.js | 7 +++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 98034dacdf1..281a81c8d9b 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -64,7 +64,7 @@ test( "open", function() { }); test( "value", function() { - expect( 5 ); + expect( 6 ); var input = $( "#datepicker" ).datepicker(), picker = input.datepicker( "widget" ), inline = $( "#inline" ).datepicker(); @@ -75,19 +75,21 @@ test( "value", function() { "first day marked as selected" ); equal( input.datepicker( "value" ), "1/1/14", "getter" ); + input.val( "abc" ); + equal( input.datepicker( "value" ), "abc", + "Invalid values should be returned without formatting." ); + inline.datepicker( "value", "1/1/14" ); ok( inline.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), "first day marked as selected" ); equal( inline.datepicker( "value" ), "1/1/14", "getter" ); - // TODO: Handle for invalid values. - input.datepicker( "destroy" ); inline.datepicker( "destroy" ); }); test( "valueAsDate", function() { - expect( 5 ); + expect( 6 ); var input = $( "#datepicker" ).datepicker(), picker = input.datepicker( "widget" ), inline = $( "#inline" ).datepicker(); @@ -99,14 +101,15 @@ test( "valueAsDate", function() { TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "getter" ); + input.val( "a/b/c" ); + equal( input.datepicker( "valueAsDate" ), null, "Invalid dates return null" ); + inline.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); ok( inline.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), "first day marked as selected" ); TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "getter" ); - // TODO: Handle for invalid values. - input.datepicker( "destroy" ); inline.datepicker( "destroy" ); }); diff --git a/ui/datepicker.js b/ui/datepicker.js index 05351361196..65fbcd380da 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -639,17 +639,20 @@ $.widget( "ui.datepicker", { if ( arguments.length ) { this._value( value ); } else { - return this.date.format(); + return this.isValid() ? this.date.format() : this.element.val(); } }, valueAsDate: function( value ) { if ( arguments.length ) { this._value( value ); } else { - return this.date.date(); + return this.isValid() ? this.date.date() : null; } }, isValid: function() { + if ( this.inline ) { + return true; + } return Globalize.parseDate( this.element.val(), this.options.dateFormat ) !== null; }, _destroy: function() { From 6c0a1c303b615364f395725b2a9d4e7b3869c954 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Fri, 3 Jan 2014 09:55:06 -0500 Subject: [PATCH 20/53] Datepicker: Update the picker as the user types valid dates --- tests/unit/datepicker/datepicker_core.js | 7 ++++++- ui/datepicker.js | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index c9d0403b240..377c4c201dd 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -218,7 +218,7 @@ asyncTest( "baseStructure", function() { }); test( "Keyboard handling", function() { - expect( 8 ); + expect( 9 ); var input = $( "#datepicker" ).datepicker(), instance = input.datepicker( "instance" ), date = new Date(); @@ -260,6 +260,11 @@ test( "Keyboard handling", function() { TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Keystroke esc - abandoned" ); + input.val( "1/2/14" ) + .simulate( "keyup" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 2 ), + "Picker updated as user types into input" ); + input.datepicker( "destroy" ); }); diff --git a/ui/datepicker.js b/ui/datepicker.js index 65fbcd380da..f8328e1cab6 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -227,6 +227,12 @@ $.widget( "ui.datepicker", { break; } }, + keyup: function() { + if ( this.isValid() && !this.inline ) { + this.date.setTime( this.element.val() ).select(); + this.refresh(); + } + }, mousedown: function( event ) { if (this.isOpen) { suppressExpandOnFocus = true; From 24110f2464868edc94d52cdc9b03d75dc2b41444 Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Thu, 9 Jan 2014 09:15:42 -0500 Subject: [PATCH 21/53] Date: Update test suite to coding standards --- tests/unit/date/date_core.js | 169 +++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 78 deletions(-) diff --git a/tests/unit/date/date_core.js b/tests/unit/date/date_core.js index 3452580c840..09ffd206abe 100644 --- a/tests/unit/date/date_core.js +++ b/tests/unit/date/date_core.js @@ -1,56 +1,67 @@ -module("date: core"); +module( "date: core" ); -test("Check Date Setup", 2, function(){ - ok(true,"First Test Always Passes"); - ok($.date(), "Load JQuery Date"); +test( "Check Date Setup", 2, function() { + ok( true, "First Test Always Passes" ); + ok( $.date(), "Load JQuery Date" ); }); -test("Check Sets and Gets", 6, function(){ + +test( "Check Sets and Gets", 6, function() { var date = $.date(); - equal(date.setYear(2012).year(), 2012, "Set year and retrieve"); - equal(date.setMonth(9).month(), 9, "Set month and retrieve"); - equal(date.setDay(15).day(), 15, "Set day and retrieve"); - equal(date.setFullDate(2012,9,15).year(), 2012, "Set full date and retrieve year"); - equal(date.month(), 9, "Set full date and retrieve month"); - equal(date.day(), 15, "Set full date and retrieve day"); + equal( date.setYear( 2012 ).year(), 2012, "Set year and retrieve" ); + equal( date.setMonth( 9 ).month(), 9, "Set month and retrieve" ); + equal( date.setDay( 15 ).day(), 15, "Set day and retrieve" ); + equal( date.setFullDate( 2012, 9, 15 ).year(), 2012, "Set full date and retrieve year" ); + equal( date.month(), 9, "Set full date and retrieve month" ); + equal( date.day(), 15, "Set full date and retrieve day" ); }); -test("Date Adjustments - Normal Use Cases", 10, function(){ + +test( "Date Adjustments - Normal Use Cases", 10, function() { var date = $.date(); - //Use October 15, 2012 - date.setFullDate(2012,9,15); - equal(date.adjust("D",1).day(),16,"Add 1 day"); - equal(date.adjust("D",-1).day(),15,"Subtract 1 day"); - equal(date.adjust("M",1).month(),10,"Add 1 month"); - equal(date.adjust("M",-1).month(),9,"Subtract 1 month"); - equal(date.adjust("Y",1).year(),2013,"Add 1 year"); - equal(date.adjust("Y",-1).year(),2012,"Subtract 1 year"); - //Check changing one value impact another. Ex: Day impacts month - //Use April 30th 2012 - date.setFullDate(2012,3,30); - equal(date.adjust("D",1).month(),4,"Add 1 day to change month from April to May"); - equal(date.adjust("D",-1).month(),3,"Subtract 1 day to change month from May to April"); - //Use December 31st 2012 - date.setFullDate(2012,11,31); - equal(date.adjust("D",1).year(),2013,"Add 1 day to change year from 2012 to 2013"); - equal(date.adjust("D",-1).year(),2012,"Subtract 1 day to change month from 2013 to 2012"); + + // Use October 15, 2012 + date.setFullDate( 2012, 9, 15 ); + equal( date.adjust( "D", 1 ).day(), 16, "Add 1 day" ); + equal( date.adjust( "D", -1 ).day(), 15, "Subtract 1 day" ); + equal( date.adjust( "M", 1 ).month(), 10, "Add 1 month" ); + equal( date.adjust( "M", -1 ).month(), 9, "Subtract 1 month" ); + equal( date.adjust( "Y", 1 ).year(), 2013, "Add 1 year" ); + equal( date.adjust( "Y", -1 ).year(), 2012, "Subtract 1 year" ); + + // Check changing one value impact another. Ex: Day impacts month + // Use April 30th 2012 + date.setFullDate( 2012, 3, 30 ); + equal( date.adjust( "D", 1 ).month(), 4, "Add 1 day to change month from April to May" ); + equal( date.adjust( "D", -1 ).month(), 3, "Subtract 1 day to change month from May to April" ); + + // Use December 31st 2012 + date.setFullDate( 2012, 11, 31 ); + equal( date.adjust( "D", 1 ).year(), 2013, "Add 1 day to change year from 2012 to 2013" ); + equal( date.adjust( "D", -1 ).year(), 2012, + "Subtract 1 day to change month from 2013 to 2012" ); }); -test("Date Adjustments - Month Overflow Edge Cases", 2, function(){ +test( "Date Adjustments - Month Overflow Edge Cases", 2, function() { var date = $.date(); - //Use May 31 2012 - date.setFullDate(2012,4,31); - equal(date.adjust("M",1).day(),30,"Add 1 month from May to June sets days to 30, last day in June (prevent Overflow)"); - equal(date.adjust("M",-1).day(),30,"Subtract 1 month from June to May sets days to 30 in May"); + + // Use May 31 2012 + date.setFullDate( 2012, 4, 31 ); + equal( date.adjust( "M", 1 ).day(), 30, + "Add 1 month from May to June sets days to 30, last day in June (prevent Overflow)" ); + equal( date.adjust( "M", -1 ).day(), 30, + "Subtract 1 month from June to May sets days to 30 in May" ); }); -test("Date Adjustments - Leap Year Edge Cases", 1, function(){ +test( "Date Adjustments - Leap Year Edge Cases", 1, function() { var date = $.date(); - //Use February 29 2012 a Leap year - date.setFullDate(2012,1,29); - equal(date.adjust("Y",1).day(),28,"Feb 29 2012, add a year to convert to Feb 28, 2013"); + + // Use February 29 2012 a Leap year + date.setFullDate( 2012, 1, 29 ); + equal( date.adjust( "Y", 1 ).day(), 28, + "Feb 29 2012, add a year to convert to Feb 28, 2013" ); }); -test("List days of Week", 2, function(){ +test( "List days of Week", 2, function() { var date = $.date(), offset0 = [ { "fullname": "Sunday", "shortname": "Sun" }, @@ -70,50 +81,51 @@ test("List days of Week", 2, function(){ { "fullname": "Samstag", "shortname": "Sa." }, { "fullname": "Sonntag", "shortname": "So." } ]; - deepEqual(date.weekdays(), offset0, "Get weekdays with start of day on 0 (English)"); + + deepEqual( date.weekdays(), offset0, "Get weekdays with start of day on 0 (English)" ); Globalize.locale( "de-DE" ); - deepEqual(date.weekdays(), offset1, "Get weekdays with start of day on 1 (Germany)"); - //Revert Globalize changes back to English + deepEqual( date.weekdays(), offset1, "Get weekdays with start of day on 1 (Germany)" ); + + // Revert Globalize changes back to English Globalize.locale( "en" ); }); -test("Leap Year Check", 8, function(){ +test( "Leap Year Check", 8, function() { var date = $.date(); - ok(date.setYear( 2008 ).isLeapYear(), "2008 is a Leap Year"); - ok(!date.setYear( 2009 ).isLeapYear(), "2009 is not a Leap Year"); - ok(!date.setYear( 2010 ).isLeapYear(), "2010 is not a Leap Year"); - ok(!date.setYear( 2011 ).isLeapYear(), "2011 is not a Leap Year"); - ok(date.isLeapYear( 2012 ), "2012 is a Leap Year"); - ok(!date.isLeapYear( 2013 ), "2013 is not a Leap Year"); - ok(!date.isLeapYear( 2014 ), "2014 is not a Leap year"); - ok(!date.isLeapYear( 2015 ), "2015 is not a Leap year"); + ok( date.setYear( 2008 ).isLeapYear(), "2008 is a Leap Year" ); + ok( !date.setYear( 2009 ).isLeapYear(), "2009 is not a Leap Year" ); + ok( !date.setYear( 2010 ).isLeapYear(), "2010 is not a Leap Year" ); + ok( !date.setYear( 2011 ).isLeapYear(), "2011 is not a Leap Year" ); + ok( date.isLeapYear( 2012 ), "2012 is a Leap Year" ); + ok( !date.isLeapYear( 2013 ), "2013 is not a Leap Year" ); + ok( !date.isLeapYear( 2014 ), "2014 is not a Leap year" ); + ok( !date.isLeapYear( 2015 ), "2015 is not a Leap year" ); }); -test("Days in Month", 3, function(){ +test( "Days in Month", 3, function() { var date = $.date(); date.setFullDate( 2012, 1, 1 ); - equal(date.daysInMonth(), 29, "Leap Year implicit check for 29 days"); - equal(date.daysInMonth( 2012, 1 ), 29, "Leap Year explicit check for 29 days"); - equal(date.daysInMonth( 2011, 3 ), 30, "April has 30 days"); + equal( date.daysInMonth(), 29, "Leap Year implicit check for 29 days" ); + equal( date.daysInMonth( 2012, 1 ), 29, "Leap Year explicit check for 29 days" ); + equal( date.daysInMonth( 2011, 3 ), 30, "April has 30 days" ); }); -test("Month Name", 2, function(){ +test( "Month Name", 2, function() { var date = $.date(); - equal(date.setMonth(3).monthName(), "April", "Month name return April (English)"); + equal( date.setMonth( 3 ).monthName(), "April", "Month name return April (English)" ); Globalize.locale( "de-DE" ); - equal(date.setMonth(2).monthName(), "März", "Month name return March (German)"); + equal( date.setMonth( 2 ).monthName(), "März", "Month name return March (German)" ); Globalize.locale( "en" ); - }); -test("Clone", 2, function(){ +test( "Clone", 2, function() { var date = $.date(), date2 = date.clone(); - ok(date2, "Created cloned object"); - notEqual(date.adjust("Y",1).year(), date2.year(), "Object manipulated independently"); + ok( date2, "Created cloned object" ); + notEqual( date.adjust( "Y", 1 ).year(), date2.year(), "Object manipulated independently" ); }); -test("Days", 1, function(){ +test( "Days", 1, function() { //TODO needs work var date = $.date(); date.eachDay = function( day ) { @@ -136,7 +148,7 @@ test("Days", 1, function(){ day.title = "A good day!"; } }; - ok(date.days(), "Date days() returns"); + ok( date.days(), "Date days() returns"); }); test( "Months", 5, function(){ @@ -152,25 +164,26 @@ test( "Months", 5, function(){ ok( firstMonth.month() === lastMonth.month() - 1 ); }); -test("Equal", 4, function(){ +test( "Equal", 4, function() { var date = $.date(); - date.setFullDate(2012, 9, 16); - ok(date.equal(new Date(2012, 9, 16)), "Does date equal provide date"); - ok(!date.equal(new Date(2011, 9, 16)), "Does date year not equal provide date"); - ok(!date.equal(new Date(2012, 8, 16)), "Does date month not equal provide date"); - ok(!date.equal(new Date(2012, 9, 15)), "Does date day not equal provide date"); + date.setFullDate( 2012, 9, 16 ); + ok( date.equal( new Date( 2012, 9, 16 ) ), "Does date equal provide date" ); + ok( !date.equal( new Date( 2011, 9, 16 ) ), "Does date year not equal provide date" ); + ok( !date.equal( new Date( 2012, 8, 16 ) ), "Does date month not equal provide date" ); + ok( !date.equal( new Date( 2012, 9, 15 ) ), "Does date day not equal provide date" ); }); -test("Date", 1, function(){ +test( "Date", 1, function() { var date = $.date(); - ok(date.date() instanceof Date, "Date returned"); + ok( date.date() instanceof Date, "Date returned" ); }); -test("Format", 4, function(){ +test( "Format", 4, function() { var date = $.date(); - date.setFullDate(2012, 9, 16); - equal(date.format({ date: "short" }), "10/16/12", "Checking default US format"); - equal(date.format({ pattern: "yyyy/MM/dd" }), "2012/10/16", "Checking yyyy/MM/dd format"); - equal(date.format({ pattern: "yy/dd/MM" }), "12/16/10", "Checking yy/dd/MM format"); - equal(date.format({ pattern: "MMMM dd, yyyy" }), "October 16, 2012", "Checking MMMM dd, yyyy format"); + date.setFullDate( 2012, 9, 16 ); + equal( date.format({ date: "short" }), "10/16/12", "Checking default US format" ); + equal( date.format({ pattern: "yyyy/MM/dd" }), "2012/10/16", "Checking yyyy/MM/dd format" ); + equal( date.format({ pattern: "yy/dd/MM" }), "12/16/10", "Checking yy/dd/MM format" ); + equal( date.format({ pattern: "MMMM dd, yyyy" }), "October 16, 2012", + "Checking MMMM dd, yyyy format" ); }); From 8a115dc8f48f6622deda531bcdb2c3a94a9f128e Mon Sep 17 00:00:00 2001 From: TJ VanToll Date: Thu, 9 Jan 2014 09:48:43 -0500 Subject: [PATCH 22/53] Date: Make `$.date()` a constructor function Move methods to prototype. --- external/date.js | 321 ++++++++++++++++++----------------- tests/unit/date/date_core.js | 8 +- 2 files changed, 173 insertions(+), 156 deletions(-) diff --git a/external/date.js b/external/date.js index b8d1ef30d33..101afb17581 100644 --- a/external/date.js +++ b/external/date.js @@ -20,168 +20,185 @@ var weekdays = [ "sun", "mon", "tue", "wed", "thu", "fri", "sat" ], Globalize.locale( "en" ); $.date = function( date, globalFormat ) { - //TODO: Need to refactor $.date to be a constructor, move the methods to a prototype. + if ( !( this instanceof $.date ) ) { + return new $.date( date, globalFormat ); + } if ( typeof date === "string" && date.length ) { - date = Globalize.parseDate( date, globalFormat ); + this.dateObject = Globalize.parseDate( date, globalFormat ); } - date = date || new Date(); + this.dateObject = this.dateObject || new Date(); + this.globalFormat = globalFormat; +}; - return { - setFormat: function( format ) { - if ( format ) { - globalFormat = format; - } - return this; - }, - //TODO: same as the underlying Date object's terminology, but still misleading. - //TODO: We can use .setTime() instead of new Date and rename to setTimestamp. - setTime: function( time ) { - date = new Date( time ); - return this; - }, - setDay: function( day ) { - date = new Date( date.getFullYear(), date.getMonth(), day, date.getHours(), date.getMinutes(), date.getSeconds() ); - return this; - }, - setMonth: function( month ) { - // Overflow example: Month is October 31 (yeah Halloween) and month is changed to April with 30 days, - // the new date will me May 1. We will honor the month the user wants to set and if and overflow - // occurs, set to last day of month. - var days = date.getDay(), year = date.getFullYear(); - if ( days > this.daysInMonth( year, month ) ) { - // Overflow - days = this.daysInMonth( year, month ); - } - date = new Date( year, month, days, date.getHours(), date.getMinutes(), date.getSeconds() ); - return this; - }, - setYear: function( year ) { - var day = date.getDate(), - month = date.getMonth(); - // Check if Leap, and February and day is 29th - if ( this.isLeapYear( year ) && month == 1 && day == 29 ) { - // set day to last day of February - day = this.daysInMonth( year, month ); - } - date = new Date( year, month, day, date.getHours(), date.getMinutes(), date.getSeconds() ); - return this; - }, - setFullDate: function( year, month, day ) { - date = new Date( year, month, day ); - return this; - }, - adjust: function( period, offset ) { - var day = period == "D" ? date.getDate() + offset : date.getDate(), - month = period == "M" ? date.getMonth() + offset : date.getMonth(), - year = period == "Y" ? date.getFullYear() + offset : date.getFullYear(); - // If not day, update the day to the new month and year - if ( period != "D" ) { - day = Math.max( 1, Math.min( day, this.daysInMonth( year, month ) ) ); - } - date = new Date( year, month, day, date.getHours(), date.getMinutes(), date.getSeconds() ); - return this; - }, - daysInMonth: function( year, month ) { - year = year || date.getFullYear(); - month = month || date.getMonth(); - return 32 - new Date( year, month, 32 ).getDate(); - }, - monthName: function() { - return Globalize.format( date, { pattern: "MMMM" } ); - }, - day: function() { - return date.getDate(); - }, - month: function() { - return date.getMonth(); - }, - year: function() { - return date.getFullYear(); - }, - isLeapYear: function( year ) { - year = year || date.getFullYear(); - return new Date( year, 1, 29 ).getMonth() == 1; +$.date.prototype = { + setFormat: function( format ) { + if ( format ) { + this.globalFormat = format; + } + return this; + }, + //TODO: same as the underlying Date object's terminology, but still misleading. + //TODO: We can use .setTime() instead of new Date and rename to setTimestamp. + setTime: function( time ) { + this.dateObject = new Date( time ); + return this; + }, + setDay: function( day ) { + var date = this.dateObject; + this.dateObject = new Date( date.getFullYear(), date.getMonth(), day, date.getHours(), + date.getMinutes(), date.getSeconds() ); + return this; + }, + setMonth: function( month ) { + // Overflow example: Month is October 31 (yeah Halloween) and month is changed to April with 30 days, + // the new date will me May 1. We will honor the month the user wants to set and if and overflow + // occurs, set to last day of month. + var date = this.dateObject, + days = date.getDay(), year = date.getFullYear(); + if ( days > this.daysInMonth( year, month ) ) { - }, - weekdays: function() { - var result = []; - for ( var dow = 0; dow < 7; dow++ ) { - var day = ( dow + weekdaysRev[ Globalize.locale().supplemental.weekData.firstDay() ] ) % 7; - result.push({ - shortname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/abbreviated", weekdays[ day ] ]), - fullname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/wide", weekdays[ day ] ]), - }); - } - return result; - }, - days: function() { - var result = [], - today = $.date(), - firstDayOfMonth = new Date( this.year(), date.getMonth(), 1 ).getDay(), - leadDays = ( firstDayOfMonth - weekdaysRev[ Globalize.locale().supplemental.weekData.firstDay() ] + 7 ) % 7, - rows = Math.ceil( ( leadDays + this.daysInMonth() ) / 7 ), - printDate = new Date( this.year(), date.getMonth(), 1 - leadDays ); - for ( var row = 0; row < rows; row++ ) { - var week = result[ result.length ] = { - number: Globalize.format( printDate, { pattern: "w" } ), - days: [] + // Overflow + days = this.daysInMonth( year, month ); + } + this.dateObject = new Date( year, month, days, date.getHours(), + date.getMinutes(), date.getSeconds() ); + return this; + }, + setYear: function( year ) { + var date = this.dateObject, + day = date.getDate(), + month = date.getMonth(); + + // Check if Leap, and February and day is 29th + if ( this.isLeapYear( year ) && month == 1 && day == 29 ) { + + // set day to last day of February + day = this.daysInMonth( year, month ); + } + this.dateObject = new Date( year, month, day, date.getHours(), + date.getMinutes(), date.getSeconds() ); + return this; + }, + setFullDate: function( year, month, day ) { + this.dateObject = new Date( year, month, day ); + return this; + }, + adjust: function( period, offset ) { + var date = this.dateObject, + day = period == "D" ? date.getDate() + offset : date.getDate(), + month = period == "M" ? date.getMonth() + offset : date.getMonth(), + year = period == "Y" ? date.getFullYear() + offset : date.getFullYear(); + + // If not day, update the day to the new month and year + if ( period != "D" ) { + day = Math.max( 1, Math.min( day, this.daysInMonth( year, month ) ) ); + } + this.dateObject = new Date( year, month, day, date.getHours(), + date.getMinutes(), date.getSeconds() ); + return this; + }, + daysInMonth: function( year, month ) { + var date = this.dateObject; + year = year || date.getFullYear(); + month = month || date.getMonth(); + return 32 - new Date( year, month, 32 ).getDate(); + }, + monthName: function() { + return Globalize.format( this.dateObject, { pattern: "MMMM" } ); + }, + day: function() { + return this.dateObject.getDate(); + }, + month: function() { + return this.dateObject.getMonth(); + }, + year: function() { + return this.dateObject.getFullYear(); + }, + isLeapYear: function( year ) { + year = year || this.dateObject.getFullYear(); + return new Date( year, 1, 29 ).getMonth() == 1; + }, + weekdays: function() { + var result = []; + for ( var dow = 0; dow < 7; dow++ ) { + var day = ( dow + weekdaysRev[ Globalize.locale().supplemental.weekData.firstDay() ] ) % 7; + result.push({ + shortname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/abbreviated", weekdays[ day ] ]), + fullname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/wide", weekdays[ day ] ]), + }); + } + return result; + }, + days: function() { + var result = [], + today = $.date(), + date = this.dateObject, + firstDayOfMonth = new Date( this.year(), date.getMonth(), 1 ).getDay(), + leadDays = ( firstDayOfMonth - weekdaysRev[ Globalize.locale().supplemental.weekData.firstDay() ] + 7 ) % 7, + rows = Math.ceil( ( leadDays + this.daysInMonth() ) / 7 ), + printDate = new Date( this.year(), date.getMonth(), 1 - leadDays ); + for ( var row = 0; row < rows; row++ ) { + var week = result[ result.length ] = { + number: Globalize.format( printDate, { pattern: "w" } ), + days: [] + }; + for ( var dayx = 0; dayx < 7; dayx++ ) { + var day = week.days[ week.days.length ] = { + lead: printDate.getMonth() != date.getMonth(), + date: printDate.getDate(), + timestamp: printDate.getTime(), + current: this.selected && this.selected.equal( printDate ), + today: today.equal( printDate ) }; - for ( var dayx = 0; dayx < 7; dayx++ ) { - var day = week.days[ week.days.length ] = { - lead: printDate.getMonth() != date.getMonth(), - date: printDate.getDate(), - timestamp: printDate.getTime(), - current: this.selected && this.selected.equal( printDate ), - today: today.equal( printDate ) - }; - day.render = day.selectable = !day.lead; - if ( this.eachDay ) { - this.eachDay( day ); - } - // TODO use adjust("D", 1)? - printDate.setDate( printDate.getDate() + 1 ); + day.render = day.selectable = !day.lead; + if ( this.eachDay ) { + this.eachDay( day ); } + // TODO use adjust("D", 1)? + printDate.setDate( printDate.getDate() + 1 ); } - return result; - }, - // specialized for multi-month template, could be used in general - months: function( add ) { - var clone, - result = [ this ]; + } + return result; + }, + // specialized for multi-month template, could be used in general + months: function( add ) { + var clone, + result = [ this ]; - for ( var i = 0; i < add; i++ ) { - clone = this.clone(); - clone.adjust( "M", i + 1 ); - result.push( clone ); - } - result[ 0 ].first = true; - result[ result.length - 1 ].last = true; - return result; - }, - select: function() { - this.selected = this.clone(); - return this; - }, - clone: function() { - return $.date( new Date(date.getFullYear(), date.getMonth(), - date.getDate(), date.getHours(), - date.getMinutes(), date.getSeconds()), globalFormat ); - }, - // TODO compare year, month, day each for better performance - equal: function( other ) { - function format( date ) { - return Globalize.format( date, { pattern: "yyyyMMdd" } ); - } - return format( date ) === format( other ); - }, - date: function() { - return date; - }, - format: function( format ) { - return Globalize.format( date, format || globalFormat ); + for ( var i = 0; i < add; i++ ) { + clone = this.clone(); + clone.adjust( "M", i + 1 ); + result.push( clone ); } - }; + result[ 0 ].first = true; + result[ result.length - 1 ].last = true; + return result; + }, + select: function() { + this.selected = this.clone(); + return this; + }, + clone: function() { + var date = this.dateObject; + return new $.date( new Date( date.getFullYear(), date.getMonth(), + date.getDate(), date.getHours(), + date.getMinutes(), date.getSeconds()), this.globalFormat ); + }, + // TODO compare year, month, day each for better performance + equal: function( other ) { + function format( date ) { + return Globalize.format( date, { pattern: "yyyyMMdd" } ); + } + return format( this.dateObject ) === format( other ); + }, + date: function() { + return this.dateObject; + }, + format: function( format ) { + return Globalize.format( this.dateObject, format || this.globalFormat ); + } }; }( jQuery )); diff --git a/tests/unit/date/date_core.js b/tests/unit/date/date_core.js index 09ffd206abe..db94cb67d56 100644 --- a/tests/unit/date/date_core.js +++ b/tests/unit/date/date_core.js @@ -1,8 +1,9 @@ module( "date: core" ); -test( "Check Date Setup", 2, function() { - ok( true, "First Test Always Passes" ); - ok( $.date(), "Load JQuery Date" ); +test( "Instantiation", function() { + expect( 2 ); + ok( new $.date() instanceof $.date, "constructor function" ); + ok( $.date() instanceof $.date, "instantiation without new" ); }); test( "Check Sets and Gets", 6, function() { @@ -38,7 +39,6 @@ test( "Date Adjustments - Normal Use Cases", 10, function() { equal( date.adjust( "D", 1 ).year(), 2013, "Add 1 day to change year from 2012 to 2013" ); equal( date.adjust( "D", -1 ).year(), 2012, "Subtract 1 day to change month from 2013 to 2012" ); - }); test( "Date Adjustments - Month Overflow Edge Cases", 2, function() { From 50447d2ec7e55a0d56b68513a75089bc68d62a46 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 23 Apr 2014 17:49:03 +0200 Subject: [PATCH 23/53] Datepicker: Fix code style issues --- demos/datepicker/show-week.html | 2 +- external/date.js | 2 +- tests/unit/datepicker/datepicker_core.js | 142 +- tests/unit/datepicker/datepicker_events.js | 151 +- tests/unit/datepicker/datepicker_methods.js | 2 +- tests/unit/datepicker/datepicker_options.js | 1284 ++++++++--------- .../datepicker/datepicker_test_helpers.js | 20 +- ui/datepicker.js | 62 +- 8 files changed, 854 insertions(+), 811 deletions(-) diff --git a/demos/datepicker/show-week.html b/demos/datepicker/show-week.html index c7faf102052..321698373ce 100644 --- a/demos/datepicker/show-week.html +++ b/demos/datepicker/show-week.html @@ -28,7 +28,7 @@

      The datepicker can show the week of the year. The calculation follows - Unicode CLDR specification. + Unicode CLDR specification. This means that some days from one year may be placed into weeks 'belonging' to another year.

      diff --git a/external/date.js b/external/date.js index 101afb17581..e7c17a58dcf 100644 --- a/external/date.js +++ b/external/date.js @@ -126,7 +126,7 @@ $.date.prototype = { var day = ( dow + weekdaysRev[ Globalize.locale().supplemental.weekData.firstDay() ] ) % 7; result.push({ shortname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/abbreviated", weekdays[ day ] ]), - fullname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/wide", weekdays[ day ] ]), + fullname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/wide", weekdays[ day ] ]) }); } return result; diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index 377c4c201dd..189f03d932a 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -40,7 +40,7 @@ asyncTest( "baseStructure", function() { ok( header.children( ":eq(1)" ).is( ".ui-datepicker-next" ) && header.children( ":eq(1)" ).html() !== "", "Structure - next link" ); title = header.children( ":last" ).children( ":first" ); - ok( title.is( "div.ui-datepicker-title" ) && title.html() !== "","Structure - title division" ); + ok( title.is( "div.ui-datepicker-title" ) && title.html() !== "", "Structure - title division" ); equal( title.children().length, 2, "Structure - title child count" ); ok( title.children( ":first" ).is( "span.ui-datepicker-month" ) && title.children( ":first" ).text() !== "", "Structure - month text" ); ok( title.children( ":last" ).is( "span.ui-datepicker-year" ) && title.children( ":last" ).text() !== "", "Structure - year text" ); @@ -95,7 +95,7 @@ asyncTest( "baseStructure", function() { function step3() { // Multi-month 2 - inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: 2 }); + inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: 2 } ); dp = inp.datepicker( "widget" ).find( ".ui-datepicker" ); inp.focus(); setTimeout(function() { @@ -121,7 +121,7 @@ asyncTest( "baseStructure", function() { function step4() { // Multi-month 3 - inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: 3 }); + inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: 3 } ); dp = inp.datepicker( "widget" ).find( ".ui-datepicker" ); inp.focus(); setTimeout(function() { @@ -135,7 +135,7 @@ asyncTest( "baseStructure", function() { function step5() { // Multi-month [2, 2] - inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: [ 2, 2 ] }); + inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: [ 2, 2 ] } ); dp = inp.datepicker( "widget" ).find( ".ui-datepicker" ); inp.focus(); setTimeout(function() { @@ -224,39 +224,39 @@ test( "Keyboard handling", function() { date = new Date(); input.datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke enter" ); // Enter = Select today's date by default input.val( "1/1/14" ).datepicker( "open" ) - .simulate("keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Keystroke enter - preset" ); // Control + Home = Change the calendar to the current month input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.HOME }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.HOME } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke ctrl+home" ); // Control + End = Close the calendar and clear the input input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.END }); + .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.END } ); equal( input.val(), "", "Keystroke ctrl+end" ); input.val( "" ).datepicker( "open" ); ok( instance.isOpen, "datepicker is open before escape" ); - input.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); + input.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); ok( !instance.isOpen, "escape closes the datepicker" ); input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); + .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Keystroke esc - preset" ); input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE }); + .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Keystroke esc - abandoned" ); @@ -279,38 +279,38 @@ asyncTest( "keyboard handling", function() { picker = input.datepicker( "widget" ); ok( !picker.is( ":visible" ), "datepicker closed" ); input.val( "" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { ok( picker.is( ":visible" ), "Keystroke down opens datepicker" ); input.datepicker( "destroy" ); step2(); }); - }; + } function step2() { input.datepicker(); - picker = input.datepicker( "widget" ) + picker = input.datepicker( "widget" ); ok( !picker.is( ":visible" ), "datepicker closed" ); input.val( "" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.UP }); + .simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); setTimeout(function() { ok( picker.is( ":visible" ), "Keystroke up opens datepicker" ); input.datepicker( "destroy" ); step3(); }); - }; + } function step3() { input.datepicker() .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.LEFT }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); date = new Date( 2013, 12 - 1, 31 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke left to switch to previous day" ); @@ -318,17 +318,17 @@ asyncTest( "keyboard handling", function() { input.datepicker( "destroy" ); step4(); }, 100 ); - }; + } function step4() { input.datepicker() .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); date = new Date( 2014, 1 - 1, 2 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke right to switch to next day" ); @@ -336,17 +336,17 @@ asyncTest( "keyboard handling", function() { input.datepicker( "destroy" ); step5(); }, 100 ); - }; + } function step5() { input.datepicker() .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.UP }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.UP } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); date = new Date( 2013, 12 - 1, 25 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke up to move to the previous week" ); @@ -354,17 +354,17 @@ asyncTest( "keyboard handling", function() { input.datepicker( "destroy" ); step6(); }, 100 ); - }; + } function step6() { input.datepicker() .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); date = new Date( 2014, 1 - 1, 8 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke down to move to the next week" ); @@ -372,17 +372,17 @@ asyncTest( "keyboard handling", function() { input.datepicker( "destroy" ); step7(); }, 100 ); - }; + } function step7() { input.datepicker() .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); date = new Date( 2013, 12 - 1, 1 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke Page Up moves date to previous month" ); @@ -390,17 +390,17 @@ asyncTest( "keyboard handling", function() { input.datepicker( "destroy" ); step8(); }, 100 ); - }; + } function step8() { input.datepicker() .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP, altKey: true }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP, altKey: true } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); date = new Date( 2013, 1 - 1, 1 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke Page Up + Ctrl moves date to previous year" ); @@ -408,17 +408,17 @@ asyncTest( "keyboard handling", function() { input.datepicker( "destroy" ); step9(); }, 100 ); - }; + } function step9() { input.datepicker() .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); date = new Date( 2014, 2 - 1, 1 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke Page Down moves date to next month" ); @@ -426,17 +426,17 @@ asyncTest( "keyboard handling", function() { input.datepicker( "destroy" ); step10(); }, 100 ); - }; + } function step10() { input.datepicker() .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); date = new Date( 2015, 1 - 1, 1 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke Page Down + Ctrl moves date to next year" ); @@ -444,18 +444,18 @@ asyncTest( "keyboard handling", function() { input.datepicker( "destroy" ); step11(); }, 100 ); - }; + } // Check for moving to short months function step11() { input.datepicker() .val( "3/31/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); date = new Date( 2014, 2 - 1, 28 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke Page Up and short months" ); @@ -463,17 +463,17 @@ asyncTest( "keyboard handling", function() { input.datepicker( "destroy" ); step12(); }, 100 ); - }; + } function step12() { input.datepicker() .val( "1/30/16" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); date = new Date( 2016, 2 - 1, 29 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke Page Down and leap years" ); @@ -481,23 +481,23 @@ asyncTest( "keyboard handling", function() { input.datepicker( "destroy" ); start(); }, 100 ); - }; + } step1(); }); /* // TODO: Re-add tests if we implement a stepMonths option - input.datepicker( "option", { stepMonths: 2, gotoCurrent: false }) + input.datepicker( "option", { stepMonths: 2, gotoCurrent: false } ) .datepicker( "close" ).val( "02/04/2008" ).datepicker( "open" ) - .late( "keydown", { keyCode: $.ui.keyCode.PAGE_UP }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .late( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2007, 12 - 1, 4 ), "Keystroke pgup step 2" ); input.val( "02/04/2008" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN }) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER }); + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 4 - 1, 4 ), "Keystroke pgdn step 2" ); */ @@ -516,8 +516,8 @@ test( "mouse", function() { input.val( "2/4/08" ).datepicker( "open" ); $( ".ui-datepicker-calendar tbody a:contains(12)", picker ).simulate( "mousedown", {} ); - TestHelpers.datepicker.equalsDate( input.datepicker("valueAsDate"), new Date( 2008, 2 - 1, 12 ), - "Mouse click - preset") ; + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 12 ), + "Mouse click - preset" ) ; input.val( "" ).datepicker( "open" ); $( "button.ui-datepicker-close", picker ).simulate( "click", {} ); @@ -547,15 +547,15 @@ test( "mouse", function() { "Mouse click - previous" ); input.val( "2/4/08" ).datepicker( "open" ); - $(".ui-datepicker-next", picker ).simulate( "click" ); - $(".ui-datepicker-calendar tbody a:contains(18)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate( input.datepicker("valueAsDate"), new Date( 2008, 3 - 1, 18 ), + $( ".ui-datepicker-next", picker ).simulate( "click" ); + $( ".ui-datepicker-calendar tbody a:contains(18)", picker ).simulate( "mousedown" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 3 - 1, 18 ), "Mouse click - next" ); /* // TODO: Re-add when min and max options are introduced. // Previous/next with minimum/maximum - input.datepicker("option", { + input.datepicker( "option", { minDate: new Date( 2008, 2 - 1, 2 ), maxDate: new Date( 2008, 2 - 1, 26 ) }).val( "2/4/08" ).datepicker( "open" ); @@ -580,13 +580,13 @@ test( "mouse", function() { date.setDate( 10 ); TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), date, "Mouse click inline" ); - inline.datepicker( "option", { showButtonPanel: true }) + inline.datepicker( "option", { showButtonPanel: true } ) .datepicker( "valueAsDate", new Date( 2008, 2 - 1, 4 )); $( ".ui-datepicker-calendar tbody a:contains(12)", picker ).simulate( "mousedown", {} ); TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 12 ), "Mouse click inline - preset" ); - inline.datepicker("option", { showButtonPanel: true }); + inline.datepicker( "option", { showButtonPanel: true } ); $( ".ui-datepicker-current", picker ).simulate( "click", {} ); $( ".ui-datepicker-calendar tbody a:contains(14)", picker ).simulate( "mousedown", {} ); date.setDate( 14 ); diff --git a/tests/unit/datepicker/datepicker_events.js b/tests/unit/datepicker/datepicker_events.js index 63e2b0e9cc7..9ee254e5351 100644 --- a/tests/unit/datepicker/datepicker_events.js +++ b/tests/unit/datepicker/datepicker_events.js @@ -1,3 +1,10 @@ +// The implement of events is completely changing therefore these tests are no longer directly +// relevant. Leaving them around commented out so we can ensure the functionality is replicated. +// For example: +// TODO: In the old implementation the Enter key select's today's date when the has +// focus and is empty. Do we want to replicate this behavior in the rewrite? +/* + (function( $ ) { module( "datepicker: events" ); @@ -18,12 +25,6 @@ test( "select", function() { expect( 0 ); }); -// The implement of events is completely changing therefore these tests are no longer directly -// relevant. Leaving them around commented out so we can ensure the functionality is replicated. -// For example: -// TODO: In the old implementation the Enter key select's today's date when the has -// focus and is empty. Do we want to replicate this behavior in the rewrite? -/* var selectedThis = null, selectedDate = null, selectedInst = null; @@ -40,110 +41,110 @@ function callback2(year, month, inst) { selectedInst = inst; } -test("events", function() { +test( "events", function() { expect( 26 ); var dateStr, newMonthYear, inp2, - inp = TestHelpers.datepicker.init("#inp", {onSelect: callback}), + inp = TestHelpers.datepicker.init( "#inp", {onSelect: callback}), date = new Date(); // onSelect - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - equal(selectedThis, inp[0], "Callback selected this"); - equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback selected inst"); - equal(selectedDate, $.datepicker.formatDate("mm/dd/yy", date), - "Callback selected date"); - inp.val("").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + inp.val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(selectedThis, inp[0], "Callback selected this" ); + equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback selected inst" ); + equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", date), + "Callback selected date" ); + inp.val( "" ).datepicker( "show" ). + simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() + 7); - equal(selectedDate, $.datepicker.formatDate("mm/dd/yy", date), - "Callback selected date - ctrl+down"); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); - equal(selectedDate, $.datepicker.formatDate("mm/dd/yy", date), - "Callback selected date - esc"); + equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", date), + "Callback selected date - ctrl+down" ); + inp.val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); + equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", date), + "Callback selected date - esc" ); dateStr = "02/04/2008"; - inp.val(dateStr).datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + inp.val(dateStr).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); equal(dateStr, selectedDate, - "onSelect is called after enter keydown"); + "onSelect is called after enter keydown" ); // onChangeMonthYear - inp.datepicker("option", {onChangeMonthYear: callback2, onSelect: null}). - val("").datepicker("show"); + inp.datepicker( "option", {onChangeMonthYear: callback2, onSelect: null}). + val( "" ).datepicker( "show" ); newMonthYear = function(date) { return date.getFullYear() + "/" + (date.getMonth() + 1); }; date = new Date(); date.setDate(1); - inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_UP}); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_UP}); date.setMonth(date.getMonth() - 1); - equal(selectedThis, inp[0], "Callback change month/year this"); - equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback change month/year inst"); + equal(selectedThis, inp[0], "Callback change month/year this" ); + equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback change month/year inst" ); equal(selectedDate, newMonthYear(date), - "Callback change month/year date - pgup"); - inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); + "Callback change month/year date - pgup" ); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); date.setMonth(date.getMonth() + 1); equal(selectedDate, newMonthYear(date), - "Callback change month/year date - pgdn"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); + "Callback change month/year date - pgdn" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); date.setFullYear(date.getFullYear() - 1); equal(selectedDate, newMonthYear(date), - "Callback change month/year date - ctrl+pgup"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.HOME}); + "Callback change month/year date - ctrl+pgup" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.HOME}); date.setFullYear(date.getFullYear() + 1); equal(selectedDate, newMonthYear(date), - "Callback change month/year date - ctrl+home"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); + "Callback change month/year date - ctrl+home" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); date.setFullYear(date.getFullYear() + 1); equal(selectedDate, newMonthYear(date), - "Callback change month/year date - ctrl+pgdn"); - inp.datepicker("setDate", new Date(2007, 1 - 1, 26)); - equal(selectedDate, "2007/1", "Callback change month/year date - setDate"); + "Callback change month/year date - ctrl+pgdn" ); + inp.datepicker( "setDate", new Date(2007, 1 - 1, 26)); + equal(selectedDate, "2007/1", "Callback change month/year date - setDate" ); selectedDate = null; - inp.datepicker("setDate", new Date(2007, 1 - 1, 12)); - ok(selectedDate == null, "Callback change month/year date - setDate no change"); + inp.datepicker( "setDate", new Date(2007, 1 - 1, 12)); + ok(selectedDate == null, "Callback change month/year date - setDate no change" ); // onChangeMonthYear step by 2 - inp.datepicker("option", {stepMonths: 2}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.PAGE_UP}); + inp.datepicker( "option", {stepMonths: 2}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_UP}); date.setMonth(date.getMonth() - 14); equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - pgup"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); + "Callback change month/year by 2 date - pgup" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); date.setMonth(date.getMonth() - 12); equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - ctrl+pgup"); - inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); + "Callback change month/year by 2 date - ctrl+pgup" ); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); date.setMonth(date.getMonth() + 2); equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - pgdn"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); + "Callback change month/year by 2 date - pgdn" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); date.setMonth(date.getMonth() + 12); equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - ctrl+pgdn"); + "Callback change month/year by 2 date - ctrl+pgdn" ); // onClose - inp.datepicker("option", {onClose: callback, onChangeMonthYear: null, stepMonths: 1}). - val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); - equal(selectedThis, inp[0], "Callback close this"); - equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback close inst"); - equal(selectedDate, "", "Callback close date - esc"); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - equal(selectedDate, $.datepicker.formatDate("mm/dd/yy", new Date()), - "Callback close date - enter"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); - equal(selectedDate, "02/04/2008", "Callback close date - preset"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.END}); - equal(selectedDate, "", "Callback close date - ctrl+end"); + inp.datepicker( "option", {onClose: callback, onChangeMonthYear: null, stepMonths: 1}). + val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); + equal(selectedThis, inp[0], "Callback close this" ); + equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback close inst" ); + equal(selectedDate, "", "Callback close date - esc" ); + inp.val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", new Date()), + "Callback close date - enter" ); + inp.val( "02/04/2008" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); + equal(selectedDate, "02/04/2008", "Callback close date - preset" ); + inp.val( "02/04/2008" ).datepicker( "show" ). + simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.END}); + equal(selectedDate, "", "Callback close date - ctrl+end" ); - inp2 = TestHelpers.datepicker.init("#inp2"); - inp2.datepicker().datepicker("option", {onClose: callback}).datepicker("show"); - inp.datepicker("show"); - equal(selectedThis, inp2[0], "Callback close this"); + inp2 = TestHelpers.datepicker.init( "#inp2" ); + inp2.datepicker().datepicker( "option", {onClose: callback}).datepicker( "show" ); + inp.datepicker( "show" ); + equal(selectedThis, inp2[0], "Callback close this" ); }); -*/ })( jQuery ); + */ diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 281a81c8d9b..7efea106918 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -51,7 +51,7 @@ test( "enable / disable", function() { test( "widget", function() { expect( 1 ); var actual = $( "#datepicker" ).datepicker().datepicker( "widget" ); - deepEqual( $("body > .ui-front" )[ 0 ], actual[ 0 ] ); + deepEqual( $( "body > .ui-front" )[ 0 ], actual[ 0 ] ); actual.remove(); }); diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index c08c370e26f..173e19e5203 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -44,7 +44,7 @@ test( "dateFormat", function() { expect( 2 ); var input = $( "#datepicker" ).val( "1/1/14" ).datepicker(), picker = input.datepicker( "widget" ), - firstDayLink = picker.find( "td[id]:first a" ); + firstDayLink = picker.find( "td[id]:first a" ); input.datepicker( "open" ); firstDayLink.trigger( "mousedown" ); @@ -60,7 +60,7 @@ test( "eachDay", function() { expect( 5 ); var timestamp, input = $( "#datepicker" ).datepicker(), - picker = input.datepicker( "widget" ); + picker = input.datepicker( "widget" ), firstCell = picker.find( "td[id]:first" ); equal( firstCell.find( "a" ).length, 1, "days are selectable by default" ); @@ -155,85 +155,85 @@ test( "showWeek", function() { }); /* -test("setDefaults", function() { +test( "setDefaults", function() { expect( 3 ); - TestHelpers.datepicker.init("#inp"); - equal($.datepicker._defaults.showOn, "focus", "Initial showOn"); + TestHelpers.datepicker.init( "#inp" ); + equal($.datepicker._defaults.showOn, "focus", "Initial showOn" ); $.datepicker.setDefaults({showOn: "button"}); - equal($.datepicker._defaults.showOn, "button", "Change default showOn"); + equal($.datepicker._defaults.showOn, "button", "Change default showOn" ); $.datepicker.setDefaults({showOn: "focus"}); - equal($.datepicker._defaults.showOn, "focus", "Restore showOn"); + equal($.datepicker._defaults.showOn, "focus", "Restore showOn" ); }); -test("option", function() { +test( "option", function() { expect( 17 ); - var inp = TestHelpers.datepicker.init("#inp"), + var inp = TestHelpers.datepicker.init( "#inp" ), inst = $.data(inp[0], TestHelpers.datepicker.PROP_NAME); // Set option - equal(inst.settings.showOn, null, "Initial setting showOn"); - equal($.datepicker._get(inst, "showOn"), "focus", "Initial instance showOn"); - equal($.datepicker._defaults.showOn, "focus", "Initial default showOn"); - inp.datepicker("option", "showOn", "button"); - equal(inst.settings.showOn, "button", "Change setting showOn"); - equal($.datepicker._get(inst, "showOn"), "button", "Change instance showOn"); - equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); - inp.datepicker("option", {showOn: "both"}); - equal(inst.settings.showOn, "both", "Change setting showOn"); - equal($.datepicker._get(inst, "showOn"), "both", "Change instance showOn"); - equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); - inp.datepicker("option", "showOn", undefined); - equal(inst.settings.showOn, null, "Clear setting showOn"); - equal($.datepicker._get(inst, "showOn"), "focus", "Restore instance showOn"); - equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); + equal(inst.settings.showOn, null, "Initial setting showOn" ); + equal($.datepicker._get(inst, "showOn" ), "focus", "Initial instance showOn" ); + equal($.datepicker._defaults.showOn, "focus", "Initial default showOn" ); + inp.datepicker( "option", "showOn", "button" ); + equal(inst.settings.showOn, "button", "Change setting showOn" ); + equal($.datepicker._get(inst, "showOn" ), "button", "Change instance showOn" ); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn" ); + inp.datepicker( "option", {showOn: "both"}); + equal(inst.settings.showOn, "both", "Change setting showOn" ); + equal($.datepicker._get(inst, "showOn" ), "both", "Change instance showOn" ); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn" ); + inp.datepicker( "option", "showOn", undefined); + equal(inst.settings.showOn, null, "Clear setting showOn" ); + equal($.datepicker._get(inst, "showOn" ), "focus", "Restore instance showOn" ); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn" ); // Get option - inp = TestHelpers.datepicker.init("#inp"); - equal(inp.datepicker("option", "showOn"), "focus", "Initial setting showOn"); - inp.datepicker("option", "showOn", "button"); - equal(inp.datepicker("option", "showOn"), "button", "Change instance showOn"); - inp.datepicker("option", "showOn", undefined); - equal(inp.datepicker("option", "showOn"), "focus", "Reset instance showOn"); - deepEqual(inp.datepicker("option", "all"), {showAnim: ""}, "Get instance settings"); - deepEqual(inp.datepicker("option", "defaults"), $.datepicker._defaults, - "Get default settings"); + inp = TestHelpers.datepicker.init( "#inp" ); + equal(inp.datepicker( "option", "showOn" ), "focus", "Initial setting showOn" ); + inp.datepicker( "option", "showOn", "button" ); + equal(inp.datepicker( "option", "showOn" ), "button", "Change instance showOn" ); + inp.datepicker( "option", "showOn", undefined); + equal(inp.datepicker( "option", "showOn" ), "focus", "Reset instance showOn" ); + deepEqual(inp.datepicker( "option", "all" ), {showAnim: ""}, "Get instance settings" ); + deepEqual(inp.datepicker( "option", "defaults" ), $.datepicker._defaults, + "Get default settings" ); }); test( "disabled", function() { expect(8); - var inp = TestHelpers.datepicker.init("#inp"); - ok(!inp.datepicker("isDisabled"), "Initially marked as enabled"); - ok(!inp[0].disabled, "Field initially enabled"); - inp.datepicker("option", "disabled", true); - ok(inp.datepicker("isDisabled"), "Marked as disabled"); - ok(inp[0].disabled, "Field now disabled"); - inp.datepicker("option", "disabled", false); - ok(!inp.datepicker("isDisabled"), "Marked as enabled"); - ok(!inp[0].disabled, "Field now enabled"); - inp.datepicker("destroy"); - - inp = TestHelpers.datepicker.init("#inp", { disabled: true }); - ok(inp.datepicker("isDisabled"), "Initially marked as disabled"); - ok(inp[0].disabled, "Field initially disabled"); + var inp = TestHelpers.datepicker.init( "#inp" ); + ok(!inp.datepicker( "isDisabled" ), "Initially marked as enabled" ); + ok(!inp[0].disabled, "Field initially enabled" ); + inp.datepicker( "option", "disabled", true); + ok(inp.datepicker( "isDisabled" ), "Marked as disabled" ); + ok(inp[0].disabled, "Field now disabled" ); + inp.datepicker( "option", "disabled", false); + ok(!inp.datepicker( "isDisabled" ), "Marked as enabled" ); + ok(!inp[0].disabled, "Field now enabled" ); + inp.datepicker( "destroy" ); + + inp = TestHelpers.datepicker.init( "#inp", { disabled: true }); + ok(inp.datepicker( "isDisabled" ), "Initially marked as disabled" ); + ok(inp[0].disabled, "Field initially disabled" ); }); -test("change", function() { +test( "change", function() { expect( 12 ); - var inp = TestHelpers.datepicker.init("#inp"), + var inp = TestHelpers.datepicker.init( "#inp" ), inst = $.data(inp[0], TestHelpers.datepicker.PROP_NAME); - equal(inst.settings.showOn, null, "Initial setting showOn"); - equal($.datepicker._get(inst, "showOn"), "focus", "Initial instance showOn"); - equal($.datepicker._defaults.showOn, "focus", "Initial default showOn"); - inp.datepicker("change", "showOn", "button"); - equal(inst.settings.showOn, "button", "Change setting showOn"); - equal($.datepicker._get(inst, "showOn"), "button", "Change instance showOn"); - equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); - inp.datepicker("change", {showOn: "both"}); - equal(inst.settings.showOn, "both", "Change setting showOn"); - equal($.datepicker._get(inst, "showOn"), "both", "Change instance showOn"); - equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); - inp.datepicker("change", "showOn", undefined); - equal(inst.settings.showOn, null, "Clear setting showOn"); - equal($.datepicker._get(inst, "showOn"), "focus", "Restore instance showOn"); - equal($.datepicker._defaults.showOn, "focus", "Retain default showOn"); + equal(inst.settings.showOn, null, "Initial setting showOn" ); + equal($.datepicker._get(inst, "showOn" ), "focus", "Initial instance showOn" ); + equal($.datepicker._defaults.showOn, "focus", "Initial default showOn" ); + inp.datepicker( "change", "showOn", "button" ); + equal(inst.settings.showOn, "button", "Change setting showOn" ); + equal($.datepicker._get(inst, "showOn" ), "button", "Change instance showOn" ); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn" ); + inp.datepicker( "change", {showOn: "both"}); + equal(inst.settings.showOn, "both", "Change setting showOn" ); + equal($.datepicker._get(inst, "showOn" ), "both", "Change instance showOn" ); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn" ); + inp.datepicker( "change", "showOn", undefined); + equal(inst.settings.showOn, null, "Clear setting showOn" ); + equal($.datepicker._get(inst, "showOn" ), "focus", "Restore instance showOn" ); + equal($.datepicker._defaults.showOn, "focus", "Retain default showOn" ); }); (function() { @@ -377,135 +377,135 @@ test("change", function() { }); })(); -test("otherMonths", function() { +test( "otherMonths", function() { expect( 8 ); - var inp = TestHelpers.datepicker.init("#inp"), - pop = $("#ui-datepicker-div"); - inp.val("06/01/2009").datepicker("show"); - equal(pop.find("tbody").text(), + var inp = TestHelpers.datepicker.init( "#inp" ), + pop = $( "#ui-datepicker-div" ); + inp.val( "06/01/2009" ).datepicker( "show" ); + equal(pop.find( "tbody" ).text(), // support: IE <9, jQuery <1.8 // In IE7/8 with jQuery <1.8, encoded spaces behave in strange ways $( "\u00a0123456789101112131415161718192021222324252627282930\u00a0\u00a0\u00a0\u00a0" ).text(), - "Other months - none"); - ok(pop.find("td:last *").length === 0, "Other months - no content"); - inp.datepicker("hide").datepicker("option", "showOtherMonths", true).datepicker("show"); - equal(pop.find("tbody").text(), "311234567891011121314151617181920212223242526272829301234", - "Other months - show"); - ok(pop.find("td:last span").length === 1, "Other months - span content"); - inp.datepicker("hide").datepicker("option", "selectOtherMonths", true).datepicker("show"); - equal(pop.find("tbody").text(), "311234567891011121314151617181920212223242526272829301234", - "Other months - select"); - ok(pop.find("td:last a").length === 1, "Other months - link content"); - inp.datepicker("hide").datepicker("option", "showOtherMonths", false).datepicker("show"); - equal(pop.find("tbody").text(), + "Other months - none" ); + ok(pop.find( "td:last *" ).length === 0, "Other months - no content" ); + inp.datepicker( "hide" ).datepicker( "option", "showOtherMonths", true).datepicker( "show" ); + equal(pop.find( "tbody" ).text(), "311234567891011121314151617181920212223242526272829301234", + "Other months - show" ); + ok(pop.find( "td:last span" ).length === 1, "Other months - span content" ); + inp.datepicker( "hide" ).datepicker( "option", "selectOtherMonths", true).datepicker( "show" ); + equal(pop.find( "tbody" ).text(), "311234567891011121314151617181920212223242526272829301234", + "Other months - select" ); + ok(pop.find( "td:last a" ).length === 1, "Other months - link content" ); + inp.datepicker( "hide" ).datepicker( "option", "showOtherMonths", false).datepicker( "show" ); + equal(pop.find( "tbody" ).text(), // support: IE <9, jQuery <1.8 // In IE7/8 with jQuery <1.8, encoded spaces behave in strange ways $( "\u00a0123456789101112131415161718192021222324252627282930\u00a0\u00a0\u00a0\u00a0" ).text(), - "Other months - none"); - ok(pop.find("td:last *").length === 0, "Other months - no content"); + "Other months - none" ); + ok(pop.find( "td:last *" ).length === 0, "Other months - no content" ); }); -test("defaultDate", function() { +test( "defaultDate", function() { expect( 16 ); - var inp = TestHelpers.datepicker.init("#inp"), + var inp = TestHelpers.datepicker.init( "#inp" ), date = new Date(); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date null"); + inp.val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date null" ); // Numeric values - inp.datepicker("option", {defaultDate: -2}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + inp.datepicker( "option", {defaultDate: -2}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() - 2); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date -2"); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date -2" ); date = new Date(); - inp.datepicker("option", {defaultDate: 3}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + inp.datepicker( "option", {defaultDate: 3}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() + 3); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date 3"); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date 3" ); date = new Date(); - inp.datepicker("option", {defaultDate: 1 / "a"}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date NaN"); + inp.datepicker( "option", {defaultDate: 1 / "a"}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date NaN" ); // String offset values - inp.datepicker("option", {defaultDate: "-1d"}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + inp.datepicker( "option", {defaultDate: "-1d"}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() - 1); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date -1d"); - inp.datepicker("option", {defaultDate: "+3D"}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date -1d" ); + inp.datepicker( "option", {defaultDate: "+3D"}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() + 4); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date +3D"); - inp.datepicker("option", {defaultDate: " -2 w "}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date +3D" ); + inp.datepicker( "option", {defaultDate: " -2 w "}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date = new Date(); date.setDate(date.getDate() - 14); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date -2 w"); - inp.datepicker("option", {defaultDate: "+1 W"}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date -2 w" ); + inp.datepicker( "option", {defaultDate: "+1 W"}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setDate(date.getDate() + 21); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date +1 W"); - inp.datepicker("option", {defaultDate: " -1 m "}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date +1 W" ); + inp.datepicker( "option", {defaultDate: " -1 m "}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date = TestHelpers.datepicker.addMonths(new Date(), -1); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date -1 m"); - inp.datepicker("option", {defaultDate: "+2M"}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date -1 m" ); + inp.datepicker( "option", {defaultDate: "+2M"}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date = TestHelpers.datepicker.addMonths(new Date(), 2); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date +2M"); - inp.datepicker("option", {defaultDate: "-2y"}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date +2M" ); + inp.datepicker( "option", {defaultDate: "-2y"}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date = new Date(); date.setFullYear(date.getFullYear() - 2); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date -2y"); - inp.datepicker("option", {defaultDate: "+1 Y "}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date -2y" ); + inp.datepicker( "option", {defaultDate: "+1 Y "}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date.setFullYear(date.getFullYear() + 3); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date +1 Y"); - inp.datepicker("option", {defaultDate: "+1M +10d"}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date +1 Y" ); + inp.datepicker( "option", {defaultDate: "+1M +10d"}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date = TestHelpers.datepicker.addMonths(new Date(), 1); date.setDate(date.getDate() + 10); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date +1M +10d"); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date +1M +10d" ); // String date values - inp.datepicker("option", {defaultDate: "07/04/2007"}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + inp.datepicker( "option", {defaultDate: "07/04/2007"}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date = new Date(2007, 7 - 1, 4); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date 07/04/2007"); - inp.datepicker("option", {dateFormat: "yy-mm-dd", defaultDate: "2007-04-02"}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date 07/04/2007" ); + inp.datepicker( "option", {dateFormat: "yy-mm-dd", defaultDate: "2007-04-02"}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date = new Date(2007, 4 - 1, 2); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date 2007-04-02"); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date 2007-04-02" ); // Date value date = new Date(2007, 1 - 1, 26); - inp.datepicker("option", {dateFormat: "mm/dd/yy", defaultDate: date}). - datepicker("hide").val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, "Default date 01/26/2007"); + inp.datepicker( "option", {dateFormat: "mm/dd/yy", defaultDate: date}). + datepicker( "hide" ).val( "" ).datepicker( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, "Default date 01/26/2007" ); }); -test("miscellaneous", function() { +test( "miscellaneous", function() { expect( 19 ); var curYear, longNames, shortNames, date, - dp = $("#ui-datepicker-div"), - inp = TestHelpers.datepicker.init("#inp"); + dp = $( "#ui-datepicker-div" ), + inp = TestHelpers.datepicker.init( "#inp" ); // Year range function genRange(start, offset) { var i = start, @@ -516,398 +516,398 @@ test("miscellaneous", function() { return range; } curYear = new Date().getFullYear(); - inp.val("02/04/2008").datepicker("show"); - equal(dp.find(".ui-datepicker-year").text(), "2008", "Year range - read-only default"); - inp.datepicker("hide").datepicker("option", {changeYear: true}).datepicker("show"); - equal(dp.find(".ui-datepicker-year").text(), genRange(2008 - 10, 21), "Year range - changeable default"); - inp.datepicker("hide").datepicker("option", {yearRange: "c-6:c+2", changeYear: true}).datepicker("show"); - equal(dp.find(".ui-datepicker-year").text(), genRange(2008 - 6, 9), "Year range - c-6:c+2"); - inp.datepicker("hide").datepicker("option", {yearRange: "2000:2010", changeYear: true}).datepicker("show"); - equal(dp.find(".ui-datepicker-year").text(), genRange(2000, 11), "Year range - 2000:2010"); - inp.datepicker("hide").datepicker("option", {yearRange: "-5:+3", changeYear: true}).datepicker("show"); - equal(dp.find(".ui-datepicker-year").text(), genRange(curYear - 5, 9), "Year range - -5:+3"); - inp.datepicker("hide").datepicker("option", {yearRange: "2000:-5", changeYear: true}).datepicker("show"); - equal(dp.find(".ui-datepicker-year").text(), genRange(2000, curYear - 2004), "Year range - 2000:-5"); - inp.datepicker("hide").datepicker("option", {yearRange: "", changeYear: true}).datepicker("show"); - equal(dp.find(".ui-datepicker-year").text(), genRange(curYear, 1), "Year range - -6:+2"); + inp.val( "02/04/2008" ).datepicker( "show" ); + equal(dp.find( ".ui-datepicker-year" ).text(), "2008", "Year range - read-only default" ); + inp.datepicker( "hide" ).datepicker( "option", {changeYear: true}).datepicker( "show" ); + equal(dp.find( ".ui-datepicker-year" ).text(), genRange(2008 - 10, 21), "Year range - changeable default" ); + inp.datepicker( "hide" ).datepicker( "option", {yearRange: "c-6:c+2", changeYear: true}).datepicker( "show" ); + equal(dp.find( ".ui-datepicker-year" ).text(), genRange(2008 - 6, 9), "Year range - c-6:c+2" ); + inp.datepicker( "hide" ).datepicker( "option", {yearRange: "2000:2010", changeYear: true}).datepicker( "show" ); + equal(dp.find( ".ui-datepicker-year" ).text(), genRange(2000, 11), "Year range - 2000:2010" ); + inp.datepicker( "hide" ).datepicker( "option", {yearRange: "-5:+3", changeYear: true}).datepicker( "show" ); + equal(dp.find( ".ui-datepicker-year" ).text(), genRange(curYear - 5, 9), "Year range - -5:+3" ); + inp.datepicker( "hide" ).datepicker( "option", {yearRange: "2000:-5", changeYear: true}).datepicker( "show" ); + equal(dp.find( ".ui-datepicker-year" ).text(), genRange(2000, curYear - 2004), "Year range - 2000:-5" ); + inp.datepicker( "hide" ).datepicker( "option", {yearRange: "", changeYear: true}).datepicker( "show" ); + equal(dp.find( ".ui-datepicker-year" ).text(), genRange(curYear, 1), "Year range - -6:+2" ); // Navigation as date format - inp.datepicker("option", {showButtonPanel: true}); - equal(dp.find(".ui-datepicker-prev").text(), "Prev", "Navigation prev - default"); - equal(dp.find(".ui-datepicker-current").text(), "Today", "Navigation current - default"); - equal(dp.find(".ui-datepicker-next").text(), "Next", "Navigation next - default"); - inp.datepicker("hide").datepicker("option", {navigationAsDateFormat: true, prevText: "< M", currentText: "MM", nextText: "M >"}). - val("02/04/2008").datepicker("show"); + inp.datepicker( "option", {showButtonPanel: true}); + equal(dp.find( ".ui-datepicker-prev" ).text(), "Prev", "Navigation prev - default" ); + equal(dp.find( ".ui-datepicker-current" ).text(), "Today", "Navigation current - default" ); + equal(dp.find( ".ui-datepicker-next" ).text(), "Next", "Navigation next - default" ); + inp.datepicker( "hide" ).datepicker( "option", {navigationAsDateFormat: true, prevText: "< M", currentText: "MM", nextText: "M >"}). + val( "02/04/2008" ).datepicker( "show" ); longNames = $.datepicker.regional[""].monthNames; shortNames = $.datepicker.regional[""].monthNamesShort; date = new Date(); - equal(dp.find(".ui-datepicker-prev").text(), "< " + shortNames[0], "Navigation prev - as date format"); - equal(dp.find(".ui-datepicker-current").text(), - longNames[date.getMonth()], "Navigation current - as date format"); - equal(dp.find(".ui-datepicker-next").text(), - shortNames[2] + " >", "Navigation next - as date format"); - inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); - equal(dp.find(".ui-datepicker-prev").text(), - "< " + shortNames[1], "Navigation prev - as date format + pgdn"); - equal(dp.find(".ui-datepicker-current").text(), - longNames[date.getMonth()], "Navigation current - as date format + pgdn"); - equal(dp.find(".ui-datepicker-next").text(), - shortNames[3] + " >", "Navigation next - as date format + pgdn"); - inp.datepicker("hide").datepicker("option", {gotoCurrent: true}). - val("02/04/2008").datepicker("show"); - equal(dp.find(".ui-datepicker-prev").text(), - "< " + shortNames[0], "Navigation prev - as date format + goto current"); - equal(dp.find(".ui-datepicker-current").text(), - longNames[1], "Navigation current - as date format + goto current"); - equal(dp.find(".ui-datepicker-next").text(), - shortNames[2] + " >", "Navigation next - as date format + goto current"); + equal(dp.find( ".ui-datepicker-prev" ).text(), "< " + shortNames[0], "Navigation prev - as date format" ); + equal(dp.find( ".ui-datepicker-current" ).text(), + longNames[date.getMonth()], "Navigation current - as date format" ); + equal(dp.find( ".ui-datepicker-next" ).text(), + shortNames[2] + " >", "Navigation next - as date format" ); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); + equal(dp.find( ".ui-datepicker-prev" ).text(), + "< " + shortNames[1], "Navigation prev - as date format + pgdn" ); + equal(dp.find( ".ui-datepicker-current" ).text(), + longNames[date.getMonth()], "Navigation current - as date format + pgdn" ); + equal(dp.find( ".ui-datepicker-next" ).text(), + shortNames[3] + " >", "Navigation next - as date format + pgdn" ); + inp.datepicker( "hide" ).datepicker( "option", {gotoCurrent: true}). + val( "02/04/2008" ).datepicker( "show" ); + equal(dp.find( ".ui-datepicker-prev" ).text(), + "< " + shortNames[0], "Navigation prev - as date format + goto current" ); + equal(dp.find( ".ui-datepicker-current" ).text(), + longNames[1], "Navigation current - as date format + goto current" ); + equal(dp.find( ".ui-datepicker-next" ).text(), + shortNames[2] + " >", "Navigation next - as date format + goto current" ); }); -test("minMax", function() { +test( "minMax", function() { expect( 23 ); var date, - inp = TestHelpers.datepicker.init("#inp"), - dp = $("#ui-datepicker-div"), + inp = TestHelpers.datepicker.init( "#inp" ), + dp = $( "#ui-datepicker-div" ), lastYear = new Date(2007, 6 - 1, 4), nextYear = new Date(2009, 6 - 1, 4), minDate = new Date(2008, 2 - 1, 29), maxDate = new Date(2008, 12 - 1, 7); - inp.val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), lastYear, - "Min/max - null, null - ctrl+pgup"); - inp.val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), nextYear, - "Min/max - null, null - ctrl+pgdn"); - inp.datepicker("option", {minDate: minDate}). - datepicker("hide").val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, - "Min/max - 02/29/2008, null - ctrl+pgup"); - inp.val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), nextYear, - "Min/max - 02/29/2008, null - ctrl+pgdn"); - inp.datepicker("option", {maxDate: maxDate}). - datepicker("hide").val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, - "Min/max - 02/29/2008, 12/07/2008 - ctrl+pgup"); - inp.val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, - "Min/max - 02/29/2008, 12/07/2008 - ctrl+pgdn"); - inp.datepicker("option", {minDate: null}). - datepicker("hide").val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), lastYear, - "Min/max - null, 12/07/2008 - ctrl+pgup"); - inp.val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, - "Min/max - null, 12/07/2008 - ctrl+pgdn"); + inp.val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), lastYear, + "Min/max - null, null - ctrl+pgup" ); + inp.val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), nextYear, + "Min/max - null, null - ctrl+pgdn" ); + inp.datepicker( "option", {minDate: minDate}). + datepicker( "hide" ).val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), minDate, + "Min/max - 02/29/2008, null - ctrl+pgup" ); + inp.val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), nextYear, + "Min/max - 02/29/2008, null - ctrl+pgdn" ); + inp.datepicker( "option", {maxDate: maxDate}). + datepicker( "hide" ).val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), minDate, + "Min/max - 02/29/2008, 12/07/2008 - ctrl+pgup" ); + inp.val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), maxDate, + "Min/max - 02/29/2008, 12/07/2008 - ctrl+pgdn" ); + inp.datepicker( "option", {minDate: null}). + datepicker( "hide" ).val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), lastYear, + "Min/max - null, 12/07/2008 - ctrl+pgup" ); + inp.val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), maxDate, + "Min/max - null, 12/07/2008 - ctrl+pgdn" ); // Relative dates date = new Date(); date.setDate(date.getDate() - 7); - inp.datepicker("option", {minDate: "-1w", maxDate: "+1 M +10 D "}). - datepicker("hide").val("").datepicker("show"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, - "Min/max - -1w, +1 M +10 D - ctrl+pgup"); + inp.datepicker( "option", {minDate: "-1w", maxDate: "+1 M +10 D "}). + datepicker( "hide" ).val( "" ).datepicker( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, + "Min/max - -1w, +1 M +10 D - ctrl+pgup" ); date = TestHelpers.datepicker.addMonths(new Date(), 1); date.setDate(date.getDate() + 10); - inp.val("").datepicker("show"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date, - "Min/max - -1w, +1 M +10 D - ctrl+pgdn"); + inp.val( "" ).datepicker( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date, + "Min/max - -1w, +1 M +10 D - ctrl+pgdn" ); // With existing date - inp = TestHelpers.datepicker.init("#inp"); - inp.val("06/04/2008").datepicker("option", {minDate: minDate}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 6 - 1, 4), "Min/max - setDate > min"); - inp.datepicker("option", {minDate: null}).val("01/04/2008").datepicker("option", {minDate: minDate}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, "Min/max - setDate < min"); - inp.datepicker("option", {minDate: null}).val("06/04/2008").datepicker("option", {maxDate: maxDate}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 6 - 1, 4), "Min/max - setDate < max"); - inp.datepicker("option", {maxDate: null}).val("01/04/2009").datepicker("option", {maxDate: maxDate}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, "Min/max - setDate > max"); - inp.datepicker("option", {maxDate: null}).val("01/04/2008").datepicker("option", {minDate: minDate, maxDate: maxDate}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, "Min/max - setDate < min"); - inp.datepicker("option", {maxDate: null}).val("06/04/2008").datepicker("option", {minDate: minDate, maxDate: maxDate}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), new Date(2008, 6 - 1, 4), "Min/max - setDate > min, < max"); - inp.datepicker("option", {maxDate: null}).val("01/04/2009").datepicker("option", {minDate: minDate, maxDate: maxDate}); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, "Min/max - setDate > max"); - - inp.datepicker("option", {yearRange: "-0:+1"}).val("01/01/" + new Date().getFullYear()); - ok(dp.find(".ui-datepicker-prev").hasClass("ui-state-disabled"), "Year Range Test - previous button disabled at 1/1/minYear"); - inp.datepicker("setDate", "12/30/" + new Date().getFullYear()); - ok(dp.find(".ui-datepicker-next").hasClass("ui-state-disabled"), "Year Range Test - next button disabled at 12/30/maxYear"); - - inp.datepicker("option", { + inp = TestHelpers.datepicker.init( "#inp" ); + inp.val( "06/04/2008" ).datepicker( "option", {minDate: minDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), new Date(2008, 6 - 1, 4), "Min/max - setDate > min" ); + inp.datepicker( "option", {minDate: null}).val( "01/04/2008" ).datepicker( "option", {minDate: minDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), minDate, "Min/max - setDate < min" ); + inp.datepicker( "option", {minDate: null}).val( "06/04/2008" ).datepicker( "option", {maxDate: maxDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), new Date(2008, 6 - 1, 4), "Min/max - setDate < max" ); + inp.datepicker( "option", {maxDate: null}).val( "01/04/2009" ).datepicker( "option", {maxDate: maxDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), maxDate, "Min/max - setDate > max" ); + inp.datepicker( "option", {maxDate: null}).val( "01/04/2008" ).datepicker( "option", {minDate: minDate, maxDate: maxDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), minDate, "Min/max - setDate < min" ); + inp.datepicker( "option", {maxDate: null}).val( "06/04/2008" ).datepicker( "option", {minDate: minDate, maxDate: maxDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), new Date(2008, 6 - 1, 4), "Min/max - setDate > min, < max" ); + inp.datepicker( "option", {maxDate: null}).val( "01/04/2009" ).datepicker( "option", {minDate: minDate, maxDate: maxDate}); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), maxDate, "Min/max - setDate > max" ); + + inp.datepicker( "option", {yearRange: "-0:+1"}).val( "01/01/" + new Date().getFullYear()); + ok(dp.find( ".ui-datepicker-prev" ).hasClass( "ui-state-disabled" ), "Year Range Test - previous button disabled at 1/1/minYear" ); + inp.datepicker( "setDate", "12/30/" + new Date().getFullYear()); + ok(dp.find( ".ui-datepicker-next" ).hasClass( "ui-state-disabled" ), "Year Range Test - next button disabled at 12/30/maxYear" ); + + inp.datepicker( "option", { minDate: new Date(1900, 0, 1), maxDate: "-6Y", yearRange: "1900:-6" }).val( "" ); - ok(dp.find(".ui-datepicker-next").hasClass("ui-state-disabled"), "Year Range Test - next button disabled"); - ok(!dp.find(".ui-datepicker-prev").hasClass("ui-state-disabled"), "Year Range Test - prev button enabled"); + ok(dp.find( ".ui-datepicker-next" ).hasClass( "ui-state-disabled" ), "Year Range Test - next button disabled" ); + ok(!dp.find( ".ui-datepicker-prev" ).hasClass( "ui-state-disabled" ), "Year Range Test - prev button enabled" ); - inp.datepicker("option", { + inp.datepicker( "option", { minDate: new Date(1900, 0, 1), maxDate: "1/25/2007", yearRange: "1900:2007" }).val( "" ); - ok(dp.find(".ui-datepicker-next").hasClass("ui-state-disabled"), "Year Range Test - next button disabled"); - ok(!dp.find(".ui-datepicker-prev").hasClass("ui-state-disabled"), "Year Range Test - prev button enabled"); + ok(dp.find( ".ui-datepicker-next" ).hasClass( "ui-state-disabled" ), "Year Range Test - next button disabled" ); + ok(!dp.find( ".ui-datepicker-prev" ).hasClass( "ui-state-disabled" ), "Year Range Test - prev button enabled" ); }); -test("setDate", function() { +test( "setDate", function() { expect( 24 ); var inl, alt, minDate, maxDate, dateAndTimeToSet, dateAndTimeClone, - inp = TestHelpers.datepicker.init("#inp"), + inp = TestHelpers.datepicker.init( "#inp" ), date1 = new Date(2008, 6 - 1, 4), date2 = new Date(); - ok(inp.datepicker("getDate") == null, "Set date - default"); - inp.datepicker("setDate", date1); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - 2008-06-04"); + ok(inp.datepicker( "getDate" ) == null, "Set date - default" ); + inp.datepicker( "setDate", date1); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date1, "Set date - 2008-06-04" ); date1 = new Date(); date1.setDate(date1.getDate() + 7); - inp.datepicker("setDate", +7); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - +7"); + inp.datepicker( "setDate", +7); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date1, "Set date - +7" ); date2.setFullYear(date2.getFullYear() + 2); - inp.datepicker("setDate", "+2y"); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date2, "Set date - +2y"); - inp.datepicker("setDate", date1, date2); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - two dates"); - inp.datepicker("setDate"); - ok(inp.datepicker("getDate") == null, "Set date - null"); + inp.datepicker( "setDate", "+2y" ); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date2, "Set date - +2y" ); + inp.datepicker( "setDate", date1, date2); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date1, "Set date - two dates" ); + inp.datepicker( "setDate" ); + ok(inp.datepicker( "getDate" ) == null, "Set date - null" ); // Relative to current date date1 = new Date(); date1.setDate(date1.getDate() + 7); - inp.datepicker("setDate", "c +7"); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - c +7"); + inp.datepicker( "setDate", "c +7" ); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date1, "Set date - c +7" ); date1.setDate(date1.getDate() + 7); - inp.datepicker("setDate", "c+7"); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - c+7"); + inp.datepicker( "setDate", "c+7" ); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date1, "Set date - c+7" ); date1.setDate(date1.getDate() - 21); - inp.datepicker("setDate", "c -3 w"); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date - c -3 w"); + inp.datepicker( "setDate", "c -3 w" ); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date1, "Set date - c -3 w" ); // Inline - inl = TestHelpers.datepicker.init("#inl"); + inl = TestHelpers.datepicker.init( "#inl" ); date1 = new Date(2008, 6 - 1, 4); date2 = new Date(); - TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date2, "Set date inline - default"); - inl.datepicker("setDate", date1); - TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date1, "Set date inline - 2008-06-04"); + TestHelpers.datepicker.equalsDate(inl.datepicker( "getDate" ), date2, "Set date inline - default" ); + inl.datepicker( "setDate", date1); + TestHelpers.datepicker.equalsDate(inl.datepicker( "getDate" ), date1, "Set date inline - 2008-06-04" ); date1 = new Date(); date1.setDate(date1.getDate() + 7); - inl.datepicker("setDate", +7); - TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date1, "Set date inline - +7"); + inl.datepicker( "setDate", +7); + TestHelpers.datepicker.equalsDate(inl.datepicker( "getDate" ), date1, "Set date inline - +7" ); date2.setFullYear(date2.getFullYear() + 2); - inl.datepicker("setDate", "+2y"); - TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date2, "Set date inline - +2y"); - inl.datepicker("setDate", date1, date2); - TestHelpers.datepicker.equalsDate(inl.datepicker("getDate"), date1, "Set date inline - two dates"); - inl.datepicker("setDate"); - ok(inl.datepicker("getDate") == null, "Set date inline - null"); + inl.datepicker( "setDate", "+2y" ); + TestHelpers.datepicker.equalsDate(inl.datepicker( "getDate" ), date2, "Set date inline - +2y" ); + inl.datepicker( "setDate", date1, date2); + TestHelpers.datepicker.equalsDate(inl.datepicker( "getDate" ), date1, "Set date inline - two dates" ); + inl.datepicker( "setDate" ); + ok(inl.datepicker( "getDate" ) == null, "Set date inline - null" ); // Alternate field - alt = $("#alt"); - inp.datepicker("option", {altField: "#alt", altFormat: "yy-mm-dd"}); + alt = $( "#alt" ); + inp.datepicker( "option", {altField: "#alt", altFormat: "yy-mm-dd"}); date1 = new Date(2008, 6 - 1, 4); - inp.datepicker("setDate", date1); - equal(inp.val(), "06/04/2008", "Set date alternate - 06/04/2008"); - equal(alt.val(), "2008-06-04", "Set date alternate - 2008-06-04"); + inp.datepicker( "setDate", date1); + equal(inp.val(), "06/04/2008", "Set date alternate - 06/04/2008" ); + equal(alt.val(), "2008-06-04", "Set date alternate - 2008-06-04" ); // With minimum/maximum - inp = TestHelpers.datepicker.init("#inp"); + inp = TestHelpers.datepicker.init( "#inp" ); date1 = new Date(2008, 1 - 1, 4); date2 = new Date(2008, 6 - 1, 4); minDate = new Date(2008, 2 - 1, 29); maxDate = new Date(2008, 3 - 1, 28); - inp.val("").datepicker("option", {minDate: minDate}).datepicker("setDate", date2); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date2, "Set date min/max - setDate > min"); - inp.datepicker("setDate", date1); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, "Set date min/max - setDate < min"); - inp.val("").datepicker("option", {maxDate: maxDate, minDate: null}).datepicker("setDate", date1); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), date1, "Set date min/max - setDate < max"); - inp.datepicker("setDate", date2); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, "Set date min/max - setDate > max"); - inp.val("").datepicker("option", {minDate: minDate}).datepicker("setDate", date1); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), minDate, "Set date min/max - setDate < min"); - inp.datepicker("setDate", date2); - TestHelpers.datepicker.equalsDate(inp.datepicker("getDate"), maxDate, "Set date min/max - setDate > max"); + inp.val( "" ).datepicker( "option", {minDate: minDate}).datepicker( "setDate", date2); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date2, "Set date min/max - setDate > min" ); + inp.datepicker( "setDate", date1); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), minDate, "Set date min/max - setDate < min" ); + inp.val( "" ).datepicker( "option", {maxDate: maxDate, minDate: null}).datepicker( "setDate", date1); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), date1, "Set date min/max - setDate < max" ); + inp.datepicker( "setDate", date2); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), maxDate, "Set date min/max - setDate > max" ); + inp.val( "" ).datepicker( "option", {minDate: minDate}).datepicker( "setDate", date1); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), minDate, "Set date min/max - setDate < min" ); + inp.datepicker( "setDate", date2); + TestHelpers.datepicker.equalsDate(inp.datepicker( "getDate" ), maxDate, "Set date min/max - setDate > max" ); dateAndTimeToSet = new Date(2008, 3 - 1, 28, 1, 11, 0); dateAndTimeClone = new Date(2008, 3 - 1, 28, 1, 11, 0); - inp.datepicker("setDate", dateAndTimeToSet); - equal(dateAndTimeToSet.getTime(), dateAndTimeClone.getTime(), "Date object passed should not be changed by setDate"); + inp.datepicker( "setDate", dateAndTimeToSet); + equal(dateAndTimeToSet.getTime(), dateAndTimeClone.getTime(), "Date object passed should not be changed by setDate" ); }); -test("altField", function() { +test( "altField", function() { expect( 10 ); - var inp = TestHelpers.datepicker.init("#inp"), - alt = $("#alt"); + var inp = TestHelpers.datepicker.init( "#inp" ), + alt = $( "#alt" ); // No alternate field set - alt.val(""); - inp.val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - equal(inp.val(), "06/04/2008", "Alt field - dp - enter"); - equal(alt.val(), "", "Alt field - alt not set"); + alt.val( "" ); + inp.val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(inp.val(), "06/04/2008", "Alt field - dp - enter" ); + equal(alt.val(), "", "Alt field - alt not set" ); // Alternate field set - alt.val(""); - inp.datepicker("option", {altField: "#alt", altFormat: "yy-mm-dd"}). - val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - equal(inp.val(), "06/04/2008", "Alt field - dp - enter"); - equal(alt.val(), "2008-06-04", "Alt field - alt - enter"); + alt.val( "" ); + inp.datepicker( "option", {altField: "#alt", altFormat: "yy-mm-dd"}). + val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(inp.val(), "06/04/2008", "Alt field - dp - enter" ); + equal(alt.val(), "2008-06-04", "Alt field - alt - enter" ); // Move from initial date - alt.val(""); - inp.val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - equal(inp.val(), "07/04/2008", "Alt field - dp - pgdn"); - equal(alt.val(), "2008-07-04", "Alt field - alt - pgdn"); + alt.val( "" ); + inp.val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(inp.val(), "07/04/2008", "Alt field - dp - pgdn" ); + equal(alt.val(), "2008-07-04", "Alt field - alt - pgdn" ); // Alternate field set - closed - alt.val(""); - inp.val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). - simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); - equal(inp.val(), "06/04/2008", "Alt field - dp - pgdn/esc"); - equal(alt.val(), "", "Alt field - alt - pgdn/esc"); + alt.val( "" ); + inp.val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); + equal(inp.val(), "06/04/2008", "Alt field - dp - pgdn/esc" ); + equal(alt.val(), "", "Alt field - alt - pgdn/esc" ); // Clear date and alternate - alt.val(""); - inp.val("06/04/2008").datepicker("show"); - inp.simulate("keydown", {ctrlKey: true, keyCode: $.ui.keyCode.END}); - equal(inp.val(), "", "Alt field - dp - ctrl+end"); - equal(alt.val(), "", "Alt field - alt - ctrl+end"); + alt.val( "" ); + inp.val( "06/04/2008" ).datepicker( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.END}); + equal(inp.val(), "", "Alt field - dp - ctrl+end" ); + equal(alt.val(), "", "Alt field - alt - ctrl+end" ); }); -test("autoSize", function() { +test( "autoSize", function() { expect( 15 ); - var inp = TestHelpers.datepicker.init("#inp"); - equal(inp.prop("size"), 20, "Auto size - default"); - inp.datepicker("option", "autoSize", true); - equal(inp.prop("size"), 10, "Auto size - mm/dd/yy"); - inp.datepicker("option", "dateFormat", "m/d/yy"); - equal(inp.prop("size"), 10, "Auto size - m/d/yy"); - inp.datepicker("option", "dateFormat", "D M d yy"); - equal(inp.prop("size"), 15, "Auto size - D M d yy"); - inp.datepicker("option", "dateFormat", "DD, MM dd, yy"); - equal(inp.prop("size"), 29, "Auto size - DD, MM dd, yy"); + var inp = TestHelpers.datepicker.init( "#inp" ); + equal(inp.prop( "size" ), 20, "Auto size - default" ); + inp.datepicker( "option", "autoSize", true); + equal(inp.prop( "size" ), 10, "Auto size - mm/dd/yy" ); + inp.datepicker( "option", "dateFormat", "m/d/yy" ); + equal(inp.prop( "size" ), 10, "Auto size - m/d/yy" ); + inp.datepicker( "option", "dateFormat", "D M d yy" ); + equal(inp.prop( "size" ), 15, "Auto size - D M d yy" ); + inp.datepicker( "option", "dateFormat", "DD, MM dd, yy" ); + equal(inp.prop( "size" ), 29, "Auto size - DD, MM dd, yy" ); // French - inp.datepicker("option", $.extend({autoSize: false}, $.datepicker.regional.fr)); - equal(inp.prop("size"), 29, "Auto size - fr - default"); - inp.datepicker("option", "autoSize", true); - equal(inp.prop("size"), 10, "Auto size - fr - dd/mm/yy"); - inp.datepicker("option", "dateFormat", "m/d/yy"); - equal(inp.prop("size"), 10, "Auto size - fr - m/d/yy"); - inp.datepicker("option", "dateFormat", "D M d yy"); - equal(inp.prop("size"), 18, "Auto size - fr - D M d yy"); - inp.datepicker("option", "dateFormat", "DD, MM dd, yy"); - equal(inp.prop("size"), 28, "Auto size - fr - DD, MM dd, yy"); + inp.datepicker( "option", $.extend({autoSize: false}, $.datepicker.regional.fr)); + equal(inp.prop( "size" ), 29, "Auto size - fr - default" ); + inp.datepicker( "option", "autoSize", true); + equal(inp.prop( "size" ), 10, "Auto size - fr - dd/mm/yy" ); + inp.datepicker( "option", "dateFormat", "m/d/yy" ); + equal(inp.prop( "size" ), 10, "Auto size - fr - m/d/yy" ); + inp.datepicker( "option", "dateFormat", "D M d yy" ); + equal(inp.prop( "size" ), 18, "Auto size - fr - D M d yy" ); + inp.datepicker( "option", "dateFormat", "DD, MM dd, yy" ); + equal(inp.prop( "size" ), 28, "Auto size - fr - DD, MM dd, yy" ); // Hebrew - inp.datepicker("option", $.extend({autoSize: false}, $.datepicker.regional.he)); - equal(inp.prop("size"), 28, "Auto size - he - default"); - inp.datepicker("option", "autoSize", true); - equal(inp.prop("size"), 10, "Auto size - he - dd/mm/yy"); - inp.datepicker("option", "dateFormat", "m/d/yy"); - equal(inp.prop("size"), 10, "Auto size - he - m/d/yy"); - inp.datepicker("option", "dateFormat", "D M d yy"); - equal(inp.prop("size"), 16, "Auto size - he - D M d yy"); - inp.datepicker("option", "dateFormat", "DD, MM dd, yy"); - equal(inp.prop("size"), 23, "Auto size - he - DD, MM dd, yy"); + inp.datepicker( "option", $.extend({autoSize: false}, $.datepicker.regional.he)); + equal(inp.prop( "size" ), 28, "Auto size - he - default" ); + inp.datepicker( "option", "autoSize", true); + equal(inp.prop( "size" ), 10, "Auto size - he - dd/mm/yy" ); + inp.datepicker( "option", "dateFormat", "m/d/yy" ); + equal(inp.prop( "size" ), 10, "Auto size - he - m/d/yy" ); + inp.datepicker( "option", "dateFormat", "D M d yy" ); + equal(inp.prop( "size" ), 16, "Auto size - he - D M d yy" ); + inp.datepicker( "option", "dateFormat", "DD, MM dd, yy" ); + equal(inp.prop( "size" ), 23, "Auto size - he - DD, MM dd, yy" ); }); -test("daylightSaving", function() { +test( "daylightSaving", function() { expect( 25 ); - var inp = TestHelpers.datepicker.init("#inp"), - dp = $("#ui-datepicker-div"); + var inp = TestHelpers.datepicker.init( "#inp" ), + dp = $( "#ui-datepicker-div" ); ok(true, "Daylight saving - " + new Date()); // Australia, Sydney - AM change, southern hemisphere - inp.val("04/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(6) a", dp).simulate("click"); - equal(inp.val(), "04/05/2008", "Daylight saving - Australia 04/05/2008"); - inp.val("04/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(7) a", dp).simulate("click"); - equal(inp.val(), "04/06/2008", "Daylight saving - Australia 04/06/2008"); - inp.val("04/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(8) a", dp).simulate("click"); - equal(inp.val(), "04/07/2008", "Daylight saving - Australia 04/07/2008"); - inp.val("10/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(6) a", dp).simulate("click"); - equal(inp.val(), "10/04/2008", "Daylight saving - Australia 10/04/2008"); - inp.val("10/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(7) a", dp).simulate("click"); - equal(inp.val(), "10/05/2008", "Daylight saving - Australia 10/05/2008"); - inp.val("10/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(8) a", dp).simulate("click"); - equal(inp.val(), "10/06/2008", "Daylight saving - Australia 10/06/2008"); + inp.val( "04/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(6) a", dp).simulate( "click" ); + equal(inp.val(), "04/05/2008", "Daylight saving - Australia 04/05/2008" ); + inp.val( "04/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(7) a", dp).simulate( "click" ); + equal(inp.val(), "04/06/2008", "Daylight saving - Australia 04/06/2008" ); + inp.val( "04/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(8) a", dp).simulate( "click" ); + equal(inp.val(), "04/07/2008", "Daylight saving - Australia 04/07/2008" ); + inp.val( "10/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(6) a", dp).simulate( "click" ); + equal(inp.val(), "10/04/2008", "Daylight saving - Australia 10/04/2008" ); + inp.val( "10/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(7) a", dp).simulate( "click" ); + equal(inp.val(), "10/05/2008", "Daylight saving - Australia 10/05/2008" ); + inp.val( "10/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(8) a", dp).simulate( "click" ); + equal(inp.val(), "10/06/2008", "Daylight saving - Australia 10/06/2008" ); // Brasil, Brasilia - midnight change, southern hemisphere - inp.val("02/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(20) a", dp).simulate("click"); - equal(inp.val(), "02/16/2008", "Daylight saving - Brasil 02/16/2008"); - inp.val("02/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(21) a", dp).simulate("click"); - equal(inp.val(), "02/17/2008", "Daylight saving - Brasil 02/17/2008"); - inp.val("02/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(22) a", dp).simulate("click"); - equal(inp.val(), "02/18/2008", "Daylight saving - Brasil 02/18/2008"); - inp.val("10/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(13) a", dp).simulate("click"); - equal(inp.val(), "10/11/2008", "Daylight saving - Brasil 10/11/2008"); - inp.val("10/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(14) a", dp).simulate("click"); - equal(inp.val(), "10/12/2008", "Daylight saving - Brasil 10/12/2008"); - inp.val("10/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(15) a", dp).simulate("click"); - equal(inp.val(), "10/13/2008", "Daylight saving - Brasil 10/13/2008"); + inp.val( "02/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(20) a", dp).simulate( "click" ); + equal(inp.val(), "02/16/2008", "Daylight saving - Brasil 02/16/2008" ); + inp.val( "02/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(21) a", dp).simulate( "click" ); + equal(inp.val(), "02/17/2008", "Daylight saving - Brasil 02/17/2008" ); + inp.val( "02/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(22) a", dp).simulate( "click" ); + equal(inp.val(), "02/18/2008", "Daylight saving - Brasil 02/18/2008" ); + inp.val( "10/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(13) a", dp).simulate( "click" ); + equal(inp.val(), "10/11/2008", "Daylight saving - Brasil 10/11/2008" ); + inp.val( "10/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(14) a", dp).simulate( "click" ); + equal(inp.val(), "10/12/2008", "Daylight saving - Brasil 10/12/2008" ); + inp.val( "10/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(15) a", dp).simulate( "click" ); + equal(inp.val(), "10/13/2008", "Daylight saving - Brasil 10/13/2008" ); // Lebanon, Beirut - midnight change, northern hemisphere - inp.val("03/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(34) a", dp).simulate("click"); - equal(inp.val(), "03/29/2008", "Daylight saving - Lebanon 03/29/2008"); - inp.val("03/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(35) a", dp).simulate("click"); - equal(inp.val(), "03/30/2008", "Daylight saving - Lebanon 03/30/2008"); - inp.val("03/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(36) a", dp).simulate("click"); - equal(inp.val(), "03/31/2008", "Daylight saving - Lebanon 03/31/2008"); - inp.val("10/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(27) a", dp).simulate("click"); - equal(inp.val(), "10/25/2008", "Daylight saving - Lebanon 10/25/2008"); - inp.val("10/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(28) a", dp).simulate("click"); - equal(inp.val(), "10/26/2008", "Daylight saving - Lebanon 10/26/2008"); - inp.val("10/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(29) a", dp).simulate("click"); - equal(inp.val(), "10/27/2008", "Daylight saving - Lebanon 10/27/2008"); + inp.val( "03/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(34) a", dp).simulate( "click" ); + equal(inp.val(), "03/29/2008", "Daylight saving - Lebanon 03/29/2008" ); + inp.val( "03/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(35) a", dp).simulate( "click" ); + equal(inp.val(), "03/30/2008", "Daylight saving - Lebanon 03/30/2008" ); + inp.val( "03/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(36) a", dp).simulate( "click" ); + equal(inp.val(), "03/31/2008", "Daylight saving - Lebanon 03/31/2008" ); + inp.val( "10/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(27) a", dp).simulate( "click" ); + equal(inp.val(), "10/25/2008", "Daylight saving - Lebanon 10/25/2008" ); + inp.val( "10/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(28) a", dp).simulate( "click" ); + equal(inp.val(), "10/26/2008", "Daylight saving - Lebanon 10/26/2008" ); + inp.val( "10/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(29) a", dp).simulate( "click" ); + equal(inp.val(), "10/27/2008", "Daylight saving - Lebanon 10/27/2008" ); // US, Eastern - AM change, northern hemisphere - inp.val("03/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(13) a", dp).simulate("click"); - equal(inp.val(), "03/08/2008", "Daylight saving - US 03/08/2008"); - inp.val("03/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(14) a", dp).simulate("click"); - equal(inp.val(), "03/09/2008", "Daylight saving - US 03/09/2008"); - inp.val("03/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(15) a", dp).simulate("click"); - equal(inp.val(), "03/10/2008", "Daylight saving - US 03/10/2008"); - inp.val("11/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(6) a", dp).simulate("click"); - equal(inp.val(), "11/01/2008", "Daylight saving - US 11/01/2008"); - inp.val("11/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(7) a", dp).simulate("click"); - equal(inp.val(), "11/02/2008", "Daylight saving - US 11/02/2008"); - inp.val("11/01/2008").datepicker("show"); - $(".ui-datepicker-calendar td:eq(8) a", dp).simulate("click"); - equal(inp.val(), "11/03/2008", "Daylight saving - US 11/03/2008"); + inp.val( "03/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(13) a", dp).simulate( "click" ); + equal(inp.val(), "03/08/2008", "Daylight saving - US 03/08/2008" ); + inp.val( "03/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(14) a", dp).simulate( "click" ); + equal(inp.val(), "03/09/2008", "Daylight saving - US 03/09/2008" ); + inp.val( "03/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(15) a", dp).simulate( "click" ); + equal(inp.val(), "03/10/2008", "Daylight saving - US 03/10/2008" ); + inp.val( "11/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(6) a", dp).simulate( "click" ); + equal(inp.val(), "11/01/2008", "Daylight saving - US 11/01/2008" ); + inp.val( "11/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(7) a", dp).simulate( "click" ); + equal(inp.val(), "11/02/2008", "Daylight saving - US 11/02/2008" ); + inp.val( "11/01/2008" ).datepicker( "show" ); + $( ".ui-datepicker-calendar td:eq(8) a", dp).simulate( "click" ); + equal(inp.val(), "11/03/2008", "Daylight saving - US 11/03/2008" ); }); var beforeShowThis = null, @@ -928,86 +928,86 @@ function beforeDay(date) { beforeShowDayThis = this; beforeShowDayOK &= (date > new Date(2008, 1 - 1, 26) && date < new Date(2008, 3 - 1, 6)); - return [(date.getDate() % 2 === 0), (date.getDate() % 10 === 0 ? "day10" : ""), - (date.getDate() % 3 === 0 ? "Divisble by 3" : "")]; + return [(date.getDate() % 2 === 0), (date.getDate() % 10 === 0 ? "day10" : "" ), + (date.getDate() % 3 === 0 ? "Divisble by 3" : "" )]; } -test("callbacks", function() { +test( "callbacks", function() { expect( 13 ); // Before show var dp, day20, day21, - inp = TestHelpers.datepicker.init("#inp", {beforeShow: beforeAll}), - inst = $.data(inp[0], "datepicker"); - equal($.datepicker._get(inst, "currentText"), "Today", "Before show - initial"); - inp.val("02/04/2008").datepicker("show"); - equal($.datepicker._get(inst, "currentText"), "Current", "Before show - changed"); - ok(beforeShowThis.id === inp[0].id, "Before show - this OK"); - ok(beforeShowInput.id === inp[0].id, "Before show - input OK"); - deepEqual(beforeShowInst, inst, "Before show - inst OK"); - inp.datepicker("hide").datepicker("destroy"); + inp = TestHelpers.datepicker.init( "#inp", {beforeShow: beforeAll}), + inst = $.data(inp[0], "datepicker" ); + equal($.datepicker._get(inst, "currentText" ), "Today", "Before show - initial" ); + inp.val( "02/04/2008" ).datepicker( "show" ); + equal($.datepicker._get(inst, "currentText" ), "Current", "Before show - changed" ); + ok(beforeShowThis.id === inp[0].id, "Before show - this OK" ); + ok(beforeShowInput.id === inp[0].id, "Before show - input OK" ); + deepEqual(beforeShowInst, inst, "Before show - inst OK" ); + inp.datepicker( "hide" ).datepicker( "destroy" ); // Before show day - inp = TestHelpers.datepicker.init("#inp", {beforeShowDay: beforeDay}); - dp = $("#ui-datepicker-div"); - inp.val("02/04/2008").datepicker("show"); - ok(beforeShowDayThis.id === inp[0].id, "Before show day - this OK"); - ok(beforeShowDayOK, "Before show day - dates OK"); - day20 = dp.find(".ui-datepicker-calendar td:contains('20')"); - day21 = dp.find(".ui-datepicker-calendar td:contains('21')"); - ok(!day20.is(".ui-datepicker-unselectable"), "Before show day - unselectable 20"); - ok(day21.is(".ui-datepicker-unselectable"), "Before show day - unselectable 21"); - ok(day20.is(".day10"), "Before show day - CSS 20"); - ok(!day21.is(".day10"), "Before show day - CSS 21"); - ok(!day20.attr("title"), "Before show day - title 20"); - ok(day21.attr("title") === "Divisble by 3", "Before show day - title 21"); - inp.datepicker("hide").datepicker("destroy"); + inp = TestHelpers.datepicker.init( "#inp", {beforeShowDay: beforeDay}); + dp = $( "#ui-datepicker-div" ); + inp.val( "02/04/2008" ).datepicker( "show" ); + ok(beforeShowDayThis.id === inp[0].id, "Before show day - this OK" ); + ok(beforeShowDayOK, "Before show day - dates OK" ); + day20 = dp.find( ".ui-datepicker-calendar td:contains('20')" ); + day21 = dp.find( ".ui-datepicker-calendar td:contains('21')" ); + ok(!day20.is( ".ui-datepicker-unselectable" ), "Before show day - unselectable 20" ); + ok(day21.is( ".ui-datepicker-unselectable" ), "Before show day - unselectable 21" ); + ok(day20.is( ".day10" ), "Before show day - CSS 20" ); + ok(!day21.is( ".day10" ), "Before show day - CSS 21" ); + ok(!day20.attr( "title" ), "Before show day - title 20" ); + ok(day21.attr( "title" ) === "Divisble by 3", "Before show day - title 21" ); + inp.datepicker( "hide" ).datepicker( "destroy" ); }); -test("beforeShowDay - tooltips with quotes", function() { +test( "beforeShowDay - tooltips with quotes", function() { expect( 1 ); var inp, dp; - inp = TestHelpers.datepicker.init("#inp", { + inp = TestHelpers.datepicker.init( "#inp", { beforeShowDay: function() { return [ true, "", "'" ]; } }); - dp = $("#ui-datepicker-div"); + dp = $( "#ui-datepicker-div" ); - inp.datepicker("show"); - equal( dp.find( ".ui-datepicker-calendar td:contains('9')").attr( "title" ), "'" ); - inp.datepicker("hide").datepicker("destroy"); + inp.datepicker( "show" ); + equal( dp.find( ".ui-datepicker-calendar td:contains('9')" ).attr( "title" ), "'" ); + inp.datepicker( "hide" ).datepicker( "destroy" ); }); -test("localisation", function() { +test( "localisation", function() { expect( 24 ); var dp, month, day, date, - inp = TestHelpers.datepicker.init("#inp", $.datepicker.regional.fr); - inp.datepicker("option", {dateFormat: "DD, d MM yy", showButtonPanel:true, changeMonth:true, changeYear:true}).val("").datepicker("show"); - dp = $("#ui-datepicker-div"); - equal($(".ui-datepicker-close", dp).text(), "Fermer", "Localisation - close"); - $(".ui-datepicker-close", dp).simulate("mouseover"); - equal($(".ui-datepicker-prev", dp).text(), "Précédent", "Localisation - previous"); - equal($(".ui-datepicker-current", dp).text(), "Aujourd'hui", "Localisation - current"); - equal($(".ui-datepicker-next", dp).text(), "Suivant", "Localisation - next"); + inp = TestHelpers.datepicker.init( "#inp", $.datepicker.regional.fr); + inp.datepicker( "option", {dateFormat: "DD, d MM yy", showButtonPanel:true, changeMonth:true, changeYear:true}).val( "" ).datepicker( "show" ); + dp = $( "#ui-datepicker-div" ); + equal($( ".ui-datepicker-close", dp).text(), "Fermer", "Localisation - close" ); + $( ".ui-datepicker-close", dp).simulate( "mouseover" ); + equal($( ".ui-datepicker-prev", dp).text(), "Précédent", "Localisation - previous" ); + equal($( ".ui-datepicker-current", dp).text(), "Aujourd'hui", "Localisation - current" ); + equal($( ".ui-datepicker-next", dp).text(), "Suivant", "Localisation - next" ); month = 0; - $(".ui-datepicker-month option", dp).each(function() { + $( ".ui-datepicker-month option", dp).each(function() { equal($(this).text(), $.datepicker.regional.fr.monthNamesShort[month], "Localisation - month " + month); month++; }); day = 1; - $(".ui-datepicker-calendar th", dp).each(function() { + $( ".ui-datepicker-calendar th", dp).each(function() { equal($(this).text(), $.datepicker.regional.fr.dayNamesMin[day], "Localisation - day " + day); day = (day + 1) % 7; }); - inp.simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); date = new Date(); equal(inp.val(), $.datepicker.regional.fr.dayNames[date.getDay()] + ", " + date.getDate() + " " + $.datepicker.regional.fr.monthNames[date.getMonth()] + - " " + date.getFullYear(), "Localisation - formatting"); + " " + date.getFullYear(), "Localisation - formatting" ); }); -test("noWeekends", function() { +test( "noWeekends", function() { expect( 31 ); var i, date; for (i = 1; i <= 31; i++) { @@ -1017,7 +1017,7 @@ test("noWeekends", function() { } }); -test("iso8601Week", function() { +test( "iso8601Week", function() { expect( 12 ); var date = new Date(2000, 12 - 1, 31); equal($.datepicker.iso8601Week(date), 52, "ISO 8601 week " + date); @@ -1045,74 +1045,74 @@ test("iso8601Week", function() { equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); }); -test("parseDate", function() { +test( "parseDate", function() { expect( 26 ); - TestHelpers.datepicker.init("#inp"); + TestHelpers.datepicker.init( "#inp" ); var currentYear, gmtDate, fr, settings, zh; - ok($.datepicker.parseDate("d m y", "") == null, "Parse date empty"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("d m y", "3 2 01"), - new Date(2001, 2 - 1, 3), "Parse date d m y"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("dd mm yy", "03 02 2001"), - new Date(2001, 2 - 1, 3), "Parse date dd mm yy"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("d m y", "13 12 01"), - new Date(2001, 12 - 1, 13), "Parse date d m y"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("dd mm yy", "13 12 2001"), - new Date(2001, 12 - 1, 13), "Parse date dd mm yy"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-o", "01-34"), - new Date(2001, 2 - 1, 3), "Parse date y-o"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("yy-oo", "2001-347"), - new Date(2001, 12 - 1, 13), "Parse date yy-oo"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("oo yy", "348 2004"), - new Date(2004, 12 - 1, 13), "Parse date oo yy"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("D d M y", "Sat 3 Feb 01"), - new Date(2001, 2 - 1, 3), "Parse date D d M y"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("d MM DD yy", "3 February Saturday 2001"), - new Date(2001, 2 - 1, 3), "Parse date dd MM DD yy"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("DD, MM d, yy", "Saturday, February 3, 2001"), - new Date(2001, 2 - 1, 3), "Parse date DD, MM d, yy"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("'day' d 'of' MM (''DD''), yy", - "day 3 of February ('Saturday'), 2001"), new Date(2001, 2 - 1, 3), - "Parse date 'day' d 'of' MM (''DD''), yy"); + ok($.datepicker.parseDate( "d m y", "" ) == null, "Parse date empty" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "d m y", "3 2 01" ), + new Date(2001, 2 - 1, 3), "Parse date d m y" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "dd mm yy", "03 02 2001" ), + new Date(2001, 2 - 1, 3), "Parse date dd mm yy" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "d m y", "13 12 01" ), + new Date(2001, 12 - 1, 13), "Parse date d m y" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "dd mm yy", "13 12 2001" ), + new Date(2001, 12 - 1, 13), "Parse date dd mm yy" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "y-o", "01-34" ), + new Date(2001, 2 - 1, 3), "Parse date y-o" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "yy-oo", "2001-347" ), + new Date(2001, 12 - 1, 13), "Parse date yy-oo" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "oo yy", "348 2004" ), + new Date(2004, 12 - 1, 13), "Parse date oo yy" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "D d M y", "Sat 3 Feb 01" ), + new Date(2001, 2 - 1, 3), "Parse date D d M y" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "d MM DD yy", "3 February Saturday 2001" ), + new Date(2001, 2 - 1, 3), "Parse date dd MM DD yy" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "DD, MM d, yy", "Saturday, February 3, 2001" ), + new Date(2001, 2 - 1, 3), "Parse date DD, MM d, yy" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "'day' d 'of' MM (''DD''), yy", + "day 3 of February ('Saturday'), 2001" ), new Date(2001, 2 - 1, 3), + "Parse date 'day' d 'of' MM (''DD''), yy" ); currentYear = new Date().getFullYear(); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", (currentYear - 2000) + "-02-03"), - new Date(currentYear, 2 - 1, 3), "Parse date y-m-d - default cutuff"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", (currentYear - 2000 + 10) + "-02-03"), - new Date(currentYear+10, 2 - 1, 3), "Parse date y-m-d - default cutuff"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", (currentYear - 2000 + 11) + "-02-03"), - new Date(currentYear-89, 2 - 1, 3), "Parse date y-m-d - default cutuff"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", "80-02-03", {shortYearCutoff: 80}), - new Date(2080, 2 - 1, 3), "Parse date y-m-d - cutoff 80"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", "81-02-03", {shortYearCutoff: 80}), - new Date(1981, 2 - 1, 3), "Parse date y-m-d - cutoff 80"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", (currentYear - 2000 + 60) + "-02-03", {shortYearCutoff: "+60"}), - new Date(currentYear + 60, 2 - 1, 3), "Parse date y-m-d - cutoff +60"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("y-m-d", (currentYear - 2000 + 61) + "-02-03", {shortYearCutoff: "+60"}), - new Date(currentYear - 39, 2 - 1, 3), "Parse date y-m-d - cutoff +60"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "y-m-d", (currentYear - 2000) + "-02-03" ), + new Date(currentYear, 2 - 1, 3), "Parse date y-m-d - default cutuff" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "y-m-d", (currentYear - 2000 + 10) + "-02-03" ), + new Date(currentYear+10, 2 - 1, 3), "Parse date y-m-d - default cutuff" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "y-m-d", (currentYear - 2000 + 11) + "-02-03" ), + new Date(currentYear-89, 2 - 1, 3), "Parse date y-m-d - default cutuff" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "y-m-d", "80-02-03", {shortYearCutoff: 80}), + new Date(2080, 2 - 1, 3), "Parse date y-m-d - cutoff 80" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "y-m-d", "81-02-03", {shortYearCutoff: 80}), + new Date(1981, 2 - 1, 3), "Parse date y-m-d - cutoff 80" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "y-m-d", (currentYear - 2000 + 60) + "-02-03", {shortYearCutoff: "+60"}), + new Date(currentYear + 60, 2 - 1, 3), "Parse date y-m-d - cutoff +60" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "y-m-d", (currentYear - 2000 + 61) + "-02-03", {shortYearCutoff: "+60"}), + new Date(currentYear - 39, 2 - 1, 3), "Parse date y-m-d - cutoff +60" ); gmtDate = new Date(2001, 2 - 1, 3); gmtDate.setMinutes(gmtDate.getMinutes() - gmtDate.getTimezoneOffset()); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("@", "981158400000"), gmtDate, "Parse date @"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("!", "631167552000000000"), gmtDate, "Parse date !"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "@", "981158400000" ), gmtDate, "Parse date @" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "!", "631167552000000000" ), gmtDate, "Parse date !" ); fr = $.datepicker.regional.fr; settings = {dayNamesShort: fr.dayNamesShort, dayNames: fr.dayNames, monthNamesShort: fr.monthNamesShort, monthNames: fr.monthNames}; - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("D d M y", "Lun. 9 Avril 01", settings), - new Date(2001, 4 - 1, 9), "Parse date D M y with settings"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("d MM DD yy", "9 Avril Lundi 2001", settings), - new Date(2001, 4 - 1, 9), "Parse date d MM DD yy with settings"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("DD, MM d, yy", "Lundi, Avril 9, 2001", settings), - new Date(2001, 4 - 1, 9), "Parse date DD, MM d, yy with settings"); - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("'jour' d 'de' MM (''DD''), yy", "jour 9 de Avril ('Lundi'), 2001", settings), - new Date(2001, 4 - 1, 9), "Parse date 'jour' d 'de' MM (''DD''), yy with settings"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "D d M y", "Lun. 9 Avril 01", settings), + new Date(2001, 4 - 1, 9), "Parse date D M y with settings" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "d MM DD yy", "9 Avril Lundi 2001", settings), + new Date(2001, 4 - 1, 9), "Parse date d MM DD yy with settings" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "DD, MM d, yy", "Lundi, Avril 9, 2001", settings), + new Date(2001, 4 - 1, 9), "Parse date DD, MM d, yy with settings" ); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "'jour' d 'de' MM (''DD''), yy", "jour 9 de Avril ('Lundi'), 2001", settings), + new Date(2001, 4 - 1, 9), "Parse date 'jour' d 'de' MM (''DD''), yy with settings" ); zh = $.datepicker.regional["zh-CN"]; - TestHelpers.datepicker.equalsDate($.datepicker.parseDate("yy M d", "2011 十一月 22", zh), - new Date(2011, 11 - 1, 22), "Parse date yy M d with zh-CN"); + TestHelpers.datepicker.equalsDate($.datepicker.parseDate( "yy M d", "2011 十一月 22", zh), + new Date(2011, 11 - 1, 22), "Parse date yy M d with zh-CN" ); }); -test("parseDateErrors", function() { +test( "parseDateErrors", function() { expect( 17 ); - TestHelpers.datepicker.init("#inp"); + TestHelpers.datepicker.init( "#inp" ); var fr, settings; function expectError(expr, value, error) { try { @@ -1123,114 +1123,114 @@ test("parseDateErrors", function() { equal(e, error, "Parsed error " + value); } } - expectError(function() { $.datepicker.parseDate(null, "Sat 2 01"); }, - "Sat 2 01", "Invalid arguments"); - expectError(function() { $.datepicker.parseDate("d m y", null); }, - "null", "Invalid arguments"); - expectError(function() { $.datepicker.parseDate("d m y", "Sat 2 01"); }, - "Sat 2 01 - d m y", "Missing number at position 0"); - expectError(function() { $.datepicker.parseDate("dd mm yy", "Sat 2 01"); }, - "Sat 2 01 - dd mm yy", "Missing number at position 0"); - expectError(function() { $.datepicker.parseDate("d m y", "3 Feb 01"); }, - "3 Feb 01 - d m y", "Missing number at position 2"); - expectError(function() { $.datepicker.parseDate("dd mm yy", "3 Feb 01"); }, - "3 Feb 01 - dd mm yy", "Missing number at position 2"); - expectError(function() { $.datepicker.parseDate("d m y", "3 2 AD01"); }, - "3 2 AD01 - d m y", "Missing number at position 4"); - expectError(function() { $.datepicker.parseDate("d m yy", "3 2 AD01"); }, - "3 2 AD01 - dd mm yy", "Missing number at position 4"); - expectError(function() { $.datepicker.parseDate("y-o", "01-D01"); }, - "2001-D01 - y-o", "Missing number at position 3"); - expectError(function() { $.datepicker.parseDate("yy-oo", "2001-D01"); }, - "2001-D01 - yy-oo", "Missing number at position 5"); - expectError(function() { $.datepicker.parseDate("D d M y", "D7 3 Feb 01"); }, - "D7 3 Feb 01 - D d M y", "Unknown name at position 0"); - expectError(function() { $.datepicker.parseDate("D d M y", "Sat 3 M2 01"); }, - "Sat 3 M2 01 - D d M y", "Unknown name at position 6"); - expectError(function() { $.datepicker.parseDate("DD, MM d, yy", "Saturday- Feb 3, 2001"); }, - "Saturday- Feb 3, 2001 - DD, MM d, yy", "Unexpected literal at position 8"); - expectError(function() { $.datepicker.parseDate("'day' d 'of' MM (''DD''), yy", - "day 3 of February (\"Saturday\"), 2001"); }, - "day 3 of Mon2 ('Day7'), 2001", "Unexpected literal at position 19"); - expectError(function() { $.datepicker.parseDate("d m y", "29 2 01"); }, - "29 2 01 - d m y", "Invalid date"); + expectError(function() { $.datepicker.parseDate(null, "Sat 2 01" ); }, + "Sat 2 01", "Invalid arguments" ); + expectError(function() { $.datepicker.parseDate( "d m y", null); }, + "null", "Invalid arguments" ); + expectError(function() { $.datepicker.parseDate( "d m y", "Sat 2 01" ); }, + "Sat 2 01 - d m y", "Missing number at position 0" ); + expectError(function() { $.datepicker.parseDate( "dd mm yy", "Sat 2 01" ); }, + "Sat 2 01 - dd mm yy", "Missing number at position 0" ); + expectError(function() { $.datepicker.parseDate( "d m y", "3 Feb 01" ); }, + "3 Feb 01 - d m y", "Missing number at position 2" ); + expectError(function() { $.datepicker.parseDate( "dd mm yy", "3 Feb 01" ); }, + "3 Feb 01 - dd mm yy", "Missing number at position 2" ); + expectError(function() { $.datepicker.parseDate( "d m y", "3 2 AD01" ); }, + "3 2 AD01 - d m y", "Missing number at position 4" ); + expectError(function() { $.datepicker.parseDate( "d m yy", "3 2 AD01" ); }, + "3 2 AD01 - dd mm yy", "Missing number at position 4" ); + expectError(function() { $.datepicker.parseDate( "y-o", "01-D01" ); }, + "2001-D01 - y-o", "Missing number at position 3" ); + expectError(function() { $.datepicker.parseDate( "yy-oo", "2001-D01" ); }, + "2001-D01 - yy-oo", "Missing number at position 5" ); + expectError(function() { $.datepicker.parseDate( "D d M y", "D7 3 Feb 01" ); }, + "D7 3 Feb 01 - D d M y", "Unknown name at position 0" ); + expectError(function() { $.datepicker.parseDate( "D d M y", "Sat 3 M2 01" ); }, + "Sat 3 M2 01 - D d M y", "Unknown name at position 6" ); + expectError(function() { $.datepicker.parseDate( "DD, MM d, yy", "Saturday- Feb 3, 2001" ); }, + "Saturday- Feb 3, 2001 - DD, MM d, yy", "Unexpected literal at position 8" ); + expectError(function() { $.datepicker.parseDate( "'day' d 'of' MM (''DD''), yy", + "day 3 of February (\"Saturday\" ), 2001" ); }, + "day 3 of Mon2 ('Day7'), 2001", "Unexpected literal at position 19" ); + expectError(function() { $.datepicker.parseDate( "d m y", "29 2 01" ); }, + "29 2 01 - d m y", "Invalid date" ); fr = $.datepicker.regional.fr; settings = {dayNamesShort: fr.dayNamesShort, dayNames: fr.dayNames, monthNamesShort: fr.monthNamesShort, monthNames: fr.monthNames}; - expectError(function() { $.datepicker.parseDate("D d M y", "Mon 9 Avr 01", settings); }, - "Mon 9 Avr 01 - D d M y", "Unknown name at position 0"); - expectError(function() { $.datepicker.parseDate("D d M y", "Lun. 9 Apr 01", settings); }, - "Lun. 9 Apr 01 - D d M y", "Unknown name at position 7"); + expectError(function() { $.datepicker.parseDate( "D d M y", "Mon 9 Avr 01", settings); }, + "Mon 9 Avr 01 - D d M y", "Unknown name at position 0" ); + expectError(function() { $.datepicker.parseDate( "D d M y", "Lun. 9 Apr 01", settings); }, + "Lun. 9 Apr 01 - D d M y", "Unknown name at position 7" ); }); -test("Ticket #7244: date parser does not fail when too many numbers are passed into the date function", function() { +test( "Ticket #7244: date parser does not fail when too many numbers are passed into the date function", function() { expect( 4 ); var date; try{ - date = $.datepicker.parseDate("dd/mm/yy", "18/04/19881"); - ok(false, "Did not properly detect an invalid date"); + date = $.datepicker.parseDate( "dd/mm/yy", "18/04/19881" ); + ok(false, "Did not properly detect an invalid date" ); }catch(e){ - ok("invalid date detected"); + ok( "invalid date detected" ); } try { - date = $.datepicker.parseDate("dd/mm/yy", "18/04/1988 @ 2:43 pm"); + date = $.datepicker.parseDate( "dd/mm/yy", "18/04/1988 @ 2:43 pm" ); equal(date.getDate(), 18); equal(date.getMonth(), 3); equal(date.getFullYear(), 1988); } catch(e) { - ok(false, "Did not properly parse date with extra text separated by whitespace"); + ok(false, "Did not properly parse date with extra text separated by whitespace" ); } }); -test("formatDate", function() { +test( "formatDate", function() { expect( 16 ); - TestHelpers.datepicker.init("#inp"); + TestHelpers.datepicker.init( "#inp" ); var gmtDate, fr, settings; - equal($.datepicker.formatDate("d m y", new Date(2001, 2 - 1, 3)), - "3 2 01", "Format date d m y"); - equal($.datepicker.formatDate("dd mm yy", new Date(2001, 2 - 1, 3)), - "03 02 2001", "Format date dd mm yy"); - equal($.datepicker.formatDate("d m y", new Date(2001, 12 - 1, 13)), - "13 12 01", "Format date d m y"); - equal($.datepicker.formatDate("dd mm yy", new Date(2001, 12 - 1, 13)), - "13 12 2001", "Format date dd mm yy"); - equal($.datepicker.formatDate("yy-o", new Date(2001, 2 - 1, 3)), - "2001-34", "Format date yy-o"); - equal($.datepicker.formatDate("yy-oo", new Date(2001, 2 - 1, 3)), - "2001-034", "Format date yy-oo"); - equal($.datepicker.formatDate("D M y", new Date(2001, 2 - 1, 3)), - "Sat Feb 01", "Format date D M y"); - equal($.datepicker.formatDate("DD MM yy", new Date(2001, 2 - 1, 3)), - "Saturday February 2001", "Format date DD MM yy"); - equal($.datepicker.formatDate("DD, MM d, yy", new Date(2001, 2 - 1, 3)), - "Saturday, February 3, 2001", "Format date DD, MM d, yy"); - equal($.datepicker.formatDate("'day' d 'of' MM (''DD''), yy", + equal($.datepicker.formatDate( "d m y", new Date(2001, 2 - 1, 3)), + "3 2 01", "Format date d m y" ); + equal($.datepicker.formatDate( "dd mm yy", new Date(2001, 2 - 1, 3)), + "03 02 2001", "Format date dd mm yy" ); + equal($.datepicker.formatDate( "d m y", new Date(2001, 12 - 1, 13)), + "13 12 01", "Format date d m y" ); + equal($.datepicker.formatDate( "dd mm yy", new Date(2001, 12 - 1, 13)), + "13 12 2001", "Format date dd mm yy" ); + equal($.datepicker.formatDate( "yy-o", new Date(2001, 2 - 1, 3)), + "2001-34", "Format date yy-o" ); + equal($.datepicker.formatDate( "yy-oo", new Date(2001, 2 - 1, 3)), + "2001-034", "Format date yy-oo" ); + equal($.datepicker.formatDate( "D M y", new Date(2001, 2 - 1, 3)), + "Sat Feb 01", "Format date D M y" ); + equal($.datepicker.formatDate( "DD MM yy", new Date(2001, 2 - 1, 3)), + "Saturday February 2001", "Format date DD MM yy" ); + equal($.datepicker.formatDate( "DD, MM d, yy", new Date(2001, 2 - 1, 3)), + "Saturday, February 3, 2001", "Format date DD, MM d, yy" ); + equal($.datepicker.formatDate( "'day' d 'of' MM (''DD''), yy", new Date(2001, 2 - 1, 3)), "day 3 of February ('Saturday'), 2001", - "Format date 'day' d 'of' MM ('DD'), yy"); + "Format date 'day' d 'of' MM ('DD'), yy" ); gmtDate = new Date(2001, 2 - 1, 3); - gmtDate.setMinutes(gmtDate.getMinutes() - gmtDate.getTimezoneOffset()); - equal($.datepicker.formatDate("@", gmtDate), "981158400000", "Format date @"); - equal($.datepicker.formatDate("!", gmtDate), "631167552000000000", "Format date !"); + gmtDate.setMinutes( gmtDate.getMinutes() - gmtDate.getTimezoneOffset() ); + equal($.datepicker.formatDate( "@", gmtDate), "981158400000", "Format date @" ); + equal($.datepicker.formatDate( "!", gmtDate), "631167552000000000", "Format date !" ); fr = $.datepicker.regional.fr; settings = {dayNamesShort: fr.dayNamesShort, dayNames: fr.dayNames, monthNamesShort: fr.monthNamesShort, monthNames: fr.monthNames}; - equal($.datepicker.formatDate("D M y", new Date(2001, 4 - 1, 9), settings), - "lun. avril 01", "Format date D M y with settings"); - equal($.datepicker.formatDate("DD MM yy", new Date(2001, 4 - 1, 9), settings), - "lundi avril 2001", "Format date DD MM yy with settings"); - equal($.datepicker.formatDate("DD, MM d, yy", new Date(2001, 4 - 1, 9), settings), - "lundi, avril 9, 2001", "Format date DD, MM d, yy with settings"); - equal($.datepicker.formatDate("'jour' d 'de' MM (''DD''), yy", + equal($.datepicker.formatDate( "D M y", new Date(2001, 4 - 1, 9), settings), + "lun. avril 01", "Format date D M y with settings" ); + equal($.datepicker.formatDate( "DD MM yy", new Date(2001, 4 - 1, 9), settings), + "lundi avril 2001", "Format date DD MM yy with settings" ); + equal($.datepicker.formatDate( "DD, MM d, yy", new Date(2001, 4 - 1, 9), settings), + "lundi, avril 9, 2001", "Format date DD, MM d, yy with settings" ); + equal($.datepicker.formatDate( "'jour' d 'de' MM (''DD''), yy", new Date(2001, 4 - 1, 9), settings), "jour 9 de avril ('lundi'), 2001", - "Format date 'jour' d 'de' MM (''DD''), yy with settings"); + "Format date 'jour' d 'de' MM (''DD''), yy with settings" ); }); // TODO: Fix this test so it isn't mysteriously flaky in Browserstack on certain OS/Browser combos -// test("Ticket 6827: formatDate day of year calculation is wrong during day lights savings time", function(){ +// test( "Ticket 6827: formatDate day of year calculation is wrong during day lights savings time", function(){ // expect( 1 ); -// var time = $.datepicker.formatDate("oo", new Date("2010/03/30 12:00:00 CDT")); -// equal(time, "089"); +// var time = $.datepicker.formatDate( "oo", new Date( "2010/03/30 12:00:00 CDT" )); +// equal(time, "089" ); // }); test( "Ticket 7602: Stop datepicker from appearing with beforeShow event handler", function() { @@ -1265,7 +1265,7 @@ test( "Ticket 7602: Stop datepicker from appearing with beforeShow event handler }); dp = $( "#ui-datepicker-div" ); inp.datepicker( "show" ); - equal( dp.css( "display" ), "none","beforeShow returns false" ); + equal( dp.css( "display" ), "none", "beforeShow returns false" ); inp.datepicker( "destroy" ); }); */ diff --git a/tests/unit/datepicker/datepicker_test_helpers.js b/tests/unit/datepicker/datepicker_test_helpers.js index 060c6bf9505..1d922c72220 100644 --- a/tests/unit/datepicker/datepicker_test_helpers.js +++ b/tests/unit/datepicker/datepicker_test_helpers.js @@ -1,18 +1,18 @@ TestHelpers.datepicker = { - addMonths: function(date, offset) { - var maxDay = 32 - new Date(date.getFullYear(), date.getMonth() + offset, 32).getDate(); - date.setDate(Math.min(date.getDate(), maxDay)); - date.setMonth(date.getMonth() + offset); + addMonths: function( date, offset ) { + var maxDay = 32 - new Date( date.getFullYear(), date.getMonth() + offset, 32 ).getDate(); + date.setDate( Math.min( date.getDate(), maxDay ) ); + date.setMonth( date.getMonth() + offset ); return date; }, - equalsDate: function(d1, d2, message) { - if (!d1 || !d2) { - ok(false, message + " - missing date"); + equalsDate: function( d1, d2, message ) { + if ( !d1 || !d2 ) { + ok( false, message + " - missing date" ); return; } - d1 = new Date(d1.getFullYear(), d1.getMonth(), d1.getDate()); - d2 = new Date(d2.getFullYear(), d2.getMonth(), d2.getDate()); - equal(d1.toString(), d2.toString(), message); + d1 = new Date( d1.getFullYear(), d1.getMonth(), d1.getDate() ); + d2 = new Date( d2.getFullYear(), d2.getMonth(), d2.getDate() ); + equal( d1.toString(), d2.toString(), message ); }, init: function( id, options ) { options = $.extend( { show: false }, options || {} ); diff --git a/ui/datepicker.js b/ui/datepicker.js index f8328e1cab6..0330f5a81d0 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -52,6 +52,7 @@ $.widget( "ui.datepicker", { open: null, select: null }, + _create: function() { this.options.dateFormat = this.options.dateFormat || { date: "short" }; this.date = $.date( null, this.options.dateFormat ); @@ -167,6 +168,7 @@ $.widget( "ui.datepicker", { newCell.children( "a" ).addClass( "ui-state-focus" ); } }, + _createPicker: function() { this.picker = $( "
      " ) .addClass( "ui-front" ) @@ -200,10 +202,10 @@ $.widget( "ui.datepicker", { case $.ui.keyCode.DOWN: case $.ui.keyCode.UP: clearTimeout( this.closeTimer ); - this._delay(function() { + this._delay( function() { this.open( event ); this.grid.focus( 1 ); - }, 1); + }, 1 ); break; case $.ui.keyCode.HOME: if ( event.ctrlKey ) { @@ -252,7 +254,7 @@ $.widget( "ui.datepicker", { } this._delay( function() { suppressExpandOnFocus = false; - }, 100); + }, 100 ); }, blur: function() { suppressExpandOnFocus = false; @@ -266,7 +268,7 @@ $.widget( "ui.datepicker", { // also allows tabbing inside the calendar without it closing this.closeTimer = this._delay( function() { this.close( event ); - }, 150); + }, 150 ); }, focusin: function() { clearTimeout( this.closeTimer ); @@ -291,6 +293,7 @@ $.widget( "ui.datepicker", { } }); }, + _appendTo: function() { var element = this.options.appendTo; @@ -305,7 +308,7 @@ $.widget( "ui.datepicker", { } if ( !element.length ) { - element = this.document[0].body; + element = this.document[ 0 ].body; } return element; @@ -358,10 +361,10 @@ $.widget( "ui.datepicker", { html += "
      " + "
      "; - if ( months[i].first ) { + if ( months[ i ].first ) { html += this._buildPreviousLink(); } - if ( months[i].last ) { + if ( months[ i ].last ) { html += this._buildNextLink(); } @@ -375,8 +378,10 @@ $.widget( "ui.datepicker", { html += this._buildButtons(); this.date = currentDate; + return html; }, + _buildHeader: function() { return "
      " + this._buildPreviousLink() + @@ -384,8 +389,10 @@ $.widget( "ui.datepicker", { this._buildTitlebar() + "
      "; }, + _buildPreviousLink: function() { var labels = Globalize.translate( "datepicker" ); + return ""; }, + _buildNextLink: function() { var labels = Globalize.translate( "datepicker" ); + return ""; }, + _buildTitlebar: function() { var labels = Globalize.translate( "datepicker" ); + return "
      " + "
      " + this._buildTitle() + @@ -411,6 +422,7 @@ $.widget( "ui.datepicker", { ", " + labels.datePickerRole + "" + "
      "; }, + _buildTitle: function() { return "" + this.date.monthName() + @@ -419,6 +431,7 @@ $.widget( "ui.datepicker", { this.date.year() + ""; }, + _buildGrid: function() { return "" + @@ -426,6 +439,7 @@ $.widget( "ui.datepicker", { this._buildGridBody() + "
      "; }, + _buildGridHeading: function() { var cells = "", i = 0, @@ -437,10 +451,12 @@ $.widget( "ui.datepicker", { for ( i; i < this.date.weekdays().length; i++ ) { cells += this._buildGridHeaderCell( this.date.weekdays()[i] ); } + return "" + "" + cells + "" + ""; }, + _buildGridHeaderCell: function( day ) { return "" + "" + @@ -448,16 +464,20 @@ $.widget( "ui.datepicker", { "" + ""; }, + _buildGridBody: function() { // this.date.days() is not cached, and it has O(n^2) complexity. It is run O(n) times. So, it equals O(n^3). Not good at all. Caching. var days = this.date.days(), i = 0, rows = ""; + for ( i; i < days.length; i++ ) { - rows += this._buildWeekRow( days[i] ); + rows += this._buildWeekRow( days[ i ] ); } + return "" + rows + ""; }, + _buildWeekRow: function( week ) { var cells = "", i = 0; @@ -470,6 +490,7 @@ $.widget( "ui.datepicker", { } return "" + cells + ""; }, + _buildDayCell: function( day ) { var contents = "", idAttribute = day.render ? ( "id=" + this.id + "-" + day.date ) : "", @@ -488,6 +509,7 @@ $.widget( "ui.datepicker", { contents + ""; }, + _buildDayLink: function( day ) { var link, classes = [ "ui-state-default" ], @@ -511,10 +533,13 @@ $.widget( "ui.datepicker", { if ( day.today ) { link += ", " + labels.currentText + ""; } + return link; }, + _buildDayDisplay: function( day ) { var classes = []; + if ( day.current ) { classes.push( "ui-state-active" ); } @@ -525,20 +550,23 @@ $.widget( "ui.datepicker", { classes.push( day.extraClasses.split( " " ) ); } - return "" + - day.date + ""; + return "" + day.date + ""; }, + _buildButtons: function() { var labels = Globalize.translate( "datepicker" ); + return "
      " + "" + "" + "
      "; }, + _focusTrigger: function() { suppressExpandOnFocus = true; this.element.focus(); }, + // Refreshing the entire datepicker during interaction confuses screen readers, specifically // because the grid heading is marked up as a live region and will often not update if it's // destroyed and recreated instead of just having its text change. Additionally, interacting @@ -555,6 +583,7 @@ $.widget( "ui.datepicker", { this._refreshMultiplePicker(); } }, + _refreshMultiplePicker: function() { for ( var i = 0; i < this.options.numberOfMonths; i++ ) { $( ".ui-datepicker-title", this.picker ).eq( i ).html( this._buildTitle() ); @@ -568,6 +597,7 @@ $.widget( "ui.datepicker", { // to maintain the currently focused grid and base queries like this off of it. $( this.picker ).find( ".ui-state-focus" ).not( ":first" ).removeClass( "ui-state-focus" ); }, + open: function( event ) { if ( this.inline || this.isOpen ) { return; @@ -598,6 +628,7 @@ $.widget( "ui.datepicker", { this._trigger( "open", event ); }, + close: function( event ) { if ( this.inline ) { return; @@ -611,16 +642,19 @@ $.widget( "ui.datepicker", { this.isOpen = false; this._trigger( "close", event ); }, + _setHiddenPicker: function() { this.picker .attr( "aria-hidden", "true" ) .attr( "aria-expanded", "false" ); }, + _buildPosition: function() { return $.extend( {}, { of: this.element }, this.options.position ); }, + select: function( event, time ) { this.date.setTime( time ).select(); this.refresh(); @@ -634,6 +668,7 @@ $.widget( "ui.datepicker", { date: this.date.format() }); }, + _value: function( value ) { this.date.setTime( value ).select(); if ( !this.inline ) { @@ -641,6 +676,7 @@ $.widget( "ui.datepicker", { } this.refresh(); }, + value: function( value ) { if ( arguments.length ) { this._value( value ); @@ -648,6 +684,7 @@ $.widget( "ui.datepicker", { return this.isValid() ? this.date.format() : this.element.val(); } }, + valueAsDate: function( value ) { if ( arguments.length ) { this._value( value ); @@ -655,12 +692,15 @@ $.widget( "ui.datepicker", { return this.isValid() ? this.date.date() : null; } }, + isValid: function() { if ( this.inline ) { return true; } + return Globalize.parseDate( this.element.val(), this.options.dateFormat ) !== null; }, + _destroy: function() { if ( this.inline ) { this.picker.empty(); @@ -671,9 +711,11 @@ $.widget( "ui.datepicker", { .removeAttr( "aria-owns" ); } }, + widget: function() { return this.picker; }, + _setOption: function( key, value ) { this._super( key, value ); From 88f4a1516ad0b20392c62e9f94e8c5ab8a55f8ce Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Sat, 26 Apr 2014 13:30:09 +0200 Subject: [PATCH 24/53] Datepicker: Rename `select()` method to `_select()` Make `select()` a private method as it's not part of the specification. --- ui/datepicker.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index 0330f5a81d0..86b290ab724 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -79,7 +79,7 @@ $.widget( "ui.datepicker", { }, "click .ui-datepicker-current": function( event ) { event.preventDefault(); - this.select( event, new Date().getTime() ); + this._select( event, new Date().getTime() ); }, "click .ui-datepicker-close": function( event ) { event.preventDefault(); @@ -89,7 +89,7 @@ $.widget( "ui.datepicker", { event.preventDefault(); // TODO exclude clicks on lead days or handle them correctly // TODO store/read more then just date, also required for multi month picker - this.select( event, $( event.currentTarget ).data( "timestamp" ) ); + this._select( event, $( event.currentTarget ).data( "timestamp" ) ); if ( this.inline ) { this.grid.focus( 1 ); } @@ -655,7 +655,7 @@ $.widget( "ui.datepicker", { }, this.options.position ); }, - select: function( event, time ) { + _select: function( event, time ) { this.date.setTime( time ).select(); this.refresh(); if ( !this.inline ) { From e8ea5f4a7252402730054eb7a549b0a5d4ca23e9 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Sat, 26 Apr 2014 14:31:28 +0200 Subject: [PATCH 25/53] Datepicker: Use `short` instead of `abbreviated` format for table header Revert to old behavior with two instead of three chars. Fixes layout issue with wrong day table cell width. --- external/date.js | 2 +- tests/unit/date/date_core.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/external/date.js b/external/date.js index e7c17a58dcf..06a67553205 100644 --- a/external/date.js +++ b/external/date.js @@ -125,7 +125,7 @@ $.date.prototype = { for ( var dow = 0; dow < 7; dow++ ) { var day = ( dow + weekdaysRev[ Globalize.locale().supplemental.weekData.firstDay() ] ) % 7; result.push({ - shortname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/abbreviated", weekdays[ day ] ]), + shortname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/short", weekdays[ day ] ]), fullname: Globalize.locale().main([ "dates/calendars/gregorian/days/format/wide", weekdays[ day ] ]) }); } diff --git a/tests/unit/date/date_core.js b/tests/unit/date/date_core.js index db94cb67d56..65665431c32 100644 --- a/tests/unit/date/date_core.js +++ b/tests/unit/date/date_core.js @@ -64,13 +64,13 @@ test( "Date Adjustments - Leap Year Edge Cases", 1, function() { test( "List days of Week", 2, function() { var date = $.date(), offset0 = [ - { "fullname": "Sunday", "shortname": "Sun" }, - { "fullname": "Monday", "shortname": "Mon" }, - { "fullname": "Tuesday", "shortname": "Tue" }, - { "fullname": "Wednesday", "shortname": "Wed" }, - { "fullname": "Thursday", "shortname": "Thu" }, - { "fullname": "Friday", "shortname": "Fri" }, - { "fullname": "Saturday", "shortname": "Sat" } + { "fullname": "Sunday", "shortname": "Su" }, + { "fullname": "Monday", "shortname": "Mo" }, + { "fullname": "Tuesday", "shortname": "Tu" }, + { "fullname": "Wednesday", "shortname": "We" }, + { "fullname": "Thursday", "shortname": "Th" }, + { "fullname": "Friday", "shortname": "Fr" }, + { "fullname": "Saturday", "shortname": "Sa" } ], offset1 = [ { "fullname": "Montag", "shortname": "Mo." }, From fb8d50dfebf19b1e31712edc1c169174212e9c3f Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Sat, 26 Apr 2014 14:44:31 +0200 Subject: [PATCH 26/53] Datepicker: Properly define default for `dateFormat` option --- ui/datepicker.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index 86b290ab724..f2dfa2d1594 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -34,7 +34,7 @@ var idIncrement = 0, $.widget( "ui.datepicker", { options: { appendTo: null, - dateFormat: null, + dateFormat: { date: "short" }, // TODO review eachDay: $.noop, numberOfMonths: 1, @@ -54,7 +54,6 @@ $.widget( "ui.datepicker", { }, _create: function() { - this.options.dateFormat = this.options.dateFormat || { date: "short" }; this.date = $.date( null, this.options.dateFormat ); this.date.eachDay = this.options.eachDay; From 38f998905a6ecc0b8bc99d017511176235126891 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Sat, 26 Apr 2014 15:07:32 +0200 Subject: [PATCH 27/53] Datepicker: Fix populate alternate field demo --- demos/datepicker/alt-field.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demos/datepicker/alt-field.html b/demos/datepicker/alt-field.html index 4fd64bf6f19..89463fcdad9 100644 --- a/demos/datepicker/alt-field.html +++ b/demos/datepicker/alt-field.html @@ -18,8 +18,8 @@ $(function() { $( "#datepicker" ).datepicker({ select: function( event, ui ) { - var date = Globalize.parseDate( ui.date ); - $( "#alternate" ).val( Globalize.format( date, "dddd, d MMMM, yyyy" ) ); + var date = Globalize.parseDate( ui.date, { date: "short" } ); + $( "#alternate" ).val( Globalize.format( date, { date: "long" } ) ); } }); }); From 400936161ed5815b38d99457d03b2fabab0dc048 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Sat, 26 Apr 2014 14:05:56 +0200 Subject: [PATCH 28/53] Datepicker: Fix icon trigger demo --- demos/datepicker/icon-trigger.html | 31 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/demos/datepicker/icon-trigger.html b/demos/datepicker/icon-trigger.html index e880df27e7f..03fb91f812f 100644 --- a/demos/datepicker/icon-trigger.html +++ b/demos/datepicker/icon-trigger.html @@ -16,21 +16,22 @@ From 62922fee03274e3318a6938cb5422402106accd2 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Fri, 16 May 2014 00:19:32 +0200 Subject: [PATCH 29/53] Datepicker: Introduce value option Change status caching, fix existing value related methods, introduce $.date construction with date object, selected property is null by default, add selected getter --- external/date.js | 7 + tests/unit/datepicker/datepicker_core.js | 479 ++++++++------------ tests/unit/datepicker/datepicker_methods.js | 47 +- ui/datepicker.js | 121 +++-- 4 files changed, 281 insertions(+), 373 deletions(-) diff --git a/external/date.js b/external/date.js index 06a67553205..8df3f63de92 100644 --- a/external/date.js +++ b/external/date.js @@ -26,9 +26,13 @@ $.date = function( date, globalFormat ) { if ( typeof date === "string" && date.length ) { this.dateObject = Globalize.parseDate( date, globalFormat ); } + if ( $.type( date ) === "date" ) { + this.dateObject = date; + } this.dateObject = this.dateObject || new Date(); this.globalFormat = globalFormat; + this.selected = null; }; $.date.prototype = { @@ -180,6 +184,9 @@ $.date.prototype = { this.selected = this.clone(); return this; }, + selectedDate: function() { + return this.selected.date(); + }, clone: function() { var date = this.dateObject; return new $.date( new Date( date.getFullYear(), date.getMonth(), diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index 189f03d932a..e93836194c8 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -64,7 +64,7 @@ asyncTest( "baseStructure", function() { inp.datepicker( "close" ).datepicker( "destroy" ); step2(); - }); + }, 50 ); } function step2() { @@ -217,269 +217,223 @@ asyncTest( "baseStructure", function() { step1(); }); -test( "Keyboard handling", function() { +asyncTest( "Keyboard handling: input", function() { expect( 9 ); - var input = $( "#datepicker" ).datepicker(), - instance = input.datepicker( "instance" ), - date = new Date(); - - input.datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke enter" ); - - // Enter = Select today's date by default - input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), - "Keystroke enter - preset" ); - - // Control + Home = Change the calendar to the current month - input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.HOME } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke ctrl+home" ); - - // Control + End = Close the calendar and clear the input - input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.END } ); - equal( input.val(), "", "Keystroke ctrl+end" ); - - input.val( "" ).datepicker( "open" ); - ok( instance.isOpen, "datepicker is open before escape" ); - input.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); - ok( !instance.isOpen, "escape closes the datepicker" ); - - input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), - "Keystroke esc - preset" ); - - input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), - "Keystroke esc - abandoned" ); - - input.val( "1/2/14" ) - .simulate( "keyup" ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 2 ), - "Picker updated as user types into input" ); - - input.datepicker( "destroy" ); -}); - -asyncTest( "keyboard handling", function() { - expect( 14 ); - var picker, - input = $( "#datepicker" ), - date = new Date(); + var picker, instance, + input = $( "#datepicker" ).datepicker(); function step1() { - input.datepicker(); + TestHelpers.datepicker.init( input ); picker = input.datepicker( "widget" ); + ok( !picker.is( ":visible" ), "datepicker closed" ); - input.val( "" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + input.val( "" ).simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { ok( picker.is( ":visible" ), "Keystroke down opens datepicker" ); input.datepicker( "destroy" ); step2(); - }); + }, 100 ); } function step2() { - input.datepicker(); + TestHelpers.datepicker.init( input ); picker = input.datepicker( "widget" ); + ok( !picker.is( ":visible" ), "datepicker closed" ); - input.val( "" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + input.val( "" ).simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); setTimeout(function() { ok( picker.is( ":visible" ), "Keystroke up opens datepicker" ); input.datepicker( "destroy" ); step3(); - }); + }, 100 ); } function step3() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + TestHelpers.datepicker.init( input ); + instance = input.datepicker( "instance" ); - setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2013, 12 - 1, 31 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke left to switch to previous day" ); + input + .val( "" ) + .datepicker( "open" ); + ok( instance.isOpen, "datepicker is open before escape" ); - input.datepicker( "destroy" ); - step4(); - }, 100 ); - } + input.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + ok( !instance.isOpen, "escape closes the datepicker" ); - function step4() { - input.datepicker() + input .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + .datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), + "Keystroke esc - preset" ); - setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2014, 1 - 1, 2 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke right to switch to next day" ); - - input.datepicker( "destroy" ); - step5(); - }, 100 ); + input + .val( "1/1/14" ) + .datepicker( "open" ) + .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), + "Keystroke esc - abandoned" ); + + input + .val( "1/2/14" ) + .simulate( "keyup" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 2 ), + "Picker updated as user types into input" ); + + input.datepicker( "destroy" ); + start(); } - function step5() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + step1(); +}); - setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.UP } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2013, 12 - 1, 25 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke up to move to the previous week" ); +asyncTest( "keyboard handling: calendar", function() { + expect( 7 ); - input.datepicker( "destroy" ); - step6(); - }, 100 ); - } + var input = $( "#datepicker" ); - function step6() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step1() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2014, 1 - 1, 8 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke down to move to the next week" ); - - input.datepicker( "destroy" ); - step7(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2013, 12 - 1, 31 ), + "Keystroke left to switch to previous day" + ); + input.datepicker( "destroy" ); + step2(); + }, 50 ); }, 100 ); } - function step7() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step2() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2013, 12 - 1, 1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Up moves date to previous month" ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2014, 1 - 1, 2 ), + "Keystroke right to switch to next day" + ); input.datepicker( "destroy" ); - step8(); + step3(); }, 100 ); } - function step8() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step3() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP, altKey: true } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2013, 1 - 1, 1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Up + Ctrl moves date to previous year" ); - - input.datepicker( "destroy" ); - step9(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2013, 12 - 1, 25 ), + "Keystroke up to move to the previous week" + ); + input.datepicker( "destroy" ); + step4(); + }, 50 ); }, 100 ); } - function step9() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step4() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2014, 2 - 1, 1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Down moves date to next month" ); - - input.datepicker( "destroy" ); - step10(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2014, 1 - 1, 8 ), + "Keystroke down to move to the next week" + ); + input.datepicker( "destroy" ); + step5(); + }, 50 ); }, 100 ); } - function step10() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step5() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2015, 1 - 1, 1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Down + Ctrl moves date to next year" ); - - input.datepicker( "destroy" ); - step11(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2014, 2 - 1, 1 ), + "Keystroke Page Down moves date to next month" + ); + input.datepicker( "destroy" ); + step6(); + }, 50 ); }, 100 ); } - // Check for moving to short months - function step11() { - input.datepicker() - .val( "3/31/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step6() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2014, 2 - 1, 28 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Up and short months" ); - - input.datepicker( "destroy" ); - step12(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2015, 1 - 1, 1 ), + "Keystroke Page Down + Ctrl moves date to next year" + ); + input.datepicker( "destroy" ); + step7(); + }, 50 ); }, 100 ); } - function step12() { - input.datepicker() - .val( "1/30/16" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + // Check for moving to short months + function step7() { + input.val( "3/31/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2016, 2 - 1, 29 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Down and leap years" ); - - input.datepicker( "destroy" ); - start(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2014, 2 - 1, 28 ), + "Keystroke Page Up and short months" + ); + input.datepicker( "destroy" ); + start(); + }, 50 ); }, 100 ); } @@ -502,110 +456,37 @@ asyncTest( "keyboard handling", function() { "Keystroke pgdn step 2" ); */ -test( "mouse", function() { - expect( 13 ); - var input = $( "#datepicker" ).datepicker(), - picker = input.datepicker( "widget" ), - inline = $( "#inline" ).datepicker, - date = new Date(); - - input.val( "" ).datepicker( "open" ); - $( ".ui-datepicker-calendar tbody a:contains(10)", picker ).simulate( "mousedown", {} ); - date.setDate( 10 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Mouse click" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( ".ui-datepicker-calendar tbody a:contains(12)", picker ).simulate( "mousedown", {} ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 12 ), - "Mouse click - preset" ) ; - - input.val( "" ).datepicker( "open" ); - $( "button.ui-datepicker-close", picker ).simulate( "click", {} ); - ok( input.datepicker( "valueAsDate" ) == null, "Mouse click - close" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( "button.ui-datepicker-close", picker ).simulate( "click", {} ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 4 ), - "Mouse click - close + preset" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( "a.ui-datepicker-prev", picker ).simulate( "click", {} ); - $( "button.ui-datepicker-close", picker ).simulate( "click", {} ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 4 ), - "Mouse click - abandoned" ); - - // Current/previous/next - input.val( "" ).datepicker( "open" ); - $( ".ui-datepicker-current", picker ).simulate( "click", {} ); - date.setDate( new Date().getDate() ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Mouse click - current" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( ".ui-datepicker-prev", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(16)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 16 ), - "Mouse click - previous" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( ".ui-datepicker-next", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(18)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 3 - 1, 18 ), - "Mouse click - next" ); - - /* - // TODO: Re-add when min and max options are introduced. - // Previous/next with minimum/maximum - input.datepicker( "option", { - minDate: new Date( 2008, 2 - 1, 2 ), - maxDate: new Date( 2008, 2 - 1, 26 ) - }).val( "2/4/08" ).datepicker( "open" ); - $( ".ui-datepicker-prev", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(16)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 16 ), - "Mouse click - previous + min/max" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( ".ui-datepicker-next", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(18)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate(input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 18 ), - "Mouse click - next + min/max" ); - */ - - // Inline - inline = TestHelpers.datepicker.init( "#inline" ); - picker = $( ".ui-datepicker-inline", inline ); - date = new Date(); - inline.datepicker( "valueAsDate", date ); - $( ".ui-datepicker-calendar tbody a:contains(10)", picker ).simulate( "mousedown", {} ); - date.setDate( 10 ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), date, "Mouse click inline" ); - - inline.datepicker( "option", { showButtonPanel: true } ) - .datepicker( "valueAsDate", new Date( 2008, 2 - 1, 4 )); - $( ".ui-datepicker-calendar tbody a:contains(12)", picker ).simulate( "mousedown", {} ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 12 ), - "Mouse click inline - preset" ); - - inline.datepicker( "option", { showButtonPanel: true } ); - $( ".ui-datepicker-current", picker ).simulate( "click", {} ); - $( ".ui-datepicker-calendar tbody a:contains(14)", picker ).simulate( "mousedown", {} ); - date.setDate( 14 ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), date, "Mouse click inline - current" ); - - inline.datepicker( "valueAsDate", new Date( 2008, 2 - 1, 4) ); - $( ".ui-datepicker-prev", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(16)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 16 ), - "Mouse click inline - previous" ); - - inline.datepicker( "valueAsDate", new Date( 2008, 2 - 1, 4) ); - $( ".ui-datepicker-next", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(18)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), new Date( 2008, 3 - 1, 18 ), - "Mouse click inline - next" ); - - input.datepicker( "destroy" ); - inline.datepicker( "destroy" ); +asyncTest( "mouse", function() { + expect( 3 ); + + var input = TestHelpers.datepicker.init( $( "#datepicker" ).val( "" ) ), + picker = input.datepicker( "widget" ); + + input.datepicker( "open" ); + + setTimeout(function() { + input.simulate( "click" ); + strictEqual( input.datepicker( "valueAsDate" ), null, "Mouse click - close" ); + + input.val( "2/4/08" ).datepicker( "refresh" ).datepicker( "open" ); + input.simulate( "click" ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2008, 2 - 1, 4 ), + "Mouse click - close + preset" + ); + + input.val( "2/4/08" ).datepicker( "refresh" ).datepicker( "open" ); + picker.find( "a.ui-calendar-prev" ).simulate( "click" ); + input.simulate( "click" ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2008, 2 - 1, 4 ), + "Mouse click - abandoned" + ); + + start(); + }, 100 ); }); })( jQuery ); diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 7efea106918..79a9d64e758 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -64,54 +64,43 @@ test( "open", function() { }); test( "value", function() { - expect( 6 ); + expect( 4 ); + var input = $( "#datepicker" ).datepicker(), - picker = input.datepicker( "widget" ), - inline = $( "#inline" ).datepicker(); + picker = input.datepicker( "widget" ); input.datepicker( "value", "1/1/14" ); equal( input.val(), "1/1/14", "input's value set" ); - ok( picker.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), - "first day marked as selected" ); + + input.datepicker( "open" ); + ok( picker.find( "a[data-timestamp]" ).eq( 0 ).hasClass( "ui-state-active" ), "first day marked as selected" ); equal( input.datepicker( "value" ), "1/1/14", "getter" ); input.val( "abc" ); - equal( input.datepicker( "value" ), "abc", - "Invalid values should be returned without formatting." ); - - inline.datepicker( "value", "1/1/14" ); - ok( inline.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), - "first day marked as selected" ); - equal( inline.datepicker( "value" ), "1/1/14", "getter" ); - - input.datepicker( "destroy" ); - inline.datepicker( "destroy" ); + equal( input.datepicker( "value" ), "abc", "Invalid values should be returned without formatting." ); }); test( "valueAsDate", function() { expect( 6 ); - var input = $( "#datepicker" ).datepicker(), + + var input = TestHelpers.datepicker.init( "#datepicker" ), picker = input.datepicker( "widget" ), - inline = $( "#inline" ).datepicker(); + date1 = new Date( 2008, 6 - 1, 4 ); input.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); - equal( input.val(), "1/1/14", "input's value set" ); - ok( picker.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), - "first day marked as selected" ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), - new Date( 2014, 0, 1 ), "getter" ); + equal( input.val(), "1/1/14", "Input's value set" ); + ok( picker.find( "a[data-timestamp]" ).eq( 0 ).hasClass( "ui-state-active" ), "First day marked as selected" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Getter" ); input.val( "a/b/c" ); equal( input.datepicker( "valueAsDate" ), null, "Invalid dates return null" ); - inline.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); - ok( inline.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), - "first day marked as selected" ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), - new Date( 2014, 0, 1 ), "getter" ); + input.val( "" ).datepicker( "destroy" ); + input = TestHelpers.datepicker.init( "#datepicker" ); - input.datepicker( "destroy" ); - inline.datepicker( "destroy" ); + strictEqual( input.datepicker( "valueAsDate" ), null, "Set date - default" ); + input.datepicker( "valueAsDate", date1 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date - 2008-06-04" ); }); test( "isValid", function() { diff --git a/ui/datepicker.js b/ui/datepicker.js index f2dfa2d1594..a629e6120fa 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -28,14 +28,14 @@ // TODO use uniqueId, if possible var idIncrement = 0, - // TODO move this to the instance + // TODO Move this to the instance suppressExpandOnFocus = false; $.widget( "ui.datepicker", { options: { appendTo: null, dateFormat: { date: "short" }, - // TODO review + // TODO Review eachDay: $.noop, numberOfMonths: 1, position: { @@ -45,6 +45,7 @@ $.widget( "ui.datepicker", { showWeek: false, show: true, hide: true, + value: null, // callbacks beforeOpen: null, @@ -54,17 +55,22 @@ $.widget( "ui.datepicker", { }, _create: function() { - this.date = $.date( null, this.options.dateFormat ); - - this.date.eachDay = this.options.eachDay; this.id = "ui-datepicker-" + idIncrement; idIncrement++; + if ( this.element.is( "input" ) ) { + if ( !this.options.value && this.isValid() ) { + this.options.value = this._getParsedValue(); + } this._createPicker(); } else { this.inline = true; this.picker = this.element; } + + this.date = $.date( this.options.value, this.options.dateFormat ).select(); + this.date.eachDay = this.options.eachDay; + this._on( this.picker, { "click .ui-datepicker-prev": function( event ) { event.preventDefault(); @@ -86,8 +92,9 @@ $.widget( "ui.datepicker", { }, "mousedown .ui-datepicker-calendar a": function( event ) { event.preventDefault(); - // TODO exclude clicks on lead days or handle them correctly - // TODO store/read more then just date, also required for multi month picker + + // TODO Exclude clicks on lead days or handle them correctly + // TODO Store/read more then just date, also required for multi month picker this._select( event, $( event.currentTarget ).data( "timestamp" ) ); if ( this.inline ) { this.grid.focus( 1 ); @@ -96,7 +103,7 @@ $.widget( "ui.datepicker", { "keydown .ui-datepicker-calendar": "_handleKeydown" }); - // TODO use hoverable (no delegation support)? convert to _on? + // TODO Use hoverable (no delegation support)? convert to _on? this.picker.delegate( ".ui-datepicker-header a, .ui-datepicker-calendar a", "mouseenter.datepicker mouseleave.datepicker", function() { $( this ).toggleClass( "ui-state-hover" ); }); @@ -106,7 +113,8 @@ $.widget( "ui.datepicker", { _handleKeydown: function( event ) { if ( jQuery.inArray( event.keyCode, [ 13, 33, 34, 35, 36, 37, 38, 39, 40 ] ) === -1 ) { - //only interested navigation keys + + // Only interested navigation keys return; } event.preventDefault(); @@ -156,7 +164,7 @@ $.widget( "ui.datepicker", { newId = this.id + "-" + this.date.day(); newCell = $( "#" + newId ); - // TODO unnecessary optimization? is it really needed? + // TODO Unnecessary optimization? is it really needed? if ( !newCell.length ) { return; } @@ -171,7 +179,8 @@ $.widget( "ui.datepicker", { _createPicker: function() { this.picker = $( "
      " ) .addClass( "ui-front" ) - // TODO add a ui-datepicker-popup class, move position:absolte to that + + // TODO Add a ui-datepicker-popup class, move position:absolute to that .css( "position", "absolute" ) .uniqueId() .hide(); @@ -186,6 +195,7 @@ $.widget( "ui.datepicker", { keydown: function( event ) { switch ( event.keyCode ) { case $.ui.keyCode.TAB: + // Waiting for close() will make popup hide too late, which breaks tab key behavior this.picker.hide(); this.close( event ); @@ -217,6 +227,8 @@ $.widget( "ui.datepicker", { } } break; + + // TODO This is not in specs, keep? case $.ui.keyCode.END: if ( event.ctrlKey ) { this.element.val( "" ); @@ -230,12 +242,12 @@ $.widget( "ui.datepicker", { }, keyup: function() { if ( this.isValid() && !this.inline ) { - this.date.setTime( this.element.val() ).select(); + this.date.setTime( this._getParsedValue() ).select(); this.refresh(); } }, mousedown: function( event ) { - if (this.isOpen) { + if ( this.isOpen ) { suppressExpandOnFocus = true; this.close(); return; @@ -262,7 +274,8 @@ $.widget( "ui.datepicker", { this._on( this.picker, { focusout: function( event ) { - // use a timer to allow click to clear it and letting that + + // Use a timer to allow click to clear it and letting that // handle the closing instead of opening again // also allows tabbing inside the calendar without it closing this.closeTimer = this._delay( function() { @@ -275,7 +288,8 @@ $.widget( "ui.datepicker", { mouseup: function() { clearTimeout( this.closeTimer ); }, - // TODO on TAB (or shift TAB), make sure it ends up on something useful in DOM order + + // TODO On TAB (or shift TAB), make sure it ends up on something useful in DOM order keyup: function( event ) { if ( event.keyCode === $.ui.keyCode.ESCAPE && this.picker.is( ":visible" ) ) { this.close( event ); @@ -312,6 +326,7 @@ $.widget( "ui.datepicker", { return element; }, + _createTmpl: function() { this._createDatepicker(); this.picker.find( "button" ).button(); @@ -319,10 +334,12 @@ $.widget( "ui.datepicker", { if ( this.inline ) { this.picker.children().addClass( "ui-datepicker-inline" ); } - // against display:none in datepicker.css + + // Against display:none in datepicker.css this.picker.find( ".ui-datepicker" ).css( "display", "block" ); this.grid = this.picker.find( ".ui-datepicker-calendar" ); }, + _createDatepicker: function() { var multiClasses = [], pickerHtml = ""; @@ -345,6 +362,7 @@ $.widget( "ui.datepicker", { .html( pickerHtml ) .appendTo( this.picker ); }, + _buildMultiplePicker: function() { var headerClass, html = "", @@ -353,6 +371,7 @@ $.widget( "ui.datepicker", { i = 0; for ( i; i < months.length; i++ ) { + // TODO Shouldn't we pass date as a parameter to build* fns instead of setting this.date? this.date = months[ i ]; headerClass = months[ i ].first ? "ui-corner-left" : @@ -465,6 +484,7 @@ $.widget( "ui.datepicker", { }, _buildGridBody: function() { + // this.date.days() is not cached, and it has O(n^2) complexity. It is run O(n) times. So, it equals O(n^3). Not good at all. Caching. var days = this.date.days(), i = 0, @@ -572,8 +592,9 @@ $.widget( "ui.datepicker", { // with the prev and next links would cause loss of focus issues because the links being // interacted with will disappear while focused. refresh: function() { - //determine which day gridcell to focus after refresh - //TODO: Prevent disabled cells from being focused + + // Determine which day gridcell to focus after refresh + // TODO: Prevent disabled cells from being focused if ( this.options.numberOfMonths === 1 ) { this.grid = $( this._buildGrid() ); $( ".ui-datepicker-title", this.picker ).html( this._buildTitle() ); @@ -605,10 +626,6 @@ $.widget( "ui.datepicker", { return; } - // TODO explain this - this.date = $.date( this.element.val(), this.options.dateFormat ); - this.date.eachDay = this.options.eachDay; - this.date.select(); this.refresh(); this.picker @@ -620,8 +637,8 @@ $.widget( "ui.datepicker", { this._show( this.picker, this.options.show ); - // take trigger out of tab order to allow shift-tab to skip trigger - // TODO does this really make sense? related bug: tab-shift moves focus to last element on page + // Take trigger out of tab order to allow shift-tab to skip trigger + // TODO Does this really make sense? related bug: tab-shift moves focus to last element on page this.element.attr( "tabindex", -1 ); this.isOpen = true; @@ -649,46 +666,39 @@ $.widget( "ui.datepicker", { }, _buildPosition: function() { - return $.extend( {}, { - of: this.element - }, this.options.position ); + return $.extend( { of: this.element }, this.options.position ); }, _select: function( event, time ) { - this.date.setTime( time ).select(); - this.refresh(); + this._setOption( "value", new Date( time ) ); if ( !this.inline ) { - this.element.val( this.date.format() ); this.close(); this._focusTrigger(); } this._trigger( "select", event, { - // TODO replace with value option to initialise and read - date: this.date.format() - }); - }, - _value: function( value ) { - this.date.setTime( value ).select(); - if ( !this.inline ) { - this.element.val( this.date.format() ); - } - this.refresh(); + // TODO Replace with value option to initialise and read + date: this.value() + }); }, value: function( value ) { if ( arguments.length ) { - this._value( value ); + this._setOption( "value", Globalize.parseDate( value, this.options.dateFormat ) ); } else { - return this.isValid() ? this.date.format() : this.element.val(); + if ( this.inline ) { + return Globalize.format( this.date.selected(), this.options.dateFormat ); + } else { + return this.element.val(); + } } }, valueAsDate: function( value ) { if ( arguments.length ) { - this._value( value ); + this._setOption( "value", value ); } else { - return this.isValid() ? this.date.date() : null; + return this.option( "value" ); } }, @@ -697,7 +707,7 @@ $.widget( "ui.datepicker", { return true; } - return Globalize.parseDate( this.element.val(), this.options.dateFormat ) !== null; + return this._getParsedValue() !== null; }, _destroy: function() { @@ -715,7 +725,28 @@ $.widget( "ui.datepicker", { return this.picker; }, + _getParsedValue: function() { + return Globalize.parseDate( this.element.val(), this.options.dateFormat ); + }, + + option: function( key ) { + if ( arguments.length === 0 || ( arguments.length === 1 && key === "value" ) ) { + this.options.value = this.inline ? this.date.selected() : this._getParsedValue(); + } + return this._superApply( arguments ); + }, + _setOption: function( key, value ) { + if ( key === "value" ) { + if ( $.type( value ) === "date" ) { + this.date.setTime( value.getTime() ).select(); + this.refresh(); + if ( !this.inline ) { + this.element.val( this.date.format() ); + } + } + } + this._super( key, value ); if ( key === "appendTo" ) { From cf055edd00fa049c581988da16cd625de9da95d1 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Tue, 3 Jun 2014 23:18:51 +0200 Subject: [PATCH 30/53] Calendar: Add calendar widget Add calendar widget by copying and renaming datepicker widget files. Remove datepicker functionality, options and methods from Calendar. Remove calendar functionality, options and methods from Datepicker. Adjust tests due to split and changed specification. Remove duplicated demo files and fix some demos. Simplify calendar generation, use CSS instead of inline styles. Fix destroy method. Make use of uniqueId method. Fix focus highlighting when month is changed. Add version property. Add common unit tests. Fix input keyboard handling. --- Gruntfile.js | 1 + build/tasks/testswarm.js | 1 + demos/{datepicker => calendar}/buttonbar.html | 8 +- .../inline.html => calendar/default.html} | 10 +- .../dropdown-month-year.html | 8 +- demos/calendar/index.html | 22 + demos/calendar/localization.html | 41 + demos/{datepicker => calendar}/min-max.html | 8 +- .../multiple-calendars.html | 10 +- demos/calendar/multiple-months.html | 33 + .../other-months.html | 10 +- demos/{datepicker => calendar}/show-week.html | 10 +- demos/datepicker/alt-field.html | 5 +- demos/datepicker/animation.html | 1 + demos/datepicker/date-formats.html | 33 +- demos/datepicker/date-range.html | 49 - demos/datepicker/default.html | 1 + demos/datepicker/icon-trigger.html | 1 + demos/datepicker/index.html | 14 +- demos/datepicker/localization.html | 1 + demos/index.html | 1 + tests/unit/all.html | 1 + tests/unit/calendar/all.html | 26 + tests/unit/calendar/calendar.html | 46 + tests/unit/calendar/calendar_common.js | 14 + tests/unit/calendar/calendar_core.js | 365 ++++++ tests/unit/calendar/calendar_events.js | 0 tests/unit/calendar/calendar_methods.js | 71 ++ tests/unit/calendar/calendar_options.js | 175 +++ tests/unit/calendar/calendar_test_helpers.js | 22 + tests/unit/datepicker/datepicker.html | 5 +- tests/unit/datepicker/datepicker_common.js | 22 +- tests/unit/datepicker/datepicker_core.js | 408 +----- tests/unit/datepicker/datepicker_methods.js | 62 +- tests/unit/datepicker/datepicker_options.js | 1130 +---------------- .../datepicker/datepicker_test_helpers.js | 4 +- tests/unit/index.html | 1 + themes/base/base.css | 1 + themes/base/calendar.css | 178 +++ themes/base/datepicker.css | 170 +-- ui/calendar.js | 503 ++++++++ ui/datepicker.js | 573 ++------- 42 files changed, 1739 insertions(+), 2306 deletions(-) rename demos/{datepicker => calendar}/buttonbar.html (84%) rename demos/{datepicker/inline.html => calendar/default.html} (69%) rename demos/{datepicker => calendar}/dropdown-month-year.html (82%) create mode 100644 demos/calendar/index.html create mode 100644 demos/calendar/localization.html rename demos/{datepicker => calendar}/min-max.html (81%) rename demos/{datepicker => calendar}/multiple-calendars.html (75%) create mode 100644 demos/calendar/multiple-months.html rename demos/{datepicker => calendar}/other-months.html (77%) rename demos/{datepicker => calendar}/show-week.html (77%) delete mode 100644 demos/datepicker/date-range.html create mode 100644 tests/unit/calendar/all.html create mode 100644 tests/unit/calendar/calendar.html create mode 100644 tests/unit/calendar/calendar_common.js create mode 100644 tests/unit/calendar/calendar_core.js create mode 100644 tests/unit/calendar/calendar_events.js create mode 100644 tests/unit/calendar/calendar_methods.js create mode 100644 tests/unit/calendar/calendar_options.js create mode 100644 tests/unit/calendar/calendar_test_helpers.js create mode 100644 themes/base/calendar.css create mode 100644 ui/calendar.js diff --git a/Gruntfile.js b/Gruntfile.js index b8610b6f641..3d9a11963db 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -28,6 +28,7 @@ var "core", "accordion", "autocomplete", + "calendar", "button", "datepicker", "dialog", diff --git a/build/tasks/testswarm.js b/build/tasks/testswarm.js index a147b8f573c..0d500ef09dd 100644 --- a/build/tasks/testswarm.js +++ b/build/tasks/testswarm.js @@ -14,6 +14,7 @@ var versions = { "Accordion": "accordion/accordion.html", "Autocomplete": "autocomplete/autocomplete.html", "Button": "button/button.html", + "Calendar": "calendar/calendar.html", "Core": "core/core.html", "Core_deprecated": "core/core_deprecated.html", "Datepicker": "datepicker/datepicker.html", diff --git a/demos/datepicker/buttonbar.html b/demos/calendar/buttonbar.html similarity index 84% rename from demos/datepicker/buttonbar.html rename to demos/calendar/buttonbar.html index 446bc9fe495..498fe1041f4 100644 --- a/demos/datepicker/buttonbar.html +++ b/demos/calendar/buttonbar.html @@ -2,7 +2,7 @@ - jQuery UI Datepicker - Display button bar + jQuery UI Calendar - Display button bar @@ -11,12 +11,12 @@ + - @@ -11,21 +11,21 @@ + - -Date:
      +
      -

      Display the datepicker embedded in the page instead of in an overlay. Simply call .datepicker() on a div instead of an input.

      +

      The calendar is a widget for selecting a date using a visual calendar representation.

      diff --git a/demos/datepicker/dropdown-month-year.html b/demos/calendar/dropdown-month-year.html similarity index 82% rename from demos/datepicker/dropdown-month-year.html rename to demos/calendar/dropdown-month-year.html index c6102917f43..050a89157ff 100644 --- a/demos/datepicker/dropdown-month-year.html +++ b/demos/calendar/dropdown-month-year.html @@ -2,7 +2,7 @@ - jQuery UI Datepicker - Display month & year menus + jQuery UI Calendar - Display month & year menus @@ -11,12 +11,12 @@ + - + + + + + + + + + + + + + +
      + + +
      +

      Localize the calendar calendar language and format (English / Western formatting is the default). The calendar includes built-in support for languages that read right-to-left, such as Arabic and Hebrew.

      +
      + + diff --git a/demos/datepicker/min-max.html b/demos/calendar/min-max.html similarity index 81% rename from demos/datepicker/min-max.html rename to demos/calendar/min-max.html index 6dcc16a48bc..c449bbdd55c 100644 --- a/demos/datepicker/min-max.html +++ b/demos/calendar/min-max.html @@ -2,7 +2,7 @@ - jQuery UI Datepicker - Restrict date range + jQuery UI Calendar - Restrict date range @@ -11,18 +11,18 @@ + - -

      Date:

      +

      Restrict the range of selectable dates with the minDate and maxDate options. Set the beginning and end dates as actual dates (new Date(2009, 1 - 1, 26)), as a numeric offset from today (-20), or as a string of periods and units ('+1M +10D'). For the last, use 'D' for days, 'W' for weeks, 'M' for months, or 'Y' for years.

      diff --git a/demos/datepicker/multiple-calendars.html b/demos/calendar/multiple-calendars.html similarity index 75% rename from demos/datepicker/multiple-calendars.html rename to demos/calendar/multiple-calendars.html index 3d8278cd625..f67609d143e 100644 --- a/demos/datepicker/multiple-calendars.html +++ b/demos/calendar/multiple-calendars.html @@ -2,7 +2,7 @@ - jQuery UI Datepicker - Display multiple months + jQuery UI Calendar - Display multiple months @@ -11,12 +11,12 @@ + - + + + + + + + + + + + + + +
      + +
      +

      Set the numberOfMonths option to an integer of 2 or more to show multiple months in a single calendar.

      +
      + + diff --git a/demos/datepicker/other-months.html b/demos/calendar/other-months.html similarity index 77% rename from demos/datepicker/other-months.html rename to demos/calendar/other-months.html index bfb3f1af36f..0228c62df5b 100644 --- a/demos/datepicker/other-months.html +++ b/demos/calendar/other-months.html @@ -2,7 +2,7 @@ - jQuery UI Datepicker - Dates in other months + jQuery UI Calendar - Dates in other months @@ -11,12 +11,12 @@ + - @@ -11,12 +11,12 @@ + - + + diff --git a/demos/datepicker/date-formats.html b/demos/datepicker/date-formats.html index 2c5045b6f33..97ef529a609 100644 --- a/demos/datepicker/date-formats.html +++ b/demos/datepicker/date-formats.html @@ -12,29 +12,24 @@ + - - - - - - - - - - - - - - - - - - -
      -

      Select the date range to search for.

      -
      - - diff --git a/demos/datepicker/default.html b/demos/datepicker/default.html index be0c453c2b3..b2439b29ab3 100644 --- a/demos/datepicker/default.html +++ b/demos/datepicker/default.html @@ -12,6 +12,7 @@ + + diff --git a/demos/datepicker/index.html b/demos/datepicker/index.html index d9c8dfc10da..5587564831f 100644 --- a/demos/datepicker/index.html +++ b/demos/datepicker/index.html @@ -9,19 +9,11 @@ diff --git a/demos/datepicker/localization.html b/demos/datepicker/localization.html index 61aa7c9a2da..701e5c36b66 100644 --- a/demos/datepicker/localization.html +++ b/demos/datepicker/localization.html @@ -11,6 +11,7 @@ + diff --git a/demos/index.html b/demos/index.html index f37874a4481..c4a0fdf9501 100644 --- a/demos/index.html +++ b/demos/index.html @@ -11,6 +11,7 @@
    1. accordion
    2. autocomplete
    3. button
    4. +
    5. calendar
    6. datepicker
    7. dialog
    8. draggable
    9. diff --git a/tests/unit/all.html b/tests/unit/all.html index f8a4636d051..85e21077412 100644 --- a/tests/unit/all.html +++ b/tests/unit/all.html @@ -21,6 +21,7 @@ "button/button.html", "core/core.html", "core/core_deprecated.html", + "calendar/calendar.html", "datepicker/datepicker.html", "dialog/dialog.html", "draggable/draggable.html", diff --git a/tests/unit/calendar/all.html b/tests/unit/calendar/all.html new file mode 100644 index 00000000000..5b22b047024 --- /dev/null +++ b/tests/unit/calendar/all.html @@ -0,0 +1,26 @@ + + + + + jQuery UI Calendar Test Suite + + + + + + + + + + + + + +
      +
      + +
      + + diff --git a/tests/unit/calendar/calendar.html b/tests/unit/calendar/calendar.html new file mode 100644 index 00000000000..ed536414fbc --- /dev/null +++ b/tests/unit/calendar/calendar.html @@ -0,0 +1,46 @@ + + + + + jQuery UI Calendar Test Suite + + + + + + + + + + + + + + + + + + + + + + +
      +
      + +
      +
      + +
      + + diff --git a/tests/unit/calendar/calendar_common.js b/tests/unit/calendar/calendar_common.js new file mode 100644 index 00000000000..f3a01171ba5 --- /dev/null +++ b/tests/unit/calendar/calendar_common.js @@ -0,0 +1,14 @@ +TestHelpers.commonWidgetTests( "calendar", { + defaults: { + dateFormat: { date: "short" }, + disabled: false, + eachDay: $.noop, + numberOfMonths: 1, + showWeek: false, + value: null, + + // callbacks + create: null, + select: null + } +}); diff --git a/tests/unit/calendar/calendar_core.js b/tests/unit/calendar/calendar_core.js new file mode 100644 index 00000000000..7c216b740bc --- /dev/null +++ b/tests/unit/calendar/calendar_core.js @@ -0,0 +1,365 @@ +(function( $ ) { + +module( "calendar: core" ); + +TestHelpers.testJshint( "calendar" ); + +test( "base structure", function() { + expect( 22 ); + + var header, title, table, thead, week, child, buttonpane, + element = $( "#calendar" ).calendar(), + dp = element.calendar( "widget" ); + + function step1() { + ok( !dp.is( ".ui-calendar-rtl" ), "Structure - not right-to-left" ); + ok( !dp.is( ".ui-calendar-multi" ), "Structure - not multi-month" ); + equal( dp.children().length, 3, "Structure - child count (header, calendar)" ); + + header = dp.children( ":first" ); + ok( header.is( "div.ui-calendar-header" ), "Structure - header division" ); + equal( header.children().length, 3, "Structure - header child count" ); + ok( header.children( ":first" ).is( ".ui-calendar-prev" ) && header.children( ":first" ).html() !== "", "Structure - prev link" ); + ok( header.children( ":eq(1)" ).is( ".ui-calendar-next" ) && header.children( ":eq(1)" ).html() !== "", "Structure - next link" ); + + title = header.children( ":last" ).children( ":first" ); + ok( title.is( "div.ui-calendar-title" ) && title.html() !== "", "Structure - title division" ); + equal( title.children().length, 2, "Structure - title child count" ); + ok( title.children( ":first" ).is( "span.ui-calendar-month" ) && title.children( ":first" ).text() !== "", "Structure - month text" ); + ok( title.children( ":last" ).is( "span.ui-calendar-year" ) && title.children( ":last" ).text() !== "", "Structure - year text" ); + + table = dp.children( ":eq(1)" ); + ok( table.is( "table.ui-calendar-calendar" ), "Structure - month table" ); + ok( table.children( ":first" ).is( "thead" ), "Structure - month table thead" ); + + thead = table.children( ":first" ).children( ":first" ); + ok( thead.is( "tr" ), "Structure - month table title row" ); + equal( thead.find( "th" ).length, 7, "Structure - month table title cells" ); + ok( table.children( ":eq(1)" ).is( "tbody" ), "Structure - month table body" ); + ok( table.children( ":eq(1)" ).children( "tr" ).length >= 4, "Structure - month table week count" ); + + week = table.children( ":eq(1)" ).children( ":first" ); + ok( week.is( "tr" ), "Structure - month table week row" ); + equal( week.children().length, 7, "Structure - week child count" ); + + element.calendar( "destroy" ); + + step2(); + } + + function step2() { + element = $( "#calendar" ).calendar( { numberOfMonths: 2 } ); + dp = element.calendar( "widget" ); + + ok( dp.is( ".ui-calendar-multi" ), "Structure multi [2] - multi-month" ); + equal( dp.children().length, 4, "Structure multi [2] - child count" ); + + child = dp.children( ":eq(2)" ); + ok( child.is( "div.ui-calendar-row-break" ), "Structure multi [2] - row break" ); + + element.calendar( "destroy" ); + } + + step1(); +}); + +test( "Localization", function() { + expect( 5 ); + + var defaultLocale = Globalize.locale(), + element = $( "#calendar" ), + date = new Date( 2014, 0, 1 ), + initCalendar = function() { + element + .calendar() + .calendar( "valueAsDate", date ); + }, + testLocalization = function( message ) { + equal( + element.find( ".ui-calendar-month" ).text(), + "Januar", message + "titlebar year" + ); + equal( + element.find( "thead th:first" ).text(), + "Mo.", message + "teader first day" + ); + equal( + element.find( "thead th:last" ).text(), + "So.", message + "header last day" + ); + equal( + element.find( ".ui-calendar-prev" ).text(), + "", message + "header next" + ); + }; + + Globalize.locale( "de-DE" ); + initCalendar(); + testLocalization( "Init: " ); + element.calendar( "destroy" ); + + Globalize.locale( defaultLocale.locale ); +}); + +asyncTest( "keyboard handling", function() { + expect( 10 ); + + var element = $( "#calendar" ); + + function step1() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar + .focusGrid( element ) + .simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2013, 12 - 1, 31 ), + "Keystroke left to switch to previous day" + ); + element.calendar( "destroy" ); + step2(); + }, 50 ); + } + + function step2() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ) + .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2014, 1 - 1, 2 ), + "Keystroke right to switch to next day" + ); + step3(); + } + + function step3() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2013, 12 - 1, 25 ), + "Keystroke up to move to the previous week" + ); + element.calendar( "destroy" ); + step4(); + }, 50 ); + } + + function step4() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2014, 1 - 1, 8 ), + "Keystroke down to move to the next week" + ); + element.calendar( "destroy" ); + step5(); + }, 50 ); + } + + function step5() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2013, 12 - 1, 1 ), + "Keystroke Page Up moves date to previous month" + ); + element.calendar( "destroy" ); + step6(); + }, 50 ); + } + + function step6() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP, altKey: true } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2013, 1 - 1, 1 ), + "Keystroke Page Up + Ctrl moves date to previous year" + ); + element.calendar( "destroy" ); + step7(); + }, 50 ); + } + + function step7() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2014, 2 - 1, 1 ), + "Keystroke Page Down moves date to next month" + ); + element.calendar( "destroy" ); + step8(); + }, 50 ); + } + + function step8() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2015, 1 - 1, 1 ), + "Keystroke Page Down + Ctrl moves date to next year" + ); + element.calendar( "destroy" ); + step9(); + }, 50 ); + } + + // Check for moving to short months + function step9() { + element.calendar({ value: new Date( 2014, 3 - 1, 31 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2014, 2 - 1, 28 ), + "Keystroke Page Up and short months" + ); + element.calendar( "destroy" ); + step10(); + }, 50 ); + } + + function step10() { + element.calendar({ value: new Date( 2016, 1 - 1, 30 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2016, 2 - 1, 29 ), + "Keystroke Page Down and leap years" + ); + element.calendar( "destroy" ); + start(); + }, 50 ); + } + + step1(); +}); + +asyncTest( "mouse", function() { + expect( 6 ); + + var element = $( "#calendar" ).calendar(), + date = new Date(); + + function step1() { + $( "tbody a:contains(10)", element ).simulate( "mousedown" ); + date.setDate( 10 ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + date, + "Mouse click" + ); + + element.calendar( "option", "value", new Date( 2008, 2 - 1, 4) ); + $( ".ui-calendar-calendar tbody a:contains(12)", element ).simulate( "mousedown" ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2008, 2 - 1, 12 ), + "Mouse click - preset" + ); + + // Previous/next + element.calendar( "option", "value", new Date( 2008, 2 - 1, 4) ); + $( ".ui-calendar-prev", element ).simulate( "click" ); + $( ".ui-calendar-calendar tbody a:contains(16)", element ).simulate( "mousedown" ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2008, 1 - 1, 16 ), + "Mouse click - previous" + ); + + element.calendar( "option", "value", new Date( 2008, 2 - 1, 4) ); + $( ".ui-calendar-next", element ).simulate( "click" ); + $( ".ui-calendar-calendar tbody a:contains(18)", element ).simulate( "mousedown" ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2008, 3 - 1, 18 ), + "Mouse click - next" + ); + + step2(); + } + + // Previous/next with minimum/maximum + function step2() { + element.calendar( "destroy" ); + element.calendar({ + value: new Date( 2008, 3 - 1, 4), + min: new Date( 2008, 2 - 1, 2 ), + max: new Date( 2008, 2 - 1, 26 ) + }); + + $( ".ui-calendar-prev", element ).simulate( "click" ); + $( "tbody a:contains(16)", element ).simulate( "mousedown" ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2008, 2 - 1, 16 ), + "Mouse click - previous + min/max" + ); + step3(); + } + + function step3() { + element.calendar( "destroy" ); + element.calendar({ + value: new Date( 2008, 1 - 1, 4), + min: new Date( 2008, 2 - 1, 2 ), + max: new Date( 2008, 2 - 1, 26 ) + }); + + $( ".ui-calendar-next", element ).simulate( "click" ); + $( "tbody a:contains(18)", element ).simulate( "mousedown" ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2008, 2 - 1, 18 ), + "Mouse click - next + min/max" + ); + start(); + } + + step1(); +}); + +})( jQuery ); diff --git a/tests/unit/calendar/calendar_events.js b/tests/unit/calendar/calendar_events.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/unit/calendar/calendar_methods.js b/tests/unit/calendar/calendar_methods.js new file mode 100644 index 00000000000..0603f5d9551 --- /dev/null +++ b/tests/unit/calendar/calendar_methods.js @@ -0,0 +1,71 @@ +(function( $ ) { + +module( "calendar: methods" ); + +test( "destroy", function() { + expect( 1 ); + + domEqual( "#calendar", function() { + $( "#calendar" ).calendar().calendar( "destroy" ); + }); +}); + +test( "enable / disable", function() { + expect( 6 ); + + var element = $( "#calendar" ).calendar(); + + ok( !element.calendar( "option", "disabled" ), "initially enabled" ); + ok( !element.hasClass( "ui-calendar-disabled" ), "does not have disabled class name" ); + + element.calendar( "disable" ); + ok( element.calendar( "option", "disabled" ), "disabled option is set" ); + ok( element.hasClass( "ui-calendar-disabled" ), "calendar has disabled class name" ); + + element.calendar( "enable" ); + ok( !element.calendar( "option", "disabled" ), "enabled after enable() call" ); + ok( !element.hasClass( "ui-calendar-disabled" ), "no longer has disabled class name" ); +}); + +test( "widget", function() { + expect( 1 ); + + var element = $( "#calendar" ).calendar(), + widget = element.calendar( "widget" ); + + strictEqual( widget[ 0 ], element[ 0 ] ); +}); + +test( "value", function() { + expect( 3 ); + var element = $( "#calendar" ).calendar(); + + element.calendar( "value", "1/1/14" ); + ok( element.find( "a[data-timestamp]:first" ).hasClass( "ui-state-active" ), "first day marked as selected" ); + equal( element.calendar( "value" ), "1/1/14", "getter" ); + + element.calendar( "value", "abc" ); + equal( element.calendar( "value" ), "1/1/14", "Setting invalid values should be ignored." ); +}); + +test( "valueAsDate", function() { + expect( 4 ); + + var element = $( "#calendar" ).calendar(), + date1 = new Date( 2008, 6 - 1, 4 ), + date2 = new Date(); + + element.calendar( "valueAsDate", new Date( 2014, 0, 1 ) ); + ok( element.find( "a[data-timestamp]:first" ).hasClass( "ui-state-active" ), "First day marked as selected" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2014, 0, 1 ), "Getter" ); + + element.calendar( "destroy" ); + + element.calendar(); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), date2, "Set date - default" ); + + element.calendar( "valueAsDate", date1 ); + TestHelpers.calendar.equalsDate(element.calendar( "valueAsDate" ), date1, "Set date - 2008-06-04" ); +}); + +})( jQuery ); diff --git a/tests/unit/calendar/calendar_options.js b/tests/unit/calendar/calendar_options.js new file mode 100644 index 00000000000..454e7400579 --- /dev/null +++ b/tests/unit/calendar/calendar_options.js @@ -0,0 +1,175 @@ +(function( $ ) { + +module( "calendar: options" ); + +test( "dateFormat", function() { + expect( 2 ); + var element = $( "#calendar" ).calendar({ + value: "1/1/14" + }), + firstDayLink = element.calendar( "widget" ).find( "td[id]:first a" ); + + firstDayLink.trigger( "mousedown" ); + equal( element.calendar( "value" ), "1/1/14", "default formatting" ); + + element.calendar( "option", "dateFormat", { date: "full" } ); + equal( element.calendar( "value" ), "Wednesday, January 1, 2014", "updated formatting" ); +}); + +test( "eachDay", function() { + expect( 5 ); + var timestamp, + input = $( "#calendar" ).calendar(), + picker = input.calendar( "widget" ), + firstCell = picker.find( "td[id]:first" ); + + equal( firstCell.find( "a" ).length, 1, "days are selectable by default" ); + timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 ); + equal( new Date( timestamp ).getDate(), 1, "first available day is the 1st by default" ); + + // Do not render the 1st of the month + input.calendar( "option", "eachDay", function( day ) { + if ( day.date === 1 ) { + day.render = false; + } + }); + firstCell = picker.find( "td[id]:first" ); + timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 ); + equal( new Date( timestamp ).getDate(), 2, "first available day is the 2nd" ); + + // Display the 1st of the month but make it not selectable. + input.calendar( "option", "eachDay", function( day ) { + if ( day.date === 1 ) { + day.selectable = false; + } + }); + firstCell = picker.find( "td[id]:first" ); + equal( firstCell.find( "a" ).length, 0, "the 1st is not selectable" ); + + input.calendar( "option", "eachDay", function( day ) { + if ( day.date === 1 ) { + day.extraClasses = "ui-custom"; + } + }); + ok( picker.find( "td[id]:first a" ).hasClass( "ui-custom" ), "extraClasses applied" ); + + input.calendar( "destroy" ); +}); + +test( "showWeek", function() { + expect( 7 ); + var input = $( "#calendar" ).calendar(), + container = input.calendar( "widget" ); + + equal( container.find( "thead th" ).length, 7, "just 7 days, no column cell" ); + equal( container.find( ".ui-calendar-week-col" ).length, 0, + "no week column cells present" ); + input.calendar( "destroy" ); + + input = $( "#calendar" ).calendar({ showWeek: true }); + container = input.calendar( "widget" ); + equal( container.find( "thead th" ).length, 8, "7 days + a column cell" ); + ok( container.find( "thead th:first" ).is( ".ui-calendar-week-col" ), + "first cell should have ui-datepicker-week-col class name" ); + equal( container.find( ".ui-calendar-week-col" ).length, + container.find( "tr" ).length, "one week cell for each week" ); + input.calendar( "destroy" ); + + input = $( "#calendar" ).calendar(); + container = input.calendar( "widget" ); + equal( container.find( "thead th" ).length, 7, "no week column" ); + input.calendar( "option", "showWeek", true ); + equal( container.find( "thead th" ).length, 8, "supports changing option after init" ); +}); + +test( "min / max", function() { + expect( 0 ); +}); + +/* +// TODO: Move this to $.date, Globalize or calendar widget +test( "daylightSaving", function() { + expect( 25 ); + var inp = TestHelpers.calendar.init( "#inp" ), + dp = $( "#ui-datepicker-div" ); + ok(true, "Daylight saving - " + new Date()); + // Australia, Sydney - AM change, southern hemisphere + inp.val( "04/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(6) a", dp).simulate( "click" ); + equal(inp.val(), "04/05/2008", "Daylight saving - Australia 04/05/2008" ); + inp.val( "04/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(7) a", dp).simulate( "click" ); + equal(inp.val(), "04/06/2008", "Daylight saving - Australia 04/06/2008" ); + inp.val( "04/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(8) a", dp).simulate( "click" ); + equal(inp.val(), "04/07/2008", "Daylight saving - Australia 04/07/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(6) a", dp).simulate( "click" ); + equal(inp.val(), "10/04/2008", "Daylight saving - Australia 10/04/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(7) a", dp).simulate( "click" ); + equal(inp.val(), "10/05/2008", "Daylight saving - Australia 10/05/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(8) a", dp).simulate( "click" ); + equal(inp.val(), "10/06/2008", "Daylight saving - Australia 10/06/2008" ); + // Brasil, Brasilia - midnight change, southern hemisphere + inp.val( "02/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(20) a", dp).simulate( "click" ); + equal(inp.val(), "02/16/2008", "Daylight saving - Brasil 02/16/2008" ); + inp.val( "02/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(21) a", dp).simulate( "click" ); + equal(inp.val(), "02/17/2008", "Daylight saving - Brasil 02/17/2008" ); + inp.val( "02/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(22) a", dp).simulate( "click" ); + equal(inp.val(), "02/18/2008", "Daylight saving - Brasil 02/18/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(13) a", dp).simulate( "click" ); + equal(inp.val(), "10/11/2008", "Daylight saving - Brasil 10/11/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(14) a", dp).simulate( "click" ); + equal(inp.val(), "10/12/2008", "Daylight saving - Brasil 10/12/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(15) a", dp).simulate( "click" ); + equal(inp.val(), "10/13/2008", "Daylight saving - Brasil 10/13/2008" ); + // Lebanon, Beirut - midnight change, northern hemisphere + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(34) a", dp).simulate( "click" ); + equal(inp.val(), "03/29/2008", "Daylight saving - Lebanon 03/29/2008" ); + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(35) a", dp).simulate( "click" ); + equal(inp.val(), "03/30/2008", "Daylight saving - Lebanon 03/30/2008" ); + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(36) a", dp).simulate( "click" ); + equal(inp.val(), "03/31/2008", "Daylight saving - Lebanon 03/31/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(27) a", dp).simulate( "click" ); + equal(inp.val(), "10/25/2008", "Daylight saving - Lebanon 10/25/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(28) a", dp).simulate( "click" ); + equal(inp.val(), "10/26/2008", "Daylight saving - Lebanon 10/26/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(29) a", dp).simulate( "click" ); + equal(inp.val(), "10/27/2008", "Daylight saving - Lebanon 10/27/2008" ); + // US, Eastern - AM change, northern hemisphere + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(13) a", dp).simulate( "click" ); + equal(inp.val(), "03/08/2008", "Daylight saving - US 03/08/2008" ); + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(14) a", dp).simulate( "click" ); + equal(inp.val(), "03/09/2008", "Daylight saving - US 03/09/2008" ); + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(15) a", dp).simulate( "click" ); + equal(inp.val(), "03/10/2008", "Daylight saving - US 03/10/2008" ); + inp.val( "11/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(6) a", dp).simulate( "click" ); + equal(inp.val(), "11/01/2008", "Daylight saving - US 11/01/2008" ); + inp.val( "11/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(7) a", dp).simulate( "click" ); + equal(inp.val(), "11/02/2008", "Daylight saving - US 11/02/2008" ); + inp.val( "11/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(8) a", dp).simulate( "click" ); + equal(inp.val(), "11/03/2008", "Daylight saving - US 11/03/2008" ); + }); + */ + +})(jQuery); diff --git a/tests/unit/calendar/calendar_test_helpers.js b/tests/unit/calendar/calendar_test_helpers.js new file mode 100644 index 00000000000..94f3644c3a2 --- /dev/null +++ b/tests/unit/calendar/calendar_test_helpers.js @@ -0,0 +1,22 @@ +TestHelpers.calendar = { + addMonths: function( date, offset ) { + var maxDay = 32 - new Date( date.getFullYear(), date.getMonth() + offset, 32 ).getDate(); + date.setDate( Math.min( date.getDate(), maxDay ) ); + date.setMonth( date.getMonth() + offset ); + return date; + }, + equalsDate: function( d1, d2, message ) { + if ( !d1 || !d2 ) { + ok( false, message + " - missing date" ); + return; + } + d1 = new Date( d1.getFullYear(), d1.getMonth(), d1.getDate() ); + d2 = new Date( d2.getFullYear(), d2.getMonth(), d2.getDate() ); + equal( d1.toString(), d2.toString(), message ); + }, + focusGrid: function( element ) { + element.find( "table:tabbable" ).simulate( "focus" ); + + return $( ":focus" ); + } +}; diff --git a/tests/unit/datepicker/datepicker.html b/tests/unit/datepicker/datepicker.html index f55dd97900c..e85b4926084 100644 --- a/tests/unit/datepicker/datepicker.html +++ b/tests/unit/datepicker/datepicker.html @@ -14,11 +14,12 @@ @@ -25,7 +32,7 @@
      -

      Restrict the range of selectable dates with the minDate and maxDate options. Set the beginning and end dates as actual dates (new Date(2009, 1 - 1, 26)), as a numeric offset from today (-20), or as a string of periods and units ('+1M +10D'). For the last, use 'D' for days, 'W' for weeks, 'M' for months, or 'Y' for years.

      +

      Restrict the range of selectable dates with the min and max options. Set the beginning and end dates as actual dates (new Date(2009, 1 - 1, 26)).

      diff --git a/tests/unit/calendar/calendar_common.js b/tests/unit/calendar/calendar_common.js index f3a01171ba5..49f8b5f05f6 100644 --- a/tests/unit/calendar/calendar_common.js +++ b/tests/unit/calendar/calendar_common.js @@ -3,6 +3,8 @@ TestHelpers.commonWidgetTests( "calendar", { dateFormat: { date: "short" }, disabled: false, eachDay: $.noop, + max: null, + min: null, numberOfMonths: 1, showWeek: false, value: null, diff --git a/tests/unit/calendar/calendar_methods.js b/tests/unit/calendar/calendar_methods.js index 0603f5d9551..a60ed3c4c76 100644 --- a/tests/unit/calendar/calendar_methods.js +++ b/tests/unit/calendar/calendar_methods.js @@ -49,9 +49,10 @@ test( "value", function() { }); test( "valueAsDate", function() { - expect( 4 ); + expect( 11 ); - var element = $( "#calendar" ).calendar(), + var minDate, maxDate, dateAndTimeToSet, dateAndTimeClone, + element = $( "#calendar" ).calendar(), date1 = new Date( 2008, 6 - 1, 4 ), date2 = new Date(); @@ -66,6 +67,68 @@ test( "valueAsDate", function() { element.calendar( "valueAsDate", date1 ); TestHelpers.calendar.equalsDate(element.calendar( "valueAsDate" ), date1, "Set date - 2008-06-04" ); + + // With minimum/maximum + element = $( "#calendar" ).calendar(); + date1 = new Date( 2008, 1 - 1, 4 ); + date2 = new Date( 2008, 6 - 1, 4 ); + minDate = new Date( 2008, 2 - 1, 29 ); + maxDate = new Date( 2008, 3 - 1, 28 ); + + element + .calendar( "option", { min: minDate } ) + .calendar( "valueAsDate", date2 ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + date2, "Set date min/max - value > min" + ); + + element.calendar( "valueAsDate", date1 ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + date2, + "Set date min/max - value < min" + ); + + element + .calendar( "option", { max: maxDate, min: null } ) + .calendar( "valueAsDate", date1 ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + date1, + "Set date min/max - value < max" + ); + + element.calendar( "valueAsDate", date2 ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + date1, + "Set date min/max - value > max" + ); + + element + .calendar( "option", { min: minDate } ) + .calendar( "valueAsDate", date1 ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + date1, + "Set date min/max - value < min" + ); + + element.calendar( "valueAsDate", date2 ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + date1, "Set date min/max - value > max" + ); + + dateAndTimeToSet = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); + dateAndTimeClone = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); + element.calendar( "valueAsDate", dateAndTimeToSet ); + equal( + dateAndTimeToSet.getTime(), + dateAndTimeClone.getTime(), + "Date object passed should not be changed by valueAsDate" + ); }); })( jQuery ); diff --git a/tests/unit/calendar/calendar_options.js b/tests/unit/calendar/calendar_options.js index 454e7400579..6be4f8733e8 100644 --- a/tests/unit/calendar/calendar_options.js +++ b/tests/unit/calendar/calendar_options.js @@ -83,8 +83,48 @@ test( "showWeek", function() { }); test( "min / max", function() { - expect( 0 ); -}); + expect( 7 ); + + // With existing date + var element = $( "#calendar" ).calendar(), + minDate = new Date( 2008, 2 - 1, 29 ), + maxDate = new Date( 2008, 12 - 1, 7 ); + + element + .calendar( "option", { min: minDate } ) + .calendar( "value", "6/4/08" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min" ); + + element + .calendar( "option", { min: minDate } ) + .calendar( "value", "1/4/08" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value < min" ); + + element + .calendar( "option", { min: null } ) + .calendar( "value", "6/4/08" ) + .calendar( "option", { max: maxDate } ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value < max" ); + + element + .calendar( "option", { max: maxDate } ) + .calendar( "value", "1/4/09" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - setDate > max" ); + + element + .calendar( "option", { min: minDate, max: maxDate } ) + .calendar( "value", "1/4/08" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value < min" ); + + element + .calendar( "option", { min: minDate, max: maxDate } ) + .calendar( "value", "6/4/08" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min, < max" ); + + element + .calendar( "option", { min: minDate, max: maxDate } ) + .calendar( "value", "1/4/09" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > max" );}); /* // TODO: Move this to $.date, Globalize or calendar widget diff --git a/tests/unit/datepicker/datepicker_common.js b/tests/unit/datepicker/datepicker_common.js index 4bf06d3ad14..1544db58e84 100644 --- a/tests/unit/datepicker/datepicker_common.js +++ b/tests/unit/datepicker/datepicker_common.js @@ -4,6 +4,8 @@ TestHelpers.commonWidgetTests( "datepicker", { dateFormat: { date: "short" }, disabled: false, eachDay: $.noop, + max: null, + min: null, numberOfMonths: 1, position: { my: "left top", diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 8ec477e879e..995d0c527a0 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -61,11 +61,13 @@ test( "value", function() { }); test( "valueAsDate", function() { - expect( 6 ); + expect( 13 ); - var input = TestHelpers.datepicker.init( "#datepicker" ), + var minDate, maxDate, dateAndTimeToSet, dateAndTimeClone, + input = TestHelpers.datepicker.init( "#datepicker" ), picker = input.datepicker( "widget" ), - date1 = new Date( 2008, 6 - 1, 4 ); + date1 = new Date( 2008, 6 - 1, 4 ), + date2 = new Date(); input.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); equal( input.val(), "1/1/14", "Input's value set" ); @@ -81,6 +83,36 @@ test( "valueAsDate", function() { strictEqual( input.datepicker( "valueAsDate" ), null, "Set date - default" ); input.datepicker( "valueAsDate", date1 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date - 2008-06-04" ); + + // With minimum/maximum + input = TestHelpers.datepicker.init( "#datepicker" ); + date1 = new Date( 2008, 1 - 1, 4 ); + date2 = new Date( 2008, 6 - 1, 4 ); + minDate = new Date( 2008, 2 - 1, 29 ); + maxDate = new Date( 2008, 3 - 1, 28 ); + + input.val( "" ).datepicker( "option", { min: minDate } ).datepicker( "valueAsDate", date2 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date2, "Set date min/max - value > min" ); + + input.datepicker( "valueAsDate", date1 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date2, "Set date min/max - value < min" ); + + input.val( "" ).datepicker( "option", { max: maxDate, min: null } ).datepicker( "valueAsDate", date1 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date min/max - value < max" ); + + input.datepicker( "valueAsDate", date2 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date min/max - value > max" ); + + input.val( "" ).datepicker( "option", { min: minDate } ).datepicker( "valueAsDate", date1 ); + ok( input.datepicker( "valueAsDate" ) === null, "Set date min/max - value < min" ); + + input.datepicker( "valueAsDate", date2 ); + ok( input.datepicker( "valueAsDate" ) === null, "Set date min/max - value > max" ); + + dateAndTimeToSet = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); + dateAndTimeClone = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); + input.datepicker( "valueAsDate", dateAndTimeToSet ); + equal( dateAndTimeToSet.getTime(), dateAndTimeClone.getTime(), "Date object passed should not be changed by valueAsDate" ); }); test( "isValid", function() { diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index 9b2845fec09..16546424dac 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -149,6 +149,42 @@ test( "showWeek", function() { input.datepicker( "option", "showWeek", true ); equal( container.find( "thead th" ).length, 8, "supports changing option after init" ); }); + +test( "min / max", function() { + expect( 14 ); + + var inp = TestHelpers.datepicker.init( "#datepicker" ), + minDate = new Date( 2008, 2 - 1, 29 ), + maxDate = new Date( 2008, 12 - 1, 7 ); + + inp.val( "6/4/08" ).datepicker( "option", { min: minDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min" ); + ok( inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { min: null } ).val( "1/4/08" ).datepicker( "option", { min: minDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 4 ), "Min/max - value < min" ); + ok( !inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { min: null } ).val( "6/4/08" ).datepicker( "option", { max: maxDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value < max" ); + ok( inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { max: null } ).val( "1/4/09" ).datepicker( "option", { max: maxDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2009, 1 - 1, 4 ), "Min/max - setDate > max" ); + ok( !inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { max: null } ).val( "1/4/08" ).datepicker( "option", { min: minDate, max: maxDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 4 ), "Min/max - value < min" ); + ok( !inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { max: null } ).val( "6/4/08" ).datepicker( "option", { min: minDate, max: maxDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min, < max" ); + ok( inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { max: null } ).val( "1/4/09" ).datepicker( "option", { min: minDate, max: maxDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2009, 1 - 1, 4 ), "Min/max - value > max" ); + ok( !inp.datepicker( "isValid" ) ); +}); test( "Stop datepicker from appearing with beforeOpen event handler", function() { expect( 3 ); diff --git a/ui/calendar.js b/ui/calendar.js index 20f2e6e13cd..029a44ae2c8 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -31,6 +31,8 @@ return $.widget( "ui.calendar", { options: { dateFormat: { date: "short" }, eachDay: $.noop, + max: null, + min: null, numberOfMonths: 1, showWeek: false, value: null, @@ -314,38 +316,33 @@ return $.widget( "ui.calendar", { }, _buildDayCell: function( day ) { - var contents = "", + var content = "", attributes = [ - "aria-selected" + ( day.current ? "\"true\"" : "\"false\"" ) - ]; + "role='gridcell'", + "aria-selected='" + day.current ? true : false + "'" + ], + selectable = ( day.selectable && this._isValid( new Date( day.timestamp ) ) ); if ( day.render ) { - attributes.push( "id=\"" + this.id + "-" + day.date + "\"" ); - } - - if ( day.selectable ) { - attributes.push( "aria-disabled=\"true\"" ); - } + attributes.push( "id='" + this.id + "-" + day.date + "'" ); - if ( day.render ) { - if ( day.selectable ) { - contents = this._buildDayLink( day ); - } else { - contents = this._buildDayDisplay( day ); + if ( !selectable ) { + attributes.push( "aria-disabled='true'" ); + attributes.push( "class='ui-state-disabled'" ); } + + content = this._buildDayElement( day, selectable ); } - return "" + - contents + - ""; + return "" + content + ""; }, - _buildDayLink: function( day ) { - var link, - classes = [ "ui-state-default" ], - labels = Globalize.translate( "datepicker" ); + _buildDayElement: function( day, selectable ) { + var classes = [ "ui-state-default" ], + labels = Globalize.translate( "datepicker" ), + content = ""; - if ( day === this.date ) { + if ( day === this.date && selectable ) { classes.push( "ui-state-focus" ); } if ( day.current ) { @@ -359,30 +356,18 @@ return $.widget( "ui.calendar", { classes.push( day.extraClasses.split( " " ) ); } - link = "" + day.date + ""; - if ( day.today ) { - link += ", " + - labels.currentText + ""; + classes = " class='" + classes.join( " " ) + "'"; + if ( selectable ) { + content = "" + day.date + ""; + } else { + content = "" + day.date + ""; } - return link; - }, - - _buildDayDisplay: function( day ) { - var classes = []; - - if ( day.current ) { - classes.push( "ui-state-active" ); - } if ( day.today ) { - classes.push( "ui-state-highlight" ); - } - if ( day.extraClasses ) { - classes.push( day.extraClasses.split( " " ) ); + content += ", " + labels.currentText + ""; } - return "" + day.date + ""; + return content; }, _buildButtons: function() { @@ -457,6 +442,26 @@ return $.widget( "ui.calendar", { } }, + _isValid: function( value ) { + if ( $.type( value ) !== "date" ) { + return false; + } + + if ( $.type( this.options.max ) === "date" ) { + if ( value > this.options.max ) { + return false; + } + } + + if ( $.type( this.options.min ) === "date" ) { + if ( value < this.options.min ) { + return false; + } + } + + return true; + }, + _destroy: function() { this.element .off( ".calendar" ) @@ -476,21 +481,29 @@ return $.widget( "ui.calendar", { _setOption: function( key, value ) { if ( key === "value" ) { - if ( $.type( value ) === "date" ) { + if ( this._isValid( value ) ) { this.date.setTime( value.getTime() ).select(); this.refresh(); } } + if ( key === "max" || key === "min" ) { + if ( $.type( value ) === "date" || value === null ) { + this._super( key, value ); + this.refresh(); + } + return; + } + this._super( key, value ); if ( key === "eachDay" ) { - this.date.eachDay = this.options.eachDay; + this.date.eachDay = value; this.refresh(); } if ( key === "dateFormat" ) { - this.date.setFormat( this.options.dateFormat ); + this.date.setFormat( value ); } if ( key === "showWeek" ) { diff --git a/ui/datepicker.js b/ui/datepicker.js index 669600e76bd..b9ce2e5f0bd 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -38,6 +38,8 @@ $.widget( "ui.datepicker", { dateFormat: { date: "short" }, // TODO Review eachDay: $.noop, + max: null, + min: null, numberOfMonths: 1, position: { my: "left top", @@ -55,9 +57,23 @@ $.widget( "ui.datepicker", { }, _create: function() { + if ( $.type( this.options.max ) === "string" ) { + this.options.max = Globalize.parseDate( this.options.max , { pattern: "yyyy-MM-dd" } ); + } + if ( $.type( this.options.min ) === "string" ) { + this.options.min = Globalize.parseDate( this.options.min , { pattern: "yyyy-MM-dd" } ); + } + this._createCalendar(); }, + _getCreateOptions: function() { + return { + max: this.element.attr( "max" ), + min: this.element.attr( "min" ) + }; + }, + _createCalendar: function() { var that = this; @@ -70,6 +86,8 @@ $.widget( "ui.datepicker", { .calendar({ dateFormat: this.options.dateFormat, eachDay: this.options.eachDay, + max: this.options.max, + min: this.options.min, numberOfMonths: this.options.numberOfMonths, showWeek: this.options.showWeek, value: this._getParsedValue(), @@ -282,11 +300,7 @@ $.widget( "ui.datepicker", { value: function( value ) { if ( arguments.length ) { - var date = Globalize.parseDate( value, this.options.dateFormat ); - if ( $.type( date ) === "date" ) { - this.valueAsDate( date ); - this.element.val( value ); - } + this.valueAsDate( Globalize.parseDate( value , this.options.dateFormat ) ); } else { return ( this._getParsedValue() !== null ) ? this.element.val() : null; } @@ -294,7 +308,7 @@ $.widget( "ui.datepicker", { valueAsDate: function( value ) { if ( arguments.length ) { - if ( $.type( value ) === "date" ) { + if ( this.calendarInstance._isValid( value ) ) { this.calendarInstance.valueAsDate( value ); this.element.val( Globalize.format( value, this.options.dateFormat ) ); } @@ -304,7 +318,7 @@ $.widget( "ui.datepicker", { }, isValid: function() { - return this._getParsedValue() !== null; + return this.calendarInstance._isValid( this._getParsedValue() ); }, _destroy: function() { @@ -320,7 +334,7 @@ $.widget( "ui.datepicker", { }, _getParsedValue: function() { - return Globalize.parseDate( this.element.val(), this.options.dateFormat ); + return Globalize.parseDate( this.element.val() , this.options.dateFormat ); }, _setOption: function( key, value ) { @@ -343,5 +357,4 @@ $.widget( "ui.datepicker", { } } }); - })); From 217bdc80148b3f9cbb6969e10be7fdd354a2e783 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 4 Jun 2014 23:40:39 +0200 Subject: [PATCH 32/53] Calendar: Remove select callback reference --- ui/calendar.js | 5 +---- ui/datepicker.js | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/ui/calendar.js b/ui/calendar.js index 029a44ae2c8..0e4b078d80d 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -420,10 +420,7 @@ return $.widget( "ui.calendar", { _select: function( event, time ) { this._setOption( "value", new Date( time ) ); - this._trigger( "select", event, { - // TODO Replace with value option to initialise and read - date: this.value() - }); + this._trigger( "select", event ); }, value: function( value ) { diff --git a/ui/datepicker.js b/ui/datepicker.js index b9ce2e5f0bd..6a2fda7340a 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -91,11 +91,11 @@ $.widget( "ui.datepicker", { numberOfMonths: this.options.numberOfMonths, showWeek: this.options.showWeek, value: this._getParsedValue(), - select: function( event, data ) { - that.element.val( data.date ); + select: function( event ) { + that.element.val( that.calendarInstance.value() ); that.close(); that._focusTrigger(); - that._trigger( "select", event, data); + that._trigger( "select", event ); } }) .calendar( "instance" ); From 12090f3e2fed90df9681166ea190e40226457bf9 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 5 Jun 2014 02:39:46 +0200 Subject: [PATCH 33/53] Datepicker: Code clean up for events --- ui/datepicker.js | 204 +++++++++++++++++++++++------------------------ 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index 6a2fda7340a..8ddfb2d95aa 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -65,6 +65,10 @@ $.widget( "ui.datepicker", { } this._createCalendar(); + + this._on( this._inputEvents ); + this._on( this.calendar, this._calendarEvents ); + this._on( this.document, this._documentEvents ); }, _getCreateOptions: function() { @@ -105,120 +109,116 @@ $.widget( "ui.datepicker", { this.element .attr( "aria-haspopup", "true" ) .attr( "aria-owns", this.calendar.attr( "id" ) ); + }, - this._on({ - keydown: function( event ) { - switch ( event.keyCode ) { - case $.ui.keyCode.TAB: - - // Waiting for close() will make popup hide too late, which breaks tab key behavior - this.calendar.hide(); + _inputEvents: { + keydown: function( event ) { + switch ( event.keyCode ) { + case $.ui.keyCode.TAB: + // Waiting for close() will make popup hide too late, which breaks tab key behavior + this.calendar.hide(); + this.close( event ); + break; + case $.ui.keyCode.ESCAPE: + if ( this.isOpen ) { this.close( event ); - break; - case $.ui.keyCode.ESCAPE: + } + break; + case $.ui.keyCode.ENTER: + this.calendarInstance._handleKeydown( event ); + break; + case $.ui.keyCode.DOWN: + case $.ui.keyCode.UP: + clearTimeout( this.closeTimer ); + this._delay( function() { + this.open( event ); + this.calendarInstance.grid.focus( 1 ); + }, 1 ); + break; + case $.ui.keyCode.HOME: + if ( event.ctrlKey ) { + this.valueAsDate( new Date() ); + event.preventDefault(); if ( this.isOpen ) { - this.close( event ); - } - break; - case $.ui.keyCode.ENTER: - this.calendarInstance._handleKeydown( event ); - break; - case $.ui.keyCode.DOWN: - case $.ui.keyCode.UP: - clearTimeout( this.closeTimer ); - this._delay( function() { - this.open( event ); - this.calendarInstance.grid.focus( 1 ); - }, 1 ); - break; - case $.ui.keyCode.HOME: - if ( event.ctrlKey ) { - this.valueAsDate( new Date() ); - event.preventDefault(); - if ( this.isOpen ) { this.calendarInstance.refresh(); - } else { - this.open( event ); - } - } - break; - - // TODO This is not in specs, keep? - case $.ui.keyCode.END: - if ( event.ctrlKey ) { - this.element.val( "" ); - event.preventDefault(); - if ( this.isOpen ) { - this.close( event ); - } - } - break; - } - }, - keyup: function() { - if ( this.isValid() ) { - this.valueAsDate( this._getParsedValue() ); - } - }, - mousedown: function( event ) { - if ( this.isOpen ) { - suppressExpandOnFocus = true; - this.close(); - return; - } - this.open( event ); - clearTimeout( this.closeTimer ); - }, - focus: function( event ) { - if ( !suppressExpandOnFocus ) { - this._delay( function() { - if ( !this.isOpen ) { + } else { this.open( event ); } - }, 1); - } + } + break; + // TODO This is not in specs, keep? + case $.ui.keyCode.END: + if ( event.ctrlKey ) { + this.element.val( "" ); + event.preventDefault(); + if ( this.isOpen ) { + this.close( event ); + } + } + break; + } + }, + keyup: function() { + if ( this.isValid() ) { + this.valueAsDate( this._getParsedValue() ); + } + }, + mousedown: function( event ) { + if ( this.isOpen ) { + suppressExpandOnFocus = true; + this.close(); + return; + } + this.open( event ); + clearTimeout( this.closeTimer ); + }, + focus: function( event ) { + if ( !suppressExpandOnFocus ) { this._delay( function() { - suppressExpandOnFocus = false; - }, 100 ); - }, - blur: function() { - suppressExpandOnFocus = false; + if ( !this.isOpen ) { + this.open( event ); + } + }, 1); } - }); - - this._on( this.calendar, { - focusout: function( event ) { + this._delay( function() { + suppressExpandOnFocus = false; + }, 100 ); + }, + blur: function() { + suppressExpandOnFocus = false; + } + }, - // Use a timer to allow click to clear it and letting that - // handle the closing instead of opening again - // also allows tabbing inside the calendar without it closing - this.closeTimer = this._delay( function() { - this.close( event ); - }, 150 ); - }, - focusin: function() { - clearTimeout( this.closeTimer ); - }, - mouseup: function() { - clearTimeout( this.closeTimer ); - }, - - // TODO On TAB (or shift TAB), make sure it ends up on something useful in DOM order - keyup: function( event ) { - if ( event.keyCode === $.ui.keyCode.ESCAPE && this.calendar.is( ":visible" ) ) { - this.close( event ); - this._focusTrigger(); - } + _calendarEvents: { + focusout: function( event ) { + // use a timer to allow click to clear it and letting that + // handle the closing instead of opening again + // also allows tabbing inside the calendar without it closing + this.closeTimer = this._delay( function() { + this.close( event ); + }, 150 ); + }, + focusin: function() { + clearTimeout( this.closeTimer ); + }, + mouseup: function() { + clearTimeout( this.closeTimer ); + }, + // TODO on TAB (or shift TAB), make sure it ends up on something useful in DOM order + keyup: function( event ) { + if ( event.keyCode === $.ui.keyCode.ESCAPE && this.calendar.is( ":visible" ) ) { + this.close( event ); + this._focusTrigger(); } - }); + } + }, - this._on( this.document, { - click: function( event ) { - if ( this.isOpen && !$( event.target ).closest( this.element.add( this.calendar ) ).length ) { - this.close( event ); - } + _documentEvents: { + click: function( event ) { + if ( this.isOpen && !$( event.target ).closest( this.element.add( this.calendar ) ).length ) { + this.close( event ); } - }); + } }, _appendTo: function() { From 31f9bd7a5b4d557647c7207e65ee96375fc0ffca Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 5 Jun 2014 20:32:27 +0200 Subject: [PATCH 34/53] Datepicker: Remove unwanted keyboard shortcut Remove shortcut for closing the calendar and erasing the date (CTRL+END) --- ui/datepicker.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index 8ddfb2d95aa..e3af29927b3 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -146,16 +146,6 @@ $.widget( "ui.datepicker", { } } break; - // TODO This is not in specs, keep? - case $.ui.keyCode.END: - if ( event.ctrlKey ) { - this.element.val( "" ); - event.preventDefault(); - if ( this.isOpen ) { - this.close( event ); - } - } - break; } }, keyup: function() { From 9b53f0787c3f2c6f389de956ed16d7a9acf2f530 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 12 Jun 2014 14:36:45 +0200 Subject: [PATCH 35/53] Datepicker: Simplify usage of calendar options and avoid duplications --- ui/datepicker.js | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index e3af29927b3..4d8581c4481 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -26,26 +26,19 @@ } }(function( $ ) { -// TODO Use uniqueId, if possible -var idIncrement = 0, - // TODO Move this to the instance +var widget, + calendarOptions = [ "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ], + // TODO Move this to the instance? suppressExpandOnFocus = false; -$.widget( "ui.datepicker", { +widget = $.widget( "ui.datepicker", { version: "@VERSION", options: { appendTo: null, - dateFormat: { date: "short" }, - // TODO Review - eachDay: $.noop, - max: null, - min: null, - numberOfMonths: 1, position: { my: "left top", at: "left bottom" }, - showWeek: false, show: true, hide: true, @@ -87,13 +80,7 @@ $.widget( "ui.datepicker", { // Initialize calendar widget this.calendarInstance = this.calendar - .calendar({ - dateFormat: this.options.dateFormat, - eachDay: this.options.eachDay, - max: this.options.max, - min: this.options.min, - numberOfMonths: this.options.numberOfMonths, - showWeek: this.options.showWeek, + .calendar( $.extend( {}, this.options, { value: this._getParsedValue(), select: function( event ) { that.element.val( that.calendarInstance.value() ); @@ -101,7 +88,7 @@ $.widget( "ui.datepicker", { that._focusTrigger(); that._trigger( "select", event ); } - }) + }) ) .calendar( "instance" ); this._setHiddenPicker(); @@ -330,7 +317,7 @@ $.widget( "ui.datepicker", { _setOption: function( key, value ) { this._super( key, value ); - if ( $.inArray( key, [ "showWeek", "numberOfMonths", "dateFormat", "eachDay", "min", "max" ] ) !== -1 ) { + if ( $.inArray( key, calendarOptions ) !== -1 ) { this.calendarInstance._setOption( key, value ); } @@ -347,4 +334,11 @@ $.widget( "ui.datepicker", { } } }); + +$.each( calendarOptions, function( index, option ) { + $.ui.datepicker.prototype.options[ option ] = $.ui.calendar.prototype.options[ option ]; +}); + +return widget; + })); From 088b7a48cf19719b9eda937938b3937434d9b76e Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Tue, 17 Jun 2014 00:46:08 +0200 Subject: [PATCH 36/53] Datepicker tests: Add open and close unit tests --- tests/unit/datepicker/datepicker_events.js | 43 ++++++++++++++++++--- tests/unit/datepicker/datepicker_methods.js | 19 +++++++++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/tests/unit/datepicker/datepicker_events.js b/tests/unit/datepicker/datepicker_events.js index 9ee254e5351..ac3d41c13d6 100644 --- a/tests/unit/datepicker/datepicker_events.js +++ b/tests/unit/datepicker/datepicker_events.js @@ -14,15 +14,48 @@ test( "beforeOpen", function() { }); test( "close", function() { - expect( 0 ); + expect( 4 ); + + var shouldFire, + input = TestHelpers.datepicker.init( "#datepicker", { + close: function() { + ok( shouldFire, "close event fired" ); + } + }); + + shouldFire = false; + input.datepicker( "open" ); + shouldFire = true; + input.datepicker( "close" ); + + shouldFire = false; + input.datepicker( "open" ); + shouldFire = true; + $( "body" ).trigger( "mousedown" ); + + shouldFire = false; + input.datepicker( "open" ); + shouldFire = true; + input.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + + shouldFire = false; + input.datepicker( "open" ); + shouldFire = true; + input.datepicker( "widget" ).find( "tbody tr:first a:first" ).simulate( "mousedown" ); }); test( "open", function() { - expect( 0 ); -}); + expect( 2 ); -test( "select", function() { - expect( 0 ); + var input = TestHelpers.datepicker.init( "#datepicker", { + open: function() { + ok( true, "open event fired on open" ); + ok( widget.is( ":visible" ), "calendar open on open" ); + } + }), + widget = input.datepicker( "widget" ); + + input.datepicker( "open" ); }); var selectedThis = null, diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 995d0c527a0..9ea1e5d9ebf 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -43,6 +43,25 @@ test( "widget", function() { actual.remove(); }); +test( "open / close", function() { + expect( 7 ); + + var input = TestHelpers.datepicker.initNewInput({ show: false, hide: false }), + calendar = input.datepicker( "widget" ); + + ok( calendar.is( ":hidden" ), "calendar hidden on init" ); + + input.datepicker( "open" ); + ok( calendar.is( ":visible" ), "open: calendar visible" ); + equal( calendar.attr( "aria-hidden" ), "false", "open: calendar aria-hidden" ); + equal( calendar.attr( "aria-expanded" ), "true", "close: calendar aria-expanded" ); + + input.datepicker( "close" ); + ok( !calendar.is( ":visible" ), "close: calendar hidden" ); + equal( calendar.attr( "aria-hidden" ), "true", "close: calendar aria-hidden" ); + equal( calendar.attr( "aria-expanded" ), "false", "close: calendar aria-expanded" ); +}); + test( "value", function() { expect( 4 ); From 7a9f5f4ef479af145c3c9a849a0d56d412189dff Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Tue, 17 Jun 2014 16:25:24 +0200 Subject: [PATCH 37/53] Datepicker: Improve document click event --- ui/datepicker.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index 4d8581c4481..b752bd69280 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -191,8 +191,12 @@ widget = $.widget( "ui.datepicker", { }, _documentEvents: { - click: function( event ) { - if ( this.isOpen && !$( event.target ).closest( this.element.add( this.calendar ) ).length ) { + mousedown: function( event ) { + if ( !this.isOpen ) { + return; + } + + if ( !$( event.target ).closest( this.element.add( this.calendar ) ).length ) { this.close( event ); } } From 54d6f50acc397e56a5a2e95e69ad8fc0defbbc98 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Tue, 17 Jun 2014 19:05:06 +0200 Subject: [PATCH 38/53] Calendar: Fix broken day table cell attributes --- ui/calendar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/calendar.js b/ui/calendar.js index 0e4b078d80d..5df265d328d 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -319,7 +319,7 @@ return $.widget( "ui.calendar", { var content = "", attributes = [ "role='gridcell'", - "aria-selected='" + day.current ? true : false + "'" + "aria-selected='" + ( day.current ? true : false ) + "'" ], selectable = ( day.selectable && this._isValid( new Date( day.timestamp ) ) ); From 2b2871bb0e7965298e5d23eaad201a79efc6cf8c Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Tue, 17 Jun 2014 19:53:08 +0200 Subject: [PATCH 39/53] Datepicker tests: Rewrite event unit tests --- tests/unit/datepicker/datepicker_events.js | 207 +++++++----------- .../datepicker/datepicker_test_helpers.js | 6 +- 2 files changed, 81 insertions(+), 132 deletions(-) diff --git a/tests/unit/datepicker/datepicker_events.js b/tests/unit/datepicker/datepicker_events.js index ac3d41c13d6..ff5438cd2f1 100644 --- a/tests/unit/datepicker/datepicker_events.js +++ b/tests/unit/datepicker/datepicker_events.js @@ -1,16 +1,32 @@ -// The implement of events is completely changing therefore these tests are no longer directly -// relevant. Leaving them around commented out so we can ensure the functionality is replicated. -// For example: -// TODO: In the old implementation the Enter key select's today's date when the has -// focus and is empty. Do we want to replicate this behavior in the rewrite? -/* - (function( $ ) { module( "datepicker: events" ); test( "beforeOpen", function() { - expect( 0 ); + expect( 3 ); + + var input = TestHelpers.datepicker.init( "#datepicker", { + beforeOpen: function() { + ok( true, "beforeOpen event fired before open" ); + ok( input.datepicker( "widget" ).is( ":hidden" ), "calendar hidden on beforeOpen" ); + }, + open: function() { + ok( input.datepicker( "widget" ).is( ":visible" ), "calendar open on open" ); + } + }); + + input + .datepicker( "open" ) + .datepicker( "close" ) + .datepicker( "option", { + beforeOpen: function() { + return false; + }, + open: function() { + ok( false, "calendar should not open when openBefore is canceled" ); + } + }) + .datepicker( "open" ); }); test( "close", function() { @@ -58,126 +74,61 @@ test( "open", function() { input.datepicker( "open" ); }); -var selectedThis = null, -selectedDate = null, -selectedInst = null; - -function callback(date, inst) { - selectedThis = this; - selectedDate = date; - selectedInst = inst; -} - -function callback2(year, month, inst) { - selectedThis = this; - selectedDate = year + "/" + month; - selectedInst = inst; -} - -test( "events", function() { - expect( 26 ); - var dateStr, newMonthYear, inp2, - inp = TestHelpers.datepicker.init( "#inp", {onSelect: callback}), - date = new Date(); - // onSelect - inp.val( "" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - equal(selectedThis, inp[0], "Callback selected this" ); - equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback selected inst" ); - equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", date), - "Callback selected date" ); - inp.val( "" ).datepicker( "show" ). - simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.DOWN}). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() + 7); - equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", date), - "Callback selected date - ctrl+down" ); - inp.val( "" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); - equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", date), - "Callback selected date - esc" ); - dateStr = "02/04/2008"; - inp.val(dateStr).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - equal(dateStr, selectedDate, - "onSelect is called after enter keydown" ); - // onChangeMonthYear - inp.datepicker( "option", {onChangeMonthYear: callback2, onSelect: null}). - val( "" ).datepicker( "show" ); - newMonthYear = function(date) { - return date.getFullYear() + "/" + (date.getMonth() + 1); - }; - date = new Date(); - date.setDate(1); - inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_UP}); - date.setMonth(date.getMonth() - 1); - equal(selectedThis, inp[0], "Callback change month/year this" ); - equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback change month/year inst" ); - equal(selectedDate, newMonthYear(date), - "Callback change month/year date - pgup" ); - inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); - date.setMonth(date.getMonth() + 1); - equal(selectedDate, newMonthYear(date), - "Callback change month/year date - pgdn" ); - inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); - date.setFullYear(date.getFullYear() - 1); - equal(selectedDate, newMonthYear(date), - "Callback change month/year date - ctrl+pgup" ); - inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.HOME}); - date.setFullYear(date.getFullYear() + 1); - equal(selectedDate, newMonthYear(date), - "Callback change month/year date - ctrl+home" ); - inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); - date.setFullYear(date.getFullYear() + 1); - equal(selectedDate, newMonthYear(date), - "Callback change month/year date - ctrl+pgdn" ); - inp.datepicker( "setDate", new Date(2007, 1 - 1, 26)); - equal(selectedDate, "2007/1", "Callback change month/year date - setDate" ); - selectedDate = null; - inp.datepicker( "setDate", new Date(2007, 1 - 1, 12)); - ok(selectedDate == null, "Callback change month/year date - setDate no change" ); - // onChangeMonthYear step by 2 - inp.datepicker( "option", {stepMonths: 2}). - datepicker( "hide" ).val( "" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_UP}); - date.setMonth(date.getMonth() - 14); - equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - pgup" ); - inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); - date.setMonth(date.getMonth() - 12); - equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - ctrl+pgup" ); - inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); - date.setMonth(date.getMonth() + 2); - equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - pgdn" ); - inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); - date.setMonth(date.getMonth() + 12); - equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - ctrl+pgdn" ); - // onClose - inp.datepicker( "option", {onClose: callback, onChangeMonthYear: null, stepMonths: 1}). - val( "" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); - equal(selectedThis, inp[0], "Callback close this" ); - equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback close inst" ); - equal(selectedDate, "", "Callback close date - esc" ); - inp.val( "" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", new Date()), - "Callback close date - enter" ); - inp.val( "02/04/2008" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); - equal(selectedDate, "02/04/2008", "Callback close date - preset" ); - inp.val( "02/04/2008" ).datepicker( "show" ). - simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.END}); - equal(selectedDate, "", "Callback close date - ctrl+end" ); - - inp2 = TestHelpers.datepicker.init( "#inp2" ); - inp2.datepicker().datepicker( "option", {onClose: callback}).datepicker( "show" ); - inp.datepicker( "show" ); - equal(selectedThis, inp2[0], "Callback close this" ); +asyncTest( "select", function() { + expect( 4 ); + + var input = TestHelpers.datepicker.init( "#datepicker", { + select: function( event ) { + ok( true, "select event fired " + message ); + equal( + event.originalEvent.type, + "calendarselect", + "select originalEvent " + message + ); + } + }), + widget = input.datepicker( "widget" ), + message = ""; + + function step1() { + message = "on calendar cell click"; + input + .simulate( "focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + widget.find( "tbody tr:first a:first" ).simulate( "mousedown" ); + input.datepicker( "close" ); + step2(); + }, 100 ); + } + + function step2() { + message = "on calendar cell enter"; + input + .simulate( "focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + input.datepicker( "close" ); + step3(); + }, 100 ); + } + + function step3() { + message = "on calendar escape (not expected)"; + input + .simulate( "focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + input.datepicker( "close" ); + start(); + }, 100 ); + } + + step1(); }); })( jQuery ); - */ diff --git a/tests/unit/datepicker/datepicker_test_helpers.js b/tests/unit/datepicker/datepicker_test_helpers.js index e8048725d6a..3548f6ea360 100644 --- a/tests/unit/datepicker/datepicker_test_helpers.js +++ b/tests/unit/datepicker/datepicker_test_helpers.js @@ -15,14 +15,12 @@ TestHelpers.datepicker = { equal( d1.toString(), d2.toString(), message ); }, init: function( id, options ) { - options = $.extend( { show: false }, options || {} ); + options = $.extend( { show: false, hide: false }, options || {} ); return $( id ).datepicker( options ); }, initNewInput: function( options ) { options = $.extend( { show: false, hide: false }, options || {} ); return $( "" ).datepicker( options ) .appendTo( "#qunit-fixture" ); - }, - onFocus: TestHelpers.onFocus, - PROP_NAME: "datepicker" + } }; From d743e21a16571440d456eb5b8cf52f3e9013d3f2 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 18 Jun 2014 01:35:51 +0200 Subject: [PATCH 40/53] Calendar: Fix multiple calendar styles --- themes/base/calendar.css | 8 +++++--- themes/base/datepicker.css | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/themes/base/calendar.css b/themes/base/calendar.css index 608afed4465..af889551672 100644 --- a/themes/base/calendar.css +++ b/themes/base/calendar.css @@ -109,15 +109,16 @@ } /* with multiple calendars */ -.ui-calendar.ui-calendar-multi { - width: 100%; +.ui-calendar-multi { + width: auto; + display: inline-block; } .ui-calendar-multi .ui-calendar-group { float: left; } .ui-calendar-multi .ui-calendar-group table { width: 95%; - margin: 0 auto .4em; + margin: 0 2.5% .4em; } .ui-calendar-multi-2 .ui-calendar-group { width: 50%; @@ -176,3 +177,4 @@ border-right-width: 0; border-left-width: 1px; } + diff --git a/themes/base/datepicker.css b/themes/base/datepicker.css index 7fc235fe676..0dd5e736cae 100644 --- a/themes/base/datepicker.css +++ b/themes/base/datepicker.css @@ -9,6 +9,7 @@ * http://api.jqueryui.com/datepicker/#theming */ .ui-datepicker { - display: none; - position: absolute; + display: none; + position: absolute; } + From e71122c56f58037fa53f7074da60ec82efa01d3d Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 18 Jun 2014 01:44:13 +0200 Subject: [PATCH 41/53] Calendar: Fix German localization --- external/localization.js | 6 +++--- tests/unit/calendar/calendar_core.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/external/localization.js b/external/localization.js index 98d8f813052..4fd436aca00 100644 --- a/external/localization.js +++ b/external/localization.js @@ -2767,10 +2767,10 @@ var regions = { "dateFormat": "d" }, "de": { - "closeText": "schlie\u00dfen", - "prevText": "<zur\u00fcck", + "closeText": "Schlie\u00dfen", + "prevText": "<Zur\u00fcck", "nextText": "Vor>", - "currentText": "heute", + "currentText": "Heute", "weekHeader": "Wo", "dateFormat": "d" }, diff --git a/tests/unit/calendar/calendar_core.js b/tests/unit/calendar/calendar_core.js index 7c216b740bc..78c4a24ccae 100644 --- a/tests/unit/calendar/calendar_core.js +++ b/tests/unit/calendar/calendar_core.js @@ -89,7 +89,7 @@ test( "Localization", function() { ); equal( element.find( ".ui-calendar-prev" ).text(), - " Date: Wed, 18 Jun 2014 02:05:42 +0200 Subject: [PATCH 42/53] Datepicker: Fix localization demo --- demos/calendar/localization.html | 11 +++++++---- demos/datepicker/localization.html | 14 +++++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/demos/calendar/localization.html b/demos/calendar/localization.html index 75822cf32df..1f607f7a4c5 100644 --- a/demos/calendar/localization.html +++ b/demos/calendar/localization.html @@ -16,12 +16,15 @@ diff --git a/demos/datepicker/localization.html b/demos/datepicker/localization.html index 701e5c36b66..01f90531eca 100644 --- a/demos/datepicker/localization.html +++ b/demos/datepicker/localization.html @@ -17,11 +17,15 @@ From 2407e599363885450035ae46087157dbf54348b0 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 18 Jun 2014 02:31:31 +0200 Subject: [PATCH 43/53] Datepicker: Several minor code improvements Several minor code improvements and make suppressExpandOnFocus an internal variable --- ui/datepicker.js | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index b752bd69280..75392dc5fe4 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -27,9 +27,7 @@ }(function( $ ) { var widget, - calendarOptions = [ "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ], - // TODO Move this to the instance? - suppressExpandOnFocus = false; + calendarOptions = [ "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ]; widget = $.widget( "ui.datepicker", { version: "@VERSION", @@ -50,6 +48,8 @@ widget = $.widget( "ui.datepicker", { }, _create: function() { + this.suppressExpandOnFocus = false; + if ( $.type( this.options.max ) === "string" ) { this.options.max = Globalize.parseDate( this.options.max , { pattern: "yyyy-MM-dd" } ); } @@ -93,9 +93,10 @@ widget = $.widget( "ui.datepicker", { this._setHiddenPicker(); - this.element - .attr( "aria-haspopup", "true" ) - .attr( "aria-owns", this.calendar.attr( "id" ) ); + this.element.attr({ + "aria-haspopup": true, + "aria-owns": this.calendar.attr( "id" ) + }); }, _inputEvents: { @@ -142,7 +143,7 @@ widget = $.widget( "ui.datepicker", { }, mousedown: function( event ) { if ( this.isOpen ) { - suppressExpandOnFocus = true; + this.suppressExpandOnFocus = true; this.close(); return; } @@ -150,19 +151,17 @@ widget = $.widget( "ui.datepicker", { clearTimeout( this.closeTimer ); }, focus: function( event ) { - if ( !suppressExpandOnFocus ) { + if ( !this.suppressExpandOnFocus && !this.isOpen ) { this._delay( function() { - if ( !this.isOpen ) { - this.open( event ); - } + this.open( event ); }, 1); } this._delay( function() { - suppressExpandOnFocus = false; + this.suppressExpandOnFocus = false; }, 100 ); }, blur: function() { - suppressExpandOnFocus = false; + this.suppressExpandOnFocus = false; } }, @@ -223,7 +222,7 @@ widget = $.widget( "ui.datepicker", { }, _focusTrigger: function() { - suppressExpandOnFocus = true; + this.suppressExpandOnFocus = true; this.element.focus(); }, @@ -241,14 +240,14 @@ widget = $.widget( "ui.datepicker", { } this.calendarInstance.refresh(); - this.calendar - .attr( "aria-hidden", "false" ) - .attr( "aria-expanded", "true" ) + .attr({ + "aria-hidden": false, + "aria-expanded": true + }) .show() .position( this._buildPosition() ) .hide(); - this._show( this.calendar, this.options.show ); // Take trigger out of tab order to allow shift-tab to skip trigger @@ -270,9 +269,10 @@ widget = $.widget( "ui.datepicker", { }, _setHiddenPicker: function() { - this.calendar - .attr( "aria-hidden", "true" ) - .attr( "aria-expanded", "false" ); + this.calendar.attr({ + "aria-hidden": true, + "aria-expanded": false + }); }, _buildPosition: function() { @@ -305,9 +305,7 @@ widget = $.widget( "ui.datepicker", { _destroy: function() { this.calendarInstance.destroy(); this.calendar.remove(); - this.element - .removeAttr( "aria-haspopup" ) - .removeAttr( "aria-owns" ); + this.element.removeAttr( "aria-haspopup aria-owns" ); }, widget: function() { @@ -338,7 +336,7 @@ widget = $.widget( "ui.datepicker", { } } }); - + $.each( calendarOptions, function( index, option ) { $.ui.datepicker.prototype.options[ option ] = $.ui.calendar.prototype.options[ option ]; }); From fc87a82819e024d7ce2a38b7c0bdd7361e119905 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 18 Jun 2014 02:49:30 +0200 Subject: [PATCH 44/53] Calendar: Fix hover event setting and removing --- ui/calendar.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/calendar.js b/ui/calendar.js index 5df265d328d..d83949595eb 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -73,8 +73,7 @@ return $.widget( "ui.calendar", { "keydown .ui-calendar-calendar": "_handleKeydown" }); - // TODO Use hoverable (no delegation support)? convert to _on? - this.element.delegate( ".ui-calendar-header a, .ui-calendar-calendar a", "mouseenter.calendar mouseleave.calendar", function() { + this.element.on( "mouseenter.calendar mouseleave.calendar", ".ui-calendar-header a, .ui-calendar-calendar a", function() { $( this ).toggleClass( "ui-state-hover" ); }); From 7edd77b4d8687ca31892e7b30f7dd54ccb91a497 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 19 Jun 2014 17:37:37 +0200 Subject: [PATCH 45/53] Calendar: Focus class is not removed when using arrow keys --- ui/calendar.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/calendar.js b/ui/calendar.js index d83949595eb..4babc20af1d 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -138,8 +138,9 @@ return $.widget( "ui.calendar", { this.grid .attr( "aria-activedescendant", id ) + .find( ".ui-state-focus" ) .removeClass( "ui-state-focus" ); - $( "#" + id + " a" ).addClass( "ui-state-focus" ); + $( "#" + id + " > a" ).addClass( "ui-state-focus" ); }, _createCalendar: function() { From a86d5b020f8906f58e009277fbda524735feb3c0 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 19 Jun 2014 17:42:21 +0200 Subject: [PATCH 46/53] Datepicker: Remove unwanted CTRL+HOME shortcut --- ui/datepicker.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index 75392dc5fe4..83a4dfa9ddf 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -123,17 +123,6 @@ widget = $.widget( "ui.datepicker", { this.calendarInstance.grid.focus( 1 ); }, 1 ); break; - case $.ui.keyCode.HOME: - if ( event.ctrlKey ) { - this.valueAsDate( new Date() ); - event.preventDefault(); - if ( this.isOpen ) { - this.calendarInstance.refresh(); - } else { - this.open( event ); - } - } - break; } }, keyup: function() { From 7a4fa37eb492191b1d00e53448fbd22ca33a90c0 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 19 Jun 2014 18:36:57 +0200 Subject: [PATCH 47/53] Datepicker: Remove support for enter key on input --- ui/datepicker.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index 83a4dfa9ddf..fc4a0ea2c99 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -112,9 +112,6 @@ widget = $.widget( "ui.datepicker", { this.close( event ); } break; - case $.ui.keyCode.ENTER: - this.calendarInstance._handleKeydown( event ); - break; case $.ui.keyCode.DOWN: case $.ui.keyCode.UP: clearTimeout( this.closeTimer ); From 1b271130a804f1443b191be0b1895cf1ab6a3425 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 19 Jun 2014 18:59:28 +0200 Subject: [PATCH 48/53] Datepicker: Improve localization handling, code style --- tests/unit/calendar/calendar_core.js | 8 ++- ui/calendar.js | 79 ++++++++++++++-------------- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/tests/unit/calendar/calendar_core.js b/tests/unit/calendar/calendar_core.js index 78c4a24ccae..829094ac327 100644 --- a/tests/unit/calendar/calendar_core.js +++ b/tests/unit/calendar/calendar_core.js @@ -64,7 +64,7 @@ test( "base structure", function() { }); test( "Localization", function() { - expect( 5 ); + expect( 10 ); var defaultLocale = Globalize.locale(), element = $( "#calendar" ), @@ -102,6 +102,12 @@ test( "Localization", function() { testLocalization( "Init: " ); element.calendar( "destroy" ); + Globalize.locale( defaultLocale.locale ); + initCalendar(); + Globalize.locale( "de-DE" ); + element.calendar( "refresh" ); + testLocalization( "After init: " ); + Globalize.locale( defaultLocale.locale ); }); diff --git a/ui/calendar.js b/ui/calendar.js index 4babc20af1d..dbc73697a72 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -43,6 +43,7 @@ return $.widget( "ui.calendar", { _create: function() { this.id = this.element.uniqueId().attr( "id" ); + this.labels = Globalize.translate( "datepicker" ); this.date = $.date( this.options.value, this.options.dateFormat ).select(); this.date.eachDay = this.options.eachDay; @@ -73,7 +74,7 @@ return $.widget( "ui.calendar", { "keydown .ui-calendar-calendar": "_handleKeydown" }); - this.element.on( "mouseenter.calendar mouseleave.calendar", ".ui-calendar-header a, .ui-calendar-calendar a", function() { + this.element.on( "mouseenter.calendar mouseleave.calendar", ".ui-calendar-header button, .ui-calendar-calendar a", function() { $( this ).toggleClass( "ui-state-hover" ); }); @@ -162,8 +163,6 @@ return $.widget( "ui.calendar", { }) .html( pickerHtml ); - this.element.find( "button" ).button(); - this.grid = this.element.find( ".ui-calendar-calendar" ); }, @@ -207,41 +206,39 @@ return $.widget( "ui.calendar", { _buildHeader: function() { return "
      " + - this._buildPreviousLink() + - this._buildNextLink() + - this._buildTitlebar() + - "
      "; + this._buildPreviousLink() + + this._buildNextLink() + + this._buildTitlebar() + + "
      "; }, _buildPreviousLink: function() { - var labels = Globalize.translate( "datepicker" ); - - return ""; }, _buildNextLink: function() { - var labels = Globalize.translate( "datepicker" ); - - return ""; }, _buildTitlebar: function() { - var labels = Globalize.translate( "datepicker" ); - return "
      " + - "
      " + - this._buildTitle() + - "
      " + - ", " + labels.datePickerRole + "" + - "
      "; + "
      " + + this._buildTitle() + + "
      " + + ", " + + this.labels.datePickerRole + + "" + + "
      "; }, _buildTitle: function() { @@ -255,36 +252,36 @@ return $.widget( "ui.calendar", { _buildGrid: function() { return "" + - this._buildGridHeading() + - this._buildGridBody() + - "
      "; + "aria-labelledby='" + this.id + "-month-lbl' tabindex='0' " + + "aria-activedescendant='" + this.id + "-" + this.date.day() + "'>" + + this._buildGridHeading() + + this._buildGridBody() + + ""; }, _buildGridHeading: function() { var cells = "", i = 0, - labels = Globalize.translate( "datepicker" ), weekDayLength = this.date.weekdays().length; if ( this.options.showWeek ) { - cells += "" + labels.weekHeader + ""; + cells += "" + this.labels.weekHeader + ""; } for ( ; i < weekDayLength; i++ ) { cells += this._buildGridHeaderCell( this.date.weekdays()[ i ] ); } return "" + - "" + cells + "" + - ""; + "" + cells + "" + + ""; }, _buildGridHeaderCell: function( day ) { return "" + - "" + - day.shortname + - "" + - ""; + "" + + day.shortname + + "" + + ""; }, _buildGridBody: function() { @@ -339,7 +336,6 @@ return $.widget( "ui.calendar", { _buildDayElement: function( day, selectable ) { var classes = [ "ui-state-default" ], - labels = Globalize.translate( "datepicker" ), content = ""; if ( day === this.date && selectable ) { @@ -364,18 +360,16 @@ return $.widget( "ui.calendar", { } if ( day.today ) { - content += ", " + labels.currentText + ""; + content += ", " + this.labels.currentText + ""; } return content; }, _buildButtons: function() { - var labels = Globalize.translate( "datepicker" ); - return "
      " + - "" + - "
      "; + "" + + "
      "; }, // Refreshing the entire calendar during interaction confuses screen readers, specifically @@ -384,6 +378,7 @@ return $.widget( "ui.calendar", { // with the prev and next links would cause loss of focus issues because the links being // interacted with will disappear while focused. refresh: function() { + this.labels = Globalize.translate( "datepicker" ); // Determine which day gridcell to focus after refresh // TODO: Prevent disabled cells from being focused @@ -391,6 +386,10 @@ return $.widget( "ui.calendar", { this.grid = $( this._buildGrid() ); this.element.find( ".ui-calendar-title" ).html( this._buildTitle() ); this.element.find( ".ui-calendar-calendar" ).replaceWith( this.grid ); + $( ".ui-calendar-prev", this.element ).attr( "title", this.labels.prevText ) + .children().html( this.labels.prevText ); + $( ".ui-calendar-next", this.element ).attr( "title", this.labels.nextText ) + .children().html( this.labels.nextText ); } else { this._refreshMultiplePicker(); } From b0877b14ffb4f067ec4c1262541eb03feb0363e6 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Fri, 20 Jun 2014 00:13:20 +0200 Subject: [PATCH 49/53] Calendar: Add buttons option --- demos/calendar/buttonbar.html | 8 +- tests/unit/calendar/calendar_common.js | 1 + tests/unit/calendar/calendar_core.js | 26 ++++- tests/unit/calendar/calendar_options.js | 105 +++++++++++++++++++ tests/unit/calendar/calendar_test_helpers.js | 4 +- tests/unit/datepicker/datepicker_common.js | 1 + tests/unit/datepicker/datepicker_core.js | 2 +- tests/unit/datepicker/datepicker_options.js | 18 +++- ui/calendar.js | 73 +++++++++++-- ui/datepicker.js | 6 +- 10 files changed, 223 insertions(+), 21 deletions(-) diff --git a/demos/calendar/buttonbar.html b/demos/calendar/buttonbar.html index 498fe1041f4..7ed390b25d1 100644 --- a/demos/calendar/buttonbar.html +++ b/demos/calendar/buttonbar.html @@ -17,7 +17,11 @@ @@ -27,7 +31,7 @@
      -

      Display a button for selecting Today's date and a Done button for closing the calendar with the boolean showButtonPanel option. Each button is enabled by default when the bar is displayed, but can be turned off with additional options. Button text is customizable.

      +

      Display a button for selecting Today's date with the buttons option.

      diff --git a/tests/unit/calendar/calendar_common.js b/tests/unit/calendar/calendar_common.js index 49f8b5f05f6..a669fdfc2c1 100644 --- a/tests/unit/calendar/calendar_common.js +++ b/tests/unit/calendar/calendar_common.js @@ -1,5 +1,6 @@ TestHelpers.commonWidgetTests( "calendar", { defaults: { + buttons: [], dateFormat: { date: "short" }, disabled: false, eachDay: $.noop, diff --git a/tests/unit/calendar/calendar_core.js b/tests/unit/calendar/calendar_core.js index 829094ac327..a0b969d08c1 100644 --- a/tests/unit/calendar/calendar_core.js +++ b/tests/unit/calendar/calendar_core.js @@ -5,7 +5,7 @@ module( "calendar: core" ); TestHelpers.testJshint( "calendar" ); test( "base structure", function() { - expect( 22 ); + expect( 26 ); var header, title, table, thead, week, child, buttonpane, element = $( "#calendar" ).calendar(), @@ -14,7 +14,7 @@ test( "base structure", function() { function step1() { ok( !dp.is( ".ui-calendar-rtl" ), "Structure - not right-to-left" ); ok( !dp.is( ".ui-calendar-multi" ), "Structure - not multi-month" ); - equal( dp.children().length, 3, "Structure - child count (header, calendar)" ); + equal( dp.children().length, 2, "Structure - child count (header, calendar)" ); header = dp.children( ":first" ); ok( header.is( "div.ui-calendar-header" ), "Structure - header division" ); @@ -42,17 +42,33 @@ test( "base structure", function() { ok( week.is( "tr" ), "Structure - month table week row" ); equal( week.children().length, 7, "Structure - week child count" ); - element.calendar( "destroy" ); - step2(); } function step2() { + element.calendar( "option", "buttons", { + "test": function() {}, + "test button": function() {} + }); + + equal( dp.children().length, 3, "Structure buttons - child count (header, calendar, buttonpane)" ); + + buttonpane = dp.children( ".ui-calendar-buttonpane" ); + equal( buttonpane.children( "div.ui-calendar-buttonset" ).length, 1, "Structure buttons - buttonset" ); + equal( buttonpane.find( "button.ui-button:first" ).text(), "test", "Structure buttons - buttonset" ); + equal( buttonpane.find( "button.ui-button:eq(1)" ).text(), "test button", "Structure buttons - buttonset" ); + + element.calendar( "destroy" ); + step3(); + } + + function step3() { + // Multi-month 2 element = $( "#calendar" ).calendar( { numberOfMonths: 2 } ); dp = element.calendar( "widget" ); ok( dp.is( ".ui-calendar-multi" ), "Structure multi [2] - multi-month" ); - equal( dp.children().length, 4, "Structure multi [2] - child count" ); + equal( dp.children().length, 3, "Structure multi [2] - child count" ); child = dp.children( ":eq(2)" ); ok( child.is( "div.ui-calendar-row-break" ), "Structure multi [2] - row break" ); diff --git a/tests/unit/calendar/calendar_options.js b/tests/unit/calendar/calendar_options.js index 6be4f8733e8..0e6a512fd1c 100644 --- a/tests/unit/calendar/calendar_options.js +++ b/tests/unit/calendar/calendar_options.js @@ -2,6 +2,111 @@ module( "calendar: options" ); +test("buttons", function() { + expect( 21 ); + + var btn, i, newButtons, + buttons = { + "Ok": function( event ) { + ok(true, "button click fires callback" ); + equal( this, element[ 0 ], "context of callback" ); + equal( event.target, btn[ 0 ], "event target" ); + }, + "Cancel": function( event ) { + ok( true, "button click fires callback" ); + equal( this, element[ 0 ], "context of callback" ); + equal( event.target, btn[ 1 ], "event target" ); + } + }, + element = $( "#calendar" ).calendar({ buttons: buttons }); + + btn = element.calendar( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( btn.length, 2, "number of buttons" ); + + i = 0; + $.each( buttons, function( key ) { + equal( btn.eq( i ).text(), key, "text of button " + ( i + 1 ) ); + i++; + }); + + ok( btn.parent().hasClass( "ui-calendar-buttonset" ), "buttons in container" ); + ok( + element.calendar( "widget" ).hasClass( "ui-calendar-buttons" ), + "calendar wrapper adds class about having buttons" + ); + + btn.trigger( "click" ); + + newButtons = { + "Close": function( event ) { + ok( true, "button click fires callback" ); + equal( this, element[ 0 ], "context of callback" ); + equal( event.target, btn[ 0 ], "event target" ); + } + }; + + deepEqual( + element.calendar( "option", "buttons" ), + buttons, + ".calendar('option', 'buttons') getter" + ); + element.calendar( "option", "buttons", newButtons ); + deepEqual( + element.calendar( "option", "buttons" ), + newButtons, + ".calendar('option', 'buttons', ...) setter" + ); + + btn = element.calendar( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( btn.length, 1, "number of buttons after setter" ); + btn.trigger( "click" ); + + i = 0; + $.each( newButtons, function( key ) { + equal( btn.eq( i ).text(), key, "text of button " + ( i + 1 ) ); + i += 1; + }); + + element.calendar( "option", "buttons", null ); + btn = element.calendar( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( btn.length, 0, "all buttons have been removed" ); + equal( element.find( ".ui-calendar-buttonset").length, 0, "buttonset has been removed" ); + equal( element.parent().hasClass( "ui-calendar-buttons" ), false, "dialog wrapper removes class about having buttons" ); + + element.remove(); +}); + +test( "buttons - advanced", function() { + expect( 7 ); + + var buttons, + element = $( "#calendar" ).calendar({ + buttons: [{ + text: "a button", + "class": "additional-class", + id: "my-button-id", + click: function() { + equal(this, element[ 0 ], "correct context"); + }, + icons: { + primary: "ui-icon-cancel" + }, + showText: false + }] + }); + + buttons = element.calendar( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( buttons.length, 1, "correct number of buttons" ); + equal( buttons.attr( "id" ), "my-button-id", "correct id" ); + equal (buttons.text(), "a button", "correct label" ); + ok( buttons.hasClass( "additional-class" ), "additional classes added" ); + deepEqual( buttons.button( "option", "icons" ), { primary: "ui-icon-cancel", secondary: null } ); + equal( buttons.button( "option", "text" ), false ); + buttons.click(); + + element.remove(); +}); + test( "dateFormat", function() { expect( 2 ); var element = $( "#calendar" ).calendar({ diff --git a/tests/unit/calendar/calendar_test_helpers.js b/tests/unit/calendar/calendar_test_helpers.js index 94f3644c3a2..c5dfbd4a271 100644 --- a/tests/unit/calendar/calendar_test_helpers.js +++ b/tests/unit/calendar/calendar_test_helpers.js @@ -15,7 +15,9 @@ TestHelpers.calendar = { equal( d1.toString(), d2.toString(), message ); }, focusGrid: function( element ) { - element.find( "table:tabbable" ).simulate( "focus" ); + element.find( ":tabbable" ).last().simulate( "focus" ); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.TAB } ); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.TAB } ); return $( ":focus" ); } diff --git a/tests/unit/datepicker/datepicker_common.js b/tests/unit/datepicker/datepicker_common.js index 1544db58e84..25c6d429893 100644 --- a/tests/unit/datepicker/datepicker_common.js +++ b/tests/unit/datepicker/datepicker_common.js @@ -1,6 +1,7 @@ TestHelpers.commonWidgetTests( "datepicker", { defaults: { appendTo: null, + buttons: [], dateFormat: { date: "short" }, disabled: false, eachDay: $.noop, diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index f0d93c781a9..ffb7ab6c4f2 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -29,7 +29,7 @@ asyncTest( "base structure", function() { setTimeout(function() { ok( widget.is( ":visible" ), "Datepicker visible" ); - equal( widget.children().length, 3, "Child count" ); + equal( widget.children().length, 2, "Child count" ); ok( widget.is( ".ui-calendar" ), "Class ui-calendar" ); ok( widget.is( ".ui-datepicker" ), "Class ui-datepicker" ); ok( widget.is( ".ui-front" ), "Class ui-front" ); diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index 16546424dac..9cba995651e 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -40,6 +40,22 @@ test( "appendTo", function() { input.datepicker( "destroy" ); }); +test("buttons", function() { + expect( 3 ); + + var btn, + buttons = { + "Ok": function() {}, + "Cancel": function() {} + }, + element = $( "#datepicker" ).datepicker({ buttons: buttons }); + + btn = element.datepicker( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( btn.length, 2, "number of buttons" ); + ok( btn.parent().hasClass( "ui-calendar-buttonset" ), "buttons in container"); + ok( element.datepicker( "widget" ).hasClass( "ui-calendar-buttons" ), "calendar wrapper adds class about having buttons" ); +}); + test( "dateFormat", function() { expect( 2 ); var input = $( "#datepicker" ).val( "1/1/14" ).datepicker(), @@ -149,7 +165,7 @@ test( "showWeek", function() { input.datepicker( "option", "showWeek", true ); equal( container.find( "thead th" ).length, 8, "supports changing option after init" ); }); - + test( "min / max", function() { expect( 14 ); diff --git a/ui/calendar.js b/ui/calendar.js index dbc73697a72..4d444592fdc 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -13,6 +13,7 @@ // AMD. Register as an anonymous module. // TODO Add globalize and $.date + // TODO: Keep button even if its optional? define([ "jquery", "./core", @@ -29,6 +30,7 @@ return $.widget( "ui.calendar", { version: "@VERSION", options: { + buttons: [], dateFormat: { date: "short" }, eachDay: $.noop, max: null, @@ -59,10 +61,6 @@ return $.widget( "ui.calendar", { this.date.adjust( "M", this.options.numberOfMonths ); this.refresh(); }, - "click .ui-calendar-current": function( event ) { - event.preventDefault(); - this._select( event, new Date().getTime() ); - }, "mousedown .ui-calendar-calendar a": function( event ) { event.preventDefault(); @@ -149,7 +147,7 @@ return $.widget( "ui.calendar", { pickerHtml = ""; if ( this.options.numberOfMonths === 1 ) { - pickerHtml = this._buildHeader() + this._buildGrid() + this._buildButtons(); + pickerHtml = this._buildHeader() + this._buildGrid(); } else { pickerHtml = this._buildMultiplePicker(); classes += " ui-calendar-multi"; @@ -163,6 +161,8 @@ return $.widget( "ui.calendar", { }) .html( pickerHtml ); + this._createButtonPane(); + this.grid = this.element.find( ".ui-calendar-calendar" ); }, @@ -197,7 +197,7 @@ return $.widget( "ui.calendar", { html += this._buildTitlebar() + "
      " + this._buildGrid() + "
      "; } - html += "
      " + this._buildButtons(); + html += "
      "; this.date = currentDate; @@ -366,10 +366,58 @@ return $.widget( "ui.calendar", { return content; }, - _buildButtons: function() { - return "
      " + - "" + - "
      "; + _createButtonPane: function() { + this.buttonPane = $( "
      " ) + .addClass( "ui-calendar-buttonpane ui-widget-content ui-helper-clearfix" ); + + this.buttonSet = $( "
      " ) + .addClass( "ui-calendar-buttonset" ) + .appendTo( this.buttonPane ); + + this._createButtons(); + }, + + _createButtons: function() { + var that = this, + buttons = this.options.buttons; + + // if we already have a button pane, remove it + this.buttonPane.remove(); + this.buttonSet.empty(); + + if ( $.isEmptyObject( buttons ) || ( $.isArray( buttons ) && !buttons.length ) ) { + this.element.removeClass( "ui-calendar-buttons" ); + return; + } + + $.each( buttons, function( name, props ) { + var click, buttonOptions; + props = $.isFunction( props ) ? + { click: props, text: name } : + props; + // Default to a non-submitting button + props = $.extend( { type: "button" }, props ); + // Change the context for the click callback to be the main element + click = props.click; + props.click = function() { + click.apply( that._buttonClickContext(), arguments ); + }; + buttonOptions = { + icons: props.icons, + text: props.showText + }; + delete props.icons; + delete props.showText; + $( "", props ) + .button( buttonOptions ) + .appendTo( that.buttonSet ); + }); + this.element.addClass( "ui-calendar-buttons" ); + this.buttonPane.appendTo( this.element ); + }, + + _buttonClickContext: function() { + return this.element[ 0 ]; }, // Refreshing the entire calendar during interaction confuses screen readers, specifically @@ -390,6 +438,7 @@ return $.widget( "ui.calendar", { .children().html( this.labels.prevText ); $( ".ui-calendar-next", this.element ).attr( "title", this.labels.nextText ) .children().html( this.labels.nextText ); + this._createButtons(); } else { this._refreshMultiplePicker(); } @@ -493,6 +542,10 @@ return $.widget( "ui.calendar", { this._super( key, value ); + if ( key === "buttons" ) { + this._createButtons(); + } + if ( key === "eachDay" ) { this.date.eachDay = value; this.refresh(); diff --git a/ui/datepicker.js b/ui/datepicker.js index fc4a0ea2c99..dc719442eb2 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -27,7 +27,7 @@ }(function( $ ) { var widget, - calendarOptions = [ "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ]; + calendarOptions = [ "buttons", "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ]; widget = $.widget( "ui.datepicker", { version: "@VERSION", @@ -91,6 +91,10 @@ widget = $.widget( "ui.datepicker", { }) ) .calendar( "instance" ); + this.calendarInstance._buttonClickContext = function() { + return that.element[ 0 ]; + }; + this._setHiddenPicker(); this.element.attr({ From 2cbd50063a52280b26a2d5c10415ae00bff208ab Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 2 Jul 2014 19:22:49 +0200 Subject: [PATCH 50/53] Calendar: Adjust files to match reorganization of external directory --- tests/unit/date/all.html | 22 +++++++++++----------- tests/unit/date/date.html | 6 +++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/unit/date/all.html b/tests/unit/date/all.html index ca2d66e37d9..8f31592f14b 100644 --- a/tests/unit/date/all.html +++ b/tests/unit/date/all.html @@ -1,20 +1,20 @@ - - jQuery UI Date Test Suite + + jQuery UI Date Test Suite - + - - - - - + + + + + - + diff --git a/tests/unit/date/date.html b/tests/unit/date/date.html index d759fdeaee7..b2dd2c41e22 100644 --- a/tests/unit/date/date.html +++ b/tests/unit/date/date.html @@ -5,14 +5,14 @@ jQuery UI Date Test Suite - - + + TestHelpers.loadResources({ js: [ - "external/globalize.js", + "external/globalize/globalize.js", "external/localization.js", "external/date.js" ] From a4c2033c34c5178dfcc420ed2171d01db37fa313 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Fri, 29 Aug 2014 21:19:30 +0200 Subject: [PATCH 51/53] Datepicker tests: Remove pass-through options unit tests Add Test if these options are set on the underlying calendar instance --- tests/unit/datepicker/datepicker_methods.js | 38 +---- tests/unit/datepicker/datepicker_options.js | 148 +++----------------- 2 files changed, 25 insertions(+), 161 deletions(-) diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 9ea1e5d9ebf..0cefcabf888 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -80,13 +80,11 @@ test( "value", function() { }); test( "valueAsDate", function() { - expect( 13 ); + expect( 6 ); - var minDate, maxDate, dateAndTimeToSet, dateAndTimeClone, - input = TestHelpers.datepicker.init( "#datepicker" ), + var input = TestHelpers.datepicker.init( "#datepicker" ), picker = input.datepicker( "widget" ), - date1 = new Date( 2008, 6 - 1, 4 ), - date2 = new Date(); + date1 = new Date( 2008, 6 - 1, 4 ); input.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); equal( input.val(), "1/1/14", "Input's value set" ); @@ -102,36 +100,6 @@ test( "valueAsDate", function() { strictEqual( input.datepicker( "valueAsDate" ), null, "Set date - default" ); input.datepicker( "valueAsDate", date1 ); TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date - 2008-06-04" ); - - // With minimum/maximum - input = TestHelpers.datepicker.init( "#datepicker" ); - date1 = new Date( 2008, 1 - 1, 4 ); - date2 = new Date( 2008, 6 - 1, 4 ); - minDate = new Date( 2008, 2 - 1, 29 ); - maxDate = new Date( 2008, 3 - 1, 28 ); - - input.val( "" ).datepicker( "option", { min: minDate } ).datepicker( "valueAsDate", date2 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date2, "Set date min/max - value > min" ); - - input.datepicker( "valueAsDate", date1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date2, "Set date min/max - value < min" ); - - input.val( "" ).datepicker( "option", { max: maxDate, min: null } ).datepicker( "valueAsDate", date1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date min/max - value < max" ); - - input.datepicker( "valueAsDate", date2 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date min/max - value > max" ); - - input.val( "" ).datepicker( "option", { min: minDate } ).datepicker( "valueAsDate", date1 ); - ok( input.datepicker( "valueAsDate" ) === null, "Set date min/max - value < min" ); - - input.datepicker( "valueAsDate", date2 ); - ok( input.datepicker( "valueAsDate" ) === null, "Set date min/max - value > max" ); - - dateAndTimeToSet = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); - dateAndTimeClone = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); - input.datepicker( "valueAsDate", dateAndTimeToSet ); - equal( dateAndTimeToSet.getTime(), dateAndTimeClone.getTime(), "Date object passed should not be changed by valueAsDate" ); }); test( "isValid", function() { diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index 9cba995651e..db1ecda149c 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -40,76 +40,34 @@ test( "appendTo", function() { input.datepicker( "destroy" ); }); -test("buttons", function() { - expect( 3 ); - - var btn, - buttons = { - "Ok": function() {}, - "Cancel": function() {} +test( "Pass-through options", function() { + expect( 8 ); + + var options = { + "buttons": { "Test": $.noop }, + "dateFormat": { date: "full" }, + "eachDay": function( day ) { day; }, + "max": new Date( 2000, 0, 1 ), + "min": new Date( 2000, 0, 2 ), + "numberOfMonths": 3, + "showWeek": true }, - element = $( "#datepicker" ).datepicker({ buttons: buttons }); + input = $( "#datepicker" ).val( "1/1/14" ).datepicker(), + datepickerInstance = input.datepicker( "instance" ); - btn = element.datepicker( "widget" ).find( ".ui-calendar-buttonpane button" ); - equal( btn.length, 2, "number of buttons" ); - ok( btn.parent().hasClass( "ui-calendar-buttonset" ), "buttons in container"); - ok( element.datepicker( "widget" ).hasClass( "ui-calendar-buttons" ), "calendar wrapper adds class about having buttons" ); -}); + $.each( options, function( key, value ) { + input.datepicker( "option", key, value ); -test( "dateFormat", function() { - expect( 2 ); - var input = $( "#datepicker" ).val( "1/1/14" ).datepicker(), - picker = input.datepicker( "widget" ), - firstDayLink = picker.find( "td[id]:first a" ); - - input.datepicker( "open" ); - firstDayLink.trigger( "mousedown" ); - equal( input.val(), "1/1/14", "default formatting" ); - - input.datepicker( "option", "dateFormat", { date: "full" } ); - equal( input.val(), "Wednesday, January 1, 2014", "updated formatting" ); - - input.datepicker( "destroy" ); -}); + deepEqual( + datepickerInstance.calendar.calendar( "option", key ), + value, + "option " + key + ": correct value" + ); -test( "eachDay", function() { - expect( 5 ); - var timestamp, - input = $( "#datepicker" ).datepicker(), - picker = input.datepicker( "widget" ), - firstCell = picker.find( "td[id]:first" ); - - equal( firstCell.find( "a" ).length, 1, "days are selectable by default" ); - timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 ); - equal( new Date( timestamp ).getDate(), 1, "first available day is the 1st by default" ); - - // Do not render the 1st of the month - input.datepicker( "option", "eachDay", function( day ) { - if ( day.date === 1 ) { - day.render = false; + if ( key === "dateFormat" ) { + equal( input.val(), "Wednesday, January 1, 2014", "option " + key + ": updated format" ); } }); - firstCell = picker.find( "td[id]:first" ); - timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 ); - equal( new Date( timestamp ).getDate(), 2, "first available day is the 2nd" ); - - // Display the 1st of the month but make it not selectable. - input.datepicker( "option", "eachDay", function( day ) { - if ( day.date === 1 ) { - day.selectable = false; - } - }); - firstCell = picker.find( "td[id]:first" ); - equal( firstCell.find( "a" ).length, 0, "the 1st is not selectable" ); - - input.datepicker( "option", "eachDay", function( day ) { - if ( day.date === 1 ) { - day.extraClasses = "ui-custom"; - } - }); - ok( picker.find( "td[id]:first a" ).hasClass( "ui-custom" ), "extraClasses applied" ); - - input.datepicker( "destroy" ); }); asyncTest( "position", function() { @@ -140,68 +98,6 @@ asyncTest( "position", function() { }); }); -test( "showWeek", function() { - expect( 7 ); - var input = $( "#datepicker" ).datepicker(), - container = input.datepicker( "widget" ); - - equal( container.find( "thead th" ).length, 7, "just 7 days, no column cell" ); - equal( container.find( ".ui-calendar-week-col" ).length, 0, - "no week column cells present" ); - input.datepicker( "destroy" ); - - input = $( "#datepicker" ).datepicker({ showWeek: true }); - container = input.datepicker( "widget" ); - equal( container.find( "thead th" ).length, 8, "7 days + a column cell" ); - ok( container.find( "thead th:first" ).is( ".ui-calendar-week-col" ), - "first cell should have ui-calendar-week-col class name" ); - equal( container.find( ".ui-calendar-week-col" ).length, - container.find( "tr" ).length, "one week cell for each week" ); - input.datepicker( "destroy" ); - - input = $( "#datepicker" ).datepicker(); - container = input.datepicker( "widget" ); - equal( container.find( "thead th" ).length, 7, "no week column" ); - input.datepicker( "option", "showWeek", true ); - equal( container.find( "thead th" ).length, 8, "supports changing option after init" ); -}); - -test( "min / max", function() { - expect( 14 ); - - var inp = TestHelpers.datepicker.init( "#datepicker" ), - minDate = new Date( 2008, 2 - 1, 29 ), - maxDate = new Date( 2008, 12 - 1, 7 ); - - inp.val( "6/4/08" ).datepicker( "option", { min: minDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min" ); - ok( inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { min: null } ).val( "1/4/08" ).datepicker( "option", { min: minDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 4 ), "Min/max - value < min" ); - ok( !inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { min: null } ).val( "6/4/08" ).datepicker( "option", { max: maxDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value < max" ); - ok( inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { max: null } ).val( "1/4/09" ).datepicker( "option", { max: maxDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2009, 1 - 1, 4 ), "Min/max - setDate > max" ); - ok( !inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { max: null } ).val( "1/4/08" ).datepicker( "option", { min: minDate, max: maxDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 4 ), "Min/max - value < min" ); - ok( !inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { max: null } ).val( "6/4/08" ).datepicker( "option", { min: minDate, max: maxDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min, < max" ); - ok( inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { max: null } ).val( "1/4/09" ).datepicker( "option", { min: minDate, max: maxDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2009, 1 - 1, 4 ), "Min/max - value > max" ); - ok( !inp.datepicker( "isValid" ) ); -}); - test( "Stop datepicker from appearing with beforeOpen event handler", function() { expect( 3 ); From 97ce362f8f27ea949018758ccac518c8f98cc4c4 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Sat, 30 Aug 2014 02:06:52 +0200 Subject: [PATCH 52/53] Calendar: Use _on for link hover events --- ui/calendar.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/calendar.js b/ui/calendar.js index 4d444592fdc..7af55b541f2 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -69,16 +69,18 @@ return $.widget( "ui.calendar", { this._select( event, $( event.currentTarget ).data( "timestamp" ) ); this.grid.focus( 1 ); }, + "mouseenter .ui-calendar-header button, .ui-calendar-calendar a": "_hover", + "mouseleave .ui-calendar-header button, .ui-calendar-calendar a": "_hover", "keydown .ui-calendar-calendar": "_handleKeydown" }); - this.element.on( "mouseenter.calendar mouseleave.calendar", ".ui-calendar-header button, .ui-calendar-calendar a", function() { - $( this ).toggleClass( "ui-state-hover" ); - }); - this._createCalendar(); }, + _hover: function( event ) { + $( event.currentTarget ).toggleClass( "ui-state-hover" ); + }, + _handleKeydown: function( event ) { // Only interested navigation keys From d364a898a5eebaccf910c2c7e1f2e8b968171c61 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Mon, 1 Sep 2014 15:36:38 +0200 Subject: [PATCH 53/53] Datepicker: Add missing handling for disabled option --- tests/unit/calendar/calendar_methods.js | 13 +++++++------ tests/unit/datepicker/datepicker_methods.js | 17 ++++++++++------- tests/unit/datepicker/datepicker_options.js | 3 ++- ui/calendar.js | 6 ++++++ ui/datepicker.js | 13 ++++++++++++- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/tests/unit/calendar/calendar_methods.js b/tests/unit/calendar/calendar_methods.js index a60ed3c4c76..7588b395bb5 100644 --- a/tests/unit/calendar/calendar_methods.js +++ b/tests/unit/calendar/calendar_methods.js @@ -11,20 +11,21 @@ test( "destroy", function() { }); test( "enable / disable", function() { - expect( 6 ); + expect( 8 ); var element = $( "#calendar" ).calendar(); - ok( !element.calendar( "option", "disabled" ), "initially enabled" ); - ok( !element.hasClass( "ui-calendar-disabled" ), "does not have disabled class name" ); - element.calendar( "disable" ); ok( element.calendar( "option", "disabled" ), "disabled option is set" ); - ok( element.hasClass( "ui-calendar-disabled" ), "calendar has disabled class name" ); + ok( element.hasClass( "ui-calendar-disabled" ), "has disabled widget class name" ); + ok( element.hasClass( "ui-state-disabled" ), "has disabled state class name" ); + equal( element.attr( "aria-disabled" ), "true", "has ARIA disabled" ); element.calendar( "enable" ); ok( !element.calendar( "option", "disabled" ), "enabled after enable() call" ); - ok( !element.hasClass( "ui-calendar-disabled" ), "no longer has disabled class name" ); + ok( !element.hasClass( "ui-calendar-disabled" ), "no longer has disabled widget class name" ); + ok( !element.hasClass( "ui-state-disabled" ), "no longer has disabled state class name" ); + equal( element.attr( "aria-disabled" ), "false", "no longer has ARIA disabled" ); }); test( "widget", function() { diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 0cefcabf888..123b2e809d0 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -18,21 +18,24 @@ test( "destroy", function() { }); test( "enable / disable", function() { - expect( 6 ); + expect( 10 ); var input = TestHelpers.datepicker.init( "#datepicker" ), - widget = input.datepicker( "widget" ); - - ok( !input.datepicker( "option", "disabled" ), "initially enabled" ); - ok( !widget.hasClass( "ui-datepicker-disabled" ), "does not have disabled class name" ); + calendar = input.datepicker( "widget" ); input.datepicker( "disable" ); ok( input.datepicker( "option", "disabled" ), "disabled option is set" ); - ok( widget.hasClass( "ui-datepicker-disabled" ), "datepicker has disabled class name" ); + ok( calendar.hasClass( "ui-datepicker-disabled" ), "has disabled widget class name" ); + ok( input.hasClass( "ui-state-disabled" ), "has disabled state class name" ); + equal( input.attr( "aria-disabled" ), "true", "has ARIA disabled" ); + equal( input.attr( "disabled" ), "disabled", "input disabled" ); input.datepicker( "enable" ); ok( !input.datepicker( "option", "disabled" ), "enabled after enable() call" ); - ok( !widget.hasClass( "ui-datepicker-disabled" ), "no longer has disabled class name" ); + ok( !calendar.hasClass( "ui-datepicker-disabled" ), "no longer has disabled widget class name" ); + ok( !input.hasClass( "ui-state-disabled" ), "no longer has disabled state class name" ); + equal( input.attr( "aria-disabled" ), "false", "no longer has ARIA disabled" ); + equal( input.attr( "disabled" ), undefined, "input no longer disabled" ); }); test( "widget", function() { diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index db1ecda149c..d60ff35130c 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -41,11 +41,12 @@ test( "appendTo", function() { }); test( "Pass-through options", function() { - expect( 8 ); + expect( 9 ); var options = { "buttons": { "Test": $.noop }, "dateFormat": { date: "full" }, + "disabled": true, "eachDay": function( day ) { day; }, "max": new Date( 2000, 0, 1 ), "min": new Date( 2000, 0, 2 ), diff --git a/ui/calendar.js b/ui/calendar.js index 7af55b541f2..49834a1b686 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -548,6 +548,12 @@ return $.widget( "ui.calendar", { this._createButtons(); } + if ( key === "disabled" ) { + this.element + .toggleClass( "ui-state-disabled", value ) + .attr( "aria-disabled", value ); + } + if ( key === "eachDay" ) { this.date.eachDay = value; this.refresh(); diff --git a/ui/datepicker.js b/ui/datepicker.js index dc719442eb2..87a42f92e92 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -27,7 +27,7 @@ }(function( $ ) { var widget, - calendarOptions = [ "buttons", "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ]; + calendarOptions = [ "buttons", "dateFormat", "disabled", "eachDay", "max", "min", "numberOfMonths", "showWeek" ]; widget = $.widget( "ui.datepicker", { version: "@VERSION", @@ -321,6 +321,17 @@ widget = $.widget( "ui.datepicker", { this.element.val( this.calendarInstance.value() ); } + if ( key === "disabled" ) { + this.element + .prop( "disabled", value ) + .toggleClass( "ui-state-disabled", value ) + .attr( "aria-disabled", value ); + + if ( value ) { + this.close(); + } + } + if ( key === "position" ) { this.calendar.position( this._buildPosition() ); }