PageSourceSearch

https://edjoin.org/Scripts/plugins/dateRangePicker/daterangepicker.js

js edjoin.org collected 2026-09-25 09:43:13 UTC 29,302 bytes, 724 lines download raw bytes

1/**
2* @version: 1.1
3* @author: Dan Grossman http://www.dangrossman.info/
4* @date: 2013-03-04
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!function ($) {
10
11    var DateRangePicker = function (element, options, cb) {
12        var hasOptions = typeof options == 'object';
13        var localeObject;
14
15        //state
16        this.startDate = Date.today();
17        this.endDate = Date.today();
18        this.minDate = false;
19        this.maxDate = false;
20        this.changed = false;
21        this.cleared = false;
22        this.showDropdowns = false;
23        this.ranges = {};
24        this.dateLimit = false;
25        this.opens = 'right';
26        this.cb = function () { };
27        this.format = 'MM/dd/yyyy';
28        this.separator = ' - ';
29        this.showWeekNumbers = false;
30        this.buttonClasses = ['btn-success'];
31        this.applyClass = 'btn btn-small btn-success';
32        this.clearClass = 'btn btn-small';
33        this.locale = {
34            applyLabel: 'Apply',
35            clearLabel:"Clear",
36            fromLabel: 'From',
37            toLabel: 'To',
38            weekLabel: 'W',
39            customRangeLabel: 'Custom Range',
40            daysOfWeek: Date.CultureInfo.shortestDayNames,
41            monthNames: Date.CultureInfo.monthNames,
42            firstDay: 0
43        };
44
45        localeObject = this.locale;
46
47        this.leftCalendar = {
48            month: Date.today().set({ day: 1, month: this.startDate.getMonth(), year: this.startDate.getFullYear() }),
49            calendar: Array()
50        };
51
52        this.rightCalendar = {
53            month: Date.today().set({ day: 1, month: this.endDate.getMonth(), year: this.endDate.getFullYear() }),
54            calendar: Array()
55        };
56
57        //element that triggered the date range picker
58        this.element = $(element);
59
60        if (this.element.hasClass('pull-right'))
61            this.opens = 'left';
62
63        if (this.element.is('input')) {
64            this.element.on({
65                click: $.proxy(this.show, this),
66                focus: $.proxy(this.show, this)
67            });
68        } else {
69            this.element.on('click', $.proxy(this.show, this));
70        }
71
72        if (hasOptions) {
73            if(typeof options.locale == 'object') {
74                $.each(localeObject, function (property, value) {
75                    localeObject[property] = options.locale[property] || value;
76                });
77            }
78
79            if (options.applyClass) {
80                this.applyClass = options.applyClass;
81            }
82
83            if (options.clearClass) {
84                this.clearClass = options.clearClass;
85            }
86        }
87
88        var DRPTemplate = '<div class="daterangepicker dropdown-menu">' +
89                '<div class="calendar left"></div>' +
90                '<div class="calendar right"></div>' +
91                '<div class="ranges">' +
92                  '<div class="range_inputs">' +
93                    '<div class="daterangepicker_start_input" style="float: left">' +
94                      '<label for="daterangepicker_start">' + this.locale.fromLabel + '</label>' +
95                      '<input class="input-mini" type="text" name="daterangepicker_start" value="" disabled="disabled" />' +
96                    '</div>' +
97                    '<div class="daterangepicker_end_input" style="float: left; padding-left: 11px">' +
98                      '<label for="daterangepicker_end">' + this.locale.toLabel + '</label>' +
99                      '<input class="input-mini" type="text" name="daterangepicker_end" value="" disabled="disabled" />' +
100                    '</div>' +
101                    '<button class="' + this.applyClass + ' applyBtn" disabled="disabled">' + this.locale.applyLabel + '</button>&nbsp;' +
102                    '<button class="' + this.clearClass + ' clearBtn">' + this.locale.clearLabel + '</button>' +
103                  '</div>' +
104                '</div>' +
105              '</div>';
106
107        this.container = $(DRPTemplate).appendTo('body');
108
109        if (hasOptions) {
110
111            if (typeof options.format == 'string')
112                this.format = options.format;
113
114            if (typeof options.separator == 'string')
115                this.separator = options.separator;
116
117            if (typeof options.startDate == 'string')
118                this.startDate = Date.parseExact(options.startDate, this.format);
119
120            if (typeof options.endDate == 'string')
121                this.endDate = Date.parseExact(options.endDate, this.format);
122
123            if (typeof options.minDate == 'string')
124                this.minDate = Date.parseExact(options.minDate, this.format);
125
126            if (typeof options.maxDate == 'string')
127                this.maxDate = Date.parseExact(options.maxDate, this.format);
128
129            if (typeof options.startDate == 'object')
130                this.startDate = options.startDate;
131
132            if (typeof options.endDate == 'object')
133                this.endDate = options.endDate;
134
135            if (typeof options.minDate == 'object')
136                this.minDate = options.minDate;
137
138            if (typeof options.maxDate == 'object')
139                this.maxDate = options.maxDate;
140
141            if (typeof options.ranges == 'object') {
142                for (var range in options.ranges) {
143
144                    var start = options.ranges[range][0];
145                    var end = options.ranges[range][1];
146
147                    if (typeof start == 'string')
148                        start = Date.parse(start);
149
150                    if (typeof end == 'string')
151                        end = Date.parse(end);
152
153                    // If we have a min/max date set, bound this range
154                    // to it, but only if it would otherwise fall
155                    // outside of the min/max.
156                    if (this.minDate && start < this.minDate)
157                        start = this.minDate;
158
159                    if (this.maxDate && end > this.maxDate)
160                        end = this.maxDate;
161
162                    // If the end of the range is before the minimum (if min is set) OR
163                    // the start of the range is after the max (also if set) don't display this
164                    // range option.
165                    if ((this.minDate && end < this.minDate) || (this.maxDate && start > this.maxDate))
166                    {
167                        continue;
168                    }
169
170                    this.ranges[range] = [start, end];
171                }
172
173                var list = '<ul>';
174                for (var range in this.ranges) {
175                    list += '<li>' + range + '</li>';
176                }
177                list += '<li>' + this.locale.customRangeLabel + '</li>';
178                list += '</ul>';
179                this.container.find('.ranges').prepend(list);
180            }
181            
182            if (typeof options.dateLimit == 'object')
183                this.dateLimit = options.dateLimit;
184
185            // update day names order to firstDay
186            if (typeof options.locale == 'object') {
187                if (typeof options.locale.firstDay == 'number') {
188                    this.locale.firstDay = options.locale.firstDay;
189                    var iterator = options.locale.firstDay;
190                    while (iterator > 0) {
191                        this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
192                        iterator--;
193                    }
194                }
195            }
196
197            if (typeof options.opens == 'string')
198                this.opens = options.opens;
199
200            if (typeof options.showWeekNumbers == 'boolean') {
201                this.showWeekNumbers = options.showWeekNumbers;
202            }
203
204            if (typeof options.buttonClasses == 'string') {
205                this.buttonClasses = [options.buttonClasses];
206            }
207
208            if (typeof options.buttonClasses == 'object') {
209                this.buttonClasses = options.buttonClasses;
210            }
211            
212            if (typeof options.showDropdowns == 'boolean') {
213                this.showDropdowns = options.showDropdowns;
214            }
215
216        }
217
218        //apply CSS classes to buttons
219        var c = this.container;
220        $.each(this.buttonClasses, function (idx, val) {
221            c.find('button').addClass(val);
222        });
223
224        if (this.opens == 'right') {
225            //swap calendar positions
226            var left = this.container.find('.calendar.left');
227            var right = this.container.find('.calendar.right');
228            left.removeClass('left').addClass('right');
229            right.removeClass('right').addClass('left');
230        }
231
232        if (typeof options == 'undefined' || typeof options.ranges == 'undefined')
233            this.container.find('.calendar').show();
234
235        if (typeof cb == 'function')
236            this.cb = cb;
237
238        this.container.addClass('opens' + this.opens);
239
240        //try parse date if in text input
241        if (!hasOptions || (typeof options.startDate == 'undefined' && typeof options.endDate == 'undefined')) {
242            if ($(this.element).is('input[type=text]')) {
243                var val = $(this.element).val();
244                var split = val.split(this.separator);
245                
246                if(split.length == 2) {
247                    this.startDate = Date.parseExact(split[0], this.format);
248                    this.endDate = Date.parseExact(split[1], this.format);
249                }
250            }
251        }        
252
253        //event listeners
254        this.container.on('mousedown', $.proxy(this.mousedown, this));
255        this.container.find('.calendar').on('click', '.prev', $.proxy(this.clickPrev, this));
256        this.container.find('.calendar').on('click', '.next', $.proxy(this.clickNext, this));
257        this.container.find('.ranges').on('click', 'button.applyBtn', $.proxy(this.clickApply, this));
258        this.container.find('.ranges').on('click', 'button.clearBtn', $.proxy(this.clickClear, this));
259
260        this.container.find('.calendar').on('click', 'td.available', $.proxy(this.clickDate, this));
261        this.container.find('.calendar').on('mouseenter', 'td.available', $.proxy(this.enterDate, this));
262        this.container.find('.calendar').on('mouseleave', 'td.available', $.proxy(this.updateView, this));
263
264        this.container.find('.ranges').on('click', 'li', $.proxy(this.clickRange, this));
265        this.container.find('.ranges').on('mouseenter', 'li', $.proxy(this.enterRange, this));
266        this.container.find('.ranges').on('mouseleave', 'li', $.proxy(this.updateView, this));
267        
268        this.container.find('.calendar').on('change', 'select.yearselect', $.proxy(this.updateYear, this));
269        this.container.find('.calendar').on('change', 'select.monthselect', $.proxy(this.updateMonth, this));
270
271        this.element.on('keyup', $.proxy(this.updateFromControl, this));
272
273        this.updateView();
274        this.updateCalendars();
275
276    };
277
278    DateRangePicker.prototype = {
279
280        constructor: DateRangePicker,
281
282        mousedown: function (e) {        
283            e.stopPropagation();
284            
285            //allow select list to function normally
286            if(!this.showDropdowns || $(e.target).not('select').length)
287                e.preventDefault();
288        },
289
290        updateView: function () {
291            this.leftCalendar.month.set({ month: this.startDate.getMonth(), year: this.startDate.getFullYear() });
292            this.rightCalendar.month.set({ month: this.endDate.getMonth(), year: this.endDate.getFullYear() });
293
294            this.container.find('input[name=daterangepicker_start]').val(this.startDate.toString(this.format));
295            this.container.find('input[name=daterangepicker_end]').val(this.endDate.toString(this.format));
296
297            if (this.startDate.equals(this.endDate) || this.startDate.isBefore(this.endDate)) {
298                this.container.find('button.applyBtn').removeAttr('disabled');
299            } else {
300                this.container.find('button.applyBtn').attr('disabled', 'disabled');
301            }
302        },
303
304        updateFromControl: function () {
305            if (!this.element.is('input')) return;
306
307            var dateString = this.element.val().split(this.separator);
308            var start = Date.parseExact(dateString[0], this.format);
309            var end = Date.parseExact(dateString[1], this.format);
310
311            if (start == null || end == null) return;
312            if (end.isBefore(start)) return;
313
314            this.startDate = start;
315            this.endDate = end;
316
317            this.updateView();
318            this.cb(this.startDate, this.endDate);
319            this.updateCalendars();
320        },
321
322        notify: function () {
323            if (!this.cleared) {
324              this.updateView();
325            }
326
327            if (this.element.is('input')) {
328                this.element.val(this.cleared ? '' : this.startDate.toString(this.format) + this.separator + this.endDate.toString(this.format));
329            }
330            var arg1 = (this.cleared ? null : this.startDate),
331                arg2 = (this.cleared ? null : this.endDate);
332            this.cleared = false;
333            this.cb(arg1,arg2);
334        },
335
336        move: function () {
337            if (this.opens == 'left') {
338                this.container.css({
339                    top: this.element.offset().top + this.element.outerHeight(),
340                    right: $(window).width() - this.element.offset().left - this.element.outerWidth(),
341                    left: 'auto'
342                });
343            } else {
344                this.container.css({
345                    top: this.element.offset().top + this.element.outerHeight(),
346                    left: this.element.offset().left,
347                    right: 'auto'
348                });
349            }
350        },
351
352        show: function (e) {
353            this.container.show();
354            this.move();
355
356            if (e) {
357                e.stopPropagation();
358                e.preventDefault();
359            }
360
361            this.changed = false;
362            
363            this.element.trigger('shown',{target:e.target,picker:this});
364
365            $(document).on('mousedown', $.proxy(this.hide, this));
366        },
367
368        hide: function (e) {
369            this.container.hide();
370            $(document).off('mousedown', this.hide);
371
372            if (this.changed) {
373                this.changed = false;
374                this.notify();
375            }
376        },
377
378        enterRange: function (e) {
379            var label = e.target.innerHTML;
380            if (label == this.locale.customRangeLabel) {
381                this.updateView();
382            } else {
383                var dates = this.ranges[label];
384                this.container.find('input[name=daterangepicker_start]').val(dates[0].toString(this.format));
385                this.container.find('input[name=daterangepicker_end]').val(dates[1].toString(this.format));
386            }
387        },
388
389        clickRange: function (e) {
390            var label = e.target.innerHTML;
391            if (label == this.locale.customRangeLabel) {
392                this.container.find('.calendar').show();
393            } else {
394                var dates = this.ranges[label];
395
396                this.startDate = dates[0];
397                this.endDate = dates[1];
398
399                this.leftCalendar.month.set({ month: this.startDate.getMonth(), year: this.startDate.getFullYear() });
400                this.rightCalendar.month.set({ month: this.endDate.getMonth(), year: this.endDate.getFullYear() });
401                this.updateCalendars();
402
403                this.changed = true;
404
405                this.container.find('.calendar').hide();
406                this.hide();
407            }
408        },
409
410        clickPrev: function (e) {
411            var cal = $(e.target).parents('.calendar');
412            if (cal.hasClass('left')) {
413                this.leftCalendar.month.add({ months: -1 });
414            } else {
415                this.rightCalendar.month.add({ months: -1 });
416            }
417            this.updateCalendars();
418        },
419
420        clickNext: function (e) {
421            var cal = $(e.target).parents('.calendar');
422            if (cal.hasClass('left')) {
423                this.leftCalendar.month.add({ months: 1 });
424            } else {
425                this.rightCalendar.month.add({ months: 1 });
426            }
427            this.updateCalendars();
428        },
429
430        enterDate: function (e) {
431
432            var title = $(e.target).attr('title');
433            var row = title.substr(1, 1);
434            var col = title.substr(3, 1);
435            var cal = $(e.target).parents('.calendar');
436
437            if (cal.hasClass('left')) {
438                this.container.find('input[name=daterangepicker_start]').val(this.leftCalendar.calendar[row][col].toString(this.format));
439            } else {
440                this.container.find('input[name=daterangepicker_end]').val(this.rightCalendar.calendar[row][col].toString(this.format));
441            }
442
443        },
444
445        clickDate: function (e) {
446            var title = $(e.target).attr('title');
447            var row = title.substr(1, 1);
448            var col = title.substr(3, 1);
449            var cal = $(e.target).parents('.calendar');
450
451            if (cal.hasClass('left')) {
452                startDate = this.leftCalendar.calendar[row][col];
453                endDate = this.endDate;
454                if (typeof this.dateLimit == 'object') {
455                    var maxDate = new Date(startDate).add(this.dateLimit);
456                    if (endDate.isAfter(maxDate)) {
457                        endDate = maxDate;
458                    }
459                }
460                this.element.trigger('clicked', {
461                  dir: 'left',
462                  picker: this
463                });
464            } else {
465                startDate = this.startDate;
466                endDate = this.rightCalendar.calendar[row][col];
467                if (typeof this.dateLimit == 'object') {
468                    var negConfig = {
469                        days: 0 - this.dateLimit.days,
470                        months: 0 - this.dateLimit.months,
471                        years: 0 - this.dateLimit.years
472                    };
473                    var minDate = new Date(endDate).add(negConfig);
474                    if (startDate.isBefore(minDate)) {
475                        startDate = minDate;
476                    }
477                }                
478                this.element.trigger('clicked', {
479                  dir: 'right',
480                  picker: this
481                });
482            }
483
484            cal.find('td').removeClass('active');
485
486            if (startDate.equals(endDate) || startDate.isBefore(endDate)) {
487                $(e.target).addClass('active');
488                if (!startDate.equals(this.startDate) || !endDate.equals(this.endDate))
489                    this.changed = true;
490                this.startDate = startDate;
491                this.endDate = endDate;
492            } else if (startDate.isAfter(endDate)) {
493                $(e.target).addClass('active');
494                this.changed = true;
495                this.startDate = startDate;
496                this.endDate = startDate.clone().add(1).days();
497            }
498
499            this.leftCalendar.month.set({ month: this.startDate.getMonth(), year: this.startDate.getFullYear() });
500            this.rightCalendar.month.set({ month: this.endDate.getMonth(), year: this.endDate.getFullYear() });
501            this.updateCalendars();
502        },
503
504        clickApply: function (e) {
505            this.hide();
506        },
507
508        clickClear: function (e) {
509            this.changed = true;
510            this.cleared = true;
511            this.hide();
512        },
513        
514        updateYear: function(e) {
515            var year = parseInt($(e.target).val());
516            var isLeft = $(e.target).closest('.calendar').hasClass('left');
517            
518            if(isLeft) {
519                this.leftCalendar.month.set({ month: this.startDate.getMonth(), year: year });
520            } else { 
521                this.rightCalendar.month.set({ month: this.endDate.getMonth(), year: year });
522            }
523            
524            this.updateCalendars();
525        },
526        
527        updateMonth: function(e) {
528            var month = parseInt($(e.target).val());
529            var isLeft = $(e.target).closest('.calendar').hasClass('left');
530            
531            if(isLeft) {
532                this.leftCalendar.month.set({ month: month, year: this.startDate.getFullYear() });
533            } else {
534                this.rightCalendar.month.set({ month: month, year: this.endDate.getFullYear() });
535            }
536            
537            this.updateCalendars();
538        },
539
540        updateCalendars: function () {
541            this.leftCalendar.calendar = this.buildCalendar(this.leftCalendar.month.getMonth(), this.leftCalendar.month.getFullYear());
542            this.rightCalendar.calendar = this.buildCalendar(this.rightCalendar.month.getMonth(), this.rightCalendar.month.getFullYear());
543            this.container.find('.calendar.left').html(this.renderCalendar(this.leftCalendar.calendar, this.startDate, this.minDate, this.maxDate));
544            this.container.find('.calendar.right').html(this.renderCalendar(this.rightCalendar.calendar, this.endDate, this.startDate, this.maxDate));
545
546            this.container.find('.ranges li').removeClass('active');
547            var customRange = true;
548            var i = 0;
549            for (var range in this.ranges) {
550                if (this.startDate.equals(this.ranges[range][0]) && this.endDate.equals(this.ranges[range][1])) {
551                    customRange = false;
552                    this.container.find('.ranges li:eq(' + i + ')').addClass('active');
553                }
554                i++;
555            }
556            if (customRange)
557                this.container.find('.ranges li:last').addClass('active');
558
559            this.element.trigger('updated', this);
560        },
561
562        buildCalendar: function (month, year) {
563
564            var firstDay = Date.today().set({ day: 1, month: month, year: year });
565            var lastMonth = firstDay.clone().add(-1).day().getMonth();
566            var lastYear = firstDay.clone().add(-1).day().getFullYear();
567
568            var daysInMonth = Date.getDaysInMonth(year, month);
569            var daysInLastMonth = Date.getDaysInMonth(lastYear, lastMonth);
570
571            var dayOfWeek = firstDay.getDay();
572
573            //initialize a 6 rows x 7 columns array for the calendar
574            var calendar = Array();
575            for (var i = 0; i < 6; i++) {
576                calendar[i] = Array();
577            }
578
579            //populate the calendar with date objects
580            var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1;
581            if (startDay > daysInLastMonth)
582                startDay -= 7;
583
584            if (dayOfWeek == this.locale.firstDay)
585                startDay = daysInLastMonth - 6;
586
587            var curDate = Date.today().set({ day: startDay, month: lastMonth, year: lastYear });
588            for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = curDate.clone().add(1).day()) {
589                if (i > 0 && col % 7 == 0) {
590                    col = 0;
591                    row++;
592                }
593                calendar[row][col] = curDate;
594            }
595
596            return calendar;
597
598        },
599        
600        renderDropdowns: function (selected, minDate, maxDate) {                        
601            var currentMonth = selected.getMonth();
602            var monthHtml = '<select class="monthselect">';
603            var inMinYear = false;
604            var inMaxYear = false;
605            
606            for (var m = 0; m < 12; m++) {
607                if ((!inMinYear || m >= minDate.getMonth()) && (!inMaxYear || m <= maxDate.getMonth())) {
608                    monthHtml += "<option value='" + m + "'" +
609                        (m === currentMonth ? " selected='selected'" : "") +
610                        ">" + this.locale.monthNames[m] + "</option>";
611                }
612            }
613            monthHtml += "</select>";
614            
615            var currentYear = selected.getFullYear();
616            var maxYear = (maxDate && maxDate.getFullYear()) || (currentYear + 5);
617            var minYear = (minDate && minDate.getFullYear()) || (currentYear - 50); 
618            var yearHtml = '<select class="yearselect">'
619            
620            for (var y = minYear; y <= maxYear; y++) {
621                yearHtml += '<option value="' + y + '"' +
622                    (y === currentYear ? ' selected="selected"' : '') +
623                    '>' + y + '</option>';
624            }
625            
626            yearHtml += '</select>';
627            
628            return monthHtml + yearHtml;
629        },
630
631        renderCalendar: function (calendar, selected, minDate, maxDate) {
632            var html = '<table class="table-condensed">';
633            html += '<thead>';
634            html += '<tr>';
635            
636            // add empty cell for week number
637            if (this.showWeekNumbers)
638                html += '<th></th>';
639            
640            if (!minDate || minDate < calendar[1][1]) {
641                html += '<th class="prev available"><i class="icon-arrow-left"></i></th>';
642            } else {
643                html += '<th></th>';
644            }
645            
646            var dateHtml = this.locale.monthNames[calendar[1][1].getMonth()] + calendar[1][1].toString(" yyyy");
647            
648            if (this.showDropdowns) {
649                dateHtml = this.renderDropdowns(calendar[1][1], minDate, maxDate);
650            }
651            
652            html += '<th colspan="5" style="width: auto">' + dateHtml + '</th>';
653            if (!maxDate || maxDate > calendar[1][1]) {
654                html += '<th class="next available"><i class="icon-arrow-right"></i></th>';
655            } else {
656                html += '<th></th>';
657            }
658
659            html += '</tr>';
660            html += '<tr>';
661            
662            // add week number label
663            if (this.showWeekNumbers)
664                html += '<th class="week">' + this.locale.weekLabel + '</th>';
665
666            $.each(this.locale.daysOfWeek, function (index, dayOfWeek) {
667                html += '<th>' + dayOfWeek + '</th>';
668            });
669
670            html += '</tr>';
671            html += '</thead>';
672            html += '<tbody>';
673
674            for (var row = 0; row < 6; row++) {
675                html += '<tr>';
676                
677                // add week number
678                if (this.showWeekNumbers)
679                    html += '<td class="week">' + calendar[row][0].getWeek() + '</td>';
680                
681                for (var col = 0; col < 7; col++) {
682                    var cname = 'available ';
683                    cname += (calendar[row][col].getMonth() == calendar[1][1].getMonth()) ? '' : 'off';
684
685                    // Normalise the time so the comparison won't fail
686                    selected.setHours(0,0,0,0);
687
688                    if ((minDate && calendar[row][col] < minDate) || (maxDate && calendar[row][col] > maxDate)) {
689                        cname = ' off disabled ';
690                    } else if (calendar[row][col].equals(selected)) {
691                        cname += ' active ';
692                        if (calendar[row][col].equals(this.startDate)) { cname += ' start-date '; }
693                        if (calendar[row][col].equals(this.endDate)) { cname += ' end-date '; }
694                    } else if (calendar[row][col] >= this.startDate && calendar[row][col] <= this.endDate) {
695                        cname += ' in-range ';
696                        if (calendar[row][col].equals(this.startDate)) { cname += ' start-date '; }
697                        if (calendar[row][col].equals(this.endDate)) { cname += ' end-date '; }
698                    }
699                    
700                    var title = 'r' + row + 'c' + col;
701                    html += '<td class="' + cname.replace(/\s+/g,' ').replace(/^\s?(.*?)\s?$/,'$1') + '" title="' + title + '">' + calendar[row][col].getDate() + '</td>';
702                }
703                html += '</tr>';
704            }
705
706            html += '</tbody>';
707            html += '</table>';
708
709            return html;
710
711        }
712
713    };
714
715    $.fn.daterangepicker = function (options, cb) {
716      this.each(function() {
717        var el = $(this);
718        if (!el.data('daterangepicker'))
719          el.data('daterangepicker', new DateRangePicker(el, options, cb));
720      });
721      return this;
722    };
723
724} (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.