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