Calendar: Add calendar widget

Add calendar widget by copying and renaming datepicker widget files.
Remove datepicker functionality, options and methods from Calendar.
Remove calendar functionality, options and methods from Datepicker.
Adjust tests due to split and changed specification. Remove duplicated
demo files and fix some demos. Simplify calendar generation, use CSS
instead of inline styles. Fix destroy method. Make use of uniqueId
method. Fix focus highlighting when month is changed. Add version
property. Add common unit tests. Fix input keyboard handling.
This commit is contained in:
Felix Nagel 2014-06-03 23:18:51 +02:00 committed by Scott González
parent 35b52a0dfc
commit 510ba08cff
42 changed files with 1721 additions and 2340 deletions

View File

@ -28,6 +28,7 @@ var
"core",
"accordion",
"autocomplete",
"calendar",
"button",
"datepicker",
"dialog",

View File

@ -13,6 +13,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",

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Datepicker - Display button bar</title>
<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>
@ -11,12 +11,12 @@
<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>
<link rel="stylesheet" href="../demos.css">
<script>
$(function() {
$( "#datepicker" ).datepicker({
$( "#calendar" ).calendar({
showButtonPanel: true
});
});
@ -24,7 +24,7 @@
</head>
<body>
<p>Date: <input type="text" id="datepicker"></p>
<div id="calendar"></div>
<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>

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Datepicker - Display inline</title>
<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>
@ -11,21 +11,21 @@
<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>
<link rel="stylesheet" href="../demos.css">
<script>
$(function() {
$( "#datepicker" ).datepicker();
$( "#calendar" ).calendar();
});
</script>
</head>
<body>
Date: <div id="datepicker"></div>
<div id="calendar"></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>
<p>The calendar is a widget for selecting a date using a visual calendar representation.</p>
</div>
</body>
</html>

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Datepicker - Display month &amp; year menus</title>
<title>jQuery UI Calendar - Display month &amp; 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>
@ -11,12 +11,12 @@
<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>
<link rel="stylesheet" href="../demos.css">
<script>
$(function() {
$( "#datepicker" ).datepicker({
$( "#calendar" ).calendar({
changeMonth: true,
changeYear: true
});
@ -25,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>

22
demos/calendar/index.html Normal file
View File

@ -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 &amp; 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>

View File

@ -0,0 +1,41 @@
<!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" ).calendar(),
date = calendar.calendar( "valueAsDate" );
$( "#locale" ).change( function() {
Globalize.locale( $( this ).val() );
calendar.calendar( "valueAsDate", date );
});
});
</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 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>

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Datepicker - Restrict date range</title>
<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>
@ -11,18 +11,18 @@
<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>
<link rel="stylesheet" href="../demos.css">
<script>
$(function() {
$( "#datepicker" ).datepicker({ minDate: -20, maxDate: "+1M +10D" });
$( "#calendar" ).calendar({ minDate: -20, maxDate: "+1M +10D" });
});
</script>
</head>
<body>
<p>Date: <input type="text" id="datepicker"></p>
<div id="calendar"></div>
<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>

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Datepicker - Display multiple months</title>
<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>
@ -11,12 +11,12 @@
<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>
<link rel="stylesheet" href="../demos.css">
<script>
$(function() {
$( "#datepicker" ).datepicker({
$( "#calendar" ).calendar({
numberOfMonths: 3,
showButtonPanel: true
});
@ -25,10 +25,10 @@
</head>
<body>
<p>Date: <input type="text" id="datepicker"></p>
<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 datepicker.</p>
<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>

View File

@ -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>

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Datepicker - Dates in other months</title>
<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>
@ -11,12 +11,12 @@
<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>
<link rel="stylesheet" href="../demos.css">
<script>
$(function() {
$( "#datepicker" ).datepicker({
$( "#calendar" ).calendar({
eachDay: function( day ) {
if ( day.lead ) {
day.render = true;
@ -30,10 +30,10 @@
</head>
<body>
<p>Date: <input type="text" id="datepicker"></p>
<div id="calendar"></div>
<div class="demo-description">
<p>The datepicker can show dates that come from other than the main month
<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>

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Datepicker - Show week of the year</title>
<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>
@ -11,12 +11,12 @@
<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>
<link rel="stylesheet" href="../demos.css">
<script>
$(function() {
$( "#datepicker" ).datepicker({
$( "#calendar" ).calendar({
showWeek: true
});
});
@ -24,10 +24,10 @@
</head>
<body>
<p>Date: <input type="text" id="datepicker"></p>
<div id="calendar"></div>
<div class="demo-description">
<p>The datepicker can show the week of the year. The calculation follows
<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>

View File

@ -1,36 +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="../../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/datepicker.js"></script>
<link rel="stylesheet" href="../demos.css">
<script>
$(function() {
$( "#datepicker" ).datepicker({
select: function( event, ui ) {
var date = Globalize.parseDate( ui.date, { date: "short" } );
$( "#alternate" ).val( Globalize.format( date, { date: "long" } ) );
}
});
});
</script>
</head>
<body>
<p>Date: <input type="text" id="datepicker">&nbsp;<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>

View File

@ -18,6 +18,7 @@
<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">

View File

@ -12,29 +12,21 @@
<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();
$( "#format" ).change(function() {
var format;
switch( $( this ).val() ) {
case "short":
$( "#datepicker" ).datepicker( "option", "dateFormat", {
date: "short"
});
break;
case "long":
$( "#datepicker" ).datepicker( "option", "dateFormat", {
date: "long"
});
break;
case "iso":
$( "#datepicker" ).datepicker( "option", "dateFormat", {
pattern: "yyyy-MM-dd"
});
break;
var value,
datepicker = $( "#datepicker" ).datepicker();
$( "#format" ).change( function() {
value = $( this ).val();
if ( value === "iso" ) {
datepicker.datepicker( "option", "dateFormat", { pattern: "yyyy-MM-dd" } );
} else {
datepicker.datepicker( "option", "dateFormat", { date: value } );
}
});
});

View File

@ -1,49 +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="../../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/datepicker.js"></script>
<link rel="stylesheet" href="../demos.css">
<script>
$(function() {
$( "#from" ).datepicker({
defaultDate: "+1w",
changeMonth: true,
numberOfMonths: 3,
onClose: function( selectedDate ) {
$( "#to" ).datepicker( "option", "minDate", selectedDate );
}
});
$( "#to" ).datepicker({
defaultDate: "+1w",
changeMonth: true,
numberOfMonths: 3,
onClose: function( selectedDate ) {
$( "#from" ).datepicker( "option", "maxDate", selectedDate );
}
});
});
</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>

View File

@ -12,6 +12,7 @@
<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>

View File

@ -10,6 +10,7 @@
<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>

View File

@ -9,19 +9,10 @@
<ul>
<li><a href="default.html">Default functionality</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 &amp; 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="date-formats.html">Format date</a></li>
<li><a href="icon-trigger.html">Icon trigger</a></li>
<li><a href="localization.html">Localize calendar</a></li>
</ul>
</body>

View File

@ -11,6 +11,7 @@
<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>
<link rel="stylesheet" href="../demos.css">

View File

@ -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>

View File

@ -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",

View File

@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Calendar Test Suite</title>
<script src="../../jquery.js"></script>
<link rel="stylesheet" href="../../../external/qunit/qunit.css">
<link rel="stylesheet" href="../qunit-composite.css">
<script src="../../../external/qunit/qunit.js"></script>
<script src="../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>

View File

@ -0,0 +1,46 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI Calendar Test Suite</title>
<script src="../../jquery.js"></script>
<script src="../../../external/jquery-simulate/jquery.simulate.js"></script>
<script src="../../../external/globalize/globalize.js"></script>
<script src="../../../external/localization.js"></script>
<script src="../../../external/date.js"></script>
<link rel="stylesheet" href="../../../external/qunit/qunit.css">
<script src="../../../external/qunit/qunit.js"></script>
<script src="../testsuite.js"></script>
<script>
TestHelpers.loadResources({
css: [ "core", "calendar" ],
js: [
"ui/core.js",
"ui/widget.js",
"ui/button.js",
"ui/calendar.js"
]
});
</script>
<script src="calendar_common.js"></script>
<script src="calendar_core.js"></script>
<script src="calendar_events.js"></script>
<script src="calendar_methods.js"></script>
<script src="calendar_options.js"></script>
<script src="calendar_test_helpers.js"></script>
<script src="../swarminject.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture">
<div id="calendar"></div>
<div id="calendar2"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,14 @@
TestHelpers.commonWidgetTests( "calendar", {
defaults: {
dateFormat: { date: "short" },
disabled: false,
eachDay: $.noop,
numberOfMonths: 1,
showWeek: false,
value: null,
// callbacks
create: null,
select: null
}
});

View File

@ -0,0 +1,365 @@
(function( $ ) {
module( "calendar: core" );
TestHelpers.testJshint( "calendar" );
test( "base structure", function() {
expect( 22 );
var header, title, table, thead, week, child, buttonpane,
element = $( "#calendar" ).calendar(),
dp = element.calendar( "widget" );
function step1() {
ok( !dp.is( ".ui-calendar-rtl" ), "Structure - not right-to-left" );
ok( !dp.is( ".ui-calendar-multi" ), "Structure - not multi-month" );
equal( dp.children().length, 3, "Structure - child count (header, calendar)" );
header = dp.children( ":first" );
ok( header.is( "div.ui-calendar-header" ), "Structure - header division" );
equal( header.children().length, 3, "Structure - header child count" );
ok( header.children( ":first" ).is( ".ui-calendar-prev" ) && header.children( ":first" ).html() !== "", "Structure - prev link" );
ok( header.children( ":eq(1)" ).is( ".ui-calendar-next" ) && header.children( ":eq(1)" ).html() !== "", "Structure - next link" );
title = header.children( ":last" ).children( ":first" );
ok( title.is( "div.ui-calendar-title" ) && title.html() !== "", "Structure - title division" );
equal( title.children().length, 2, "Structure - title child count" );
ok( title.children( ":first" ).is( "span.ui-calendar-month" ) && title.children( ":first" ).text() !== "", "Structure - month text" );
ok( title.children( ":last" ).is( "span.ui-calendar-year" ) && title.children( ":last" ).text() !== "", "Structure - year text" );
table = dp.children( ":eq(1)" );
ok( table.is( "table.ui-calendar-calendar" ), "Structure - month table" );
ok( table.children( ":first" ).is( "thead" ), "Structure - month table thead" );
thead = table.children( ":first" ).children( ":first" );
ok( thead.is( "tr" ), "Structure - month table title row" );
equal( thead.find( "th" ).length, 7, "Structure - month table title cells" );
ok( table.children( ":eq(1)" ).is( "tbody" ), "Structure - month table body" );
ok( table.children( ":eq(1)" ).children( "tr" ).length >= 4, "Structure - month table week count" );
week = table.children( ":eq(1)" ).children( ":first" );
ok( week.is( "tr" ), "Structure - month table week row" );
equal( week.children().length, 7, "Structure - week child count" );
element.calendar( "destroy" );
step2();
}
function step2() {
element = $( "#calendar" ).calendar( { numberOfMonths: 2 } );
dp = element.calendar( "widget" );
ok( dp.is( ".ui-calendar-multi" ), "Structure multi [2] - multi-month" );
equal( dp.children().length, 4, "Structure multi [2] - child count" );
child = dp.children( ":eq(2)" );
ok( child.is( "div.ui-calendar-row-break" ), "Structure multi [2] - row break" );
element.calendar( "destroy" );
}
step1();
});
test( "Localization", function() {
expect( 5 );
var defaultLocale = Globalize.locale(),
element = $( "#calendar" ),
date = new Date( 2014, 0, 1 ),
initCalendar = function() {
element
.calendar()
.calendar( "valueAsDate", date );
},
testLocalization = function( message ) {
equal(
element.find( ".ui-calendar-month" ).text(),
"Januar", message + "titlebar year"
);
equal(
element.find( "thead th:first" ).text(),
"Mo.", message + "teader first day"
);
equal(
element.find( "thead th:last" ).text(),
"So.", message + "header last day"
);
equal(
element.find( ".ui-calendar-prev" ).text(),
"<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 );
});
asyncTest( "keyboard handling", function() {
expect( 10 );
var element = $( "#calendar" );
function step1() {
element.calendar({ value: new Date( 2014, 1 - 1, 1 ) });
TestHelpers.calendar
.focusGrid( element )
.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2013, 12 - 1, 31 ),
"Keystroke left to switch to previous day"
);
element.calendar( "destroy" );
step2();
}, 50 );
}
function step2() {
element.calendar({ value: new Date( 2014, 1 - 1, 1 ) });
TestHelpers.calendar.focusGrid( element )
.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } )
.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2014, 1 - 1, 2 ),
"Keystroke right to switch to next day"
);
step3();
}
function step3() {
element.calendar({ value: new Date( 2014, 1 - 1, 1 ) });
TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.UP } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2013, 12 - 1, 25 ),
"Keystroke up to move to the previous week"
);
element.calendar( "destroy" );
step4();
}, 50 );
}
function step4() {
element.calendar({ value: new Date( 2014, 1 - 1, 1 ) });
TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2014, 1 - 1, 8 ),
"Keystroke down to move to the next week"
);
element.calendar( "destroy" );
step5();
}, 50 );
}
function step5() {
element.calendar({ value: new Date( 2014, 1 - 1, 1 ) });
TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2013, 12 - 1, 1 ),
"Keystroke Page Up moves date to previous month"
);
element.calendar( "destroy" );
step6();
}, 50 );
}
function step6() {
element.calendar({ value: new Date( 2014, 1 - 1, 1 ) });
TestHelpers.calendar.focusGrid( element )
.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP, altKey: true } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2013, 1 - 1, 1 ),
"Keystroke Page Up + ALT moves date to previous year"
);
element.calendar( "destroy" );
step7();
}, 50 );
}
function step7() {
element.calendar({ value: new Date( 2014, 1 - 1, 1 ) });
TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2014, 2 - 1, 1 ),
"Keystroke Page Down moves date to next month"
);
element.calendar( "destroy" );
step8();
}, 50 );
}
function step8() {
element.calendar({ value: new Date( 2014, 1 - 1, 1 ) });
TestHelpers.calendar.focusGrid( element )
.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2015, 1 - 1, 1 ),
"Keystroke Page Down + 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 ) });
TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2014, 2 - 1, 28 ),
"Keystroke Page Up and short months"
);
element.calendar( "destroy" );
step10();
}, 50 );
}
function step10() {
element.calendar({ value: new Date( 2016, 1 - 1, 30 ) });
TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2016, 2 - 1, 29 ),
"Keystroke Page Down and leap years"
);
element.calendar( "destroy" );
start();
}, 50 );
}
step1();
});
asyncTest( "mouse", function() {
expect( 6 );
var element = $( "#calendar" ).calendar(),
date = new Date();
function step1() {
$( "tbody a:contains(10)", element ).simulate( "mousedown" );
date.setDate( 10 );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
date,
"Mouse click"
);
element.calendar( "option", "value", new Date( 2008, 2 - 1, 4) );
$( ".ui-calendar-calendar tbody a:contains(12)", element ).simulate( "mousedown" );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2008, 2 - 1, 12 ),
"Mouse click - preset"
);
// Previous/next
element.calendar( "option", "value", new Date( 2008, 2 - 1, 4) );
$( ".ui-calendar-prev", element ).simulate( "click" );
$( ".ui-calendar-calendar tbody a:contains(16)", element ).simulate( "mousedown" );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2008, 1 - 1, 16 ),
"Mouse click - previous"
);
element.calendar( "option", "value", new Date( 2008, 2 - 1, 4) );
$( ".ui-calendar-next", element ).simulate( "click" );
$( ".ui-calendar-calendar tbody a:contains(18)", element ).simulate( "mousedown" );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2008, 3 - 1, 18 ),
"Mouse click - next"
);
step2();
}
// Previous/next with minimum/maximum
function step2() {
element.calendar( "destroy" );
element.calendar({
value: new Date( 2008, 3 - 1, 4),
min: new Date( 2008, 2 - 1, 2 ),
max: new Date( 2008, 2 - 1, 26 )
});
$( ".ui-calendar-prev", element ).simulate( "click" );
$( "tbody a:contains(16)", element ).simulate( "mousedown" );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2008, 2 - 1, 16 ),
"Mouse click - previous + min/max"
);
step3();
}
function step3() {
element.calendar( "destroy" );
element.calendar({
value: new Date( 2008, 1 - 1, 4),
min: new Date( 2008, 2 - 1, 2 ),
max: new Date( 2008, 2 - 1, 26 )
});
$( ".ui-calendar-next", element ).simulate( "click" );
$( "tbody a:contains(18)", element ).simulate( "mousedown" );
TestHelpers.calendar.equalsDate(
element.calendar( "valueAsDate" ),
new Date( 2008, 2 - 1, 18 ),
"Mouse click - next + min/max"
);
start();
}
step1();
});
})( jQuery );

View File

View File

@ -0,0 +1,71 @@
(function( $ ) {
module( "calendar: methods" );
test( "destroy", function() {
expect( 1 );
domEqual( "#calendar", function() {
$( "#calendar" ).calendar().calendar( "destroy" );
});
});
test( "enable / disable", function() {
expect( 6 );
var element = $( "#calendar" ).calendar();
ok( !element.calendar( "option", "disabled" ), "initially enabled" );
ok( !element.hasClass( "ui-calendar-disabled" ), "does not have disabled class name" );
element.calendar( "disable" );
ok( element.calendar( "option", "disabled" ), "disabled option is set" );
ok( element.hasClass( "ui-calendar-disabled" ), "calendar has disabled class name" );
element.calendar( "enable" );
ok( !element.calendar( "option", "disabled" ), "enabled after enable() call" );
ok( !element.hasClass( "ui-calendar-disabled" ), "no longer has disabled class name" );
});
test( "widget", function() {
expect( 1 );
var element = $( "#calendar" ).calendar(),
widget = element.calendar( "widget" );
strictEqual( widget[ 0 ], element[ 0 ] );
});
test( "value", function() {
expect( 3 );
var element = $( "#calendar" ).calendar();
element.calendar( "value", "1/1/14" );
ok( element.find( "a[data-timestamp]:first" ).hasClass( "ui-state-active" ), "first day marked as selected" );
equal( element.calendar( "value" ), "1/1/14", "getter" );
element.calendar( "value", "abc" );
equal( element.calendar( "value" ), "1/1/14", "Setting invalid values should be ignored." );
});
test( "valueAsDate", function() {
expect( 4 );
var element = $( "#calendar" ).calendar(),
date1 = new Date( 2008, 6 - 1, 4 ),
date2 = new Date();
element.calendar( "valueAsDate", new Date( 2014, 0, 1 ) );
ok( element.find( "a[data-timestamp]:first" ).hasClass( "ui-state-active" ), "First day marked as selected" );
TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2014, 0, 1 ), "Getter" );
element.calendar( "destroy" );
element.calendar();
TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), date2, "Set date - default" );
element.calendar( "valueAsDate", date1 );
TestHelpers.calendar.equalsDate(element.calendar( "valueAsDate" ), date1, "Set date - 2008-06-04" );
});
})( jQuery );

View File

@ -0,0 +1,175 @@
(function( $ ) {
module( "calendar: options" );
test( "dateFormat", function() {
expect( 2 );
var element = $( "#calendar" ).calendar({
value: "1/1/14"
}),
firstDayLink = element.calendar( "widget" ).find( "td[id]:first a" );
firstDayLink.trigger( "mousedown" );
equal( element.calendar( "value" ), "1/1/14", "default formatting" );
element.calendar( "option", "dateFormat", { date: "full" } );
equal( element.calendar( "value" ), "Wednesday, January 1, 2014", "updated formatting" );
});
test( "eachDay", function() {
expect( 5 );
var timestamp,
input = $( "#calendar" ).calendar(),
picker = input.calendar( "widget" ),
firstCell = picker.find( "td[id]:first" );
equal( firstCell.find( "a" ).length, 1, "days are selectable by default" );
timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 );
equal( new Date( timestamp ).getDate(), 1, "first available day is the 1st by default" );
// Do not render the 1st of the month
input.calendar( "option", "eachDay", function( day ) {
if ( day.date === 1 ) {
day.render = false;
}
});
firstCell = picker.find( "td[id]:first" );
timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 );
equal( new Date( timestamp ).getDate(), 2, "first available day is the 2nd" );
// Display the 1st of the month but make it not selectable.
input.calendar( "option", "eachDay", function( day ) {
if ( day.date === 1 ) {
day.selectable = false;
}
});
firstCell = picker.find( "td[id]:first" );
equal( firstCell.find( "a" ).length, 0, "the 1st is not selectable" );
input.calendar( "option", "eachDay", function( day ) {
if ( day.date === 1 ) {
day.extraClasses = "ui-custom";
}
});
ok( picker.find( "td[id]:first a" ).hasClass( "ui-custom" ), "extraClasses applied" );
input.calendar( "destroy" );
});
test( "showWeek", function() {
expect( 7 );
var input = $( "#calendar" ).calendar(),
container = input.calendar( "widget" );
equal( container.find( "thead th" ).length, 7, "just 7 days, no column cell" );
equal( container.find( ".ui-calendar-week-col" ).length, 0,
"no week column cells present" );
input.calendar( "destroy" );
input = $( "#calendar" ).calendar({ showWeek: true });
container = input.calendar( "widget" );
equal( container.find( "thead th" ).length, 8, "7 days + a column cell" );
ok( container.find( "thead th:first" ).is( ".ui-calendar-week-col" ),
"first cell should have ui-datepicker-week-col class name" );
equal( container.find( ".ui-calendar-week-col" ).length,
container.find( "tr" ).length, "one week cell for each week" );
input.calendar( "destroy" );
input = $( "#calendar" ).calendar();
container = input.calendar( "widget" );
equal( container.find( "thead th" ).length, 7, "no week column" );
input.calendar( "option", "showWeek", true );
equal( container.find( "thead th" ).length, 8, "supports changing option after init" );
});
test( "min / max", function() {
expect( 0 );
});
/*
// TODO: Move this to $.date, Globalize or calendar widget
test( "daylightSaving", function() {
expect( 25 );
var inp = TestHelpers.calendar.init( "#inp" ),
dp = $( "#ui-datepicker-div" );
ok(true, "Daylight saving - " + new Date());
// Australia, Sydney - AM change, southern hemisphere
inp.val( "04/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(6) a", dp).simulate( "click" );
equal(inp.val(), "04/05/2008", "Daylight saving - Australia 04/05/2008" );
inp.val( "04/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(7) a", dp).simulate( "click" );
equal(inp.val(), "04/06/2008", "Daylight saving - Australia 04/06/2008" );
inp.val( "04/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(8) a", dp).simulate( "click" );
equal(inp.val(), "04/07/2008", "Daylight saving - Australia 04/07/2008" );
inp.val( "10/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(6) a", dp).simulate( "click" );
equal(inp.val(), "10/04/2008", "Daylight saving - Australia 10/04/2008" );
inp.val( "10/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(7) a", dp).simulate( "click" );
equal(inp.val(), "10/05/2008", "Daylight saving - Australia 10/05/2008" );
inp.val( "10/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(8) a", dp).simulate( "click" );
equal(inp.val(), "10/06/2008", "Daylight saving - Australia 10/06/2008" );
// Brasil, Brasilia - midnight change, southern hemisphere
inp.val( "02/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(20) a", dp).simulate( "click" );
equal(inp.val(), "02/16/2008", "Daylight saving - Brasil 02/16/2008" );
inp.val( "02/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(21) a", dp).simulate( "click" );
equal(inp.val(), "02/17/2008", "Daylight saving - Brasil 02/17/2008" );
inp.val( "02/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(22) a", dp).simulate( "click" );
equal(inp.val(), "02/18/2008", "Daylight saving - Brasil 02/18/2008" );
inp.val( "10/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(13) a", dp).simulate( "click" );
equal(inp.val(), "10/11/2008", "Daylight saving - Brasil 10/11/2008" );
inp.val( "10/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(14) a", dp).simulate( "click" );
equal(inp.val(), "10/12/2008", "Daylight saving - Brasil 10/12/2008" );
inp.val( "10/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(15) a", dp).simulate( "click" );
equal(inp.val(), "10/13/2008", "Daylight saving - Brasil 10/13/2008" );
// Lebanon, Beirut - midnight change, northern hemisphere
inp.val( "03/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(34) a", dp).simulate( "click" );
equal(inp.val(), "03/29/2008", "Daylight saving - Lebanon 03/29/2008" );
inp.val( "03/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(35) a", dp).simulate( "click" );
equal(inp.val(), "03/30/2008", "Daylight saving - Lebanon 03/30/2008" );
inp.val( "03/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(36) a", dp).simulate( "click" );
equal(inp.val(), "03/31/2008", "Daylight saving - Lebanon 03/31/2008" );
inp.val( "10/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(27) a", dp).simulate( "click" );
equal(inp.val(), "10/25/2008", "Daylight saving - Lebanon 10/25/2008" );
inp.val( "10/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(28) a", dp).simulate( "click" );
equal(inp.val(), "10/26/2008", "Daylight saving - Lebanon 10/26/2008" );
inp.val( "10/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(29) a", dp).simulate( "click" );
equal(inp.val(), "10/27/2008", "Daylight saving - Lebanon 10/27/2008" );
// US, Eastern - AM change, northern hemisphere
inp.val( "03/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(13) a", dp).simulate( "click" );
equal(inp.val(), "03/08/2008", "Daylight saving - US 03/08/2008" );
inp.val( "03/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(14) a", dp).simulate( "click" );
equal(inp.val(), "03/09/2008", "Daylight saving - US 03/09/2008" );
inp.val( "03/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(15) a", dp).simulate( "click" );
equal(inp.val(), "03/10/2008", "Daylight saving - US 03/10/2008" );
inp.val( "11/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(6) a", dp).simulate( "click" );
equal(inp.val(), "11/01/2008", "Daylight saving - US 11/01/2008" );
inp.val( "11/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(7) a", dp).simulate( "click" );
equal(inp.val(), "11/02/2008", "Daylight saving - US 11/02/2008" );
inp.val( "11/01/2008" ).calendar( "show" );
$( ".ui-calendar-calendar td:eq(8) a", dp).simulate( "click" );
equal(inp.val(), "11/03/2008", "Daylight saving - US 11/03/2008" );
});
*/
})(jQuery);

View File

@ -0,0 +1,22 @@
TestHelpers.calendar = {
addMonths: function( date, offset ) {
var maxDay = 32 - new Date( date.getFullYear(), date.getMonth() + offset, 32 ).getDate();
date.setDate( Math.min( date.getDate(), maxDay ) );
date.setMonth( date.getMonth() + offset );
return date;
},
equalsDate: function( d1, d2, message ) {
if ( !d1 || !d2 ) {
ok( false, message + " - missing date" );
return;
}
d1 = new Date( d1.getFullYear(), d1.getMonth(), d1.getDate() );
d2 = new Date( d2.getFullYear(), d2.getMonth(), d2.getDate() );
equal( d1.toString(), d2.toString(), message );
},
focusGrid: function( element ) {
element.find( "table:tabbable" ).simulate( "focus" );
return $( ":focus" );
}
};

View File

@ -14,11 +14,12 @@
<script src="../testsuite.js"></script>
<script>
TestHelpers.loadResources({
css: [ "core", "datepicker" ],
css: [ "core", "calendar", "datepicker" ],
js: [
"ui/core.js",
"ui/widget.js",
"ui/button.js",
"ui/calendar.js",
"ui/position.js",
"ui/datepicker.js"
]
@ -39,10 +40,8 @@
<div id="qunit"></div>
<div id="qunit-fixture">
<input type="text" id="datepicker">
<input type="text" id="datepicker2">
<div id="inline"></div>
</div>
</body>

View File

@ -1,7 +1,23 @@
/*
TestHelpers.commonWidgetTests( "datepicker", {
defaults: {
disabled: false
appendTo: null,
dateFormat: { date: "short" },
disabled: false,
eachDay: $.noop,
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
}
});
*/

View File

@ -12,213 +12,35 @@ test( "input's value determines starting date", function() {
input.datepicker( "open" );
equal( picker.find( ".ui-datepicker-month" ).html(), "January", "correct month displayed" );
equal( picker.find( ".ui-datepicker-year" ).html(), "2014", "correct year displayed" );
equal( picker.find( ".ui-state-focus" ).html(), "1", "correct day highlighted" );
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" );
input.val( "" ).datepicker( "destroy" );
});
asyncTest( "baseStructure", function() {
expect( 42 );
var header, title, table, thead, week, panel, inl, child,
inp = TestHelpers.datepicker.initNewInput(),
dp = inp.datepicker( "widget" ).find( ".ui-datepicker" );
asyncTest( "base structure", function() {
expect( 5 );
function step1() {
inp.focus();
setTimeout(function() {
ok( dp.is( ":visible" ), "Structure - datepicker visible" );
ok( !dp.is( ".ui-datepicker-rtl" ), "Structure - not right-to-left" );
ok( !dp.is( ".ui-datepicker-multi" ), "Structure - not multi-month" );
equal( dp.children().length, 3, "Structure - child count (header, calendar, buttonpane)" );
var input = TestHelpers.datepicker.initNewInput(),
widget = input.datepicker( "widget" );
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( ".ui-datepicker-prev" ) && header.children( ":first" ).html() !== "", "Structure - prev link" );
ok( header.children( ":eq(1)" ).is( ".ui-datepicker-next" ) && header.children( ":eq(1)" ).html() !== "", "Structure - next link" );
input.focus();
title = header.children( ":last" ).children( ":first" );
ok( title.is( "div.ui-datepicker-title" ) && title.html() !== "", "Structure - title division" );
equal( title.children().length, 2, "Structure - title child count" );
ok( title.children( ":first" ).is( "span.ui-datepicker-month" ) && title.children( ":first" ).text() !== "", "Structure - month text" );
ok( title.children( ":last" ).is( "span.ui-datepicker-year" ) && title.children( ":last" ).text() !== "", "Structure - year text" );
setTimeout(function() {
ok( widget.is( ":visible" ), "Datepicker visible" );
equal( widget.children().length, 3, "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" );
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" );
// TODO: Preserve these class names or let the user use :first-child and :last-child?
// ok( week.children( ":first" ).is( "td.ui-datepicker-week-end" ), "Structure - month table first day cell" );
// ok( week.children( ":last" ).is( "td.ui-datepicker-week-end" ), "Structure - month table second day cell" );
inp.datepicker( "close" ).datepicker( "destroy" );
step2();
}, 50 );
}
function step2() {
// Editable month/year and button panel
inp = TestHelpers.datepicker.initNewInput({
changeMonth: true,
changeYear: true,
showButtonPanel: true
});
dp = inp.datepicker( "widget" ).find( ".ui-datepicker" );
inp.focus();
setTimeout(function() {
title = dp.find( "div.ui-datepicker-title" );
// TODO: Re-add tests when changeMonth and changeYear are re-implemented
//ok( title.children( ":first" ).is( "select.ui-datepicker-month" ), "Structure - month selector" );
//ok( title.children( ":last" ).is( "select.ui-datepicker-year" ), "Structure - year selector" );
panel = dp.children( ":last" );
ok( panel.is( "div.ui-datepicker-buttonpane" ), "Structure - button panel division" );
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( "close" ).datepicker( "destroy" );
step3();
});
}
function step3() {
// Multi-month 2
inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: 2 } );
dp = inp.datepicker( "widget" ).find( ".ui-datepicker" );
inp.focus();
setTimeout(function() {
ok( dp.is( ".ui-datepicker-multi" ), "Structure multi [2] - multi-month" );
equal( dp.children().length, 4, "Structure multi [2] - child count" );
child = dp.children( ":first" );
// TODO: Implement ui-datepicker-group-first class name
// ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure multi [2] - first month division" );
child = dp.children( ":eq(1)" );
// TODO: Implement ui-datepicker-group-last class name
// ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure 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( "close" ).datepicker( "destroy" );
step4();
});
}
function step4() {
// Multi-month 3
inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: 3 } );
dp = inp.datepicker( "widget" ).find( ".ui-datepicker" );
inp.focus();
setTimeout(function() {
ok( dp.is( ".ui-datepicker-multi-3" ), "Structure multi [3] - multi-3" );
ok( !dp.is( ".ui-datepicker-multi-2" ), "Structure multi [3] - Trac #6704" );
inp.datepicker( "close" ).datepicker( "destroy" );
step5();
});
}
function step5() {
// Multi-month [2, 2]
inp = TestHelpers.datepicker.initNewInput({ numberOfMonths: [ 2, 2 ] } );
dp = inp.datepicker( "widget" ).find( ".ui-datepicker" );
inp.focus();
setTimeout(function() {
/*
TODO: Re-add after array form of the numberOfMonths option is implemented.
ok( dp.is( ".ui-datepicker-multi" ), "Structure multi - multi-month" );
equal( dp.children().length, 6, "Structure multi [2,2] - child count" );
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" );
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" );
child = dp.children( ":eq(2)" );
ok( child.is( "div.ui-datepicker-row-break" ), "Structure multi [2,2] - row break" );
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( "close" ).datepicker( "destroy" );
step6();
});
}
function step6() {
// Inline
inl = TestHelpers.datepicker.init( "#inline" );
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, 3, "Structure inline - child count (header, calendar, buttonpane)" );
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" );
step7();
}
function step7() {
// Inline multi-month
inl = TestHelpers.datepicker.init( "#inline", { numberOfMonths: 2 } );
dp = inl.datepicker( "widget" ).find( ".ui-datepicker" );
ok( dp.is( ".ui-datepicker-inline" ) && dp.is( ".ui-datepicker-multi" ), "Structure inline multi - main div" );
equal( dp.children().length, 4, "Structure inline multi - child count" );
child = dp.children( ":first" );
// TODO: Implement ui-datepicker-group-first class name
// ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-first" ), "Structure inline multi - first month division" );
child = dp.children( ":eq(1)" );
// TODO: Implement ui-datepicker-group-last class name
// ok( child.is( "div.ui-datepicker-group" ) && child.is( "div.ui-datepicker-group-last" ), "Structure inline multi - second month division" );
child = dp.children( ":eq(2)" );
ok( child.is( "div.ui-datepicker-row-break" ), "Structure inline multi - row break" );
inl.datepicker( "destroy" );
input.datepicker( "close" );
start();
}
step1();
}, 50 );
});
asyncTest( "Keyboard handling: input", function() {
expect( 9 );
expect( 10 );
var picker, instance,
input = $( "#datepicker" ).datepicker();
@ -254,6 +76,15 @@ asyncTest( "Keyboard handling: input", function() {
TestHelpers.datepicker.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 } );
TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ),
"Keystroke enter - preset" );
input
.val( "" )
.datepicker( "open" );
@ -290,174 +121,13 @@ asyncTest( "Keyboard handling: input", function() {
step1();
});
asyncTest( "keyboard handling: calendar", function() {
expect( 7 );
var input = $( "#datepicker" );
function step1() {
input.val( "1/1/14" );
TestHelpers.datepicker.init( input );
input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.datepicker.equalsDate(
input.datepicker( "valueAsDate" ),
new Date( 2013, 12 - 1, 31 ),
"Keystroke left to switch to previous day"
);
input.datepicker( "destroy" );
step2();
}, 50 );
}, 100 );
}
function step2() {
input.val( "1/1/14" );
TestHelpers.datepicker.init( input );
input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
setTimeout(function() {
$( ":focus" )
.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } )
.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.datepicker.equalsDate(
input.datepicker( "valueAsDate" ),
new Date( 2014, 1 - 1, 2 ),
"Keystroke right to switch to next day"
);
input.datepicker( "destroy" );
step3();
}, 100 );
}
function step3() {
input.val( "1/1/14" );
TestHelpers.datepicker.init( input );
input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.UP } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.datepicker.equalsDate(
input.datepicker( "valueAsDate" ),
new Date( 2013, 12 - 1, 25 ),
"Keystroke up to move to the previous week"
);
input.datepicker( "destroy" );
step4();
}, 50 );
}, 100 );
}
function step4() {
input.val( "1/1/14" );
TestHelpers.datepicker.init( input );
input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.datepicker.equalsDate(
input.datepicker( "valueAsDate" ),
new Date( 2014, 1 - 1, 8 ),
"Keystroke down to move to the next week"
);
input.datepicker( "destroy" );
step5();
}, 50 );
}, 100 );
}
function step5() {
input.val( "1/1/14" );
TestHelpers.datepicker.init( input );
input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.datepicker.equalsDate(
input.datepicker( "valueAsDate" ),
new Date( 2014, 2 - 1, 1 ),
"Keystroke Page Down moves date to next month"
);
input.datepicker( "destroy" );
step6();
}, 50 );
}, 100 );
}
function step6() {
input.val( "1/1/14" );
TestHelpers.datepicker.init( input );
input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.datepicker.equalsDate(
input.datepicker( "valueAsDate" ),
new Date( 2015, 1 - 1, 1 ),
"Keystroke Page Down + Ctrl moves date to next year"
);
input.datepicker( "destroy" );
step7();
}, 50 );
}, 100 );
}
// Check for moving to short months
function step7() {
input.val( "3/31/14" );
TestHelpers.datepicker.init( input );
input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } );
setTimeout(function() {
$( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.datepicker.equalsDate(
input.datepicker( "valueAsDate" ),
new Date( 2014, 2 - 1, 28 ),
"Keystroke Page Up and short months"
);
input.datepicker( "destroy" );
start();
}, 50 );
}, 100 );
}
step1();
// TODO: implement
test( "ARIA", function() {
expect( 0 );
});
/*
// TODO: Re-add tests if we implement a stepMonths option
input.datepicker( "option", { stepMonths: 2, gotoCurrent: false } )
.datepicker( "close" ).val( "02/04/2008" ).datepicker( "open" )
.late( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } )
.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2007, 12 - 1, 4 ),
"Keystroke pgup step 2" );
input.val( "02/04/2008" ).datepicker( "open" )
.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } )
.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } );
TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 4 - 1, 4 ),
"Keystroke pgdn step 2" );
*/
asyncTest( "mouse", function() {
expect( 3 );
expect( 4 );
var input = TestHelpers.datepicker.init( $( "#datepicker" ).val( "" ) ),
picker = input.datepicker( "widget" );
@ -465,23 +135,32 @@ asyncTest( "mouse", function() {
input.datepicker( "open" );
setTimeout(function() {
input.val( "4/4/08" ).datepicker( "refresh" ).datepicker( "open" );
$( ".ui-calendar-calendar tbody a:contains(12)", picker ).simulate( "mousedown", {} );
TestHelpers.datepicker.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( "2/4/08" ).datepicker( "refresh" ).datepicker( "open" );
input.val( "4/4/08" ).datepicker( "refresh" ).datepicker( "open" );
input.simulate( "click" );
TestHelpers.datepicker.equalsDate(
input.datepicker( "valueAsDate" ),
new Date( 2008, 2 - 1, 4 ),
new Date( 2008, 4 - 1, 4 ),
"Mouse click - close + preset"
);
input.val( "2/4/08" ).datepicker( "refresh" ).datepicker( "open" );
input.val( "4/4/08" ).datepicker( "refresh" ).datepicker( "open" );
picker.find( "a.ui-calendar-prev" ).simulate( "click" );
input.simulate( "click" );
TestHelpers.datepicker.equalsDate(
input.datepicker( "valueAsDate" ),
new Date( 2008, 2 - 1, 4 ),
new Date( 2008, 4 - 1, 4 ),
"Mouse click - abandoned"
);
@ -490,3 +169,4 @@ asyncTest( "mouse", function() {
});
})( jQuery );

View File

@ -3,66 +3,43 @@
module( "datepicker: methods" );
test( "destroy", function() {
expect( 10 );
var input = $( "#datepicker" ).datepicker(),
inline = $( "#inline" ).datepicker();
expect( 3 );
ok( input.datepicker( "instance" ), "instance created" );
ok( input.attr( "aria-owns" ), "aria-owns attribute added" );
ok( input.attr( "aria-haspopup" ), "aria-haspopup attribute added" );
input.datepicker( "destroy" );
ok( !input.datepicker( "instance" ), "instance removed" );
ok( !input.attr( "aria-owns" ), "aria-owns attribute removed" );
ok( !input.attr( "aria-haspopup" ), "aria-haspopup attribute removed" );
ok( inline.datepicker( "instance" ), "instance created" );
ok( inline.children().length > 0, "inline datepicker has children" );
inline.datepicker( "destroy" );
ok( !inline.datepicker( "instance" ), "instance removed" );
ok( inline.children().length === 0, "inline picker no longer has children" );
var input = $( "#datepicker" );
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( 6 );
var inl,
inp = TestHelpers.datepicker.init( "#datepicker" ),
dp = inp.datepicker( "widget" );
ok( !inp.datepicker( "option", "disabled" ), "initially enabled" );
ok( !dp.hasClass( "ui-datepicker-disabled" ), "does not have disabled class name" );
var input = TestHelpers.datepicker.init( "#datepicker" ),
widget = input.datepicker( "widget" );
inp.datepicker( "disable" );
ok( inp.datepicker( "option", "disabled" ), "disabled option is set" );
ok( dp.hasClass( "ui-datepicker-disabled" ), "datepicker has disabled class name" );
ok( !input.datepicker( "option", "disabled" ), "initially enabled" );
ok( !widget.hasClass( "ui-datepicker-disabled" ), "does not have disabled class name" );
inp.datepicker( "enable" );
ok( !inp.datepicker( "option", "disabled" ), "enabled after enable() call" );
ok( !dp.hasClass( "ui-datepicker-disabled" ), "no longer has disabled class name" );
input.datepicker( "disable" );
ok( input.datepicker( "option", "disabled" ), "disabled option is set" );
ok( widget.hasClass( "ui-datepicker-disabled" ), "datepicker has disabled class name" );
// Inline
inl = TestHelpers.datepicker.init( "#inline" );
dp = inl.datepicker( "instance" );
// TODO: Disabling inline pickers does not work.
// TODO: When changeMonth and changeYear options are implemented ensure their dropdowns
// are properly disabled when in an inline picker.
input.datepicker( "enable" );
ok( !input.datepicker( "option", "disabled" ), "enabled after enable() call" );
ok( !widget.hasClass( "ui-datepicker-disabled" ), "no longer has disabled class name" );
});
test( "widget", function() {
expect( 1 );
var actual = $( "#datepicker" ).datepicker().datepicker( "widget" );
deepEqual( $( "body > .ui-front" )[ 0 ], actual[ 0 ] );
actual.remove();
});
test( "close", function() {
expect( 0 );
});
test( "open", function() {
expect( 0 );
});
test( "value", function() {
expect( 4 );
@ -77,7 +54,7 @@ test( "value", function() {
equal( input.datepicker( "value" ), "1/1/14", "getter" );
input.val( "abc" );
equal( input.datepicker( "value" ), "abc", "Invalid values should be returned without formatting." );
strictEqual( input.datepicker( "value" ), null, "Invalid values should return null." );
});
test( "valueAsDate", function() {

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@ TestHelpers.datepicker = {
return $( id ).datepicker( options );
},
initNewInput: function( options ) {
options = $.extend( { show: false }, options || {} );
options = $.extend( { show: false, hide: false }, options || {} );
return $( "<input>" ).datepicker( options )
.appendTo( "#qunit-fixture" );
},

View File

@ -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>

View File

@ -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");

178
themes/base/calendar.css Normal file
View File

@ -0,0 +1,178 @@
/*!
* 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;
}
.ui-calendar .ui-calendar-header {
position: relative;
padding: .2em 0;
}
.ui-calendar .ui-calendar-prev,
.ui-calendar .ui-calendar-next {
position: absolute;
top: 2px;
width: 19px;
height: 18px;
}
.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-hover,
.ui-calendar .ui-calendar-next-hover {
top: 1px;
}
.ui-calendar .ui-calendar-prev {
left: 2px;
}
.ui-calendar .ui-calendar-next {
right: 2px;
}
.ui-calendar .ui-calendar-prev-hover {
left: 1px;
}
.ui-calendar .ui-calendar-next-hover {
right: 1px;
}
.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;
}
.ui-calendar .ui-calendar-title select {
font-size: 1em;
margin: 1px 0;
}
.ui-calendar select.ui-calendar-month,
.ui-calendar select.ui-calendar-year {
width: 49%;
}
.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 span,
.ui-calendar td a {
display: block;
padding: .2em;
text-align: right;
text-decoration: none;
}
.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.ui-calendar-multi {
width: 100%;
}
.ui-calendar-multi .ui-calendar-group {
float: left;
}
.ui-calendar-multi .ui-calendar-group table {
width: 95%;
margin: 0 auto .4em;
}
.ui-calendar-multi-2 .ui-calendar-group {
width: 50%;
}
.ui-calendar-multi-3 .ui-calendar-group {
width: 33.3%;
}
.ui-calendar-multi-4 .ui-calendar-group {
width: 25%;
}
.ui-calendar-multi .ui-calendar-group-last .ui-calendar-header,
.ui-calendar-multi .ui-calendar-group-middle .ui-calendar-header {
border-left-width: 0;
}
.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;
}
.ui-calendar-rtl .ui-calendar-group-last .ui-calendar-header,
.ui-calendar-rtl .ui-calendar-group-middle .ui-calendar-header {
border-right-width: 0;
border-left-width: 1px;
}

View File

@ -9,172 +9,6 @@
* 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: 19px;
height: 18px;
}
.ui-datepicker .ui-datepicker-prev:not(.ui-state-hover):not(.ui-state-focus),
.ui-datepicker .ui-datepicker-next:not(.ui-state-hover):not(.ui-state-focus) {
background: none;
border: none;
}
.ui-datepicker .ui-datepicker-prev-hover,
.ui-datepicker .ui-datepicker-next-hover {
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 .ui-icon,
.ui-datepicker .ui-datepicker-next .ui-icon {
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;
display: none;
position: absolute;
}

497
ui/calendar.js Normal file
View File

@ -0,0 +1,497 @@
/*!
* 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/
*/
(function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
// TODO Add globalize and $.date
define([
"jquery",
"./core",
"./widget",
"./button"
], factory );
} else {
// Browser globals
factory( jQuery );
}
}(function( $ ) {
return $.widget( "ui.calendar", {
version: "@VERSION",
options: {
dateFormat: { date: "short" },
eachDay: $.noop,
numberOfMonths: 1,
showWeek: false,
value: null,
// callbacks
select: null
},
_create: function() {
this.id = this.element.uniqueId().attr( "id" );
this.date = $.date( this.options.value, this.options.dateFormat ).select();
this.date.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();
},
"click .ui-calendar-current": function( event ) {
event.preventDefault();
this._select( event, new Date().getTime() );
},
"mousedown .ui-calendar-calendar a": function( event ) {
event.preventDefault();
// TODO Exclude clicks on lead days or handle them correctly
// TODO Store/read more then just date, also required for multi month picker
this._select( event, $( event.currentTarget ).data( "timestamp" ) );
this.grid.focus();
},
"keydown .ui-calendar-calendar": "_handleKeydown"
});
// TODO Use hoverable (no delegation support)? convert to _on?
this.element.delegate( ".ui-calendar-header a, .ui-calendar-calendar a", "mouseenter.calendar mouseleave.calendar", function() {
$( this ).toggleClass( "ui-state-hover" );
});
this._createCalendar();
},
_handleKeydown: function( event ) {
var oldMonth = this.date.month(),
oldYear = this.date.year();
// TODO: Handle for pickers with multiple months
switch ( event.keyCode ) {
case $.ui.keyCode.ENTER:
this.element.find(
"#" + this.grid.attr( "aria-activedescendant" ) + " > a:first"
).mousedown();
return;
case $.ui.keyCode.PAGE_UP:
this.date.adjust( event.altKey ? "Y" : "M", -1 );
break;
case $.ui.keyCode.PAGE_DOWN:
this.date.adjust( event.altKey ? "Y" : "M", 1 );
break;
case $.ui.keyCode.END:
this.date.setDay( this.date.daysInMonth() );
break;
case $.ui.keyCode.HOME:
this.date.setDay( 1 );
break;
case $.ui.keyCode.LEFT:
this.date.adjust( "D", -1 );
break;
case $.ui.keyCode.UP:
this.date.adjust( "D", -7 );
break;
case $.ui.keyCode.RIGHT:
this.date.adjust( "D", 1 );
break;
case $.ui.keyCode.DOWN:
this.date.adjust( "D", 7 );
break;
default:
event.preventDefault();
return;
}
if ( this.date.month() !== oldMonth || this.date.year() !== oldYear ) {
this.refresh();
this.grid.focus();
}
this._setActiveDescendant();
},
_setActiveDescendant: function() {
var id = this.id + "-" + this.date.day();
this.grid
.attr( "aria-activedescendant", id )
.find( ".ui-state-focus" )
.removeClass( "ui-state-focus" );
this.grid.find( "#" + id ).find ( "a" ).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() + this._buildButtons();
} else {
pickerHtml = this._buildMultiplePicker();
classes += " ui-calendar-multi";
}
this.element
.addClass( classes )
.attr({
role: "region",
"aria-labelledby": this.id + "-title"
})
.html( pickerHtml );
this.element.find( "button" ).button();
this.grid = this.element.find( ".ui-calendar-calendar" );
},
_buildMultiplePicker: function() {
var headerClass,
html = "",
currentDate = this.date,
months = this.date.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.date = months[ i ];
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='ui-calendar-header ui-widget-header ui-helper-clearfix " +
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._buildButtons();
this.date = 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 labels = Globalize.translate( "datepicker" );
return "<button class='ui-calendar-prev ui-corner-all' title='" + labels.prevText + "'>" +
"<span class='ui-icon ui-icon-circle-triangle-w'>" +
labels.prevText +
"</span>" +
"</button>";
},
_buildNextLink: function() {
var labels = Globalize.translate( "datepicker" );
return "<button class='ui-calendar-next ui-corner-all' title='" + labels.nextText + "'>" +
"<span class='ui-icon ui-icon-circle-triangle-e'>" +
labels.nextText +
"</span>" +
"</button>";
},
_buildTitlebar: function() {
var labels = Globalize.translate( "datepicker" );
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'>, " + labels.datePickerRole + "</span>" +
"</div>";
},
_buildTitle: function() {
return "<span class='ui-calendar-month'>" +
this.date.monthName() +
"</span> " +
"<span class='ui-calendar-year'>" +
this.date.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.id + "-" + this.date.day() + "'>" +
this._buildGridHeading() +
this._buildGridBody() +
"</table>";
},
_buildGridHeading: function() {
var cells = "",
i = 0,
labels = Globalize.translate( "datepicker" ),
weekDayLength = this.date.weekdays().length;
if ( this.options.showWeek ) {
cells += "<th class='ui-calendar-week-col'>" + labels.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.date.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 contents = "",
attributes = [
"aria-selected" + ( day.current ? "\"true\"" : "\"false\"" )
];
if ( day.render ) {
attributes.push( "id=\"" + this.id + "-" + day.date + "\"" );
}
if ( day.selectable ) {
attributes.push( "aria-disabled=\"true\"" );
}
if ( day.render ) {
if ( day.selectable ) {
contents = this._buildDayLink( day );
} else {
contents = this._buildDayDisplay( day );
}
}
return "<td role='gridcell' " + attributes.join( " " ) + ">" +
contents +
"</td>";
},
_buildDayLink: function( day ) {
var link,
classes = [ "ui-state-default" ],
labels = Globalize.translate( "datepicker" );
if ( day === this.date ) {
classes.push( "ui-state-focus" );
}
if ( day.current ) {
classes.push( "ui-state-active" );
}
if ( day.today ) {
classes.push( "ui-state-highlight" );
}
// TODO Explain and document this
if ( day.extraClasses ) {
classes.push( day.extraClasses.split( " " ) );
}
link = "<a href='#' tabindex='-1' data-timestamp='" + day.timestamp +
"' class='" + classes.join( " " ) + "'>" + day.date + "</a>";
if ( day.today ) {
link += "<span class='ui-helper-hidden-accessible'>, " +
labels.currentText + "</span>";
}
return link;
},
_buildDayDisplay: function( day ) {
var classes = [];
if ( day.current ) {
classes.push( "ui-state-active" );
}
if ( day.today ) {
classes.push( "ui-state-highlight" );
}
if ( day.extraClasses ) {
classes.push( day.extraClasses.split( " " ) );
}
return "<span class='" + classes.join( " " ) + "'>" + day.date + "</span>";
},
_buildButtons: function() {
var labels = Globalize.translate( "datepicker" );
return "<div class='ui-calendar-buttonpane ui-widget-content'>" +
"<button class='ui-calendar-current'>" + labels.currentText + "</button>" +
"</div>";
},
// 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() {
// 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 );
} 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.date.adjust( "M", 1 );
}
this.date.adjust( "M", -this.options.numberOfMonths );
// TODO: This assumes focus is on the first grid. For multi pickers, the widget needs
// to maintain the currently focused grid and base queries like this off of it.
this.element.find( ".ui-state-focus" ).not( ":first" ).removeClass( "ui-state-focus" );
},
_setHiddenPicker: function() {
this.element.attr({
"aria-hidden": "true",
"aria-expanded": "false"
});
},
_select: function( event, time ) {
this._setOption( "value", new Date( time ) );
this._trigger( "select", event, {
// TODO Replace with value option to initialise and read
date: this.value()
});
},
value: function( value ) {
if ( arguments.length ) {
this._setOption( "value", Globalize.parseDate( value, this.options.dateFormat ) );
} else {
return Globalize.format( this.option( "value" ), this.options.dateFormat );
}
},
valueAsDate: function( value ) {
if ( arguments.length ) {
this._setOption( "value", value );
} else {
return this.option( "value" );
}
},
_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();
},
option: function( key ) {
if ( arguments.length === 0 || ( arguments.length === 1 && key === "value" ) ) {
this.options.value = this.date.selectedDate();
}
return this._superApply( arguments );
},
_setOption: function( key, value ) {
if ( key === "value" ) {
if ( $.type( value ) === "date" ) {
this.date.setTime( value.getTime() ).select();
this.refresh();
}
}
this._super( key, value );
if ( key === "eachDay" ) {
this.date.eachDay = this.options.eachDay;
this.refresh();
}
if ( key === "dateFormat" ) {
this.date.setFormat( this.options.dateFormat );
}
if ( key === "showWeek" ) {
this.refresh();
}
}
});
}));

View File

@ -21,7 +21,7 @@
"jquery",
"./core",
"./widget",
"./button",
"./calendar",
"./position"
], factory );
} else {
@ -31,12 +31,13 @@
}
}(function( $ ) {
// TODO use uniqueId, if possible
// TODO Use uniqueId, if possible
var idIncrement = 0,
// TODO Move this to the instance
suppressExpandOnFocus = false;
$.widget( "ui.datepicker", {
version: "@VERSION",
options: {
appendTo: null,
dateFormat: { date: "short" },
@ -50,7 +51,6 @@ $.widget( "ui.datepicker", {
showWeek: false,
show: true,
hide: true,
value: null,
// callbacks
beforeOpen: null,
@ -60,141 +60,38 @@ $.widget( "ui.datepicker", {
},
_create: function() {
this.id = "ui-datepicker-" + idIncrement;
idIncrement++;
this._createCalendar();
},
if ( this.element.is( "input" ) ) {
if ( !this.options.value && this.isValid() ) {
this.options.value = this._getParsedValue();
}
this._createPicker();
} else {
this.inline = true;
this.picker = this.element;
}
_createCalendar: function() {
var that = this;
this.date = $.date( this.options.value, this.options.dateFormat ).select();
this.date.eachDay = this.options.eachDay;
this.calendar = $( "<div>" )
.addClass( "ui-front ui-datepicker" )
.appendTo( this._appendTo() );
this._on( this.picker, {
"click .ui-datepicker-prev": function( event ) {
event.preventDefault();
this.date.adjust( "M", -this.options.numberOfMonths );
this.refresh();
},
"click .ui-datepicker-next": function( event ) {
event.preventDefault();
this.date.adjust( "M", this.options.numberOfMonths );
this.refresh();
},
"click .ui-datepicker-current": function( event ) {
event.preventDefault();
this._select( event, new Date().getTime() );
},
"click .ui-datepicker-close": function( event ) {
event.preventDefault();
this.close( event );
},
"mousedown .ui-datepicker-calendar a": function( event ) {
event.preventDefault();
// TODO Exclude clicks on lead days or handle them correctly
// TODO Store/read more then just date, also required for multi month picker
this._select( event, $( event.currentTarget ).data( "timestamp" ) );
if ( this.inline ) {
this.grid.focus();
// Initialize calendar widget
this.calendarInstance = this.calendar
.calendar({
dateFormat: this.options.dateFormat,
eachDay: this.options.eachDay,
numberOfMonths: this.options.numberOfMonths,
showWeek: this.options.showWeek,
value: this._getParsedValue(),
select: function( event, data ) {
that.element.val( data.date );
that.close();
that._focusTrigger();
that._trigger( "select", event, data);
}
},
"keydown .ui-datepicker-calendar": "_handleKeydown"
});
})
.calendar( "instance" );
// TODO Use hoverable (no delegation support)? convert to _on?
this.picker.delegate( ".ui-datepicker-header a, .ui-datepicker-calendar a", "mouseenter.datepicker mouseleave.datepicker", function() {
$( this ).toggleClass( "ui-state-hover" );
});
this._createTmpl();
},
_handleKeydown: function( event ) {
if ( jQuery.inArray( event.keyCode, [ 13, 33, 34, 35, 36, 37, 38, 39, 40 ] ) === -1 ) {
// Only interested navigation keys
return;
}
event.preventDefault();
var newId, newCell,
activeCell = $( "#" + this.grid.attr( "aria-activedescendant" ) ),
oldMonth = this.date.month(),
oldYear = this.date.year();
// TODO: Handle for pickers with multiple months
switch ( event.keyCode ) {
case $.ui.keyCode.ENTER:
activeCell.children( "a:first" ).mousedown();
return;
case $.ui.keyCode.PAGE_UP:
this.date.adjust( event.altKey ? "Y" : "M", -1 );
break;
case $.ui.keyCode.PAGE_DOWN:
this.date.adjust( event.altKey ? "Y" : "M", 1 );
break;
case $.ui.keyCode.END:
this.date.setDay( this.date.daysInMonth() );
break;
case $.ui.keyCode.HOME:
this.date.setDay( 1 );
break;
case $.ui.keyCode.LEFT:
this.date.adjust( "D", -1 );
break;
case $.ui.keyCode.UP:
this.date.adjust( "D", -7 );
break;
case $.ui.keyCode.RIGHT:
this.date.adjust( "D", 1 );
break;
case $.ui.keyCode.DOWN:
this.date.adjust( "D", 7 );
break;
default:
return;
}
if ( this.date.month() !== oldMonth || this.date.year() !== oldYear ) {
this.refresh();
this.grid.focus();
} else {
newId = this.id + "-" + this.date.day();
newCell = $( "#" + newId );
// TODO Unnecessary optimization? is it really needed?
if ( !newCell.length ) {
return;
}
this.grid.attr("aria-activedescendant", newId);
this.grid.find( ".ui-state-focus" ).removeClass( "ui-state-focus" );
newCell.children( "a" ).addClass( "ui-state-focus" );
}
},
_createPicker: function() {
this.picker = $( "<div>" )
.addClass( "ui-front" )
// TODO Add a ui-datepicker-popup class, move position:absolute to that
.css( "position", "absolute" )
.uniqueId()
.hide();
this._setHiddenPicker();
this.picker.appendTo( this._appendTo() );
this.element
.attr( "aria-haspopup", "true" )
.attr( "aria-owns", this.picker.attr( "id" ) );
.attr( "aria-owns", this.calendar.attr( "id" ) );
this._on({
keydown: function( event ) {
@ -202,7 +99,7 @@ $.widget( "ui.datepicker", {
case $.ui.keyCode.TAB:
// Waiting for close() will make popup hide too late, which breaks tab key behavior
this.picker.hide();
this.calendar.hide();
this.close( event );
break;
case $.ui.keyCode.ESCAPE:
@ -211,22 +108,22 @@ $.widget( "ui.datepicker", {
}
break;
case $.ui.keyCode.ENTER:
this._handleKeydown( event );
this.calendarInstance._handleKeydown( event );
break;
case $.ui.keyCode.DOWN:
case $.ui.keyCode.UP:
clearTimeout( this.closeTimer );
this._delay( function() {
this.open( event );
this.grid.focus();
this.calendarInstance.grid.focus();
}, 1 );
break;
case $.ui.keyCode.HOME:
if ( event.ctrlKey ) {
this.date.setTime( new Date() );
this.valueAsDate( new Date() );
event.preventDefault();
if ( this.isOpen ) {
this.refresh();
this.calendarInstance.refresh();
} else {
this.open( event );
}
@ -244,8 +141,7 @@ $.widget( "ui.datepicker", {
}
},
keyup: function() {
if ( this.isValid() && !this.inline ) {
this.date.setTime( this._getParsedValue() ).select();
if ( this.isValid() ) {
this.refresh();
}
},
@ -275,7 +171,7 @@ $.widget( "ui.datepicker", {
}
});
this._on( this.picker, {
this._on( this.calendar, {
focusout: function( event ) {
// Use a timer to allow click to clear it and letting that
@ -294,7 +190,7 @@ $.widget( "ui.datepicker", {
// TODO On TAB (or shift TAB), make sure it ends up on something useful in DOM order
keyup: function( event ) {
if ( event.keyCode === $.ui.keyCode.ESCAPE && this.picker.is( ":visible" ) ) {
if ( event.keyCode === $.ui.keyCode.ESCAPE && this.calendar.is( ":visible" ) ) {
this.close( event );
this._focusTrigger();
}
@ -303,7 +199,7 @@ $.widget( "ui.datepicker", {
this._on( this.document, {
click: function( event ) {
if ( this.isOpen && !$( event.target ).closest( this.element.add( this.picker ) ).length ) {
if ( this.isOpen && !$( event.target ).closest( this.element.add( this.calendar ) ).length ) {
this.close( event );
}
}
@ -319,7 +215,7 @@ $.widget( "ui.datepicker", {
this.document.find( element ).eq( 0 );
}
if ( !element ) {
if ( !element || !element[ 0 ] ) {
element = this.element.closest( ".ui-front" );
}
@ -330,315 +226,33 @@ $.widget( "ui.datepicker", {
return element;
},
_createTmpl: function() {
this._createDatepicker();
this.picker.find( "button" ).button();
if ( this.inline ) {
this.picker.children().addClass( "ui-datepicker-inline" );
}
// Against display:none in datepicker.css
this.picker.find( ".ui-datepicker" ).css( "display", "block" );
this.grid = this.picker.find( ".ui-datepicker-calendar" );
},
_createDatepicker: function() {
var multiClasses = [],
pickerHtml = "";
if (this.options.numberOfMonths === 1 ) {
pickerHtml = this._buildHeader() + this._buildGrid() + this._buildButtons();
} else {
pickerHtml = this._buildMultiplePicker();
multiClasses.push( "ui-datepicker-multi" );
multiClasses.push( "ui-datepicker-multi-" + this.options.numberOfMonths );
}
$( "<div>" )
.addClass( "ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all" )
.addClass( multiClasses.join( " " ) )
.attr({
role: "region",
"aria-labelledby": this.id + "-title"
})
.html( pickerHtml )
.appendTo( this.picker );
},
_buildMultiplePicker: function() {
var headerClass,
html = "",
currentDate = this.date,
months = this.date.months( this.options.numberOfMonths - 1 ),
i = 0;
for ( i; i < months.length; i++ ) {
// TODO Shouldn't we pass date as a parameter to build* fns instead of setting this.date?
this.date = months[ i ];
headerClass = months[ i ].first ? "ui-corner-left" :
months[ i ].last ? "ui-corner-right" : "";
html += "<div class='ui-datepicker-group'>" +
"<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix " + headerClass + "'>";
if ( months[ i ].first ) {
html += this._buildPreviousLink();
}
if ( months[ i ].last ) {
html += this._buildNextLink();
}
html += this._buildTitlebar();
html += "</div>";
html += this._buildGrid();
html += "</div>";
}
html += "<div class='ui-datepicker-row-break'></div>";
html += this._buildButtons();
this.date = currentDate;
return html;
},
_buildHeader: function() {
return "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix ui-corner-all'>" +
this._buildPreviousLink() +
this._buildNextLink() +
this._buildTitlebar() +
"</div>";
},
_buildPreviousLink: function() {
var labels = Globalize.translate( "datepicker" );
return "<button class='ui-datepicker-prev ui-corner-all' " +
"title='" + labels.prevText + "'>" +
"<span class='ui-icon ui-icon-circle-triangle-w'>" +
labels.prevText +
"</span>" +
"</button>";
},
_buildNextLink: function() {
var labels = Globalize.translate( "datepicker" );
return "<button class='ui-datepicker-next ui-corner-all' " +
"title='" + labels.nextText + "'>" +
"<span class='ui-icon ui-icon-circle-triangle-e'>" +
labels.nextText +
"</span>" +
"</button>";
},
_buildTitlebar: function() {
var labels = Globalize.translate( "datepicker" );
return "<div role='header' id='" + this.id + "-title'>" +
"<div id='" + this.id + "-month-lbl' class='ui-datepicker-title'>" +
this._buildTitle() +
"</div>" +
"<span class='ui-helper-hidden-accessible'>, " + labels.datePickerRole + "</span>" +
"</div>";
},
_buildTitle: function() {
return "<span class='ui-datepicker-month'>" +
this.date.monthName() +
"</span> " +
"<span class='ui-datepicker-year'>" +
this.date.year() +
"</span>";
},
_buildGrid: function() {
return "<table class='ui-datepicker-calendar' role='grid' aria-readonly='true' " +
"aria-labelledby='" + this.id + "-month-lbl' tabindex='0' aria-activedescendant='" + this.id + "-" + this.date.day() + "'>" +
this._buildGridHeading() +
this._buildGridBody() +
"</table>";
},
_buildGridHeading: function() {
var cells = "",
i = 0,
labels = Globalize.translate( "datepicker" );
if ( this.options.showWeek ) {
cells += "<th class='ui-datepicker-week-col'>" + labels.weekHeader + "</th>";
}
for ( i; i < this.date.weekdays().length; 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() is not cached, and it has O(n^2) complexity. It is run O(n) times. So, it equals O(n^3). Not good at all. Caching.
var days = this.date.days(),
i = 0,
rows = "";
for ( i; i < days.length; i++ ) {
rows += this._buildWeekRow( days[ i ] );
}
return "<tbody role='presentation'>" + rows + "</tbody>";
},
_buildWeekRow: function( week ) {
var cells = "",
i = 0;
if ( this.options.showWeek ) {
cells += "<td class='ui-datepicker-week-col'>" + week.number + "</td>";
}
for ( i; i < week.days.length; i++ ) {
cells += this._buildDayCell( week.days[i] );
}
return "<tr role='row'>" + cells + "</tr>";
},
_buildDayCell: function( day ) {
var contents = "",
idAttribute = day.render ? ( "id=" + this.id + "-" + day.date ) : "",
ariaSelectedAttribute = "aria-selected=" + ( day.current ? "true" : "false" ),
ariaDisabledAttribute = day.selectable ? "" : "aria-disabled=true";
if ( day.render ) {
if ( day.selectable ) {
contents = this._buildDayLink( day );
} else {
contents = this._buildDayDisplay( day );
}
}
return "<td role='gridcell' " + idAttribute + " " + ariaSelectedAttribute + " " + ariaDisabledAttribute + ">" +
contents +
"</td>";
},
_buildDayLink: function( day ) {
var link,
classes = [ "ui-state-default" ],
labels = Globalize.translate( "datepicker" );
if ( day.date === this.date.day() ) {
classes.push( "ui-state-focus" );
}
if ( day.current ) {
classes.push( "ui-state-active" );
}
if ( day.today ) {
classes.push( "ui-state-highlight" );
}
if ( day.extraClasses ) {
classes.push( day.extraClasses.split( " " ) );
}
link = "<a href='#' tabindex='-1' data-timestamp='" + day.timestamp + "' class='" + classes.join( " " ) + "'>" +
day.date + "</a>";
if ( day.today ) {
link += "<span class='ui-helper-hidden-accessible'>, " + labels.currentText + "</span>";
}
return link;
},
_buildDayDisplay: function( day ) {
var classes = [];
if ( day.current ) {
classes.push( "ui-state-active" );
}
if ( day.today ) {
classes.push( "ui-state-highlight" );
}
if ( day.extraClasses ) {
classes.push( day.extraClasses.split( " " ) );
}
return "<span class='" + classes.join( "" ) + "'>" + day.date + "</span>";
},
_buildButtons: function() {
var labels = Globalize.translate( "datepicker" );
return "<div class='ui-datepicker-buttonpane ui-widget-content'>" +
"<button class='ui-datepicker-current'>" + labels.currentText + "</button>" +
"<button class='ui-datepicker-close'>" + labels.closeText + "</button>" +
"</div>";
},
_focusTrigger: function() {
suppressExpandOnFocus = true;
this.element.focus();
},
// Refreshing the entire datepicker during interaction confuses screen readers, specifically
// because the grid heading is marked up as a live region and will often not update if it's
// destroyed and recreated instead of just having its text change. Additionally, interacting
// with the prev and next links would cause loss of focus issues because the links being
// interacted with will disappear while focused.
refresh: function() {
// Determine which day gridcell to focus after refresh
// TODO: Prevent disabled cells from being focused
if ( this.options.numberOfMonths === 1 ) {
this.grid = $( this._buildGrid() );
$( ".ui-datepicker-title", this.picker ).html( this._buildTitle() );
$( ".ui-datepicker-calendar", this.picker ).replaceWith( this.grid );
} else {
this._refreshMultiplePicker();
}
},
_refreshMultiplePicker: function() {
for ( var i = 0; i < this.options.numberOfMonths; i++ ) {
$( ".ui-datepicker-title", this.picker ).eq( i ).html( this._buildTitle() );
$( ".ui-datepicker-calendar", this.picker ).eq( i ).html( this._buildGrid() );
this.date.adjust( "M", 1 );
}
this.date.adjust( "M", -this.options.numberOfMonths );
// TODO: This assumes focus is on the first grid. For multi pickers, the widget needs
// to maintain the currently focused grid and base queries like this off of it.
$( this.picker ).find( ".ui-state-focus" ).not( ":first" ).removeClass( "ui-state-focus" );
this.calendarInstance.option( "value", this._getParsedValue() );
},
open: function( event ) {
if ( this.inline || this.isOpen ) {
if ( this.isOpen ) {
return;
}
if ( this._trigger( "beforeOpen", event ) === false ) {
return;
}
this.refresh();
this.calendarInstance.refresh();
this.picker
this.calendar
.attr( "aria-hidden", "false" )
.attr( "aria-expanded", "true" )
.show()
.position( this._buildPosition() )
.hide();
this._show( this.picker, this.options.show );
this._show( this.calendar, this.options.show );
// Take trigger out of tab order to allow shift-tab to skip trigger
// TODO Does this really make sense? related bug: tab-shift moves focus to last element on page
@ -649,12 +263,8 @@ $.widget( "ui.datepicker", {
},
close: function( event ) {
if ( this.inline ) {
return;
}
this._setHiddenPicker();
this._hide( this.picker, this.options.hide );
this._hide( this.calendar, this.options.hide );
this.element.attr( "tabindex" , 0 );
@ -663,7 +273,7 @@ $.widget( "ui.datepicker", {
},
_setHiddenPicker: function() {
this.picker
this.calendar
.attr( "aria-hidden", "true" )
.attr( "aria-expanded", "false" );
},
@ -672,108 +282,66 @@ $.widget( "ui.datepicker", {
return $.extend( { of: this.element }, this.options.position );
},
_select: function( event, time ) {
this._setOption( "value", new Date( time ) );
if ( !this.inline ) {
this.close();
this._focusTrigger();
}
this._trigger( "select", event, {
// TODO Replace with value option to initialise and read
date: this.value()
});
},
value: function( value ) {
if ( arguments.length ) {
this._setOption( "value", Globalize.parseDate( value, this.options.dateFormat ) );
} else {
if ( this.inline ) {
return Globalize.format( this.date.selected(), this.options.dateFormat );
} else {
return this.element.val();
var date = Globalize.parseDate( value, this.options.dateFormat );
if ( $.type( date ) === "date" ) {
this.valueAsDate( date );
this.element.val( value );
}
} else {
return this._getParsedValue() ? this.element.val() : null;
}
},
valueAsDate: function( value ) {
if ( arguments.length ) {
this._setOption( "value", value );
if ( $.type( value ) === "date" ) {
this.calendarInstance.valueAsDate( value );
this.element.val( Globalize.format( value, this.options.dateFormat ) );
}
} else {
return this.option( "value" );
return this._getParsedValue();
}
},
isValid: function() {
if ( this.inline ) {
return true;
}
return this._getParsedValue() !== null;
},
_destroy: function() {
if ( this.inline ) {
this.picker.empty();
} else {
this.picker.remove();
this.element
.removeAttr( "aria-haspopup" )
.removeAttr( "aria-owns" );
}
this.calendarInstance.destroy();
this.calendar.remove();
this.element
.removeAttr( "aria-haspopup" )
.removeAttr( "aria-owns" );
},
widget: function() {
return this.picker;
return this.calendar;
},
_getParsedValue: function() {
return Globalize.parseDate( this.element.val(), this.options.dateFormat );
},
option: function( key ) {
if ( arguments.length === 0 || ( arguments.length === 1 && key === "value" ) ) {
this.options.value = this.inline ? this.date.selected() : this._getParsedValue();
}
return this._superApply( arguments );
},
_setOption: function( key, value ) {
if ( key === "value" ) {
if ( $.type( value ) === "date" ) {
this.date.setTime( value.getTime() ).select();
this.refresh();
if ( !this.inline ) {
this.element.val( this.date.format() );
}
}
}
this._super( key, value );
if ( key === "appendTo" ) {
this.picker.appendTo( this._appendTo() );
if ( $.inArray( key, [ "showWeek", "numberOfMonths", "dateFormat", "eachDay", "min", "max" ] ) !== -1 ) {
this.calendarInstance.option( key, value );
}
if ( key === "eachDay" ) {
this.date.eachDay = this.options.eachDay;
this.refresh();
if ( key === "appendTo" ) {
this.calendar.appendTo( this._appendTo() );
}
if ( key === "dateFormat" ) {
this.date.setFormat( this.options.dateFormat );
if ( !this.inline ) {
this.element.val( this.date.format() );
}
}
if ( key === "showWeek" ) {
this.refresh();
this.element.val( this.calendarInstance.value() );
}
if ( key === "position" ) {
this.picker.position( this._buildPosition() );
this.calendar.position( this._buildPosition() );
}
}
});