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