diff options
67 files changed, 9723 insertions, 5883 deletions
diff --git a/Gruntfile.js b/Gruntfile.js index 3018702f5..5d77e7393 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 3aca0e7b2..b889c6c36 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", "Datepicker": "datepicker/datepicker.html", "Dialog": "dialog/dialog.html", diff --git a/demos/calendar/buttonbar.html b/demos/calendar/buttonbar.html new file mode 100644 index 000000000..de2ae22dc --- /dev/null +++ b/demos/calendar/buttonbar.html @@ -0,0 +1,37 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Calendar - Display button bar</title> + <link rel="stylesheet" href="../../themes/base/all.css"> + <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> + <script src="../../ui/core.js"></script> + <script src="../../ui/widget.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/calendar.js"></script> + <script src="../../ui/position.js"></script> + <link rel="stylesheet" href="../demos.css"> + <script> + $(function() { + $( "#calendar" ).calendar({ + buttons: { + Today: function() { + $( this ).calendar( "valueAsDate", new Date() ); + } + } + }); + }); + </script> +</head> +<body> + +<div id="calendar"></div> + +<div class="demo-description"> +<p>Display a button for selecting Today's date with the <code>buttons</code> option.</p> +</div> +</body> +</html> diff --git a/demos/calendar/default.html b/demos/calendar/default.html new file mode 100644 index 000000000..a67c2d702 --- /dev/null +++ b/demos/calendar/default.html @@ -0,0 +1,31 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Calendar - Default functionality</title> + <link rel="stylesheet" href="../../themes/base/all.css"> + <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> + <script src="../../ui/core.js"></script> + <script src="../../ui/widget.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/calendar.js"></script> + <script src="../../ui/position.js"></script> + <link rel="stylesheet" href="../demos.css"> + <script> + $(function() { + $( "#calendar" ).calendar(); + }); + </script> +</head> +<body> + +<div id="calendar"></div> + +<div class="demo-description"> +<p>The calendar is a widget for selecting a date using a visual calendar representation.</p> +</div> +</body> +</html> diff --git a/demos/datepicker/dropdown-month-year.html b/demos/calendar/dropdown-month-year.html index 8668018d9..050a89157 100644 --- a/demos/datepicker/dropdown-month-year.html +++ b/demos/calendar/dropdown-month-year.html @@ -2,16 +2,21 @@ <html lang="en"> <head> <meta charset="utf-8"> - <title>jQuery UI Datepicker - Display month & year menus</title> + <title>jQuery UI Calendar - Display month & year menus</title> <link rel="stylesheet" href="../../themes/base/all.css"> <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> <script src="../../ui/core.js"></script> <script src="../../ui/widget.js"></script> - <script src="../../ui/datepicker.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/calendar.js"></script> + <script src="../../ui/position.js"></script> <link rel="stylesheet" href="../demos.css"> <script> $(function() { - $( "#datepicker" ).datepicker({ + $( "#calendar" ).calendar({ changeMonth: true, changeYear: true }); @@ -20,7 +25,7 @@ </head> <body> -<p>Date: <input type="text" id="datepicker"></p> +<div id="calendar"></div> <div class="demo-description"> <p>Show month and year dropdowns in place of the static month/year header to facilitate navigation through large timeframes. Add the boolean <code>changeMonth</code> and <code>changeYear</code> options.</p> diff --git a/demos/calendar/index.html b/demos/calendar/index.html new file mode 100644 index 000000000..751301a00 --- /dev/null +++ b/demos/calendar/index.html @@ -0,0 +1,22 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>jQuery UI Calendar Demos</title> +</head> +<body> + +<ul> + <li><a href="default.html">Default functionality</a></li> + <li><a href="buttonbar.html">Display button bar</a></li> + <li><a href="dropdown-month-year.html">Display month & year menus</a></li> + <li><a href="localization.html">Localize calendar</a></li> + <li><a href="min-max.html">Restrict date range</a></li> + <li><a href="multiple-months.html">Display multiple months</a></li> + <li><a href="other-months.html">Dates in other months</a></li> + <li><a href="show-week.html">Show week of the year</a></li> +</ul> + +</body> +</html> diff --git a/demos/calendar/localization.html b/demos/calendar/localization.html new file mode 100644 index 000000000..9b1e7d070 --- /dev/null +++ b/demos/calendar/localization.html @@ -0,0 +1,43 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Calendar - Localize calendar</title> + <link rel="stylesheet" href="../../themes/base/all.css"> + <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> + <script src="../../ui/core.js"></script> + <script src="../../ui/widget.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/calendar.js"></script> + <script src="../../ui/position.js"></script> + <link rel="stylesheet" href="../demos.css"> + <script> + $(function() { + var calendar = $( "#calendar" ), + select = $( "#locale" ); + + Globalize.locale( select.val() ); + calendar.calendar(); + select.change( function() { + Globalize.locale( $( this ).val() ); + calendar.calendar( "valueAsDate", calendar.calendar( "valueAsDate" ) ); + }); + }); + </script> +</head> +<body> + +<div id="calendar"></div> +<select id="locale"> + <option value="de-DE" selected>German (Deutsch)</option> + <option value="en">English</option> +</select> + +<div class="demo-description"> +<p>Localize the 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.</p> +</div> +</body> +</html> diff --git a/demos/calendar/min-max.html b/demos/calendar/min-max.html new file mode 100644 index 000000000..fa8471e2c --- /dev/null +++ b/demos/calendar/min-max.html @@ -0,0 +1,38 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Calendar - Restrict date range</title> + <link rel="stylesheet" href="../../themes/base/all.css"> + <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> + <script src="../../ui/core.js"></script> + <script src="../../ui/widget.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/calendar.js"></script> + <script src="../../ui/position.js"></script> + <link rel="stylesheet" href="../demos.css"> + <script> + $(function() { + var now = new Date(), + dateMin = new Date( now.getFullYear(), now.getMonth(), now.getDay() + 1 ), + dateMax = new Date( now.getFullYear(), now.getMonth(), now.getDay() + 8 ); + + $( "#calendar" ).calendar({ + min: dateMin, + max: dateMax + }); + }); + </script> +</head> +<body> + +<div id="calendar"></div> + +<div class="demo-description"> +<p>Restrict the range of selectable dates with the <code>min</code> and <code>max</code> options. Set the beginning and end dates as actual dates (<code>new Date(2009, 1 - 1, 26)</code>).</p> +</div> +</body> +</html> diff --git a/demos/calendar/multiple-months.html b/demos/calendar/multiple-months.html new file mode 100644 index 000000000..8366b2204 --- /dev/null +++ b/demos/calendar/multiple-months.html @@ -0,0 +1,33 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Calendar - Display multiple months</title> + <link rel="stylesheet" href="../../themes/base/all.css"> + <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> + <script src="../../ui/core.js"></script> + <script src="../../ui/widget.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/calendar.js"></script> + <script src="../../ui/position.js"></script> + <link rel="stylesheet" href="../demos.css"> + <script> + $(function() { + $( "#calendar" ).calendar({ + numberOfMonths: 3 + }); + }); + </script> +</head> +<body> + +<div id="calendar"></div> + +<div class="demo-description"> +<p>Set the <code>numberOfMonths</code> option to an integer of 2 or more to show multiple months in a single calendar.</p> +</div> +</body> +</html> diff --git a/demos/calendar/other-months.html b/demos/calendar/other-months.html new file mode 100644 index 000000000..0228c62df --- /dev/null +++ b/demos/calendar/other-months.html @@ -0,0 +1,40 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Calendar - Dates in other months</title> + <link rel="stylesheet" href="../../themes/base/all.css"> + <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> + <script src="../../ui/core.js"></script> + <script src="../../ui/widget.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/calendar.js"></script> + <script src="../../ui/position.js"></script> + <link rel="stylesheet" href="../demos.css"> + <script> + $(function() { + $( "#calendar" ).calendar({ + eachDay: function( day ) { + if ( day.lead ) { + day.render = true; + day.selectable = true; + day.extraClasses = "ui-priority-secondary"; + } + } + }); + }); + </script> +</head> +<body> + +<div id="calendar"></div> + +<div class="demo-description"> +<p>The calendar can show dates that come from other than the main month + being displayed. These other dates can also be made selectable.</p> +</div> +</body> +</html> diff --git a/demos/calendar/show-week.html b/demos/calendar/show-week.html new file mode 100644 index 000000000..08687aa0d --- /dev/null +++ b/demos/calendar/show-week.html @@ -0,0 +1,35 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Calendar - Show week of the year</title> + <link rel="stylesheet" href="../../themes/base/all.css"> + <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> + <script src="../../ui/core.js"></script> + <script src="../../ui/widget.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/calendar.js"></script> + <script src="../../ui/position.js"></script> + <link rel="stylesheet" href="../demos.css"> + <script> + $(function() { + $( "#calendar" ).calendar({ + showWeek: true + }); + }); + </script> +</head> +<body> + +<div id="calendar"></div> + +<div class="demo-description"> +<p>The calendar can show the week of the year. The calculation follows + <a href="http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table">Unicode CLDR specification</a>. + This means that some days from one year may be placed into weeks 'belonging' to another year.</p> +</div> +</body> +</html> diff --git a/demos/datepicker/alt-field.html b/demos/datepicker/alt-field.html deleted file mode 100644 index 7e3b50a6d..000000000 --- a/demos/datepicker/alt-field.html +++ /dev/null @@ -1,29 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <title>jQuery UI Datepicker - Populate alternate field</title> - <link rel="stylesheet" href="../../themes/base/all.css"> - <script src="../../external/jquery/jquery.js"></script> - <script src="../../ui/core.js"></script> - <script src="../../ui/widget.js"></script> - <script src="../../ui/datepicker.js"></script> - <link rel="stylesheet" href="../demos.css"> - <script> - $(function() { - $( "#datepicker" ).datepicker({ - altField: "#alternate", - altFormat: "DD, d MM, yy" - }); - }); - </script> -</head> -<body> - -<p>Date: <input type="text" id="datepicker"> <input type="text" id="alternate" size="30"/></p> - -<div class="demo-description"> -<p>Populate an alternate field with its own date format whenever a date is selected using the <code>altField</code> and <code>altFormat</code> options. This feature could be used to present a human-friendly date for user selection, while passing a more computer-friendly date through for further processing.</p> -</div> -</body> -</html> diff --git a/demos/datepicker/animation.html b/demos/datepicker/animation.html index 36860cb42..04b196627 100644 --- a/demos/datepicker/animation.html +++ b/demos/datepicker/animation.html @@ -5,6 +5,9 @@ <title>jQuery UI Datepicker - Animations</title> <link rel="stylesheet" href="../../themes/base/all.css"> <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> <script src="../../ui/core.js"></script> <script src="../../ui/widget.js"></script> <script src="../../ui/effect.js"></script> @@ -14,13 +17,20 @@ <script src="../../ui/effect-drop.js"></script> <script src="../../ui/effect-fold.js"></script> <script src="../../ui/effect-slide.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/calendar.js"></script> + <script src="../../ui/position.js"></script> <script src="../../ui/datepicker.js"></script> <link rel="stylesheet" href="../demos.css"> <script> $(function() { $( "#datepicker" ).datepicker(); $( "#anim" ).on( "change", function() { - $( "#datepicker" ).datepicker( "option", "showAnim", $( this ).val() ); + var value = $( this ).val(), + hideValue = $( this ).find( "option:selected" ).data( "hide" ); + $( "#datepicker" ) + .datepicker( "option", "show", value ) + .datepicker( "option", "hide", hideValue || value ); }); }); </script> @@ -31,16 +41,15 @@ <p>Animations:<br /> <select id="anim"> - <option value="show">Show (default)</option> - <option value="slideDown">Slide down</option> - <option value="fadeIn">Fade in</option> + <option value="show">Fade in/out (default)</option> + <option value="slideDown" data-hide="slideUp">Slide down/up</option> <option value="blind">Blind (UI Effect)</option> <option value="bounce">Bounce (UI Effect)</option> <option value="clip">Clip (UI Effect)</option> <option value="drop">Drop (UI Effect)</option> <option value="fold">Fold (UI Effect)</option> <option value="slide">Slide (UI Effect)</option> - <option value="">None</option> + <option value="false">None</option> </select> </p> diff --git a/demos/datepicker/buttonbar.html b/demos/datepicker/buttonbar.html deleted file mode 100644 index 040ec1112..000000000 --- a/demos/datepicker/buttonbar.html +++ /dev/null @@ -1,28 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <title>jQuery UI Datepicker - Display button bar</title> - <link rel="stylesheet" href="../../themes/base/all.css"> - <script src="../../external/jquery/jquery.js"></script> - <script src="../../ui/core.js"></script> - <script src="../../ui/widget.js"></script> - <script src="../../ui/datepicker.js"></script> - <link rel="stylesheet" href="../demos.css"> - <script> - $(function() { - $( "#datepicker" ).datepicker({ - showButtonPanel: true - }); - }); - </script> -</head> -<body> - -<p>Date: <input type="text" id="datepicker"></p> - -<div class="demo-description"> -<p>Display a button for selecting Today's date and a Done button for closing the calendar with the boolean <code>showButtonPanel</code> option. Each button is enabled by default when the bar is displayed, but can be turned off with additional options. Button text is customizable.</p> -</div> -</body> -</html> diff --git a/demos/datepicker/date-formats.html b/demos/datepicker/date-formats.html index 5664beb5b..f00310a47 100644 --- a/demos/datepicker/date-formats.html +++ b/demos/datepicker/date-formats.html @@ -5,15 +5,29 @@ <title>jQuery UI Datepicker - Format date</title> <link rel="stylesheet" href="../../themes/base/all.css"> <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> <script src="../../ui/core.js"></script> <script src="../../ui/widget.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/position.js"></script> + <script src="../../ui/calendar.js"></script> <script src="../../ui/datepicker.js"></script> <link rel="stylesheet" href="../demos.css"> <script> $(function() { - $( "#datepicker" ).datepicker(); + var value, + datepicker = $( "#datepicker" ).datepicker(); + $( "#format" ).on( "change", function() { - $( "#datepicker" ).datepicker( "option", "dateFormat", $( this ).val() ); + value = $( this ).val(); + + if ( value === "iso" ) { + datepicker.datepicker( "option", "dateFormat", { pattern: "yyyy-MM-dd" } ); + } else { + datepicker.datepicker( "option", "dateFormat", { date: value } ); + } }); }); </script> @@ -24,12 +38,9 @@ <p>Format options:<br /> <select id="format"> - <option value="mm/dd/yy">Default - mm/dd/yy</option> - <option value="yy-mm-dd">ISO 8601 - yy-mm-dd</option> - <option value="d M, y">Short - d M, y</option> - <option value="d MM, y">Medium - d MM, y</option> - <option value="DD, d MM, yy">Full - DD, d MM, yy</option> - <option value="'day' d 'of' MM 'in the year' yy">With text - 'day' d 'of' MM 'in the year' yy</option> + <option value="short">Short - M/d/yy in "en" locale</option> + <option value="long">Long - MMMM d, y in "en" locale</option> + <option value="iso">ISO 8601 - yyyy-MM-dd</option> </select> </p> diff --git a/demos/datepicker/date-range.html b/demos/datepicker/date-range.html deleted file mode 100644 index 8d606969c..000000000 --- a/demos/datepicker/date-range.html +++ /dev/null @@ -1,57 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <title>jQuery UI Datepicker - Select a Date Range</title> - <link rel="stylesheet" href="../../themes/base/all.css"> - <script src="../../external/jquery/jquery.js"></script> - <script src="../../ui/core.js"></script> - <script src="../../ui/widget.js"></script> - <script src="../../ui/datepicker.js"></script> - <link rel="stylesheet" href="../demos.css"> - <script> - $(function() { - var dateFormat = "mm/dd/yy", - from = $( "#from" ) - .datepicker({ - defaultDate: "+1w", - changeMonth: true, - numberOfMonths: 3 - }) - .on( "change", function() { - to.datepicker( "option", "minDate", getDate( this ) ); - }), - to = $( "#to" ).datepicker({ - defaultDate: "+1w", - changeMonth: true, - numberOfMonths: 3 - }) - .on( "change", function() { - from.datepicker( "option", "maxDate", getDate( this ) ); - }); - - function getDate( element ) { - var date; - try { - date = $.datepicker.parseDate( dateFormat, element.value ); - } catch( error ) { - date = null; - } - - return date; - } - }); - </script> -</head> -<body> - -<label for="from">From</label> -<input type="text" id="from" name="from"/> -<label for="to">to</label> -<input type="text" id="to" name="to"/> - -<div class="demo-description"> -<p>Select the date range to search for.</p> -</div> -</body> -</html> diff --git a/demos/datepicker/datepicker-ar.js b/demos/datepicker/datepicker-ar.js deleted file mode 100644 index fc6d2a883..000000000 --- a/demos/datepicker/datepicker-ar.js +++ /dev/null @@ -1,36 +0,0 @@ -/* Arabic Translation for jQuery UI date picker plugin. */ -/* Khaled Alhourani -- me@khaledalhourani.com */ -/* NOTE: monthNames are the original months names and they are the Arabic names, not the new months name فبراير - يناير and there isn't any Arabic roots for these months */ -(function( factory ) { - if ( typeof define === "function" && define.amd ) { - - // AMD. Register as an anonymous module. - define([ "../jquery.ui.datepicker" ], factory ); - } else { - - // Browser globals - factory( jQuery.datepicker ); - } -}(function( datepicker ) { - datepicker.regional['ar'] = { - closeText: 'إغلاق', - prevText: '<السابق', - nextText: 'التالي>', - currentText: 'اليوم', - monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'مايو', 'حزيران', - 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], - monthNamesShort: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'], - dayNames: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], - dayNamesShort: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], - dayNamesMin: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], - weekHeader: 'أسبوع', - dateFormat: 'dd/mm/yy', - firstDay: 6, - isRTL: true, - showMonthAfterYear: false, - yearSuffix: ''}; - datepicker.setDefaults(datepicker.regional['ar']); - - return datepicker.regional['ar']; - -})); diff --git a/demos/datepicker/datepicker-fr.js b/demos/datepicker/datepicker-fr.js deleted file mode 100644 index 33fa80d48..000000000 --- a/demos/datepicker/datepicker-fr.js +++ /dev/null @@ -1,38 +0,0 @@ -/* French initialisation for the jQuery UI date picker plugin. */ -/* Written by Keith Wood (kbwood{at}iinet.com.au), - Stéphane Nahmani (sholby@sholby.net), - Stéphane Raimbault <stephane.raimbault@gmail.com> */ -(function( factory ) { - if ( typeof define === "function" && define.amd ) { - - // AMD. Register as an anonymous module. - define([ "../jquery.ui.datepicker" ], factory ); - } else { - - // Browser globals - factory( jQuery.datepicker ); - } -}(function( datepicker ) { - datepicker.regional['fr'] = { - closeText: 'Fermer', - prevText: 'Précédent', - nextText: 'Suivant', - currentText: 'Aujourd\'hui', - monthNames: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', - 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'], - monthNamesShort: ['janv.', 'févr.', 'mars', 'avril', 'mai', 'juin', - 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'], - dayNames: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], - dayNamesShort: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], - dayNamesMin: ['D','L','M','M','J','V','S'], - weekHeader: 'Sem.', - dateFormat: 'dd/mm/yy', - firstDay: 1, - isRTL: false, - showMonthAfterYear: false, - yearSuffix: ''}; - datepicker.setDefaults(datepicker.regional['fr']); - - return datepicker.regional['fr']; - -})); diff --git a/demos/datepicker/datepicker-he.js b/demos/datepicker/datepicker-he.js deleted file mode 100644 index bf58038f5..000000000 --- a/demos/datepicker/datepicker-he.js +++ /dev/null @@ -1,36 +0,0 @@ -/* Hebrew initialisation for the UI Datepicker extension. */ -/* Written by Amir Hardon (ahardon at gmail dot com). */ -(function( factory ) { - if ( typeof define === "function" && define.amd ) { - - // AMD. Register as an anonymous module. - define([ "../jquery.ui.datepicker" ], factory ); - } else { - - // Browser globals - factory( jQuery.datepicker ); - } -}(function( datepicker ) { - datepicker.regional['he'] = { - closeText: 'סגור', - prevText: '<הקודם', - nextText: 'הבא>', - currentText: 'היום', - monthNames: ['ינואר','פברואר','מרץ','אפריל','מאי','יוני', - 'יולי','אוגוסט','ספטמבר','אוקטובר','נובמבר','דצמבר'], - monthNamesShort: ['ינו','פבר','מרץ','אפר','מאי','יוני', - 'יולי','אוג','ספט','אוק','נוב','דצמ'], - dayNames: ['ראשון','שני','שלישי','רביעי','חמישי','שישי','שבת'], - dayNamesShort: ['א\'','ב\'','ג\'','ד\'','ה\'','ו\'','שבת'], - dayNamesMin: ['א\'','ב\'','ג\'','ד\'','ה\'','ו\'','שבת'], - weekHeader: 'Wk', - dateFormat: 'dd/mm/yy', - firstDay: 0, - isRTL: true, - showMonthAfterYear: false, - yearSuffix: ''}; - datepicker.setDefaults(datepicker.regional['he']); - - return datepicker.regional['he']; - -})); diff --git a/demos/datepicker/datepicker-zh-TW.js b/demos/datepicker/datepicker-zh-TW.js deleted file mode 100644 index ebfd73ac5..000000000 --- a/demos/datepicker/datepicker-zh-TW.js +++ /dev/null @@ -1,36 +0,0 @@ -/* Chinese initialisation for the jQuery UI date picker plugin. */ -/* Written by Ressol (ressol@gmail.com). */ -(function( factory ) { - if ( typeof define === "function" && define.amd ) { - - // AMD. Register as an anonymous module. - define([ "../jquery.ui.datepicker" ], factory ); - } else { - - // Browser globals - factory( jQuery.datepicker ); - } -}(function( datepicker ) { - datepicker.regional['zh-TW'] = { - closeText: '關閉', - prevText: '<上月', - nextText: '下月>', - currentText: '今天', - monthNames: ['一月','二月','三月','四月','五月','六月', - '七月','八月','九月','十月','十一月','十二月'], - monthNamesShort: ['一月','二月','三月','四月','五月','六月', - '七月','八月','九月','十月','十一月','十二月'], - dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'], - dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'], - dayNamesMin: ['日','一','二','三','四','五','六'], - weekHeader: '周', - dateFormat: 'yy/mm/dd', - firstDay: 1, - isRTL: false, - showMonthAfterYear: true, - yearSuffix: '年'}; - datepicker.setDefaults(datepicker.regional['zh-TW']); - - return datepicker.regional['zh-TW']; - -})); diff --git a/demos/datepicker/default.html b/demos/datepicker/default.html index a11d5def7..b2439b29a 100644 --- a/demos/datepicker/default.html +++ b/demos/datepicker/default.html @@ -5,8 +5,14 @@ <title>jQuery UI Datepicker - Default functionality</title> <link rel="stylesheet" href="../../themes/base/all.css"> <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> <script src="../../ui/core.js"></script> <script src="../../ui/widget.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/position.js"></script> + <script src="../../ui/calendar.js"></script> <script src="../../ui/datepicker.js"></script> <link rel="stylesheet" href="../demos.css"> <script> diff --git a/demos/datepicker/icon-trigger.html b/demos/datepicker/icon-trigger.html index 8268f947b..c694631e1 100644 --- a/demos/datepicker/icon-trigger.html +++ b/demos/datepicker/icon-trigger.html @@ -5,18 +5,34 @@ <title>jQuery UI Datepicker - Icon trigger</title> <link rel="stylesheet" href="../../themes/base/all.css"> <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> <script src="../../ui/core.js"></script> <script src="../../ui/widget.js"></script> + <script src="../../ui/calendar.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/position.js"></script> <script src="../../ui/datepicker.js"></script> <link rel="stylesheet" href="../demos.css"> <script> $(function() { - $( "#datepicker" ).datepicker({ - showOn: "button", - buttonImage: "images/calendar.gif", - buttonImageOnly: true, - buttonText: "Select date" - }); + var allowOpen = false, + datepicker = $( "#datepicker" ).datepicker({ + beforeOpen: function() { + return allowOpen; + }, + open: function() { + allowOpen = false; + } + }); + + $( "<img src='images/calendar.gif' alt='Open Datepicker'>") + .insertAfter( datepicker ) + .click( function() { + allowOpen = true; + datepicker.focus(); + }); }); </script> </head> diff --git a/demos/datepicker/index.html b/demos/datepicker/index.html index d9c8dfc10..a56df7bd6 100644 --- a/demos/datepicker/index.html +++ b/demos/datepicker/index.html @@ -9,19 +9,10 @@ <ul> <li><a href="default.html">Default functionality</a></li> + <li><a href="animation.html">Animations</a></li> <li><a href="date-formats.html">Format date</a></li> - <li><a href="min-max.html">Restrict date range</a></li> - <li><a href="localization.html">Localize calendar</a></li> - <li><a href="alt-field.html">Populate alternate field</a></li> - <li><a href="inline.html">Display inline</a></li> - <li><a href="buttonbar.html">Display button bar</a></li> - <li><a href="dropdown-month-year.html">Display month & year menus</a></li> - <li><a href="other-months.html">Dates in other months</a></li> - <li><a href="show-week.html">Show week of the year</a></li> - <li><a href="multiple-calendars.html">Display multiple months</a></li> <li><a href="icon-trigger.html">Icon trigger</a></li> - <li><a href="animation.html">Animations</a></li> - <li><a href="date-range.html">Date Range</a></li> + <li><a href="localization.html">Localize calendar</a></li> </ul> </body> diff --git a/demos/datepicker/inline.html b/demos/datepicker/inline.html deleted file mode 100644 index 5d120457b..000000000 --- a/demos/datepicker/inline.html +++ /dev/null @@ -1,26 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <title>jQuery UI Datepicker - Display inline</title> - <link rel="stylesheet" href="../../themes/base/all.css"> - <script src="../../external/jquery/jquery.js"></script> - <script src="../../ui/core.js"></script> - <script src="../../ui/widget.js"></script> - <script src="../../ui/datepicker.js"></script> - <link rel="stylesheet" href="../demos.css"> - <script> - $(function() { - $( "#datepicker" ).datepicker(); - }); - </script> -</head> -<body> - -Date: <div id="datepicker"></div> - -<div class="demo-description"> -<p>Display the datepicker embedded in the page instead of in an overlay. Simply call .datepicker() on a div instead of an input.</p> -</div> -</body> -</html> diff --git a/demos/datepicker/localization.html b/demos/datepicker/localization.html index 72933f011..36449a516 100644 --- a/demos/datepicker/localization.html +++ b/demos/datepicker/localization.html @@ -5,20 +5,27 @@ <title>jQuery UI Datepicker - Localize calendar</title> <link rel="stylesheet" href="../../themes/base/all.css"> <script src="../../external/jquery/jquery.js"></script> + <script src="../../external/globalize/globalize.js"></script> + <script src="../../external/date.js"></script> + <script src="../../external/localization.js"></script> <script src="../../ui/core.js"></script> <script src="../../ui/widget.js"></script> + <script src="../../ui/button.js"></script> + <script src="../../ui/calendar.js"></script> + <script src="../../ui/position.js"></script> <script src="../../ui/datepicker.js"></script> - <script src="datepicker-ar.js"></script> - <script src="datepicker-fr.js"></script> - <script src="datepicker-he.js"></script> - <script src="datepicker-zh-TW.js"></script> <link rel="stylesheet" href="../demos.css"> <script> $(function() { - $( "#datepicker" ).datepicker( $.datepicker.regional[ "fr" ] ); - $( "#locale" ).on( "change", function() { - $( "#datepicker" ).datepicker( "option", - $.datepicker.regional[ $( this ).val() ] ); + var datepicker = $( "#datepicker" ), + select = $( "#locale" ); + + Globalize.locale( select.val() ); + datepicker.datepicker(); + + select.on( "change", function() { + Globalize.locale( $( this ).val() ); + datepicker.datepicker( "valueAsDate", datepicker.datepicker( "valueAsDate" ) ); }); }); </script> @@ -27,11 +34,8 @@ <p>Date: <input type="text" id="datepicker"/> <select id="locale"> - <option value="ar">Arabic (‫(العربية</option> - <option value="zh-TW">Chinese Traditional (繁體中文)</option> - <option value="">English</option> - <option value="fr" selected="selected">French (Français)</option> - <option value="he">Hebrew (‫(עברית</option> + <option value="de-DE" selected>German (Deutsch)</option> + <option value="en">English</option> </select></p> <div class="demo-description"> diff --git a/demos/datepicker/min-max.html b/demos/datepicker/min-max.html deleted file mode 100644 index 4052c1785..000000000 --- a/demos/datepicker/min-max.html +++ /dev/null @@ -1,26 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <title>jQuery UI Datepicker - Restrict date range</title> - <link rel="stylesheet" href="../../themes/base/all.css"> - <script src="../../external/jquery/jquery.js"></script> - <script src="../../ui/core.js"></script> - <script src="../../ui/widget.js"></script> - <script src="../../ui/datepicker.js"></script> - <link rel="stylesheet" href="../demos.css"> - <script> - $(function() { - $( "#datepicker" ).datepicker({ minDate: -20, maxDate: "+1M +10D" }); - }); - </script> -</head> -<body> - -<p>Date: <input type="text" id="datepicker"></p> - -<div class="demo-description"> -<p>Restrict the range of selectable dates with the <code>minDate</code> and <code>maxDate</code> 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.</p> -</div> -</body> -</html> diff --git a/demos/datepicker/multiple-calendars.html b/demos/datepicker/multiple-calendars.html deleted file mode 100644 index 716b6c68e..000000000 --- a/demos/datepicker/multiple-calendars.html +++ /dev/null @@ -1,29 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <title>jQuery UI Datepicker - Display multiple months</title> - <link rel="stylesheet" href="../../themes/base/all.css"> - <script src="../../external/jquery/jquery.js"></script> - <script src="../../ui/core.js"></script> - <script src="../../ui/widget.js"></script> - <script src="../../ui/datepicker.js"></script> - <link rel="stylesheet" href="../demos.css"> - <script> - $(function() { - $( "#datepicker" ).datepicker({ - numberOfMonths: 3, - showButtonPanel: true - }); - }); - </script> -</head> -<body> - -<p>Date: <input type="text" id="datepicker"></p> - -<div class="demo-description"> -<p>Set the <code>numberOfMonths</code> option to an integer of 2 or more to show multiple months in a single datepicker.</p> -</div> -</body> -</html> diff --git a/demos/datepicker/other-months.html b/demos/datepicker/other-months.html deleted file mode 100644 index 71a06c5d2..000000000 --- a/demos/datepicker/other-months.html +++ /dev/null @@ -1,30 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <title>jQuery UI Datepicker - Dates in other months</title> - <link rel="stylesheet" href="../../themes/base/all.css"> - <script src="../../external/jquery/jquery.js"></script> - <script src="../../ui/core.js"></script> - <script src="../../ui/widget.js"></script> - <script src="../../ui/datepicker.js"></script> - <link rel="stylesheet" href="../demos.css"> - <script> - $(function() { - $( "#datepicker" ).datepicker({ - showOtherMonths: true, - selectOtherMonths: true - }); - }); - </script> -</head> -<body> - -<p>Date: <input type="text" id="datepicker"></p> - -<div class="demo-description"> -<p>The datepicker can show dates that come from other than the main month - being displayed. These other dates can also be made selectable.</p> -</div> -</body> -</html> diff --git a/demos/datepicker/show-week.html b/demos/datepicker/show-week.html deleted file mode 100644 index 362645a7e..000000000 --- a/demos/datepicker/show-week.html +++ /dev/null @@ -1,32 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <title>jQuery UI Datepicker - Show week of the year</title> - <link rel="stylesheet" href="../../themes/base/all.css"> - <script src="../../external/jquery/jquery.js"></script> - <script src="../../ui/core.js"></script> - <script src="../../ui/widget.js"></script> - <script src="../../ui/datepicker.js"></script> - <link rel="stylesheet" href="../demos.css"> - <script> - $(function() { - $( "#datepicker" ).datepicker({ - showWeek: true, - firstDay: 1 - }); - }); - </script> -</head> -<body> - -<p>Date: <input type="text" id="datepicker"></p> - -<div class="demo-description"> -<p>The datepicker can show the week of the year. The default calculation follows - the ISO 8601 definition: the week starts on Monday, the first week of the year - contains the first Thursday of the year. This means that some days from one - year may be placed into weeks 'belonging' to another year.</p> -</div> -</body> -</html> diff --git a/demos/index.html b/demos/index.html index f37874a44..c4a0fdf95 100644 --- a/demos/index.html +++ b/demos/index.html @@ -11,6 +11,7 @@ <li><a href="accordion/">accordion</a></li> <li><a href="autocomplete/">autocomplete</a></li> <li><a href="button/">button</a></li> + <li><a href="calendar/">calendar</a></li> <li><a href="datepicker/">datepicker</a></li> <li><a href="dialog/">dialog</a></li> <li><a href="draggable/">draggable</a></li> diff --git a/external/date.js b/external/date.js new file mode 100644 index 000000000..4d404efe1 --- /dev/null +++ b/external/date.js @@ -0,0 +1,217 @@ +/* + * Calendar math built on jquery-global + * + * Based on Marc Grabanski's jQuery Date Plugin + * http://marcgrabanski.com/articles/jquery-date-plugin + */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ + "jquery", + "globalize" + ], factory ); + } else { + + // Browser globals + factory( jQuery, Globalize ); + } +}( function( $, Globalize ) { + +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" ); + +$.date = function( date, globalFormat ) { + if ( !( this instanceof $.date ) ) { + return new $.date( 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; +}; + +$.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 ) ) { + + // 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/short", 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(), + month: printDate.getMonth(), + year: printDate.getFullYear(), + timestamp: printDate.getTime(), + 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 ); + } + } + 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; + }, + 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 ); + } +}; + +} ) ); diff --git a/external/globalize-old/LICENSE b/external/globalize-old/LICENSE new file mode 100644 index 000000000..9c8b02240 --- /dev/null +++ b/external/globalize-old/LICENSE @@ -0,0 +1,21 @@ +Copyright Software Freedom Conservancy, Inc. +http://jquery.org/license + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/external/globalize/globalize.culture.de-DE.js b/external/globalize-old/globalize.culture.de-DE.js index 5466bd75e..5466bd75e 100644 --- a/external/globalize/globalize.culture.de-DE.js +++ b/external/globalize-old/globalize.culture.de-DE.js diff --git a/external/globalize/globalize.culture.ja-JP.js b/external/globalize-old/globalize.culture.ja-JP.js index a9469d709..a9469d709 100644 --- a/external/globalize/globalize.culture.ja-JP.js +++ b/external/globalize-old/globalize.culture.ja-JP.js diff --git a/external/globalize-old/globalize.js b/external/globalize-old/globalize.js new file mode 100644 index 000000000..a38a32625 --- /dev/null +++ b/external/globalize-old/globalize.js @@ -0,0 +1,1585 @@ +/*! + * Globalize + * + * http://github.com/jquery/globalize + * + * Copyright Software Freedom Conservancy, Inc. + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ + +(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; + } +}; +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 <language code>-<country/region code> + 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; + +Globalize.cultures.en = Globalize.cultures[ "default" ]; + +Globalize.cultureSelector = "en"; + +// +// private variables +// + +regexHex = /^0x[a-f0-9]+$/i; +regexInfinity = /^[+\-]?infinity$/i; +regexParseFloat = /^[+\-]?\d*\.?\d*(e[+\-]?\d+)?$/; +regexTrim = /^\s+|\s+$/g; + +// +// 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; + } + + // 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 : []; + + } 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]"; +}; + +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, "" ); +}; + +truncate = function( value ) { + if ( isNaN( value ) ) { + return NaN; + } + return Math[ value < 0 ? "ceil" : "floor" ]( value ); +}; + +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 + "\'."; + } + 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(); + } + } + else { + ret = value.toString(); + } + return ret; + } + + 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; + } + } + + 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 ); + + // 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 current = ar[ 0 ], + clength = current.length; + + 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 ); + break; + case "f": + // Deciseconds + case "ff": + // Centiseconds + case "fff": + // Milliseconds + ret.push( + padZeros( value.getMilliseconds(), 3 ).substr( 0, clength ) + ); + 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 ) + ); + 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 ) + ); + break; + case "g": + case "gg": + if ( cal.eras ) { + ret.push( + cal.eras[ getEra(value, eras) ].name + ); + } + 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 ); + } + else if ( exponent < 0 ) { + exponent = -exponent; + numberString = zeroPad( numberString, exponent + 1, true ); + right = numberString.slice( -exponent, numberString.length ) + right; + numberString = numberString.slice( 0, -exponent ); + } + + if ( precision > 0 ) { + right = formatInfo[ "." ] + + ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) ); + } + else { + right = ""; + } + + var stringIndex = numberString.length - 1, + sep = formatInfo[ "," ], + ret = ""; + + 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++; + } + } + + return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right; + }; + + 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; + } + if ( !format || format === "i" ) { + return culture.name.length ? value.toLocaleString() : value.toString(); + } + 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 ); + } + 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; + } + + var patternParts = /n|\$|-|%/g, + ret = ""; + for ( ; ; ) { + var index = patternParts.lastIndex, + ar = patternParts.exec( pattern ); + + ret += pattern.slice( index, ar ? ar.index : pattern.length ); + + if ( !ar ) { + break; + } + + 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; + } + } + + return ret; + }; + +}()); + +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; + }; + + 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 ); + } + return ret; + }; + + 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 ) + ]; + } + value = toUpper( value ); + var i = arrayIndexOf( abbr ? upperMonths[1] : upperMonths[0], value ); + if ( i < 0 ) { + i = arrayIndexOf( abbr ? upperMonthsGen[1] : upperMonthsGen[0], value ); + } + return i; + }; + + 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; + } + } + + // 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; + } + + // 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?)"; + break; + case "zzz": + add = "([+-]?\\d\\d?:\\d{2})"; + break; + case "zz": case "z": + add = "([+-]?\\d\\d?)"; + break; + case "/": + add = "(\\/)"; + break; + default: + throw "Invalid date format pattern \'" + m + "\'."; + } + if ( add ) { + regexp.push( add ); + } + groups.push( match[0] ); + } + 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 }; + + // cache the regex for this format. + return re[ format ] = parseRegExp; + }; + + outOfRange = function( value, low, high ) { + return value < low || value > high; + }; + + toUpper = function( value ) { + // "he-IL" has non-breaking space in weekday names. + return value.split( "\u00A0" ).join( " " ).toUpperCase(); + }; + + toUpperArray = function( arr ) { + var results = []; + for ( var i = 0, l = arr.length; i < l; i++ ) { + results[ i ] = toUpper( arr[i] ); + } + return results; + }; + + 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; + } + } + // if pm designator token was found make sure the hours fit the 24-hour clock. + if ( pmHour && hour < 12 ) { + hour += 12; + } + 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 ); + } + return result; + }; +}()); + +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; + } + 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; + } + } + 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; + } + return 0; + }); + // exact match + for ( i = 0; i < l; i++ ) { + lang = prioritized[ i ].lang; + match = cultures[ lang ]; + if ( match ) { + return match; + } + } + + // neutral language match + for ( i = 0; i < l; i++ ) { + lang = prioritized[ i ].lang; + do { + var index = lang.lastIndexOf( "-" ); + if ( index === -1 ) { + break; + } + // strip off the last part. e.g. en-US => en + lang = lang.substr( 0, index ); + match = cultures[ lang ]; + if ( match ) { + return match; + } + } + 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; + } + } + } + } + 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; +}; + +Globalize.localize = function( key, cultureSelector ) { + return this.findClosestCulture( cultureSelector ).messages[ key ] || + this.cultures[ "default" ].messages[ key ]; +}; + +Globalize.parseDate = function( value, formats, culture ) { + culture = this.findClosestCulture( culture ); + + 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 ) { + break; + } + } + } + } + } else { + patterns = culture.calendar.patterns; + for ( prop in patterns ) { + date = parseExact( value, patterns[prop], culture ); + if ( date ) { + break; + } + } + } + + return date || null; +}; + +Globalize.parseInt = function( value, radix, cultureSelector ) { + return truncate( Globalize.parseFloat(value, radix, cultureSelector) ); +}; + +Globalize.parseFloat = function( value, radix, cultureSelector ) { + // radix argument is optional + if ( typeof radix !== "number" ) { + cultureSelector = radix; + radix = 10; + } + + var culture = this.findClosestCulture( cultureSelector ); + var ret = NaN, + nf = culture.numberFormat; + + 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["."] ); + } + + //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, "" ); + + // allow infinity or hexidecimal + if ( regexInfinity.test(value) ) { + ret = parseFloat( value ); + } + else if ( !radix && regexHex.test(value) ) { + ret = parseInt( value, 16 ); + } + 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 ]; + } + + sign = sign || "+"; + + // 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 ); + } + // 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( "" ); + } + // build a natively parsable number string + var p = sign + integer; + if ( fraction !== null ) { + p += "." + fraction; + } + if ( exponent !== null ) { + // exponent itself may have a number patternd + var expSignInfo = parseNegativePattern( exponent, nf, "-n" ); + p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ]; + } + if ( regexParseFloat.test(p) ) { + ret = parseFloat( p ); + } + } + return ret; +}; + +Globalize.culture = function( cultureSelector ) { + // setter + if ( typeof cultureSelector !== "undefined" ) { + this.cultureSelector = cultureSelector; + } + // getter + return this.findClosestCulture( cultureSelector ) || this.cultures[ "default" ]; +}; + +}( this ));
\ No newline at end of file diff --git a/external/globalize/globalize.js b/external/globalize/globalize.js index a38a32625..1086d339d 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 <language code>-<country/region code> - 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: "$" + +}( this, function() { + +/** + * 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() { + + + + var alwaysArray = function( stringOrArray ) { + return typeof stringOrArray === "string" ? [ stringOrArray ] : stringOrArray; + }; + + + + + var common = function( Cldr ) { + + Cldr.prototype.main = function( path ) { + path = alwaysArray( path ); + return this.get( [ "main/{languageId}" ].concat( path ) ); + }; + + }; + + + + + var arrayIsArray = Array.isArray || function( obj ) { + return Object.prototype.toString.call( obj ) === "[object Array]"; + }; + + + + + var pathNormalize = function( path, attributes ) { + if ( arrayIsArray( path ) ) { + path = path.join( "/" ); } - }, - // 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. - */ + if ( typeof path !== "string" ) { + throw new Error( "invalid path \"" + path + "\"" ); } - }, - // For localized strings - messages: {} -}; + // 1: Ignore leading slash `/` + // 2: Ignore leading `cldr/` + path = path + .replace( /^\// , "" ) /* 1 */ + .replace( /^cldr\// , "" ); /* 2 */ + + // Replace {attribute}'s + path = path.replace( /{[a-zA-Z]+}/g, function( name ) { + name = name.replace( /^{([^}]*)}$/, "$1" ); + return attributes[ name ]; + }); -Globalize.cultures[ "default" ].calendar = Globalize.cultures[ "default" ].calendars.standard; + return path.split( "/" ); + }; -Globalize.cultures.en = Globalize.cultures[ "default" ]; -Globalize.cultureSelector = "en"; -// -// private variables -// -regexHex = /^0x[a-f0-9]+$/i; -regexInfinity = /^[+\-]?infinity$/i; -regexParseFloat = /^[+\-]?\d*\.?\d*(e[+\-]?\d+)?$/; -regexTrim = /^\s+|\s+$/g; + var arraySome = function( array, callback ) { + var i, length; + if ( array.some ) { + return array.some( callback ); + } + for ( i = 0, length = array.length; i < length; i++ ) { + if ( callback( array[ i ], i, array ) ) { + return true; + } + } + return false; + }; -// -// 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 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 <alias> 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 <alias> data? + + // 1.3 If the tag is grandfathered (see <variable id="$grandfathered" type="choice"> 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; } - } - 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; - } - // 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 : []; + // 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 ]; + }); - } else { - clone = src && isObject(src) ? src : {}; - } + // [4] + return matchFound ? match : maxLanguageId; + }; - // 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; - } - } + + + 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 ) + "\"" ); } - } - // Return the modified object - return target; -}; + // 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 -isArray = Array.isArray || function( obj ) { - return Object.prototype.toString.call( obj ) === "[object Array]"; -}; + locale = locale.replace( /-/, "_" ); -isFunction = function( obj ) { - return Object.prototype.toString.call( obj ) === "[object Function]"; -}; + // TODO normalize unicode locale extensions. Currently, skipped. + // unicodeLocaleExtensions = locale.split( "_u_" )[ 1 ]; + locale = locale.split( "_u_" )[ 0 ]; -isObject = function( obj ) { - return Object.prototype.toString.call( obj ) === "[object Object]"; -}; + // TODO normalize transformed extensions. Currently, skipped. + // transformedExtensions = locale.split( "_t_" )[ 1 ]; + locale = locale.split( "_t_" )[ 0 ]; -startsWith = function( value, pattern ) { - return value.indexOf( pattern ) === 0; -}; + unicodeLanguageId = locale; -trim = function( value ) { - return ( value + "" ).replace( regexTrim, "" ); -}; + // unicodeLanguageId = ... + switch ( true ) { -truncate = function( value ) { - if ( isNaN( value ) ) { - return NaN; - } - return Math[ value < 0 ? "ceil" : "floor" ]( value ); -}; + // 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; -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; + // 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 "\\": - if ( escaped ) { - strings.push( "\\" ); - } - escaped = !escaped; + + // 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; + + // language.., or root + case /^([a-z]{2}|root)(\b|_)/.test( unicodeLanguageId ): + language = unicodeLanguageId.split( "_" )[ 0 ]; + script = "Zzzz"; + territory = "ZZ"; + variant = unicodeLanguageId.split( "_" )[ 1 ]; + break; + default: - strings.push( c ); - escaped = false; + language = "und"; 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 + "\'."; - } - 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(); + + // 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.<lang>. 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 { - ret = value.toString(); + return node[ path[ i ] ]; + }; + + + + + var bundleParentLookup = function( Cldr, locale ) { + var parent; + + if ( locale === "root" ) { + return; } - return ret; - } - 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 ); + // First, try to find parent on supplemental data. + parent = resourceGet( Cldr._resolved, pathNormalize( [ "supplemental/parentLocales/parentLocale", locale ] ) ); + if ( parent ) { + return parent; } - else { - r = s; + + // Or truncate locale. + parent = locale.substr( 0, locale.lastIndexOf( "_" ) ); + if ( !parent ) { + return "root"; } - return r; - } - function hasDay() { - if ( foundDay || checkedDay ) { - return foundDay; + return parent; + }; + + + + + // @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 ] ]; } - foundDay = dayPartRegExp.test( format ); - checkedDay = true; - return foundDay; - } + node[ path[ i ] ] = value; + }; + - function getPart( date, part ) { - if ( converted ) { - return converted[ part ]; + + + var arrayForEach = function( array, callback ) { + var i, length; + if ( array.forEach ) { + return array.forEach( callback ); } - switch ( part ) { - case 0: - return date.getFullYear(); - case 1: - return date.getMonth(); - case 2: - return date.getDate(); - default: - throw "Invalid part value " + part; + for ( i = 0, length = array.length; i < length; i++ ) { + callback( array[ i ], i, array ); } - } + }; - 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 ); + var jsonMerge = (function() { - // 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 ); + // 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 ] ) ) { - if ( !ar ) { - break; - } + // Concat Arrays + destination[ prop ] = destination[ prop ].concat( source[ prop ] ); - // do not replace any matches that occur inside a string literal. - if ( quoteCount % 2 ) { - ret.push( ar[0] ); - continue; - } + } else if ( prop in destination && typeof destination[ prop ] === "object" ) { - var current = ar[ 0 ], - clength = current.length; + // Merge Objects + destination[ prop ] = merge( destination[ prop ], source[ prop ] ); + + } else { + + // Set new values + destination[ prop ] = source[ prop ]; - 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 ); - break; - case "f": - // Deciseconds - case "ff": - // Centiseconds - case "fff": - // Milliseconds - ret.push( - padZeros( value.getMilliseconds(), 3 ).substr( 0, clength ) - ); - 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 ) - ); - 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 ) - ); - break; - case "g": - case "gg": - if ( cal.eras ) { - ret.push( - cal.eras[ getEra(value, eras) ].name - ); } - 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 ); - } - 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 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; } - if ( precision > 0 ) { - right = formatInfo[ "." ] + - ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) ); + // Resolve path + normalizedPath = pathNormalize( path, attributes ); + + // Check resolved (cached) data first + value = resourceGet( Cldr._resolved, normalizedPath ); + if ( value ) { + return value; } - else { - right = ""; + + // Check raw data + value = resourceGet( Cldr._raw, normalizedPath ); + + if ( !value ) { + // Or, lookup at parent locale + parent = bundleParentLookup( Cldr, locale ); + value = lookup( Cldr, parent, path, jsonMerge( attributes, { languageId: parent }), locale ); } - var stringIndex = numberString.length - 1, - sep = formatInfo[ "," ], - ret = ""; + // Set resolved (cached) + resourceSet( Cldr._resolved, normalizedPath, value ); - 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) : "" ); + return value; + }; - stringIndex -= curSize; + return lookup; - if ( curGroupIndex < groupSizes.length ) { - curSize = groupSizes[ curGroupIndex ]; - curGroupIndex++; - } +}()); + + + var itemGetResolved = function( Cldr, path, attributes ) { + // Resolve path + var normalizedPath = pathNormalize( path, attributes ); + + return resourceGet( Cldr._resolved, normalizedPath ); + }; + + + + + 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" ); } + Cldr._raw = jsonMerge( Cldr._raw, json ); + }; - return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right; + 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 ); }; - 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; + common( Cldr ); + + return Cldr; + + + +}()); + + + var arrayMap = function( array, callback ) { + var clone, i, length; + if ( array.map ) { + return array.map( callback ); } - if ( !format || format === "i" ) { - return culture.name.length ? value.toLocaleString() : value.toString(); + for ( clone = [], i = 0, length = array.length; i < length; i++ ) { + clone[ 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 ); - } - 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 clone; + }; + + + + + var objectValues = function( object ) { + var i, + result = []; + + for ( i in object ) { + result.push( object[ i ] ); } - var patternParts = /n|\$|-|%/g, - ret = ""; - for ( ; ; ) { - var index = patternParts.lastIndex, - ar = patternParts.exec( pattern ); + return result; + }; - ret += pattern.slice( index, ar ? ar.index : pattern.length ); - if ( !ar ) { - break; + + + /** + * 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 }; + }); + }; + + - switch ( ar[0] ) { - case "n": - ret += number; + + /** + * 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 "$": - ret += nf.currency.symbol; + + 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 "-": - // don't make 0 negative - if ( /[1-9]/.test(number) ) { - ret += nf[ "-" ]; + + 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 "%": - ret += nf.percent.symbol; + + case "pattern" in pattern: + result = pattern.pattern; break; + + default: + throw new Error( "Invalid pattern" ); } + + } else { + throw new Error( "Invalid pattern" ); + } + + if ( !result ) { + throw new Error( "Pattern not found" ); } - return ret; + return result; }; -}()); -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; + + var datetimeWeekDays = [ "sun", "mon", "tue", "wed", "thu", "fri", "sat" ]; + + + + var arrayIndexOf = function( array, item ) { + if ( array.indexOf ) { + return array.indexOf( item ); } - } - 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; + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[i] === item ) { + return i; } } - return year; + return -1; }; - 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 ); - } - return ret; + + + + /** + * firstDayOfWeek + */ + var datetimeFirstDayOfWeek = function( cldr ) { + return arrayIndexOf( datetimeWeekDays, cldr.supplemental.weekData.firstDay() ); }; - 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 ) - ]; - } - value = toUpper( value ); - var i = arrayIndexOf( abbr ? upperMonths[1] : upperMonths[0], value ); - if ( i < 0 ) { - i = arrayIndexOf( abbr ? upperMonthsGen[1] : upperMonthsGen[0], value ); + + + + /** + * 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; + }; + + + + + /** + * 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 ); } - return i; + return date; }; - 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 = {}; + + + + /** + * 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 ); } - else { - var reFormat = re[ format ]; - if ( reFormat ) { - return reFormat; - } + for ( length = str.length; length < count; length += 1 ) { + str = ( right ? ( str + "0" ) : ( "0" + str ) ); } + return str; + }; + + - // 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; + + /** + * 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(); } - // 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+)"; + 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; - case "tt": case "t": - add = "(\\D*)"; + + // Hour + case "h": // 1-12 + ret = ( date.getHours() % 12 ) || 12; + pad = true; break; - case "yyyy": - case "fff": - case "ff": - case "f": - add = "(\\d{" + len + "})"; + + case "H": // 0-23 + ret = date.getHours(); + pad = true; 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?)"; + + 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; - case "zzz": - add = "([+-]?\\d\\d?:\\d{2})"; + + // Second + case "s": + ret = date.getSeconds(); + pad = true; break; - case "zz": case "z": - add = "([+-]?\\d\\d?)"; + + case "S": + ret = Math.round( date.getMilliseconds() * Math.pow( 10, length - 3 ) ); + pad = true; break; - case "/": - add = "(\\/)"; + + 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: - throw "Invalid date format pattern \'" + m + "\'."; + return current; } - if ( add ) { - regexp.push( add ); + if ( pad ) { + ret = stringPad( ret, length ); } - groups.push( match[0] ); - } - 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 }; - - // cache the regex for this format. - return re[ format ] = parseRegExp; + return ret; + }); }; - outOfRange = function( value, low, high ) { - return value < low || value > high; - }; - toUpper = function( value ) { - // "he-IL" has non-breaking space in weekday names. - return value.split( "\u00A0" ).join( " " ).toUpperCase(); - }; - toUpperArray = function( arr ) { - var results = []; - for ( var i = 0, l = arr.length; i < l; i++ ) { - results[ i ] = toUpper( arr[i] ); + + 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 results; + return true; }; - 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; + + + + /** + * 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/; } } - } - 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; + + function oneOrTwoDigitsIfLengthOne() { + if ( length === 1 ) { + return tokenRe = /\d\d?/; + } } - } - // if pm designator token was found make sure the hours fit the 24-hour clock. - if ( pmHour && hour < 12 ) { - hour += 12; - } - 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 ); - } - return result; - }; -}()); -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) ]; + function twoDigitsIfLengthTwo() { + if ( length === 2 ) { + return tokenRe = /\d\d/; + } } - else if ( endsWith(value, pos) ) { - ret = [ "+", value.substr(0, value.length - pos.length) ]; + + // 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; } - break; - case "- n": - neg += " "; - pos += " "; - /* falls through */ - case "-n": - if ( startsWith(value, neg) ) { - ret = [ "-", value.substr(neg.length) ]; + + token.type = current; + chr = current.charAt( 0 ), + length = current.length; + + switch ( chr ) { + + // 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; + } + + /* 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 = /./; } - else if ( startsWith(value, pos) ) { - ret = [ "+", value.substr(pos.length) ]; + + if ( !tokenRe ) { + return false; } - break; - case "(n)": - if ( startsWith(value, "(") && endsWith(value, ")") ) { - ret = [ "-", value.substr(1, value.length - 2) ]; + + // Get lexeme and consume it. + value = value.replace( new RegExp( "^" + tokenRe.source ), function( lexeme ) { + token.lexeme = lexeme; + return ""; + }); + + if ( !token.lexeme ) { + return false; } - 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; - } -}; + tokens.push( token ); + return true; + }); -Globalize.findClosestCulture = function( name ) { - var match; - if ( !name ) { - return this.findClosestCulture( this.cultureSelector ) || this.cultures[ "default" ]; - } - if ( typeof name === "string" ) { - name = name.split( "," ); + return valid ? tokens : []; + }; + + + var datetimeParse = (function() { + + function outOfRange( value, low, high ) { + return value < low || value > high; } - 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; - } - 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; - } - } - prioritized.push({ lang: lang, pri: pri }); + + /** + * 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; } - prioritized.sort(function( a, b ) { - if ( a.pri < b.pri ) { - return 1; - } else if ( a.pri > b.pri ) { - return -1; + + valid = arrayEvery( tokens, function( token ) { + var century, chr, value, length; + + if ( token.type === "literal" ) { + // continue + return true; } - return 0; - }); - // exact match - for ( i = 0; i < l; i++ ) { - lang = prioritized[ i ].lang; - match = cultures[ lang ]; - if ( match ) { - return match; + + 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(); } - } - // neutral language match - for ( i = 0; i < l; i++ ) { - lang = prioritized[ i ].lang; - do { - var index = lang.lastIndexOf( "-" ); - if ( index === -1 ) { + switch ( chr ) { + + // Era + case "G": + truncateAt.push( YEAR ); + era = +token.value; break; - } - // strip off the last part. e.g. en-US => en - lang = lang.substr( 0, index ); - match = cultures[ lang ]; - if ( match ) { - return match; - } + + // 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" ); } - while ( 1 ); + + return true; + }); + + if ( !valid || amPm && hour24 ) { + return null; } - // 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; - } - } + if ( era === 0 ) { + // 1 BC = year 0 + date.setFullYear( date.getFullYear() * -1 + 1 ); } - } - 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; -}; + if ( amPm === "pm" && date.getHours() !== 12 ) { + date.setHours( date.getHours() + 12 ); + } -Globalize.localize = function( key, cultureSelector ) { - return this.findClosestCulture( cultureSelector ).messages[ key ] || - this.cultures[ "default" ].messages[ key ]; -}; + // Truncate date at the most precise unit defined. Eg. + // If value is "12/31", and pattern is "MM/dd": + // => new Date( <current Year>, 12, 31, 0, 0, 0, 0 ); + truncateAt = Math.max.apply( null, truncateAt ); + date = datetimeStartOf( date, units[ truncateAt ] ); -Globalize.parseDate = function( value, formats, culture ) { - culture = this.findClosestCulture( culture ); + return date; + }; - 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 ) { - break; - } - } - } +}()); + + + 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 ); } - } else { - patterns = culture.calendar.patterns; - for ( prop in patterns ) { - date = parseExact( value, patterns[prop], culture ); - if ( date ) { - break; + for ( i = 0, length = array.length; i < length; i++ ) { + if ( callback( array[ i ], i, array ) ) { + return true; } } - } + return false; + }; - return date || null; -}; -Globalize.parseInt = function( value, radix, cultureSelector ) { - return truncate( Globalize.parseFloat(value, radix, cultureSelector) ); -}; -Globalize.parseFloat = function( value, radix, cultureSelector ) { - // radix argument is optional - if ( typeof radix !== "number" ) { - cultureSelector = radix; - radix = 10; - } - var culture = this.findClosestCulture( cultureSelector ); - var ret = NaN, - nf = culture.numberFormat; + var defaultLocale; - 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["."] ); + function getLocale( locale ) { + return locale ? new Cldr( locale ) : defaultLocale; } - //Remove percentage character from number string before parsing - if ( value.indexOf(culture.numberFormat.percent.symbol) > -1){ - value = value.replace( culture.numberFormat.percent.symbol, "" ); - } + 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 ); + }; - // remove spaces: leading, trailing and between - and number. Used for negative currency pt-BR - value = value.replace( / /g, "" ); + /** + * 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 ); + }; - // allow infinity or hexidecimal - if ( regexInfinity.test(value) ) { - ret = parseFloat( value ); - } - else if ( !radix && regexHex.test(value) ) { - ret = parseInt( value, 16 ); - } - 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 ]; - } + /** + * 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; + }; - // try parsing as "-n" - if ( sign === "" && nf.pattern[0] !== "-n" ) { - signInfo = parseNegativePattern( value, nf, "-n" ); - sign = signInfo[ 0 ]; - num = signInfo[ 1 ]; - } + /** + * 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 ); - sign = sign || "+"; + value = datetimeFormat( value, pattern, locale ); - // 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 if ( typeof value === "number" ) { + // TODO value = numberFormat( value, pattern, locale ); + throw new Error( "Number Format not implemented yet" ); } - else { - integer = intAndFraction.substr( 0, decimalPos ); - fraction = intAndFraction.substr( decimalPos + decSep.length ); - } - // 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( "" ); - } - // build a natively parsable number string - var p = sign + integer; - if ( fraction !== null ) { - p += "." + fraction; - } - if ( exponent !== null ) { - // exponent itself may have a number patternd - var expSignInfo = parseNegativePattern( exponent, nf, "-n" ); - p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ]; - } - if ( regexParseFloat.test(p) ) { - ret = parseFloat( p ); - } - } - return ret; -}; -Globalize.culture = function( cultureSelector ) { - // setter - if ( typeof cultureSelector !== "undefined" ) { - this.cultureSelector = cultureSelector; - } - // getter - return this.findClosestCulture( cultureSelector ) || this.cultures[ "default" ]; -}; + 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; + + -}( this ));
\ No newline at end of file +})); diff --git a/external/localization.js b/external/localization.js new file mode 100644 index 000000000..a03fcd9b4 --- /dev/null +++ b/external/localization.js @@ -0,0 +1,3137 @@ +/** + * CLDR locale data + */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ + "globalize" + ], factory ); + } else { + + // Browser globals + factory( Globalize ); + } +}( function( Globalize ) { + +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" + } +}; + +$.each( regions, function( name, value ) { + Globalize.loadTranslations( name, { + datepicker : value + }); +}); + +return Globalize; + +} ) ); diff --git a/tests/lib/bootstrap.js b/tests/lib/bootstrap.js index 47289494c..2829c6639 100644 --- a/tests/lib/bootstrap.js +++ b/tests/lib/bootstrap.js @@ -2,8 +2,11 @@ requirejs.config({ paths: { + "date": "../../../external/date", "globalize": "../../../external/globalize/globalize", - "globalize/ja-JP": "../../../external/globalize/globalize.culture.ja-JP", + "globalize-locales": "../../../external/localization", + "globalize-old": "../../../external/globalize-old/globalize", + "globalize-old/ja-JP": "../../../external/globalize-old/globalize.culture.ja-JP", "jquery": jqueryUrl(), "jquery-simulate": "../../../external/jquery-simulate/jquery.simulate", "jshint": "../../../external/jshint/jshint", @@ -16,7 +19,8 @@ requirejs.config({ "ui": "../../../ui" }, shim: { - "globalize/ja-JP": [ "globalize" ], + "date": [ "globalize-locales" ], + "globalize-old/ja-JP": [ "globalize-old" ], "jquery-simulate": [ "jquery" ], "qunit-assert-close": [ "qunit" ], "testswarm": [ "qunit" ] diff --git a/tests/unit/all.html b/tests/unit/all.html index c8352615a..f06dad6d9 100644 --- a/tests/unit/all.html +++ b/tests/unit/all.html @@ -20,6 +20,7 @@ "autocomplete/autocomplete.html", "button/button.html", "core/core.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 000000000..65f71988b --- /dev/null +++ b/tests/unit/calendar/all.html @@ -0,0 +1,26 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Calendar Test Suite</title> + + <script src="../../../external/jquery/jquery.js"></script> + + <link rel="stylesheet" href="../../../external/qunit/qunit.css"> + <link rel="stylesheet" href="../../../external/qunit-composite/qunit-composite.css"> + <script src="../../../external/qunit/qunit.js"></script> + <script src="../../../external/qunit-composite/qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "calendar" ); + </script> +</head> +<body> + +<div id="qunit"></div> +<div id="qunit-fixture"> + +</div> +</body> +</html> diff --git a/tests/unit/calendar/calendar.html b/tests/unit/calendar/calendar.html new file mode 100644 index 000000000..0d28b6351 --- /dev/null +++ b/tests/unit/calendar/calendar.html @@ -0,0 +1,21 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Calendar Test Suite</title> + + <script src="../../../external/requirejs/require.js"></script> + <script src="../../lib/css.js" data-modules="core calendar"></script> + <script src="../../lib/bootstrap.js" data-widget="datepicker"></script> +</head> +<body> + +<div id="qunit"></div> +<div id="qunit-fixture"> + +<div id="calendar"></div> +<div id="calendar2"></div> + +</div> +</body> +</html> diff --git a/tests/unit/calendar/common.js b/tests/unit/calendar/common.js new file mode 100644 index 000000000..bb0f64d0d --- /dev/null +++ b/tests/unit/calendar/common.js @@ -0,0 +1,26 @@ +define( [ + "lib/common", + "ui/calendar", + "globalize-locales" +], function( common ) { + +common.testWidget( "calendar", { + defaults: { + buttons: [], + classes: {}, + dateFormat: { date: "short" }, + disabled: false, + eachDay: $.noop, + max: null, + min: null, + numberOfMonths: 1, + showWeek: false, + value: null, + + // callbacks + create: null, + select: null + } +}); + +} ); diff --git a/tests/unit/calendar/core.js b/tests/unit/calendar/core.js new file mode 100644 index 000000000..92753748f --- /dev/null +++ b/tests/unit/calendar/core.js @@ -0,0 +1,390 @@ +define( [ + "jquery", + "./helper", + "globalize", + "ui/calendar" +], function( $, testHelper, Globalize ) { + +module( "calendar: core" ); + +test( "base structure", function() { + expect( 26 ); + + 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, 2, "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" ); + + 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, 3, "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( 10 ); + + 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(), + "<Zurück", message + "header prev" + ); + equal( + element.find( ".ui-calendar-next" ).text(), + "Vor>", message + "header next" + ); + }; + + Globalize.locale( "de-DE" ); + initCalendar(); + testLocalization( "Init: " ); + element.calendar( "destroy" ); + + Globalize.locale( defaultLocale.locale ); + initCalendar(); + Globalize.locale( "de-DE" ); + element.calendar( "refresh" ); + testLocalization( "After init: " ); + + Globalize.locale( defaultLocale.locale ); +}); + +asyncTest( "keyboard handling", function() { + expect( 10 ); + + var element = $( "#calendar" ); + + function step1() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + testHelper + .focusGrid( element ) + .simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + setTimeout(function() { + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + testHelper.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 ) }); + + testHelper.focusGrid( element ) + .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + + testHelper.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 ) }); + + testHelper.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + setTimeout(function() { + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + testHelper.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 ) }); + + testHelper.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + testHelper.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 ) }); + + testHelper.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + setTimeout(function() { + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + testHelper.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 ) }); + + testHelper.focusGrid( element ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP, altKey: true } ); + setTimeout(function() { + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + testHelper.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2013, 1 - 1, 1 ), + "Keystroke Page Up + ALT moves date to previous year" + ); + element.calendar( "destroy" ); + step7(); + }, 50 ); + } + + function step7() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + testHelper.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + setTimeout(function() { + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + testHelper.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 ) }); + + testHelper.focusGrid( element ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true } ); + setTimeout(function() { + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + testHelper.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2015, 1 - 1, 1 ), + "Keystroke Page Down + ALT 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 ) }); + + testHelper.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + setTimeout(function() { + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + testHelper.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 ) }); + + testHelper.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + setTimeout(function() { + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + testHelper.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 button:contains(10)", element ).simulate( "mousedown" ); + date.setDate( 10 ); + testHelper.equalsDate( + element.calendar( "valueAsDate" ), + date, + "Mouse click" + ); + + element.calendar( "option", "value", new Date( 2008, 2 - 1, 4) ); + $( ".ui-calendar-calendar tbody button:contains(12)", element ).simulate( "mousedown" ); + testHelper.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 button:contains(16)", element ).simulate( "mousedown" ); + testHelper.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 button:contains(18)", element ).simulate( "mousedown" ); + testHelper.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 button:contains(16)", element ).simulate( "mousedown" ); + testHelper.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 button:contains(18)", element ).simulate( "mousedown" ); + testHelper.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2008, 2 - 1, 18 ), + "Mouse click - next + min/max" + ); + start(); + } + + step1(); +}); + +} ); diff --git a/tests/unit/calendar/events.js b/tests/unit/calendar/events.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/unit/calendar/events.js diff --git a/tests/unit/calendar/helper.js b/tests/unit/calendar/helper.js new file mode 100644 index 000000000..a82e96705 --- /dev/null +++ b/tests/unit/calendar/helper.js @@ -0,0 +1,31 @@ +define( [ + "jquery", + "lib/helper" +], function( $, helper ) { + +return $.extend( helper, { + 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( ":tabbable" ).last().simulate( "focus" ); + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.TAB } ); + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.TAB } ); + + return $( document.activeElement ); + } +} ); + +} ); diff --git a/tests/unit/calendar/methods.js b/tests/unit/calendar/methods.js new file mode 100644 index 000000000..e28d30d6b --- /dev/null +++ b/tests/unit/calendar/methods.js @@ -0,0 +1,145 @@ +define( [ + "jquery", + "./helper", + "ui/calendar" +], function( $, testHelper ) { + +module( "calendar: methods" ); + +test( "destroy", function( assert ) { + expect( 1 ); + + assert.domEqual( "#calendar", function() { + $( "#calendar" ).calendar().calendar( "destroy" ); + }); +}); + +test( "enable / disable", function() { + expect( 8 ); + + var element = $( "#calendar" ).calendar(); + + element.calendar( "disable" ); + ok( element.calendar( "option", "disabled" ), "disabled option is set" ); + 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 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() { + 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( "button[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( 11 ); + + var minDate, maxDate, dateAndTimeToSet, dateAndTimeClone, + element = $( "#calendar" ).calendar(), + date1 = new Date( 2008, 6 - 1, 4 ), + date2; + + element.calendar( "valueAsDate", new Date( 2014, 0, 1 ) ); + ok( element.find( "button[data-timestamp]:first" ) + .hasClass( "ui-state-active" ), + "First day marked as selected" + ); + testHelper.equalsDate( element.calendar( "valueAsDate" ), new Date( 2014, 0, 1 ), "Getter" ); + + element.calendar( "destroy" ); + + element.calendar(); + equal( element.calendar( "valueAsDate" ), null, "Set date - default" ); + + element.calendar( "valueAsDate", date1 ); + testHelper.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 ); + testHelper.equalsDate( + element.calendar( "valueAsDate" ), + date2, "Set date min/max - value > min" + ); + + element.calendar( "valueAsDate", date1 ); + testHelper.equalsDate( + element.calendar( "valueAsDate" ), + date2, + "Set date min/max - value < min" + ); + + element + .calendar( "option", { max: maxDate, min: null } ) + .calendar( "valueAsDate", date1 ); + testHelper.equalsDate( + element.calendar( "valueAsDate" ), + date1, + "Set date min/max - value < max" + ); + + element.calendar( "valueAsDate", date2 ); + testHelper.equalsDate( + element.calendar( "valueAsDate" ), + date1, + "Set date min/max - value > max" + ); + + element + .calendar( "option", { min: minDate } ) + .calendar( "valueAsDate", date1 ); + testHelper.equalsDate( + element.calendar( "valueAsDate" ), + date1, + "Set date min/max - value < min" + ); + + element.calendar( "valueAsDate", date2 ); + testHelper.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" + ); +}); + +} ); diff --git a/tests/unit/calendar/options.js b/tests/unit/calendar/options.js new file mode 100644 index 000000000..c6049fce3 --- /dev/null +++ b/tests/unit/calendar/options.js @@ -0,0 +1,323 @@ +define( [ + "jquery", + "./helper", + "ui/calendar" +], function( $, testHelper ) { + +module( "calendar: options" ); + +test("buttons", function() { + expect( 21 ); + + var button, i, newButtons, + buttons = { + "Ok": function( event ) { + ok(true, "button click fires callback" ); + equal( this, element[ 0 ], "context of callback" ); + equal( event.target, button[ 0 ], "event target" ); + }, + "Cancel": function( event ) { + ok( true, "button click fires callback" ); + equal( this, element[ 0 ], "context of callback" ); + equal( event.target, button[ 1 ], "event target" ); + } + }, + element = $( "#calendar" ).calendar({ buttons: buttons }); + + button = element.calendar( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( button.length, 2, "number of buttons" ); + + i = 0; + $.each( buttons, function( key ) { + equal( button.eq( i ).text(), key, "text of button " + ( i + 1 ) ); + i++; + }); + + ok( button.parent().hasClass( "ui-calendar-buttonset" ), "buttons in container" ); + ok( + element.calendar( "widget" ).hasClass( "ui-calendar-buttons" ), + "calendar wrapper adds class about having buttons" + ); + + button.trigger( "click" ); + + newButtons = { + "Close": function( event ) { + ok( true, "button click fires callback" ); + equal( this, element[ 0 ], "context of callback" ); + equal( event.target, button[ 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" + ); + + button = element.calendar( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( button.length, 1, "number of buttons after setter" ); + button.trigger( "click" ); + + i = 0; + $.each( newButtons, function( key ) { + equal( button.eq( i ).text(), key, "text of button " + ( i + 1 ) ); + i += 1; + }); + + element.calendar( "option", "buttons", null ); + button = element.calendar( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( button.length, 0, "all buttons have been removed" ); + equal( element.find( ".ui-calendar-buttonset" ).length, 0, "buttonset has been removed" ); + equal( element.hasClass( "ui-calendar-buttons" ), false, "calendar element 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(); + + element.calendar( "value", "1/1/14" ); + + element.calendar( "widget" ).find( "td[id]:first button" ).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( "button" ).length, 1, "days are selectable by default" ); + timestamp = parseInt( firstCell.find( "button" ).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( "button" ).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" ); + ok( firstCell.find( "button" ).prop( "disabled" ), "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 button" ).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( 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" ); + testHelper.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min" ); + + element + .calendar( "option", { min: minDate } ) + .calendar( "value", "1/4/08" ); + testHelper.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 } ); + testHelper.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value < max" ); + + element + .calendar( "option", { max: maxDate } ) + .calendar( "value", "1/4/09" ); + testHelper.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" ); + testHelper.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" ); + testHelper.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" ); + testHelper.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > max" );}); + +/* +// TODO: Move this to $.date, Globalize or calendar widget +test( "daylightSaving", function() { + expect( 25 ); + var inp = testHelper.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" ); + }); + */ + +} ); diff --git a/tests/unit/date/all.html b/tests/unit/date/all.html new file mode 100644 index 000000000..5248d7ace --- /dev/null +++ b/tests/unit/date/all.html @@ -0,0 +1,26 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Date Test Suite</title> + + <script src="../../../external/jquery/jquery.js"></script> + + <link rel="stylesheet" href="../../../external/qunit/qunit.css"> + <link rel="stylesheet" href="../../../external/qunit-composite/qunit-composite.css"> + <script src="../../../external/qunit/qunit.js"></script> + <script src="../../../external/qunit-composite/qunit-composite.js"></script> + <script src="../subsuite.js"></script> + + <script> + testAllVersions( "date" ); + </script> +</head> +<body> + +<div id="qunit"></div> +<div id="qunit-fixture"> + +</div> +</body> +</html>
\ No newline at end of file diff --git a/tests/unit/date/core.js b/tests/unit/date/core.js new file mode 100644 index 000000000..e6dc16dcf --- /dev/null +++ b/tests/unit/date/core.js @@ -0,0 +1,197 @@ +define( [ + "jquery", + "globalize", + "date" +], function( $, Globalize ) { + +module( "date: core" ); + +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() { + 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(), + 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" } + ], + 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.locale( "de-DE" ); + 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() { + 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.locale( "de-DE" ); + equal( date.setMonth( 2 ).monthName(), "März", "Month name return March (German)" ); + Globalize.locale( "en" ); +}); + +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" ); +}); + +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( "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({ 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/date/date.html b/tests/unit/date/date.html new file mode 100644 index 000000000..1d9ee6c1f --- /dev/null +++ b/tests/unit/date/date.html @@ -0,0 +1,19 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>jQuery UI Date Test Suite</title> + + <script src="../../../external/requirejs/require.js"></script> + <script src="../../lib/css.js"></script> + <script src="../../lib/bootstrap.js" data-modules="core"></script> +</head> +<body> + +<div id="qunit"></div> +<div id="qunit-fixture"> + <div><input type="text" id="inp"><input type="text" id="alt"><div id="inl"></div></div> + <p><input type="text" id="inp2"></p> +</div> +</body> +</html> diff --git a/tests/unit/datepicker/common.js b/tests/unit/datepicker/common.js index 1eecc85cb..bd7aca89e 100644 --- a/tests/unit/datepicker/common.js +++ b/tests/unit/datepicker/common.js @@ -1,7 +1,35 @@ -/* -TestHelpers.commonWidgetTests( "datepicker", { +define( [ + "lib/common", + "ui/datepicker", + "globalize-locales" +], function( common ) { + +common.testWidget( "datepicker", { defaults: { - disabled: false + appendTo: null, + buttons: [], + classes: {}, + dateFormat: { date: "short" }, + disabled: false, + eachDay: $.noop, + max: null, + min: null, + numberOfMonths: 1, + position: { + my: "left top", + at: "left bottom" + }, + show: true, + showWeek: false, + hide: true, + + // callbacks + beforeOpen: null, + close: null, + create: null, + open: null, + select: null } }); -*/ + +} ); diff --git a/tests/unit/datepicker/core.js b/tests/unit/datepicker/core.js index 446f6757e..a9f5b0abf 100644 --- a/tests/unit/datepicker/core.js +++ b/tests/unit/datepicker/core.js @@ -1,530 +1,173 @@ define( [ "jquery", - "lib/common", "./helper", - "ui/datepicker", - "ui/i18n/datepicker-he" -], function( $, common, testHelper ) { + "ui/datepicker" +], function( $, testHelper ) { -module( "datepicker: core", { - setup: function() { - $( "body" ).trigger( "focus" ); - } -}); - -common.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 - 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 ); - var header, title, table, thead, week, panel, inl, child, - inp = testHelper.initNewInput(), - dp = $( "#ui-datepicker-div" ); - - function step1() { - testHelper.onFocus( inp, 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" ); - - 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" ); - - title = header.children( ":last" ); - 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" ); - - table = dp.children( ":eq(1)" ); - ok( table.is( "table.ui-datepicker-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" ); - 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" ); - step2(); - }); - } - - function step2() { - // Editable month/year and button panel - inp = testHelper.initNewInput({ - changeMonth: true, - changeYear: true, - showButtonPanel: true - }); - testHelper.onFocus( inp, 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" ); - - panel = dp.children( ":last" ); - ok( panel.is( "div.ui-datepicker-buttonpane" ), "Structure - button panel division" ); - equal( panel.children().length, 2, "Structure - button panel child count" ); - 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" ); - step3(); - }); - } - - function step3() { - // Multi-month 2 - inp = testHelper.initNewInput({ numberOfMonths: 2 }); - testHelper.onFocus( inp, function() { - ok( dp.is( ".ui-datepicker-multi" ), "Structure multi [2] - multi-month" ); - equal( dp.children().length, 3, "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" ); - - 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" ); - - 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" ); - step4(); - }); - } - - function step4() { - // Multi-month 3 - inp = testHelper.initNewInput({ numberOfMonths: 3 }); - testHelper.onFocus( inp, 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" ); - step5(); - }); - } +module( "datepicker: core" ); - function step5() { - // Multi-month [2, 2] - inp = testHelper.initNewInput({ numberOfMonths: [ 2, 2 ] }); - testHelper.onFocus( inp, function() { - ok( dp.is( ".ui-datepicker-multi" ), "Structure multi - multi-month" ); - equal( dp.children().length, 6, "Structure multi [2,2] - child count" ); +test( "input's value determines starting date", function() { + expect( 3 ); - child = dp.children( ":first" ); - ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure multi [2,2] - first month division" ); + var input = $( "#datepicker" ).val( "1/1/14" ).datepicker(), + picker = input.datepicker( "widget" ); - child = dp.children( ":eq(1)" ); - ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure multi [2,2] - second month division" ); + input.datepicker( "open" ); - child = dp.children( ":eq(2)" ); - ok( child.is( "div.ui-datepicker-row-break" ), "Structure multi [2,2] - row break" ); + equal( picker.find( ".ui-calendar-month" ).html(), "January", "correct month displayed" ); + equal( picker.find( ".ui-calendar-year" ).html(), "2014", "correct year displayed" ); + equal( picker.find( ".ui-state-active" ).html(), "1", "correct day highlighted" ); - child = dp.children( ":eq(3)" ); - ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure multi [2,2] - third month division" ); - - child = dp.children( ":eq(4)" ); - ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure multi [2,2] - fourth month division" ); - - 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 = testHelper.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 = testHelper.init( "#inl", { numberOfMonths: 2 } ); - dp = inl.children(); + input.val( "" ).datepicker( "destroy" ); +}); - 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" ); +asyncTest( "base structure", function() { + expect( 5 ); - child = dp.children( ":first" ); - ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure inline multi - first month division" ); + var input = testHelper.initNewInput(), + widget = input.datepicker( "widget" ); - 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" ); + input.focus(); - child = dp.children( ":eq(2)" ); - ok( child.is( "div.ui-datepicker-row-break" ), "Structure inline multi - row break" ); + setTimeout(function() { + ok( widget.is( ":visible" ), "Datepicker visible" ); + 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" ); - inl.datepicker( "destroy" ); - start(); - }); - } - - step1(); + input.datepicker( "close" ); + start(); + }, 50 ); }); -asyncTest( "customStructure", function() { - expect( 20 ); - var header, panel, title, thead, - inp = testHelper.initNewInput( $.datepicker.regional.he ), - dp = $( "#ui-datepicker-div" ); +asyncTest( "Keyboard handling: input", function() { + expect( 10 ); + var picker, instance, + input = $( "#datepicker" ).datepicker(); function step1() { - inp.datepicker( "option", "showButtonPanel", true ); - - testHelper.onFocus( inp, function() { - ok( dp.is( ".ui-datepicker-rtl" ), "Structure RTL - right-to-left" ); + testHelper.init( input ); + picker = input.datepicker( "widget" ); - 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" ); + ok( !picker.is( ":visible" ), "datepicker closed" ); - 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" ); - - inp.datepicker( "hide" ).datepicker( "destroy" ); + input.val( "" ).simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + ok( picker.is( ":visible" ), "Keystroke down opens datepicker" ); + input.datepicker( "destroy" ); step2(); - }); + }, 100 ); } - // Hide prev/next function step2() { - inp = testHelper.initNewInput({ - hideIfNoPrevNext: true, - minDate: new Date( 2008, 2 - 1, 4 ), - maxDate: new Date( 2008, 2 - 1, 14 ) - }); - inp.val( "02/10/2008" ); - - testHelper.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" ); - - inp.datepicker( "hide" ).datepicker( "destroy" ); - step3(); - }); - } - - // Changeable Month with read-only year - function step3() { - inp = testHelper.initNewInput({ changeMonth: true }); - - testHelper.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" ); - - inp.datepicker( "hide" ).datepicker( "destroy" ); - step4(); - }); - } - - // Changeable year with read-only month - function step4() { - inp = testHelper.initNewInput({ changeYear: true }); + testHelper.init( input ); + picker = input.datepicker( "widget" ); - testHelper.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" ); + ok( !picker.is( ":visible" ), "datepicker closed" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); - step5(); - }); + input.val( "" ).simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + setTimeout(function() { + ok( picker.is( ":visible" ), "Keystroke up opens datepicker" ); + input.datepicker( "destroy" ); + step3(); + }, 100 ); } - // Read-only first day of week - function step5() { - inp = testHelper.initNewInput({ changeFirstDay: false }); - - testHelper.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.datepicker( "hide" ).datepicker( "destroy" ); - start(); - }); + function step3() { + testHelper.init( input ); + instance = input.datepicker( "instance" ); + + // Enter = Select preset date + input + .val( "1/1/14" ) + .datepicker( "refresh" ) + .datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + testHelper.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), + "Keystroke enter - preset" ); + + 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 } ); + testHelper.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 } ); + testHelper.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), + "Keystroke esc - abandoned" ); + + input + .val( "1/2/14" ) + .simulate( "keyup" ); + testHelper.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 2 ), + "Picker updated as user types into input" ); + + input.datepicker( "destroy" ); + start(); } - // 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 ); + step1(); }); -test("keystrokes", function() { - expect( 26 ); - var inp = testHelper.init("#inp"), - date = new Date(); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - testHelper.equalsDate(inp.datepicker("getDate"), date, "Keystroke enter"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - testHelper.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}); - testHelper.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}); - ok(inp.datepicker("getDate") == null, "Keystroke esc"); - inp.val("02/04/2008").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ESCAPE}); - testHelper.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}); - testHelper.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); - testHelper.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); - testHelper.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}); - date.setDate(date.getDate() + 1); - testHelper.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+right"); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.RIGHT}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() - 1); - testHelper.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}); - date.setDate(date.getDate() - 7); - testHelper.equalsDate(inp.datepicker("getDate"), date, "Keystroke ctrl+up"); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.UP}). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() + 7); - testHelper.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}); - date.setDate(date.getDate() + 7); - testHelper.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); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.equalsDate(inp.datepicker("getDate"), new Date(2008, 4 - 1, 4), - "Keystroke pgdn step 2"); +// TODO: implement +test( "ARIA", function() { + expect( 0 ); }); -test("mouse", function() { - expect( 15 ); - var inl, - inp = testHelper.init("#inp"), - dp = $("#ui-datepicker-div"), - date = new Date(); - inp.val("").datepicker("show"); - $(".ui-datepicker-calendar tbody a:contains(10)", dp).simulate("click", {}); - date.setDate(10); - testHelper.equalsDate(inp.datepicker("getDate"), date, "Mouse click"); - inp.val("02/04/2008").datepicker("show"); - $(".ui-datepicker-calendar tbody a:contains(12)", dp).simulate("click", {}); - testHelper.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", {}); - testHelper.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", {}); - testHelper.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); - testHelper.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"); - testHelper.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"); - testHelper.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"); - testHelper.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"); - testHelper.equalsDate(inp.datepicker("getDate"), new Date(2008, 2 - 1, 18), - "Mouse click - next + min/max"); - // Inline - inl = testHelper.init("#inl"); - 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); - testHelper.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", {}); - testHelper.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); - testHelper.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"); - testHelper.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"); - testHelper.equalsDate(inl.datepicker("getDate"), new Date(2008, 3 - 1, 18), - "Mouse click inline - next"); +asyncTest( "mouse", function() { + expect( 4 ); + + var input = testHelper.init( $( "#datepicker" ).val( "" ) ), + picker = input.datepicker( "widget" ); + + input.datepicker( "open" ); + + setTimeout(function() { + input.val( "4/4/08" ).datepicker( "refresh" ).datepicker( "open" ); + $( ".ui-calendar-calendar tbody button:contains(12)", picker ).simulate( "mousedown", {} ); + testHelper.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2008, 4 - 1, 12 ), + "Mouse click - preset" + ); + + input.val( "" ).datepicker( "refresh" ); + input.simulate( "click" ); + strictEqual( input.datepicker( "valueAsDate" ), null, "Mouse click - close" ); + + input.val( "4/4/08" ).datepicker( "refresh" ).datepicker( "open" ); + input.simulate( "click" ); + testHelper.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2008, 4 - 1, 4 ), + "Mouse click - close + preset" + ); + + input.val( "4/4/08" ).datepicker( "refresh" ).datepicker( "open" ); + picker.find( "a.ui-calendar-prev" ).simulate( "click" ); + input.simulate( "click" ); + testHelper.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2008, 4 - 1, 4 ), + "Mouse click - abandoned" + ); + + start(); + }, 100 ); }); } ); diff --git a/tests/unit/datepicker/datepicker.html b/tests/unit/datepicker/datepicker.html index 26d8de3e0..3b20272b1 100644 --- a/tests/unit/datepicker/datepicker.html +++ b/tests/unit/datepicker/datepicker.html @@ -5,7 +5,7 @@ <title>jQuery UI Datepicker Test Suite</title> <script src="../../../external/requirejs/require.js"></script> - <script src="../../lib/css.js" data-modules="core datepicker"></script> + <script src="../../lib/css.js" data-modules="core calendar datepicker"></script> <script src="../../lib/bootstrap.js" data-widget="datepicker"></script> </head> <body> @@ -13,8 +13,8 @@ <div id="qunit"></div> <div id="qunit-fixture"> -<div><input type="text" id="inp"><input type="text" id="alt"><div id="inl"></div></div> -<p><input type="text" id="inp2"></p> +<input type="text" id="datepicker"> +<input type="text" id="datepicker2"> </div> </body> diff --git a/tests/unit/datepicker/events.js b/tests/unit/datepicker/events.js index 634fb6b79..dcadfecf9 100644 --- a/tests/unit/datepicker/events.js +++ b/tests/unit/datepicker/events.js @@ -4,151 +4,135 @@ define( [ "ui/datepicker" ], function( $, testHelper ) { -module("datepicker: events"); - -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 = testHelper.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], testHelper.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], testHelper.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], testHelper.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 = testHelper.init("#inp2"); - inp2.datepicker().datepicker("option", {onClose: callback}).datepicker("show"); - inp.datepicker("show"); - equal(selectedThis, inp2[0], "Callback close this"); -}); +module( "datepicker: events" ); -test("beforeShowDay-getDate", function() { +test( "beforeOpen", function() { expect( 3 ); - var inp = testHelper.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 - $( "<span>January 2010</span>" ).text(), "Initial month"); - $("a.ui-datepicker-next", dp).trigger( "click" ); - $("a.ui-datepicker-next", dp).trigger( "click" ); - // contains non-breaking space - equal($("div.ui-datepicker-title").text(), - $( "<span>March 2010</span>" ).text(), "After next clicks"); - inp.datepicker("hide").datepicker("show"); - $("a.ui-datepicker-prev", dp).trigger( "click" ); - $("a.ui-datepicker-prev", dp).trigger( "click" ); - // contains non-breaking space - equal($("div.ui-datepicker-title").text(), - $( "<span>November 2009</span>" ).text(), "After prev clicks"); - inp.datepicker("hide"); + + var input = testHelper.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() { + expect( 4 ); + + var shouldFire, + input = testHelper.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 button:first" ).simulate( "mousedown" ); +}); + +test( "open", function() { + expect( 2 ); + + var input = testHelper.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" ); +}); + +asyncTest( "select", function() { + expect( 4 ); + + var input = testHelper.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 button: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() { + $( document.activeElement ) + .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() { + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + input.datepicker( "close" ); + start(); + }, 100 ); + } + + step1(); }); } ); diff --git a/tests/unit/datepicker/helper.js b/tests/unit/datepicker/helper.js index 4cd9b48ff..7cc21bb66 100644 --- a/tests/unit/datepicker/helper.js +++ b/tests/unit/datepicker/helper.js @@ -5,34 +5,30 @@ define( [ ], function( $, helper ) { return $.extend( helper, { - 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 ) { - $.datepicker.setDefaults( $.datepicker.regional[ "" ] ); - return $( id ).datepicker( $.extend( { showAnim: "" }, options || {} ) ); + options = $.extend( { show: false, hide: false }, options || {} ); + return $( id ).datepicker( options ); }, - initNewInput: function( options ) { - var id = $( "<input>" ).appendTo( "#qunit-fixture" ); - return this.init( id, options ); - }, - - PROP_NAME: "datepicker" + options = $.extend( { show: false, hide: false }, options || {} ); + return $( "<input>" ).datepicker( options ) + .appendTo( "#qunit-fixture" ); + } } ); } ); diff --git a/tests/unit/datepicker/methods.js b/tests/unit/datepicker/methods.js index 14fefacc2..db3762fff 100644 --- a/tests/unit/datepicker/methods.js +++ b/tests/unit/datepicker/methods.js @@ -4,123 +4,125 @@ define( [ "ui/datepicker" ], function( $, testHelper ) { -module("datepicker: methods"); - -test("destroy", function() { - expect( 33 ); - var inl, - inp = testHelper.init("#inp"); - ok(inp.is(".hasDatepicker"), "Default - marker class set"); - ok($.data(inp[0], testHelper.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], testHelper.PROP_NAME), "Default - instance absent"); - ok(inp.next().is("#alt"), "Default - button absent"); - // With button - inp= testHelper.init("#inp", {showOn: "both"}); - ok(inp.is(".hasDatepicker"), "Button - marker class set"); - ok($.data(inp[0], testHelper.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], testHelper.PROP_NAME), "Button - instance absent"); - ok(inp.next().is("#alt"), "Button - button removed"); - // With append text - inp = testHelper.init("#inp", {appendText: "Testing"}); - ok(inp.is(".hasDatepicker"), "Append - marker class set"); - ok($.data(inp[0], testHelper.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], testHelper.PROP_NAME), "Append - instance absent"); - ok(inp.next().is("#alt"), "Append - append text removed"); - // With both - inp= testHelper.init("#inp", {showOn: "both", buttonImageOnly: true, - buttonImage: "images/calendar.gif", appendText: "Testing"}); - ok(inp.is(".hasDatepicker"), "Both - marker class set"); - ok($.data(inp[0], testHelper.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], testHelper.PROP_NAME), "Both - instance absent"); - ok(inp.next().is("#alt"), "Both - button and append text absent"); - // Inline - inl = testHelper.init("#inl"); - ok(inl.is(".hasDatepicker"), "Inline - marker class set"); - ok(inl.html() !== "", "Inline - datepicker present"); - ok($.data(inl[0], testHelper.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], testHelper.PROP_NAME), "Inline - instance absent"); - ok(inl.next().length === 0 || inl.next().is("p"), "Inline - button absent"); +module( "datepicker: methods" ); + +test( "destroy", function( assert ) { + expect( 3 ); + + var input = $( "#datepicker" ); + assert.domEqual( input, function() { + input.datepicker(); + ok( input.attr( "aria-owns" ), "aria-owns attribute added" ); + ok( input.attr( "aria-haspopup" ), "aria-haspopup attribute added" ); + input.datepicker( "destroy" ); + }); +}); + +test( "enable / disable", function() { + expect( 10 ); + + var input = testHelper.init( "#datepicker" ), + calendar = input.datepicker( "widget" ); + + input.datepicker( "disable" ); + ok( input.datepicker( "option", "disabled" ), "disabled option is set" ); + 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( !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() { + expect( 1 ); + + var actual = $( "#datepicker" ).datepicker().datepicker( "widget" ); + deepEqual( $( "body > .ui-front" )[ 0 ], actual[ 0 ] ); + actual.remove(); +}); + +test( "open / close", function() { + expect( 7 ); + + var input = testHelper.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("enableDisable", function() { - expect( 33 ); - var inl, dp, - inp = testHelper.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 = testHelper.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 = testHelper.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"); - // Inline - inl = testHelper.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"); +test( "value", function() { + expect( 4 ); + + var input = $( "#datepicker" ).datepicker(), + picker = input.datepicker( "widget" ); + + input.datepicker( "value", "1/1/14" ); + equal( input.val(), "1/1/14", "input's value set" ); + + input.datepicker( "open" ); + ok( + picker.find( "button[data-timestamp]" ).eq( 0 ).hasClass( "ui-state-active" ), + "first day marked as selected" + ); + equal( input.datepicker( "value" ), "1/1/14", "getter" ); + + input.val( "abc" ); + strictEqual( input.datepicker( "value" ), null, "Invalid values should return null." ); +}); + +test( "valueAsDate", function() { + expect( 6 ); + + var input = testHelper.init( "#datepicker" ), + picker = input.datepicker( "widget" ), + 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( "button[data-timestamp]" ).eq( 0 ).hasClass( "ui-state-active" ), + "First day marked as selected" + ); + testHelper.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Getter" ); + + input.val( "a/b/c" ); + equal( input.datepicker( "valueAsDate" ), null, "Invalid dates return null" ); + + input.val( "" ).datepicker( "destroy" ); + input = testHelper.init( "#datepicker" ); + + strictEqual( input.datepicker( "valueAsDate" ), null, "Set date - default" ); + input.datepicker( "valueAsDate", date1 ); + testHelper.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date - 2008-06-04" ); +}); + +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" ); }); } ); diff --git a/tests/unit/datepicker/options.js b/tests/unit/datepicker/options.js index 573b29b32..32979eac8 100644 --- a/tests/unit/datepicker/options.js +++ b/tests/unit/datepicker/options.js @@ -1,1127 +1,136 @@ define( [ "jquery", "./helper", - "ui/datepicker", - "ui/i18n/datepicker-fr", - "ui/i18n/datepicker-he", - "ui/i18n/datepicker-zh-CN" + "ui/datepicker" ], function( $, testHelper ) { -module("datepicker: options"); - -test("setDefaults", function() { - expect( 3 ); - testHelper.init("#inp"); - equal($.datepicker._defaults.showOn, "focus", "Initial showOn"); - $.datepicker.setDefaults({showOn: "button"}); - equal($.datepicker._defaults.showOn, "button", "Change default showOn"); - $.datepicker.setDefaults({showOn: "focus"}); - equal($.datepicker._defaults.showOn, "focus", "Restore showOn"); -}); - -test("option", function() { - expect( 17 ); - var inp = testHelper.init("#inp"), - inst = $.data(inp[0], testHelper.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"); - // Get option - inp = testHelper.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 = testHelper.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 = testHelper.init("#inp", { disabled: true }); - ok(inp.datepicker("isDisabled"), "Initially marked as disabled"); - ok(inp[0].disabled, "Field initially disabled"); -}); - -test("change", function() { - expect( 12 ); - var inp = testHelper.init("#inp"), - inst = $.data(inp[0], testHelper.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"); -}); - -(function() { - var url = window.location.search; - url = decodeURIComponent( url.slice( url.indexOf( "swarmURL=" ) + 9 ) ); - - // TODO: This test occassionally fails in IE in TestSwarm - if ( $.ui.ie && url && url.indexOf( "http" ) === 0 ) { - return; - } - - asyncTest( "invocation", function() { - var button, image, - isOldIE = $.ui.ie && ( !document.documentMode || document.documentMode < 9 ), - body = $( "body" ); - - expect( isOldIE ? 25 : 29 ); - - function step0() { - var inp = testHelper.initNewInput(), - dp = $( "#ui-datepicker-div" ); - - button = inp.siblings( "button" ); - ok( button.length === 0, "Focus - button absent" ); - image = inp.siblings( "img" ); - ok( image.length === 0, "Focus - image absent" ); - - testHelper.onFocus( inp, function() { - ok( dp.is( ":visible" ), "Focus - rendered on focus" ); - inp.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); - ok( !dp.is( ":visible" ), "Focus - hidden on exit" ); - step1(); - }); - } - - function step1() { - - var inp = testHelper.initNewInput(), - dp = $( "#ui-datepicker-div" ); - - testHelper.onFocus( inp, function() { - ok( dp.is( ":visible" ), "Focus - rendered on focus" ); - body.simulate( "mousedown", {} ); - ok( !dp.is( ":visible" ), "Focus - hidden on external click" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); - - step2(); - }); +module( "datepicker: options" ); + +test( "appendTo", function() { + expect( 6 ); + var container, + detached = $( "<div>" ), + 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( "Pass-through options", function() { + 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 ), + numberOfMonths: 3, + showWeek: true + }, + input = $( "#datepicker" ).val( "1/1/14" ).datepicker(), + datepickerInstance = input.datepicker( "instance" ); + + $.each( options, function( key, value ) { + input.datepicker( "option", key, value ); + + deepEqual( + datepickerInstance.calendar.calendar( "option", key ), + value, + "option " + key + ": correct value" + ); + + if ( key === "dateFormat" ) { + equal( input.val(), "Wednesday, January 1, 2014", "option " + key + ": updated format" ); } - - function step2() { - var inp = testHelper.initNewInput({ - showOn: "button", - buttonText: "Popup" - }), - dp = $( "#ui-datepicker-div" ); - - ok( !dp.is( ":visible" ), "Button - initially hidden" ); - button = inp.siblings( "button" ); - image = inp.siblings( "img" ); - ok( button.length === 1, "Button - button present" ); - ok( image.length === 0, "Button - image absent" ); - equal( button.text(), "Popup", "Button - button text" ); - - testHelper.onFocus( inp, function() { - ok( !dp.is( ":visible" ), "Button - not rendered on focus" ); - button.trigger( "click" ); - ok( dp.is( ":visible" ), "Button - rendered on button click" ); - button.trigger( "click" ); - ok( !dp.is( ":visible" ), "Button - hidden on second button click" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); - - step3(); - }); - } - - function step3() { - var inp = testHelper.initNewInput({ - showOn: "button", - buttonImageOnly: true, - buttonImage: "images/calendar.gif", - buttonText: "Cal" - }), - dp = $( "#ui-datepicker-div" ); - - ok( !dp.is( ":visible" ), "Image button - initially hidden" ); - button = inp.siblings( "button" ); - ok( button.length === 0, "Image button - button absent" ); - image = inp.siblings( "img" ); - ok( image.length === 1, "Image button - image present" ); - ok( /images\/calendar\.gif$/.test( image.attr( "src" ) ), "Image button - image source" ); - equal( image.attr( "title" ), "Cal", "Image button - image text" ); - - testHelper.onFocus( inp, function() { - ok( !dp.is( ":visible" ), "Image button - not rendered on focus" ); - image.trigger( "click" ); - ok( dp.is( ":visible" ), "Image button - rendered on image click" ); - image.trigger( "click" ); - ok( !dp.is( ":visible" ), "Image button - hidden on second image click" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); - - step4(); - }); - } - - function step4() { - var inp = testHelper.initNewInput({ - showOn: "both", - buttonImage: "images/calendar.gif" - }), - dp = $( "#ui-datepicker-div" ); - - ok( !dp.is( ":visible" ), "Both - initially hidden" ); - button = inp.siblings( "button" ); - ok( button.length === 1, "Both - button present" ); - image = inp.siblings( "img" ); - ok( image.length === 0, "Both - image absent" ); - image = button.children( "img" ); - ok( image.length === 1, "Both - button image present" ); - - // TODO: This test occasionally fails to focus in IE8 in BrowserStack - if ( !isOldIE ) { - testHelper.onFocus( inp, function() { - ok( dp.is( ":visible" ), "Both - rendered on focus" ); - body.simulate( "mousedown", {} ); - ok( !dp.is( ":visible" ), "Both - hidden on external click" ); - button.trigger( "click" ); - ok( dp.is( ":visible" ), "Both - rendered on button click" ); - button.trigger( "click" ); - ok( !dp.is( ":visible" ), "Both - hidden on second button click" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); - - start(); - }); - } else { - start(); - } - } - - step0(); }); -})(); - -test("otherMonths", function() { - expect( 8 ); - var inp = testHelper.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 - $( "<span>\u00a0123456789101112131415161718192021222324252627282930\u00a0\u00a0\u00a0\u00a0</span>" ).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 - $( "<span>\u00a0123456789101112131415161718192021222324252627282930\u00a0\u00a0\u00a0\u00a0</span>" ).text(), - "Other months - none"); - ok(pop.find("td:last *").length === 0, "Other months - no content"); -}); - -test("defaultDate", function() { - expect( 16 ); - var inp = testHelper.init("#inp"), - date = new Date(); - inp.val("").datepicker("show"). - simulate("keydown", {keyCode: $.ui.keyCode.ENTER}); - testHelper.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}); - date.setDate(date.getDate() - 2); - testHelper.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}); - date.setDate(date.getDate() + 3); - testHelper.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}); - testHelper.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}); - date.setDate(date.getDate() - 1); - testHelper.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); - testHelper.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); - testHelper.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); - testHelper.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 = testHelper.addMonths(new Date(), -1); - testHelper.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 = testHelper.addMonths(new Date(), 2); - testHelper.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); - testHelper.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); - testHelper.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 = testHelper.addMonths(new Date(), 1); - date.setDate(date.getDate() + 10); - testHelper.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}); - date = new Date(2007, 7 - 1, 4); - testHelper.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); - testHelper.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}); - testHelper.equalsDate(inp.datepicker("getDate"), date, "Default date 01/26/2007"); -}); - -test("miscellaneous", function() { - expect( 19 ); - var curYear, longNames, shortNames, date, - dp = $("#ui-datepicker-div"), - inp = testHelper.init("#inp"); - // Year range - function genRange(start, offset) { - var i = start, - range = ""; - for (; i < start + offset; i++) { - range += i; - } - 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"); - - // 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"); - 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"); }); -test("minMax", function() { - expect( 23 ); - var date, - inp = testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.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}); - testHelper.equalsDate(inp.datepicker("getDate"), date, - "Min/max - -1w, +1 M +10 D - ctrl+pgup"); - date = testHelper.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}); - testHelper.equalsDate(inp.datepicker("getDate"), date, - "Min/max - -1w, +1 M +10 D - ctrl+pgdn"); - // With existing date - inp = testHelper.init("#inp"); - inp.val("06/04/2008").datepicker("option", {minDate: minDate}); - testHelper.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}); - testHelper.equalsDate(inp.datepicker("getDate"), minDate, "Min/max - setDate < min"); - inp.datepicker("option", {minDate: null}).val("06/04/2008").datepicker("option", {maxDate: maxDate}); - testHelper.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}); - testHelper.equalsDate(inp.datepicker("getDate"), maxDate, "Min/max - setDate > max"); - inp.datepicker("option", {maxDate: null}).val("01/04/2008").datepicker("option", {minDate: minDate, maxDate: maxDate}); - testHelper.equalsDate(inp.datepicker("getDate"), minDate, "Min/max - setDate < min"); - inp.datepicker("option", {maxDate: null}).val("06/04/2008").datepicker("option", {minDate: minDate, maxDate: maxDate}); - testHelper.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}); - testHelper.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: "-7Y", - yearRange: "1900:-7" - }).val( "" ); - ok(dp.find(".ui-datepicker-next").hasClass("ui-state-disabled"), "Year Range Test - relative - next button disabled"); - ok(!dp.find(".ui-datepicker-prev").hasClass("ui-state-disabled"), "Year Range Test - relative - prev button enabled"); - - 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 - absolute - next button disabled"); - ok(!dp.find(".ui-datepicker-prev").hasClass("ui-state-disabled"), "Year Range Test - absolute - prev button enabled"); -}); - -test("setDate", function() { - expect( 24 ); - var inl, alt, minDate, maxDate, dateAndTimeToSet, dateAndTimeClone, - inp = testHelper.init("#inp"), - date1 = new Date(2008, 6 - 1, 4), - date2 = new Date(); - ok(inp.datepicker("getDate") == null, "Set date - default"); - inp.datepicker("setDate", date1); - testHelper.equalsDate(inp.datepicker("getDate"), date1, "Set date - 2008-06-04"); - date1 = new Date(); - date1.setDate(date1.getDate() + 7); - inp.datepicker("setDate", +7); - testHelper.equalsDate(inp.datepicker("getDate"), date1, "Set date - +7"); - date2.setFullYear(date2.getFullYear() + 2); - inp.datepicker("setDate", "+2y"); - testHelper.equalsDate(inp.datepicker("getDate"), date2, "Set date - +2y"); - inp.datepicker("setDate", date1, date2); - testHelper.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"); - testHelper.equalsDate(inp.datepicker("getDate"), date1, "Set date - c +7"); - date1.setDate(date1.getDate() + 7); - inp.datepicker("setDate", "c+7"); - testHelper.equalsDate(inp.datepicker("getDate"), date1, "Set date - c+7"); - date1.setDate(date1.getDate() - 21); - inp.datepicker("setDate", "c -3 w"); - testHelper.equalsDate(inp.datepicker("getDate"), date1, "Set date - c -3 w"); - // Inline - inl = testHelper.init("#inl"); - date1 = new Date(2008, 6 - 1, 4); - date2 = new Date(); - testHelper.equalsDate(inl.datepicker("getDate"), date2, "Set date inline - default"); - inl.datepicker("setDate", date1); - testHelper.equalsDate(inl.datepicker("getDate"), date1, "Set date inline - 2008-06-04"); - date1 = new Date(); - date1.setDate(date1.getDate() + 7); - inl.datepicker("setDate", +7); - testHelper.equalsDate(inl.datepicker("getDate"), date1, "Set date inline - +7"); - date2.setFullYear(date2.getFullYear() + 2); - inl.datepicker("setDate", "+2y"); - testHelper.equalsDate(inl.datepicker("getDate"), date2, "Set date inline - +2y"); - inl.datepicker("setDate", date1, date2); - testHelper.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"}); - 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"); - // With minimum/maximum - inp = testHelper.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); - testHelper.equalsDate(inp.datepicker("getDate"), date2, "Set date min/max - setDate > min"); - inp.datepicker("setDate", date1); - testHelper.equalsDate(inp.datepicker("getDate"), minDate, "Set date min/max - setDate < min"); - inp.val("").datepicker("option", {maxDate: maxDate, minDate: null}).datepicker("setDate", date1); - testHelper.equalsDate(inp.datepicker("getDate"), date1, "Set date min/max - setDate < max"); - inp.datepicker("setDate", date2); - testHelper.equalsDate(inp.datepicker("getDate"), maxDate, "Set date min/max - setDate > max"); - inp.val("").datepicker("option", {minDate: minDate}).datepicker("setDate", date1); - testHelper.equalsDate(inp.datepicker("getDate"), minDate, "Set date min/max - setDate < min"); - inp.datepicker("setDate", date2); - testHelper.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"); -}); - -test("altField", function() { - expect( 10 ); - var inp = testHelper.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"); - // 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"); - // 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"); - // 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"); - // 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"); -}); - -test("autoSize", function() { - expect( 15 ); - var inp = testHelper.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"); - - // 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"); -}); - -test("daylightSaving", function() { - expect( 25 ); - var inp = testHelper.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"); - // 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"); - // 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"); - // 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"); -}); - -var beforeShowThis = null, - beforeShowInput = null, - beforeShowInst = null, - beforeShowDayThis = null, - beforeShowDayOK = true; - -function beforeAll(input, inst) { - beforeShowThis = this; - beforeShowInput = input; - beforeShowInst = inst; - return {currentText: "Current"}; -} - -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" : "")]; -} - -test("callbacks", function() { - expect( 13 ); - // Before show - var dp, day20, day21, - inp = testHelper.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 = testHelper.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() { - expect( 1 ); - var inp, dp; - inp = testHelper.init("#inp", { - beforeShowDay: function() { - return [ true, "", "'" ]; - } - }); - dp = $("#ui-datepicker-div"); - - inp.datepicker("show"); - equal( dp.find( ".ui-datepicker-calendar td:contains('9')").attr( "title" ), "'" ); - inp.datepicker("hide").datepicker("destroy"); -}); - -test("localisation", function() { - expect( 24 ); - var dp, month, day, date, - inp = testHelper.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() { - equal($(this).text(), $.datepicker.regional.fr.monthNamesShort[month], - "Localisation - month " + month); - month++; - }); - day = 1; - $(".ui-datepicker-calendar th", dp).each(function() { - equal($(this).text(), $.datepicker.regional.fr.dayNamesMin[day], - "Localisation - day " + day); - day = (day + 1) % 7; +asyncTest( "position", function(assert) { + expect( 3 ); + var input = $( "<input>" ).datepicker().appendTo( "body" ).css({ + position: "absolute", + top: 0, + left: 0 + }), + container = input.datepicker( "widget" ); + + input.datepicker( "open" ); + setTimeout(function() { + assert.close( input.offset().left, container.offset().left, 1, "left sides line up by default" ); + assert.close( 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" + }); + assert.close( container.offset().left, input.offset().left + input.outerWidth(), 1, + "datepicker on right hand side of input after position change" ); + + input.remove(); + start(); }); - 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"); -}); - -test("noWeekends", function() { - expect( 31 ); - var i, date; - for (i = 1; i <= 31; i++) { - date = new Date(2001, 1 - 1, i); - deepEqual($.datepicker.noWeekends(date), [(i + 1) % 7 >= 2, ""], - "No weekends " + date); - } -}); - -test("iso8601Week", function() { - expect( 12 ); - var date = new Date(2000, 12 - 1, 31); - equal($.datepicker.iso8601Week(date), 52, "ISO 8601 week " + date); - date = new Date(2001, 1 - 1, 1); - equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); - date = new Date(2001, 1 - 1, 7); - equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); - date = new Date(2001, 1 - 1, 8); - equal($.datepicker.iso8601Week(date), 2, "ISO 8601 week " + date); - date = new Date(2003, 12 - 1, 28); - equal($.datepicker.iso8601Week(date), 52, "ISO 8601 week " + date); - date = new Date(2003, 12 - 1, 29); - equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); - date = new Date(2004, 1 - 1, 4); - equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); - date = new Date(2004, 1 - 1, 5); - equal($.datepicker.iso8601Week(date), 2, "ISO 8601 week " + date); - date = new Date(2009, 12 - 1, 28); - equal($.datepicker.iso8601Week(date), 53, "ISO 8601 week " + date); - date = new Date(2010, 1 - 1, 3); - equal($.datepicker.iso8601Week(date), 53, "ISO 8601 week " + date); - date = new Date(2010, 1 - 1, 4); - equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); - date = new Date(2010, 1 - 1, 10); - equal($.datepicker.iso8601Week(date), 1, "ISO 8601 week " + date); -}); - -test("parseDate", function() { - expect( 26 ); - testHelper.init("#inp"); - var currentYear, gmtDate, fr, settings, zh; - ok($.datepicker.parseDate("d m y", "") == null, "Parse date empty"); - testHelper.equalsDate($.datepicker.parseDate("d m y", "3 2 01"), - new Date(2001, 2 - 1, 3), "Parse date d m y"); - testHelper.equalsDate($.datepicker.parseDate("dd mm yy", "03 02 2001"), - new Date(2001, 2 - 1, 3), "Parse date dd mm yy"); - testHelper.equalsDate($.datepicker.parseDate("d m y", "13 12 01"), - new Date(2001, 12 - 1, 13), "Parse date d m y"); - testHelper.equalsDate($.datepicker.parseDate("dd mm yy", "13 12 2001"), - new Date(2001, 12 - 1, 13), "Parse date dd mm yy"); - testHelper.equalsDate($.datepicker.parseDate("y-o", "01-34"), - new Date(2001, 2 - 1, 3), "Parse date y-o"); - testHelper.equalsDate($.datepicker.parseDate("yy-oo", "2001-347"), - new Date(2001, 12 - 1, 13), "Parse date yy-oo"); - testHelper.equalsDate($.datepicker.parseDate("oo yy", "348 2004"), - new Date(2004, 12 - 1, 13), "Parse date oo yy"); - testHelper.equalsDate($.datepicker.parseDate("D d M y", "Sat 3 Feb 01"), - new Date(2001, 2 - 1, 3), "Parse date D d M y"); - testHelper.equalsDate($.datepicker.parseDate("d MM DD yy", "3 February Saturday 2001"), - new Date(2001, 2 - 1, 3), "Parse date dd MM DD yy"); - testHelper.equalsDate($.datepicker.parseDate("DD, MM d, yy", "Saturday, February 3, 2001"), - new Date(2001, 2 - 1, 3), "Parse date DD, MM d, yy"); - testHelper.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(); - testHelper.equalsDate($.datepicker.parseDate("y-m-d", (currentYear - 2000) + "-02-03"), - new Date(currentYear, 2 - 1, 3), "Parse date y-m-d - default cutuff"); - testHelper.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"); - testHelper.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"); - testHelper.equalsDate($.datepicker.parseDate("y-m-d", "80-02-03", {shortYearCutoff: 80}), - new Date(2080, 2 - 1, 3), "Parse date y-m-d - cutoff 80"); - testHelper.equalsDate($.datepicker.parseDate("y-m-d", "81-02-03", {shortYearCutoff: 80}), - new Date(1981, 2 - 1, 3), "Parse date y-m-d - cutoff 80"); - testHelper.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"); - testHelper.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()); - testHelper.equalsDate($.datepicker.parseDate("@", "981158400000"), gmtDate, "Parse date @"); - testHelper.equalsDate($.datepicker.parseDate("!", "631167552000000000"), gmtDate, "Parse date !"); - - fr = $.datepicker.regional.fr; - settings = {dayNamesShort: fr.dayNamesShort, dayNames: fr.dayNames, - monthNamesShort: fr.monthNamesShort, monthNames: fr.monthNames}; - testHelper.equalsDate($.datepicker.parseDate("D d M y", "Lun. 9 avr. 01", settings), - new Date(2001, 4 - 1, 9), "Parse date D M y with settings"); - testHelper.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"); - testHelper.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"); - testHelper.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"]; - testHelper.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() { - expect( 18 ); - testHelper.init("#inp"); - var fr, settings; - function expectError(expr, value, error) { - try { - expr(); - ok(false, "Parsed error " + value); - } - catch (e) { - 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("mm dd yy", "2 1 01"); }, - "2 1 01 - dd mm yy", "Missing number at position 4"); - 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"); -}); - -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"); - }catch(e){ - ok("invalid date detected"); - } - - try { - 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"); - } }); -test("formatDate", function() { - expect( 16 ); - testHelper.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", - new Date(2001, 2 - 1, 3)), "day 3 of February ('Saturday'), 2001", - "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 !"); - 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. avr. 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"); -}); - -// 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(){ -// expect( 1 ); -// 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() { +test( "Stop datepicker from appearing with beforeOpen event handler", function() { expect( 3 ); - var inp, dp; - - inp = testHelper.init( "#inp", { - beforeShow: function() { - } + var input = testHelper.init( "#datepicker", { + beforeOpen: function() {} }); - dp = $( "#ui-datepicker-div" ); - inp.datepicker( "show" ); - equal( dp.css( "display" ), "block", "beforeShow returns nothing" ); - inp.datepicker( "hide" ).datepicker( "destroy" ); - inp = testHelper.init( "#inp", { - beforeShow: function() { + input.datepicker( "open" ); + ok( input.datepicker( "widget" ).is( ":visible" ), "beforeOpen returns nothing" ); + input.datepicker( "close" ).datepicker( "destroy" ); + + input = testHelper.init( "#datepicker", { + beforeOpen: function() { return true; } }); - dp = $( "#ui-datepicker-div" ); - inp.datepicker( "show" ); - equal( dp.css( "display" ), "block", "beforeShow returns true" ); - inp.datepicker( "hide" ); - inp.datepicker( "destroy" ); + input.datepicker( "open" ); + ok( input.datepicker( "widget" ).is( ":visible" ), "beforeOpen returns true" ); + input.datepicker( "close" ).datepicker( "destroy" ); - inp = testHelper.init( "#inp", { - beforeShow: function() { + input = testHelper.init( "#datepicker", { + beforeOpen: function() { return false; } }); - dp = $( "#ui-datepicker-div" ); - inp.datepicker( "show" ); - equal( dp.css( "display" ), "none","beforeShow returns false" ); - inp.datepicker( "destroy" ); + input.datepicker( "open" ); + ok( !input.datepicker( "widget" ).is( ":visible" ), "beforeOpen returns false" ); + input.datepicker( "destroy" ); }); } ); diff --git a/tests/unit/index.html b/tests/unit/index.html index 418cfd34c..d6c214fa5 100644 --- a/tests/unit/index.html +++ b/tests/unit/index.html @@ -39,6 +39,7 @@ <ul> <li><a href="accordion/accordion.html">Accordion</a></li> <li><a href="autocomplete/autocomplete.html">Autocomplete</a></li> + <li><a href="calendar/calendar.html">Calendar</a></li> <li><a href="button/button.html">Button</a></li> <li><a href="datepicker/datepicker.html">Datepicker</a></li> <li><a href="dialog/dialog.html">Dialog</a></li> diff --git a/tests/unit/spinner/options.js b/tests/unit/spinner/options.js index fa57e6096..424932da4 100644 --- a/tests/unit/spinner/options.js +++ b/tests/unit/spinner/options.js @@ -1,8 +1,8 @@ define( [ "jquery", "ui/spinner", - "globalize", - "globalize/ja-JP" + "globalize-old", + "globalize-old/ja-JP" ], function( $ ) { module( "spinner: options" ); diff --git a/tests/visual/compound/datepicker_dialog.html b/tests/visual/compound/datepicker_dialog.html index 52c91a083..374b2aeb5 100644 --- a/tests/visual/compound/datepicker_dialog.html +++ b/tests/visual/compound/datepicker_dialog.html @@ -6,6 +6,9 @@ <link rel="stylesheet" href="../visual.css"> <link rel="stylesheet" href="../../../themes/base/all.css"> <script src="../../../external/jquery/jquery.js"></script> + <script src="../../../external/globalize/globalize.js"></script> + <script src="../../../external/date.js"></script> + <script src="../../../external/localization.js"></script> <script src="../../../ui/core.js"></script> <script src="../../../ui/widget.js"></script> <script src="../../../ui/mouse.js"></script> @@ -13,6 +16,7 @@ <script src="../../../ui/resizable.js"></script> <script src="../../../ui/draggable.js"></script> <script src="../../../ui/button.js"></script> + <script src="../../../ui/calendar.js"></script> <script src="../../../ui/datepicker.js"></script> <script src="../../../ui/dialog.js"></script> <script> diff --git a/tests/visual/compound/dialog_widgets.html b/tests/visual/compound/dialog_widgets.html index 2258231e9..0f6396d9d 100644 --- a/tests/visual/compound/dialog_widgets.html +++ b/tests/visual/compound/dialog_widgets.html @@ -6,6 +6,9 @@ <link rel="stylesheet" href="../visual.css"> <link rel="stylesheet" href="../../../themes/base/all.css"> <script src="../../../external/jquery/jquery.js"></script> + <script src="../../../external/globalize/globalize.js"></script> + <script src="../../../external/date.js"></script> + <script src="../../../external/localization.js"></script> <script src="../../../ui/core.js"></script> <script src="../../../ui/widget.js"></script> <script src="../../../ui/mouse.js"></script> @@ -16,6 +19,7 @@ <script src="../../../ui/accordion.js"></script> <script src="../../../ui/autocomplete.js"></script> <script src="../../../ui/button.js"></script> + <script src="../../../ui/calendar.js"></script> <script src="../../../ui/datepicker.js"></script> <script src="../../../ui/dialog.js"></script> <script src="../../../ui/progressbar.js"></script> diff --git a/tests/visual/dialog/complex-dialogs.html b/tests/visual/dialog/complex-dialogs.html index 9a6209be9..59844c6a9 100644 --- a/tests/visual/dialog/complex-dialogs.html +++ b/tests/visual/dialog/complex-dialogs.html @@ -15,6 +15,10 @@ <script src="../../../ui/dialog.js"></script> <!-- stuff needed to make things complex --> + <script src="../../../external/globalize/globalize.js"></script> + <script src="../../../external/date.js"></script> + <script src="../../../external/localization.js"></script> + <script src="../../../ui/calendar.js"></script> <script src="../../../ui/datepicker.js"></script> <script src="../../../ui/menu.js"></script> <script src="../../../ui/autocomplete.js"></script> diff --git a/themes/base/base.css b/themes/base/base.css index 3ed02661f..aa8e6d157 100644 --- a/themes/base/base.css +++ b/themes/base/base.css @@ -13,6 +13,7 @@ @import url("accordion.css"); @import url("autocomplete.css"); @import url("button.css"); +@import url("calendar.css"); @import url("datepicker.css"); @import url("dialog.css"); @import url("draggable.css"); diff --git a/themes/base/calendar.css b/themes/base/calendar.css new file mode 100644 index 000000000..def9fe4ff --- /dev/null +++ b/themes/base/calendar.css @@ -0,0 +1,158 @@ +/*! + * jQuery UI Calendar @VERSION + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/calendar/#theming + */ +.ui-calendar { + width: 17em; + padding: .2em .2em 0; +} + +/* calendar header */ +.ui-calendar .ui-calendar-header { + position: relative; + padding: .2em 0; +} +.ui-calendar .ui-calendar-prev, +.ui-calendar .ui-calendar-next { + cursor: pointer; + position: absolute; + top: 2px; + width: 36px; + height: 31px; +} +.ui-calendar .ui-calendar-prev:not(.ui-state-hover):not(.ui-state-focus), +.ui-calendar .ui-calendar-next:not(.ui-state-hover):not(.ui-state-focus) { + background: none; + border: none; +} +.ui-calendar .ui-calendar-prev { + left: 2px; +} +.ui-calendar .ui-calendar-next { + right: 2px; +} +.ui-calendar .ui-calendar-prev .ui-icon, +.ui-calendar .ui-calendar-next .ui-icon { + display: block; + position: absolute; + left: 50%; + margin-left: -8px; + top: 50%; + margin-top: -8px; +} +.ui-calendar .ui-calendar-title { + line-height: 1.8em; + text-align: center; +} + +/* calendar grid */ +.ui-calendar table { + width: 100%; + font-size: .9em; + border-collapse: collapse; + margin: 0 0 .4em; +} +.ui-calendar th { + padding: .7em .3em; + text-align: center; + font-weight: bold; + border: 0; +} +.ui-calendar td { + border: 0; + padding: 1px; +} +.ui-calendar td button { + display: block; + padding: .2em; + text-align: right; + cursor: pointer; + width: 100%; +} +.ui-calendar td button::-moz-focus-inner { + padding: 0; + border: 0; +} +.ui-calendar .ui-state-disabled button { + cursor: default; +} + +/* button pane */ +.ui-calendar .ui-calendar-buttonpane { + background-image: none; + margin: .7em 0 0 0; + padding: 0 .2em; + border-left: 0; + border-right: 0; + border-bottom: 0; +} +.ui-calendar .ui-calendar-buttonpane button { + float: right; + margin: .5em .2em .4em; + cursor: pointer; + padding: .2em .6em .3em .6em; + width: auto; + overflow: visible; +} +.ui-calendar .ui-calendar-buttonpane button.ui-calendar-current { + float: left; +} + +/* with multiple calendars */ +.ui-calendar-multi { + width: auto; + display: inline-block; +} +.ui-calendar-multi .ui-calendar-group { + width: 17em; + float: left; +} +.ui-calendar-multi .ui-calendar-group table { + width: 95%; + margin: 0 2.5% .4em; +} +.ui-calendar-multi .ui-calendar-buttonpane { + clear: left; +} +.ui-calendar-row-break { + clear: both; + width: 100%; + font-size: 0; +} + +/* RTL support */ +.ui-calendar-rtl { + direction: rtl; +} +.ui-calendar-rtl .ui-calendar-prev { + right: 2px; + left: auto; +} +.ui-calendar-rtl .ui-calendar-next { + left: 2px; + right: auto; +} +.ui-calendar-rtl .ui-calendar-prev:hover { + right: 1px; + left: auto; +} +.ui-calendar-rtl .ui-calendar-next:hover { + left: 1px; + right: auto; +} +.ui-calendar-rtl .ui-calendar-buttonpane { + clear: right; +} +.ui-calendar-rtl .ui-calendar-buttonpane button { + float: left; +} +.ui-calendar-rtl .ui-calendar-buttonpane button.ui-calendar-current, +.ui-calendar-rtl .ui-calendar-group { + float: right; +} diff --git a/themes/base/datepicker.css b/themes/base/datepicker.css index a77eab7b2..b19f5bda8 100644 --- a/themes/base/datepicker.css +++ b/themes/base/datepicker.css @@ -9,167 +9,7 @@ * http://api.jqueryui.com/datepicker/#theming */ .ui-datepicker { - width: 17em; - padding: .2em .2em 0; display: none; -} -.ui-datepicker .ui-datepicker-header { - position: relative; - padding: .2em 0; -} -.ui-datepicker .ui-datepicker-prev, -.ui-datepicker .ui-datepicker-next { - position: absolute; - top: 2px; - width: 1.8em; - height: 1.8em; -} -.ui-datepicker .ui-datepicker-prev-hover, -.ui-datepicker .ui-datepicker-next-hover { - top: 1px; -} -.ui-datepicker .ui-datepicker-prev { - left: 2px; -} -.ui-datepicker .ui-datepicker-next { - right: 2px; -} -.ui-datepicker .ui-datepicker-prev-hover { - left: 1px; -} -.ui-datepicker .ui-datepicker-next-hover { - right: 1px; -} -.ui-datepicker .ui-datepicker-prev span, -.ui-datepicker .ui-datepicker-next span { - display: block; position: absolute; - left: 50%; - margin-left: -8px; - top: 50%; - margin-top: -8px; -} -.ui-datepicker .ui-datepicker-title { - margin: 0 2.3em; - line-height: 1.8em; - text-align: center; -} -.ui-datepicker .ui-datepicker-title select { - font-size: 1em; - margin: 1px 0; -} -.ui-datepicker select.ui-datepicker-month, -.ui-datepicker select.ui-datepicker-year { - width: 45%; -} -.ui-datepicker table { - width: 100%; - font-size: .9em; - border-collapse: collapse; - margin: 0 0 .4em; -} -.ui-datepicker th { - padding: .7em .3em; - text-align: center; - font-weight: bold; - border: 0; -} -.ui-datepicker td { - border: 0; - padding: 1px; -} -.ui-datepicker td span, -.ui-datepicker td a { - display: block; - padding: .2em; - text-align: right; - text-decoration: none; -} -.ui-datepicker .ui-datepicker-buttonpane { - background-image: none; - margin: .7em 0 0 0; - padding: 0 .2em; - border-left: 0; - border-right: 0; - border-bottom: 0; -} -.ui-datepicker .ui-datepicker-buttonpane button { - float: right; - margin: .5em .2em .4em; - cursor: pointer; - padding: .2em .6em .3em .6em; - width: auto; - overflow: visible; -} -.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { - float: left; } -/* with multiple calendars */ -.ui-datepicker.ui-datepicker-multi { - width: auto; -} -.ui-datepicker-multi .ui-datepicker-group { - float: left; -} -.ui-datepicker-multi .ui-datepicker-group table { - width: 95%; - margin: 0 auto .4em; -} -.ui-datepicker-multi-2 .ui-datepicker-group { - width: 50%; -} -.ui-datepicker-multi-3 .ui-datepicker-group { - width: 33.3%; -} -.ui-datepicker-multi-4 .ui-datepicker-group { - width: 25%; -} -.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, -.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { - border-left-width: 0; -} -.ui-datepicker-multi .ui-datepicker-buttonpane { - clear: left; -} -.ui-datepicker-row-break { - clear: both; - width: 100%; - font-size: 0; -} - -/* RTL support */ -.ui-datepicker-rtl { - direction: rtl; -} -.ui-datepicker-rtl .ui-datepicker-prev { - right: 2px; - left: auto; -} -.ui-datepicker-rtl .ui-datepicker-next { - left: 2px; - right: auto; -} -.ui-datepicker-rtl .ui-datepicker-prev:hover { - right: 1px; - left: auto; -} -.ui-datepicker-rtl .ui-datepicker-next:hover { - left: 1px; - right: auto; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane { - clear: right; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane button { - float: left; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, -.ui-datepicker-rtl .ui-datepicker-group { - float: right; -} -.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, -.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { - border-right-width: 0; - border-left-width: 1px; -} diff --git a/ui/calendar.js b/ui/calendar.js new file mode 100644 index 000000000..a358f69a0 --- /dev/null +++ b/ui/calendar.js @@ -0,0 +1,614 @@ +/*! + * jQuery UI Calendar @VERSION + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Datepicker +//>>group: Widgets +//>>description: Displays a calendar for inline date selection. +//>>docs: http://api.jqueryui.com/calendar/ +//>>demos: http://jqueryui.com/calendar/ + +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + // TODO: Keep button even if its optional? + define( [ + "jquery", + "globalize", + "./core", + "./widget", + "./button", + "date" + ], factory ); + } else { + + // Browser globals + factory( jQuery, Globalize ); + } +}( function( $, Globalize ) { + +return $.widget( "ui.calendar", { + version: "@VERSION", + options: { + buttons: [], + dateFormat: { date: "short" }, + eachDay: $.noop, + max: null, + min: null, + numberOfMonths: 1, + showWeek: false, + value: null, + + // callbacks + select: null + }, + + refreshRelatedOptions: { + eachDay: true, + max: true, + min: true, + showWeek: true, + value: true + }, + + _create: function() { + this.id = this.element.uniqueId().attr( "id" ); + this.labels = Globalize.translate( "datepicker" ); + this.buttonClickContext = this.element[ 0 ]; + + this.date = $.date( this.options.value, this.options.dateFormat ); + this.viewDate = this.date.clone(); + this.viewDate.eachDay = this.options.eachDay; + + this._on( this.element, { + "click .ui-calendar-prev": function( event ) { + event.preventDefault(); + this.date.adjust( "M", -this.options.numberOfMonths ); + this._refresh(); + }, + "click .ui-calendar-next": function( event ) { + event.preventDefault(); + this.date.adjust( "M", this.options.numberOfMonths ); + this._refresh(); + }, + "mousedown .ui-calendar-calendar button": function( event ) { + event.preventDefault(); + + this._setOption( "value", new Date( $( event.currentTarget ).data( "timestamp" ) ) ); + this.refresh(); + this._trigger( "select", event ); + this.grid.focus(); + }, + "mouseenter .ui-calendar-header button": "_hover", + "mouseleave .ui-calendar-header button": "_hover", + "mouseenter .ui-calendar-calendar button": "_hover", + "mouseleave .ui-calendar-calendar button": "_hover", + "keydown .ui-calendar-calendar": "_handleKeydown" + } ); + + this._createCalendar(); + }, + + _hover: function( event ) { + $( event.currentTarget ).toggleClass( "ui-state-hover" ); + }, + + _handleKeydown: function( event ) { + switch ( event.keyCode ) { + case $.ui.keyCode.ENTER: + this.activeDescendant.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: + event.preventDefault(); + return; + } + + if ( this._needsRefresh() ) { + this._refresh(); + this.grid.focus(); + } + + this._setActiveDescendant(); + }, + + _needsRefresh: function() { + if ( this.date.month() !== this.viewDate.month() || this.date.year() !== this.viewDate.year() ) { + + // Check if the needed day is already present in our grid due + // to eachDay option changes (eg. other-months demo) + return !this.grid.find( + this._sanitizeSelector( "#" + this._getDayId( this.date ) ) + ).length; + } + + return false; + }, + + _setActiveDescendant: function() { + var id = this._getDayId( this.date ); + + this.grid + .attr( "aria-activedescendant", id ) + .find( ".ui-state-focus" ) + .removeClass( "ui-state-focus" ); + + this.activeDescendant = this.grid.find( + this._sanitizeSelector( "#" + id ) + " > button" + ).addClass( "ui-state-focus" ); + }, + + _createCalendar: function() { + var classes = "ui-calendar ui-widget ui-widget-content ui-helper-clearfix ui-corner-all", + pickerHtml = ""; + + if ( this.options.numberOfMonths === 1 ) { + pickerHtml = this._buildHeader() + this._buildGrid(); + } else { + pickerHtml = this._buildMultiplePicker(); + classes += " ui-calendar-multi"; + } + + this.element + .addClass( classes ) + .attr( { + role: "region", + "aria-labelledby": this.id + "-title" + } ) + .html( pickerHtml ); + + this._createButtonPane(); + + this.grid = this.element.find( ".ui-calendar-calendar" ); + }, + + _buildMultiplePicker: function() { + var headerClass, + html = "", + currentDate = this.viewDate, + months = this.viewDate.months( this.options.numberOfMonths - 1 ), + i = 0; + + for ( ; i < months.length; i++ ) { + + // TODO: Shouldn't we pass date as a parameter to build* fns instead of setting this.date? + this.viewDate = months[ i ]; + headerClass = "ui-calendar-header ui-widget-header ui-helper-clearfix"; + if ( months[ i ].first ) { + headerClass += " ui-corner-left"; + } else if ( months[ i ].last ) { + headerClass += " ui-corner-right"; + } + + html += "<div class='ui-calendar-group'>" + + "<div class='" + headerClass + "'>"; + if ( months[ i ].first ) { + html += this._buildPreviousLink(); + } else if ( months[ i ].last ) { + html += this._buildNextLink(); + } + + html += this._buildTitlebar() + "</div>" + this._buildGrid() + "</div>"; + } + + html += "<div class='ui-calendar-row-break'></div>"; + + this.viewDate = currentDate; + + return html; + }, + + _buildHeader: function() { + return "<div class='ui-calendar-header ui-widget-header ui-helper-clearfix ui-corner-all'>" + + this._buildPreviousLink() + + this._buildNextLink() + + this._buildTitlebar() + + "</div>"; + }, + + _buildPreviousLink: function() { + var prevText = this._getTranslation( "prevText" ); + + return "<button class='ui-calendar-prev ui-corner-all' title='" + + prevText + "'>" + + "<span class='ui-icon ui-icon-circle-triangle-w'>" + + prevText + + "</span>" + + "</button>"; + }, + + _buildNextLink: function() { + var nextText = this._getTranslation( "nextText" ); + + return "<button class='ui-calendar-next ui-corner-all' title='" + + nextText + "'>" + + "<span class='ui-icon ui-icon-circle-triangle-e'>" + + nextText + + "</span>" + + "</button>"; + }, + + _buildTitlebar: function() { + return "<div role='header' id='" + this.id + "-title'>" + + "<div id='" + this.id + "-month-label' class='ui-calendar-title'>" + + this._buildTitle() + + "</div>" + + "<span class='ui-helper-hidden-accessible'>, " + + this._getTranslation( "datePickerRole" ) + + "</span>" + + "</div>"; + }, + + _buildTitle: function() { + return "<span class='ui-calendar-month'>" + + this.viewDate.monthName() + + "</span> " + + "<span class='ui-calendar-year'>" + + this.viewDate.year() + + "</span>"; + }, + + _buildGrid: function() { + return "<table class='ui-calendar-calendar' role='grid' aria-readonly='true' " + + "aria-labelledby='" + this.id + "-month-label' tabindex='0' " + + "aria-activedescendant='" + this._getDayId( this.date ) + "'>" + + this._buildGridHeading() + + this._buildGridBody() + + "</table>"; + }, + + _buildGridHeading: function() { + var cells = "", + i = 0, + weekDayLength = this.viewDate.weekdays().length; + + if ( this.options.showWeek ) { + cells += "<th class='ui-calendar-week-col'>" + this._getTranslation( "weekHeader" ) + "</th>"; + } + for ( ; i < weekDayLength; i++ ) { + cells += this._buildGridHeaderCell( this.date.weekdays()[ i ] ); + } + + return "<thead role='presentation'>" + + "<tr role='row'>" + cells + "</tr>" + + "</thead>"; + }, + + _buildGridHeaderCell: function( day ) { + return "<th role='columnheader' abbr='" + day.fullname + "' aria-label='" + day.fullname + "'>" + + "<span title='" + day.fullname + "'>" + + day.shortname + + "</span>" + + "</th>"; + }, + + _buildGridBody: function() { + + // this.date.days() needs caching as it has O(n^2) complexity. + var days = this.viewDate.days(), + i = 0, + rows = ""; + + for ( ; i < days.length; i++ ) { + rows += this._buildWeekRow( days[ i ] ); + } + + return "<tbody role='presentation'>" + rows + "</tbody>"; + }, + + _buildWeekRow: function( week ) { + var cells = "", + i = 0; + + if ( this.options.showWeek ) { + cells += "<td class='ui-calendar-week-col'>" + week.number + "</td>"; + } + for ( ; i < week.days.length; i++ ) { + cells += this._buildDayCell( week.days[ i ] ); + } + + return "<tr role='row'>" + cells + "</tr>"; + }, + + _buildDayCell: function( day ) { + var content = "", + attributes = [ + "role='gridcell'", + "aria-selected='" + ( this._isCurrent( day ) ? true : false ) + "'" + ], + selectable = ( day.selectable && this._isValid( new Date( day.timestamp ) ) ); + + if ( day.render ) { + attributes.push( "id='" + this.id + "-" + day.year + "-" + day.month + "-" + day.date + "'" ); + + if ( !selectable ) { + attributes.push( "aria-disabled='true'" ); + attributes.push( "class='ui-state-disabled'" ); + } + + content = this._buildDayElement( day, selectable ); + } + + return "<td " + attributes.join( " " ) + ">" + content + "</td>"; + }, + + _getDayId: function( date ) { + return this.id + "-" + date.year() + "-" + date.month() + "-" + date.day(); + }, + + _buildDayElement: function( day, selectable ) { + var attributes, content, + classes = [ "ui-state-default" ]; + + if ( day === this.date && selectable ) { + classes.push( "ui-state-focus" ); + } + if ( this._isCurrent( day ) ) { + classes.push( "ui-state-active" ); + } + if ( day.today ) { + classes.push( "ui-state-highlight" ); + } + if ( day.extraClasses ) { + classes.push( day.extraClasses.split( " " ) ); + } + + attributes = " class='" + classes.join( " " ) + "'"; + if ( selectable ) { + attributes += " tabindex='-1' data-timestamp='" + day.timestamp + "'"; + } else { + attributes += " disabled='disabled'"; + } + content = "<button" + attributes + ">" + day.date + "</button>"; + + if ( day.today ) { + content += "<span class='ui-helper-hidden-accessible'>, " + this._getTranslation( "currentText" ) + "</span>"; + } + + return content; + }, + + _isCurrent: function( day ) { + return this.options.value && day.timestamp === this.options.value.getTime(); + }, + + _createButtonPane: function() { + this.buttonPane = $( "<div>" ) + .addClass( "ui-calendar-buttonpane ui-widget-content ui-helper-clearfix" ); + + this.buttonSet = $( "<div>" ) + .addClass( "ui-calendar-buttonset" ) + .appendTo( this.buttonPane ); + + this._createButtons(); + }, + + _createButtons: function() { + var that = this, + buttons = this.options.buttons; + + 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; + $( "<button></button>", props ) + .button( buttonOptions ) + .appendTo( that.buttonSet ); + } ); + this.element.addClass( "ui-calendar-buttons" ); + this.buttonPane.appendTo( this.element ); + }, + + _refresh: function() { + this.viewDate.setTime( this.date.date().getTime() ); + this.refresh(); + }, + + // Refreshing the entire calendar 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() { + this.labels = Globalize.translate( "datepicker" ); + + // Determine which day gridcell to focus after refresh + // TODO: Prevent disabled cells from being focused + if ( this.options.numberOfMonths === 1 ) { + 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 ); + this._createButtons(); + } else { + this._refreshMultiplePicker(); + } + }, + + _refreshMultiplePicker: function() { + var i = 0; + + for ( ; i < this.options.numberOfMonths; i++ ) { + this.element.find( ".ui-calendar-title" ).eq( i ).html( this._buildTitle() ); + this.element.find( ".ui-calendar-calendar" ).eq( i ).html( this._buildGrid() ); + this.viewDate.adjust( "M", 1 ); + } + this.viewDate.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.element.find( ".ui-state-focus" ).not( ":first" ).removeClass( "ui-state-focus" ); + }, + + _getTranslation: function( key ) { + return $( "<a>" ).text( this.labels[ key ] ).html(); + }, + + _sanitizeSelector: function( hash ) { + return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : ""; + }, + + _setHiddenPicker: function() { + this.element.attr( { + "aria-hidden": "true", + "aria-expanded": "false" + } ); + }, + + value: function( value ) { + if ( arguments.length ) { + this.valueAsDate( Globalize.parseDate( value, this.options.dateFormat ) ); + } else { + return Globalize.format( this.option( "value" ), this.options.dateFormat ); + } + }, + + valueAsDate: function( value ) { + if ( arguments.length ) { + this.option( "value", value ); + } else { + return this.options.value; + } + }, + + _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" ) + .removeClass( "ui-calendar ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-calendar-multi" ) + .removeAttr( "role aria-labelledby" ) + .removeUniqueId() + .empty(); + }, + + _setOptions: function( options ) { + var that = this, + refresh = false; + + $.each( options, function( key, value ) { + that._setOption( key, value ); + + if ( key in that.refreshRelatedOptions ) { + refresh = true; + } + } ); + + if ( refresh ) { + this._refresh(); + } + }, + + _setOption: function( key, value ) { + if ( key === "value" ) { + if ( this._isValid( value ) ) { + this.date.setTime( value.getTime() ); + this._super( key, value ); + } + return; + } + + if ( key === "max" || key === "min" ) { + if ( $.type( value ) === "date" || value === null ) { + this._super( key, value ); + } + return; + } + + this._super( key, value ); + + if ( key === "buttons" ) { + this._createButtons(); + } + + if ( key === "disabled" ) { + this.element + .toggleClass( "ui-state-disabled", value ) + .attr( "aria-disabled", value ); + } + + if ( key === "eachDay" ) { + this.viewDate.eachDay = value; + } + + if ( key === "dateFormat" ) { + this.date.setFormat( value ); + } + } +} ); + +} ) ); diff --git a/ui/datepicker.js b/ui/datepicker.js index cf63cbb20..dc8154e32 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -9,12 +9,9 @@ //>>label: Datepicker //>>group: Widgets -//>>description: Displays a calendar from an input or inline for selecting dates. +//>>description: Displays a calendar for input-based date selection. //>>docs: http://api.jqueryui.com/datepicker/ //>>demos: http://jqueryui.com/datepicker/ -//>>css.structure: ../themes/base/core.css -//>>css.structure: ../themes/base/datepicker.css -//>>css.theme: ../themes/base/theme.css (function( factory ) { if ( typeof define === "function" && define.amd ) { @@ -22,2071 +19,333 @@ // AMD. Register as an anonymous module. define([ "jquery", - "./core" + "globalize", + "./core", + "./widget", + "./calendar", + "./position" ], factory ); } else { // Browser globals - factory( jQuery ); + factory( jQuery, Globalize ); } -}(function( $ ) { +}(function( $, Globalize ) { -$.extend($.ui, { datepicker: { version: "@VERSION" } }); +var widget = $.widget( "ui.datepicker", { + version: "@VERSION", + options: { + appendTo: null, + position: { + my: "left top", + at: "left bottom" + }, + show: true, + hide: true, -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 - // <div style="z-index: -10;"><div style="z-index: 0;"></div></div> - 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($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")); -} - -$.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; + // callbacks + beforeOpen: null, + close: null, + open: null, + select: null }, - /* 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); - } - }, + calendarOptions: [ "buttons", "dateFormat", "disabled", "eachDay", "max", + "min", "numberOfMonths", "showWeek" ], - /* 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($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))}; - }, + _create: function() { + this.suppressExpandOnFocus = false; + this._createCalendar(); - /* 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).on( "keydown", this._doKeyDown). - on( "keypress", this._doKeyPress).on( "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 ); - } + this._on( this._inputEvents ); + this._on( this.calendar, this._calendarEvents ); + this._on( this.document, this._documentEvents ); }, - /* Make attachments based on settings. */ - _attachments: function(input, inst) { - var showOn, buttonText, buttonImage, - appendText = this._get(inst, "appendText"), - isRTL = this._get(inst, "isRTL"); + _getCreateOptions: function() { + var max = this.element.attr( "max" ), + min = this.element.attr( "min" ), + options = {}; - if (inst.append) { - inst.append.remove(); - } - if (appendText) { - inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>"); - input[isRTL ? "before" : "after"](inst.append); + if ( max !== undefined ) { + options.max = Globalize.parseDate( max, { pattern: "yyyy-MM-dd" } ); } - input.off("focus", this._showDatepicker); - - if (inst.trigger) { - inst.trigger.remove(); + if ( min !== undefined ) { + options.min = Globalize.parseDate( min, { pattern: "yyyy-MM-dd" } ); } - showOn = this._get(inst, "showOn"); - if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field - input.on( "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") ? - $("<img/>").addClass(this._triggerClass). - attr({ src: buttonImage, alt: buttonText, title: buttonText }) : - $("<button type='button'></button>").addClass(this._triggerClass). - html(!buttonImage ? buttonText : $("<img/>").attr( - { src:buttonImage, alt:buttonText, title:buttonText }))); - input[isRTL ? "before" : "after"](inst.trigger); - inst.trigger.on( "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]); - } - 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" ); + return options; }, - /* 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 + _createCalendar: function() { + var that = this; - if (!inst) { - this.uuid += 1; - id = "dp" + this.uuid; - this._dialogInput = $("<input type='text' id='" + id + - "' style='position: absolute; top: -100px; width: 0px;'/>"); - this._dialogInput.on( "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; - } + this.calendar = $( "<div>" ) + .addClass( "ui-front ui-datepicker" ) + .appendTo( this._appendTo() ); - nodeName = target.nodeName.toLowerCase(); - $.removeData(target, "datepicker"); - if (nodeName === "input") { - inst.append.remove(); - inst.trigger.remove(); - $target.removeClass(this.markerClassName). - off("focus", this._showDatepicker). - off("keydown", this._doKeyDown). - off("keypress", this._doKeyPress). - off("keyup", this._doKeyUp); - } else if (nodeName === "div" || nodeName === "span") { - $target.removeClass(this.markerClassName).empty(); - } - - if ( datepicker_instActive === inst ) { - datepicker_instActive = null; - } - }, - - /* 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"); - - if (!$target.hasClass(this.markerClassName)) { - return; - } - - 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 - }, - - /* 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)) { - return; - } + // Initialize calendar widget + this.calendarInstance = this.calendar + .calendar( $.extend( {}, this.options, { + value: this._getParsedValue(), + select: function( event ) { + that.element.val( that.calendarInstance.value() ); + that.close(); + that._focusTrigger(); + that._trigger( "select", event ); + } + }) ) + .calendar( "instance" ); - 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; - }, + this.calendarInstance.buttonClickContext = that.element[ 0 ]; - /* 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; - }, + this._setHiddenPicker(); - /* 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"; - } + this.element.attr({ + "aria-haspopup": true, + "aria-owns": this.calendar.attr( "id" ) + }); }, - /* 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(); + _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.DOWN: + case $.ui.keyCode.UP: + clearTimeout( this.closeTimer ); + this._delay( function() { + this.open( event ); + this.calendarInstance.grid.focus(); + }, 1 ); + break; } - - 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); + }, + keyup: function() { + if ( this.isValid() ) { + this.refresh(); } - if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) { - inst.settings.maxDate = this._formatDate(inst, maxDate); + }, + mousedown: function( event ) { + if ( this.isOpen ) { + this.suppressExpandOnFocus = true; + this.close(); + return; } - if ( "disabled" in settings ) { - if ( settings.disabled ) { - this._disableDatepicker(target); - } else { - this._enableDatepicker(target); - } - } - 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 - 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"); - } - // next month/year on alt +right - break; - case 40: if (event.ctrlKey || event.metaKey) { - $.datepicker._adjustDate(event.target, +7, "D"); - } - 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); - } + this.open( event ); + clearTimeout( this.closeTimer ); + }, + focus: function( event ) { + if ( !this.suppressExpandOnFocus && !this.isOpen ) { + this._delay( function() { + this.open( event ); + }); } - catch (err) { + this._delay( function() { + this.suppressExpandOnFocus = false; + }, 100 ); + }, + blur: function() { + this.suppressExpandOnFocus = false; + } + }, + + _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(); } } - 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); + _documentEvents: { + mousedown: function( event ) { + if ( !this.isOpen ) { + return; } - if ( $.datepicker._shouldFocusInput( inst ) ) { - inst.input.trigger( "focus" ); + if ( !$( event.target ).closest( this.element.add( this.calendar ) ).length ) { + this.close( event ); } - - $.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); + _appendTo: function() { + var element = this.options.appendTo; - var origyearshtml, - numMonths = this._getNumberOfMonths(inst), - cols = numMonths[1], - width = 17, - activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" ); - - if ( activeCell.length > 0 ) { - datepicker_handleMouseover.apply( activeCell.get( 0 ) ); + if ( element ) { + element = element.jquery || element.nodeType ? + $( element ) : + this.document.find( element ).eq( 0 ); } - 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"); + if ( !element || !element[ 0 ] ) { + element = this.element.closest( ".ui-front, dialog" ); } - 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.trigger( "focus" ); + if ( !element.length ) { + element = this.document[ 0 ].body; } - // 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); - } - origyearshtml = inst.yearshtml = null; - }, 0); - } + return element; }, - // #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" ); + _focusTrigger: function() { + this.suppressExpandOnFocus = true; + this.element.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; + refresh: function() { + this.calendarInstance.option( "value", this._getParsedValue() ); }, - /* 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"))) { + open: function( event ) { + if ( this.isOpen ) { 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._inDialog = false; - } - }, - - /* Tidy up after a dialog display. */ - _tidyDialog: function(inst) { - inst.dpDiv.removeClass(this._dialogClass).off(".ui-datepicker-calendar"); - }, - - /* Close date picker if clicked elsewhere. */ - _checkExternalClick: function(event) { - if (!$.datepicker._curInst) { + if ( this._trigger( "beforeOpen", event ) === false ) { return; } - var $target = $(event.target), - inst = $.datepicker._getInst($target[0]); + this.calendarInstance.refresh(); + this.calendar + .attr({ + "aria-hidden": false, + "aria-expanded": true + }) + .show() + .position( this._buildPosition() ) + .hide(); + this._show( this.calendar, this.options.show ); - 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(); - } - }, + // 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; - /* 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; - } - 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(); - } - this._notifyChange(inst); - this._adjustDate(target); + this._trigger( "open", event ); }, - /* Action for selecting a new month/year. */ - _selectMonthYear: function(id, select, period) { - var target = $(id), - inst = this._getInst(target[0]); + close: function( event ) { + this._setHiddenPicker(); + this._hide( this.calendar, this.options.hide ); - inst["selected" + (period === "M" ? "Month" : "Year")] = - inst["draw" + (period === "M" ? "Month" : "Year")] = - parseInt(select.options[select.selectedIndex].value,10); + this.element.attr( "tabindex", 0 ); - this._notifyChange(inst); - this._adjustDate(target); + this.isOpen = false; + this._trigger( "close", event ); }, - /* Action for selecting a day. */ - _selectDay: function(id, month, year, td) { - var inst, - target = $(id); - - if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) { - return; - } - - 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)); + _setHiddenPicker: function() { + this.calendar.attr({ + "aria-hidden": true, + "aria-expanded": false + }); }, - /* Erase the input field and hide the date picker. */ - _clearDate: function(id) { - var target = $(id); - this._selectDate(target, ""); + _buildPosition: function() { + return $.extend( { of: this.element }, this.options.position ); }, - /* 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); + value: function( value ) { + if ( arguments.length ) { + this.valueAsDate( Globalize.parseDate( value, this.options.dateFormat ) ); } else { - this._hideDatepicker(); - this._lastInput = inst.input[0]; - if (typeof(inst.input[0]) !== "object") { - inst.input.trigger( "focus" ); // restore focus - } - 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); }); + return this._getParsedValue() ? this.element.val() : null; } }, - /* 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)); - - time = checkDate.getTime(); - checkDate.setMonth(0); // Compare with Jan 1 - checkDate.setDate(1); - return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; - }, - - /* 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)))), - minSize = (match === "y" ? size : 1), - digits = new RegExp("^\\d{" + minSize + "," + 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(); - } - } 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(); - } - } - } - - if (iValue < value.length){ - extra = value.substr(iValue); - if (!/^\s+/.test(extra)) { - throw "Extra/unparsed characters found in date: " + extra; - } - } - - if (year === -1) { - year = new Date().getFullYear(); - } else if (year < 100) { - year += new Date().getFullYear() - new Date().getFullYear() % 100 + - (year <= shortYearCutoff ? 0 : -100); - } - - if (doy > -1) { - month = 1; - day = doy; - do { - dim = this._getDaysInMonth(year, month - 1); - if (day <= dim) { - break; - } - month++; - day -= dim; - } while (true); - } - - 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 - } - 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 ""; - } - - 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); - } - } + valueAsDate: function( value ) { + if ( arguments.length ) { + if ( this.calendarInstance._isValid( value ) ) { + this.calendarInstance.valueAsDate( value ); + this.element.val( Globalize.format( value, this.options.dateFormat ) ); } + } else { + return this._getParsedValue(); } - return output; }, - /* 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); - } - } - } - return chars; + isValid: function() { + return this.calendarInstance._isValid( this._getParsedValue() ); }, - /* Get a setting value, defaulting if necessary. */ - _get: function(inst, name) { - return inst.settings[name] !== undefined ? - inst.settings[name] : this._defaults[name]; + _destroy: function() { + this.calendarInstance.destroy(); + this.calendar.remove(); + this.element.removeAttr( "aria-haspopup aria-owns" ); }, - /* Parse existing date and initialise date picker. */ - _setDateFromField: function(inst, noDefault) { - if (inst.input.val() === inst.lastVal) { - return; - } - - 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); - } - 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); + widget: function() { + return this.calendar; }, - /* Retrieve the default date shown on opening. */ - _getDefaultDate: function(inst) { - return this._restrictMinMax(inst, - this._determineDate(inst, this._get(inst, "defaultDate"), new Date())); + _getParsedValue: function() { + return Globalize.parseDate( this.element.val(), this.options.dateFormat ); }, - /* 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); + _setOption: function( key, value ) { + this._super( key, value ); - 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); + if ( $.inArray( key, this.calendarOptions ) !== -1 ) { + this.calendarInstance.option( key, value ); } - return this._daylightSavingAdjust(newDate); - }, - /* 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; + if ( key === "appendTo" ) { + this.calendar.appendTo( this._appendTo() ); } - date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0); - return date; - }, - /* 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 ( key === "dateFormat" ) { + this.element.val( this.calendarInstance.value() ); } - }, - - /* 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).on(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 ( key === "disabled" ) { + this.element + .prop( "disabled", value ) + .toggleClass( "ui-state-disabled", value ) + .attr( "aria-disabled", value ); - 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--; - } + if ( value ) { + this.close(); } } - 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) ? - "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" + - " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" : - (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+ prevText +"'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>")); - - 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) ? - "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" + - " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" : - (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+ nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>")); - - 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 ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" + - this._get(inst, "closeText") + "</button>" : ""); - - buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") + - (this._isInRange(inst, gotoDate) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" + - ">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : ""; - - 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 += "<div class='ui-datepicker-group"; - if (numMonths[1] > 1) { - switch (col) { - case 0: calender += " ui-datepicker-group-first"; - cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break; - case numMonths[1]-1: calender += " ui-datepicker-group-last"; - cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break; - default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break; - } - } - calender += "'>"; - } - calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" + - (/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 - "</div><table class='ui-datepicker-calendar'><thead>" + - "<tr>"; - thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : ""); - for (dow = 0; dow < 7; dow++) { // days of the week - day = (dow + firstDay) % 7; - thead += "<th scope='col'" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" + - "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>"; - } - calender += thead + "</tr></thead><tbody>"; - 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 += "<tr>"; - tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" + - this._get(inst, "calculateWeek")(printDate) + "</td>"); - 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 += "<td class='" + - ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends - (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months - ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key - (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ? - // or defaultDate is current printedDate and defaultDate is selectedDate - " " + this._dayOverClass : "") + // highlight selected day - (unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") + // highlight unselectable days - (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates - (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day - (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different) - ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "'") + "'" : "") + // cell title - (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions - (otherMonth && !showOtherMonths ? " " : // display for other months - (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" + - (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") + - (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day - (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months - "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date - printDate.setDate(printDate.getDate() + 1); - printDate = this._daylightSavingAdjust(printDate); - } - calender += tbody + "</tr>"; - } - drawMonth++; - if (drawMonth > 11) { - drawMonth = 0; - drawYear++; - } - calender += "</tbody></table>" + (isMultiMonth ? "</div>" + - ((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : ""); - group += calender; - } - html += group; - } - html += buttonPanel; - inst._keyEvent = false; - return html; - }, - - /* 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 = "<div class='ui-datepicker-title'>", - monthHtml = ""; - - // month selection - if (secondary || !changeMonth) { - monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>"; - } else { - inMinYear = (minDate && minDate.getFullYear() === drawYear); - inMaxYear = (maxDate && maxDate.getFullYear() === drawYear); - monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>"; - for ( month = 0; month < 12; month++) { - if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) { - monthHtml += "<option value='" + month + "'" + - (month === drawMonth ? " selected='selected'" : "") + - ">" + monthNamesShort[month] + "</option>"; - } - } - monthHtml += "</select>"; - } - - if (!showMonthAfterYear) { - html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : ""); - } - - // year selection - if ( !inst.yearshtml ) { - inst.yearshtml = ""; - if (secondary || !changeYear) { - html += "<span class='ui-datepicker-year'>" + drawYear + "</span>"; - } 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 += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>"; - for (; year <= endYear; year++) { - inst.yearshtml += "<option value='" + year + "'" + - (year === drawYear ? " selected='selected'" : "") + - ">" + year + "</option>"; - } - inst.yearshtml += "</select>"; - - html += inst.yearshtml; - inst.yearshtml = null; - } - } - - html += this._get(inst, "yearSuffix"); - if (showMonthAfterYear) { - html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml; - } - html += "</div>"; // Close datepicker_header - return html; - }, - - /* 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))); - - inst.selectedDay = date.getDate(); - inst.drawMonth = inst.selectedMonth = date.getMonth(); - inst.drawYear = inst.selectedYear = date.getFullYear(); - if (period === "M" || period === "Y") { - this._notifyChange(inst); - } - }, - - /* 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]); - } - }, - - /* 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)); - }, - - /* 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); - }, - - /* Find the number of days in a given month. */ - _getDaysInMonth: function(year, month) { - return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate(); - }, - - /* Find the day of the week of the first of a month. */ - _getFirstDayOfMonth: function(year, month) { - return new Date(year, month, 1).getDay(); - }, - - /* 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())); + if ( key === "position" ) { + this.calendar.position( this._buildPosition() ); } - 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")}; - }, - - /* 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; - } - 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)); } }); -/* - * 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.on( "mouseout", selector, 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"); - } - }) - .on( "mouseover", selector, datepicker_handleMouseover ); -} - -function datepicker_handleMouseover() { - if (!$.datepicker._isDisabledDatepicker( datepicker_instActive.inline? datepicker_instActive.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).on( "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"; +$.each( $.ui.datepicker.prototype.calendarOptions, function( index, option ) { + $.ui.datepicker.prototype.options[ option ] = $.ui.calendar.prototype.options[ option ]; +}); -return $.datepicker; +return widget; })); |