1/*! version : 4.17.47 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 114 'pageUp': 33, 115 33: 'pageUp', 116 'pageDown': 34, 117 34: 'pageDown', 118 'shift': 16, 119 16: 'shift', 120 'control': 17, 121 17: 'control', 122 'space': 32, 123 32: 'space', 124 't': 84, 125 84: 't', 126 'delete': 46, 127 46: 'delete' 128 }, 129 keyState = {}, 130 131 /******************************************************************************** 132 * 133 * Private functions 134 * 135 ********************************************************************************/ 136 137 hasTimeZone = function () { 138 return moment.tz !== undefined && options.timeZone !== undefined && options.timeZone !== null && options.timeZone !== ''; 139 }, 140 141 getMoment = function (d) { 142 var returnMoment; 143 144 if (d === undefined || d === null) { 145 returnMoment = moment(); //TODO should this use format? and locale? 146 } else if (moment.isDate(d) || moment.isMoment(d)) { 147 // If the date that is passed in is already a Date() or moment() object, 148 // pass it directly to moment. 149 returnMoment = moment(d); 150 } else if (hasTimeZone()) { // There is a string to parse and a default time zone 151 // parse with the tz function which takes a default time zone if it is not in the format str
151ing 152 returnMoment = moment.tz(d, parseFormats, options.useStrict, options.timeZone); 153 } else { 154 returnMoment = moment(d, parseFormats, options.useStrict); 155 } 156 157 if (hasTimeZone()) { 158 returnMoment.tz(options.timeZone); 159 } 160 161 return returnMoment; 162 }, 163 164 isEnabled = function (granularity) { 165 if (typeof granularity !== 'string' || granularity.length > 1) { 166 throw new TypeError('isEnabled expects a single character string parameter'); 167 } 168 switch (granularity) { 169 case 'y': 170 return actualFormat.indexOf('Y') !== -1; 171 case 'M': 172 return actualFormat.indexOf('M') !== -1; 173 case 'd': 174 return actualFormat.toLowerCase().indexOf('d') !== -1; 175 case 'h': 176 case 'H': 177 return actualFormat.toLowerCase().indexOf('h') !== -1; 178 case 'm': 179 return actualFormat.indexOf('m') !== -1; 180 case 's': 181 return actualFormat.indexOf('s') !== -1; 182 default: 183 return false; 184 } 185 }, 186 187 hasTime = function () { 188 return (isEnabled('h') || isEnabled('m') || isEnabled('s')); 189 }, 190 191 hasDate = function () { 192 return (isEnabled('y') || isEnabled('M') || isEnabled('d')); 193 }, 194 195 getDatePickerTemplate = function () { 196 var headTemplate = $('<thead>') 197 .append($('<tr>') 198 .append($('<th>').addClass('prev').attr('data-action', 'previous') 199 .append($('<span>').addClass(options.icons.previous)) 200 ) 201 .append($('<th>').addClass('picker-switch').attr('data-action', 'pickerSwitch').attr('colspan', (options.calendarWeeks ? '6' : '5'))) 202 .append($('<th>').addClass('next').attr('data-action', 'next') 203 .append($('<span>').addClass(options.icons.next)) 204 ) 205 ), 206 contTemplate = $('<tbody>') 207 .append($('<tr>') 208 .append($('<td>').attr('colspan', (options.calendarWeeks ? '8' : '7'))) 209 ); 210 211 return [ 212 $('<div>').addClass('datepicker-days') 213 .append($('<table>').addClass('table-condensed') 214 .append(headTemplate) 215 .append($('<tbody>')) 216 ), 217 $('<div>').addClass('datepicker-months') 218 .append($('<table>').addClass('table-condensed') 219 .append(headTemplate.clone()) 220 .append(contTemplate.clone()) 221 ), 222 $('<div>').addClass('datepicker-years') 223 .append($('<table>').addClass('table-condensed') 224 .append(headTemplate.clone()) 225 .append(contTemplate.clone()) 226 ), 227 $('<div>').addClass('datepicker-decades') 228 .append($('<table>').addClass('table-condensed') 229 .append(headTemplate.clone()) 230 .append(contTemplate.clone()) 231 ) 232 ]; 233 }, 234 235 getTimePickerMainTemplate = function () { 236 var topRow = $('<tr>'), 237 middleRow = $('<tr>'), 238 bottomRow = $('<tr>'); 239 240 if (isEnabled('h')) { 241 topRow.append($('<td>') 242 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementHour }).addClass('btn').attr('data-action', 'incrementHours').append($('<span>').addClass(options.icons.up)))); 243 middleRow.append($('<td>') 244 .append($('<span>').addClass('timepicker-hour').attr({ 'data-time-component': 'hours', 'title': options.tooltips.pickHour }).attr('data-action', 'showHours'))); 245 bottomRow.append($('<td>') 246 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementHour }).addClass('btn').attr('data-action', 'decrementHours').append($('<span>').addClass(options.icons.down)))); 247 } 248 if (isEnabled('m')) { 249 if (isEnabled('h')) { 250 topRow.append($('<td>').addClass('separator')); 251 middleRow.append($('<td>').addClass('separator').html(':')); 252 bottomRow.append($('<td>').addClass('separator')); 253 } 254 topRow.append($('<td>') 255 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementMinute }).addClass('btn').attr('data-action', 'incrementMinutes') 256 .append($('<span>').addClass(options.icons.up)))); 257 middleRow.append($('<td>') 258 .append($('<span>').addClass('timepicker-minute').attr({ 'data-time-component': 'minutes', 'title': options.tooltips.pickMinute }).attr('data-action', 'showMinutes'))); 259 bottomRow.append($('<td>') 260 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementMinute }).addClass('btn').attr('data-action', 'decrementMinutes') 261 .append($('<span>').addClass(options.icons.down)))); 262 } 263 if (isEnabled('s')) { 264 if (isEnabled('m')) { 265 topRow.append($('<td>').addClass('separator')); 266 middleRow.append($('<td>').addClass('separator').html(':')); 267 bottomRow.append($('<td>').addClass('separator')); 268 } 269 topRow.append($('<td>') 270 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementSecond }).addClass('btn').attr('data-action', 'incrementSeconds') 271 .append($('<span>').addClass(options.icons.up)))); 272 middleRow.append($('<td>') 273 .append($('<span>').addClass('timepicker-second').attr({ 'data-time-component': '
273seconds', 'title': options.tooltips.pickSecond }).attr('data-action', 'showSeconds'))); 274 bottomRow.append($('<td>') 275 .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementSecond }).addClass('btn').attr('data-action', 'decrementSeconds') 276 .append($('<span>').addClass(options.icons.down)))); 277 } 278 279 if (!use24Hours) { 280 topRow.append($('<td>').addClass('separator')); 281 middleRow.append($('<td>') 282 .append($('<button>').addClass('btn btn-primary').attr({ 'data-action': 'togglePeriod', tabindex: '-1', 'title': options.tooltips.togglePeriod }))); 283 bottomRow.append($('<td>').addClass('separator')); 284 } 285 286 return $('<div>').addClass('timepicker-picker') 287 .append($('<table>').addClass('table-condensed') 288 .append([topRow, middleRow, bottomRow])); 289 }, 290 291 getTimePickerTemplate = function () { 292 var hoursView = $('<div>').addClass('timepicker-hours') 293 .append($('<table>').addClass('table-condensed')), 294 minutesView = $('<div>').addClass('timepicker-minutes') 295 .append($('<table>').addClass('table-condensed')), 296 secondsView = $('<div>').addClass('timepicker-seconds') 297 .append($('<table>').addClass('table-condensed')), 298 ret = [getTimePickerMainTemplate()]; 299 300 if (isEnabled('h')) { 301 ret.push(hoursView); 302 } 303 if (isEnabled('m')) { 304 ret.push(minutesView); 305 } 306 if (isEnabled('s')) { 307 ret.push(secondsView); 308 } 309 310 return ret; 311 }, 312 313 getToolbar = function () { 314 var row = []; 315 if (options.showTodayButton) { 316 row.push($('<td>').append($('<a>').attr({ 'data-action': 'today', 'title': options.tooltips.today }).append($('<span>').addClass(options.icons.today)))); 317 } 318 if (!options.sideBySide && hasDate() && hasTime()) { 319 row.push($('<td>').append($('<a>').attr({ 'data-action': 'togglePicker', 'title': options.tooltips.selectTime }).append($('<span>').addClass(options.icons.time)))); 320 } 321 if (options.showClear) { 322 row.push($('<td>').append($('<a>').attr({ 'data-action': 'clear', 'title': options.tooltips.clear }).append($('<span>').addClass(options.icons.clear)))); 323 } 324 if (options.showClose) { 325 row.push($('<td>').append($('<a>').attr({ 'data-action': 'close', 'title': options.tooltips.close }).append($('<span>').addClass(options.icons.close)))); 326 } 327 return $('<table>').addClass('table-condensed').append($('<tbody>').append($('<tr>').append(row))); 328 }, 329 330 getTemplate = function () { 331 var template = $('<div>').addClass('bootstrap-datetimepicker-widget dropdown-menu'), 332 dateView = $('<div>').addClass('datepicker').append(getDatePickerTemplate()), 333 timeView = $('<div>').addClass('timepicker').append(getTimePickerTemplate()), 334 content = $('<ul>').addClass('list-unstyled'), 335 toolbar = $('<li>').addClass('picker-switch' + (options.collapse ? ' accordion-toggle' : '')).append(getToolbar()); 336 337 if (options.inline) { 338 template.removeClass('dropdown-menu'); 339 } 340 341 if (use24Hours) { 342 template.addClass('usetwentyfour'); 343 } 344 345 if (isEnabled('s') && !use24Hours) { 346 template.addClass('wider'); 347 } 348 349 if (options.sideBySide && hasDate() && hasTime()) { 350 template.addClass('timepicker-sbs'); 351 if (options.toolbarPlacement === 'top') { 352 template.append(toolbar); 353 } 354 template.append( 355 $('<div>').addClass('row') 356 .append(dateView.addClass('col-md-6')) 357 .append(timeView.addClass('col-md-6')) 358 ); 359 if (options.toolbarPlacement === 'bottom') { 360 template.append(toolbar); 361 } 362 return template; 363 } 364 365 if (options.toolbarPlacement === 'top') { 366 content.append(toolbar); 367 } 368 if (hasDate()) { 369 content.append($('<li>').addClass((options.collapse && hasTime() ? 'collapse in' : '')).append(dateView)); 370 } 371 if (options.toolbarPlacement === 'default') { 372 content.append(toolbar); 373 } 374 if (hasTime()) { 375 content.append($('<li>
375').addClass((options.collapse && hasDate() ? 'collapse' : '')).append(timeView)); 376 } 377 if (options.toolbarPlacement === 'bottom') { 378 content.append(toolbar); 379 } 380 return template.append(content); 381 }, 382 383 dataToOptions = function () { 384 var eData, 385 dataOptions = {}; 386 387 if (element.is('input') || options.inline) { 388 eData = element.data(); 389 } else { 390 eData = element.find('input').data(); 391 } 392 393 if (eData.dateOptions && eData.dateOptions instanceof Object) { 394 dataOptions = $.extend(true, dataOptions, eData.dateOptions); 395 } 396 397 $.each(options, function (key) { 398 var attributeName = 'date' + key.charAt(0).toUpperCase() + key.slice(1); 399 if (eData[attributeName] !== undefined) { 400 dataOptions[key] = eData[attributeName]; 401 } 402 }); 403 return dataOptions; 404 }, 405 406 place = function () { 407 var position = (component || element).position(), 408 offset = (component || element).offset(), 409 vertical = options.widgetPositioning.vertical, 410 horizontal = options.widgetPositioning.horizontal, 411 parent; 412 413 if (options.widgetParent) { 414 parent = options.widgetParent.append(widget); 415 } else if (element.is('input')) { 416 parent = element.after(widget).parent(); 417 } else if (options.inline) { 418 parent = element.append(widget); 419 return; 420 } else { 421 parent = element; 422 element.children().first().after(widget); 423 } 424 425 // Top and bottom logic 426 if (vertical === 'auto') { 427 if (offset.top + widget.height() * 1.5 >= $(window).height() + $(window).scrollTop() && 428 widget.height() + element.outerHeight() < offset.top) { 429 vertical = 'top'; 430 } else { 431 vertical = 'bottom'; 432 } 433 } 434 435 // Left and right logic 436 if (horizontal === 'auto') { 437 if (parent.width() < offset.left + widget.outerWidth() / 2 && 438 offset.left + widget.outerWidth() > $(window).width()) { 439 horizontal = 'right'; 440 } else { 441 horizontal = 'left'; 442 } 443 } 444 445 if (vertical === 'top') { 446 widget.addClass('top').removeClass('bottom'); 447 } else { 448 widget.addClass('bottom').removeClass('top'); 449 } 450 451 if (horizontal === 'right') { 452 widget.addClass('pull-right'); 453 } else { 454 widget.removeClass('pull-right'); 455 } 456 457 // find the first parent element that has a non-static css positioning 458 if (parent.css('position') === 'static') { 459 parent = parent.parents().filter(function () { 460 return $(this).css('position') !== 'static'; 461 }).first(); 462 } 463 464 if (parent.length === 0) { 465 throw new Error('datetimepicker component should be placed within a non-static positioned container'); 466 } 467 468 widget.css({ 469 top: vertical === 'top' ? 'auto' : position.top + element.outerHeight(), 470 bottom: vertical === 'top' ? parent.outerHeight() - (parent === element ? 0 : position.top) : 'auto', 471 left: horizontal === 'left' ? (parent === element ? 0 : position.left) : 'auto', 472 right: horizontal === 'left' ? 'auto' : parent.outerWidth() - element.outerWidth() - (parent === element ? 0 : position.left) 473 }); 474 }, 475 476 notifyEvent = function (e) { 477 if (e.type === 'dp.change' && ((e.date && e.date.isSame(e.oldDate)) || (!e.date && !e.oldDate))) { 478 return; 479 } 480 element.trigger(e); 481 }, 482 483 viewUpdate = function (e) { 484 if (e === 'y') { 485 e = 'YYYY'; 486 } 487 notifyEvent({ 488 type: 'dp.update', 489 change: e,
490 viewDate: viewDate.clone() 491 }); 492 }, 493 494 showMode = function (dir) { 495 if (!widget) { 496 return; 497 } 498 if (dir) { 499 currentViewMode = Math.max(minViewModeNumber, Math.min(3, currentViewMode + dir)); 500 } 501 widget.find('.datepicker > div').hide().filter('.datepicker-' + datePickerModes[currentViewMode].clsName).show(); 502 }, 503 504 fillDow = function () { 505 var row = $('<tr>'), 506 currentDate = viewDate.clone().startOf('w').startOf('d'); 507 508 if (options.calendarWeeks === true) { 509 row.append($('<th>').addClass('cw').text('#')); 510 } 511 512 while (currentDate.isBefore(viewDate.clone().endOf('w'))) { 513 row.append($('<th>').addClass('dow').text(currentDate.format('dd'))); 514 currentDate.add(1, 'd'); 515 } 516 widget.find('.datepicker-days thead').append(row); 517 }, 518 519 isInDisabledDates = function (testDate) { 520 return options.disabledDates[testDate.format('YYYY-MM-DD')] === true; 521 }, 522 523 isInEnabledDates = function (testDate) { 524 return options.enabledDates[testDate.format('YYYY-MM-DD')] === true; 525 }, 526 527 isInDisabledHours = function (testDate) { 528 return options.disabledHours[testDate.format('H')] === true; 529 }, 530 531 isInEnabledHours = function (testDate) { 532 return options.enabledHours[testDate.format('H')] === true; 533 }, 534 535 isValid = function (targetMoment, granularity) { 536 if (!targetMoment.isValid()) { 537 return false; 538 } 539 if (options.disabledDates && granularity === 'd' && isInDisabledDates(targetMoment)) { 540 return false; 541 } 542 if (options.enabledDates && granularity === 'd' && !isInEnabledDates(targetMoment)) { 543 return false; 544 } 545 if (options.minDate && targetMoment.isBefore(options.minDate, granularity)) { 546 return false; 547 } 548 if (options.maxDate && targetMoment.isAfter(options.maxDate, granularity)) { 549 return false; 550 } 551 if (options.daysOfWeekDisabled && granularity === 'd' && options.daysOfWeekDisabled.indexOf(targetMoment.day()) !== -1) { 552 return false; 553 } 554 if (options.disabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && isInDisabledHours(targetMoment)) { 555 return false; 556 } 557 if (options.enabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && !isInEnabledHours(targetMoment)) { 558 return false; 559 } 560 if (options.disabledTimeIntervals && (granularity === 'h' || granularity === 'm' || granularity === 's')) { 561 var found = false; 562 $.each(options.disabledTimeIntervals, function () { 563 if (targetMoment.isBetween(this[0], this[1])) { 564 found = true; 565 return false; 566 } 567 }); 568 if (found) { 569 return false; 570 } 571 } 572 return true; 573 }, 574 575 fillMonths = function () { 576 var spans = [], 577 monthsShort = viewDate.clone().startOf('y').startOf('d'); 578 while (monthsShort.isSame(viewDate, 'y')) { 579 spans.push($('<span>').attr('data-action', 'selectMonth').addClass('month').text(monthsShort.format('MMM'))); 580 monthsShort.add(1, 'M'); 581 } 582 widget.find('.datepicker-months td').empty().append(spans); 583 }, 584 585 updateMonths = function () { 586 var monthsView = widget.find('.datepicker-months'), 587 monthsViewHeader = monthsView.find('th'), 588 months = monthsView.find('tbody').find('span'); 589 590 monthsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevYear); 591 monthsViewHeader.eq(1).attr('title', options.tooltips.selectYear); 592 monthsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextYear); 593 594 monthsView.find('.disabled').removeClass('disabled'); 595 596 if (!isValid(viewDate.clone().subtract(1, 'y'), 'y')) { 597 monthsViewHeader.eq(0).addClass('disabled'); 598 } 599 600 monthsViewHeader.eq(1).text(viewDate.year()); 601 602 if (!isValid(viewDate.clone().add(1, 'y'), 'y')) { 603 monthsViewHeader.eq(2).addClass('disabled'); 604 } 605 606 months.removeClass('active'); 607 if (date.isSame(viewDate, 'y') && !unset) { 608 months.eq(date.month()).addClass('active'); 609 } 610 611 months.each(function (index) { 612 if (!isValid(viewDate.clone().month(index), 'M')) { 613 $(this).addClass('disabled'); 614 } 615 }); 616 }, 617 618 updateYears = function () { 619 var yearsView = widget.find('.datepicker-years'), 620 yearsViewHeader = yearsView.find('th'),
621 startYear = viewDate.clone().subtract(5, 'y'), 622 endYear = viewDate.clone().add(6, 'y'), 623 html = ''; 624 625 yearsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevDecade); 626 yearsViewHeader.eq(1).attr('title', options.tooltips.selectDecade); 627 yearsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextDecade); 628 629 yearsView.find('.disabled').removeClass('disabled'); 630 631 if (options.minDate && options.minDate.isAfter(startYear, 'y')) { 632 yearsViewHeader.eq(0).addClass('disabled'); 633 } 634 635 yearsViewHeader.eq(1).text(startYear.year() + '-' + endYear.year()); 636 637 if (options.maxDate && options.maxDate.isBefore(endYear, 'y')) { 638 yearsViewHeader.eq(2).addClass('disabled'); 639 } 640 641 while (!startYear.isAfter(endYear, 'y')) { 642 html += '<span data-action="selectYear" class="year' + (startYear.isSame(date, 'y') && !unset ? ' active' : '') + (!isValid(startYear, 'y') ? ' disabled' : '') + '">' + startYear.year() + '</span>'; 643 startYear.add(1, 'y'); 644 } 645 646 yearsView.find('td').html(html); 647 }, 648 649 updateDecades = function () { 650 var decadesView = widget.find('.datepicker-decades'), 651 decadesViewHeader = decadesView.find('th'), 652 startDecade = moment({ y: viewDate.year() - (viewDate.year() % 100) - 1 }), 653 endDecade = startDecade.clone().add(100, 'y'), 654 startedAt = startDecade.clone(), 655 minDateDecade = false, 656 maxDateDecade = false, 657 endDecadeYear, 658 html = ''; 659 660 decadesViewHeader.eq(0).find('span').attr('title', options.tooltips.prevCentury); 661 decadesViewHeader.eq(2).find('span').attr('title', options.tooltips.nextCentury); 662 663 decadesView.find('.disabled').removeClass('disabled'); 664 665 if (startDecade.isSame(moment({ y: 1900 })) || (options.minDate && options.minDate.isAfter(startDecade, 'y'))) { 666 decadesViewHeader.eq(0).addClass('disabled'); 667 } 668 669 decadesViewHeader.eq(1).text(startDecade.year() + '-' + endDecade.year()); 670 671 if (startDecade.isSame(moment({ y: 2000 })) || (options.maxDate && options.maxDate.isBefore(endDecade, 'y'))) { 672 decadesViewHeader.eq(2).addClass('disabled'); 673 } 674 675 while (!startDecade.isAfter(endDecade, 'y')) { 676 endDecadeYear = startDecade.year() + 12; 677 minDateDecade = options.minDate && options.minDate.isAfter(startDecade, 'y') && options.minDate.year() <= endDecadeYear; 678 maxDateDecade = options.maxDate && options.maxDate.isAfter(startDecade, 'y') && options.maxDate.year() <= endDecadeYear; 679 html += '<span data-action="selectDecade" class="decade' + (date.isAfter(startDecade) && date.year() <= endDecadeYear ? ' active' : '') + 680 (!isValid(startDecade, 'y') && !minDateDecade && !maxDateDecade ? ' disabled' : '') + '" data-selection="' + (startDecade.year() + 6) + '">' + (startDecade.year() + 1) + ' - ' + (startDecade.year() + 12) + '</span>'; 681 startDecade.add(12, 'y'); 682 } 683 html += '<span></span><span></span><span></span>'; //push the dangling block over, at least this way it's even 684 685 decadesView.find('td').html(html); 686 decadesViewHeader.eq(1).text((startedAt.year() + 1) + '-' + (startDecade.year())); 687 }, 688 689 fillDate = function () { 690 var daysView = widget.find('.datepicker-days'), 691 daysViewHeader = daysView.find('th'), 692 currentDate, 693 html = [], 694 row, 695 clsNames = [], 696 i; 697 698 if (!hasDate()) { 699 return; 700 } 701 702 daysViewHeader.eq(0).find('span').attr('title', options.tooltips.prevMonth); 703 daysViewHeader.eq(1).attr('title', options.tooltips.selectMonth); 704 daysViewHeader.eq(2).find('span').attr('title', options.tooltips.nextMonth); 705 706 daysView.find('.disabled').removeClass('disabled'); 707 daysViewHeader.eq(1).text(viewDate.format(options.dayViewHeaderFormat)); 708 709 if (!isValid(viewDate.clone().subtract(1, 'M'), 'M')) { 710 daysViewHeader.eq(0).addClass('disabled'); 711 } 712 if (!isValid(viewDate.clone().add(1, 'M'), 'M')) { 713 daysViewHeader.eq(2).addClass('disabled'); 714 } 715
716 currentDate = viewDate.clone().startOf('M').startOf('w').startOf('d'); 717 718 for (i = 0; i < 42; i++) { //always display 42 days (should show 6 weeks) 719 if (currentDate.weekday() === 0) { 720 row = $('<tr>'); 721 if (options.calendarWeeks) { 722 row.append('<td class="cw">' + currentDate.week() + '</td>'); 723 } 724 html.push(row); 725 } 726 clsNames = ['day']; 727 if (currentDate.isBefore(viewDate, 'M')) { 728 clsNames.push('old'); 729 } 730 if (currentDate.isAfter(viewDate, 'M')) { 731 clsNames.push('new'); 732 } 733 if (currentDate.isSame(date, 'd') && !unset) { 734 clsNames.push('active'); 735 } 736 if (!isValid(currentDate, 'd')) { 737 clsNames.push('disabled'); 738 } 739 if (currentDate.isSame(getMoment(), 'd')) { 740 clsNames.push('today'); 741 } 742 if (currentDate.day() === 0 || currentDate.day() === 6) { 743 clsNames.push('weekend'); 744 } 745 notifyEvent({ 746 type: 'dp.classify', 747 date: currentDate, 748 classNames: clsNames 749 }); 750 row.append('<td data-action="selectDay" data-day="' + currentDate.format('L') + '" class="' + clsNames.join(' ') + '">' + currentDate.date() + '</td>'); 751 currentDate.add(1, 'd'); 752 } 753 754 daysView.find('tbody').empty().append(html); 755 756 updateMonths(); 757 758 updateYears(); 759 760 updateDecades(); 761 }, 762 763 fillHours = function () { 764 var table = widget.find('.timepicker-hours table'), 765 currentHour = viewDate.clone().startOf('d'), 766 html = [], 767 row = $('<tr>'); 768 769 if (viewDate.hour() > 11 && !use24Hours) { 770 currentHour.hour(12); 771 } 772 while (currentHour.isSame(viewDate, 'd') && (use24Hours || (viewDate.hour() < 12 && currentHour.hour() < 12) || viewDate.hour() > 11)) { 773 if (currentHour.hour() % 4 === 0) { 774 row = $('<tr>'); 775 html.push(row); 776 } 777 row.append('<td data-action="selectHour" class="hour' + (!isValid(currentHour, 'h') ? ' disabled' : '') + '">' + currentHour.format(use24Hours ? 'HH' : 'hh') + '</td>'); 778 currentHour.add(1, 'h'); 779 } 780 table.empty().append(html); 781 }, 782 783 fillMinutes = function () { 784 var table = widget.find('.timepicker-minutes table'), 785 currentMinute = viewDate.clone().startOf('h'), 786 html = [], 787 row = $('<tr>'), 788 step = options.stepping === 1 ? 5 : options.stepping; 789 790 while (viewDate.isSame(currentMinute, 'h')) { 791 if (currentMinute.minute() % (step * 4) === 0) { 792 row = $('<tr>'); 793 html.push(row); 794 } 795 row.append('<td data-action="selectMinute" class="minute' + (!isValid(currentMinute, 'm') ? ' disabled' : '') + '">' + currentMinute.format('mm') + '</td>'); 796 currentMinute.add(step, 'm'); 797 } 798 table.empty().append(html); 799 }, 800 801 fillSeconds = function () { 802 var table = widget.find('.timepicker-seconds table'), 803 currentSecond = viewDate.clone().startOf('m'), 804 html = [], 805 row = $('<tr>'); 806 807 while (viewDate.isSame(currentSecond, 'm')) { 808 if (currentSecond.second() % 20 === 0) { 809 row = $('<tr>'); 810 html.push(row); 811 } 812 row.append('<td data-action="selectSecond" class="second' + (!isValid(currentSecond, 's') ? ' disabled' : '') + '">' + currentSecond.format('ss') + '</td>'); 813 currentSecond.add(5, 's'); 814 } 815 816 table.empty().append(html); 817 }, 818 819 fillTime = function () {
820 var toggle, newDate, timeComponents = widget.find('.timepicker span[data-time-component]'); 821 822 if (!use24Hours) { 823 toggle = widget.find('.timepicker [data-action=togglePeriod]'); 824 newDate = date.clone().add((date.hours() >= 12) ? -12 : 12, 'h'); 825 826 toggle.text(date.format('A')); 827 828 if (isValid(newDate, 'h')) { 829 toggle.removeClass('disabled'); 830 } else { 831 toggle.addClass('disabled'); 832 } 833 } 834 timeComponents.filter('[data-time-component=hours]').text(date.format(use24Hours ? 'HH' : 'hh')); 835 timeComponents.filter('[data-time-component=minutes]').text(date.format('mm')); 836 timeComponents.filter('[data-time-component=seconds]').text(date.format('ss')); 837 838 fillHours(); 839 fillMinutes(); 840 fillSeconds(); 841 }, 842 843 update = function () { 844 if (!widget) { 845 return; 846 } 847 fillDate(); 848 fillTime(); 849 }, 850 851 setValue = function (targetMoment) { 852 var oldDate = unset ? null : date; 853 854 // case of calling setValue(null or false) 855 if (!targetMoment) { 856 unset = true; 857 input.val(''); 858 element.data('date', ''); 859 notifyEvent({ 860 type: 'dp.change', 861 date: false, 862 oldDate: oldDate 863 }); 864 update(); 865 return; 866 } 867 868 targetMoment = targetMoment.clone().locale(options.locale); 869 870 if (hasTimeZone()) { 871 targetMoment.tz(options.timeZone); 872 } 873 874 if (options.stepping !== 1) { 875 targetMoment.minutes((Math.round(targetMoment.minutes() / options.stepping) * options.stepping)).seconds(0); 876 877 while (options.minDate && targetMoment.isBefore(options.minDate)) { 878 targetMoment.add(options.stepping, 'minutes'); 879 } 880 } 881 882 if (isValid(targetMoment)) { 883 date = targetMoment; 884 viewDate = date.clone(); 885 input.val(date.format(actualFormat)); 886 element.data('date', date.format(actualFormat)); 887 unset = false; 888 update(); 889 notifyEvent({ 890 type: 'dp.change', 891 date: date.clone(), 892 oldDate: oldDate 893 }); 894 } else { 895 if (!options.keepInvalid) { 896 input.val(unset ? '' : date.format(actualFormat)); 897 } else { 898 notifyEvent({ 899 type: 'dp.change', 900 date: targetMoment, 901 oldDate: oldDate 902 }); 903 } 904 notifyEvent({ 905 type: 'dp.error', 906 date: targetMoment, 907 oldDate: oldDate 908 }); 909 } 910 }, 911 912 /** 913 * Hides the widget. Possibly will emit dp.hide 914 */ 915 hide = function () { 916 var transitioning = false; 917 if (!widget) { 918 return picker; 919 } 920 // Ignore event if in the middle of a picker transition 921 widget.find('.collapse').each(function () { 922 var collapseData = $(this).data('collapse'); 923 if (collapseData && collapseData.transitioning) { 924 transitioning = true; 925 return false; 926 } 927 return true; 928 }); 929 if (transitioning) { 930 return picker; 931 } 932 if (component && component.hasClass('btn')) { 933 component.toggleClass('active'); 934 } 935 widget.hide(); 936 937 $(window).off('resize', place); 938 widget.off('click', '[data-action]'); 939 widget.off('mousedown', false); 940 941 widget.remove(); 942 widget = false;
943 944 notifyEvent({ 945 type: 'dp.hide', 946 date: date.clone() 947 }); 948 949 input.blur(); 950 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('.datepickerbutton').length === 0) { 2346 component = element.find('.input-group-addon'); 2347 } else { 2348 component = element.find('.datepickerbutton'); 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.