month-picker.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
  1. /*
  2. The jQuery UI Month Picker Version 3.0.4
  3. https://github.com/KidSysco/jquery-ui-month-picker/
  4. https://kidsysco.github.io/jquery-ui-month-picker/
  5. Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
  6. This program is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see
  16. <http://www.gnu.org/licenses/gpl-3.0.txt>.
  17. */
  18. (function ($, window, document, Date) {
  19. 'use strict';
  20. var _setupErr = 'MonthPicker Error: ';
  21. // This test must be run before any rererence is made to jQuery.
  22. // In case the user didn't load jQuery or jQuery UI the plugin
  23. // will fail before it get's to this test + there is no reason
  24. // to perform this test for every MonthPicker instance being created.
  25. if (!$ || !$.ui || !$.ui.button || !$.ui.datepicker) {
  26. alert(_setupErr + 'The jQuery UI button and datepicker plug-ins must be loaded.');
  27. return;
  28. }
  29. // Creates an alias to jQuery UI's .button() that dosen't
  30. // conflict with Bootstrap.js button (#35)
  31. $.widget.bridge('jqueryUIButton', $.ui.button);
  32. var _speeds = $.fx.speeds;
  33. var _eventsNs = '.MonthPicker';
  34. //var _textfieldClass = 'month-year-input';
  35. var _textfieldClass = 'hasDatepicker';
  36. var _clearHint = 'month-picker-clear-hint';
  37. var _iconClass = '.ui-button-icon-primary';
  38. var _disabledClass = 'month-picker-disabled';
  39. var _todayClass = 'ui-state-highlight';
  40. var _selectedClass = 'ui-state-active';
  41. var _defaultClass = 'ui-state-default';
  42. var _defaultPos = { my: 'left top+1', at: 'left bottom' };
  43. var _RTL_defaultPos = { my: 'right top+1', at: 'right bottom' };
  44. var _posErr = _setupErr + 'The jQuery UI position plug-in must be loaded.';
  45. var _badOptValErr = _setupErr + 'Unsupported % option value, supported values are: ';
  46. var _badMinMaxVal = _setupErr + '"_" is not a valid %Month value.';
  47. var _openedInstance = null;
  48. var _hasPosition = !!$.ui.position;
  49. var _animVals = {
  50. Animation: ['slideToggle', 'fadeToggle', 'none'],
  51. ShowAnim: ['fadeIn', 'slideDown', 'none'],
  52. HideAnim: ['fadeOut', 'slideUp', 'none']
  53. };
  54. var _setOptionHooks = {
  55. ValidationErrorMessage: '_createValidationMessage',
  56. Disabled: '_setDisabledState',
  57. ShowIcon: '_updateButton',
  58. Button: '_updateButton',
  59. ShowOn: '_updateFieldEvents',
  60. IsRTL: '_setRTL',
  61. AltFormat: '_updateAlt',
  62. AltField: '_updateAlt',
  63. StartYear: '_setPickerYear',
  64. MinMonth: '_setMinMonth',
  65. MaxMonth: '_setMaxMonth',
  66. SelectedMonth: '_setSelectedMonth'
  67. };
  68. var $noop = $.noop;
  69. var $proxy = $.proxy;
  70. var $datepicker = $.datepicker;
  71. var click = 'click' + _eventsNs;
  72. function _toMonth(date) {
  73. return date.getMonth() + (date.getFullYear() * 12);
  74. }
  75. function _toYear(month) {
  76. return Math.floor(month / 12);
  77. }
  78. function _stayActive() {
  79. $(this).addClass(_selectedClass);
  80. }
  81. function _setActive( el, state ) {
  82. return el[ state ? 'on' : 'off' ]('mousenter mouseout', _stayActive )
  83. .toggleClass(_selectedClass, state);
  84. }
  85. function _between(month, from, until) {
  86. return (!from || month >= from) && (!until || month <= until);
  87. }
  88. function _encodeMonth(_inst, _val) {
  89. if (_val === null) {
  90. return _val;
  91. } else if (_val instanceof Date) {
  92. return _toMonth(_val);
  93. } else if ($.isNumeric(_val)) {
  94. return _toMonth(new Date) + parseInt(_val, 10);
  95. }
  96. var _date = _inst._parseMonth(_val);
  97. if (_date) {
  98. return _toMonth(_date);
  99. }
  100. return _parsePeriod(_val);
  101. }
  102. function _event(_name, _inst) {
  103. return $proxy(_inst.options[_name] || $noop, _inst.element[0]);
  104. }
  105. function _parsePeriod(_val, _initDate) {
  106. // Parsing is done by replacing tokens in the value to form
  107. // a JSON object with it's keys and values reversed
  108. // (example '+1y +2m' will turn into {"+1":"y","+2":"m"})
  109. // After that we just revers the keys and values.
  110. var _json = $.trim(_val);
  111. _json = _json.replace(/y/i, '":"y"');
  112. _json = _json.replace(/m/i, '":"m"');
  113. try {
  114. var _rev = JSON.parse( '{"' + _json.replace(/ /g, ',"') + '}' ), obj = {};
  115. for (var key in _rev) {
  116. obj[ _rev[key] ] = key;
  117. }
  118. var _month = _toMonth(new Date);
  119. _month += (parseInt(obj.m, 10) || 0);
  120. return _month + (parseInt(obj.y, 10) || 0) * 12;
  121. } catch (e) {
  122. return false;
  123. }
  124. }
  125. function _makeDefaultButton(options) {
  126. // this refers to the associated input field.
  127. return $('<span id="MonthPicker_Button_' + this.id + '" class="month-picker-open-button">' + options.i18n.buttonText + '</span>')
  128. .jqueryUIButton({
  129. text: false,
  130. icons: {
  131. // Defaults to 'ui-icon-calculator'.
  132. primary: options.ButtonIcon
  133. }
  134. });
  135. }
  136. function _applyArrowButton($el, dir) {
  137. $el.jqueryUIButton('option', {
  138. icons: {
  139. primary: 'ui-icon-circle-triangle-' + (dir ? 'w' : 'e')
  140. }
  141. });
  142. }
  143. function _isInline(elem) {
  144. return !elem.is('input');
  145. }
  146. $.MonthPicker = {
  147. VERSION: '3.0.4', // Added in version 2.4;
  148. i18n: {
  149. year: 'Año',
  150. prevYear: 'Año Anterior',
  151. nextYear: 'Siguiente Año',
  152. next12Years: 'Siguentes 12 Años',
  153. prev12Years: 'Anteriores 12 Años',
  154. nextLabel: 'Siguiente',
  155. prevLabel: 'Anterior',
  156. buttonText: 'Abrir calendario',
  157. jumpYears: 'Saltar Años',
  158. backTo: 'Regresar a',
  159. months: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic']
  160. }
  161. };
  162. var _markup =
  163. '<div class="ui-widget-header month-picker-header ui-corner-all">' +
  164. '<table class="month-picker-year-table">' +
  165. '<tr>' +
  166. '<td class="month-picker-previous"><a /></td>' +
  167. '<td class="month-picker-title"><a /></td>' +
  168. '<td class="month-picker-next"><a /></td>' +
  169. '</tr>' +
  170. '</table>' +
  171. '</div>' +
  172. '<div>' +
  173. '<table class="month-picker-month-table" />' +
  174. '</div>';
  175. // Groups state and functionallity to fade in the jump years hint
  176. // when the user mouses over the Year 2016 text.
  177. // NOTE: An invocation of this function:
  178. // 1: Is an independent instance with it's own unique state.
  179. // 2: Assumes that there is no previous hint applied to the
  180. // button (it dosen't remove the existing hint).
  181. function _applyButtonHint(_button, _hintText) {
  182. var _speed = 125, _currentLabel, _startTimeout, _labelElem = $();
  183. _button.on('mouseenter' + _eventsNs + 'h', _prepareToStart);
  184. // Setp 1: Wait to make sure the user isn't just mousing over and
  185. // away from the button.
  186. // NOTE: If _fadeOutHint() is triggered on mouseleave before the
  187. // timeout is triggered the animation is canceled.
  188. function _prepareToStart() {
  189. _startTimeout = setTimeout(_fadeOutLabel, 175);
  190. }
  191. // Setp 2: Fade out the label (Year 2016) text to 45%.
  192. function _fadeOutLabel() {
  193. _startTimeout = null;
  194. _labelElem = $('span', _button).animate({ opacity: 0.45 }, _speed, _fadeInHint);
  195. }
  196. // Setp 3: Fade in the hint text (Jump years).
  197. function _fadeInHint() {
  198. _currentLabel = _labelElem.text();
  199. _labelElem.animate({ opacity: 1 }, _speed).text(_hintText);
  200. }
  201. _button.on('mouseleave' + _eventsNs + 'h', _fadeOutHint);
  202. function _fadeOutHint() {
  203. if (_startTimeout) {
  204. // If the user is just moving over and away from the button, cancel
  205. // the animation completely.
  206. clearTimeout(_startTimeout);
  207. } else {
  208. // Setp 4: Fade out the hint text (Jump years) to 45%.
  209. _labelElem = $('span', _button).animate({ opacity: 0.45 }, _speed, _fadeInLabel);
  210. }
  211. }
  212. // Setp 5: Fade in the label (Year 2016) text.
  213. function _fadeInLabel() {
  214. _labelElem.text( _currentLabel ).animate({opacity: 1}, _speed);
  215. }
  216. // Adds a function to the button elemene which is called when the
  217. // user clicks the button (the hint needs to be removed).
  218. _button.data(_clearHint, function() {
  219. clearTimeout(_startTimeout);
  220. _labelElem.stop().css({ opacity: 1 });
  221. _button.off(_eventsNs + 'h');
  222. });
  223. } // End _applyButtonHint()
  224. function _setDisabled(_button, _value) {
  225. var _btnWidget = _button.data('ui-button');
  226. if (_btnWidget.option('disabled') !== _value) {
  227. _btnWidget.option('disabled', _value);
  228. }
  229. }
  230. $.widget("KidSysco.MonthPicker", {
  231. /******* Properties *******/
  232. options: {
  233. i18n: {},
  234. IsRTL: false,
  235. Position: null,
  236. StartYear: null,
  237. ShowIcon: true,
  238. UseInputMask: false,
  239. ValidationErrorMessage: null,
  240. Disabled: false,
  241. MonthFormat: 'mm/yy',
  242. Animation: 'fadeToggle',
  243. ShowAnim: null,
  244. HideAnim: null,
  245. ShowOn: null,
  246. MinMonth: null,
  247. MaxMonth: null,
  248. Duration: 'normal',
  249. Button: _makeDefaultButton,
  250. ButtonIcon: 'ui-icon-calculator'
  251. },
  252. _monthPickerButton: $(),
  253. _validationMessage: $(),
  254. _selectedBtn: $(),
  255. /******* jQuery UI Widget Factory Overrides ********/
  256. _destroy: function () {
  257. var _elem = this.element;
  258. if ($.mask && this.options.UseInputMask) {
  259. _elem.unmask();
  260. if (!this.GetSelectedDate()) {
  261. _elem.val('');
  262. }
  263. }
  264. _elem.removeClass(_textfieldClass).off(_eventsNs);
  265. $(document).off(_eventsNs + this.uuid);
  266. this._monthPickerMenu.remove();
  267. var _button = this._monthPickerButton.off(click);
  268. if (this._removeOldBtn) {
  269. _button.remove();
  270. }
  271. this._validationMessage.remove();
  272. if (_openedInstance === this) {
  273. _openedInstance = null;
  274. }
  275. },
  276. _setOption: function (key, value) {
  277. switch (key) {
  278. case 'i18n':
  279. // Pass a clone i18n object to the this._super.
  280. value = $.extend({}, value);
  281. break;
  282. case 'Position':
  283. if (!_hasPosition) {
  284. alert(_posErr);
  285. return;
  286. }
  287. break;
  288. case 'MonthFormat':
  289. var date = this.GetSelectedDate();
  290. if (date) {
  291. this.element.val( this.FormatMonth(date, value) );
  292. }
  293. break;
  294. }
  295. // Make sure the user passed in a valid Animation, ShowAnim and HideAnim options values.
  296. if (key in _animVals && $.inArray(value, _animVals[key]) === -1) {
  297. alert(_badOptValErr.replace(/%/, key) + _animVals[key]);
  298. return;
  299. }
  300. // In jQuery UI 1.8, manually invoke the _setOption method from the base widget.
  301. //$.Widget.prototype._setOption.apply(this, arguments);
  302. // In jQuery UI 1.9 and above, you use the _super method instead.
  303. this._super(key, value);
  304. _setOptionHooks[key] ? this[ _setOptionHooks[key] ](value) : 0;
  305. },
  306. _create: function () {
  307. var _el = this.element, _opts = this.options, _type = _el.attr('type');
  308. // According to http://www.w3.org/TR/html-markup/input.html#input
  309. // An input element with no type attribute specified represents the same thing as an
  310. // input element with its type attribute set to "text".
  311. // TLDR:
  312. // http://www.w3.org/TR/html5/forms.html#the-input-element
  313. // https://api.jquery.com/text-selector/
  314. // $.inArray(void 0, ['text', 'month', void 0]) returns -1 when searching for undefined in IE8 (#45)
  315. // This is only noticable in the real version of IE8, emulated versions
  316. // from the dev tools in modern browsers do not suffer from this issue.
  317. // if (!_el.is('input,div,span') || $.inArray(_el.attr('type'), ['text', 'month', void 0]) === -1) {
  318. if (!_el.is('input,div,span') || (_type !== 'text' && _type !== 'month' && _type !== void 0)) {
  319. var error = _setupErr + 'MonthPicker can only be called on text or month inputs.';
  320. // Call alert first so that IE<10 won't trip over console.log and swallow all errors.
  321. alert(error + ' \n\nSee (developer tools) for more details.');
  322. console.error(error + '\n Caused by:');
  323. console.log(_el[0]);
  324. return false;
  325. }
  326. if (!$.mask && _opts.UseInputMask) {
  327. alert(_setupErr + 'The UseInputMask option requires the Input Mask Plugin. Get it from digitalbush.com');
  328. return false;
  329. }
  330. if (_opts.Position !== null && !_hasPosition) {
  331. alert(_posErr);
  332. return false;
  333. }
  334. // Make sure the user passed in a valid Animation, ShowAnim and HideAnim options values.
  335. for (var opt in _animVals) {
  336. if (_opts[opt] !== null && $.inArray(_opts[opt], _animVals[opt]) === -1) {
  337. alert(_badOptValErr.replace(/%/, opt) + _animVals[opt]);
  338. return false;
  339. }
  340. }
  341. this._isMonthInputType = _el.attr('type') === 'month';
  342. if (this._isMonthInputType) {
  343. this.options.MonthFormat = this.MonthInputFormat;
  344. _el.css('width', 'auto');
  345. }
  346. var _menu = this._monthPickerMenu = $('<div id="MonthPicker_' + _el[0].id + '" class="month-picker ui-widget ui-widget-content ui-corner-all"></div>').hide();
  347. var isInline = _isInline(_el);
  348. $(_markup).appendTo(_menu);
  349. _menu.appendTo( isInline ? _el : document.body );
  350. this._titleButton =
  351. $('.month-picker-title', _menu)
  352. .click($proxy(this._showYearsClickHandler, this))
  353. .find('a').jqueryUIButton()
  354. .removeClass(_defaultClass);
  355. this._applyJumpYearsHint();
  356. this._createValidationMessage();
  357. this._prevButton = $('.month-picker-previous>a', _menu)
  358. .jqueryUIButton({ text: false })
  359. .removeClass(_defaultClass);
  360. this._nextButton = $('.month-picker-next>a', _menu)
  361. .jqueryUIButton({ text: false })
  362. .removeClass(_defaultClass);
  363. this._setRTL(_opts.IsRTL); //Assigns icons to the next/prev buttons.
  364. $(_iconClass, this._nextButton).text(this._i18n('nextLabel'));
  365. $(_iconClass, this._prevButton).text(this._i18n('prevLabel'));
  366. var $table = $('.month-picker-month-table', _menu);
  367. for (var i = 0; i < 12; i++) {
  368. var $tr = !(i % 3) ? $('<tr />').appendTo($table) : $tr;
  369. // Use <a> tag instead of <button> to avoid issues
  370. // only with Google Chrome (#50).
  371. $tr.append('<td><a class="button-' + (i + 1) + '" /></td>');
  372. }
  373. this._buttons = $('a', $table).jqueryUIButton();
  374. _menu.on('mousedown' + _eventsNs, function (event) {
  375. event.preventDefault();
  376. });
  377. // Checks and initailizes Min/MaxMonth properties
  378. // (creates _setMinMonth and _setMaxMonth methods).
  379. var me = this, Month = 'Month';
  380. $.each(['Min', 'Max'], function(i, type) {
  381. me["_set" + type + Month] = function(val) {
  382. if ((me['_' + type + Month] = _encodeMonth(me, val)) === false) {
  383. alert(_badMinMaxVal.replace(/%/, type).replace(/_/, val));
  384. }
  385. };
  386. me._setOption(type + Month, me.options[type + Month]);
  387. });
  388. var _selMonth = _opts.SelectedMonth;
  389. if (_selMonth !== void 0) {
  390. var month = _encodeMonth(this, _selMonth);
  391. _el.val( this._formatMonth(new Date( _toYear(month), month % 12, 1)) );
  392. }
  393. this._updateAlt();
  394. this._setUseInputMask();
  395. this._setDisabledState();
  396. this.Destroy = this.destroy;
  397. if (isInline) {
  398. this.Open();
  399. } else {
  400. // Update the alt field if the user manually changes
  401. // the input field.
  402. _el.addClass(_textfieldClass);
  403. _el.change($proxy(this._updateAlt, this));
  404. }
  405. },
  406. /****** Publicly Accessible API functions ******/
  407. GetSelectedDate: function () {
  408. return this._parseMonth();
  409. },
  410. GetSelectedYear: function () {
  411. var date = this.GetSelectedDate();
  412. return date ? date.getFullYear() : NaN;
  413. },
  414. GetSelectedMonth: function () {
  415. var date = this.GetSelectedDate();
  416. return date ? date.getMonth()+1 : NaN;
  417. },
  418. Validate: function() {
  419. var _date = this.GetSelectedDate();
  420. if (this.options.ValidationErrorMessage !== null && !this.options.Disabled) {
  421. this._validationMessage.toggle(!_date);
  422. }
  423. return _date;
  424. },
  425. GetSelectedMonthYear: function () {
  426. var date = this.Validate();
  427. return date ? (date.getMonth() + 1) + '/' + date.getFullYear() : null;
  428. },
  429. Disable: function () {
  430. this._setOption("Disabled", true);
  431. },
  432. Enable: function () {
  433. this._setOption("Disabled", false);
  434. },
  435. ClearAllCallbacks: function () {
  436. for (var _opt in this.options) {
  437. if (_opt.indexOf('On') === 0) {
  438. this.options[_opt] = $noop;
  439. }
  440. }
  441. },
  442. Clear: function () {
  443. this.element.val('');
  444. $(this.options.AltField).val('');
  445. this._validationMessage.hide();
  446. },
  447. Toggle: function (event) {
  448. return this._visible ? this.Close(event) : this.Open(event);
  449. },
  450. Open: function (event) {
  451. var _elem = this.element, _opts = this.options;
  452. if (!_opts.Disabled && !this._visible) {
  453. // Allow the user to prevent opening the menu.
  454. event = event || $.Event();
  455. if (_event('OnBeforeMenuOpen', this)(event) === false || event.isDefaultPrevented()) {
  456. return;
  457. }
  458. this._visible = true;
  459. this._ajustYear(_opts);
  460. var _menu = this._monthPickerMenu;
  461. this._showMonths();
  462. if (_isInline(_elem)) {
  463. _menu.css('position', 'static').show();
  464. _event('OnAfterMenuOpen', this)();
  465. } else {
  466. // If there is an open menu close it first.
  467. if (_openedInstance) {
  468. _openedInstance.Close(event);
  469. }
  470. _openedInstance = this;
  471. $(document).on('mousedown' + _eventsNs + this.uuid, $proxy(this.Close, this))
  472. .on('keydown' + _eventsNs + this.uuid, $proxy(this._keyDown, this));
  473. // Trun off validation so that clicking one of the months
  474. // won't blur the input field and trogger vlaidation
  475. // befroe the month was chosen (click event was triggered).
  476. // It is turned back on when Hide() is called.
  477. _elem.off('blur' + _eventsNs).focus();
  478. var _anim = _opts.ShowAnim || _opts.Animation,
  479. _noAnim = _anim === 'none';
  480. // jQuery UI overrides jQuery.show and dosen't
  481. // call the start callback.
  482. // see: http://api.jqueryui.com/show/
  483. _menu[ _noAnim ? 'fadeIn' : _anim ]({
  484. duration: _noAnim ? 0 : this._duration(),
  485. start: $proxy(this._position, this, _menu),
  486. complete: _event('OnAfterMenuOpen', this)
  487. });
  488. }
  489. }
  490. },
  491. Close: function (event) {
  492. var _elem = this.element;
  493. if (!_isInline(_elem) && this._visible) {
  494. var _menu = this._monthPickerMenu,
  495. _opts = this.options;
  496. event = event || $.Event();
  497. if (_event('OnBeforeMenuClose', this)(event) === false || event.isDefaultPrevented()) {
  498. return;
  499. }
  500. // If the menu is closed while in jump years mode, bring back
  501. // the jump years hint.
  502. if (this._backToYear) {
  503. this._applyJumpYearsHint();
  504. this._backToYear = 0;
  505. }
  506. this._visible = false;
  507. _openedInstance = null;
  508. $(document).off('keydown' + _eventsNs + this.uuid)
  509. .off('mousedown' + _eventsNs + this.uuid);
  510. this.Validate();
  511. _elem.on('blur' + _eventsNs, $proxy(this.Validate, this));
  512. var _callback = _event('OnAfterMenuClose', this);
  513. var _anim = _opts.HideAnim || _opts.Animation;
  514. if (_anim === 'none') {
  515. _menu.hide(0, _callback);
  516. } else {
  517. _menu[ _anim ](this._duration(), _callback);
  518. }
  519. }
  520. },
  521. /**
  522. * Methods the user can override to use a third party library
  523. * such as http://momentjs.com for parsing and formatting months.
  524. */
  525. MonthInputFormat: 'yy-mm',
  526. ParseMonth: function (str, format) {
  527. try {
  528. return $datepicker.parseDate('dd' + format, '01' + str);
  529. } catch (e) {
  530. return null;
  531. }
  532. },
  533. FormatMonth: function (date, format) {
  534. try {
  535. return $datepicker.formatDate(format, date) || null;
  536. } catch (e) {
  537. return null;
  538. }
  539. },
  540. /****** Private and Misc Utility functions ******/
  541. _setSelectedMonth: function (_selMonth) {
  542. var month = _encodeMonth(this, _selMonth), _el = this.element;
  543. if (month) {
  544. var date = new Date( _toYear(month), month % 12, 1 );
  545. _el.val( this._formatMonth( date ) );
  546. this._updateAlt(0, date);
  547. this._validationMessage.hide();
  548. } else {
  549. this.Clear();
  550. }
  551. this._ajustYear(this.options);
  552. this._showMonths();
  553. },
  554. _applyJumpYearsHint: function() {
  555. _applyButtonHint(this._titleButton, this._i18n('jumpYears'));
  556. },
  557. _i18n: function(str) {
  558. var _trans = this.options.i18n[str];
  559. if (typeof _trans === 'undefined') {
  560. return $.MonthPicker.i18n[str];
  561. } else {
  562. return _trans;
  563. }
  564. },
  565. _parseMonth: function (str, format) {
  566. return this.ParseMonth(str || this.element.val(), format || this.options.MonthFormat);
  567. },
  568. _formatMonth: function (date, format) {
  569. return this.FormatMonth(date || this._parseMonth(), format || this.options.MonthFormat);
  570. },
  571. _updateButton: function () {
  572. var isDisabled = this.options.Disabled;
  573. this._createButton();
  574. // If the button is a jQuery UI button,
  575. // plain HTML button or an input we support disable it,
  576. // otherwise the user must handle the diabled state
  577. // by creating an appropriate button by passing
  578. // a function. See Button option: Img tag tests for
  579. // more details.
  580. var _button = this._monthPickerButton;
  581. try {
  582. _button.jqueryUIButton('option', 'disabled', isDisabled);
  583. } catch (e) {
  584. _button.filter('button,input').prop('disabled', isDisabled);
  585. }
  586. this._updateFieldEvents();
  587. },
  588. _createButton: function () {
  589. var _elem = this.element, _opts = this.options;
  590. if (_isInline(_elem)) return;
  591. var _oldButton = this._monthPickerButton.off(_eventsNs);
  592. var _btnOpt = _opts.ShowIcon ? _opts.Button : false;
  593. if ($.isFunction(_btnOpt)) {
  594. var _params = $.extend(true, {i18n: $.extend(true, {}, $.MonthPicker.i18n)}, this.options);
  595. _btnOpt = _btnOpt.call(_elem[0], _params);
  596. }
  597. var _removeOldBtn = false;
  598. this._monthPickerButton = ( _btnOpt instanceof $ ? _btnOpt : $(_btnOpt) )
  599. .each(function() {
  600. if (!$.contains(document.body, this)) {
  601. _removeOldBtn = true;
  602. $(this).insertAfter(_elem);
  603. }
  604. })
  605. .on(click, $proxy(this.Toggle, this))
  606. .on('mousedown' + _eventsNs, function(r) {
  607. r.preventDefault();
  608. });
  609. if (this._removeOldBtn) {
  610. _oldButton.remove();
  611. }
  612. this._removeOldBtn = _removeOldBtn;
  613. },
  614. _updateFieldEvents: function () {
  615. var _events = click + ' focus' + _eventsNs;
  616. this.element.off(_events);
  617. if (this.options.ShowOn === 'both' || !this._monthPickerButton.length) {
  618. this.element.on(_events, $proxy(this.Open, this));
  619. }
  620. },
  621. _createValidationMessage: function () {
  622. var _errMsg = this.options.ValidationErrorMessage, _elem = this.element;
  623. if ($.inArray(_errMsg, [null, '']) === -1) {
  624. var _msgEl = $('<span id="MonthPicker_Validation_' + _elem[0].id + '" class="month-picker-invalid-message">' + _errMsg + '</span>');
  625. var _button = this._monthPickerButton;
  626. this._validationMessage = _msgEl.insertAfter(_button.length ? _button : _elem);
  627. _elem.on('blur' + _eventsNs, $proxy(this.Validate, this));
  628. } else {
  629. this._validationMessage.remove();
  630. }
  631. },
  632. _setRTL: function(value) {
  633. _applyArrowButton( this._prevButton.css('float', value ? 'right' : 'left'), !value );
  634. _applyArrowButton( this._nextButton.css('float', value ? 'left' : 'right'), value );
  635. },
  636. _keyDown: function (event) {
  637. // Don't use $.ui.keyCode to help minification.
  638. switch (event.keyCode) {
  639. case 13: // Enter.
  640. if (!this.element.val()) {
  641. this._chooseMonth(new Date().getMonth() + 1);
  642. }
  643. this.Close(event);
  644. break;
  645. case 27: // Escape
  646. case 9: // Tab
  647. this.Close(event);
  648. break;
  649. }
  650. },
  651. _duration: function() {
  652. var _dur = this.options.Duration;
  653. if ($.isNumeric(_dur)) {
  654. return _dur;
  655. }
  656. return _dur in _speeds ? _speeds[ _dur ] : _speeds._default;
  657. },
  658. _position: _hasPosition ?
  659. function($menu) {
  660. var _defauts = this.options.IsRTL ? _RTL_defaultPos : _defaultPos;
  661. var _posOpts = $.extend(_defauts, this.options.Position);
  662. // Only in IE and jQuery 1.12.0 or 2.2.0, .position() will add scrollTop to the top coordinate (#40)
  663. return $menu.position($.extend({of: this.element}, _posOpts));
  664. } :
  665. function($menu) {
  666. // Only in IE and jQuery 1.12.0 or 2.2.0, .offset() will add scrollTop to the top coordinate (#40)
  667. var _el = this.element,
  668. _css = { top: (_el.offset().top + _el.height() + 7) + 'px' };
  669. if (this.options.IsRTL) {
  670. _css.left = (_el.offset().left-$menu.width()+_el.width() + 7) + 'px';
  671. } else {
  672. _css.left = _el.offset().left + 'px';
  673. }
  674. return $menu.css(_css);
  675. },
  676. _setUseInputMask: function () {
  677. if (!this._isMonthInputType) {
  678. try {
  679. if (this.options.UseInputMask) {
  680. this.element.mask( this._formatMonth(new Date).replace(/\d/g, 9) );
  681. } else {
  682. this.element.unmask();
  683. }
  684. } catch (e) {}
  685. }
  686. },
  687. _setDisabledState: function () {
  688. var isDisabled = this.options.Disabled, _elem = this.element;
  689. // Disable the associated input field.
  690. _elem[0].disabled = isDisabled;
  691. _elem.toggleClass(_disabledClass, isDisabled);
  692. if (isDisabled) {
  693. this._validationMessage.hide();
  694. }
  695. this.Close();
  696. this._updateButton();
  697. _event('OnAfterSetDisabled', this)(isDisabled);
  698. },
  699. _getPickerYear: function () {
  700. return this._pickerYear;
  701. },
  702. _setPickerYear: function (year) {
  703. this._pickerYear = year || new Date().getFullYear();
  704. this._titleButton.jqueryUIButton({ label: this._i18n('year') + ' ' + this._pickerYear });
  705. },
  706. // When calling this method with a falsy (undefined) date
  707. // value, this.element.val() is used as the date value.
  708. //
  709. // Therefore it's important to update the input field
  710. // before calling this method.
  711. _updateAlt: function (noop, date) {
  712. var _field = $(this.options.AltField);
  713. if (_field.length) {
  714. _field.val( this._formatMonth(date, this.options.AltFormat) );
  715. }
  716. },
  717. _chooseMonth: function (month) {
  718. var _year = this._getPickerYear();
  719. var date = new Date(_year, month-1);
  720. this.element.val(this._formatMonth( date )).blur();
  721. this.element.change(); /*Agregado para validar fecha*/
  722. this._updateAlt(0, date);
  723. _setActive( this._selectedBtn, false );
  724. this._selectedBtn = _setActive( $(this._buttons[month-1]), true );
  725. _event('OnAfterChooseMonth', this)(date);
  726. },
  727. _chooseYear: function (year) {
  728. this._backToYear = 0;
  729. this._setPickerYear(year);
  730. this._buttons.removeClass(_todayClass);
  731. this._showMonths();
  732. this._applyJumpYearsHint();
  733. _event('OnAfterChooseYear', this)();
  734. },
  735. _showMonths: function () {
  736. var _months = this._i18n('months');
  737. this._prevButton
  738. .attr('title', this._i18n('prevYear'))
  739. .off(click)
  740. .on(click, $proxy(this._addToYear, this, -1));
  741. this._nextButton
  742. .attr('title', this._i18n('nextYear'))
  743. .off(click)
  744. .on(click, $proxy(this._addToYear, this, 1));
  745. this._buttons.off(_eventsNs);
  746. var me = this, _onMonthClick = $proxy(me._onMonthClick, me);
  747. $.each(_months, function(index, monthName) {
  748. $(me._buttons[index])
  749. .on(click, {month: index+1}, _onMonthClick )
  750. .jqueryUIButton('option', 'label', monthName);
  751. });
  752. this._decorateButtons();
  753. },
  754. _showYearsClickHandler: function () {
  755. this._buttons.removeClass(_todayClass);
  756. if (!this._backToYear) {
  757. this._backToYear = this._getPickerYear();
  758. this._showYears();
  759. var _label = this._i18n('backTo') + ' ' + this._getPickerYear();
  760. this._titleButton.jqueryUIButton({ label: _label }).data( _clearHint )();
  761. _event('OnAfterChooseYears', this)();
  762. } else {
  763. this._setPickerYear(this._backToYear);
  764. this._applyJumpYearsHint();
  765. this._showMonths();
  766. this._backToYear = 0;
  767. }
  768. },
  769. _showYears: function () {
  770. var _currYear = this._getPickerYear(),
  771. _yearDifferential = -4,
  772. _firstYear = (_currYear + _yearDifferential),
  773. AMOUNT_TO_ADD = 12,
  774. _thisYear = new Date().getFullYear();
  775. var _minDate = this._MinMonth;
  776. var _maxDate = this._MaxMonth;
  777. var _minYear = _minDate ? _toYear(_minDate) : 0;
  778. var _maxYear = _maxDate ? _toYear(_maxDate) : 0;
  779. this._prevButton
  780. .attr('title', this._i18n('prev12Years'))
  781. .off(click)
  782. .on(click, $proxy(this._addToYears, this, -AMOUNT_TO_ADD));
  783. this._nextButton
  784. .attr('title', this._i18n('next12Years'))
  785. .off(click)
  786. .on(click, $proxy(this._addToYears, this, AMOUNT_TO_ADD));
  787. _setDisabled(this._prevButton, _minYear && (_firstYear - 1) < _minYear);
  788. _setDisabled(this._nextButton, _maxYear && (_firstYear + 12) -1 > _maxYear);
  789. this._buttons.off(_eventsNs);
  790. _setActive( this._selectedBtn, false );
  791. var _selYear = this.GetSelectedYear();
  792. var _onClick = $proxy(this._onYearClick, this);
  793. var _todayWithinBounds = _between(_thisYear, _minYear, _maxYear);
  794. var _selWithinBounds = _between(_selYear, _minYear, _maxYear);
  795. for (var _counter = 0; _counter < 12; _counter++) {
  796. var _year = _currYear + _yearDifferential;
  797. var _btn = $( this._buttons[_counter] ).jqueryUIButton({
  798. disabled: !_between(_year, _minYear, _maxYear),
  799. label: _year
  800. })
  801. .toggleClass(_todayClass, _year === _thisYear && _todayWithinBounds) // Heighlight the current year.
  802. .on(click, { year: _year }, _onClick );
  803. // Heighlight the selected year.
  804. if (_selWithinBounds && _selYear && _selYear === _year) {
  805. this._selectedBtn = _setActive( _btn , true );
  806. }
  807. _yearDifferential++;
  808. }
  809. },
  810. _onMonthClick: function(event) {
  811. this._chooseMonth(event.data.month);
  812. this.Close(event);
  813. },
  814. _onYearClick: function(event) {
  815. this._chooseYear(event.data.year);
  816. },
  817. _addToYear: function(amount) {
  818. this._setPickerYear( this._getPickerYear() + amount );
  819. this.element.focus();
  820. this._decorateButtons();
  821. _event('OnAfter' + (amount > 0 ? 'Next' : 'Previous') + 'Year', this)();
  822. },
  823. _addToYears: function(amount) {
  824. this._pickerYear = this._getPickerYear() + amount;
  825. this._showYears();
  826. this.element.focus();
  827. _event('OnAfter' + (amount > 0 ? 'Next' : 'Previous') + 'Years', this)();
  828. },
  829. _ajustYear: function(_opts) {
  830. var _year = _opts.StartYear || this.GetSelectedYear() || new Date().getFullYear();
  831. if (this._MinMonth !== null) {
  832. _year = Math.max(_toYear(this._MinMonth), _year);
  833. }
  834. if (this._MaxMonth !== null) {
  835. _year = Math.min(_toYear(this._MaxMonth), _year);
  836. }
  837. this._setPickerYear( _year );
  838. },
  839. _decorateButtons: function() {
  840. var _curYear = this._getPickerYear(), _todaysMonth = _toMonth(new Date),
  841. _minDate = this._MinMonth, _maxDate = this._MaxMonth;
  842. // Heighlight the selected month.
  843. _setActive( this._selectedBtn, false );
  844. var _sel = this.GetSelectedDate();
  845. var _withinBounds = _between(_sel ? _toMonth(_sel) : null, _minDate, _maxDate);
  846. if (_sel && _sel.getFullYear() === _curYear) {
  847. this._selectedBtn = _setActive( $(this._buttons[_sel.getMonth()]) , _withinBounds );
  848. }
  849. // Disable the next/prev button if we've reached the min/max year.
  850. _setDisabled(this._prevButton, _minDate && _curYear == _toYear(_minDate));
  851. _setDisabled(this._nextButton, _maxDate && _curYear == _toYear(_maxDate));
  852. for (var i = 0; i < 12; i++) {
  853. // Disable the button if the month is not between the
  854. // min and max interval.
  855. var _month = (_curYear * 12) + i, _isBetween = _between(_month, _minDate, _maxDate);
  856. $(this._buttons[i])
  857. .jqueryUIButton({ disabled: !_isBetween })
  858. .toggleClass(_todayClass, _isBetween && _month == _todaysMonth); // Highlights today's month.
  859. }
  860. }
  861. });
  862. }(jQuery, window, document, Date));