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