1/** 2* @version: 2.1.24 3* @author: Dan Grossman http://www.dangrossman.info/ 4* @copyright: Copyright (c) 2012-2016 Dan Grossman. All rights reserved. 5* @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php 6* @website: https://www.improvely.com/ 7*/ 8// Follow the UMD template https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js 9(function (root, factory) { 10 if (typeof define === 'function' && define.amd) { 11 // AMD. Make globaly available as well 12 define(['moment', 'jquery'], function (moment, jquery) { 13 return (root.daterangepicker = factory(moment, jquery)); 14 }); 15 } else if (typeof module === 'object' && module.exports) { 16 // Node / Browserify 17 //isomorphic issue 18 var jQuery = (typeof window != 'undefined') ? window.jQuery : undefined; 19 if (!jQuery) { 20 jQuery = require('jquery'); 21 if (!jQuery.fn) jQuery.fn = {}; 22 } 23 module.exports = factory(require('moment'), jQuery); 24 } else { 25 // Browser globals 26 root.daterangepicker = factory(root.moment, root.jQuery); 27 } 28}(this, function (moment, $) { 29 var DateRangePicker = function (element, options, cb) { 30 31 //default settings for options 32 // If there is parentEl options we set it 33 // Otherwise we check if inline options is set and we set the element.parent() by default or the body for a tootlip picker 34 this.parentEl = (options && options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : 35 (options && options.inline) ? element.parent() : $('body'); 36 this.element = $(element); 37 this.startDate = moment().startOf('day'); 38 this.endDate = moment().endOf('day'); 39 this.minDate = false; 40 this.maxDate = false; 41 this.dateLimit = false; 42 this.autoApply = false; 43 this.singleDatePicker = false; 44 this.showDropdowns = false; 45 this.showWeekNumbers = false; 46 this.showISOWeekNumbers = false; 47 this.showCustomRangeLabel = true; 48 this.timePicker = false; 49 this.timePicker24Hour = false; 50 this.timePickerIncrement = 1; 51 this.timePickerSeconds = false; 52 this.linkedCalendars = true; 53 this.autoUpdateInput = true; 54 this.alwaysShowCalendars = false; 55 this.inline = false; 56 this.ranges = {}; 57 this.hideRightCalendar = false; 58 59 this.opens = 'right'; 60 if (this.element.hasClass('pull-right')) 61 this.opens = 'left'; 62 63 this.drops = 'down'; 64 if (this.element.hasClass('dropup')) 65 this.drops = 'up'; 66 67 this.buttonClasses = 'btn btn-sm'; 68 this.applyClass = 'btn-success'; 69 this.cancelClass = 'btn-default'; 70 71 this.locale = { 72 direction: 'ltr', 73 format: 'DD/MM/YYYY', 74 separator: ' - ', 75 applyLabel: 'Apply', 76 cancelLabel: 'Cancel', 77 weekLabel: 'W', 78 customRangeLabel: 'Custom Range', 79 daysOfWeek: moment.weekdaysMin(), 80 monthNames: moment.monthsShort(), 81 firstDay: moment.localeData().firstDayOfWeek() 82 }; 83 84 this.callback = function () { }; 85 86 //some state information 87 this.isShowing = false; 88 this.leftCalendar = {}; 89 this.rightCalendar = {}; 90 91 //custom options from user 92 if (typeof options !== 'object' || options === null) 93 options = {}; 94 95 //allow setting options with data attributes 96 //data-api options will be overwritten with custom javascript options 97 options = $.extend(this.element.data(), options); 98 99 // 100 // handle all the possible options overriding defaults 101 // 102 103 if (typeof options.locale === 'object') { 104 105 if (typeof options.locale.direction === 'string') 106 this.locale.direction = options.locale.direction; 107 108 if (typeof options.locale.format === 'string') 109 this.locale.format = options.locale.format; 110 111 if (typeof options.locale.separator === 'string') 112 this.locale.separator = options.locale.separator; 113 114 if (typeof options.locale.daysOfWeek === 'object') 115 this.locale.daysOfWeek = options.locale.daysOfWeek.slice(); 116 117 if (typeof options.locale.monthNames === 'object') 118 this.locale.monthNames = options.locale.monthNames.slice(); 119 120 if (typeof options.locale.firstDay === 'number') 121 this.locale.firstDay = options.locale.firstDay; 122 123 if (typeof options.locale.applyLabel === 'string') 124 this.locale.applyLabel = options.locale.applyLabel; 125 126 if (typeof options.locale.cancelLabel === 'string') 127 this.locale.cancelLabel = options.locale.cancelLabel; 128 129 if (typeof options.locale.weekLabel === 'string') 130 this.locale.weekLabel = options.locale.weekLabel; 131 132 if (typeof options.locale.customRangeLabel === 'string') 133 this.locale.customRangeLabel = options.locale.customRangeLabel; 134 135 } 136 137 if (typeof options.startDate === 'string') 138 this.startDate = moment(options.startDate, this.locale.format); 139 140 if (typeof options.endDate === 'string') 141 this.endDate = moment(options.endDate, this.locale.format); 142 143 if (typeof options.minDate === 'string') 144 this.minDate = moment(options.minDate, this.locale.format); 145 146 if (typeof options.maxDate === 'string') 147 this.maxDate = moment(options.maxDate, this.locale.format); 148 149 if (typeof options.startDate === 'object') 150 this.startDate = moment(options.startDate); 151 152 if (typeof options.endDate === 'object') 153 this.endDate = moment(options.endDate); 154 155 if (typeof options.minDate === 'object') 156 this.minDate = moment(options.minDate); 157 158 if (typeof options.maxDate === 'object') 159 this.maxDate = moment(options.maxDate); 160 161 // sanity check for bad options 162 if (this.minDate && this.startDate.isBefore(this.minDate)) 163 this.startDate = this.minDate.clone(); 164 165 // sanity check for bad options 166 if (this.maxDate && this.endDate.isAfter(this.maxDate)) 167 this.endDate = this.maxDate.clone(); 168 169 if (typeof options.applyClass === 'string') 170 this.applyClass = options.applyClass; 171 172 if (typeof options.cancelClass === 'string') 173 this.cancelClass = options.cancelClass; 174 175 if (typeof options.dateLimit === 'object') 176 this.dateLimit = options.dateLimit; 177 178 if (typeof options.opens === 'string') 179 this.opens = options.opens; 180 181 if (typeof options.drops === 'string') 182 this.drops = options.drops; 183 184 if (typeof options.showWeekNumbers === 'boolean') 185 this.showWeekNumbers = options.showWeekNumbers; 186 187 if (typeof options.showISOWeekNumbers === 'boolean') 188 this.showISOWeekNumbers = options.showISOWeekNumbers; 189
190 if (typeof options.buttonClasses === 'string') 191 this.buttonClasses = options.buttonClasses; 192 193 if (typeof options.buttonClasses === 'object') 194 this.buttonClasses = options.buttonClasses.join(' '); 195 196 if (typeof options.showDropdowns === 'boolean') 197 this.showDropdowns = options.showDropdowns; 198 199 if (typeof options.showCustomRangeLabel === 'boolean') 200 this.showCustomRangeLabel = options.showCustomRangeLabel; 201 202 if (typeof options.singleDatePicker === 'boolean') { 203 this.singleDatePicker = options.singleDatePicker; 204 if (this.singleDatePicker) 205 this.endDate = this.startDate.clone(); 206 } 207 208 if (typeof options.hideRightCalendar === 'boolean') { 209 this.hideRightCalendar = options.hideRightCalendar; 210 if (this.hideRightCalendar) 211 this.endDate = this.startDate.clone(); 212 } 213 214 215 216 if (typeof options.timePicker === 'boolean') 217 this.timePicker = options.timePicker; 218 219 if (typeof options.timePickerSeconds === 'boolean') 220 this.timePickerSeconds = options.timePickerSeconds; 221 222 if (typeof options.timePickerIncrement === 'number') 223 this.timePickerIncrement = options.timePickerIncrement; 224 225 if (typeof options.timePicker24Hour === 'boolean') 226 this.timePicker24Hour = options.timePicker24Hour; 227 228 if (typeof options.autoApply === 'boolean') 229 this.autoApply = options.autoApply; 230 231 if (typeof options.autoUpdateInput === 'boolean') 232 this.autoUpdateInput = options.autoUpdateInput; 233 234 if (typeof options.linkedCalendars === 'boolean') 235 this.linkedCalendars = options.linkedCalendars; 236 237 if (typeof options.isInvalidDate === 'function') 238 this.isInvalidDate = options.isInvalidDate; 239 240 if (typeof options.isCustomDate === 'function') 241 this.isCustomDate = options.isCustomDate; 242 243 if (typeof options.alwaysShowCalendars === 'boolean') 244 this.alwaysShowCalendars = options.alwaysShowCalendars; 245 246 if (typeof options.inline === 'boolean') 247 this.inline = options.inline; 248 249 // update day names order to firstDay 250 if (this.locale.firstDay != 0) { 251 var iterator = this.locale.firstDay; 252 while (iterator > 0) { 253 this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift()); 254 iterator--; 255 } 256 } 257 258 //html template for the picker UI 259 260 261 262 if (this.hideRightCalendar) { //agenda 263 if (typeof options.template !== 'string' && !(options.template instanceof $)) 264 options.template = '<div style="position:relative !important;top: 0; left: 0;" class="col-sm-12 daterangepicker ' + (this.inline ? 'daterangepicker-inline' : 'daterangepicker-tooltip dropdown-menu') + '">' + 265 '<div class="calendar left">' + 266 '<div class="daterangepicker_input">' + 267 '<input style="display:none;" class="input-mini form-control" type="hidden" id="daterangepicker_start" name="daterangepicker_start" value="" />' + 268 '<div class="calendar-time">' + 269 '<div></div>' + 270 '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' + 271 '</div>' + 272 '</div>' + 273 '<div class="calendar-table"></div>' + 274 '</div>' + 275 '<div class="calendar right">' + 276 '<div class="daterangepicker_input">' + 277 '<input style="display:none;" class="input-mini form-control" type="hidden" id="daterangepicker_end" name="daterangepicker_end" value="" />' + 278 '<div class="calendar-time">' + 279 '<div></div>' + 280 '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' + 281 '</div>' + 282 '</div>' + 283 '</div>' + 284 285 '</div>'; 286 287 } else { 288 289 if (typeof options.template !== 'string' && !(options.template instanceof $)) 290 options.template = '<div class="daterangepicker ' + (this.inl
vendor: 5,370 bytes, lines 290-403
290ine ? 'daterangepicker-inline' : 'daterangepicker-tooltip dropdown-menu') + '">' + 291 '<div class="calendar left">' + 292 '<div class="daterangepicker_input">' + 293 '<input class="input-mini form-control" type="text" name="daterangepicker_start" value="" />' + 294 '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' + 295 '<div class="calendar-time">' + 296 '<div></div>' + 297 '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' + 298 '</div>' + 299 '</div>' + 300 '<div class="calendar-table"></div>' + 301 '</div>' + 302 '<div class="calendar right">' + 303 '<div class="daterangepicker_input">' + 304 '<input class="input-mini form-control" type="text" name="daterangepicker_end" value="" />' + 305 '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' + 306 '<div class="calendar-time">' + 307 '<div></div>' + 308 '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' + 309 '</div>' + 310 '</div>' + 311 '<div class="calendar-table"></div>' + 312 '</div>' + 313 314 '</div>'; 315 } 316 this.container = $(options.template).appendTo(this.parentEl); 317 this.container.addClass(this.locale.direction); 318 319 var start, end, range; 320 321 //if no start/end dates set, check if an input element contains initial values 322 if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') { 323 if ($(this.element).is('input[type=text]')) { 324 var val = $(this.element).val(), 325 split = val.split(this.locale.separator); 326 327 start = end = null; 328 329 if (split.length == 2) { 330 start = moment(split[0], this.locale.format); 331 end = moment(split[1], this.locale.format); 332 } else if (this.singleDatePicker && val !== "") { 333 start = moment(val, this.locale.format); 334 end = moment(val, this.locale.format); 335 } 336 if (start !== null && end !== null) { 337 this.setStartDate(start); 338 this.setEndDate(end); 339 } 340 } 341 } 342 343 if (typeof options.ranges === 'object') { 344 for (range in options.ranges) { 345 346 if (typeof options.ranges[range][0] === 'string') 347 start = moment(options.ranges[range][0], this.locale.format); 348 else 349 start = moment(options.ranges[range][0]); 350 351 if (typeof options.ranges[range][1] === 'string') 352 end = moment(options.ranges[range][1], this.locale.format); 353 else 354 end = moment(options.ranges[range][1]); 355 356 // If the start or end date exceed those allowed by the minDate or dateLimit 357 // options, shorten the range to the allowable period. 358 if (this.minDate && start.isBefore(this.minDate)) 359 start = this.minDate.clone(); 360 361 var maxDate = this.maxDate; 362 if (this.dateLimit && maxDate && start.clone().add(this.dateLimit).isAfter(maxDate)) 363 maxDate = start.clone().add(this.dateLimit); 364 if (maxDate && end.isAfter(maxDate)) 365 end = maxDate.clone(); 366 367 // If the end of the range is before the minimum or the start of the range is 368 // after the maximum, don't display this range option at all. 369 if ((this.minDate && end.isBefore(this.minDate, this.timepicker ? 'minute' : 'day')) 370 || (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day'))) 371 continue; 372 373 //Support unicode chars in the range names. 374 var elem = document.createElement('textarea'); 375 elem.innerHTML = range; 376 var rangeHtml = elem.value; 377 378 this.ranges[rangeHtml] = [start, end]; 379 } 380 381 var list = '<ul>'; 382 for (range in this.ranges) { 383 list += '<li data-range-key="' + range + '">' + range + '</li>'; 384 } 385 if (this.showCustomRangeLabel) { 386 list += '<li data-range-key="' + this.locale.customRangeLabel + '">' + this.locale.customRangeLabel + '</li>'; 387 } 388 list += '</ul>'; 389 this.container.find('.ranges').prepend(list); 390 } 391 392 if (typeof cb === 'function') { 393 this.callback = cb; 394 } 395 396 if (!this.timePicker) { 397 this.startDate = this.startDate.startOf('day'); 398 this.endDate = this.endDate.endOf('day'); 399 this.container.find('.calendar-time').hide(); 400 } 401 402 // can't be used together for now 403 // You can only use it with inline option
404 if (this.timePicker && this.autoApply && !this.inline) 405 this.autoApply = false; 406 407 if (this.autoApply && typeof options.ranges !== 'object') { 408 this.container.find('.ranges').hide(); 409 } else if (this.autoApply) { 410 this.container.find('.applyBtn, .cancelBtn').addClass('hide'); 411 } 412 413 if (this.singleDatePicker) { 414 this.container.addClass('single'); 415 this.container.find('.calendar.left').addClass('single'); 416 this.container.find('.calendar.left').show(); 417 this.container.find('.calendar.right').hide(); 418 this.container.find('.daterangepicker_input input, .daterangepicker_input > i').hide(); 419 if (this.timePicker) { 420 this.container.find('.ranges ul').hide(); 421 } else { 422 this.container.find('.ranges').hide(); 423 } 424 } 425 426 ///hideRightCalendar 427 if (this.hideRightCalendar) { 428 429 } 430 431 if ((typeof options.ranges === 'undefined' && !this.hideRightCalendar)) { 432 this.container.addClass('show-calendar'); 433 } 434 435 if ((typeof options.ranges === 'undefined' && !this.singleDatePicker) || this.alwaysShowCalendars) { 436 this.container.addClass('show-calendar'); 437 } 438 439 this.container.addClass('opens' + this.opens); 440 441 //swap the position of the predefined ranges if opens right 442 if (typeof options.ranges !== 'undefined' && this.opens == 'right') { 443 this.container.find('.ranges').prependTo(this.container.find('.calendar.left').parent()); 444 } 445 446 //apply CSS classes and labels to buttons 447 this.container.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses); 448 if (this.applyClass.length) 449 this.container.find('.applyBtn').addClass(this.applyClass); 450 if (this.cancelClass.length) 451 this.container.find('.cancelBtn').addClass(this.cancelClass); 452 this.container.find('.applyBtn').html(this.locale.applyLabel); 453 this.container.find('.cancelBtn').html(this.locale.cancelLabel); 454 455 // 456 // event listeners 457 // 458 459 this.container.find('.calendar') 460 .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this)) 461 .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this)) 462 .on('mousedown.daterangepicker', 'td.available', $.proxy(this.clickDate, this)) 463 .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this)) 464 .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this)) 465 .on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this)) 466 .on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this)) 467 .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this)) 468 .on('click.daterangepicker', '.daterangepicker_input input', $.proxy(this.showCalendars, this)) 469 .on('focus.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsFocused, this)) 470 .on('blur.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsBlurred, this)) 471 .on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this)); 472 473 this.container.find('.ranges') 474 .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this)) 475 .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this)) 476 .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this)) 477 .on('mouseenter.daterangepicker', 'li', $.proxy(this.hoverRange, this)) 478 .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this)); 479 480 if (this.element.is('input') || this.element.is('button')) { 481 this.element.on({ 482 'click.daterangepicker': $.proxy(this.show, this),
483 'focus.daterangepicker': $.proxy(this.show, this), 484 'keyup.daterangepicker': $.proxy(this.elementChanged, this), 485 'keydown.daterangepicker': $.proxy(this.keydown, this) 486 }); 487 } else { 488 this.element.on('click.daterangepicker', $.proxy(this.toggle, this)); 489 } 490 491 // 492 // if attached to a text input, set the initial value 493 // 494 495 if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { 496 this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); 497 this.element.trigger('change'); 498 } else if (this.element.is('input') && this.autoUpdateInput) { 499 this.element.val(this.startDate.format(this.locale.format)); 500 this.element.trigger('change'); 501 } 502 503 // Show the daterangepicker since it's started for inline mode 504 if (this.inline) 505 this.show() 506 }; 507 508 DateRangePicker.prototype = { 509 510 constructor: DateRangePicker, 511 512 setStartDate: function (startDate) { 513 if (typeof startDate === 'string') { 514 515 this.startDate = moment(startDate, this.locale.format); 516 517 if (this.hideRightCalendar) { 518 this.updateView(); 519 } 520 } 521 522 if (typeof startDate === 'object') 523 this.startDate = moment(startDate); 524 525 if (this.startDate.isValid()) { 526 if (!this.timePicker) 527 this.startDate = this.startDate.startOf('day'); 528 529 if (this.timePicker && this.timePickerIncrement) 530 this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); 531 532 if (this.minDate && this.startDate.isBefore(this.minDate)) { 533 this.startDate = this.minDate; 534 if (this.timePicker && this.timePickerIncrement) 535 this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); 536 } 537 538 if (this.maxDate && this.startDate.isAfter(this.maxDate)) { 539 this.startDate = this.maxDate; 540 if (this.timePicker && this.timePickerIncrement) 541 this.startDate.minute(Math.floor(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); 542 } 543 } 544 545 if (!this.isShowing) 546 this.updateElement(); 547 548 this.updateMonthsInView(); 549 550 if (startDate == "") 551 $('#datefrom').val(""); 552 else 553 $('#datefrom').val(this.startDate.format('DD-MM-YYYY')); 554 }, 555 556 setEndDate: function (endDate) { 557 if (typeof endDate === 'string') { 558 this.endDate = moment(endDate, this.locale.format); 559 560 if (this.hideRightCalendar) { 561 this.updateView(); 562 } 563 } 564 565 if (typeof endDate === 'object') 566 this.endDate = moment(endDate); 567 568 if (this.endDate.isValid()) { 569 if (!this.timePicker) 570 this.endDate = this.endDate.endOf('day'); 571 572 if (this.timePicker && this.timePickerIncrement) 573 this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement); 574 575 if (this.endDate.isBefore(this.startDate)) 576 this.endDate = this.startDate.clone(); 577 578 if (this.maxDate && this.endDate.isAfter(this.maxDate)) 579 this.endDate = this.maxDate; 580 581 if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate)) 582 this.endDate = this.startDate.clone().add(this.dateLimit); 583 584 this.previousRightTime = this.endDate.clone(); 585 } 586 587 if (!this.isShowing) 588 this.updateElement(); 589 590 this.updateMonthsInView(); 591 if (endDate == "") 592 $('#dateto').val(""); 593 else 594 $('#dateto').val(this.endDate.format('DD-MM-YYYY')); 595 596 }, 597 598 isInvalidDate: function () { 599 return false; 600 }, 601 602 isCustomDate: function () { 603 return false; 604 }, 605 606 updateView: function () { 607 if (this.timePicker) { 608 this.renderTimePicker('left'); 609 this.renderTimePicker('right'); 610 if (!this.endDate) { 611 this.container.find('.right .calendar-time select').attr('disabled', 'disabled').addClass('disabled'); 612 } else { 613 this.container.find('.right .calendar-time select').removeAttr('disabled').removeClass('disabled'); 614 } 615 } 616 if (this.endDate) { 617 this.container.find('input[name="daterangepicker_end"]').removeClass('active'); 618 this.container.find('input[name="daterangepicker_start"]').addClass('active'); 619 } else { 620 this.container.find('input[name="daterangepicker_end"]').addClass('active'); 621 this.container.find('input[name="daterangepicker_start"]').removeClass('active'); 622 } 623 this.updateMonthsInView(); 624 this.updateCalendars();
vendor: 23,411 bytes, lines 625-1145
625 this.updateFormInputs(); 626 627 628 629 ///////////////// agenda 630 /* 631 if (this.hideRightCalendar) { 632 633 var myStartDate = this.startDate; 634 $('#datefrom').val(myStartDate.format('DD-MM-YYYY')); 635 636 if (this.endDate) { 637 var myEndDate = this.endDate; 638 $('#dateto').val(myEndDate.format('DD-MM-YYYY')); 639 640 } 641 else { 642 $('#dateto').val(myStartDate.format('DD-MM-YYYY')); 643 } 644 } 645 */ 646 647 }, 648 649 updateMonthsInView: function () { 650 if (this.startDate.isValid()) { 651 if (this.endDate) { 652 //if both dates are visible already, do nothing 653 if (!this.singleDatePicker && this.leftCalendar.month && this.rightCalendar.month && 654 (this.startDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.startDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM')) 655 && 656 (this.endDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.endDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM')) 657 ) { 658 return; 659 } 660 661 this.leftCalendar.month = this.startDate.clone().date(2); 662 if (!this.linkedCalendars && (this.endDate.month() != this.startDate.month() || this.endDate.year() != this.startDate.year())) { 663 this.rightCalendar.month = this.endDate.clone().date(2); 664 } else { 665 this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month'); 666 } 667 668 } 669 else { 670 if (this.leftCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM') && this.rightCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM')) { 671 this.leftCalendar.month = this.startDate.clone().date(2); 672 this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month'); 673 } 674 } 675 } 676 else { 677 if (this.endDate.isValid()) { 678 this.leftCalendar.month = this.endDate.clone().date(2); 679 this.rightCalendar.month = this.endDate.clone().date(2).add(1, 'month'); 680 } 681 } 682 683 if (this.maxDate && this.linkedCalendars && !this.singleDatePicker && this.rightCalendar.month > this.maxDate) { 684 this.rightCalendar.month = this.maxDate.clone().date(2); 685 this.leftCalendar.month = this.maxDate.clone().date(2).subtract(1, 'month'); 686 } 687 }, 688 689 updateCalendars: function () { 690 691 if (this.timePicker) { 692 var hour, minute, second; 693 if (this.endDate) { 694 hour = parseInt(this.container.find('.left .hourselect').val(), 10); 695 minute = parseInt(this.container.find('.left .minuteselect').val(), 10); 696 second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0; 697 if (!this.timePicker24Hour) { 698 var ampm = this.container.find('.left .ampmselect').val(); 699 if (ampm === 'PM' && hour < 12) 700 hour += 12; 701 if (ampm === 'AM' && hour === 12) 702 hour = 0; 703 } 704 } else { 705 hour = parseInt(this.container.find('.right .hourselect').val(), 10); 706 minute = parseInt(this.container.find('.right .minuteselect').val(), 10); 707 second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0; 708 if (!this.timePicker24Hour) { 709 var ampm = this.container.find('.right .ampmselect').val(); 710 if (ampm === 'PM' && hour < 12) 711 hour += 12; 712 if (ampm === 'AM' && hour === 12) 713 hour = 0; 714 } 715 } 716 this.leftCalendar.month.hour(hour).minute(minute).second(second); 717 this.rightCalendar.month.hour(hour).minute(minute).second(second); 718 } 719 720 this.renderCalendar('left'); 721 this.renderCalendar('right'); 722 723 //highlight any predefined range matching the current start and end dates 724 this.container.find('.ranges li').removeClass('active'); 725 if (this.endDate == null) return; 726 727 this.calculateChosenLabel(); 728 }, 729 730 renderCalendar: function (side) { 731 732 // 733 // Build the matrix of dates that will populate the calendar 734 // 735 736 var calendar = side == 'left' ? this.leftCalendar : this.rightCalendar; 737 var month = calendar.month.month(); 738 var year = calendar.month.year(); 739 var hour = calendar.month.hour(); 740 var minute = calendar.month.minute(); 741 var second = calendar.month.second(); 742 var daysInMonth = moment([year, month]).daysInMonth(); 743 var firstDay = moment([year, month, 1]); 744 var lastDay = moment([year, month, daysInMonth]); 745 var lastMonth = moment(firstDay).subtract(1, 'month').month(); 746 var lastYear = moment(firstDay).subtract(1, 'month').year(); 747 var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth(); 748 var dayOfWeek = firstDay.day(); 749 750 //initialize a 6 rows x 7 columns array for the calendar 751 var calendar = []; 752 calendar.firstDay = firstDay; 753 calendar.lastDay = lastDay; 754 755 for (var i = 0; i < 6; i++) { 756 calendar[i] = []; 757 } 758 759 //populate the calendar with date objects 760 var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1; 761 if (startDay > daysInLastMonth) 762 startDay -= 7; 763 764 if (dayOfWeek == this.locale.firstDay) 765 startDay = daysInLastMonth - 6; 766 767 var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]); 768 769 var col, row; 770 for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) { 771 if (i > 0 && col % 7 === 0) { 772 col = 0; 773 row++; 774 } 775 calendar[row][col] = curDate.clone().hour(hour).minute(minute).second(second); 776 curDate.hour(12); 777 778 if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate) && side == 'left') { 779 calendar[row][col] = this.minDate.clone(); 780 } 781 782 if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate) && side == 'right') { 783 calendar[row][col] = this.maxDate.clone(); 784 } 785 786 } 787 788 //make the calendar object available to hoverDate/clickDate 789 if (side == 'left') { 790 this.leftCalendar.calendar = calendar; 791 } else { 792 this.rightCalendar.calendar = calendar; 793 } 794 795 // 796 // Display the calendar 797 // 798 799 var minDate = side == 'left' ? this.minDate : this.startDate; 800 var maxDate = this.maxDate; 801 var selected = side == 'left' ? this.startDate : this.endDate; 802 var arrow = this.locale.direction == 'ltr' ? { left: 'chevron-left', right: 'chevron-right' } : { left: 'chevron-right', right: 'chevron-left' }; 803 804 var html = '<table class="table">'; 805 html += '<thead>'; 806 html += '<tr class="thead">'; 807 808 // add empty cell for week number 809 if (this.showWeekNumbers || this.showISOWeekNumbers) 810 html += '<th></th>'; 811 812 if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.linkedCalendars || side == 'left')) { 813 html += '<th class="prev available"><i class="fa fa-' + arrow.left + ' glyphicon glyphicon-' + arrow.left + '"></i></th>'; 814 } else { 815 html += '<th></th>'; 816 } 817 818 var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY"); 819 820 if (this.showDropdowns) { 821 var currentMonth = calendar[1][1].month(); 822 var currentYear = calendar[1][1].year(); 823 var maxYear = (maxDate && maxDate.year()) || (currentYear + 5); 824 var minYear = (minDate && minDate.year()) || (currentYear - 50); 825 var inMinYear = currentYear == minYear; 826 var inMaxYear = currentYear == maxYear; 827 828 var monthHtml = '<select class="monthselect">'; 829 for (var m = 0; m < 12; m++) { 830 if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) { 831 monthHtml += "<option value='" + m + "'" + 832 (m === currentMonth ? " selected='selected'" : "") + 833 ">" + this.locale.monthNames[m] + "</option>"; 834 } else { 835 monthHtml += "<option value='" + m + "'" + 836 (m === currentMonth ? " selected='selected'" : "") + 837 " disabled='disabled'>" + this.locale.monthNames[m] + "</option>"; 838 } 839 } 840 monthHtml += "</select>"; 841 842 var yearHtml = '<select class="yearselect">'; 843 for (var y = minYear; y <= maxYear; y++) { 844 yearHtml += '<option value="' + y + '"' + 845 (y === currentYear ? ' selected="selected"' : '') + 846 '>' + y + '</option>'; 847 } 848 yearHtml += '</select>'; 849 850 dateHtml = monthHtml + yearHtml; 851 } 852 853 html += '<th colspan="5" class="month">' + dateHtml + '</th>'; 854 if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.linkedCalendars || side == 'right' || this.singleDatePicker)) { 855 html += '<th class="next available"><i class="fa fa-' + arrow.right + ' glyphicon glyphicon-' + arrow.right + '"></i></th>'; 856 } else { 857 // html += '<th></th>'; 858 html += '<th class="next available"><i class="fa fa-' + arrow.right + ' glyphicon glyphicon-' + arrow.right + '"></i></th>'; 859 } 860 861 html += '</tr>'; 862 html += '<tr>'; 863 864 // add week number label 865 if (this.showWeekNumbers || this.showISOWeekNumbers) 866 html += '<th class="week">' + this.locale.weekLabel + '</th>'; 867 868 $.each(this.locale.daysOfWeek, function (index, dayOfWeek) { 869 html += '<th>' + dayOfWeek + '</th>'; 870 }); 871 872 html += '</tr>'; 873 html += '</thead>'; 874 html += '<tbody>'; 875 876 //adjust maxDate to reflect the dateLimit setting in order to 877 //grey out end dates beyond the dateLimit 878 if (this.endDate == null && this.dateLimit) { 879 var maxLimit = this.startDate.clone().add(this.dateLimit).endOf('day'); 880 if (!maxDate || maxLimit.isBefore(maxDate)) { 881 maxDate = maxLimit; 882 } 883 } 884 885 for (var row = 0; row < 6; row++) { 886 html += '<tr>'; 887 888 // add week number 889 if (this.showWeekNumbers) 890 html += '<td class="week">' + calendar[row][0].week() + '</td>'; 891 else if (this.showISOWeekNumbers) 892 html += '<td class="week">' + calendar[row][0].isoWeek() + '</td>'; 893 894 for (var col = 0; col < 7; col++) { 895 896 var classes = []; 897 898 //highlight today's date 899 if (calendar[row][col].isSame(new Date(), "day")) 900 classes.push('today'); 901 902 //highlight weekends 903 if (calendar[row][col].isoWeekday() > 5) 904 classes.push('weekend'); 905 906 //grey out the dates in other months displayed at beginning and end of this calendar 907 if (calendar[row][col].month() != calendar[1][1].month()) 908 classes.push('off'); 909 910 //don't allow selection of dates before the minimum date 911 if (this.minDate && calendar[row][col].isBefore(this.minDate, 'day')) 912 classes.push('off', 'disabled'); 913 914 //don't allow selection of dates after the maximum date 915 if (maxDate && calendar[row][col].isAfter(maxDate, 'day')) 916 classes.push('off', 'disabled'); 917 918 //don't allow selection of date if a custom function decides it's invalid 919 if (this.isInvalidDate(calendar[row][col])) 920 classes.push('off', 'disabled'); 921 922 //highlight the currently selected start date 923 if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD')) 924 classes.push('active', 'start-date'); 925 926 //highlight the currently selected end date 927 if (this.endDate != null && calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD')) 928 classes.push('active', 'end-date'); 929 930 //highlight dates in-between the selected dates 931 if (this.endDate != null && calendar[row][col] > this.startDate && calendar[row][col] < this.endDate) 932 classes.push('in-range'); 933 934 //apply custom classes for this date 935 var isCustom = this.isCustomDate(calendar[row][col]); 936 if (isCustom !== false) { 937 if (typeof isCustom === 'string') 938 classes.push(isCustom); 939 else 940 Array.prototype.push.apply(classes, isCustom); 941 } 942 943 var cname = '', disabled = false; 944 for (var i = 0; i < classes.length; i++) { 945 cname += classes[i] + ' '; 946 if (classes[i] == 'disabled') 947 disabled = true; 948 } 949 if (!disabled) 950 cname += 'available'; 951 952 html += '<td class="' + cname.replace(/^\s+|\s+$/g, '') + '" data-title="' + 'r' + row + 'c' + col + '">' + calendar[row][col].date() + '</td>'; 953 954 } 955 html += '</tr>'; 956 } 957 958 html += '</tbody>'; 959 html += '</table>'; 960 961 this.container.find('.calendar.' + side + ' .calendar-table').html(html); 962 963 }, 964 965 renderTimePicker: function (side) { 966 967 // Don't bother updating the time picker if it's currently disabled 968 // because an end date hasn't been clicked yet 969 if (side == 'right' && !this.endDate) return; 970 971 var html, selected, minDate, maxDate = this.maxDate; 972 973 if (this.dateLimit && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate))) 974 maxDate = this.startDate.clone().add(this.dateLimit); 975 976 if (side == 'left') { 977 selected = this.startDate.clone(); 978 minDate = this.minDate; 979 } else if (side == 'right') { 980 selected = this.endDate.clone(); 981 minDate = this.startDate; 982 983 //Preserve the time already selected 984 var timeSelector = this.container.find('.calendar.right .calendar-time div'); 985 if (!this.endDate && timeSelector.html() != '') { 986 987 selected.hour(timeSelector.find('.hourselect option:selected').val() || selected.hour()); 988 selected.minute(timeSelector.find('.minuteselect option:selected').val() || selected.minute()); 989 selected.second(timeSelector.find('.secondselect option:selected').val() || selected.second()); 990 991 if (!this.timePicker24Hour) { 992 var ampm = timeSelector.find('.ampmselect option:selected').val(); 993 if (ampm === 'PM' && selected.hour() < 12) 994 selected.hour(selected.hour() + 12); 995 if (ampm === 'AM' && selected.hour() === 12) 996 selected.hour(0); 997 } 998 999 } 1000 1001 if (selected.isBefore(this.startDate)) 1002 selected = this.startDate.clone(); 1003 1004 if (maxDate && selected.isAfter(maxDate)) 1005 selected = maxDate.clone(); 1006 1007 } 1008 1009 // 1010 // hours 1011 // 1012 1013 html = '<select class="hourselect">'; 1014 1015 var start = this.timePicker24Hour ? 0 : 1; 1016 var end = this.timePicker24Hour ? 23 : 12; 1017 1018 for (var i = start; i <= end; i++) { 1019 var i_in_24 = i; 1020 if (!this.timePicker24Hour) 1021 i_in_24 = selected.hour() >= 12 ? (i == 12 ? 12 : i + 12) : (i == 12 ? 0 : i); 1022 1023 var time = selected.clone().hour(i_in_24); 1024 var disabled = false; 1025 if (minDate && time.minute(59).isBefore(minDate)) 1026 disabled = true; 1027 if (maxDate && time.minute(0).isAfter(maxDate)) 1028 disabled = true; 1029 1030 if (i_in_24 == selected.hour() && !disabled) { 1031 html += '<option value="' + i + '" selected="selected">' + i + '</option>'; 1032 } else if (disabled) { 1033 html += '<option value="' + i + '" disabled="disabled" class="disabled">' + i + '</option>'; 1034 } else { 1035 html += '<option value="' + i + '">' + i + '</option>'; 1036 } 1037 } 1038 1039 html += '</select> '; 1040 1041 // 1042 // minutes 1043 // 1044 1045 html += ': <select class="minuteselect">'; 1046 1047 for (var i = 0; i < 60; i += this.timePickerIncrement) { 1048 var padded = i < 10 ? '0' + i : i; 1049 var time = selected.clone().minute(i); 1050 1051 var disabled = false; 1052 if (minDate && time.second(59).isBefore(minDate)) 1053 disabled = true; 1054 if (maxDate && time.second(0).isAfter(maxDate)) 1055 disabled = true; 1056 1057 if (selected.minute() == i && !disabled) { 1058 html += '<option value="' + i + '" selected="selected">' + padded + '</option>'; 1059 } else if (disabled) { 1060 html += '<option value="' + i + '" disabled="disabled" class="disabled">' + padded + '</option>'; 1061 } else { 1062 html += '<option value="' + i + '">' + padded + '</option>'; 1063 } 1064 } 1065 1066 html += '</select> '; 1067 1068 // 1069 // seconds 1070 // 1071 1072 if (this.timePickerSeconds) { 1073 html += ': <select class="secondselect">'; 1074 1075 for (var i = 0; i < 60; i++) { 1076 var padded = i < 10 ? '0' + i : i; 1077 var time = selected.clone().second(i); 1078 1079 var disabled = false; 1080 if (minDate && time.isBefore(minDate)) 1081 disabled = true; 1082 if (maxDate && time.isAfter(maxDate)) 1083 disabled = true; 1084 1085 if (selected.second() == i && !disabled) { 1086 html += '<option value="' + i + '" selected="selected">' + padded + '</option>'; 1087 } else if (disabled) { 1088 html += '<option value="' + i + '" disabled="disabled" class="disabled">' + padded + '</option>'; 1089 } else { 1090 html += '<option value="' + i + '">' + padded + '</option>'; 1091 } 1092 } 1093 1094 html += '</select> '; 1095 } 1096 1097 // 1098 // AM/PM 1099 // 1100 1101 if (!this.timePicker24Hour) { 1102 html += '<select class="ampmselect">'; 1103 1104 var am_html = ''; 1105 var pm_html = ''; 1106 1107 if (minDate && selected.clone().hour(12).minute(0).second(0).isBefore(minDate)) 1108 am_html = ' disabled="disabled" class="disabled"'; 1109 1110 if (maxDate && selected.clone().hour(0).minute(0).second(0).isAfter(maxDate)) 1111 pm_html = ' disabled="disabled" class="disabled"'; 1112 1113 if (selected.hour() >= 12) { 1114 html += '<option value="AM"' + am_html + '>AM</option><option value="PM" selected="selected"' + pm_html + '>PM</option>'; 1115 } else { 1116 html += '<option value="AM" selected="selected"' + am_html + '>AM</option><option value="PM"' + pm_html + '>PM</option>'; 1117 } 1118 1119 html += '</select>'; 1120 } 1121 1122 this.container.find('.calendar.' + side + ' .calendar-time div').html(html); 1123 1124 }, 1125 1126 updateFormInputs: function () { 1127 1128 //ignore mouse movements while an above-calendar text input has focus 1129 if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) 1130 return; 1131 1132 this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.locale.format)); 1133 if (this.endDate) 1134 this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.locale.format)); 1135 1136 if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) { 1137 this.container.find('button.applyBtn').removeAttr('disabled'); 1138 } else { 1139 this.container.find('button.applyBtn').attr('disabled', 'disabled'); 1140 } 1141 1142 }, 1143 1144 move: function () { 1145 // Should not move if it's inline mode
1146 if (this.inline) 1147 return; 1148 1149 var parentOffset = { top: 0, left: 0 }, 1150 containerTop; 1151 var parentRightEdge = $(window).width(); 1152 if (!this.parentEl.is('body')) { 1153 parentOffset = { 1154 top: this.parentEl.offset().top - this.parentEl.scrollTop(), 1155 left: this.parentEl.offset().left - this.parentEl.scrollLeft() 1156 }; 1157 parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left; 1158 } 1159 1160 if (this.drops == 'up') 1161 containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top; 1162 else 1163 containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top; 1164 this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('dropup'); 1165 1166 if (this.opens == 'left') { 1167 this.container.css({ 1168 top: containerTop, 1169 right: parentRightEdge - this.element.offset().left - this.element.outerWidth(), 1170 left: 'auto' 1171 }); 1172 if (this.container.offset().left < 0) { 1173 this.container.css({ 1174 right: 'auto', 1175 left: 9 1176 }); 1177 } 1178 } else if (this.opens == 'center') { 1179 this.container.css({ 1180 top: containerTop, 1181 left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2 1182 - this.container.outerWidth() / 2, 1183 right: 'auto' 1184 }); 1185 if (this.container.offset().left < 0) { 1186 this.container.css({ 1187 right: 'auto', 1188 left: 9 1189 }); 1190 } 1191 } else { 1192 this.container.css({ 1193 top: containerTop, 1194 left: this.element.offset().left - parentOffset.left, 1195 right: 'auto' 1196 }); 1197 if (this.container.offset().left + this.container.outerWidth() > $(window).width()) { 1198 this.container.css({ 1199 left: 'auto', 1200 right: 0 1201 }); 1202 } 1203 } 1204 }, 1205 1206 show: function (e) { 1207 if (this.isShowing) return; 1208 1209 // Create a click proxy that is private to this instance of datepicker, for unbinding 1210 this._outsideClickProxy = $.proxy(function (e) { this.outsideClick(e); }, this); 1211 1212 // Bind global datepicker mousedown for hiding and 1213 $(document) 1214 .on('mousedown.daterangepicker', this._outsideClickProxy) 1215 // also support mobile devices 1216 .on('touchend.daterangepicker', this._outsideClickProxy) 1217 // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them 1218 .on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy) 1219 // and also close when focus changes to outside the picker (eg. tabbing between controls) 1220 .on('focusin.daterangepicker', this._outsideClickProxy); 1221 1222 // Reposition the picker if the window is resized while it's open 1223 $(window).on('resize.daterangepicker', $.proxy(function (e) { this.move(e); }, this)); 1224 1225 this.oldStartDate = this.startDate.clone(); 1226 this.oldEndDate = this.endDate.clone(); 1227 this.previousRightTime = this.endDate.clone(); 1228 1229 this.updateView(); 1230 this.container.show(); 1231 this.move(); 1232 this.element.trigger('show.daterangepicker', this); 1233 this.isShowing = true; 1234 }, 1235 1236 hide: function (e) { 1237 if (!this.isShowing) return; 1238 1239 //if picker is attached to a text input, update it 1240 this.updateElement(); 1241 1242 $(document).off('.daterangepicker'); 1243 $(window).off('.daterangepicker'); 1244 this.container.hide(); 1245 this.element.trigger('hide.daterangepicker', this); 1246 this.isShowing = false; 1247 }, 1248 1249 toggle: function (e) {
1250 if (this.isShowing && !this.inline) { 1251 this.hide(); 1252 } else { 1253 this.show(); 1254 } 1255 }, 1256 1257 outsideClick: function (e) { 1258 var target = $(e.target); 1259 // if the page is clicked anywhere except within the daterangerpicker/button 1260 // itself then call this.hide() 1261 if ( 1262 // ie modal dialog fix 1263 e.type == "focusin" || 1264 target.closest(this.element).length || 1265 target.closest(this.container).length || 1266 target.closest('.calendar-table').length || 1267 this.inline 1268 ) return; 1269 this.hide(); 1270 this.element.trigger('outsideClick.daterangepicker', this); 1271 }, 1272 1273 showCalendars: function () { 1274 this.container.addClass('show-calendar'); 1275 this.move(); 1276 this.element.trigger('showCalendar.daterangepicker', this); 1277 }, 1278 1279 hideCalendars: function () { 1280 this.container.removeClass('show-calendar'); 1281 this.element.trigger('hideCalendar.daterangepicker', this); 1282 }, 1283 1284 hoverRange: function (e) { 1285 1286 //ignore mouse movements while an above-calendar text input has focus 1287 if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) 1288 return; 1289 1290 var label = e.target.getAttribute('data-range-key'); 1291 1292 if (label == this.locale.customRangeLabel) { 1293 this.updateView(); 1294 } else { 1295 var dates = this.ranges[label]; 1296 this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.locale.format)); 1297 this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.locale.format)); 1298 } 1299 1300 }, 1301 1302 clickRange: function (e) { 1303 var label = e.target.getAttribute('data-range-key'); 1304 this.chosenLabel = label; 1305 if (label == this.locale.customRangeLabel) { 1306 this.showCalendars(); 1307 } else { 1308 var dates = this.ranges[label]; 1309 this.startDate = dates[0]; 1310 this.endDate = dates[1]; 1311 1312 if (!this.timePicker) { 1313 this.startDate.startOf('day'); 1314 this.endDate.endOf('day'); 1315 } 1316 1317 if (!this.alwaysShowCalendars) 1318 this.hideCalendars(); 1319 this.clickApply(); 1320 } 1321 }, 1322 1323 clickPrev: function (e) { 1324 var cal = $(e.target).parents('.calendar'); 1325 if (cal.hasClass('left')) { 1326 this.leftCalendar.month.subtract(1, 'month'); 1327 if (this.linkedCalendars) 1328 this.rightCalendar.month.subtract(1, 'month'); 1329 } else { 1330 this.rightCalendar.month.subtract(1, 'month'); 1331 } 1332 this.updateCalendars(); 1333 }, 1334 1335 clickNext: function (e) { 1336 var cal = $(e.target).parents('.calendar'); 1337 if (cal.hasClass('left')) { 1338 this.leftCalendar.month.add(1, 'month'); 1339 } else { 1340 this.rightCalendar.month.add(1, 'month'); 1341 if (this.linkedCalendars) 1342 this.leftCalendar.month.add(1, 'month'); 1343 } 1344 this.updateCalendars(); 1345 }, 1346 1347 hoverDate: function (e) { 1348 1349 //ignore mouse movements while an above-calendar text input has focus 1350 //if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus")) 1351 // return; 1352 1353 //ignore dates that can't be selected 1354 if (!$(e.target).hasClass('available')) return; 1355 1356 //have the text inputs above calendars reflect the date being hovered over 1357 var title = $(e.target).attr('data-title'); 1358 var row = title.substr(1, 1); 1359 var col = title.substr(3, 1); 1360 var cal = $(e.target).parents('.calendar'); 1361 var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; 1362 1363 if (this.endDate && !this.container.find('input[name=daterangepicker_start]').is(":focus")) { 1364 this.container.find('input[name=daterangepicker_start]').val(date.format(this.locale.format)); 1365 } else if (!this.endDate && !this.container.find('input[name=daterangepicker_end]').is(":focus")) { 1366 this.container.find('input[name=daterangepicker_end]').val(date.format(this.locale.format)); 1367 } 1368 1369 //highlight the dates between the start date and the date being hovered as a potential end date 1370 var leftCalendar = this.leftCalendar; 1371 var rightCalendar = this.rightCalendar; 1372 var startDate = this.startDate; 1373 if (!this.endDate) { 1374 this.container.find('.calendar td').each(function (index, el) { 1375 1376 //skip week numbers, only look at dates 1377 if ($(el).hasClass('week')) return; 1378 1379 var title = $(el).attr('data-title'); 1380 var row = title.substr(1, 1); 1381 var col = title.substr(3, 1); 1382 var cal = $(el).parents('.calendar'); 1383 var dt = cal.hasClass('left') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col]; 1384 1385 if ((dt.isAfter(startDate) && dt.isBefore(date)) || dt.isSame(date, 'day')) { 1386 $(el).addClass('in-range'); 1387 } else { 1388 $(el).removeClass('in-range'); 1389 } 1390 1391 }); 1392 } 1393 1394 }, 1395 1396 clickDate: function (e) { 1397 1398 if (!$(e.target).hasClass('available')) return; 1399 1400 var title = $(e.target).attr('data-title'); 1401 var row = title.substr(1, 1); 1402 var col = title.substr(3, 1); 1403 var cal = $(e.target).parents('.calendar'); 1404 var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col]; 1405 1406 // 1407 // this function needs to do a few things: 1408 // * alternate between selecting a start and end date for the range, 1409 // * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date 1410 // * if autoapply is enabled, and an end date was chosen, apply the selection 1411 // * if single date picker mode, and time picker isn't enabled, apply the selection immediately 1412 // * if one of the inputs above the calendars was focused, cancel that manual input 1413 // 1414 1415 if (this.endDate || date.isBefore(this.startDate, 'day')) { //picking start 1416 if (this.timePicker) { 1417 var hour = parseInt(this.container.find('.left .hourselect').val(), 10); 1418 if (!this.timePicker24Hour) { 1419 var ampm = this.container.find('.left .ampmselect').val(); 1420 if (ampm === 'PM' && hour < 12) 1421 hour += 12; 1422 if (ampm === 'AM' && hour === 12) 1423 hour = 0; 1424 } 1425 var minute = parseInt(this.container.find('.left .minuteselect').val(), 10); 1426 var second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0; 1427 date = date.clone().hour(hour).minute(minute).second(second); 1428 } 1429 this.endDate = null; 1430 this.setStartDate(date.clone()); 1431 } else if (!this.endDate && date.isBefore(this.startDate)) { 1432 //special case: clicking the same date for start/end, 1433 //but the time of the end date is before the start date 1434 this.setEndDate(this.startDate.clone()); 1435 } else { // picking end 1436 if (this.timePicker) { 1437 var hour = parseInt(this.container.find('.right .hourselect').val(), 10); 1438 if (!this.timePicker24Hour) { 1439 var ampm = this.container.find('.right .ampmselect').val(); 1440 if (ampm === 'PM' && hour < 12) 1441 hour += 12; 1442 if (ampm === 'AM' && hour === 12) 1443 hour = 0; 1444 } 1445 var minute = parseInt(this.container.find('.right .minuteselect').val(), 10); 1446 var second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0; 1447 date = date.clone().hour(hour).minute(minute).second(second); 1448 } 1449 this.setEndDate(date.clone()); 1450 if (this.autoApply) { 1451 this.calculateChosenLabel(); 1452 this.clickApply(); 1453 } 1454 } 1455 1456 if (this.singleDatePicker) { 1457 this.setEndDate(this.startDate); 1458 if (!this.timePicker) 1459 this.clickApply(); 1460 } 1461 1462 this.updateView(); 1463 1464 1465 1466 1467 //This is to cancel the blur event handler if the mouse was in one of the inputs 1468 e.stopPropagation(); 1469 1470 }, 1471 1472 calculateChosenLabel: function () { 1473 var customRange = true; 1474 var i = 0; 1475 for (var range in this.ranges) { 1476 if (this.timePicker) { 1477 if (this.startDate.isSame(this.ranges[range][0]) && this.endDate.isSame(this.ranges[range][1])) { 1478 customRange = false; 1479 this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html(); 1480 break; 1481 } 1482 } else { 1483 //ignore times when comparing dates if time picker is not enabled 1484 if (this.startDate.format('YYYY-MM-DD') == this.ranges[range][0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == this.ranges[range][1].format('YYYY-MM-DD')) { 1485 customRange = false; 1486 this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html(); 1487 break; 1488 } 1489 } 1490 i++; 1491 } 1492 if (customRange && this.showCustomRangeLabel) { 1493 this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html(); 1494 this.showCalendars();
1495 } 1496 }, 1497 1498 clickApply: function (e) { 1499 // Should not hide the picker if it's inline mode, but should update the parentElement 1500 (!this.inline) ? this.hide() : this.updateElement(); 1501 this.element.trigger('apply.daterangepicker', this); 1502 }, 1503 1504 clickCancel: function (e) { 1505 this.startDate = this.oldStartDate; 1506 this.endDate = this.oldEndDate; 1507 // Should not hide the picker if it's inline mode, but should update the parentElement 1508 (!this.inline) ? this.hide() : this.updateElement(); 1509 this.element.trigger('cancel.daterangepicker', this); 1510 1511 }, 1512 1513 monthOrYearChanged: function (e) { 1514 var isLeft = $(e.target).closest('.calendar').hasClass('left'), 1515 leftOrRight = isLeft ? 'left' : 'right', 1516 cal = this.container.find('.calendar.' + leftOrRight); 1517 1518 // Month must be Number for new moment versions 1519 var month = parseInt(cal.find('.monthselect').val(), 10); 1520 var year = cal.find('.yearselect').val(); 1521 1522 if (!isLeft) { 1523 if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) { 1524 month = this.startDate.month(); 1525 year = this.startDate.year(); 1526 } 1527 } 1528 1529 if (this.minDate) { 1530 if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) { 1531 month = this.minDate.month(); 1532 year = this.minDate.year(); 1533 } 1534 } 1535 1536 if (this.maxDate) { 1537 if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) { 1538 month = this.maxDate.month(); 1539 year = this.maxDate.year(); 1540 } 1541 } 1542 1543 if (isLeft) { 1544 this.leftCalendar.month.month(month).year(year); 1545 if (this.linkedCalendars) 1546 this.rightCalendar.month = this.leftCalendar.month.clone().add(1, 'month'); 1547 } else { 1548 this.rightCalendar.month.month(month).year(year); 1549 if (this.linkedCalendars) 1550 this.leftCalendar.month = this.rightCalendar.month.clone().subtract(1, 'month'); 1551 } 1552 this.updateCalendars(); 1553 }, 1554 1555 timeChanged: function (e) { 1556 1557 var cal = $(e.target).closest('.calendar'), 1558 isLeft = cal.hasClass('left'); 1559 1560 var hour = parseInt(cal.find('.hourselect').val(), 10); 1561 var minute = parseInt(cal.find('.minuteselect').val(), 10); 1562 var second = this.timePickerSeconds ? parseInt(cal.find('.secondselect').val(), 10) : 0; 1563 1564 if (!this.timePicker24Hour) { 1565 var ampm = cal.find('.ampmselect').val(); 1566 if (ampm === 'PM' && hour < 12) 1567 hour += 12; 1568 if (ampm === 'AM' && hour === 12) 1569 hour = 0; 1570 } 1571 1572 if (isLeft) { 1573 var start = this.startDate.clone(); 1574 start.hour(hour); 1575 start.minute(minute); 1576 start.second(second); 1577 this.setStartDate(start); 1578 if (this.singleDatePicker) { 1579 this.endDate = this.startDate.clone(); 1580 } else if (this.endDate && this.endDate.format('YYYY-MM-DD') == start.format('YYYY-MM-DD') && this.endDate.isBefore(start)) { 1581 this.setEndDate(start.clone()); 1582 } 1583 } else if (this.endDate) { 1584 var end = this.endDate.clone(); 1585 end.hour(hour); 1586 end.minute(minute); 1587 end.second(second); 1588 this.setEndDate(end); 1589 } 1590 1591 // Update the parent element with the autoplay option in inline mode
1592 if (this.inline && this.autoApply) 1593 this.updateElement() 1594 1595 //update the calendars so all clickable dates reflect the new time component 1596 this.updateCalendars(); 1597 1598 //update the form inputs above the calendars with the new time 1599 this.updateFormInputs(); 1600 1601 //re-render the time pickers because changing one selection can affect what's enabled in another 1602 this.renderTimePicker('left'); 1603 this.renderTimePicker('right'); 1604 1605 }, 1606 1607 formInputsChanged: function (e) { 1608 var isRight = $(e.target).closest('.calendar').hasClass('right'); 1609 var start = moment(this.container.find('input[name="daterangepicker_start"]').val(), this.locale.format); 1610 var end = moment(this.container.find('input[name="daterangepicker_end"]').val(), this.locale.format); 1611 1612 if (start.isValid() && end.isValid()) { 1613 1614 if (isRight && end.isBefore(start)) 1615 start = end.clone(); 1616 1617 this.setStartDate(start); 1618 this.setEndDate(end); 1619 1620 if (isRight) { 1621 this.container.find('input[name="daterangepicker_start"]').val(this.startDate.format(this.locale.format)); 1622 } else { 1623 this.container.find('input[name="daterangepicker_end"]').val(this.endDate.format(this.locale.format)); 1624 } 1625 1626 } 1627 1628 this.updateView(); 1629 1630 // Update the parent element with the autoplay option in inline mode 1631 if (this.inline && this.autoApply) 1632 this.updateElement() 1633 }, 1634 1635 formInputsFocused: function (e) { 1636 1637 // Highlight the focused input 1638 this.container.find('input[name="daterangepicker_start"], input[name="daterangepicker_end"]').removeClass('active'); 1639 $(e.target).addClass('active'); 1640 1641 // Set the state such that if the user goes back to using a mouse, 1642 // the calendars are aware we're selecting the end of the range, not 1643 // the start. This allows someone to edit the end of a date range without 1644 // re-selecting the beginning, by clicking on the end date input then 1645 // using the calendar. 1646 var isRight = $(e.target).closest('.calendar').hasClass('right'); 1647 if (isRight) { 1648 this.endDate = null; 1649 this.setStartDate(this.startDate.clone()); 1650 this.updateView(); 1651 } 1652 1653 }, 1654 1655 formInputsBlurred: function (e) { 1656 1657 // this function has one purpose right now: if you tab from the first 1658 // text input to the second in the UI, the endDate is nulled so that 1659 // you can click another, but if you tab out without clicking anything 1660 // or changing the input value, the old endDate should be retained 1661 1662 if (!this.endDate) { 1663 var val = this.container.find('input[name="daterangepicker_end"]').val(); 1664 var end = moment(val, this.locale.format); 1665 if (end.isValid()) { 1666 this.setEndDate(end); 1667 this.updateView(); 1668 } 1669 } 1670 1671 }, 1672 1673 elementChanged: function () { 1674 if (!this.element.is('input')) return; 1675 if (!this.element.val().length) return; 1676 if (this.element.val().length < this.locale.format.length) return; 1677 1678 var dateString = this.element.val().split(this.locale.separator), 1679 start = null, 1680 end = null; 1681 1682 if (dateString.length === 2) { 1683 start = moment(dateString[0], this.locale.format); 1684 end = moment(dateString[1], this.locale.format); 1685 } 1686 1687 if (this.singleDatePicker || start === null || end === null) { 1688 start = moment(this.element.val(), this.locale.format); 1689 end = start; 1690 1691 } 1692 1693 if (!start.isValid() || !end.isValid()) return; 1694 1695 1696 this.setStartDate(start); 1697 this.setEndDate(end); 1698 this.updateView(); 1699 }, 1700 1701 keydown: function (e) { 1702 //hide on tab or enter 1703 if ((e.keyCode === 9) || (e.keyCode === 13)) { 1704 this.hide(); 1705 } 1706 }, 1707 1708 updateElement: function () { 1709 1710 //incomplete date selection, revert to last values 1711 if (!this.endDate) { 1712 this.startDate = this.oldStartDate.clone(); 1713 this.endDate = this.oldEndDate.clone(); 1714 } 1715 1716 //if a new date range was selected, invoke the user callback function 1717 if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate)) 1718 this.callback(this.startDate, this.endDate, this.chosenLabel); 1719 1720 if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) { 1721 this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format)); 1722 this.element.trigger('change'); 1723 } else if (this.element.is('input') && this.autoUpdateInput) { 1724 this.element.val(this.startDate.format(this.locale.format)); 1725 this.element.trigger('change'); 1726 } 1727 }, 1728 1729 remove: function () { 1730 this.container.remove(); 1731 this.element.off('.daterangepicker'); 1732 this.element.removeData(); 1733 } 1734 1735 }; 1736 1737 $.fn.daterangepicker = function (options, callback) { 1738 this.each(function () { 1739 var el = $(this); 1740 if (el.data('daterangepicker')) 1741 el.data('daterangepicker').remove(); 1742 el.data('daterangepicker', new DateRangePicker(el, options, callback)); 1743 }); 1744 return this; 1745 }; 1746 1747 return DateRangePicker; 1748 1749}));
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.