month-picker.js 38 KB

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