File indexing completed on 2024-06-02 06:06:32
0001 /* 0002 The jQuery UI Month Picker Version 3.0.4 0003 https://github.com/KidSysco/jquery-ui-month-picker/ 0004 0005 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> 0006 This program is free software: you can redistribute it and/or modify 0007 it under the terms of the GNU General Public License as published by 0008 the Free Software Foundation, either version 3 of the License, or 0009 (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program. If not, see 0018 <http://www.gnu.org/licenses/gpl-3.0.txt>. 0019 */ 0020 0021 (function ($, window, document, Date) { 0022 'use strict'; 0023 0024 var _setupErr = 'MonthPicker Error: '; 0025 // This test must be run before any rererence is made to jQuery. 0026 // In case the user didn't load jQuery or jQuery UI the plugin 0027 // will fail before it get's to this test + there is no reason 0028 // to perform this test for every MonthPicker instance being created. 0029 if (!$ || !$.ui || !$.ui.button || !$.ui.datepicker) { 0030 alert(_setupErr + 'The jQuery UI button and datepicker plug-ins must be loaded.'); 0031 return; 0032 } 0033 0034 // Creates an alias to jQuery UI's .button() that dosen't 0035 // conflict with Bootstrap.js button (#35) 0036 $.widget.bridge('jqueryUIButton', $.ui.button); 0037 0038 var _speeds = $.fx.speeds; 0039 var _eventsNs = '.MonthPicker'; 0040 var _textfieldClass = 'month-year-input'; 0041 var _clearHint = 'month-picker-clear-hint'; 0042 var _iconClass = '.ui-button-icon-primary'; 0043 var _disabledClass = 'month-picker-disabled'; 0044 var _todayClass = 'ui-state-highlight'; 0045 var _selectedClass = 'ui-state-active'; 0046 var _defaultClass = 'ui-state-default'; 0047 var _defaultPos = { my: 'left top+1', at: 'left bottom' }; 0048 var _RTL_defaultPos = { my: 'right top+1', at: 'right bottom' }; 0049 var _posErr = _setupErr + 'The jQuery UI position plug-in must be loaded.'; 0050 var _badOptValErr = _setupErr + 'Unsupported % option value, supported values are: '; 0051 var _badMinMaxVal = _setupErr + '"_" is not a valid %Month value.'; 0052 var _openedInstance = null; 0053 var _hasPosition = !!$.ui.position; 0054 var _animVals = { 0055 Animation: ['slideToggle', 'fadeToggle', 'none'], 0056 ShowAnim: ['fadeIn', 'slideDown', 'none'], 0057 HideAnim: ['fadeOut', 'slideUp', 'none'] 0058 }; 0059 var _setOptionHooks = { 0060 ValidationErrorMessage: '_createValidationMessage', 0061 Disabled: '_setDisabledState', 0062 ShowIcon: '_updateButton', 0063 Button: '_updateButton', 0064 ShowOn: '_updateFieldEvents', 0065 IsRTL: '_setRTL', 0066 AltFormat: '_updateAlt', 0067 AltField: '_updateAlt', 0068 StartYear: '_setPickerYear', 0069 MinMonth: '_setMinMonth', 0070 MaxMonth: '_setMaxMonth', 0071 SelectedMonth: '_setSelectedMonth' 0072 }; 0073 var $noop = $.noop; 0074 var $proxy = $.proxy; 0075 var $datepicker = $.datepicker; 0076 var click = 'click' + _eventsNs; 0077 0078 function _toMonth(date) { 0079 return date.getMonth() + (date.getFullYear() * 12); 0080 } 0081 0082 function _toYear(month) { 0083 return Math.floor(month / 12); 0084 } 0085 0086 function _stayActive() { 0087 $(this).addClass(_selectedClass); 0088 } 0089 0090 function _setActive( el, state ) { 0091 return el[ state ? 'on' : 'off' ]('mousenter mouseout', _stayActive ) 0092 .toggleClass(_selectedClass, state); 0093 } 0094 0095 function _between(month, from, until) { 0096 return (!from || month >= from) && (!until || month <= until); 0097 } 0098 0099 function _encodeMonth(_inst, _val) { 0100 if (_val === null) { 0101 return _val; 0102 } else if (_val instanceof Date) { 0103 return _toMonth(_val); 0104 } else if ($.isNumeric(_val)) { 0105 return _toMonth(new Date) + parseInt(_val, 10); 0106 } 0107 0108 var _date = _inst._parseMonth(_val); 0109 if (_date) { 0110 return _toMonth(_date); 0111 } 0112 0113 return _parsePeriod(_val); 0114 } 0115 0116 function _event(_name, _inst) { 0117 return $proxy(_inst.options[_name] || $noop, _inst.element[0]); 0118 } 0119 0120 function _parsePeriod(_val, _initDate) { 0121 // Parsing is done by replacing tokens in the value to form 0122 // a JSON object with it's keys and values reversed 0123 // (example '+1y +2m' will turn into {"+1":"y","+2":"m"}) 0124 // After that we just revers the keys and values. 0125 var _json = $.trim(_val); 0126 _json = _json.replace(/y/i, '":"y"'); 0127 _json = _json.replace(/m/i, '":"m"'); 0128 0129 try { 0130 var _rev = JSON.parse( '{"' + _json.replace(/ /g, ',"') + '}' ), obj = {}; 0131 0132 for (var key in _rev) { 0133 obj[ _rev[key] ] = key; 0134 } 0135 0136 var _month = _toMonth(new Date); 0137 _month += (parseInt(obj.m, 10) || 0); 0138 return _month + (parseInt(obj.y, 10) || 0) * 12; 0139 } catch (e) { 0140 return false; 0141 } 0142 } 0143 0144 function _makeDefaultButton(options) { 0145 // this refers to the associated input field. 0146 return $('<span id="MonthPicker_Button_' + this.id + '" class="month-picker-open-button">' + options.i18n.buttonText + '</span>') 0147 .jqueryUIButton({ 0148 text: false, 0149 icons: { 0150 // Defaults to 'ui-icon-calculator'. 0151 primary: options.ButtonIcon 0152 } 0153 }); 0154 } 0155 0156 function _applyArrowButton($el, dir) { 0157 $el.jqueryUIButton('option', { 0158 icons: { 0159 primary: 'ui-icon-circle-triangle-' + (dir ? 'w' : 'e') 0160 } 0161 }); 0162 } 0163 0164 function _isInline(elem) { 0165 return !elem.is('input'); 0166 } 0167 0168 $.MonthPicker = { 0169 VERSION: '3.0.4', // Added in version 2.4; 0170 i18n: { 0171 year: 'Year', 0172 prevYear: 'Previous Year', 0173 nextYear: 'Next Year', 0174 next12Years: 'Jump Forward 12 Years', 0175 prev12Years: 'Jump Back 12 Years', 0176 nextLabel: 'Next', 0177 prevLabel: 'Prev', 0178 buttonText: 'Open Month Chooser', 0179 jumpYears: 'Jump Years', 0180 backTo: 'Back to', 0181 months: ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'June', 'July', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'] 0182 } 0183 }; 0184 0185 var _markup = 0186 '<div class="ui-widget-header month-picker-header ui-corner-all">' + 0187 '<table class="month-picker-year-table">' + 0188 '<tr>' + 0189 '<td class="month-picker-previous"><a /></td>' + 0190 '<td class="month-picker-title"><a /></td>' + 0191 '<td class="month-picker-next"><a /></td>' + 0192 '</tr>' + 0193 '</table>' + 0194 '</div>' + 0195 '<div>' + 0196 '<table class="month-picker-month-table" />' + 0197 '</div>'; 0198 0199 // Groups state and functionallity to fade in the jump years hint 0200 // when the user mouses over the Year 2016 text. 0201 // NOTE: An invocation of this function: 0202 // 1: Is an independent instance with it's own unique state. 0203 // 2: Assumes that there is no previous hint applied to the 0204 // button (it dosen't remove the existing hint). 0205 function _applyButtonHint(_button, _hintText) { 0206 var _speed = 125, _currentLabel, _startTimeout, _labelElem = $(); 0207 0208 _button.on('mouseenter' + _eventsNs + 'h', _prepareToStart); 0209 0210 // Setp 1: Wait to make sure the user isn't just mousing over and 0211 // away from the button. 0212 // NOTE: If _fadeOutHint() is triggered on mouseleave before the 0213 // timeout is triggered the animation is canceled. 0214 function _prepareToStart() { 0215 _startTimeout = setTimeout(_fadeOutLabel, 175); 0216 } 0217 0218 // Setp 2: Fade out the label (Year 2016) text to 45%. 0219 function _fadeOutLabel() { 0220 _startTimeout = null; 0221 _labelElem = $('span', _button).animate({ opacity: 0.45 }, _speed, _fadeInHint); 0222 } 0223 0224 // Setp 3: Fade in the hint text (Jump years). 0225 function _fadeInHint() { 0226 _currentLabel = _labelElem.text(); 0227 _labelElem.animate({ opacity: 1 }, _speed).text(_hintText); 0228 } 0229 0230 _button.on('mouseleave' + _eventsNs + 'h', _fadeOutHint); 0231 0232 function _fadeOutHint() { 0233 if (_startTimeout) { 0234 // If the user is just moving over and away from the button, cancel 0235 // the animation completely. 0236 clearTimeout(_startTimeout); 0237 } else { 0238 // Setp 4: Fade out the hint text (Jump years) to 45%. 0239 _labelElem = $('span', _button).animate({ opacity: 0.45 }, _speed, _fadeInLabel); 0240 } 0241 } 0242 0243 // Setp 5: Fade in the label (Year 2016) text. 0244 function _fadeInLabel() { 0245 _labelElem.text( _currentLabel ).animate({opacity: 1}, _speed); 0246 } 0247 0248 // Adds a function to the button elemene which is called when the 0249 // user clicks the button (the hint needs to be removed). 0250 _button.data(_clearHint, function() { 0251 clearTimeout(_startTimeout); 0252 _labelElem.stop().css({ opacity: 1 }); 0253 _button.off(_eventsNs + 'h'); 0254 }); 0255 } // End _applyButtonHint() 0256 0257 function _setDisabled(_button, _value) { 0258 var _btnWidget = _button.data('ui-button'); 0259 if (_btnWidget.option('disabled') !== _value) { 0260 _btnWidget.option('disabled', _value); 0261 } 0262 } 0263 0264 $.widget("KidSysco.MonthPicker", { 0265 0266 /******* Properties *******/ 0267 0268 options: { 0269 i18n: {}, 0270 IsRTL: false, 0271 Position: null, 0272 StartYear: null, 0273 ShowIcon: true, 0274 UseInputMask: false, 0275 ValidationErrorMessage: null, 0276 Disabled: false, 0277 MonthFormat: 'mm/yy', 0278 Animation: 'fadeToggle', 0279 ShowAnim: null, 0280 HideAnim: null, 0281 ShowOn: null, 0282 MinMonth: null, 0283 MaxMonth: null, 0284 Duration: 'normal', 0285 Button: _makeDefaultButton, 0286 ButtonIcon: 'ui-icon-calculator' 0287 }, 0288 0289 _monthPickerButton: $(), 0290 _validationMessage: $(), 0291 _selectedBtn: $(), 0292 0293 /******* jQuery UI Widget Factory Overrides ********/ 0294 0295 _destroy: function () { 0296 var _elem = this.element; 0297 if ($.mask && this.options.UseInputMask) { 0298 _elem.unmask(); 0299 0300 if (!this.GetSelectedDate()) { 0301 _elem.val(''); 0302 } 0303 } 0304 0305 _elem.removeClass(_textfieldClass).off(_eventsNs); 0306 0307 $(document).off(_eventsNs + this.uuid); 0308 0309 this._monthPickerMenu.remove(); 0310 0311 var _button = this._monthPickerButton.off(click); 0312 if (this._removeOldBtn) { 0313 _button.remove(); 0314 } 0315 0316 this._validationMessage.remove(); 0317 0318 if (_openedInstance === this) { 0319 _openedInstance = null; 0320 } 0321 }, 0322 0323 _setOption: function (key, value) { 0324 switch (key) { 0325 case 'i18n': 0326 // Pass a clone i18n object to the this._super. 0327 value = $.extend({}, value); 0328 break; 0329 case 'Position': 0330 if (!_hasPosition) { 0331 alert(_posErr); 0332 return; 0333 } 0334 break; 0335 case 'MonthFormat': 0336 var date = this.GetSelectedDate(); 0337 if (date) { 0338 this.element.val( this.FormatMonth(date, value) ); 0339 } 0340 break; 0341 } 0342 0343 // Make sure the user passed in a valid Animation, ShowAnim and HideAnim options values. 0344 if (key in _animVals && $.inArray(value, _animVals[key]) === -1) { 0345 alert(_badOptValErr.replace(/%/, key) + _animVals[key]); 0346 return; 0347 } 0348 0349 // In jQuery UI 1.8, manually invoke the _setOption method from the base widget. 0350 //$.Widget.prototype._setOption.apply(this, arguments); 0351 // In jQuery UI 1.9 and above, you use the _super method instead. 0352 this._super(key, value); 0353 0354 _setOptionHooks[key] ? this[ _setOptionHooks[key] ](value) : 0; 0355 }, 0356 0357 _create: function () { 0358 var _el = this.element, _opts = this.options, _type = _el.attr('type'); 0359 // According to http://www.w3.org/TR/html-markup/input.html#input 0360 // An input element with no type attribute specified represents the same thing as an 0361 // input element with its type attribute set to "text". 0362 // TLDR: 0363 // http://www.w3.org/TR/html5/forms.html#the-input-element 0364 // https://api.jquery.com/text-selector/ 0365 0366 // $.inArray(void 0, ['text', 'month', void 0]) returns -1 when searching for undefined in IE8 (#45) 0367 // This is only noticable in the real version of IE8, emulated versions 0368 // from the dev tools in modern browsers do not suffer from this issue. 0369 // if (!_el.is('input,div,span') || $.inArray(_el.attr('type'), ['text', 'month', void 0]) === -1) { 0370 if (!_el.is('input,div,span') || (_type !== 'text' && _type !== 'month' && _type !== void 0)) { 0371 var error = _setupErr + 'MonthPicker can only be called on text or month inputs.'; 0372 // Call alert first so that IE<10 won't trip over console.log and swallow all errors. 0373 alert(error + ' \n\nSee (developer tools) for more details.'); 0374 0375 console.error(error + '\n Caused by:'); 0376 console.log(_el[0]); 0377 return false; 0378 } 0379 0380 if (!$.mask && _opts.UseInputMask) { 0381 alert(_setupErr + 'The UseInputMask option requires the Input Mask Plugin. Get it from digitalbush.com'); 0382 return false; 0383 } 0384 0385 if (_opts.Position !== null && !_hasPosition) { 0386 alert(_posErr); 0387 return false; 0388 } 0389 0390 // Make sure the user passed in a valid Animation, ShowAnim and HideAnim options values. 0391 for (var opt in _animVals) { 0392 if (_opts[opt] !== null && $.inArray(_opts[opt], _animVals[opt]) === -1) { 0393 alert(_badOptValErr.replace(/%/, opt) + _animVals[opt]); 0394 return false; 0395 } 0396 } 0397 0398 this._isMonthInputType = _el.attr('type') === 'month'; 0399 if (this._isMonthInputType) { 0400 this.options.MonthFormat = this.MonthInputFormat; 0401 _el.css('width', 'auto'); 0402 } 0403 0404 var _menu = this._monthPickerMenu = $('<div id="MonthPicker_' + _el[0].id + '" class="month-picker ui-widget ui-widget-content ui-corner-all"></div>').hide(); 0405 var isInline = _isInline(_el); 0406 0407 $(_markup).appendTo(_menu); 0408 _menu.appendTo( isInline ? _el : document.body ); 0409 0410 this._titleButton = 0411 $('.month-picker-title', _menu) 0412 .click($proxy(this._showYearsClickHandler, this)) 0413 .find('a').jqueryUIButton() 0414 .removeClass(_defaultClass); 0415 0416 this._applyJumpYearsHint(); 0417 this._createValidationMessage(); 0418 0419 this._prevButton = $('.month-picker-previous>a', _menu) 0420 .jqueryUIButton({ text: false }) 0421 .removeClass(_defaultClass); 0422 0423 this._nextButton = $('.month-picker-next>a', _menu) 0424 .jqueryUIButton({ text: false }) 0425 .removeClass(_defaultClass); 0426 0427 this._setRTL(_opts.IsRTL); //Assigns icons to the next/prev buttons. 0428 0429 $(_iconClass, this._nextButton).text(this._i18n('nextLabel')); 0430 $(_iconClass, this._prevButton).text(this._i18n('prevLabel')); 0431 0432 var $table = $('.month-picker-month-table', _menu); 0433 for (var i = 0; i < 12; i++) { 0434 var $tr = !(i % 3) ? $('<tr />').appendTo($table) : $tr; 0435 0436 // Use <a> tag instead of <button> to avoid issues 0437 // only with Google Chrome (#50). 0438 $tr.append('<td><a class="button-' + (i + 1) + '" /></td>'); 0439 } 0440 0441 this._buttons = $('a', $table).jqueryUIButton(); 0442 0443 _menu.on('mousedown' + _eventsNs, function (event) { 0444 event.preventDefault(); 0445 }); 0446 0447 // Checks and initailizes Min/MaxMonth properties 0448 // (creates _setMinMonth and _setMaxMonth methods). 0449 var me = this, Month = 'Month'; 0450 $.each(['Min', 'Max'], function(i, type) { 0451 me["_set" + type + Month] = function(val) { 0452 if ((me['_' + type + Month] = _encodeMonth(me, val)) === false) { 0453 alert(_badMinMaxVal.replace(/%/, type).replace(/_/, val)); 0454 } 0455 }; 0456 0457 me._setOption(type + Month, me.options[type + Month]); 0458 }); 0459 0460 var _selMonth = _opts.SelectedMonth; 0461 if (_selMonth !== void 0) { 0462 var month = _encodeMonth(this, _selMonth); 0463 _el.val( this._formatMonth(new Date( _toYear(month), month % 12, 1)) ); 0464 } 0465 0466 this._updateAlt(); 0467 0468 this._setUseInputMask(); 0469 this._setDisabledState(); 0470 this.Destroy = this.destroy; 0471 0472 if (isInline) { 0473 this.Open(); 0474 } else { 0475 // Update the alt field if the user manually changes 0476 // the input field. 0477 _el.addClass(_textfieldClass); 0478 _el.change($proxy(this._updateAlt, this)); 0479 } 0480 }, 0481 0482 /****** Publicly Accessible API functions ******/ 0483 0484 GetSelectedDate: function () { 0485 return this._parseMonth(); 0486 }, 0487 0488 GetSelectedYear: function () { 0489 var date = this.GetSelectedDate(); 0490 return date ? date.getFullYear() : NaN; 0491 }, 0492 0493 GetSelectedMonth: function () { 0494 var date = this.GetSelectedDate(); 0495 return date ? date.getMonth()+1 : NaN; 0496 }, 0497 0498 Validate: function() { 0499 var _date = this.GetSelectedDate(); 0500 0501 if (this.options.ValidationErrorMessage !== null && !this.options.Disabled) { 0502 this._validationMessage.toggle(!_date); 0503 } 0504 0505 return _date; 0506 }, 0507 0508 GetSelectedMonthYear: function () { 0509 var date = this.Validate(); 0510 return date ? (date.getMonth() + 1) + '/' + date.getFullYear() : null; 0511 }, 0512 0513 Disable: function () { 0514 this._setOption("Disabled", true); 0515 }, 0516 0517 Enable: function () { 0518 this._setOption("Disabled", false); 0519 }, 0520 0521 ClearAllCallbacks: function () { 0522 for (var _opt in this.options) { 0523 if (_opt.indexOf('On') === 0) { 0524 this.options[_opt] = $noop; 0525 } 0526 } 0527 }, 0528 0529 Clear: function () { 0530 this.element.val(''); 0531 $(this.options.AltField).val(''); 0532 this._validationMessage.hide(); 0533 }, 0534 0535 Toggle: function (event) { 0536 return this._visible ? this.Close(event) : this.Open(event); 0537 }, 0538 0539 Open: function (event) { 0540 var _elem = this.element, _opts = this.options; 0541 if (!_opts.Disabled && !this._visible) { 0542 // Allow the user to prevent opening the menu. 0543 event = event || $.Event(); 0544 if (_event('OnBeforeMenuOpen', this)(event) === false || event.isDefaultPrevented()) { 0545 return; 0546 } 0547 0548 this._visible = true; 0549 this._ajustYear(_opts); 0550 0551 var _menu = this._monthPickerMenu; 0552 this._showMonths(); 0553 0554 if (_isInline(_elem)) { 0555 _menu.css('position', 'static').show(); 0556 _event('OnAfterMenuOpen', this)(); 0557 } else { 0558 // If there is an open menu close it first. 0559 if (_openedInstance) { 0560 _openedInstance.Close(event); 0561 } 0562 0563 _openedInstance = this; 0564 $(document).on('mousedown' + _eventsNs + this.uuid, $proxy(this.Close, this)) 0565 .on('keydown' + _eventsNs + this.uuid, $proxy(this._keyDown, this)); 0566 0567 // Trun off validation so that clicking one of the months 0568 // won't blur the input field and trogger vlaidation 0569 // befroe the month was chosen (click event was triggered). 0570 // It is turned back on when Hide() is called. 0571 _elem.off('blur' + _eventsNs).focus(); 0572 0573 var _anim = _opts.ShowAnim || _opts.Animation, 0574 _noAnim = _anim === 'none'; 0575 0576 // jQuery UI overrides jQuery.show and dosen't 0577 // call the start callback. 0578 // see: http://api.jqueryui.com/show/ 0579 _menu[ _noAnim ? 'fadeIn' : _anim ]({ 0580 duration: _noAnim ? 0 : this._duration(), 0581 start: $proxy(this._position, this, _menu), 0582 complete: _event('OnAfterMenuOpen', this) 0583 }); 0584 } 0585 } 0586 }, 0587 0588 Close: function (event) { 0589 var _elem = this.element; 0590 if (!_isInline(_elem) && this._visible) { 0591 var _menu = this._monthPickerMenu, 0592 _opts = this.options; 0593 0594 event = event || $.Event(); 0595 if (_event('OnBeforeMenuClose', this)(event) === false || event.isDefaultPrevented()) { 0596 return; 0597 } 0598 0599 // If the menu is closed while in jump years mode, bring back 0600 // the jump years hint. 0601 if (this._backToYear) { 0602 this._applyJumpYearsHint(); 0603 this._backToYear = 0; 0604 } 0605 0606 this._visible = false; 0607 _openedInstance = null; 0608 $(document).off('keydown' + _eventsNs + this.uuid) 0609 .off('mousedown' + _eventsNs + this.uuid); 0610 0611 this.Validate(); 0612 _elem.on('blur' + _eventsNs, $proxy(this.Validate, this)); 0613 var _callback = _event('OnAfterMenuClose', this); 0614 0615 var _anim = _opts.HideAnim || _opts.Animation; 0616 if (_anim === 'none') { 0617 _menu.hide(0, _callback); 0618 } else { 0619 _menu[ _anim ](this._duration(), _callback); 0620 } 0621 } 0622 }, 0623 0624 /** 0625 * Methods the user can override to use a third party library 0626 * such as http://momentjs.com for parsing and formatting months. 0627 */ 0628 MonthInputFormat: 'yy-mm', 0629 0630 ParseMonth: function (str, format) { 0631 try { 0632 return $datepicker.parseDate('dd' + format, '01' + str); 0633 } catch (e) { 0634 return null; 0635 } 0636 }, 0637 0638 FormatMonth: function (date, format) { 0639 try { 0640 return $datepicker.formatDate(format, date) || null; 0641 } catch (e) { 0642 return null; 0643 } 0644 }, 0645 0646 /****** Private and Misc Utility functions ******/ 0647 0648 _setSelectedMonth: function (_selMonth) { 0649 var month = _encodeMonth(this, _selMonth), _el = this.element; 0650 0651 if (month) { 0652 var date = new Date( _toYear(month), month % 12, 1 ); 0653 _el.val( this._formatMonth( date ) ); 0654 0655 this._updateAlt(0, date); 0656 this._validationMessage.hide(); 0657 } else { 0658 this.Clear(); 0659 } 0660 0661 this._ajustYear(this.options); 0662 this._showMonths(); 0663 }, 0664 0665 _applyJumpYearsHint: function() { 0666 _applyButtonHint(this._titleButton, this._i18n('jumpYears')); 0667 }, 0668 0669 _i18n: function(str) { 0670 var _trans = this.options.i18n[str]; 0671 0672 if (typeof _trans === 'undefined') { 0673 return $.MonthPicker.i18n[str]; 0674 } else { 0675 return _trans; 0676 } 0677 }, 0678 0679 _parseMonth: function (str, format) { 0680 return this.ParseMonth(str || this.element.val(), format || this.options.MonthFormat); 0681 }, 0682 0683 _formatMonth: function (date, format) { 0684 return this.FormatMonth(date || this._parseMonth(), format || this.options.MonthFormat); 0685 }, 0686 0687 _updateButton: function () { 0688 var isDisabled = this.options.Disabled; 0689 0690 this._createButton(); 0691 0692 // If the button is a jQuery UI button, 0693 // plain HTML button or an input we support disable it, 0694 // otherwise the user must handle the diabled state 0695 // by creating an appropriate button by passing 0696 // a function. See Button option: Img tag tests for 0697 // more details. 0698 var _button = this._monthPickerButton; 0699 try { 0700 _button.jqueryUIButton('option', 'disabled', isDisabled); 0701 } catch (e) { 0702 _button.filter('button,input').prop('disabled', isDisabled); 0703 } 0704 0705 this._updateFieldEvents(); 0706 }, 0707 0708 _createButton: function () { 0709 var _elem = this.element, _opts = this.options; 0710 if (_isInline(_elem)) return; 0711 0712 var _oldButton = this._monthPickerButton.off(_eventsNs); 0713 var _btnOpt = _opts.ShowIcon ? _opts.Button : false; 0714 0715 if ($.isFunction(_btnOpt)) { 0716 var _params = $.extend(true, {i18n: $.extend(true, {}, $.MonthPicker.i18n)}, this.options); 0717 _btnOpt = _btnOpt.call(_elem[0], _params); 0718 } 0719 0720 var _removeOldBtn = false; 0721 this._monthPickerButton = ( _btnOpt instanceof $ ? _btnOpt : $(_btnOpt) ) 0722 .each(function() { 0723 if (!$.contains(document.body, this)) { 0724 _removeOldBtn = true; 0725 $(this).insertAfter(_elem); 0726 } 0727 }) 0728 .on(click, $proxy(this.Toggle, this)) 0729 .on('mousedown' + _eventsNs, function(r) { 0730 r.preventDefault(); 0731 }); 0732 0733 if (this._removeOldBtn) { 0734 _oldButton.remove(); 0735 } 0736 0737 this._removeOldBtn = _removeOldBtn; 0738 }, 0739 0740 _updateFieldEvents: function () { 0741 var _events = click + ' focus' + _eventsNs; 0742 this.element.off(_events); 0743 if (this.options.ShowOn === 'both' || !this._monthPickerButton.length) { 0744 this.element.on(_events, $proxy(this.Open, this)); 0745 } 0746 }, 0747 0748 _createValidationMessage: function () { 0749 var _errMsg = this.options.ValidationErrorMessage, _elem = this.element; 0750 if ($.inArray(_errMsg, [null, '']) === -1) { 0751 var _msgEl = $('<span id="MonthPicker_Validation_' + _elem[0].id + '" class="month-picker-invalid-message">' + _errMsg + '</span>'); 0752 0753 var _button = this._monthPickerButton; 0754 this._validationMessage = _msgEl.insertAfter(_button.length ? _button : _elem); 0755 0756 _elem.on('blur' + _eventsNs, $proxy(this.Validate, this)); 0757 } else { 0758 this._validationMessage.remove(); 0759 } 0760 }, 0761 0762 _setRTL: function(value) { 0763 _applyArrowButton( this._prevButton.css('float', value ? 'right' : 'left'), !value ); 0764 _applyArrowButton( this._nextButton.css('float', value ? 'left' : 'right'), value ); 0765 }, 0766 0767 _keyDown: function (event) { 0768 // Don't use $.ui.keyCode to help minification. 0769 switch (event.keyCode) { 0770 case 13: // Enter. 0771 if (!this.element.val()) { 0772 this._chooseMonth(new Date().getMonth() + 1); 0773 } 0774 this.Close(event); 0775 break; 0776 case 27: // Escape 0777 case 9: // Tab 0778 this.Close(event); 0779 break; 0780 } 0781 }, 0782 0783 _duration: function() { 0784 var _dur = this.options.Duration; 0785 0786 if ($.isNumeric(_dur)) { 0787 return _dur; 0788 } 0789 0790 return _dur in _speeds ? _speeds[ _dur ] : _speeds._default; 0791 }, 0792 0793 _position: _hasPosition ? 0794 function($menu) { 0795 var _defauts = this.options.IsRTL ? _RTL_defaultPos : _defaultPos; 0796 var _posOpts = $.extend(_defauts, this.options.Position); 0797 0798 // Only in IE and jQuery 1.12.0 or 2.2.0, .position() will add scrollTop to the top coordinate (#40) 0799 return $menu.position($.extend({of: this.element}, _posOpts)); 0800 } : 0801 function($menu) { 0802 // Only in IE and jQuery 1.12.0 or 2.2.0, .offset() will add scrollTop to the top coordinate (#40) 0803 var _el = this.element, 0804 _css = { top: (_el.offset().top + _el.height() + 7) + 'px' }; 0805 0806 if (this.options.IsRTL) { 0807 _css.left = (_el.offset().left-$menu.width()+_el.width() + 7) + 'px'; 0808 } else { 0809 _css.left = _el.offset().left + 'px'; 0810 } 0811 0812 return $menu.css(_css); 0813 }, 0814 0815 _setUseInputMask: function () { 0816 if (!this._isMonthInputType) { 0817 try { 0818 if (this.options.UseInputMask) { 0819 this.element.mask( this._formatMonth(new Date).replace(/\d/g, 9) ); 0820 } else { 0821 this.element.unmask(); 0822 } 0823 } catch (e) {} 0824 } 0825 }, 0826 0827 _setDisabledState: function () { 0828 var isDisabled = this.options.Disabled, _elem = this.element; 0829 0830 // Disable the associated input field. 0831 _elem[0].disabled = isDisabled; 0832 _elem.toggleClass(_disabledClass, isDisabled); 0833 0834 if (isDisabled) { 0835 this._validationMessage.hide(); 0836 } 0837 0838 this.Close(); 0839 this._updateButton(); 0840 0841 _event('OnAfterSetDisabled', this)(isDisabled); 0842 }, 0843 0844 _getPickerYear: function () { 0845 return this._pickerYear; 0846 }, 0847 0848 _setPickerYear: function (year) { 0849 this._pickerYear = year || new Date().getFullYear(); 0850 this._titleButton.jqueryUIButton({ label: this._i18n('year') + ' ' + this._pickerYear }); 0851 }, 0852 0853 // When calling this method with a falsy (undefined) date 0854 // value, this.element.val() is used as the date value. 0855 // 0856 // Therefore it's important to update the input field 0857 // before calling this method. 0858 _updateAlt: function (noop, date) { 0859 var _field = $(this.options.AltField); 0860 if (_field.length) { 0861 _field.val( this._formatMonth(date, this.options.AltFormat) ); 0862 } 0863 }, 0864 0865 _chooseMonth: function (month) { 0866 var _year = this._getPickerYear(); 0867 var date = new Date(_year, month-1); 0868 this.element.val(this._formatMonth( date )).blur(); 0869 this._updateAlt(0, date); 0870 0871 _setActive( this._selectedBtn, false ); 0872 this._selectedBtn = _setActive( $(this._buttons[month-1]), true ); 0873 0874 _event('OnAfterChooseMonth', this)(date); 0875 }, 0876 0877 _chooseYear: function (year) { 0878 this._backToYear = 0; 0879 this._setPickerYear(year); 0880 this._buttons.removeClass(_todayClass); 0881 this._showMonths(); 0882 this._applyJumpYearsHint(); 0883 0884 _event('OnAfterChooseYear', this)(); 0885 }, 0886 0887 _showMonths: function () { 0888 var _months = this._i18n('months'); 0889 0890 this._prevButton 0891 .attr('title', this._i18n('prevYear')) 0892 .off(click) 0893 .on(click, $proxy(this._addToYear, this, -1)); 0894 0895 this._nextButton 0896 .attr('title', this._i18n('nextYear')) 0897 .off(click) 0898 .on(click, $proxy(this._addToYear, this, 1)); 0899 0900 this._buttons.off(_eventsNs); 0901 0902 var me = this, _onMonthClick = $proxy(me._onMonthClick, me); 0903 $.each(_months, function(index, monthName) { 0904 $(me._buttons[index]) 0905 .on(click, {month: index+1}, _onMonthClick ) 0906 .jqueryUIButton('option', 'label', monthName); 0907 }); 0908 0909 this._decorateButtons(); 0910 }, 0911 0912 _showYearsClickHandler: function () { 0913 this._buttons.removeClass(_todayClass); 0914 if (!this._backToYear) { 0915 this._backToYear = this._getPickerYear(); 0916 this._showYears(); 0917 0918 var _label = this._i18n('backTo') + ' ' + this._getPickerYear(); 0919 this._titleButton.jqueryUIButton({ label: _label }).data( _clearHint )(); 0920 0921 _event('OnAfterChooseYears', this)(); 0922 } else { 0923 this._setPickerYear(this._backToYear); 0924 this._applyJumpYearsHint(); 0925 this._showMonths(); 0926 this._backToYear = 0; 0927 } 0928 }, 0929 0930 _showYears: function () { 0931 var _currYear = this._getPickerYear(), 0932 _yearDifferential = -4, 0933 _firstYear = (_currYear + _yearDifferential), 0934 AMOUNT_TO_ADD = 12, 0935 _thisYear = new Date().getFullYear(); 0936 0937 var _minDate = this._MinMonth; 0938 var _maxDate = this._MaxMonth; 0939 var _minYear = _minDate ? _toYear(_minDate) : 0; 0940 var _maxYear = _maxDate ? _toYear(_maxDate) : 0; 0941 this._prevButton 0942 .attr('title', this._i18n('prev12Years')) 0943 .off(click) 0944 .on(click, $proxy(this._addToYears, this, -AMOUNT_TO_ADD)); 0945 0946 this._nextButton 0947 .attr('title', this._i18n('next12Years')) 0948 .off(click) 0949 .on(click, $proxy(this._addToYears, this, AMOUNT_TO_ADD)); 0950 0951 _setDisabled(this._prevButton, _minYear && (_firstYear - 1) < _minYear); 0952 _setDisabled(this._nextButton, _maxYear && (_firstYear + 12) -1 > _maxYear); 0953 0954 this._buttons.off(_eventsNs); 0955 0956 _setActive( this._selectedBtn, false ); 0957 0958 var _selYear = this.GetSelectedYear(); 0959 var _onClick = $proxy(this._onYearClick, this); 0960 var _todayWithinBounds = _between(_thisYear, _minYear, _maxYear); 0961 var _selWithinBounds = _between(_selYear, _minYear, _maxYear); 0962 0963 for (var _counter = 0; _counter < 12; _counter++) { 0964 var _year = _currYear + _yearDifferential; 0965 0966 var _btn = $( this._buttons[_counter] ).jqueryUIButton({ 0967 disabled: !_between(_year, _minYear, _maxYear), 0968 label: _year 0969 }) 0970 .toggleClass(_todayClass, _year === _thisYear && _todayWithinBounds) // Heighlight the current year. 0971 .on(click, { year: _year }, _onClick ); 0972 0973 // Heighlight the selected year. 0974 if (_selWithinBounds && _selYear && _selYear === _year) { 0975 this._selectedBtn = _setActive( _btn , true ); 0976 } 0977 0978 _yearDifferential++; 0979 } 0980 }, 0981 0982 _onMonthClick: function(event) { 0983 this._chooseMonth(event.data.month); 0984 this.Close(event); 0985 }, 0986 0987 _onYearClick: function(event) { 0988 this._chooseYear(event.data.year); 0989 }, 0990 0991 _addToYear: function(amount) { 0992 this._setPickerYear( this._getPickerYear() + amount ); 0993 this.element.focus(); 0994 this._decorateButtons(); 0995 0996 _event('OnAfter' + (amount > 0 ? 'Next' : 'Previous') + 'Year', this)(); 0997 }, 0998 0999 _addToYears: function(amount) { 1000 this._pickerYear = this._getPickerYear() + amount; 1001 this._showYears(); 1002 this.element.focus(); 1003 1004 _event('OnAfter' + (amount > 0 ? 'Next' : 'Previous') + 'Years', this)(); 1005 }, 1006 1007 _ajustYear: function(_opts) { 1008 var _year = _opts.StartYear || this.GetSelectedYear() || new Date().getFullYear(); 1009 if (this._MinMonth !== null) { 1010 _year = Math.max(_toYear(this._MinMonth), _year); 1011 } 1012 if (this._MaxMonth !== null) { 1013 _year = Math.min(_toYear(this._MaxMonth), _year); 1014 } 1015 1016 this._setPickerYear( _year ); 1017 }, 1018 1019 _decorateButtons: function() { 1020 var _curYear = this._getPickerYear(), _todaysMonth = _toMonth(new Date), 1021 _minDate = this._MinMonth, _maxDate = this._MaxMonth; 1022 1023 // Heighlight the selected month. 1024 _setActive( this._selectedBtn, false ); 1025 var _sel = this.GetSelectedDate(); 1026 var _withinBounds = _between(_sel ? _toMonth(_sel) : null, _minDate, _maxDate); 1027 1028 if (_sel && _sel.getFullYear() === _curYear) { 1029 this._selectedBtn = _setActive( $(this._buttons[_sel.getMonth()]) , _withinBounds ); 1030 } 1031 1032 // Disable the next/prev button if we've reached the min/max year. 1033 _setDisabled(this._prevButton, _minDate && _curYear == _toYear(_minDate)); 1034 _setDisabled(this._nextButton, _maxDate && _curYear == _toYear(_maxDate)); 1035 1036 for (var i = 0; i < 12; i++) { 1037 // Disable the button if the month is not between the 1038 // min and max interval. 1039 var _month = (_curYear * 12) + i, _isBetween = _between(_month, _minDate, _maxDate); 1040 1041 $(this._buttons[i]) 1042 .jqueryUIButton({ disabled: !_isBetween }) 1043 .toggleClass(_todayClass, _isBetween && _month == _todaysMonth); // Highlights today's month. 1044 } 1045 } 1046 }); 1047 }(jQuery, window, document, Date));