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