datepicker.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. /**
  2. *
  3. * Date picker
  4. * Author: Stefan Petre www.eyecon.ro
  5. *
  6. * Dual licensed under the MIT and GPL licenses
  7. *
  8. */
  9. (function ($) {
  10. var DatePicker = function () {
  11. var ids = {},
  12. views = {
  13. years: 'datepickerViewYears',
  14. moths: 'datepickerViewMonths',
  15. days: 'datepickerViewDays'
  16. },
  17. tpl = {
  18. wrapper: '<div class="datepicker"><div class="datepickerBorderT" /><div class="datepickerBorderB" /><div class="datepickerBorderL" /><div class="datepickerBorderR" /><div class="datepickerBorderTL" /><div class="datepickerBorderTR" /><div class="datepickerBorderBL" /><div class="datepickerBorderBR" /><div class="datepickerContainer"><table cellspacing="0" cellpadding="0"><tbody><tr></tr></tbody></table></div></div>',
  19. head: [
  20. '<td>',
  21. '<table cellspacing="0" cellpadding="0">',
  22. '<thead>',
  23. '<tr>',
  24. '<th class="datepickerGoPrev"><a href="#"><span><%=prev%></span></a></th>',
  25. '<th colspan="6" class="datepickerMonth"><a href="#"><span></span></a></th>',
  26. '<th class="datepickerGoNext"><a href="#"><span><%=next%></span></a></th>',
  27. '</tr>',
  28. '<tr class="datepickerDoW">',
  29. '<th><span><%=week%></span></th>',
  30. '<th><span><%=day1%></span></th>',
  31. '<th><span><%=day2%></span></th>',
  32. '<th><span><%=day3%></span></th>',
  33. '<th><span><%=day4%></span></th>',
  34. '<th><span><%=day5%></span></th>',
  35. '<th><span><%=day6%></span></th>',
  36. '<th><span><%=day7%></span></th>',
  37. '</tr>',
  38. '</thead>',
  39. '</table></td>'
  40. ],
  41. space : '<td class="datepickerSpace"><div></div></td>',
  42. days: [
  43. '<tbody class="datepickerDays">',
  44. '<tr>',
  45. '<th class="datepickerWeek"><a href="#"><span><%=weeks[0].week%></span></a></th>',
  46. '<td class="<%=weeks[0].days[0].classname%>"><a href="#"><span><%=weeks[0].days[0].text%></span></a></td>',
  47. '<td class="<%=weeks[0].days[1].classname%>"><a href="#"><span><%=weeks[0].days[1].text%></span></a></td>',
  48. '<td class="<%=weeks[0].days[2].classname%>"><a href="#"><span><%=weeks[0].days[2].text%></span></a></td>',
  49. '<td class="<%=weeks[0].days[3].classname%>"><a href="#"><span><%=weeks[0].days[3].text%></span></a></td>',
  50. '<td class="<%=weeks[0].days[4].classname%>"><a href="#"><span><%=weeks[0].days[4].text%></span></a></td>',
  51. '<td class="<%=weeks[0].days[5].classname%>"><a href="#"><span><%=weeks[0].days[5].text%></span></a></td>',
  52. '<td class="<%=weeks[0].days[6].classname%>"><a href="#"><span><%=weeks[0].days[6].text%></span></a></td>',
  53. '</tr>',
  54. '<tr>',
  55. '<th class="datepickerWeek"><a href="#"><span><%=weeks[1].week%></span></a></th>',
  56. '<td class="<%=weeks[1].days[0].classname%>"><a href="#"><span><%=weeks[1].days[0].text%></span></a></td>',
  57. '<td class="<%=weeks[1].days[1].classname%>"><a href="#"><span><%=weeks[1].days[1].text%></span></a></td>',
  58. '<td class="<%=weeks[1].days[2].classname%>"><a href="#"><span><%=weeks[1].days[2].text%></span></a></td>',
  59. '<td class="<%=weeks[1].days[3].classname%>"><a href="#"><span><%=weeks[1].days[3].text%></span></a></td>',
  60. '<td class="<%=weeks[1].days[4].classname%>"><a href="#"><span><%=weeks[1].days[4].text%></span></a></td>',
  61. '<td class="<%=weeks[1].days[5].classname%>"><a href="#"><span><%=weeks[1].days[5].text%></span></a></td>',
  62. '<td class="<%=weeks[1].days[6].classname%>"><a href="#"><span><%=weeks[1].days[6].text%></span></a></td>',
  63. '</tr>',
  64. '<tr>',
  65. '<th class="datepickerWeek"><a href="#"><span><%=weeks[2].week%></span></a></th>',
  66. '<td class="<%=weeks[2].days[0].classname%>"><a href="#"><span><%=weeks[2].days[0].text%></span></a></td>',
  67. '<td class="<%=weeks[2].days[1].classname%>"><a href="#"><span><%=weeks[2].days[1].text%></span></a></td>',
  68. '<td class="<%=weeks[2].days[2].classname%>"><a href="#"><span><%=weeks[2].days[2].text%></span></a></td>',
  69. '<td class="<%=weeks[2].days[3].classname%>"><a href="#"><span><%=weeks[2].days[3].text%></span></a></td>',
  70. '<td class="<%=weeks[2].days[4].classname%>"><a href="#"><span><%=weeks[2].days[4].text%></span></a></td>',
  71. '<td class="<%=weeks[2].days[5].classname%>"><a href="#"><span><%=weeks[2].days[5].text%></span></a></td>',
  72. '<td class="<%=weeks[2].days[6].classname%>"><a href="#"><span><%=weeks[2].days[6].text%></span></a></td>',
  73. '</tr>',
  74. '<tr>',
  75. '<th class="datepickerWeek"><a href="#"><span><%=weeks[3].week%></span></a></th>',
  76. '<td class="<%=weeks[3].days[0].classname%>"><a href="#"><span><%=weeks[3].days[0].text%></span></a></td>',
  77. '<td class="<%=weeks[3].days[1].classname%>"><a href="#"><span><%=weeks[3].days[1].text%></span></a></td>',
  78. '<td class="<%=weeks[3].days[2].classname%>"><a href="#"><span><%=weeks[3].days[2].text%></span></a></td>',
  79. '<td class="<%=weeks[3].days[3].classname%>"><a href="#"><span><%=weeks[3].days[3].text%></span></a></td>',
  80. '<td class="<%=weeks[3].days[4].classname%>"><a href="#"><span><%=weeks[3].days[4].text%></span></a></td>',
  81. '<td class="<%=weeks[3].days[5].classname%>"><a href="#"><span><%=weeks[3].days[5].text%></span></a></td>',
  82. '<td class="<%=weeks[3].days[6].classname%>"><a href="#"><span><%=weeks[3].days[6].text%></span></a></td>',
  83. '</tr>',
  84. '<tr>',
  85. '<th class="datepickerWeek"><a href="#"><span><%=weeks[4].week%></span></a></th>',
  86. '<td class="<%=weeks[4].days[0].classname%>"><a href="#"><span><%=weeks[4].days[0].text%></span></a></td>',
  87. '<td class="<%=weeks[4].days[1].classname%>"><a href="#"><span><%=weeks[4].days[1].text%></span></a></td>',
  88. '<td class="<%=weeks[4].days[2].classname%>"><a href="#"><span><%=weeks[4].days[2].text%></span></a></td>',
  89. '<td class="<%=weeks[4].days[3].classname%>"><a href="#"><span><%=weeks[4].days[3].text%></span></a></td>',
  90. '<td class="<%=weeks[4].days[4].classname%>"><a href="#"><span><%=weeks[4].days[4].text%></span></a></td>',
  91. '<td class="<%=weeks[4].days[5].classname%>"><a href="#"><span><%=weeks[4].days[5].text%></span></a></td>',
  92. '<td class="<%=weeks[4].days[6].classname%>"><a href="#"><span><%=weeks[4].days[6].text%></span></a></td>',
  93. '</tr>',
  94. '<tr>',
  95. '<th class="datepickerWeek"><a href="#"><span><%=weeks[5].week%></span></a></th>',
  96. '<td class="<%=weeks[5].days[0].classname%>"><a href="#"><span><%=weeks[5].days[0].text%></span></a></td>',
  97. '<td class="<%=weeks[5].days[1].classname%>"><a href="#"><span><%=weeks[5].days[1].text%></span></a></td>',
  98. '<td class="<%=weeks[5].days[2].classname%>"><a href="#"><span><%=weeks[5].days[2].text%></span></a></td>',
  99. '<td class="<%=weeks[5].days[3].classname%>"><a href="#"><span><%=weeks[5].days[3].text%></span></a></td>',
  100. '<td class="<%=weeks[5].days[4].classname%>"><a href="#"><span><%=weeks[5].days[4].text%></span></a></td>',
  101. '<td class="<%=weeks[5].days[5].classname%>"><a href="#"><span><%=weeks[5].days[5].text%></span></a></td>',
  102. '<td class="<%=weeks[5].days[6].classname%>"><a href="#"><span><%=weeks[5].days[6].text%></span></a></td>',
  103. '</tr>',
  104. '</tbody>'
  105. ],
  106. months: [
  107. '<tbody class="<%=className%>">',
  108. '<tr>',
  109. '<td colspan="2"><a href="#"><span><%=data[0]%></span></a></td>',
  110. '<td colspan="2"><a href="#"><span><%=data[1]%></span></a></td>',
  111. '<td colspan="2"><a href="#"><span><%=data[2]%></span></a></td>',
  112. '<td colspan="2"><a href="#"><span><%=data[3]%></span></a></td>',
  113. '</tr>',
  114. '<tr>',
  115. '<td colspan="2"><a href="#"><span><%=data[4]%></span></a></td>',
  116. '<td colspan="2"><a href="#"><span><%=data[5]%></span></a></td>',
  117. '<td colspan="2"><a href="#"><span><%=data[6]%></span></a></td>',
  118. '<td colspan="2"><a href="#"><span><%=data[7]%></span></a></td>',
  119. '</tr>',
  120. '<tr>',
  121. '<td colspan="2"><a href="#"><span><%=data[8]%></span></a></td>',
  122. '<td colspan="2"><a href="#"><span><%=data[9]%></span></a></td>',
  123. '<td colspan="2"><a href="#"><span><%=data[10]%></span></a></td>',
  124. '<td colspan="2"><a href="#"><span><%=data[11]%></span></a></td>',
  125. '</tr>',
  126. '</tbody>'
  127. ]
  128. },
  129. defaults = {
  130. flat: false,
  131. starts: 1,
  132. prev: '&#9664;',
  133. next: '&#9654;',
  134. lastSel: false,
  135. mode: 'single',
  136. view: 'days',
  137. calendars: 1,
  138. format: 'Y-m-d',
  139. position: 'bottom',
  140. eventName: 'click',
  141. onRender: function(){return {};},
  142. onChange: function(){return true;},
  143. onShow: function(){return true;},
  144. onBeforeShow: function(){return true;},
  145. onHide: function(){return true;},
  146. locale: {
  147. days: ["日", "一", "二", "三", "四", "五", "六", "日"],
  148. daysShort: ["日", "一", "二", "三", "四", "五", "六", "日"],
  149. daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"],
  150. months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
  151. monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
  152. weekMin: '周'
  153. }
  154. },
  155. fill = function(el) {
  156. var options = $(el).data('datepicker');
  157. var cal = $(el);
  158. var currentCal = Math.floor(options.calendars/2), date, data, dow, month, cnt = 0, week, days, indic, indic2, html, tblCal;
  159. cal.find('td>table tbody').remove();
  160. for (var i = 0; i < options.calendars; i++) {
  161. date = new Date(options.current);
  162. date.addMonths(-currentCal + i);
  163. tblCal = cal.find('table').eq(i+1);
  164. switch (tblCal[0].className) {
  165. case 'datepickerViewDays':
  166. dow = formatDate(date, 'B, Y');
  167. break;
  168. case 'datepickerViewMonths':
  169. dow = date.getFullYear();
  170. break;
  171. case 'datepickerViewYears':
  172. dow = (date.getFullYear()-6) + ' - ' + (date.getFullYear()+5);
  173. break;
  174. }
  175. tblCal.find('thead tr:first th:eq(1) span').text(dow);
  176. dow = date.getFullYear()-6;
  177. data = {
  178. data: [],
  179. className: 'datepickerYears'
  180. }
  181. for ( var j = 0; j < 12; j++) {
  182. data.data.push(dow + j);
  183. }
  184. html = tmpl(tpl.months.join(''), data);
  185. date.setDate(1);
  186. data = {weeks:[], test: 10};
  187. month = date.getMonth();
  188. var dow = (date.getDay() - options.starts) % 7;
  189. date.addDays(-(dow + (dow < 0 ? 7 : 0)));
  190. week = -1;
  191. cnt = 0;
  192. while (cnt < 42) {
  193. indic = parseInt(cnt/7,10);
  194. indic2 = cnt%7;
  195. if (!data.weeks[indic]) {
  196. week = date.getWeekNumber();
  197. data.weeks[indic] = {
  198. week: week,
  199. days: []
  200. };
  201. }
  202. data.weeks[indic].days[indic2] = {
  203. text: date.getDate(),
  204. classname: []
  205. };
  206. if (month != date.getMonth()) {
  207. data.weeks[indic].days[indic2].classname.push('datepickerNotInMonth');
  208. }
  209. if (date.getDay() == 0) {
  210. data.weeks[indic].days[indic2].classname.push('datepickerSunday');
  211. }
  212. if (date.getDay() == 6) {
  213. data.weeks[indic].days[indic2].classname.push('datepickerSaturday');
  214. }
  215. var fromUser = options.onRender(date);
  216. var val = date.valueOf();
  217. if (fromUser.selected || options.date == val || $.inArray(val, options.date) > -1 || (options.mode == 'range' && val >= options.date[0] && val <= options.date[1])) {
  218. data.weeks[indic].days[indic2].classname.push('datepickerSelected');
  219. }
  220. if (fromUser.disabled) {
  221. data.weeks[indic].days[indic2].classname.push('datepickerDisabled');
  222. }
  223. if (fromUser.className) {
  224. data.weeks[indic].days[indic2].classname.push(fromUser.className);
  225. }
  226. data.weeks[indic].days[indic2].classname = data.weeks[indic].days[indic2].classname.join(' ');
  227. cnt++;
  228. date.addDays(1);
  229. }
  230. html = tmpl(tpl.days.join(''), data) + html;
  231. data = {
  232. data: options.locale.monthsShort,
  233. className: 'datepickerMonths'
  234. };
  235. html = tmpl(tpl.months.join(''), data) + html;
  236. tblCal.append(html);
  237. }
  238. },
  239. parseDate = function (date, format) {
  240. if (date.constructor == Date) {
  241. return new Date(date);
  242. }
  243. var parts = date.split(/\W+/);
  244. var against = format.split(/\W+/), d, m, y, h, min, now = new Date();
  245. for (var i = 0; i < parts.length; i++) {
  246. switch (against[i]) {
  247. case 'd':
  248. case 'e':
  249. d = parseInt(parts[i],10);
  250. break;
  251. case 'm':
  252. m = parseInt(parts[i], 10)-1;
  253. break;
  254. case 'Y':
  255. case 'y':
  256. y = parseInt(parts[i], 10);
  257. y += y > 100 ? 0 : (y < 29 ? 2000 : 1900);
  258. break;
  259. case 'H':
  260. case 'I':
  261. case 'k':
  262. case 'l':
  263. h = parseInt(parts[i], 10);
  264. break;
  265. case 'P':
  266. case 'p':
  267. if (/pm/i.test(parts[i]) && h < 12) {
  268. h += 12;
  269. } else if (/am/i.test(parts[i]) && h >= 12) {
  270. h -= 12;
  271. }
  272. break;
  273. case 'M':
  274. min = parseInt(parts[i], 10);
  275. break;
  276. }
  277. }
  278. return new Date(
  279. y === undefined ? now.getFullYear() : y,
  280. m === undefined ? now.getMonth() : m,
  281. d === undefined ? now.getDate() : d,
  282. h === undefined ? now.getHours() : h,
  283. min === undefined ? now.getMinutes() : min,
  284. 0
  285. );
  286. },
  287. formatDate = function(date, format) {
  288. var m = date.getMonth();
  289. var d = date.getDate();
  290. var y = date.getFullYear();
  291. var wn = date.getWeekNumber();
  292. var w = date.getDay();
  293. var s = {};
  294. var hr = date.getHours();
  295. var pm = (hr >= 12);
  296. var ir = (pm) ? (hr - 12) : hr;
  297. var dy = date.getDayOfYear();
  298. if (ir == 0) {
  299. ir = 12;
  300. }
  301. var min = date.getMinutes();
  302. var sec = date.getSeconds();
  303. var parts = format.split(''), part;
  304. for ( var i = 0; i < parts.length; i++ ) {
  305. part = parts[i];
  306. switch (parts[i]) {
  307. case 'a':
  308. part = date.getDayName();
  309. break;
  310. case 'A':
  311. part = date.getDayName(true);
  312. break;
  313. case 'b':
  314. part = date.getMonthName();
  315. break;
  316. case 'B':
  317. part = date.getMonthName(true);
  318. break;
  319. case 'C':
  320. part = 1 + Math.floor(y / 100);
  321. break;
  322. case 'd':
  323. part = (d < 10) ? ("0" + d) : d;
  324. break;
  325. case 'e':
  326. part = d;
  327. break;
  328. case 'H':
  329. part = (hr < 10) ? ("0" + hr) : hr;
  330. break;
  331. case 'I':
  332. part = (ir < 10) ? ("0" + ir) : ir;
  333. break;
  334. case 'j':
  335. part = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy;
  336. break;
  337. case 'k':
  338. part = hr;
  339. break;
  340. case 'l':
  341. part = ir;
  342. break;
  343. case 'm':
  344. part = (m < 9) ? ("0" + (1+m)) : (1+m);
  345. break;
  346. case 'M':
  347. part = (min < 10) ? ("0" + min) : min;
  348. break;
  349. case 'p':
  350. case 'P':
  351. part = pm ? "PM" : "AM";
  352. break;
  353. case 's':
  354. part = Math.floor(date.getTime() / 1000);
  355. break;
  356. case 'S':
  357. part = (sec < 10) ? ("0" + sec) : sec;
  358. break;
  359. case 'u':
  360. part = w + 1;
  361. break;
  362. case 'w':
  363. part = w;
  364. break;
  365. case 'y':
  366. part = ('' + y).substr(2, 2);
  367. break;
  368. case 'Y':
  369. part = y;
  370. break;
  371. }
  372. parts[i] = part;
  373. }
  374. return parts.join('');
  375. },
  376. extendDate = function(options) {
  377. if (Date.prototype.tempDate) {
  378. return;
  379. }
  380. Date.prototype.tempDate = null;
  381. Date.prototype.months = options.months;
  382. Date.prototype.monthsShort = options.monthsShort;
  383. Date.prototype.days = options.days;
  384. Date.prototype.daysShort = options.daysShort;
  385. Date.prototype.getMonthName = function(fullName) {
  386. return this[fullName ? 'months' : 'monthsShort'][this.getMonth()];
  387. };
  388. Date.prototype.getDayName = function(fullName) {
  389. return this[fullName ? 'days' : 'daysShort'][this.getDay()];
  390. };
  391. Date.prototype.addDays = function (n) {
  392. this.setDate(this.getDate() + n);
  393. this.tempDate = this.getDate();
  394. };
  395. Date.prototype.addMonths = function (n) {
  396. if (this.tempDate == null) {
  397. this.tempDate = this.getDate();
  398. }
  399. this.setDate(1);
  400. this.setMonth(this.getMonth() + n);
  401. this.setDate(Math.min(this.tempDate, this.getMaxDays()));
  402. };
  403. Date.prototype.addYears = function (n) {
  404. if (this.tempDate == null) {
  405. this.tempDate = this.getDate();
  406. }
  407. this.setDate(1);
  408. this.setFullYear(this.getFullYear() + n);
  409. this.setDate(Math.min(this.tempDate, this.getMaxDays()));
  410. };
  411. Date.prototype.getMaxDays = function() {
  412. var tmpDate = new Date(Date.parse(this)),
  413. d = 28, m;
  414. m = tmpDate.getMonth();
  415. d = 28;
  416. while (tmpDate.getMonth() == m) {
  417. d ++;
  418. tmpDate.setDate(d);
  419. }
  420. return d - 1;
  421. };
  422. Date.prototype.getFirstDay = function() {
  423. var tmpDate = new Date(Date.parse(this));
  424. tmpDate.setDate(1);
  425. return tmpDate.getDay();
  426. };
  427. Date.prototype.getWeekNumber = function() {
  428. var tempDate = new Date(this);
  429. tempDate.setDate(tempDate.getDate() - (tempDate.getDay() + 6) % 7 + 3);
  430. var dms = tempDate.valueOf();
  431. tempDate.setMonth(0);
  432. tempDate.setDate(4);
  433. return Math.round((dms - tempDate.valueOf()) / (604800000)) + 1;
  434. };
  435. Date.prototype.getDayOfYear = function() {
  436. var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
  437. var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
  438. var time = now - then;
  439. return Math.floor(time / 24*60*60*1000);
  440. };
  441. },
  442. layout = function (el) {
  443. var options = $(el).data('datepicker');
  444. var cal = $('#' + options.id);
  445. if (!options.extraHeight) {
  446. var divs = $(el).find('div');
  447. options.extraHeight = divs.get(0).offsetHeight + divs.get(1).offsetHeight;
  448. options.extraWidth = divs.get(2).offsetWidth + divs.get(3).offsetWidth;
  449. }
  450. var tbl = cal.find('table:first').get(0);
  451. var width = tbl.offsetWidth;
  452. var height = tbl.offsetHeight;
  453. cal.css({
  454. width: width + options.extraWidth + 'px',
  455. height: height + options.extraHeight + 'px'
  456. }).find('div.datepickerContainer').css({
  457. width: width + 'px',
  458. height: height + 'px'
  459. });
  460. },
  461. click = function(ev) {
  462. if ($(ev.target).is('span')) {
  463. ev.target = ev.target.parentNode;
  464. }
  465. var el = $(ev.target);
  466. if (el.is('a')) {
  467. ev.target.blur();
  468. if (el.hasClass('datepickerDisabled')) {
  469. return false;
  470. }
  471. var options = $(this).data('datepicker');
  472. var parentEl = el.parent();
  473. var tblEl = parentEl.parent().parent().parent();
  474. var tblIndex = $('table', this).index(tblEl.get(0)) - 1;
  475. var tmp = new Date(options.current);
  476. var changed = false;
  477. var fillIt = false;
  478. if (parentEl.is('th')) {
  479. if (parentEl.hasClass('datepickerWeek') && options.mode == 'range' && !parentEl.next().hasClass('datepickerDisabled')) {
  480. var val = parseInt(parentEl.next().text(), 10);
  481. tmp.addMonths(tblIndex - Math.floor(options.calendars/2));
  482. if (parentEl.next().hasClass('datepickerNotInMonth')) {
  483. tmp.addMonths(val > 15 ? -1 : 1);
  484. }
  485. tmp.setDate(val);
  486. options.date[0] = (tmp.setHours(0,0,0,0)).valueOf();
  487. tmp.setHours(23,59,59,0);
  488. tmp.addDays(6);
  489. options.date[1] = tmp.valueOf();
  490. fillIt = true;
  491. changed = true;
  492. options.lastSel = false;
  493. } else if (parentEl.hasClass('datepickerMonth')) {
  494. tmp.addMonths(tblIndex - Math.floor(options.calendars/2));
  495. switch (tblEl.get(0).className) {
  496. case 'datepickerViewDays':
  497. tblEl.get(0).className = 'datepickerViewMonths';
  498. el.find('span').text(tmp.getFullYear());
  499. break;
  500. case 'datepickerViewMonths':
  501. tblEl.get(0).className = 'datepickerViewYears';
  502. el.find('span').text((tmp.getFullYear()-6) + ' - ' + (tmp.getFullYear()+5));
  503. break;
  504. case 'datepickerViewYears':
  505. tblEl.get(0).className = 'datepickerViewDays';
  506. el.find('span').text(formatDate(tmp, 'B, Y'));
  507. break;
  508. }
  509. } else if (parentEl.parent().parent().is('thead')) {
  510. switch (tblEl.get(0).className) {
  511. case 'datepickerViewDays':
  512. options.current.addMonths(parentEl.hasClass('datepickerGoPrev') ? -1 : 1);
  513. break;
  514. case 'datepickerViewMonths':
  515. options.current.addYears(parentEl.hasClass('datepickerGoPrev') ? -1 : 1);
  516. break;
  517. case 'datepickerViewYears':
  518. options.current.addYears(parentEl.hasClass('datepickerGoPrev') ? -12 : 12);
  519. break;
  520. }
  521. fillIt = true;
  522. }
  523. } else if (parentEl.is('td') && !parentEl.hasClass('datepickerDisabled')) {
  524. switch (tblEl.get(0).className) {
  525. case 'datepickerViewMonths':
  526. options.current.setMonth(tblEl.find('tbody.datepickerMonths td').index(parentEl));
  527. options.current.setFullYear(parseInt(tblEl.find('thead th.datepickerMonth span').text(), 10));
  528. options.current.addMonths(Math.floor(options.calendars/2) - tblIndex);
  529. tblEl.get(0).className = 'datepickerViewDays';
  530. break;
  531. case 'datepickerViewYears':
  532. options.current.setFullYear(parseInt(el.text(), 10));
  533. tblEl.get(0).className = 'datepickerViewMonths';
  534. break;
  535. default:
  536. var val = parseInt(el.text(), 10);
  537. tmp.addMonths(tblIndex - Math.floor(options.calendars/2));
  538. if (parentEl.hasClass('datepickerNotInMonth')) {
  539. tmp.addMonths(val > 15 ? -1 : 1);
  540. }
  541. tmp.setDate(val);
  542. switch (options.mode) {
  543. case 'multiple':
  544. val = (tmp.setHours(0,0,0,0)).valueOf();
  545. if ($.inArray(val, options.date) > -1) {
  546. $.each(options.date, function(nr, dat){
  547. if (dat == val) {
  548. options.date.splice(nr,1);
  549. return false;
  550. }
  551. });
  552. } else {
  553. options.date.push(val);
  554. }
  555. break;
  556. case 'range':
  557. if (!options.lastSel) {
  558. options.date[0] = (tmp.setHours(0,0,0,0)).valueOf();
  559. }
  560. val = (tmp.setHours(23,59,59,0)).valueOf();
  561. if (val < options.date[0]) {
  562. options.date[1] = options.date[0] + 86399000;
  563. options.date[0] = val - 86399000;
  564. } else {
  565. options.date[1] = val;
  566. }
  567. options.lastSel = !options.lastSel;
  568. break;
  569. default:
  570. options.date = tmp.valueOf();
  571. break;
  572. }
  573. break;
  574. }
  575. fillIt = true;
  576. changed = true;
  577. }
  578. if (fillIt) {
  579. fill(this);
  580. }
  581. if (changed) {
  582. options.onChange.apply(this, prepareDate(options));
  583. }
  584. }
  585. return false;
  586. },
  587. prepareDate = function (options) {
  588. var tmp;
  589. if (options.mode == 'single') {
  590. tmp = new Date(options.date);
  591. return [formatDate(tmp, options.format), tmp, options.el];
  592. } else {
  593. tmp = [[],[], options.el];
  594. $.each(options.date, function(nr, val){
  595. var date = new Date(val);
  596. tmp[0].push(formatDate(date, options.format));
  597. tmp[1].push(date);
  598. });
  599. return tmp;
  600. }
  601. },
  602. getViewport = function () {
  603. var m = document.compatMode == 'CSS1Compat';
  604. return {
  605. l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft),
  606. t : window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop),
  607. w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth),
  608. h : window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight)
  609. };
  610. },
  611. isChildOf = function(parentEl, el, container) {
  612. if (parentEl == el) {
  613. return true;
  614. }
  615. if (parentEl.contains) {
  616. return parentEl.contains(el);
  617. }
  618. if ( parentEl.compareDocumentPosition ) {
  619. return !!(parentEl.compareDocumentPosition(el) & 16);
  620. }
  621. var prEl = el.parentNode;
  622. while(prEl && prEl != container) {
  623. if (prEl == parentEl)
  624. return true;
  625. prEl = prEl.parentNode;
  626. }
  627. return false;
  628. },
  629. show = function (ev) {
  630. var cal = $('#' + $(this).data('datepickerId'));
  631. if (!cal.is(':visible')) {
  632. var calEl = cal.get(0);
  633. fill(calEl);
  634. var options = cal.data('datepicker');
  635. options.onBeforeShow.apply(this, [cal.get(0)]);
  636. var pos = $(this).offset();
  637. var viewPort = getViewport();
  638. var top = pos.top;
  639. var left = pos.left;
  640. var oldDisplay = $.curCSS(calEl, 'display');
  641. cal.css({
  642. visibility: 'hidden',
  643. display: 'block'
  644. });
  645. layout(calEl);
  646. switch (options.position){
  647. case 'top':
  648. top -= calEl.offsetHeight;
  649. break;
  650. case 'left':
  651. left -= calEl.offsetWidth;
  652. break;
  653. case 'right':
  654. left += this.offsetWidth;
  655. break;
  656. case 'bottom':
  657. top += this.offsetHeight;
  658. break;
  659. }
  660. if (top + calEl.offsetHeight > viewPort.t + viewPort.h) {
  661. top = pos.top - calEl.offsetHeight;
  662. }
  663. if (top < viewPort.t) {
  664. top = pos.top + this.offsetHeight + calEl.offsetHeight;
  665. }
  666. if (left + calEl.offsetWidth > viewPort.l + viewPort.w) {
  667. left = pos.left - calEl.offsetWidth;
  668. }
  669. if (left < viewPort.l) {
  670. left = pos.left + this.offsetWidth
  671. }
  672. cal.css({
  673. visibility: 'visible',
  674. display: 'block',
  675. top: top + 'px',
  676. left: left + 'px'
  677. });
  678. if (options.onShow.apply(this, [cal.get(0)]) != false) {
  679. cal.show();
  680. }
  681. $(document).bind('mousedown', {cal: cal, trigger: this}, hide);
  682. }
  683. return false;
  684. },
  685. hide = function (ev) {
  686. if (ev.target != ev.data.trigger && !isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) {
  687. if (ev.data.cal.data('datepicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) {
  688. ev.data.cal.hide();
  689. }
  690. $(document).unbind('mousedown', hide);
  691. }
  692. };
  693. return {
  694. init: function(options){
  695. options = $.extend({}, defaults, options||{});
  696. extendDate(options.locale);
  697. options.calendars = Math.max(1, parseInt(options.calendars,10)||1);
  698. options.mode = /single|multiple|range/.test(options.mode) ? options.mode : 'single';
  699. return this.each(function(){
  700. if (!$(this).data('datepicker')) {
  701. options.el = this;
  702. if (options.date.constructor == String) {
  703. options.date = parseDate(options.date, options.format);
  704. options.date.setHours(0,0,0,0);
  705. }
  706. if (options.mode != 'single') {
  707. if (options.date.constructor != Array) {
  708. options.date = [options.date.valueOf()];
  709. if (options.mode == 'range') {
  710. options.date.push(((new Date(options.date[0])).setHours(23,59,59,0)).valueOf());
  711. }
  712. } else {
  713. for (var i = 0; i < options.date.length; i++) {
  714. options.date[i] = (parseDate(options.date[i], options.format).setHours(0,0,0,0)).valueOf();
  715. }
  716. if (options.mode == 'range') {
  717. options.date[1] = ((new Date(options.date[1])).setHours(23,59,59,0)).valueOf();
  718. }
  719. }
  720. } else {
  721. options.date = options.date.valueOf();
  722. }
  723. if (!options.current) {
  724. options.current = new Date();
  725. } else {
  726. options.current = parseDate(options.current, options.format);
  727. }
  728. options.current.setDate(1);
  729. options.current.setHours(0,0,0,0);
  730. var id = 'datepicker_' + parseInt(Math.random() * 1000), cnt;
  731. options.id = id;
  732. $(this).data('datepickerId', options.id);
  733. var cal = $(tpl.wrapper).attr('id', id).bind('click', click).data('datepicker', options);
  734. if (options.className) {
  735. cal.addClass(options.className);
  736. }
  737. var html = '';
  738. for (var i = 0; i < options.calendars; i++) {
  739. cnt = options.starts;
  740. if (i > 0) {
  741. html += tpl.space;
  742. }
  743. html += tmpl(tpl.head.join(''), {
  744. week: options.locale.weekMin,
  745. prev: options.prev,
  746. next: options.next,
  747. day1: options.locale.daysMin[(cnt++)%7],
  748. day2: options.locale.daysMin[(cnt++)%7],
  749. day3: options.locale.daysMin[(cnt++)%7],
  750. day4: options.locale.daysMin[(cnt++)%7],
  751. day5: options.locale.daysMin[(cnt++)%7],
  752. day6: options.locale.daysMin[(cnt++)%7],
  753. day7: options.locale.daysMin[(cnt++)%7]
  754. });
  755. }
  756. cal
  757. .find('tr:first').append(html)
  758. .find('table').addClass(views[options.view]);
  759. fill(cal.get(0));
  760. if (options.flat) {
  761. cal.appendTo(this).show().css('position', 'relative');
  762. layout(cal.get(0));
  763. } else {
  764. cal.appendTo(document.body);
  765. $(this).bind(options.eventName, show);
  766. }
  767. }
  768. });
  769. },
  770. showPicker: function() {
  771. return this.each( function () {
  772. if ($(this).data('datepickerId')) {
  773. show.apply(this);
  774. }
  775. });
  776. },
  777. hidePicker: function() {
  778. return this.each( function () {
  779. if ($(this).data('datepickerId')) {
  780. $('#' + $(this).data('datepickerId')).hide();
  781. }
  782. });
  783. },
  784. setDate: function(date, shiftTo){
  785. return this.each(function(){
  786. if ($(this).data('datepickerId')) {
  787. var cal = $('#' + $(this).data('datepickerId'));
  788. var options = cal.data('datepicker');
  789. options.date = date;
  790. if (options.date.constructor == String) {
  791. options.date = parseDate(options.date, options.format);
  792. options.date.setHours(0,0,0,0);
  793. }
  794. if (options.mode != 'single') {
  795. if (options.date.constructor != Array) {
  796. options.date = [options.date.valueOf()];
  797. if (options.mode == 'range') {
  798. options.date.push(((new Date(options.date[0])).setHours(23,59,59,0)).valueOf());
  799. }
  800. } else {
  801. for (var i = 0; i < options.date.length; i++) {
  802. options.date[i] = (parseDate(options.date[i], options.format).setHours(0,0,0,0)).valueOf();
  803. }
  804. if (options.mode == 'range') {
  805. options.date[1] = ((new Date(options.date[1])).setHours(23,59,59,0)).valueOf();
  806. }
  807. }
  808. } else {
  809. options.date = options.date.valueOf();
  810. }
  811. if (shiftTo) {
  812. options.current = new Date (options.mode != 'single' ? options.date[0] : options.date);
  813. }
  814. fill(cal.get(0));
  815. }
  816. });
  817. },
  818. getDate: function(formated) {
  819. if (this.size() > 0) {
  820. return prepareDate($('#' + $(this).data('datepickerId')).data('datepicker'))[formated ? 0 : 1];
  821. }
  822. },
  823. clear: function(){
  824. return this.each(function(){
  825. if ($(this).data('datepickerId')) {
  826. var cal = $('#' + $(this).data('datepickerId'));
  827. var options = cal.data('datepicker');
  828. if (options.mode != 'single') {
  829. options.date = [];
  830. fill(cal.get(0));
  831. }
  832. }
  833. });
  834. },
  835. fixLayout: function(){
  836. return this.each(function(){
  837. if ($(this).data('datepickerId')) {
  838. var cal = $('#' + $(this).data('datepickerId'));
  839. var options = cal.data('datepicker');
  840. if (options.flat) {
  841. layout(cal.get(0));
  842. }
  843. }
  844. });
  845. }
  846. };
  847. }();
  848. $.fn.extend({
  849. DatePicker: DatePicker.init,
  850. DatePickerHide: DatePicker.hidePicker,
  851. DatePickerShow: DatePicker.showPicker,
  852. DatePickerSetDate: DatePicker.setDate,
  853. DatePickerGetDate: DatePicker.getDate,
  854. DatePickerClear: DatePicker.clear,
  855. DatePickerLayout: DatePicker.fixLayout
  856. });
  857. })(jQuery);
  858. (function(){
  859. var cache = {};
  860. this.tmpl = function tmpl(str, data){
  861. // Figure out if we're getting a template, or if we need to
  862. // load the template - and be sure to cache the result.
  863. var fn = !/\W/.test(str) ?
  864. cache[str] = cache[str] ||
  865. tmpl(document.getElementById(str).innerHTML) :
  866. // Generate a reusable function that will serve as a template
  867. // generator (and which will be cached).
  868. new Function("obj",
  869. "var p=[],print=function(){p.push.apply(p,arguments);};" +
  870. // Introduce the data as local variables using with(){}
  871. "with(obj){p.push('" +
  872. // Convert the template into pure JavaScript
  873. str
  874. .replace(/[\r\t\n]/g, " ")
  875. .split("<%").join("\t")
  876. .replace(/((^|%>)[^\t]*)'/g, "$1\r")
  877. .replace(/\t=(.*?)%>/g, "',$1,'")
  878. .split("\t").join("');")
  879. .split("%>").join("p.push('")
  880. .split("\r").join("\\'")
  881. + "');}return p.join('');");
  882. // Provide some basic currying to the user
  883. return data ? fn( data ) : fn;
  884. };
  885. })();