1/** 2* @version: 1.0.1 3* @author: Dan Grossman http://www.dangrossman.info/ 4* @date: 2012-08-20 5* @copyright: Copyright (c) 2012 Dan Grossman. All rights reserved. 6* @license: Licensed under Apache License v2.0. See http://www.apache.org/licenses/LICENSE-2.0 7* @website: http://www.improvely.com/ 8*/ 9 10!function ($) { 11 12 var DateRangePicker = function (element, options, cb) { 13 var hasOptions = typeof options == 'object' 14 var localeObject; 15 16 //state 17 this.startDate = Date.today(); 18 this.endDate = Date.today(); 19 this.minDate = false; 20 this.maxDate = false; 21 this.changed = false; 22 this.ranges = {}; 23 this.opens = 'right'; 24 this.cb = function () { }; 25 this.format = 'MM/dd/yyyy'; 26 this.separator = ' - '; 27 this.showWeekNumbers = false; 28 this.buttonClasses = ['btn-success']; 29 this.locale = { 30 applyLabel: 'Apply', 31 fromLabel: 'From', 32 toLabel: 'To', 33 weekLabel: 'W', 34 customRangeLabel: 'Custom Range', 35 daysOfWeek: Date.CultureInfo.shortestDayNames, 36 monthNames: Date.CultureInfo.monthNames, 37 firstDay: 0 38 }; 39 40 localeObject = this.locale; 41 42 this.leftCalendar = { 43 month: Date.today().set({ day: 1, month: this.startDate.getMonth(), year: this.startDate.getFullYear() }), 44 calendar: Array() 45 }; 46 47 this.rightCalendar = { 48 month: Date.today().set({ day: 1, month: this.endDate.getMonth(), year: this.endDate.getFullYear() }), 49 calendar: Array() 50 }; 51 52 // by default, the daterangepicker element is placed at the bottom of HTML body 53 this.parentEl = 'body'; 54 55 //element that triggered the date range picker 56 this.element = $(element); 57 58 if (this.element.hasClass('pull-right')) 59 this.opens = 'left'; 60 61 if (this.element.is('input')) { 62 this.element.on({ 63 click: $.proxy(this.show, this), 64 focus: $.proxy(this.show, this) 65 }); 66 } else { 67 this.element.on('click', $.proxy(this.show, this)); 68 } 69 70 if (hasOptions) { 71 if(typeof options.locale == 'object') { 72 $.each(localeObject, function (property, value) { 73 localeObject[property] = options.locale[property] || value; 74 }); 75 } 76 } 77 78 var DRPTemplate = '<div class="daterangepicker dropdown-menu">' + 79 '<div class="calendar left"></div>' + 80 '<div class="calendar right"></div>' + 81 '<div class="ranges">' + 82 '<div class="range_inputs">' + 83 "<div>" + 84 '<label for="daterangepicker_start">' + this.locale.fromLabel + '</label>' + 85 '<input class="m-wrap input-sm form-control" type="text" name="daterangepicker_start" value="" disabled="disabled" />' + 86 '</div>' + 87 "<div>" + 88 '<label for="daterangepicker_end">' + this.locale.toLabel + '</label>' + 89 '<input class="m-wrap input-sm form-control" type="text" name="daterangepicker_end" value="" disabled="disabled" />' + 90 '</div>' + 91 '<button class="btn " disabled="disabled">' + this.locale.applyLabel + '</button>' + 92 '</div>' + 93 '</div>' + 94 '</div>'; 95 96 this.parentEl = (hasOptions && options.parentEl && $(options.parentEl)) || $(this.parentEl); 97 //the date range picker 98 this.container = $(DRPTemplate).appendTo(this.parentEl); 99 100 if (hasOptions) { 101 102 if (typeof options.format == 'string') 103 this.format = options.format; 104 105 if (typeof options.separator == 'string') 106 this.separator = options.separator; 107 108 if (typeof options.startDate == 'string') 109 this.startDate = Date.parse(options.startDate, this.format); 110 111 if (typeof options.endDate == 'string') 112 this.endDate = Date.parse(options.endDate, this.format); 113 114 if (typeof options.minDate == 'string') 115 this.minDate = Date.parse(options.minDate, this.format); 116 117 if (typeof options.maxDate == 'string') 118 this.maxDate = Date.parse(options.maxDate, this.format); 119 120 121 if (typeof options.startDate == 'object') 122 this.startDate = options.startDate; 123 124 if (typeof options.endDate == 'object') 125 this.endDate = options.endDate; 126 127 if (typeof options.minDate == 'object') 128 this.minDate = options.minDate; 129 130 if (typeof options.maxDate == 'object') 131 this.maxDate = options.maxDate; 132 133 if (typeof options.ranges == 'object') { 134 for (var range in options.ranges) { 135 136 var start = options.ranges[range][0]; 137 var end = options.ranges[range][1]; 138 139 if (typeof start == 'string') 140 start = Date.parse(start); 141 142 if (typeof end == 'string') 143 end = Date.parse(end); 144 145 // If we have a min/max date set, bound this range 146 // to it, but only if it would otherwise fall 147 // outside of the min/max. 148 if (this.minDate && start < this.minDate) 149 start = this.minDate; 150 151 if (this.maxDate && end > this.maxDate) 152 end = this.maxDate; 153 154 // If the end of the range is before the minimum (if min is set) OR 155 // the start of the range is after the max (also if set) don't display this 156 // range option. 157 if ((this.minDate && end < this.minDate) || (this.maxDate && start > this.maxDate)) 158 { 159 continue; 160 } 161 162 this.ranges[range] = [start, end]; 163 } 164 165 var list = '<ul>'; 166 for (var range in this.ranges) { 167 list += '<li>' + range + '</li>'; 168 } 169 list += '<li>' + this.locale.customRangeLabel + '</li>'; 170 list += '</ul>'; 171 this.container.find('.ranges').prepend(list); 172 } 173 174 // update day names order to firstDay 175 if (typeof options.locale == 'object') { 176 if (typeof options.locale.firstDay == 'number') { 177 this.locale.firstDay = options.locale.firstDay; 178 var iterator = options.locale.firstDay; 179 while (iterator > 0) { 180 this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift()); 181 iterator--; 182 } 183 } 184 } 185 186 if (typeof options.opens == 'string') 187 this.opens = options.opens; 188 189 if (typeof options.showWeekNumbers == 'boolean') { 190 this.showWeekNumbers = options.showWeekNumbers; 191 } 192
193 if (typeof options.buttonClasses == 'string') { 194 this.buttonClasses = [options.buttonClasses]; 195 } 196 197 if (typeof options.buttonClasses == 'object') { 198 this.buttonClasses = options.buttonClasses; 199 } 200 201 } 202 203 //apply CSS classes to buttons 204 var c = this.container; 205 $.each(this.buttonClasses, function (idx, val) { 206 c.find('button').addClass(val); 207 }); 208 209 if (this.opens == 'right') { 210 //swap calendar positions 211 var left = this.container.find('.calendar.left'); 212 var right = this.container.find('.calendar.right'); 213 left.removeClass('left').addClass('right'); 214 right.removeClass('right').addClass('left'); 215 } 216 217 if (typeof options == 'undefined' || typeof options.ranges == 'undefined') 218 this.container.find('.calendar').show(); 219 220 if (typeof cb == 'function') 221 this.cb = cb; 222 223 this.container.addClass('opens' + this.opens); 224 225 //event listeners 226 this.container.on('mousedown', $.proxy(this.mousedown, this)); 227 this.container.find('.calendar').on('click', '.prev', $.proxy(this.clickPrev, this)); 228 this.container.find('.calendar').on('click', '.next', $.proxy(this.clickNext, this)); 229 this.container.find('.ranges').on('click', 'button', $.proxy(this.clickApply, this)); 230 231 this.container.find('.calendar').on('click', 'td.available', $.proxy(this.clickDate, this)); 232 this.container.find('.calendar').on('mouseenter', 'td.available', $.proxy(this.enterDate, this)); 233 this.container.find('.calendar').on('mouseleave', 'td.available', $.proxy(this.updateView, this)); 234 235 this.container.find('.ranges').on('click', 'li', $.proxy(this.clickRange, this)); 236 this.container.find('.ranges').on('mouseenter', 'li', $.proxy(this.enterRange, this)); 237 this.container.find('.ranges').on('mouseleave', 'li', $.proxy(this.updateView, this)); 238 239 this.element.on('keyup', $.proxy(this.updateFromControl, this)); 240 241 this.updateView(); 242 this.updateCalendars(); 243 244 }; 245 246 DateRangePicker.prototype = { 247 248 constructor: DateRangePicker, 249 250 mousedown: function (e) { 251 e.stopPropagation(); 252 e.preventDefault(); 253 }, 254 255 updateView: function () { 256 this.leftCalendar.month.set({ month: this.startDate.getMonth(), year: this.startDate.getFullYear() }); 257 this.rightCalendar.month.set({ month: this.endDate.getMonth(), year: this.endDate.getFullYear() }); 258 259 this.container.find('input[name=daterangepicker_start]').val(this.startDate.toString(this.format)); 260 this.container.find('input[name=daterangepicker_end]').val(this.endDate.toString(this.format)); 261 262 if (this.startDate.equals(this.endDate) || this.startDate.isBefore(this.endDate)) { 263 this.container.find('button').removeAttr('disabled'); 264 } else { 265 this.container.find('button').attr('disabled', 'disabled'); 266 } 267 }, 268 269 updateFromControl: function () { 270 if (!this.element.is('input')) return; 271 272 var dateString = this.element.val().split(this.separator); 273 var start = Date.parseExact(dateString[0], this.format); 274 var end = Date.parseExact(dateString[1], this.format); 275 276 if (start == null || end == null) return; 277 if (end.isBefore(start)) return; 278 279 this.startDate = start; 280 this.endDate = end; 281 282 this.updateView(); 283 this.cb(this.startDate, this.endDate); 284 this.updateCalendars(); 285 }, 286 287 notify: function () { 288 this.updateView(); 289 290 if (this.element.is('input')) { 291 this.element.val(this.startDate.toString(this.format) + this.separator + this.endDate.toString(this.format)); 292 } 293 this.cb(this.startDate, this.endDate); 294 }, 295 296 move: function () { 297 var parentOffset = { 298 top: this.parentEl.offset().top - this.parentEl.scrollTop(), 299 left: this.parentEl.offset().left - this.parentEl.scrollLeft() 300 }; 301 if (this.opens == 'left') { 302 this.container.css({ 303 top: this.element.offset().top + this.element.outerHeight(), 304 right: $(window).width() - this.element.offset().left - this.element.outerWidth() - parentOffset.left, 305 left: 'auto' 306 }); 307 } else { 308 this.container.css({ 309 top: this.element.offset().top + this.element.outerHeight(), 310 left: this.element.offset().left - parentOffset.left, 311 right: 'auto' 312 }); 313 } 314 }, 315 316 show: function (e) { 317 this.container.show(); 318 this.move(); 319 320 if (e) { 321 e.stopPropagation(); 322 e.preventDefault(); 323 } 324 325 this.changed = false;
326 327 $(document).on('mousedown', $.proxy(this.hide, this)); 328 }, 329 330 hide: function (e) { 331 this.container.hide(); 332 $(document).off('mousedown', this.hide); 333 334 if (this.changed) { 335 this.changed = false; 336 this.notify(); 337 } 338 }, 339 340 enterRange: function (e) { 341 var label = e.target.innerHTML; 342 if (label == this.locale.customRangeLabel) { 343 this.updateView(); 344 } else { 345 var dates = this.ranges[label]; 346 this.container.find('input[name=daterangepicker_start]').val(dates[0].toString(this.format)); 347 this.container.find('input[name=daterangepicker_end]').val(dates[1].toString(this.format)); 348 } 349 }, 350 351 clickRange: function (e) { 352 var label = e.target.innerHTML; 353 if (label == this.locale.customRangeLabel) { 354 this.container.find('.calendar').show(); 355 } else { 356 var dates = this.ranges[label]; 357 358 this.startDate = dates[0]; 359 this.endDate = dates[1]; 360 361 this.leftCalendar.month.set({ month: this.startDate.getMonth(), year: this.startDate.getFullYear() }); 362 this.rightCalendar.month.set({ month: this.endDate.getMonth(), year: this.endDate.getFullYear() }); 363 this.updateCalendars(); 364 365 this.changed = true; 366 367 this.container.find('.calendar').hide(); 368 this.hide(); 369 } 370 }, 371 372 clickPrev: function (e) { 373 var cal = $(e.target).parents('.calendar'); 374 if (cal.hasClass('left')) { 375 this.leftCalendar.month.add({ months: -1 }); 376 } else { 377 this.rightCalendar.month.add({ months: -1 }); 378 } 379 this.updateCalendars(); 380 }, 381 382 clickNext: function (e) { 383 var cal = $(e.target).parents('.calendar'); 384 if (cal.hasClass('left')) { 385 this.leftCalendar.month.add({ months: 1 }); 386 } else { 387 this.rightCalendar.month.add({ months: 1 }); 388 } 389 this.updateCalendars(); 390 }, 391 392 enterDate: function (e) { 393 394 var title = $(e.target).attr('title'); 395 var row = title.substr(1, 1); 396 var col = title.substr(3, 1); 397 var cal = $(e.target).parents('.calendar'); 398 399 if (cal.hasClass('left')) { 400 this.container.find('input[name=daterangepicker_start]').val(this.leftCalendar.calendar[row][col].toString(this.format)); 401 } else { 402 this.container.find('input[name=daterangepicker_end]').val(this.rightCalendar.calendar[row][col].toString(this.format)); 403 } 404 405 }, 406 407 clickDate: function (e) { 408 var title = $(e.target).attr('title'); 409 var row = title.substr(1, 1); 410 var col = title.substr(3, 1); 411 var cal = $(e.target).parents('.calendar'); 412 413 if (cal.hasClass('left')) { 414 startDate = this.leftCalendar.calendar[row][col]; 415 endDate = this.endDate; 416 } else { 417 startDate = this.startDate; 418 endDate = this.rightCalendar.calendar[row][col]; 419 } 420 421 cal.find('td').removeClass('active'); 422 423 if (startDate.equals(endDate) || startDate.isBefore(endDate)) { 424 $(e.target).addClass('active'); 425 if (!startDate.equals(this.startDate) || !endDate.equals(this.endDate)) 426 this.changed = true; 427 this.startDate = startDate; 428 this.endDate = endDate; 429 } 430 431 this.leftCalendar.month.set({ month: this.startDate.getMonth(), year: this.startDate.getFullYear() }); 432 this.rightCalendar.month.set({ month: this.endDate.getMonth(), year: this.endDate.getFullYear() }); 433 this.updateCalendars(); 434 }, 435 436 clickApply: function (e) { 437 this.hide(); 438 }, 439 440 updateCalendars: function () { 441 this.leftCalendar.calendar = this.buildCalendar(this.leftCalendar.month.getMonth(), this.leftCalendar.month.getFullYear()); 442 this.rightCalendar.calendar = this.buildCalendar(this.rightCalendar.month.getMonth(), this.rightCalendar.month.getFullYear()); 443 this.container.find('.calendar.left').html(this.renderCalendar(this.leftCalendar.calendar, this.startDate, this.minDate, this.endDate)); 444 this.container.find('.calendar.right').html(this.renderCalendar(this.rightCalendar.calendar, this.endDate, this.startDate, this.maxDate)); 445 }, 446 447 buildCalendar: function (month, year) { 448 449 var firstDay = Date.today().set({ day: 1, month: month, year: year }); 450 var lastMonth = firstDay.clone().add(-1).day().getMonth(); 451 var lastYear = firstDay.clone().add(-1).day().getFullYear(); 452 453 var daysInMonth = Date.getDaysInMonth(year, month); 454 var daysInLastMonth = Date.getDaysInMonth(lastYear, lastMonth); 455 456 var dayOfWeek = firstDay.getDay(); 457 458 //initialize a 6 rows x 7 columns array for the calendar 459 var calendar = Array(); 460 for (var i = 0; i < 6; i++) { 461 calendar[i] = Array(); 462 } 463 464 //populate the calendar with date objects 465 var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1; 466 if (startDay > daysInLastMonth) 467 startDay -= 7; 468 469 if (dayOfWeek == this.locale.firstDay) 470 startDay = daysInLastMonth - 6; 471 472 var curDate = Date.today().set({ day: startDay, month: lastMonth, year: lastYear }); 473 for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = curDate.clone().add(1).day()) { 474 if (i > 0 && col % 7 == 0) { 475 col = 0; 476 row++; 477 } 478 calendar[row][col] = curDate; 479 } 480 481 return calendar; 482 483 }, 484 485 renderCalendar: function (calendar, selected, minDate, maxDate) { 486 var html = '<table class="table-condensed">'; 487 html += '<thead>'; 488 html += '<tr>'; 489 490 // add empty cell for week number 491 if (this.showWeekNumbers) 492 html += '<th></th>'; 493 494 if (!minDate || minDate < calendar[1][1]) 495 { 496 html += '<th class="prev available"><i class="icon-angle-left"></i></th>'; 497 } 498 else 499 { 500 html += '<th></th>'; 501 } 502 html += '<th colspan="5" style="width: auto">' + this.locale.monthNames[calendar[1][1].getMonth()] + calendar[1][1].toString(" yyyy") + '</th>'; 503 if (!maxDate || maxDate > calendar[1][1]) 504 { 505 html += '<th class="next available"><i class="icon-angle-right"></i></th>'; 506 } 507 else 508 { 509 html += '<th></th>'; 510 } 511 512 html += '</tr>'; 513 html += '<tr>'; 514 515 // add week number label 516 if (this.showWeekNumbers) 517 html += '<th class="week">' + this.locale.weekLabel + '</th>'; 518 519 $.each(this.locale.daysOfWeek, function (index, dayOfWeek) { 520 html += '<th>' + dayOfWeek + '</th>'; 521 }); 522 523 html += '</tr>'; 524 html += '</thead>'; 525 html += '<tbody>'; 526 527 for (var row = 0; row < 6; row++) { 528 html += '<tr>'; 529 530 // add week number 531 if (this.showWeekNumbers) 532 html += '<td class="week">' + calendar[row][0].getWeek() + '</td>'; 533 534 for (var col = 0; col < 7; col++) { 535 var cname = 'available '; 536 cname += (calendar[row][col].getMonth() == calendar[1][1].getMonth()) ? '' : 'off'; 537 538 // Normalise the time so the comparison won't fail 539 selected.setHours(0,0,0,0); 540 541 if ( (minDate && calendar[row][col] < minDate) || (maxDate && calendar[row][col] > maxDate)) 542 { 543 cname = 'off disabled'; 544 } 545 else if (calendar[row][col].equals(selected)) 546 { 547 cname += 'active'; 548 } 549 550 var title = 'r' + row + 'c' + col; 551 html += '<td class="' + cname + '" title="' + title + '">' + calendar[row][col].getDate() + '</td>'; 552 } 553 html += '</tr>'; 554 } 555 556 html += '</tbody>'; 557 html += '</table>'; 558 559 return html; 560 561 } 562 563 }; 564 565 $.fn.daterangepicker = function (options, cb) { 566 this.each(function() { 567 var el = $(this); 568 if (!el.data('daterangepicker')) 569 el.data('daterangepicker', new DateRangePicker(el, options, cb)); 570 }); 571 return this; 572 }; 573 574} (window.jQuery);
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.