vendor: 107,334 bytes, lines 1-2652
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 '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 string 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($('<i>').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($('<i>').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($('<i>').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($('<i>').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($('<i>').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($('<i>').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($('<i>').addClass(options.icons.up)))); 271 middleRow.append($('<td>') 272 .append($('<span>').addClass('timepicker-second').attr({ 'data-time-component': 'seconds', '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($('<i>').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($('<i>').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($('<i>').addClass(options.icons.time)))); 319 } 320 if (options.showClear) { 321 row.push($('<td>').append($('<a>').attr({ 'data-action': 'clear', 'title': options.tooltips.clear }).append($('<i>').addClass(options.icons.clear)))); 322 } 323 if (options.showClose) { 324 row.push($('<td>').append($('<a>').attr({ 'data-action': 'close', 'title': options.tooltips.close }).append($('<i>').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 show' : '')).append(dateView)); 369 } 370 if (options.toolbarPlacement === 'default') { 371 content.append(toolbar); 372 } 373 if (hasTime()) { 374 content.append($('<li>').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 viewDate = date.clone(); 951 952 return picker; 953 }, 954 955 clear = function () { 956 setValue(null); 957 }, 958 959 parseInputDate = function (inputDate) { 960 if (options.parseInputDate === undefined) { 961 if (!moment.isMoment(inputDate) || inputDate instanceof Date) { 962 inputDate = getMoment(inputDate); 963 } 964 } else { 965 inputDate = options.parseInputDate(inputDate); 966 } 967 //inputDate.locale(options.locale); 968 return inputDate; 969 }, 970 971 /******************************************************************************** 972 * 973 * Widget UI interaction functions 974 * 975 ********************************************************************************/ 976 actions = { 977 next: function () { 978 var navFnc = datePickerModes[currentViewMode].navFnc; 979 viewDate.add(datePickerModes[currentViewMode].navStep, navFnc); 980 fillDate(); 981 viewUpdate(navFnc); 982 }, 983 984 previous: function () { 985 var navFnc = datePickerModes[currentViewMode].navFnc; 986 viewDate.subtract(datePickerModes[currentViewMode].navStep, navFnc); 987 fillDate(); 988 viewUpdate(navFnc); 989 }, 990 991 pickerSwitch: function () { 992 showMode(1); 993 }, 994 995 selectMonth: function (e) { 996 var month = $(e.target).closest('tbody').find('span').index($(e.target)); 997 viewDate.month(month); 998 if (currentViewMode === minViewModeNumber) { 999 setValue(date.clone().year(viewDate.year()).month(viewDate.month())); 1000 if (!options.inline) { 1001 hide(); 1002 } 1003 } else { 1004 showMode(-1); 1005 fillDate(); 1006 } 1007 viewUpdate('M'); 1008 }, 1009 1010 selectYear: function (e) { 1011 var year = parseInt($(e.target).text(), 10) || 0; 1012 viewDate.year(year); 1013 if (currentViewMode === minViewModeNumber) { 1014 setValue(date.clone().year(viewDate.year())); 1015 if (!options.inline) { 1016 hide(); 1017 } 1018 } else { 1019 showMode(-1); 1020 fillDate(); 1021 } 1022 viewUpdate('YYYY'); 1023 }, 1024 1025 selectDecade: function (e) { 1026 var year = parseInt($(e.target).data('selection'), 10) || 0; 1027 viewDate.year(year); 1028 if (currentViewMode === minViewModeNumber) { 1029 setValue(date.clone().year(viewDate.year())); 1030 if (!options.inline) { 1031 hide(); 1032 } 1033 } else { 1034 showMode(-1); 1035 fillDate(); 1036 } 1037 viewUpdate('YYYY'); 1038 }, 1039 1040 selectDay: function (e) { 1041 var day = viewDate.clone(); 1042 if ($(e.target).is('.old')) { 1043 day.subtract(1, 'M'); 1044 } 1045 if ($(e.target).is('.new')) { 1046 day.add(1, 'M'); 1047 } 1048 setValue(day.date(parseInt($(e.target).text(), 10))); 1049 if (!hasTime() && !options.keepOpen && !options.inline) { 1050 hide(); 1051 } 1052 }, 1053 1054 incrementHours: function () { 1055 var newDate = date.clone().add(1, 'h'); 1056 if (isValid(newDate, 'h')) { 1057 setValue(newDate); 1058 } 1059 }, 1060 1061 incrementMinutes: function () { 1062 var newDate = date.clone().add(options.stepping, 'm'); 1063 if (isValid(newDate, 'm')) { 1064 setValue(newDate); 1065 } 1066 }, 1067 1068 incrementSeconds: function () { 1069 var newDate = date.clone().add(1, 's'); 1070 if (isValid(newDate, 's')) { 1071 setValue(newDate); 1072 } 1073 }, 1074 1075 decrementHours: function () { 1076 var newDate = date.clone().subtract(1, 'h'); 1077 if (isValid(newDate, 'h')) { 1078 setValue(newDate); 1079 } 1080 }, 1081 1082 decrementMinutes: function () { 1083 var newDate = date.clone().subtract(options.stepping, 'm'); 1084 if (isValid(newDate, 'm')) { 1085 setValue(newDate); 1086 } 1087 }, 1088 1089 decrementSeconds: function () { 1090 var newDate = date.clone().subtract(1, 's'); 1091 if (isValid(newDate, 's')) { 1092 setValue(newDate); 1093 } 1094 }, 1095 1096 togglePeriod: function () { 1097 setValue(date.clone().add((date.hours() >= 12) ? -12 : 12, 'h')); 1098 }, 1099 1100 togglePicker: function (e) { 1101 var $this = $(e.target), 1102 $link = $this.closest('a'), 1103 $parent = $this.closest('ul'), 1104 expanded = $parent.find('.show'), 1105 closed = $parent.find('.collapse:not(.show)'), 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('show'); 1118 closed.addClass('show'); 1119 } 1120 if ($this.is('i')) { 1121 $this.toggleClass(options.icons.time + ' ' + options.icons.date); 1122 if ($this.hasClass(options.icons.date)) { 1123 $link.attr('title', options.tooltips.selectDate); 1124 } else { 1125 $link.attr('title', options.tooltips.selectTime); 1126 } 1127 } else { 1128 $this.find('i').toggleClass(options.icons.time + ' ' + options.icons.date); 1129 if ($this.hasClass(options.icons.date)) { 1130 $link.attr('title', options.tooltips.selectDate); 1131 } else { 1132 $link.attr('title', options.tooltips.selectTime); 1133 } 1134 } 1135 1136 // NOTE: uncomment if toggled state will be restored in show() 1137 //if (component) { 1138 // component.find('span').toggleClass(options.icons.time + ' ' + options.icons.date); 1139 //} 1140 } 1141 }, 1142 1143 showPicker: function () { 1144 widget.find('.timepicker > div:not(.timepicker-picker)').hide(); 1145 widget.find('.timepicker .timepicker-picker').show(); 1146 }, 1147 1148 showHours: function () { 1149 widget.find('.timepicker .timepicker-picker').hide(); 1150 widget.find('.timepicker .timepicker-hours').show(); 1151 }, 1152 1153 showMinutes: function () { 1154 widget.find('.timepicker .timepicker-picker').hide(); 1155 widget.find('.timepicker .timepicker-minutes').show(); 1156 }, 1157 1158 showSeconds: function () { 1159 widget.find('.timepicker .timepicker-picker').hide(); 1160 widget.find('.timepicker .timepicker-seconds').show(); 1161 }, 1162 1163 selectHour: function (e) { 1164 var hour = parseInt($(e.target).text(), 10); 1165 1166 if (!use24Hours) { 1167 if (date.hours() >= 12) { 1168 if (hour !== 12) { 1169 hour += 12; 1170 } 1171 } else { 1172 if (hour === 12) { 1173 hour = 0; 1174 } 1175 } 1176 } 1177 setValue(date.clone().hours(hour)); 1178 actions.showPicker.call(picker); 1179 }, 1180 1181 selectMinute: function (e) { 1182 setValue(date.clone().minutes(parseInt($(e.target).text(), 10))); 1183 actions.showPicker.call(picker); 1184 }, 1185 1186 selectSecond: function (e) { 1187 setValue(date.clone().seconds(parseInt($(e.target).text(), 10))); 1188 actions.showPicker.call(picker); 1189 }, 1190 1191 clear: clear, 1192 1193 today: function () { 1194 var todaysDate = getMoment(); 1195 if (isValid(todaysDate, 'd')) { 1196 setValue(todaysDate); 1197 } 1198 }, 1199 1200 close: hide 1201 }, 1202 1203 doAction = function (e) { 1204 if ($(e.currentTarget).is('.disabled')) { 1205 return false; 1206 } 1207 actions[$(e.currentTarget).data('action')].apply(picker, arguments); 1208 return false; 1209 }, 1210 1211 /** 1212 * Shows the widget. Possibly will emit dp.show and dp.change 1213 */ 1214 show = function () { 1215 var currentMoment, 1216 useCurrentGranularity = { 1217 'year': function (m) { 1218 return m.month(0).date(1).hours(0).seconds(0).minutes(0); 1219 }, 1220 'month': function (m) { 1221 return m.date(1).hours(0).seconds(0).minutes(0); 1222 }, 1223 'day': function (m) { 1224 return m.hours(0).seconds(0).minutes(0); 1225 }, 1226 'hour': function (m) { 1227 return m.seconds(0).minutes(0); 1228 }, 1229 'minute': function (m) { 1230 return m.seconds(0); 1231 } 1232 }; 1233 1234 if (input.prop('disabled') || (!options.ignoreReadonly && input.prop('readonly')) || widget) { 1235 return picker; 1236 } 1237 if (input.val() !== undefined && input.val().trim().length !== 0) { 1238 setValue(parseInputDate(input.val().trim())); 1239 } else if (unset && options.useCurrent && (options.inline || (input.is('input') && input.val().trim().length === 0))) { 1240 currentMoment = getMoment(); 1241 if (typeof options.useCurrent === 'string') { 1242 currentMoment = useCurrentGranularity[options.useCurrent](currentMoment); 1243 } 1244 setValue(currentMoment); 1245 } 1246 widget = getTemplate(); 1247 1248 fillDow(); 1249 fillMonths(); 1250 1251 widget.find('.timepicker-hours').hide(); 1252 widget.find('.timepicker-minutes').hide(); 1253 widget.find('.timepicker-seconds').hide(); 1254 1255 update(); 1256 showMode(); 1257 1258 $(window).on('resize', place); 1259 widget.on('click', '[data-action]', doAction); // this handles clicks on the widget 1260 widget.on('mousedown', false); 1261 1262 if (component && component.hasClass('btn')) { 1263 component.toggleClass('active'); 1264 } 1265 place(); 1266 widget.show(); 1267 if (options.focusOnShow && !input.is(':focus')) { 1268 input.focus(); 1269 } 1270 1271 notifyEvent({ 1272 type: 'dp.show' 1273 }); 1274 return picker; 1275 }, 1276 1277 /** 1278 * Shows or hides the widget 1279 */ 1280 toggle = function () { 1281 return (widget ? hide() : show()); 1282 }, 1283 1284 keydown = function (e) { 1285 var handler = null, 1286 index, 1287 index2, 1288 pressedKeys = [], 1289 pressedModifiers = {}, 1290 currentKey = e.which, 1291 keyBindKeys, 1292 allModifiersPressed, 1293 pressed = 'p'; 1294 1295 keyState[currentKey] = pressed; 1296 1297 for (index in keyState) { 1298 if (keyState.hasOwnProperty(index) && keyState[index] === pressed) { 1299 pressedKeys.push(index); 1300 if (parseInt(index, 10) !== currentKey) { 1301 pressedModifiers[index] = true; 1302 } 1303 } 1304 } 1305 1306 for (index in options.keyBinds) { 1307 if (options.keyBinds.hasOwnProperty(index) && typeof (options.keyBinds[index]) === 'function') { 1308 keyBindKeys = index.split(' '); 1309 if (keyBindKeys.length === pressedKeys.length && keyMap[currentKey] === keyBindKeys[keyBindKeys.length - 1]) { 1310 allModifiersPressed = true; 1311 for (index2 = keyBindKeys.length - 2; index2 >= 0; index2--) { 1312 if (!(keyMap[keyBindKeys[index2]] in pressedModifiers)) { 1313 allModifiersPressed = false; 1314 break; 1315 } 1316 } 1317 if (allModifiersPressed) { 1318 handler = options.keyBinds[index]; 1319 break; 1320 } 1321 } 1322 } 1323 } 1324 1325 if (handler) { 1326 handler.call(picker, widget); 1327 e.stopPropagation(); 1328 e.preventDefault(); 1329 } 1330 }, 1331 1332 keyup = function (e) { 1333 keyState[e.which] = 'r'; 1334 e.stopPropagation(); 1335 e.preventDefault(); 1336 }, 1337 1338 change = function (e) { 1339 var val = $(e.target).val().trim(), 1340 parsedDate = val ? parseInputDate(val) : null; 1341 setValue(parsedDate); 1342 e.stopImmediatePropagation(); 1343 return false; 1344 }, 1345 1346 attachDatePickerElementEvents = function () { 1347 input.on({ 1348 'change': change, 1349 'blur': options.debug ? '' : hide, 1350 'keydown': keydown, 1351 'keyup': keyup, 1352 'focus': options.allowInputToggle ? show : '' 1353 }); 1354 1355 if (element.is('input')) { 1356 input.on({ 1357 'focus': show 1358 }); 1359 } else if (component) { 1360 component.on('click', toggle); 1361 component.on('mousedown', false); 1362 } 1363 }, 1364 1365 detachDatePickerElementEvents = function () { 1366 input.off({ 1367 'change': change, 1368 'blur': blur, 1369 'keydown': keydown, 1370 'keyup': keyup, 1371 'focus': options.allowInputToggle ? hide : '' 1372 }); 1373 1374 if (element.is('input')) { 1375 input.off({ 1376 'focus': show 1377 }); 1378 } else if (component) { 1379 component.off('click', toggle); 1380 component.off('mousedown', false); 1381 } 1382 }, 1383 1384 indexGivenDates = function (givenDatesArray) { 1385 // Store given enabledDates and disabledDates as keys. 1386 // This way we can check their existence in O(1) time instead of looping through whole array. 1387 // (for example: options.enabledDates['2014-02-27'] === true) 1388 var givenDatesIndexed = {}; 1389 $.each(givenDatesArray, function () { 1390 var dDate = parseInputDate(this); 1391 if (dDate.isValid()) { 1392 givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true; 1393 } 1394 }); 1395 return (Object.keys(givenDatesIndexed).length) ? givenDatesIndexed : false; 1396 }, 1397 1398 indexGivenHours = function (givenHoursArray) { 1399 // Store given enabledHours and disabledHours as keys. 1400 // This way we can check their existence in O(1) time instead of looping through whole array. 1401 // (for example: options.enabledHours['2014-02-27'] === true) 1402 var givenHoursIndexed = {}; 1403 $.each(givenHoursArray, function () { 1404 givenHoursIndexed[this] = true; 1405 }); 1406 return (Object.keys(givenHoursIndexed).length) ? givenHoursIndexed : false; 1407 }, 1408 1409 initFormatting = function () { 1410 var format = options.format || 'L LT'; 1411 1412 actualFormat = format.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput) { 1413 var newinput = date.localeData().longDateFormat(formatInput) || formatInput; 1414 return newinput.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput2) { //temp fix for #740 1415 return date.localeData().longDateFormat(formatInput2) || formatInput2; 1416 }); 1417 }); 1418 1419 1420 parseFormats = options.extraFormats ? options.extraFormats.slice() : []; 1421 if (parseFormats.indexOf(format) < 0 && parseFormats.indexOf(actualFormat) < 0) { 1422 parseFormats.push(actualFormat); 1423 } 1424 1425 use24Hours = (actualFormat.toLowerCase().indexOf('a') < 1 && actualFormat.replace(/\[.*?\]/g, '').indexOf('h') < 1); 1426 1427 if (isEnabled('y')) { 1428 minViewModeNumber = 2; 1429 } 1430 if (isEnabled('M')) { 1431 minViewModeNumber = 1; 1432 } 1433 if (isEnabled('d')) { 1434 minViewModeNumber = 0; 1435 } 1436 1437 currentViewMode = Math.max(minViewModeNumber, currentViewMode); 1438 1439 if (!unset) { 1440 setValue(date); 1441 } 1442 }; 1443 1444 /******************************************************************************** 1445 * 1446 * Public API functions 1447 * ===================== 1448 * 1449 * Important: Do not expose direct references to private objects or the options 1450 * object to the outer world. Always return a clone when returning values or make 1451 * a clone when setting a private variable. 1452 * 1453 ********************************************************************************/ 1454 picker.destroy = function () { 1455 ///<summary>Destroys the widget and removes all attached event listeners</summary> 1456 hide(); 1457 detachDatePickerElementEvents(); 1458 element.removeData('DateTimePicker'); 1459 element.removeData('date'); 1460 }; 1461 1462 picker.toggle = toggle; 1463 1464 picker.show = show; 1465 1466 picker.hide = hide; 1467 1468 picker.disable = function () { 1469 ///<summary>Disables the input element, the component is attached to, by adding a disabled="true" attribute to it. 1470 ///If the widget was visible before that call it is hidden. Possibly emits dp.hide</summary> 1471 hide(); 1472 if (component && component.hasClass('btn')) { 1473 component.addClass('disabled'); 1474 } 1475 input.prop('disabled', true); 1476 return picker; 1477 }; 1478 1479 picker.enable = function () { 1480 ///<summary>Enables the input element, the component is attached to, by removing disabled attribute from it.</summary> 1481 if (component && component.hasClass('btn')) { 1482 component.removeClass('disabled'); 1483 } 1484 input.prop('disabled', false); 1485 return picker; 1486 }; 1487 1488 picker.ignoreReadonly = function (ignoreReadonly) { 1489 if (arguments.length === 0) { 1490 return options.ignoreReadonly; 1491 } 1492 if (typeof ignoreReadonly !== 'boolean') { 1493 throw new TypeError('ignoreReadonly () expects a boolean parameter'); 1494 } 1495 options.ignoreReadonly = ignoreReadonly; 1496 return picker; 1497 }; 1498 1499 picker.options = function (newOptions) { 1500 if (arguments.length === 0) { 1501 return $.extend(true, {}, options); 1502 } 1503 1504 if (!(newOptions instanceof Object)) { 1505 throw new TypeError('options() options parameter should be an object'); 1506 } 1507 $.extend(true, options, newOptions); 1508 $.each(options, function (key, value) { 1509 if (picker[key] !== undefined) { 1510 picker[key](value); 1511 } else { 1512 throw new TypeError('option ' + key + ' is not recognized!'); 1513 } 1514 }); 1515 return picker; 1516 }; 1517 1518 picker.date = function (newDate) { 1519 ///<signature helpKeyword="$.fn.datetimepicker.date"> 1520 ///<summary>Returns the component's model current date, a moment object or null if not set.</summary> 1521 ///<returns type="Moment">date.clone()</returns> 1522 ///</signature> 1523 ///<signature> 1524 ///<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> 1525 ///<param name="newDate" locid="$.fn.datetimepicker.date_p:newDate">Takes string, Date, moment, null parameter.</param> 1526 ///</signature> 1527 if (arguments.length === 0) { 1528 if (unset) { 1529 return null; 1530 } 1531 return date.clone(); 1532 } 1533 1534 if (newDate !== null && typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) { 1535 throw new TypeError('date() parameter must be one of [null, string, moment or Date]'); 1536 } 1537 1538 setValue(newDate === null ? null : parseInputDate(newDate)); 1539 return picker; 1540 }; 1541 1542 picker.format = function (newFormat) { 1543 ///<summary>test su</summary> 1544 ///<param name="newFormat">info about para</param> 1545 ///<returns type="string|boolean">returns foo</returns> 1546 if (arguments.length === 0) { 1547 return options.format; 1548 } 1549 1550 if ((typeof newFormat !== 'string') && ((typeof newFormat !== 'boolean') || (newFormat !== false))) { 1551 throw new TypeError('format() expects a string or boolean:false parameter ' + newFormat); 1552 } 1553 1554 options.format = newFormat; 1555 if (actualFormat) { 1556 initFormatting(); // reinit formatting 1557 } 1558 return picker; 1559 }; 1560 1561 picker.timeZone = function (newZone) { 1562 if (arguments.length === 0) { 1563 return options.timeZone; 1564 } 1565 1566 if (typeof newZone !== 'string') { 1567 throw new TypeError('newZone() expects a string parameter'); 1568 } 1569 1570 options.timeZone = newZone; 1571 1572 return picker; 1573 }; 1574 1575 picker.dayViewHeaderFormat = function (newFormat) { 1576 if (arguments.length === 0) { 1577 return options.dayViewHeaderFormat; 1578 } 1579 1580 if (typeof newFormat !== 'string') { 1581 throw new TypeError('dayViewHeaderFormat() expects a string parameter'); 1582 } 1583 1584 options.dayViewHeaderFormat = newFormat; 1585 return picker; 1586 }; 1587 1588 picker.extraFormats = function (formats) { 1589 if (arguments.length === 0) { 1590 return options.extraFormats; 1591 } 1592 1593 if (formats !== false && !(formats instanceof Array)) { 1594 throw new TypeError('extraFormats() expects an array or false parameter'); 1595 } 1596 1597 options.extraFormats = formats; 1598 if (parseFormats) { 1599 initFormatting(); // reinit formatting 1600 } 1601 return picker; 1602 }; 1603 1604 picker.disabledDates = function (dates) { 1605 ///<signature helpKeyword="$.fn.datetimepicker.disabledDates"> 1606 ///<summary>Returns an array with the currently set disabled dates on the component.</summary> 1607 ///<returns type="array">options.disabledDates</returns> 1608 ///</signature> 1609 ///<signature> 1610 ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of 1611 ///options.enabledDates if such exist.</summary> 1612 ///<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.</param> 1613 ///</signature> 1614 if (arguments.length === 0) { 1615 return (options.disabledDates ? $.extend({}, options.disabledDates) : options.disabledDates); 1616 } 1617 1618 if (!dates) { 1619 options.disabledDates = false; 1620 update(); 1621 return picker; 1622 } 1623 if (!(dates instanceof Array)) { 1624 throw new TypeError('disabledDates() expects an array parameter'); 1625 } 1626 options.disabledDates = indexGivenDates(dates); 1627 options.enabledDates = false; 1628 update(); 1629 return picker; 1630 }; 1631 1632 picker.enabledDates = function (dates) { 1633 ///<signature helpKeyword="$.fn.datetimepicker.enabledDates"> 1634 ///<summary>Returns an array with the currently set enabled dates on the component.</summary> 1635 ///<returns type="array">options.enabledDates</returns> 1636 ///</signature> 1637 ///<signature> 1638 ///<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> 1639 ///<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> 1640 ///</signature> 1641 if (arguments.length === 0) { 1642 return (options.enabledDates ? $.extend({}, options.enabledDates) : options.enabledDates); 1643 } 1644 1645 if (!dates) { 1646 options.enabledDates = false; 1647 update(); 1648 return picker; 1649 } 1650 if (!(dates instanceof Array)) { 1651 throw new TypeError('enabledDates() expects an array parameter'); 1652 } 1653 options.enabledDates = indexGivenDates(dates); 1654 options.disabledDates = false; 1655 update(); 1656 return picker; 1657 }; 1658 1659 picker.daysOfWeekDisabled = function (daysOfWeekDisabled) { 1660 if (arguments.length === 0) { 1661 return options.daysOfWeekDisabled.splice(0); 1662 } 1663 1664 if ((typeof daysOfWeekDisabled === 'boolean') && !daysOfWeekDisabled) { 1665 options.daysOfWeekDisabled = false; 1666 update(); 1667 return picker; 1668 } 1669 1670 if (!(daysOfWeekDisabled instanceof Array)) { 1671 throw new TypeError('daysOfWeekDisabled() expects an array parameter'); 1672 } 1673 options.daysOfWeekDisabled = daysOfWeekDisabled.reduce(function (previousValue, currentValue) { 1674 currentValue = parseInt(currentValue, 10); 1675 if (currentValue > 6 || currentValue < 0 || isNaN(currentValue)) { 1676 return previousValue; 1677 } 1678 if (previousValue.indexOf(currentValue) === -1) { 1679 previousValue.push(currentValue); 1680 } 1681 return previousValue; 1682 }, []).sort(); 1683 if (options.useCurrent && !options.keepInvalid) { 1684 var tries = 0; 1685 while (!isValid(date, 'd')) { 1686 date.add(1, 'd'); 1687 if (tries === 31) { 1688 throw 'Tried 31 times to find a valid date'; 1689 } 1690 tries++; 1691 } 1692 setValue(date); 1693 } 1694 update(); 1695 return picker; 1696 }; 1697 1698 picker.maxDate = function (maxDate) { 1699 if (arguments.length === 0) { 1700 return options.maxDate ? options.maxDate.clone() : options.maxDate; 1701 } 1702 1703 if ((typeof maxDate === 'boolean') && maxDate === false) { 1704 options.maxDate = false; 1705 update(); 1706 return picker; 1707 } 1708 1709 if (typeof maxDate === 'string') { 1710 if (maxDate === 'now' || maxDate === 'moment') { 1711 maxDate = getMoment(); 1712 } 1713 } 1714 1715 var parsedDate = parseInputDate(maxDate); 1716 1717 if (!parsedDate.isValid()) { 1718 throw new TypeError('maxDate() Could not parse date parameter: ' + maxDate); 1719 } 1720 if (options.minDate && parsedDate.isBefore(options.minDate)) { 1721 throw new TypeError('maxDate() date parameter is before options.minDate: ' + parsedDate.format(actualFormat)); 1722 } 1723 options.maxDate = parsedDate; 1724 if (options.useCurrent && !options.keepInvalid && date.isAfter(maxDate)) { 1725 setValue(options.maxDate); 1726 } 1727 if (viewDate.isAfter(parsedDate)) { 1728 viewDate = parsedDate.clone().subtract(options.stepping, 'm'); 1729 } 1730 update(); 1731 return picker; 1732 }; 1733 1734 picker.minDate = function (minDate) { 1735 if (arguments.length === 0) { 1736 return options.minDate ? options.minDate.clone() : options.minDate; 1737 } 1738 1739 if ((typeof minDate === 'boolean') && minDate === false) { 1740 options.minDate = false; 1741 update(); 1742 return picker; 1743 } 1744 1745 if (typeof minDate === 'string') { 1746 if (minDate === 'now' || minDate === 'moment') { 1747 minDate = getMoment(); 1748 } 1749 } 1750 1751 var parsedDate = parseInputDate(minDate); 1752 1753 if (!parsedDate.isValid()) { 1754 throw new TypeError('minDate() Could not parse date parameter: ' + minDate); 1755 } 1756 if (options.maxDate && parsedDate.isAfter(options.maxDate)) { 1757 throw new TypeError('minDate() date parameter is after options.maxDate: ' + parsedDate.format(actualFormat)); 1758 } 1759 options.minDate = parsedDate; 1760 if (options.useCurrent && !options.keepInvalid && date.isBefore(minDate)) { 1761 setValue(options.minDate); 1762 } 1763 if (viewDate.isBefore(parsedDate)) { 1764 viewDate = parsedDate.clone().add(options.stepping, 'm'); 1765 } 1766 update(); 1767 return picker; 1768 }; 1769 1770 picker.defaultDate = function (defaultDate) { 1771 ///<signature helpKeyword="$.fn.datetimepicker.defaultDate"> 1772 ///<summary>Returns a moment with the options.defaultDate option configuration or false if not set</summary> 1773 ///<returns type="Moment">date.clone()</returns> 1774 ///</signature> 1775 ///<signature> 1776 ///<summary>Will set the picker's inital date. If a boolean:false value is passed the options.defaultDate parameter is cleared.</summary> 1777 ///<param name="defaultDate" locid="$.fn.datetimepicker.defaultDate_p:defaultDate">Takes a string, Date, moment, boolean:false</param> 1778 ///</signature> 1779 if (arguments.length === 0) { 1780 return options.defaultDate ? options.defaultDate.clone() : options.defaultDate; 1781 } 1782 if (!defaultDate) { 1783 options.defaultDate = false; 1784 return picker; 1785 } 1786 1787 if (typeof defaultDate === 'string') { 1788 if (defaultDate === 'now' || defaultDate === 'moment') { 1789 defaultDate = getMoment(); 1790 } else { 1791 defaultDate = getMoment(defaultDate); 1792 } 1793 } 1794 1795 var parsedDate = parseInputDate(defaultDate); 1796 if (!parsedDate.isValid()) { 1797 throw new TypeError('defaultDate() Could not parse date parameter: ' + defaultDate); 1798 } 1799 if (!isValid(parsedDate)) { 1800 throw new TypeError('defaultDate() date passed is invalid according to component setup validations'); 1801 } 1802 1803 options.defaultDate = parsedDate; 1804 1805 if ((options.defaultDate && options.inline) || input.val().trim() === '') { 1806 setValue(options.defaultDate); 1807 } 1808 return picker; 1809 }; 1810 1811 picker.locale = function (locale) { 1812 if (arguments.length === 0) { 1813 return options.locale; 1814 } 1815 1816 if (!moment.localeData(locale)) { 1817 throw new TypeError('locale() locale ' + locale + ' is not loaded from moment locales!'); 1818 } 1819 1820 options.locale = locale; 1821 date.locale(options.locale); 1822 viewDate.locale(options.locale); 1823 1824 if (actualFormat) { 1825 initFormatting(); // reinit formatting 1826 } 1827 if (widget) { 1828 hide(); 1829 show(); 1830 } 1831 return picker; 1832 }; 1833 1834 picker.stepping = function (stepping) { 1835 if (arguments.length === 0) { 1836 return options.stepping; 1837 } 1838 1839 stepping = parseInt(stepping, 10); 1840 if (isNaN(stepping) || stepping < 1) { 1841 stepping = 1; 1842 } 1843 options.stepping = stepping; 1844 return picker; 1845 }; 1846 1847 picker.useCurrent = function (useCurrent) { 1848 var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute']; 1849 if (arguments.length === 0) { 1850 return options.useCurrent; 1851 } 1852 1853 if ((typeof useCurrent !== 'boolean') && (typeof useCurrent !== 'string')) { 1854 throw new TypeError('useCurrent() expects a boolean or string parameter'); 1855 } 1856 if (typeof useCurrent === 'string' && useCurrentOptions.indexOf(useCurrent.toLowerCase()) === -1) { 1857 throw new TypeError('useCurrent() expects a string parameter of ' + useCurrentOptions.join(', ')); 1858 } 1859 options.useCurrent = useCurrent; 1860 return picker; 1861 }; 1862 1863 picker.collapse = function (collapse) { 1864 if (arguments.length === 0) { 1865 return options.collapse; 1866 } 1867 1868 if (typeof collapse !== 'boolean') { 1869 throw new TypeError('collapse() expects a boolean parameter'); 1870 } 1871 if (options.collapse === collapse) { 1872 return picker; 1873 } 1874 options.collapse = collapse; 1875 if (widget) { 1876 hide(); 1877 show(); 1878 } 1879 return picker; 1880 }; 1881 1882 picker.icons = function (icons) { 1883 if (arguments.length === 0) { 1884 return $.extend({}, options.icons); 1885 } 1886 1887 if (!(icons instanceof Object)) { 1888 throw new TypeError('icons() expects parameter to be an Object'); 1889 } 1890 $.extend(options.icons, icons); 1891 if (widget) { 1892 hide(); 1893 show(); 1894 } 1895 return picker; 1896 }; 1897 1898 picker.tooltips = function (tooltips) { 1899 if (arguments.length === 0) { 1900 return $.extend({}, options.tooltips); 1901 } 1902 1903 if (!(tooltips instanceof Object)) { 1904 throw new TypeError('tooltips() expects parameter to be an Object'); 1905 } 1906 $.extend(options.tooltips, tooltips); 1907 if (widget) { 1908 hide(); 1909 show(); 1910 } 1911 return picker; 1912 }; 1913 1914 picker.useStrict = function (useStrict) { 1915 if (arguments.length === 0) { 1916 return options.useStrict; 1917 } 1918 1919 if (typeof useStrict !== 'boolean') { 1920 throw new TypeError('useStrict() expects a boolean parameter'); 1921 } 1922 options.useStrict = useStrict; 1923 return picker; 1924 }; 1925 1926 picker.sideBySide = function (sideBySide) { 1927 if (arguments.length === 0) { 1928 return options.sideBySide; 1929 } 1930 1931 if (typeof sideBySide !== 'boolean') { 1932 throw new TypeError('sideBySide() expects a boolean parameter'); 1933 } 1934 options.sideBySide = sideBySide; 1935 if (widget) { 1936 hide(); 1937 show(); 1938 } 1939 return picker; 1940 }; 1941 1942 picker.viewMode = function (viewMode) { 1943 if (arguments.length === 0) { 1944 return options.viewMode; 1945 } 1946 1947 if (typeof viewMode !== 'string') { 1948 throw new TypeError('viewMode() expects a string parameter'); 1949 } 1950 1951 if (viewModes.indexOf(viewMode) === -1) { 1952 throw new TypeError('viewMode() parameter must be one of (' + viewModes.join(', ') + ') value'); 1953 } 1954 1955 options.viewMode = viewMode; 1956 currentViewMode = Math.max(viewModes.indexOf(viewMode), minViewModeNumber); 1957 1958 showMode(); 1959 return picker; 1960 }; 1961 1962 picker.toolbarPlacement = function (toolbarPlacement) { 1963 if (arguments.length === 0) { 1964 return options.toolbarPlacement; 1965 } 1966 1967 if (typeof toolbarPlacement !== 'string') { 1968 throw new TypeError('toolbarPlacement() expects a string parameter'); 1969 } 1970 if (toolbarPlacements.indexOf(toolbarPlacement) === -1) { 1971 throw new TypeError('toolbarPlacement() parameter must be one of (' + toolbarPlacements.join(', ') + ') value'); 1972 } 1973 options.toolbarPlacement = toolbarPlacement; 1974 1975 if (widget) { 1976 hide(); 1977 show(); 1978 } 1979 return picker; 1980 }; 1981 1982 picker.widgetPositioning = function (widgetPositioning) { 1983 if (arguments.length === 0) { 1984 return $.extend({}, options.widgetPositioning); 1985 } 1986 1987 if (({}).toString.call(widgetPositioning) !== '[object Object]') { 1988 throw new TypeError('widgetPositioning() expects an object variable'); 1989 } 1990 if (widgetPositioning.horizontal) { 1991 if (typeof widgetPositioning.horizontal !== 'string') { 1992 throw new TypeError('widgetPositioning() horizontal variable must be a string'); 1993 } 1994 widgetPositioning.horizontal = widgetPositioning.horizontal.toLowerCase(); 1995 if (horizontalModes.indexOf(widgetPositioning.horizontal) === -1) { 1996 throw new TypeError('widgetPositioning() expects horizontal parameter to be one of (' + horizontalModes.join(', ') + ')'); 1997 } 1998 options.widgetPositioning.horizontal = widgetPositioning.horizontal; 1999 } 2000 if (widgetPositioning.vertical) { 2001 if (typeof widgetPositioning.vertical !== 'string') { 2002 throw new TypeError('widgetPositioning() vertical variable must be a string'); 2003 } 2004 widgetPositioning.vertical = widgetPositioning.vertical.toLowerCase(); 2005 if (verticalModes.indexOf(widgetPositioning.vertical) === -1) { 2006 throw new TypeError('widgetPositioning() expects vertical parameter to be one of (' + verticalModes.join(', ') + ')'); 2007 } 2008 options.widgetPositioning.vertical = widgetPositioning.vertical; 2009 } 2010 update(); 2011 return picker; 2012 }; 2013 2014 picker.calendarWeeks = function (calendarWeeks) { 2015 if (arguments.length === 0) { 2016 return options.calendarWeeks; 2017 } 2018 2019 if (typeof calendarWeeks !== 'boolean') { 2020 throw new TypeError('calendarWeeks() expects parameter to be a boolean value'); 2021 } 2022 2023 options.calendarWeeks = calendarWeeks; 2024 update(); 2025 return picker; 2026 }; 2027 2028 picker.showTodayButton = function (showTodayButton) { 2029 if (arguments.length === 0) { 2030 return options.showTodayButton; 2031 } 2032 2033 if (typeof showTodayButton !== 'boolean') { 2034 throw new TypeError('showTodayButton() expects a boolean parameter'); 2035 } 2036 2037 options.showTodayButton = showTodayButton; 2038 if (widget) { 2039 hide(); 2040 show(); 2041 } 2042 return picker; 2043 }; 2044 2045 picker.showClear = function (showClear) { 2046 if (arguments.length === 0) { 2047 return options.showClear; 2048 } 2049 2050 if (typeof showClear !== 'boolean') { 2051 throw new TypeError('showClear() expects a boolean parameter'); 2052 } 2053 2054 options.showClear = showClear; 2055 if (widget) { 2056 hide(); 2057 show(); 2058 } 2059 return picker; 2060 }; 2061 2062 picker.widgetParent = function (widgetParent) { 2063 if (arguments.length === 0) { 2064 return options.widgetParent; 2065 } 2066 2067 if (typeof widgetParent === 'string') { 2068 widgetParent = $(widgetParent); 2069 } 2070 2071 if (widgetParent !== null && (typeof widgetParent !== 'string' && !(widgetParent instanceof $))) { 2072 throw new TypeError('widgetParent() expects a string or a jQuery object parameter'); 2073 } 2074 2075 options.widgetParent = widgetParent; 2076 if (widget) { 2077 hide(); 2078 show(); 2079 } 2080 return picker; 2081 }; 2082 2083 picker.keepOpen = function (keepOpen) { 2084 if (arguments.length === 0) { 2085 return options.keepOpen; 2086 } 2087 2088 if (typeof keepOpen !== 'boolean') { 2089 throw new TypeError('keepOpen() expects a boolean parameter'); 2090 } 2091 2092 options.keepOpen = keepOpen; 2093 return picker; 2094 }; 2095 2096 picker.focusOnShow = function (focusOnShow) { 2097 if (arguments.length === 0) { 2098 return options.focusOnShow; 2099 } 2100 2101 if (typeof focusOnShow !== 'boolean') { 2102 throw new TypeError('focusOnShow() expects a boolean parameter'); 2103 } 2104 2105 options.focusOnShow = focusOnShow; 2106 return picker; 2107 }; 2108 2109 picker.inline = function (inline) { 2110 if (arguments.length === 0) { 2111 return options.inline; 2112 } 2113 2114 if (typeof inline !== 'boolean') { 2115 throw new TypeError('inline() expects a boolean parameter'); 2116 } 2117 2118 options.inline = inline; 2119 return picker; 2120 }; 2121 2122 picker.clear = function () { 2123 clear(); 2124 return picker; 2125 }; 2126 2127 picker.keyBinds = function (keyBinds) { 2128 if (arguments.length === 0) { 2129 return options.keyBinds; 2130 } 2131 2132 options.keyBinds = keyBinds; 2133 return picker; 2134 }; 2135 2136 picker.getMoment = function (d) { 2137 return getMoment(d); 2138 }; 2139 2140 picker.debug = function (debug) { 2141 if (typeof debug !== 'boolean') { 2142 throw new TypeError('debug() expects a boolean parameter'); 2143 } 2144 2145 options.debug = debug; 2146 return picker; 2147 }; 2148 2149 picker.allowInputToggle = function (allowInputToggle) { 2150 if (arguments.length === 0) { 2151 return options.allowInputToggle; 2152 } 2153 2154 if (typeof allowInputToggle !== 'boolean') { 2155 throw new TypeError('allowInputToggle() expects a boolean parameter'); 2156 } 2157 2158 options.allowInputToggle = allowInputToggle; 2159 return picker; 2160 }; 2161 2162 picker.showClose = function (showClose) { 2163 if (arguments.length === 0) { 2164 return options.showClose; 2165 } 2166 2167 if (typeof showClose !== 'boolean') { 2168 throw new TypeError('showClose() expects a boolean parameter'); 2169 } 2170 2171 options.showClose = showClose; 2172 return picker; 2173 }; 2174 2175 picker.keepInvalid = function (keepInvalid) { 2176 if (arguments.length === 0) { 2177 return options.keepInvalid; 2178 } 2179 2180 if (typeof keepInvalid !== 'boolean') { 2181 throw new TypeError('keepInvalid() expects a boolean parameter'); 2182 } 2183 options.keepInvalid = keepInvalid; 2184 return picker; 2185 }; 2186 2187 picker.datepickerInput = function (datepickerInput) { 2188 if (arguments.length === 0) { 2189 return options.datepickerInput; 2190 } 2191 2192 if (typeof datepickerInput !== 'string') { 2193 throw new TypeError('datepickerInput() expects a string parameter'); 2194 } 2195 2196 options.datepickerInput = datepickerInput; 2197 return picker; 2198 }; 2199 2200 picker.parseInputDate = function (parseInputDate) { 2201 if (arguments.length === 0) { 2202 return options.parseInputDate; 2203 } 2204 2205 if (typeof parseInputDate !== 'function') { 2206 throw new TypeError('parseInputDate() sholud be as function'); 2207 } 2208 2209 options.parseInputDate = parseInputDate; 2210 2211 return picker; 2212 }; 2213 2214 picker.disabledTimeIntervals = function (disabledTimeIntervals) { 2215 ///<signature helpKeyword="$.fn.datetimepicker.disabledTimeIntervals"> 2216 ///<summary>Returns an array with the currently set disabled dates on the component.</summary> 2217 ///<returns type="array">options.disabledTimeIntervals</returns> 2218 ///</signature> 2219 ///<signature> 2220 ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of 2221 ///options.enabledDates if such exist.</summary> 2222 ///<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> 2223 ///</signature> 2224 if (arguments.length === 0) { 2225 return (options.disabledTimeIntervals ? $.extend({}, options.disabledTimeIntervals) : options.disabledTimeIntervals); 2226 } 2227 2228 if (!disabledTimeIntervals) { 2229 options.disabledTimeIntervals = false; 2230 update(); 2231 return picker; 2232 } 2233 if (!(disabledTimeIntervals instanceof Array)) { 2234 throw new TypeError('disabledTimeIntervals() expects an array parameter'); 2235 } 2236 options.disabledTimeIntervals = disabledTimeIntervals; 2237 update(); 2238 return picker; 2239 }; 2240 2241 picker.disabledHours = function (hours) { 2242 ///<signature helpKeyword="$.fn.datetimepicker.disabledHours"> 2243 ///<summary>Returns an array with the currently set disabled hours on the component.</summary> 2244 ///<returns type="array">options.disabledHours</returns> 2245 ///</signature> 2246 ///<signature> 2247 ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of 2248 ///options.enabledHours if such exist.</summary> 2249 ///<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> 2250 ///</signature> 2251 if (arguments.length === 0) { 2252 return (options.disabledHours ? $.extend({}, options.disabledHours) : options.disabledHours); 2253 } 2254 2255 if (!hours) { 2256 options.disabledHours = false; 2257 update(); 2258 return picker; 2259 } 2260 if (!(hours instanceof Array)) { 2261 throw new TypeError('disabledHours() expects an array parameter'); 2262 } 2263 options.disabledHours = indexGivenHours(hours); 2264 options.enabledHours = false; 2265 if (options.useCurrent && !options.keepInvalid) { 2266 var tries = 0; 2267 while (!isValid(date, 'h')) { 2268 date.add(1, 'h'); 2269 if (tries === 24) { 2270 throw 'Tried 24 times to find a valid date'; 2271 } 2272 tries++; 2273 } 2274 setValue(date); 2275 } 2276 update(); 2277 return picker; 2278 }; 2279 2280 picker.enabledHours = function (hours) { 2281 ///<signature helpKeyword="$.fn.datetimepicker.enabledHours"> 2282 ///<summary>Returns an array with the currently set enabled hours on the component.</summary> 2283 ///<returns type="array">options.enabledHours</returns> 2284 ///</signature> 2285 ///<signature> 2286 ///<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> 2287 ///<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> 2288 ///</signature> 2289 if (arguments.length === 0) { 2290 return (options.enabledHours ? $.extend({}, options.enabledHours) : options.enabledHours); 2291 } 2292 2293 if (!hours) { 2294 options.enabledHours = false; 2295 update(); 2296 return picker; 2297 } 2298 if (!(hours instanceof Array)) { 2299 throw new TypeError('enabledHours() expects an array parameter'); 2300 } 2301 options.enabledHours = indexGivenHours(hours); 2302 options.disabledHours = false; 2303 if (options.useCurrent && !options.keepInvalid) { 2304 var tries = 0; 2305 while (!isValid(date, 'h')) { 2306 date.add(1, 'h'); 2307 if (tries === 24) { 2308 throw 'Tried 24 times to find a valid date'; 2309 } 2310 tries++; 2311 } 2312 setValue(date); 2313 } 2314 update(); 2315 return picker; 2316 }; 2317 /** 2318 * 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. 2319 * @param {Takes string, viewDate, moment, null parameter.} newDate 2320 * @returns {viewDate.clone()} 2321 */ 2322 picker.viewDate = function (newDate) { 2323 if (arguments.length === 0) { 2324 return viewDate.clone(); 2325 } 2326 2327 if (!newDate) { 2328 viewDate = date.clone(); 2329 return picker; 2330 } 2331 2332 if (typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) { 2333 throw new TypeError('viewDate() parameter must be one of [string, moment or Date]'); 2334 } 2335 2336 viewDate = parseInputDate(newDate); 2337 viewUpdate(); 2338 return picker; 2339 }; 2340 2341 // initializing element and component attributes 2342 if (element.is('input')) { 2343 input = element; 2344 } else { 2345 input = element.find(options.datepickerInput); 2346 if (input.length === 0) { 2347 input = element.find('input'); 2348 } else if (!input.is('input')) { 2349 throw new Error('CSS class "' + options.datepickerInput + '" cannot be applied to non input element'); 2350 } 2351 } 2352 2353 if (element.hasClass('input-group')) { 2354 // in case there is more then one 'input-group-addon' Issue #48 2355 // issue - BS4.4 dropped support for input-group-addon -- changed to input-group-text 2356 if (element.find('.datepickerbutton').length === 0) { 2357 component = element.find('.input-group-text'); 2358 if (component.length === 0) { 2359 component = element.find('.input-group-addon'); 2360 } 2361 } else { 2362 component = element.find('.datepickerbutton'); 2363 } 2364 } 2365 2366 if (!options.inline && !input.is('input')) { 2367 throw new Error('Could not initialize DateTimePicker without an input element'); 2368 } 2369 2370 // Set defaults for date here now instead of in var declaration 2371 date = getMoment(); 2372 viewDate = date.clone(); 2373 2374 $.extend(true, options, dataToOptions()); 2375 2376 picker.options(options); 2377 2378 initFormatting(); 2379 2380 attachDatePickerElementEvents(); 2381 2382 if (input.prop('disabled')) { 2383 picker.disable(); 2384 } 2385 if (input.is('input') && input.val().trim().length !== 0) { 2386 setValue(parseInputDate(input.val().trim())); 2387 } 2388 else if (options.defaultDate && input.attr('placeholder') === undefined) { 2389 setValue(options.defaultDate); 2390 } 2391 if (options.inline) { 2392 show(); 2393 } 2394 return picker; 2395 }; 2396 2397 /******************************************************************************** 2398 * 2399 * jQuery plugin constructor and defaults object 2400 * 2401 ********************************************************************************/ 2402 2403 /** 2404 * See (http://jquery.com/). 2405 * @name jQuery 2406 * @class 2407 * See the jQuery Library (http://jquery.com/) for full details. This just 2408 * documents the function and classes that are added to jQuery by this plug-in. 2409 */ 2410 /** 2411 * See (http://jquery.com/) 2412 * @name fn 2413 * @class 2414 * See the jQuery Library (http://jquery.com/) for full details. This just 2415 * documents the function and classes that are added to jQuery by this plug-in. 2416 * @memberOf jQuery 2417 */ 2418 /** 2419 * Show comments 2420 * @class datetimepicker 2421 * @memberOf jQuery.fn 2422 */ 2423 $.fn.datetimepicker = function (options) { 2424 options = options || {}; 2425 2426 var args = Array.prototype.slice.call(arguments, 1), 2427 isInstance = true, 2428 thisMethods = ['destroy', 'hide', 'show', 'toggle'], 2429 returnValue; 2430 2431 if (typeof options === 'object') { 2432 return this.each(function () { 2433 var $this = $(this), 2434 _options; 2435 if (!$this.data('DateTimePicker')) { 2436 // create a private copy of the defaults object 2437 _options = $.extend(true, {}, $.fn.datetimepicker.defaults, options); 2438 $this.data('DateTimePicker', dateTimePicker($this, _options)); 2439 } 2440 }); 2441 } else if (typeof options === 'string') { 2442 this.each(function () { 2443 var $this = $(this), 2444 instance = $this.data('DateTimePicker'); 2445 if (!instance) { 2446 throw new Error('bootstrap-datetimepicker("' + options + '") method was called on an element that is not using DateTimePicker'); 2447 } 2448 2449 returnValue = instance[options].apply(instance, args); 2450 isInstance = returnValue === instance; 2451 }); 2452 2453 if (isInstance || $.inArray(options, thisMethods) > -1) { 2454 return this; 2455 } 2456 2457 return returnValue; 2458 } 2459 2460 throw new TypeError('Invalid arguments for DateTimePicker: ' + options); 2461 }; 2462 2463 $.fn.datetimepicker.defaults = { 2464 timeZone: '', 2465 format: false, 2466 dayViewHeaderFormat: 'MMMM YYYY', 2467 extraFormats: false, 2468 stepping: 1, 2469 minDate: false, 2470 maxDate: false, 2471 useCurrent: true, 2472 collapse: true, 2473 locale: moment.locale(), 2474 defaultDate: false, 2475 disabledDates: false, 2476 enabledDates: false, 2477 icons: { 2478 time: 'fa fa-clock-o', 2479 date: 'fa fa-calendar', 2480 up: 'fa fa-chevron-up', 2481 down: 'fa fa-chevron-down', 2482 previous: 'fa fa-chevron-left', 2483 next: 'fa fa-chevron-right', 2484 today: 'fa fa-crosshairs', 2485 clear: 'fa fa-trash-o', 2486 close: 'fa fa-times' 2487 }, 2488 tooltips: { 2489 today: 'Go to today', 2490 clear: 'Clear selection', 2491 close: 'Close the picker', 2492 selectMonth: 'Select Month', 2493 prevMonth: 'Previous Month', 2494 nextMonth: 'Next Month', 2495 selectYear: 'Select Year', 2496 prevYear: 'Previous Year', 2497 nextYear: 'Next Year', 2498 selectDecade: 'Select Decade', 2499 prevDecade: 'Previous Decade', 2500 nextDecade: 'Next Decade', 2501 prevCentury: 'Previous Century', 2502 nextCentury: 'Next Century', 2503 pickHour: 'Pick Hour', 2504 incrementHour: 'Increment Hour', 2505 decrementHour: 'Decrement Hour', 2506 pickMinute: 'Pick Minute', 2507 incrementMinute: 'Increment Minute', 2508 decrementMinute: 'Decrement Minute', 2509 pickSecond: 'Pick Second', 2510 incrementSecond: 'Increment Second', 2511 decrementSecond: 'Decrement Second', 2512 togglePeriod: 'Toggle Period', 2513 selectTime: 'Select Time', 2514 selectDate: 'Select Date' 2515 }, 2516 useStrict: false, 2517 sideBySide: false, 2518 daysOfWeekDisabled: false, 2519 calendarWeeks: false, 2520 viewMode: 'days', 2521 toolbarPlacement: 'default', 2522 showTodayButton: false, 2523 showClear: false, 2524 showClose: false, 2525 widgetPositioning: { 2526 horizontal: 'auto', 2527 vertical: 'auto' 2528 }, 2529 widgetParent: null, 2530 ignoreReadonly: false, 2531 keepOpen: false, 2532 focusOnShow: true, 2533 inline: false, 2534 keepInvalid: false, 2535 datepickerInput: '.datepickerinput', 2536 keyBinds: { 2537 up: function (widget) { 2538 if (!widget) { 2539 return; 2540 } 2541 var d = this.date() || this.getMoment(); 2542 if (widget.find('.datepicker').is(':visible')) { 2543 this.date(d.clone().subtract(7, 'd')); 2544 } else { 2545 this.date(d.clone().add(this.stepping(), 'm')); 2546 } 2547 }, 2548 down: function (widget) { 2549 if (!widget) { 2550 this.show(); 2551 return; 2552 } 2553 var d = this.date() || this.getMoment(); 2554 if (widget.find('.datepicker').is(':visible')) { 2555 this.date(d.clone().add(7, 'd')); 2556 } else { 2557 this.date(d.clone().subtract(this.stepping(), 'm')); 2558 } 2559 }, 2560 'control up': function (widget) { 2561 if (!widget) { 2562 return; 2563 } 2564 var d = this.date() || this.getMoment(); 2565 if (widget.find('.datepicker').is(':visible')) { 2566 this.date(d.clone().subtract(1, 'y')); 2567 } else { 2568 this.date(d.clone().add(1, 'h')); 2569 } 2570 }, 2571 'control down': function (widget) { 2572 if (!widget) { 2573 return; 2574 } 2575 var d = this.date() || this.getMoment(); 2576 if (widget.find('.datepicker').is(':visible')) { 2577 this.date(d.clone().add(1, 'y')); 2578 } else { 2579 this.date(d.clone().subtract(1, 'h')); 2580 } 2581 }, 2582 left: function (widget) { 2583 if (!widget) { 2584 return; 2585 } 2586 var d = this.date() || this.getMoment(); 2587 if (widget.find('.datepicker').is(':visible')) { 2588 this.date(d.clone().subtract(1, 'd')); 2589 } 2590 }, 2591 right: function (widget) { 2592 if (!widget) { 2593 return; 2594 } 2595 var d = this.date() || this.getMoment(); 2596 if (widget.find('.datepicker').is(':visible')) { 2597 this.date(d.clone().add(1, 'd')); 2598 } 2599 }, 2600 pageUp: function (widget) { 2601 if (!widget) { 2602 return; 2603 } 2604 var d = this.date() || this.getMoment(); 2605 if (widget.find('.datepicker').is(':visible')) { 2606 this.date(d.clone().subtract(1, 'M')); 2607 } 2608 }, 2609 pageDown: function (widget) { 2610 if (!widget) { 2611 return; 2612 } 2613 var d = this.date() || this.getMoment(); 2614 if (widget.find('.datepicker').is(':visible')) { 2615 this.date(d.clone().add(1, 'M')); 2616 } 2617 }, 2618 enter: function () { 2619 this.hide(); 2620 }, 2621 escape: function () { 2622 this.hide(); 2623 }, 2624 //tab: function (widget) { //this break the flow of the form. disabling for now 2625 // var toggle = widget.find('.picker-switch a[data-action="togglePicker"]'); 2626 // if(toggle.length > 0) toggle.click(); 2627 //}, 2628 'control space': function (widget) { 2629 if (!widget) { 2630 return; 2631 } 2632 if (widget.find('.timepicker').is(':visible')) { 2633 widget.find('.btn[data-action="togglePeriod"]').click(); 2634 } 2635 }, 2636 t: function () { 2637 this.date(this.getMoment()); 2638 }, 2639 'delete': function () { 2640 this.clear(); 2641 } 2642 }, 2643 debug: false, 2644 allowInputToggle: false, 2645 disabledTimeIntervals: false, 2646 disabledHours: false, 2647 enabledHours: false, 2648 viewDate: false 2649 }; 2650 2651 return $.fn.datetimepicker; 2652}));
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.