zebra-datepicker-rails 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +13 -0
- data/lib/zebra-datepicker-rails/engine.rb +6 -0
- data/lib/zebra-datepicker-rails/railtie.rb +5 -0
- data/lib/zebra-datepicker-rails/version.rb +5 -0
- data/lib/zebra-datepicker-rails.rb +12 -0
- data/vendor/assets/images/zebra-datepicker/calendar-disabled.png +0 -0
- data/vendor/assets/images/zebra-datepicker/calendar.png +0 -0
- data/vendor/assets/images/zebra-datepicker/metallic/default-date.png +0 -0
- data/vendor/assets/images/zebra-datepicker/metallic/disabled-date.png +0 -0
- data/vendor/assets/images/zebra-datepicker/metallic/header.png +0 -0
- data/vendor/assets/images/zebra-datepicker/metallic/selected-date.png +0 -0
- data/vendor/assets/javascripts/zebra-datepicker/zebra_datepicker.src.js +2878 -0
- data/vendor/assets/stylesheets/zebra-datepicker/bootstrap.css +0 -0
- data/vendor/assets/stylesheets/zebra-datepicker/default.css +98 -0
- data/vendor/assets/stylesheets/zebra-datepicker/metallic.css +102 -0
- data/zebra-datepicker-rails.gemspec +25 -0
- metadata +121 -0
@@ -0,0 +1,2878 @@
|
|
1
|
+
/**
|
2
|
+
* Zebra_DatePicker
|
3
|
+
*
|
4
|
+
* Zebra_DatePicker is a small, compact and highly configurable date picker plugin for jQuery
|
5
|
+
*
|
6
|
+
* Visit {@link http://stefangabos.ro/jquery/zebra-datepicker/} for more information.
|
7
|
+
*
|
8
|
+
* For more resources visit {@link http://stefangabos.ro/}
|
9
|
+
*
|
10
|
+
* @author Stefan Gabos <contact@stefangabos.ro>
|
11
|
+
* @version 1.8.4 (last revision: August 11, 2013)
|
12
|
+
* @copyright (c) 2011 - 2013 Stefan Gabos
|
13
|
+
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU LESSER GENERAL PUBLIC LICENSE
|
14
|
+
* @package Zebra_DatePicker
|
15
|
+
*/
|
16
|
+
;(function($) {
|
17
|
+
|
18
|
+
$.Zebra_DatePicker = function(element, options) {
|
19
|
+
|
20
|
+
var defaults = {
|
21
|
+
|
22
|
+
// setting this property to a jQuery element, will result in the date picker being always visible, the indicated
|
23
|
+
// element being the date picker's container;
|
24
|
+
always_visible: false,
|
25
|
+
|
26
|
+
// days of the week; Sunday to Saturday
|
27
|
+
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
28
|
+
|
29
|
+
// by default, the abbreviated name of a day consists of the first 2 letters from the day's full name;
|
30
|
+
// while this is common for most languages, there are also exceptions for languages like Thai, Loa, Myanmar,
|
31
|
+
// etc. where this is not correct; for these cases, specify an array with the abbreviations to be used for
|
32
|
+
// the 7 days of the week; leave it FALSE to use the first 2 letters of a day's name as the abbreviation.
|
33
|
+
//
|
34
|
+
// default is FALSE
|
35
|
+
days_abbr: false,
|
36
|
+
|
37
|
+
// direction of the calendar
|
38
|
+
//
|
39
|
+
// a positive or negative integer: n (a positive integer) creates a future-only calendar beginning at n days
|
40
|
+
// after today; -n (a negative integer); if n is 0, the calendar has no restrictions. use boolean true for
|
41
|
+
// a future-only calendar starting with today and use boolean false for a past-only calendar ending today.
|
42
|
+
//
|
43
|
+
// you may also set this property to an array with two elements in the following combinations:
|
44
|
+
//
|
45
|
+
// - first item is boolean TRUE (calendar starts today), an integer > 0 (calendar starts n days after
|
46
|
+
// today), or a valid date given in the format defined by the "format" attribute, using English for
|
47
|
+
// month names (calendar starts at the specified date), and the second item is boolean FALSE (the calendar
|
48
|
+
// has no ending date), an integer > 0 (calendar ends n days after the starting date), or a valid date
|
49
|
+
// given in the format defined by the "format" attribute, using English for month names, and which occurs
|
50
|
+
// after the starting date (calendar ends at the specified date)
|
51
|
+
//
|
52
|
+
// - first item is boolean FALSE (calendar ends today), an integer < 0 (calendar ends n days before today),
|
53
|
+
// or a valid date given in the format defined by the "format" attribute, using English for month names
|
54
|
+
// (calendar ends at the specified date), and the second item is an integer > 0 (calendar ends n days
|
55
|
+
// before the ending date), or a valid date given in the format defined by the "format" attribute, using
|
56
|
+
// English for month names and which occurs before the starting date (calendar starts at the specified
|
57
|
+
// date)
|
58
|
+
//
|
59
|
+
// [1, 7] - calendar starts tomorrow and ends seven days after that
|
60
|
+
// [true, 7] - calendar starts today and ends seven days after that
|
61
|
+
// ['2013-01-01', false] - calendar starts on January 1st 2013 and has no ending date ("format" is YYYY-MM-DD)
|
62
|
+
// [false, '2012-01-01'] - calendar ends today and starts on January 1st 2012 ("format" is YYYY-MM-DD)
|
63
|
+
//
|
64
|
+
// note that "disabled_dates" property will still apply!
|
65
|
+
//
|
66
|
+
// default is 0 (no restrictions)
|
67
|
+
direction: 0,
|
68
|
+
|
69
|
+
// an array of disabled dates in the following format: 'day month year weekday' where "weekday" is optional
|
70
|
+
// and can be 0-6 (Saturday to Sunday); the syntax is similar to cron's syntax: the values are separated by
|
71
|
+
// spaces and may contain * (asterisk) - (dash) and , (comma) delimiters:
|
72
|
+
//
|
73
|
+
// ['1 1 2012'] would disable January 1, 2012;
|
74
|
+
// ['* 1 2012'] would disable all days in January 2012;
|
75
|
+
// ['1-10 1 2012'] would disable January 1 through 10 in 2012;
|
76
|
+
// ['1,10 1 2012'] would disable January 1 and 10 in 2012;
|
77
|
+
// ['1-10,20,22,24 1-3 *'] would disable 1 through 10, plus the 22nd and 24th of January through March for every year;
|
78
|
+
// ['* * * 0,6'] would disable all Saturdays and Sundays;
|
79
|
+
// ['01 07 2012', '02 07 2012', '* 08 2012'] would disable 1st and 2nd of July 2012, and all of August of 2012
|
80
|
+
//
|
81
|
+
// default is FALSE, no disabled dates
|
82
|
+
disabled_dates: false,
|
83
|
+
|
84
|
+
// an array of enabled dates in the same format as required for "disabled_dates" property.
|
85
|
+
// to be used together with the "disabled_dates" property by first setting the "disabled_dates" property to
|
86
|
+
// something like "[* * * *]" (which will disable everything) and the setting the "enabled_dates" property to,
|
87
|
+
// say, "[* * * 0,6]" to enable just weekends.
|
88
|
+
enabled_dates: false,
|
89
|
+
|
90
|
+
// week's starting day
|
91
|
+
//
|
92
|
+
// valid values are 0 to 6, Sunday to Saturday
|
93
|
+
//
|
94
|
+
// default is 1, Monday
|
95
|
+
first_day_of_week: 1,
|
96
|
+
|
97
|
+
// format of the returned date
|
98
|
+
//
|
99
|
+
// accepts the following characters for date formatting: d, D, j, l, N, w, S, F, m, M, n, Y, y borrowing
|
100
|
+
// syntax from (PHP's date function)
|
101
|
+
//
|
102
|
+
// note that when setting a date format without days ('d', 'j'), the users will be able to select only years
|
103
|
+
// and months, and when setting a format without months and days ('F', 'm', 'M', 'n', 'd', 'j'), the
|
104
|
+
// users will be able to select only years; likewise, when setting a date format with just months ('F', 'm',
|
105
|
+
// 'M', 'n') or just years ('Y', 'y'), users will be able to select only months and years, respectively.
|
106
|
+
//
|
107
|
+
// also note that the value of the "view" property (see below) may be overridden if it is the case: a value of
|
108
|
+
// "days" for the "view" property makes no sense if the date format doesn't allow the selection of days.
|
109
|
+
//
|
110
|
+
// default is Y-m-d
|
111
|
+
format: 'Y-m-d',
|
112
|
+
|
113
|
+
// should the icon for opening the datepicker be inside the element?
|
114
|
+
// if set to FALSE, the icon will be placed to the right of the parent element, while if set to TRUE it will
|
115
|
+
// be placed to the right of the parent element, but *inside* the element itself
|
116
|
+
//
|
117
|
+
// default is TRUE
|
118
|
+
inside: true,
|
119
|
+
|
120
|
+
// the caption for the "Clear" button
|
121
|
+
lang_clear_date: 'Clear date',
|
122
|
+
|
123
|
+
// months names
|
124
|
+
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
125
|
+
|
126
|
+
// by default, the abbreviated name of a month consists of the first 3 letters from the month's full name;
|
127
|
+
// while this is common for most languages, there are also exceptions for languages like Thai, Loa, Myanmar,
|
128
|
+
// etc. where this is not correct; for these cases, specify an array with the abbreviations to be used for
|
129
|
+
// the months of the year; leave it FALSE to use the first 3 letters of a month's name as the abbreviation.
|
130
|
+
//
|
131
|
+
// default is FALSE
|
132
|
+
months_abbr: false,
|
133
|
+
|
134
|
+
// the offset, in pixels (x, y), to shift the date picker's position relative to the top-right of the icon
|
135
|
+
// that toggles the date picker or, if the icon is disabled, relative to the top-right corner of the element
|
136
|
+
// the plugin is attached to.
|
137
|
+
//
|
138
|
+
// note that this only applies if the position of element relative to the browser's viewport doesn't require
|
139
|
+
// the date picker to be placed automatically so that it is visible!
|
140
|
+
//
|
141
|
+
// default is [5, -5]
|
142
|
+
offset: [5, -5],
|
143
|
+
|
144
|
+
// if set as a jQuery element with a Zebra_DatePicker attached, that particular date picker will use the
|
145
|
+
// current date picker's value as starting date
|
146
|
+
// note that the rules set in the "direction" property will still apply, only that the reference date will
|
147
|
+
// not be the current system date but the value selected in the current date picker
|
148
|
+
// default is FALSE (not paired with another date picker)
|
149
|
+
pair: false,
|
150
|
+
|
151
|
+
// should the element the calendar is attached to, be read-only?
|
152
|
+
// if set to TRUE, a date can be set only through the date picker and cannot be entered manually
|
153
|
+
//
|
154
|
+
// default is TRUE
|
155
|
+
readonly_element: true,
|
156
|
+
|
157
|
+
// should days from previous and/or next month be selectable when visible?
|
158
|
+
// note that if the value of this property is set to TRUE, the value of "show_other_months" will be considered
|
159
|
+
// TRUE regardless of the actual value!
|
160
|
+
//
|
161
|
+
// default is FALSE
|
162
|
+
select_other_months: false,
|
163
|
+
|
164
|
+
// should the "Clear date" button be visible?
|
165
|
+
//
|
166
|
+
// accepted values are:
|
167
|
+
//
|
168
|
+
// - 0 (zero) - the button for clearing a previously selected date is shown only if a previously selected date
|
169
|
+
// already exists; this means that if the input the date picker is attached to is empty, and the user selects
|
170
|
+
// a date for the first time, this button will not be visible; once the user picked a date and opens the date
|
171
|
+
// picker again, this time the button will be visible.
|
172
|
+
//
|
173
|
+
// - TRUE will make the button visible all the time
|
174
|
+
//
|
175
|
+
// - FALSE will disable the button
|
176
|
+
//
|
177
|
+
// default is "0" (without quotes)
|
178
|
+
show_clear_date: 0,
|
179
|
+
|
180
|
+
// should a calendar icon be added to the elements the plugin is attached to?
|
181
|
+
//
|
182
|
+
// default is TRUE
|
183
|
+
show_icon: true,
|
184
|
+
|
185
|
+
// should days from previous and/or next month be visible?
|
186
|
+
//
|
187
|
+
// default is TRUE
|
188
|
+
show_other_months: true,
|
189
|
+
|
190
|
+
// should the "Today" button be visible?
|
191
|
+
// setting it to anything but boolean FALSE will enable the button and will use the property's value as
|
192
|
+
// caption for the button; setting it to FALSE will disable the button
|
193
|
+
//
|
194
|
+
// default is "Today"
|
195
|
+
show_select_today: 'Today',
|
196
|
+
|
197
|
+
// should an extra column be shown, showing the number of each week?
|
198
|
+
// anything other than FALSE will enable this feature, and use the given value as column title
|
199
|
+
// i.e. show_week_number: 'Wk' would enable this feature and have "Wk" as the column's title
|
200
|
+
//
|
201
|
+
// default is FALSE
|
202
|
+
show_week_number: false,
|
203
|
+
|
204
|
+
// a default date to start the date picker with
|
205
|
+
// must be specified in the format defined by the "format" property, or it will be ignored!
|
206
|
+
// note that this value is used only if there is no value in the field the date picker is attached to!
|
207
|
+
start_date: false,
|
208
|
+
|
209
|
+
// how should the date picker start; valid values are "days", "months" and "years"
|
210
|
+
// note that the date picker is always cycling days-months-years when clicking in the date picker's header,
|
211
|
+
// and years-months-days when selecting dates (unless one or more of the views are missing due to the date's
|
212
|
+
// format)
|
213
|
+
//
|
214
|
+
// also note that the value of the "view" property may be overridden if the date's format requires so! (i.e.
|
215
|
+
// "days" for the "view" property makes no sense if the date format doesn't allow the selection of days)
|
216
|
+
//
|
217
|
+
// default is "days"
|
218
|
+
view: 'days',
|
219
|
+
|
220
|
+
// days of the week that are considered "weekend days"
|
221
|
+
// valid values are 0 to 6, Sunday to Saturday
|
222
|
+
//
|
223
|
+
// default values are 0 and 6 (Saturday and Sunday)
|
224
|
+
weekend_days: [0, 6],
|
225
|
+
|
226
|
+
// when set to TRUE, day numbers < 10 will be prefixed with 0; set to FALSE if you don't want that
|
227
|
+
//
|
228
|
+
// default is TRUE
|
229
|
+
zero_pad: false,
|
230
|
+
|
231
|
+
// callback function to be executed whenever the user changes the view (days/months/years), as well as when
|
232
|
+
// the user navigates by clicking on the "next"/"previous" icons in any of the views;
|
233
|
+
//
|
234
|
+
// the callback function called by this event takes 3 arguments - the first argument represents the current
|
235
|
+
// view (can be "days", "months" or "years"), the second argument represents an array containing the "active"
|
236
|
+
// elements (not disabled) from the view, as jQuery elements, allowing for easy customization and interaction
|
237
|
+
// with particular cells in the date picker's view, while the third argument is a reference to the element
|
238
|
+
// the date picker is attached to, as a jQuery object
|
239
|
+
//
|
240
|
+
// for simplifying searching for particular dates, each element in the second argument will also have a
|
241
|
+
// "date" data attribute whose format depends on the value of the "view" argument:
|
242
|
+
// - YYYY-MM-DD for elements in the "days" view
|
243
|
+
// - YYYY-MM for elements in the "months" view
|
244
|
+
// - YYYY for elements in the "years" view
|
245
|
+
onChange: null,
|
246
|
+
|
247
|
+
// callback function to be executed when the user clicks the "Clear" button
|
248
|
+
// the callback function takes a single argument:
|
249
|
+
// - a reference to the element the date picker is attached to, as a jQuery object
|
250
|
+
onClear: null,
|
251
|
+
|
252
|
+
// callback function to be executed when a date is selected
|
253
|
+
// the callback function takes 4 arguments:
|
254
|
+
// - the date in the format specified by the "format" attribute;
|
255
|
+
// - the date in YYYY-MM-DD format
|
256
|
+
// - the date as a JavaScript Date object
|
257
|
+
// - a reference to the element the date picker is attached to, as a jQuery object
|
258
|
+
onSelect: null
|
259
|
+
|
260
|
+
};
|
261
|
+
|
262
|
+
// private properties
|
263
|
+
var view, datepicker, icon, header, daypicker, monthpicker, yearpicker, cleardate, current_system_month, current_system_year,
|
264
|
+
current_system_day, first_selectable_month, first_selectable_year, first_selectable_day, selected_month, selected_year,
|
265
|
+
default_day, default_month, default_year, enabled_dates, disabled_dates, shim, start_date, end_date, last_selectable_day,
|
266
|
+
last_selectable_year, last_selectable_month, daypicker_cells, monthpicker_cells, yearpicker_cells, views, clickables,
|
267
|
+
selecttoday, footer, show_select_today, timeout;
|
268
|
+
|
269
|
+
var plugin = this;
|
270
|
+
|
271
|
+
plugin.settings = {};
|
272
|
+
|
273
|
+
// the jQuery version of the element
|
274
|
+
// "element" (without the $) will point to the DOM element
|
275
|
+
var $element = $(element);
|
276
|
+
|
277
|
+
/**
|
278
|
+
* Constructor method. Initializes the date picker.
|
279
|
+
*
|
280
|
+
* @return void
|
281
|
+
*/
|
282
|
+
var init = function(update) {
|
283
|
+
|
284
|
+
// merge default settings with user-settings (unless we're just updating settings)
|
285
|
+
if (!update) plugin.settings = $.extend({}, defaults, options);
|
286
|
+
|
287
|
+
// if the element should be read-only, set the "readonly" attribute
|
288
|
+
if (plugin.settings.readonly_element) $element.attr('readonly', 'readonly');
|
289
|
+
|
290
|
+
// determine the views the user can cycle through, depending on the format
|
291
|
+
// that is, if the format doesn't contain the day, the user will be able to cycle only through years and months,
|
292
|
+
// whereas if the format doesn't contain months nor days, the user will only be able to select years
|
293
|
+
|
294
|
+
var
|
295
|
+
|
296
|
+
// the characters that may be present in the date format and that represent days, months and years
|
297
|
+
date_chars = {
|
298
|
+
days: ['d', 'j', 'D'],
|
299
|
+
months: ['F', 'm', 'M', 'n', 't'],
|
300
|
+
years: ['o', 'Y', 'y']
|
301
|
+
},
|
302
|
+
|
303
|
+
// some defaults
|
304
|
+
has_days = false,
|
305
|
+
has_months = false,
|
306
|
+
has_years = false,
|
307
|
+
type = null;
|
308
|
+
|
309
|
+
// iterate through all the character blocks
|
310
|
+
for (type in date_chars)
|
311
|
+
|
312
|
+
// iterate through the characters of each block
|
313
|
+
$.each(date_chars[type], function(index, character) {
|
314
|
+
|
315
|
+
// if current character exists in the "format" property
|
316
|
+
if (plugin.settings.format.indexOf(character) > -1)
|
317
|
+
|
318
|
+
// set to TRUE the appropriate flag
|
319
|
+
if (type == 'days') has_days = true;
|
320
|
+
else if (type == 'months') has_months = true;
|
321
|
+
else if (type == 'years') has_years = true;
|
322
|
+
|
323
|
+
});
|
324
|
+
|
325
|
+
// if user can cycle through all the views, set the flag accordingly
|
326
|
+
if (has_days && has_months && has_years) views = ['years', 'months', 'days'];
|
327
|
+
|
328
|
+
// if user can cycle only through year and months, set the flag accordingly
|
329
|
+
else if (!has_days && has_months && has_years) views = ['years', 'months'];
|
330
|
+
|
331
|
+
// if user can only see the year picker, set the flag accordingly
|
332
|
+
else if (!has_days && !has_months && has_years) views = ['years'];
|
333
|
+
|
334
|
+
// if user can only see the month picker, set the flag accordingly
|
335
|
+
else if (!has_days && has_months && !has_years) views = ['months'];
|
336
|
+
|
337
|
+
// if invalid format (no days, no months, no years) use the default where the user is able to cycle through
|
338
|
+
// all the views
|
339
|
+
else views = ['years', 'months', 'days'];
|
340
|
+
|
341
|
+
// if the starting view is not amongst the views the user can cycle through, set the correct starting view
|
342
|
+
if ($.inArray(plugin.settings.view, views) == -1) plugin.settings.view = views[views.length - 1];
|
343
|
+
|
344
|
+
// parse the rules for disabling dates and turn them into arrays of arrays
|
345
|
+
|
346
|
+
// array that will hold the rules for enabling/disabling dates
|
347
|
+
disabled_dates = []; enabled_dates = [];
|
348
|
+
|
349
|
+
var dates;
|
350
|
+
|
351
|
+
// it's the same logic for preparing the enabled/disable dates...
|
352
|
+
for (var l = 0; l < 2; l++) {
|
353
|
+
|
354
|
+
// first time we're doing disabled dates,
|
355
|
+
if (l === 0) dates = plugin.settings.disabled_dates;
|
356
|
+
|
357
|
+
// second time we're doing enabled_dates
|
358
|
+
else dates = plugin.settings.enabled_dates;
|
359
|
+
|
360
|
+
// if we have a non-empty array
|
361
|
+
if ($.isArray(dates) && dates.length > 0)
|
362
|
+
|
363
|
+
// iterate through the rules
|
364
|
+
$.each(dates, function() {
|
365
|
+
|
366
|
+
// split the values in rule by white space
|
367
|
+
var rules = this.split(' ');
|
368
|
+
|
369
|
+
// there can be a maximum of 4 rules (days, months, years and, optionally, day of the week)
|
370
|
+
for (var i = 0; i < 4; i++) {
|
371
|
+
|
372
|
+
// if one of the values is not available
|
373
|
+
// replace it with a * (wildcard)
|
374
|
+
if (!rules[i]) rules[i] = '*';
|
375
|
+
|
376
|
+
// if rule contains a comma, create a new array by splitting the rule by commas
|
377
|
+
// if there are no commas create an array containing the rule's string
|
378
|
+
rules[i] = (rules[i].indexOf(',') > -1 ? rules[i].split(',') : new Array(rules[i]));
|
379
|
+
|
380
|
+
// iterate through the items in the rule
|
381
|
+
for (var j = 0; j < rules[i].length; j++)
|
382
|
+
|
383
|
+
// if item contains a dash (defining a range)
|
384
|
+
if (rules[i][j].indexOf('-') > -1) {
|
385
|
+
|
386
|
+
// get the lower and upper limits of the range
|
387
|
+
var limits = rules[i][j].match(/^([0-9]+)\-([0-9]+)/);
|
388
|
+
|
389
|
+
// if range is valid
|
390
|
+
if (null !== limits) {
|
391
|
+
|
392
|
+
// iterate through the range
|
393
|
+
for (var k = to_int(limits[1]); k <= to_int(limits[2]); k++)
|
394
|
+
|
395
|
+
// if value is not already among the values of the rule
|
396
|
+
// add it to the rule
|
397
|
+
if ($.inArray(k, rules[i]) == -1) rules[i].push(k + '');
|
398
|
+
|
399
|
+
// remove the range indicator
|
400
|
+
rules[i].splice(j, 1);
|
401
|
+
|
402
|
+
}
|
403
|
+
|
404
|
+
}
|
405
|
+
|
406
|
+
// iterate through the items in the rule
|
407
|
+
// and make sure that numbers are numbers
|
408
|
+
for (j = 0; j < rules[i].length; j++) rules[i][j] = (isNaN(to_int(rules[i][j])) ? rules[i][j] : to_int(rules[i][j]));
|
409
|
+
|
410
|
+
}
|
411
|
+
|
412
|
+
// add to the correct list of processed rules
|
413
|
+
// first time we're doing disabled dates,
|
414
|
+
if (l === 0) disabled_dates.push(rules);
|
415
|
+
|
416
|
+
// second time we're doing enabled_dates
|
417
|
+
else enabled_dates.push(rules);
|
418
|
+
|
419
|
+
});
|
420
|
+
|
421
|
+
}
|
422
|
+
|
423
|
+
var
|
424
|
+
|
425
|
+
// cache the current system date
|
426
|
+
date = new Date(),
|
427
|
+
|
428
|
+
// when the date picker's starting date depends on the value of another date picker, this value will be
|
429
|
+
// set by the other date picker
|
430
|
+
// this value will be used as base for all calculations (if not set, will be the same as the current
|
431
|
+
// system date)
|
432
|
+
reference_date = (!plugin.settings.reference_date ? ($element.data('zdp_reference_date') && undefined !== $element.data('zdp_reference_date') ? $element.data('zdp_reference_date') : date) : plugin.settings.reference_date),
|
433
|
+
|
434
|
+
tmp_start_date, tmp_end_date;
|
435
|
+
|
436
|
+
// reset these values here as this method might be called more than once during a date picker's lifetime
|
437
|
+
// (when the selectable dates depend on the values from another date picker)
|
438
|
+
start_date = undefined; end_date = undefined;
|
439
|
+
|
440
|
+
// extract the date parts
|
441
|
+
// also, save the current system month/day/year - we'll use them to highlight the current system date
|
442
|
+
first_selectable_month = reference_date.getMonth();
|
443
|
+
current_system_month = date.getMonth();
|
444
|
+
first_selectable_year = reference_date.getFullYear();
|
445
|
+
current_system_year = date.getFullYear();
|
446
|
+
first_selectable_day = reference_date.getDate();
|
447
|
+
current_system_day = date.getDate();
|
448
|
+
|
449
|
+
// check if the calendar has any restrictions
|
450
|
+
|
451
|
+
// calendar is future-only, starting today
|
452
|
+
// it means we have a starting date (the current system date), but no ending date
|
453
|
+
if (plugin.settings.direction === true) start_date = reference_date;
|
454
|
+
|
455
|
+
// calendar is past only, ending today
|
456
|
+
else if (plugin.settings.direction === false) {
|
457
|
+
|
458
|
+
// it means we have an ending date (the reference date), but no starting date
|
459
|
+
end_date = reference_date;
|
460
|
+
|
461
|
+
// extract the date parts
|
462
|
+
last_selectable_month = end_date.getMonth();
|
463
|
+
last_selectable_year = end_date.getFullYear();
|
464
|
+
last_selectable_day = end_date.getDate();
|
465
|
+
|
466
|
+
} else if (
|
467
|
+
|
468
|
+
// if direction is not given as an array and the value is an integer > 0
|
469
|
+
(!$.isArray(plugin.settings.direction) && is_integer(plugin.settings.direction) && to_int(plugin.settings.direction) > 0) ||
|
470
|
+
|
471
|
+
// or direction is given as an array
|
472
|
+
($.isArray(plugin.settings.direction) && (
|
473
|
+
|
474
|
+
// and first entry is a valid date
|
475
|
+
(tmp_start_date = check_date(plugin.settings.direction[0])) ||
|
476
|
+
// or a boolean TRUE
|
477
|
+
plugin.settings.direction[0] === true ||
|
478
|
+
// or an integer > 0
|
479
|
+
(is_integer(plugin.settings.direction[0]) && plugin.settings.direction[0] > 0)
|
480
|
+
|
481
|
+
) && (
|
482
|
+
|
483
|
+
// and second entry is a valid date
|
484
|
+
(tmp_end_date = check_date(plugin.settings.direction[1])) ||
|
485
|
+
// or a boolean FALSE
|
486
|
+
plugin.settings.direction[1] === false ||
|
487
|
+
// or integer >= 0
|
488
|
+
(is_integer(plugin.settings.direction[1]) && plugin.settings.direction[1] >= 0)
|
489
|
+
|
490
|
+
))
|
491
|
+
|
492
|
+
) {
|
493
|
+
|
494
|
+
// if an exact starting date was given, use that as a starting date
|
495
|
+
if (tmp_start_date) start_date = tmp_start_date;
|
496
|
+
|
497
|
+
// otherwise
|
498
|
+
else
|
499
|
+
|
500
|
+
// figure out the starting date
|
501
|
+
// use the Date object to normalize the date
|
502
|
+
// for example, 2011 05 33 will be transformed to 2011 06 02
|
503
|
+
start_date = new Date(
|
504
|
+
first_selectable_year,
|
505
|
+
first_selectable_month,
|
506
|
+
first_selectable_day + (!$.isArray(plugin.settings.direction) ? to_int(plugin.settings.direction) : to_int(plugin.settings.direction[0] === true ? 0 : plugin.settings.direction[0]))
|
507
|
+
);
|
508
|
+
|
509
|
+
// re-extract the date parts
|
510
|
+
first_selectable_month = start_date.getMonth();
|
511
|
+
first_selectable_year = start_date.getFullYear();
|
512
|
+
first_selectable_day = start_date.getDate();
|
513
|
+
|
514
|
+
// if an exact ending date was given and the date is after the starting date, use that as a ending date
|
515
|
+
if (tmp_end_date && +tmp_end_date >= +start_date) end_date = tmp_end_date;
|
516
|
+
|
517
|
+
// if have information about the ending date
|
518
|
+
else if (!tmp_end_date && plugin.settings.direction[1] !== false && $.isArray(plugin.settings.direction))
|
519
|
+
|
520
|
+
// figure out the ending date
|
521
|
+
// use the Date object to normalize the date
|
522
|
+
// for example, 2011 05 33 will be transformed to 2011 06 02
|
523
|
+
end_date = new Date(
|
524
|
+
first_selectable_year,
|
525
|
+
first_selectable_month,
|
526
|
+
first_selectable_day + to_int(plugin.settings.direction[1])
|
527
|
+
);
|
528
|
+
|
529
|
+
// if a valid ending date exists
|
530
|
+
if (end_date) {
|
531
|
+
|
532
|
+
// extract the date parts
|
533
|
+
last_selectable_month = end_date.getMonth();
|
534
|
+
last_selectable_year = end_date.getFullYear();
|
535
|
+
last_selectable_day = end_date.getDate();
|
536
|
+
|
537
|
+
}
|
538
|
+
|
539
|
+
} else if (
|
540
|
+
|
541
|
+
// if direction is not given as an array and the value is an integer < 0
|
542
|
+
(!$.isArray(plugin.settings.direction) && is_integer(plugin.settings.direction) && to_int(plugin.settings.direction) < 0) ||
|
543
|
+
|
544
|
+
// or direction is given as an array
|
545
|
+
($.isArray(plugin.settings.direction) && (
|
546
|
+
|
547
|
+
// and first entry is boolean FALSE
|
548
|
+
plugin.settings.direction[0] === false ||
|
549
|
+
// or an integer < 0
|
550
|
+
(is_integer(plugin.settings.direction[0]) && plugin.settings.direction[0] < 0)
|
551
|
+
|
552
|
+
) && (
|
553
|
+
|
554
|
+
// and second entry is a valid date
|
555
|
+
(tmp_start_date = check_date(plugin.settings.direction[1])) ||
|
556
|
+
// or an integer >= 0
|
557
|
+
(is_integer(plugin.settings.direction[1]) && plugin.settings.direction[1] >= 0)
|
558
|
+
|
559
|
+
))
|
560
|
+
|
561
|
+
) {
|
562
|
+
|
563
|
+
// figure out the ending date
|
564
|
+
// use the Date object to normalize the date
|
565
|
+
// for example, 2011 05 33 will be transformed to 2011 06 02
|
566
|
+
end_date = new Date(
|
567
|
+
first_selectable_year,
|
568
|
+
first_selectable_month,
|
569
|
+
first_selectable_day + (!$.isArray(plugin.settings.direction) ? to_int(plugin.settings.direction) : to_int(plugin.settings.direction[0] === false ? 0 : plugin.settings.direction[0]))
|
570
|
+
);
|
571
|
+
|
572
|
+
// re-extract the date parts
|
573
|
+
last_selectable_month = end_date.getMonth();
|
574
|
+
last_selectable_year = end_date.getFullYear();
|
575
|
+
last_selectable_day = end_date.getDate();
|
576
|
+
|
577
|
+
// if an exact starting date was given, and the date is before the ending date, use that as a starting date
|
578
|
+
if (tmp_start_date && +tmp_start_date < +end_date) start_date = tmp_start_date;
|
579
|
+
|
580
|
+
// if have information about the starting date
|
581
|
+
else if (!tmp_start_date && $.isArray(plugin.settings.direction))
|
582
|
+
|
583
|
+
// figure out the staring date
|
584
|
+
// use the Date object to normalize the date
|
585
|
+
// for example, 2011 05 33 will be transformed to 2011 06 02
|
586
|
+
start_date = new Date(
|
587
|
+
last_selectable_year,
|
588
|
+
last_selectable_month,
|
589
|
+
last_selectable_day - to_int(plugin.settings.direction[1])
|
590
|
+
);
|
591
|
+
|
592
|
+
// if a valid starting date exists
|
593
|
+
if (start_date) {
|
594
|
+
|
595
|
+
// extract the date parts
|
596
|
+
first_selectable_month = start_date.getMonth();
|
597
|
+
first_selectable_year = start_date.getFullYear();
|
598
|
+
first_selectable_day = start_date.getDate();
|
599
|
+
|
600
|
+
}
|
601
|
+
|
602
|
+
// if there are disabled dates
|
603
|
+
} else if ($.isArray(plugin.settings.disabled_dates) && plugin.settings.disabled_dates.length > 0)
|
604
|
+
|
605
|
+
// iterate through the rules for disabling dates
|
606
|
+
for (var interval in disabled_dates)
|
607
|
+
|
608
|
+
// only if there is a rule that disables *everything*
|
609
|
+
if (disabled_dates[interval][0] == '*' && disabled_dates[interval][1] == '*' && disabled_dates[interval][2] == '*' && disabled_dates[interval][3] == '*') {
|
610
|
+
|
611
|
+
var tmpDates = [];
|
612
|
+
|
613
|
+
// iterate through the rules for enabling dates
|
614
|
+
// looking for the minimum/maximum selectable date (if it's the case)
|
615
|
+
$.each(enabled_dates, function() {
|
616
|
+
|
617
|
+
var rule = this;
|
618
|
+
|
619
|
+
// if the rule doesn't apply to all years
|
620
|
+
if (rule[2][0] != '*')
|
621
|
+
|
622
|
+
// format date and store it in our stack
|
623
|
+
tmpDates.push(parseInt(
|
624
|
+
rule[2][0] +
|
625
|
+
(rule[1][0] == '*' ? '12' : str_pad(rule[1][0], 2)) +
|
626
|
+
(rule[0][0] == '*' ? (rule[1][0] == '*' ? '31' : new Date(rule[2][0], rule[1][0], 0).getDate()) : str_pad(rule[0][0], 2)), 10));
|
627
|
+
|
628
|
+
});
|
629
|
+
|
630
|
+
// sort dates ascending
|
631
|
+
tmpDates.sort();
|
632
|
+
|
633
|
+
// if we have any rules
|
634
|
+
if (tmpDates.length > 0) {
|
635
|
+
|
636
|
+
// get date parts
|
637
|
+
var matches = (tmpDates[0] + '').match(/([0-9]{4})([0-9]{2})([0-9]{2})/);
|
638
|
+
|
639
|
+
// assign the date parts to the appropriate variables
|
640
|
+
first_selectable_year = parseInt(matches[1], 10);
|
641
|
+
first_selectable_month = parseInt(matches[2], 10) - 1;
|
642
|
+
first_selectable_day = parseInt(matches[3], 10);
|
643
|
+
|
644
|
+
}
|
645
|
+
|
646
|
+
// don't look further
|
647
|
+
break;
|
648
|
+
|
649
|
+
}
|
650
|
+
|
651
|
+
// if first selectable date exists but is disabled, find the actual first selectable date
|
652
|
+
if (is_disabled(first_selectable_year, first_selectable_month, first_selectable_day)) {
|
653
|
+
|
654
|
+
// loop until we find the first selectable year
|
655
|
+
while (is_disabled(first_selectable_year)) {
|
656
|
+
|
657
|
+
// if calendar is past-only,
|
658
|
+
if (!start_date) {
|
659
|
+
|
660
|
+
// decrement the year
|
661
|
+
first_selectable_year--;
|
662
|
+
|
663
|
+
// because we've changed years, reset the month to December
|
664
|
+
first_selectable_month = 11;
|
665
|
+
|
666
|
+
// otherwise
|
667
|
+
} else {
|
668
|
+
|
669
|
+
// increment the year
|
670
|
+
first_selectable_year++;
|
671
|
+
|
672
|
+
// because we've changed years, reset the month to January
|
673
|
+
first_selectable_month = 0;
|
674
|
+
|
675
|
+
}
|
676
|
+
|
677
|
+
}
|
678
|
+
|
679
|
+
// loop until we find the first selectable month
|
680
|
+
while (is_disabled(first_selectable_year, first_selectable_month)) {
|
681
|
+
|
682
|
+
// if calendar is past-only
|
683
|
+
if (!start_date) {
|
684
|
+
|
685
|
+
// decrement the month
|
686
|
+
first_selectable_month--;
|
687
|
+
|
688
|
+
// because we've changed months, reset the day to the last day of the month
|
689
|
+
first_selectable_day = new Date(first_selectable_year, first_selectable_month + 1, 0).getDate();
|
690
|
+
|
691
|
+
// otherwise
|
692
|
+
} else {
|
693
|
+
|
694
|
+
// increment the month
|
695
|
+
first_selectable_month++;
|
696
|
+
|
697
|
+
// because we've changed months, reset the day to the first day of the month
|
698
|
+
first_selectable_day = 1;
|
699
|
+
|
700
|
+
}
|
701
|
+
|
702
|
+
// if we moved to a following year
|
703
|
+
if (first_selectable_month > 11) {
|
704
|
+
|
705
|
+
// increment the year
|
706
|
+
first_selectable_year++;
|
707
|
+
|
708
|
+
// reset the month to January
|
709
|
+
first_selectable_month = 0;
|
710
|
+
|
711
|
+
// because we've changed months, reset the day to the first day of the month
|
712
|
+
first_selectable_day = 1;
|
713
|
+
|
714
|
+
// if we moved to a previous year
|
715
|
+
} else if (first_selectable_month < 0) {
|
716
|
+
|
717
|
+
// decrement the year
|
718
|
+
first_selectable_year--;
|
719
|
+
|
720
|
+
// reset the month to December
|
721
|
+
first_selectable_month = 11;
|
722
|
+
|
723
|
+
// because we've changed months, reset the day to the last day of the month
|
724
|
+
first_selectable_day = new Date(first_selectable_year, first_selectable_month + 1, 0).getDate();
|
725
|
+
|
726
|
+
}
|
727
|
+
|
728
|
+
}
|
729
|
+
|
730
|
+
// loop until we find the first selectable day
|
731
|
+
while (is_disabled(first_selectable_year, first_selectable_month, first_selectable_day)) {
|
732
|
+
|
733
|
+
// if calendar is past-only, decrement the day
|
734
|
+
if (!start_date) first_selectable_day--;
|
735
|
+
|
736
|
+
// otherwise, increment the day
|
737
|
+
else first_selectable_day++;
|
738
|
+
|
739
|
+
// use the Date object to normalize the date
|
740
|
+
// for example, 2011 05 33 will be transformed to 2011 06 02
|
741
|
+
date = new Date(first_selectable_year, first_selectable_month, first_selectable_day);
|
742
|
+
|
743
|
+
// re-extract date parts from the normalized date
|
744
|
+
// as we use them in the current loop
|
745
|
+
first_selectable_year = date.getFullYear();
|
746
|
+
first_selectable_month = date.getMonth();
|
747
|
+
first_selectable_day = date.getDate();
|
748
|
+
|
749
|
+
}
|
750
|
+
|
751
|
+
// use the Date object to normalize the date
|
752
|
+
// for example, 2011 05 33 will be transformed to 2011 06 02
|
753
|
+
date = new Date(first_selectable_year, first_selectable_month, first_selectable_day);
|
754
|
+
|
755
|
+
// re-extract date parts from the normalized date
|
756
|
+
// as we use them in the current loop
|
757
|
+
first_selectable_year = date.getFullYear();
|
758
|
+
first_selectable_month = date.getMonth();
|
759
|
+
first_selectable_day = date.getDate();
|
760
|
+
|
761
|
+
}
|
762
|
+
|
763
|
+
// get the default date, from the element, and check if it represents a valid date, according to the required format
|
764
|
+
var default_date = check_date($element.val() || (plugin.settings.start_date ? plugin.settings.start_date : ''));
|
765
|
+
|
766
|
+
// if there is a default date but it is disabled
|
767
|
+
if (default_date && is_disabled(default_date.getFullYear(), default_date.getMonth(), default_date.getDate()))
|
768
|
+
|
769
|
+
// clear the value of the parent element
|
770
|
+
$element.val('');
|
771
|
+
|
772
|
+
// updates value for the date picker whose starting date depends on the selected date (if any)
|
773
|
+
update_dependent(default_date);
|
774
|
+
|
775
|
+
// if date picker is not always visible
|
776
|
+
if (!plugin.settings.always_visible) {
|
777
|
+
|
778
|
+
// if we're just creating the date picker
|
779
|
+
if (!update) {
|
780
|
+
|
781
|
+
// if a calendar icon should be added to the element the plugin is attached to, create the icon now
|
782
|
+
if (plugin.settings.show_icon) {
|
783
|
+
|
784
|
+
// strangely, in Firefox 21+ (or maybe even earlier) input elements have their "display" property
|
785
|
+
// set to "inline" instead of "inline-block" as do all the other browsers.
|
786
|
+
// because this behavior brakes the positioning of the icon, we'll set the "display" property to
|
787
|
+
// "inline-block" before anything else;
|
788
|
+
if (browser.name == 'firefox' && $element.is('input[type="text"]') && $element.css('display') == 'inline') $element.css('display', 'inline-block');
|
789
|
+
|
790
|
+
// we create a wrapper for the parent element so that we can later position the icon
|
791
|
+
// also, make sure the wrapper inherits some important css properties of the parent element
|
792
|
+
var icon_wrapper = jQuery('<span class="Zebra_DatePicker_Icon_Wrapper"></span>').css({
|
793
|
+
'display': $element.css('display'),
|
794
|
+
'position': $element.css('position') == 'static' ? 'relative' : $element.css('position'),
|
795
|
+
'float': $element.css('float'),
|
796
|
+
'top': $element.css('top'),
|
797
|
+
'right': $element.css('right'),
|
798
|
+
'bottom': $element.css('bottom'),
|
799
|
+
'left': $element.css('left')
|
800
|
+
});
|
801
|
+
|
802
|
+
// put wrapper around the element
|
803
|
+
// also, make sure we set some important css properties for it
|
804
|
+
$element.wrap(icon_wrapper).css({
|
805
|
+
'position': 'relative',
|
806
|
+
'top': 'auto',
|
807
|
+
'right': 'auto',
|
808
|
+
'bottom': 'auto',
|
809
|
+
'left': 'auto'
|
810
|
+
});
|
811
|
+
|
812
|
+
// create the actual calendar icon (show a disabled icon if the element is disabled)
|
813
|
+
icon = jQuery('<button type="button" class="Zebra_DatePicker_Icon' + ($element.attr('disabled') == 'disabled' ? ' Zebra_DatePicker_Icon_Disabled' : '') + '">Pick a date</button>');
|
814
|
+
|
815
|
+
// a reference to the icon, as a global property
|
816
|
+
plugin.icon = icon;
|
817
|
+
|
818
|
+
// the date picker will open when clicking both the icon and the element the plugin is attached to
|
819
|
+
clickables = icon.add($element);
|
820
|
+
|
821
|
+
// if calendar icon is not visible, the date picker will open when clicking the element
|
822
|
+
} else clickables = $element;
|
823
|
+
|
824
|
+
// attach the click event to the clickable elements (icon and/or element)
|
825
|
+
clickables.bind('click', function(e) {
|
826
|
+
|
827
|
+
e.preventDefault();
|
828
|
+
|
829
|
+
// if element is not disabled
|
830
|
+
if (!$element.attr('disabled'))
|
831
|
+
|
832
|
+
// if the date picker is visible, hide it
|
833
|
+
if (datepicker.css('display') != 'none') plugin.hide();
|
834
|
+
|
835
|
+
// if the date picker is not visible, show it
|
836
|
+
else plugin.show();
|
837
|
+
|
838
|
+
});
|
839
|
+
|
840
|
+
// if icon exists, inject it into the DOM, right after the parent element (and inside the wrapper)
|
841
|
+
if (undefined !== icon) icon.insertAfter($element);
|
842
|
+
|
843
|
+
}
|
844
|
+
|
845
|
+
// if calendar icon exists
|
846
|
+
if (undefined !== icon) {
|
847
|
+
|
848
|
+
// needed when updating: remove any inline style set previously by library,
|
849
|
+
// so we get the right values below
|
850
|
+
icon.attr('style', '');
|
851
|
+
|
852
|
+
// if calendar icon is to be placed *inside* the element
|
853
|
+
// add an extra class to the icon
|
854
|
+
if (plugin.settings.inside) icon.addClass('Zebra_DatePicker_Icon_Inside');
|
855
|
+
|
856
|
+
var
|
857
|
+
|
858
|
+
// get element' width and height (including margins)
|
859
|
+
element_width = $element.outerWidth(),
|
860
|
+
element_height = $element.outerHeight(),
|
861
|
+
element_margin_left = parseInt($element.css('marginLeft'), 10) || 0,
|
862
|
+
element_margin_top = parseInt($element.css('marginTop'), 10) || 0,
|
863
|
+
|
864
|
+
// get icon's width, height and margins
|
865
|
+
icon_width = icon.outerWidth(),
|
866
|
+
icon_height = icon.outerHeight(),
|
867
|
+
icon_margin_left = parseInt(icon.css('marginLeft'), 10) || 0,
|
868
|
+
icon_margin_right = parseInt(icon.css('marginRight'), 10) || 0;
|
869
|
+
|
870
|
+
// if icon is to be placed *inside* the element
|
871
|
+
// position the icon accordingly
|
872
|
+
if (plugin.settings.inside)
|
873
|
+
|
874
|
+
icon.css({
|
875
|
+
'top': element_margin_top + ((element_height - icon_height) / 2),
|
876
|
+
'left': element_margin_left + (element_width - icon_width - icon_margin_right)
|
877
|
+
});
|
878
|
+
|
879
|
+
// if icon is to be placed to the right of the element
|
880
|
+
// position the icon accordingly
|
881
|
+
else
|
882
|
+
|
883
|
+
icon.css({
|
884
|
+
'top': element_margin_top + ((element_height - icon_height) / 2),
|
885
|
+
'left': element_margin_left + element_width + icon_margin_left
|
886
|
+
});
|
887
|
+
|
888
|
+
}
|
889
|
+
|
890
|
+
}
|
891
|
+
|
892
|
+
// if calendar icon exists (there's no icon if the date picker is always visible or it is specifically hidden)
|
893
|
+
if (undefined !== icon)
|
894
|
+
|
895
|
+
// if parent element is not visible (has display: none, width and height are explicitly set to 0, an ancestor
|
896
|
+
// element is hidden, so the element is not shown on the page), hide the icon, or show it otherwise
|
897
|
+
if (!($element.is(':visible'))) icon.hide(); else icon.show();
|
898
|
+
|
899
|
+
// if the "Today" button is to be shown and it makes sense to be shown
|
900
|
+
// (the "days" view is available and "today" is not a disabled date)
|
901
|
+
show_select_today = (plugin.settings.show_select_today !== false && $.inArray('days', views) > -1 && !is_disabled(current_system_year, current_system_month, current_system_day) ? plugin.settings.show_select_today : false);
|
902
|
+
|
903
|
+
// if we just needed to recompute the things above, return now
|
904
|
+
if (update) return;
|
905
|
+
|
906
|
+
// if icon exists, update its position when the page is resized
|
907
|
+
if (icon) $(window).bind('resize', _resize);
|
908
|
+
|
909
|
+
// generate the container that will hold everything
|
910
|
+
var html = '' +
|
911
|
+
'<div class="Zebra_DatePicker">' +
|
912
|
+
'<table class="dp_header">' +
|
913
|
+
'<tr>' +
|
914
|
+
'<td class="dp_previous">«</td>' +
|
915
|
+
'<td class="dp_caption"> </td>' +
|
916
|
+
'<td class="dp_next">»</td>' +
|
917
|
+
'</tr>' +
|
918
|
+
'</table>' +
|
919
|
+
'<table class="dp_daypicker"></table>' +
|
920
|
+
'<table class="dp_monthpicker"></table>' +
|
921
|
+
'<table class="dp_yearpicker"></table>' +
|
922
|
+
'<table class="dp_footer"><tr>' +
|
923
|
+
'<td class="dp_today"' + (plugin.settings.show_clear_date !== false ? ' style="width:50%"' : '') + '>' + show_select_today + '</td>' +
|
924
|
+
'<td class="dp_clear"' + (show_select_today !== false ? ' style="width:50%"' : '') + '>' + plugin.settings.lang_clear_date + '</td>' +
|
925
|
+
'</tr></table>' +
|
926
|
+
'</div>';
|
927
|
+
|
928
|
+
// create a jQuery object out of the HTML above and create a reference to it
|
929
|
+
datepicker = $(html);
|
930
|
+
|
931
|
+
// a reference to the calendar, as a global property
|
932
|
+
plugin.datepicker = datepicker;
|
933
|
+
|
934
|
+
// create references to the different parts of the date picker
|
935
|
+
header = $('table.dp_header', datepicker);
|
936
|
+
daypicker = $('table.dp_daypicker', datepicker);
|
937
|
+
monthpicker = $('table.dp_monthpicker', datepicker);
|
938
|
+
yearpicker = $('table.dp_yearpicker', datepicker);
|
939
|
+
footer = $('table.dp_footer', datepicker);
|
940
|
+
selecttoday = $('td.dp_today', footer);
|
941
|
+
cleardate = $('td.dp_clear', footer);
|
942
|
+
|
943
|
+
// if date picker is not always visible
|
944
|
+
if (!plugin.settings.always_visible)
|
945
|
+
|
946
|
+
// inject the container into the DOM
|
947
|
+
$('body').append(datepicker);
|
948
|
+
|
949
|
+
// otherwise, if element is not disabled
|
950
|
+
else if (!$element.attr('disabled')) {
|
951
|
+
|
952
|
+
// inject the date picker into the designated container element
|
953
|
+
plugin.settings.always_visible.append(datepicker);
|
954
|
+
|
955
|
+
// and make it visible right away
|
956
|
+
plugin.show();
|
957
|
+
|
958
|
+
}
|
959
|
+
|
960
|
+
// add the mouseover/mousevents to all to the date picker's cells
|
961
|
+
// except those that are not selectable
|
962
|
+
datepicker.
|
963
|
+
delegate('td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month, .dp_blocked, .dp_week_number)', 'mouseover', function() {
|
964
|
+
$(this).addClass('dp_hover');
|
965
|
+
}).
|
966
|
+
delegate('td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month, .dp_blocked, .dp_week_number)', 'mouseout', function() {
|
967
|
+
$(this).removeClass('dp_hover');
|
968
|
+
});
|
969
|
+
|
970
|
+
// prevent text highlighting for the text in the header
|
971
|
+
// (for the case when user keeps clicking the "next" and "previous" buttons)
|
972
|
+
disable_text_select($('td', header));
|
973
|
+
|
974
|
+
// event for when clicking the "previous" button
|
975
|
+
$('.dp_previous', header).bind('click', function() {
|
976
|
+
|
977
|
+
// if button is not disabled
|
978
|
+
if (!$(this).hasClass('dp_blocked')) {
|
979
|
+
|
980
|
+
// if view is "months"
|
981
|
+
// decrement year by one
|
982
|
+
if (view == 'months') selected_year--;
|
983
|
+
|
984
|
+
// if view is "years"
|
985
|
+
// decrement years by 12
|
986
|
+
else if (view == 'years') selected_year -= 12;
|
987
|
+
|
988
|
+
// if view is "days"
|
989
|
+
// decrement the month and
|
990
|
+
// if month is out of range
|
991
|
+
else if (--selected_month < 0) {
|
992
|
+
|
993
|
+
// go to the last month of the previous year
|
994
|
+
selected_month = 11;
|
995
|
+
selected_year--;
|
996
|
+
|
997
|
+
}
|
998
|
+
|
999
|
+
// generate the appropriate view
|
1000
|
+
manage_views();
|
1001
|
+
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
});
|
1005
|
+
|
1006
|
+
// attach a click event to the caption in header
|
1007
|
+
$('.dp_caption', header).bind('click', function() {
|
1008
|
+
|
1009
|
+
// if current view is "days", take the user to the next view, depending on the format
|
1010
|
+
if (view == 'days') view = ($.inArray('months', views) > -1 ? 'months' : ($.inArray('years', views) > -1 ? 'years' : 'days'));
|
1011
|
+
|
1012
|
+
// if current view is "months", take the user to the next view, depending on the format
|
1013
|
+
else if (view == 'months') view = ($.inArray('years', views) > -1 ? 'years' : ($.inArray('days', views) > -1 ? 'days' : 'months'));
|
1014
|
+
|
1015
|
+
// if current view is "years", take the user to the next view, depending on the format
|
1016
|
+
else view = ($.inArray('days', views) > -1 ? 'days' : ($.inArray('months', views) > -1 ? 'months' : 'years'));
|
1017
|
+
|
1018
|
+
// generate the appropriate view
|
1019
|
+
manage_views();
|
1020
|
+
|
1021
|
+
});
|
1022
|
+
|
1023
|
+
// event for when clicking the "next" button
|
1024
|
+
$('.dp_next', header).bind('click', function() {
|
1025
|
+
|
1026
|
+
// if button is not disabled
|
1027
|
+
if (!$(this).hasClass('dp_blocked')) {
|
1028
|
+
|
1029
|
+
// if view is "months"
|
1030
|
+
// increment year by 1
|
1031
|
+
if (view == 'months') selected_year++;
|
1032
|
+
|
1033
|
+
// if view is "years"
|
1034
|
+
// increment years by 12
|
1035
|
+
else if (view == 'years') selected_year += 12;
|
1036
|
+
|
1037
|
+
// if view is "days"
|
1038
|
+
// increment the month and
|
1039
|
+
// if month is out of range
|
1040
|
+
else if (++selected_month == 12) {
|
1041
|
+
|
1042
|
+
// go to the first month of the next year
|
1043
|
+
selected_month = 0;
|
1044
|
+
selected_year++;
|
1045
|
+
|
1046
|
+
}
|
1047
|
+
|
1048
|
+
// generate the appropriate view
|
1049
|
+
manage_views();
|
1050
|
+
|
1051
|
+
}
|
1052
|
+
|
1053
|
+
});
|
1054
|
+
|
1055
|
+
// attach a click event for the cells in the day picker
|
1056
|
+
daypicker.delegate('td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month, .dp_week_number)', 'click', function() {
|
1057
|
+
|
1058
|
+
// if other months are selectable and currently clicked cell contains a class with the cell's date
|
1059
|
+
if (plugin.settings.select_other_months && null !== (matches = $(this).attr('class').match(/date\_([0-9]{4})(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])/)))
|
1060
|
+
|
1061
|
+
// use the stored date
|
1062
|
+
select_date(matches[1], matches[2], matches[3], 'days', $(this));
|
1063
|
+
|
1064
|
+
// put selected date in the element the plugin is attached to, and hide the date picker
|
1065
|
+
else select_date(selected_year, selected_month, to_int($(this).html()), 'days', $(this));
|
1066
|
+
|
1067
|
+
});
|
1068
|
+
|
1069
|
+
// attach a click event for the cells in the month picker
|
1070
|
+
monthpicker.delegate('td:not(.dp_disabled)', 'click', function() {
|
1071
|
+
|
1072
|
+
// get the month we've clicked on
|
1073
|
+
var matches = $(this).attr('class').match(/dp\_month\_([0-9]+)/);
|
1074
|
+
|
1075
|
+
// set the selected month
|
1076
|
+
selected_month = to_int(matches[1]);
|
1077
|
+
|
1078
|
+
// if user can select only years and months
|
1079
|
+
if ($.inArray('days', views) == -1)
|
1080
|
+
|
1081
|
+
// put selected date in the element the plugin is attached to, and hide the date picker
|
1082
|
+
select_date(selected_year, selected_month, 1, 'months', $(this));
|
1083
|
+
|
1084
|
+
else {
|
1085
|
+
|
1086
|
+
// direct the user to the "days" view
|
1087
|
+
view = 'days';
|
1088
|
+
|
1089
|
+
// if date picker is always visible
|
1090
|
+
// empty the value in the text box the date picker is attached to
|
1091
|
+
if (plugin.settings.always_visible) $element.val('');
|
1092
|
+
|
1093
|
+
// generate the appropriate view
|
1094
|
+
manage_views();
|
1095
|
+
|
1096
|
+
}
|
1097
|
+
|
1098
|
+
});
|
1099
|
+
|
1100
|
+
// attach a click event for the cells in the year picker
|
1101
|
+
yearpicker.delegate('td:not(.dp_disabled)', 'click', function() {
|
1102
|
+
|
1103
|
+
// set the selected year
|
1104
|
+
selected_year = to_int($(this).html());
|
1105
|
+
|
1106
|
+
// if user can select only years
|
1107
|
+
if ($.inArray('months', views) == -1)
|
1108
|
+
|
1109
|
+
// put selected date in the element the plugin is attached to, and hide the date picker
|
1110
|
+
select_date(selected_year, 1, 1, 'years', $(this));
|
1111
|
+
|
1112
|
+
else {
|
1113
|
+
|
1114
|
+
// direct the user to the "months" view
|
1115
|
+
view = 'months';
|
1116
|
+
|
1117
|
+
// if date picker is always visible
|
1118
|
+
// empty the value in the text box the date picker is attached to
|
1119
|
+
if (plugin.settings.always_visible) $element.val('');
|
1120
|
+
|
1121
|
+
// generate the appropriate view
|
1122
|
+
manage_views();
|
1123
|
+
|
1124
|
+
}
|
1125
|
+
|
1126
|
+
});
|
1127
|
+
|
1128
|
+
// function to execute when the "Today" button is clicked
|
1129
|
+
$(selecttoday).bind('click', function(e) {
|
1130
|
+
|
1131
|
+
e.preventDefault();
|
1132
|
+
|
1133
|
+
// select the current date
|
1134
|
+
select_date(current_system_year, current_system_month, current_system_day, 'days', $('.dp_current', daypicker));
|
1135
|
+
|
1136
|
+
// if date picker is always visible
|
1137
|
+
if (plugin.settings.always_visible)
|
1138
|
+
|
1139
|
+
// repaint the datepicker so it centers on the currently selected date
|
1140
|
+
plugin.show();
|
1141
|
+
|
1142
|
+
// hide the date picker
|
1143
|
+
plugin.hide();
|
1144
|
+
|
1145
|
+
});
|
1146
|
+
|
1147
|
+
// function to execute when the "Clear" button is clicked
|
1148
|
+
$(cleardate).bind('click', function(e) {
|
1149
|
+
|
1150
|
+
e.preventDefault();
|
1151
|
+
|
1152
|
+
// clear the element's value
|
1153
|
+
$element.val('');
|
1154
|
+
|
1155
|
+
// if date picker is not always visible
|
1156
|
+
if (!plugin.settings.always_visible) {
|
1157
|
+
|
1158
|
+
// reset these values
|
1159
|
+
default_day = null; default_month = null; default_year = null; selected_month = null; selected_year = null;
|
1160
|
+
|
1161
|
+
}
|
1162
|
+
|
1163
|
+
// hide the date picker
|
1164
|
+
plugin.hide();
|
1165
|
+
|
1166
|
+
// if a callback function exists for when clearing a date
|
1167
|
+
if (plugin.settings.onClear && typeof plugin.settings.onClear == 'function')
|
1168
|
+
|
1169
|
+
// execute the callback function and pass as argument the element the plugin is attached to
|
1170
|
+
plugin.settings.onClear($element);
|
1171
|
+
|
1172
|
+
});
|
1173
|
+
|
1174
|
+
// if date picker is not always visible
|
1175
|
+
if (!plugin.settings.always_visible)
|
1176
|
+
|
1177
|
+
// bind some events to the document
|
1178
|
+
$(document).bind({
|
1179
|
+
|
1180
|
+
//whenever anything is clicked on the page or a key is pressed
|
1181
|
+
'mousedown': _mousedown,
|
1182
|
+
'keyup': _keyup
|
1183
|
+
|
1184
|
+
});
|
1185
|
+
|
1186
|
+
// last thing is to pre-render some of the date picker right away
|
1187
|
+
manage_views();
|
1188
|
+
|
1189
|
+
};
|
1190
|
+
|
1191
|
+
/**
|
1192
|
+
* Destroys the date picker.
|
1193
|
+
*
|
1194
|
+
* @return void
|
1195
|
+
*/
|
1196
|
+
plugin.destroy = function() {
|
1197
|
+
|
1198
|
+
// remove the attached icon (if it exists)...
|
1199
|
+
if (undefined !== plugin.icon) plugin.icon.remove();
|
1200
|
+
|
1201
|
+
// ...and the calendar
|
1202
|
+
plugin.datepicker.remove();
|
1203
|
+
|
1204
|
+
// remove associated event handlers from the document
|
1205
|
+
$(document).unbind('keyup', _keyup);
|
1206
|
+
$(document).unbind('mousedown', _mousedown);
|
1207
|
+
$(window).unbind('resize', _resize);
|
1208
|
+
|
1209
|
+
// remove association with the element
|
1210
|
+
$element.removeData('Zebra_DatePicker');
|
1211
|
+
|
1212
|
+
// completely delete object
|
1213
|
+
delete plugin;
|
1214
|
+
|
1215
|
+
};
|
1216
|
+
|
1217
|
+
/**
|
1218
|
+
* Hides the date picker.
|
1219
|
+
*
|
1220
|
+
* @return void
|
1221
|
+
*/
|
1222
|
+
plugin.hide = function() {
|
1223
|
+
|
1224
|
+
// if date picker is not always visible
|
1225
|
+
if (!plugin.settings.always_visible) {
|
1226
|
+
|
1227
|
+
// hide the iFrameShim in Internet Explorer 6
|
1228
|
+
iframeShim('hide');
|
1229
|
+
|
1230
|
+
// hide the date picker
|
1231
|
+
datepicker.hide();
|
1232
|
+
|
1233
|
+
}
|
1234
|
+
|
1235
|
+
};
|
1236
|
+
|
1237
|
+
/**
|
1238
|
+
* Shows the date picker.
|
1239
|
+
*
|
1240
|
+
* @return void
|
1241
|
+
*/
|
1242
|
+
plugin.show = function() {
|
1243
|
+
|
1244
|
+
// always show the view defined in settings
|
1245
|
+
view = plugin.settings.view;
|
1246
|
+
|
1247
|
+
// get the default date, from the element, and check if it represents a valid date, according to the required format
|
1248
|
+
var default_date = check_date($element.val() || (plugin.settings.start_date ? plugin.settings.start_date : ''));
|
1249
|
+
|
1250
|
+
// if the value represents a valid date
|
1251
|
+
if (default_date) {
|
1252
|
+
|
1253
|
+
// extract the date parts
|
1254
|
+
// we'll use these to highlight the default date in the date picker and as starting point to
|
1255
|
+
// what year and month to start the date picker with
|
1256
|
+
// why separate values? because selected_* will change as user navigates within the date picker
|
1257
|
+
default_month = default_date.getMonth();
|
1258
|
+
selected_month = default_date.getMonth();
|
1259
|
+
default_year = default_date.getFullYear();
|
1260
|
+
selected_year = default_date.getFullYear();
|
1261
|
+
default_day = default_date.getDate();
|
1262
|
+
|
1263
|
+
// if the default date represents a disabled date
|
1264
|
+
if (is_disabled(default_year, default_month, default_day)) {
|
1265
|
+
|
1266
|
+
// clear the value of the parent element
|
1267
|
+
$element.val('');
|
1268
|
+
|
1269
|
+
// the calendar will start with the first selectable year/month
|
1270
|
+
selected_month = first_selectable_month;
|
1271
|
+
selected_year = first_selectable_year;
|
1272
|
+
|
1273
|
+
}
|
1274
|
+
|
1275
|
+
// if a default value is not available, or value does not represent a valid date
|
1276
|
+
} else {
|
1277
|
+
|
1278
|
+
// the calendar will start with the first selectable year/month
|
1279
|
+
selected_month = first_selectable_month;
|
1280
|
+
selected_year = first_selectable_year;
|
1281
|
+
|
1282
|
+
}
|
1283
|
+
|
1284
|
+
// generate the appropriate view
|
1285
|
+
manage_views();
|
1286
|
+
|
1287
|
+
// if date picker is not always visible and the calendar icon is visible
|
1288
|
+
if (!plugin.settings.always_visible) {
|
1289
|
+
|
1290
|
+
var
|
1291
|
+
|
1292
|
+
// get the date picker width and height
|
1293
|
+
datepicker_width = datepicker.outerWidth(),
|
1294
|
+
datepicker_height = datepicker.outerHeight(),
|
1295
|
+
|
1296
|
+
// compute the date picker's default left and top
|
1297
|
+
// this will be computed relative to the icon's top-right corner (if the calendar icon exists), or
|
1298
|
+
// relative to the element's top-right corner otherwise, to which the offsets given at initialization
|
1299
|
+
// are added/subtracted
|
1300
|
+
left = (undefined !== icon ? icon.offset().left + icon.outerWidth(true) : $element.offset().left + $element.outerWidth(true)) + plugin.settings.offset[0],
|
1301
|
+
top = (undefined !== icon ? icon.offset().top : $element.offset().top) - datepicker_height + plugin.settings.offset[1],
|
1302
|
+
|
1303
|
+
// get browser window's width and height
|
1304
|
+
window_width = $(window).width(),
|
1305
|
+
window_height = $(window).height(),
|
1306
|
+
|
1307
|
+
// get browser window's horizontal and vertical scroll offsets
|
1308
|
+
window_scroll_top = $(window).scrollTop(),
|
1309
|
+
window_scroll_left = $(window).scrollLeft();
|
1310
|
+
|
1311
|
+
// if date picker is outside the viewport, adjust its position so that it is visible
|
1312
|
+
if (left + datepicker_width > window_scroll_left + window_width) left = window_scroll_left + window_width - datepicker_width;
|
1313
|
+
if (left < window_scroll_left) left = window_scroll_left;
|
1314
|
+
if (top + datepicker_height > window_scroll_top + window_height) top = window_scroll_top + window_height - datepicker_height;
|
1315
|
+
if (top < window_scroll_top) top = window_scroll_top;
|
1316
|
+
|
1317
|
+
// make the date picker visible
|
1318
|
+
datepicker.css({
|
1319
|
+
'left': left,
|
1320
|
+
'top': top
|
1321
|
+
});
|
1322
|
+
|
1323
|
+
// fade-in the date picker
|
1324
|
+
// for Internet Explorer < 9 show the date picker instantly or fading alters the font's weight
|
1325
|
+
datepicker.fadeIn(browser.name == 'explorer' && browser.version < 9 ? 0 : 150, 'linear');
|
1326
|
+
|
1327
|
+
// show the iFrameShim in Internet Explorer 6
|
1328
|
+
iframeShim();
|
1329
|
+
|
1330
|
+
// if date picker is always visible, show it
|
1331
|
+
} else datepicker.show();
|
1332
|
+
|
1333
|
+
};
|
1334
|
+
|
1335
|
+
/**
|
1336
|
+
* Updates the configuration options given as argument
|
1337
|
+
*
|
1338
|
+
* @param object values An object containing any number of configuration options to be updated
|
1339
|
+
*
|
1340
|
+
* @return void
|
1341
|
+
*/
|
1342
|
+
plugin.update = function(values) {
|
1343
|
+
|
1344
|
+
// if original direction not saved, save it now
|
1345
|
+
if (plugin.original_direction) plugin.original_direction = plugin.direction;
|
1346
|
+
|
1347
|
+
// update configuration options
|
1348
|
+
plugin.settings = $.extend(plugin.settings, values);
|
1349
|
+
|
1350
|
+
// reinitialize the object with the new options
|
1351
|
+
init(true);
|
1352
|
+
|
1353
|
+
};
|
1354
|
+
|
1355
|
+
/**
|
1356
|
+
* Checks if a string represents a valid date according to the format defined by the "format" property.
|
1357
|
+
*
|
1358
|
+
* @param string str_date A string representing a date, formatted accordingly to the "format" property.
|
1359
|
+
* For example, if "format" is "Y-m-d" the string should look like "2011-06-01"
|
1360
|
+
*
|
1361
|
+
* @return mixed Returns a JavaScript Date object if string represents a valid date according
|
1362
|
+
* formatted according to the "format" property, or FALSE otherwise.
|
1363
|
+
*
|
1364
|
+
* @access private
|
1365
|
+
*/
|
1366
|
+
var check_date = function(str_date) {
|
1367
|
+
|
1368
|
+
// treat argument as a string
|
1369
|
+
str_date += '';
|
1370
|
+
|
1371
|
+
// if value is given
|
1372
|
+
if ($.trim(str_date) !== '') {
|
1373
|
+
|
1374
|
+
var
|
1375
|
+
|
1376
|
+
// prepare the format by removing white space from it
|
1377
|
+
// and also escape characters that could have special meaning in a regular expression
|
1378
|
+
format = escape_regexp(plugin.settings.format),
|
1379
|
+
|
1380
|
+
// allowed characters in date's format
|
1381
|
+
format_chars = ['d','D','j','l','N','S','w','F','m','M','n','Y','y'],
|
1382
|
+
|
1383
|
+
// "matches" will contain the characters defining the date's format
|
1384
|
+
matches = [],
|
1385
|
+
|
1386
|
+
// "regexp" will contain the regular expression built for each of the characters used in the date's format
|
1387
|
+
regexp = [],
|
1388
|
+
|
1389
|
+
// "position" will contain the position of the caracter found in the date's format
|
1390
|
+
position = null,
|
1391
|
+
|
1392
|
+
// "segments" will contain the matches of the regular expression
|
1393
|
+
segments = null;
|
1394
|
+
|
1395
|
+
// iterate through the allowed characters in date's format
|
1396
|
+
for (var i = 0; i < format_chars.length; i++)
|
1397
|
+
|
1398
|
+
// if character is found in the date's format
|
1399
|
+
if ((position = format.indexOf(format_chars[i])) > -1)
|
1400
|
+
|
1401
|
+
// save it, alongside the character's position
|
1402
|
+
matches.push({character: format_chars[i], position: position});
|
1403
|
+
|
1404
|
+
// sort characters defining the date's format based on their position, ascending
|
1405
|
+
matches.sort(function(a, b){ return a.position - b.position; });
|
1406
|
+
|
1407
|
+
// iterate through the characters defining the date's format
|
1408
|
+
$.each(matches, function(index, match) {
|
1409
|
+
|
1410
|
+
// add to the array of regular expressions, based on the character
|
1411
|
+
switch (match.character) {
|
1412
|
+
|
1413
|
+
case 'd': regexp.push('0[1-9]|[12][0-9]|3[01]'); break;
|
1414
|
+
case 'D': regexp.push('[a-z]{3}'); break;
|
1415
|
+
case 'j': regexp.push('[1-9]|[12][0-9]|3[01]'); break;
|
1416
|
+
case 'l': regexp.push('[a-z]+'); break;
|
1417
|
+
case 'N': regexp.push('[1-7]'); break;
|
1418
|
+
case 'S': regexp.push('st|nd|rd|th'); break;
|
1419
|
+
case 'w': regexp.push('[0-6]'); break;
|
1420
|
+
case 'F': regexp.push('[a-z]+'); break;
|
1421
|
+
case 'm': regexp.push('0[1-9]|1[012]+'); break;
|
1422
|
+
case 'M': regexp.push('[a-z]{3}'); break;
|
1423
|
+
case 'n': regexp.push('[1-9]|1[012]'); break;
|
1424
|
+
case 'Y': regexp.push('[0-9]{4}'); break;
|
1425
|
+
case 'y': regexp.push('[0-9]{2}'); break;
|
1426
|
+
|
1427
|
+
}
|
1428
|
+
|
1429
|
+
});
|
1430
|
+
|
1431
|
+
// if we have an array of regular expressions
|
1432
|
+
if (regexp.length) {
|
1433
|
+
|
1434
|
+
// we will replace characters in the date's format in reversed order
|
1435
|
+
matches.reverse();
|
1436
|
+
|
1437
|
+
// iterate through the characters in date's format
|
1438
|
+
$.each(matches, function(index, match) {
|
1439
|
+
|
1440
|
+
// replace each character with the appropriate regular expression
|
1441
|
+
format = format.replace(match.character, '(' + regexp[regexp.length - index - 1] + ')');
|
1442
|
+
|
1443
|
+
});
|
1444
|
+
|
1445
|
+
// the final regular expression
|
1446
|
+
regexp = new RegExp('^' + format + '$', 'ig');
|
1447
|
+
|
1448
|
+
// if regular expression was matched
|
1449
|
+
if ((segments = regexp.exec(str_date))) {
|
1450
|
+
|
1451
|
+
// check if date is a valid date (i.e. there's no February 31)
|
1452
|
+
|
1453
|
+
var tmpdate = new Date(),
|
1454
|
+
original_day = tmpdate.getDate(),
|
1455
|
+
original_month = tmpdate.getMonth() + 1,
|
1456
|
+
original_year = tmpdate.getFullYear(),
|
1457
|
+
english_days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
|
1458
|
+
english_months = ['January','February','March','April','May','June','July','August','September','October','November','December'],
|
1459
|
+
iterable,
|
1460
|
+
|
1461
|
+
// by default, we assume the date is valid
|
1462
|
+
valid = true;
|
1463
|
+
|
1464
|
+
// reverse back the characters in the date's format
|
1465
|
+
matches.reverse();
|
1466
|
+
|
1467
|
+
// iterate through the characters in the date's format
|
1468
|
+
$.each(matches, function(index, match) {
|
1469
|
+
|
1470
|
+
// if the date is not valid, don't look further
|
1471
|
+
if (!valid) return true;
|
1472
|
+
|
1473
|
+
// based on the character
|
1474
|
+
switch (match.character) {
|
1475
|
+
|
1476
|
+
case 'm':
|
1477
|
+
case 'n':
|
1478
|
+
|
1479
|
+
// extract the month from the value entered by the user
|
1480
|
+
original_month = to_int(segments[index + 1]);
|
1481
|
+
|
1482
|
+
break;
|
1483
|
+
|
1484
|
+
case 'd':
|
1485
|
+
case 'j':
|
1486
|
+
|
1487
|
+
// extract the day from the value entered by the user
|
1488
|
+
original_day = to_int(segments[index + 1]);
|
1489
|
+
|
1490
|
+
break;
|
1491
|
+
|
1492
|
+
case 'D':
|
1493
|
+
case 'l':
|
1494
|
+
case 'F':
|
1495
|
+
case 'M':
|
1496
|
+
|
1497
|
+
// if day is given as day name, we'll check against the names in the used language
|
1498
|
+
if (match.character == 'D' || match.character == 'l') iterable = plugin.settings.days;
|
1499
|
+
|
1500
|
+
// if month is given as month name, we'll check against the names in the used language
|
1501
|
+
else iterable = plugin.settings.months;
|
1502
|
+
|
1503
|
+
// by default, we assume the day or month was not entered correctly
|
1504
|
+
valid = false;
|
1505
|
+
|
1506
|
+
// iterate through the month/days in the used language
|
1507
|
+
$.each(iterable, function(key, value) {
|
1508
|
+
|
1509
|
+
// if month/day was entered correctly, don't look further
|
1510
|
+
if (valid) return true;
|
1511
|
+
|
1512
|
+
// if month/day was entered correctly
|
1513
|
+
if (segments[index + 1].toLowerCase() == value.substring(0, (match.character == 'D' || match.character == 'M' ? 3 : value.length)).toLowerCase()) {
|
1514
|
+
|
1515
|
+
// extract the day/month from the value entered by the user
|
1516
|
+
switch (match.character) {
|
1517
|
+
|
1518
|
+
case 'D': segments[index + 1] = english_days[key].substring(0, 3); break;
|
1519
|
+
case 'l': segments[index + 1] = english_days[key]; break;
|
1520
|
+
case 'F': segments[index + 1] = english_months[key]; original_month = key + 1; break;
|
1521
|
+
case 'M': segments[index + 1] = english_months[key].substring(0, 3); original_month = key + 1; break;
|
1522
|
+
|
1523
|
+
}
|
1524
|
+
|
1525
|
+
// day/month value is valid
|
1526
|
+
valid = true;
|
1527
|
+
|
1528
|
+
}
|
1529
|
+
|
1530
|
+
});
|
1531
|
+
|
1532
|
+
break;
|
1533
|
+
|
1534
|
+
case 'Y':
|
1535
|
+
|
1536
|
+
// extract the year from the value entered by the user
|
1537
|
+
original_year = to_int(segments[index + 1]);
|
1538
|
+
|
1539
|
+
break;
|
1540
|
+
|
1541
|
+
case 'y':
|
1542
|
+
|
1543
|
+
// extract the year from the value entered by the user
|
1544
|
+
original_year = '19' + to_int(segments[index + 1]);
|
1545
|
+
|
1546
|
+
break;
|
1547
|
+
|
1548
|
+
}
|
1549
|
+
});
|
1550
|
+
|
1551
|
+
// if everything is ok so far
|
1552
|
+
if (valid) {
|
1553
|
+
|
1554
|
+
// generate a Date object using the values entered by the user
|
1555
|
+
// (handle also the case when original_month and/or original_day are undefined - i.e date format is "Y-m" or "Y")
|
1556
|
+
var date = new Date(original_year, (original_month || 1) - 1, original_day || 1);
|
1557
|
+
|
1558
|
+
// if, after that, the date is the same as the date entered by the user
|
1559
|
+
if (date.getFullYear() == original_year && date.getDate() == (original_day || 1) && date.getMonth() == ((original_month || 1) - 1))
|
1560
|
+
|
1561
|
+
// return the date as JavaScript date object
|
1562
|
+
return date;
|
1563
|
+
|
1564
|
+
}
|
1565
|
+
|
1566
|
+
}
|
1567
|
+
|
1568
|
+
}
|
1569
|
+
|
1570
|
+
// if script gets this far, return false as something must've went wrong
|
1571
|
+
return false;
|
1572
|
+
|
1573
|
+
}
|
1574
|
+
|
1575
|
+
};
|
1576
|
+
|
1577
|
+
/**
|
1578
|
+
* Prevents the possibility of selecting text on a given element. Used on the "previous" and "next" buttons
|
1579
|
+
* where text might get accidentally selected when user quickly clicks on the buttons.
|
1580
|
+
*
|
1581
|
+
* Code by http://chris-barr.com/index.php/entry/disable_text_selection_with_jquery/
|
1582
|
+
*
|
1583
|
+
* @param jQuery Element el A jQuery element on which to prevents text selection.
|
1584
|
+
*
|
1585
|
+
* @return void
|
1586
|
+
*
|
1587
|
+
* @access private
|
1588
|
+
*/
|
1589
|
+
var disable_text_select = function(el) {
|
1590
|
+
|
1591
|
+
// if browser is Firefox
|
1592
|
+
if (browser.name == 'firefox') el.css('MozUserSelect', 'none');
|
1593
|
+
|
1594
|
+
// if browser is Internet Explorer
|
1595
|
+
else if (browser.name == 'explorer') el.bind('selectstart', function() { return false; });
|
1596
|
+
|
1597
|
+
// for the other browsers
|
1598
|
+
else el.mousedown(function() { return false; });
|
1599
|
+
|
1600
|
+
};
|
1601
|
+
|
1602
|
+
/**
|
1603
|
+
* Escapes special characters in a string, preparing it for use in a regular expression.
|
1604
|
+
*
|
1605
|
+
* @param string str The string in which special characters should be escaped.
|
1606
|
+
*
|
1607
|
+
* @return string Returns the string with escaped special characters.
|
1608
|
+
*
|
1609
|
+
* @access private
|
1610
|
+
*/
|
1611
|
+
var escape_regexp = function(str) {
|
1612
|
+
|
1613
|
+
// return string with special characters escaped
|
1614
|
+
return str.replace(/([-.,*+?^${}()|[\]\/\\])/g, '\\$1');
|
1615
|
+
|
1616
|
+
};
|
1617
|
+
|
1618
|
+
/**
|
1619
|
+
* Formats a JavaScript date object to the format specified by the "format" property.
|
1620
|
+
* Code taken from http://electricprism.com/aeron/calendar/
|
1621
|
+
*
|
1622
|
+
* @param date date A valid JavaScript date object
|
1623
|
+
*
|
1624
|
+
* @return string Returns a string containing the formatted date
|
1625
|
+
*
|
1626
|
+
* @access private
|
1627
|
+
*/
|
1628
|
+
var format = function(date) {
|
1629
|
+
|
1630
|
+
var result = '',
|
1631
|
+
|
1632
|
+
// extract parts of the date:
|
1633
|
+
// day number, 1 - 31
|
1634
|
+
j = date.getDate(),
|
1635
|
+
|
1636
|
+
// day of the week, 0 - 6, Sunday - Saturday
|
1637
|
+
w = date.getDay(),
|
1638
|
+
|
1639
|
+
// the name of the day of the week Sunday - Saturday
|
1640
|
+
l = plugin.settings.days[w],
|
1641
|
+
|
1642
|
+
// the month number, 1 - 12
|
1643
|
+
n = date.getMonth() + 1,
|
1644
|
+
|
1645
|
+
// the month name, January - December
|
1646
|
+
f = plugin.settings.months[n - 1],
|
1647
|
+
|
1648
|
+
// the year (as a string)
|
1649
|
+
y = date.getFullYear() + '';
|
1650
|
+
|
1651
|
+
// iterate through the characters in the format
|
1652
|
+
for (var i = 0; i < plugin.settings.format.length; i++) {
|
1653
|
+
|
1654
|
+
// extract the current character
|
1655
|
+
var chr = plugin.settings.format.charAt(i);
|
1656
|
+
|
1657
|
+
// see what character it is
|
1658
|
+
switch(chr) {
|
1659
|
+
|
1660
|
+
// year as two digits
|
1661
|
+
case 'y': y = y.substr(2);
|
1662
|
+
|
1663
|
+
// year as four digits
|
1664
|
+
case 'Y': result += y; break;
|
1665
|
+
|
1666
|
+
// month number, prefixed with 0
|
1667
|
+
case 'm': n = str_pad(n, 2);
|
1668
|
+
|
1669
|
+
// month number, not prefixed with 0
|
1670
|
+
case 'n': result += n; break;
|
1671
|
+
|
1672
|
+
// month name, three letters
|
1673
|
+
case 'M': f = ($.isArray(plugin.settings.months_abbr) && undefined !== plugin.settings.months_abbr[n - 1] ? plugin.settings.months_abbr[n - 1] : plugin.settings.months[n - 1].substr(0, 3));
|
1674
|
+
|
1675
|
+
// full month name
|
1676
|
+
case 'F': result += f; break;
|
1677
|
+
|
1678
|
+
// day number, prefixed with 0
|
1679
|
+
case 'd': j = str_pad(j, 2);
|
1680
|
+
|
1681
|
+
// day number not prefixed with 0
|
1682
|
+
case 'j': result += j; break;
|
1683
|
+
|
1684
|
+
// day name, three letters
|
1685
|
+
case 'D': l = ($.isArray(plugin.settings.days_abbr) && undefined !== plugin.settings.days_abbr[w] ? plugin.settings.days_abbr[w] : plugin.settings.days[w].substr(0, 3));
|
1686
|
+
|
1687
|
+
// full day name
|
1688
|
+
case 'l': result += l; break;
|
1689
|
+
|
1690
|
+
// ISO-8601 numeric representation of the day of the week, 1 - 7
|
1691
|
+
case 'N': w++;
|
1692
|
+
|
1693
|
+
// day of the week, 0 - 6
|
1694
|
+
case 'w': result += w; break;
|
1695
|
+
|
1696
|
+
// English ordinal suffix for the day of the month, 2 characters
|
1697
|
+
// (st, nd, rd or th (works well with j))
|
1698
|
+
case 'S':
|
1699
|
+
|
1700
|
+
if (j % 10 == 1 && j != '11') result += 'st';
|
1701
|
+
|
1702
|
+
else if (j % 10 == 2 && j != '12') result += 'nd';
|
1703
|
+
|
1704
|
+
else if (j % 10 == 3 && j != '13') result += 'rd';
|
1705
|
+
|
1706
|
+
else result += 'th';
|
1707
|
+
|
1708
|
+
break;
|
1709
|
+
|
1710
|
+
// this is probably the separator
|
1711
|
+
default: result += chr;
|
1712
|
+
|
1713
|
+
}
|
1714
|
+
|
1715
|
+
}
|
1716
|
+
|
1717
|
+
// return formated date
|
1718
|
+
return result;
|
1719
|
+
|
1720
|
+
};
|
1721
|
+
|
1722
|
+
/**
|
1723
|
+
* Generates the day picker view, and displays it
|
1724
|
+
*
|
1725
|
+
* @return void
|
1726
|
+
*
|
1727
|
+
* @access private
|
1728
|
+
*/
|
1729
|
+
var generate_daypicker = function() {
|
1730
|
+
|
1731
|
+
var
|
1732
|
+
|
1733
|
+
// get the number of days in the selected month
|
1734
|
+
days_in_month = new Date(selected_year, selected_month + 1, 0).getDate(),
|
1735
|
+
|
1736
|
+
// get the selected month's starting day (from 0 to 6)
|
1737
|
+
first_day = new Date(selected_year, selected_month, 1).getDay(),
|
1738
|
+
|
1739
|
+
// how many days are there in the previous month
|
1740
|
+
days_in_previous_month = new Date(selected_year, selected_month, 0).getDate(),
|
1741
|
+
|
1742
|
+
// how many days are there to be shown from the previous month
|
1743
|
+
days_from_previous_month = first_day - plugin.settings.first_day_of_week;
|
1744
|
+
|
1745
|
+
// the final value of how many days are there to be shown from the previous month
|
1746
|
+
days_from_previous_month = days_from_previous_month < 0 ? 7 + days_from_previous_month : days_from_previous_month;
|
1747
|
+
|
1748
|
+
// manage header caption and enable/disable navigation buttons if necessary
|
1749
|
+
manage_header(plugin.settings.months[selected_month] + ', ' + selected_year);
|
1750
|
+
|
1751
|
+
// start generating the HTML
|
1752
|
+
var html = '<tr>';
|
1753
|
+
|
1754
|
+
// if a column featuring the number of the week is to be shown
|
1755
|
+
if (plugin.settings.show_week_number)
|
1756
|
+
|
1757
|
+
// column title
|
1758
|
+
html += '<th>' + plugin.settings.show_week_number + '</th>';
|
1759
|
+
|
1760
|
+
// name of week days
|
1761
|
+
// show the abbreviated day names (or only the first two letters of the full name if no abbreviations are specified)
|
1762
|
+
// and also, take in account the value of the "first_day_of_week" property
|
1763
|
+
for (var i = 0; i < 7; i++)
|
1764
|
+
|
1765
|
+
html += '<th>' + ($.isArray(plugin.settings.days_abbr) && undefined !== plugin.settings.days_abbr[(plugin.settings.first_day_of_week + i) % 7] ? plugin.settings.days_abbr[(plugin.settings.first_day_of_week + i) % 7] : plugin.settings.days[(plugin.settings.first_day_of_week + i) % 7].substr(0, 2)) + '</th>';
|
1766
|
+
|
1767
|
+
html += '</tr><tr>';
|
1768
|
+
|
1769
|
+
// the calendar shows a total of 42 days
|
1770
|
+
for (i = 0; i < 42; i++) {
|
1771
|
+
|
1772
|
+
// seven days per row
|
1773
|
+
if (i > 0 && i % 7 === 0) html += '</tr><tr>';
|
1774
|
+
|
1775
|
+
// if week number is to be shown
|
1776
|
+
if (i % 7 === 0 && plugin.settings.show_week_number)
|
1777
|
+
|
1778
|
+
// show ISO 8601 week number
|
1779
|
+
html += '<td class="dp_week_number">' + getWeekNumber(new Date(selected_year, selected_month, (i - days_from_previous_month + 1))) + '</td>';
|
1780
|
+
|
1781
|
+
// the number of the day in month
|
1782
|
+
var day = (i - days_from_previous_month + 1);
|
1783
|
+
|
1784
|
+
// if dates in previous/next month can be selected, and this is one of those days
|
1785
|
+
if (plugin.settings.select_other_months && (i < days_from_previous_month || day > days_in_month)) {
|
1786
|
+
|
1787
|
+
// use the Date object to normalize the date
|
1788
|
+
// for example, 2011 05 33 will be transformed to 2011 06 02
|
1789
|
+
var real_date = new Date(selected_year, selected_month, day),
|
1790
|
+
real_year = real_date.getFullYear(),
|
1791
|
+
real_month = real_date.getMonth(),
|
1792
|
+
real_day = real_date.getDate();
|
1793
|
+
|
1794
|
+
// extract normalized date parts and merge them
|
1795
|
+
real_date = real_year + str_pad(real_month, 2) + str_pad(real_day, 2);
|
1796
|
+
|
1797
|
+
}
|
1798
|
+
|
1799
|
+
// if this is a day from the previous month
|
1800
|
+
if (i < days_from_previous_month)
|
1801
|
+
|
1802
|
+
html += '<td class="' + (plugin.settings.select_other_months && !is_disabled(real_year, real_month, real_day) ? 'dp_not_in_month_selectable date_' + real_date : 'dp_not_in_month') + '">' + (plugin.settings.select_other_months || plugin.settings.show_other_months ? str_pad(days_in_previous_month - days_from_previous_month + i + 1, plugin.settings.zero_pad ? 2 : 0) : ' ') + '</td>';
|
1803
|
+
|
1804
|
+
// if this is a day from the next month
|
1805
|
+
else if (day > days_in_month)
|
1806
|
+
|
1807
|
+
html += '<td class="' + (plugin.settings.select_other_months && !is_disabled(real_year, real_month, real_day) ? 'dp_not_in_month_selectable date_' + real_date : 'dp_not_in_month') + '">' + (plugin.settings.select_other_months || plugin.settings.show_other_months ? str_pad(day - days_in_month, plugin.settings.zero_pad ? 2 : 0) : ' ') + '</td>';
|
1808
|
+
|
1809
|
+
// if this is a day from the current month
|
1810
|
+
else {
|
1811
|
+
|
1812
|
+
var
|
1813
|
+
|
1814
|
+
// get the week day (0 to 6, Sunday to Saturday)
|
1815
|
+
weekday = (plugin.settings.first_day_of_week + i) % 7,
|
1816
|
+
|
1817
|
+
class_name = '';
|
1818
|
+
|
1819
|
+
// if date needs to be disabled
|
1820
|
+
if (is_disabled(selected_year, selected_month, day)) {
|
1821
|
+
|
1822
|
+
// if day is in weekend
|
1823
|
+
if ($.inArray(weekday, plugin.settings.weekend_days) > -1) class_name = 'dp_weekend_disabled';
|
1824
|
+
|
1825
|
+
// if work day
|
1826
|
+
else class_name += ' dp_disabled';
|
1827
|
+
|
1828
|
+
// highlight the current system date
|
1829
|
+
if (selected_month == current_system_month && selected_year == current_system_year && current_system_day == day) class_name += ' dp_disabled_current';
|
1830
|
+
|
1831
|
+
// if there are no restrictions
|
1832
|
+
} else {
|
1833
|
+
|
1834
|
+
// if day is in weekend
|
1835
|
+
if ($.inArray(weekday, plugin.settings.weekend_days) > -1) class_name = 'dp_weekend';
|
1836
|
+
|
1837
|
+
// highlight the currently selected date
|
1838
|
+
if (selected_month == default_month && selected_year == default_year && default_day == day) class_name += ' dp_selected';
|
1839
|
+
|
1840
|
+
// highlight the current system date
|
1841
|
+
if (selected_month == current_system_month && selected_year == current_system_year && current_system_day == day) class_name += ' dp_current';
|
1842
|
+
|
1843
|
+
}
|
1844
|
+
|
1845
|
+
// print the day of the month
|
1846
|
+
html += '<td' + (class_name !== '' ? ' class="' + $.trim(class_name) + '"' : '') + '>' + (plugin.settings.zero_pad ? str_pad(day, 2) : day) + '</td>';
|
1847
|
+
|
1848
|
+
}
|
1849
|
+
|
1850
|
+
}
|
1851
|
+
|
1852
|
+
// wrap up generating the day picker
|
1853
|
+
html += '</tr>';
|
1854
|
+
|
1855
|
+
// inject the day picker into the DOM
|
1856
|
+
daypicker.html($(html));
|
1857
|
+
|
1858
|
+
// if date picker is always visible
|
1859
|
+
if (plugin.settings.always_visible)
|
1860
|
+
|
1861
|
+
// cache all the cells
|
1862
|
+
// (we need them so that we can easily remove the "dp_selected" class from all of them when user selects a date)
|
1863
|
+
daypicker_cells = $('td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month, .dp_blocked, .dp_week_number)', daypicker);
|
1864
|
+
|
1865
|
+
// make the day picker visible
|
1866
|
+
daypicker.show();
|
1867
|
+
|
1868
|
+
};
|
1869
|
+
|
1870
|
+
/**
|
1871
|
+
* Generates the month picker view, and displays it
|
1872
|
+
*
|
1873
|
+
* @return void
|
1874
|
+
*
|
1875
|
+
* @access private
|
1876
|
+
*/
|
1877
|
+
var generate_monthpicker = function() {
|
1878
|
+
|
1879
|
+
// manage header caption and enable/disable navigation buttons if necessary
|
1880
|
+
manage_header(selected_year);
|
1881
|
+
|
1882
|
+
// start generating the HTML
|
1883
|
+
var html = '<tr>';
|
1884
|
+
|
1885
|
+
// iterate through all the months
|
1886
|
+
for (var i = 0; i < 12; i++) {
|
1887
|
+
|
1888
|
+
// three month per row
|
1889
|
+
if (i > 0 && i % 3 === 0) html += '</tr><tr>';
|
1890
|
+
|
1891
|
+
var class_name = 'dp_month_' + i;
|
1892
|
+
|
1893
|
+
// if month needs to be disabled
|
1894
|
+
if (is_disabled(selected_year, i)) class_name += ' dp_disabled';
|
1895
|
+
|
1896
|
+
// else, if a date is already selected and this is that particular month, highlight it
|
1897
|
+
else if (default_month !== false && default_month == i) class_name += ' dp_selected';
|
1898
|
+
|
1899
|
+
// else, if this the current system month, highlight it
|
1900
|
+
else if (current_system_month == i && current_system_year == selected_year) class_name += ' dp_current';
|
1901
|
+
|
1902
|
+
// first three letters of the month's name
|
1903
|
+
html += '<td class="' + $.trim(class_name) + '">' + ($.isArray(plugin.settings.months_abbr) && undefined !== plugin.settings.months_abbr[i] ? plugin.settings.months_abbr[i] : plugin.settings.months[i].substr(0, 3)) + '</td>';
|
1904
|
+
|
1905
|
+
}
|
1906
|
+
|
1907
|
+
// wrap up
|
1908
|
+
html += '</tr>';
|
1909
|
+
|
1910
|
+
// inject into the DOM
|
1911
|
+
monthpicker.html($(html));
|
1912
|
+
|
1913
|
+
// if date picker is always visible
|
1914
|
+
if (plugin.settings.always_visible)
|
1915
|
+
|
1916
|
+
// cache all the cells
|
1917
|
+
// (we need them so that we can easily remove the "dp_selected" class from all of them when user selects a month)
|
1918
|
+
monthpicker_cells = $('td:not(.dp_disabled)', monthpicker);
|
1919
|
+
|
1920
|
+
// make the month picker visible
|
1921
|
+
monthpicker.show();
|
1922
|
+
|
1923
|
+
};
|
1924
|
+
|
1925
|
+
/**
|
1926
|
+
* Generates the year picker view, and displays it
|
1927
|
+
*
|
1928
|
+
* @return void
|
1929
|
+
*
|
1930
|
+
* @access private
|
1931
|
+
*/
|
1932
|
+
var generate_yearpicker = function() {
|
1933
|
+
|
1934
|
+
// manage header caption and enable/disable navigation buttons if necessary
|
1935
|
+
manage_header(selected_year - 7 + ' - ' + (selected_year + 4));
|
1936
|
+
|
1937
|
+
// start generating the HTML
|
1938
|
+
var html = '<tr>';
|
1939
|
+
|
1940
|
+
// we're showing 9 years at a time, current year in the middle
|
1941
|
+
for (var i = 0; i < 12; i++) {
|
1942
|
+
|
1943
|
+
// three years per row
|
1944
|
+
if (i > 0 && i % 3 === 0) html += '</tr><tr>';
|
1945
|
+
|
1946
|
+
var class_name = '';
|
1947
|
+
|
1948
|
+
// if year needs to be disabled
|
1949
|
+
if (is_disabled(selected_year - 7 + i)) class_name += ' dp_disabled';
|
1950
|
+
|
1951
|
+
// else, if a date is already selected and this is that particular year, highlight it
|
1952
|
+
else if (default_year && default_year == selected_year - 7 + i) class_name += ' dp_selected';
|
1953
|
+
|
1954
|
+
// else, if this is the current system year, highlight it
|
1955
|
+
else if (current_system_year == (selected_year - 7 + i)) class_name += ' dp_current';
|
1956
|
+
|
1957
|
+
// first three letters of the month's name
|
1958
|
+
html += '<td' + ($.trim(class_name) !== '' ? ' class="' + $.trim(class_name) + '"' : '') + '>' + (selected_year - 7 + i) + '</td>';
|
1959
|
+
|
1960
|
+
}
|
1961
|
+
|
1962
|
+
// wrap up
|
1963
|
+
html += '</tr>';
|
1964
|
+
|
1965
|
+
// inject into the DOM
|
1966
|
+
yearpicker.html($(html));
|
1967
|
+
|
1968
|
+
// if date picker is always visible
|
1969
|
+
if (plugin.settings.always_visible)
|
1970
|
+
|
1971
|
+
// cache all the cells
|
1972
|
+
// (we need them so that we can easily remove the "dp_selected" class from all of them when user selects a year)
|
1973
|
+
yearpicker_cells = $('td:not(.dp_disabled)', yearpicker);
|
1974
|
+
|
1975
|
+
// make the year picker visible
|
1976
|
+
yearpicker.show();
|
1977
|
+
|
1978
|
+
};
|
1979
|
+
|
1980
|
+
/**
|
1981
|
+
* Generates an iFrame shim in Internet Explorer 6 so that the date picker appears above select boxes.
|
1982
|
+
*
|
1983
|
+
* @return void
|
1984
|
+
*
|
1985
|
+
* @access private
|
1986
|
+
*/
|
1987
|
+
var iframeShim = function(action) {
|
1988
|
+
|
1989
|
+
// this is necessary only if browser is Internet Explorer 6
|
1990
|
+
if (browser.name == 'explorer' && browser.version == 6) {
|
1991
|
+
|
1992
|
+
// if the iFrame was not yet created
|
1993
|
+
// "undefined" evaluates as FALSE
|
1994
|
+
if (!shim) {
|
1995
|
+
|
1996
|
+
// the iFrame has to have the element's zIndex minus 1
|
1997
|
+
var zIndex = to_int(datepicker.css('zIndex')) - 1;
|
1998
|
+
|
1999
|
+
// create the iFrame
|
2000
|
+
shim = jQuery('<iframe>', {
|
2001
|
+
'src': 'javascript:document.write("")',
|
2002
|
+
'scrolling': 'no',
|
2003
|
+
'frameborder': 0,
|
2004
|
+
'allowtransparency': 'true',
|
2005
|
+
css: {
|
2006
|
+
'zIndex': zIndex,
|
2007
|
+
'position': 'absolute',
|
2008
|
+
'top': -1000,
|
2009
|
+
'left': -1000,
|
2010
|
+
'width': datepicker.outerWidth(),
|
2011
|
+
'height': datepicker.outerHeight(),
|
2012
|
+
'filter': 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)',
|
2013
|
+
'display': 'none'
|
2014
|
+
}
|
2015
|
+
});
|
2016
|
+
|
2017
|
+
// inject iFrame into DOM
|
2018
|
+
$('body').append(shim);
|
2019
|
+
|
2020
|
+
}
|
2021
|
+
|
2022
|
+
// what do we need to do
|
2023
|
+
switch (action) {
|
2024
|
+
|
2025
|
+
// hide the iFrame?
|
2026
|
+
case 'hide':
|
2027
|
+
|
2028
|
+
// set the iFrame's display property to "none"
|
2029
|
+
shim.hide();
|
2030
|
+
|
2031
|
+
break;
|
2032
|
+
|
2033
|
+
// show the iFrame?
|
2034
|
+
default:
|
2035
|
+
|
2036
|
+
// get date picker top and left position
|
2037
|
+
var offset = datepicker.offset();
|
2038
|
+
|
2039
|
+
// position the iFrame shim right underneath the date picker
|
2040
|
+
// and set its display to "block"
|
2041
|
+
shim.css({
|
2042
|
+
'top': offset.top,
|
2043
|
+
'left': offset.left,
|
2044
|
+
'display': 'block'
|
2045
|
+
});
|
2046
|
+
|
2047
|
+
}
|
2048
|
+
|
2049
|
+
}
|
2050
|
+
|
2051
|
+
};
|
2052
|
+
|
2053
|
+
/**
|
2054
|
+
* Checks if, according to the restrictions of the calendar and/or the values defined by the "disabled_dates"
|
2055
|
+
* property, a day, a month or a year needs to be disabled.
|
2056
|
+
*
|
2057
|
+
* @param integer year The year to check
|
2058
|
+
* @param integer month The month to check
|
2059
|
+
* @param integer day The day to check
|
2060
|
+
*
|
2061
|
+
* @return boolean Returns TRUE if the given value is not disabled or FALSE otherwise
|
2062
|
+
*
|
2063
|
+
* @access private
|
2064
|
+
*/
|
2065
|
+
var is_disabled = function(year, month, day) {
|
2066
|
+
|
2067
|
+
// don't check bogus values
|
2068
|
+
if ((undefined === year || isNaN(year)) && (undefined === month || isNaN(month)) && (undefined === day || isNaN(day))) return false;
|
2069
|
+
|
2070
|
+
// if calendar has direction restrictions
|
2071
|
+
if (!(!$.isArray(plugin.settings.direction) && to_int(plugin.settings.direction) === 0)) {
|
2072
|
+
|
2073
|
+
var
|
2074
|
+
// normalize and merge arguments then transform the result to an integer
|
2075
|
+
now = to_int(str_concat(year, (typeof month != 'undefined' ? str_pad(month, 2) : ''), (typeof day != 'undefined' ? str_pad(day, 2) : ''))),
|
2076
|
+
|
2077
|
+
// get the length of the argument
|
2078
|
+
len = (now + '').length;
|
2079
|
+
|
2080
|
+
// if we're checking days
|
2081
|
+
if (len == 8 && (
|
2082
|
+
|
2083
|
+
// day is before the first selectable date
|
2084
|
+
(typeof start_date != 'undefined' && now < to_int(str_concat(first_selectable_year, str_pad(first_selectable_month, 2), str_pad(first_selectable_day, 2)))) ||
|
2085
|
+
|
2086
|
+
// or day is after the last selectable date
|
2087
|
+
(typeof end_date != 'undefined' && now > to_int(str_concat(last_selectable_year, str_pad(last_selectable_month, 2), str_pad(last_selectable_day, 2))))
|
2088
|
+
|
2089
|
+
// day needs to be disabled
|
2090
|
+
)) return true;
|
2091
|
+
|
2092
|
+
// if we're checking months
|
2093
|
+
else if (len == 6 && (
|
2094
|
+
|
2095
|
+
// month is before the first selectable month
|
2096
|
+
(typeof start_date != 'undefined' && now < to_int(str_concat(first_selectable_year, str_pad(first_selectable_month, 2)))) ||
|
2097
|
+
|
2098
|
+
// or day is after the last selectable date
|
2099
|
+
(typeof end_date != 'undefined' && now > to_int(str_concat(last_selectable_year, str_pad(last_selectable_month, 2))))
|
2100
|
+
|
2101
|
+
// month needs to be disabled
|
2102
|
+
)) return true;
|
2103
|
+
|
2104
|
+
// if we're checking years
|
2105
|
+
else if (len == 4 && (
|
2106
|
+
|
2107
|
+
// year is before the first selectable year
|
2108
|
+
(typeof start_date != 'undefined' && now < first_selectable_year) ||
|
2109
|
+
|
2110
|
+
// or day is after the last selectable date
|
2111
|
+
(typeof end_date != 'undefined' && now > last_selectable_year)
|
2112
|
+
|
2113
|
+
// year needs to be disabled
|
2114
|
+
)) return true;
|
2115
|
+
|
2116
|
+
}
|
2117
|
+
|
2118
|
+
// if month is given as argument, increment it (as JavaScript uses 0 for January, 1 for February...)
|
2119
|
+
if (typeof month != 'undefined') month = month + 1;
|
2120
|
+
|
2121
|
+
// by default, we assume the day/month/year is not enabled nor disabled
|
2122
|
+
var disabled = false, enabled = false;
|
2123
|
+
|
2124
|
+
// if there are rules for disabling dates
|
2125
|
+
if (disabled_dates)
|
2126
|
+
|
2127
|
+
// iterate through the rules for disabling dates
|
2128
|
+
$.each(disabled_dates, function() {
|
2129
|
+
|
2130
|
+
// if the date is to be disabled, don't look any further
|
2131
|
+
if (disabled) return;
|
2132
|
+
|
2133
|
+
var rule = this;
|
2134
|
+
|
2135
|
+
// if the rules apply for the current year
|
2136
|
+
if ($.inArray(year, rule[2]) > -1 || $.inArray('*', rule[2]) > -1)
|
2137
|
+
|
2138
|
+
// if the rules apply for the current month
|
2139
|
+
if ((typeof month != 'undefined' && $.inArray(month, rule[1]) > -1) || $.inArray('*', rule[1]) > -1)
|
2140
|
+
|
2141
|
+
// if the rules apply for the current day
|
2142
|
+
if ((typeof day != 'undefined' && $.inArray(day, rule[0]) > -1) || $.inArray('*', rule[0]) > -1) {
|
2143
|
+
|
2144
|
+
// if day is to be disabled whatever the day
|
2145
|
+
// don't look any further
|
2146
|
+
if (rule[3] == '*') return (disabled = true);
|
2147
|
+
|
2148
|
+
// get the weekday
|
2149
|
+
var weekday = new Date(year, month - 1, day).getDay();
|
2150
|
+
|
2151
|
+
// if weekday is to be disabled
|
2152
|
+
// don't look any further
|
2153
|
+
if ($.inArray(weekday, rule[3]) > -1) return (disabled = true);
|
2154
|
+
|
2155
|
+
}
|
2156
|
+
|
2157
|
+
});
|
2158
|
+
|
2159
|
+
// if there are rules that explicitly enable dates
|
2160
|
+
if (enabled_dates)
|
2161
|
+
|
2162
|
+
// iterate through the rules for enabling dates
|
2163
|
+
$.each(enabled_dates, function() {
|
2164
|
+
|
2165
|
+
// if the date is to be enabled, don't look any further
|
2166
|
+
if (enabled) return;
|
2167
|
+
|
2168
|
+
var rule = this;
|
2169
|
+
|
2170
|
+
// if the rules apply for the current year
|
2171
|
+
if ($.inArray(year, rule[2]) > -1 || $.inArray('*', rule[2]) > -1) {
|
2172
|
+
|
2173
|
+
// the year is enabled
|
2174
|
+
enabled = true;
|
2175
|
+
|
2176
|
+
// if we're also checking months
|
2177
|
+
if (typeof month != 'undefined') {
|
2178
|
+
|
2179
|
+
// we assume the month is enabled
|
2180
|
+
enabled = true;
|
2181
|
+
|
2182
|
+
// if the rules apply for the current month
|
2183
|
+
if ($.inArray(month, rule[1]) > -1 || $.inArray('*', rule[1]) > -1) {
|
2184
|
+
|
2185
|
+
// if we're also checking days
|
2186
|
+
if (typeof day != 'undefined') {
|
2187
|
+
|
2188
|
+
// we assume the day is enabled
|
2189
|
+
enabled = true;
|
2190
|
+
|
2191
|
+
// if the rules apply for the current day
|
2192
|
+
if ($.inArray(day, rule[0]) > -1 || $.inArray('*', rule[0]) > -1) {
|
2193
|
+
|
2194
|
+
// if day is to be enabled whatever the day
|
2195
|
+
// don't look any further
|
2196
|
+
if (rule[3] == '*') return (enabled = true);
|
2197
|
+
|
2198
|
+
// get the weekday
|
2199
|
+
var weekday = new Date(year, month - 1, day).getDay();
|
2200
|
+
|
2201
|
+
// if weekday is to be enabled
|
2202
|
+
// don't look any further
|
2203
|
+
if ($.inArray(weekday, rule[3]) > -1) return (enabled = true);
|
2204
|
+
|
2205
|
+
// if we get this far, it means the day is not enabled
|
2206
|
+
enabled = false;
|
2207
|
+
|
2208
|
+
// if day is not enabled
|
2209
|
+
} else enabled = false;
|
2210
|
+
|
2211
|
+
}
|
2212
|
+
|
2213
|
+
// if month is not enabled
|
2214
|
+
} else enabled = false;
|
2215
|
+
|
2216
|
+
}
|
2217
|
+
|
2218
|
+
}
|
2219
|
+
|
2220
|
+
});
|
2221
|
+
|
2222
|
+
// if checked date is enabled, return false
|
2223
|
+
if (enabled_dates && enabled) return false;
|
2224
|
+
|
2225
|
+
// if checked date is disabled return false
|
2226
|
+
else if (disabled_dates && disabled) return true;
|
2227
|
+
|
2228
|
+
// if script gets this far it means that the day/month/year doesn't need to be disabled
|
2229
|
+
return false;
|
2230
|
+
|
2231
|
+
};
|
2232
|
+
|
2233
|
+
/**
|
2234
|
+
* Checks whether a value is an integer number.
|
2235
|
+
*
|
2236
|
+
* @param mixed value Value to check
|
2237
|
+
*
|
2238
|
+
* @return Returns TRUE if the value represents an integer number, or FALSE otherwise
|
2239
|
+
*
|
2240
|
+
* @access private
|
2241
|
+
*/
|
2242
|
+
var is_integer = function(value) {
|
2243
|
+
|
2244
|
+
// return TRUE if value represents an integer number, or FALSE otherwise
|
2245
|
+
return (value + '').match(/^\-?[0-9]+$/) ? true : false;
|
2246
|
+
|
2247
|
+
};
|
2248
|
+
|
2249
|
+
/**
|
2250
|
+
* Sets the caption in the header of the date picker and enables or disables navigation buttons when necessary.
|
2251
|
+
*
|
2252
|
+
* @param string caption String that needs to be displayed in the header
|
2253
|
+
*
|
2254
|
+
* @return void
|
2255
|
+
*
|
2256
|
+
* @access private
|
2257
|
+
*/
|
2258
|
+
var manage_header = function(caption) {
|
2259
|
+
|
2260
|
+
// update the caption in the header
|
2261
|
+
$('.dp_caption', header).html(caption);
|
2262
|
+
|
2263
|
+
// if calendar has direction restrictions or we're looking only at months
|
2264
|
+
if (!(!$.isArray(plugin.settings.direction) && to_int(plugin.settings.direction) === 0) || (views.length == 1 && views[0] == 'months')) {
|
2265
|
+
|
2266
|
+
// get the current year and month
|
2267
|
+
var year = selected_year,
|
2268
|
+
month = selected_month,
|
2269
|
+
next, previous;
|
2270
|
+
|
2271
|
+
// if current view is showing days
|
2272
|
+
if (view == 'days') {
|
2273
|
+
|
2274
|
+
// check if we can click on the "previous" button
|
2275
|
+
previous = !is_disabled(month - 1 < 0 ? str_concat(year - 1, '11') : str_concat(year, str_pad(month - 1, 2)));
|
2276
|
+
|
2277
|
+
// check if we can click on the "next" button
|
2278
|
+
next = !is_disabled(month + 1 > 11 ? str_concat(year + 1, '00') : str_concat(year, str_pad(month + 1, 2)));
|
2279
|
+
|
2280
|
+
// if current view is showing months
|
2281
|
+
} else if (view == 'months') {
|
2282
|
+
|
2283
|
+
// check if we can click on the "previous" button
|
2284
|
+
if (!start_date || start_date.getFullYear() <= year - 1) previous = true;
|
2285
|
+
|
2286
|
+
// check if we can click on the "next" button
|
2287
|
+
if (!end_date || end_date.getFullYear() >= year + 1) next = true;
|
2288
|
+
|
2289
|
+
// if current view is showing years
|
2290
|
+
} else if (view == 'years') {
|
2291
|
+
|
2292
|
+
// check if we can click on the "previous" button
|
2293
|
+
if (!start_date || start_date.getFullYear() < year - 7) previous = true;
|
2294
|
+
|
2295
|
+
// check if we can click on the "next" button
|
2296
|
+
if (!end_date || end_date.getFullYear() > year + 4) next = true;
|
2297
|
+
|
2298
|
+
}
|
2299
|
+
|
2300
|
+
// if we cannot click on the "previous" button
|
2301
|
+
if (!previous) {
|
2302
|
+
|
2303
|
+
// disable the "previous" button
|
2304
|
+
$('.dp_previous', header).addClass('dp_blocked');
|
2305
|
+
$('.dp_previous', header).removeClass('dp_hover');
|
2306
|
+
|
2307
|
+
// otherwise enable the "previous" button
|
2308
|
+
} else $('.dp_previous', header).removeClass('dp_blocked');
|
2309
|
+
|
2310
|
+
// if we cannot click on the "next" button
|
2311
|
+
if (!next) {
|
2312
|
+
|
2313
|
+
// disable the "next" button
|
2314
|
+
$('.dp_next', header).addClass('dp_blocked');
|
2315
|
+
$('.dp_next', header).removeClass('dp_hover');
|
2316
|
+
|
2317
|
+
// otherwise enable the "next" button
|
2318
|
+
} else $('.dp_next', header).removeClass('dp_blocked');
|
2319
|
+
|
2320
|
+
}
|
2321
|
+
|
2322
|
+
};
|
2323
|
+
|
2324
|
+
/**
|
2325
|
+
* Shows the appropriate view (days, months or years) according to the current value of the "view" property.
|
2326
|
+
*
|
2327
|
+
* @return void
|
2328
|
+
*
|
2329
|
+
* @access private
|
2330
|
+
*/
|
2331
|
+
var manage_views = function() {
|
2332
|
+
|
2333
|
+
// if the day picker was not yet generated
|
2334
|
+
if (daypicker.text() === '' || view == 'days') {
|
2335
|
+
|
2336
|
+
// if the day picker was not yet generated
|
2337
|
+
if (daypicker.text() === '') {
|
2338
|
+
|
2339
|
+
// if date picker is not always visible
|
2340
|
+
if (!plugin.settings.always_visible)
|
2341
|
+
|
2342
|
+
// temporarily set the date picker's left outside of view
|
2343
|
+
// so that we can later grab its width and height
|
2344
|
+
datepicker.css('left', -1000);
|
2345
|
+
|
2346
|
+
// temporarily make the date picker visible
|
2347
|
+
// so that we can later grab its width and height
|
2348
|
+
datepicker.show();
|
2349
|
+
|
2350
|
+
// generate the day picker
|
2351
|
+
generate_daypicker();
|
2352
|
+
|
2353
|
+
// get the day picker's width and height
|
2354
|
+
var width = daypicker.outerWidth(),
|
2355
|
+
height = daypicker.outerHeight();
|
2356
|
+
|
2357
|
+
// make the month picker have the same size as the day picker
|
2358
|
+
monthpicker.css({
|
2359
|
+
'width': width,
|
2360
|
+
'height': height
|
2361
|
+
});
|
2362
|
+
|
2363
|
+
// make the year picker have the same size as the day picker
|
2364
|
+
yearpicker.css({
|
2365
|
+
'width': width,
|
2366
|
+
'height': height
|
2367
|
+
});
|
2368
|
+
|
2369
|
+
// make the header and the footer have the same size as the day picker
|
2370
|
+
header.css('width', width);
|
2371
|
+
footer.css('width', width);
|
2372
|
+
|
2373
|
+
// hide the date picker again
|
2374
|
+
datepicker.hide();
|
2375
|
+
|
2376
|
+
// if the day picker was previously generated at least once
|
2377
|
+
// generate the day picker
|
2378
|
+
} else generate_daypicker();
|
2379
|
+
|
2380
|
+
// hide the year and the month pickers
|
2381
|
+
monthpicker.hide();
|
2382
|
+
yearpicker.hide();
|
2383
|
+
|
2384
|
+
// if the view is "months"
|
2385
|
+
} else if (view == 'months') {
|
2386
|
+
|
2387
|
+
// generate the month picker
|
2388
|
+
generate_monthpicker();
|
2389
|
+
|
2390
|
+
// hide the day and the year pickers
|
2391
|
+
daypicker.hide();
|
2392
|
+
yearpicker.hide();
|
2393
|
+
|
2394
|
+
// if the view is "years"
|
2395
|
+
} else if (view == 'years') {
|
2396
|
+
|
2397
|
+
// generate the year picker
|
2398
|
+
generate_yearpicker();
|
2399
|
+
|
2400
|
+
// hide the day and the month pickers
|
2401
|
+
daypicker.hide();
|
2402
|
+
monthpicker.hide();
|
2403
|
+
|
2404
|
+
}
|
2405
|
+
|
2406
|
+
// if a callback function exists for when navigating through months/years
|
2407
|
+
if (plugin.settings.onChange && typeof plugin.settings.onChange == 'function' && undefined !== view) {
|
2408
|
+
|
2409
|
+
// get the "active" elements in the view (ignoring the disabled ones)
|
2410
|
+
var elements = (view == 'days' ?
|
2411
|
+
daypicker.find('td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month, .dp_blocked)') :
|
2412
|
+
(view == 'months' ?
|
2413
|
+
monthpicker.find('td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month, .dp_blocked)') :
|
2414
|
+
yearpicker.find('td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month, .dp_blocked)')));
|
2415
|
+
|
2416
|
+
// iterate through the active elements
|
2417
|
+
// and attach a "date" data attribute to each element in the form of
|
2418
|
+
// YYYY-MM-DD if the view is "days"
|
2419
|
+
// YYYY-MM if the view is "months"
|
2420
|
+
// YYYY if the view is "years"
|
2421
|
+
// so it's easy to identify elements in the list
|
2422
|
+
elements.each(function() {
|
2423
|
+
|
2424
|
+
// if view is "days"
|
2425
|
+
if (view == 'days')
|
2426
|
+
|
2427
|
+
// attach a "date" data attribute to each element in the form of of YYYY-MM-DD for easily identifying sought elements
|
2428
|
+
$(this).data('date', selected_year + '-' + str_pad(selected_month + 1, 2) + '-' + str_pad(to_int($(this).text()), 2));
|
2429
|
+
|
2430
|
+
// if view is "months"
|
2431
|
+
else if (view == 'months') {
|
2432
|
+
|
2433
|
+
// get the month's number for the element's class
|
2434
|
+
var matches = $(this).attr('class').match(/dp\_month\_([0-9]+)/);
|
2435
|
+
|
2436
|
+
// attach a "date" data attribute to each element in the form of of YYYY-MM for easily identifying sought elements
|
2437
|
+
$(this).data('date', selected_year + '-' + str_pad(to_int(matches[1]) + 1, 2));
|
2438
|
+
|
2439
|
+
// if view is "years"
|
2440
|
+
} else
|
2441
|
+
|
2442
|
+
// attach a "date" data attribute to each element in the form of of YYYY for easily identifying sought elements
|
2443
|
+
$(this).data('date', to_int($(this).text()));
|
2444
|
+
|
2445
|
+
});
|
2446
|
+
|
2447
|
+
// execute the callback function and send as arguments the current view, the elements in the view, and
|
2448
|
+
// the element the plugin is attached to
|
2449
|
+
plugin.settings.onChange(view, elements, $element);
|
2450
|
+
|
2451
|
+
}
|
2452
|
+
|
2453
|
+
// assume the footer is visible
|
2454
|
+
footer.show();
|
2455
|
+
|
2456
|
+
// if the button for clearing a previously selected date needs to be visible all the time,
|
2457
|
+
// or the "Clear" button needs to be shown only when a date was previously selected, and now it's the case,
|
2458
|
+
// or the date picker is always visible and the "Clear" button was not explicitly disabled
|
2459
|
+
if (
|
2460
|
+
plugin.settings.show_clear_date === true ||
|
2461
|
+
(plugin.settings.show_clear_date === 0 && $element.val() !== '') ||
|
2462
|
+
(plugin.settings.always_visible && plugin.settings.show_clear_date !== false)
|
2463
|
+
) {
|
2464
|
+
|
2465
|
+
// show the "Clear" button
|
2466
|
+
cleardate.show();
|
2467
|
+
|
2468
|
+
// if the "Today" button is visible
|
2469
|
+
if (show_select_today) {
|
2470
|
+
|
2471
|
+
// show it, and set it's width to 50% of the available space
|
2472
|
+
selecttoday.css('width', '50%');
|
2473
|
+
|
2474
|
+
// the "Clear date" button only takes up 50% of the available space
|
2475
|
+
cleardate.css('width', '50%');
|
2476
|
+
|
2477
|
+
// if the "Today" button is not visible
|
2478
|
+
} else {
|
2479
|
+
|
2480
|
+
// hide the "Today" button
|
2481
|
+
selecttoday.hide();
|
2482
|
+
|
2483
|
+
// the "Clear date" button takes up 100% of the available space
|
2484
|
+
cleardate.css('width', '100%');
|
2485
|
+
|
2486
|
+
}
|
2487
|
+
|
2488
|
+
// otherwise
|
2489
|
+
} else {
|
2490
|
+
|
2491
|
+
// hide the "Clear" button
|
2492
|
+
cleardate.hide();
|
2493
|
+
|
2494
|
+
// if the "Today" button is visible, it will now take up all the available space
|
2495
|
+
if (show_select_today) selecttoday.show().css('width', '100%');
|
2496
|
+
|
2497
|
+
// if the "Today" button is also not visible, hide the footer entirely
|
2498
|
+
else footer.hide();
|
2499
|
+
|
2500
|
+
}
|
2501
|
+
|
2502
|
+
|
2503
|
+
};
|
2504
|
+
|
2505
|
+
/**
|
2506
|
+
* Puts the specified date in the element the plugin is attached to, and hides the date picker.
|
2507
|
+
*
|
2508
|
+
* @param integer year The year
|
2509
|
+
*
|
2510
|
+
* @param integer month The month
|
2511
|
+
*
|
2512
|
+
* @param integer day The day
|
2513
|
+
*
|
2514
|
+
* @param string view The view from where the method was called
|
2515
|
+
*
|
2516
|
+
* @param object cell The element that was clicked
|
2517
|
+
*
|
2518
|
+
* @return void
|
2519
|
+
*
|
2520
|
+
* @access private
|
2521
|
+
*/
|
2522
|
+
var select_date = function(year, month, day, view, cell) {
|
2523
|
+
|
2524
|
+
var
|
2525
|
+
|
2526
|
+
// construct a new date object from the arguments
|
2527
|
+
default_date = new Date(year, month, day, 12, 0, 0),
|
2528
|
+
|
2529
|
+
// pointer to the cells in the current view
|
2530
|
+
view_cells = (view == 'days' ? daypicker_cells : (view == 'months' ? monthpicker_cells : yearpicker_cells)),
|
2531
|
+
|
2532
|
+
// the selected date, formatted correctly
|
2533
|
+
selected_value = format(default_date);
|
2534
|
+
|
2535
|
+
// set the currently selected and formated date as the value of the element the plugin is attached to
|
2536
|
+
$element.val(selected_value);
|
2537
|
+
|
2538
|
+
// if date picker is always visible
|
2539
|
+
if (plugin.settings.always_visible) {
|
2540
|
+
|
2541
|
+
// extract the date parts and reassign values to these variables
|
2542
|
+
// so that everything will be correctly highlighted
|
2543
|
+
default_month = default_date.getMonth();
|
2544
|
+
selected_month = default_date.getMonth();
|
2545
|
+
default_year = default_date.getFullYear();
|
2546
|
+
selected_year = default_date.getFullYear();
|
2547
|
+
default_day = default_date.getDate();
|
2548
|
+
|
2549
|
+
// remove the "selected" class from all cells in the current view
|
2550
|
+
view_cells.removeClass('dp_selected');
|
2551
|
+
|
2552
|
+
// add the "selected" class to the currently selected cell
|
2553
|
+
cell.addClass('dp_selected');
|
2554
|
+
|
2555
|
+
}
|
2556
|
+
|
2557
|
+
// hide the date picker
|
2558
|
+
plugin.hide();
|
2559
|
+
|
2560
|
+
// updates value for the date picker whose starting date depends on the selected date (if any)
|
2561
|
+
update_dependent(default_date);
|
2562
|
+
|
2563
|
+
// if a callback function exists for when selecting a date
|
2564
|
+
if (plugin.settings.onSelect && typeof plugin.settings.onSelect == 'function')
|
2565
|
+
|
2566
|
+
// execute the callback function
|
2567
|
+
plugin.settings.onSelect(selected_value, year + '-' + str_pad(month + 1, 2) + '-' + str_pad(day, 2), default_date, $element);
|
2568
|
+
|
2569
|
+
// move focus to the element the plugin is attached to
|
2570
|
+
$element.focus();
|
2571
|
+
|
2572
|
+
};
|
2573
|
+
|
2574
|
+
/**
|
2575
|
+
* Concatenates any number of arguments and returns them as string.
|
2576
|
+
*
|
2577
|
+
* @return string Returns the concatenated values.
|
2578
|
+
*
|
2579
|
+
* @access private
|
2580
|
+
*/
|
2581
|
+
var str_concat = function() {
|
2582
|
+
|
2583
|
+
var str = '';
|
2584
|
+
|
2585
|
+
// concatenate as string
|
2586
|
+
for (var i = 0; i < arguments.length; i++) str += (arguments[i] + '');
|
2587
|
+
|
2588
|
+
// return the concatenated values
|
2589
|
+
return str;
|
2590
|
+
|
2591
|
+
};
|
2592
|
+
|
2593
|
+
/**
|
2594
|
+
* Left-pad a string to a certain length with zeroes.
|
2595
|
+
*
|
2596
|
+
* @param string str The string to be padded.
|
2597
|
+
*
|
2598
|
+
* @param integer len The length to which the string must be padded
|
2599
|
+
*
|
2600
|
+
* @return string Returns the string left-padded with leading zeroes
|
2601
|
+
*
|
2602
|
+
* @access private
|
2603
|
+
*/
|
2604
|
+
var str_pad = function(str, len) {
|
2605
|
+
|
2606
|
+
// make sure argument is a string
|
2607
|
+
str += '';
|
2608
|
+
|
2609
|
+
// pad with leading zeroes until we get to the desired length
|
2610
|
+
while (str.length < len) str = '0' + str;
|
2611
|
+
|
2612
|
+
// return padded string
|
2613
|
+
return str;
|
2614
|
+
|
2615
|
+
};
|
2616
|
+
|
2617
|
+
/**
|
2618
|
+
* Returns the integer representation of a string
|
2619
|
+
*
|
2620
|
+
* @return int Returns the integer representation of the string given as argument
|
2621
|
+
*
|
2622
|
+
* @access private
|
2623
|
+
*/
|
2624
|
+
var to_int = function(str) {
|
2625
|
+
|
2626
|
+
// return the integer representation of the string given as argument
|
2627
|
+
return parseInt(str , 10);
|
2628
|
+
|
2629
|
+
};
|
2630
|
+
|
2631
|
+
/**
|
2632
|
+
* Updates the paired date picker (whose starting date depends on the value of the current date picker)
|
2633
|
+
*
|
2634
|
+
* @param date date A JavaScript date object representing the currently selected date
|
2635
|
+
*
|
2636
|
+
* @return void
|
2637
|
+
*
|
2638
|
+
* @access private
|
2639
|
+
*/
|
2640
|
+
var update_dependent = function(date) {
|
2641
|
+
|
2642
|
+
// if the pair element exists
|
2643
|
+
if (plugin.settings.pair) {
|
2644
|
+
|
2645
|
+
// iterate through the pair elements (as there may be more than just one)
|
2646
|
+
$.each(plugin.settings.pair, function() {
|
2647
|
+
|
2648
|
+
var $pair = $(this);
|
2649
|
+
|
2650
|
+
// chances are that in the beginning the pair element doesn't have the Zebra_DatePicker attached to it yet
|
2651
|
+
// (as the "start" element is usually created before the "end" element)
|
2652
|
+
// so we'll have to rely on "data" to send the starting date to the pair element
|
2653
|
+
|
2654
|
+
// therefore, if Zebra_DatePicker is not yet attached
|
2655
|
+
if (!($pair.data && $pair.data('Zebra_DatePicker')))
|
2656
|
+
|
2657
|
+
// set the starting date like this
|
2658
|
+
$pair.data('zdp_reference_date', date);
|
2659
|
+
|
2660
|
+
// if Zebra_DatePicker is attached to the pair element
|
2661
|
+
else {
|
2662
|
+
|
2663
|
+
// reference the date picker object attached to the other element
|
2664
|
+
var dp = $pair.data('Zebra_DatePicker');
|
2665
|
+
|
2666
|
+
// update the other date picker's starting date
|
2667
|
+
// the value depends on the original value of the "direction" attribute
|
2668
|
+
// (also, if the pair date picker does not have a direction, set it to 1)
|
2669
|
+
dp.update({
|
2670
|
+
'reference_date': date,
|
2671
|
+
'direction': dp.settings.direction === 0 ? 1 : dp.settings.direction
|
2672
|
+
});
|
2673
|
+
|
2674
|
+
// if the other date picker is always visible, update the visuals now
|
2675
|
+
if (dp.settings.always_visible) dp.show();
|
2676
|
+
|
2677
|
+
}
|
2678
|
+
|
2679
|
+
});
|
2680
|
+
|
2681
|
+
}
|
2682
|
+
|
2683
|
+
};
|
2684
|
+
|
2685
|
+
/**
|
2686
|
+
* Calculate the ISO 8601 week number for a given date.
|
2687
|
+
*
|
2688
|
+
* Code is based on the algorithm at http://www.tondering.dk/claus/cal/week.php#calcweekno
|
2689
|
+
*/
|
2690
|
+
var getWeekNumber = function(date) {
|
2691
|
+
|
2692
|
+
var y = date.getFullYear(),
|
2693
|
+
m = date.getMonth() + 1,
|
2694
|
+
d = date.getDate(),
|
2695
|
+
a, b, c, s, e, f, g, n, w;
|
2696
|
+
|
2697
|
+
// If month jan. or feb.
|
2698
|
+
if (m < 3) {
|
2699
|
+
|
2700
|
+
a = y - 1;
|
2701
|
+
b = (a / 4 | 0) - (a / 100 | 0) + (a / 400 | 0);
|
2702
|
+
c = ((a - 1) / 4 | 0) - ((a - 1) / 100 | 0) + ((a - 1) / 400 | 0);
|
2703
|
+
s = b - c;
|
2704
|
+
e = 0;
|
2705
|
+
f = d - 1 + 31 * (m - 1);
|
2706
|
+
|
2707
|
+
// If month mar. through dec.
|
2708
|
+
} else {
|
2709
|
+
|
2710
|
+
a = y;
|
2711
|
+
b = (a / 4 | 0) - (a / 100 | 0) + (a / 400 | 0);
|
2712
|
+
c = ((a - 1) / 4 | 0) - ((a - 1) / 100 | 0) + ((a - 1) / 400 | 0);
|
2713
|
+
s = b - c;
|
2714
|
+
e = s + 1;
|
2715
|
+
f = d + ((153 * (m - 3) + 2) / 5 | 0) + 58 + s;
|
2716
|
+
|
2717
|
+
}
|
2718
|
+
|
2719
|
+
g = (a + b) % 7;
|
2720
|
+
// ISO Weekday (0 is monday, 1 is tuesday etc.)
|
2721
|
+
d = (f + g - e) % 7;
|
2722
|
+
n = f + 3 - d;
|
2723
|
+
|
2724
|
+
if (n < 0) w = 53 - ((g - s) / 5 | 0);
|
2725
|
+
|
2726
|
+
else if (n > 364 + s) w = 1;
|
2727
|
+
|
2728
|
+
else w = (n / 7 | 0) + 1;
|
2729
|
+
|
2730
|
+
return w;
|
2731
|
+
|
2732
|
+
};
|
2733
|
+
|
2734
|
+
/**
|
2735
|
+
* Function to be called when the "onKeyUp" event occurs
|
2736
|
+
*
|
2737
|
+
* Why as a separate function and not inline when binding the event? Because only this way we can "unbind" it
|
2738
|
+
* if the date picker is destroyed
|
2739
|
+
*
|
2740
|
+
* @return void
|
2741
|
+
*
|
2742
|
+
* @access private
|
2743
|
+
*/
|
2744
|
+
var _keyup = function(e) {
|
2745
|
+
|
2746
|
+
// if the date picker is visible
|
2747
|
+
// and the pressed key is ESC
|
2748
|
+
// hide the date picker
|
2749
|
+
if (datepicker.css('display') == 'block' || e.which == 27) plugin.hide();
|
2750
|
+
|
2751
|
+
};
|
2752
|
+
|
2753
|
+
/**
|
2754
|
+
* Function to be called when the "onMouseDown" event occurs
|
2755
|
+
*
|
2756
|
+
* Why as a separate function and not inline when binding the event? Because only this way we can "unbind" it
|
2757
|
+
* if the date picker is destroyed
|
2758
|
+
*
|
2759
|
+
* @return void
|
2760
|
+
*
|
2761
|
+
* @access private
|
2762
|
+
*/
|
2763
|
+
var _mousedown = function(e) {
|
2764
|
+
|
2765
|
+
// if the date picker is visible
|
2766
|
+
if (datepicker.css('display') == 'block') {
|
2767
|
+
|
2768
|
+
// if the calendar icon is visible and we clicked it, let the onClick event of the icon to handle the event
|
2769
|
+
// (we want it to toggle the date picker)
|
2770
|
+
if (plugin.settings.show_icon && $(e.target).get(0) === icon.get(0)) return true;
|
2771
|
+
|
2772
|
+
// if what's clicked is not inside the date picker
|
2773
|
+
// hide the date picker
|
2774
|
+
if ($(e.target).parents().filter('.Zebra_DatePicker').length === 0) plugin.hide();
|
2775
|
+
|
2776
|
+
}
|
2777
|
+
|
2778
|
+
};
|
2779
|
+
|
2780
|
+
/**
|
2781
|
+
* Function to be called when the "onResize" event occurs
|
2782
|
+
*
|
2783
|
+
* Why as a separate function and not inline when binding the event? Because only this way we can "unbind" it
|
2784
|
+
* if the date picker is destroyed
|
2785
|
+
*
|
2786
|
+
* @return void
|
2787
|
+
*
|
2788
|
+
* @access private
|
2789
|
+
*/
|
2790
|
+
var _resize = function() {
|
2791
|
+
|
2792
|
+
// hide the date picker
|
2793
|
+
plugin.hide();
|
2794
|
+
|
2795
|
+
// we use timeouts so that we do not call the "update" method on *every* step of the resize event
|
2796
|
+
|
2797
|
+
// clear a previously set timeout
|
2798
|
+
clearTimeout(timeout);
|
2799
|
+
|
2800
|
+
// set timeout again
|
2801
|
+
timeout = setTimeout(function() {
|
2802
|
+
|
2803
|
+
// update the date picker
|
2804
|
+
plugin.update();
|
2805
|
+
|
2806
|
+
}, 100);
|
2807
|
+
|
2808
|
+
};
|
2809
|
+
|
2810
|
+
// since with jQuery 1.9.0 the $.browser object was removed, we rely on this piece of code from
|
2811
|
+
// http://www.quirksmode.org/js/detect.html to detect the browser
|
2812
|
+
var browser = {
|
2813
|
+
init: function () {
|
2814
|
+
this.name = this.searchString(this.dataBrowser) || '';
|
2815
|
+
this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || '';
|
2816
|
+
},
|
2817
|
+
searchString: function (data) {
|
2818
|
+
for (var i=0;i<data.length;i++) {
|
2819
|
+
var dataString = data[i].string;
|
2820
|
+
var dataProp = data[i].prop;
|
2821
|
+
this.versionSearchString = data[i].versionSearch || data[i].identity;
|
2822
|
+
if (dataString) {
|
2823
|
+
if (dataString.indexOf(data[i].subString) != -1)
|
2824
|
+
return data[i].identity;
|
2825
|
+
}
|
2826
|
+
else if (dataProp)
|
2827
|
+
return data[i].identity;
|
2828
|
+
}
|
2829
|
+
},
|
2830
|
+
searchVersion: function (dataString) {
|
2831
|
+
var index = dataString.indexOf(this.versionSearchString);
|
2832
|
+
if (index == -1) return;
|
2833
|
+
return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
|
2834
|
+
},
|
2835
|
+
dataBrowser: [
|
2836
|
+
{
|
2837
|
+
string: navigator.userAgent,
|
2838
|
+
subString: 'Firefox',
|
2839
|
+
identity: 'firefox'
|
2840
|
+
},
|
2841
|
+
{
|
2842
|
+
string: navigator.userAgent,
|
2843
|
+
subString: 'MSIE',
|
2844
|
+
identity: 'explorer',
|
2845
|
+
versionSearch: 'MSIE'
|
2846
|
+
}
|
2847
|
+
]
|
2848
|
+
};
|
2849
|
+
|
2850
|
+
browser.init();
|
2851
|
+
|
2852
|
+
// initialize the plugin
|
2853
|
+
init();
|
2854
|
+
|
2855
|
+
};
|
2856
|
+
|
2857
|
+
$.fn.Zebra_DatePicker = function(options) {
|
2858
|
+
|
2859
|
+
// iterate through all the elements to which we need to attach the date picker to
|
2860
|
+
return this.each(function() {
|
2861
|
+
|
2862
|
+
// if element has a date picker already attached
|
2863
|
+
if (undefined !== $(this).data('Zebra_DatePicker'))
|
2864
|
+
|
2865
|
+
// remove the attached date picker
|
2866
|
+
$(this).data('Zebra_DatePicker').destroy();
|
2867
|
+
|
2868
|
+
// create an instance of the plugin
|
2869
|
+
var plugin = new $.Zebra_DatePicker(this, options);
|
2870
|
+
|
2871
|
+
// save a reference to the newly created object
|
2872
|
+
$(this).data('Zebra_DatePicker', plugin);
|
2873
|
+
|
2874
|
+
});
|
2875
|
+
|
2876
|
+
};
|
2877
|
+
|
2878
|
+
})(jQuery);
|