mobiscroll.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. /*jslint eqeq: true, plusplus: true, undef: true, sloppy: true, vars: true, forin: true */
  2. (function ($) {
  3. var ms = $.mobiscroll,
  4. date = new Date(),
  5. defaults = {
  6. dateFormat: 'mm/dd/yy',
  7. dateOrder: 'mmddy',
  8. timeWheels: 'hhiiA',
  9. timeFormat: 'hh:ii A',
  10. startYear: date.getFullYear() - 100,
  11. endYear: date.getFullYear() + 1,
  12. monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
  13. monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
  14. dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
  15. dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
  16. shortYearCutoff: '+10',
  17. monthText: 'Month',
  18. dayText: 'Day',
  19. yearText: 'Year',
  20. hourText: 'Hours',
  21. minuteText: 'Minutes',
  22. secText: 'Seconds',
  23. ampmText: ' ',
  24. nowText: 'Now',
  25. showNow: false,
  26. stepHour: 1,
  27. stepMinute: 1,
  28. stepSecond: 1,
  29. separator: ' '
  30. },
  31. preset = function (inst) {
  32. var that = $(this),
  33. html5def = {},
  34. format;
  35. // Force format for html5 date inputs (experimental)
  36. if (that.is('input')) {
  37. switch (that.attr('type')) {
  38. case 'date':
  39. format = 'yy-mm-dd';
  40. break;
  41. case 'datetime':
  42. format = 'yy-mm-ddTHH:ii:ssZ';
  43. break;
  44. case 'datetime-local':
  45. format = 'yy-mm-ddTHH:ii:ss';
  46. break;
  47. case 'month':
  48. format = 'yy-mm';
  49. html5def.dateOrder = 'mmyy';
  50. break;
  51. case 'time':
  52. format = 'HH:ii:ss';
  53. break;
  54. }
  55. // Check for min/max attributes
  56. var min = that.attr('min'),
  57. max = that.attr('max');
  58. if (min) {
  59. html5def.minDate = ms.parseDate(format, min);
  60. }
  61. if (max) {
  62. html5def.maxDate = ms.parseDate(format, max);
  63. }
  64. }
  65. // Set year-month-day order
  66. var s = $.extend({}, defaults, html5def, inst.settings),
  67. offset = 0,
  68. wheels = [],
  69. ord = [],
  70. o = {},
  71. i,
  72. k,
  73. f = { y: 'getFullYear', m: 'getMonth', d: 'getDate', h: getHour, i: getMinute, s: getSecond, a: getAmPm },
  74. p = s.preset,
  75. dord = s.dateOrder,
  76. tord = s.timeWheels,
  77. regen = dord.match(/D/),
  78. ampm = tord.match(/a/i),
  79. hampm = tord.match(/h/),
  80. hformat = p == 'datetime' ? s.dateFormat + s.separator + s.timeFormat : p == 'time' ? s.timeFormat : s.dateFormat,
  81. defd = new Date(),
  82. stepH = s.stepHour,
  83. stepM = s.stepMinute,
  84. stepS = s.stepSecond,
  85. mind = s.minDate || new Date(s.startYear, 0, 1),
  86. maxd = s.maxDate || new Date(s.endYear, 11, 31, 23, 59, 59);
  87. inst.settings = s;
  88. format = format || hformat;
  89. if (p.match(/date/i)) {
  90. // Determine the order of year, month, day wheels
  91. $.each(['y', 'm', 'd'], function (j, v) {
  92. i = dord.search(new RegExp(v, 'i'));
  93. if (i > -1) {
  94. ord.push({ o: i, v: v });
  95. }
  96. });
  97. ord.sort(function (a, b) { return a.o > b.o ? 1 : -1; });
  98. $.each(ord, function (i, v) {
  99. o[v.v] = i;
  100. });
  101. var w = {};
  102. for (k = 0; k < 3; k++) {
  103. if (k == o.y) {
  104. offset++;
  105. w[s.yearText] = {};
  106. var start = mind.getFullYear(),
  107. end = maxd.getFullYear();
  108. for (i = start; i <= end; i++) {
  109. w[s.yearText][i] = dord.match(/yy/i) ? i : (i + '').substr(2, 2);
  110. }
  111. } else if (k == o.m) {
  112. offset++;
  113. w[s.monthText] = {};
  114. for (i = 0; i < 12; i++) {
  115. var str = dord.replace(/[dy]/gi, '').replace(/mm/, i < 9 ? '0' + (i + 1) : i + 1).replace(/m/, (i + 1));
  116. w[s.monthText][i] = str.match(/MM/) ? str.replace(/MM/, '<span class="dw-mon">' + s.monthNames[i] + '</span>') : str.replace(/M/, '<span class="dw-mon">' + s.monthNamesShort[i] + '</span>');
  117. }
  118. } else if (k == o.d) {
  119. offset++;
  120. w[s.dayText] = {};
  121. for (i = 1; i < 32; i++) {
  122. w[s.dayText][i] = dord.match(/dd/i) && i < 10 ? '0' + i : i;
  123. }
  124. }
  125. }
  126. wheels.push(w);
  127. }
  128. if (p.match(/time/i)) {
  129. // Determine the order of hours, minutes, seconds wheels
  130. ord = [];
  131. $.each(['h', 'i', 's', 'a'], function (i, v) {
  132. i = tord.search(new RegExp(v, 'i'));
  133. if (i > -1) {
  134. ord.push({ o: i, v: v });
  135. }
  136. });
  137. ord.sort(function (a, b) {
  138. return a.o > b.o ? 1 : -1;
  139. });
  140. $.each(ord, function (i, v) {
  141. o[v.v] = offset + i;
  142. });
  143. w = {};
  144. for (k = offset; k < offset + 4; k++) {
  145. if (k == o.h) {
  146. offset++;
  147. w[s.hourText] = {};
  148. for (i = 0; i < (hampm ? 12 : 24); i += stepH) {
  149. w[s.hourText][i] = hampm && i == 0 ? 12 : tord.match(/hh/i) && i < 10 ? '0' + i : i;
  150. }
  151. } else if (k == o.i) {
  152. offset++;
  153. w[s.minuteText] = {};
  154. for (i = 0; i < 60; i += stepM) {
  155. w[s.minuteText][i] = tord.match(/ii/) && i < 10 ? '0' + i : i;
  156. }
  157. } else if (k == o.s) {
  158. offset++;
  159. w[s.secText] = {};
  160. for (i = 0; i < 60; i += stepS) {
  161. w[s.secText][i] = tord.match(/ss/) && i < 10 ? '0' + i : i;
  162. }
  163. } else if (k == o.a) {
  164. offset++;
  165. var upper = tord.match(/A/);
  166. w[s.ampmText] = { 0: upper ? 'AM' : 'am', 1: upper ? 'PM' : 'pm' };
  167. }
  168. }
  169. wheels.push(w);
  170. }
  171. function get(d, i, def) {
  172. if (o[i] !== undefined) {
  173. return +d[o[i]];
  174. }
  175. if (def !== undefined) {
  176. return def;
  177. }
  178. return defd[f[i]] ? defd[f[i]]() : f[i](defd);
  179. }
  180. function step(v, st) {
  181. return Math.floor(v / st) * st;
  182. }
  183. function getHour(d) {
  184. var hour = d.getHours();
  185. hour = hampm && hour >= 12 ? hour - 12 : hour;
  186. return step(hour, stepH);
  187. }
  188. function getMinute(d) {
  189. return step(d.getMinutes(), stepM);
  190. }
  191. function getSecond(d) {
  192. return step(d.getSeconds(), stepS);
  193. }
  194. function getAmPm(d) {
  195. return ampm && d.getHours() > 11 ? 1 : 0;
  196. }
  197. function getDate(d) {
  198. var hour = get(d, 'h', 0);
  199. return new Date(get(d, 'y'), get(d, 'm'), get(d, 'd', 1), get(d, 'a') ? hour + 12 : hour, get(d, 'i', 0), get(d, 's', 0));
  200. }
  201. inst.setDate = function (d, fill, time, temp) {
  202. var i;
  203. // Set wheels
  204. for (i in o) {
  205. this.temp[o[i]] = d[f[i]] ? d[f[i]]() : f[i](d);
  206. }
  207. this.setValue(true, fill, time, temp);
  208. };
  209. inst.getDate = function (d) {
  210. return getDate(d);
  211. };
  212. return {
  213. button3Text: s.showNow ? s.nowText : undefined,
  214. button3: s.showNow ? function () { inst.setDate(new Date(), false, 0.3, true); } : undefined,
  215. wheels: wheels,
  216. headerText: function (v) {
  217. return ms.formatDate(hformat, getDate(inst.temp), s);
  218. },
  219. /**
  220. * Builds a date object from the wheel selections and formats it to the given date/time format
  221. * @param {Array} d - An array containing the selected wheel values
  222. * @return {String} - The formatted date string
  223. */
  224. formatResult: function (d) {
  225. return ms.formatDate(format, getDate(d), s);
  226. },
  227. /**
  228. * Builds a date object from the input value and returns an array to set wheel values
  229. * @return {Array} - An array containing the wheel values to set
  230. */
  231. parseValue: function (val) {
  232. var d = new Date(),
  233. i,
  234. result = [];
  235. try {
  236. d = ms.parseDate(format, val, s);
  237. } catch (e) {
  238. }
  239. // Set wheels
  240. for (i in o) {
  241. result[o[i]] = d[f[i]] ? d[f[i]]() : f[i](d);
  242. }
  243. return result;
  244. },
  245. /**
  246. * Validates the selected date to be in the minDate / maxDate range and sets unselectable values to disabled
  247. * @param {Object} dw - jQuery object containing the generated html
  248. * @param {Integer} [i] - Index of the changed wheel, not set for initial validation
  249. */
  250. validate: function (dw, i) {
  251. var temp = inst.temp, //.slice(0),
  252. mins = { y: mind.getFullYear(), m: 0, d: 1, h: 0, i: 0, s: 0, a: 0 },
  253. maxs = { y: maxd.getFullYear(), m: 11, d: 31, h: step(hampm ? 11 : 23, stepH), i: step(59, stepM), s: step(59, stepS), a: 1 },
  254. minprop = true,
  255. maxprop = true;
  256. $.each(['y', 'm', 'd', 'a', 'h', 'i', 's'], function (x, i) {
  257. if (o[i] !== undefined) {
  258. var min = mins[i],
  259. max = maxs[i],
  260. maxdays = 31,
  261. val = get(temp, i),
  262. t = $('.dw-ul', dw).eq(o[i]),
  263. y,
  264. m;
  265. if (i == 'd') {
  266. y = get(temp, 'y');
  267. m = get(temp, 'm');
  268. maxdays = 32 - new Date(y, m, 32).getDate();
  269. max = maxdays;
  270. if (regen) {
  271. $('.dw-li', t).each(function () {
  272. var that = $(this),
  273. d = that.data('val'),
  274. w = new Date(y, m, d).getDay(),
  275. str = dord.replace(/[my]/gi, '').replace(/dd/, d < 10 ? '0' + d : d).replace(/d/, d);
  276. $('.dw-i', that).html(str.match(/DD/) ? str.replace(/DD/, '<span class="dw-day">' + s.dayNames[w] + '</span>') : str.replace(/D/, '<span class="dw-day">' + s.dayNamesShort[w] + '</span>'));
  277. });
  278. }
  279. }
  280. if (minprop && mind) {
  281. min = mind[f[i]] ? mind[f[i]]() : f[i](mind);
  282. }
  283. if (maxprop && maxd) {
  284. max = maxd[f[i]] ? maxd[f[i]]() : f[i](maxd);
  285. }
  286. if (i != 'y') {
  287. var i1 = $('.dw-li', t).index($('.dw-li[data-val="' + min + '"]', t)),
  288. i2 = $('.dw-li', t).index($('.dw-li[data-val="' + max + '"]', t));
  289. $('.dw-li', t).removeClass('dw-v').slice(i1, i2 + 1).addClass('dw-v');
  290. if (i == 'd') { // Hide days not in month
  291. $('.dw-li', t).removeClass('dw-h').slice(maxdays).addClass('dw-h');
  292. }
  293. }
  294. if (val < min) {
  295. val = min;
  296. }
  297. if (val > max) {
  298. val = max;
  299. }
  300. if (minprop) {
  301. minprop = val == min;
  302. }
  303. if (maxprop) {
  304. maxprop = val == max;
  305. }
  306. // Disable some days
  307. if (s.invalid && i == 'd') {
  308. var idx = [];
  309. // Disable exact dates
  310. if (s.invalid.dates) {
  311. $.each(s.invalid.dates, function (i, v) {
  312. if (v.getFullYear() == y && v.getMonth() == m) {
  313. idx.push(v.getDate() - 1);
  314. }
  315. });
  316. }
  317. // Disable days of week
  318. if (s.invalid.daysOfWeek) {
  319. var first = new Date(y, m, 1).getDay(),
  320. j;
  321. $.each(s.invalid.daysOfWeek, function (i, v) {
  322. for (j = v - first; j < maxdays; j += 7) {
  323. if (j >= 0) {
  324. idx.push(j);
  325. }
  326. }
  327. });
  328. }
  329. // Disable days of month
  330. if (s.invalid.daysOfMonth) {
  331. $.each(s.invalid.daysOfMonth, function (i, v) {
  332. v = (v + '').split('/');
  333. if (v[1]) {
  334. if (v[0] - 1 == m) {
  335. idx.push(v[1] - 1);
  336. }
  337. } else {
  338. idx.push(v[0] - 1);
  339. }
  340. });
  341. }
  342. $.each(idx, function (i, v) {
  343. $('.dw-li', t).eq(v).removeClass('dw-v');
  344. });
  345. }
  346. // Set modified value
  347. temp[o[i]] = val;
  348. }
  349. });
  350. },
  351. methods: {
  352. /**
  353. * Returns the currently selected date.
  354. * @param {Boolean} temp - If true, return the currently shown date on the picker, otherwise the last selected one
  355. * @return {Date}
  356. */
  357. getDate: function (temp) {
  358. var inst = $(this).mobiscroll('getInst');
  359. if (inst) {
  360. return inst.getDate(temp ? inst.temp : inst.values);
  361. }
  362. },
  363. /**
  364. * Sets the selected date
  365. * @param {Date} d - Date to select.
  366. * @param {Boolean} [fill] - Also set the value of the associated input element. Default is true.
  367. * @return {Object} - jQuery object to maintain chainability
  368. */
  369. setDate: function (d, fill, time, temp) {
  370. if (fill == undefined) {
  371. fill = false;
  372. }
  373. return this.each(function () {
  374. var inst = $(this).mobiscroll('getInst');
  375. if (inst) {
  376. inst.setDate(d, fill, time, temp);
  377. }
  378. });
  379. }
  380. }
  381. };
  382. };
  383. $.each(['date', 'time', 'datetime'], function (i, v) {
  384. ms.presets[v] = preset;
  385. ms.presetShort(v);
  386. });
  387. /**
  388. * Format a date into a string value with a specified format.
  389. * @param {String} format - Output format.
  390. * @param {Date} date - Date to format.
  391. * @param {Object} settings - Settings.
  392. * @return {String} - Returns the formatted date string.
  393. */
  394. ms.formatDate = function (format, date, settings) {
  395. if (!date) {
  396. return null;
  397. }
  398. var s = $.extend({}, defaults, settings),
  399. look = function (m) { // Check whether a format character is doubled
  400. var n = 0;
  401. while (i + 1 < format.length && format.charAt(i + 1) == m) {
  402. n++;
  403. i++;
  404. }
  405. return n;
  406. },
  407. f1 = function (m, val, len) { // Format a number, with leading zero if necessary
  408. var n = '' + val;
  409. if (look(m)) {
  410. while (n.length < len) {
  411. n = '0' + n;
  412. }
  413. }
  414. return n;
  415. },
  416. f2 = function (m, val, s, l) { // Format a name, short or long as requested
  417. return (look(m) ? l[val] : s[val]);
  418. },
  419. i,
  420. output = '',
  421. literal = false;
  422. for (i = 0; i < format.length; i++) {
  423. if (literal) {
  424. if (format.charAt(i) == "'" && !look("'")) {
  425. literal = false;
  426. } else {
  427. output += format.charAt(i);
  428. }
  429. } else {
  430. switch (format.charAt(i)) {
  431. case 'd':
  432. output += f1('d', date.getDate(), 2);
  433. break;
  434. case 'D':
  435. output += f2('D', date.getDay(), s.dayNamesShort, s.dayNames);
  436. break;
  437. case 'o':
  438. output += f1('o', (date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000, 3);
  439. break;
  440. case 'm':
  441. output += f1('m', date.getMonth() + 1, 2);
  442. break;
  443. case 'M':
  444. output += f2('M', date.getMonth(), s.monthNamesShort, s.monthNames);
  445. break;
  446. case 'y':
  447. output += (look('y') ? date.getFullYear() : (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
  448. break;
  449. case 'h':
  450. var h = date.getHours();
  451. output += f1('h', (h > 12 ? (h - 12) : (h == 0 ? 12 : h)), 2);
  452. break;
  453. case 'H':
  454. output += f1('H', date.getHours(), 2);
  455. break;
  456. case 'i':
  457. output += f1('i', date.getMinutes(), 2);
  458. break;
  459. case 's':
  460. output += f1('s', date.getSeconds(), 2);
  461. break;
  462. case 'a':
  463. output += date.getHours() > 11 ? 'pm' : 'am';
  464. break;
  465. case 'A':
  466. output += date.getHours() > 11 ? 'PM' : 'AM';
  467. break;
  468. case "'":
  469. if (look("'")) {
  470. output += "'";
  471. } else {
  472. literal = true;
  473. }
  474. break;
  475. default:
  476. output += format.charAt(i);
  477. }
  478. }
  479. }
  480. return output;
  481. };
  482. /**
  483. * Extract a date from a string value with a specified format.
  484. * @param {String} format - Input format.
  485. * @param {String} value - String to parse.
  486. * @param {Object} settings - Settings.
  487. * @return {Date} - Returns the extracted date.
  488. */
  489. ms.parseDate = function (format, value, settings) {
  490. var def = new Date();
  491. if (!format || !value) {
  492. return def;
  493. }
  494. value = (typeof value == 'object' ? value.toString() : value + '');
  495. var s = $.extend({}, defaults, settings),
  496. shortYearCutoff = s.shortYearCutoff,
  497. year = def.getFullYear(),
  498. month = def.getMonth() + 1,
  499. day = def.getDate(),
  500. doy = -1,
  501. hours = def.getHours(),
  502. minutes = def.getMinutes(),
  503. seconds = 0, //def.getSeconds(),
  504. ampm = -1,
  505. literal = false, // Check whether a format character is doubled
  506. lookAhead = function (match) {
  507. var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
  508. if (matches) {
  509. iFormat++;
  510. }
  511. return matches;
  512. },
  513. getNumber = function (match) { // Extract a number from the string value
  514. lookAhead(match);
  515. var size = (match == '@' ? 14 : (match == '!' ? 20 : (match == 'y' ? 4 : (match == 'o' ? 3 : 2)))),
  516. digits = new RegExp('^\\d{1,' + size + '}'),
  517. num = value.substr(iValue).match(digits);
  518. if (!num) {
  519. return 0;
  520. }
  521. //throw 'Missing number at position ' + iValue;
  522. iValue += num[0].length;
  523. return parseInt(num[0], 10);
  524. },
  525. getName = function (match, s, l) { // Extract a name from the string value and convert to an index
  526. var names = (lookAhead(match) ? l : s),
  527. i;
  528. for (i = 0; i < names.length; i++) {
  529. if (value.substr(iValue, names[i].length).toLowerCase() == names[i].toLowerCase()) {
  530. iValue += names[i].length;
  531. return i + 1;
  532. }
  533. }
  534. return 0;
  535. //throw 'Unknown name at position ' + iValue;
  536. },
  537. checkLiteral = function () {
  538. //if (value.charAt(iValue) != format.charAt(iFormat))
  539. //throw 'Unexpected literal at position ' + iValue;
  540. iValue++;
  541. },
  542. iValue = 0,
  543. iFormat;
  544. for (iFormat = 0; iFormat < format.length; iFormat++) {
  545. if (literal) {
  546. if (format.charAt(iFormat) == "'" && !lookAhead("'")) {
  547. literal = false;
  548. } else {
  549. checkLiteral();
  550. }
  551. } else {
  552. switch (format.charAt(iFormat)) {
  553. case 'd':
  554. day = getNumber('d');
  555. break;
  556. case 'D':
  557. getName('D', s.dayNamesShort, s.dayNames);
  558. break;
  559. case 'o':
  560. doy = getNumber('o');
  561. break;
  562. case 'm':
  563. month = getNumber('m');
  564. break;
  565. case 'M':
  566. month = getName('M', s.monthNamesShort, s.monthNames);
  567. break;
  568. case 'y':
  569. year = getNumber('y');
  570. break;
  571. case 'H':
  572. hours = getNumber('H');
  573. break;
  574. case 'h':
  575. hours = getNumber('h');
  576. break;
  577. case 'i':
  578. minutes = getNumber('i');
  579. break;
  580. case 's':
  581. seconds = getNumber('s');
  582. break;
  583. case 'a':
  584. ampm = getName('a', ['am', 'pm'], ['am', 'pm']) - 1;
  585. break;
  586. case 'A':
  587. ampm = getName('A', ['am', 'pm'], ['am', 'pm']) - 1;
  588. break;
  589. case "'":
  590. if (lookAhead("'")) {
  591. checkLiteral();
  592. } else {
  593. literal = true;
  594. }
  595. break;
  596. default:
  597. checkLiteral();
  598. }
  599. }
  600. }
  601. if (year < 100) {
  602. year += new Date().getFullYear() - new Date().getFullYear() % 100 +
  603. (year <= (typeof shortYearCutoff != 'string' ? shortYearCutoff : new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)) ? 0 : -100);
  604. }
  605. if (doy > -1) {
  606. month = 1;
  607. day = doy;
  608. do {
  609. var dim = 32 - new Date(year, month - 1, 32).getDate();
  610. if (day <= dim) {
  611. break;
  612. }
  613. month++;
  614. day -= dim;
  615. } while (true);
  616. }
  617. hours = (ampm == -1) ? hours : ((ampm && hours < 12) ? (hours + 12) : (!ampm && hours == 12 ? 0 : hours));
  618. var date = new Date(year, month - 1, day, hours, minutes, seconds);
  619. if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day) {
  620. throw 'Invalid date';
  621. }
  622. return date;
  623. };
  624. })(jQuery);