1/*! version : 4.17.42 2 ========================================================= 3 bootstrap-datetimejs 4 https://github.com/Eonasdan/bootstrap-datetimepicker 5 Copyright (c) 2015 Jonathan Peterson 6 ========================================================= 7 */ 8/* 9 The MIT License (MIT) 10 11 Copyright (c) 2015 Jonathan Peterson 12 13 Permission is hereby granted, free of charge, to any person obtaining a copy 14 of this software and associated documentation files (the "Software"), to deal 15 in the Software without restriction, including without limitation the rights 16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 copies of the Software, and to permit persons to whom the Software is 18 furnished to do so, subject to the following conditions: 19 20 The above copyright notice and this permission notice shall be included in 21 all copies or substantial portions of the Software. 22 23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 THE SOFTWARE. 30 */ 31/*global define:false */ 32/*global exports:false */ 33/*global require:false */ 34/*global jQuery:false */ 35/*global moment:false */ 36(function (factory) { 37 'use strict'; 38 if (typeof define === 'function' && define.amd) { 39 // AMD is used - Register as an anonymous module. 40 define(['jquery', 'moment'], factory); 41 } else if (typeof exports === 'object') { 42 module.exports = factory(require('jquery'), require('moment')); 43 } else { 44 // Neither AMD nor CommonJS used. Use global variables. 45 if (typeof jQuery === 'undefined') { 46 throw 'bootstrap-datetimepicker requires jQuery to be loaded first'; 47 } 48 if (typeof moment === 'undefined') { 49 throw 'bootstrap-datetimepicker requires Moment.js to be loaded first'; 50 } 51 factory(jQuery, moment); 52 } 53}(function ($, moment) { 54 'use strict'; 55 if (!moment) { 56 throw new Error('bootstrap-datetimepicker requires Moment.js to be loaded first'); 57 } 58 59 var dateTimePicker = function (element, options) { 60 var picker = {}, 61 date, 62 viewDate, 63 unset = true, 64 input, 65 component = false, 66 widget = false, 67 use24Hours, 68 minViewModeNumber = 0, 69 actualFormat, 70 parseFormats, 71 currentViewMode, 72 datePickerModes = [ 73 { 74 clsName: 'days', 75 navFnc: 'M', 76 navStep: 1 77 }, 78 { 79 clsName: 'months', 80 navFnc: 'y', 81 navStep: 1 82 }, 83 { 84 clsName: 'years', 85 navFnc: 'y', 86 navStep: 10 87 }, 88 { 89 clsName: 'decades', 90 navFnc: 'y', 91 navStep: 100 92 } 93 ], 94 viewModes = ['days', 'months', 'years', 'decades'], 95 verticalModes = ['top', 'bottom', 'auto'], 96 horizontalModes = ['left', 'right', 'auto'], 97 toolbarPlacements = ['default', 'top', 'bottom'], 98 keyMap = { 99 'up': 38, 100 38: 'up', 101 'down': 40, 102 40: 'down', 103 'left': 37, 104 37: 'left', 105 'right': 39, 106 39: 'right', 107 'tab': 9, 108 9: 'tab', 109 'escape': 27, 110 27: 'escape', 111 'enter': 13, 112 13: 'enter', 113 'pageUp': 33, 114 33: 'pageUp', 115 'pageDown': 34, 116 34: 'pageDown', 117 'shift': 16, 118 16: 'shift', 119 'control': 17, 120 17: 'control', 121 'space': 32, 122 32: 'space', 123 't': 84, 124 84: 't', 125 'delete': 46, 126 46: 'delete' 127 }, 128 keyState = {}, 129 130 /******************************************************************************** 131 * 132 * Private functions 133 * 134 ********************************************************************************/ 135 136 hasTimeZone = function () { 137 return moment.tz !== undefined && options.timeZone !== undefined && options.timeZone !== null && options.timeZone !== ''; 138 }, 139 140 getMoment = function (d) { 141 var returnMoment; 142 143 if (d === undefined || d === null) { 144 returnMoment = moment(); //TODO should this use format? and locale? 145 } else if (moment.isDate(d) || moment.isMoment(d)) { 146 // If the date that is passed in is already a Date() or moment() object, 147 // pass it directly to moment. 148 returnMoment = moment(d); 149 } else if (hasTimeZone()) { // There is a string to parse and a default time zone 150 // parse with the tz function which takes a default time zone if it is not in the format str
150ing 151 returnMoment = moment.tz(d, parseFormats, options.useStrict, options.timeZone); 152 } else { 153 returnMoment = moment(d, parseFormats, options.useStrict); 154 } 155 156 if (hasTimeZone()) { 157 returnMoment.tz(options.timeZone); 158 } 159 160 return returnMoment; 161 }, 162 163 isEnabled = function (granularity) { 164 if (typeof granularity !== 'string' || granularity.length > 1) { 165 throw new TypeError('isEnabled expects a single character string parameter'); 166 } 167 switch (granularity) { 168 case 'y': 169 return actualFormat.indexOf('Y') !== -1; 170 case 'M': 171 return actualFormat.indexOf('M') !== -1; 172 case 'd': 173 return actualFormat.toLowerCase().indexOf('d') !== -1; 174 case 'h': 175 case 'H': 176 return actualFormat.toLowerCase().indexOf('h') !== -1; 177 case 'm': 178 return actualFormat.indexOf('m') !== -1; 179 case 's': 180 return actualFormat.indexOf('s') !== -1; 181 default: 182 return false; 183 } 184 }, 185 186 hasTime = function () { 187 return (isEnabled('h') || isEnabled('m') || isEnabled('s')); 188 }, 189 190 hasDate = function () { 191 return (isEnabled('y') || isEnabled('M') || isEnabled('d')); 192 }, 193 194 getDatePickerTemplate = function () { 195 var headTemplate = $('<thead>') 196 .append($('<tr>') 197 .append($('<th>').addClass('prev').attr('data-action', 'previous') 198 .append($('<span>').addClass(options.icons.previous)) 199 ) 200 .append($('<th>').addClass('picker-switch').attr('data-action', 'pickerSwitch').attr('colspan', (options.calendarWeeks ? '6' : '5'))) 201 .append($('<th>').addClass('next').attr('data-action', 'next') 202 .append($('<span>').addClass(options.icons.next)) 203 ) 204 ), 205 contTemplate = $('<tbody>') 206 .append($('<tr>') 207 .append($('<td>').attr('colspan', (options.calendarWeeks ? '8' : '7'))) 208 ); 209 210 return [ 211 $('<div>').addClass('datepicker-days') 212 .append($('<table>').addClass('table-condensed') 213 .append(headTemplate) 214 .append($('<tbody>')) 215 ), 216 $('<div>').addClass('datepicker-months') 217 .append($('<table>').addClass('table-condensed') 218 .append(headTemplate.clone()) 219 .append(contTemplate.clone()) 220 ), 221 $('<div>').addClass('datepicker-years') 222 .append($('<table>').addClass('table-condensed') 223 .append(headTemplate.clone()) 224 .append(contTemplate.clone()) 225 ), 226 $('<div>').addClass('datepicker-decades') 227 .append($('<table>').addClass('table-condensed') 228 .append(headTemplate.clone()) 229 .append(contTemplate.clone()) 230 ) 231 ]; 232 }, 233 234 getTimePickerMainTemplate = function () { 235 var topRow = $('<tr>'), 236 middleRow = $('<tr>'), 237 bottomRow = $('<tr>'); 238 239 if (isEnabled('h')) { 240 topRow.append($('<td>') 241 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementHour }).addClass('btn').attr('data-action', 'incrementHours').append($('<span>').addClass(options.icons.up)))); 242 middleRow.append($('<td>') 243 .append($('<span>').addClass('timepicker-hour').attr({ 'data-time-component': 'hours', 'title': options.tooltips.pickHour }).attr('data-action', 'showHours'))); 244 bottomRow.append($('<td>') 245 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementHour }).addClass('btn').attr('data-action', 'decrementHours').append($('<span>').addClass(options.icons.down)))); 246 } 247 if (isEnabled('m')) { 248 if (isEnabled('h')) { 249 topRow.append($('<td>').addClass('separator')); 250 middleRow.append($('<td>').addClass('separator').html(':')); 251 bottomRow.append($('<td>').addClass('separator')); 252 } 253 topRow.append($('<td>') 254 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementMinute }).addClass('btn').attr('data-action', 'incrementMinutes') 255 .append($('<span>').addClass(options.icons.up)))); 256 middleRow.append($('<td>') 257 .append($('<span>').addClass('timepicker-minute').attr({ 'data-time-component': 'minutes', 'title': options.tooltips.pickMinute }).attr('data-action', 'showMinutes'))); 258 bottomRow.append($('<td>') 259 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementMinute }).addClass('btn').attr('data-action', 'decrementMinutes') 260 .append($('<span>').addClass(options.icons.down)))); 261 } 262 if (isEnabled('s')) { 263 if (isEnabled('m')) { 264 topRow.append($('<td>').addClass('separator')); 265 middleRow.append($('<td>').addClass('separator').html(':')); 266 bottomRow.append($('<td>').addClass('separator')); 267 } 268 topRow.append($('<td>') 269 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementSecond }).addClass('btn').attr('data-action', 'incrementSeconds') 270 .append($('<span>').addClass(options.icons.up)))); 271 middleRow.append($('<td>') 272 .append($('<span>').addClass('timepicker-second').attr({ 'data-time-component': '
272seconds', 'title': options.tooltips.pickSecond }).attr('data-action', 'showSeconds'))); 273 bottomRow.append($('<td>') 274 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementSecond }).addClass('btn').attr('data-action', 'decrementSeconds') 275 .append($('<span>').addClass(options.icons.down)))); 276 } 277 278 if (!use24Hours) { 279 topRow.append($('<td>').addClass('separator')); 280 middleRow.append($('<td>') 281 .append($('<button>').addClass('btn btn-primary').attr({ 'data-action': 'togglePeriod', tabindex: '-1', 'title': options.tooltips.togglePeriod }))); 282 bottomRow.append($('<td>').addClass('separator')); 283 } 284 285 return $('<div>').addClass('timepicker-picker') 286 .append($('<table>').addClass('table-condensed') 287 .append([topRow, middleRow, bottomRow])); 288 }, 289 290 getTimePickerTemplate = function () { 291 var hoursView = $('<div>').addClass('timepicker-hours') 292 .append($('<table>').addClass('table-condensed')), 293 minutesView = $('<div>').addClass('timepicker-minutes') 294 .append($('<table>').addClass('table-condensed')), 295 secondsView = $('<div>').addClass('timepicker-seconds') 296 .append($('<table>').addClass('table-condensed')), 297 ret = [getTimePickerMainTemplate()]; 298 299 if (isEnabled('h')) { 300 ret.push(hoursView); 301 } 302 if (isEnabled('m')) { 303 ret.push(minutesView); 304 } 305 if (isEnabled('s')) { 306 ret.push(secondsView); 307 } 308 309 return ret; 310 }, 311 312 getToolbar = function () { 313 var row = []; 314 if (options.showTodayButton) { 315 row.push($('<td>').append($('<a>').attr({ 'data-action': 'today', 'title': options.tooltips.today }).append($('<span>').addClass(options.icons.today)))); 316 } 317 if (!options.sideBySide && hasDate() && hasTime()) { 318 row.push($('<td>').append($('<a>').attr({ 'data-action': 'togglePicker', 'title': options.tooltips.selectTime }).append($('<span>').addClass(options.icons.time)))); 319 } 320 if (options.showClear) { 321 row.push($('<td>').append($('<a>').attr({ 'data-action': 'clear', 'title': options.tooltips.clear }).append($('<span>').addClass(options.icons.clear)))); 322 } 323 if (options.showClose) { 324 row.push($('<td>').append($('<a>').attr({ 'data-action': 'close', 'title': options.tooltips.close }).append($('<span>').addClass(options.icons.close)))); 325 } 326 return $('<table>').addClass('table-condensed').append($('<tbody>').append($('<tr>').append(row))); 327 }, 328 329 getTemplate = function () { 330 var template = $('<div>').addClass('bootstrap-datetimepicker-widget dropdown-menu'), 331 dateView = $('<div>').addClass('datepicker').append(getDatePickerTemplate()), 332 timeView = $('<div>').addClass('timepicker').append(getTimePickerTemplate()), 333 content = $('<ul>').addClass('list-unstyled'), 334 toolbar = $('<li>').addClass('picker-switch' + (options.collapse ? ' accordion-toggle' : '')).append(getToolbar()); 335 336 if (options.inline) { 337 template.removeClass('dropdown-menu'); 338 } 339 340 if (use24Hours) { 341 template.addClass('usetwentyfour'); 342 } 343 344 if (isEnabled('s') && !use24Hours) { 345 template.addClass('wider'); 346 } 347 348 if (options.sideBySide && hasDate() && hasTime()) { 349 template.addClass('timepicker-sbs'); 350 if (options.toolbarPlacement === 'top') { 351 template.append(toolbar); 352 } 353 template.append( 354 $('<div>').addClass('row') 355 .append(dateView.addClass('col-md-6')) 356 .append(timeView.addClass('col-md-6')) 357 ); 358 if (options.toolbarPlacement === 'bottom') { 359 template.append(toolbar); 360 } 361 return template; 362 } 363 364 if (options.toolbarPlacement === 'top') { 365 content.append(toolbar); 366 } 367 if (hasDate()) { 368 content.append($('<li>').addClass((options.collapse && hasTime() ? 'collapse in' : '')).append(dateView)); 369 } 370 if (options.toolbarPlacement === 'default') { 371 content.append(toolbar); 372 } 373 if (hasTime()) { 374 content.append($('<li>
374').addClass((options.collapse && hasDate() ? 'collapse' : '')).append(timeView)); 375 } 376 if (options.toolbarPlacement === 'bottom') { 377 content.append(toolbar); 378 } 379 return template.append(content); 380 }, 381 382 dataToOptions = function () { 383 var eData, 384 dataOptions = {}; 385 386 if (element.is('input') || options.inline) { 387 eData = element.data(); 388 } else { 389 eData = element.find('input').data(); 390 } 391 392 if (eData.dateOptions && eData.dateOptions instanceof Object) { 393 dataOptions = $.extend(true, dataOptions, eData.dateOptions); 394 } 395 396 $.each(options, function (key) { 397 var attributeName = 'date' + key.charAt(0).toUpperCase() + key.slice(1); 398 if (eData[attributeName] !== undefined) { 399 dataOptions[key] = eData[attributeName]; 400 } 401 }); 402 return dataOptions; 403 }, 404 405 place = function () { 406 var position = (component || element).position(), 407 offset = (component || element).offset(), 408 vertical = options.widgetPositioning.vertical, 409 horizontal = options.widgetPositioning.horizontal, 410 parent; 411 412 if (options.widgetParent) { 413 parent = options.widgetParent.append(widget); 414 } else if (element.is('input')) { 415 parent = element.after(widget).parent(); 416 } else if (options.inline) { 417 parent = element.append(widget); 418 return; 419 } else { 420 parent = element; 421 element.children().first().after(widget); 422 } 423 424 // !! ODOO FIX START !! 425 var parentOffset = parent.offset(); 426 position.top = offset.top - parentOffset.top; 427 position.left = offset.left - parentOffset.left; 428 // !! ODOO FIX END !! 429 430 // Top and bottom logic 431 if (vertical === 'auto') { 432 if (offset.top + widget.height() * 1.5 >= $(window).height() + $(window).scrollTop() && 433 widget.height() + element.outerHeight() < offset.top) { 434 vertical = 'top'; 435 } else { 436 vertical = 'bottom'; 437 } 438 } 439 440 // Left and right logic 441 if (horizontal === 'auto') { 442 if (parent.width() < offset.left + widget.outerWidth() / 2 && 443 offset.left + widget.outerWidth() > $(window).width()) { 444 horizontal = 'right'; 445 } else { 446 horizontal = 'left'; 447 } 448 } 449 450 if (vertical === 'top') { 451 widget.addClass('top').removeClass('bottom'); 452 } else { 453 widget.addClass('bottom').removeClass('top'); 454 } 455 456 if (horizontal === 'right') { 457 widget.addClass('pull-right'); 458 } else { 459 widget.removeClass('pull-right'); 460 } 461 462 // find the first parent element that has a relative css positioning 463 if (parent.css('position') !== 'relative') { 464 parent = parent.parents().filter(function () { 465 return $(this).css('position') === 'relative'; 466 }).first(); 467 } 468 469 if (parent.length === 0) { 470 throw new Error('datetimepicker component should be placed within a relative positioned container'); 471 } 472 473 widget.css({ 474 top: vertical === 'top' ? 'auto' : position.top + element.outerHeight(), 475 bottom: vertical === 'top' ? parent.outerHeight() - (parent === element ? 0 : position.top) : 'auto', 476 left: horizontal === 'left' ? (parent === element ? 0 : position.left) : 'auto', 477 right: horizontal === 'left' ? 'auto' : parent.outerWidth() - element.outerWidth() - (parent === element ? 0 : position.left) 478 }); 479 }, 480 481 notifyEvent = function (e) { 482 if (e.type === 'dp.change') { 483 // check _isUTC flag to ensure that we are not comparing apples and oranges 484 var bothUTC = e.date && e.oldDate && e.date._isUTC === e.oldDate._isUTC; 485 if ((bothUTC && e.date.isSame(e.oldDate)) || (!e.date && !e.oldDate)) { 486 return; 487 } 488 } 489 element.trigger(e); 490 }, 491 492 viewUpdate = function (e) { 493 if (e === 'y') { 494 e = 'YYYY'; 495 } 496 notifyEvent({ 497 type: 'dp.update', 498 change: e,
499 viewDate: viewDate.clone() 500 }); 501 }, 502 503 showMode = function (dir) { 504 if (!widget) { 505 return; 506 } 507 if (dir) { 508 currentViewMode = Math.max(minViewModeNumber, Math.min(3, currentViewMode + dir)); 509 } 510 widget.find('.datepicker > div').hide().filter('.datepicker-' + datePickerModes[currentViewMode].clsName).show(); 511 }, 512 513 fillDow = function () { 514 var row = $('<tr>'), 515 currentDate = viewDate.clone().startOf('w').startOf('d'); 516 517 if (options.calendarWeeks === true) { 518 row.append($('<th>').addClass('cw').text('#')); 519 } 520 521 while (currentDate.isBefore(viewDate.clone().endOf('w'))) { 522 row.append($('<th>').addClass('dow').text(currentDate.format('dd'))); 523 currentDate.add(1, 'd'); 524 } 525 widget.find('.datepicker-days thead').append(row); 526 }, 527 528 isInDisabledDates = function (testDate) { 529 return options.disabledDates[testDate.format('YYYY-MM-DD')] === true; 530 }, 531 532 isInEnabledDates = function (testDate) { 533 return options.enabledDates[testDate.format('YYYY-MM-DD')] === true; 534 }, 535 536 isInDisabledHours = function (testDate) { 537 return options.disabledHours[testDate.format('H')] === true; 538 }, 539 540 isInEnabledHours = function (testDate) { 541 return options.enabledHours[testDate.format('H')] === true; 542 }, 543 544 isValid = function (targetMoment, granularity) { 545 if (!targetMoment.isValid()) { 546 return false; 547 } 548 if (options.disabledDates && granularity === 'd' && isInDisabledDates(targetMoment)) { 549 return false; 550 } 551 if (options.enabledDates && granularity === 'd' && !isInEnabledDates(targetMoment)) { 552 return false; 553 } 554 if (options.minDate && targetMoment.isBefore(options.minDate, granularity)) { 555 return false; 556 } 557 if (options.maxDate && targetMoment.isAfter(options.maxDate, granularity)) { 558 return false; 559 } 560 if (options.daysOfWeekDisabled && granularity === 'd' && options.daysOfWeekDisabled.indexOf(targetMoment.day()) !== -1) { 561 return false; 562 } 563 if (options.disabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && isInDisabledHours(targetMoment)) { 564 return false; 565 } 566 if (options.enabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && !isInEnabledHours(targetMoment)) { 567 return false; 568 } 569 if (options.disabledTimeIntervals && (granularity === 'h' || granularity === 'm' || granularity === 's')) { 570 var found = false; 571 $.each(options.disabledTimeIntervals, function () { 572 if (targetMoment.isBetween(this[0], this[1])) { 573 found = true; 574 return false; 575 } 576 }); 577 if (found) { 578 return false; 579 } 580 } 581 return true; 582 }, 583 584 fillMonths = function () { 585 var spans = [], 586 monthsShort = viewDate.clone().startOf('y').startOf('d'); 587 while (monthsShort.isSame(viewDate, 'y')) { 588 spans.push($('<span>').attr('data-action', 'selectMonth').addClass('month').text(monthsShort.format('MMM'))); 589 monthsShort.add(1, 'M'); 590 } 591 widget.find('.datepicker-months td').empty().append(spans); 592 }, 593 594 updateMonths = function () { 595 var monthsView = widget.find('.datepicker-months'), 596 monthsViewHeader = monthsView.find('th'), 597 months = monthsView.find('tbody').find('span'); 598 599 monthsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevYear); 600 monthsViewHeader.eq(1).attr('title', options.tooltips.selectYear); 601 monthsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextYear); 602 603 monthsView.find('.disabled').removeClass('disabled'); 604 605 if (!isValid(viewDate.clone().subtract(1, 'y'), 'y')) { 606 monthsViewHeader.eq(0).addClass('disabled'); 607 } 608 609 monthsViewHeader.eq(1).text(viewDate.year()); 610 611 if (!isValid(viewDate.clone().add(1, 'y'), 'y')) { 612 monthsViewHeader.eq(2).addClass('disabled'); 613 } 614 615 months.removeClass('active'); 616 if (date.isSame(viewDate, 'y') && !unset) { 617 months.eq(date.month()).addClass('active'); 618 } 619 620 months.each(function (index) { 621 if (!isValid(viewDate.clone().month(index), 'M')) { 622 $(this).addClass('disabled'); 623 } 624 }); 625 }, 626 627 updateYears = function () { 628 var yearsView = widget.find('.datepicker-years'), 629 yearsViewHeader = yearsView.find('th'),
630 startYear = viewDate.clone().subtract(5, 'y'), 631 endYear = viewDate.clone().add(6, 'y'), 632 html = ''; 633 634 yearsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevDecade); 635 yearsViewHeader.eq(1).attr('title', options.tooltips.selectDecade); 636 yearsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextDecade); 637 638 yearsView.find('.disabled').removeClass('disabled'); 639 640 if (options.minDate && options.minDate.isAfter(startYear, 'y')) { 641 yearsViewHeader.eq(0).addClass('disabled'); 642 } 643 644 yearsViewHeader.eq(1).text(startYear.year() + '-' + endYear.year()); 645 646 if (options.maxDate && options.maxDate.isBefore(endYear, 'y')) { 647 yearsViewHeader.eq(2).addClass('disabled'); 648 } 649 650 while (!startYear.isAfter(endYear, 'y')) { 651 html += '<span data-action="selectYear" class="year' + (startYear.isSame(date, 'y') && !unset ? ' active' : '') + (!isValid(startYear, 'y') ? ' disabled' : '') + '">' + startYear.year() + '</span>'; 652 startYear.add(1, 'y'); 653 } 654 655 yearsView.find('td').html(html); 656 }, 657 658 updateDecades = function () { 659 var decadesView = widget.find('.datepicker-decades'), 660 decadesViewHeader = decadesView.find('th'), 661 startDecade = moment({ y: viewDate.year() - (viewDate.year() % 100) - 1 }), 662 endDecade = startDecade.clone().add(100, 'y'), 663 startedAt = startDecade.clone(), 664 minDateDecade = false, 665 maxDateDecade = false, 666 endDecadeYear, 667 html = ''; 668 669 decadesViewHeader.eq(0).find('span').attr('title', options.tooltips.prevCentury); 670 decadesViewHeader.eq(2).find('span').attr('title', options.tooltips.nextCentury); 671 672 decadesView.find('.disabled').removeClass('disabled'); 673 674 if (startDecade.isSame(moment({ y: 1900 })) || (options.minDate && options.minDate.isAfter(startDecade, 'y'))) { 675 decadesViewHeader.eq(0).addClass('disabled'); 676 } 677 678 decadesViewHeader.eq(1).text(startDecade.year() + '-' + endDecade.year()); 679 680 if (startDecade.isSame(moment({ y: 2000 })) || (options.maxDate && options.maxDate.isBefore(endDecade, 'y'))) { 681 decadesViewHeader.eq(2).addClass('disabled'); 682 } 683 684 while (!startDecade.isAfter(endDecade, 'y')) { 685 endDecadeYear = startDecade.year() + 12; 686 minDateDecade = options.minDate && options.minDate.isAfter(startDecade, 'y') && options.minDate.year() <= endDecadeYear; 687 maxDateDecade = options.maxDate && options.maxDate.isAfter(startDecade, 'y') && options.maxDate.year() <= endDecadeYear; 688 html += '<span data-action="selectDecade" class="decade' + (date.isAfter(startDecade) && date.year() <= endDecadeYear ? ' active' : '') + 689 (!isValid(startDecade, 'y') && !minDateDecade && !maxDateDecade ? ' disabled' : '') + '" data-selection="' + (startDecade.year() + 6) + '">' + (startDecade.year() + 1) + ' - ' + (startDecade.year() + 12) + '</span>'; 690 startDecade.add(12, 'y'); 691 } 692 html += '<span></span><span></span><span></span>'; //push the dangling block over, at least this way it's even 693 694 decadesView.find('td').html(html); 695 decadesViewHeader.eq(1).text((startedAt.year() + 1) + '-' + (startDecade.year())); 696 }, 697 698 fillDate = function () { 699 var daysView = widget.find('.datepicker-days'), 700 daysViewHeader = daysView.find('th'), 701 currentDate, 702 html = [], 703 row, 704 clsNames = [], 705 i; 706 707 if (!hasDate()) { 708 return; 709 } 710 711 daysViewHeader.eq(0).find('span').attr('title', options.tooltips.prevMonth); 712 daysViewHeader.eq(1).attr('title', options.tooltips.selectMonth); 713 daysViewHeader.eq(2).find('span').attr('title', options.tooltips.nextMonth); 714 715 daysView.find('.disabled').removeClass('disabled'); 716 daysViewHeader.eq(1).text(viewDate.format(options.dayViewHeaderFormat)); 717 718 if (!isValid(viewDate.clone().subtract(1, 'M'), 'M')) { 719 daysViewHeader.eq(0).addClass('disabled'); 720 } 721 if (!isValid(viewDate.clone().add(1, 'M'), 'M')) { 722 daysViewHeader.eq(2).addClass('disabled'); 723 } 724
725 currentDate = viewDate.clone().startOf('M').startOf('w').startOf('d'); 726 // !! ODOO FIX START !! 727 var now = getMoment(); 728 // !! ODOO FIX END !! 729 730 for (i = 0; i < 42; i++) { //always display 42 days (should show 6 weeks) 731 if (currentDate.weekday() === 0) { 732 row = $('<tr>'); 733 if (options.calendarWeeks) { 734 row.append('<td class="cw">' + currentDate.week() + '</td>'); 735 } 736 html.push(row); 737 } 738 clsNames = ['day']; 739 if (currentDate.isBefore(viewDate, 'M')) { 740 clsNames.push('old'); 741 } 742 if (currentDate.isAfter(viewDate, 'M')) { 743 clsNames.push('new'); 744 } 745 if (currentDate.isSame(date, 'd') && !unset) { 746 clsNames.push('active'); 747 } 748 if (!isValid(currentDate, 'd')) { 749 clsNames.push('disabled'); 750 } 751 // !! ODOO FIX START !! 752 if (currentDate.date() === now.date() && currentDate.month() === now.month() && currentDate.year() === now.year()) { 753 // !! ODOO FIX END !! 754 clsNames.push('today'); 755 } 756 if (currentDate.day() === 0 || currentDate.day() === 6) { 757 clsNames.push('weekend'); 758 } 759 notifyEvent({ 760 type: 'dp.classify', 761 date: currentDate, 762 classNames: clsNames 763 }); 764 row.append('<td data-action="selectDay" data-day="' + currentDate.format('L') + '" class="' + clsNames.join(' ') + '">' + currentDate.date() + '</td>'); 765 currentDate.add(1, 'd'); 766 } 767 768 daysView.find('tbody').empty().append(html); 769 770 updateMonths(); 771 772 updateYears(); 773 774 updateDecades(); 775 }, 776 777 fillHours = function () { 778 var table = widget.find('.timepicker-hours table'), 779 currentHour = viewDate.clone().startOf('d'), 780 html = [], 781 row = $('<tr>'); 782 783 if (viewDate.hour() > 11 && !use24Hours) { 784 currentHour.hour(12); 785 } 786 while (currentHour.isSame(viewDate, 'd') && (use24Hours || (viewDate.hour() < 12 && currentHour.hour() < 12) || viewDate.hour() > 11)) { 787 if (currentHour.hour() % 4 === 0) { 788 row = $('<tr>'); 789 html.push(row); 790 } 791 row.append('<td data-action="selectHour" class="hour' + (!isValid(currentHour, 'h') ? ' disabled' : '') + '">' + currentHour.format(use24Hours ? 'HH' : 'hh') + '</td>'); 792 currentHour.add(1, 'h'); 793 } 794 table.empty().append(html); 795 }, 796 797 fillMinutes = function () { 798 var table = widget.find('.timepicker-minutes table'), 799 currentMinute = viewDate.clone().startOf('h'), 800 html = [], 801 row = $('<tr>'), 802 step = options.stepping === 1 ? 5 : options.stepping; 803 804 while (viewDate.isSame(currentMinute, 'h')) { 805 if (currentMinute.minute() % (step * 4) === 0) { 806 row = $('<tr>'); 807 html.push(row); 808 } 809 row.append('<td data-action="selectMinute" class="minute' + (!isValid(currentMinute, 'm') ? ' disabled' : '') + '">' + currentMinute.format('mm') + '</td>'); 810 currentMinute.add(step, 'm'); 811 } 812 table.empty().append(html); 813 }, 814 815 fillSeconds = function () { 816 var table = widget.find('.timepicker-seconds table'), 817 currentSecond = viewDate.clone().startOf('m'), 818 html = [], 819 row = $('<tr>'); 820 821 while (viewDate.isSame(currentSecond, 'm')) { 822 if (currentSecond.second() % 20 === 0) { 823 row = $('<tr>'); 824 html.push(row); 825 } 826 row.append('<td data-action="selectSecond" class="second' + (!isValid(currentSecond, 's') ? ' disabled' : '') + '">' + currentSecond.format('ss') + '</td>'); 827 currentSecond.add(5, 's'); 828 } 829 830 table.empty().append(html); 831 }, 832 833 fillTime = function () {
834 var toggle, newDate, timeComponents = widget.find('.timepicker span[data-time-component]'); 835 836 if (!use24Hours) { 837 toggle = widget.find('.timepicker [data-action=togglePeriod]'); 838 newDate = date.clone().add((date.hours() >= 12) ? -12 : 12, 'h'); 839 840 toggle.text(date.format('A')); 841 842 if (isValid(newDate, 'h')) { 843 toggle.removeClass('disabled'); 844 } else { 845 toggle.addClass('disabled'); 846 } 847 } 848 timeComponents.filter('[data-time-component=hours]').text(date.format(use24Hours ? 'HH' : 'hh')); 849 timeComponents.filter('[data-time-component=minutes]').text(date.format('mm')); 850 timeComponents.filter('[data-time-component=seconds]').text(date.format('ss')); 851 852 fillHours(); 853 fillMinutes(); 854 fillSeconds(); 855 }, 856 857 update = function () { 858 if (!widget) { 859 return; 860 } 861 fillDate(); 862 fillTime(); 863 }, 864 865 setValue = function (targetMoment) { 866 var oldDate = unset ? null : date; 867 868 // case of calling setValue(null or false) 869 if (!targetMoment) { 870 unset = true; 871 input.val(''); 872 element.data('date', ''); 873 notifyEvent({ 874 type: 'dp.change', 875 date: false, 876 oldDate: oldDate 877 }); 878 update(); 879 return; 880 } 881 882 targetMoment = targetMoment.clone().locale(options.locale); 883 884 if (hasTimeZone()) { 885 targetMoment.tz(options.timeZone); 886 } 887 888 if (options.stepping !== 1) { 889 targetMoment.minutes((Math.round(targetMoment.minutes() / options.stepping) * options.stepping)).seconds(0); 890 891 while (options.minDate && targetMoment.isBefore(options.minDate)) { 892 targetMoment.add(options.stepping, 'minutes'); 893 } 894 } 895 896 if (isValid(targetMoment)) { 897 date = targetMoment; 898 //viewDate = date.clone(); // TODO this doesn't work right on first use 899 input.val(date.format(actualFormat)); 900 element.data('date', date.format(actualFormat)); 901 unset = false; 902 update(); 903 notifyEvent({ 904 type: 'dp.change', 905 date: date.clone(), 906 oldDate: oldDate 907 }); 908 } else { 909 if (!options.keepInvalid) { 910 input.val(unset ? '' : date.format(actualFormat)); 911 } else { 912 notifyEvent({ 913 type: 'dp.change', 914 date: targetMoment, 915 oldDate: oldDate 916 }); 917 } 918 notifyEvent({ 919 type: 'dp.error', 920 date: targetMoment, 921 oldDate: oldDate 922 }); 923 } 924 }, 925 926 /** 927 * Hides the widget. Possibly will emit dp.hide 928 */ 929 hide = function () { 930 var transitioning = false; 931 if (!widget) { 932 return picker; 933 } 934 // Ignore event if in the middle of a picker transition 935 widget.find('.collapse').each(function () { 936 var collapseData = $(this).data('collapse'); 937 if (collapseData && collapseData.transitioning) { 938 transitioning = true; 939 return false;
940 } 941 return true; 942 }); 943 if (transitioning) { 944 return picker; 945 } 946 if (component && component.hasClass('btn')) { 947 component.toggleClass('active'); 948 } 949 widget.hide(); 950 951 $(window).off('resize', place); 952 widget.off('click', '[data-action]'); 953 widget.off('mousedown', false); 954 955 widget.remove(); 956 widget = false; 957 958 notifyEvent({ 959 type: 'dp.hide', 960 date: date.clone() 961 }); 962 963 input.blur(); 964 965 viewDate = date.clone(); 966 967 return picker; 968 }, 969 970 clear = function () { 971 setValue(null); 972 }, 973 974 parseInputDate = function (inputDate) { 975 if (options.parseInputDate === undefined) { 976 if (!moment.isMoment(inputDate)) { 977 inputDate = getMoment(inputDate); 978 } 979 } else { 980 inputDate = options.parseInputDate(inputDate); 981 } 982 //inputDate.locale(options.locale); 983 return inputDate; 984 }, 985 986 /******************************************************************************** 987 * 988 * Widget UI interaction functions 989 * 990 ********************************************************************************/ 991 actions = { 992 next: function () { 993 var navFnc = datePickerModes[currentViewMode].navFnc; 994 viewDate.add(datePickerModes[currentViewMode].navStep, navFnc); 995 fillDate(); 996 viewUpdate(navFnc); 997 }, 998 999 previous: function () { 1000 var navFnc = datePickerModes[currentViewMode].navFnc; 1001 viewDate.subtract(datePickerModes[currentViewMode].navStep, navFnc); 1002 fillDate(); 1003 viewUpdate(navFnc); 1004 }, 1005 1006 pickerSwitch: function () { 1007 showMode(1); 1008 }, 1009 1010 selectMonth: function (e) { 1011 var month = $(e.target).closest('tbody').find('span').index($(e.target)); 1012 viewDate.month(month); 1013 if (currentViewMode === minViewModeNumber) { 1014 setValue(date.clone().year(viewDate.year()).month(viewDate.month())); 1015 if (!options.inline) { 1016 hide(); 1017 } 1018 } else { 1019 showMode(-1); 1020 fillDate(); 1021 } 1022 viewUpdate('M'); 1023 }, 1024 1025 selectYear: function (e) { 1026 var year = parseInt($(e.target).text(), 10) || 0; 1027 viewDate.year(year); 1028 if (currentViewMode === minViewModeNumber) { 1029 setValue(date.clone().year(viewDate.year())); 1030 if (!options.inline) { 1031 hide(); 1032 } 1033 } else { 1034 showMode(-1); 1035 fillDate(); 1036 } 1037 viewUpdate('YYYY'); 1038 }, 1039 1040 selectDecade: function (e) { 1041 var year = parseInt($(e.target).data('selection'), 10) || 0; 1042 viewDate.year(year); 1043 if (currentViewMode === minViewModeNumber) { 1044 setValue(date.clone().year(viewDate.year())); 1045 if (!options.inline) { 1046 hide(); 1047 } 1048 } else { 1049 showMode(-1); 1050 fillDate(); 1051 } 1052 viewUpdate('YYYY'); 1053 }, 1054 1055 selectDay: function (e) { 1056 var day = viewDate.clone(); 1057 if ($(e.target).is('.old')) { 1058 day.subtract(1, 'M'); 1059 } 1060 if ($(e.target).is('.new')) { 1061 day.add(1, 'M'); 1062 } 1063 setValue(day.date(parseInt($(e.target).text(), 10))); 1064 if (!hasTime() && !options.keepOpen && !options.inline) { 1065 hide(); 1066 } 1067 }, 1068 1069 incrementHours: function () { 1070 var newDate = date.clone().add(1, 'h'); 1071 if (isValid(newDate, 'h')) { 1072 setValue(newDate); 1073 } 1074 }, 1075 1076 incrementMinutes: function () { 1077 var newDate = date.clone().add(options.stepping, 'm'); 1078 if (isValid(newDate, 'm')) { 1079 setValue(newDate); 1080 } 1081 }, 1082 1083 incrementSeconds: function () { 1084 var newDate = date.clone().add(1, 's'); 1085 if (isValid(newDate, 's')) { 1086 setValue(newDate); 1087 } 1088 }, 1089 1090 decrementHours: function () { 1091 var newDate = date.clone().subtract(1, 'h'); 1092 if (isValid(newDate, 'h')) { 1093 setValue(newDate); 1094 } 1095 }, 1096 1097 decrementMinutes: function () { 1098 var newDate = date.clone().subtract(options.stepping, 'm'); 1099 if (isValid(newDate, 'm')) { 1100 setValue(newDate); 1101 } 1102 }, 1103 1104 decrementSeconds: function () { 1105 var newDate = date.clone().subtract(1, 's'); 1106 if (isValid(newDate, 's')) { 1107 setValue(newDate); 1108 } 1109 }, 1110 1111 togglePeriod: function () { 1112 setValue(date.clone().add((date.hours() >= 12) ? -12 : 12, 'h')); 1113 }, 1114 1115 togglePicker: function (e) { 1116 var $this = $(e.target), 1117 $parent = $this.closest('ul'), 1118 expanded = $parent.find('.in'), 1119 closed = $parent.find('.collapse:not(.in)'), 1120 collapseData; 1121 1122 if (expanded && expanded.length) { 1123 collapseData = expanded.data('collapse'); 1124 if (collapseData && collapseData.transitioning) { 1125 return; 1126 } 1127 if (expanded.collapse) { // if collapse plugin is available through bootstrap.js then use it 1128 expanded.collapse('hide'); 1129 closed.collapse('show'); 1130 } else { // otherwise just toggle in class on the two views 1131 expanded.removeClass('in'); 1132 closed.addClass('in'); 1133 } 1134 if ($this.is('span')) { 1135 $this.toggleClass(options.icons.time + ' ' + options.icons.date); 1136 } else { 1137 $this.find('span').toggleClass(options.icons.time + ' ' + options.icons.date); 1138 } 1139 1140 // NOTE: uncomment if toggled state will be restored in show() 1141 //if (component) { 1142 // component.find('span').toggleClass(options.icons.time + ' ' + options.icons.date); 1143 //} 1144 } 1145 }, 1146 1147 showPicker: function () { 1148 widget.find('.timepicker > div:not(.timepicker-picker)').hide(); 1149 widget.find('.timepicker .timepicker-picker').show(); 1150 }, 1151 1152 showHours: function () { 1153 widget.find('.timepicker .timepicker-picker').hide(); 1154 widget.find('.timepicker .timepicker-hours').show(); 1155 }, 1156 1157 showMinutes: function () { 1158 widget.find('.timepicker .timepicker-picker').hide(); 1159 widget.find('.timepicker .timepicker-minutes').show(); 1160 }, 1161 1162 showSeconds: function () { 1163 widget.find('.timepicker .timepicker-picker').hide(); 1164 widget.find('.timepicker .timepicker-seconds').show(); 1165 }, 1166 1167 selectHour: function (e) { 1168 var hour = parseInt($(e.target).text(), 10); 1169 1170 if (!use24Hours) { 1171 if (date.hours() >= 12) { 1172 if (hour !== 12) { 1173 hour += 12; 1174 } 1175 } else { 1176 if (hour === 12) { 1177 hour = 0; 1178 } 1179 } 1180 } 1181 setValue(date.clone().hours(hour)); 1182 actions.showPicker.call(picker); 1183 }, 1184 1185 selectMinute: function (e) { 1186 setValue(date.clone().minutes(parseInt($(e.target).text(), 10))); 1187 actions.showPicker.call(picker); 1188 }, 1189 1190 selectSecond: function (e) { 1191 setValue(date.clone().seconds(parseInt($(e.target).text(), 10))); 1192 actions.showPicker.call(picker); 1193 }, 1194 1195 clear: clear, 1196 1197 today: function () { 1198 var todaysDate = getMoment(); 1199 if (isValid(todaysDate, 'd')) { 1200 setValue(todaysDate); 1201 } 1202 }, 1203 1204 close: hide 1205 }, 1206 1207 doAction = function (e) { 1208 if ($(e.currentTarget).is('.disabled')) { 1209 return false;
1210 } 1211 actions[$(e.currentTarget).data('action')].apply(picker, arguments); 1212 return false; 1213 }, 1214 1215 /** 1216 * Shows the widget. Possibly will emit dp.show and dp.change 1217 */ 1218 show = function () { 1219 var currentMoment, 1220 useCurrentGranularity = { 1221 'year': function (m) { 1222 return m.month(0).date(1).hours(0).seconds(0).minutes(0); 1223 }, 1224 'month': function (m) { 1225 return m.date(1).hours(0).seconds(0).minutes(0); 1226 }, 1227 'day': function (m) { 1228 return m.hours(0).seconds(0).minutes(0); 1229 }, 1230 'hour': function (m) { 1231 return m.seconds(0).minutes(0); 1232 }, 1233 'minute': function (m) { 1234 return m.seconds(0); 1235 } 1236 }; 1237 1238 if (input.prop('disabled') || (!options.ignoreReadonly && input.prop('readonly')) || widget) { 1239 return picker; 1240 } 1241 if (input.val() !== undefined && input.val().trim().length !== 0) { 1242 setValue(parseInputDate(input.val().trim())); 1243 } else if (unset && options.useCurrent && (options.inline || (input.is('input') && input.val().trim().length === 0))) { 1244 currentMoment = getMoment(); 1245 if (typeof options.useCurrent === 'string') { 1246 currentMoment = useCurrentGranularity[options.useCurrent](currentMoment); 1247 } 1248 setValue(currentMoment); 1249 } 1250 widget = getTemplate(); 1251 1252 fillDow(); 1253 fillMonths(); 1254 1255 widget.find('.timepicker-hours').hide(); 1256 widget.find('.timepicker-minutes').hide(); 1257 widget.find('.timepicker-seconds').hide(); 1258 1259 update(); 1260 showMode(); 1261 1262 $(window).on('resize', place); 1263 widget.on('click', '[data-action]', doAction); // this handles clicks on the widget 1264 widget.on('mousedown', false); 1265 1266 if (component && component.hasClass('btn')) { 1267 component.toggleClass('active'); 1268 } 1269 place(); 1270 widget.show(); 1271 if (options.focusOnShow && !input.is(':focus')) { 1272 input.focus(); 1273 } 1274 1275 notifyEvent({ 1276 type: 'dp.show' 1277 }); 1278 return picker; 1279 }, 1280 1281 /** 1282 * Shows or hides the widget 1283 */ 1284 toggle = function () { 1285 return (widget ? hide() : show()); 1286 }, 1287 1288 keydown = function (e) { 1289 var handler = null, 1290 index, 1291 index2, 1292 pressedKeys = [], 1293 pressedModifiers = {}, 1294 currentKey = e.which, 1295 keyBindKeys, 1296 allModifiersPressed, 1297 pressed = 'p'; 1298 1299 keyState[currentKey] = pressed; 1300 1301 for (index in keyState) { 1302 if (keyState.hasOwnProperty(index) && keyState[index] === pressed) { 1303 pressedKeys.push(index); 1304 if (parseInt(index, 10) !== currentKey) { 1305 pressedModifiers[index] = true; 1306 } 1307 } 1308 } 1309 1310 for (index in options.keyBinds) { 1311 if (options.keyBinds.hasOwnProperty(index) && typeof (options.keyBinds[index]) === 'function') { 1312 keyBindKeys = index.split(' '); 1313 if (keyBindKeys.length === pressedKeys.length && keyMap[currentKey] === keyBindKeys[keyBindKeys.length - 1]) { 1314 allModifiersPressed = true; 1315 for (index2 = keyBindKeys.length - 2; index2 >= 0; index2--) { 1316 if (!(keyMap[keyBindKeys[index2]] in pressedModifiers)) { 1317 allModifiersPressed = false;
1318 break; 1319 } 1320 } 1321 if (allModifiersPressed) { 1322 handler = options.keyBinds[index]; 1323 break; 1324 } 1325 } 1326 } 1327 } 1328 1329 if (handler) { 1330 handler.call(picker, widget); 1331 e.stopPropagation(); 1332 e.preventDefault(); 1333 } 1334 }, 1335 1336 keyup = function (e) { 1337 keyState[e.which] = 'r'; 1338 e.stopPropagation(); 1339 e.preventDefault(); 1340 }, 1341 1342 change = function (e) { 1343 var val = $(e.target).val().trim(), 1344 parsedDate = val ? parseInputDate(val) : null; 1345 setValue(parsedDate); 1346 e.stopImmediatePropagation(); 1347 return false; 1348 }, 1349 1350 attachDatePickerElementEvents = function () { 1351 input.on({ 1352 'change': change, 1353 'blur': options.debug ? '' : hide, 1354 'keydown': keydown, 1355 'keyup': keyup, 1356 'focus': options.allowInputToggle ? show : '' 1357 }); 1358 1359 if (element.is('input')) { 1360 input.on({ 1361 'focus': show 1362 }); 1363 } else if (component) { 1364 component.on('click', toggle); 1365 component.on('mousedown', false); 1366 } 1367 }, 1368 1369 detachDatePickerElementEvents = function () { 1370 input.off({ 1371 'change': change, 1372 'blur': blur, 1373 'keydown': keydown, 1374 'keyup': keyup, 1375 'focus': options.allowInputToggle ? hide : '' 1376 }); 1377 1378 if (element.is('input')) { 1379 input.off({ 1380 'focus': show 1381 }); 1382 } else if (component) { 1383 component.off('click', toggle); 1384 component.off('mousedown', false); 1385 } 1386 }, 1387 1388 indexGivenDates = function (givenDatesArray) { 1389 // Store given enabledDates and disabledDates as keys. 1390 // This way we can check their existence in O(1) time instead of looping through whole array. 1391 // (for example: options.enabledDates['2014-02-27'] === true) 1392 var givenDatesIndexed = {}; 1393 $.each(givenDatesArray, function () { 1394 var dDate = parseInputDate(this); 1395 if (dDate.isValid()) { 1396 givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true; 1397 } 1398 }); 1399 return (Object.keys(givenDatesIndexed).length) ? givenDatesIndexed : false; 1400 }, 1401 1402 indexGivenHours = function (givenHoursArray) { 1403 // Store given enabledHours and disabledHours as keys. 1404 // This way we can check their existence in O(1) time instead of looping through whole array. 1405 // (for example: options.enabledHours['2014-02-27'] === true) 1406 var givenHoursIndexed = {}; 1407 $.each(givenHoursArray, function () { 1408 givenHoursIndexed[this] = true; 1409 }); 1410 return (Object.keys(givenHoursIndexed).length) ? givenHoursIndexed : false; 1411 }, 1412 1413 initFormatting = function () { 1414 var format = options.format || 'L LT'; 1415 1416 actualFormat = format.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput) { 1417 var newinput = date.localeData().longDateFormat(formatInput) || formatInput; 1418 return newinput.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput2) { //temp fix for #740 1419 return date.localeData().longDateFormat(formatInput2) || formatInput2; 1420 }); 1421 }); 1422 1423 1424 parseFormats = options.extraFormats ? options.extraFormats.slice() : []; 1425 if (parseFormats.indexOf(format) < 0 && parseFormats.indexOf(actualFormat) < 0) { 1426 parseFormats.push(actualFormat); 1427 } 1428 1429 use24Hours = (actualFormat.toLowerCase().indexOf('a') < 1 && actualFormat.replace(/\[.*?\]/g, '').indexOf('h') < 1); 1430 1431 if (isEnabled('y')) { 1432 minViewModeNumber = 2; 1433 } 1434 if (isEnabled('M')) { 1435 minViewModeNumber = 1; 1436 } 1437 if (isEnabled('d')) { 1438 minViewModeNumber = 0; 1439 } 1440
1441 currentViewMode = Math.max(minViewModeNumber, currentViewMode); 1442 1443 if (!unset) { 1444 setValue(date); 1445 } 1446 }; 1447 1448 /******************************************************************************** 1449 * 1450 * Public API functions 1451 * ===================== 1452 * 1453 * Important: Do not expose direct references to private objects or the options 1454 * object to the outer world. Always return a clone when returning values or make 1455 * a clone when setting a private variable. 1456 * 1457 ********************************************************************************/ 1458 picker.destroy = function () { 1459 ///<summary>Destroys the widget and removes all attached event listeners</summary> 1460 hide(); 1461 detachDatePickerElementEvents(); 1462 element.removeData('DateTimePicker'); 1463 element.removeData('date'); 1464 }; 1465 1466 picker.toggle = toggle; 1467 1468 picker.show = show; 1469 1470 picker.hide = hide; 1471 1472 picker.disable = function () { 1473 ///<summary>Disables the input element, the component is attached to, by adding a disabled="true" attribute to it. 1474 ///If the widget was visible before that call it is hidden. Possibly emits dp.hide</summary> 1475 hide(); 1476 if (component && component.hasClass('btn')) { 1477 component.addClass('disabled'); 1478 } 1479 input.prop('disabled', true); 1480 return picker; 1481 }; 1482 1483 picker.enable = function () { 1484 ///<summary>Enables the input element, the component is attached to, by removing disabled attribute from it.</summary> 1485 if (component && component.hasClass('btn')) { 1486 component.removeClass('disabled'); 1487 } 1488 input.prop('disabled', false); 1489 return picker; 1490 }; 1491 1492 picker.ignoreReadonly = function (ignoreReadonly) { 1493 if (arguments.length === 0) { 1494 return options.ignoreReadonly; 1495 } 1496 if (typeof ignoreReadonly !== 'boolean') { 1497 throw new TypeError('ignoreReadonly () expects a boolean parameter'); 1498 } 1499 options.ignoreReadonly = ignoreReadonly; 1500 return picker; 1501 }; 1502 1503 picker.options = function (newOptions) { 1504 if (arguments.length === 0) { 1505 return $.extend(true, {}, options); 1506 } 1507 1508 if (!(newOptions instanceof Object)) { 1509 throw new TypeError('options() options parameter should be an object'); 1510 } 1511 $.extend(true, options, newOptions); 1512 $.each(options, function (key, value) { 1513 if (picker[key] !== undefined) { 1514 picker[key](value); 1515 } else { 1516 throw new TypeError('option ' + key + ' is not recognized!'); 1517 } 1518 }); 1519 return picker; 1520 }; 1521 1522 picker.date = function (newDate) { 1523 ///<signature helpKeyword="$.fn.datetimepicker.date"> 1524 ///<summary>Returns the component's model current date, a moment object or null if not set.</summary> 1525 ///<returns type="Moment">date.clone()</returns> 1526 ///</signature> 1527 ///<signature> 1528 ///<summary>Sets the components model current moment to it. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration.</summary> 1529 ///<param name="newDate" locid="$.fn.datetimepicker.date_p:newDate">Takes string, Date, moment, null parameter.</param> 1530 ///</signature> 1531 if (arguments.length === 0) { 1532 if (unset) { 1533 return null; 1534 } 1535 return date.clone(); 1536 } 1537 1538 if (newDate !== null && typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) { 1539 throw new TypeError('date() parameter must be one of [null, string, moment or Date]'); 1540 } 1541 1542 setValue(newDate === null ? null : parseInputDate(newDate)); 1543 return picker; 1544 }; 1545 1546 picker.format = function (newFormat) { 1547 ///<summary>test su</summary> 1548 ///<param name="newFormat">info about para</param> 1549 ///<returns type="string|boolean">returns foo</returns> 1550 if (arguments.length === 0) { 1551 return options.format; 1552 } 1553 1554 if ((typeof newFormat !== 'string') && ((typeof newFormat !== 'boolean') || (newFormat !== false))) { 1555 throw new TypeError('format() expects a string or boolean:false parameter ' + newFormat); 1556 } 1557 1558 options.format = newFormat; 1559 if (actualFormat) { 1560 initFormatting(); // reinit formatting 1561 } 1562 return picker; 1563 }; 1564 1565 picker.timeZone = function (newZone) { 1566 if (arguments.length === 0) { 1567 return options.timeZone; 1568 } 1569 1570 if (typeof newZone !== 'string') { 1571 throw new TypeError('newZone() expects a string parameter'); 1572 } 1573 1574 options.timeZone = newZone; 1575 1576 return picker; 1577 }; 1578 1579 picker.dayViewHeaderFormat = function (newFormat) { 1580 if (arguments.length === 0) { 1581 return options.dayViewHeaderFormat; 1582 } 1583 1584 if (typeof newFormat !== 'string') { 1585 throw new TypeError('dayViewHeaderFormat() expects a string parameter'); 1586 } 1587 1588 options.dayViewHeaderFormat = newFormat; 1589 return picker; 1590 }; 1591 1592 picker.extraFormats = function (formats) { 1593 if (arguments.length === 0) { 1594 return options.extraFormats; 1595 } 1596 1597 if (formats !== false && !(formats instanceof Array)) { 1598 throw new TypeError('extraFormats() expects an array or false parameter'); 1599 } 1600 1601 options.extraFormats = formats; 1602 if (parseFormats) { 1603 initFormatting(); // reinit formatting 1604 } 1605 return picker; 1606 }; 1607 1608 picker.disabledDates = function (dates) { 1609 ///<signature helpKeyword="$.fn.datetimepicker.disabledDates"> 1610 ///<summary>Returns an array with the currently set disabled dates on the component.</summary> 1611 ///<returns type="array">options.disabledDates</returns> 1612 ///</signature> 1613 ///<signature> 1614 ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of 1615 ///options.enabledDates if such exist.</summary> 1616 ///<param name="dates" locid="$.fn.datetimepicker.disabledDates_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.
1616</param> 1617 ///</signature> 1618 if (arguments.length === 0) { 1619 return (options.disabledDates ? $.extend({}, options.disabledDates) : options.disabledDates); 1620 } 1621 1622 if (!dates) { 1623 options.disabledDates = false; 1624 update(); 1625 return picker; 1626 } 1627 if (!(dates instanceof Array)) { 1628 throw new TypeError('disabledDates() expects an array parameter'); 1629 } 1630 options.disabledDates = indexGivenDates(dates); 1631 options.enabledDates = false; 1632 update(); 1633 return picker; 1634 }; 1635 1636 picker.enabledDates = function (dates) { 1637 ///<signature helpKeyword="$.fn.datetimepicker.enabledDates"> 1638 ///<summary>Returns an array with the currently set enabled dates on the component.</summary> 1639 ///<returns type="array">options.enabledDates</returns> 1640 ///</signature> 1641 ///<signature> 1642 ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledDates if such exist.</summary> 1643 ///<param name="dates" locid="$.fn.datetimepicker.enabledDates_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.</param> 1644 ///</signature> 1645 if (arguments.length === 0) { 1646 return (options.enabledDates ? $.extend({}, options.enabledDates) : options.enabledDates); 1647 } 1648 1649 if (!dates) { 1650 options.enabledDates = false; 1651 update(); 1652 return picker; 1653 } 1654 if (!(dates instanceof Array)) { 1655 throw new TypeError('enabledDates() expects an array parameter'); 1656 } 1657 options.enabledDates = indexGivenDates(dates); 1658 options.disabledDates = false; 1659 update(); 1660 return picker; 1661 }; 1662 1663 picker.daysOfWeekDisabled = function (daysOfWeekDisabled) { 1664 if (arguments.length === 0) { 1665 return options.daysOfWeekDisabled.splice(0); 1666 } 1667 1668 if ((typeof daysOfWeekDisabled === 'boolean') && !daysOfWeekDisabled) { 1669 options.daysOfWeekDisabled = false; 1670 update(); 1671 return picker; 1672 } 1673 1674 if (!(daysOfWeekDisabled instanceof Array)) { 1675 throw new TypeError('daysOfWeekDisabled() expects an array parameter'); 1676 } 1677 options.daysOfWeekDisabled = daysOfWeekDisabled.reduce(function (previousValue, currentValue) { 1678 currentValue = parseInt(currentValue, 10); 1679 if (currentValue > 6 || currentValue < 0 || isNaN(currentValue)) { 1680 return previousValue; 1681 } 1682 if (previousValue.indexOf(currentValue) === -1) { 1683 previousValue.push(currentValue); 1684 } 1685 return previousValue; 1686 }, []).sort(); 1687 if (options.useCurrent && !options.keepInvalid) { 1688 var tries = 0; 1689 while (!isValid(date, 'd')) { 1690 date.add(1, 'd'); 1691 if (tries === 31) { 1692 throw 'Tried 31 times to find a valid date'; 1693 } 1694 tries++; 1695 } 1696 setValue(date); 1697 } 1698 update(); 1699 return picker; 1700 }; 1701 1702 picker.maxDate = function (maxDate) { 1703 if (arguments.length === 0) { 1704 return options.maxDate ? options.maxDate.clone() : options.maxDate; 1705 } 1706 1707 if ((typeof maxDate === 'boolean') && maxDate === false) { 1708 options.maxDate = false; 1709 update(); 1710 return picker; 1711 } 1712 1713 if (typeof maxDate === 'string') { 1714 if (maxDate === 'now' || maxDate === 'moment') { 1715 maxDate = getMoment(); 1716 } 1717 } 1718 1719 var parsedDate = parseInputDate(maxDate); 1720 1721 if (!parsedDate.isValid()) { 1722 throw new TypeError('maxDate() Could not parse date parameter: ' + maxDate); 1723 } 1724 if (options.minDate && parsedDate.isBefore(options.minDate)) { 1725 throw new TypeError('maxDate() date parameter is before options.minDate: ' + parsedDate.format(actualFormat)); 1726 } 1727 options.maxDate = parsedDate; 1728 if (options.useCurrent && !options.keepInvalid && date.isAfter(maxDate)) { 1729 setValue(options.maxDate); 1730 } 1731 if (viewDate.isAfter(parsedDate)) { 1732 viewDate = parsedDate.clone().subtract(options.stepping, 'm'); 1733 } 1734 update(); 1735 return picker; 1736 }; 1737 1738 picker.minDate = function (minDate) { 1739 if (arguments.length === 0) { 1740 return options.minDate ? options.minDate.clone() : options.minDate; 1741 } 1742 1743 if ((typeof minDate === 'boolean') && minDate === false) { 1744 options.minDate = false;
1745 update(); 1746 return picker; 1747 } 1748 1749 if (typeof minDate === 'string') { 1750 if (minDate === 'now' || minDate === 'moment') { 1751 minDate = getMoment(); 1752 } 1753 } 1754 1755 var parsedDate = parseInputDate(minDate); 1756 1757 if (!parsedDate.isValid()) { 1758 throw new TypeError('minDate() Could not parse date parameter: ' + minDate); 1759 } 1760 if (options.maxDate && parsedDate.isAfter(options.maxDate)) { 1761 throw new TypeError('minDate() date parameter is after options.maxDate: ' + parsedDate.format(actualFormat)); 1762 } 1763 options.minDate = parsedDate; 1764 if (options.useCurrent && !options.keepInvalid && date.isBefore(minDate)) { 1765 setValue(options.minDate); 1766 } 1767 if (viewDate.isBefore(parsedDate)) { 1768 viewDate = parsedDate.clone().add(options.stepping, 'm'); 1769 } 1770 update(); 1771 return picker; 1772 }; 1773 1774 picker.defaultDate = function (defaultDate) { 1775 ///<signature helpKeyword="$.fn.datetimepicker.defaultDate"> 1776 ///<summary>Returns a moment with the options.defaultDate option configuration or false if not set</summary> 1777 ///<returns type="Moment">date.clone()</returns> 1778 ///</signature> 1779 ///<signature> 1780 ///<summary>Will set the picker's inital date. If a boolean:false value is passed the options.defaultDate parameter is cleared.</summary> 1781 ///<param name="defaultDate" locid="$.fn.datetimepicker.defaultDate_p:defaultDate">Takes a string, Date, moment, boolean:false</param> 1782 ///</signature> 1783 if (arguments.length === 0) { 1784 return options.defaultDate ? options.defaultDate.clone() : options.defaultDate; 1785 } 1786 if (!defaultDate) { 1787 options.defaultDate = false; 1788 return picker; 1789 } 1790 1791 if (typeof defaultDate === 'string') { 1792 if (defaultDate === 'now' || defaultDate === 'moment') { 1793 defaultDate = getMoment(); 1794 } else { 1795 defaultDate = getMoment(defaultDate); 1796 } 1797 } 1798 1799 var parsedDate = parseInputDate(defaultDate); 1800 if (!parsedDate.isValid()) { 1801 throw new TypeError('defaultDate() Could not parse date parameter: ' + defaultDate); 1802 } 1803 if (!isValid(parsedDate)) { 1804 throw new TypeError('defaultDate() date passed is invalid according to component setup validations'); 1805 } 1806 1807 options.defaultDate = parsedDate; 1808 1809 if ((options.defaultDate && options.inline) || input.val().trim() === '') { 1810 setValue(options.defaultDate); 1811 } 1812 return picker; 1813 }; 1814 1815 picker.locale = function (locale) { 1816 if (arguments.length === 0) { 1817 return options.locale; 1818 } 1819 1820 if (!moment.localeData(locale)) { 1821 throw new TypeError('locale() locale ' + locale + ' is not loaded from moment locales!'); 1822 } 1823 1824 options.locale = locale; 1825 date.locale(options.locale); 1826 viewDate.locale(options.locale); 1827 1828 if (actualFormat) { 1829 initFormatting(); // reinit formatting 1830 } 1831 if (widget) { 1832 hide(); 1833 show(); 1834 } 1835 return picker; 1836 }; 1837 1838 picker.stepping = function (stepping) { 1839 if (arguments.length === 0) { 1840 return options.stepping; 1841 } 1842 1843 stepping = parseInt(stepping, 10); 1844 if (isNaN(stepping) || stepping < 1) { 1845 stepping = 1; 1846 } 1847 options.stepping = stepping; 1848 return picker; 1849 }; 1850 1851 picker.useCurrent = function (useCurrent) { 1852 var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute']; 1853 if (arguments.length === 0) { 1854 return options.useCurrent; 1855 } 1856 1857 if ((typeof useCurrent !== 'boolean') && (typeof useCurrent !== 'string')) { 1858 throw new TypeError('useCurrent() expects a boolean or string parameter'); 1859 } 1860 if (typeof useCurrent === 'string' && useCurrentOptions.indexOf(useCurrent.toLowerCase()) === -1) { 1861 throw new TypeError('useCurrent() expects a string parameter of ' + useCurrentOptions.join(', ')); 1862 } 1863 options.useCurrent = useCurrent; 1864 return picker; 1865 }; 1866 1867 picker.collapse = function (collapse) { 1868 if (arguments.length === 0) { 1869 return options.collapse; 1870 } 1871 1872 if (typeof collapse !== 'boolean') { 1873 throw new TypeError('collapse() expects a boolean parameter'); 1874 } 1875 if (options.collapse === collapse) { 1876 return picker; 1877 } 1878 options.collapse = collapse; 1879 if (widget) { 1880 hide(); 1881 show(); 1882 } 1883 return picker; 1884 }; 1885 1886 picker.icons = function (icons) { 1887 if (arguments.length === 0) { 1888 return $.extend({}, options.icons); 1889 } 1890 1891 if (!(icons instanceof Object)) { 1892 throw new TypeError('icons() expects parameter to be an Object'); 1893 } 1894 $.extend(options.icons, icons); 1895 if (widget) { 1896 hide(); 1897 show(); 1898 } 1899 return picker; 1900 }; 1901 1902 picker.tooltips = function (tooltips) { 1903 if (arguments.length === 0) { 1904 return $.extend({}, options.tooltips); 1905 } 1906 1907 if (!(tooltips instanceof Object)) { 1908 throw new TypeError('tooltips() expects parameter to be an Object'); 1909 } 1910 $.extend(options.tooltips, tooltips); 1911 if (widget) { 1912 hide(); 1913 show(); 1914 } 1915 return picker; 1916 }; 1917 1918 picker.useStrict = function (useStrict) { 1919 if (arguments.length === 0) { 1920 return options.useStrict; 1921 } 1922 1923 if (typeof useStrict !== 'boolean') { 1924 throw new TypeError('useStrict() expects a boolean parameter'); 1925 } 1926 options.useStrict = useStrict; 1927 return picker; 1928 }; 1929 1930 picker.sideBySide = function (sideBySide) { 1931 if (arguments.length === 0) { 1932 return options.sideBySide; 1933 } 1934 1935 if (typeof sideBySide !== 'boolean') { 1936 throw new TypeError('sideBySide() expects a boolean parameter'); 1937 } 1938 options.sideBySide = sideBySide; 1939 if (widget) { 1940 hide(); 1941 show(); 1942 } 1943 return picker; 1944 }; 1945 1946 picker.viewMode = function (viewMode) { 1947 if (arguments.length === 0) { 1948 return options.viewMode; 1949 } 1950 1951 if (typeof viewMode !== 'string') { 1952 throw new TypeError('viewMode() expects a string parameter'); 1953 } 1954 1955 if (viewModes.indexOf(viewMode) === -1) { 1956 throw new TypeError('viewMode() parameter must be one of (' + viewModes.join(', ') + ') value'); 1957 } 1958
1959 options.viewMode = viewMode; 1960 currentViewMode = Math.max(viewModes.indexOf(viewMode), minViewModeNumber); 1961 1962 showMode(); 1963 return picker; 1964 }; 1965 1966 picker.toolbarPlacement = function (toolbarPlacement) { 1967 if (arguments.length === 0) { 1968 return options.toolbarPlacement; 1969 } 1970 1971 if (typeof toolbarPlacement !== 'string') { 1972 throw new TypeError('toolbarPlacement() expects a string parameter'); 1973 } 1974 if (toolbarPlacements.indexOf(toolbarPlacement) === -1) { 1975 throw new TypeError('toolbarPlacement() parameter must be one of (' + toolbarPlacements.join(', ') + ') value'); 1976 } 1977 options.toolbarPlacement = toolbarPlacement; 1978 1979 if (widget) { 1980 hide(); 1981 show(); 1982 } 1983 return picker; 1984 }; 1985 1986 picker.widgetPositioning = function (widgetPositioning) { 1987 if (arguments.length === 0) { 1988 return $.extend({}, options.widgetPositioning); 1989 } 1990 1991 if (({}).toString.call(widgetPositioning) !== '[object Object]') { 1992 throw new TypeError('widgetPositioning() expects an object variable'); 1993 } 1994 if (widgetPositioning.horizontal) { 1995 if (typeof widgetPositioning.horizontal !== 'string') { 1996 throw new TypeError('widgetPositioning() horizontal variable must be a string'); 1997 } 1998 widgetPositioning.horizontal = widgetPositioning.horizontal.toLowerCase(); 1999 if (horizontalModes.indexOf(widgetPositioning.horizontal) === -1) { 2000 throw new TypeError('widgetPositioning() expects horizontal parameter to be one of (' + horizontalModes.join(', ') + ')'); 2001 } 2002 options.widgetPositioning.horizontal = widgetPositioning.horizontal; 2003 } 2004 if (widgetPositioning.vertical) { 2005 if (typeof widgetPositioning.vertical !== 'string') { 2006 throw new TypeError('widgetPositioning() vertical variable must be a string'); 2007 } 2008 widgetPositioning.vertical = widgetPositioning.vertical.toLowerCase(); 2009 if (verticalModes.indexOf(widgetPositioning.vertical) === -1) { 2010 throw new TypeError('widgetPositioning() expects vertical parameter to be one of (' + verticalModes.join(', ') + ')'); 2011 } 2012 options.widgetPositioning.vertical = widgetPositioning.vertical; 2013 } 2014 update(); 2015 return picker; 2016 }; 2017 2018 picker.calendarWeeks = function (calendarWeeks) { 2019 if (arguments.length === 0) { 2020 return options.calendarWeeks; 2021 } 2022 2023 if (typeof calendarWeeks !== 'boolean') { 2024 throw new TypeError('calendarWeeks() expects parameter to be a boolean value'); 2025 } 2026 2027 options.calendarWeeks = calendarWeeks; 2028 update(); 2029 return picker; 2030 }; 2031 2032 picker.showTodayButton = function (showTodayButton) { 2033 if (arguments.length === 0) { 2034 return options.showTodayButton; 2035 } 2036 2037 if (typeof showTodayButton !== 'boolean') { 2038 throw new TypeError('showTodayButton() expects a boolean parameter'); 2039 } 2040 2041 options.showTodayButton = showTodayButton; 2042 if (widget) { 2043 hide(); 2044 show(); 2045 } 2046 return picker; 2047 }; 2048 2049 picker.showClear = function (showClear) { 2050 if (arguments.length === 0) { 2051 return options.showClear; 2052 } 2053 2054 if (typeof showClear !== 'boolean') { 2055 throw new TypeError('showClear() expects a boolean parameter'); 2056 } 2057 2058 options.showClear = showClear; 2059 if (widget) { 2060 hide(); 2061 show(); 2062 } 2063 return picker; 2064 }; 2065 2066 picker.widgetParent = function (widgetParent) { 2067 if (arguments.length === 0) { 2068 return options.widgetParent; 2069 } 2070 2071 if (typeof widgetParent === 'string') { 2072 widgetParent = $(widgetParent); 2073 } 2074 2075 if (widgetParent !== null && (typeof widgetParent !== 'string' && !(widgetParent instanceof $))) { 2076 throw new TypeError('widgetParent() expects a string or a jQuery object parameter'); 2077 } 2078 2079 options.widgetParent = widgetParent; 2080 if (widget) { 2081 hide(); 2082 show(); 2083 } 2084 return picker; 2085 }; 2086 2087 picker.keepOpen = function (keepOpen) { 2088 if (arguments.length === 0) { 2089 return options.keepOpen; 2090 } 2091 2092 if (typeof keepOpen !== 'boolean') { 2093 throw new TypeError('keepOpen() expects a boolean parameter'); 2094 } 2095 2096 options.keepOpen = keepOpen; 2097 return picker; 2098 }; 2099 2100 picker.focusOnShow = function (focusOnShow) { 2101 if (arguments.length === 0) { 2102 return options.focusOnShow; 2103 } 2104 2105 if (typeof focusOnShow !== 'boolean') { 2106 throw new TypeError('focusOnShow() expects a boolean parameter'); 2107 } 2108 2109 options.focusOnShow = focusOnShow; 2110 return picker; 2111 }; 2112 2113 picker.inline = function (inline) { 2114 if (arguments.length === 0) { 2115 return options.inline; 2116 } 2117 2118 if (typeof inline !== 'boolean') {
2119 throw new TypeError('inline() expects a boolean parameter'); 2120 } 2121 2122 options.inline = inline; 2123 return picker; 2124 }; 2125 2126 picker.clear = function () { 2127 clear(); 2128 return picker; 2129 }; 2130 2131 picker.keyBinds = function (keyBinds) { 2132 if (arguments.length === 0) { 2133 return options.keyBinds; 2134 } 2135 2136 options.keyBinds = keyBinds; 2137 return picker; 2138 }; 2139 2140 picker.getMoment = function (d) { 2141 return getMoment(d); 2142 }; 2143 2144 picker.debug = function (debug) { 2145 if (typeof debug !== 'boolean') { 2146 throw new TypeError('debug() expects a boolean parameter'); 2147 } 2148 2149 options.debug = debug; 2150 return picker; 2151 }; 2152 2153 picker.allowInputToggle = function (allowInputToggle) { 2154 if (arguments.length === 0) { 2155 return options.allowInputToggle; 2156 } 2157 2158 if (typeof allowInputToggle !== 'boolean') { 2159 throw new TypeError('allowInputToggle() expects a boolean parameter'); 2160 } 2161 2162 options.allowInputToggle = allowInputToggle; 2163 return picker; 2164 }; 2165 2166 picker.showClose = function (showClose) { 2167 if (arguments.length === 0) { 2168 return options.showClose; 2169 } 2170 2171 if (typeof showClose !== 'boolean') { 2172 throw new TypeError('showClose() expects a boolean parameter'); 2173 } 2174 2175 options.showClose = showClose; 2176 return picker; 2177 }; 2178 2179 picker.keepInvalid = function (keepInvalid) { 2180 if (arguments.length === 0) { 2181 return options.keepInvalid; 2182 } 2183 2184 if (typeof keepInvalid !== 'boolean') { 2185 throw new TypeError('keepInvalid() expects a boolean parameter'); 2186 } 2187 options.keepInvalid = keepInvalid; 2188 return picker; 2189 }; 2190 2191 picker.datepickerInput = function (datepickerInput) { 2192 if (arguments.length === 0) { 2193 return options.datepickerInput; 2194 } 2195 2196 if (typeof datepickerInput !== 'string') { 2197 throw new TypeError('datepickerInput() expects a string parameter'); 2198 } 2199 2200 options.datepickerInput = datepickerInput; 2201 return picker; 2202 }; 2203 2204 picker.parseInputDate = function (parseInputDate) { 2205 if (arguments.length === 0) { 2206 return options.parseInputDate; 2207 } 2208 2209 if (typeof parseInputDate !== 'function') { 2210 throw new TypeError('parseInputDate() sholud be as function'); 2211 } 2212 2213 options.parseInputDate = parseInputDate; 2214 2215 return picker; 2216 }; 2217 2218 picker.disabledTimeIntervals = function (disabledTimeIntervals) { 2219 ///<signature helpKeyword="$.fn.datetimepicker.disabledTimeIntervals"> 2220 ///<summary>Returns an array with the currently set disabled dates on the component.</summary> 2221 ///<returns type="array">options.disabledTimeIntervals</returns> 2222 ///</signature> 2223 ///<signature> 2224 ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of 2225 ///options.enabledDates if such exist.</summary> 2226 ///<param name="dates" locid="$.fn.datetimepicker.disabledTimeIntervals_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.</param> 2227 ///</signature> 2228 if (arguments.length === 0) { 2229 return (options.disabledTimeIntervals ? $.extend({}, options.disabledTimeIntervals) : options.disabledTimeIntervals); 2230 } 2231 2232 if (!disabledTimeIntervals) { 2233 options.disabledTimeIntervals = false; 2234 update(); 2235 return picker; 2236 } 2237 if (!(disabledTimeIntervals instanceof Array)) { 2238 throw new TypeError('disabledTimeIntervals() expects an array parameter'); 2239 } 2240 options.disabledTimeIntervals = disabledTimeIntervals; 2241 update(); 2242 return picker; 2243 }; 2244 2245 picker.disabledHours = function (hours) { 2246 ///<signature helpKeyword="$.fn.datetimepicker.disabledHours"> 2247 ///<summary>Returns an array with the currently set disabled hours on the component.</summary> 2248 ///<returns type="array">options.disabledHours</returns> 2249 ///</signature> 2250 ///<signature> 2251 ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of 2252 ///options.enabledHours if such exist.</summary> 2253 ///<param name="hours" locid="$.fn.datetimepicker.disabledHours_p:hours">Takes an [ int ] of values and disallows the user to select only from those hours.</param> 2254 ///</signature> 2255 if (arguments.length === 0) { 2256 return (options.disabledHours ? $.extend({}, options.disabledHours) : options.disabledHours); 2257 } 2258 2259 if (!hours) { 2260 options.disabledHours = false;
2261 update(); 2262 return picker; 2263 } 2264 if (!(hours instanceof Array)) { 2265 throw new TypeError('disabledHours() expects an array parameter'); 2266 } 2267 options.disabledHours = indexGivenHours(hours); 2268 options.enabledHours = false; 2269 if (options.useCurrent && !options.keepInvalid) { 2270 var tries = 0; 2271 while (!isValid(date, 'h')) { 2272 date.add(1, 'h'); 2273 if (tries === 24) { 2274 throw 'Tried 24 times to find a valid date'; 2275 } 2276 tries++; 2277 } 2278 setValue(date); 2279 } 2280 update(); 2281 return picker; 2282 }; 2283 2284 picker.enabledHours = function (hours) { 2285 ///<signature helpKeyword="$.fn.datetimepicker.enabledHours"> 2286 ///<summary>Returns an array with the currently set enabled hours on the component.</summary> 2287 ///<returns type="array">options.enabledHours</returns> 2288 ///</signature> 2289 ///<signature> 2290 ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledHours if such exist.</summary> 2291 ///<param name="hours" locid="$.fn.datetimepicker.enabledHours_p:hours">Takes an [ int ] of values and allows the user to select only from those hours.</param> 2292 ///</signature> 2293 if (arguments.length === 0) { 2294 return (options.enabledHours ? $.extend({}, options.enabledHours) : options.enabledHours); 2295 } 2296 2297 if (!hours) { 2298 options.enabledHours = false; 2299 update(); 2300 return picker; 2301 } 2302 if (!(hours instanceof Array)) { 2303 throw new TypeError('enabledHours() expects an array parameter'); 2304 } 2305 options.enabledHours = indexGivenHours(hours); 2306 options.disabledHours = false; 2307 if (options.useCurrent && !options.keepInvalid) { 2308 var tries = 0; 2309 while (!isValid(date, 'h')) { 2310 date.add(1, 'h'); 2311 if (tries === 24) { 2312 throw 'Tried 24 times to find a valid date'; 2313 } 2314 tries++; 2315 } 2316 setValue(date); 2317 } 2318 update(); 2319 return picker; 2320 }; 2321 /** 2322 * Returns the component's model current viewDate, a moment object or null if not set. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration. 2323 * @param {Takes string, viewDate, moment, null parameter.} newDate 2324 * @returns {viewDate.clone()} 2325 */ 2326 picker.viewDate = function (newDate) { 2327 if (arguments.length === 0) { 2328 return viewDate.clone(); 2329 } 2330 2331 if (!newDate) { 2332 viewDate = date.clone(); 2333 return picker; 2334 } 2335 2336 if (typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) { 2337 throw new TypeError('viewDate() parameter must be one of [string, moment or Date]'); 2338 } 2339 2340 viewDate = parseInputDate(newDate); 2341 viewUpdate(); 2342 return picker; 2343 }; 2344 2345 // initializing element and component attributes 2346 if (element.is('input')) { 2347 input = element; 2348 } else { 2349 input = element.find(options.datepickerInput); 2350 if (input.length === 0) { 2351 input = element.find('input'); 2352 } else if (!input.is('input')) { 2353 throw new Error('CSS class "' + options.datepickerInput + '" cannot be applied to non input element'); 2354 } 2355 } 2356 2357 if (element.hasClass('input-group')) { 2358 // in case there is more then one 'input-group-addon' Issue #48 2359 if (element.find('.datepickerbutton').length === 0) { 2360 component = element.find('.input-group-addon'); 2361 } else { 2362 component = element.find('.datepickerbutton'); 2363 } 2364 } 2365 2366 if (!options.inline && !input.is('input')) { 2367 throw new Error('Could not initialize DateTimePicker without an input element'); 2368 } 2369 2370 // Set defaults for date here now instead of in var declaration 2371 date = getMoment(); 2372 viewDate = date.clone(); 2373 2374 $.extend(true, options, dataToOptions()); 2375 2376 picker.options(options); 2377 2378 initFormatting(); 2379 2380 attachDatePickerElementEvents(); 2381 2382 if (input.prop('disabled')) { 2383 picker.disable(); 2384 } 2385 if (input.is('input') && input.val().trim().length !== 0) { 2386 setValue(parseInputDate(input.val().trim())); 2387 } 2388 else if (options.defaultDate && input.attr('placeholder')
2388=== undefined) { 2389 setValue(options.defaultDate); 2390 } 2391 if (options.inline) { 2392 show(); 2393 } 2394 return picker; 2395 }; 2396 2397 /******************************************************************************** 2398 * 2399 * jQuery plugin constructor and defaults object 2400 * 2401 ********************************************************************************/ 2402 2403 /** 2404 * See (http://jquery.com/). 2405 * @name jQuery 2406 * @class 2407 * See the jQuery Library (http://jquery.com/) for full details. This just 2408 * documents the function and classes that are added to jQuery by this plug-in. 2409 */ 2410 /** 2411 * See (http://jquery.com/) 2412 * @name fn 2413 * @class 2414 * See the jQuery Library (http://jquery.com/) for full details. This just 2415 * documents the function and classes that are added to jQuery by this plug-in. 2416 * @memberOf jQuery 2417 */ 2418 /** 2419 * Show comments 2420 * @class datetimepicker 2421 * @memberOf jQuery.fn 2422 */ 2423 $.fn.datetimepicker = function (options) { 2424 options = options || {}; 2425 2426 var args = Array.prototype.slice.call(arguments, 1), 2427 isInstance = true, 2428 thisMethods = ['destroy', 'hide', 'show', 'toggle'], 2429 returnValue; 2430 2431 if (typeof options === 'object') { 2432 return this.each(function () { 2433 var $this = $(this), 2434 _options; 2435 if (!$this.data('DateTimePicker')) { 2436 // create a private copy of the defaults object 2437 _options = $.extend(true, {}, $.fn.datetimepicker.defaults, options); 2438 $this.data('DateTimePicker', dateTimePicker($this, _options)); 2439 } 2440 }); 2441 } else if (typeof options === 'string') { 2442 this.each(function () { 2443 var $this = $(this), 2444 instance = $this.data('DateTimePicker'); 2445 if (!instance) { 2446 throw new Error('bootstrap-datetimepicker("' + options + '") method was called on an element that is not using DateTimePicker'); 2447 } 2448 2449 returnValue = instance[options].apply(instance, args); 2450 isInstance = returnValue === instance; 2451 }); 2452 2453 if (isInstance || $.inArray(options, thisMethods) > -1) { 2454 return this; 2455 } 2456 2457 return returnValue; 2458 } 2459 2460 throw new TypeError('Invalid arguments for DateTimePicker: ' + options); 2461 }; 2462 2463 $.fn.datetimepicker.defaults = { 2464 timeZone: '', 2465 format: false, 2466 dayViewHeaderFormat: 'MMMM YYYY', 2467 extraFormats: false, 2468 stepping: 1, 2469 minDate: false, 2470 maxDate: false, 2471 useCurrent: true, 2472 collapse: true, 2473 locale: moment.locale(), 2474 defaultDate: false, 2475 disabledDates: false, 2476 enabledDates: false, 2477 icons: { 2478 time: 'glyphicon glyphicon-time', 2479 date: 'glyphicon glyphicon-calendar', 2480 up: 'glyphicon glyphicon-chevron-up', 2481 down: 'glyphicon glyphicon-chevron-down', 2482 previous: 'glyphicon glyphicon-chevron-left', 2483 next: 'glyphicon glyphicon-chevron-right', 2484 today: 'glyphicon glyphicon-screenshot', 2485 clear: 'glyphicon glyphicon-trash', 2486 close: 'glyphicon glyphicon-remove' 2487 }, 2488 tooltips: { 2489 today: 'Go to today', 2490 clear: 'Clear selection', 2491 close: 'Close the picker', 2492 selectMonth: 'Select Month', 2493 prevMonth: 'Previous Month', 2494 nextMonth: 'Next Month', 2495 selectYear: 'Select Year', 2496 prevYear: 'Previous Year', 2497 nextYear: 'Next Year', 2498 selectDecade: 'Select Decade', 2499 prevDecade: 'Previous Decade', 2500 nextDecade: 'Next Decade', 2501 prevCentury: 'Previous Century', 2502 nextCentury: 'Next Century', 2503 pickHour: 'Pick Hour', 2504 incrementHour: 'Increment Hour', 2505 decrementHour: 'Decrement Hour', 2506 pickMinute: 'Pick Minute', 2507 incrementMinute: 'Increment Minute', 2508 decrementMinute: 'Decrement Minute', 2509 pickSecond: 'Pick Second', 2510 incrementSecond: 'Increment Second', 2511 decrementSecond: 'Decrement Second',
2512 togglePeriod: 'Toggle Period', 2513 selectTime: 'Select Time' 2514 }, 2515 useStrict: false, 2516 sideBySide: false, 2517 daysOfWeekDisabled: false, 2518 calendarWeeks: false, 2519 viewMode: 'days', 2520 toolbarPlacement: 'default', 2521 showTodayButton: false, 2522 showClear: false, 2523 showClose: false, 2524 widgetPositioning: { 2525 horizontal: 'auto', 2526 vertical: 'auto' 2527 }, 2528 widgetParent: null, 2529 ignoreReadonly: false, 2530 keepOpen: false, 2531 focusOnShow: true, 2532 inline: false, 2533 keepInvalid: false, 2534 datepickerInput: '.datepickerinput', 2535 keyBinds: { 2536 up: function (widget) { 2537 if (!widget) { 2538 return; 2539 } 2540 var d = this.date() || this.getMoment(); 2541 if (widget.find('.datepicker').is(':visible')) { 2542 this.date(d.clone().subtract(7, 'd')); 2543 } else { 2544 this.date(d.clone().add(this.stepping(), 'm')); 2545 } 2546 }, 2547 down: function (widget) { 2548 if (!widget) { 2549 this.show(); 2550 return; 2551 } 2552 var d = this.date() || this.getMoment(); 2553 if (widget.find('.datepicker').is(':visible')) { 2554 this.date(d.clone().add(7, 'd')); 2555 } else { 2556 this.date(d.clone().subtract(this.stepping(), 'm')); 2557 } 2558 }, 2559 'control up': function (widget) { 2560 if (!widget) { 2561 return; 2562 } 2563 var d = this.date() || this.getMoment(); 2564 if (widget.find('.datepicker').is(':visible')) { 2565 this.date(d.clone().subtract(1, 'y')); 2566 } else { 2567 this.date(d.clone().add(1, 'h')); 2568 } 2569 }, 2570 'control down': function (widget) { 2571 if (!widget) { 2572 return; 2573 } 2574 var d = this.date() || this.getMoment(); 2575 if (widget.find('.datepicker').is(':visible')) { 2576 this.date(d.clone().add(1, 'y')); 2577 } else { 2578 this.date(d.clone().subtract(1, 'h')); 2579 } 2580 }, 2581 left: function (widget) { 2582 if (!widget) { 2583 return; 2584 } 2585 var d = this.date() || this.getMoment(); 2586 if (widget.find('.datepicker').is(':visible')) { 2587 this.date(d.clone().subtract(1, 'd')); 2588 } 2589 }, 2590 right: function (widget) { 2591 if (!widget) { 2592 return; 2593 } 2594 var d = this.date() || this.getMoment(); 2595 if (widget.find('.datepicker').is(':visible')) { 2596 this.date(d.clone().add(1, 'd')); 2597 } 2598 }, 2599 pageUp: function (widget) { 2600 if (!widget) { 2601 return; 2602 } 2603 var d = this.date() || this.getMoment(); 2604 if (widget.find('.datepicker').is(':visible')) { 2605 this.date(d.clone().subtract(1, 'M')); 2606 } 2607 }, 2608 pageDown: function (widget) { 2609 if (!widget) { 2610 return; 2611 } 2612 var d = this.date() || this.getMoment(); 2613 if (widget.find('.datepicker').is(':visible')) { 2614 this.date(d.clone().add(1, 'M')); 2615 } 2616 }, 2617 enter: function () { 2618 this.hide(); 2619 }, 2620 escape: function () { 2621 this.hide(); 2622 }, 2623 //tab: function (widget) { //this break the flow of the form. disabling for now 2624 // var toggle = widget.find('.picker-switch a[data-action="togglePicker"]'); 2625 // if(toggle.length > 0) toggle.click(); 2626 //}, 2627 'control space': function (widget) { 2628 if (!widget) { 2629 return; 2630 } 2631 if (widget.find('.timepicker').is(':visible')) { 2632 widget.find('.btn[data-action="togglePeriod"]').click(); 2633 } 2634 }, 2635 t: function () { 2636 this.date(this.getMoment()); 2637 }, 2638 'delete': function () { 2639 this.clear(); 2640 } 2641 }, 2642 debug: false, 2643 allowInputToggle: false, 2644 disabledTimeIntervals: false, 2645 disabledHours: false, 2646 enabledHours: false, 2647 viewDate: false 2648 }; 2649 2650 return $.fn.datetimepicker; 2651}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.