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