PageSourceSearch

https://www.visitlesvos.gr/wp-content/themes/visitLesvos/js/evo-calendar.js

js visitlesvos.gr collected 2026-09-24 20:49:05 UTC 50,237 bytes, 1,143 lines download raw bytes

1/*!
2 * Evo Calendar - Simple and Modern-looking Event Calendar Plugin
3 *
4 * Licensed under the MIT License
5 * 
6 * Version: 1.1.3
7 * Author: Edlyn Villegas
8 * Docs: https://edlynvillegas.github.com/evo-calendar
9 * Repo: https://github.com/edlynvillegas/evo-calendar
10 * Issues: https://github.com/edlynvillegas/evo-calendar/issues
11 * 
12 */
13
14;(function(factory) {
15    'use strict';
16    if (typeof define === 'function' && define.amd) {
17        define(['jquery'], factory);
18    } else if (typeof exports !== 'undefined') {
19        module.exports = factory(require('jquery'));
20    } else {
21        factory(jQuery);
22    }
23
24}(function($) {
25    'use strict';
26    var EvoCalendar = window.EvoCalendar || {};
27    
28    EvoCalendar = (function() {
29        var instanceUid = 0;
30        function EvoCalendar(element, settings) {
31            var _ = this;
32            _.defaults = {
33                theme: null,
34                format: 'mm/dd/yyyy',
35                titleFormat: 'MM yyyy',
36                eventHeaderFormat: 'MM d, yyyy',
37                firstDayOfWeek: 0,
38                language: 'en',
39                todayHighlight: false,
40                sidebarDisplayDefault: true,
41                sidebarToggler: true,
42                eventDisplayDefault: true,
43                eventListToggler: true,
44                calendarEvents: null
45            };
46            _.options = $.extend({}, _.defaults, settings);
47
48            _.initials = {
49                default_class: $(element)[0].classList.value,
50                validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
51                dates: {
52                    en: {
53                        days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
54                        daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
55                        daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
56                        months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
56November", "December"],
57                        monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
58                        noEventForToday: "No event for today.. so take a rest! :)",
59                        noEventForThisDay: "No event for this day.. so take a rest! :)",
60                        previousYearText: "Previous year",
61                        nextYearText: "Next year",
62                        closeSidebarText: "Close sidebar",
63                        closeEventListText: "Close event list"
64                    },
65                    el: {
66                        days: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο"],
67                        daysShort: ["Κυρ", "Δευ", "Τρί", "Τετ", "Πεμ", "Παρ", "Σάβ"],
68                        daysMin: ["Κυ", "Δε", "Τρ", "Τε", "Πε", "Πα", "Σά"],
69                        months: ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"],
70                        monthsShort: ["Ιαν", "Φεβ", "Μαρ", "Απρ", "Μάι", "Ιούν", "Ιούλ", "Αύγ", "Σεπ", "Οκτ", "Νοέ", "Δεκ"],
71                        noEventForToday: "Καμία εκδήλωση για σήμερα.. ξεκουραστείτε λοιπόν! :)",
72                        noEventForThisDay: "Καμία εκδήλωση για αυτήν την ημέρα.. οπότε ξεκουραστείτε! :)",
73                        previousYearText: "Προηγούμενο έτος",
74                        nextYearText: "Προσεχές έτος",
75                        closeSidebarText: "Κλείσιμο πλαϊνής γραμμής",
76                        closeEventListText: "Κλείσιμο λίστας συμβάντων"
77                    },
78                    es: {
79                        days: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"],
80                        daysShort: ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"],
81                        daysMin: ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa"],
82                        months: ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
83                        monthsShort: ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"],
84                        noEventForToday: "No hay evento para hoy.. ¡así que descanse! :)",
85                        noEventForThisDay: "Ningún evento para este día.. ¡así que descanse! :)",
86                        previousYearText: "Año anterior",
87                        nextYearText: "El próximo año",
88                        closeSidebarText: "Cerrar la barra lateral",
89                        closeEventListText: "Cerrar la lista de eventos"
90                    },
91                    de: {
92                        days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"],
93                        daysShort: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
94                        daysMin: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
95                        months: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
96                        monthsShort: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"],
97                        noEventForToday: "Keine Veranstaltung für heute.. also ruhen Sie sich aus! :)",
98                        noEventForThisDay: "Keine Veranstaltung für diesen Tag.. also ruhen Sie sich aus! :)",
99                        previousYearText: "Vorheriges Jahr",
100                        nextYearText: "Nächstes Jahr",
101                        closeSidebarText: "Schließen Sie die Seitenleiste",
102                        closeEventListText: "Schließen Sie die Ereignisliste"
103                    },
104                    pt: {
105                        days: ["Domingo", "Segunda-Feira", "Terça-Feira", "Quarta-Feira", "Quinta-Feira", "Sexta-Feira", "SÃ
105¡bado"],
106                        daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"],
107                        daysMin: ["Do", "2a", "3a", "4a", "5a", "6a", "Sa"],
108                        months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
109                        monthsShort: ["Jan", "Feb", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
110                        noEventForToday: "Nenhum evento para hoje.. então descanse! :)",
111                        noEventForThisDay: "Nenhum evento para este dia.. então descanse! :)",
112                        previousYearText: "Ano anterior",
113                        nextYearText: "Próximo ano",
114                        closeSidebarText: "Feche a barra lateral",
115                        closeEventListText: "Feche a lista de eventos"
116                    },
117                    fr: {
118                        days: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"],
119                        daysShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"],
120                        daysMin: ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"],
121                        months: ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"],
122                        monthsShort: ["Jan", "Fév", "Mar", "Avr", "Mai", "Juin", "Juil", "Août", "Sept", "Oct", "Nov", "Déc"],
123                        noEventForToday: "Rien pour aujourd'hui... Belle journée :)",
124                        noEventForThisDay: "Rien pour ce jour-ci... Profite de te réposer :)",
125                        previousYearText: "Année précédente",
126                        nextYearText: "L'année prochaine",
127                        closeSidebarText: "Fermez la barre latérale",
128                        closeEventListText: "Fermer la liste des événements"
129                    },
130                    nl: {
131                        days: ["Zondag", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag"],
132                        daysShort: ["Zon", "Maan", "Din", "Woe", "Don", "Vrij", "Zat"],
133                        daysMin: ["Zo", "Ma", "Di", "Wo", "Do", "Vr", "Za"],
134                        months: ["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"],
135                        monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"],
136                        noEventForToday: "Geen event voor vandaag.. dus rust even uit! :)",
137                        noEventForThisDay: "Geen event voor deze dag.. dus rust even uit! :)",
138                        previousYearText: "Vorig jaar",
139                        nextYearText: "Volgend jaar",
140                        closeSidebarText: "Sluit de zijbalk",
141                        closeEventListText: "Sluit de event lijst"
142                    },
143                    id: {
144                        days: ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"],
145                        daysShort: ["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"],
146                        daysMin: ["Mi", "Sn", "Sl", "Ra", "Ka", "Ju", "Sa"],
147                        months: ["Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"],
148                        monthsShort: ["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Agu", "Sep", "Okt", "Nov", "Des"],
149                        noEventForToday: "Tidak ada Acara untuk Sekarang.. Jadi Beristirahatlah! :)",
150                        noEventForThisDay: "Tidak ada Acara untuk Hari Ini.. Jadi Beristirahatlah! :)",
151                        previousYearText: "Tahun Sebelumnya",
152                        nextYearText: "Tahun Berikutnya",
153                        closeSidebarText: "Tutup Sidebar",
154                        closeEventListText: "Tutup Daftar Acara"
155                    }
156                }
157            }
158            _.initials.weekends = {
159                sun: _.initials.dates[_.options.language].daysShort[0],
160                sat: _.initials.dates[_.options.language].daysShort[6]
161            }
162
163
164            // Format Calendar Events into selected format
165            if(_.options.calendarEvents != null) {
166                for(var i=0; i < _.options.calendarEvents.length; i++) {
167                    // If event doesn't have an id, throw an error message
168                    if(!_.options.calendarEvents[i].id) {
169                        console.log("%c Event named: \""+_.options.calendarEvents[i].name+"\" doesn't have a unique ID ", "color:white;font-weight:bold;background-color:#e21d1d;");
170                    }
171                    if(_.isValidDate(_.options.calendarEvents[i].date)) {
172                        _.options.calendarEvents[i].date = _.formatDate(_.options.calendarEvents[i].date, _.options.format)
173                    }
174                }
175            }
176
177            // Global variables
178            _.startingDay = null;
179            _.monthLength = null;
180            _.windowW = $(window).width();
181            
182            // CURRENT
183            _.$current = {
184                month: (isNaN(this.month) || this.month == null) ? new Date().getMonth() : this.month,
185                year: (isNaN(this.year) || this.year == null) ? new Date().getFullYear() : this.year,
186                date: _.formatDate(_.initials.dates[_.defaults.language].months[new Date().getMonth()]+' '+new Date().getDate()+' '+ new Date().getFullYear(), _.options.format)
187            }
188
189            // ACTIVE
190            _.$active = {
191                month: _.$current.month,
192                year: _.$current.year,
193                date: _.$current.date,
194                event_date: _.$current.date,
195                events: []
196            }
197
198            // LABELS
199            _.$label = {
200                days: [],
201                months: _.initials.dates[_.defaults.language].months,
202                days_in_month: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
203            }
204
205            // HTML Markups (template)
206            _.$markups = {
207                calendarHTML: '',
208                mainHTML: '',
209                sidebarHTML: '',
210                eventHTML: ''
211            }
212            // HTML DOM elements
213            _.$elements = {
214                calendarEl: $(element),
215                innerEl: null,
216                sidebarEl: null,
217                eventEl: null,
218
219                sidebarToggler: null,
220                eventListToggler: null,
221
222                activeDayEl: null,
223                activeMonthEl: null,
224                activeYearEl: null
225            }
226            _.$breakpoints = {
227                tablet: 768,
228                mobile: 425
229            }
230            _.$UI = {
231                hasSidebar: true,
232                hasEvent: true
233            }
234
235            _.formatDate = $.proxy(_.formatDate, _);
236            _.selectDate = $.proxy(_.selectDate, _);
237            _.selectMonth = $.proxy(_.selectMonth, _);
238            _.selectYear = $.proxy(_.selectYear, _);
239            _.selectEvent = $.proxy(_.selectEvent, _);
240            _.toggleSidebar = $.proxy(_.toggleSidebar, _);
241            _.toggleEventList = $.proxy(_.toggleEventList, _);
242            
243            _.instanceUid = instanceUid++;
244
245            _.init(true);
246        }
247
248        return EvoCalendar;
249
250    }());
251
252    // v1.0.0 - Initialize plugin
253    EvoCalendar.prototype.init = function(init) {
254        var _ = this;
255        
256        if (!$(_.$elements.calendarEl).hasClass('calendar-initialized')) {
257            $(_.$elements.calendarEl).addClass('evo-calendar calendar-initialized');
258            if (_.windowW <= _.$breakpoints.tablet) { // tablet/mobile
259                _.toggleSidebar(false);
260                _.toggleEventList(false);
261            } else {
262                if (!_.options.sidebarDisplayDefault) _.toggleSidebar(false);
263                else _.toggleSidebar(true);
264
265                if (!_.options.eventDisplayDefault) _.toggleEventList(false);
266                else _.toggleEventList(true);
267            }
268            if (_.options.theme) _.setTheme(_.options.theme); // set calendar theme
269            _.buildTheBones(); // start building the calendar components
270        }
271    };
272    // v1.0.0 - Destroy plugin
273    EvoCalendar.prototype.destroy = function() {
274        var _ = this;
275        // code here
276        _.destroyEventListener();
277        if (_.$elements.calendarEl) {
278            _.$elements.calendarEl.removeClass('calendar-initialized');
279            _.$elements.calendarEl.removeClass('evo-calendar');
280            _.$elements.calendarEl.removeClass('sidebar-hide');
281            _.$elements.calendarEl.removeClass('event-hide');
282        }
283        _.$elements.calendarEl.empty();
284        _.$elements.calendarEl.attr('class', _.initials.default_class);
285        $(_.$elements.calendarEl).trigger("destroy", [_])
286    }
287
288    // v1.0.0 - Limit title (...)
289    EvoCalendar.prototype.limitTitle = function(title, limit) {
290        var newTitle = [];
291        limit = limit === undefined ? 18 : limit;
292        if ((title).split(' ').join('').length > limit) {
293            var t = title.split(' ');
294            for (var i=0; i<t.length; i++) {
295                if (t[i].length + newTitle.join('').length <= limit) {
296                    newTitle.push(t[i])
297                }
298            }
299            return newTitle.join(' ') + '...'
300        }
301        return title;
302    }
303
304    // v1.1.2 - Check and filter strings
305    EvoCalendar.prototype.stringCheck = function(d) {
306        return d.replace(/[^\w]/g, '\\$&');
307    }
308            
309    // v1.0.0 - Parse format (date)
310    EvoCalendar.prototype.parseFormat = function(format) {
311        var _ = this;
312        if (typeof format.toValue === 'function' && typeof format.toDisplay === 'function')
313            return format;
314        // IE treats \0 as a string end in inputs (truncating the value),
315        // so it's a bad format delimiter, anyway
316        var separators = format.replace(_.initials.validParts, '\0').split('\0'),
317            parts = format.match(_.initials.validParts);
318        if (!separators || !separators.length || !parts || parts.length === 0){
319            console.log("%c Invalid date format ", "color:white;font-weight:bold;background-color:#e21d1d;");
320        }
321        return {separators: separators, parts: parts};
322    };
323    
324    // v1.0.0 - Format date
325    EvoCalendar.prototype.formatDate = function(date, format, language) {
326        var _ = this;
327        if (!date)
328            return '';
329        language = language ? language : _.defaults.language
330        if (typeof format === 'string')
331            format = _.parseFormat(format);
332        if (format.toDisplay)
333            return format.toDisplay(date, format, language);
334
335        var ndate = new Date(date);
336        // if (!_.isValidDate(ndate)) { // test
337        //     ndate = new Date(date.replace(/-/g, '/'))
338        // }
339        
340        var val = {
341            d: ndate.getDate(),
342            D: _.initials.dates[language].daysShort[ndate.getDay()],
343            DD: _.initials.dates[language].days[ndate.getDay()],
344            m: ndate.getMonth() + 1,
345            M: _.initials.dates[language].monthsShort[ndate.getMonth()],
346            MM: _.initials.dates[language].months[ndate.getMonth()],
347            yy: ndate.getFullYear().toString().substring(2),
348            yyyy: ndate.getFullYear()
349        };
350        
351        val.dd = (val.d < 10 ? '0' : '') + val.d;
352        val.mm = (val.m < 10 ? '0' : '') + val.m;
353        date = [];
354        var seps = $.extend([], format.separators);
355        for (var i=0, cnt = format.parts.length; i <= cnt; i++){
356            if (seps.length)
357                date.push(seps.shift());
358            date.push(val[format.parts[i]]);
359        }
360        return date.join('');
361    };
362
363    // v1.0.0 - Get dates between two dates
364    EvoCalendar.prototype.getBetweenDates = function(dates) {
365        var _ = this, betweenDates = [];
366        for (var x = 0; x < _.monthLength; x++) {
367            var active_date = _.formatDate(_.$label.months[_.$active.month] +' '+ (x + 1) +' '+ _.$active.year, _.options.format);
368            if (_.isBetweenDates(active_date, dates)) {
369                betweenDates.push(active_date);
370            }
371        }
372        return betweenDates;
373    };
374
375    // v1.0.0 - Check if date is between the passed calendar date 
376    EvoCalendar.prototype.isBetweenDates = function(active_date, dates) {
377        var sd, ed;
378        if (dates instanceof Array) {
379            sd = new Date(dates[0]);
380            ed = new Date(dates[1]);
381        } else {
382            sd = new Date(dates);
383            ed = new Date(dates);
384        }
385        if (sd <= new Date(active_date) && ed >= new Date(active_date)) {
386            return true;
387        }
388        return false;
389    }
390    
391    // v1.0.0 - Check if event has the same event type in the same date
392    EvoCalendar.prototype.hasSameDayEventType = function(date, type) {
393        var _ = this, eventLength = 0;
394
395        for (var i = 0; i < _.options.calendarEvents.length; i++) {
396            if (_.options.calendarEvents[i].date instanceof Array) {
397                var arr = _.getBetweenDates(_.options.calendarEvents[i].date);
398                for (var x = 0; x < arr.length; x++) {
399                    if(date === arr[x] && type === _.options.calendarEvents[i].type) {
400                        eventLength++;
401                    }
402                }
403            } else {
404                if(date === _.options.calendarEvents[i].date && type === _.options.calendarEvents[i].type) {
405                    eventLength++;
406                }
407            }
408        }
409
410        if (eventLength > 0) {
411            return true;
412        }
413        return false;
414    }
415    
416    // v1.0.0 - Set calendar theme
417    EvoCalendar.prototype.setTheme = function(themeName) {
418        var _ = this;
419        var prevTheme = _.options.theme;
420        _.options.theme = themeName.toLowerCase().split(' ').join('-');
421
422        if (_.options.theme) $(_.$elements.calendarEl).removeClass(prevTheme);
423        if (_.options.theme !== 'default') $(_.$elements.calendarEl).addClass(_.options.theme);
424    }
425
426    // v1.0.0 - Called in every resize
427    EvoCalendar.prototype.resize = function() {
428        var _ = this;
429        _.windowW = $(window).width();
430
431        if (_.windowW <= _.$breakpoints.tablet) { // tablet
432            _.toggleSidebar(false);
433            _.toggleEventList(false);
434
435            if (_.windowW <= _.$breakpoints.mobile) { // mobile
436                $(window)
437                    .off('click.evocalendar.evo-' + _.instanceUid)
438            } else {
439                $(window)
440                    .on('click.evocalendar.evo-' + _.instanceUid, $.proxy(_.toggleOutside, _));
441            }
442        } else {
443            if (!_.options.sidebarDisplayDefault) _.toggleSidebar(false);
444            else _.toggleSidebar(true);
445
446            if (!_.options.eventDisplayDefault) _.toggleEventList(false);
447            else _.toggleEventList(true);
448            
449            $(window)
450                .off('click.evocalendar.evo-' + _.instanceUid);
451        }
452    }
453
454    // v1.0.0 - Initialize event listeners
455    EvoCalendar.prototype.initEventListener = function() {
456        var _ = this;
457
458        // resize
459        $(window)
460            .off('resize.evocalendar.evo-' + _.instanceUid)
461            .on('resize.evocalendar.evo-' + _.instanceUid, $.proxy(_.resize, _));
462
463        // IF sidebarToggler: set event listener: toggleSidebar
464        if(_.options.sidebarToggler) {
465            _.$elements.sidebarToggler
466            .off('click.evocalendar')
467            .on('click.evocalendar', _.toggleSidebar);
468        }
469        
470        // IF eventListToggler: set event listener: toggleEventList
471        if(_.options.eventListToggler) {
472            _.$elements.eventListToggler
473            .off('click.evocalendar')
474            .on('click.evocalendar', _.toggleEventList);
475        }
476
477        // set event listener for each month
478        _.$elements.sidebarEl.find('[data-month-val]')
479        .off('click.evocalendar')
480        .on('click.evocalendar', _.selectMonth);
481
482        // set event listener for year
483        _.$elements.sidebarEl.find('[data-year-val]')
484        .off('click.evocalendar')
485        .on('click.evocalendar', _.selectYear);
486
487        // set event listener for every event listed
488        _.$elements.eventEl.find('[data-event-index]')
489        .off('click.evocalendar')
490        .on('click.evocalendar', _.selectEvent);
491    };
492    
493    // v1.0.0 - Destroy event listeners
494    EvoCalendar.prototype.destroyEventListener = function() {
495        var _ = this;
496        
497        $(window).off('resize.evocalendar.evo-' + _.instanceUid);
498        $(window).off('click.evocalendar.evo-' + _.instanceUid);
499        
500        // IF sidebarToggler: remove event listener: toggleSidebar
501        if(_.options.sidebarToggler) {
502            _.$elements.sidebarToggler
503            .off('click.evocalendar');
504        }
505        
506        // IF eventListToggler: remove event listener: toggleEventList
507        if(_.options.eventListToggler) {
508            _.$elements.eventListToggler
509            .off('click.evocalendar');
510        }
511
512        // remove event listener for each day
513        _.$elements.innerEl.find('.calendar-day').children()
514        .off('click.evocalendar')
515
516        // remove event listener for each month
517        _.$elements.sidebarEl.find('[data-month-val]')
518        .off('click.evocalendar');
519
520        // remove event listener for year
521        _.$elements.sidebarEl.find('[data-year-val]')
522        .off('click.evocalendar');
523
524        // remove event listener for every event listed
525        _.$elements.eventEl.find('[data-event-index]')
526        .off('click.evocalendar');
527    };
528    
529    // v1.0.0 - Calculate days (incl. monthLength, startingDays based on :firstDayOfWeekName)
530    EvoCalendar.prototype.calculateDays = function() {
531        var _ = this, nameDays, weekStart, firstDay;
532        _.monthLength = _.$label.days_in_month[_.$active.month];
532 // find number of days in month
533        if (_.$active.month == 1) { // compensate for leap year - february only!
534            if((_.$active.year % 4 == 0 && _.$active.year % 100 != 0) || _.$active.year % 400 == 0){
535                _.monthLength = 29;
536            }
537        }
538        nameDays = _.initials.dates[_.options.language].daysShort;
539        weekStart = _.options.firstDayOfWeek;
540        
541        while (_.$label.days.length < nameDays.length) {
542            if (weekStart == nameDays.length) {
543                weekStart=0;
544            }
545            _.$label.days.push(nameDays[weekStart]);
546            weekStart++;
547        }
548        firstDay = new Date(_.$active.year, _.$active.month).getDay() - weekStart;
549        _.startingDay = firstDay < 0 ? (_.$label.days.length + firstDay) : firstDay;
550    }
551
552    // v1.0.0 - Build the bones! (incl. sidebar, inner, events), called once in every initialization
553    EvoCalendar.prototype.buildTheBones = function() {
554        var _ = this;
555        _.calculateDays();
556        
557        if (!_.$elements.calendarEl.html()) {
558            var markup;
559
560            // --- BUILDING MARKUP BEGINS --- //
561
562            // sidebar
563            markup = '<div class="calendar-sidebar">'+
564                        '<div class="calendar-year">'+
565                        '<button class="icon-button" role="button" data-year-val="prev" title="'+_.initials.dates[_.options.language].previousYearText+'">'+
566                                '<span class="chevron-arrow-left"></span>'+
567                            '</button>'+
568                            '&nbsp;<p></p>&nbsp;'+
569                            '<button class="icon-button" role="button" data-year-val="next" title="'+_.initials.dates[_.options.language].nextYearText+'">'+
570                                '<span class="chevron-arrow-right"></span>'+
571                            '</button>'+
572                        '</div><div class="month-list">'+
573                        '<ul class="calendar-months">';
574                            for(var i = 0; i < _.$label.months.length; i++) {
575                                markup += '<li class="month" role="button" data-month-val="'+i+'">'+_.initials.dates[_.options.language].months[i]+'</li>';
576                            }
577                        markup += '</ul>';
578            markup += '</div></div>';
579        
580            // inner
581            markup += '<div class="calendar-inner">'+
582                            '<table class="calendar-table">'+
583                                '<tr><th colspan="7"></th></tr>'+
584                                '<tr class="calendar-header">';
585                                for(var i = 0; i < _.$label.days.length; i++ ){
586                                    var headerClass = "calendar-header-day";
587                                    if (_.$label.days[i] === _.initials.weekends.sat || _.$label.days[i] === _.initials.weekends.sun) {
588                                        headerClass += ' --weekend';
589                                    }
590                                    markup += '<td class="'+headerClass+'">'+_.$label.days[i]+'</td>';
591                                }
592                                markup += '</tr></table>'+
593                        '</div>';
594
595            // events
596            markup += '<div class="calendar-events">'+
597                            '<div class="event-header"><p></p></div>'+
598                            '<div class="event-list"></div>'+
599                        '</div>';
600
601            // --- Finally, build it now! --- //
602            _.$elements.calendarEl.html(markup);
603
604            if (!_.$elements.sidebarEl) _.$elements.sidebarEl = $(_.$elements.calendarEl).find('.calendar-sidebar');
605            if (!_.$elements.innerEl) _.$elements.innerEl = $(_.$elements.calendarEl).find('.calendar-inner');
606            if (!_.$elements.eventEl) _.$elements.eventEl = $(_.$elements.calendarEl).find('.calendar-events');
607
608            // if: _.options.sidebarToggler
609            if(_.options.sidebarToggler) {
610                $(_.$elements.sidebarEl).append('<span id="sidebarToggler" role="button" aria-pressed title="'+_.initials.dates[_.options.language].closeSidebarText+'"><button class="icon-button"><span class="bars"></span></button></span>');
611                if(!_.$elements.sidebarToggler) _.$elements.sidebarToggler = $(_.$elements.sidebarEl).find('span#sidebarToggler');
612            }
613            if(_.options.eventListToggler) {
614                $(_.$elements.calendarEl).append('<span id="eventListToggler" role="button" aria-pressed title="'+_.initials.dates[_.options.language].closeEventListText+'"><button class="icon-button"><span class="chevron-arrow-right"></span></button></span>');
615                if(!_.$elements.eventListToggler) _.$elements.eventListToggler = $(_.$elements.calendarEl).find('span#eventListToggler');
616            }
617        }
618
619        _.buildSidebarYear();
620        _.buildSidebarMonths();
621        _.buildCalendar();
622        _.buildEventList();
623        _.initEventListener(); // test
624
625        _.resize();
626    }
627
628    // v1.0.0 - Build Event: Event list
629    EvoCalendar.prototype.buildEventList = function() {
630        var _ = this, markup, hasEventToday = false;
631        
632        _.$active.events = [];
633        // Event date
634        var title = _.formatDate(_.$active.date, _.options.eventHeaderFormat, _.options.language);
635        _.$elements.eventEl.find('.event-header > p').text(title);
636        // Event list
637        var eventListEl = _.$elements.eventEl.find('.event-list');
638        // Clear event list item(s)
639        if (eventListEl.children().length > 0) eventListEl.empty();
640        if (_.options.calendarEvents) {
641            for (var i = 0; i < _.options.calendarEvents.length; i++) {
642                if(_.isBetweenDates(_.$active.date, _.options.calendarEvents[i].date)) {
643                    eventAdder(_.options.calendarEvents[i])
644                }
645                else if (_.options.calendarEvents[i].everyYear) {
646                    var d = new Date(_.$active.date).getMonth() + 1 + ' ' + new Date(_.$active.date).getDate();
647                    var dd = new Date(_.options.calendarEvents[i].date).getMonth() + 1 + ' ' + new Date(_.options.calendarEvents[i].date).getDate();
648                    // var dates = [_.formatDate(_.options.calendarEvents[i].date[0], 'mm/dd'), _.formatDate(_.options.calendarEvents[i].date[1], 'mm/dd')];
649
650                    if(d==dd) {
651                        eventAdder(_.options.calendarEvents[i])
652                    }
653                }
654            };
655        }
656        function eventAdder(event) {
657            hasEventToday = true;
658            _.addEventList(event)
659        }
660        // IF: no event for the selected date
661        if(!hasEventToday) {
662            markup = '<div class="event-empty">';
663            if (_.$active.date === _.$current.date) {
664                markup += '<p>'+_.initials.dates[_.options.language].noEventForToday+'</p>';
665            } else {
666                markup += '<p>'+_.initials.dates[_.options.language].noEventForThisDay+'</p>';
667            }
668            markup += '</div>';
669        }
670        eventListEl.append(markup)
671    }
672
673    // v1.0.0 - Add single event to event list
674    EvoCalendar.prototype.addEventList = function(event_data) {
675        var _ = this, markup;
676        var eventListEl = _.$elements.eventEl.find('.event-list');
677        if (eventListEl.find('[data-event-index]').length === 0) eventListEl.empty();
678        _.$active.events.push(event_data);
679        markup = '<div class="event-container" role="button" data-event-index="'+(event_data.id)+'">';
680        markup += '<div class="event-icon"><div class="event-bullet-'+event_data.type+'"';
681        if (event_data.color) {
682            markup += 'style="background-color:'+event_data.color+'"'
683        }
684        markup += '></div></div><div class="event-info"><p class="event-title">'+_.limitTitle(event_data.name);
685        if (event_data.badge) markup += '<span>'+event_data.badge+'</span>';
686        markup += '</p>'
687        if (event_data.description) markup += '<p class="event-desc">'+event_data.description+'</p>';
688        markup += '</div>';
689        markup += '</div>';
690        eventListEl.append(markup);
691
692        _.$elements.eventEl.find('[data-event-index="'+(event_data.id)+'"]')
693        .off('click.evocalendar')
694        .on('click.evocalendar', _.selectEvent);
695    }
696    // v1.0.0 - Remove single event to event list
697    EvoCalendar.prototype.removeEventList = function(event_data) {
698        var _ = this, markup;
699        var eventListEl = _.$elements.eventEl.find('.event-list');
700        if (eventListEl.find('[data-event-index="'+event_data+'"]').length === 0) return; // event not in active events
701        eventListEl.find('[data-event-index="'+event_data+'"]').remove();
702        if (eventListEl.find('[data-event-index]').length === 0) {
703            eventListEl.empty();
704            if (_.$active.date === _.$current.date) {
705                markup += '<p>'+_.initials.dates[_.options.language].noEventForToday+'</p>';
706            } else {
707                markup += '<p>'+_.initials.dates[_.options.language].noEventForThisDay+'</p>';
708            }
709            eventListEl.append(markup)
710        }
711    }
712    
713    // v1.0.0 - Build Sidebar: Year text
714    EvoCalendar.prototype.buildSidebarYear = function() {
715        var _ = this;
716        
717        _.$elements.sidebarEl.find('.calendar-year > p').text(_.$active.year);
718    }
719
720    // v1.0.0 - Build Sidebar: Months list text
721    EvoCalendar.prototype.buildSidebarMonths = function() {
722        var _ = this;
723        
724        _.$elements.sidebarEl.find('.calendar-months > [data-month-val]').removeClass('active-month');
725        _.$elements.sidebarEl.find('.calendar-months > [data-month-val="'+_.$active.month+'"]').addClass('active-month');
726    }
727
728    // v1.0.0 - Build Calendar: Title, Days
729    EvoCalendar.prototype.buildCalendar = function() {
730        var _ = this, markup, title;
731        
732        _.calculateDays();
733
734        title = _.formatDate(new Date(_.$label.months[_.$active.month] +' 1 '+ _.$active.year), _.options.titleFormat, _.options.language);
735        _.$elements.innerEl.find('.calendar-table th').text(title);
736
737        _.$elements.innerEl.find('.calendar-body').remove(); // Clear days
738        
739        markup += '<tr class="calendar-body">';
740                    var day = 1;
741                    for (var i = 0; i < 9; i++) { // this loop is for is weeks (rows)
742                        for (var j = 0; j < _.$label.days.length; j++) { // this loop is for weekdays (cells)
743                            if (day <= _.monthLength && (i > 0 || j >= _.startingDay)) {
744                                var dayClass = "calendar-day";
745                                if (_.$label.days[j] === _.initials.weekends.sat || _.$label.days[j] === _.initials.weekends.sun) {
746                                    dayClass += ' --weekend'; // add '--weekend' to sat sun
747                                }
748                                markup += '<td class="'+dayClass+'">';
749
750                                var thisDay = _.formatDate(_.$label.months[_.$active.month]+' '+day+' '+_.$active.year, _.options.format);
751                                markup += '<div class="day" role="button" data-date-val="'+thisDay+'">'+day+'</div>';
752                                day++;
753                            } else {
754                                markup += '<td>';
755                            }
756                            markup += '</td>';
757                        }
758                        if (day > _.monthLength) {
759                            break; // stop making rows if we've run out of days
760                        } else {
761                            markup += '</tr><tr class="calendar-body">'; // add if not
762                        }
763                    }
764                    markup += '</tr>';
765        _.$elements.innerEl.find('.calendar-table').append(markup);
766
767        if(_.options.todayHighlight) {
768            _.$elements.innerEl.find("[data-date-val='" + _.$current.date + "']").addClass('calendar-today');
769        }
770        
771        // set event listener for each day
772        _.$elements.innerEl.find('.calendar-day').children()
773        .off('click.evocalendar')
774        .on('click.evocalendar', _.selectDate)
775
776        var selectedDate = _.$elements.innerEl.find("[data-date-val='" + _.$active.date + "']");
777        
778        if (selectedDate) {
779            // Remove active class to all
780            _.$elements.innerEl.children().removeClass('calendar-active');
781            // Add active class to selected date
782            selectedDate.addClass('calendar-active');
783        }
784        if(_.options.calendarEvents != null) { // For event indicator (dots)
785            _.buildEventIndicator();
786        }
787    };
788
789    // v1.0.0 - Add event indicator/s (dots)
790    EvoCalendar.prototype.addEventIndicator = function(event) {
791        var _ = this, htmlToAppend, thisDate;
792        var event_date = event.date;
793        var type = _.stringCheck(event.type);
794        
795        if (event_date instanceof Array) {
796            if (event.everyYear) {
797                for (var x=0; x<event_date.length; x++) {
798                    event_date[x] = _.formatDate(new Date(event_date[x]).setFullYear(_.$active.year), _.options.format);
799                }
800            }
801            var active_date = _.getBetweenDates(event_date);
802            
803            for (var i=0; i<active_date.length; i++) {
804                appendDot(active_date[i]);
805            }
806        } else {
807            if (event.everyYear) {
808                event_date = _.formatDate(new Date(event_date).setFullYear(_.$active.year), _.options.format);
809            }
810            appendDot(event_date);
811        }
812
813        function appendDot(date) {
814            thisDate = _.$elements.innerEl.find('[data-date-val="'+date+'"]');
815
816            if (thisDate.find('span.event-indicator').length === 0) {
817                thisDate.append('<span class="event-indicator"></span>');
818            }
819            
820            if (thisDate.find('span.event-indicator > .type-bullet > .type-'+type).length === 0) {
821                htmlToAppend = '<div class="type-bullet"><div ';
822                
823                htmlToAppend += 'class="type-'+event.type+'"'
824                if (event.color) { htmlToAppend += 'style="background-color:'+event.color+'"' }
825                htmlToAppend += '></div></div>';
826                thisDate.find('.event-indicator').append(htmlToAppend);
827            }
828        }      
829    };
830    
831    // v1.0.0 - Remove event indicator/s (dots)
832    EvoCalendar.prototype.removeEventIndicator = function(event) {
833        var _ = this;
834        var event_date = event.date;
835        var type = _.stringCheck(event.type);
836
837        if (event_date instanceof Array) {
838            var active_date = _.getBetweenDates(event_date);
839            
840            for (var i=0; i<active_date.length; i++) {
841                removeDot(active_date[i]);
842            }
843        } else {
844            removeDot(event_date);
845        }
846
847        function removeDot(date) {
848            // Check if no '.event-indicator', 'cause nothing to remove
849            if (_.$elements.innerEl.find('[data-date-val="'+date+'"] span.event-indicator').length === 0) {
850                return;
851            }
852
853            // // If has no type of event, then delete 
854            if (!_.hasSameDayEventType(date, type)) {
855                _.$elements.innerEl.find('[data-date-val="'+date+'"] span.event-indicator > .type-bullet > .type-'+type).parent().remove();
856            }
857        }
858    };
859    
860    /****************
861    *    METHODS    *
862    ****************/
863
864    // v1.0.0 - Build event indicator on each date
865    EvoCalendar.prototype.buildEventIndicator = function() {
866        var _ = this;
867        
868        // prevent duplication
869        _.$elements.innerEl.find('.calendar-day > day > .event-indicator').empty();
870        
871        for (var i = 0; i < _.options.calendarEvents.length; i++) {
872            _.addEventIndicator(_.options.calendarEvents[i]);
873        }
874    };
875
876    // v1.0.0 - Select event
877    EvoCalendar.prototype.selectEvent = function(event) {
878        var _ = this;
879        var el = $(event.target).closest('.event-container');
880        var id = $(el).data('eventIndex').toString();
881        var index = _.options.calendarEvents.map(function (event) { return (event.id).toString() }).indexOf(id);
882        var modified_event = _.options.calendarEvents[index];
883        if (modified_event.date instanceof Array) {
884            modified_event.dates_range = _.getBetweenDates(modified_event.date);
885        }
886        $(_.$elements.calendarEl).trigger("selectEvent", [_.options.calendarEvents[index]])
887    }
888
889    // v1.0.0 - Select year
890    EvoCalendar.prototype.selectYear = function(event) {
891        var _ = this;
892        var el, yearVal;
893
894        if (typeof event === 'string' || typeof event === 'number') {
895            if ((parseInt(event)).toString().length === 4) {
896                yearVal = parseInt(event);
897            }
898        } else {
899            el = $(event.target).closest('[data-year-val]');
900            yearVal = $(el).data('yearVal');
901        }
902
903        if(yearVal == "prev") {
904            --_.$active.year;
905        } else if (yearVal == "next") {
906            ++_.$active.year;
907        } else if (typeof yearVal === 'number') {
908            _.$active.year = yearVal;
909        }
910        
911        if (_.windowW <= _.$breakpoints.mobile) {
912            if(_.$UI.hasSidebar) _.toggleSidebar(false);
913        }
914        
915        $(_.$elements.calendarEl).trigger("selectYear", [_.$active.year])
916
917        _.buildSidebarYear();
918        _.buildCalendar();
919    };
920
921    // v1.0.0 - Select month
922    EvoCalendar.prototype.selectMonth = function(event) {
923        var _ = this;
924        
925        if (typeof event === 'string' || typeof event === 'number') {
926            if (event >= 0 && event <=_.$label.months.length) {
927                // if: 0-11
928                _.$active.month = (event).toString();
929            }
930        } else {
931            // if month is manually selected
932            _.$active.month = $(event.currentTarget).data('monthVal');
933        }
934        
935        _.buildSidebarMonths();
936        _.buildCalendar();
937        
938        if (_.windowW <= _.$breakpoints.tablet) {
939            if(_.$UI.hasSidebar) _.toggleSidebar(false);
940        }
941
942        // EVENT FIRED: selectMonth
943        $(_.$elements.calendarEl).trigger("selectMonth", [_.initials.dates[_.options.language].months[_.$active.month], _.$active.month])
944    };
945
946    // v1.0.0 - Select specific date
947    EvoCalendar.prototype.selectDate = function(event) {
948        var _ = this;
949        var oldDate = _.$active.date;
950        var date, year, month, activeDayEl, isSameDate;
951
952        if (typeof event === 'string' || typeof event === 'number' || event instanceof Date) {
953            date = _.formatDate(new Date(event), _.options.format)
954            year = new Date(date).getFullYear();
955            month = new Date(date).getMonth();
956            
957            if (_.$active.year !== year) _.selectYear(year);
958            if (_.$active.month !== month) _.selectMonth(month);
959            activeDayEl = _.$elements.innerEl.find("[data-date-val='" + date + "']");
960        } else {
961            activeDayEl = $(event.currentTarget);
962            date = activeDayEl.data('dateVal')
963        }
964        isSameDate = _.$active.date === date;
965        // Set new active date
966        _.$active.date = date;
967        _.$active.event_date = date;
968        // Remove active class to all
969        _.$elements.innerEl.find('[data-date-val]').removeClass('calendar-active');
970        // Add active class to selected date
971        activeDayEl.addClass('calendar-active');
972        // Build event list if not the same date events built
973        if (!isSameDate) _.buildEventList();
974
975        // EVENT FIRED: selectDate
976        $(_.$elements.calendarEl).trigger("selectDate", [_.$active.date, oldDate])
977    };
978    
979    // v1.0.0 - Return active date
980    EvoCalendar.prototype.getActiveDate = function() {
981        var _ = this;
982        return _.$active.date;
983    }
984    
985    // v1.0.0 - Return active events
986    EvoCalendar.prototype.getActiveEvents = function() {
987        var _ = this;
988        return _.$active.events;
989    }
990
991    // v1.0.0 - Hide Sidebar/Event List if clicked outside
992    EvoCalendar.prototype.toggleOutside = function(event) {
993        var _ = this, isInnerClicked;
994        
995        isInnerClicked = event.target === _.$elements.innerEl[0];
996
997        if (_.$UI.hasSidebar && isInnerClicked) _.toggleSidebar(false);
998        if (_.$UI.hasEvent && isInnerClicked) _.toggleEventList(false);
999    }
1000
1001    // v1.0.0 - Toggle Sidebar
1002    EvoCalendar.prototype.toggleSidebar = function(event) {
1003        var _ = this;
1004
1005        if (event === undefined || event.originalEvent) {
1006            $(_.$elements.calendarEl).toggleClass('sidebar-hide');
1007            _.$UI.hasSidebar = !_.$UI.hasSidebar;
1008        } else {
1009            if(event) {
1010                $(_.$elements.calendarEl).removeClass('sidebar-hide');
1011                _.$UI.hasSidebar = true;
1012            } else {
1013                $(_.$elements.calendarEl).addClass('sidebar-hide');
1014                _.$UI.hasSidebar = false;
1015            }
1016        }
1017
1018        if (_.windowW <= _.$breakpoints.tablet) {
1019            if (_.$UI.hasSidebar && _.$UI.hasEvent) _.toggleEventList();
1020        }
1021    };
1022
1023    // v1.0.0 - Toggle Event list
1024    EvoCalendar.prototype.toggleEventList = function(event) {
1025        var _ = this;
1026
1027        if (event === undefined || event.originalEvent) {
1028            $(_.$elements.calendarEl).toggleClass('event-hide');
1029            _.$UI.hasEvent = !_.$UI.hasEvent;
1030        } else {
1031            if(event) {
1032                $(_.$elements.calendarEl).removeClass('event-hide');
1033                _.$UI.hasEvent = true;
1034            } else {
1035                $(_.$elements.calendarEl).addClass('event-hide');
1036                _.$UI.hasEvent = false;
1037            }
1038        }
1039
1040        if (_.windowW <= _.$breakpoints.tablet) {
1041            if (_.$UI.hasEvent && _.$UI.hasSidebar) _.toggleSidebar();
1042        }
1043    };
1044
1045    // v1.0.0 - Add Calendar Event(s)
1046    EvoCalendar.prototype.addCalendarEvent = function(arr) {
1047        var _ = this;
1048
1049        function addEvent(data) {
1050            if(!data.id) {
1051                console.log("%c Event named: \""+data.name+"\" doesn't have a unique ID ", "color:white;font-weight:bold;background-color:#e21d1d;");
1052            }
1053
1054            if (data.date instanceof Array) {
1055                for (var j=0; j < data.date.length; j++) {
1056                    if(isDateValid(data.date[j])) {
1057                        data.date[j] = _.formatDate(new Date(data.date[j]), _.options.format);
1058                    }
1059                }
1060            } else {
1061                if(isDateValid(data.date)) {
1062                    data.date = _.formatDate(new Date(data.date), _.options.format);
1063                }
1064            }
1065            
1066            if (!_.options.calendarEvents) _.options.calendarEvents = [];
1067            _.options.calendarEvents.push(data);
1068            // add to date's indicator
1069            _.addEventIndicator(data);
1070            // add to event list IF active.event_date === data.date
1071            if (_.$active.event_date === data.date) _.addEventList(data);
1072            // _.$elements.innerEl.find("[data-date-val='" + data.date + "']")
1073
1074            function isDateValid(date) {
1075                if(_.isValidDate(date)) {
1076                    return true;
1077                } else {
1078                    console.log("%c Event named: \""+data.name+"\" has invalid date ", "color:white;font-weight:bold;background-color:#e21d1d;");
1079                }
1080                return false;
1081            }
1082        }
1083        if (arr instanceof Array) { // Arrays of events
1084            for(var i=0; i < arr.length; i++) {
1085                addEvent(arr[i])
1086            }
1087        } else if (typeof arr === 'object') { // Single event
1088            addEvent(arr)
1089        }
1090    };
1091
1092    // v1.0.0 - Remove Calendar Event(s)
1093    EvoCalendar.prototype.removeCalendarEvent = function(arr) {
1094        var _ = this;
1095
1096        function deleteEvent(data) {
1097            // Array index
1098            var index = _.options.calendarEvents.map(function (event) { return event.id }).indexOf(data);
1099            
1100            if (index >= 0) {
1101                var event = _.options.calendarEvents[index];
1102                // Remove event from calendar events
1103                _.options.calendarEvents.splice(index, 1);
1104                // remove to event list
1105                _.removeEventList(data);
1106                // remove event indicator
1107                _.removeEventIndicator(event);
1108            } else {
1109                console.log("%c "+data+": ID not found ", "color:white;font-weight:bold;background-color:#e21d1d;");
1110            }
1111        }
1112        if (arr instanceof Array) { // Arrays of index
1113            for(var i=0; i < arr.length; i++) {
1114                deleteEvent(arr[i])
1115            }
1116        } else { // Single index
1117            deleteEvent(arr)
1118        }
1119    };
1120
1121    // v1.0.0 - Check if date is valid
1122    EvoCalendar.prototype.isValidDate = function(d){
1123        return new Date(d) && !isNaN(new Date(d).getTime());
1124    }
1125
1126    $.fn.evoCalendar = function() {
1127        var _ = this,
1128            opt = arguments[0],
1129            args = Array.prototype.slice.call(arguments, 1),
1130            l = _.length,
1131            i,
1132            ret;
1133        for (i = 0; i < l; i++) {
1134            if (typeof opt == 'object' || typeof opt == 'undefined')
1135                _[i].evoCalendar = new EvoCalendar(_[i], opt);
1136            else
1137                ret = _[i].evoCalendar[opt].apply(_[i].evoCalendar, args);
1138            if (typeof ret != 'undefined') return ret;
1139        }
1140        return _;
1141    };
1142
1143}));

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.