Package not found. Please check the package name and try again.

jquery-monthpicker-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.
data/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # jquery-monthpicker-rails
2
+
3
+ This gem packages the jQuery Monthpicker assets for the Rails 3.1+ asset pipeline.
4
+
5
+ # Usage
6
+
7
+ In your Gemfile, add:
8
+
9
+ ```ruby
10
+ gem "jquery-monthpicker-rails"
11
+ ```
12
+
13
+ Then require the javascript in your application.js
14
+
15
+ ```js
16
+ //= require jquery.ui.monthpicker
17
+ ```
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/jquery/monthpicker/rails/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "jquery-monthpicker-rails"
6
+ s.version = Jquery::Monthpicker::Rails::VERSION
7
+ s.authors = ["Charles Maresh"]
8
+ s.email = ["cmaresh@gmail.com"]
9
+ s.homepage = "https://github.com/cmaresh/jquery-monthpicker-rails"
10
+ s.summary = "jQuery Monthpicker packaged for the Rails asset pipeline"
11
+ s.description = "jQuery Monthpicker's JavaScript files packaged for the Rails 3.1+ asset pipeline"
12
+ s.license = "MIT"
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+
16
+ s.add_dependency "railties", ">= 3.1.0"
17
+ s.add_dependency "jquery-ui-rails", ">= 3.0"
18
+
19
+ s.files = `git ls-files`.split("\n").reject { |f| f =~ /^testapp/ }
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
21
+ s.require_path = 'lib'
22
+ end
@@ -0,0 +1 @@
1
+ require "jquery/monthpicker/rails"
@@ -0,0 +1,2 @@
1
+ require "jquery/monthpicker/rails/engine"
2
+ require "jquery/monthpicker/rails/version"
@@ -0,0 +1,8 @@
1
+ module Jquery
2
+ module Monthpicker
3
+ module Rails
4
+ class Engine < ::Rails::Engine
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module Jquery
2
+ module Monthpicker
3
+ module Rails
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,1451 @@
1
+ /*!
2
+ * jQuery UI Monthpicker
3
+ *
4
+ * MIT License
5
+ * Copyright (c) 2011, Julien Poumailloux
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software i
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+ (function ($) {
26
+
27
+ $.extend($.ui, { monthpicker: { version: "@VERSION" } });
28
+
29
+ var PROP_NAME = 'monthpicker';
30
+ var instActive;
31
+
32
+ /* Month picker manager.
33
+ Use the singleton instance of this class, $.monthpicker, to interact with the date picker.
34
+ Settings for (groups of) month pickers are maintained in an instance object,
35
+ allowing multiple different settings on the same page. */
36
+
37
+ function Monthpicker() {
38
+ this.uuid = 0;
39
+ this._keyEvent = false; // If the last event was a key event
40
+ this._curInst = null; // The current instance in use
41
+ this._disabledInputs = []; // List of date picker inputs that have been disabled
42
+ this._monthpickerShowing = false; // True if the popup picker is showing , false if not
43
+ this._mainDivId = 'ui-monthpicker-div'; // The ID of the main monthpicker division
44
+ this._triggerClass = 'ui-monthpicker-trigger'; // The name of the trigger marker class
45
+ this._dialogClass = 'ui-monthpicker-dialog'; // The name of the dialog marker class
46
+ this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
47
+ this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
48
+ this.regional = []; // Available regional settings, indexed by language code
49
+ this.regional[''] = { // Default regional settings
50
+ closeText: "Done", // Display text for close link
51
+ prevText: 'Prev', // Display text for previous month link
52
+ nextText: 'Next', // Display text for next month link
53
+ currentText: "Current", // Display text for current month link
54
+ monthNames: ['January','February','March','April','May','June',
55
+ 'July','August','September','October','November','December'], // Names of months for drop-down and formatting
56
+ monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
57
+ dateFormat: 'mm/yy',
58
+ isRTL: false,
59
+ yearSuffix: '' // Additional text to append to the year in the month headers
60
+ };
61
+ this._defaults = { // Global defaults for all the date picker instances
62
+ showOn: 'focus', // 'focus' for popup on focus,
63
+ // 'button' for trigger button, or 'both' for either
64
+ showAnim: 'fadeIn', // Name of jQuery animation for popup
65
+ showButtonPanel: false, // True to show button panel, false to not show it
66
+ appendText: "", // Display text following the input box, e.g. showing the format
67
+ buttonText: '...', // Text for trigger button
68
+ buttonImage: '', // URL for trigger button image
69
+ gotoCurrent: false, // True if today link goes back to current selection instead
70
+ changeYear: false, // True if year can be selected directly, false if only prev/next
71
+ navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
72
+ yearRange: 'c-10:c+10', // Range of years to display in drop-down,
73
+ // either relative to today's year (-nn:+nn), relative to currently displayed year
74
+ // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
75
+ beforeShow: null, // Function that takes an input field and
76
+ // returns a set of custom settings for the date picker
77
+ onSelect: null, // Define a callback function when a date is selected
78
+ onChangeYear: null, // Define a callback function when the year is changed
79
+ onClose: null, // Define a callback function when the monthpicker is closed
80
+ stepYears: 1, // Number of months to step back/forward
81
+ stepBigYears: 3, // Number of months to step back/forward
82
+ defaultDate: null, // Used when field is blank: actual date,
83
+ minDate: null, // The earliest selectable date, or null for no limit
84
+ maxDate: null, // The latest selectable date, or null for no limit
85
+ altField: '', // Selector for an alternate field to store selected dates into
86
+ altFormat: '', // The date format to use for the alternate field
87
+ disabled: false // The initial disabled state
88
+ };
89
+ $.extend(this._defaults, this.regional['']);
90
+ this.dpDiv = bindHover($('<div id="' + this._mainDivId + '" class="ui-monthpicker ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'));
91
+ }
92
+
93
+ $.extend(Monthpicker.prototype, {
94
+ /* Class name added to elements to indicate already configured with a date picker. */
95
+ markerClassName: 'hasMonthpicker',
96
+
97
+ /* Debug logging (if enabled). */
98
+ log: function () {
99
+ if (this.debug)
100
+ console.log.apply('', arguments);
101
+ },
102
+
103
+ // TODO rename to "widget" when switching to widget factory
104
+ _widgetMonthpicker: function() {
105
+ return this.dpDiv;
106
+ },
107
+
108
+ /* Override the default settings for all instances of the date picker.
109
+ * @param settings object - the new settings to use as defaults (anonymous object)
110
+ * @return the manager object
111
+ */
112
+ setDefaults: function(settings) {
113
+ extendRemove(this._defaults, settings || {});
114
+ return this;
115
+ },
116
+
117
+
118
+ /* Retrieve the instance data for the target control.
119
+ @param target element - the target input field or division or span
120
+ @return object - the associated instance data
121
+ @throws error if a jQuery problem getting data */
122
+ _getInst: function(target) {
123
+ try {
124
+ return $.data(target, PROP_NAME);
125
+ }
126
+ catch (err) {
127
+ throw 'Missing instance data for this monthpicker';
128
+ }
129
+ },
130
+
131
+ /* Get a setting value, defaulting if necessary. */
132
+ _get: function(inst, name) {
133
+ return inst.settings[name] !== undefined ?
134
+ inst.settings[name] : this._defaults[name];
135
+ },
136
+
137
+ /* Attach the month picker to a jQuery selection.
138
+ @param target element - the target input field or division or span
139
+ @param settings object - the new settings to use for this month picker instance (anonymous) */
140
+ _attachMonthpicker: function(target, settings) {
141
+ // check for settings on the control itself - in namespace 'month:'
142
+ var inlineSettings = null;
143
+ for (var attrName in this._defaults) {
144
+ var attrValue = target.getAttribute('month:' + attrName);
145
+ if (attrValue) {
146
+ inlineSettings = inlineSettings || {};
147
+ try {
148
+ inlineSettings[attrName] = eval(attrValue);
149
+ } catch (err) {
150
+ inlineSettings[attrName] = attrValue;
151
+ }
152
+ }
153
+ }
154
+ var nodeName = target.nodeName.toLowerCase();
155
+ if (!target.id) {
156
+ this.uuid += 1;
157
+ target.id = 'dp' + this.uuid;
158
+ }
159
+ var inst = this._newInst($(target));
160
+ inst.settings = $.extend({}, settings || {}, inlineSettings || {});
161
+ if (nodeName == 'input') {
162
+ this._connectMonthpicker(target, inst);
163
+ }
164
+ },
165
+
166
+ /* Create a new instance object. */
167
+ _newInst: function(target) {
168
+ var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
169
+ return {id: id, input: target, // associated target
170
+ selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
171
+ drawMonth: 0, drawYear: 0, // month being drawn
172
+ dpDiv: this.dpDiv // presentation div
173
+ };
174
+ },
175
+
176
+ /* Attach the date picker to an input field. */
177
+ _connectMonthpicker: function(target, inst) {
178
+ var input = $(target);
179
+ inst.append = $([]);
180
+ inst.trigger = $([]);
181
+ if (input.hasClass(this.markerClassName))
182
+ return;
183
+ this._attachments(input, inst);
184
+ input.addClass(this.markerClassName).keydown(this._doKeyDown).
185
+ keypress(this._doKeyPress).keyup(this._doKeyUp).
186
+ bind("setData.monthpicker", function(event, key, value) {
187
+ inst.settings[key] = value;
188
+ }).bind("getData.monthpicker", function(event, key) {
189
+ return this._get(inst, key);
190
+ });
191
+ //this._autoSize(inst);
192
+ $.data(target, PROP_NAME, inst);
193
+ //If disabled option is true, disable the monthpicker once it has been attached to the input (see ticket #5665)
194
+ if( inst.settings.disabled ) {
195
+ this._disableMonthpicker( target );
196
+ }
197
+ },
198
+
199
+ /* Make attachments based on settings. */
200
+ _attachments: function(input, inst) {
201
+ var appendText = this._get(inst, "appendText"),
202
+ isRTL = this._get(inst, "isRTL");
203
+
204
+ if (appendText) {
205
+ inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
206
+ input[isRTL ? "before" : "after"](inst.append);
207
+ }
208
+
209
+ input.unbind('focus', this._showMonthpicker);
210
+ if (inst.trigger)
211
+ inst.trigger.remove();
212
+ var showOn = this._get(inst, 'showOn');
213
+ if (showOn == 'focus' || showOn == 'both') // pop-up month picker when in the marked field
214
+ input.focus(this._showMonthpicker);
215
+ if (showOn === "button" || showOn === "both") { // pop-up month picker when button clicked
216
+ buttonText = this._get(inst, "buttonText");
217
+ buttonImage = this._get(inst, "buttonImage");
218
+ inst.trigger = $(this._get(inst, "buttonImageOnly") ?
219
+ $("<img/>").addClass(this._triggerClass).
220
+ attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
221
+ $("<button type='button'></button>").addClass(this._triggerClass).
222
+ html(!buttonImage ? buttonText : $("<img/>").attr(
223
+ { src:buttonImage, alt:buttonText, title:buttonText })));
224
+ input[isRTL ? "before" : "after"](inst.trigger);
225
+ inst.trigger.click(function() {
226
+ if ($.monthpicker._monthpickerShowing && $.monthpicker._lastInput === input[0]) {
227
+ $.monthpicker._hideMonthpicker();
228
+ } else if ($.monthpicker._monthpickerShowing && $.monthpicker._lastInput !== input[0]) {
229
+ $.monthpicker._hideMonthpicker();
230
+ $.monthpicker._showMonthpicker(input[0]);
231
+ } else {
232
+ $.monthpicker._showMonthpicker(input[0]);
233
+ }
234
+ return false;
235
+ });
236
+ }
237
+ },
238
+
239
+ /* Close month picker if clicked elsewhere. */
240
+ _checkExternalClick: function(event) {
241
+ if (!$.monthpicker._curInst)
242
+ return;
243
+
244
+ var $target = $(event.target),
245
+ inst = $.monthpicker._getInst($target[0]);
246
+
247
+ if ( ( ( $target[0].id != $.monthpicker._mainDivId &&
248
+ $target.parents('#' + $.monthpicker._mainDivId).length == 0 &&
249
+ !$target.hasClass($.monthpicker.markerClassName) &&
250
+ !$target.hasClass($.monthpicker._triggerClass) &&
251
+ $.monthpicker._monthpickerShowing ) ) ||
252
+ ( $target.hasClass($.monthpicker.markerClassName) && $.monthpicker._curInst != inst ) )
253
+ $.monthpicker._hideMonthpicker();
254
+ },
255
+
256
+ /* Pop-up the month picker for a given input field.
257
+ If false returned from beforeShow event handler do not show.
258
+ @param input element - the input field attached to the date picker or
259
+ event - if triggered by focus */
260
+ _showMonthpicker: function(input) {
261
+ input = input.target || input;
262
+ if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
263
+ input = $('input', input.parentNode)[0];
264
+ if ($.monthpicker._isDisabledMonthpicker(input) || $.monthpicker._lastInput == input) // already here
265
+ return;
266
+ var inst = $.monthpicker._getInst(input);
267
+ if ($.monthpicker._curInst && $.monthpicker._curInst != inst) {
268
+ $.monthpicker._curInst.dpDiv.stop(true, true);
269
+ if ( inst && $.monthpicker._monthpickerShowing ) {
270
+ $.monthpicker._hideMonthpicker( $.monthpicker._curInst.input[0] );
271
+ }
272
+ }
273
+ var beforeShow = $.monthpicker._get(inst, 'beforeShow');
274
+ var beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
275
+ if(beforeShowSettings === false){
276
+ //false
277
+ return;
278
+ }
279
+ extendRemove(inst.settings, beforeShowSettings);
280
+ inst.lastVal = null;
281
+ $.monthpicker._lastInput = input;
282
+ $.monthpicker._setDateFromField(inst);
283
+ if ($.monthpicker._inDialog) // hide cursor
284
+ input.value = '';
285
+ if (!$.monthpicker._pos) { // position below input
286
+ $.monthpicker._pos = $.monthpicker._findPos(input);
287
+ $.monthpicker._pos[1] += input.offsetHeight; // add the height
288
+ }
289
+ var isFixed = false;
290
+ $(input).parents().each(function() {
291
+ isFixed |= $(this).css('position') == 'fixed';
292
+ return !isFixed;
293
+ });
294
+ if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
295
+ $.monthpicker._pos[0] -= document.documentElement.scrollLeft;
296
+ $.monthpicker._pos[1] -= document.documentElement.scrollTop;
297
+ }
298
+ var offset = {left: $.monthpicker._pos[0], top: $.monthpicker._pos[1]};
299
+ $.monthpicker._pos = null;
300
+ //to avoid flashes on Firefox
301
+ inst.dpDiv.empty();
302
+ // determine sizing offscreen
303
+ inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
304
+ $.monthpicker._updateMonthpicker(inst);
305
+ // fix width for dynamic number of date pickers
306
+ // and adjust position before showing
307
+ offset = $.monthpicker._checkOffset(inst, offset, isFixed);
308
+ inst.dpDiv.css({position: ($.monthpicker._inDialog && $.blockUI ?
309
+ 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
310
+ left: offset.left + 'px', top: offset.top + 'px'});
311
+ var showAnim = $.monthpicker._get(inst, 'showAnim');
312
+ var duration = $.monthpicker._get(inst, 'duration');
313
+ var postProcess = function() {
314
+ var cover = inst.dpDiv.find('iframe.ui-monthpicker-cover'); // IE6- only
315
+ if( !! cover.length ){
316
+ var borders = $.monthpicker._getBorders(inst.dpDiv);
317
+ cover.css({left: -borders[0], top: -borders[1],
318
+ width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
319
+ }
320
+ };
321
+ inst.dpDiv.zIndex($(input).zIndex()+1);
322
+ $.monthpicker._monthpickerShowing = true;
323
+
324
+ if ($.effects && $.effects[showAnim])
325
+ inst.dpDiv.show(showAnim, $.monthpicker._get(inst, 'showOptions'), duration, postProcess);
326
+ else
327
+ inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
328
+ if (!showAnim || !duration)
329
+ postProcess();
330
+ if (inst.input.is(':visible') && !inst.input.is(':disabled'))
331
+ inst.input.focus();
332
+ $.monthpicker._curInst = inst;
333
+ },
334
+
335
+ /* Generate the date picker content. */
336
+ _updateMonthpicker: function(inst) {
337
+
338
+ var self = this;
339
+ self.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
340
+ var borders = $.monthpicker._getBorders(inst.dpDiv);
341
+ instActive = inst; // for delegate hover events
342
+ inst.dpDiv.empty().append(this._generateHTML(inst));
343
+ this._attachHandlers(inst);
344
+ var cover = inst.dpDiv.find('iframe.ui-monthpicker-cover'); // IE6- only
345
+ if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6
346
+ cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
347
+ }
348
+ inst.dpDiv.find('.' + this._dayOverClass + ' a').mouseover();
349
+
350
+ if (inst == $.monthpicker._curInst && $.monthpicker._monthpickerShowing && inst.input &&
351
+ // #6694 - don't focus the input if it's already focused
352
+ // this breaks the change event in IE
353
+ inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement)
354
+ inst.input.focus();
355
+ // deffered render of the years select (to avoid flashes on Firefox)
356
+ if( inst.yearshtml ){
357
+ var origyearshtml = inst.yearshtml;
358
+ setTimeout(function(){
359
+ //assure that inst.yearshtml didn't change.
360
+ if( origyearshtml === inst.yearshtml && inst.yearshtml ){
361
+ inst.dpDiv.find('select.ui-monthpicker-year:first').replaceWith(inst.yearshtml);
362
+ }
363
+ origyearshtml = inst.yearshtml = null;
364
+ }, 0);
365
+ }
366
+ },
367
+
368
+ /* Trigger custom callback of onClose. */
369
+ _triggerOnClose: function(inst) {
370
+ var onClose = this._get(inst, 'onClose');
371
+ if (onClose)
372
+ onClose.apply((inst.input ? inst.input[0] : null),
373
+ [(inst.input ? inst.input.val() : ''), inst]);
374
+ },
375
+
376
+ /* Hide the month picker from view.
377
+ @param input element - the input field attached to the month picker */
378
+ _hideMonthpicker: function(input) {
379
+ var inst = this._curInst;
380
+ if (!inst || (input && inst != $.data(input, PROP_NAME)))
381
+ return;
382
+ if (this._monthpickerShowing) {
383
+ var showAnim = this._get(inst, 'showAnim');
384
+ var duration = this._get(inst, 'duration');
385
+ var postProcess = function() {
386
+ $.monthpicker._tidyDialog(inst);
387
+ $.monthpicker._curInst = null;
388
+ };
389
+
390
+ if ( $.effects && $.effects[ showAnim ] )
391
+ inst.dpDiv.hide(showAnim, $.monthpicker._get(inst, 'showOptions'), duration, postProcess);
392
+ else
393
+ inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' :
394
+ (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
395
+ if (!showAnim)
396
+ postProcess();
397
+ $.monthpicker._triggerOnClose(inst);
398
+ this._monthpickerShowing = false;
399
+ this._lastInput = null;
400
+ if (this._inDialog) {
401
+ this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
402
+ if ($.blockUI) {
403
+ $.unblockUI();
404
+ $('body').append(this.dpDiv);
405
+ }
406
+ }
407
+ this._inDialog = false;
408
+ }
409
+ },
410
+
411
+ /* Handle keystrokes. */
412
+ _doKeyDown: function(event) {
413
+ var onSelect, dateStr, sel,
414
+ inst = $.monthpicker._getInst(event.target),
415
+ handled = true,
416
+ isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
417
+
418
+ inst._keyEvent = true;
419
+ if ($.monthpicker._monthpickerShowing) {
420
+ switch (event.keyCode) {
421
+ case 9: $.monthpicker._hideMonthpicker();
422
+ handled = false;
423
+ break; // hide on tab out
424
+ case 13: sel = $("td." + $.monthpicker._dayOverClass + ":not(." +
425
+ $.monthpicker._currentClass + ")", inst.dpDiv);
426
+
427
+ if (sel[0]) {
428
+ $.monthpicker._selectMonth(event.target, inst.selectedYear, inst.selectedMonth, sel[0]);
429
+ }
430
+
431
+ onSelect = $.monthpicker._get(inst, "onSelect");
432
+ if (onSelect) {
433
+ dateStr = $.monthpicker._formatDate(inst);
434
+
435
+ // trigger custom callback
436
+ onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
437
+ } else {
438
+ $.monthpicker._hideMonthpicker();
439
+ }
440
+
441
+ return false; // don't submit the form
442
+ case 27: $.monthpicker._hideMonthpicker();
443
+ break; // hide on escape
444
+ case 33: $.monthpicker._adjustDate(event.target, (event.ctrlKey ?
445
+ -$.monthpicker._get(inst, "stepBigYears") :
446
+ -$.monthpicker._get(inst, "stepYears")), "Y");
447
+ break; // previous year on page up/+ ctrl
448
+ case 34: $.monthpicker._adjustDate(event.target, (event.ctrlKey ?
449
+ +$.monthpicker._get(inst, "stepBigYears") :
450
+ +$.monthpicker._get(inst, "stepYears")), "Y");
451
+ break; // next year on page down/+ ctrl
452
+ case 35: if (event.ctrlKey || event.metaKey) {
453
+ $.monthpicker._clearDate(event.target);
454
+ }
455
+ handled = event.ctrlKey || event.metaKey;
456
+ break; // clear on ctrl or command +end
457
+ case 36: if (event.ctrlKey || event.metaKey) {
458
+ $.monthpicker._gotoCurrent(event.target);
459
+ }
460
+ handled = event.ctrlKey || event.metaKey;
461
+ break; // current on ctrl or command +home
462
+ case 37: if (event.ctrlKey || event.metaKey) {
463
+ $.monthpicker._adjustDate(event.target, (isRTL ? +1 : -1), "M");
464
+ }
465
+ handled = event.ctrlKey || event.metaKey;
466
+ // -1 month on ctrl or command +left
467
+ if (event.originalEvent.altKey) {
468
+ $.monthpicker._adjustDate(event.target, (event.ctrlKey ?
469
+ -$.monthpicker._get(inst, "stepBigYears") :
470
+ -$.monthpicker._get(inst, "stepYears")), "Y");
471
+ }
472
+ // next year on alt +left on Mac
473
+ break;
474
+ case 38: if (event.ctrlKey || event.metaKey) {
475
+ $.monthpicker._adjustDate(event.target, -3, "M");
476
+ }
477
+ handled = event.ctrlKey || event.metaKey;
478
+ break; // -1 quarter on ctrl or command +up
479
+ case 39: if (event.ctrlKey || event.metaKey) {
480
+ $.monthpicker._adjustDate(event.target, (isRTL ? -1 : +1), "M");
481
+ }
482
+ handled = event.ctrlKey || event.metaKey;
483
+ // +1 month on ctrl or command +right
484
+ if (event.originalEvent.altKey) {
485
+ $.monthpicker._adjustDate(event.target, (event.ctrlKey ?
486
+ +$.monthpicker._get(inst, "stepBigYears") :
487
+ +$.monthpicker._get(inst, "stepYears")), "Y");
488
+ }
489
+ // next year on alt +right
490
+ break;
491
+ case 40: if (event.ctrlKey || event.metaKey) {
492
+ $.monthpicker._adjustDate(event.target, +3, "M");
493
+ }
494
+ handled = event.ctrlKey || event.metaKey;
495
+ break; // +1 quarter on ctrl or command +down
496
+ default: handled = false;
497
+ }
498
+ } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
499
+ $.monthpicker._showMonthpicker(this);
500
+ } else {
501
+ handled = false;
502
+ }
503
+
504
+ if (handled) {
505
+ event.preventDefault();
506
+ event.stopPropagation();
507
+ }
508
+ },
509
+
510
+ /* Is the first field in a jQuery collection disabled as a monthpicker?
511
+ @param target element - the target input field or division or span
512
+ @return boolean - true if disabled, false if enabled */
513
+ _isDisabledMonthpicker: function(target) {
514
+ if (!target) {
515
+ return false;
516
+ }
517
+ for (var i = 0; i < this._disabledInputs.length; i++) {
518
+ if (this._disabledInputs[i] == target)
519
+ return true;
520
+ }
521
+ return false;
522
+ },
523
+
524
+ /* Tidy up after a dialog display. */
525
+ _tidyDialog: function(inst) {
526
+ inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-monthpicker-calendar');
527
+ },
528
+
529
+ /* Retrieve the size of left and top borders for an element.
530
+ @param elem (jQuery object) the element of interest
531
+ @return (number[2]) the left and top borders */
532
+ _getBorders: function(elem) {
533
+ var convert = function(value) {
534
+ return {thin: 1, medium: 2, thick: 3}[value] || value;
535
+ };
536
+ return [parseFloat(convert(elem.css('border-left-width'))),
537
+ parseFloat(convert(elem.css('border-top-width')))];
538
+ },
539
+
540
+ /* Check positioning to remain on screen. */
541
+ _checkOffset: function(inst, offset, isFixed) {
542
+ var dpWidth = inst.dpDiv.outerWidth();
543
+ var dpHeight = inst.dpDiv.outerHeight();
544
+ var inputWidth = inst.input ? inst.input.outerWidth() : 0;
545
+ var inputHeight = inst.input ? inst.input.outerHeight() : 0;
546
+ var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
547
+ var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
548
+
549
+ offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
550
+ offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
551
+
552
+ // now check if monthpicker is showing outside window viewport - move to a better place if so.
553
+ offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
554
+ Math.abs(offset.left + dpWidth - viewWidth) : 0);
555
+ offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
556
+ Math.abs(dpHeight + inputHeight) : 0);
557
+
558
+ return offset;
559
+ },
560
+
561
+ /* Find an object's position on the screen. */
562
+ _findPos: function(obj) {
563
+ var inst = this._getInst(obj);
564
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) {
565
+ obj = obj['nextSibling'];
566
+ }
567
+ var position = $(obj).offset();
568
+ return [position.left, position.top];
569
+ },
570
+
571
+ /* Adjust one of the month sub-fields. */
572
+ _adjustDate: function(id, offset, period) {
573
+ var target = $(id);
574
+ var inst = this._getInst(target[0]);
575
+ if (this._isDisabledMonthpicker(target[0])) {
576
+ return;
577
+ }
578
+ this._adjustInstDate(inst, offset, period);
579
+ this._updateMonthpicker(inst);
580
+ },
581
+
582
+ /* Adjust one of the date sub-fields. */
583
+ _adjustInstDate: function(inst, offset, period) {
584
+ var year = inst.drawYear + (period == 'Y' ? offset : 0);
585
+ var month = Math.min(inst.selectedMonth, 12) + (period === "M" ? offset : 0);
586
+ var date = this._restrictMinMax(inst, new Date(year, month, 1));
587
+ inst.drawYear = inst.selectedYear = date.getFullYear();
588
+ inst.selectedMonth = date.getMonth();
589
+
590
+ if (period == 'Y') {
591
+ this._notifyChange(inst);
592
+ }
593
+ },
594
+
595
+ /* Notify change of month/year. */
596
+ _notifyChange: function(inst) {
597
+ var onChange = this._get(inst, 'onChangeYear');
598
+ if (onChange)
599
+ onChange.apply((inst.input ? inst.input[0] : null), [inst.selectedYear, inst]);
600
+ },
601
+
602
+ /* Detach a datepicker from its control.
603
+ * @param target element - the target input field or division or span
604
+ */
605
+ _destroyMonthpicker: function(target) {
606
+ var nodeName,
607
+ $target = $(target),
608
+ inst = $.data(target, PROP_NAME);
609
+
610
+ if (!$target.hasClass(this.markerClassName)) {
611
+ return;
612
+ }
613
+
614
+ nodeName = target.nodeName.toLowerCase();
615
+ $.removeData(target, PROP_NAME);
616
+ if (nodeName === "input") {
617
+ inst.append.remove();
618
+ inst.trigger.remove();
619
+ $target.removeClass(this.markerClassName).
620
+ unbind("focus", this._showMonthpicker).
621
+ unbind("keydown", this._doKeyDown).
622
+ unbind("keypress", this._doKeyPress).
623
+ unbind("keyup", this._doKeyUp);
624
+ } else if (nodeName === "div" || nodeName === "span") {
625
+ $target.removeClass(this.markerClassName).empty();
626
+ }
627
+ },
628
+
629
+ /* Enable the date picker to a jQuery selection.
630
+ @param target element - the target input field or division or span */
631
+ _enableMonthpicker: function(target) {
632
+ var $target = $(target);
633
+ var inst = $.data(target, PROP_NAME);
634
+ if (!$target.hasClass(this.markerClassName)) {
635
+ return;
636
+ }
637
+ var nodeName = target.nodeName.toLowerCase();
638
+ if (nodeName == 'input') {
639
+ target.disabled = false;
640
+ inst.trigger.filter('button').
641
+ each(function() { this.disabled = false; }).end().
642
+ filter('img').css({opacity: '1.0', cursor: ''});
643
+ }
644
+ this._disabledInputs = $.map(this._disabledInputs,
645
+ function(value) { return (value == target ? null : value); }); // delete entry
646
+ },
647
+
648
+ /* Disable the month picker to a jQuery selection.
649
+ @param target element - the target input field or division or span */
650
+ _disableMonthpicker: function(target) {
651
+ var $target = $(target);
652
+ var inst = $.data(target, PROP_NAME);
653
+ if (!$target.hasClass(this.markerClassName)) {
654
+ return;
655
+ }
656
+ var nodeName = target.nodeName.toLowerCase();
657
+ if (nodeName == 'input') {
658
+ target.disabled = true;
659
+ inst.trigger.filter('button').
660
+ each(function() { this.disabled = true; }).end().
661
+ filter('img').css({opacity: '0.5', cursor: 'default'});
662
+ }
663
+ this._disabledInputs = $.map(this._disabledInputs,
664
+ function(value) { return (value == target ? null : value); }); // delete entry
665
+ this._disabledInputs[this._disabledInputs.length] = target;
666
+ },
667
+
668
+ /* Generate the HTML for the current state of the date picker. */
669
+ _generateHTML: function(inst) {
670
+ var printDate, hideIfNoPrevNext;
671
+
672
+ hideIfNoPrevNext = false;
673
+ var today = new Date();
674
+ today = new Date(today.getFullYear(), today.getMonth(), today.getDate()); // clear time
675
+ var currentDate = (!inst.currentMonth ? new Date(9999, 9, 9) :
676
+ new Date(inst.currentYear, inst.currentMonth, 1));
677
+ var html = '';
678
+ var year = currentDate && currentDate.year ? currentDate.year : 2011;
679
+ var prevText = this._get(inst, 'prevText');
680
+ var nextText = this._get(inst, 'nextText');
681
+ var stepYears = this._get(inst, 'stepYears');
682
+ var monthNames = this._get(inst, 'monthNames');
683
+ var monthNamesShort = this._get(inst, 'monthNamesShort');
684
+ var drawYear = inst.drawYear;
685
+ var showButtonPanel = this._get(inst, "showButtonPanel");
686
+ var isRTL = this._get(inst, "isRTL");
687
+ var defaultDate = this._getDefaultDate(inst);
688
+ var navigationAsDateFormat = this._get(inst, "navigationAsDateFormat");
689
+ defaultDate.setDate(1);
690
+
691
+ prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
692
+ new Date(drawYear - stepYears, 1, 1),
693
+ this._getFormatConfig(inst)));
694
+ nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
695
+ new Date(drawYear + stepYears, 1, 1),
696
+ this._getFormatConfig(inst)));
697
+
698
+ var prev = (this._canAdjustYear(inst, -1, drawYear) ?
699
+ "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
700
+ " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
701
+ (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+ prevText +"'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>"));
702
+
703
+ var next = (this._canAdjustYear(inst, +1, drawYear) ?
704
+ "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
705
+ " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
706
+ (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+ nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>"));
707
+
708
+ html += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix ui-corner-all">' +
709
+ prev + next + this._generateYearHeader(inst, drawYear) + // draw year header
710
+ '</div><table class="ui-datepicker-calendar"><tbody>';
711
+
712
+ // draw months table
713
+ for(var month = 0; month <= 11; month++) {
714
+ if (month % 3 === 0) {
715
+ html += '<tr>';
716
+ }
717
+
718
+ printDate = new Date(drawYear, month, 1);
719
+ var selectedDate = new Date(drawYear, inst.selectedMonth, 1);
720
+
721
+ html += '<td class="'
722
+ + (drawYear == inst.currentYear && month == inst.currentMonth ? " " + this._currentClass : "") // highlight selected month
723
+ + ((month === inst.selectedMonth && drawYear === inst.selectedYear && inst._keyEvent) || // user pressed key
724
+ (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
725
+ // or defaultDate is current printedDate and defaultDate is selectedDate
726
+ " " + this._dayOverClass : "") // highlight selected day
727
+ + '" data-month="' + month + '" data-year="' + drawYear + '" data-handler="selectMonth" data-event="click">'
728
+ + '<a class="ui-state-default'
729
+ + (drawYear == inst.currentYear && month == inst.currentMonth ? ' ui-state-active' : '') // highlight selected month
730
+ + (drawYear == today.getFullYear() && month == today.getMonth() ? ' ui-state-highlight' : '') // highlight today (if different)
731
+ + '" href="#">' + (inst.settings && inst.settings.monthNamesShort ? inst.settings.monthNamesShort[month] : this._defaults.monthNamesShort[month]) + '</a>' + '</td>'; // display selectable date
732
+
733
+ if (month % 3 === 2) {
734
+ html += '</tr>';
735
+ }
736
+ }
737
+ html += '</tbody></table>';
738
+
739
+ var controls = "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" +
740
+ this._get(inst, "closeText") + "</button>";
741
+
742
+ var currentText = this._get(inst, "currentText");
743
+ var gotoDate = (this._get(inst, "gotoCurrent") && inst.currentMonth ? currentDate : today);
744
+ currentText = (!navigationAsDateFormat ? currentText :
745
+ this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
746
+
747
+ var buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
748
+ "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='current' data-event='click'" +
749
+ ">" + currentText + "</button>" + (isRTL ? "" : controls) + "</div>" : "";
750
+
751
+ html += buttonPanel;
752
+
753
+ return html;
754
+ },
755
+
756
+ /* Generate the year header. */
757
+ _generateYearHeader: function(inst, drawYear) {
758
+ var changeYear = this._get(inst, 'changeYear');
759
+ var html = '<div class="ui-datepicker-title">';
760
+ // year selection
761
+ if ( !inst.yearshtml ) {
762
+ inst.yearshtml = '';
763
+
764
+ if (changeYear) {
765
+ // determine range of years to display
766
+ var years = this._get(inst, 'yearRange').split(':');
767
+ var thisYear = new Date().getFullYear();
768
+ var determineYear = function(value) {
769
+ var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
770
+ (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
771
+ parseInt(value, 10)));
772
+ return (isNaN(year) ? thisYear : year);
773
+ };
774
+ var year = determineYear(years[0]);
775
+ var endYear = Math.max(year, determineYear(years[1] || ''));
776
+
777
+ inst.yearshtml += '<select class="ui-datepicker-year" ' +
778
+ "data-handler='selectYear' data-event='change'>";
779
+
780
+ for (; year <= endYear; year++) {
781
+ inst.yearshtml += '<option value="' + year + '"' +
782
+ (year == drawYear ? ' selected="selected"' : '') +
783
+ '>' + year + '</option>';
784
+ }
785
+ inst.yearshtml += '</select>';
786
+ } else {
787
+ inst.yearshtml += '<span class="ui-datepicker-year">' + drawYear + '</span>';
788
+ }
789
+
790
+ html += inst.yearshtml;
791
+ inst.yearshtml = null;
792
+ }
793
+ html += this._get(inst, 'yearSuffix');
794
+ html += '</div>'; // Close monthpicker_header
795
+ return html;
796
+ },
797
+
798
+ /* Provide the configuration settings for formatting/parsing. */
799
+ _getFormatConfig: function(inst) {
800
+ var shortYearCutoff = this._get(inst, 'shortYearCutoff');
801
+ shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
802
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
803
+ return {shortYearCutoff: shortYearCutoff,
804
+ dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
805
+ monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
806
+ },
807
+
808
+ /* Parse existing date and initialise date picker. */
809
+ _setDateFromField: function(inst, noDefault) {
810
+ if (inst.input.val() == inst.lastVal) {
811
+ return;
812
+ }
813
+ var dateFormat = this._get(inst, 'dateFormat');
814
+ var dates = inst.lastVal = inst.input ? inst.input.val() : null;
815
+ var date, defaultDate;
816
+ date = defaultDate = this._getDefaultDate(inst);
817
+ var settings = this._getFormatConfig(inst);
818
+ try {
819
+ date = this.parseDate(dateFormat, dates, settings) || defaultDate;
820
+ } catch (event) {
821
+ this.log(event);
822
+ dates = (noDefault ? '' : dates);
823
+ }
824
+ inst.selectedMonth = date.getMonth();
825
+ inst.drawYear = inst.selectedYear = date.getFullYear();
826
+ inst.currentMonth = (dates ? date.getMonth() : 0);
827
+ inst.currentYear = (dates ? date.getFullYear() : 0);
828
+ this._adjustInstDate(inst);
829
+ },
830
+
831
+ /* Retrieve the default date shown on opening. */
832
+ _getDefaultDate: function(inst) {
833
+ return this._restrictMinMax(inst,
834
+ this._determineDate(inst, this._get(inst, 'defaultDate'), new Date()));
835
+ },
836
+
837
+ /* A date may be specified as an exact value or a relative one. */
838
+ _determineDate: function(inst, date, defaultDate) {
839
+ var offsetNumeric = function(offset) {
840
+ var date = new Date();
841
+ date.setDate(1);
842
+ date.setMonth(date.getMonth() + offset);
843
+ return date;
844
+ };
845
+ var offsetString = function(offset) {
846
+ try {
847
+ return $.monthpicker.parseDate($.monthpicker._get(inst, 'dateFormat'),
848
+ offset, $.monthpicker._getFormatConfig(inst));
849
+ }
850
+ catch (e) {
851
+ // Ignore
852
+ }
853
+ var date = (offset.toLowerCase().match(/^c/) ?
854
+ $.monthpicker._getDate(inst) : null) || new Date();
855
+ var year = date.getFullYear();
856
+ var month = date.getMonth();
857
+ var day = 1;
858
+ var pattern = /([+-]?[0-9]+)\s*(m|M|y|Y)?/g;
859
+ var matches = pattern.exec(offset);
860
+ while (matches) {
861
+ switch (matches[2] || 'm') {
862
+ case 'm' : case 'M' :
863
+ month += parseInt(matches[1],10);
864
+ day = 1
865
+ break;
866
+ case 'y': case 'Y' :
867
+ year += parseInt(matches[1],10);
868
+ day = 1
869
+ break;
870
+ }
871
+ matches = pattern.exec(offset);
872
+ }
873
+ return new Date(year, month, day);
874
+ };
875
+ var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) :
876
+ (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
877
+ newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate);
878
+ if (newDate) {
879
+ newDate.setHours(0);
880
+ newDate.setMinutes(0);
881
+ newDate.setSeconds(0);
882
+ newDate.setMilliseconds(0);
883
+ }
884
+ return newDate;
885
+ },
886
+
887
+ /* Determine the current maximum date - ensure no time components are set. */
888
+ _getMinMaxDate: function(inst, minMax) {
889
+ return this._determineDate(inst, this._get(inst, minMax + 'Date'), null);
890
+ },
891
+
892
+ /* Ensure a date is within any min/max bounds. */
893
+ _restrictMinMax: function(inst, date) {
894
+ var minDate = this._getMinMaxDate(inst, 'min');
895
+ var maxDate = this._getMinMaxDate(inst, 'max');
896
+ var newDate = (minDate && date < minDate ? minDate : date);
897
+ newDate = (maxDate && newDate > maxDate ? maxDate : newDate);
898
+ return newDate;
899
+ },
900
+
901
+ /* Determines if we should allow a "next/prev" year display change. */
902
+ _canAdjustYear: function(inst, offset, curYear) {
903
+ var date = new Date(curYear + offset, 1, 1);
904
+ return this._isInRange(inst, date);
905
+ },
906
+
907
+ /* Is the given date in the accepted range? */
908
+ _isInRange: function(inst, date) {
909
+ var yearSplit, currentYear,
910
+ minDate = this._getMinMaxDate(inst, "min"),
911
+ maxDate = this._getMinMaxDate(inst, "max"),
912
+ minYear = null,
913
+ maxYear = null,
914
+ years = this._get(inst, "yearRange");
915
+ if (years){
916
+ yearSplit = years.split(":");
917
+ currentYear = new Date().getFullYear();
918
+ minYear = parseInt(yearSplit[0], 10);
919
+ maxYear = parseInt(yearSplit[1], 10);
920
+ if ( yearSplit[0].match(/[+\-].*/) ) {
921
+ minYear += currentYear;
922
+ }
923
+ if ( yearSplit[1].match(/[+\-].*/) ) {
924
+ maxYear += currentYear;
925
+ }
926
+ }
927
+
928
+ return ((!minDate || date.getTime() >= minDate.getTime()) &&
929
+ (!maxDate || date.getTime() <= maxDate.getTime()) &&
930
+ (!minYear || date.getFullYear() >= minYear) &&
931
+ (!maxYear || date.getFullYear() <= maxYear));
932
+ },
933
+
934
+ /* Action for selecting a new month/year. */
935
+ _selectYear: function(id, select, period) {
936
+ var target = $(id);
937
+ var inst = this._getInst(target[0]);
938
+ inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
939
+ inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
940
+ parseInt(select.options[select.selectedIndex].value,10);
941
+ this._notifyChange(inst);
942
+ this._adjustDate(target);
943
+ },
944
+
945
+ /* Action for current link. */
946
+ _gotoCurrent: function(id) {
947
+ var date,
948
+ target = $(id),
949
+ inst = this._getInst(target[0]);
950
+
951
+ if (this._get(inst, "gotoCurrent") && inst.currentYear) {
952
+ inst.selectedDay = inst.currentDay;
953
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth;
954
+ inst.drawYear = inst.selectedYear = inst.currentYear;
955
+ } else {
956
+ date = new Date();
957
+ inst.selectedDay = date.getDate();
958
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
959
+ inst.drawYear = inst.selectedYear = date.getFullYear();
960
+ }
961
+ this._notifyChange(inst);
962
+ this._adjustDate(target);
963
+ },
964
+
965
+ /* Action for selecting a month. */
966
+ _selectMonth: function(id, year, month, td) {
967
+ var target = $(id);
968
+ var inst = this._getInst(target[0]);
969
+
970
+ if ($(td).hasClass(this._unselectableClass) || this._isDisabledMonthpicker(target[0])) {
971
+ return;
972
+ }
973
+
974
+ inst.selectedMonth = inst.currentMonth = month;
975
+ inst.selectedYear = inst.currentYear = year;
976
+ this._selectDate(id, this._formatDate(inst, inst.currentMonth, inst.currentYear));
977
+ },
978
+
979
+ /* Parse a string value into a date object.
980
+ See formatDate below for the possible formats.
981
+
982
+ @param format string - the expected format of the date
983
+ @param value string - the date in the above format
984
+ @param settings Object - attributes include:
985
+ shortYearCutoff number - the cutoff year for determining the century (optional)
986
+ monthNamesShort string[12] - abbreviated names of the months (optional)
987
+ monthNames string[12] - names of the months (optional)
988
+ @return Date - the extracted date value or null if value is blank */
989
+ parseDate: function (format, value, settings) {
990
+ if (format == null || value == null)
991
+ throw 'Invalid arguments';
992
+ value = (typeof value == 'object' ? value.toString() : value + '');
993
+ if (value == '')
994
+ return null;
995
+ var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
996
+ shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
997
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
998
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
999
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
1000
+ var year = -1;
1001
+ var month = -1;
1002
+ var literal = false;
1003
+ // Check whether a format character is doubled
1004
+ var lookAhead = function(match) {
1005
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
1006
+ if (matches)
1007
+ iFormat++;
1008
+ return matches;
1009
+ };
1010
+ // Extract a number from the string value
1011
+ var getNumber = function(match) {
1012
+ var isDoubled = lookAhead(match);
1013
+ var size = (match == '@' ? 14 : (match == '!' ? 20 :
1014
+ (match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2))));
1015
+ var digits = new RegExp('^\\d{1,' + size + '}');
1016
+ var num = value.substring(iValue).match(digits);
1017
+ if (!num)
1018
+ throw 'Missing number at position ' + iValue;
1019
+ iValue += num[0].length;
1020
+ return parseInt(num[0], 10);
1021
+ };
1022
+ // Extract a name from the string value and convert to an index
1023
+ var getName = function(match, shortNames, longNames) {
1024
+ var names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
1025
+ return [ [k, v] ];
1026
+ }).sort(function (a, b) {
1027
+ return -(a[1].length - b[1].length);
1028
+ });
1029
+ var index = -1;
1030
+ $.each(names, function (i, pair) {
1031
+ var name = pair[1];
1032
+ if (value.substr(iValue, name.length).toLowerCase() == name.toLowerCase()) {
1033
+ index = pair[0];
1034
+ iValue += name.length;
1035
+ return false;
1036
+ }
1037
+ });
1038
+ if (index != -1)
1039
+ return index + 1;
1040
+ else
1041
+ throw 'Unknown name at position ' + iValue;
1042
+ };
1043
+ // Confirm that a literal character matches the string value
1044
+ var checkLiteral = function() {
1045
+ if (value.charAt(iValue) != format.charAt(iFormat))
1046
+ throw 'Unexpected literal at position ' + iValue;
1047
+ iValue++;
1048
+ };
1049
+ var iValue = 0;
1050
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
1051
+ if (literal)
1052
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1053
+ literal = false;
1054
+ else
1055
+ checkLiteral();
1056
+ else
1057
+ switch (format.charAt(iFormat)) {
1058
+ case 'm':
1059
+ month = getNumber('m');
1060
+ break;
1061
+ case 'M':
1062
+ month = getName('M', monthNamesShort, monthNames);
1063
+ break;
1064
+ case 'y':
1065
+ year = getNumber('y');
1066
+ break;
1067
+ case '@':
1068
+ var date = new Date(getNumber('@'));
1069
+ year = date.getFullYear();
1070
+ month = date.getMonth() + 1;
1071
+ day = date.getDate();
1072
+ break;
1073
+ case '!':
1074
+ var date = new Date((getNumber('!') - this._ticksTo1970) / 10000);
1075
+ year = date.getFullYear();
1076
+ month = date.getMonth() + 1;
1077
+ break;
1078
+ case "'":
1079
+ if (lookAhead("'"))
1080
+ checkLiteral();
1081
+ else
1082
+ literal = true;
1083
+ break;
1084
+ default:
1085
+ checkLiteral();
1086
+ }
1087
+ }
1088
+ if (iValue < value.length){
1089
+ throw "Extra/unparsed characters found in date: " + value.substring(iValue);
1090
+ }
1091
+ if (year == -1)
1092
+ year = new Date().getFullYear();
1093
+ else if (year < 100)
1094
+ year += new Date().getFullYear() - new Date().getFullYear() % 100 +
1095
+ (year <= shortYearCutoff ? 0 : -100);
1096
+ var date = new Date(year, month - 1, 1);
1097
+ if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != 1)
1098
+ throw 'Invalid date'; // E.g. 31/02/00
1099
+ return date;
1100
+ },
1101
+
1102
+ /* Format a date object into a string value.
1103
+ The format can be combinations of the following:
1104
+ d - day of month (no leading zero)
1105
+ dd - day of month (two digit)
1106
+ o - day of year (no leading zeros)
1107
+ oo - day of year (three digit)
1108
+ D - day name short
1109
+ DD - day name long
1110
+ m - month of year (no leading zero)
1111
+ mm - month of year (two digit)
1112
+ M - month name short
1113
+ MM - month name long
1114
+ y - year (two digit)
1115
+ yy - year (four digit)
1116
+ @ - Unix timestamp (ms since 01/01/1970)
1117
+ ! - Windows ticks (100ns since 01/01/0001)
1118
+ '...' - literal text
1119
+ '' - single quote
1120
+
1121
+ @param format string - the desired format of the date
1122
+ @param date Date - the date value to format
1123
+ @param settings Object - attributes include:
1124
+ dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
1125
+ dayNames string[7] - names of the days from Sunday (optional)
1126
+ monthNamesShort string[12] - abbreviated names of the months (optional)
1127
+ monthNames string[12] - names of the months (optional)
1128
+ @return string - the date in the above format */
1129
+ formatDate: function (format, date, settings) {
1130
+ if (!date)
1131
+ return '';
1132
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
1133
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
1134
+ // Check whether a format character is doubled
1135
+ var lookAhead = function(match) {
1136
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
1137
+ if (matches)
1138
+ iFormat++;
1139
+ return matches;
1140
+ };
1141
+ // Format a number, with leading zero if necessary
1142
+ var formatNumber = function(match, value, len) {
1143
+ var num = '' + value;
1144
+ if (lookAhead(match))
1145
+ while (num.length < len)
1146
+ num = '0' + num;
1147
+ return num;
1148
+ };
1149
+ // Format a name, short or long as requested
1150
+ var formatName = function(match, value, shortNames, longNames) {
1151
+ return (lookAhead(match) ? longNames[value] : shortNames[value]);
1152
+ };
1153
+ var output = '';
1154
+ var literal = false;
1155
+ if (date)
1156
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
1157
+ if (literal)
1158
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1159
+ literal = false;
1160
+ else
1161
+ output += format.charAt(iFormat);
1162
+ else
1163
+ switch (format.charAt(iFormat)) {
1164
+ case 'm':
1165
+ output += formatNumber('m', date.getMonth() + 1, 2);
1166
+ break;
1167
+ case 'M':
1168
+ output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
1169
+ break;
1170
+ case 'y':
1171
+ output += (lookAhead('y') ? date.getFullYear() :
1172
+ (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
1173
+ break;
1174
+ case '@':
1175
+ output += date.getTime();
1176
+ break;
1177
+ case '!':
1178
+ output += date.getTime() * 10000 + this._ticksTo1970;
1179
+ break;
1180
+ case "'":
1181
+ if (lookAhead("'"))
1182
+ output += "'";
1183
+ else
1184
+ literal = true;
1185
+ break;
1186
+ default:
1187
+ output += format.charAt(iFormat);
1188
+ }
1189
+ }
1190
+ return output;
1191
+ },
1192
+
1193
+ /* Erase the input field and hide the date picker. */
1194
+ _clearDate: function(id) {
1195
+ var target = $(id);
1196
+ this._selectDate(target, "");
1197
+ },
1198
+
1199
+ /* Update the input field with the selected date. */
1200
+ _selectDate: function(id, dateStr) {
1201
+ var target = $(id);
1202
+ var inst = this._getInst(target[0]);
1203
+ dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
1204
+ if (inst.input)
1205
+ inst.input.val(dateStr);
1206
+ this._updateAlternate(inst);
1207
+ var onSelect = this._get(inst, 'onSelect');
1208
+ if (onSelect)
1209
+ onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
1210
+ else if (inst.input)
1211
+ inst.input.trigger('change'); // fire the change event
1212
+ this._hideMonthpicker();
1213
+ this._lastInput = inst.input[0];
1214
+ if (typeof(inst.input[0]) != 'object')
1215
+ inst.input.focus(); // restore focus
1216
+ this._lastInput = null;
1217
+ },
1218
+
1219
+ /* Update any alternate field to synchronise with the main field. */
1220
+ _updateAlternate: function(inst) {
1221
+ var altField = this._get(inst, 'altField');
1222
+ if (altField) { // update alternate field too
1223
+ var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
1224
+ var date = this._getDate(inst);
1225
+ var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
1226
+ $(altField).each(function() { $(this).val(dateStr); });
1227
+ }
1228
+ },
1229
+
1230
+ /* Set the date(s) directly. */
1231
+ _setDate: function(inst, date, noChange) {
1232
+ var clear = !date,
1233
+ origMonth = inst.selectedMonth,
1234
+ origYear = inst.selectedYear,
1235
+ newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
1236
+
1237
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
1238
+ inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
1239
+ if ((origYear !== inst.selectedYear) && !noChange) {
1240
+ this._notifyChange(inst);
1241
+ }
1242
+ this._adjustInstDate(inst);
1243
+ if (inst.input) {
1244
+ inst.input.val(clear ? "" : this._formatDate(inst));
1245
+ }
1246
+ },
1247
+
1248
+ /* Set the dates for a jQuery selection.
1249
+ * @param target element - the target input field or division or span
1250
+ * @param date Date - the new date
1251
+ */
1252
+ _setDateMonthpicker: function(target, date) {
1253
+ var inst = this._getInst(target);
1254
+ if (inst) {
1255
+ this._setDate(inst, date);
1256
+ this._updateMonthpicker(inst);
1257
+ this._updateAlternate(inst);
1258
+ }
1259
+ },
1260
+
1261
+ _getDate: function(inst) {
1262
+ var date = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
1263
+ new Date(inst.currentYear, inst.currentMonth, 1));
1264
+
1265
+ return date;
1266
+ },
1267
+
1268
+ /* Attach the onxxx handlers. These are declared statically so
1269
+ * they work with static code transformers like Caja.
1270
+ */
1271
+ _attachHandlers: function(inst) {
1272
+ var stepYears = this._get(inst, "stepYears"),
1273
+ id = "#" + inst.id.replace( /\\\\/g, "\\" );
1274
+ inst.dpDiv.find("[data-handler]").map(function () {
1275
+ var handler = {
1276
+ prev: function () {
1277
+ $.monthpicker._adjustDate(id, -stepYears, "Y");
1278
+ },
1279
+ next: function () {
1280
+ $.monthpicker._adjustDate(id, +stepYears, "Y");
1281
+ },
1282
+ hide: function () {
1283
+ $.monthpicker._hideMonthpicker();
1284
+ },
1285
+ current: function () {
1286
+ $.monthpicker._gotoCurrent(id);
1287
+ },
1288
+ selectMonth: function () {
1289
+ $.monthpicker._selectMonth(id, +this.getAttribute("data-year"), +this.getAttribute("data-month"), this);
1290
+ return false;
1291
+ },
1292
+ selectYear: function () {
1293
+ $.monthpicker._selectYear(id, this, "Y");
1294
+ return false;
1295
+ }
1296
+ };
1297
+ $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
1298
+ });
1299
+ },
1300
+
1301
+ /* Update or retrieve the settings for a month picker attached to an input field or division.
1302
+ * @param target element - the target input field or division or span
1303
+ * @param name object - the new settings to update or
1304
+ * string - the name of the setting to change or retrieve,
1305
+ * when retrieving also "all" for all instance settings or
1306
+ * "defaults" for all global defaults
1307
+ * @param value any - the new value for the setting
1308
+ * (omit if above is an object or to retrieve a value)
1309
+ */
1310
+ _optionMonthpicker: function(target, name, value) {
1311
+ var settings, date, minDate, maxDate,
1312
+ inst = this._getInst(target);
1313
+
1314
+ if (arguments.length === 2 && typeof name === "string") {
1315
+ return (name === "defaults" ? $.extend({}, $.monthpicker._defaults) :
1316
+ (inst ? (name === "all" ? $.extend({}, inst.settings) :
1317
+ this._get(inst, name)) : null));
1318
+ }
1319
+
1320
+ settings = name || {};
1321
+ if (typeof name === "string") {
1322
+ settings = {};
1323
+ settings[name] = value;
1324
+ }
1325
+
1326
+ if (inst) {
1327
+ if (this._curInst === inst) {
1328
+ this._hideMonthpicker();
1329
+ }
1330
+
1331
+ date = this._getDateMonthpicker(target, true);
1332
+ minDate = this._getMinMaxDate(inst, "min");
1333
+ maxDate = this._getMinMaxDate(inst, "max");
1334
+ extendRemove(inst.settings, settings);
1335
+ // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
1336
+ if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
1337
+ inst.settings.minDate = this._formatDate(inst, minDate);
1338
+ }
1339
+ if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
1340
+ inst.settings.maxDate = this._formatDate(inst, maxDate);
1341
+ }
1342
+ if ( "disabled" in settings ) {
1343
+ if ( settings.disabled ) {
1344
+ this._disableMonthpicker(target);
1345
+ } else {
1346
+ this._enableMonthpicker(target);
1347
+ }
1348
+ }
1349
+ this._attachments($(target), inst);
1350
+ this._setDate(inst, date);
1351
+ this._updateAlternate(inst);
1352
+ this._updateMonthpicker(inst);
1353
+ }
1354
+ },
1355
+
1356
+
1357
+ /* Get the date(s) for the first entry in a jQuery selection.
1358
+ * @param target element - the target input field or division or span
1359
+ * @return Date - the current date
1360
+ */
1361
+ _getDateMonthpicker: function(target, noDefault) {
1362
+ var inst = this._getInst(target);
1363
+ if (inst && !inst.inline) {
1364
+ this._setDateFromField(inst, noDefault);
1365
+ }
1366
+ return (inst ? this._getDate(inst) : null);
1367
+ },
1368
+
1369
+ /* Format the given date for display. */
1370
+ _formatDate: function(inst, month, year) {
1371
+ if (!month) {
1372
+ inst.currentMonth = inst.selectedMonth;
1373
+ inst.currentYear = inst.selectedYear;
1374
+ }
1375
+ var date = (month ? (typeof month == 'object' ? month :
1376
+ new Date(year, month, 1)) :
1377
+ new Date(inst.currentYear, inst.currentMonth, 1));
1378
+ return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
1379
+ }
1380
+ });
1381
+
1382
+ /* jQuery extend now ignores nulls! */
1383
+ function extendRemove(target, props) {
1384
+ $.extend(target, props);
1385
+ for (var name in props)
1386
+ if (props[name] == null || props[name] == undefined)
1387
+ target[name] = props[name];
1388
+ return target;
1389
+ };
1390
+
1391
+ /*
1392
+ * Bind hover events for monthpicker elements.
1393
+ * Done via delegate so the binding only occurs once in the lifetime of the parent div.
1394
+ * Global instActive, set by _updateMonthpicker allows the handlers to find their way back to the active picker.
1395
+ */
1396
+ function bindHover(dpDiv) {
1397
+ var selector = '.ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a';
1398
+ return dpDiv.delegate(selector, 'mouseout', function() {
1399
+ $(this).removeClass('ui-state-hover');
1400
+ if (this.className.indexOf('ui-datepicker-prev') != -1) $(this).removeClass('ui-datepicker-prev-hover');
1401
+ if (this.className.indexOf('ui-datepicker-next') != -1) $(this).removeClass('ui-datepicker-next-hover');
1402
+ })
1403
+ .delegate(selector, 'mouseover', function(){
1404
+ if (!$.monthpicker._isDisabledMonthpicker( instActive.input[0])) {
1405
+ $(this).parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
1406
+ $(this).addClass('ui-state-hover');
1407
+ if (this.className.indexOf('ui-datepicker-prev') != -1) $(this).addClass('ui-datepicker-prev-hover');
1408
+ if (this.className.indexOf('ui-datepicker-next') != -1) $(this).addClass('ui-datepicker-next-hover');
1409
+ }
1410
+ });
1411
+ }
1412
+
1413
+ /* Invoke the monthpicker functionality.
1414
+ @param options Object - settings for attaching new monthpicker functionality
1415
+ @return jQuery object */
1416
+ $.fn.monthpicker = function(options){
1417
+
1418
+ /* Verify an empty collection wasn't passed */
1419
+ if ( !this.length ) {
1420
+ return this;
1421
+ }
1422
+
1423
+ /* Initialise the date picker. */
1424
+ if (!$.monthpicker.initialized) {
1425
+ $(document).mousedown($.monthpicker._checkExternalClick).
1426
+ find('body').append($.monthpicker.dpDiv);
1427
+ $.monthpicker.initialized = true;
1428
+ }
1429
+
1430
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
1431
+ if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
1432
+ return $.monthpicker["_" + options + "Monthpicker"].
1433
+ apply($.monthpicker, [this[0]].concat(otherArgs));
1434
+ }
1435
+ if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
1436
+ return $.monthpicker["_" + options + "Monthpicker"].
1437
+ apply($.monthpicker, [this[0]].concat(otherArgs));
1438
+ }
1439
+
1440
+ return this.each(function() {
1441
+ typeof options === 'string' ?
1442
+ $.monthpicker['_' + options + 'Monthpicker'].
1443
+ apply($.monthpicker, [this].concat(otherArgs)) :
1444
+ $.monthpicker._attachMonthpicker(this, options);
1445
+ });
1446
+ };
1447
+
1448
+ $.monthpicker = new Monthpicker(); // singleton instance
1449
+ $.monthpicker.initialized = false;
1450
+
1451
+ })(jQuery);