calendar.js 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638
  1. /*!
  2. * # Fomantic-UI - Calendar
  3. * http://github.com/fomantic/Fomantic-UI/
  4. *
  5. *
  6. * Released under the MIT license
  7. * http://opensource.org/licenses/MIT
  8. *
  9. */
  10. ;(function ($, window, document, undefined) {
  11. 'use strict';
  12. $.isFunction = $.isFunction || function(obj) {
  13. return typeof obj === "function" && typeof obj.nodeType !== "number";
  14. };
  15. window = (typeof window != 'undefined' && window.Math == Math)
  16. ? window
  17. : (typeof self != 'undefined' && self.Math == Math)
  18. ? self
  19. : Function('return this')()
  20. ;
  21. $.fn.calendar = function(parameters) {
  22. var
  23. $allModules = $(this),
  24. moduleSelector = $allModules.selector || '',
  25. time = new Date().getTime(),
  26. performance = [],
  27. query = arguments[0],
  28. methodInvoked = (typeof query == 'string'),
  29. queryArguments = [].slice.call(arguments, 1),
  30. returnedValue,
  31. timeGapTable = {
  32. '5': {'row': 4, 'column': 3 },
  33. '10': {'row': 3, 'column': 2 },
  34. '15': {'row': 2, 'column': 2 },
  35. '20': {'row': 3, 'column': 1 },
  36. '30': {'row': 2, 'column': 1 }
  37. }
  38. ;
  39. $allModules
  40. .each(function () {
  41. var
  42. settings = ( $.isPlainObject(parameters) )
  43. ? $.extend(true, {}, $.fn.calendar.settings, parameters)
  44. : $.extend({}, $.fn.calendar.settings),
  45. className = settings.className,
  46. namespace = settings.namespace,
  47. selector = settings.selector,
  48. formatter = settings.formatter,
  49. parser = settings.parser,
  50. metadata = settings.metadata,
  51. timeGap = timeGapTable[settings.minTimeGap],
  52. error = settings.error,
  53. eventNamespace = '.' + namespace,
  54. moduleNamespace = 'module-' + namespace,
  55. $module = $(this),
  56. $input = $module.find(selector.input),
  57. $container = $module.find(selector.popup),
  58. $activator = $module.find(selector.activator),
  59. element = this,
  60. instance = $module.data(moduleNamespace),
  61. isTouch,
  62. isTouchDown = false,
  63. focusDateUsedForRange = false,
  64. module
  65. ;
  66. module = {
  67. initialize: function () {
  68. module.debug('Initializing calendar for', element, $module);
  69. isTouch = module.get.isTouch();
  70. module.setup.config();
  71. module.setup.popup();
  72. module.setup.inline();
  73. module.setup.input();
  74. module.setup.date();
  75. module.create.calendar();
  76. module.bind.events();
  77. module.instantiate();
  78. },
  79. instantiate: function () {
  80. module.verbose('Storing instance of calendar');
  81. instance = module;
  82. $module.data(moduleNamespace, instance);
  83. },
  84. destroy: function () {
  85. module.verbose('Destroying previous calendar for', element);
  86. $module.removeData(moduleNamespace);
  87. module.unbind.events();
  88. },
  89. setup: {
  90. config: function () {
  91. if (module.get.minDate() !== null) {
  92. module.set.minDate($module.data(metadata.minDate));
  93. }
  94. if (module.get.maxDate() !== null) {
  95. module.set.maxDate($module.data(metadata.maxDate));
  96. }
  97. module.setting('type', module.get.type());
  98. },
  99. popup: function () {
  100. if (settings.inline) {
  101. return;
  102. }
  103. if (!$activator.length) {
  104. $activator = $module.children().first();
  105. if (!$activator.length) {
  106. return;
  107. }
  108. }
  109. if ($.fn.popup === undefined) {
  110. module.error(error.popup);
  111. return;
  112. }
  113. if (!$container.length) {
  114. //prepend the popup element to the activator's parent so that it has less chance of messing with
  115. //the styling (eg input action button needs to be the last child to have correct border radius)
  116. var $activatorParent = $activator.parent(),
  117. domPositionFunction = $activatorParent.closest(selector.append).length !== 0 ? 'appendTo' : 'prependTo';
  118. $container = $('<div/>').addClass(className.popup)[domPositionFunction]($activatorParent);
  119. }
  120. $container.addClass(className.calendar);
  121. var onVisible = settings.onVisible;
  122. var onHidden = settings.onHidden;
  123. if (!$input.length) {
  124. //no input, $container has to handle focus/blur
  125. $container.attr('tabindex', '0');
  126. onVisible = function () {
  127. module.focus();
  128. return settings.onVisible.apply($container, arguments);
  129. };
  130. onHidden = function () {
  131. module.blur();
  132. return settings.onHidden.apply($container, arguments);
  133. };
  134. }
  135. var onShow = function () {
  136. //reset the focus date onShow
  137. module.set.focusDate(module.get.date());
  138. module.set.mode(settings.startMode);
  139. return settings.onShow.apply($container, arguments);
  140. };
  141. var on = settings.on || ($input.length ? 'focus' : 'click');
  142. var options = $.extend({}, settings.popupOptions, {
  143. popup: $container,
  144. on: on,
  145. hoverable: on === 'hover',
  146. onShow: onShow,
  147. onVisible: onVisible,
  148. onHide: settings.onHide,
  149. onHidden: onHidden
  150. });
  151. module.popup(options);
  152. },
  153. inline: function () {
  154. if ($activator.length && !settings.inline) {
  155. return;
  156. }
  157. $container = $('<div/>').addClass(className.calendar).appendTo($module);
  158. if (!$input.length) {
  159. $container.attr('tabindex', '0');
  160. }
  161. },
  162. input: function () {
  163. if (settings.touchReadonly && $input.length && isTouch) {
  164. $input.prop('readonly', true);
  165. }
  166. },
  167. date: function () {
  168. var date;
  169. if (settings.initialDate) {
  170. date = parser.date(settings.initialDate, settings);
  171. } else if ($module.data(metadata.date) !== undefined) {
  172. date = parser.date($module.data(metadata.date), settings);
  173. } else if ($input.length) {
  174. date = parser.date($input.val(), settings);
  175. }
  176. module.set.date(date, settings.formatInput, false);
  177. }
  178. },
  179. create: {
  180. calendar: function () {
  181. var i, r, c, p, row, cell, pageGrid;
  182. var mode = module.get.mode();
  183. var today = new Date();
  184. var date = module.get.date();
  185. var focusDate = module.get.focusDate();
  186. var display = focusDate || date || settings.initialDate || today;
  187. display = module.helper.dateInRange(display);
  188. if (!focusDate) {
  189. focusDate = display;
  190. module.set.focusDate(focusDate, false, false);
  191. }
  192. var isYear = mode === 'year';
  193. var isMonth = mode === 'month';
  194. var isDay = mode === 'day';
  195. var isHour = mode === 'hour';
  196. var isMinute = mode === 'minute';
  197. var isTimeOnly = settings.type === 'time';
  198. var multiMonth = Math.max(settings.multiMonth, 1);
  199. var monthOffset = !isDay ? 0 : module.get.monthOffset();
  200. var minute = display.getMinutes();
  201. var hour = display.getHours();
  202. var day = display.getDate();
  203. var startMonth = display.getMonth() + monthOffset;
  204. var year = display.getFullYear();
  205. var columns = isDay ? settings.showWeekNumbers ? 8 : 7 : isHour ? 4 : timeGap['column'];
  206. var rows = isDay || isHour ? 6 : timeGap['row'];
  207. var pages = isDay ? multiMonth : 1;
  208. var container = $container;
  209. var tooltipPosition = container.hasClass("left") ? "right center" : "left center";
  210. container.empty();
  211. if (pages > 1) {
  212. pageGrid = $('<div/>').addClass(className.grid).appendTo(container);
  213. }
  214. for (p = 0; p < pages; p++) {
  215. if (pages > 1) {
  216. var pageColumn = $('<div/>').addClass(className.column).appendTo(pageGrid);
  217. container = pageColumn;
  218. }
  219. var month = startMonth + p;
  220. var firstMonthDayColumn = (new Date(year, month, 1).getDay() - settings.firstDayOfWeek % 7 + 7) % 7;
  221. if (!settings.constantHeight && isDay) {
  222. var requiredCells = new Date(year, month + 1, 0).getDate() + firstMonthDayColumn;
  223. rows = Math.ceil(requiredCells / 7);
  224. }
  225. var yearChange = isYear ? 10 : isMonth ? 1 : 0;
  226. var monthChange = isDay ? 1 : 0;
  227. var dayChange = isHour || isMinute ? 1 : 0;
  228. var prevNextDay = isHour || isMinute ? day : 1;
  229. var prevDate = new Date(year - yearChange, month - monthChange, prevNextDay - dayChange, hour);
  230. var nextDate = new Date(year + yearChange, month + monthChange, prevNextDay + dayChange, hour);
  231. var prevLast = isYear ? new Date(Math.ceil(year / 10) * 10 - 9, 0, 0) :
  232. isMonth ? new Date(year, 0, 0) : isDay ? new Date(year, month, 0) : new Date(year, month, day, -1);
  233. var nextFirst = isYear ? new Date(Math.ceil(year / 10) * 10 + 1, 0, 1) :
  234. isMonth ? new Date(year + 1, 0, 1) : isDay ? new Date(year, month + 1, 1) : new Date(year, month, day + 1);
  235. var tempMode = mode;
  236. if (isDay && settings.showWeekNumbers){
  237. tempMode += ' andweek';
  238. }
  239. var table = $('<table/>').addClass(className.table).addClass(tempMode).appendTo(container);
  240. var textColumns = columns;
  241. //no header for time-only mode
  242. if (!isTimeOnly) {
  243. var thead = $('<thead/>').appendTo(table);
  244. row = $('<tr/>').appendTo(thead);
  245. cell = $('<th/>').attr('colspan', '' + columns).appendTo(row);
  246. var headerDate = isYear || isMonth ? new Date(year, 0, 1) :
  247. isDay ? new Date(year, month, 1) : new Date(year, month, day, hour, minute);
  248. var headerText = $('<span/>').addClass(className.link).appendTo(cell);
  249. headerText.text(formatter.header(headerDate, mode, settings));
  250. var newMode = isMonth ? (settings.disableYear ? 'day' : 'year') :
  251. isDay ? (settings.disableMonth ? 'year' : 'month') : 'day';
  252. headerText.data(metadata.mode, newMode);
  253. if (p === 0) {
  254. var prev = $('<span/>').addClass(className.prev).appendTo(cell);
  255. prev.data(metadata.focusDate, prevDate);
  256. prev.toggleClass(className.disabledCell, !module.helper.isDateInRange(prevLast, mode));
  257. $('<i/>').addClass(className.prevIcon).appendTo(prev);
  258. }
  259. if (p === pages - 1) {
  260. var next = $('<span/>').addClass(className.next).appendTo(cell);
  261. next.data(metadata.focusDate, nextDate);
  262. next.toggleClass(className.disabledCell, !module.helper.isDateInRange(nextFirst, mode));
  263. $('<i/>').addClass(className.nextIcon).appendTo(next);
  264. }
  265. if (isDay) {
  266. row = $('<tr/>').appendTo(thead);
  267. if(settings.showWeekNumbers) {
  268. cell = $('<th/>').appendTo(row);
  269. cell.text(settings.text.weekNo);
  270. cell.addClass(className.weekCell);
  271. textColumns--;
  272. }
  273. for (i = 0; i < textColumns; i++) {
  274. cell = $('<th/>').appendTo(row);
  275. cell.text(formatter.dayColumnHeader((i + settings.firstDayOfWeek) % 7, settings));
  276. }
  277. }
  278. }
  279. var tbody = $('<tbody/>').appendTo(table);
  280. i = isYear ? Math.ceil(year / 10) * 10 - 9 : isDay ? 1 - firstMonthDayColumn : 0;
  281. for (r = 0; r < rows; r++) {
  282. row = $('<tr/>').appendTo(tbody);
  283. if(isDay && settings.showWeekNumbers){
  284. cell = $('<th/>').appendTo(row);
  285. cell.text(module.get.weekOfYear(year,month,i+1-settings.firstDayOfWeek));
  286. cell.addClass(className.weekCell);
  287. }
  288. for (c = 0; c < textColumns; c++, i++) {
  289. var cellDate = isYear ? new Date(i, month, 1, hour, minute) :
  290. isMonth ? new Date(year, i, 1, hour, minute) : isDay ? new Date(year, month, i, hour, minute) :
  291. isHour ? new Date(year, month, day, i) : new Date(year, month, day, hour, i * settings.minTimeGap);
  292. var cellText = isYear ? i :
  293. isMonth ? settings.text.monthsShort[i] : isDay ? cellDate.getDate() :
  294. formatter.time(cellDate, settings, true);
  295. cell = $('<td/>').addClass(className.cell).appendTo(row);
  296. cell.text(cellText);
  297. cell.data(metadata.date, cellDate);
  298. var adjacent = isDay && cellDate.getMonth() !== ((month + 12) % 12);
  299. var disabled = (!settings.selectAdjacentDays && adjacent) || !module.helper.isDateInRange(cellDate, mode) || settings.isDisabled(cellDate, mode) || module.helper.isDisabled(cellDate, mode) || !module.helper.isEnabled(cellDate, mode);
  300. if (disabled) {
  301. var disabledDate = module.helper.findDayAsObject(cellDate, mode, settings.disabledDates);
  302. if (disabledDate !== null && disabledDate[metadata.message]) {
  303. cell.attr("data-tooltip", disabledDate[metadata.message]);
  304. cell.attr("data-position", tooltipPosition);
  305. }
  306. } else {
  307. var eventDate = module.helper.findDayAsObject(cellDate, mode, settings.eventDates);
  308. if (eventDate !== null) {
  309. cell.addClass(eventDate[metadata.class] || settings.eventClass);
  310. if (eventDate[metadata.message]) {
  311. cell.attr("data-tooltip", eventDate[metadata.message]);
  312. cell.attr("data-position", tooltipPosition);
  313. }
  314. }
  315. }
  316. var active = module.helper.dateEqual(cellDate, date, mode);
  317. var isToday = module.helper.dateEqual(cellDate, today, mode);
  318. cell.toggleClass(className.adjacentCell, adjacent);
  319. cell.toggleClass(className.disabledCell, disabled);
  320. cell.toggleClass(className.activeCell, active && !adjacent);
  321. if (!isHour && !isMinute) {
  322. cell.toggleClass(className.todayCell, !adjacent && isToday);
  323. }
  324. // Allow for external modifications of each cell
  325. var cellOptions = {
  326. mode: mode,
  327. adjacent: adjacent,
  328. disabled: disabled,
  329. active: active,
  330. today: isToday
  331. };
  332. formatter.cell(cell, cellDate, cellOptions);
  333. if (module.helper.dateEqual(cellDate, focusDate, mode)) {
  334. //ensure that the focus date is exactly equal to the cell date
  335. //so that, if selected, the correct value is set
  336. module.set.focusDate(cellDate, false, false);
  337. }
  338. }
  339. }
  340. if (settings.today) {
  341. var todayRow = $('<tr/>').appendTo(tbody);
  342. var todayButton = $('<td/>').attr('colspan', '' + columns).addClass(className.today).appendTo(todayRow);
  343. todayButton.text(formatter.today(settings));
  344. todayButton.data(metadata.date, today);
  345. }
  346. module.update.focus(false, table);
  347. }
  348. }
  349. },
  350. update: {
  351. focus: function (updateRange, container) {
  352. container = container || $container;
  353. var mode = module.get.mode();
  354. var date = module.get.date();
  355. var focusDate = module.get.focusDate();
  356. var startDate = module.get.startDate();
  357. var endDate = module.get.endDate();
  358. var rangeDate = (updateRange ? focusDate : null) || date || (!isTouch ? focusDate : null);
  359. container.find('td').each(function () {
  360. var cell = $(this);
  361. var cellDate = cell.data(metadata.date);
  362. if (!cellDate) {
  363. return;
  364. }
  365. var disabled = cell.hasClass(className.disabledCell);
  366. var active = cell.hasClass(className.activeCell);
  367. var adjacent = cell.hasClass(className.adjacentCell);
  368. var focused = module.helper.dateEqual(cellDate, focusDate, mode);
  369. var inRange = !rangeDate ? false :
  370. ((!!startDate && module.helper.isDateInRange(cellDate, mode, startDate, rangeDate)) ||
  371. (!!endDate && module.helper.isDateInRange(cellDate, mode, rangeDate, endDate)));
  372. cell.toggleClass(className.focusCell, focused && (!isTouch || isTouchDown) && (!adjacent || (settings.selectAdjacentDays && adjacent)) && !disabled);
  373. if (module.helper.isTodayButton(cell)) {
  374. return;
  375. }
  376. cell.toggleClass(className.rangeCell, inRange && !active && !disabled);
  377. });
  378. }
  379. },
  380. refresh: function () {
  381. module.create.calendar();
  382. },
  383. bind: {
  384. events: function () {
  385. module.debug('Binding events');
  386. $container.on('mousedown' + eventNamespace, module.event.mousedown);
  387. $container.on('touchstart' + eventNamespace, module.event.mousedown);
  388. $container.on('mouseup' + eventNamespace, module.event.mouseup);
  389. $container.on('touchend' + eventNamespace, module.event.mouseup);
  390. $container.on('mouseover' + eventNamespace, module.event.mouseover);
  391. if ($input.length) {
  392. $input.on('input' + eventNamespace, module.event.inputChange);
  393. $input.on('focus' + eventNamespace, module.event.inputFocus);
  394. $input.on('blur' + eventNamespace, module.event.inputBlur);
  395. $input.on('click' + eventNamespace, module.event.inputClick);
  396. $input.on('keydown' + eventNamespace, module.event.keydown);
  397. } else {
  398. $container.on('keydown' + eventNamespace, module.event.keydown);
  399. }
  400. }
  401. },
  402. unbind: {
  403. events: function () {
  404. module.debug('Unbinding events');
  405. $container.off(eventNamespace);
  406. if ($input.length) {
  407. $input.off(eventNamespace);
  408. }
  409. }
  410. },
  411. event: {
  412. mouseover: function (event) {
  413. var target = $(event.target);
  414. var date = target.data(metadata.date);
  415. var mousedown = event.buttons === 1;
  416. if (date) {
  417. module.set.focusDate(date, false, true, mousedown);
  418. }
  419. },
  420. mousedown: function (event) {
  421. if ($input.length) {
  422. //prevent the mousedown on the calendar causing the input to lose focus
  423. event.preventDefault();
  424. }
  425. isTouchDown = event.type.indexOf('touch') >= 0;
  426. var target = $(event.target);
  427. var date = target.data(metadata.date);
  428. if (date) {
  429. module.set.focusDate(date, false, true, true);
  430. }
  431. },
  432. mouseup: function (event) {
  433. //ensure input has focus so that it receives keydown events for calendar navigation
  434. module.focus();
  435. event.preventDefault();
  436. event.stopPropagation();
  437. isTouchDown = false;
  438. var target = $(event.target);
  439. if (target.hasClass("disabled")) {
  440. return;
  441. }
  442. var parent = target.parent();
  443. if (parent.data(metadata.date) || parent.data(metadata.focusDate) || parent.data(metadata.mode)) {
  444. //clicked on a child element, switch to parent (used when clicking directly on prev/next <i> icon element)
  445. target = parent;
  446. }
  447. var date = target.data(metadata.date);
  448. var focusDate = target.data(metadata.focusDate);
  449. var mode = target.data(metadata.mode);
  450. if (date && settings.onSelect.call(element, date, module.get.mode()) !== false) {
  451. var forceSet = target.hasClass(className.today);
  452. module.selectDate(date, forceSet);
  453. }
  454. else if (focusDate) {
  455. module.set.focusDate(focusDate);
  456. }
  457. else if (mode) {
  458. module.set.mode(mode);
  459. }
  460. },
  461. keydown: function (event) {
  462. var keyCode = event.which;
  463. if (keyCode === 27 || keyCode === 9) {
  464. //esc || tab
  465. module.popup('hide');
  466. }
  467. if (module.popup('is visible')) {
  468. if (keyCode === 37 || keyCode === 38 || keyCode === 39 || keyCode === 40) {
  469. //arrow keys
  470. var mode = module.get.mode();
  471. var bigIncrement = mode === 'day' ? 7 : mode === 'hour' ? 4 : mode === 'minute' ? timeGap['column'] : 3;
  472. var increment = keyCode === 37 ? -1 : keyCode === 38 ? -bigIncrement : keyCode == 39 ? 1 : bigIncrement;
  473. increment *= mode === 'minute' ? settings.minTimeGap : 1;
  474. var focusDate = module.get.focusDate() || module.get.date() || new Date();
  475. var year = focusDate.getFullYear() + (mode === 'year' ? increment : 0);
  476. var month = focusDate.getMonth() + (mode === 'month' ? increment : 0);
  477. var day = focusDate.getDate() + (mode === 'day' ? increment : 0);
  478. var hour = focusDate.getHours() + (mode === 'hour' ? increment : 0);
  479. var minute = focusDate.getMinutes() + (mode === 'minute' ? increment : 0);
  480. var newFocusDate = new Date(year, month, day, hour, minute);
  481. if (settings.type === 'time') {
  482. newFocusDate = module.helper.mergeDateTime(focusDate, newFocusDate);
  483. }
  484. if (module.helper.isDateInRange(newFocusDate, mode)) {
  485. module.set.focusDate(newFocusDate);
  486. }
  487. } else if (keyCode === 13) {
  488. //enter
  489. var mode = module.get.mode();
  490. var date = module.get.focusDate();
  491. if (date && !settings.isDisabled(date, mode) && !module.helper.isDisabled(date, mode) && module.helper.isEnabled(date, mode)) {
  492. module.selectDate(date);
  493. }
  494. //disable form submission:
  495. event.preventDefault();
  496. event.stopPropagation();
  497. }
  498. }
  499. if (keyCode === 38 || keyCode === 40) {
  500. //arrow-up || arrow-down
  501. event.preventDefault(); //don't scroll
  502. module.popup('show');
  503. }
  504. },
  505. inputChange: function () {
  506. var val = $input.val();
  507. var date = parser.date(val, settings);
  508. module.set.date(date, false);
  509. },
  510. inputFocus: function () {
  511. $container.addClass(className.active);
  512. },
  513. inputBlur: function () {
  514. $container.removeClass(className.active);
  515. if (settings.formatInput) {
  516. var date = module.get.date();
  517. var text = formatter.datetime(date, settings);
  518. $input.val(text);
  519. }
  520. },
  521. inputClick: function () {
  522. module.popup('show');
  523. }
  524. },
  525. get: {
  526. weekOfYear: function(weekYear,weekMonth,weekDay) {
  527. // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
  528. var ms1d = 864e5, // milliseconds in a day
  529. ms7d = 7 * ms1d; // milliseconds in a week
  530. return function() { // return a closure so constants get calculated only once
  531. var DC3 = Date.UTC(weekYear, weekMonth, weekDay + 3) / ms1d, // an Absolute Day Number
  532. AWN = Math.floor(DC3 / 7), // an Absolute Week Number
  533. Wyr = new Date(AWN * ms7d).getUTCFullYear();
  534. return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
  535. }();
  536. },
  537. date: function () {
  538. return module.helper.sanitiseDate($module.data(metadata.date)) || null;
  539. },
  540. inputDate: function() {
  541. return $input.val();
  542. },
  543. focusDate: function () {
  544. return $module.data(metadata.focusDate) || null;
  545. },
  546. startDate: function () {
  547. var startModule = module.get.calendarModule(settings.startCalendar);
  548. return (startModule ? startModule.get.date() : $module.data(metadata.startDate)) || null;
  549. },
  550. endDate: function () {
  551. var endModule = module.get.calendarModule(settings.endCalendar);
  552. return (endModule ? endModule.get.date() : $module.data(metadata.endDate)) || null;
  553. },
  554. minDate: function() {
  555. return $module.data(metadata.minDate) || null;
  556. },
  557. maxDate: function() {
  558. return $module.data(metadata.maxDate) || null;
  559. },
  560. monthOffset: function () {
  561. return $module.data(metadata.monthOffset) || 0;
  562. },
  563. mode: function () {
  564. //only returns valid modes for the current settings
  565. var mode = $module.data(metadata.mode) || settings.startMode;
  566. var validModes = module.get.validModes();
  567. if ($.inArray(mode, validModes) >= 0) {
  568. return mode;
  569. }
  570. return settings.type === 'time' ? 'hour' :
  571. settings.type === 'month' ? 'month' :
  572. settings.type === 'year' ? 'year' : 'day';
  573. },
  574. type: function() {
  575. return $module.data(metadata.type) || settings.type;
  576. },
  577. validModes: function () {
  578. var validModes = [];
  579. if (settings.type !== 'time') {
  580. if (!settings.disableYear || settings.type === 'year') {
  581. validModes.push('year');
  582. }
  583. if (!(settings.disableMonth || settings.type === 'year') || settings.type === 'month') {
  584. validModes.push('month');
  585. }
  586. if (settings.type.indexOf('date') >= 0) {
  587. validModes.push('day');
  588. }
  589. }
  590. if (settings.type.indexOf('time') >= 0) {
  591. validModes.push('hour');
  592. if (!settings.disableMinute) {
  593. validModes.push('minute');
  594. }
  595. }
  596. return validModes;
  597. },
  598. isTouch: function () {
  599. try {
  600. document.createEvent('TouchEvent');
  601. return true;
  602. }
  603. catch (e) {
  604. return false;
  605. }
  606. },
  607. calendarModule: function (selector) {
  608. if (!selector) {
  609. return null;
  610. }
  611. if (!(selector instanceof $)) {
  612. selector = $(selector).first();
  613. }
  614. //assume range related calendars are using the same namespace
  615. return selector.data(moduleNamespace);
  616. }
  617. },
  618. set: {
  619. date: function (date, updateInput, fireChange) {
  620. updateInput = updateInput !== false;
  621. fireChange = fireChange !== false;
  622. date = module.helper.sanitiseDate(date);
  623. date = module.helper.dateInRange(date);
  624. var mode = module.get.mode();
  625. var text = formatter.datetime(date, settings);
  626. if (fireChange && settings.onBeforeChange.call(element, date, text, mode) === false) {
  627. return false;
  628. }
  629. module.set.focusDate(date);
  630. if (settings.isDisabled(date, mode)) {
  631. return false;
  632. }
  633. var endDate = module.get.endDate();
  634. if (!!endDate && !!date && date > endDate) {
  635. //selected date is greater than end date in range, so clear end date
  636. module.set.endDate(undefined);
  637. }
  638. module.set.dataKeyValue(metadata.date, date);
  639. if (updateInput && $input.length) {
  640. $input.val(text);
  641. }
  642. if (fireChange) {
  643. settings.onChange.call(element, date, text, mode);
  644. }
  645. },
  646. startDate: function (date, refreshCalendar) {
  647. date = module.helper.sanitiseDate(date);
  648. var startModule = module.get.calendarModule(settings.startCalendar);
  649. if (startModule) {
  650. startModule.set.date(date);
  651. }
  652. module.set.dataKeyValue(metadata.startDate, date, refreshCalendar);
  653. },
  654. endDate: function (date, refreshCalendar) {
  655. date = module.helper.sanitiseDate(date);
  656. var endModule = module.get.calendarModule(settings.endCalendar);
  657. if (endModule) {
  658. endModule.set.date(date);
  659. }
  660. module.set.dataKeyValue(metadata.endDate, date, refreshCalendar);
  661. },
  662. focusDate: function (date, refreshCalendar, updateFocus, updateRange) {
  663. date = module.helper.sanitiseDate(date);
  664. date = module.helper.dateInRange(date);
  665. var isDay = module.get.mode() === 'day';
  666. var oldFocusDate = module.get.focusDate();
  667. if (isDay && date && oldFocusDate) {
  668. var yearDelta = date.getFullYear() - oldFocusDate.getFullYear();
  669. var monthDelta = yearDelta * 12 + date.getMonth() - oldFocusDate.getMonth();
  670. if (monthDelta) {
  671. var monthOffset = module.get.monthOffset() - monthDelta;
  672. module.set.monthOffset(monthOffset, false);
  673. }
  674. }
  675. var changed = module.set.dataKeyValue(metadata.focusDate, date, refreshCalendar);
  676. updateFocus = (updateFocus !== false && changed && refreshCalendar === false) || focusDateUsedForRange != updateRange;
  677. focusDateUsedForRange = updateRange;
  678. if (updateFocus) {
  679. module.update.focus(updateRange);
  680. }
  681. },
  682. minDate: function (date) {
  683. date = module.helper.sanitiseDate(date);
  684. if (settings.maxDate !== null && settings.maxDate <= date) {
  685. module.verbose('Unable to set minDate variable bigger that maxDate variable', date, settings.maxDate);
  686. } else {
  687. module.setting('minDate', date);
  688. module.set.dataKeyValue(metadata.minDate, date);
  689. }
  690. },
  691. maxDate: function (date) {
  692. date = module.helper.sanitiseDate(date);
  693. if (settings.minDate !== null && settings.minDate >= date) {
  694. module.verbose('Unable to set maxDate variable lower that minDate variable', date, settings.minDate);
  695. } else {
  696. module.setting('maxDate', date);
  697. module.set.dataKeyValue(metadata.maxDate, date);
  698. }
  699. },
  700. monthOffset: function (monthOffset, refreshCalendar) {
  701. var multiMonth = Math.max(settings.multiMonth, 1);
  702. monthOffset = Math.max(1 - multiMonth, Math.min(0, monthOffset));
  703. module.set.dataKeyValue(metadata.monthOffset, monthOffset, refreshCalendar);
  704. },
  705. mode: function (mode, refreshCalendar) {
  706. module.set.dataKeyValue(metadata.mode, mode, refreshCalendar);
  707. },
  708. dataKeyValue: function (key, value, refreshCalendar) {
  709. var oldValue = $module.data(key);
  710. var equal = oldValue === value || (oldValue <= value && oldValue >= value); //equality test for dates and string objects
  711. if (value) {
  712. $module.data(key, value);
  713. } else {
  714. $module.removeData(key);
  715. }
  716. refreshCalendar = refreshCalendar !== false && !equal;
  717. if (refreshCalendar) {
  718. module.refresh();
  719. }
  720. return !equal;
  721. }
  722. },
  723. selectDate: function (date, forceSet) {
  724. module.verbose('New date selection', date);
  725. var mode = module.get.mode();
  726. var complete = forceSet || mode === 'minute' ||
  727. (settings.disableMinute && mode === 'hour') ||
  728. (settings.type === 'date' && mode === 'day') ||
  729. (settings.type === 'month' && mode === 'month') ||
  730. (settings.type === 'year' && mode === 'year');
  731. if (complete) {
  732. var canceled = module.set.date(date) === false;
  733. if (!canceled && settings.closable) {
  734. module.popup('hide');
  735. //if this is a range calendar, show the end date calendar popup and focus the input
  736. var endModule = module.get.calendarModule(settings.endCalendar);
  737. if (endModule) {
  738. endModule.popup('show');
  739. endModule.focus();
  740. }
  741. }
  742. } else {
  743. var newMode = mode === 'year' ? (!settings.disableMonth ? 'month' : 'day') :
  744. mode === 'month' ? 'day' : mode === 'day' ? 'hour' : 'minute';
  745. module.set.mode(newMode);
  746. if (mode === 'hour' || (mode === 'day' && module.get.date())) {
  747. //the user has chosen enough to consider a valid date/time has been chosen
  748. module.set.date(date);
  749. } else {
  750. module.set.focusDate(date);
  751. }
  752. }
  753. },
  754. changeDate: function (date) {
  755. module.set.date(date);
  756. },
  757. clear: function () {
  758. module.set.date(undefined);
  759. },
  760. popup: function () {
  761. return $activator.popup.apply($activator, arguments);
  762. },
  763. focus: function () {
  764. if ($input.length) {
  765. $input.focus();
  766. } else {
  767. $container.focus();
  768. }
  769. },
  770. blur: function () {
  771. if ($input.length) {
  772. $input.blur();
  773. } else {
  774. $container.blur();
  775. }
  776. },
  777. helper: {
  778. isDisabled: function(date, mode) {
  779. return (mode === 'day' || mode === 'month' || mode === 'year') && ((settings.disabledDaysOfWeek.indexOf(date.getDay()) !== -1) || settings.disabledDates.some(function(d){
  780. if(typeof d === 'string') {
  781. d = module.helper.sanitiseDate(d);
  782. }
  783. if (d instanceof Date) {
  784. return module.helper.dateEqual(date, d, mode);
  785. }
  786. if (d !== null && typeof d === 'object') {
  787. if (d[metadata.year]) {
  788. if (typeof d[metadata.year] === 'number') {
  789. return date.getFullYear() == d[metadata.year];
  790. } else if (Array.isArray(d[metadata.year])) {
  791. return d[metadata.year].indexOf(date.getFullYear()) > -1;
  792. }
  793. } else if (d[metadata.month]) {
  794. if (typeof d[metadata.month] === 'number') {
  795. return date.getMonth() == d[metadata.month];
  796. } else if (Array.isArray(d[metadata.month])) {
  797. return d[metadata.month].indexOf(date.getMonth()) > -1;
  798. } else if (d[metadata.month] instanceof Date) {
  799. var sdate = module.helper.sanitiseDate(d[metadata.month]);
  800. return (date.getMonth() == sdate.getMonth()) && (date.getFullYear() == sdate.getFullYear())
  801. }
  802. } else if (d[metadata.date] && mode === 'day') {
  803. if (d[metadata.date] instanceof Date) {
  804. return module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode);
  805. } else if (Array.isArray(d[metadata.date])) {
  806. return d[metadata.date].some(function(idate) {
  807. return module.helper.dateEqual(date, idate, mode);
  808. });
  809. }
  810. }
  811. }
  812. }));
  813. },
  814. isEnabled: function(date, mode) {
  815. if (mode === 'day') {
  816. return settings.enabledDates.length === 0 || settings.enabledDates.some(function(d){
  817. if(typeof d === 'string') {
  818. d = module.helper.sanitiseDate(d);
  819. }
  820. if (d instanceof Date) {
  821. return module.helper.dateEqual(date, d, mode);
  822. }
  823. if (d !== null && typeof d === 'object' && d[metadata.date]) {
  824. return module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode);
  825. }
  826. });
  827. } else {
  828. return true;
  829. }
  830. },
  831. findDayAsObject: function(date, mode, dates) {
  832. if (mode === 'day' || mode === 'month' || mode === 'year') {
  833. var d;
  834. for (var i = 0; i < dates.length; i++) {
  835. d = dates[i];
  836. if(typeof d === 'string') {
  837. d = module.helper.sanitiseDate(d);
  838. }
  839. if (d instanceof Date && module.helper.dateEqual(date, d, mode)) {
  840. var dateObject = {};
  841. dateObject[metadata.date] = d;
  842. return dateObject;
  843. }
  844. else if (d !== null && typeof d === 'object') {
  845. if (d[metadata.year]) {
  846. if (typeof d[metadata.year] === 'number' && date.getFullYear() == d[metadata.year]) {
  847. return d;
  848. } else if (Array.isArray(d[metadata.year])) {
  849. if (d[metadata.year].indexOf(date.getFullYear()) > -1) {
  850. return d;
  851. }
  852. }
  853. } else if (d[metadata.month]) {
  854. if (typeof d[metadata.month] === 'number' && date.getMonth() == d[metadata.month]) {
  855. return d;
  856. } else if (Array.isArray(d[metadata.month])) {
  857. if (d[metadata.month].indexOf(date.getMonth()) > -1) {
  858. return d;
  859. }
  860. } else if (d[metadata.month] instanceof Date) {
  861. var sdate = module.helper.sanitiseDate(d[metadata.month]);
  862. if ((date.getMonth() == sdate.getMonth()) && (date.getFullYear() == sdate.getFullYear())) {
  863. return d;
  864. }
  865. }
  866. } else if (d[metadata.date] && mode === 'day') {
  867. if (d[metadata.date] instanceof Date && module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode)) {
  868. return d;
  869. } else if (Array.isArray(d[metadata.date])) {
  870. if(d[metadata.date].some(function(idate) { return module.helper.dateEqual(date, idate, mode); })) {
  871. return d;
  872. }
  873. }
  874. }
  875. }
  876. }
  877. }
  878. return null;
  879. },
  880. sanitiseDate: function (date) {
  881. if (!date) {
  882. return undefined;
  883. }
  884. if (!(date instanceof Date)) {
  885. date = parser.date('' + date, settings);
  886. }
  887. if (!date || date === null || isNaN(date.getTime())) {
  888. return undefined;
  889. }
  890. return date;
  891. },
  892. dateDiff: function (date1, date2, mode) {
  893. mode = mode || 'day';
  894. var isTimeOnly = settings.type === 'time';
  895. var isYear = mode === 'year';
  896. var isYearOrMonth = isYear || mode === 'month';
  897. var isMinute = mode === 'minute';
  898. var isHourOrMinute = isMinute || mode === 'hour';
  899. //only care about a minute accuracy of settings.minTimeGap
  900. date1 = new Date(
  901. isTimeOnly ? 2000 : date1.getFullYear(),
  902. isTimeOnly ? 0 : isYear ? 0 : date1.getMonth(),
  903. isTimeOnly ? 1 : isYearOrMonth ? 1 : date1.getDate(),
  904. !isHourOrMinute ? 0 : date1.getHours(),
  905. !isMinute ? 0 : settings.minTimeGap * Math.floor(date1.getMinutes() / settings.minTimeGap));
  906. date2 = new Date(
  907. isTimeOnly ? 2000 : date2.getFullYear(),
  908. isTimeOnly ? 0 : isYear ? 0 : date2.getMonth(),
  909. isTimeOnly ? 1 : isYearOrMonth ? 1 : date2.getDate(),
  910. !isHourOrMinute ? 0 : date2.getHours(),
  911. !isMinute ? 0 : settings.minTimeGap * Math.floor(date2.getMinutes() / settings.minTimeGap));
  912. return date2.getTime() - date1.getTime();
  913. },
  914. dateEqual: function (date1, date2, mode) {
  915. return !!date1 && !!date2 && module.helper.dateDiff(date1, date2, mode) === 0;
  916. },
  917. isDateInRange: function (date, mode, minDate, maxDate) {
  918. if (!minDate && !maxDate) {
  919. var startDate = module.get.startDate();
  920. minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate;
  921. maxDate = settings.maxDate;
  922. }
  923. minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap));
  924. return !(!date ||
  925. (minDate && module.helper.dateDiff(date, minDate, mode) > 0) ||
  926. (maxDate && module.helper.dateDiff(maxDate, date, mode) > 0));
  927. },
  928. dateInRange: function (date, minDate, maxDate) {
  929. if (!minDate && !maxDate) {
  930. var startDate = module.get.startDate();
  931. minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate;
  932. maxDate = settings.maxDate;
  933. }
  934. minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap));
  935. var isTimeOnly = settings.type === 'time';
  936. return !date ? date :
  937. (minDate && module.helper.dateDiff(date, minDate, 'minute') > 0) ?
  938. (isTimeOnly ? module.helper.mergeDateTime(date, minDate) : minDate) :
  939. (maxDate && module.helper.dateDiff(maxDate, date, 'minute') > 0) ?
  940. (isTimeOnly ? module.helper.mergeDateTime(date, maxDate) : maxDate) :
  941. date;
  942. },
  943. mergeDateTime: function (date, time) {
  944. return (!date || !time) ? time :
  945. new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes());
  946. },
  947. isTodayButton: function(element) {
  948. return element.text() === settings.text.today;
  949. }
  950. },
  951. setting: function (name, value) {
  952. module.debug('Changing setting', name, value);
  953. if ($.isPlainObject(name)) {
  954. $.extend(true, settings, name);
  955. }
  956. else if (value !== undefined) {
  957. if ($.isPlainObject(settings[name])) {
  958. $.extend(true, settings[name], value);
  959. }
  960. else {
  961. settings[name] = value;
  962. }
  963. }
  964. else {
  965. return settings[name];
  966. }
  967. },
  968. internal: function (name, value) {
  969. if( $.isPlainObject(name) ) {
  970. $.extend(true, module, name);
  971. }
  972. else if(value !== undefined) {
  973. module[name] = value;
  974. }
  975. else {
  976. return module[name];
  977. }
  978. },
  979. debug: function () {
  980. if (!settings.silent && settings.debug) {
  981. if (settings.performance) {
  982. module.performance.log(arguments);
  983. }
  984. else {
  985. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  986. module.debug.apply(console, arguments);
  987. }
  988. }
  989. },
  990. verbose: function () {
  991. if (!settings.silent && settings.verbose && settings.debug) {
  992. if (settings.performance) {
  993. module.performance.log(arguments);
  994. }
  995. else {
  996. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  997. module.verbose.apply(console, arguments);
  998. }
  999. }
  1000. },
  1001. error: function () {
  1002. if (!settings.silent) {
  1003. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  1004. module.error.apply(console, arguments);
  1005. }
  1006. },
  1007. performance: {
  1008. log: function (message) {
  1009. var
  1010. currentTime,
  1011. executionTime,
  1012. previousTime
  1013. ;
  1014. if (settings.performance) {
  1015. currentTime = new Date().getTime();
  1016. previousTime = time || currentTime;
  1017. executionTime = currentTime - previousTime;
  1018. time = currentTime;
  1019. performance.push({
  1020. 'Name': message[0],
  1021. 'Arguments': [].slice.call(message, 1) || '',
  1022. 'Element': element,
  1023. 'Execution Time': executionTime
  1024. });
  1025. }
  1026. clearTimeout(module.performance.timer);
  1027. module.performance.timer = setTimeout(module.performance.display, 500);
  1028. },
  1029. display: function () {
  1030. var
  1031. title = settings.name + ':',
  1032. totalTime = 0
  1033. ;
  1034. time = false;
  1035. clearTimeout(module.performance.timer);
  1036. $.each(performance, function (index, data) {
  1037. totalTime += data['Execution Time'];
  1038. });
  1039. title += ' ' + totalTime + 'ms';
  1040. if (moduleSelector) {
  1041. title += ' \'' + moduleSelector + '\'';
  1042. }
  1043. if ((console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  1044. console.groupCollapsed(title);
  1045. if (console.table) {
  1046. console.table(performance);
  1047. }
  1048. else {
  1049. $.each(performance, function (index, data) {
  1050. console.log(data['Name'] + ': ' + data['Execution Time'] + 'ms');
  1051. });
  1052. }
  1053. console.groupEnd();
  1054. }
  1055. performance = [];
  1056. }
  1057. },
  1058. invoke: function (query, passedArguments, context) {
  1059. var
  1060. object = instance,
  1061. maxDepth,
  1062. found,
  1063. response
  1064. ;
  1065. passedArguments = passedArguments || queryArguments;
  1066. context = element || context;
  1067. if (typeof query == 'string' && object !== undefined) {
  1068. query = query.split(/[\. ]/);
  1069. maxDepth = query.length - 1;
  1070. $.each(query, function (depth, value) {
  1071. var camelCaseValue = (depth != maxDepth)
  1072. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  1073. : query
  1074. ;
  1075. if ($.isPlainObject(object[camelCaseValue]) && (depth != maxDepth)) {
  1076. object = object[camelCaseValue];
  1077. }
  1078. else if (object[camelCaseValue] !== undefined) {
  1079. found = object[camelCaseValue];
  1080. return false;
  1081. }
  1082. else if ($.isPlainObject(object[value]) && (depth != maxDepth)) {
  1083. object = object[value];
  1084. }
  1085. else if (object[value] !== undefined) {
  1086. found = object[value];
  1087. return false;
  1088. }
  1089. else {
  1090. module.error(error.method, query);
  1091. return false;
  1092. }
  1093. });
  1094. }
  1095. if ($.isFunction(found)) {
  1096. response = found.apply(context, passedArguments);
  1097. }
  1098. else if (found !== undefined) {
  1099. response = found;
  1100. }
  1101. if (Array.isArray(returnedValue)) {
  1102. returnedValue.push(response);
  1103. }
  1104. else if (returnedValue !== undefined) {
  1105. returnedValue = [returnedValue, response];
  1106. }
  1107. else if (response !== undefined) {
  1108. returnedValue = response;
  1109. }
  1110. return found;
  1111. }
  1112. };
  1113. if (methodInvoked) {
  1114. if (instance === undefined) {
  1115. module.initialize();
  1116. }
  1117. module.invoke(query);
  1118. }
  1119. else {
  1120. if (instance !== undefined) {
  1121. instance.invoke('destroy');
  1122. }
  1123. module.initialize();
  1124. }
  1125. })
  1126. ;
  1127. return (returnedValue !== undefined)
  1128. ? returnedValue
  1129. : this
  1130. ;
  1131. };
  1132. $.fn.calendar.settings = {
  1133. name : 'Calendar',
  1134. namespace : 'calendar',
  1135. silent: false,
  1136. debug: false,
  1137. verbose: false,
  1138. performance: false,
  1139. type : 'datetime', // picker type, can be 'datetime', 'date', 'time', 'month', or 'year'
  1140. firstDayOfWeek : 0, // day for first day column (0 = Sunday)
  1141. constantHeight : true, // add rows to shorter months to keep day calendar height consistent (6 rows)
  1142. today : false, // show a 'today/now' button at the bottom of the calendar
  1143. closable : true, // close the popup after selecting a date/time
  1144. monthFirst : true, // month before day when parsing/converting date from/to text
  1145. touchReadonly : true, // set input to readonly on touch devices
  1146. inline : false, // create the calendar inline instead of inside a popup
  1147. on : null, // when to show the popup (defaults to 'focus' for input, 'click' for others)
  1148. initialDate : null, // date to display initially when no date is selected (null = now)
  1149. startMode : false, // display mode to start in, can be 'year', 'month', 'day', 'hour', 'minute' (false = 'day')
  1150. minDate : null, // minimum date/time that can be selected, dates/times before are disabled
  1151. maxDate : null, // maximum date/time that can be selected, dates/times after are disabled
  1152. ampm : true, // show am/pm in time mode
  1153. disableYear : false, // disable year selection mode
  1154. disableMonth : false, // disable month selection mode
  1155. disableMinute : false, // disable minute selection mode
  1156. formatInput : true, // format the input text upon input blur and module creation
  1157. startCalendar : null, // jquery object or selector for another calendar that represents the start date of a date range
  1158. endCalendar : null, // jquery object or selector for another calendar that represents the end date of a date range
  1159. multiMonth : 1, // show multiple months when in 'day' mode
  1160. minTimeGap : 5,
  1161. showWeekNumbers : null, // show Number of Week at the very first column of a dayView
  1162. disabledDates : [], // specific day(s) which won't be selectable and contain additional information.
  1163. disabledDaysOfWeek : [], // day(s) which won't be selectable(s) (0 = Sunday)
  1164. enabledDates : [], // specific day(s) which will be selectable, all other days will be disabled
  1165. eventDates : [], // specific day(s) which will be shown in a different color and using tooltips
  1166. centuryBreak : 60, // starting short year until 99 where it will be assumed to belong to the last century
  1167. currentCentury : 2000, // century to be added to 2-digit years (00 to {centuryBreak}-1)
  1168. selectAdjacentDays : false, // The calendar can show dates from adjacent month. These adjacent month dates can also be made selectable.
  1169. // popup options ('popup', 'on', 'hoverable', and show/hide callbacks are overridden)
  1170. popupOptions: {
  1171. position: 'bottom left',
  1172. lastResort: 'bottom left',
  1173. prefer: 'opposite',
  1174. hideOnScroll: false
  1175. },
  1176. text: {
  1177. days: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
  1178. months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
  1179. monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
  1180. today: 'Today',
  1181. now: 'Now',
  1182. am: 'AM',
  1183. pm: 'PM',
  1184. weekNo: 'Week'
  1185. },
  1186. formatter: {
  1187. header: function (date, mode, settings) {
  1188. return mode === 'year' ? settings.formatter.yearHeader(date, settings) :
  1189. mode === 'month' ? settings.formatter.monthHeader(date, settings) :
  1190. mode === 'day' ? settings.formatter.dayHeader(date, settings) :
  1191. mode === 'hour' ? settings.formatter.hourHeader(date, settings) :
  1192. settings.formatter.minuteHeader(date, settings);
  1193. },
  1194. yearHeader: function (date, settings) {
  1195. var decadeYear = Math.ceil(date.getFullYear() / 10) * 10;
  1196. return (decadeYear - 9) + ' - ' + (decadeYear + 2);
  1197. },
  1198. monthHeader: function (date, settings) {
  1199. return date.getFullYear();
  1200. },
  1201. dayHeader: function (date, settings) {
  1202. var month = settings.text.months[date.getMonth()];
  1203. var year = date.getFullYear();
  1204. return month + ' ' + year;
  1205. },
  1206. hourHeader: function (date, settings) {
  1207. return settings.formatter.date(date, settings);
  1208. },
  1209. minuteHeader: function (date, settings) {
  1210. return settings.formatter.date(date, settings);
  1211. },
  1212. dayColumnHeader: function (day, settings) {
  1213. return settings.text.days[day];
  1214. },
  1215. datetime: function (date, settings) {
  1216. if (!date) {
  1217. return '';
  1218. }
  1219. var day = settings.type === 'time' ? '' : settings.formatter.date(date, settings);
  1220. var time = settings.type.indexOf('time') < 0 ? '' : settings.formatter.time(date, settings, false);
  1221. var separator = settings.type === 'datetime' ? ' ' : '';
  1222. return day + separator + time;
  1223. },
  1224. date: function (date, settings) {
  1225. if (!date) {
  1226. return '';
  1227. }
  1228. var day = date.getDate();
  1229. var month = settings.text.months[date.getMonth()];
  1230. var year = date.getFullYear();
  1231. return settings.type === 'year' ? year :
  1232. settings.type === 'month' ? month + ' ' + year :
  1233. (settings.monthFirst ? month + ' ' + day : day + ' ' + month) + ', ' + year;
  1234. },
  1235. time: function (date, settings, forCalendar) {
  1236. if (!date) {
  1237. return '';
  1238. }
  1239. var hour = date.getHours();
  1240. var minute = date.getMinutes();
  1241. var ampm = '';
  1242. if (settings.ampm) {
  1243. ampm = ' ' + (hour < 12 ? settings.text.am : settings.text.pm);
  1244. hour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
  1245. }
  1246. return hour + ':' + (minute < 10 ? '0' : '') + minute + ampm;
  1247. },
  1248. today: function (settings) {
  1249. return settings.type === 'date' ? settings.text.today : settings.text.now;
  1250. },
  1251. cell: function (cell, date, cellOptions) {
  1252. }
  1253. },
  1254. parser: {
  1255. date: function (text, settings) {
  1256. if (text instanceof Date) {
  1257. return text;
  1258. }
  1259. if (!text) {
  1260. return null;
  1261. }
  1262. text = ('' + text).trim().toLowerCase();
  1263. if (text.length === 0) {
  1264. return null;
  1265. }
  1266. // Reverse date and month in some cases
  1267. text = settings.monthFirst ? text : text.replace(/[\/\-\.]/g,'/').replace(/([0-9]+)\/([0-9]+)/,'$2/$1');
  1268. var textDate = new Date(text);
  1269. if(!isNaN(textDate.getDate())) {
  1270. return textDate;
  1271. }
  1272. var i, j, k;
  1273. var minute = -1, hour = -1, day = -1, month = -1, year = -1;
  1274. var isAm = undefined;
  1275. var isTimeOnly = settings.type === 'time';
  1276. var isDateOnly = settings.type.indexOf('time') < 0;
  1277. var words = text.split(settings.regExp.dateWords), word;
  1278. var numbers = text.split(settings.regExp.dateNumbers), number;
  1279. var parts;
  1280. var monthString;
  1281. if (!isDateOnly) {
  1282. //am/pm
  1283. isAm = $.inArray(settings.text.am.toLowerCase(), words) >= 0 ? true :
  1284. $.inArray(settings.text.pm.toLowerCase(), words) >= 0 ? false : undefined;
  1285. //time with ':'
  1286. for (i = 0; i < numbers.length; i++) {
  1287. number = numbers[i];
  1288. if (number.indexOf(':') >= 0) {
  1289. if (hour < 0 || minute < 0) {
  1290. parts = number.split(':');
  1291. for (k = 0; k < Math.min(2, parts.length); k++) {
  1292. j = parseInt(parts[k]);
  1293. if (isNaN(j)) {
  1294. j = 0;
  1295. }
  1296. if (k === 0) {
  1297. hour = j % 24;
  1298. } else {
  1299. minute = j % 60;
  1300. }
  1301. }
  1302. }
  1303. numbers.splice(i, 1);
  1304. }
  1305. }
  1306. }
  1307. if (!isTimeOnly) {
  1308. //textual month
  1309. for (i = 0; i < words.length; i++) {
  1310. word = words[i];
  1311. if (word.length <= 0) {
  1312. continue;
  1313. }
  1314. for (j = 0; j < settings.text.months.length; j++) {
  1315. monthString = settings.text.months[j];
  1316. monthString = monthString.substring(0, word.length).toLowerCase();
  1317. if (monthString === word) {
  1318. month = j + 1;
  1319. break;
  1320. }
  1321. }
  1322. if (month >= 0) {
  1323. break;
  1324. }
  1325. }
  1326. //year > settings.centuryBreak
  1327. for (i = 0; i < numbers.length; i++) {
  1328. j = parseInt(numbers[i]);
  1329. if (isNaN(j)) {
  1330. continue;
  1331. }
  1332. if (j >= settings.centuryBreak && i === numbers.length-1) {
  1333. if (j <= 99) {
  1334. j += settings.currentCentury - 100;
  1335. }
  1336. year = j;
  1337. numbers.splice(i, 1);
  1338. break;
  1339. }
  1340. }
  1341. //numeric month
  1342. if (month < 0) {
  1343. for (i = 0; i < numbers.length; i++) {
  1344. k = i > 1 || settings.monthFirst ? i : i === 1 ? 0 : 1;
  1345. j = parseInt(numbers[k]);
  1346. if (isNaN(j)) {
  1347. continue;
  1348. }
  1349. if (1 <= j && j <= 12) {
  1350. month = j;
  1351. numbers.splice(k, 1);
  1352. break;
  1353. }
  1354. }
  1355. }
  1356. //day
  1357. for (i = 0; i < numbers.length; i++) {
  1358. j = parseInt(numbers[i]);
  1359. if (isNaN(j)) {
  1360. continue;
  1361. }
  1362. if (1 <= j && j <= 31) {
  1363. day = j;
  1364. numbers.splice(i, 1);
  1365. break;
  1366. }
  1367. }
  1368. //year <= settings.centuryBreak
  1369. if (year < 0) {
  1370. for (i = numbers.length - 1; i >= 0; i--) {
  1371. j = parseInt(numbers[i]);
  1372. if (isNaN(j)) {
  1373. continue;
  1374. }
  1375. if (j <= 99) {
  1376. j += settings.currentCentury;
  1377. }
  1378. year = j;
  1379. numbers.splice(i, 1);
  1380. break;
  1381. }
  1382. }
  1383. }
  1384. if (!isDateOnly) {
  1385. //hour
  1386. if (hour < 0) {
  1387. for (i = 0; i < numbers.length; i++) {
  1388. j = parseInt(numbers[i]);
  1389. if (isNaN(j)) {
  1390. continue;
  1391. }
  1392. if (0 <= j && j <= 23) {
  1393. hour = j;
  1394. numbers.splice(i, 1);
  1395. break;
  1396. }
  1397. }
  1398. }
  1399. //minute
  1400. if (minute < 0) {
  1401. for (i = 0; i < numbers.length; i++) {
  1402. j = parseInt(numbers[i]);
  1403. if (isNaN(j)) {
  1404. continue;
  1405. }
  1406. if (0 <= j && j <= 59) {
  1407. minute = j;
  1408. numbers.splice(i, 1);
  1409. break;
  1410. }
  1411. }
  1412. }
  1413. }
  1414. if (minute < 0 && hour < 0 && day < 0 && month < 0 && year < 0) {
  1415. return null;
  1416. }
  1417. if (minute < 0) {
  1418. minute = 0;
  1419. }
  1420. if (hour < 0) {
  1421. hour = 0;
  1422. }
  1423. if (day < 0) {
  1424. day = 1;
  1425. }
  1426. if (month < 0) {
  1427. month = 1;
  1428. }
  1429. if (year < 0) {
  1430. year = new Date().getFullYear();
  1431. }
  1432. if (isAm !== undefined) {
  1433. if (isAm) {
  1434. if (hour === 12) {
  1435. hour = 0;
  1436. }
  1437. } else if (hour < 12) {
  1438. hour += 12;
  1439. }
  1440. }
  1441. var date = new Date(year, month - 1, day, hour, minute);
  1442. if (date.getMonth() !== month - 1 || date.getFullYear() !== year) {
  1443. //month or year don't match up, switch to last day of the month
  1444. date = new Date(year, month, 0, hour, minute);
  1445. }
  1446. return isNaN(date.getTime()) ? null : date;
  1447. }
  1448. },
  1449. // callback before date is changed, return false to cancel the change
  1450. onBeforeChange: function (date, text, mode) {
  1451. return true;
  1452. },
  1453. // callback when date changes
  1454. onChange: function (date, text, mode) {
  1455. },
  1456. // callback before show animation, return false to prevent show
  1457. onShow: function () {
  1458. },
  1459. // callback after show animation
  1460. onVisible: function () {
  1461. },
  1462. // callback before hide animation, return false to prevent hide
  1463. onHide: function () {
  1464. },
  1465. // callback after hide animation
  1466. onHidden: function () {
  1467. },
  1468. // callback before item is selected, return false to prevent selection
  1469. onSelect: function (date, mode) {
  1470. },
  1471. // is the given date disabled?
  1472. isDisabled: function (date, mode) {
  1473. return false;
  1474. },
  1475. selector: {
  1476. popup: '.ui.popup',
  1477. input: 'input',
  1478. activator: 'input',
  1479. append: '.inline.field,.inline.fields'
  1480. },
  1481. regExp: {
  1482. dateWords: /[^A-Za-z\u00C0-\u024F]+/g,
  1483. dateNumbers: /[^\d:]+/g
  1484. },
  1485. error: {
  1486. popup: 'UI Popup, a required component is not included in this page',
  1487. method: 'The method you called is not defined.'
  1488. },
  1489. className: {
  1490. calendar: 'calendar',
  1491. active: 'active',
  1492. popup: 'ui popup',
  1493. grid: 'ui equal width grid',
  1494. column: 'column',
  1495. table: 'ui celled center aligned unstackable table',
  1496. prev: 'prev link',
  1497. next: 'next link',
  1498. prevIcon: 'chevron left icon',
  1499. nextIcon: 'chevron right icon',
  1500. link: 'link',
  1501. cell: 'link',
  1502. disabledCell: 'disabled',
  1503. weekCell: 'disabled',
  1504. adjacentCell: 'adjacent',
  1505. activeCell: 'active',
  1506. rangeCell: 'range',
  1507. focusCell: 'focus',
  1508. todayCell: 'today',
  1509. today: 'today link'
  1510. },
  1511. metadata: {
  1512. date: 'date',
  1513. focusDate: 'focusDate',
  1514. startDate: 'startDate',
  1515. endDate: 'endDate',
  1516. minDate: 'minDate',
  1517. maxDate: 'maxDate',
  1518. mode: 'mode',
  1519. type: 'type',
  1520. monthOffset: 'monthOffset',
  1521. message: 'message',
  1522. class: 'class',
  1523. month: 'month',
  1524. year: 'year'
  1525. },
  1526. eventClass: 'blue'
  1527. };
  1528. })(jQuery, window, document);