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