Nav apraksta

bootstrap-datepicker.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. /* =========================================================
  2. * bootstrap-datepicker.js
  3. * http://www.eyecon.ro/bootstrap-datepicker
  4. * =========================================================
  5. * Copyright 2012 Stefan Petre
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * ========================================================= */
  19. !function( $ ) {
  20. // Picker object
  21. var Datepicker = function(element, options){
  22. this.element = $(element);
  23. this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy');
  24. this.picker = $(DPGlobal.template)
  25. .appendTo('body')
  26. .on({
  27. click: $.proxy(this.click, this)//,
  28. //mousedown: $.proxy(this.mousedown, this)
  29. });
  30. this.isInput = this.element.is('input');
  31. this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
  32. if (this.isInput) {
  33. this.element.on({
  34. focus: $.proxy(this.show, this),
  35. //blur: $.proxy(this.hide, this),
  36. keyup: $.proxy(this.update, this)
  37. });
  38. } else {
  39. if (this.component){
  40. this.component.on('click', $.proxy(this.show, this));
  41. } else {
  42. this.element.on('click', $.proxy(this.show, this));
  43. }
  44. }
  45. this.minViewMode = options.minViewMode||this.element.data('date-minviewmode')||0;
  46. if (typeof this.minViewMode === 'string') {
  47. switch (this.minViewMode) {
  48. case 'months':
  49. this.minViewMode = 1;
  50. break;
  51. case 'years':
  52. this.minViewMode = 2;
  53. break;
  54. default:
  55. this.minViewMode = 0;
  56. break;
  57. }
  58. }
  59. this.viewMode = options.viewMode||this.element.data('date-viewmode')||0;
  60. if (typeof this.viewMode === 'string') {
  61. switch (this.viewMode) {
  62. case 'months':
  63. this.viewMode = 1;
  64. break;
  65. case 'years':
  66. this.viewMode = 2;
  67. break;
  68. default:
  69. this.viewMode = 0;
  70. break;
  71. }
  72. }
  73. this.startViewMode = this.viewMode;
  74. this.weekStart = options.weekStart||this.element.data('date-weekstart')||0;
  75. this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
  76. this.onRender = options.onRender;
  77. this.fillDow();
  78. this.fillMonths();
  79. this.update();
  80. this.showMode();
  81. };
  82. Datepicker.prototype = {
  83. constructor: Datepicker,
  84. show: function(e) {
  85. this.picker.show();
  86. this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
  87. this.place();
  88. $(window).on('resize', $.proxy(this.place, this));
  89. if (e ) {
  90. e.stopPropagation();
  91. e.preventDefault();
  92. }
  93. if (!this.isInput) {
  94. }
  95. var that = this;
  96. $(document).on('mousedown', function(ev){
  97. if ($(ev.target).closest('.datepicker').length == 0) {
  98. that.hide();
  99. }
  100. });
  101. this.element.trigger({
  102. type: 'show',
  103. date: this.date
  104. });
  105. },
  106. hide: function(){
  107. this.picker.hide();
  108. $(window).off('resize', this.place);
  109. this.viewMode = this.startViewMode;
  110. this.showMode();
  111. if (!this.isInput) {
  112. $(document).off('mousedown', this.hide);
  113. }
  114. //this.set();
  115. this.element.trigger({
  116. type: 'hide',
  117. date: this.date
  118. });
  119. },
  120. set: function() {
  121. var formated = DPGlobal.formatDate(this.date, this.format);
  122. if (!this.isInput) {
  123. if (this.component){
  124. this.element.find('input').prop('value', formated);
  125. }
  126. this.element.data('date', formated);
  127. } else {
  128. this.element.prop('value', formated);
  129. }
  130. },
  131. setValue: function(newDate) {
  132. if (typeof newDate === 'string') {
  133. this.date = DPGlobal.parseDate(newDate, this.format);
  134. } else {
  135. this.date = new Date(newDate);
  136. }
  137. this.set();
  138. this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0);
  139. this.fill();
  140. },
  141. place: function(){
  142. var offset = this.component ? this.component.offset() : this.element.offset();
  143. this.picker.css({
  144. top: offset.top + this.height,
  145. left: offset.left
  146. });
  147. },
  148. update: function(newDate){
  149. this.date = DPGlobal.parseDate(
  150. typeof newDate === 'string' ? newDate : (this.isInput ? this.element.prop('value') : this.element.data('date')),
  151. this.format
  152. );
  153. this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0);
  154. this.fill();
  155. },
  156. fillDow: function(){
  157. var dowCnt = this.weekStart;
  158. var html = '<tr>';
  159. while (dowCnt < this.weekStart + 7) {
  160. html += '<th class="dow">'+DPGlobal.dates.daysMin[(dowCnt++)%7]+'</th>';
  161. }
  162. html += '</tr>';
  163. this.picker.find('.datepicker-days thead').append(html);
  164. },
  165. fillMonths: function(){
  166. var html = '';
  167. var i = 0
  168. while (i < 12) {
  169. html += '<span class="month">'+DPGlobal.dates.monthsShort[i++]+'</span>';
  170. }
  171. this.picker.find('.datepicker-months td').append(html);
  172. },
  173. fill: function() {
  174. var d = new Date(this.viewDate),
  175. year = d.getFullYear(),
  176. month = d.getMonth(),
  177. currentDate = this.date.valueOf();
  178. this.picker.find('.datepicker-days th:eq(1)')
  179. .text(DPGlobal.dates.months[month]+' '+year);
  180. var prevMonth = new Date(year, month-1, 28,0,0,0,0),
  181. day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth());
  182. prevMonth.setDate(day);
  183. prevMonth.setDate(day - (prevMonth.getDay() - this.weekStart + 7)%7);
  184. var nextMonth = new Date(prevMonth);
  185. nextMonth.setDate(nextMonth.getDate() + 42);
  186. nextMonth = nextMonth.valueOf();
  187. var html = [];
  188. var clsName,
  189. prevY,
  190. prevM;
  191. while(prevMonth.valueOf() < nextMonth) {
  192. if (prevMonth.getDay() === this.weekStart) {
  193. html.push('<tr>');
  194. }
  195. clsName = this.onRender(prevMonth);
  196. prevY = prevMonth.getFullYear();
  197. prevM = prevMonth.getMonth();
  198. if ((prevM < month && prevY === year) || prevY < year) {
  199. clsName += ' old';
  200. } else if ((prevM > month && prevY === year) || prevY > year) {
  201. clsName += ' new';
  202. }
  203. if (prevMonth.valueOf() === currentDate) {
  204. clsName += ' active';
  205. }
  206. html.push('<td class="day '+clsName+'">'+prevMonth.getDate() + '</td>');
  207. if (prevMonth.getDay() === this.weekEnd) {
  208. html.push('</tr>');
  209. }
  210. prevMonth.setDate(prevMonth.getDate()+1);
  211. }
  212. this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
  213. var currentYear = this.date.getFullYear();
  214. var months = this.picker.find('.datepicker-months')
  215. .find('th:eq(1)')
  216. .text(year)
  217. .end()
  218. .find('span').removeClass('active');
  219. if (currentYear === year) {
  220. months.eq(this.date.getMonth()).addClass('active');
  221. }
  222. html = '';
  223. year = parseInt(year/10, 10) * 10;
  224. var yearCont = this.picker.find('.datepicker-years')
  225. .find('th:eq(1)')
  226. .text(year + '-' + (year + 9))
  227. .end()
  228. .find('td');
  229. year -= 1;
  230. for (var i = -1; i < 11; i++) {
  231. html += '<span class="year'+(i === -1 || i === 10 ? ' old' : '')+(currentYear === year ? ' active' : '')+'">'+year+'</span>';
  232. year += 1;
  233. }
  234. yearCont.html(html);
  235. },
  236. click: function(e) {
  237. e.stopPropagation();
  238. e.preventDefault();
  239. var target = $(e.target).closest('span, td, th');
  240. if (target.length === 1) {
  241. switch(target[0].nodeName.toLowerCase()) {
  242. case 'th':
  243. switch(target[0].className) {
  244. case 'switch':
  245. this.showMode(1);
  246. break;
  247. case 'prev':
  248. case 'next':
  249. this.viewDate['set'+DPGlobal.modes[this.viewMode].navFnc].call(
  250. this.viewDate,
  251. this.viewDate['get'+DPGlobal.modes[this.viewMode].navFnc].call(this.viewDate) +
  252. DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1)
  253. );
  254. this.fill();
  255. this.set();
  256. break;
  257. }
  258. break;
  259. case 'span':
  260. if (target.is('.month')) {
  261. var month = target.parent().find('span').index(target);
  262. this.viewDate.setMonth(month);
  263. } else {
  264. var year = parseInt(target.text(), 10)||0;
  265. this.viewDate.setFullYear(year);
  266. }
  267. if (this.viewMode !== 0) {
  268. this.date = new Date(this.viewDate);
  269. this.element.trigger({
  270. type: 'changeDate',
  271. date: this.date,
  272. viewMode: DPGlobal.modes[this.viewMode].clsName
  273. });
  274. }
  275. this.showMode(-1);
  276. this.fill();
  277. this.set();
  278. break;
  279. case 'td':
  280. if (target.is('.day') && !target.is('.disabled')){
  281. var day = parseInt(target.text(), 10)||1;
  282. var month = this.viewDate.getMonth();
  283. if (target.is('.old')) {
  284. month -= 1;
  285. } else if (target.is('.new')) {
  286. month += 1;
  287. }
  288. var year = this.viewDate.getFullYear();
  289. this.date = new Date(year, month, day,0,0,0,0);
  290. this.viewDate = new Date(year, month, Math.min(28, day),0,0,0,0);
  291. this.fill();
  292. this.set();
  293. this.element.trigger({
  294. type: 'changeDate',
  295. date: this.date,
  296. viewMode: DPGlobal.modes[this.viewMode].clsName
  297. });
  298. }
  299. break;
  300. }
  301. }
  302. },
  303. mousedown: function(e){
  304. e.stopPropagation();
  305. e.preventDefault();
  306. },
  307. showMode: function(dir) {
  308. if (dir) {
  309. this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
  310. }
  311. this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
  312. }
  313. };
  314. $.fn.datepicker = function ( option, val ) {
  315. return this.each(function () {
  316. var $this = $(this),
  317. data = $this.data('datepicker'),
  318. options = typeof option === 'object' && option;
  319. if (!data) {
  320. $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
  321. }
  322. if (typeof option === 'string') data[option](val);
  323. });
  324. };
  325. $.fn.datepicker.defaults = {
  326. onRender: function(date) {
  327. return '';
  328. }
  329. };
  330. $.fn.datepicker.Constructor = Datepicker;
  331. var DPGlobal = {
  332. modes: [
  333. {
  334. clsName: 'days',
  335. navFnc: 'Month',
  336. navStep: 1
  337. },
  338. {
  339. clsName: 'months',
  340. navFnc: 'FullYear',
  341. navStep: 1
  342. },
  343. {
  344. clsName: 'years',
  345. navFnc: 'FullYear',
  346. navStep: 10
  347. }],
  348. dates:{
  349. days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
  350. daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
  351. daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
  352. months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
  353. monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  354. },
  355. isLeapYear: function (year) {
  356. return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
  357. },
  358. getDaysInMonth: function (year, month) {
  359. return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
  360. },
  361. parseFormat: function(format){
  362. var separator = format.match(/[.\/\-\s].*?/),
  363. parts = format.split(/\W+/);
  364. if (!separator || !parts || parts.length === 0){
  365. throw new Error("Invalid date format.");
  366. }
  367. return {separator: separator, parts: parts};
  368. },
  369. parseDate: function(date, format) {
  370. var parts = date.split(format.separator),
  371. date = new Date(),
  372. val;
  373. date.setHours(0);
  374. date.setMinutes(0);
  375. date.setSeconds(0);
  376. date.setMilliseconds(0);
  377. if (parts.length === format.parts.length) {
  378. var year = date.getFullYear(), day = date.getDate(), month = date.getMonth();
  379. for (var i=0, cnt = format.parts.length; i < cnt; i++) {
  380. val = parseInt(parts[i], 10)||1;
  381. switch(format.parts[i]) {
  382. case 'dd':
  383. case 'd':
  384. day = val;
  385. date.setDate(val);
  386. break;
  387. case 'mm':
  388. case 'm':
  389. month = val - 1;
  390. date.setMonth(val - 1);
  391. break;
  392. case 'yy':
  393. year = 2000 + val;
  394. date.setFullYear(2000 + val);
  395. break;
  396. case 'yyyy':
  397. year = val;
  398. date.setFullYear(val);
  399. break;
  400. }
  401. }
  402. date = new Date(year, month, day, 0 ,0 ,0);
  403. }
  404. return date;
  405. },
  406. formatDate: function(date, format){
  407. var val = {
  408. d: date.getDate(),
  409. m: date.getMonth() + 1,
  410. yy: date.getFullYear().toString().substring(2),
  411. yyyy: date.getFullYear()
  412. };
  413. val.dd = (val.d < 10 ? '0' : '') + val.d;
  414. val.mm = (val.m < 10 ? '0' : '') + val.m;
  415. var date = [];
  416. for (var i=0, cnt = format.parts.length; i < cnt; i++) {
  417. date.push(val[format.parts[i]]);
  418. }
  419. return date.join(format.separator);
  420. },
  421. headTemplate: '<thead>'+
  422. '<tr>'+
  423. '<th class="prev">&lsaquo;</th>'+
  424. '<th colspan="5" class="switch"></th>'+
  425. '<th class="next">&rsaquo;</th>'+
  426. '</tr>'+
  427. '</thead>',
  428. contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>'
  429. };
  430. DPGlobal.template = '<div class="datepicker dropdown-menu">'+
  431. '<div class="datepicker-days">'+
  432. '<table class=" table-condensed">'+
  433. DPGlobal.headTemplate+
  434. '<tbody></tbody>'+
  435. '</table>'+
  436. '</div>'+
  437. '<div class="datepicker-months">'+
  438. '<table class="table-condensed">'+
  439. DPGlobal.headTemplate+
  440. DPGlobal.contTemplate+
  441. '</table>'+
  442. '</div>'+
  443. '<div class="datepicker-years">'+
  444. '<table class="table-condensed">'+
  445. DPGlobal.headTemplate+
  446. DPGlobal.contTemplate+
  447. '</table>'+
  448. '</div>'+
  449. '</div>';
  450. }( window.jQuery );