PageSourceSearch

https://soshcaraibe.fr/themes/custom/sosh/assets/vendors/jsuites.js?v=1789501364

js soshcaraibe.fr collected 2026-09-24 13:26:05 UTC 392,352 bytes, 12,414 lines download raw bytes

1/**
2 * (c) jSuites Javascript Web Components
3 *
4 * Website: https://jsuites.net
5 * Description: Create amazing web based applications.
6 *
7 * MIT License
8 *
9 */
10;(function (global, factory) {
11    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12    typeof define === 'function' && define.amd ? define(factory) :
13    global.jSuites = factory();
14}(this, (function () {
15
16    'use strict';
17
18var jSuites = {};
19
20var Version = '4.17.5';
21
22var Events = function() {
23
24    document.jsuitesComponents = [];
25
26    var find = function(DOMElement, component) {
27        if (DOMElement[component.type] && DOMElement[component.type] == component) {
28            return true;
29        }
30        if (DOMElement.component && DOMElement.component == component) {
31            return true;
32        }
33        if (DOMElement.parentNode) {
34            return find(DOMElement.parentNode, component);
35        }
36        return false;
37    }
38
39    var isOpened = function(e) {
40        if (document.jsuitesComponents && document.jsuitesComponents.length > 0) {
41            for (var i = 0; i < document.jsuitesComponents.length; i++) {
42                if (document.jsuitesComponents[i] && ! find(e, document.jsuitesComponents[i])) {
43                    document.jsuitesComponents[i].close();
44                }
45            }
46        }
47    }
48
49    // Width of the border
50    var cornerSize = 15;
51
52    // Current element
53    var element = null;
54
55    // Controllers
56    var editorAction = false;
57
58    // Event state
59    var state = {
60        x: null,
61        y: null,
62    }
63
64    // Tooltip element
65    var tooltip = document.createElement('div')
66    tooltip.classList.add('jtooltip');
67
68    // Events
69    var mouseDown = function(e) {
70        // Check if this is the floating
71        var item = jSuites.findElement(e.target, 'jpanel');
72        // Jfloating found
73        if (item && ! item.classList.contains("readonly")) {
74            // Add focus to the chart container
75            item.focus();
76            // Keep the tracking information
77            var rect = e.target.getBoundingClientRect();
78            editorAction = {
79                e: item,
80                x: e.clientX,
81                y: e.clientY,
82                w: rect.width,
83                h: rect.height,
84                d: item.style.cursor,
85                resizing: item.style.cursor ? true : false,
86                actioned: false,
87            }
88
89            // Make sure width and height styling is OK
90            if (! item.style.width) {
91                item.style.width = rect.width + 'px';
92            }
93
94            if (! item.style.height) {
95                item.style.height = rect.height + 'px';
96            }
97
98            // Remove any selection from the page
99            var s = window.getSelection();
100            if (s.rangeCount) {
101                for (var i = 0; i < s.rangeCount; i++) {
102                    s.removeRange(s.getRangeAt(i));
103                }
104            }
105
106            e.preventDefault();
107            e.stopPropagation();
108        } else {
109            // No floating action found
110            editorAction = false;
111        }
112
113        // Verify current components tracking
114        if (e.changedTouches && e.changedTouches[0]) {
115            var x = e.changedTouches[0].clientX;
116            var y = e.changedTouches[0].clientY;
117        } else {
118            var x = e.clientX;
119            var y = e.clientY;
120        }
121
122        // Which component I am clicking
123        var path = e.path || (e.composedPath && e.composedPath());
124
125        // If path available get the first element in the chain
126        if (path) {
127            element = path[0];
128        } else {
129            // Try to guess using the coordinates
130            if (e.target && e.target.shadowRoot) {
131                var d = e.target.shadowRoot;
132            } else {
133                var d = document;
134            }
135            // Get the first target element
136            element = d.elementFromPoint(x, y);
137        }
138
139        isOpened(element);
140    }
141
142    var mouseUp = function(e) {
143        if (editorAction && editorAction.e) {
144            if (typeof(editorAction.e.refresh) == 'function' && state.actioned) {
145                editorAction.e.refresh();
146            }
147            editorAction.e.style.cursor = '';
148        }
149
150        // Reset
151        state = {
152            x: null,
153            y: null,
154        }
155
156        editorAction = false;
157    }
158
159    var mouseMove = function(e) {
160        if (editorAction) {
161            var x = e.clientX || e.pageX;
162            var y = e.clientY || e.pageY;
163
164            // Action on going
165            if (! editorAction.resizing) {
166                if (state.x == null && state.y == null) {
167                    state.x = x;
168                    state.y = y;
169                }
170
171                var dx = x - state.x;
172                var dy = y - state.y;
173                var top = editorAction.e.offsetTop + dy;
174                var left = editorAction.e.offsetLeft + dx;
175
176                // Update position
177                editorAction.e.style.top = top + 'px';
178                editorAction.e.style.left = left + 'px';
179                editorAction.e.style.cursor = "move";
180
181                state.x = x;
182                state.y = y;
183
184
185                // Update element
186                if (typeof(editorAction.e.refresh) == 'function') {
187                    state.actioned = true;
188                    editorAction.e.refresh('position', top, left);
189                }
190            } else {
191                var width = null;
192                var height = null;
193
194                if (editorAction.d == 'e-resize' || editorAction.d == 'ne-resize' || editorAction.d == 'se-resize') {
195                    // Update width
196                    width = editorAction.w + (x - editorAction.x);
197                    editorAction.e.style.width = width + 'px';
198
199                    // Update Height
200                    if (e.shiftKey) {
201                        var newHeight = (x - editorAction.x) * (editorAction.h / editorAction.w);
202                        height = editorAction.h + newHeight;
203                        editorAction.e.style.height = height + 'px';
204                    } else {
205                        var newHeight = false;
206                    }
207                }
208
209                if (! newHeight) {
210                    if (editorAction.d == 's-resize' || editorAction.d == 'se-resize' || editorAction.d == 'sw-resize') {
211                        height = editorAction.h + (y - editorAction.y);
212                        editorAction.e.style.height = height + 'px';
213                    }
214                }
215
216                // Update element
217                if (typeof(editorAction.e.refresh) == 'function') {
218                    state.actioned = true;
219                    editorAction.e.refresh('dimensions', width, height);
220                }
221            }
222        } else {
223            // Resizing action
224            var item = jSuites.findElement(e.target, 'jpanel');
225            // Found eligible component
226            if (item) {
227                if (item.getAttribute('tabindex')) {
228                    var rect = item.getBoundingClientRect();
229                    if (e.clientY - rect.top < cornerSize) {
230                        if (rect.width - (e.clientX - rect.left) < cornerSize) {
231                            item.style.cursor = 'ne-resize';
232                        } else if (e.clientX - rect.left < cornerSize) {
233                            item.style.cursor = 'nw-resize';
234                        } else {
235                            item.style.cursor = 'n-resize';
236                        }
237                    } else if (rect.height - (e.clientY - rect.top) < cornerSize) {
238                        if (rect.width - (e.clientX - rect.left) < cornerSize) {
239                            item.style.cursor = 'se-resize';
240                        } else if (e.clientX - rect.left < cornerSize) {
241                            item.style.cursor = 'sw-resize';
242                        } else {
243                            item.style.cursor = 's-resize';
244                        }
245                    } else if (rect.width - (e.clientX - rect.left) < cornerSize) {
246                        item.style.cursor = 'e-resize';
247                    } else if (e.clientX - rect.left < cornerSize) {
248                        item.style.cursor = 'w-resize';
249                    } else {
250                        item.style.cursor = '';
251                    }
252                }
253            }
254        }
255    }
256
257    var mouseOver = function(e) {
258        var message = e.target.getAttribute('data-tooltip');
259        if (message) {
260            // Instructions
261            tooltip.innerText = message;
262
263            // Position
264            if (e.changedTouches && e.changedTouches[0]) {
265                var x = e.changedTouches[0].clientX;
266                var y = e.changedTouches[0].clientY;
267            } else {
268                var x = e.clientX;
269                var y = e.clientY;
270            }
271
272            tooltip.style.top = y + 'px';
273            tooltip.style.left = x + 'px';
274            document.body.appendChild(tooltip);
275        } else if (tooltip.innerText) {
276            tooltip.innerText = '';
277            document.body.removeChild(tooltip);
278        }
279    }
280
281    var dblClick = function(e) {
282        var item = jSuites.findElement(e.target, 'jpanel');
283        if (item && typeof(item.dblclick) == 'function') {
284            // Create edition
285            item.dblclick(e);
286        }
287    }
288
289    var contextMenu = function(e) {
290        var item = document.activeElement;
291        if (item && typeof(item.contextmenu) == 'function') {
292            // Create edition
293            item.contextmenu(e);
294
295            e.preventDefault();
296            e.stopImmediatePropagation();
297        } else {
298            // Search for possible context menus
299            item = jSuites.findElement(e.target, function(o) {
300                return o.tagName && o.getAttribute('aria-contextmenu-id');
301            });
302
303            if (item) {
304                var o = document.querySelector('#' + item);
305                if (! o) {
306                    console.error('JSUITES: contextmenu id not found: ' + item);
307                } else {
308                    o.contextmenu.open(e);
309                    e.preventDefault();
310                    e.stopImmediatePropagation();
311                }
312            }
313        }
314    }
315
316    var keyDown = function(e) {
317        var item = document.activeElement;
318        if (item) {
319            if (e.key == "Delete" && typeof(item.delete) == 'function') {
320                item.delete();
321                e.preventDefault();
322                e.stopImmediatePropagation();
323            }
324        }
325
326        if (document.jsuitesComponents && document.jsuitesComponents.length) {
327            if (item = document.jsuitesComponents[document.jsuitesComponents.length - 1]) {
328                if (e.key == "Escape" && typeof(item.isOpened) == 'function' && typeof(item.close) == 'function') {
329                    if (item.isOpened()) {
330                        item.close();
331                        e.preventDefault();
332                        e.stopImmediatePropagation();
333                    }
334                }
335            }
336        }
337    }
338
339    document.addEventListener('mouseup', mouseUp);
340    document.addEventListener("mousedown", mouseDown);
341    document.addEventListener('mousemove', mouseMove);
342    document.addEventListener('mouseover', mouseOver);
343    document.addEventListener('dblclick', dblClick);
344    document.addEventListener('keydown', keyDown);
345    document.addEventListener('contextmenu', contextMenu);
346}
347
348/**
349 * Global jsuites event
350 */
351if (typeof(document) !== "undefined" && ! document.jsuitesComponents) {
352    Events();
353}
354
355jSuites.version = Version;
356
357jSuites.setExtensions = function(o) {
358    if (typeof(o) == 'object') {
359        var k = Object.keys(o);
360        for (var i = 0; i < k.length; i++) {
361            jSuites[k[i]] = o[k[i]];
362        }
363    }
364}
365
366jSuites.tracking = function(component, state) {
367    if (state == true) {
368        document.jsuitesComponents = document.jsuitesComponents.filter(function(v) {
369            return v !== null;
370        });
371
372        // Start after all events
373        setTimeout(function() {
374            document.jsuitesComponents.push(component);
375        }, 0);
376
377    } else {
378        var index = document.jsuitesComponents.indexOf(component);
379        if (index >= 0) {
380            document.jsuitesComponents.splice(index, 1);
381        }
382    }
383}
384
385/**
386 * Get or set a property from a JSON from a string.
387 */
388jSuites.path = function(str, val) {
389    str = str.split('.');
390    if (str.length) {
391        var o = this;
392        var p = null;
393        while (str.length > 1) {
394            // Get the property
395            p = str.shift();
396            // Check if the property exists
397            if (o.hasOwnProperty(p)) {
398                o = o[p];
399            } else {
400                // Property does not exists
401                if (val === undefined) {
402                    return undefined;
403                } else {
404                    // Create the property
405                    o[p] = {};
406                    // Next property
407                    o = o[p];
408                }
409            }
410        }
411        // Get the property
412        p = str.shift();
413        // Set or get the value
414        if (val !== undefined) {
415            o[p] = val;
416            // Success
417            return true;
418        } else {
419            // Return the value
420            if (o) {
421                return o[p];
422            }
423        }
424    }
425    // Something went wrong
426    return false;
vendor: 49,887 bytes, lines 427-1938
427}
428
429// Update dictionary
430jSuites.setDictionary = function(d) {
431    if (! document.dictionary) {
432        document.dictionary = {}
433    }
434    // Replace the key into the dictionary and append the new ones
435    var k = Object.keys(d);
436    for (var i = 0; i < k.length; i++) {
437        document.dictionary[k[i]] = d[k[i]];
438    }
439
440    // Translations
441    var t = null;
442    for (var i = 0; i < jSuites.calendar.weekdays.length; i++) {
443        t =  jSuites.translate(jSuites.calendar.weekdays[i]);
444        if (jSuites.calendar.weekdays[i]) {
445            jSuites.calendar.weekdays[i] = t;
446            jSuites.calendar.weekdaysShort[i] = t.substr(0,3);
447        }
448    }
449    for (var i = 0; i < jSuites.calendar.months.length; i++) {
450        t = jSuites.translate(jSuites.calendar.months[i]);
451        if (t) {
452            jSuites.calendar.months[i] = t;
453            jSuites.calendar.monthsShort[i] = t.substr(0,3);
454        }
455    }
456}
457
458// Translate
459jSuites.translate = function(t) {
460    if (typeof(document) !== "undefined" && document.dictionary) {
461        return document.dictionary[t] || t;
462     } else {
463        return t;
464     }
465}
466
467jSuites.ajax = (function(options, complete) {
468    if (Array.isArray(options)) {
469        // Create multiple request controller 
470        var multiple = {
471            instance: [],
472            complete: complete,
473        }
474
475        if (options.length > 0) {
476            for (var i = 0; i < options.length; i++) {
477                options[i].multiple = multiple;
478                multiple.instance.push(jSuites.ajax(options[i]));
479            }
480        }
481
482        return multiple;
483    }
484
485    if (! options.data) {
486        options.data = {};
487    }
488
489    if (options.type) {
490        options.method = options.type;
491    }
492
493    // Default method
494    if (! options.method) {
495        options.method = 'GET';
496    }
497
498    // Default type
499    if (! options.dataType) {
500        options.dataType = 'json';
501    }
502
503    if (options.data) {
504        // Parse object to variables format
505        var parseData = function (value, key) {
506            var vars = [];
507            if (value) {
508                var keys = Object.keys(value);
509                if (keys.length) {
510                    for (var i = 0; i < keys.length; i++) {
511                        if (key) {
512                            var k = key + '[' + keys[i] + ']';
513                        } else {
514                            var k = keys[i];
515                        }
516
517                        if (value[k] instanceof FileList) {
518                            vars[k] = value[keys[i]];
519                        } else if (value[keys[i]] === null || value[keys[i]] === undefined) {
520                            vars[k] = '';
521                        } else if (typeof(value[keys[i]]) == 'object') {
522                            var r = parseData(value[keys[i]], k);
523                            var o = Object.keys(r);
524                            for (var j = 0; j < o.length; j++) {
525                                vars[o[j]] = r[o[j]];
526                            }
527                        } else {
528                            vars[k] = value[keys[i]];
529                        }
530                    }
531                }
532            }
533
534            return vars;
535        }
536
537        var d = parseData(options.data);
538        var k = Object.keys(d);
539
540        // Data form
541        if (options.method == 'GET') {
542            if (k.length) {
543                var data = [];
544                for (var i = 0; i < k.length; i++) {
545                    data.push(k[i] + '=' + encodeURIComponent(d[k[i]]));
546                }
547
548                if (options.url.indexOf('?') < 0) {
549                    options.url += '?';
550                }
551                options.url += data.join('&');
552            }
553        } else {
554            var data = new FormData();
555            for (var i = 0; i < k.length; i++) {
556                if (d[k[i]] instanceof FileList) {
557                    if (d[k[i]].length) {
558                        for (var j = 0; j < d[k[i]].length; j++) {
559                            data.append(k[i], d[k[i]][j], d[k[i]][j].name);
560                        }
561                    }
562                } else {
563                    data.append(k[i], d[k[i]]);
564                }
565            }
566        }
567    }
568
569    var httpRequest = new XMLHttpRequest();
570    httpRequest.open(options.method, options.url, true);
571    httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
572
573    // Content type
574    if (options.contentType) {
575        httpRequest.setRequestHeader('Content-Type', options.contentType);
576    }
577
578    // Headers
579    if (options.method == 'POST') {
580        httpRequest.setRequestHeader('Accept', 'application/json');
581    } else {
582        if (options.dataType == 'blob') {
583            httpRequest.responseType = "blob";
584        } else {
585            if (! options.contentType) {
586                if (options.dataType == 'json') {
587                    httpRequest.setRequestHeader('Content-Type', 'text/json');
588                } else if (options.dataType == 'html') {
589                    httpRequest.setRequestHeader('Content-Type', 'text/html');
590                }
591            }
592        }
593    }
594
595    // No cache
596    if (options.cache != true) {
597        httpRequest.setRequestHeader('pragma', 'no-cache');
598        httpRequest.setRequestHeader('cache-control', 'no-cache');
599    }
600
601    // Authentication
602    if (options.withCredentials == true) {
603        httpRequest.withCredentials = true
604    }
605
606    // Before send
607    if (typeof(options.beforeSend) == 'function') {
608        options.beforeSend(httpRequest);
609    }
610
611    // Before send
612    if (typeof(jSuites.ajax.beforeSend) == 'function') {
613        jSuites.ajax.beforeSend(httpRequest);
614    }
615
616    if (document.ajax && typeof(document.ajax.beforeSend) == 'function') {
617        document.ajax.beforeSend(httpRequest);
618    }
619
620    httpRequest.onload = function() {
621        if (httpRequest.status === 200) {
622            if (options.dataType == 'json') {
623                try {
624                    var result = JSON.parse(httpRequest.responseText);
625
626                    if (options.success && typeof(options.success) == 'function') {
627                        options.success(result);
628                    }
629                } catch(err) {
630                    if (options.error && typeof(options.error) == 'function') {
631                        options.error(err, result);
632                    }
633                }
634            } else {
635                if (options.dataType == 'blob') {
636                    var result = httpRequest.response;
637                } else {
638                    var result = httpRequest.responseText;
639                }
640
641                if (options.success && typeof(options.success) == 'function') {
642                    options.success(result);
643                }
644            }
645        } else {
646            if (options.error && typeof(options.error) == 'function') {
647                options.error(httpRequest.responseText, httpRequest.status);
648            }
649        }
650
651        // Global queue
652        if (jSuites.ajax.queue && jSuites.ajax.queue.length > 0) {
653            jSuites.ajax.send(jSuites.ajax.queue.shift());
654        }
655
656        // Global complete method
657        if (jSuites.ajax.requests && jSuites.ajax.requests.length) {
658            // Get index of this request in the container
659            var index = jSuites.ajax.requests.indexOf(httpRequest);
660            // Remove from the ajax requests container
661            jSuites.ajax.requests.splice(index, 1);
662            // Deprected: Last one?
663            if (! jSuites.ajax.requests.length) {
664                // Object event
665                if (options.complete && typeof(options.complete) == 'function') {
666                    options.complete(result);
667                }
668            }
669            // Group requests
670            if (options.group) {
671                if (jSuites.ajax.oncomplete && typeof(jSuites.ajax.oncomplete[options.group]) == 'function') {
672                    if (! jSuites.ajax.pending(options.group)) {
673                        jSuites.ajax.oncomplete[options.group]();
674                        jSuites.ajax.oncomplete[options.group] = null;
675                    }
676                }
677            }
678            // Multiple requests controller
679            if (options.multiple && options.multiple.instance) {
680                // Get index of this request in the container
681                var index = options.multiple.instance.indexOf(httpRequest);
682                // Remove from the ajax requests container
683                options.multiple.instance.splice(index, 1);
684                // If this is the last one call method complete
685                if (! options.multiple.instance.length) {
686                    if (options.multiple.complete && typeof(options.multiple.complete) == 'function') {
687                        options.multiple.complete(result);
688                    }
689                }
690            }
691        }
692    }
693
694    // Keep the options
695    httpRequest.options = options;
696    // Data
697    httpRequest.data = data;
698
699    // Queue
700    if (options.queue == true && jSuites.ajax.requests.length > 0) {
701        jSuites.ajax.queue.push(httpRequest);
702    } else {
703        jSuites.ajax.send(httpRequest)
704    }
705
706    return httpRequest;
707});
708
709jSuites.ajax.send = function(httpRequest) {
710    if (httpRequest.data) {
711        if (Array.isArray(httpRequest.data)) {
712            httpRequest.send(httpRequest.data.join('&'));
713        } else {
714            httpRequest.send(httpRequest.data);
715        }
716    } else {
717        httpRequest.send();
718    }
719
720    jSuites.ajax.requests.push(httpRequest);
721}
722
723jSuites.ajax.exists = function(url, __callback) {
724    var http = new XMLHttpRequest();
725    http.open('HEAD', url, false);
726    http.send();
727    if (http.status) {
728        __callback(http.status);
729    }
730}
731
732jSuites.ajax.pending = function(group) {
733    var n = 0;
734    var o = jSuites.ajax.requests;
735    if (o && o.length) {
736        for (var i = 0; i < o.length; i++) {
737            if (! group || group == o[i].options.group) {
738                n++
739            }
740        }
741    }
742    return n;
743}
744
745jSuites.ajax.oncomplete = {};
746jSuites.ajax.requests = [];
747jSuites.ajax.queue = [];
748
749jSuites.alert = function(message) {
750    if (jSuites.getWindowWidth() < 800 && jSuites.dialog) {
751        jSuites.dialog.open({
752            title:'Alert',
753            message:message,
754        });
755    } else {
756        alert(message);
757    }
758}
759
760jSuites.animation = {};
761
762jSuites.animation.slideLeft = function(element, direction, done) {
763    if (direction == true) {
764        element.classList.add('slide-left-in');
765        setTimeout(function() {
766            element.classList.remove('slide-left-in');
767            if (typeof(done) == 'function') {
768                done();
769            }
770        }, 400);
771    } else {
772        element.classList.add('slide-left-out');
773        setTimeout(function() {
774            element.classList.remove('slide-left-out');
775            if (typeof(done) == 'function') {
776                done();
777            }
778        }, 400);
779    }
780}
781
782jSuites.animation.slideRight = function(element, direction, done) {
783    if (direction == true) {
784        element.classList.add('slide-right-in');
785        setTimeout(function() {
786            element.classList.remove('slide-right-in');
787            if (typeof(done) == 'function') {
788                done();
789            }
790        }, 400);
791    } else {
792        element.classList.add('slide-right-out');
793        setTimeout(function() {
794            element.classList.remove('slide-right-out');
795            if (typeof(done) == 'function') {
796                done();
797            }
798        }, 400);
799    }
800}
801
802jSuites.animation.slideTop = function(element, direction, done) {
803    if (direction == true) {
804        element.classList.add('slide-top-in');
805        setTimeout(function() {
806            element.classList.remove('slide-top-in');
807            if (typeof(done) == 'function') {
808                done();
809            }
810        }, 400);
811    } else {
812        element.classList.add('slide-top-out');
813        setTimeout(function() {
814            element.classList.remove('slide-top-out');
815            if (typeof(done) == 'function') {
816                done();
817            }
818        }, 400);
819    }
820}
821
822jSuites.animation.slideBottom = function(element, direction, done) {
823    if (direction == true) {
824        element.classList.add('slide-bottom-in');
825        setTimeout(function() {
826            element.classList.remove('slide-bottom-in');
827            if (typeof(done) == 'function') {
828                done();
829            }
830        }, 400);
831    } else {
832        element.classList.add('slide-bottom-out');
833        setTimeout(function() {
834            element.classList.remove('slide-bottom-out');
835            if (typeof(done) == 'function') {
836                done();
837            }
838        }, 100);
839    }
840}
841
842jSuites.animation.fadeIn = function(element, done) {
843    element.style.display = '';
844    element.classList.add('fade-in');
845    setTimeout(function() {
846        element.classList.remove('fade-in');
847        if (typeof(done) == 'function') {
848            done();
849        }
850    }, 2000);
851}
852
853jSuites.animation.fadeOut = function(element, done) {
854    element.classList.add('fade-out');
855    setTimeout(function() {
856        element.style.display = 'none';
857        element.classList.remove('fade-out');
858        if (typeof(done) == 'function') {
859            done();
860        }
861    }, 1000);
862}
863
864jSuites.calendar = (function(el, options) {
865    // Already created, update options
866    if (el.calendar) {
867        return el.calendar.setOptions(options, true);
868    }
869
870    // New instance
871    var obj = { type:'calendar' };
872    obj.options = {};
873
874    // Date
875    obj.date = null;
876
877    /**
878     * Update options
879     */
880    obj.setOptions = function(options, reset) {
881        // Default configuration
882        var defaults = {
883            // Render type: [ default | year-month-picker ]
884            type: 'default',
885            // Restrictions
886            validRange: null,
887            // Starting weekday - 0 for sunday, 6 for saturday
888            startingDay: null, 
889            // Date format
890            format: 'DD/MM/YYYY',
891            // Allow keyboard date entry
892            readonly: true,
893            // Today is default
894            today: false,
895            // Show timepicker
896            time: false,
897            // Show the reset button
898            resetButton: true,
899            // Placeholder
900            placeholder: '',
901            // Translations can be done here
902            months: jSuites.calendar.monthsShort,
903            monthsFull: jSuites.calendar.months,
904            weekdays: jSuites.calendar.weekdays,
905            textDone: jSuites.translate('Done'),
906            textReset: jSuites.translate('Reset'),
907            textUpdate: jSuites.translate('Update'),
908            // Value
909            value: null,
910            // Fullscreen (this is automatic set for screensize < 800)
911            fullscreen: false,
912            // Create the calendar closed as default
913            opened: false,
914            // Events
915            onopen: null,
916            onclose: null,
917            onchange: null,
918            onupdate: null,
919            // Internal mode controller
920            mode: null,
921            position: null,
922            // Data type
923            dataType: null,
924            // Controls
925            controls: true,
926        }
927
928        // Loop through our object
929        for (var property in defaults) {
930            if (options && options.hasOwnProperty(property)) {
931                obj.options[property] = options[property];
932            } else {
933                if (typeof(obj.options[property]) == 'undefined' || reset === true) {
934                    obj.options[property] = defaults[property];
935                }
936            }
937        }
938
939        // Reset button
940        if (obj.options.resetButton == false) {
941            calendarReset.style.display = 'none';
942        } else {
943            calendarReset.style.display = '';
944        }
945
946        // Readonly
947        if (obj.options.readonly) {
948            el.setAttribute('readonly', 'readonly');
949        } else {
950            el.removeAttribute('readonly');
951        }
952
953        // Placeholder
954        if (obj.options.placeholder) {
955            el.setAttribute('placeholder', obj.options.placeholder);
956        } else {
957            el.removeAttribute('placeholder');
958        }
959
960        if (jSuites.isNumeric(obj.options.value) && obj.options.value > 0) {
961            obj.options.value = jSuites.calendar.numToDate(obj.options.value);
962            // Data type numeric
963            obj.options.dataType = 'numeric';
964        }
965
966        // Texts
967        calendarReset.innerHTML = obj.options.textReset;
968        calendarConfirm.innerHTML = obj.options.textDone;
969        calendarControlsUpdateButton.innerHTML = obj.options.textUpdate;
970
971        // Define mask
972        el.setAttribute('data-mask', obj.options.format.toLowerCase());
973
974        // Value
975        if (! obj.options.value && obj.options.today) {
976            var value = jSuites.calendar.now();
977        } else {
978            var value = obj.options.value;
979        }
980
981        // Set internal date
982        if (value) {
983            // Force the update
984            obj.options.value = null;
985            // New value
986            obj.setValue(value);
987        }
988
989        return obj;
990    }
991
992    /**
993     * Open the calendar
994     */
995    obj.open = function (value) {
996        if (! calendar.classList.contains('jcalendar-focus')) {
997            if (! calendar.classList.contains('jcalendar-inline')) {
998                // Current
999                jSuites.calendar.current = obj;
1000                // Start tracking
1001                jSuites.tracking(obj, true);
1002                // Create the days
1003                obj.getDays();
1004                // Render months
1005                if (obj.options.type == 'year-month-picker') {
1006                    obj.getMonths();
1007                }
1008                // Get time
1009                if (obj.options.time) {
1010                    calendarSelectHour.value = obj.date[3];
1011                    calendarSelectMin.value = obj.date[4];
1012                }
1013
1014                // Show calendar
1015                calendar.classList.add('jcalendar-focus');
1016
1017                // Get the position of the corner helper
1018                if (jSuites.getWindowWidth() < 800 || obj.options.fullscreen) {
1019                    calendar.classList.add('jcalendar-fullsize');
1020                    // Animation
1021                    jSuites.animation.slideBottom(calendarContent, 1);
1022                } else {
1023                    calendar.classList.remove('jcalendar-fullsize');
1024
1025                    var rect = el.getBoundingClientRect();
1026                    var rectContent = calendarContent.getBoundingClientRect();
1027
1028                    if (obj.options.position) {
1029                        calendarContainer.style.position = 'fixed';
1030                        if (window.innerHeight < rect.bottom + rectContent.height) {
1031                            calendarContainer.style.top = (rect.top - (rectContent.height + 2)) + 'px';
1032                        } else {
1033                            calendarContainer.style.top = (rect.top + rect.height + 2) + 'px';
1034                        }
1035                        calendarContainer.style.left = rect.left + 'px';
1036                    } else {
1037                        if (window.innerHeight < rect.bottom + rectContent.height) {
1038                            var d = -1 * (rect.height + rectContent.height + 2);
1039                            if (d + rect.top < 0) {
1040                                d = -1 * (rect.top + rect.height);
1041                            }
1042                            calendarContainer.style.top = d + 'px';
1043                        } else {
1044                            calendarContainer.style.top = 2 + 'px'; 
1045                        }
1046
1047                        if (window.innerWidth < rect.left + rectContent.width) {
1048                            var d = window.innerWidth - (rect.left + rectContent.width + 20);
1049                            calendarContainer.style.left = d + 'px';
1050                        } else {
1051                            calendarContainer.style.left = '0px'; 
1052                        }
1053                    }
1054                }
1055
1056                // Events
1057                if (typeof(obj.options.onopen) == 'function') {
1058                    obj.options.onopen(el);
1059                }
1060            }
1061        }
1062    }
1063
1064    obj.close = function (ignoreEvents, update) {
1065        if (calendar.classList.contains('jcalendar-focus')) {
1066            if (update !== false) {
1067                var element = calendar.querySelector('.jcalendar-selected');
1068
1069                if (typeof(update) == 'string') {
1070                    var value = update;
1071                } else if (! element || element.classList.contains('jcalendar-disabled')) {
1072                    var value = obj.options.value
1073                } else {
1074                    var value = obj.getValue();
1075                }
1076
1077                obj.setValue(value);
1078            }
1079
1080            // Events
1081            if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
1082                obj.options.onclose(el);
1083            }
1084            // Hide
1085            calendar.classList.remove('jcalendar-focus');
1086            // Stop tracking
1087            jSuites.tracking(obj, false);
1088            // Current
1089            jSuites.calendar.current = null;
1090        }
1091
1092        return obj.options.value;
1093    }
1094
1095    obj.prev = function() {
1096        // Check if the visualization is the days picker or years picker
1097        if (obj.options.mode == 'years') {
1098            obj.date[0] = obj.date[0] - 12;
1099
1100            // Update picker table of days
1101            obj.getYears();
1102        } else if (obj.options.mode == 'months') {
1103            obj.date[0] = parseInt(obj.date[0]) - 1;
1104            // Update picker table of months
1105            obj.getMonths();
1106        } else {
1107            // Go to the previous month
1108            if (obj.date[1] < 2) {
1109                obj.date[0] = obj.date[0] - 1;
1110                obj.date[1] = 12;
1111            } else {
1112                obj.date[1] = obj.date[1] - 1;
1113            }
1114
1115            // Update picker table of days
1116            obj.getDays();
1117        }
1118    }
1119
1120    obj.next = function() {
1121        // Check if the visualization is the days picker or years picker
1122        if (obj.options.mode == 'years') {
1123            obj.date[0] = parseInt(obj.date[0]) + 12;
1124
1125            // Update picker table of days
1126            obj.getYears();
1127        } else if (obj.options.mode == 'months') {
1128            obj.date[0] = parseInt(obj.date[0]) + 1;
1129            // Update picker table of months
1130            obj.getMonths();
1131        } else {
1132            // Go to the previous month
1133            if (obj.date[1] > 11) {
1134                obj.date[0] = parseInt(obj.date[0]) + 1;
1135                obj.date[1] = 1;
1136            } else {
1137                obj.date[1] = parseInt(obj.date[1]) + 1;
1138            }
1139
1140            // Update picker table of days
1141            obj.getDays();
1142        }
1143    }
1144
1145    /**
1146     * Set today
1147     */
1148    obj.setToday = function() {
1149        // Today
1150        var value = new Date().toISOString().substr(0, 10);
1151        // Change value
1152        obj.setValue(value);
1153        // Value
1154        return value;
1155    }
1156
1157    obj.setValue = function(val) {
1158        if (! val) {
1159            val = '' + val;
1160        }
1161        // Values
1162        var newValue = val;
1163        var oldValue = obj.options.value;
1164
1165        if (oldValue != newValue) {
1166            // Set label
1167            if (! newValue) {
1168                obj.date = null;
1169                var val = '';
1170                el.classList.remove('jcalendar_warning');
1171                el.title = '';
1172            } else {
1173                var value = obj.setLabel(newValue, obj.options);
1174                var date = newValue.split(' ');
1175                if (! date[1]) {
1176                    date[1] = '00:00:00';
1177                }
1178                var time = date[1].split(':')
1179                var date = date[0].split('-');
1180                var y = parseInt(date[0]);
1181                var m = parseInt(date[1]);
1182                var d = parseInt(date[2]);
1183                var h = parseInt(time[0]);
1184                var i = parseInt(time[1]);
1185                obj.date = [ y, m, d, h, i, 0 ];
1186                var val = obj.setLabel(newValue, obj.options);
1187
1188                // Current selection day
1189                var current = jSuites.calendar.now(new Date(y, m-1, d), true);
1190
1191                // Available ranges
1192                if (obj.options.validRange) {
1193                    if (! obj.options.validRange[0] || current >= obj.options.validRange[0]) {
1194                        var test1 = true;
1195                    } else {
1196                        var test1 = false;
1197                    }
1198
1199                    if (! obj.options.validRange[1] || current <= obj.options.validRange[1]) {
1200                        var test2 = true;
1201                    } else {
1202                        var test2 = false;
1203                    }
1204
1205                    if (! (test1 && test2)) {
1206                        el.classList.add('jcalendar_warning');
1207                        el.title = jSuites.translate('Date outside the valid range');
1208                    } else {
1209                        el.classList.remove('jcalendar_warning');
1210                        el.title = '';
1211                    }
1212                } else {
1213                    el.classList.remove('jcalendar_warning');
1214                    el.title = '';
1215                }
1216            }
1217
1218            // New value
1219            obj.options.value = newValue;
1220
1221            if (typeof(obj.options.onchange) ==  'function') {
1222                obj.options.onchange(el, newValue, oldValue);
1223            }
1224
1225            // Lemonade JS
1226            if (el.value != val) {
1227                el.value = val;
1228                if (typeof(el.oninput) == 'function') {
1229                    el.oninput({
1230                        type: 'input',
1231                        target: el,
1232                        value: el.value
1233                    });
1234                }
1235            }
1236        }
1237
1238        obj.getDays();
1239        // Render months
1240        if (obj.options.type == 'year-month-picker') {
1241            obj.getMonths();
1242        }
1243    }
1244
1245    obj.getValue = function() {
1246        if (obj.date) {
1247            if (obj.options.time) {
1248                return jSuites.two(obj.date[0]) + '-' + jSuites.two(obj.date[1]) + '-' + jSuites.two(obj.date[2]) + ' ' + jSuites.two(obj.date[3]) + ':' + jSuites.two(obj.date[4]) + ':' + jSuites.two(0);
1249            } else {
1250                return jSuites.two(obj.date[0]) + '-' + jSuites.two(obj.date[1]) + '-' + jSuites.two(obj.date[2]) + ' ' + jSuites.two(0) + ':' + jSuites.two(0) + ':' + jSuites.two(0);
1251            }
1252        } else {
1253            return "";
1254        }
1255    }
1256
1257    /**
1258     *  Calendar
1259     */
1260    obj.update = function(element, v) {
1261        if (element.classList.contains('jcalendar-disabled')) {
1262            // Do nothing
1263        } else {
1264            var elements = calendar.querySelector('.jcalendar-selected');
1265            if (elements) {
1266                elements.classList.remove('jcalendar-selected');
1267            }
1268            element.classList.add('jcalendar-selected');
1269
1270            if (element.classList.contains('jcalendar-set-month')) {
1271                obj.date[1] = v;
1272                obj.date[2] = 1; // first day of the month
1273            } else {
1274                obj.date[2] = element.innerText;
1275            }
1276
1277            if (! obj.options.time) {
1278                obj.close();
1279            } else {
1280                obj.date[3] = calendarSelectHour.value;
1281                obj.date[4] = calendarSelectMin.value;
1282            }
1283        }
1284
1285        // Update
1286        updateActions();
1287    }
1288
1289    /**
1290     * Set to blank
1291     */
1292    obj.reset = function() {
1293        // Close calendar
1294        obj.setValue('');
1295        obj.date = null;
1296        obj.close(false, false);
1297    }
1298
1299    /**
1300     * Get calendar days
1301     */
1302    obj.getDays = function() {
1303        // Mode
1304        obj.options.mode = 'days';
1305
1306        // Setting current values in case of NULLs
1307        var date = new Date();
1308
1309        // Current selection
1310        var year = obj.date && jSuites.isNumeric(obj.date[0]) ? obj.date[0] : parseInt(date.getFullYear());
1311        var month = obj.date && jSuites.isNumeric(obj.date[1]) ? obj.date[1] : parseInt(date.getMonth()) + 1;
1312        var day = obj.date && jSuites.isNumeric(obj.date[2]) ? obj.date[2] : parseInt(date.getDate());
1313        var hour = obj.date && jSuites.isNumeric(obj.date[3]) ? obj.date[3] : parseInt(date.getHours());
1314        var min = obj.date && jSuites.isNumeric(obj.date[4]) ? obj.date[4] : parseInt(date.getMinutes());
1315
1316        // Selection container
1317        obj.date = [ year, month, day, hour, min, 0 ];
1318
1319        // Update title
1320        calendarLabelYear.innerHTML = year;
1321        calendarLabelMonth.innerHTML = obj.options.months[month - 1];
1322
1323        // Current month and Year
1324        var isCurrentMonthAndYear = (date.getMonth() == month - 1) && (date.getFullYear() == year) ? true : false;
1325        var currentDay = date.getDate();
1326
1327        // Number of days in the month
1328        var date = new Date(year, month, 0, 0, 0);
1329        var numberOfDays = date.getDate();
1330
1331        // First day
1332        var date = new Date(year, month-1, 0, 0, 0);
1333        var firstDay = date.getDay() + 1;
1334
1335        // Index value
1336        var index = obj.options.startingDay || 0;
1337
1338        // First of day relative to the starting calendar weekday
1339        firstDay = firstDay - index;
1340
1341        // Reset table
1342        calendarBody.innerHTML = '';
1343
1344        // Weekdays Row
1345        var row = document.createElement('tr');
1346        row.setAttribute('align', 'center');
1347        calendarBody.appendChild(row);
1348
1349        // Create weekdays row
1350        for (var i = 0; i < 7; i++) {
1351            var cell = document.createElement('td');
1352            cell.classList.add('jcalendar-weekday')
1353            cell.innerHTML = obj.options.weekdays[index].substr(0,1);
1354            row.appendChild(cell);
1355            // Next week day
1356            index++;
1357            // Restart index
1358            if (index > 6) {
1359                index = 0;
1360            }
1361        }
1362
1363        // Index of days
1364        var index = 0;
1365        var d = 0;
1366 
1367        // Calendar table
1368        for (var j = 0; j < 6; j++) {
1369            // Reset cells container
1370            var row = document.createElement('tr');
1371            row.setAttribute('align', 'center');
1372            row.style.height = '34px';
1373
1374            // Create cells
1375            for (var i = 0; i < 7; i++) {
1376                // Create cell
1377                var cell = document.createElement('td');
1378                cell.classList.add('jcalendar-set-day');
1379
1380                if (index >= firstDay && index < (firstDay + numberOfDays)) {
1381                    // Day cell
1382                    d++;
1383                    cell.innerHTML = d;
1384
1385                    // Selected
1386                    if (d == day) {
1387                        cell.classList.add('jcalendar-selected');
1388                    }
1389
1390                    // Current selection day is today
1391                    if (isCurrentMonthAndYear && currentDay == d) {
1392                        cell.style.fontWeight = 'bold';
1393                    }
1394
1395                    // Current selection day
1396                    var current = jSuites.calendar.now(new Date(year, month-1, d), true);
1397
1398                    // Available ranges
1399                    if (obj.options.validRange) {
1400                        if (! obj.options.validRange[0] || current >= obj.options.validRange[0]) {
1401                            var test1 = true;
1402                        } else {
1403                            var test1 = false;
1404                        }
1405
1406                        if (! obj.options.validRange[1] || current <= obj.options.validRange[1]) {
1407                            var test2 = true;
1408                        } else {
1409                            var test2 = false;
1410                        }
1411
1412                        if (! (test1 && test2)) {
1413                            cell.classList.add('jcalendar-disabled');
1414                        }
1415                    }
1416                }
1417                // Day cell
1418                row.appendChild(cell);
1419                // Index
1420                index++;
1421            }
1422
1423            // Add cell to the calendar body
1424            calendarBody.appendChild(row);
1425        }
1426
1427        // Show time controls
1428        if (obj.options.time) {
1429            calendarControlsTime.style.display = '';
1430        } else {
1431            calendarControlsTime.style.display = 'none';
1432        }
1433
1434        // Update
1435        updateActions();
1436    }
1437
1438    obj.getMonths = function() {
1439        // Mode
1440        obj.options.mode = 'months';
1441
1442        // Loading month labels
1443        var months = obj.options.months;
1444
1445        // Value
1446        var value = obj.options.value; 
1447
1448        // Current date
1449        var date = new Date();
1450        var currentYear = parseInt(date.getFullYear());
1451        var currentMonth = parseInt(date.getMonth()) + 1;
1452        var selectedYear = obj.date && jSuites.isNumeric(obj.date[0]) ? obj.date[0] : currentYear;
1453        var selectedMonth = obj.date && jSuites.isNumeric(obj.date[1]) ? obj.date[1] : currentMonth;
1454
1455        // Update title
1456        calendarLabelYear.innerHTML = obj.date[0];
1457        calendarLabelMonth.innerHTML = months[selectedMonth-1];
1458
1459        // Table
1460        var table = document.createElement('table');
1461        table.setAttribute('width', '100%');
1462
1463        // Row
1464        var row = null;
1465
1466        // Calendar table
1467        for (var i = 0; i < 12; i++) {
1468            if (! (i % 4)) {
1469                // Reset cells container
1470                var row = document.createElement('tr');
1471                row.setAttribute('align', 'center');
1472                table.appendChild(row);
1473            }
1474
1475            // Create cell
1476            var cell = document.createElement('td');
1477            cell.classList.add('jcalendar-set-month');
1478            cell.setAttribute('data-value', i+1);
1479            cell.innerText = months[i];
1480
1481            if (obj.options.validRange) {
1482                var current = selectedYear + '-' + jSuites.two(i+1);
1483                if (! obj.options.validRange[0] || current >= obj.options.validRange[0].substr(0,7)) {
1484                    var test1 = true;
1485                } else {
1486                    var test1 = false;
1487                }
1488
1489                if (! obj.options.validRange[1] || current <= obj.options.validRange[1].substr(0,7)) {
1490                    var test2 = true;
1491                } else {
1492                    var test2 = false;
1493                }
1494
1495                if (! (test1 && test2)) {
1496                    cell.classList.add('jcalendar-disabled');
1497                }
1498            }
1499
1500            if (i+1 == selectedMonth) {
1501                cell.classList.add('jcalendar-selected');
1502            }
1503
1504            if (currentYear == selectedYear && i+1 == currentMonth) {
1505                cell.style.fontWeight = 'bold';
1506            }
1507
1508            row.appendChild(cell);
1509        }
1510
1511        calendarBody.innerHTML = '<tr><td colspan="7"></td></tr>';
1512        calendarBody.children[0].children[0].appendChild(table);
1513
1514        // Update
1515        updateActions();
1516    }
1517
1518    obj.getYears = function() { 
1519        // Mode
1520        obj.options.mode = 'years';
1521
1522        // Current date
1523        var date = new Date();
1524        var currentYear = date.getFullYear();
1525        var selectedYear = obj.date && jSuites.isNumeric(obj.date[0]) ? obj.date[0] : parseInt(date.getFullYear());
1526
1527        // Array of years
1528        var y = [];
1529        for (var i = 0; i < 25; i++) {
1530            y[i] = parseInt(obj.date[0]) + (i - 12);
1531        }
1532
1533        // Assembling the year tables
1534        var table = document.createElement('table');
1535        table.setAttribute('width', '100%');
1536
1537        for (var i = 0; i < 25; i++) {
1538            if (! (i % 5)) {
1539                // Reset cells container
1540                var row = document.createElement('tr');
1541                row.setAttribute('align', 'center');
1542                table.appendChild(row);
1543            }
1544
1545            // Create cell
1546            var cell = document.createElement('td');
1547            cell.classList.add('jcalendar-set-year');
1548            cell.innerText = y[i];
1549
1550            if (selectedYear == y[i]) {
1551                cell.classList.add('jcalendar-selected');
1552            }
1553
1554            if (currentYear == y[i]) {
1555                cell.style.fontWeight = 'bold';
1556            }
1557
1558            row.appendChild(cell);
1559        }
1560
1561        calendarBody.innerHTML = '<tr><td colspan="7"></td></tr>';
1562        calendarBody.firstChild.firstChild.appendChild(table);
1563
1564        // Update
1565        updateActions();
1566    }
1567
1568    obj.setLabel = function(value, mixed) {
1569        return jSuites.calendar.getDateString(value, mixed);
1570    }
1571
1572    obj.fromFormatted = function (value, format) {
1573        return jSuites.calendar.extractDateFromString(value, format);
1574    }
1575
1576    var mouseUpControls = function(e) {
1577        var element = jSuites.findElement(e.target, 'jcalendar-container');
1578        if (element) {
1579            var action = e.target.className;
1580
1581            // Object id
1582            if (action == 'jcalendar-prev') {
1583                obj.prev();
1584            } else if (action == 'jcalendar-next') {
1585                obj.next();
1586            } else if (action == 'jcalendar-month') {
1587                obj.getMonths();
1588            } else if (action == 'jcalendar-year') {
1589                obj.getYears();
1590            } else if (action == 'jcalendar-set-year') {
1591                obj.date[0] = e.target.innerText;
1592                if (obj.options.type == 'year-month-picker') {
1593                    obj.getMonths();
1594                } else {
1595                    obj.getDays();
1596                }
1597            } else if (e.target.classList.contains('jcalendar-set-month')) {
1598                var month = parseInt(e.target.getAttribute('data-value'));
1599                if (obj.options.type == 'year-month-picker') {
1600                    obj.update(e.target, month);
1601                } else {
1602                    obj.date[1] = month;
1603                    obj.getDays();
1604                }
1605            } else if (action == 'jcalendar-confirm' || action == 'jcalendar-update' || action == 'jcalendar-close') {
1606                obj.close();
1607            } else if (action == 'jcalendar-backdrop') {
1608                obj.close(false, false);
1609            } else if (action == 'jcalendar-reset') {
1610                obj.reset();
1611            } else if (e.target.classList.contains('jcalendar-set-day') && e.target.innerText) {
1612                obj.update(e.target);
1613            }
1614        } else {
1615            obj.close();
1616        }
1617    }
1618
1619    var keyUpControls = function(e) {
1620        if (e.target.value && e.target.value.length > 3) {
1621            var test = jSuites.calendar.extractDateFromString(e.target.value, obj.options.format);
1622            if (test) {
1623                obj.setValue(test);
1624            }
1625        }
1626    }
1627
1628    // Update actions button
1629    var updateActions = function() {
1630        var currentDay = calendar.querySelector('.jcalendar-selected');
1631
1632        if (currentDay && currentDay.classList.contains('jcalendar-disabled')) {
1633            calendarControlsUpdateButton.setAttribute('disabled', 'disabled');
1634            calendarSelectHour.setAttribute('disabled', 'disabled');
1635            calendarSelectMin.setAttribute('disabled', 'disabled');
1636        } else {
1637            calendarControlsUpdateButton.removeAttribute('disabled');
1638            calendarSelectHour.removeAttribute('disabled');
1639            calendarSelectMin.removeAttribute('disabled');
1640        }
1641
1642        // Event
1643        if (typeof(obj.options.onupdate) == 'function') {
1644            obj.options.onupdate(el, obj.getValue());
1645        }
1646    }
1647
1648    var calendar = null;
1649    var calendarReset = null;
1650    var calendarConfirm = null;
1651    var calendarContainer = null;
1652    var calendarContent = null;
1653    var calendarLabelYear = null;
1654    var calendarLabelMonth = null;
1655    var calendarTable = null;
1656    var calendarBody = null;
1657
1658    var calendarControls = null;
1659    var calendarControlsTime = null;
1660    var calendarControlsUpdate = null;
1661    var calendarControlsUpdateButton = null;
1662    var calendarSelectHour = null;
1663    var calendarSelectMin = null;
1664
1665    var init = function() {
1666        // Get value from initial element if that is an input
1667        if (el.tagName == 'INPUT' && el.value) {
1668            options.value = el.value;
1669        }
1670
1671        // Calendar DOM elements
1672        calendarReset = document.createElement('div');
1673        calendarReset.className = 'jcalendar-reset';
1674
1675        calendarConfirm = document.createElement('div');
1676        calendarConfirm.className = 'jcalendar-confirm';
1677
1678        calendarControls = document.createElement('div');
1679        calendarControls.className = 'jcalendar-controls'
1680        calendarControls.style.borderBottom = '1px solid #ddd';
1681        calendarControls.appendChild(calendarReset);
1682        calendarControls.appendChild(calendarConfirm);
1683
1684        calendarContainer = document.createElement('div');
1685        calendarContainer.className = 'jcalendar-container';
1686        calendarContent = document.createElement('div');
1687        calendarContent.className = 'jcalendar-content';
1688        calendarContainer.appendChild(calendarContent);
1689
1690        // Main element
1691        if (el.tagName == 'DIV') {
1692            calendar = el;
1693            calendar.classList.add('jcalendar-inline');
1694        } else {
1695            // Add controls to the screen
1696            calendarContent.appendChild(calendarControls);
1697
1698            calendar = document.createElement('div');
1699            calendar.className = 'jcalendar';
1700        }
1701        calendar.classList.add('jcalendar-container');
1702        calendar.appendChild(calendarContainer);
1703
1704        // Table container
1705        var calendarTableContainer = document.createElement('div');
1706        calendarTableContainer.className = 'jcalendar-table';
1707        calendarContent.appendChild(calendarTableContainer);
1708
1709        // Previous button
1710        var calendarHeaderPrev = document.createElement('td');
1711        calendarHeaderPrev.setAttribute('colspan', '2');
1712        calendarHeaderPrev.className = 'jcalendar-prev';
1713
1714        // Header with year and month
1715        calendarLabelYear = document.createElement('span');
1716        calendarLabelYear.className = 'jcalendar-year';
1717        calendarLabelMonth = document.createElement('span');
1718        calendarLabelMonth.className = 'jcalendar-month';
1719
1720        var calendarHeaderTitle = document.createElement('td');
1721        calendarHeaderTitle.className = 'jcalendar-header';
1722        calendarHeaderTitle.setAttribute('colspan', '3');
1723        calendarHeaderTitle.appendChild(calendarLabelMonth);
1724        calendarHeaderTitle.appendChild(calendarLabelYear);
1725
1726        var calendarHeaderNext = document.createElement('td');
1727        calendarHeaderNext.setAttribute('colspan', '2');
1728        calendarHeaderNext.className = 'jcalendar-next';
1729
1730        var calendarHeader = document.createElement('thead');
1731        var calendarHeaderRow = document.createElement('tr');
1732        calendarHeaderRow.appendChild(calendarHeaderPrev);
1733        calendarHeaderRow.appendChild(calendarHeaderTitle);
1734        calendarHeaderRow.appendChild(calendarHeaderNext);
1735        calendarHeader.appendChild(calendarHeaderRow);
1736
1737        calendarTable = document.createElement('table');
1738        calendarBody = document.createElement('tbody');
1739        calendarTable.setAttribute('cellpadding', '0');
1740        calendarTable.setAttribute('cellspacing', '0');
1741        calendarTable.appendChild(calendarHeader);
1742        calendarTable.appendChild(calendarBody);
1743        calendarTableContainer.appendChild(calendarTable);
1744
1745        calendarSelectHour = document.createElement('select');
1746        calendarSelectHour.className = 'jcalendar-select';
1747        calendarSelectHour.onchange = function() {
1748            obj.date[3] = this.value; 
1749
1750            // Event
1751            if (typeof(obj.options.onupdate) == 'function') {
1752                obj.options.onupdate(el, obj.getValue());
1753            }
1754        }
1755
1756        for (var i = 0; i < 24; i++) {
1757            var element = document.createElement('option');
1758            element.value = i;
1759            element.innerHTML = jSuites.two(i);
1760            calendarSelectHour.appendChild(element);
1761        }
1762
1763        calendarSelectMin = document.createElement('select');
1764        calendarSelectMin.className = 'jcalendar-select';
1765        calendarSelectMin.onchange = function() {
1766            obj.date[4] = this.value;
1767
1768            // Event
1769            if (typeof(obj.options.onupdate) == 'function') {
1770                obj.options.onupdate(el, obj.getValue());
1771            }
1772        }
1773
1774        for (var i = 0; i < 60; i++) {
1775            var element = document.createElement('option');
1776            element.value = i;
1777            element.innerHTML = jSuites.two(i);
1778            calendarSelectMin.appendChild(element);
1779        }
1780
1781        // Footer controls
1782        var calendarControlsFooter = document.createElement('div');
1783        calendarControlsFooter.className = 'jcalendar-controls';
1784
1785        calendarControlsTime = document.createElement('div');
1786        calendarControlsTime.className = 'jcalendar-time';
1787        calendarControlsTime.style.maxWidth = '140px';
1788        calendarControlsTime.appendChild(calendarSelectHour);
1789        calendarControlsTime.appendChild(calendarSelectMin);
1790
1791        calendarControlsUpdateButton = document.createElement('button');
1792        calendarControlsUpdateButton.setAttribute('type', 'button');
1793        calendarControlsUpdateButton.className = 'jcalendar-update';
1794
1795        calendarControlsUpdate = document.createElement('div');
1796        calendarControlsUpdate.style.flexGrow = '10';
1797        calendarControlsUpdate.appendChild(calendarControlsUpdateButton);
1798        calendarControlsFooter.appendChild(calendarControlsTime);
1799
1800        // Only show the update button for input elements
1801        if (el.tagName == 'INPUT') {
1802            calendarControlsFooter.appendChild(calendarControlsUpdate);
1803        }
1804
1805        calendarContent.appendChild(calendarControlsFooter);
1806
1807        var calendarBackdrop = document.createElement('div');
1808        calendarBackdrop.className = 'jcalendar-backdrop';
1809        calendar.appendChild(calendarBackdrop);
1810
1811        // Handle events
1812        el.addEventListener("keyup", keyUpControls);
1813
1814        // Add global events
1815        calendar.addEventListener("swipeleft", function(e) {
1816            jSuites.animation.slideLeft(calendarTable, 0, function() {
1817                obj.next();
1818                jSuites.animation.slideRight(calendarTable, 1);
1819            });
1820            e.preventDefault();
1821            e.stopPropagation();
1822        });
1823
1824        calendar.addEventListener("swiperight", function(e) {
1825            jSuites.animation.slideRight(calendarTable, 0, function() {
1826                obj.prev();
1827                jSuites.animation.slideLeft(calendarTable, 1);
1828            });
1829            e.preventDefault();
1830            e.stopPropagation();
1831        });
1832
1833        if ('ontouchend' in document.documentElement === true) {
1834            calendar.addEventListener("touchend", mouseUpControls);
1835            el.addEventListener("touchend", obj.open);
1836        } else {
1837            calendar.addEventListener("mouseup", mouseUpControls);
1838            el.addEventListener("mouseup", obj.open);
1839        }
1840
1841        // Global controls
1842        if (! jSuites.calendar.hasEvents) {
1843            // Execute only one time
1844            jSuites.calendar.hasEvents = true;
1845            // Enter and Esc
1846            document.addEventListener("keydown", jSuites.calendar.keydown);
1847        }
1848
1849        // Set configuration
1850        obj.setOptions(options);
1851
1852        // Append element to the DOM
1853        if (el.tagName == 'INPUT') {
1854            el.parentNode.insertBefore(calendar, el.nextSibling);
1855            // Add properties
1856            el.setAttribute('autocomplete', 'off');
1857            // Element
1858            el.classList.add('jcalendar-input');
1859            // Value
1860            el.value = obj.setLabel(obj.getValue(), obj.options);
1861        } else {
1862            // Get days
1863            obj.getDays();
1864            // Hour
1865            if (obj.options.time) {
1866                calendarSelectHour.value = obj.date[3];
1867                calendarSelectMin.value = obj.date[4];
1868            }
1869        }
1870
1871        // Default opened
1872        if (obj.options.opened == true) {
1873            obj.open();
1874        }
1875
1876        // Controls
1877        if (obj.options.controls == false) {
1878            calendarContainer.classList.add('jcalendar-hide-controls');
1879        }
1880
1881        // Change method
1882        el.change = obj.setValue;
1883
1884        // Global generic value handler
1885        el.val = function(val) {
1886            if (val === undefined) {
1887                return obj.getValue();
1888            } else {
1889                obj.setValue(val);
1890            }
1891        }
1892
1893        // Keep object available from the node
1894        el.calendar = calendar.calendar = obj;
1895    }
1896
1897    init();
1898
1899    return obj;
1900});
1901
1902jSuites.calendar.keydown = function(e) {
1903    var calendar = null;
1904    if (calendar = jSuites.calendar.current) { 
1905        if (e.which == 13) {
1906            // ENTER
1907            calendar.close(false, true);
1908        } else if (e.which == 27) {
1909            // ESC
1910            calendar.close(false, false);
1911        }
1912    }
1913}
1914
1915jSuites.calendar.prettify = function(d, texts) {
1916    if (! texts) {
1917        var texts = {
1918            justNow: 'Just now',
1919            xMinutesAgo: '{0}m ago',
1920            xHoursAgo: '{0}h ago',
1921            xDaysAgo: '{0}d ago',
1922            xWeeksAgo: '{0}w ago',
1923            xMonthsAgo: '{0} mon ago',
1924            xYearsAgo: '{0}y ago',
1925        }
1926    }
1927
1928    var d1 = new Date();
1929    var d2 = new Date(d);
1930    var total = parseInt((d1 - d2) / 1000 / 60);
1931
1932    String.prototype.format = function(o) {
1933        return this.replace('{0}', o);
1934    }
1935
1936    if (total == 0) {
1937        var text = texts.justNow;
1938    }
1938 else if (total < 90) {
1939        var text = texts.xMinutesAgo.format(total);
1940    } else if (total < 1440) { // One day
1941        var text = texts.xHoursAgo.format(Math.round(total/60));
1942    } else if (total < 20160) { // 14 days
1943        var text = texts.xDaysAgo.format(Math.round(total / 1440));
1944    } else if (total < 43200) { // 30 days
1945        var text = texts.xWeeksAgo.format(Math.round(total / 10080));
1946    } else if (total < 1036800) { // 24 months
1947        var text = texts.xMonthsAgo.format(Math.round(total / 43200));
1948    } else { // 24 months+
1949        var text = texts.xYearsAgo.format(Math.round(total / 525600));
1950    }
1951
1952    return text;
1953}
1954
1955jSuites.calendar.prettifyAll = function() {
1956    var elements = document.querySelectorAll('.prettydate');
1957    for (var i = 0; i < elements.length; i++) {
1958        if (elements[i].getAttribute('data-date')) {
1959            elements[i].innerHTML = jSuites.calendar.prettify(elements[i].getAttribute('data-date'));
1960        } else {
1961            if (elements[i].innerHTML) {
1962                elements[i].setAttribute('title', elements[i].innerHTML);
1963                elements[i].setAttribute('data-date', elements[i].innerHTML);
1964                elements[i].innerHTML = jSuites.calendar.prettify(elements[i].innerHTML);
1965            }
1966        }
1967    }
1968}
1969
1970jSuites.calendar.now = function(date, dateOnly) {
1971    if (Array.isArray(date)) {
1972        var y = date[0];
1973        var m = date[1];
1974        var d = date[2];
1975        var h = date[3];
1976        var i = date[4];
1977        var s = date[5];
1978    } else {
1979        if (! date) {
1980            var date = new Date();
1981        }
1982        var y = date.getFullYear();
1983        var m = date.getMonth() + 1;
1984        var d = date.getDate();
1985        var h = date.getHours();
1986        var i = date.getMinutes();
1987        var s = date.getSeconds();
1988    }
1989
1990    if (dateOnly == true) {
1991        return jSuites.two(y) + '-' + jSuites.two(m) + '-' + jSuites.two(d);
1992    } else {
1993        return jSuites.two(y) + '-' + jSuites.two(m) + '-' + jSuites.two(d) + ' ' + jSuites.two(h) + ':' + jSuites.two(i) + ':' + jSuites.two(s);
1994    }
1995}
1996
1997jSuites.calendar.toArray = function(value) {
1998    var date = value.split(((value.indexOf('T') !== -1) ? 'T' : ' '));
1999    var time = date[1];
2000    var date = date[0].split('-');
2001    var y = parseInt(date[0]);
2002    var m = parseInt(date[1]);
2003    var d = parseInt(date[2]);
2004
2005    if (time) {
2006        var time = time.split(':');
2007        var h = parseInt(time[0]);
2008        var i = parseInt(time[1]);
2009    } else {
2010        var h = 0;
2011        var i = 0;
2012    }
2013    return [ y, m, d, h, i, 0 ];
2014}
2015
2016// Helper to extract date from a string
2017jSuites.calendar.extractDateFromString = function(date, format) {
2018    var o = jSuites.mask(date, { mask: format }, true);
2019
2020    // Check if in format Excel (Need difference with format date or type detected is numeric)
2021    if (date > 0 && Number(date) == date && (o.values.join("") !== o.value || o.type == "numeric")) {
2022        var d = new Date(Math.round((date - 25569)*86400*1000));
2023        return d.getFullYear() + "-" + jSuites.two(d.getMonth()) + "-" + jSuites.two(d.getDate()) + ' 00:00:00';
2024    }
2025
2026    var complete = false;
2027
2028    if (o.values.length === o.tokens.length && o.values[o.values.length-1].length >= o.tokens[o.tokens.length-1].length) {
2029        complete = true;
2030    }
2031
2032    if (o.date[0] && o.date[1] && (o.date[2] || complete)) {
2033        if (! o.date[2]) {
2034            o.date[2] = 1;
2035        }
2036
2037        return o.date[0] + '-' + jSuites.two(o.date[1]) + '-' + jSuites.two(o.date[2]) + ' ' + jSuites.two(o.date[3]) + ':' + jSuites.two(o.date[4])+ ':' + jSuites.two(o.date[5]);
2038    }
2039
2040    return '';
2041}
2042
2043var excelInitialTime = Date.UTC(1900, 0, 0);
2044var excelLeapYearBug = Date.UTC(1900, 1, 29);
2045var millisecondsPerDay = 86400000;
2046
2047/**
2048 * Date to number
2049 */
2050jSuites.calendar.dateToNum = function(jsDate) {
2051    if (typeof(jsDate) === 'string') {
2052        jsDate = new Date(jsDate + '  GMT+0');
2053    }
2054    var jsDateInMilliseconds = jsDate.getTime();
2055
2056    if (jsDateInMilliseconds >= excelLeapYearBug) {
2057        jsDateInMilliseconds += millisecondsPerDay;
2058    }
2059
2060    jsDateInMilliseconds -= excelInitialTime;
2061
2062    return jsDateInMilliseconds / millisecondsPerDay;
2063}
2064
2065/**
2066 * Number to date
2067 */
2068// !IMPORTANT!
2069// Excel incorrectly considers 1900 to be a leap year
2070jSuites.calendar.numToDate = function(excelSerialNumber) {
2071    var jsDateInMilliseconds = excelInitialTime + excelSerialNumber * millisecondsPerDay;
2072
2073    if (jsDateInMilliseconds >= excelLeapYearBug) {
2074        jsDateInMilliseconds -= millisecondsPerDay;
2075    }
2076
2077    const d = new Date(jsDateInMilliseconds);
2078
2079    var date = [
2080        d.getUTCFullYear(),
2081        d.getUTCMonth()+1,
2082        d.getUTCDate(),
2083        d.getUTCHours(),
2084        d.getUTCMinutes(),
2085        d.getUTCSeconds(),
2086    ];
2087
2088    return jSuites.calendar.now(date);
2089}
2090
2091// Helper to convert date into string
2092jSuites.calendar.getDateString = function(value, options) {
2093    if (! options) {
2094        var options = {};
2095    }
2096
2097    // Labels
2098    if (options && typeof(options) == 'object') {
2099        var format = options.format;
2100    } else {
2101        var format = options;
2102    }
2103
2104    if (! format) {
2105        format = 'YYYY-MM-DD';
2106    }
2107
2108    // Convert to number of hours
2109    if (format.indexOf('[h]') >= 0) {
2110        var result = 0;
2111        if (value && jSuites.isNumeric(value)) {
2112            result = parseFloat(24 * Number(value));
2113            if (format.indexOf('mm') >= 0) {
2114                var h = (''+result).split('.');
2115                if (h[1]) {
2116                    var d = 60 * parseFloat('0.' + h[1])
2117                    d = parseFloat(d.toFixed(2));
2118                } else {
2119                    var d = 0;
2120                }
2121                result = parseInt(h[0]) + ':' + jSuites.two(d);
2122            }
2123        }
2124        return result;
2125    }
2126
2127    // Date instance
2128    if (value instanceof Date) {
2129        value = jSuites.calendar.now(value);
2130    } else if (value && jSuites.isNumeric(value)) {
2131        value = jSuites.calendar.numToDate(value);
2132    }
2133
2134    // Tokens
2135    var tokens = [ 'DAY', 'WD', 'DDDD', 'DDD', 'DD', 'D', 'Q', 'HH24', 'HH12', 'HH', 'H', 'AM/PM', 'MI', 'SS', 'MS', 'YYYY', 'YYY', 'YY', 'Y', 'MONTH', 'MON', 'MMMMM', 'MMMM', 'MMM', 'MM', 'M', '.' ];
2136
2137    // Expression to extract all tokens from the string
2138    var e = new RegExp(tokens.join('|'), 'gi');
2139    // Extract
2140    var t = format.match(e);
2141
2142    // Compatibility with excel
2143    for (var i = 0; i < t.length; i++) {
2144        if (t[i].toUpperCase() == 'MM') {
2145            // Not a month, correct to minutes
2146            if (t[i-1] && t[i-1].toUpperCase().indexOf('H') >= 0) {
2147                t[i] = 'mi';
2148            } else if (t[i-2] && t[i-2].toUpperCase().indexOf('H') >= 0) {
2149                t[i] = 'mi';
2150            } else if (t[i+1] && t[i+1].toUpperCase().indexOf('S') >= 0) {
2151                t[i] = 'mi';
2152            } else if (t[i+2] && t[i+2].toUpperCase().indexOf('S') >= 0) {
2153                t[i] = 'mi';
2154            }
2155        }
2156    }
2157
2158    // Object
2159    var o = {
2160        tokens: t
2161    }
2162
2163    // Value
2164    if (value) {
2165        var d = ''+value;
2166        var splitStr = (d.indexOf('T') !== -1) ? 'T' : ' ';
2167        d = d.split(splitStr);
2168 
2169        var h = 0;
2170        var m = 0;
2171        var s = 0;
2172
2173        if (d[1]) {
2174            h = d[1].split(':');
2175            m = h[1] ? h[1] : 0;
2176            s = h[2] ? h[2] : 0;
2177            h = h[0] ? h[0] : 0;
2178        }
2179
2180        d = d[0].split('-');
2181
2182        if (d[0] && d[1] && d[2] && d[0] > 0 && d[1] > 0 && d[1] < 13 && d[2] > 0 && d[2] < 32) {
2183
2184            // Data
2185            o.data = [ d[0], d[1], d[2], h, m, s ];
2186
2187            // Value
2188            o.value = [];
2189
2190            // Calendar instance
2191            var calendar = new Date(o.data[0], o.data[1]-1, o.data[2], o.data[3], o.data[4], o.data[5]);
2192
2193            // Get method
2194            var get = function(i) {
2195                // Token
2196                var t = this.tokens[i];
2197                // Case token
2198                var s = t.toUpperCase();
2199                var v = null;
2200
2201                if (s === 'YYYY') {
2202                    v = this.data[0];
2203                } else if (s === 'YYY') {
2204                    v = this.data[0].substring(1,4);
2205                } else if (s === 'YY') {
2206                    v = this.data[0].substring(2,4);
2207                } else if (s === 'Y') {
2208                    v = this.data[0].substring(3,4);
2209                } else if (t === 'MON') {
2210                    v = jSuites.calendar.months[calendar.getMonth()].substr(0,3).toUpperCase();
2211                } else if (t === 'mon') {
2212                    v = jSuites.calendar.months[calendar.getMonth()].substr(0,3).toLowerCase();
2213                } else if (t === 'MONTH') {
2214                    v = jSuites.calendar.months[calendar.getMonth()].toUpperCase();
2215                } else if (t === 'month') {
2216                    v = jSuites.calendar.months[calendar.getMonth()].toLowerCase();
2217                } else if (s === 'MMMMM') {
2218                    v = jSuites.calendar.months[calendar.getMonth()].substr(0, 1);
2219                } else if (s === 'MMMM' || t === 'Month') {
2220                    v = jSuites.calendar.months[calendar.getMonth()];
2221                } else if (s === 'MMM' || t == 'Mon') {
2222                    v = jSuites.calendar.months[calendar.getMonth()].substr(0,3);
2223                } else if (s === 'MM') {
2224                    v = jSuites.two(this.data[1]);
2225                } else if (s === 'M') {
2226                    v = calendar.getMonth()+1;
2227                } else if (t === 'DAY') {
2228                    v = jSuites.calendar.weekdays[calendar.getDay()].toUpperCase();
2229                } else if (t === 'day') {
2230                    v = jSuites.calendar.weekdays[calendar.getDay()].toLowerCase();
2231                } else if (s === 'DDDD' || t == 'Day') {
2232                    v = jSuites.calendar.weekdays[calendar.getDay()];
2233                } else if (s === 'DDD') {
2234                    v = jSuites.calendar.weekdays[calendar.getDay()].substr(0,3);
2235                } else if (s === 'DD') {
2236                    v = jSuites.two(this.data[2]);
2237                } else if (s === 'D') {
2238                    v = this.data[2];
2239                } else if (s === 'Q') {
2240                    v = Math.floor((calendar.getMonth() + 3) / 3);
2241                } else if (s === 'HH24' || s === 'HH') {
2242                    v = jSuites.two(this.data[3]);
2243                } else if (s === 'HH12') {
2244                    if (this.data[3] > 12) {
2245                        v = jSuites.two(this.data[3] - 12);
2246                    } else {
2247                        v = jSuites.two(this.data[3]);
2248                    }
2249                } else if (s === 'H') {
2250                    v = this.data[3];
2251                } else if (s === 'MI') {
2252                    v = jSuites.two(this.data[4]);
2253                } else if (s === 'SS') {
2254                    v = jSuites.two(this.data[5]);
2255                } else if (s === 'MS') {
2256                    v = calendar.getMilliseconds();
2257                } else if (s === 'AM/PM') {
2258                    if (this.data[3] >= 12) {
2259                        v = 'PM';
2260                    } else {
2261                        v = 'AM';
2262                    }
2263                } else if (s === 'WD') {
2264                    v = jSuites.calendar.weekdays[calendar.getDay()];
2265                }
2266
2267                if (v === null) {
2268                    this.value[i] = this.tokens[i];
2269                } else {
2270                    this.value[i] = v;
2271                }
2272            }
2273
2274            for (var i = 0; i < o.tokens.length; i++) {
2275                get.call(o, i);
2276            }
2277            // Put pieces together
2278            value = o.value.join('');
2279        } else {
2280            value = '';
2281        }
2282    }
2283
2284    return value;
2285}
2286
2287// Jsuites calendar labels
2288jSuites.calendar.weekdays = [ 'Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday' ];
2289jSuites.calendar.months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
2290jSuites.calendar.weekdaysShort = [ 'Sun','Mon','Tue','Wed','Thu','Fri','Sat' ];
2291jSuites.calendar.monthsShort = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
2292
2293
2294jSuites.color = (function(el, options) {
2295    // Already created, update options
2296    if (el.color) {
2297        return el.color.setOptions(options, true);
2298    }
2299
2300    // New instance
2301    var obj = { type: 'color' };
2302    obj.options = {};
2303
2304    var container = null;
2305    var backdrop = null;
2306    var content = null;
2307    var resetButton = null;
2308    var closeButton = null;
2309    var tabs = null;
2310    var jsuitesTabs = null;
2311
2312    /**
2313     * Update options
2314     */
2315    obj.setOptions = function(options, reset) {
2316        /**
2317         * @typedef {Object} defaults
2318         * @property {(string|Array)} value - Initial value of the compontent
2319         * @property {string} placeholder - The default instruction text on the element
2320         * @property {requestCallback} onchange - Method to be execute after any changes on the element
2321         * @property {requestCallback} onclose - Method to be execute when the element is closed
2322         * @property {string} doneLabel - Label for button done
2323         * @property {string} resetLabel - Label for button reset
2324         * @property {string} resetValue - Value for button reset
2325         * @property {Bool} showResetButton - Active or note for button reset - default false
2326         */
2327        var defaults = {
2328            placeholder: '',
2329            value: null,
2330            onopen: null,
2331            onclose: null,
2332            onchange: null,
2333            closeOnChange: true,
2334            palette: null,
2335            position: null,
2336            doneLabel: 'Done',
vendor: 98,231 bytes, lines 2337-5518
2337            resetLabel: 'Reset',
2338            fullscreen: false,
2339            opened: false,
2340        }
2341
2342        if (! options) {
2343            options = {};
2344        }
2345
2346        if (options && ! options.palette) {
2347            // Default pallete
2348            options.palette = jSuites.palette();
2349        }
2350
2351        // Loop through our object
2352        for (var property in defaults) {
2353            if (options && options.hasOwnProperty(property)) {
2354                obj.options[property] = options[property];
2355            } else {
2356                if (typeof(obj.options[property]) == 'undefined' || reset === true) {
2357                    obj.options[property] = defaults[property];
2358                }
2359            }
2360        }
2361
2362        // Update the text of the controls, if they have already been created
2363        if (resetButton) {
2364            resetButton.innerHTML = obj.options.resetLabel;
2365        }
2366        if (closeButton) {
2367            closeButton.innerHTML = obj.options.doneLabel;
2368        }
2369
2370        // Update the pallete
2371        if (obj.options.palette && jsuitesTabs) {
2372            jsuitesTabs.updateContent(0, table());
2373        }
2374
2375        // Value
2376        if (typeof obj.options.value === 'string') {
2377            el.value = obj.options.value;
2378            if (el.tagName === 'INPUT') {
2379                el.style.color = el.value;
2380                el.style.backgroundColor = el.value;
2381            }
2382        }
2383
2384        // Placeholder
2385        if (obj.options.placeholder) {
2386            el.setAttribute('placeholder', obj.options.placeholder);
2387        } else {
2388            if (el.getAttribute('placeholder')) {
2389                el.removeAttribute('placeholder');
2390            }
2391        }
2392
2393        return obj;
2394    }
2395
2396    obj.select = function(color) {
2397        // Remove current selected mark
2398        var selected = container.querySelector('.jcolor-selected');
2399        if (selected) {
2400            selected.classList.remove('jcolor-selected');
2401        }
2402
2403        // Mark cell as selected
2404        if (obj.values[color]) {
2405            obj.values[color].classList.add('jcolor-selected');
2406        }
2407
2408        obj.options.value = color;
2409    }
2410
2411    /**
2412     * Open color pallete
2413     */
2414    obj.open = function() {
2415        if (! container.classList.contains('jcolor-focus')) {
2416            // Start tracking
2417            jSuites.tracking(obj, true);
2418
2419            // Show color picker
2420            container.classList.add('jcolor-focus');
2421
2422            // Select current color
2423            if (obj.options.value) {
2424                obj.select(obj.options.value);
2425            }
2426
2427            // Reset margin
2428            content.style.marginTop = '';
2429            content.style.marginLeft = '';
2430
2431            var rectContent = content.getBoundingClientRect();
2432            var availableWidth = jSuites.getWindowWidth();
2433            var availableHeight = jSuites.getWindowHeight();
2434
2435            if (availableWidth < 800 || obj.options.fullscreen == true) {
2436                content.classList.add('jcolor-fullscreen');
2437                jSuites.animation.slideBottom(content, 1);
2438                backdrop.style.display = 'block';
2439            } else {
2440                if (content.classList.contains('jcolor-fullscreen')) {
2441                    content.classList.remove('jcolor-fullscreen');
2442                    backdrop.style.display = '';
2443                }
2444
2445                if (obj.options.position) {
2446                    content.style.position = 'fixed';
2447                } else {
2448                    content.style.position = '';
2449                }
2450
2451                if (rectContent.left + rectContent.width > availableWidth) {
2452                    content.style.marginLeft = -1 * (rectContent.left + rectContent.width - (availableWidth - 20)) + 'px';
2453                }
2454                if (rectContent.top + rectContent.height > availableHeight) {
2455                    content.style.marginTop = -1 * (rectContent.top + rectContent.height - (availableHeight - 20)) + 'px';
2456                }
2457            }
2458
2459
2460            if (typeof(obj.options.onopen) == 'function') {
2461                obj.options.onopen(el);
2462            }
2463
2464            jsuitesTabs.setBorder(jsuitesTabs.getActive());
2465
2466            // Update sliders
2467            if (obj.options.value) {
2468                var rgb = HexToRgb(obj.options.value);
2469
2470                rgbInputs.forEach(function(rgbInput, index) {
2471                    rgbInput.value = rgb[index];
2472                    rgbInput.dispatchEvent(new Event('input'));
2473                });
2474            }
2475        }
2476    }
2477
2478    /**
2479     * Close color pallete
2480     */
2481    obj.close = function(ignoreEvents) {
2482        if (container.classList.contains('jcolor-focus')) {
2483            // Remove focus
2484            container.classList.remove('jcolor-focus');
2485            // Make sure backdrop is hidden
2486            backdrop.style.display = '';
2487            // Call related events
2488            if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
2489                obj.options.onclose(el);
2490            }
2491            // Stop  the object
2492            jSuites.tracking(obj, false);
2493        }
2494
2495        return obj.options.value;
2496    }
2497
2498    /**
2499     * Set value
2500     */
2501    obj.setValue = function(color) {
2502        if (! color) {
2503            color = '';
2504        }
2505
2506        if (color != obj.options.value) {
2507            obj.options.value = color;
2508            slidersResult = color;
2509
2510            // Remove current selecded mark
2511            obj.select(color);
2512
2513            // Onchange
2514            if (typeof(obj.options.onchange) == 'function') {
2515                obj.options.onchange(el, color);
2516            }
2517
2518            // Changes
2519            if (el.value != obj.options.value) {
2520                // Set input value
2521                el.value = obj.options.value;
2522                if (el.tagName === 'INPUT') {
2523                    el.style.color = el.value;
2524                    el.style.backgroundColor = el.value;
2525                }
2526
2527                // Element onchange native
2528                if (typeof(el.oninput) == 'function') {
2529                    el.oninput({
2530                        type: 'input',
2531                        target: el,
2532                        value: el.value
2533                    });
2534                }
2535            }
2536
2537            if (obj.options.closeOnChange == true) {
2538                obj.close();
2539            }
2540        }
2541    }
2542
2543    /**
2544     * Get value
2545     */
2546    obj.getValue = function() {
2547        return obj.options.value;
2548    }
2549
2550    var backdropClickControl = false;
2551
2552    // Converts a number in decimal to hexadecimal
2553    var decToHex = function(num) {
2554        var hex = num.toString(16);
2555        return hex.length === 1 ? "0" + hex : hex;
2556    }
2557
2558    // Converts a color in rgb to hexadecimal
2559    var rgbToHex = function(r, g, b) {
2560        return "#" + decToHex(r) + decToHex(g) + decToHex(b);
2561    }
2562
2563    // Converts a number in hexadecimal to decimal
2564    var hexToDec = function(hex) {
2565        return parseInt('0x' + hex);
2566    }
2567
2568    // Converts a color in hexadecimal to rgb
2569    var HexToRgb = function(hex) {
2570        return [hexToDec(hex.substr(1, 2)), hexToDec(hex.substr(3, 2)), hexToDec(hex.substr(5, 2))]
2571    }
2572
2573    var table = function() {
2574        // Content of the first tab
2575        var tableContainer = document.createElement('div');
2576        tableContainer.className = 'jcolor-grid';
2577
2578        // Cells
2579        obj.values = [];
2580
2581        // Table pallete
2582        var t = document.createElement('table');
2583        t.setAttribute('cellpadding', '7');
2584        t.setAttribute('cellspacing', '0');
2585
2586        for (var j = 0; j < obj.options.palette.length; j++) {
2587            var tr = document.createElement('tr');
2588            for (var i = 0; i < obj.options.palette[j].length; i++) {
2589                var td = document.createElement('td');
2590                var color = obj.options.palette[j][i];
2591                if (color.length < 7 && color.substr(0,1) !== '#') {
2592                    color = '#' + color;
2593                }
2594                td.style.backgroundColor = color;
2595                td.setAttribute('data-value', color);
2596                td.innerHTML = '';
2597                tr.appendChild(td);
2598
2599                // Selected color
2600                if (obj.options.value == color) {
2601                    td.classList.add('jcolor-selected');
2602                }
2603
2604                // Possible values
2605                obj.values[color] = td;
2606            }
2607            t.appendChild(tr);
2608        }
2609
2610        // Append to the table
2611        tableContainer.appendChild(t);
2612
2613        return tableContainer;
2614    }
2615
2616    // Canvas where the image will be rendered
2617    var canvas = document.createElement('canvas');
2618    canvas.width = 200;
2619    canvas.height = 160;
2620    var context = canvas.getContext("2d");
2621
2622    var resizeCanvas = function() {
2623        // Specifications necessary to correctly obtain colors later in certain positions
2624        var m = tabs.firstChild.getBoundingClientRect();
2625        canvas.width = m.width - 14;
2626        gradient()
2627    }
2628
2629    var gradient = function() {
2630        var g = context.createLinearGradient(0, 0, canvas.width, 0);
2631        // Create color gradient
2632        g.addColorStop(0,    "rgb(255,0,0)");
2633        g.addColorStop(0.15, "rgb(255,0,255)");
2634        g.addColorStop(0.33, "rgb(0,0,255)");
2635        g.addColorStop(0.49, "rgb(0,255,255)");
2636        g.addColorStop(0.67, "rgb(0,255,0)");
2637        g.addColorStop(0.84, "rgb(255,255,0)");
2638        g.addColorStop(1,    "rgb(255,0,0)");
2639        context.fillStyle = g;
2640        context.fillRect(0, 0, canvas.width, canvas.height);
2641        g = context.createLinearGradient(0, 0, 0, canvas.height);
2642        g.addColorStop(0,   "rgba(255,255,255,1)");
2643        g.addColorStop(0.5, "rgba(255,255,255,0)");
2644        g.addColorStop(0.5, "rgba(0,0,0,0)");
2645        g.addColorStop(1,   "rgba(0,0,0,1)");
2646        context.fillStyle = g;
2647        context.fillRect(0, 0, canvas.width, canvas.height);
2648    }
2649
2650    var hsl = function() {
2651        var element = document.createElement('div');
2652        element.className = "jcolor-hsl";
2653
2654        var point = document.createElement('div');
2655        point.className = 'jcolor-point';
2656
2657        var div = document.createElement('div');
2658        div.appendChild(canvas);
2659        div.appendChild(point);
2660        element.appendChild(div);
2661
2662        // Moves the marquee point to the specified position
2663        var update = function(buttons, x, y) {
2664            if (buttons === 1) {
2665                var rect = element.getBoundingClientRect();
2666                var left = x - rect.left;
2667                var top = y - rect.top;
2668                if (left < 0) {
2669                    left = 0;
2670                }
2671                if (top < 0) {
2672                    top = 0;
2673                }
2674                if (left > rect.width) {
2675                    left = rect.width;
2676                }
2677                if (top > rect.height) {
2678                    top = rect.height;
2679                }
2680                point.style.left = left + 'px';
2681                point.style.top = top + 'px';
2682                var pixel = context.getImageData(left, top, 1, 1).data;
2683                slidersResult = rgbToHex(pixel[0], pixel[1], pixel[2]);
2684            }
2685        }
2686
2687        // Applies the point's motion function to the div that contains it
2688        element.addEventListener('mousedown', function(e) {
2689            update(e.buttons, e.clientX, e.clientY);
2690        });
2691
2692        element.addEventListener('mousemove', function(e) {
2693            update(e.buttons, e.clientX, e.clientY);
2694        });
2695
2696        element.addEventListener('touchmove', function(e) {
2697            update(1, e.changedTouches[0].clientX, e.changedTouches[0].clientY);
2698        });
2699
2700        return element;
2701    }
2702
2703    var slidersResult = '';
2704
2705    var rgbInputs = [];
2706
2707    var changeInputColors = function() {
2708        if (slidersResult !== '') {
2709            for (var j = 0; j < rgbInputs.length; j++) {
2710                var currentColor = HexToRgb(slidersResult);
2711
2712                currentColor[j] = 0;
2713
2714                var newGradient = 'linear-gradient(90deg, rgb(';
2715                newGradient += currentColor.join(', ');
2716                newGradient += '), rgb(';
2717
2718                currentColor[j] = 255;
2719
2720                newGradient += currentColor.join(', ');
2721                newGradient += '))';
2722
2723                rgbInputs[j].style.backgroundImage = newGradient;
2724            }
2725        }
2726    }
2727
2728    var sliders = function() {
2729        // Content of the third tab
2730        var slidersElement = document.createElement('div');
2731        slidersElement.className = 'jcolor-sliders';
2732
2733        var slidersBody = document.createElement('div');
2734
2735        // Creates a range-type input with the specified name
2736        var createSliderInput = function(name) {
2737            var inputContainer = document.createElement('div');
2738            inputContainer.className = 'jcolor-sliders-input-container';
2739
2740            var label = document.createElement('label');
2741            label.innerText = name;
2742
2743            var subContainer = document.createElement('div');
2744            subContainer.className = 'jcolor-sliders-input-subcontainer';
2745
2746            var input = document.createElement('input');
2747            input.type = 'range';
2748            input.min = 0;
2749            input.max = 255;
2750            input.value = 0;
2751
2752            inputContainer.appendChild(label);
2753            subContainer.appendChild(input);
2754
2755            var value = document.createElement('div');
2756            value.innerText = input.value;
2757
2758            input.addEventListener('input', function() {
2759                value.innerText = input.value;
2760            });
2761
2762            subContainer.appendChild(value);
2763            inputContainer.appendChild(subContainer);
2764
2765            slidersBody.appendChild(inputContainer);
2766
2767            return input;
2768        }
2769
2770        // Creates red, green and blue inputs
2771        rgbInputs = [
2772            createSliderInput('Red'),
2773            createSliderInput('Green'),
2774            createSliderInput('Blue'),
2775        ];
2776
2777        slidersElement.appendChild(slidersBody);
2778
2779        // Element that prints the current color
2780        var slidersResultColor = document.createElement('div');
2781        slidersResultColor.className = 'jcolor-sliders-final-color';
2782
2783        var resultElement = document.createElement('div');
2784        resultElement.style.visibility = 'hidden';
2785        resultElement.innerText = 'a';
2786        slidersResultColor.appendChild(resultElement)
2787
2788        // Update the element that prints the current color
2789        var updateResult = function() {
2790            var resultColor = rgbToHex(parseInt(rgbInputs[0].value), parseInt(rgbInputs[1].value), parseInt(rgbInputs[2].value));
2791
2792            resultElement.innerText = resultColor;
2793            resultElement.style.color = resultColor;
2794            resultElement.style.removeProperty('visibility');
2795
2796            slidersResult = resultColor;
2797        }
2798
2799        // Apply the update function to color inputs
2800        rgbInputs.forEach(function(rgbInput) {
2801            rgbInput.addEventListener('input', function() {
2802                updateResult();
2803                changeInputColors();
2804            });
2805        });
2806
2807        slidersElement.appendChild(slidersResultColor);
2808
2809        return slidersElement;
2810    }
2811
2812    var init = function() {
2813        // Initial options
2814        obj.setOptions(options);
2815
2816        // Add a proper input tag when the element is an input
2817        if (el.tagName == 'INPUT') {
2818            el.classList.add('jcolor-input');
2819            el.readOnly = true;
2820        }
2821
2822        // Table container
2823        container = document.createElement('div');
2824        container.className = 'jcolor';
2825
2826        // Table container
2827        backdrop = document.createElement('div');
2828        backdrop.className = 'jcolor-backdrop';
2829        container.appendChild(backdrop);
2830
2831        // Content
2832        content = document.createElement('div');
2833        content.className = 'jcolor-content';
2834
2835        // Controls
2836        var controls = document.createElement('div');
2837        controls.className = 'jcolor-controls';
2838        content.appendChild(controls);
2839
2840        // Reset button
2841        resetButton  = document.createElement('div');
2842        resetButton.className = 'jcolor-reset';
2843        resetButton.innerHTML = obj.options.resetLabel;
2844        controls.appendChild(resetButton);
2845
2846        // Close button
2847        closeButton  = document.createElement('div');
2848        closeButton.className = 'jcolor-close';
2849        closeButton.innerHTML = obj.options.doneLabel;
2850        controls.appendChild(closeButton);
2851
2852        // Element that will be used to create the tabs
2853        tabs = document.createElement('div');
2854        content.appendChild(tabs);
2855
2856        // Starts the jSuites tabs component
2857        jsuitesTabs = jSuites.tabs(tabs, {
2858            animation: true,
2859            data: [
2860                {
2861                    title: 'Grid',
2862                    contentElement: table(),
2863                },
2864                {
2865                    title: 'Spectrum',
2866                    contentElement: hsl(),
2867                },
2868                {
2869                    title: 'Sliders',
2870                    contentElement: sliders(),
2871                }
2872            ],
2873            onchange: function(element, instance, index) {
2874                if (index === 1) {
2875                    resizeCanvas();
2876                } else {
2877                    var color = slidersResult !== '' ? slidersResult : obj.getValue();
2878
2879                    if (index === 2 && color) {
2880                        var rgb = HexToRgb(color);
2881
2882                        rgbInputs.forEach(function(rgbInput, index) {
2883                            rgbInput.value = rgb[index];
2884                            rgbInput.dispatchEvent(new Event('input'));
2885                        });
2886                    }
2887                }
2888            },
2889            palette: 'modern',
2890        });
2891
2892        container.appendChild(content);
2893
2894        // Insert picker after the element
2895        if (el.tagName == 'INPUT') {
2896            el.parentNode.insertBefore(container, el.nextSibling);
2897        } else {
2898            el.appendChild(container);
2899        }
2900
2901        container.addEventListener("click", function(e) {
2902            if (e.target.tagName == 'TD') {
2903                var value = e.target.getAttribute('data-value');
2904                if (value) {
2905                    obj.setValue(value);
2906                }
2907            } else if (e.target.classList.contains('jcolor-reset')) {
2908                obj.setValue('');
2909                obj.close();
2910            } else if (e.target.classList.contains('jcolor-close')) {
2911                if (jsuitesTabs.getActive() > 0) {
2912                    obj.setValue(slidersResult);
2913                }
2914                obj.close();
2915            } else if (e.target.classList.contains('jcolor-backdrop')) {
2916                obj.close();
2917            } else {
2918                obj.open();
2919            }
2920        });
2921
2922        /**
2923         * If element is focus open the picker
2924         */
2925        el.addEventListener("mouseup", function(e) {
2926            obj.open();
2927        });
2928
2929        // If the picker is open on the spectrum tab, it changes the canvas size when the window size is changed
2930        window.addEventListener('resize', function() {
2931            if (container.classList.contains('jcolor-focus') && jsuitesTabs.getActive() == 1) {
2932                resizeCanvas();
2933            }
2934        });
2935
2936        // Default opened
2937        if (obj.options.opened == true) {
2938            obj.open();
2939        }
2940
2941        // Change
2942        el.change = obj.setValue;
2943
2944        // Global generic value handler
2945        el.val = function(val) {
2946            if (val === undefined) {
2947                return obj.getValue();
2948            } else {
2949                obj.setValue(val);
2950            }
2951        }
2952
2953        // Keep object available from the node
2954        el.color = obj;
2955
2956        // Container shortcut
2957        container.color = obj;
2958    }
2959
2960    obj.toHex = function(rgb) {
2961        var hex = function(x) {
2962            return ("0" + parseInt(x).toString(16)).slice(-2);
2963        }
2964        if (rgb) {
2965            if (/^#[0-9A-F]{6}$/i.test(rgb)) {
2966                return rgb;
2967            } else {
2968                rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
2969                if (rgb && rgb.length) {
2970                    return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
2971                } else {
2972                    return "";
2973                }
2974            }
2975        }
2976    }
2977
2978    init();
2979
2980    return obj;
2981});
2982
2983
2984
2985jSuites.contextmenu = (function(el, options) {
2986    // New instance
2987    var obj = { type:'contextmenu'};
2988    obj.options = {};
2989
2990    // Default configuration
2991    var defaults = {
2992        items: null,
2993        onclick: null,
2994    };
2995
2996    // Loop through our object
2997    for (var property in defaults) {
2998        if (options && options.hasOwnProperty(property)) {
2999            obj.options[property] = options[property];
3000        } else {
3001            obj.options[property] = defaults[property];
3002        }
3003    }
3004
3005    // Class definition
3006    el.classList.add('jcontextmenu');
3007
3008    /**
3009     * Open contextmenu
3010     */
3011    obj.open = function(e, items) {
3012        if (items) {
3013            // Update content
3014            obj.options.items = items;
3015            // Create items
3016            obj.create(items);
3017        }
3018
3019        // Close current contextmenu
3020        if (jSuites.contextmenu.current) {
3021            jSuites.contextmenu.current.close();
3022        }
3023
3024        // Add to the opened components monitor
3025        jSuites.tracking(obj, true);
3026
3027        // Show context menu
3028        el.classList.add('jcontextmenu-focus');
3029
3030        // Current
3031        jSuites.contextmenu.current = obj;
3032
3033        // Coordinates
3034        if ((obj.options.items && obj.options.items.length > 0) || el.children.length) {
3035            if (e.target) {
3036                if (e.changedTouches && e.changedTouches[0]) {
3037                    x = e.changedTouches[0].clientX;
3038                    y = e.changedTouches[0].clientY;
3039                } else {
3040                    var x = e.clientX;
3041                    var y = e.clientY;
3042                }
3043            } else {
3044                var x = e.x;
3045                var y = e.y;
3046            }
3047
3048            var rect = el.getBoundingClientRect();
3049
3050            if (window.innerHeight < y + rect.height) {
3051                var h = y - rect.height;
3052                if (h < 0) {
3053                    h = 0;
3054                }
3055                el.style.top = h + 'px';
3056            } else {
3057                el.style.top = y + 'px';
3058            }
3059
3060            if (window.innerWidth < x + rect.width) {
3061                if (x - rect.width > 0) {
3062                    el.style.left = (x - rect.width) + 'px';
3063                } else {
3064                    el.style.left = '10px';
3065                }
3066            } else {
3067                el.style.left = x + 'px';
3068            }
3069        }
3070    }
3071
3072    obj.isOpened = function() {
3073        return el.classList.contains('jcontextmenu-focus') ? true : false;
3074    }
3075
3076    /**
3077     * Close menu
3078     */
3079    obj.close = function() {
3080        if (el.classList.contains('jcontextmenu-focus')) {
3081            el.classList.remove('jcontextmenu-focus');
3082        }
3083        jSuites.tracking(obj, false);
3084    }
3085
3086    /**
3087     * Create items based on the declared objectd
3088     * @param {object} items - List of object
3089     */
3090    obj.create = function(items) {
3091        // Update content
3092        el.innerHTML = '';
3093
3094        // Add header contextmenu
3095        var itemHeader = createHeader();
3096        el.appendChild(itemHeader);
3097
3098        // Append items
3099        for (var i = 0; i < items.length; i++) {
3100            var itemContainer = createItemElement(items[i]);
3101            el.appendChild(itemContainer);
3102        }
3103    }
3104
3105    /**
3106     * createHeader for context menu
3107     * @private
3108     * @returns {HTMLElement}
3109     */
3110    function createHeader() {
3111        var header = document.createElement('div');
3112        header.classList.add("header");
3113        header.addEventListener("click", function(e) {
3114            e.preventDefault();
3115            e.stopPropagation();
3116        });
3117        var title = document.createElement('a');
3118        title.classList.add("title");
3119        title.innerHTML = jSuites.translate("Menu");
3120
3121        header.appendChild(title);
3122
3123        var closeButton = document.createElement('a');
3124        closeButton.classList.add("close");
3125        closeButton.innerHTML = jSuites.translate("close");
3126        closeButton.addEventListener("click", function(e) {
3127            obj.close();
3128        });
3129
3130        header.appendChild(closeButton);
3131
3132        return header;
3133    }
3134
3135    /**
3136     * Private function for create a new Item element
3137     * @param {type} item
3138     * @returns {jsuitesL#15.jSuites.contextmenu.createItemElement.itemContainer}
3139     */
3140    function createItemElement(item) {
3141        if (item.type && (item.type == 'line' || item.type == 'divisor')) {
3142            var itemContainer = document.createElement('hr');
3143        } else {
3144            var itemContainer = document.createElement('div');
3145            var itemText = document.createElement('a');
3146            itemText.innerHTML = item.title;
3147
3148            if (item.tooltip) {
3149                itemContainer.setAttribute('title', item.tooltip);
3150            }
3151
3152            if (item.icon) {
3153                itemContainer.setAttribute('data-icon', item.icon);
3154            }
3155
3156            if (item.id) {
3157                itemContainer.id = item.id;
3158            }
3159
3160            if (item.disabled) {
3161                itemContainer.className = 'jcontextmenu-disabled';
3162            } else if (item.onclick) {
3163                itemContainer.method = item.onclick;
3164                itemContainer.addEventListener("mousedown", function(e) {
3165                    e.preventDefault();
3166                });
3167                itemContainer.addEventListener("mouseup", function(e) {
3168                    // Execute method
3169                    this.method(this, e);
3170                });
3171            }
3172            itemContainer.appendChild(itemText);
3173
3174            if (item.submenu) {
3175                var itemIconSubmenu = document.createElement('span');
3176                itemIconSubmenu.innerHTML = "&#9658;";
3177                itemContainer.appendChild(itemIconSubmenu);
3178                itemContainer.classList.add('jcontexthassubmenu');
3179                var el_submenu = document.createElement('div');
3180                // Class definition
3181                el_submenu.classList.add('jcontextmenu');
3182                // Focusable
3183                el_submenu.setAttribute('tabindex', '900');
3184
3185                // Append items
3186                var submenu = item.submenu;
3187                for (var i = 0; i < submenu.length; i++) {
3188                    var itemContainerSubMenu = createItemElement(submenu[i]);
3189                    el_submenu.appendChild(itemContainerSubMenu);
3190                }
3191
3192                itemContainer.appendChild(el_submenu);
3193            } else if (item.shortcut) {
3194                var itemShortCut = document.createElement('span');
3195                itemShortCut.innerHTML = item.shortcut;
3196                itemContainer.appendChild(itemShortCut);
3197            }
3198        }
3199        return itemContainer;
3200    }
3201
3202    if (typeof(obj.options.onclick) == 'function') {
3203        el.addEventListener('click', function(e) {
3204            obj.options.onclick(obj, e);
3205        });
3206    }
3207
3208    // Create items
3209    if (obj.options.items) {
3210        obj.create(obj.options.items);
3211    }
3212
3213    window.addEventListener("mousewheel", function() {
3214        obj.close();
3215    });
3216
3217    el.contextmenu = obj;
3218
3219    return obj;
3220});
3221
3222
3223jSuites.dropdown = (function(el, options) {
3224    // Already created, update options
3225    if (el.dropdown) {
3226        return el.dropdown.setOptions(options, true);
3227    }
3228
3229    // New instance
3230    var obj = { type: 'dropdown' };
3231    obj.options = {};
3232
3233    // Success
3234    var success = function(data, val) {
3235        // Set data
3236        if (data && data.length) {
3237            // Sort
3238            if (obj.options.sortResults !== false) {
3239                if(typeof obj.options.sortResults == "function") {
3240                    data.sort(obj.options.sortResults);
3241                } else {
3242                    data.sort(sortData);
3243                }
3244            }
3245
3246            obj.setData(data);
3247        }
3248
3249        // Onload method
3250        if (typeof(obj.options.onload) == 'function') {
3251            obj.options.onload(el, obj, data, val);
3252        }
3253
3254        // Set value
3255        if (val) {
3256            applyValue(val);
3257        }
3258
3259        // Component value
3260        if (val === undefined || val === null) {
3261            obj.options.value = '';
3262        }
3263        el.value = obj.options.value;
3264
3265        // Open dropdown
3266        if (obj.options.opened == true) {
3267            obj.open();
3268        }
3269    }
3270
3271    
3272    // Default sort
3273    var sortData = function(itemA, itemB) {
3274        var testA, testB;
3275        if(typeof itemA == "string") {
3276            testA = itemA;
3277        } else {
3278            if(itemA.text) {
3279                testA = itemA.text;
3280            } else if(itemA.name) {
3281                testA = itemA.name;
3282            }
3283        }
3284        
3285        if(typeof itemB == "string") {
3286            testB = itemB;
3287        } else {
3288            if(itemB.text) {
3289                testB = itemB.text;
3290            } else if(itemB.name) {
3291                testB = itemB.name;
3292            }
3293        }
3294        
3295        if (typeof testA == "string" || typeof testB == "string") {
3296            if (typeof testA != "string") { testA = ""+testA; }
3297            if (typeof testB != "string") { testB = ""+testB; }
3298            return testA.localeCompare(testB);
3299        } else {
3300            return testA - testB;
3301        }
3302    }
3303
3304    /**
3305     * Reset the options for the dropdown
3306     */
3307    var resetValue = function() {
3308        // Reset value container
3309        obj.value = {};
3310        // Remove selected
3311        for (var i = 0; i < obj.items.length; i++) {
3312            if (obj.items[i].selected == true) {
3313                if (obj.items[i].element) {
3314                    obj.items[i].element.classList.remove('jdropdown-selected')
3315                }
3316                obj.items[i].selected = null;
3317            }
3318        }
3319        // Reset options
3320        obj.options.value = '';
3321        // Reset value
3322        el.value = '';
3323    }
3324
3325    /**
3326     * Apply values to the dropdown
3327     */
3328    var applyValue = function(values) {
3329        // Reset the current values
3330        resetValue();
3331
3332        // Read values
3333        if (values !== null) {
3334            if (! values) {
3335                if (typeof(obj.value['']) !== 'undefined') {
3336                    obj.value[''] = '';
3337                }
3338            } else {
3339                if (! Array.isArray(values)) {
3340                    values = ('' + values).split(';');
3341                }
3342                for (var i = 0; i < values.length; i++) {
3343                    obj.value[values[i]] = '';
3344                }
3345            }
3346        }
3347
3348        // Update the DOM
3349        for (var i = 0; i < obj.items.length; i++) {
3350            if (typeof(obj.value[Value(i)]) !== 'undefined') {
3351                if (obj.items[i].element) {
3352                    obj.items[i].element.classList.add('jdropdown-selected')
3353                }
3354                obj.items[i].selected = true;
3355
3356                // Keep label
3357                obj.value[Value(i)] = Text(i);
3358            }
3359        }
3360
3361        // Global value
3362        obj.options.value = Object.keys(obj.value).join(';');
3363
3364        // Update labels
3365        obj.header.value = obj.getText();
3366    }
3367
3368    // Get the value of one item
3369    var Value = function(k, v) {
3370        // Legacy purposes
3371        if (! obj.options.format) {
3372            var property = 'value';
3373        } else {
3374            var property = 'id';
3375        }
3376
3377        if (obj.items[k]) {
3378            if (v !== undefined) {
3379                return obj.items[k].data[property] = v;
3380            } else {
3381                return obj.items[k].data[property];
3382            }
3383        }
3384
3385        return '';
3386    }
3387
3388    // Get the label of one item
3389    var Text = function(k, v) {
3390        // Legacy purposes
3391        if (! obj.options.format) {
3392            var property = 'text';
3393        } else {
3394            var property = 'name';
3395        }
3396
3397        if (obj.items[k]) {
3398            if (v !== undefined) {
3399                return obj.items[k].data[property] = v;
3400            } else {
3401                return obj.items[k].data[property];
3402            }
3403        }
3404
3405        return '';
3406    }
3407
3408    var getValue = function() {
3409        return Object.keys(obj.value);
3410    }
3411
3412    var getText = function() {
3413        var data = [];
3414        var k = Object.keys(obj.value);
3415        for (var i = 0; i < k.length; i++) {
3416            data.push(obj.value[k[i]]);
3417        }
3418        return data;
3419    }
3420
3421    obj.setOptions = function(options, reset) {
3422        if (! options) {
3423            options = {};
3424        }
3425
3426        // Default configuration
3427        var defaults = {
3428            url: null,
3429            data: [],
3430            format: 0,
3431            multiple: false,
3432            autocomplete: false,
3433            remoteSearch: false,
3434            lazyLoading: false,
3435            type: null,
3436            width: null,
3437            maxWidth: null,
3438            opened: false,
3439            value: null,
3440            placeholder: '',
3441            newOptions: false,
3442            position: false,
3443            onchange: null,
3444            onload: null,
3445            onopen: null,
3446            onclose: null,
3447            onfocus: null,
3448            onblur: null,
3449            oninsert: null,
3450            onbeforeinsert: null,
3451            sortResults: false,
3452            autofocus: false,
3453        }
3454
3455        // Loop through our object
3456        for (var property in defaults) {
3457            if (options && options.hasOwnProperty(property)) {
3458                obj.options[property] = options[property];
3459            } else {
3460                if (typeof(obj.options[property]) == 'undefined' || reset === true) {
3461                    obj.options[property] = defaults[property];
3462                }
3463            }
3464        }
3465
3466        // Force autocomplete search
3467        if (obj.options.remoteSearch == true || obj.options.type === 'searchbar') {
3468            obj.options.autocomplete = true;
3469        }
3470
3471        // New options
3472        if (obj.options.newOptions == true) {
3473            obj.header.classList.add('jdropdown-add');
3474        } else {
3475            obj.header.classList.remove('jdropdown-add');
3476        }
3477
3478        // Autocomplete
3479        if (obj.options.autocomplete == true) {
3480            obj.header.removeAttribute('readonly');
3481        } else {
3482            obj.header.setAttribute('readonly', 'readonly');
3483        }
3484
3485        // Place holder
3486        if (obj.options.placeholder) {
3487            obj.header.setAttribute('placeholder', obj.options.placeholder);
3488        } else {
3489            obj.header.removeAttribute('placeholder');
3490        }
3491
3492        // Remove specific dropdown typing to add again
3493        el.classList.remove('jdropdown-searchbar');
3494        el.classList.remove('jdropdown-picker');
3495        el.classList.remove('jdropdown-list');
3496
3497        if (obj.options.type == 'searchbar') {
3498            el.classList.add('jdropdown-searchbar');
3499        } else if (obj.options.type == 'list') {
3500            el.classList.add('jdropdown-list');
3501        } else if (obj.options.type == 'picker') {
3502            el.classList.add('jdropdown-picker');
3503        } else {
3504            if (jSuites.getWindowWidth() < 800) {
3505                if (obj.options.autocomplete) {
3506                    el.classList.add('jdropdown-searchbar');
3507                    obj.options.type = 'searchbar';
3508                } else {
3509                    el.classList.add('jdropdown-picker');
3510                    obj.options.type = 'picker';
3511                }
3512            } else {
3513                if (obj.options.width) {
3514                    el.style.width = obj.options.width;
3515                    el.style.minWidth = obj.options.width;
3516                } else {
3517                    el.style.removeProperty('width');
3518                    el.style.removeProperty('min-width');
3519                }
3520
3521                el.classList.add('jdropdown-default');
3522                obj.options.type = 'default';
3523            }
3524        }
3525
3526        // Close button
3527        if (obj.options.type == 'searchbar') {
3528            containerHeader.appendChild(closeButton);
3529        } else {
3530            container.insertBefore(closeButton, container.firstChild);
3531        }
3532
3533        // Load the content
3534        if (obj.options.url && ! options.data) {
3535            jSuites.ajax({
3536                url: obj.options.url,
3537                method: 'GET',
3538                dataType: 'json',
3539                success: function(data) {
3540                    if (data) {
3541                        success(data, obj.options.value);
3542                    }
3543                }
3544            });
3545        } else {
3546            success(obj.options.data, obj.options.value);
3547        }
3548
3549        // Return the instance
3550        return obj;
3551    }
3552
3553    // Helpers
3554    var containerHeader = null;
3555    var container = null;
3556    var content = null;
3557    var closeButton = null;
3558    var resetButton = null;
3559    var backdrop = null;
3560
3561    var keyTimer = null;
3562
3563    /**
3564     * Init dropdown
3565     */
3566    var init = function() {
3567        // Do not accept null
3568        if (! options) {
3569            options = {};
3570        }
3571
3572        // If the element is a SELECT tag, create a configuration object
3573        if (el.tagName == 'SELECT') {
3574            var ret = jSuites.dropdown.extractFromDom(el, options);
3575            el = ret.el;
3576            options = ret.options;
3577        }
3578
3579        // Place holder
3580        if (! options.placeholder && el.getAttribute('placeholder')) {
3581            options.placeholder = el.getAttribute('placeholder');
3582        }
3583
3584        // Value container
3585        obj.value = {};
3586        // Containers
3587        obj.items = [];
3588        obj.groups = [];
3589        // Search options
3590        obj.search = '';
3591        obj.results = null;
3592
3593        // Create dropdown
3594        el.classList.add('jdropdown');
3595
3596        // Header container
3597        containerHeader = document.createElement('div');
3598        containerHeader.className = 'jdropdown-container-header';
3599
3600        // Header
3601        obj.header = document.createElement('input');
3602        obj.header.className = 'jdropdown-header jss_object';
3603        obj.header.type = 'text';
3604        obj.header.setAttribute('autocomplete', 'off');
3605        obj.header.onfocus = function() {
3606            if (typeof(obj.options.onfocus) == 'function') {
3607                obj.options.onfocus(el);
3608            }
3609        }
3610
3611        obj.header.onblur = function() {
3612            if (typeof(obj.options.onblur) == 'function') {
3613                obj.options.onblur(el);
3614            }
3615        }
3616
3617        obj.header.onkeyup = function(e) {
3618            if (obj.options.autocomplete == true && ! keyTimer) {
3619                if (obj.search != obj.header.value.trim()) {
3620                    keyTimer = setTimeout(function() {
3621                        obj.find(obj.header.value.trim());
3622                        keyTimer = null;
3623                    }, 400);
3624                }
3625
3626                if (! el.classList.contains('jdropdown-focus')) {
3627                    obj.open();
3628                }
3629            } else {
3630                if (! obj.options.autocomplete) {
3631                    obj.next(e.key);
3632                }
3633            }
3634        }
3635
3636        // Global controls
3637        if (! jSuites.dropdown.hasEvents) {
3638            // Execute only one time
3639            jSuites.dropdown.hasEvents = true;
3640            // Enter and Esc
3641            document.addEventListener("keydown", jSuites.dropdown.keydown);
3642        }
3643
3644        // Container
3645        container = document.createElement('div');
3646        container.className = 'jdropdown-container';
3647
3648        // Dropdown content
3649        content = document.createElement('div');
3650        content.className = 'jdropdown-content';
3651
3652        // Close button
3653        closeButton = document.createElement('div');
3654        closeButton.className = 'jdropdown-close';
3655        closeButton.innerHTML = 'Done';
3656
3657        // Reset button
3658        resetButton = document.createElement('div');
3659        resetButton.className = 'jdropdown-reset';
3660        resetButton.innerHTML = 'x';
3661        resetButton.onclick = function() {
3662            obj.reset();
3663            obj.close();
3664        }
3665
3666        // Create backdrop
3667        backdrop = document.createElement('div');
3668        backdrop.className = 'jdropdown-backdrop';
3669
3670        // Append elements
3671        containerHeader.appendChild(obj.header);
3672
3673        container.appendChild(content);
3674        el.appendChild(containerHeader);
3675        el.appendChild(container);
3676        el.appendChild(backdrop);
3677
3678        // Set the otiptions
3679        obj.setOptions(options);
3680
3681        if ('ontouchsend' in document.documentElement === true) {
3682            el.addEventListener('touchsend', jSuites.dropdown.mouseup);
3683        } else {
3684            el.addEventListener('mouseup', jSuites.dropdown.mouseup);
3685        }
3686
3687        // Lazyloading
3688        if (obj.options.lazyLoading == true) {
3689            jSuites.lazyLoading(content, {
3690                loadUp: obj.loadUp,
3691                loadDown: obj.loadDown,
3692            });
3693        }
3694
3695        content.onwheel = function(e) {
3696            e.stopPropagation();
3697        }
3698
3699        // Change method
3700        el.change = obj.setValue;
3701
3702        // Global generic value handler
3703        el.val = function(val) {
3704            if (val === undefined) {
3705                return obj.getValue(obj.options.multiple ? true : false);
3706            } else {
3707                obj.setValue(val);
3708            }
3709        }
3710
3711        // Keep object available from the node
3712        el.dropdown = obj;
3713    }
3714
3715    /**
3716     * Get the current remote source of data URL
3717     */
3718    obj.getUrl = function() {
3719        return obj.options.url;
3720    }
3721
3722    /**
3723     * Set the new data from a remote source
3724     * @param {string} url - url from the remote source
3725     * @param {function} callback - callback when the data is loaded
3726     */
3727    obj.setUrl = function(url, callback) {
3728        obj.options.url = url;
3729
3730        jSuites.ajax({
3731            url: obj.options.url,
3732            method: 'GET',
3733            dataType: 'json',
3734            success: function(data) {
3735                obj.setData(data);
3736                // Callback
3737                if (typeof(callback) == 'function') {
3738                    callback(obj);
3739                }
3740            }
3741        });
3742    }
3743
3744    /**
3745     * Set ID for one item
3746     */
3747    obj.setId = function(item, v) {
3748        // Legacy purposes
3749        if (! obj.options.format) {
3750            var property = 'value';
3751        } else {
3752            var property = 'id';
3753        }
3754
3755        if (typeof(item) == 'object') {
3756            item[property] = v;
3757        } else {
3758            obj.items[item].data[property] = v;
3759        }
3760    }
3761
3762    /**
3763     * Add a new item
3764     * @param {string} title - title of the new item
3765     * @param {string} id - value/id of the new item
3766     */
3767    obj.add = function(title, id) {
3768        if (! title) {
3769            var current = obj.options.autocomplete == true ? obj.header.value : '';
3770            var title = prompt(jSuites.translate('Add A New Option'), current);
3771            if (! title) {
3772                return false;
3773            }
3774        }
3775
3776        // Id
3777        if (! id) {
3778           id = jSuites.guid();
3779        }
3780
3781        // Create new item
3782        if (! obj.options.format) {
3783            var item = {
3784                value: id,
3785                text: title,
3786            }
3787        } else {
3788            var item = {
3789                id: id,
3790                name: title,
3791            }
3792        }
3793
3794        // Callback
3795        if (typeof(obj.options.onbeforeinsert) == 'function') {
3796            var ret = obj.options.onbeforeinsert(obj, item);
3797            if (ret === false) {
3798                return false;
3799            } else if (ret) {
3800                item = ret;
3801            }
3802        }
3803
3804        // Add item to the main list
3805        obj.options.data.push(item);
3806
3807        // Create DOM
3808        var newItem = obj.createItem(item);
3809
3810        // Append DOM to the list
3811        content.appendChild(newItem.element);
3812
3813        // Callback
3814        if (typeof(obj.options.oninsert) == 'function') {
3815            obj.options.oninsert(obj, item, newItem);
3816        }
3817
3818        // Show content
3819        if (content.style.display == 'none') {
3820            content.style.display = '';
3821        }
3822
3823        // Search?
3824        if (obj.results) {
3825            obj.results.push(newItem);
3826        }
3827
3828        return item;
3829    }
3830
3831    /**
3832     * Create a new item
3833     */
3834    obj.createItem = function(data, group, groupName) {
3835        // Keep the correct source of data
3836        if (! obj.options.format) {
3837            if (! data.value && data.id !== undefined) {
3838                data.value = data.id;
3839                //delete data.id;
3840            }
3841            if (! data.text && data.name !== undefined) {
3842                data.text = data.name;
3843                //delete data.name;
3844            }
3845        } else {
3846            if (! data.id && data.value !== undefined) {
3847                data.id = data.value;
3848                //delete data.value;
3849            }
3850            if (! data.name && data.text !== undefined) {
3851                data.name = data.text
3852                //delete data.text;
3853            }
3854        }
3855
3856        // Create item
3857        var item = {};
3858        item.element = document.createElement('div');
3859        item.element.className = 'jdropdown-item';
3860        item.element.indexValue = obj.items.length;
3861        item.data = data;
3862
3863        // Groupd DOM
3864        if (group) {
3865            item.group = group; 
3866        }
3867
3868        // Id
3869        if (data.id) {
3870            item.element.setAttribute('id', data.id);
3871        }
3872
3873        // Disabled
3874        if (data.disabled == true) {
3875            item.element.setAttribute('data-disabled', true);
3876        }
3877
3878        // Tooltip
3879        if (data.tooltip) {
3880            item.element.setAttribute('title', data.tooltip);
3881        }
3882
3883        // Image
3884        if (data.image) {
3885            var image = document.createElement('img');
3886            image.className = 'jdropdown-image';
3887            image.src = data.image;
3888            if (! data.title) {
3889               image.classList.add('jdropdown-image-small');
3890            }
3891            item.element.appendChild(image);
3892        } else if (data.icon) {
3893            var icon = document.createElement('span');
3894            icon.className = "jdropdown-icon material-icons";
3895            icon.innerText = data.icon;
3896            if (! data.title) {
3897               icon.classList.add('jdropdown-icon-small');
3898            }
3899            if (data.color) {
3900                icon.style.color = data.color;
3901            }
3902            item.element.appendChild(icon);
3903        } else if (data.color) {
3904            var color = document.createElement('div');
3905            color.className = 'jdropdown-color';
3906            color.style.backgroundColor = data.color;
3907            item.element.appendChild(color);
3908        }
3909
3910        // Set content
3911        if (! obj.options.format) {
3912            var text = data.text;
3913        } else {
3914            var text = data.name;
3915        }
3916
3917        var node = document.createElement('div');
3918        node.className = 'jdropdown-description';
3919        node.innerHTML = text || '&nbsp;'; 
3920
3921        // Title
3922        if (data.title) {
3923            var title = document.createElement('div');
3924            title.className = 'jdropdown-title';
3925            title.innerText = data.title;
3926            node.appendChild(title);
3927        }
3928
3929        // Set content
3930        if (! obj.options.format) {
3931            var val = data.value;
3932        } else {
3933            var val = data.id;
3934        }
3935
3936        // Value
3937        if (obj.value[val]) {
3938            item.element.classList.add('jdropdown-selected');
3939            item.selected = true;
3940        }
3941
3942        // Keep DOM accessible
3943        obj.items.push(item);
3944
3945        // Add node to item
3946        item.element.appendChild(node);
3947
3948        return item;
3949    }
3950
3951    obj.appendData = function(data) {
3952        // Create elements
3953        if (data.length) {
3954            // Helpers
3955            var items = [];
3956            var groups = [];
3957
3958            // Prepare data
3959            for (var i = 0; i < data.length; i++) {
3960                // Process groups
3961                if (data[i].group) {
3962                    if (! groups[data[i].group]) {
3963                        groups[data[i].group] = [];
3964                    }
3965                    groups[data[i].group].push(i);
3966                } else {
3967                    items.push(i);
3968                }
3969            }
3970
3971            // Number of items counter
3972            var counter = 0;
3973
3974            // Groups
3975            var groupNames = Object.keys(groups);
3976
3977            // Append groups in case exists
3978            if (groupNames.length > 0) {
3979                for (var i = 0; i < groupNames.length; i++) {
3980                    // Group container
3981                    var group = document.createElement('div');
3982                    group.className = 'jdropdown-group';
3983                    // Group name
3984                    var groupName = document.createElement('div');
3985                    groupName.className = 'jdropdown-group-name';
3986                    groupName.innerHTML = groupNames[i];
3987                    // Group arrow
3988                    var groupArrow = document.createElement('i');
3989                    groupArrow.className = 'jdropdown-group-arrow jdropdown-group-arrow-down';
3990                    groupName.appendChild(groupArrow);
3991                    // Group items
3992                    var groupContent = document.createElement('div');
3993                    groupContent.className = 'jdropdown-group-items';
3994                    for (var j = 0; j < groups[groupNames[i]].length; j++) {
3995                        var item = obj.createItem(data[groups[groupNames[i]][j]], group, groupNames[i]);
3996
3997                        if (obj.options.lazyLoading == false || counter < 200) {
3998                            groupContent.appendChild(item.element);
3999                            counter++;
4000                        }
4001                    }
4002                    // Group itens
4003                    group.appendChild(groupName);
4004                    group.appendChild(groupContent);
4005                    // Keep group DOM
4006                    obj.groups.push(group);
4007                    // Only add to the screen if children on the group
4008                    if (groupContent.children.length > 0) {
4009                        // Add DOM to the content
4010                        content.appendChild(group);
4011                    }
4012                }
4013            }
4014
4015            if (items.length) {
4016                for (var i = 0; i < items.length; i++) {
4017                    var item = obj.createItem(data[items[i]]);
4018                    if (obj.options.lazyLoading == false || counter < 200) {
4019                        content.appendChild(item.element);
4020                        counter++;
4021                    }
4022                }
4023            }
4024        }
4025    }
4026
4027    obj.setData = function(data) {
4028        // Reset current value
4029        resetValue();
4030
4031        // Make sure the content container is blank
4032        content.innerHTML = '';
4033
4034        // Reset
4035        obj.header.value = '';
4036
4037        // Reset items and values
4038        obj.items = [];
4039
4040        // Prepare data
4041        if (data && data.length) {
4042            for (var i = 0; i < data.length; i++) {
4043                // Compatibility
4044                if (typeof(data[i]) != 'object') {
4045                    // Correct format
4046                    if (! obj.options.format) {
4047                        data[i] = {
4048                            value: data[i],
4049                            text: data[i]
4050                        }
4051                    } else {
4052                        data[i] = {
4053                            id: data[i],
4054                            name: data[i]
4055                        }
4056                    }
4057                }
4058            }
4059
4060            // Append data
4061            obj.appendData(data);
4062
4063            // Update data
4064            obj.options.data = data;
4065        } else {
4066            // Update data
4067           obj.options.data = [];
4068        }
4069
4070        obj.close();
4071    }
4072
4073    obj.getData = function() {
4074        return obj.options.data;
4075    }
4076
4077    /**
4078     * Get position of the item
4079     */
4080    obj.getPosition = function(val) {
4081        for (var i = 0; i < obj.items.length; i++) {
4082            if (Value(i) == val) {
4083                return i;
4084            }
4085        }
4086        return false;
4087    }
4088
4089    /**
4090     * Get dropdown current text
4091     */
4092    obj.getText = function(asArray) {
4093        // Get value
4094        var v = getText();
4095        // Return value
4096        if (asArray) {
4097            return v;
4098        } else {
4099            return v.join('; ');
4100        }
4101    }
4102
4103    /**
4104     * Get dropdown current value
4105     */
4106    obj.getValue = function(asArray) {
4107        // Get value
4108        var v = getValue();
4109        // Return value
4110        if (asArray) {
4111            return v;
4112        } else {
4113            return v.join(';');
4114        }
4115    }
4116
4117    /**
4118     * Change event
4119     */
4120    var change = function(oldValue) {
4121        // Lemonade JS
4122        if (el.value != obj.options.value) {
4123            el.value = obj.options.value;
4124            if (typeof(el.oninput) == 'function') {
4125                el.oninput({
4126                    type: 'input',
4127                    target: el,
4128                    value: el.value
4129                });
4130            }
4131        }
4132
4133        // Events
4134        if (typeof(obj.options.onchange) == 'function') {
4135            obj.options.onchange(el, obj, oldValue, obj.options.value);
4136        }
4137    }
4138
4139    /**
4140     * Set value
4141     */
4142    obj.setValue = function(newValue) {
4143        // Current value
4144        var oldValue = obj.getValue();
4145        // New value
4146        if (Array.isArray(newValue)) {
4147            newValue = newValue.join(';')
4148        }
4149
4150        if (oldValue !== newValue) {
4151            // Set value
4152            applyValue(newValue);
4153
4154            // Change
4155            change(oldValue);
4156        }
4157    }
4158
4159    obj.resetSelected = function() {
4160        obj.setValue(null);
4161    } 
4162
4163    obj.selectIndex = function(index, force) {
4164        // Make sure is a number
4165        var index = parseInt(index);
4166
4167        // Only select those existing elements
4168        if (obj.items && obj.items[index] && (force === true || obj.items[index].data.disabled !== true)) {
4169            // Reset cursor to a new position
4170            obj.setCursor(index, false);
4171
4172            // Behaviour
4173            if (! obj.options.multiple) {
4174                // Update value
4175                if (obj.items[index].selected) {
4176                    obj.setValue(null);
4177                } else {
4178                    obj.setValue(Value(index));
4179                }
4180
4181                // Close component
4182                obj.close();
4183            } else {
4184                // Old value
4185                var oldValue = obj.options.value;
4186
4187                // Toggle option
4188                if (obj.items[index].selected) {
4189                    obj.items[index].element.classList.remove('jdropdown-selected');
4190                    obj.items[index].selected = false;
4191
4192                    delete obj.value[Value(index)];
4193                } else {
4194                    // Select element
4195                    obj.items[index].element.classList.add('jdropdown-selected');
4196                    obj.items[index].selected = true;
4197
4198                    // Set value
4199                    obj.value[Value(index)] = Text(index);
4200                }
4201
4202                // Global value
4203                obj.options.value = Object.keys(obj.value).join(';');
4204
4205                // Update labels for multiple dropdown
4206                if (obj.options.autocomplete == false) {
4207                    obj.header.value = getText().join('; ');
4208                }
4209
4210                // Events
4211                change(oldValue);
4212            }
4213        }
4214    }
4215
4216    obj.selectItem = function(item) {
4217        obj.selectIndex(item.indexValue);
4218    }
4219
4220    var exists = function(k, result) {
4221        for (var j = 0; j < result.length; j++) {
4222            if (! obj.options.format) {
4223                if (result[j].value == k) {
4224                    return true;
4225                }
4226            } else {
4227                if (result[j].id == k) {
4228                    return true;
4229                }
4230            }
4231        }
4232        return false;
4233    }
4234
4235    obj.find = function(str) {
4236        if (obj.search == str.trim()) {
4237            return false;
4238        }
4239
4240        // Search term
4241        obj.search = str;
4242
4243        // Reset index
4244        obj.setCursor();
4245
4246        // Remove nodes from all groups
4247        if (obj.groups.length) {
4248            for (var i = 0; i < obj.groups.length; i++) {
4249                obj.groups[i].lastChild.innerHTML = '';
4250            }
4251        }
4252
4253        // Remove all nodes
4254        content.innerHTML = '';
4255
4256        // Remove current items in the remote search
4257        if (obj.options.remoteSearch == true) {
4258            // Reset results
4259            obj.results = null;
4260            // URL
4261            var url = obj.options.url + (obj.options.url.indexOf('?') > 0 ? '&' : '?') + 'q=' + str;
4262            // Remote search
4263            jSuites.ajax({
4264                url: url,
4265                method: 'GET',
4266                dataType: 'json',
4267                success: function(result) {
4268                    // Reset items
4269                    obj.items = [];
4270
4271                    // Add the current selected items to the results in case they are not there
4272                    var current = Object.keys(obj.value);
4273                    if (current.length) {
4274                        for (var i = 0; i < current.length; i++) {
4275                            if (! exists(current[i], result)) {
4276                                if (! obj.options.format) {
4277                                    result.unshift({ value: current[i], text: obj.value[current[i]] });
4278                                } else {
4279                                    result.unshift({ id: current[i], name: obj.value[current[i]] });
4280                                }
4281                            }
4282                        }
4283                    }
4284                    // Append data
4285                    obj.appendData(result);
4286                    // Show or hide results
4287                    if (! result.length) {
4288                        content.style.display = 'none';
4289                    } else {
4290                        content.style.display = '';
4291                    }
4292                }
4293            });
4294        } else {
4295            // Search terms
4296            str = new RegExp(str, 'gi');
4297
4298            // Reset search
4299            var results = [];
4300
4301            // Append options
4302            for (var i = 0; i < obj.items.length; i++) {
4303                // Item label
4304                var label = Text(i);
4305                // Item title
4306                var title = obj.items[i].data.title || '';
4307                // Group name
4308                var groupName = obj.items[i].data.group || '';
4309                // Synonym
4310                var synonym = obj.items[i].data.synonym || '';
4311                if (synonym) {
4312                    synonym = synonym.join(' ');
4313                }
4314
4315                if (str == null || obj.items[i].selected == true || label.match(str) || title.match(str) || groupName.match(str) || synonym.match(str)) {
4316                    results.push(obj.items[i]);
4317                }
4318            }
4319
4320            if (! results.length) {
4321                content.style.display = 'none';
4322
4323                // Results
4324                obj.results = null;
4325            } else {
4326                content.style.display = '';
4327
4328                // Results
4329                obj.results = results;
4330
4331                // Show 200 items at once
4332                var number = results.length || 0;
4333
4334                // Lazyloading
4335                if (obj.options.lazyLoading == true && number > 200) {
4336                    number = 200;
4337                }
4338
4339                for (var i = 0; i < number; i++) {
4340                    if (obj.results[i].group) {
4341                        if (! obj.results[i].group.parentNode) {
4342                            content.appendChild(obj.results[i].group);
4343                        }
4344                        obj.results[i].group.lastChild.appendChild(obj.results[i].element);
4345                    } else {
4346                        content.appendChild(obj.results[i].element);
4347                    }
4348                }
4349            }
4350        }
4351
4352        // Auto focus
4353        if (obj.options.autofocus == true) {
4354            obj.first();
4355        }
4356    }
4357
4358    obj.open = function() {
4359        // Focus
4360        if (! el.classList.contains('jdropdown-focus')) {
4361            // Current dropdown
4362            jSuites.dropdown.current = obj;
4363
4364            // Start tracking
4365            jSuites.tracking(obj, true);
4366
4367            // Add focus
4368            el.classList.add('jdropdown-focus');
4369
4370            // Animation
4371            if (jSuites.getWindowWidth() < 800) {
4372                if (obj.options.type == null || obj.options.type == 'picker') {
4373                    jSuites.animation.slideBottom(container, 1);
4374                }
4375            }
4376
4377            // Filter
4378            if (obj.options.autocomplete == true) {
4379                obj.header.value = obj.search;
4380                obj.header.focus();
4381            }
4382
4383            // Set cursor for the first or first selected element
4384            var k = getValue();
4385            if (k[0]) {
4386                var cursor = obj.getPosition(k[0]);
4387                if (cursor !== false) {
4388                    obj.setCursor(cursor);
4389                }
4390            }
4391
4392            // Container Size
4393            if (! obj.options.type || obj.options.type == 'default') {
4394                var rect = el.getBoundingClientRect();
4395                var rectContainer = container.getBoundingClientRect();
4396
4397                if (obj.options.position) {
4398                    container.style.position = 'fixed';
4399                    if (window.innerHeight < rect.bottom + rectContainer.height) {
4400                        container.style.top = '';
4401                        container.style.bottom = (window.innerHeight - rect.top ) + 1 + 'px';
4402                    } else {
4403                        container.style.top = rect.bottom + 'px';
4404                        container.style.bottom = '';
4405                    }
4406                    container.style.left = rect.left + 'px';
4407                } else {
4408                    if (window.innerHeight < rect.bottom + rectContainer.height) {
4409                        container.style.top = '';
4410                        container.style.bottom = rect.height + 1 + 'px';
4411                    } else {
4412                        container.style.top = '';
4413                        container.style.bottom = '';
4414                    }
4415                }
4416
4417                container.style.minWidth = rect.width + 'px';
4418
4419                if (obj.options.maxWidth) {
4420                    container.style.maxWidth = obj.options.maxWidth;
4421                }
4422
4423                if (! obj.items.length && obj.options.autocomplete == true) {
4424                    content.style.display = 'none';
4425                } else {
4426                    content.style.display = '';
4427                }
4428            }
4429        }
4430
4431        // Events
4432        if (typeof(obj.options.onopen) == 'function') {
4433            obj.options.onopen(el);
4434        }
4435    }
4436
4437    obj.close = function(ignoreEvents) {
4438        if (el.classList.contains('jdropdown-focus')) {
4439            // Update labels
4440            obj.header.value = obj.getText();
4441            // Remove cursor
4442            obj.setCursor();
4443            // Events
4444            if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
4445                obj.options.onclose(el);
4446            }
4447            // Blur
4448            if (obj.header.blur) {
4449                obj.header.blur();
4450            }
4451            // Remove focus
4452            el.classList.remove('jdropdown-focus');
4453            // Start tracking
4454            jSuites.tracking(obj, false);
4455            // Current dropdown
4456            jSuites.dropdown.current = null;
4457        }
4458
4459        return obj.getValue();
4460    }
4461
4462    /**
4463     * Set cursor
4464     */
4465    obj.setCursor = function(index, setPosition) {
4466        // Remove current cursor
4467        if (obj.currentIndex != null) {
4468            // Remove visual cursor
4469            if (obj.items && obj.items[obj.currentIndex]) {
4470                obj.items[obj.currentIndex].element.classList.remove('jdropdown-cursor');
4471            }
4472        }
4473
4474        if (index == undefined) {
4475            obj.currentIndex = null;
4476        } else {
4477            index = parseInt(index);
4478
4479            // Cursor only for visible items
4480            if (obj.items[index].element.parentNode) {
4481                obj.items[index].element.classList.add('jdropdown-cursor');
4482                obj.currentIndex = index;
4483
4484                // Update scroll to the cursor element
4485                if (setPosition !== false && obj.items[obj.currentIndex].element) {
4486                    var container = content.scrollTop;
4487                    var element = obj.items[obj.currentIndex].element;
4488                    content.scrollTop = element.offsetTop - element.scrollTop + element.clientTop - 95;
4489                }
4490            }
4491        }
4492    }
4493
4494    // Compatibility
4495    obj.resetCursor = obj.setCursor;
4496    obj.updateCursor = obj.setCursor;
4497
4498    /**
4499     * Reset cursor and selected items
4500     */
4501    obj.reset = function() {
4502        // Reset cursor
4503        obj.setCursor();
4504
4505        // Reset selected
4506        obj.setValue(null);
4507    }
4508
4509    /**
4510     * First available item
4511     */
4512    obj.first = function() {
4513        if (obj.options.lazyLoading === true) {
4514            obj.loadFirst();
4515        }
4516
4517        var items = content.querySelectorAll('.jdropdown-item');
4518        if (items.length) {
4519            var newIndex = items[0].indexValue;
4520            obj.setCursor(newIndex);
4521        }
4522    }
4523
4524    /**
4525     * Last available item 
4526     */
4527    obj.last = function() {
4528        if (obj.options.lazyLoading === true) {
4529            obj.loadLast();
4530        }
4531
4532        var items = content.querySelectorAll('.jdropdown-item');
4533        if (items.length) {
4534            var newIndex = items[items.length-1].indexValue;
4535            obj.setCursor(newIndex);
4536        }
4537    }
4538
4539    obj.next = function(letter) {
4540        var newIndex = null;
4541
4542        if (letter) {
4543            if (letter.length == 1) {
4544                // Current index
4545                var current = obj.currentIndex || -1;
4546                // Letter
4547                letter = letter.toLowerCase();
4548
4549                var e = null;
4550                var l = null;
4551                var items = content.querySelectorAll('.jdropdown-item');
4552                if (items.length) {
4553                    for (var i = 0; i < items.length; i++) {
4554                        if (items[i].indexValue > current) {
4555                            if (e = obj.items[items[i].indexValue]) {
4556                                if (l = e.element.innerText[0]) {
4557                                    l = l.toLowerCase();
4558                                    if (letter == l) {
4559                                        newIndex = items[i].indexValue;
4560                                        break;
4561                                    }
4562                                }
4563                            }
4564                        }
4565                    }
4566                    obj.setCursor(newIndex);
4567                }
4568            }
4569        } else {
4570            if (obj.currentIndex == undefined || obj.currentIndex == null) {
4571                obj.first();
4572            } else {
4573                var element = obj.items[obj.currentIndex].element;
4574
4575                var next = element.nextElementSibling;
4576                if (next) {
4577                    if (next.classList.contains('jdropdown-group')) {
4578                        next = next.lastChild.firstChild;
4579                    }
4580                    newIndex = next.indexValue;
4581                } else {
4582                    if (element.parentNode.classList.contains('jdropdown-group-items')) {
4583                        if (next = element.parentNode.parentNode.nextElementSibling) {
4584                            if (next.classList.contains('jdropdown-group')) {
4585                                next = next.lastChild.firstChild;
4586                            } else if (next.classList.contains('jdropdown-item')) {
4587                                newIndex = next.indexValue;
4588                            } else {
4589                                next = null;
4590                            }
4591                        }
4592
4593                        if (next) {
4594                            newIndex = next.indexValue;
4595                        }
4596                    }
4597                }
4598
4599                if (newIndex !== null) {
4600                    obj.setCursor(newIndex);
4601                }
4602            }
4603        }
4604    }
4605
4606    obj.prev = function() {
4607        var newIndex = null;
4608
4609        if (obj.currentIndex === null) {
4610            obj.first();
4611        } else {
4612            var element = obj.items[obj.currentIndex].element;
4613
4614            var prev = element.previousElementSibling;
4615            if (prev) {
4616                if (prev.classList.contains('jdropdown-group')) {
4617                    prev = prev.lastChild.lastChild;
4618                }
4619                newIndex = prev.indexValue;
4620            } else {
4621                if (element.parentNode.classList.contains('jdropdown-group-items')) {
4622                    if (prev = element.parentNode.parentNode.previousElementSibling) {
4623                        if (prev.classList.contains('jdropdown-group')) {
4624                            prev = prev.lastChild.lastChild;
4625                        } else if (prev.classList.contains('jdropdown-item')) {
4626                            newIndex = prev.indexValue;
4627                        } else {
4628                            prev = null
4629                        }
4630                    }
4631
4632                    if (prev) {
4633                        newIndex = prev.indexValue;
4634                    }
4635                }
4636            }
4637        }
4638
4639        if (newIndex !== null) {
4640            obj.setCursor(newIndex);
4641        }
4642    }
4643
4644    obj.loadFirst = function() {
4645        // Search
4646        if (obj.results) {
4647            var results = obj.results;
4648        } else {
4649            var results = obj.items;
4650        }
4651
4652        // Show 200 items at once
4653        var number = results.length || 0;
4654
4655        // Lazyloading
4656        if (obj.options.lazyLoading == true && number > 200) {
4657            number = 200;
4658        }
4659
4660        // Reset container
4661        content.innerHTML = '';
4662
4663        // First 200 items
4664        for (var i = 0; i < number; i++) {
4665            if (results[i].group) {
4666                if (! results[i].group.parentNode) {
4667                    content.appendChild(results[i].group);
4668                }
4669                results[i].group.lastChild.appendChild(results[i].element);
4670            } else {
4671                content.appendChild(results[i].element);
4672            }
4673        }
4674
4675        // Scroll go to the begin
4676        content.scrollTop = 0;
4677    }
4678
4679    obj.loadLast = function() {
4680        // Search
4681        if (obj.results) {
4682            var results = obj.results;
4683        } else {
4684            var results = obj.items;
4685        }
4686
4687        // Show first page
4688        var number = results.length;
4689
4690        // Max 200 items
4691        if (number > 200) {
4692            number = number - 200;
4693
4694            // Reset container
4695            content.innerHTML = '';
4696
4697            // First 200 items
4698            for (var i = number; i < results.length; i++) {
4699                if (results[i].group) {
4700                    if (! results[i].group.parentNode) {
4701                        content.appendChild(results[i].group);
4702                    }
4703                    results[i].group.lastChild.appendChild(results[i].element);
4704                } else {
4705                    content.appendChild(results[i].element);
4706                }
4707            }
4708
4709            // Scroll go to the begin
4710            content.scrollTop = content.scrollHeight;
4711        }
4712    }
4713
4714    obj.loadUp = function() {
4715        var test = false;
4716
4717        // Search
4718        if (obj.results) {
4719            var results = obj.results;
4720        } else {
4721            var results = obj.items;
4722        }
4723
4724        var items = content.querySelectorAll('.jdropdown-item');
4725        var fistItem = items[0].indexValue;
4726        fistItem = obj.items[fistItem];
4727        var index = results.indexOf(fistItem) - 1;
4728
4729        if (index > 0) {
4730            var number = 0;
4731
4732            while (index > 0 && results[index] && number < 200) {
4733                if (results[index].group) {
4734                    if (! results[index].group.parentNode) {
4735                        content.insertBefore(results[index].group, content.firstChild);
4736                    }
4737                    results[index].group.lastChild.insertBefore(results[index].element, results[index].group.lastChild.firstChild);
4738                } else {
4739                    content.insertBefore(results[index].element, content.firstChild);
4740                }
4741
4742                index--;
4743                number++;
4744            }
4745
4746            // New item added
4747            test = true;
4748        }
4749
4750        return test;
4751    }
4752
4753    obj.loadDown = function() {
4754        var test = false;
4755
4756        // Search
4757        if (obj.results) {
4758            var results = obj.results;
4759        } else {
4760            var results = obj.items;
4761        }
4762
4763        var items = content.querySelectorAll('.jdropdown-item');
4764        var lastItem = items[items.length-1].indexValue;
4765        lastItem = obj.items[lastItem];
4766        var index = results.indexOf(lastItem) + 1;
4767
4768        if (index < results.length) {
4769            var number = 0;
4770            while (index < results.length && results[index] && number < 200) {
4771                if (results[index].group) {
4772                    if (! results[index].group.parentNode) {
4773                        content.appendChild(results[index].group);
4774                    }
4775                    results[index].group.lastChild.appendChild(results[index].element);
4776                } else {
4777                    content.appendChild(results[index].element);
4778                }
4779
4780                index++;
4781                number++;
4782            }
4783
4784            // New item added
4785            test = true;
4786        }
4787
4788        return test;
4789    }
4790
4791    init();
4792
4793    return obj;
4794});
4795
4796jSuites.dropdown.keydown = function(e) {
4797    var dropdown = null;
4798    if (dropdown = jSuites.dropdown.current) {
4799        if (e.which == 13 || e.which == 9) {  // enter or tab
4800            if (dropdown.header.value && dropdown.currentIndex == null && dropdown.options.newOptions) {
4801                // if they typed something in, but it matched nothing, and newOptions are allowed, start that flow
4802                dropdown.add();
4803            } else {
4804                // Quick Select/Filter
4805                if (dropdown.currentIndex == null && dropdown.options.autocomplete == true && dropdown.header.value != "") {
4806                    dropdown.find(dropdown.header.value);
4807                }
4808                dropdown.selectIndex(dropdown.currentIndex);
4809            }
4810        } else if (e.which == 38) {  // up arrow
4811            if (dropdown.currentIndex == null) {
4812                dropdown.first();
4813            } else if (dropdown.currentIndex > 0) {
4814                dropdown.prev();
4815            }
4816            e.preventDefault();
4817        } else if (e.which == 40) {  // down arrow
4818            if (dropdown.currentIndex == null) {
4819                dropdown.first();
4820            } else if (dropdown.currentIndex + 1 < dropdown.items.length) {
4821                dropdown.next();
4822            }
4823            e.preventDefault();
4824        } else if (e.which == 36) {
4825            dropdown.first();
4826            if (! e.target.classList.contains('jdropdown-header')) {
4827                e.preventDefault();
4828            }
4829        } else if (e.which == 35) {
4830            dropdown.last();
4831            if (! e.target.classList.contains('jdropdown-header')) {
4832                e.preventDefault();
4833            }
4834        } else if (e.which == 27) {
4835            dropdown.close();
4836        } else if (e.which == 33) {  // page up
4837            if (dropdown.currentIndex == null) {
4838                dropdown.first();
4839            } else if (dropdown.currentIndex > 0) {
4840                for (var i = 0; i < 7; i++) {
4841                    dropdown.prev()
4842                }
4843            }
4844            e.preventDefault();
4845        } else if (e.which == 34) {  // page down
4846            if (dropdown.currentIndex == null) {
4847                dropdown.first();
4848            } else if (dropdown.currentIndex + 1 < dropdown.items.length) {
4849                for (var i = 0; i < 7; i++) {
4850                    dropdown.next()
4851                }
4852            }
4853            e.preventDefault();
4854        }
4855    }
4856}
4857
4858jSuites.dropdown.mouseup = function(e) {
4859    var element = jSuites.findElement(e.target, 'jdropdown');
4860    if (element) {
4861        var dropdown = element.dropdown;
4862        if (e.target.classList.contains('jdropdown-header')) {
4863            if (element.classList.contains('jdropdown-focus') && element.classList.contains('jdropdown-default')) {
4864                var rect = element.getBoundingClientRect();
4865
4866                if (e.changedTouches && e.changedTouches[0]) {
4867                    var x = e.changedTouches[0].clientX;
4868                    var y = e.changedTouches[0].clientY;
4869                } else {
4870                    var x = e.clientX;
4871                    var y = e.clientY;
4872                }
4873
4874                if (rect.width - (x - rect.left) < 30) {
4875                    if (e.target.classList.contains('jdropdown-add')) {
4876                        dropdown.add();
4877                    } else {
4878                        dropdown.close();
4879                    }
4880                } else {
4881                    if (dropdown.options.autocomplete == false) {
4882                        dropdown.close();
4883                    }
4884                }
4885            } else {
4886                dropdown.open();
4887            }
4888        } else if (e.target.classList.contains('jdropdown-group-name')) {
4889            var items = e.target.nextSibling.children;
4890            if (e.target.nextSibling.style.display != 'none') {
4891                for (var i = 0; i < items.length; i++) {
4892                    if (items[i].style.display != 'none') {
4893                        dropdown.selectItem(items[i]);
4894                    }
4895                }
4896            }
4897        } else if (e.target.classList.contains('jdropdown-group-arrow')) {
4898            if (e.target.classList.contains('jdropdown-group-arrow-down')) {
4899                e.target.classList.remove('jdropdown-group-arrow-down');
4900                e.target.classList.add('jdropdown-group-arrow-up');
4901                e.target.parentNode.nextSibling.style.display = 'none';
4902            } else {
4903                e.target.classList.remove('jdropdown-group-arrow-up');
4904                e.target.classList.add('jdropdown-group-arrow-down');
4905                e.target.parentNode.nextSibling.style.display = '';
4906            }
4907        } else if (e.target.classList.contains('jdropdown-item')) {
4908            dropdown.selectItem(e.target);
4909        } else if (e.target.classList.contains('jdropdown-image')) {
4910            dropdown.selectItem(e.target.parentNode);
4911        } else if (e.target.classList.contains('jdropdown-description')) {
4912            dropdown.selectItem(e.target.parentNode);
4913        } else if (e.target.classList.contains('jdropdown-title')) {
4914            dropdown.selectItem(e.target.parentNode.parentNode);
4915        } else if (e.target.classList.contains('jdropdown-close') || e.target.classList.contains('jdropdown-backdrop')) {
4916            dropdown.close();
4917        }
4918    }
4919}
4920
4921jSuites.dropdown.extractFromDom = function(el, options) {
4922    // Keep reference
4923    var select = el;
4924    if (! options) {
4925        options = {};
4926    }
4927    // Prepare configuration
4928    if (el.getAttribute('multiple') && (! options || options.multiple == undefined)) {
4929        options.multiple = true;
4930    }
4931    if (el.getAttribute('placeholder') && (! options || options.placeholder == undefined)) {
4932        options.placeholder = el.getAttribute('placeholder');
4933    }
4934    if (el.getAttribute('data-autocomplete') && (! options || options.autocomplete == undefined)) {
4935        options.autocomplete = true;
4936    }
4937    if (! options || options.width == undefined) {
4938        options.width = el.offsetWidth;
4939    }
4940    if (el.value && (! options || options.value == undefined)) {
4941        options.value = el.value;
4942    }
4943    if (! options || options.data == undefined) {
4944        options.data = [];
4945        for (var j = 0; j < el.children.length; j++) {
4946            if (el.children[j].tagName == 'OPTGROUP') {
4947                for (var i = 0; i < el.children[j].children.length; i++) {
4948                    options.data.push({
4949                        value: el.children[j].children[i].value,
4950                        text: el.children[j].children[i].innerHTML,
4951                        group: el.children[j].getAttribute('label'),
4952                    });
4953                }
4954            } else {
4955                options.data.push({
4956                    value: el.children[j].value,
4957                    text: el.children[j].innerHTML,
4958                });
4959            }
4960        }
4961    }
4962    if (! options || options.onchange == undefined) {
4963        options.onchange = function(a,b,c,d) {
4964            if (options.multiple == true) {
4965                if (obj.items[b].classList.contains('jdropdown-selected')) {
4966                    select.options[b].setAttribute('selected', 'selected');
4967                } else {
4968                    select.options[b].removeAttribute('selected');
4969                }
4970            } else {
4971                select.value = d;
4972            }
4973        }
4974    }
4975    // Create DIV
4976    var div = document.createElement('div');
4977    el.parentNode.insertBefore(div, el);
4978    el.style.display = 'none';
4979    el = div;
4980
4981    return { el:el, options:options };
4982}
4983
4984jSuites.editor = (function(el, options) {
4985    // New instance
4986    var obj = { type:'editor' };
4987    obj.options = {};
4988
4989    // Default configuration
4990    var defaults = {
4991        // Load data from a remove location
4992        url: null,
4993        // Initial HTML content
4994        value: '',
4995        // Initial snippet
4996        snippet: null,
4997        // Add toolbar
4998        toolbar: true,
4999        toolbarOnTop: false,
5000        // Website parser is to read websites and images from cross domain
5001        remoteParser: null,
5002        // Placeholder
5003        placeholder: null,
5004        // Parse URL
5005        filterPaste: true,
5006        // Accept drop files
5007        dropZone: true,
5008        dropAsSnippet: false,
5009        acceptImages: true,
5010        acceptFiles: false,
5011        maxFileSize: 5000000,
5012        allowImageResize: true,
5013        // Style
5014        maxHeight: null,
5015        height: null,
5016        focus: false,
5017        // Events
5018        onclick: null,
5019        onfocus: null,
5020        onblur: null,
5021        onload: null,
5022        onkeyup: null,
5023        onkeydown: null,
5024        onchange: null,
5025        extensions: null,
5026        type: null,
5027    };
5028
5029    // Loop through our object
5030    for (var property in defaults) {
5031        if (options && options.hasOwnProperty(property)) {
5032            obj.options[property] = options[property];
5033        } else {
5034            obj.options[property] = defaults[property];
5035        }
5036    }
5037
5038    // Private controllers
5039    var editorTimer = null;
5040    var editorAction = null;
5041    var files = [];
5042
5043    // Keep the reference for the container
5044    obj.el = el;
5045
5046    if (typeof(obj.options.onclick) == 'function') {
5047        el.onclick = function(e) {
5048            obj.options.onclick(el, obj, e);
5049        }
5050    }
5051
5052    // Prepare container
5053    el.classList.add('jeditor-container');
5054
5055    // Snippet
5056    var snippet = document.createElement('div');
5057    snippet.className = 'jsnippet';
5058    snippet.setAttribute('contenteditable', false);
5059
5060    // Toolbar
5061    var toolbar = document.createElement('div');
5062    toolbar.className = 'jeditor-toolbar';
5063
5064    obj.editor = document.createElement('div');
5065    obj.editor.setAttribute('contenteditable', true);
5066    obj.editor.setAttribute('spellcheck', false);
5067    obj.editor.classList.add('jeditor');
5068
5069    // Placeholder
5070    if (obj.options.placeholder) {
5071        obj.editor.setAttribute('data-placeholder', obj.options.placeholder);
5072    }
5073
5074    // Max height
5075    if (obj.options.maxHeight || obj.options.height) {
5076        obj.editor.style.overflowY = 'auto';
5077
5078        if (obj.options.maxHeight) {
5079            obj.editor.style.maxHeight = obj.options.maxHeight;
5080        }
5081        if (obj.options.height) {
5082            obj.editor.style.height = obj.options.height;
5083        }
5084    }
5085
5086    // Set editor initial value
5087    if (obj.options.url) {
5088        jSuites.ajax({
5089            url: obj.options.url,
5090            dataType: 'html',
5091            success: function(result) {
5092                obj.editor.innerHTML = result;
5093
5094                jSuites.editor.setCursor(obj.editor, obj.options.focus == 'initial' ? true : false);
5095            }
5096        })
5097    } else {
5098        if (obj.options.value) {
5099            obj.editor.innerHTML = obj.options.value;
5100        } else {
5101            // Create from existing elements
5102            for (var i = 0; i < el.children.length; i++) {
5103                obj.editor.appendChild(el.children[i]);
5104            }
5105        }
5106    }
5107
5108    // Make sure element is empty
5109    el.innerHTML = '';
5110
5111    /**
5112     * Onchange event controllers
5113     */
5114    var change = function(e) {
5115        if (typeof(obj.options.onchange) == 'function') {
5116            obj.options.onchange(el, obj, e);
5117        }
5118
5119        // Update value
5120        obj.options.value = obj.getData();
5121
5122        // Lemonade JS
5123        if (el.value != obj.options.value) {
5124            el.value = obj.options.value;
5125            if (typeof(el.oninput) == 'function') {
5126                el.oninput({
5127                    type: 'input',
5128                    target: el,
5129                    value: el.value
5130                });
5131            }
5132        }
5133    }
5134
5135    /**
5136     * Extract images from a HTML string
5137     */
5138    var extractImageFromHtml = function(html) {
5139        // Create temp element
5140        var div = document.createElement('div');
5141        div.innerHTML = html;
5142
5143        // Extract images
5144        var img = div.querySelectorAll('img');
5145
5146        if (img.length) {
5147            for (var i = 0; i < img.length; i++) {
5148                obj.addImage(img[i].src);
5149            }
5150        }
5151    }
5152
5153    /**
5154     * Insert node at caret
5155     */
5156    var insertNodeAtCaret = function(newNode) {
5157        var sel, range;
5158
5159        if (window.getSelection) {
5160            sel = window.getSelection();
5161            if (sel.rangeCount) {
5162                range = sel.getRangeAt(0);
5163                var selectedText = range.toString();
5164                range.deleteContents();
5165                range.insertNode(newNode);
5166                // move the cursor after element
5167                range.setStartAfter(newNode);
5168                range.setEndAfter(newNode);
5169                sel.removeAllRanges();
5170                sel.addRange(range);
5171            }
5172        }
5173    }
5174
5175    var updateTotalImages = function() {
5176        var o = null;
5177        if (o = snippet.children[0]) {
5178            // Make sure is a grid
5179            if (! o.classList.contains('jslider-grid')) {
5180                o.classList.add('jslider-grid');
5181            }
5182            // Quantify of images
5183            var number = o.children.length;
5184            // Set the configuration of the grid
5185            o.setAttribute('data-number', number > 4 ? 4 : number);
5186            // Total of images inside the grid
5187            if (number > 4) {
5188                o.setAttribute('data-total', number - 4);
5189            } else {
5190                o.removeAttribute('data-total');
5191            }
5192        }
5193    }
5194
5195    /**
5196     * Append image to the snippet
5197     */
5198    var appendImage = function(image) {
5199        if (! snippet.innerHTML) {
5200            obj.appendSnippet({});
5201        }
5202        snippet.children[0].appendChild(image);
5203        updateTotalImages();
5204    }
5205
5206    /**
5207     * Append snippet
5208     * @Param object data
5209     */
5210    obj.appendSnippet = function(data) {
5211        // Reset snippet
5212        snippet.innerHTML = '';
5213
5214        // Attributes
5215        var a = [ 'image', 'title', 'description', 'host', 'url' ];
5216
5217        for (var i = 0; i < a.length; i++) {
5218            var div = document.createElement('div');
5219            div.className = 'jsnippet-' + a[i];
5220            div.setAttribute('data-k', a[i]);
5221            snippet.appendChild(div);
5222            if (data[a[i]]) {
5223                if (a[i] == 'image') {
5224                    if (! Array.isArray(data.image)) {
5225                        data.image = [ data.image ];
5226                    }
5227                    for (var j = 0; j < data.image.length; j++) {
5228                        var img = document.createElement('img');
5229                        img.src = data.image[j];
5230                        div.appendChild(img);
5231                    }
5232                } else {
5233                    div.innerHTML = data[a[i]];
5234                }
5235            }
5236        }
5237
5238        obj.editor.appendChild(document.createElement('br'));
5239        obj.editor.appendChild(snippet);
5240    }
5241
5242    /**
5243     * Set editor value
5244     */
5245    obj.setData = function(o) {
5246        if (typeof(o) == 'object') {
5247            obj.editor.innerHTML = o.content;
5248        } else {
5249            obj.editor.innerHTML = o;
5250        }
5251
5252        if (obj.options.focus) {
5253            jSuites.editor.setCursor(obj.editor, true);
5254        }
5255
5256        // Reset files container
5257        files = [];
5258    }
5259
5260    obj.getFiles = function() {
5261        var f = obj.editor.querySelectorAll('.jfile');
5262        var d = [];
5263        for (var i = 0; i < f.length; i++) {
5264            if (files[f[i].src]) {
5265                d.push(files[f[i].src]);
5266            }
5267        }
5268        return d;
5269    }
5270
5271    obj.getText = function() {
5272        return obj.editor.innerText;
5273    }
5274
5275    /**
5276     * Get editor data
5277     */
5278    obj.getData = function(json) {
5279        if (! json) {
5280            var data = obj.editor.innerHTML;
5281        } else {
5282            var data = {
5283                content : '',
5284            }
5285
5286            // Get snippet
5287            if (snippet.innerHTML) {
5288                var index = 0;
5289                data.snippet = {};
5290                for (var i = 0; i < snippet.children.length; i++) {
5291                    // Get key from element
5292                    var key = snippet.children[i].getAttribute('data-k');
5293                    if (key) {
5294                        if (key == 'image') {
5295                            if (! data.snippet.image) {
5296                                data.snippet.image = [];
5297                            }
5298                            // Get all images
5299                            for (var j = 0; j < snippet.children[i].children.length; j++) {
5300                                data.snippet.image.push(snippet.children[i].children[j].getAttribute('src'))
5301                            }
5302                        } else {
5303                            data.snippet[key] = snippet.children[i].innerHTML;
5304                        }
5305                    }
5306                }
5307            }
5308
5309            // Get files
5310            var f = Object.keys(files);
5311            if (f.length) {
5312                data.files = [];
5313                for (var i = 0; i < f.length; i++) {
5314                    data.files.push(files[f[i]]);
5315                }
5316            }
5317
5318            // Get content
5319            var d = document.createElement('div');
5320            d.innerHTML = obj.editor.innerHTML;
5321            var s = d.querySelector('.jsnippet');
5322            if (s) {
5323                s.remove();
5324            }
5325
5326            var text = d.innerHTML;
5327            text = text.replace(/<br>/g, "\n");
5328            text = text.replace(/<\/div>/g, "<\/div>\n");
5329            text = text.replace(/<(?:.|\n)*?>/gm, "");
5330            data.content = text.trim();
5331
5332            // Process extensions
5333            processExtensions('getData', data);
5334        }
5335
5336        return data;
5337    }
5338
5339    // Reset
5340    obj.reset = function() {
5341        obj.editor.innerHTML = '';
5342        snippet.innerHTML = '';
5343        files = [];
5344    }
5345
5346    obj.addPdf = function(data) {
5347        if (data.result.substr(0,4) != 'data') {
5348            console.error('Invalid source');
5349        } else {
5350            var canvas = document.createElement('canvas');
5351            canvas.width = 60;
5352            canvas.height = 60;
5353
5354            var img = new Image();
5355            var ctx = canvas.getContext('2d');
5356            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
5357
5358            canvas.toBlob(function(blob) {
5359                var newImage = document.createElement('img');
5360                newImage.src = window.URL.createObjectURL(blob);
5361                newImage.title = data.name;
5362                newImage.className = 'jfile pdf';
5363
5364                files[newImage.src] = {
5365                    file: newImage.src,
5366                    extension: 'pdf',
5367                    content: data.result,
5368                }
5369
5370                //insertNodeAtCaret(newImage);
5371                document.execCommand('insertHtml', false, newImage.outerHTML);
5372            });
5373        }
5374    }
5375
5376    obj.addImage = function(src, asSnippet) {
5377        if (! src) {
5378            src = '';
5379        }
5380
5381        if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) {
5382            console.error('remoteParser not defined in your initialization');
5383        } else {
5384            // This is to process cross domain images
5385            if (src.substr(0,4) == 'data') {
5386                var extension = src.split(';')
5387                extension = extension[0].split('/');
5388                extension = extension[1];
5389            } else {
5390                var extension = src.substr(src.lastIndexOf('.') + 1);
5391                // Work for cross browsers
5392                src = obj.options.remoteParser + src;
5393            }
5394
5395            var img = new Image();
5396
5397            img.onload = function onload() {
5398                var canvas = document.createElement('canvas');
5399                canvas.width = img.width;
5400                canvas.height = img.height;
5401
5402                var ctx = canvas.getContext('2d');
5403                ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
5404
5405                canvas.toBlob(function(blob) {
5406                    var newImage = document.createElement('img');
5407                    newImage.src = window.URL.createObjectURL(blob);
5408                    newImage.classList.add('jfile');
5409                    newImage.setAttribute('tabindex', '900');
5410                    newImage.setAttribute('width', img.width);
5411                    newImage.setAttribute('height', img.height);
5412                    files[newImage.src] = {
5413                        file: newImage.src,
5414                        extension: extension,
5415                        content: canvas.toDataURL(),
5416                    }
5417
5418                    if (obj.options.dropAsSnippet || asSnippet) {
5419                        appendImage(newImage);
5420                        // Just to understand the attachment is part of a snippet
5421                        files[newImage.src].snippet = true;
5422                    } else {
5423                        //insertNodeAtCaret(newImage);
5424                        document.execCommand('insertHtml', false, newImage.outerHTML);
5425                    }
5426
5427                    change();
5428                });
5429            };
5430
5431            img.src = src;
5432        }
5433    }
5434
5435    obj.addFile = function(files) {
5436        var reader = [];
5437
5438        for (var i = 0; i < files.length; i++) {
5439            if (files[i].size > obj.options.maxFileSize) {
5440                alert('The file is too big');
5441            } else {
5442                // Only PDF or Images
5443                var type = files[i].type.split('/');
5444
5445                if (type[0] == 'image') {
5446                    type = 1;
5447                } else if (type[1] == 'pdf') {
5448                    type = 2;
5449                } else {
5450                    type = 0;
5451                }
5452
5453                if (type) {
5454                    // Create file
5455                    reader[i] = new FileReader();
5456                    reader[i].index = i;
5457                    reader[i].type = type;
5458                    reader[i].name = files[i].name;
5459                    reader[i].date = files[i].lastModified;
5460                    reader[i].size = files[i].size;
5461                    reader[i].addEventListener("load", function (data) {
5462                        // Get result
5463                        if (data.target.type == 2) {
5464                            if (obj.options.acceptFiles == true) {
5465                                obj.addPdf(data.target);
5466                            }
5467                        } else {
5468                            obj.addImage(data.target.result);
5469                        }
5470                    }, false);
5471
5472                    reader[i].readAsDataURL(files[i])
5473                } else {
5474                    alert('The extension is not allowed');
5475                }
5476            }
5477        }
5478    }
5479
5480    // Destroy
5481    obj.destroy = function() {
5482        obj.editor.removeEventListener('mouseup', editorMouseUp);
5483        obj.editor.removeEventListener('mousedown', editorMouseDown);
5484        obj.editor.removeEventListener('mousemove', editorMouseMove);
5485        obj.editor.removeEventListener('keyup', editorKeyUp);
5486        obj.editor.removeEventListener('keydown', editorKeyDown);
5487        obj.editor.removeEventListener('dragstart', editorDragStart);
5488        obj.editor.removeEventListener('dragenter', editorDragEnter);
5489        obj.editor.removeEventListener('dragover', editorDragOver);
5490        obj.editor.removeEventListener('drop', editorDrop);
5491        obj.editor.removeEventListener('paste', editorPaste);
5492        obj.editor.removeEventListener('blur', editorBlur);
5493        obj.editor.removeEventListener('focus', editorFocus);
5494
5495        el.editor = null;
5496        el.classList.remove('jeditor-container');
5497
5498        toolbar.remove();
5499        snippet.remove();
5500        obj.editor.remove();
5501    }
5502
5503    obj.upload = function() {
5504        jSuites.click(obj.file);
5505    }
5506
5507    // Elements to be removed
5508    var remove = [
5509        HTMLUnknownElement,
5510        HTMLAudioElement,
5511        HTMLEmbedElement,
5512        HTMLIFrameElement,
5513        HTMLTextAreaElement,
5514        HTMLInputElement,
5515        HTMLScriptElement
5516    ];
5517
5518    // Valid properties
5519    var validProperty = ['width', 'height', 'align', 'border', 'src', 'tabindex'];
5520
5521    // Valid CSS attributes
5522    var validStyle = ['color', 'font-weight', 'font-size', 'background', 'background-color', 'margin'];
5523
5524    var parse = function(element) {
5525       // Remove attributes
5526       if (element.attributes && element.attributes.length) {
5527           var image = null;
5528           var style = null;
5529           // Process style attribute
5530           var elementStyle = element.getAttribute('style');
5531           if (elementStyle) {
5532               style = [];
5533               var t = elementStyle.split(';');
5534               for (var j = 0; j < t.length; j++) {
5535                   var v = t[j].trim().split(':');
5536                   if (validStyle.indexOf(v[0].trim()) >= 0) {
5537                       var k = v.shift();
5538                       var v = v.join(':');
5539                       style.push(k + ':' + v);
5540                   }
5541               }
5542           }
5543           // Process image
5544           if (element.tagName.toUpperCase() == 'IMG') {
5545               if (! obj.options.acceptImages || ! element.src) {
5546                   element.parentNode.removeChild(element);
5547               } else {
5548                   // Check if is data
5549                   element.setAttribute('tabindex', '900');
5550                   // Check attributes for persistance
5551                   obj.addImage(element.src);
5552               }
5553           }
5554           // Remove attributes
5555           var attr = [];
5556           var numAttributes = element.attributes.length - 1;
5557           if (numAttributes > 0) {
5558               for (var i = numAttributes; i >= 0 ; i--) {
5559                   attr.push(element.attributes[i].name);
5560               }
5561               attr.forEach(function(v) {
5562                   if (validProperty.indexOf(v) == -1) {
5563                       element.removeAttribute(v);
5564                   }
5565               });
5566           }
5567           element.style = '';
5568           // Add valid style
5569           if (style && style.length) {
5570               element.setAttribute('style', style.join(';'));
5571           }
5572       }
5573       // Parse children
5574       if (element.children.length) {
5575           for (var i = 0; i < element.children.length; i++) {
5576               parse(element.children[i]);
5577           }
5578       }
5579
5580       if (remove.indexOf(element.constructor) >= 0) {
5581           element.remove();
5582       }
5583    }
5584
5585    var select = function(e) {
5586        var s = window.getSelection()
5587        var r = document.createRange();
5588        r.selectNode(e);
5589        s.addRange(r)
5590    }
5591
5592    var filter = function(data) {
5593        if (data) {
5594            data = data.replace(new RegExp('<!--(.*?)-->', 'gsi'), '');
5595        }
5596        var parser = new DOMParser();
5597        var d = parser.parseFromString(data, "text/html");
5598        parse(d);
5599        var span = document.createElement('span');
5600        span.innerHTML = d.firstChild.innerHTML;
5601        return span;
5602    }
5603
5604    var editorPaste = function(e) {
5605        if (obj.options.filterPaste == true) {
5606            if (e.clipboardData || e.originalEvent.clipboardData) {
5607                var html = (e.originalEvent || e).clipboardData.getData('text/html');
5608                var text = (e.originalEvent || e).clipboardData.getData('text/plain');
5609                var file = (e.originalEvent || e).clipboardData.files
5610            } else if (window.clipboardData) {
5611                var html = window.clipboardData.getData('Html');
5612                var text = window.clipboardData.getData('Text');
5613                var file = window.clipboardData.files
5614            }
5615
5616            if (file.length) {
5617                // Paste a image from the clipboard
5618                obj.addFile(file);
5619            } else {
5620                if (! html) {
5621                    html = text.split('\r\n');
5622                    if (! e.target.innerText) {
5623                        html.map(function(v) {
5624                            var d = document.createElement('div');
5625                            d.innerText = v;
5626                            obj.editor.appendChild(d);
5627                        });
5628                    } else {
5629                        html = html.map(function(v) {
5630                            return '<div>' + v + '</div>';
5631                        });
5632                        document.execCommand('insertHtml', false, html.join(''));
5633                    }
5634                } else {
5635                    var d = filter(html);
5636                    // Paste to the editor
vendor: 56,226 bytes, lines 5637-7384
5637                    //insertNodeAtCaret(d);
5638                    document.execCommand('insertHtml', false, d.innerHTML);
5639                }
5640            }
5641
5642            e.preventDefault();
5643        }
5644    }
5645
5646    var editorDragStart = function(e) {
5647        if (editorAction && editorAction.e) {
5648            e.preventDefault();
5649        }
5650    }
5651
5652    var editorDragEnter = function(e) {
5653        if (editorAction || obj.options.dropZone == false) {
5654            // Do nothing
5655        } else {
5656            el.classList.add('jeditor-dragging');
5657            e.preventDefault();
5658        }
5659    }
5660
5661    var editorDragOver = function(e) {
5662        if (editorAction || obj.options.dropZone == false) {
5663            // Do nothing
5664        } else {
5665            if (editorTimer) {
5666                clearTimeout(editorTimer);
5667            }
5668
5669            editorTimer = setTimeout(function() {
5670                el.classList.remove('jeditor-dragging');
5671            }, 100);
5672            e.preventDefault();
5673        }
5674    }
5675
5676    var editorDrop = function(e) {
5677        if (editorAction || obj.options.dropZone == false) {
5678            // Do nothing
5679        } else {
5680            // Position caret on the drop
5681            var range = null;
5682            if (document.caretRangeFromPoint) {
5683                range=document.caretRangeFromPoint(e.clientX, e.clientY);
5684            } else if (e.rangeParent) {
5685                range=document.createRange();
5686                range.setStart(e.rangeParent,e.rangeOffset);
5687            }
5688            var sel = window.getSelection();
5689            sel.removeAllRanges();
5690            sel.addRange(range);
5691            sel.anchorNode.parentNode.focus();
5692
5693            var html = (e.originalEvent || e).dataTransfer.getData('text/html');
5694            var text = (e.originalEvent || e).dataTransfer.getData('text/plain');
5695            var file = (e.originalEvent || e).dataTransfer.files;
5696
5697            if (file.length) {
5698                obj.addFile(file);
5699            } else if (text) {
5700                extractImageFromHtml(html);
5701            }
5702
5703            el.classList.remove('jeditor-dragging');
5704            e.preventDefault();
5705        }
5706    }
5707
5708    var editorBlur = function(e) {
5709        // Process extensions
5710        processExtensions('onevent', e);
5711        // Apply changes
5712        change(e);
5713        // Blur
5714        if (typeof(obj.options.onblur) == 'function') {
5715            obj.options.onblur(el, obj, e);
5716        }
5717    }
5718
5719    var editorFocus = function(e) {
5720        // Focus
5721        if (typeof(obj.options.onfocus) == 'function') {
5722            obj.options.onfocus(el, obj, e);
5723        }
5724    }
5725
5726    var editorKeyUp = function(e) {
5727        if (! obj.editor.innerHTML) {
5728            obj.editor.innerHTML = '<div><br></div>';
5729        }
5730        if (typeof(obj.options.onkeyup) == 'function') {
5731            obj.options.onkeyup(el, obj, e);
5732        }
5733    }
5734
5735    var editorKeyDown = function(e) {
5736        // Process extensions
5737        processExtensions('onevent', e);
5738
5739        if (e.key == 'Delete') {
5740            if (e.target.tagName == 'IMG') {
5741                var parent = e.target.parentNode;
5742                select(e.target);
5743                if (parent.classList.contains('jsnippet-image')) {
5744                    updateTotalImages();
5745                }
5746            }
5747        }
5748
5749        if (typeof(obj.options.onkeydown) == 'function') {
5750            obj.options.onkeydown(el, obj, e);
5751        }
5752    }
5753
5754    var editorMouseUp = function(e) {
5755        if (editorAction && editorAction.e) {
5756            editorAction.e.classList.remove('resizing');
5757
5758            if (editorAction.e.changed == true) {
5759                var image = editorAction.e.cloneNode()
5760                image.width = parseInt(editorAction.e.style.width) || editorAction.e.getAttribute('width');
5761                image.height = parseInt(editorAction.e.style.height) || editorAction.e.getAttribute('height');
5762                editorAction.e.style.width = '';
5763                editorAction.e.style.height = '';
5764                select(editorAction.e);
5765                document.execCommand('insertHtml', false, image.outerHTML);
5766            }
5767        }
5768
5769        editorAction = false;
5770    }
5771
5772    var editorMouseDown = function(e) {
5773        var close = function(snippet) {
5774            var rect = snippet.getBoundingClientRect();
5775            if (rect.width - (e.clientX - rect.left) < 40 && e.clientY - rect.top < 40) {
5776                snippet.innerHTML = '';
5777                snippet.remove();
5778            }
5779        }
5780
5781        if (e.target.tagName == 'IMG') {
5782            if (e.target.style.cursor) {
5783                var rect = e.target.getBoundingClientRect();
5784                editorAction = {
5785                    e: e.target,
5786                    x: e.clientX,
5787                    y: e.clientY,
5788                    w: rect.width,
5789                    h: rect.height,
5790                    d: e.target.style.cursor,
5791                }
5792
5793                if (! e.target.getAttribute('width')) {
5794                    e.target.setAttribute('width', rect.width)
5795                }
5796
5797                if (! e.target.getAttribute('height')) {
5798                    e.target.setAttribute('height', rect.height)
5799                }
5800
5801                var s = window.getSelection();
5802                if (s.rangeCount) {
5803                    for (var i = 0; i < s.rangeCount; i++) {
5804                        s.removeRange(s.getRangeAt(i));
5805                    }
5806                }
5807
5808                e.target.classList.add('resizing');
5809            } else {
5810                editorAction = true;
5811            }
5812        } else {
5813            if (e.target.classList.contains('jsnippet')) {
5814                close(e.target);
5815            } else if (e.target.parentNode.classList.contains('jsnippet')) {
5816                close(e.target.parentNode);
5817            }
5818
5819            editorAction = true;
5820        }
5821    }
5822
5823    var editorMouseMove = function(e) {
5824        if (e.target.tagName == 'IMG' && ! e.target.parentNode.classList.contains('jsnippet-image') && obj.options.allowImageResize == true) {
5825            if (e.target.getAttribute('tabindex')) {
5826                var rect = e.target.getBoundingClientRect();
5827                if (e.clientY - rect.top < 5) {
5828                    if (rect.width - (e.clientX - rect.left) < 5) {
5829                        e.target.style.cursor = 'ne-resize';
5830                    } else if (e.clientX - rect.left < 5) {
5831                        e.target.style.cursor = 'nw-resize';
5832                    } else {
5833                        e.target.style.cursor = 'n-resize';
5834                    }
5835                } else if (rect.height - (e.clientY - rect.top) < 5) {
5836                    if (rect.width - (e.clientX - rect.left) < 5) {
5837                        e.target.style.cursor = 'se-resize';
5838                    } else if (e.clientX - rect.left < 5) {
5839                        e.target.style.cursor = 'sw-resize';
5840                    } else {
5841                        e.target.style.cursor = 's-resize';
5842                    }
5843                } else if (rect.width - (e.clientX - rect.left) < 5) {
5844                    e.target.style.cursor = 'e-resize';
5845                } else if (e.clientX - rect.left < 5) {
5846                    e.target.style.cursor = 'w-resize';
5847                } else {
5848                    e.target.style.cursor = '';
5849                }
5850            }
5851        }
5852
5853        // Move
5854        if (e.which == 1 && editorAction && editorAction.d) {
5855            if (editorAction.d == 'e-resize' || editorAction.d == 'ne-resize' ||  editorAction.d == 'se-resize') {
5856                editorAction.e.style.width = (editorAction.w + (e.clientX - editorAction.x));
5857
5858                if (e.shiftKey) {
5859                    var newHeight = (e.clientX - editorAction.x) * (editorAction.h / editorAction.w);
5860                    editorAction.e.style.height = editorAction.h + newHeight;
5861                } else {
5862                    var newHeight =  null;
5863                }
5864            }
5865
5866            if (! newHeight) {
5867                if (editorAction.d == 's-resize' || editorAction.d == 'se-resize' || editorAction.d == 'sw-resize') {
5868                    if (! e.shiftKey) {
5869                        editorAction.e.style.height = editorAction.h + (e.clientY - editorAction.y);
5870                    }
5871                }
5872            }
5873
5874            editorAction.e.changed = true;
5875        }
5876    }
5877
5878    var processExtensions = function(method, data) {
5879        if (obj.options.extensions) {
5880            var ext = Object.keys(obj.options.extensions);
5881            if (ext.length) {
5882                for (var i = 0; i < ext.length; i++)
5883                    if (obj.options.extensions[ext[i]] && typeof(obj.options.extensions[ext[i]][method]) == 'function') {
5884                        obj.options.extensions[ext[i]][method].call(obj, data);
5885                    }
5886            }
5887        }
5888    }
5889
5890    var loadExtensions = function() {
5891        if (obj.options.extensions) {
5892            var ext = Object.keys(obj.options.extensions);
5893            if (ext.length) {
5894                for (var i = 0; i < ext.length; i++) {
5895                    if (obj.options.extensions[ext[i]] && typeof (obj.options.extensions[ext[i]]) == 'function') {
5896                        obj.options.extensions[ext[i]] = obj.options.extensions[ext[i]](el, obj);
5897                    }
5898                }
5899            }
5900        }
5901    }
5902
5903    document.addEventListener('mouseup', editorMouseUp);
5904    document.addEventListener('mousemove', editorMouseMove);
5905    obj.editor.addEventListener('mousedown', editorMouseDown);
5906    obj.editor.addEventListener('keyup', editorKeyUp);
5907    obj.editor.addEventListener('keydown', editorKeyDown);
5908    obj.editor.addEventListener('dragstart', editorDragStart);
5909    obj.editor.addEventListener('dragenter', editorDragEnter);
5910    obj.editor.addEventListener('dragover', editorDragOver);
5911    obj.editor.addEventListener('drop', editorDrop);
5912    obj.editor.addEventListener('paste', editorPaste);
5913    obj.editor.addEventListener('focus', editorFocus);
5914    obj.editor.addEventListener('blur', editorBlur);
5915
5916    // Append editor to the container
5917    el.appendChild(obj.editor);
5918    // Snippet
5919    if (obj.options.snippet) {
5920        obj.appendSnippet(obj.options.snippet);
5921    }
5922
5923    // Add toolbar
5924    if (obj.options.toolbar) {
5925        // Default toolbar configuration
5926        if (Array.isArray(obj.options.toolbar)) {
5927            var toolbarOptions = {
5928                container: true,
5929                responsive: true,
5930                items: obj.options.toolbar
5931            }
5932        } else if (obj.options.toolbar === true) {
5933            var toolbarOptions = {
5934                container: true,
5935                responsive: true,
5936                items: [],
5937            }
5938        } else {
5939            var toolbarOptions = obj.options.toolbar;
5940        }
5941
5942        // Default items
5943        if (! (toolbarOptions.items && toolbarOptions.items.length)) {
5944            toolbarOptions.items = jSuites.editor.getDefaultToolbar(obj);
5945        }
5946
5947        if (obj.options.toolbarOnTop) {
5948            // Add class
5949            el.classList.add('toolbar-on-top');
5950            // Append to the DOM
5951            el.insertBefore(toolbar, el.firstChild);
5952        } else {
5953            // Add padding to the editor
5954            obj.editor.style.padding = '15px';
5955            // Append to the DOM
5956            el.appendChild(toolbar);
5957        }
5958
5959        // Create toolbar
5960        jSuites.toolbar(toolbar, toolbarOptions);
5961
5962        toolbar.addEventListener('click', function() {
5963            obj.editor.focus();
5964        })
5965    }
5966
5967    // Upload file
5968    obj.file = document.createElement('input');
5969    obj.file.style.display = 'none';
5970    obj.file.type = 'file';
5971    obj.file.setAttribute('accept', 'image/*');
5972    obj.file.onchange = function() {
5973        obj.addFile(this.files);
5974    }
5975    el.appendChild(obj.file);
5976
5977    // Focus to the editor
5978    if (obj.editor.innerHTML && obj.options.focus) {
5979        jSuites.editor.setCursor(obj.editor, obj.options.focus == 'initial' ? true : false);
5980    }
5981
5982    // Change method
5983    el.change = obj.setData;
5984
5985    // Global generic value handler
5986    el.val = function(val) {
5987        if (val === undefined) {
5988            // Data type
5989            var o = el.getAttribute('data-html') === 'true' ? false : true;
5990            return obj.getData(o);
5991        } else {
5992            obj.setData(val);
5993        }
5994    }
5995
5996    loadExtensions();
5997
5998    el.editor = obj;
5999
6000    // Onload
6001    if (typeof(obj.options.onload) == 'function') {
6002        obj.options.onload(el, obj, obj.editor);
6003    }
6004
6005    return obj;
6006});
6007
6008jSuites.editor.setCursor = function(element, first) {
6009    element.focus();
6010    document.execCommand('selectAll');
6011    var sel = window.getSelection();
6012    var range = sel.getRangeAt(0);
6013    if (first == true) {
6014        var node = range.startContainer;
6015        var size = 0;
6016    } else {
6017        var node = range.endContainer;
6018        var size = node.length;
6019    }
6020    range.setStart(node, size);
6021    range.setEnd(node, size);
6022    sel.removeAllRanges();
6023    sel.addRange(range);
6024}
6025
6026jSuites.editor.getDefaultToolbar = function(obj) {
6027
6028    var color = function(a,b,c) {
6029        if (! c.color) {
6030            var t = null;
6031            var colorPicker = jSuites.color(c, {
6032                onchange: function(o, v) {
6033                    if (c.k === 'color') {
6034                        document.execCommand('foreColor', false, v);
6035                    } else {
6036                        document.execCommand('backColor', false, v);
6037                    }
6038                }
6039            });
6040            c.color.open();
6041        }
6042    }
6043
6044    var items = [];
6045
6046    items.push({
6047        content: 'undo',
6048        onclick: function() {
6049            document.execCommand('undo');
6050        }
6051    });
6052
6053    items.push({
6054        content: 'redo',
6055        onclick: function() {
6056            document.execCommand('redo');
6057        }
6058    });
6059
6060    items.push({
6061        type: 'divisor'
6062    });
6063
6064    if (obj.options.toolbarOnTop) {
6065        items.push({
6066            type: 'select',
6067            width: '140px',
6068            options: ['Default', 'Verdana', 'Arial', 'Courier New'],
6069            render: function (e) {
6070                return '<span style="font-family:' + e + '">' + e + '</span>';
6071            },
6072            onchange: function (a,b,c,d,e) {
6073                document.execCommand("fontName", false, d);
6074            }
6075        });
6076
6077        items.push({
6078            type: 'select',
6079            content: 'format_size',
6080            options: ['x-small', 'small', 'medium', 'large', 'x-large'],
6081            render: function (e) {
6082                return '<span style="font-size:' + e + '">' + e + '</span>';
6083            },
6084            onchange: function (a,b,c,d,e) {
6085                //var html = `<span style="font-size: ${c}">${text}</span>`;
6086                //document.execCommand('insertHtml', false, html);
6087                document.execCommand("fontSize", false, parseInt(e)+1);
6088                //var f = window.getSelection().anchorNode.parentNode
6089                //f.removeAttribute("size");
6090                //f.style.fontSize = d;
6091            }
6092        });
6093
6094        items.push({
6095            type: 'select',
6096            options: ['format_align_left', 'format_align_center', 'format_align_right', 'format_align_justify'],
6097            render: function (e) {
6098                return '<i class="material-icons">' + e + '</i>';
6099            },
6100            onchange: function (a,b,c,d,e) {
6101                var options = ['JustifyLeft','justifyCenter','justifyRight','justifyFull'];
6102                document.execCommand(options[e]);
6103            }
6104        });
6105
6106        items.push({
6107            type: 'divisor'
6108        });
6109
6110        items.push({
6111            content: 'format_color_text',
6112            k: 'color',
6113            onclick: color,
6114        });
6115
6116        items.push({
6117            content: 'format_color_fill',
6118            k: 'background-color',
6119            onclick: color,
6120        });
6121    }
6122
6123    items.push({
6124        content: 'format_bold',
6125        onclick: function(a,b,c) {
6126            document.execCommand('bold');
6127
6128            if (document.queryCommandState("bold")) {
6129                c.classList.add('selected');
6130            } else {
6131                c.classList.remove('selected');
6132            }
6133        }
6134    });
6135
6136    items.push({
6137        content: 'format_italic',
6138        onclick: function(a,b,c) {
6139            document.execCommand('italic');
6140
6141            if (document.queryCommandState("italic")) {
6142                c.classList.add('selected');
6143            } else {
6144                c.classList.remove('selected');
6145            }
6146        }
6147    });
6148
6149    items.push({
6150        content: 'format_underline',
6151        onclick: function(a,b,c) {
6152            document.execCommand('underline');
6153
6154            if (document.queryCommandState("underline")) {
6155                c.classList.add('selected');
6156            } else {
6157                c.classList.remove('selected');
6158            }
6159        }
6160    });
6161
6162    items.push({
6163        type:'divisor'
6164    });
6165
6166    items.push({
6167        content: 'format_list_bulleted',
6168        onclick: function(a,b,c) {
6169            document.execCommand('insertUnorderedList');
6170
6171            if (document.queryCommandState("insertUnorderedList")) {
6172                c.classList.add('selected');
6173            } else {
6174                c.classList.remove('selected');
6175            }
6176        }
6177    });
6178
6179    items.push({
6180        content: 'format_list_numbered',
6181        onclick: function(a,b,c) {
6182            document.execCommand('insertOrderedList');
6183
6184            if (document.queryCommandState("insertOrderedList")) {
6185                c.classList.add('selected');
6186            } else {
6187                c.classList.remove('selected');
6188            }
6189        }
6190    });
6191
6192    items.push({
6193        content: 'format_indent_increase',
6194        onclick: function(a,b,c) {
6195            document.execCommand('indent', true, null);
6196
6197            if (document.queryCommandState("indent")) {
6198                c.classList.add('selected');
6199            } else {
6200                c.classList.remove('selected');
6201            }
6202        }
6203    });
6204
6205    items.push({
6206        content: 'format_indent_decrease',
6207        onclick: function() {
6208            document.execCommand('outdent');
6209
6210            if (document.queryCommandState("outdent")) {
6211                this.classList.add('selected');
6212            } else {
6213                this.classList.remove('selected');
6214            }
6215        }
6216    });
6217
6218    if (obj.options.toolbarOnTop) {
6219        items.push({
6220            type: 'divisor'
6221        });
6222
6223        items.push({
6224            content: 'photo',
6225            onclick: function () {
6226                obj.upload();
6227            }
6228        });
6229
6230        items.push({
6231            type: 'select',
6232            content: 'table_view',
6233            columns: 10,
6234            right: true,
6235            options: [
6236                '0x0', '1x0', '2x0', '3x0', '4x0', '5x0', '6x0', '7x0', '8x0', '9x0',
6237                '0x1', '1x1', '2x1', '3x1', '4x1', '5x1', '6x1', '7x1', '8x1', '9x1',
6238                '0x2', '1x2', '2x2', '3x2', '4x2', '5x2', '6x2', '7x2', '8x2', '9x2',
6239                '0x3', '1x3', '2x3', '3x3', '4x3', '5x3', '6x3', '7x3', '8x3', '9x3',
6240                '0x4', '1x4', '2x4', '3x4', '4x4', '5x4', '6x4', '7x4', '8x4', '9x4',
6241                '0x5', '1x5', '2x5', '3x5', '4x5', '5x5', '6x5', '7x5', '8x5', '9x5',
6242                '0x6', '1x6', '2x6', '3x6', '4x6', '5x6', '6x6', '7x6', '8x6', '9x6',
6243                '0x7', '1x7', '2x7', '3x7', '4x7', '5x7', '6x7', '7x7', '8x7', '9x7',
6244                '0x8', '1x8', '2x8', '3x8', '4x8', '5x8', '6x8', '7x8', '8x8', '9x8',
6245                '0x9', '1x9', '2x9', '3x9', '4x9', '5x9', '6x9', '7x9', '8x9', '9x9',
6246            ],
6247            render: function (e, item) {
6248                if (item) {
6249                    item.onmouseover = this.onmouseover;
6250                    e = e.split('x');
6251                    item.setAttribute('data-x', e[0]);
6252                    item.setAttribute('data-y', e[1]);
6253                }
6254                var element = document.createElement('div');
6255                item.style.margin = '1px';
6256                item.style.border = '1px solid #ddd';
6257                return element;
6258            },
6259            onmouseover: function (e) {
6260                var x = parseInt(e.target.getAttribute('data-x'));
6261                var y = parseInt(e.target.getAttribute('data-y'));
6262                for (var i = 0; i < e.target.parentNode.children.length; i++) {
6263                    var element = e.target.parentNode.children[i];
6264                    var ex = parseInt(element.getAttribute('data-x'));
6265                    var ey = parseInt(element.getAttribute('data-y'));
6266                    if (ex <= x && ey <= y) {
6267                        element.style.backgroundColor = '#cae1fc';
6268                        element.style.borderColor = '#2977ff';
6269                    } else {
6270                        element.style.backgroundColor = '';
6271                        element.style.borderColor = '#ddd';
6272                    }
6273                }
6274            },
6275            onchange: function (a, b, c) {
6276                c = c.split('x');
6277                var table = document.createElement('table');
6278                var tbody = document.createElement('tbody');
6279                for (var y = 0; y <= c[1]; y++) {
6280                    var tr = document.createElement('tr');
6281                    for (var x = 0; x <= c[0]; x++) {
6282                        var td = document.createElement('td');
6283                        td.innerHTML = '';
6284                        tr.appendChild(td);
6285                    }
6286                    tbody.appendChild(tr);
6287                }
6288                table.appendChild(tbody);
6289                table.setAttribute('width', '100%');
6290                table.setAttribute('cellpadding', '6');
6291                table.setAttribute('cellspacing', '0');
6292                document.execCommand('insertHTML', false, table.outerHTML);
6293            }
6294        });
6295    }
6296
6297    return items;
6298}
6299
6300
6301jSuites.form = (function(el, options) {
6302    var obj = {};
6303    obj.options = {};
6304
6305    // Default configuration
6306    var defaults = {
6307        url: null,
6308        message: 'Are you sure? There are unsaved information in your form',
6309        ignore: false,
6310        currentHash: null,
6311        submitButton:null,
6312        validations: null,
6313        onbeforeload: null,
6314        onload: null,
6315        onbeforesave: null,
6316        onsave: null,
6317        onbeforeremove: null,
6318        onremove: null,
6319        onerror: function(el, message) {
6320            jSuites.alert(message);
6321        }
6322    };
6323
6324    // Loop through our object
6325    for (var property in defaults) {
6326        if (options && options.hasOwnProperty(property)) {
6327            obj.options[property] = options[property];
6328        } else {
6329            obj.options[property] = defaults[property];
6330        }
6331    }
6332
6333    // Validations
6334    if (! obj.options.validations) {
6335        obj.options.validations = {};
6336    }
6337
6338    // Submit Button
6339    if (! obj.options.submitButton) {
6340        obj.options.submitButton = el.querySelector('input[type=submit]');
6341    }
6342
6343    if (obj.options.submitButton && obj.options.url) {
6344        obj.options.submitButton.onclick = function() {
6345            obj.save();
6346        }
6347    }
6348
6349    if (! obj.options.validations.email) {
6350        obj.options.validations.email = jSuites.validations.email;
6351    }
6352
6353    if (! obj.options.validations.length) {
6354        obj.options.validations.length = jSuites.validations.length;
6355    }
6356
6357    if (! obj.options.validations.required) {
6358        obj.options.validations.required = jSuites.validations.required;
6359    }
6360
6361    obj.setUrl = function(url) {
6362        obj.options.url = url;
6363    }
6364
6365    obj.load = function() {
6366        jSuites.ajax({
6367            url: obj.options.url,
6368            method: 'GET',
6369            dataType: 'json',
6370            queue: true,
6371            success: function(data) {
6372                // Overwrite values from the backend
6373                if (typeof(obj.options.onbeforeload) == 'function') {
6374                    var ret = obj.options.onbeforeload(el, data);
6375                    if (ret) {
6376                        data = ret;
6377                    }
6378                }
6379                // Apply values to the form
6380                jSuites.form.setElements(el, data);
6381                // Onload methods
6382                if (typeof(obj.options.onload) == 'function') {
6383                    obj.options.onload(el, data);
6384                }
6385            }
6386        });
6387    }
6388
6389    obj.save = function() {
6390        var test = obj.validate();
6391
6392        if (test) {
6393            obj.options.onerror(el, test);
6394        } else {
6395            var data = jSuites.form.getElements(el, true);
6396
6397            if (typeof(obj.options.onbeforesave) == 'function') {
6398                var data = obj.options.onbeforesave(el, data);
6399
6400                if (data === false) {
6401                    return;
6402                }
6403            }
6404
6405            jSuites.ajax({
6406                url: obj.options.url,
6407                method: 'POST',
6408                dataType: 'json',
6409                data: data,
6410                success: function(result) {
6411                    if (typeof(obj.options.onsave) == 'function') {
6412                        obj.options.onsave(el, data, result);
6413                    }
6414                }
6415            });
6416        }
6417    }
6418
6419    obj.remove = function() {
6420        if (typeof(obj.options.onbeforeremove) == 'function') {
6421            var ret = obj.options.onbeforeremove(el, obj);
6422            if (ret === false) {
6423                return false;
6424            }
6425        }
6426
6427        jSuites.ajax({
6428            url: obj.options.url,
6429            method: 'DELETE',
6430            dataType: 'json',
6431            success: function(result) {
6432                if (typeof(obj.options.onremove) == 'function') {
6433                    obj.options.onremove(el, obj, result);
6434                }
6435
6436                obj.reset();
6437            }
6438        });
6439    }
6440
6441    var addError = function(element) {
6442        // Add error in the element
6443        element.classList.add('error');
6444        // Submit button
6445        if (obj.options.submitButton) {
6446            obj.options.submitButton.setAttribute('disabled', true);
6447        }
6448        // Return error message
6449        var error = element.getAttribute('data-error') || 'There is an error in the form';
6450        element.setAttribute('title', error);
6451        return error;
6452    }
6453
6454    var delError = function(element) {
6455        var error = false;
6456        // Remove class from this element
6457        element.classList.remove('error');
6458        element.removeAttribute('title');
6459        // Get elements in the form
6460        var elements = el.querySelectorAll("input, select, textarea, div[name]");
6461        // Run all elements 
6462        for (var i = 0; i < elements.length; i++) {
6463            if (elements[i].getAttribute('data-validation')) {
6464                if (elements[i].classList.contains('error')) {
6465                    error = true;
6466                }
6467            }
6468        }
6469
6470        if (obj.options.submitButton) {
6471            if (error) {
6472                obj.options.submitButton.setAttribute('disabled', true);
6473            } else {
6474                obj.options.submitButton.removeAttribute('disabled');
6475            }
6476        }
6477    }
6478
6479    obj.validateElement = function(element) {
6480        // Test results
6481        var test = false;
6482        // Value
6483        var value = jSuites.form.getValue(element);
6484        // Validation
6485        var validation = element.getAttribute('data-validation');
6486        // Parse
6487        if (typeof(obj.options.validations[validation]) == 'function' && ! obj.options.validations[validation](value, element)) {
6488            // Not passed in the test
6489            test = addError(element);
6490        } else {
6491            if (element.classList.contains('error')) {
6492                delError(element);
6493            }
6494        }
6495
6496        return test;
6497    }
6498
6499    obj.reset = function() {
6500        // Get elements in the form
6501        var name = null;
6502        var elements = el.querySelectorAll("input, select, textarea, div[name]");
6503        // Run all elements 
6504        for (var i = 0; i < elements.length; i++) {
6505            if (name = elements[i].getAttribute('name')) {
6506                if (elements[i].type == 'checkbox' || elements[i].type == 'radio') {
6507                    elements[i].checked = false;
6508                } else {
6509                    if (typeof(elements[i].val) == 'function') {
6510                        elements[i].val('');
6511                    } else {
6512                        elements[i].value = '';
6513                    }
6514                }
6515            }
6516        }
6517    }
6518
6519    // Run form validation
6520    obj.validate = function() {
6521        var test = [];
6522        // Get elements in the form
6523        var elements = el.querySelectorAll("input, select, textarea, div[name]");
6524        // Run all elements 
6525        for (var i = 0; i < elements.length; i++) {
6526            // Required
6527            if (elements[i].getAttribute('data-validation')) {
6528                var res = obj.validateElement(elements[i]);
6529                if (res) {
6530                    test.push(res);
6531                }
6532            }
6533        }
6534        if (test.length > 0) {
6535            return test.join('<br>');
6536        } else {
6537            return false;
6538        }
6539    }
6540
6541    // Check the form
6542    obj.getError = function() {
6543        // Validation
6544        return obj.validation() ? true : false;
6545    }
6546
6547    // Return the form hash
6548    obj.setHash = function() {
6549        return obj.getHash(jSuites.form.getElements(el));
6550    }
6551
6552    // Get the form hash
6553    obj.getHash = function(str) {
6554        var hash = 0, i, chr;
6555
6556        if (str.length === 0) {
6557            return hash;
6558        } else {
6559            for (i = 0; i < str.length; i++) {
6560              chr = str.charCodeAt(i);
6561              hash = ((hash << 5) - hash) + chr;
6562              hash |= 0;
6563            }
6564        }
6565
6566        return hash;
6567    }
6568
6569    // Is there any change in the form since start tracking?
6570    obj.isChanged = function() {
6571        var hash = obj.setHash();
6572        return (obj.options.currentHash != hash);
6573    }
6574
6575    // Restart tracking
6576    obj.resetTracker = function() {
6577        obj.options.currentHash = obj.setHash();
6578        obj.options.ignore = false;
6579    }
6580
6581    // Ignore flag
6582    obj.setIgnore = function(ignoreFlag) {
6583        obj.options.ignore = ignoreFlag ? true : false;
6584    }
6585
6586    // Start tracking in one second
6587    setTimeout(function() {
6588        obj.options.currentHash = obj.setHash();
6589    }, 1000);
6590
6591    // Validations
6592    el.addEventListener("keyup", function(e) {
6593        if (e.target.getAttribute('data-validation')) {
6594            obj.validateElement(e.target);
6595        }
6596    });
6597
6598    // Alert
6599    if (! jSuites.form.hasEvents) {
6600        window.addEventListener("beforeunload", function (e) {
6601            if (obj.isChanged() && obj.options.ignore == false) {
6602                var confirmationMessage =  obj.options.message? obj.options.message : "\o/";
6603
6604                if (confirmationMessage) {
6605                    if (typeof e == 'undefined') {
6606                        e = window.event;
6607                    }
6608
6609                    if (e) {
6610                        e.returnValue = confirmationMessage;
6611                    }
6612
6613                    return confirmationMessage;
6614                } else {
6615                    return void(0);
6616                }
6617            }
6618        });
6619
6620        jSuites.form.hasEvents = true;
6621    }
6622
6623    el.form = obj;
6624
6625    return obj;
6626});
6627
6628// Get value from one element
6629jSuites.form.getValue = function(element) {
6630    var value = null;
6631    if (element.type == 'checkbox') {
6632        if (element.checked == true) {
6633            value = element.value || true;
6634        }
6635    } else if (element.type == 'radio') {
6636        if (element.checked == true) {
6637            value = element.value;
6638        }
6639    } else if (element.type == 'file') {
6640        value = element.files;
6641    } else if (element.tagName == 'select' && element.multiple == true) {
6642        value = [];
6643        var options = element.querySelectorAll("options[selected]");
6644        for (var j = 0; j < options.length; j++) {
6645            value.push(options[j].value);
6646        }
6647    } else if (typeof(element.val) == 'function') {
6648        value = element.val();
6649    } else {
6650        value = element.value || '';
6651    }
6652
6653    return value;
6654}
6655
6656// Get form elements
6657jSuites.form.getElements = function(el, asArray) {
6658    var data = {};
6659    var name = null;
6660    var elements = el.querySelectorAll("input, select, textarea, div[name]");
6661
6662    for (var i = 0; i < elements.length; i++) {
6663        if (name = elements[i].getAttribute('name')) {
6664            data[name] = jSuites.form.getValue(elements[i]) || '';
6665        }
6666    }
6667
6668    return asArray == true ? data : JSON.stringify(data);
6669}
6670
6671//Get form elements
6672jSuites.form.setElements = function(el, data) {
6673    var name = null;
6674    var value = null;
6675    var elements = el.querySelectorAll("input, select, textarea, div[name]");
6676    for (var i = 0; i < elements.length; i++) {
6677        // Attributes
6678        var type = elements[i].getAttribute('type');
6679        if (name = elements[i].getAttribute('name')) {
6680            // Transform variable names in pathname
6681            name = name.replace(new RegExp(/\[(.*?)\]/ig), '.$1');
6682            value = null;
6683            // Seach for the data in the path
6684            if (name.match(/\./)) {
6685                var tmp = jSuites.path.call(data, name) || '';
6686                if (typeof(tmp) !== 'undefined') {
6687                    value = tmp;
6688                }
6689            } else {
6690                if (typeof(data[name]) !== 'undefined') {
6691                    value = data[name];
6692                }
6693            }
6694            // Set the values
6695            if (value !== null) {
6696                if (type == 'checkbox' || type == 'radio') {
6697                    elements[i].checked = value ? true : false;
6698                } else if (type == 'file') {
6699                    // Do nothing
6700                } else {
6701                    if (typeof (elements[i].val) == 'function') {
6702                        elements[i].val(value);
6703                    } else {
6704                        elements[i].value = value;
6705                    }
6706                }
6707            }
6708        }
6709    }
6710}
6711
6712// Legacy
6713jSuites.tracker = jSuites.form;
6714
6715jSuites.focus = function(el) {
6716    if (el.innerText.length) {
6717        var range = document.createRange();
6718        var sel = window.getSelection();
6719        var node = el.childNodes[el.childNodes.length-1];
6720        range.setStart(node, node.length)
6721        range.collapse(true)
6722        sel.removeAllRanges()
6723        sel.addRange(range)
6724        el.scrollLeft = el.scrollWidth;
6725    }
6726}
6727
6728jSuites.isNumeric = (function (num) {
6729    if (typeof(num) === 'string') {
6730        num = num.trim();
6731    }
6732    return !isNaN(num) && num !== null && num !== '';
6733});
6734
6735jSuites.guid = function() {
6736    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
6737        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
6738        return v.toString(16);
6739    });
6740}
6741
6742jSuites.getNode = function() {
6743    var node = document.getSelection().anchorNode;
6744    if (node) {
6745        return (node.nodeType == 3 ? node.parentNode : node);
6746    } else {
6747        return null;
6748    }
6749}
6750/**
6751 * Generate hash from a string
6752 */
6753jSuites.hash = function(str) {
6754    var hash = 0, i, chr;
6755
6756    if (str.length === 0) {
6757        return hash;
6758    } else {
6759        for (i = 0; i < str.length; i++) {
6760            chr = str.charCodeAt(i);
6761            if (chr > 32) {
6762                hash = ((hash << 5) - hash) + chr;
6763                hash |= 0;
6764            }
6765        }
6766    }
6767    return hash;
6768}
6769
6770/**
6771 * Generate a random color
6772 */
6773jSuites.randomColor = function(h) {
6774    var lum = -0.25;
6775    var hex = String('#' + Math.random().toString(16).slice(2, 8).toUpperCase()).replace(/[^0-9a-f]/gi, '');
6776    if (hex.length < 6) {
6777        hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
6778    }
6779    var rgb = [], c, i;
6780    for (i = 0; i < 3; i++) {
6781        c = parseInt(hex.substr(i * 2, 2), 16);
6782        c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
6783        rgb.push(("00" + c).substr(c.length));
6784    }
6785
6786    // Return hex
6787    if (h == true) {
6788        return '#' + jSuites.two(rgb[0].toString(16)) + jSuites.two(rgb[1].toString(16)) + jSuites.two(rgb[2].toString(16));
6789    }
6790
6791    return rgb;
6792}
6793
6794jSuites.getWindowWidth = function() {
6795    var w = window,
6796    d = document,
6797    e = d.documentElement,
6798    g = d.getElementsByTagName('body')[0],
6799    x = w.innerWidth || e.clientWidth || g.clientWidth;
6800    return x;
6801}
6802
6803jSuites.getWindowHeight = function() {
6804    var w = window,
6805    d = document,
6806    e = d.documentElement,
6807    g = d.getElementsByTagName('body')[0],
6808    y = w.innerHeight|| e.clientHeight|| g.clientHeight;
6809    return  y;
6810}
6811
6812jSuites.getPosition = function(e) {
6813    if (e.changedTouches && e.changedTouches[0]) {
6814        var x = e.changedTouches[0].pageX;
6815        var y = e.changedTouches[0].pageY;
6816    } else {
6817        var x = (window.Event) ? e.pageX : e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
6818        var y = (window.Event) ? e.pageY : e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
6819    }
6820
6821    return [ x, y ];
6822}
6823
6824jSuites.click = function(el) {
6825    if (el.click) {
6826        el.click();
6827    } else {
6828        var evt = new MouseEvent('click', {
6829            bubbles: true,
6830            cancelable: true,
6831            view: window
6832        });
6833        el.dispatchEvent(evt);
6834    }
6835}
6836
6837jSuites.findElement = function(element, condition) {
6838    var foundElement = false;
6839
6840    function path (element) {
6841        if (element && ! foundElement) {
6842            if (typeof(condition) == 'function') {
6843                foundElement = condition(element)
6844            } else if (typeof(condition) == 'string') {
6845                if (element.classList && element.classList.contains(condition)) {
6846                    foundElement = element;
6847                }
6848            }
6849        }
6850
6851        if (element.parentNode && ! foundElement) {
6852            path(element.parentNode);
6853        }
6854    }
6855
6856    path(element);
6857
6858    return foundElement;
6859}
6860
6861// Two digits
6862jSuites.two = function(value) {
6863    value = '' + value;
6864    if (value.length == 1) {
6865        value = '0' + value;
6866    }
6867    return value;
6868}
6869
6870jSuites.sha512 = (function(str) {
6871    function int64(msint_32, lsint_32) {
6872        this.highOrder = msint_32;
6873        this.lowOrder = lsint_32;
6874    }
6875
6876    var H = [new int64(0x6a09e667, 0xf3bcc908), new int64(0xbb67ae85, 0x84caa73b),
6877        new int64(0x3c6ef372, 0xfe94f82b), new int64(0xa54ff53a, 0x5f1d36f1),
6878        new int64(0x510e527f, 0xade682d1), new int64(0x9b05688c, 0x2b3e6c1f),
6879        new int64(0x1f83d9ab, 0xfb41bd6b), new int64(0x5be0cd19, 0x137e2179)];
6880
6881    var K = [new int64(0x428a2f98, 0xd728ae22), new int64(0x71374491, 0x23ef65cd),
6882        new int64(0xb5c0fbcf, 0xec4d3b2f), new int64(0xe9b5dba5, 0x8189dbbc),
6883        new int64(0x3956c25b, 0xf348b538), new int64(0x59f111f1, 0xb605d019),
6884        new int64(0x923f82a4, 0xaf194f9b), new int64(0xab1c5ed5, 0xda6d8118),
6885        new int64(0xd807aa98, 0xa3030242), new int64(0x12835b01, 0x45706fbe),
6886        new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, 0xd5ffb4e2),
6887        new int64(0x72be5d74, 0xf27b896f), new int64(0x80deb1fe, 0x3b1696b1),
6888        new int64(0x9bdc06a7, 0x25c71235), new int64(0xc19bf174, 0xcf692694),
6889        new int64(0xe49b69c1, 0x9ef14ad2), new int64(0xefbe4786, 0x384f25e3),
6890        new int64(0x0fc19dc6, 0x8b8cd5b5), new int64(0x240ca1cc, 0x77ac9c65),
6891        new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
6892        new int64(0x5cb0a9dc, 0xbd41fbd4), new int64(0x76f988da, 0x831153b5),
6893        new int64(0x983e5152, 0xee66dfab), new int64(0xa831c66d, 0x2db43210),
6894        new int64(0xb00327c8, 0x98fb213f), new int64(0xbf597fc7, 0xbeef0ee4),
6895        new int64(0xc6e00bf3, 0x3da88fc2), new int64(0xd5a79147, 0x930aa725),
6896        new int64(0x06ca6351, 0xe003826f), new int64(0x14292967, 0x0a0e6e70),
6897        new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
6898        new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, 0x9d95b3df),
6899        new int64(0x650a7354, 0x8baf63de), new int64(0x766a0abb, 0x3c77b2a8),
6900        new int64(0x81c2c92e, 0x47edaee6), new int64(0x92722c85, 0x1482353b),
6901        new int64(0xa2bfe8a1, 0x4cf10364), new int64(0xa81a664b, 0xbc423001),
6902        new int64(0xc24b8b70, 0xd0f89791), new int64(0xc76c51a3, 0x0654be30),
6903        new int64(0xd192e819, 0xd6ef5218), new int64(0xd6990624, 0x5565a910),
6904        new int64(0xf40e3585, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
6905        new int64(0x19a4c116, 0xb8d2d0c8), new int64(0x1e376c08, 0x5141ab53),
6906        new int64(0x2748774c, 0xdf8eeb99), new int64(0x34b0bcb5, 0xe19b48a8),
6907        new int64(0x391c0cb3, 0xc5c95a63), new int64(0x4ed8aa4a, 0xe3418acb),
6908        new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, 0xd6b2b8a3),
6909        new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
6910        new int64(0x84c87814, 0xa1f0ab72), new int64(0x8cc70208, 0x1a6439ec),
6911        new int64(0x90befffa, 0x23631e28), new int64(0xa4506ceb, 0xde82bde9),
6912        new int64(0xbef9a3f7, 0xb2c67915), new int64(0xc67178f2, 0xe372532b),
6913        new int64(0xca273ece, 0xea26619c), new int64(0xd186b8c7, 0x21c0c207),
6914        new int64(0xeada7dd6, 0xcde0eb1e), new int64(0xf57d4f7f, 0xee6ed178),
6915        new int64(0x06f067aa, 0x72176fba), new int64(0x0a637dc5, 0xa2c898a6),
6916        new int64(0x113f9804, 0xbef90dae), new int64(0x1b710b35, 0x131c471b),
6917        new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
6918        new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, 0x9c100d4c),
6919        new int64(0x4cc5d4be, 0xcb3e42b6), new int64(0x597f299c, 0xfc657e2a),
6920        new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)];
6921
6922    var W = new Array(64);
6923    var a, b, c, d, e, f, g, h, i, j;
6924    var T1, T2;
6925    var charsize = 8;
6926
6927    function utf8_encode(str) {
6928        return unescape(encodeURIComponent(str));
6929    }
6930
6931    function str2binb(str) {
6932        var bin = [];
6933        var mask = (1 << charsize) - 1;
6934        var len = str.length * charsize;
6935    
6936        for (var i = 0; i < len; i += charsize) {
6937            bin[i >> 5] |= (str.charCodeAt(i / charsize) & mask) << (32 - charsize - (i % 32));
6938        }
6939    
6940        return bin;
6941    }
6942
6943    function binb2hex(binarray) {
6944        var hex_tab = "0123456789abcdef";
6945        var str = "";
6946        var length = binarray.length * 4;
6947        var srcByte;
6948
6949        for (var i = 0; i < length; i += 1) {
6950            srcByte = binarray[i >> 2] >> ((3 - (i % 4)) * 8);
6951            str += hex_tab.charAt((srcByte >> 4) & 0xF) + hex_tab.charAt(srcByte & 0xF);
6952        }
6953
6954        return str;
6955    }
6956
6957    function safe_add_2(x, y) {
6958        var lsw, msw, lowOrder, highOrder;
6959
6960        lsw = (x.lowOrder & 0xFFFF) + (y.lowOrder & 0xFFFF);
6961        msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16);
6962        lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
6963
6964        lsw = (x.highOrder & 0xFFFF) + (y.highOrder & 0xFFFF) + (msw >>> 16);
6965        msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16);
6966        highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
6967
6968        return new int64(highOrder, lowOrder);
6969    }
6970
6971    function safe_add_4(a, b, c, d) {
6972        var lsw, msw, lowOrder, highOrder;
6973
6974        lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF);
6975        msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (lsw >>> 16);
6976        lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
6977
6978        lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (msw >>> 16);
6979        msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (lsw >>> 16);
6980        highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
6981
6982        return new int64(highOrder, lowOrder);
6983    }
6984
6985    function safe_add_5(a, b, c, d, e) {
6986        var lsw, msw, lowOrder, highOrder;
6987
6988        lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF) + (e.lowOrder & 0xFFFF);
6989        msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (e.lowOrder >>> 16) + (lsw >>> 16);
6990        lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
6991
6992        lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (e.highOrder & 0xFFFF) + (msw >>> 16);
6993        msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (e.highOrder >>> 16) + (lsw >>> 16);
6994        highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
6995
6996        return new int64(highOrder, lowOrder);
6997    }
6998
6999    function maj(x, y, z) {
7000        return new int64(
7001            (x.highOrder & y.highOrder) ^ (x.highOrder & z.highOrder) ^ (y.highOrder & z.highOrder),
7002            (x.lowOrder & y.lowOrder) ^ (x.lowOrder & z.lowOrder) ^ (y.lowOrder & z.lowOrder)
7003        );
7004    }
7005
7006    function ch(x, y, z) {
7007        return new int64(
7008            (x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder),
7009            (x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder)
7010        );
7011    }
7012
7013    function rotr(x, n) {
7014        if (n <= 32) {
7015            return new int64(
7016             (x.highOrder >>> n) | (x.lowOrder << (32 - n)),
7017             (x.lowOrder >>> n) | (x.highOrder << (32 - n))
7018            );
7019        } else {
7020            return new int64(
7021             (x.lowOrder >>> n) | (x.highOrder << (32 - n)),
7022             (x.highOrder >>> n) | (x.lowOrder << (32 - n))
7023            );
7024        }
7025    }
7026
7027    function sigma0(x) {
7028        var rotr28 = rotr(x, 28);
7029        var rotr34 = rotr(x, 34);
7030        var rotr39 = rotr(x, 39);
7031
7032        return new int64(
7033            rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder,
7034            rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder
7035        );
7036    }
7037
7038    function sigma1(x) {
7039        var rotr14 = rotr(x, 14);
7040        var rotr18 = rotr(x, 18);
7041        var rotr41 = rotr(x, 41);
7042
7043        return new int64(
7044            rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder,
7045            rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder
7046        );
7047    }
7048
7049    function gamma0(x) {
7050        var rotr1 = rotr(x, 1), rotr8 = rotr(x, 8), shr7 = shr(x, 7);
7051
7052        return new int64(
7053            rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder,
7054            rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder
7055        );
7056    }
7057
7058    function gamma1(x) {
7059        var rotr19 = rotr(x, 19);
7060        var rotr61 = rotr(x, 61);
7061        var shr6 = shr(x, 6);
7062
7063        return new int64(
7064            rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder,
7065            rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder
7066        );
7067    }
7068
7069    function shr(x, n) {
7070        if (n <= 32) {
7071            return new int64(
7072                x.highOrder >>> n,
7073                x.lowOrder >>> n | (x.highOrder << (32 - n))
7074            );
7075        } else {
7076            return new int64(
7077                0,
7078                x.highOrder << (32 - n)
7079            );
7080        }
7081    }
7082
7083    var str = utf8_encode(str);
7084    var strlen = str.length*charsize;
7085    str = str2binb(str);
7086
7087    str[strlen >> 5] |= 0x80 << (24 - strlen % 32);
7088    str[(((strlen + 128) >> 10) << 5) + 31] = strlen;
7089
7090    for (var i = 0; i < str.length; i += 32) {
7091        a = H[0];
7092        b = H[1];
7093        c = H[2];
7094        d = H[3];
7095        e = H[4];
7096        f = H[5];
7097        g = H[6];
7098        h = H[7];
7099
7100        for (var j = 0; j < 80; j++) {
7101            if (j < 16) {
7102                W[j] = new int64(str[j*2 + i], str[j*2 + i + 1]);
7103            } else {
7104                W[j] = safe_add_4(gamma1(W[j - 2]), W[j - 7], gamma0(W[j - 15]), W[j - 16]);
7105            }
7106
7107            T1 = safe_add_5(h, sigma1(e), ch(e, f, g), K[j], W[j]);
7108            T2 = safe_add_2(sigma0(a), maj(a, b, c));
7109            h = g;
7110            g = f;
7111            f = e;
7112            e = safe_add_2(d, T1);
7113            d = c;
7114            c = b;
7115            b = a;
7116            a = safe_add_2(T1, T2);
7117        }
7118
7119        H[0] = safe_add_2(a, H[0]);
7120        H[1] = safe_add_2(b, H[1]);
7121        H[2] = safe_add_2(c, H[2]);
7122        H[3] = safe_add_2(d, H[3]);
7123        H[4] = safe_add_2(e, H[4]);
7124        H[5] = safe_add_2(f, H[5]);
7125        H[6] = safe_add_2(g, H[6]);
7126        H[7] = safe_add_2(h, H[7]);
7127    }
7128
7129    var binarray = [];
7130    for (var i = 0; i < H.length; i++) {
7131        binarray.push(H[i].highOrder);
7132        binarray.push(H[i].lowOrder);
7133    }
7134
7135    return binb2hex(binarray);
7136});
7137
7138
7139jSuites.image = jSuites.upload = (function(el, options) {
7140    var obj = {};
7141    obj.options = {};
7142
7143    // Default configuration
7144    var defaults = {
7145        type: 'image',
7146        extension: '*',
7147        input: false,
7148        minWidth: false,
7149        maxWidth: null,
7150        maxHeight: null,
7151        maxJpegSizeBytes: null, // For example, 350Kb would be 350000
7152        onchange: null,
7153        multiple: false,
7154        remoteParser: null,
7155        text:{
7156            extensionNotAllowed:'The extension is not allowed',
7157        }
7158    };
7159
7160    // Loop through our object
7161    for (var property in defaults) {
7162        if (options && options.hasOwnProperty(property)) {
7163            obj.options[property] = options[property];
7164        } else {
7165            obj.options[property] = defaults[property];
7166        }
7167    }
7168
7169    // Multiple
7170    if (obj.options.multiple == true) {
7171        el.setAttribute('data-multiple', true);
7172    }
7173
7174    // Container
7175    el.content = [];
7176
7177    // Upload icon
7178    el.classList.add('jupload');
7179
7180    if (obj.options.input == true) {
7181        el.classList.add('input');
7182    }
7183
7184    obj.add = function(data) {
7185        // Reset container for single files
7186        if (obj.options.multiple == false) {
7187            el.content = [];
7188            el.innerText = '';
7189        }
7190
7191        // Append to the element
7192        if (obj.options.type == 'image') {
7193            var img = document.createElement('img');
7194            img.setAttribute('src', data.file);
7195            img.setAttribute('tabindex', -1);
7196            if (! el.getAttribute('name')) {
7197                img.className = 'jfile';
7198                img.content = data;
7199            }
7200            el.appendChild(img);
7201        } else {
7202            if (data.name) {
7203                var name = data.name;
7204            } else {
7205                var name = data.file;
7206            }
7207            var div = document.createElement('div');
7208            div.innerText = name || obj.options.type;
7209            div.classList.add('jupload-item');
7210            div.setAttribute('tabindex', -1);
7211            el.appendChild(div);
7212        }
7213
7214        if (data.content) {
7215            data.file = jSuites.guid();
7216        }
7217
7218        // Push content
7219        el.content.push(data);
7220
7221        // Onchange
7222        if (typeof(obj.options.onchange) == 'function') {
7223            obj.options.onchange(el, data);
7224        }
7225    }
7226
7227    obj.addFromFile = function(file) {
7228        var type = file.type.split('/');
7229        if (type[0] == obj.options.type) {
7230            var readFile = new FileReader();
7231            readFile.addEventListener("load", function (v) {
7232                var data = {
7233                    file: v.srcElement.result,
7234                    extension: file.name.substr(file.name.lastIndexOf('.') + 1),
7235                    name: file.name,
7236                    size: file.size,
7237                    lastmodified: file.lastModified,
7238                    content: v.srcElement.result,
7239                }
7240
7241                obj.add(data);
7242            });
7243
7244            readFile.readAsDataURL(file);
7245        } else {
7246            alert(obj.options.text.extensionNotAllowed);
7247        }
7248    }
7249
7250    obj.addFromUrl = function(src) {
7251        if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) {
7252            console.error('remoteParser not defined in your initialization');
7253        } else {
7254            // This is to process cross domain images
7255            if (src.substr(0,4) == 'data') {
7256                var extension = src.split(';')
7257                extension = extension[0].split('/');
7258                var type = extension[0].replace('data:','');
7259                if (type == obj.options.type) {
7260                    var data = {
7261                        file: src,
7262                        name: '',
7263                        extension: extension[1],
7264                        content: src,
7265                    }
7266                    obj.add(data);
7267                } else {
7268                    alert(obj.options.text.extensionNotAllowed);
7269                }
7270            } else {
7271                var extension = src.substr(src.lastIndexOf('.') + 1);
7272                // Work for cross browsers
7273                src = obj.options.remoteParser + src;
7274                // Get remove content
7275                jSuites.ajax({
7276                    url: src,
7277                    type: 'GET',
7278                    dataType: 'blob',
7279                    success: function(data) {
7280                        //add(extension[0].replace('data:',''), data);
7281                    }
7282                })
7283            }
7284        }
7285    }
7286
7287    var getDataURL = function(canvas, type) {
7288        var compression = 0.92;
7289        var lastContentLength = null;
7290        var content = canvas.toDataURL(type, compression);
7291        while (obj.options.maxJpegSizeBytes && type === 'image/jpeg' &&
7292               content.length > obj.options.maxJpegSizeBytes && content.length !== lastContentLength) {
7293            // Apply the compression
7294            compression *= 0.9;
7295            lastContentLength = content.length;
7296            content = canvas.toDataURL(type, compression);
7297        }
7298        return content;
7299    }
7300
7301    var mime = obj.options.type + '/' + obj.options.extension;
7302    var input = document.createElement('input');
7303    input.type = 'file';
7304    input.setAttribute('accept', mime);
7305    input.onchange = function() {
7306        for (var i = 0; i < this.files.length; i++) {
7307            obj.addFromFile(this.files[i]);
7308        }
7309    }
7310
7311    // Allow multiple files
7312    if (obj.options.multiple == true) {
7313        input.setAttribute('multiple', true);
7314    }
7315
7316    var current = null;
7317
7318    el.addEventListener("click", function(e) {
7319        current = null;
7320        if (! el.children.length || e.target === el) {
7321            jSuites.click(input);
7322        } else {
7323            if (e.target.parentNode == el) {
7324                current = e.target;
7325            }
7326        }
7327    });
7328
7329    el.addEventListener("dblclick", function(e) {
7330        jSuites.click(input);
7331    });
7332
7333    el.addEventListener('dragenter', function(e) {
7334        el.style.border = '1px dashed #000';
7335    });
7336
7337    el.addEventListener('dragleave', function(e) {
7338        el.style.border = '1px solid #eee';
7339    });
7340
7341    el.addEventListener('dragstop', function(e) {
7342        el.style.border = '1px solid #eee';
7343    });
7344
7345    el.addEventListener('dragover', function(e) {
7346        e.preventDefault();
7347    });
7348
7349    el.addEventListener('keydown', function(e) {
7350        if (current && e.which == 46) {
7351            var index = Array.prototype.indexOf.call(el.children, current);
7352            if (index >= 0) {
7353                el.content.splice(index, 1);
7354                current.remove();
7355                current = null;
7356            }
7357        }
7358    });
7359
7360    el.addEventListener('drop', function(e) {
7361        e.preventDefault();  
7362        e.stopPropagation();
7363
7364        var html = (e.originalEvent || e).dataTransfer.getData('text/html');
7365        var file = (e.originalEvent || e).dataTransfer.files;
7366
7367        if (file.length) {
7368            for (var i = 0; i < e.dataTransfer.files.length; i++) {
7369                obj.addFromFile(e.dataTransfer.files[i]);
7370            }
7371        } else if (html) {
7372            if (obj.options.multiple == false) {
7373                el.innerText = '';
7374            }
7375
7376            // Create temp element
7377            var div = document.createElement('div');
7378            div.innerHTML = html;
7379
7380            // Extract images
7381            var img = div.querySelectorAll('img');
7382
7383            if (img.length) {
7384                for (var i = 0; i < img.length; i++) {
7385                    obj.addFromUrl(img[i].src);
7386                }
7387            }
7388        }
7389
7390        el.style.border = '1px solid #eee';
7391
7392        return false;
7393    });
7394
7395    el.val = function(val) {
7396        if (val === undefined) {
7397            return el.content && el.content.length ? el.content : null;
7398        } else {
7399            // Reset
7400            el.innerText = '';
7401            el.content = [];
7402
7403            if (val) {
7404                if (Array.isArray(val)) {
7405                    for (var i = 0; i < val.length; i++) {
7406                        if (typeof(val[i]) == 'string') {
7407                            obj.add({ file: val[i] });
7408                        } else {
7409                            obj.add(val[i]);
7410                        }
7411                    }
7412                } else if (typeof(val) == 'string') {
7413                    obj.add({ file: val });
7414                }
7415            }
7416        }
7417    }
7418
7419    el.upload = el.image = obj;
7420
7421    return obj;
7422});
7423
7424jSuites.image.create = function(data) {
7425    var img = document.createElement('img');
7426    img.setAttribute('src', data.file);
7427    img.className = 'jfile';
7428    img.setAttribute('tabindex', -1);
7429    img.content = data;
7430
7431    return img;
7432}
7433
7434
7435jSuites.lazyLoading = (function(el, options) {
7436    var obj = {}
7437
7438    // Mandatory options
7439    if (! options.loadUp || typeof(options.loadUp) != 'function') {
7440        options.loadUp = function() {
7441            return false;
7442        }
7443    }
7444    if (! options.loadDown || typeof(options.loadDown) != 'function') {
7445        options.loadDown = function() {
7446            return false;
7447        }
7448    }
7449    // Timer ms
7450    if (! options.timer) {
7451        options.timer = 100;
7452    }
7453
7454    // Timer
7455    var timeControlLoading = null;
7456
7457    // Controls
7458    var scrollControls = function(e) {
7459        if (timeControlLoading == null) {
7460            var event = false;
7461            var scrollTop = el.scrollTop;
7462            if (el.scrollTop + (el.clientHeight * 2) >= el.scrollHeight) {
7463                if (options.loadDown()) {
7464                    if (scrollTop == el.scrollTop) {
7465                        el.scrollTop = el.scrollTop - (el.clientHeight);
7466                    }
7467                    event = true;
7468                }
7469            } else if (el.scrollTop <= el.clientHeight) {
7470                if (options.loadUp()) {
7471                    if (scrollTop == el.scrollTop) {
7472                        el.scrollTop = el.scrollTop + (el.clientHeight);
7473                    }
7474                    event = true;
7475                }
7476            }
7477
7478            timeControlLoading = setTimeout(function() {
7479                timeControlLoading = null;
7480            }, options.timer);
7481
7482            if (event) {
7483                if (typeof(options.onupdate) == 'function') {
7484                    options.onupdate();
7485                }
7486            }
7487        }
7488    }
7489
7490    // Onscroll
7491    el.onscroll = function(e) {
7492        scrollControls(e);
7493    }
7494
7495    el.onwheel = function(e) {
7496        scrollControls(e);
7497    }
7498
7499    return obj;
7500});
7501
7502jSuites.loading = (function() {
7503    var obj = {};
7504
7505    var loading = null;
7506
7507    obj.show = function(timeout) {
7508        if (! loading) {
7509            loading = document.createElement('div');
7510            loading.className = 'jloading';
7511        }
7512        document.body.appendChild(loading);
7513
7514        // Max timeout in seconds
7515        if (timeout > 0) {
7516            setTimeout(function() {
7517                obj.hide();
7518            }, timeout * 1000)
7519        }
7520    }
7521
7522    obj.hide = function() {
7523        if (loading && loading.parentNode) {
7524            document.body.removeChild(loading);
7525        }
7526    }
7527
7528    return obj;
7529})();
7530
7531jSuites.mask = (function() {
7532    // Currency 
7533    var tokens = {
7534        // Text
7535        text: [ '@' ],
7536        // Currency tokens
7537        currency: [ '#(.{1})##0?(.{1}0+)?( ?;(.*)?)?', '#' ],
7538        // Percentage
7539        percentage: [ '0{1}(.{1}0+)?%' ],
7540        // Number
7541        numeric: [ '0{1}(.{1}0+)?' ],
7542        // Data tokens
7543        datetime: [ 'YYYY', 'YYY', 'YY', 'MMMMM', 'MMMM', 'MMM', 'MM', 'DDDDD', 'DDDD', 'DDD', 'DD', 'DY', 'DAY', 'WD', 'D', 'Q', 'MONTH', 'MON', 'HH24', 'HH12', 'HH', '\\[H\\]', 'H', 'AM/PM', 'PM', 'AM', 'MI', 'SS', 'MS', 'Y', 'M' ],
7544        // Other
7545        general: [ 'A', '0', '[0-9a-zA-Z\$]+', '.']
7546    }
7547
7548    var getDate = function() {
7549        if (this.mask.toLowerCase().indexOf('[h]') !== -1) {
7550            var m = 0;
7551            if (this.date[4]) {
7552                m = parseFloat(this.date[4] / 60);
7553            }
7554            var v = parseInt(this.date[3]) + m;
7555            v /= 24;
7556        } else if (! (this.date[0] && this.date[1] && this.date[2]) && (this.date[3] || this.date[4])) {
7557            v = jSuites.two(this.date[3]) + ':' + jSuites.two(this.date[4]) + ':' + jSuites.two(this.date[5]) 
7558        } else {
7559            if (this.date[0] && this.date[1] && ! this.date[2]) {
7560                this.date[2] = 1;
7561            }
7562            v = jSuites.two(this.date[0]) + '-' + jSuites.two(this.date[1]) + '-' + jSuites.two(this.date[2]);
7563
7564            if (this.date[3] || this.date[4] || this.date[5]) {
7565                v += ' ' + jSuites.two(this.date[3]) + ':' + jSuites.two(this.date[4]) + ':' + jSuites.two(this.date[5]);
7566            }
7567        }
7568
7569        return v;
7570    }
7571
7572    var extractDate = function() {
7573         var v = '';
7574         if (! (this.date[0] && this.date[1] && this.date[2]) && (this.date[3] || this.date[4])) {
7575             if (this.mask.toLowerCase().indexOf('[h]') !== -1) {
7576                 v = parseInt(this.date[3]);
7577             } else {
7578                 v = parseInt(this.date[3]) % 24;
7579             }
7580             if (this.date[4]) {
7581                 v += parseFloat(this.date[4] / 60);
7582             }
7583             v /= 24;
7584         } else if (this.date[0] || this.date[1] || this.date[2] || this.date[3] || this.date[4] || this.date[5]) {
7585             if (this.date[0] && this.date[1] && ! this.date[2]) {
7586                 this.date[2] = 1;
7587             }
7588             var t = jSuites.calendar.now(this.date);
7589             v = jSuites.calendar.dateToNum(t);
7590             if (this.date[4]) {
7591                 v += parseFloat(this.date[4] / 60);
7592             }
7593         }
7594         return v;
7595     }
7596
7597    var isBlank = function(v) {
7598        return v === null || v === '' || v === undefined ? true : false;
7599    }
7600
7601    var isFormula = function(value) {
7602        return (''+value).chartAt(0) == '=';
7603    }
7604
7605    var isNumeric = function(t) {
7606        return t === 'currency' || t === 'percentage' || t === 'numeric' ? true : false;
7607    }
7608    /**
7609     * Get the decimal defined in the mask configuration
7610     */
7611    var getDecimal = function(v) {
7612        if (v && Number(v) == v) {
7613            return '.';
7614        } else {
7615            if (this.options.decimal) {
7616                return this.options.decimal;
7617            } else {
7618                if (this.locale) {
7619                    var t = Intl.NumberFormat(this.locale).format(1.1);
7620                    return this.options.decimal = t[1];
7621                } else {
7622                    if (! v) {
7623                        v  = this.mask;
7624                    }
7625                    var e = new RegExp('0{1}(.{1})0+', 'ig');
7626                    var t = e.exec(v);
7627                    if (t && t[1] && t[1].length == 1) {
7628                        // Save decimal
7629                        this.options.decimal = t[1];
7630                        // Return decimal
7631                        return t[1];
7632                    } else {
7633                        // Did not find any decimal last resort the default
7634                        var e = new RegExp('#{1}(.{1})#+', 'ig');
7635                        var t = e.exec(v);
7636                        if (t && t[1] && t[1].length == 1) {
7637                            if (t[1] === ',') {
7638                                this.options.decimal = '.';
7639                            } else {
7640                                this.options.decimal = ',';
7641                            }
7642                        } else {
7643                            this.options.decimal = '1.1'.toLocaleString().substring(1,2);
7644                        }
7645                    }
7646                }
7647            }
7648        }
7649
7650        if (this.options.decimal) {
7651            return this.options.decimal;
7652        } else {
7653            return null;
7654        }
7655    }
7656
7657    var ParseValue = function(v, decimal) {
7658        if (v == '') {
7659            return '';
7660        }
7661
7662        // Get decimal
7663        if (! decimal) {
7664            decimal = getDecimal.call(this);
7665        }
7666
7667        // New value
7668        v = (''+v).split(decimal);
7669
7670        // Signal
7671        var signal = v[0].match(/[-]+/g);
7672        if (signal && signal.length) {
7673            signal = true;
7674        } else {
7675            signal = false;
7676        }
7677
7678        v[0] = v[0].match(/[0-9]+/g);
7679
7680        if (v[0]) {
7681            if (signal) {
7682                v[0].unshift('-');
7683            }
7684            v[0] = v[0].join('');
7685        } else {
7686            if (signal) {
7687                v[0] = '-';
7688            }
7689        }
7690
7691        if (v[0] || v[1]) {
7692            if (v[1] !== undefined) {
7693                v[1] = v[1].match(/[0-9]+/g);
7694                if (v[1]) {
7695                    v[1] = v[1].join('');
7696                } else {
7697                    v[1] = '';
7698                }
7699            }
7700        } else {
7701            return '';
7702        }
7703        return v;
7704    }
7705
7706    var FormatValue = function(v, event) {
7707        if (v == '') {
7708            return '';
7709        }
7710        // Get decimal
7711        var d = getDecimal.call(this);
7712        // Convert value
7713        var o = this.options;
7714        // Parse value
7715        v = ParseValue.call(this, v);
7716        if (v == '') {
7717            return '';
7718        }
7719        // Temporary value
7720        if (v[0]) {
7721            var t = parseFloat(v[0] + '.1');
7722            if (o.style == 'percent') {
7723                t /= 100;
7724            }
7725        } else {
7726            var t = null;
7727        }
7728
7729        if ((v[0] == '-' || v[0] == '-00') && ! v[1] && (event && event.inputType == "deleteContentBackward")) {
7730            return '';
7731        }
7732
7733        var n = new Intl.NumberFormat(this.locale, o).format(t);
7734        n = n.split(d);
7735        if (typeof(n[1]) !== 'undefined') {
7736            var s = n[1].replace(/[0-9]*/g, '');
7737            if (s) {
7738                n[2] = s;
7739            }
7740        }
7741
7742        if (v[1] !== undefined) {
7743            n[1] = d + v[1];
7744        } else {
7745            n[1] = '';
7746        }
7747
7748        return n.join('');
7749    }
7750
7751    var Format = function(e, event) {
7752        var v = Value.call(e);
7753        if (! v) {
7754            return;
7755        }
7756
7757        // Get decimal
7758        var d = getDecimal.call(this);
7759        var n = FormatValue.call(this, v, event);
7760        var t = (n.length) - v.length;
7761        var index = Caret.call(e) + t;
7762        // Set value and update caret
7763        Value.call(e, n, index, true);
7764    }
7765
7766    var Extract = function(v) {
7767        // Keep the raw value
7768        var current = ParseValue.call(this, v);
7769        if (current) {
7770            // Negative values
7771            if (current[0] === '-') {
7772                current[0] = '-0';
7773            }
7774            return parseFloat(current.join('.'));
7775        }
7776        return null;
7777    }
7778
7779    /**
7780     * Caret getter and setter methods
7781     */
7782    var Caret = function(index, adjustNumeric) {
7783        if (index === undefined) {
7784            if (this.tagName == 'DIV') {
7785                var pos = 0;
7786                var s = window.getSelection();
7787                if (s) {
7788                    if (s.rangeCount !== 0) {
7789                        var r = s.getRangeAt(0);
7790                        var p = r.cloneRange();
7791                        p.selectNodeContents(this);
7792                        p.setEnd(r.endContainer, r.endOffset);
7793                        pos = p.toString().length;
7794                    }
7795                }
7796                return pos;
7797            } else {
7798                return this.selectionStart;
7799            }
7800        } else {
7801            // Get the current value
7802            var n = Value.call(this);
7803
7804            // Review the position
7805            if (adjustNumeric) {
7806                var p = null;
7807                for (var i = 0; i < n.length; i++) {
7808                    if (n[i].match(/[\-0-9]/g) || n[i] == '.' || n[i] == ',') {
7809                        p = i;
7810                    }
7811                }
7812
7813                // If the string has no numbers
7814                if (p === null) {
7815                    p = n.indexOf(' ');
7816                }
7817
7818                if (index >= p) {
7819                    index = p + 1;
7820                }
7821            }
7822
7823            // Do not update caret
7824            if (index > n.length) {
7825                index = n.length;
7826            }
7827
7828            if (index) {
7829                // Set caret
7830                if (this.tagName == 'DIV') {
7831                    var s = window.getSelection();
7832                    var r = document.createRange();
7833
7834                    if (this.childNodes[0]) {
7835                        r.setStart(this.childNodes[0], index);
7836                        s.removeAllRanges();
7837                        s.addRange(r);
7838                    }
7839                } else {
7840                    this.selectionStart = index;
7841                    this.selectionEnd = index;
7842                }
7843            }
7844        }
7845    }
7846
7847    /**
7848     * Value getter and setter method
7849     */
7850    var Value = function(v, updateCaret, adjustNumeric) {
7851        if (this.tagName == 'DIV') {
7852            if (v === undefined) {
7853                var v = this.innerText;
7854                if (this.value && this.value.length > v.length) {
7855                    v = this.value;
7856                }
7857                return v;
7858            } else {
7859                if (this.innerText !== v) {
7860                    this.innerText = v;
7861
7862                    if (updateCaret) {
7863                        Caret.call(this, updateCaret, adjustNumeric);
7864                    }
7865                }
7866            }
7867        } else {
7868            if (v === undefined) {
7869                return this.value;
7870            } else {
7871                if (this.value !== v) {
7872                    this.value = v;
7873                    if (updateCaret) {
7874                        Caret.call(this, updateCaret, adjustNumeric);
7875                    }
7876                }
7877            }
7878        }
7879    }
7880
7881    // Labels
7882    var weekDaysFull = jSuites.calendar.weekdays;
7883    var weekDays = jSuites.calendar.weekdaysShort;
7884    var monthsFull = jSuites.calendar.months;
7885    var months = jSuites.calendar.monthsShort;
7886
7887    var parser = {
7888        'YEAR': function(v, s) {
7889            var y = ''+new Date().getFullYear();
7890
7891            if (typeof(this.values[this.index]) === 'undefined') {
7892                this.values[this.index] = '';
7893            }
7894            if (parseInt(v) >= 0 && parseInt(v) <= 10) {
7895                if (this.values[this.index].length < s) {
7896                    this.values[this.index] += v;
7897                }
7898            }
7899            if (this.values[this.index].length == s) {
7900                if (s == 2) {
7901                    var y = y.substr(0,2) + this.values[this.index];
7902                } else if (s == 3) {
7903                    var y = y.substr(0,1) + this.values[this.index];
7904                } else if (s == 4) {
7905                    var y = this.values[this.index];
7906                }
7907                this.date[0] = y;
7908                this.index++;
7909            }
7910        },
7911        'YYYY': function(v) {
7912            parser.YEAR.call(this, v, 4);
7913        },
7914        'YYY': function(v) {
7915            parser.YEAR.call(this, v, 3);
7916        },
7917        'YY': function(v) {
7918            parser.YEAR.call(this, v, 2);
7919        },
7920        'FIND': function(v, a) {
7921            if (isBlank(this.values[this.index])) {
7922                this.values[this.index] = '';
7923            }
7924            if (this.event && this.event.inputType && this.event.inputType.indexOf('delete') > -1) {
7925                this.values[this.index] += v;
7926                return;
7927            }
7928            var pos = 0;
7929            var count = 0;
7930            var value = (this.values[this.index] + v).toLowerCase();
7931            for (var i = 0; i < a.length; i++) {
7932                if (a[i].toLowerCase().indexOf(value) == 0) {
7933                    pos = i;
7934                    count++;
7935                }
7936            }
7937            if (count > 1) {
7938                this.values[this.index] += v;
7939            } else if (count == 1) {
7940                // Jump number of chars
7941                var t = (a[pos].length - this.values[this.index].length) - 1;
7942                this.position += t;
7943
7944                this.values[this.index] = a[pos];
7945                this.index++;
7946                return pos;
7947            }
7948        },
7949        'MMM': function(v) {
7950            var ret = parser.FIND.call(this, v, months);
7951            if (ret !== undefined) {
7952                this.date[1] = ret + 1;
7953            }
7954        },
7955        'MON': function(v) {
7956            parser['MMM'].call(this, v);
7957        },
7958        'MMMM': function(v) {
7959            var ret = parser.FIND.call(this, v, monthsFull);
7960            if (ret !== undefined) {
7961                this.date[1] = ret + 1;
7962            }
7963        },
7964        'MONTH': function(v) {
7965            parser['MMMM'].call(this, v);
7966        },
7967        'MMMMM': function(v) {
7968            if (isBlank(this.values[this.index])) {
7969                this.values[this.index] = '';
7970            }
7971            var pos = 0;
7972            var count = 0;
7973            var value = (this.values[this.index] + v).toLowerCase();
7974            for (var i = 0; i < monthsFull.length; i++) {
7975                if (monthsFull[i][0].toLowerCase().indexOf(value) == 0) {
7976                    this.values[this.index] = monthsFull[i][0];
7977                    this.date[1] = i + 1;
7978                    this.index++;
7979                    break;
7980                }
7981            }
7982        },
7983        'MM': function(v) {
7984            if (isBlank(this.values[this.index])) {
7985                if (parseInt(v) > 1 && parseInt(v) < 10) {
7986                    this.date[1] = this.values[this.index] = '0' + v;
7987                    this.index++;
7988                } else if (parseInt(v) < 2) {
7989                    this.values[this.index] = v;
7990                }
7991            } else {
7992                if (this.values[this.index] == 1 && parseInt(v) < 3) {
7993                    this.date[1] = this.values[this.index] += v;
7994                    this.index++;
7995                } else if (this.values[this.index] == 0 && parseInt(v) > 0 && parseInt(v) < 10) {
7996                    this.date[1] = this.values[this.index] += v;
7997                    this.index++;
7998                }
7999            }
8000        },
8001        'M': function(v) {
8002            var test = false;
8003            if (parseInt(v) >= 0 && parseInt(v) < 10) {
8004                if (isBlank(this.values[this.index])) {
8005                    this.values[this.index] = v;
8006                    if (v > 1) {
8007                        this.date[1] = this.values[this.index];
8008                        this.index++;
8009                    }
8010                } else {
8011                    if (this.values[this.index] == 1 && parseInt(v) < 3) {
8012                        this.date[1] = this.values[this.index] += v;
8013                        this.index++;
8014                    } else if (this.values[this.index] == 0 && parseInt(v) > 0) {
8015                        this.date[1] = this.values[this.index] += v;
8016                        this.index++;
8017                    } else {
8018                        var test = true;
8019                    }
8020                }
8021            } else {
8022                var test = true;
8023            }
8024
8025            // Re-test
8026            if (test == true) {
8027                var t = parseInt(this.values[this.index]);
8028                if (t > 0 && t < 12) {
8029                    this.date[2] = this.values[this.index];
8030                    this.index++;
8031                    // Repeat the character
8032                    this.position--;
8033                }
8034            }
8035        },
8036        'D': function(v) {
8037            var test = false;
8038            if (parseInt(v) >= 0 && parseInt(v) < 10) {
8039                if (isBlank(this.values[this.index])) {
8040                    this.values[this.index] = v;
8041                    if (parseInt(v) > 3) {
8042                        this.date[2] = this.values[this.index];
8043                        this.index++;
8044                    }
8045                } else {
8046                    if (this.values[this.index] == 3 && parseInt(v) < 2) {
8047                        this.date[2] = this.values[this.index] += v;
8048                        this.index++;
8049                    } else if (this.values[this.index] == 1 || this.values[this.index] == 2) {
8050                        this.date[2] = this.values[this.index] += v;
8051                        this.index++;
8052                    } else if (this.values[this.index] == 0 && parseInt(v) > 0) {
8053                        this.date[2] = this.values[this.index] += v;
8054                        this.index++;
8055                    } else {
8056                        var test = true;
8057                    }
8058                }
8059            } else {
8060                var test = true;
8061            }
8062
8063            // Re-test
8064            if (test == true) {
8065                var t = parseInt(this.values[this.index]);
8066                if (t > 0 && t < 32) {
8067                    this.date[2] = this.values[this.index];
8068                    this.index++;
8069                    // Repeat the character
8070                    this.position--;
8071                }
8072            }
8073        },
8074        'DD': function(v) {
8075            if (isBlank(this.values[this.index])) {
8076                if (parseInt(v) > 3 && parseInt(v) < 10) {
8077                    this.date[2] = this.values[this.index] = '0' + v;
8078                    this.index++;
8079                } else if (parseInt(v) < 10) {
8080                    this.values[this.index] = v;
8081                }
8082            } else {
8083                if (this.values[this.index] == 3 && parseInt(v) < 2) {
8084                    this.date[2] = this.values[this.index] += v;
8085                    this.index++;
8086                } else if ((this.values[this.index] == 1 || this.values[this.index] == 2) && parseInt(v) < 10) {
8087                    this.date[2] = this.values[this.index] += v;
8088                    this.index++;
8089                } else if (this.values[this.index] == 0 && parseInt(v) > 0 && parseInt(v) < 10) {
8090                    this.date[2] = this.values[this.index] += v;
8091                    this.index++;
8092                }
8093            }
8094        },
8095        'DDD': function(v) {
8096            parser.FIND.call(this, v, weekDays);
8097        },
8098        'DY': function(v) {
8099            parser['DDD'].call(this, v);
8100        },
8101        'DDDD': function(v) {
8102            parser.FIND.call(this, v, weekDaysFull);
8103        },
8104        'DAY': function(v) {
8105            parser['DDDD'].call(this, v);
8106        },
8107        'HH12': function(v, two) {
8108            if (isBlank(this.values[this.index])) {
8109                if (parseInt(v) > 1 && parseInt(v) < 10) {
8110                    if (two) {
8111                        v = 0 + v;
8112                    }
8113                    this.date[3] = this.values[this.index] = v;
8114                    this.index++;
8115                } else if (parseInt(v) < 10) {
8116                    this.values[this.index] = v;
8117                }
8118            } else {
8119                if (this.values[this.index] == 1 && parseInt(v) < 3) {
8120                    this.date[3] = this.values[this.index] += v;
8121                    this.index++;
8122                } else if (this.values[this.index] < 1 && parseInt(v) < 10) {
8123                    this.date[3] = this.values[this.index] += v;
8124                    this.index++;
8125                }
8126            }
8127        },
8128        'HH24': function(v, two) {
8129            var test = false;
8130            if (parseInt(v) >= 0 && parseInt(v) < 10) {
8131                if (this.values[this.index] == null || this.values[this.index] == '') {
8132                    if (parseInt(v) > 2 && parseInt(v) < 10) {
8133                        if (two) {
8134                            v = 0 + v;
8135                        }
8136                        this.date[3] = this.values[this.index] = v;
8137                        this.index++;
8138                    } else if (parseInt(v) < 10) {
8139                        this.values[this.index] = v;
8140                    }
8141                } else {
8142                    if (this.values[this.index] == 2 && parseInt(v) < 4) {
8143                        this.date[3] = this.values[this.index] += v;
8144                        this.index++;
8145                    } else if (this.values[this.index] < 2 && parseInt(v) < 10) {
8146                        this.date[3] = this.values[this.index] += v;
8147                        this.index++;
8148                    }
8149                }
8150            }
8151        },
8152        'HH': function(v) {
8153            parser['HH24'].call(this, v, 1);
8154        },
8155        'H': function(v) {
8156            parser['HH24'].call(this, v, 0);
8157        },
8158        '\\[H\\]': function(v) {
8159            if (this.values[this.index] == undefined) {
8160                this.values[this.index] = '';
8161            }
8162            if (v.match(/[0-9]/g)) {
8163                this.date[3] = this.values[this.index] += v;
8164            } else {
8165                if (this.values[this.index].match(/[0-9]/g)) {
8166                    this.date[3] = this.values[this.index];
8167                    this.index++;
8168                    // Repeat the character
8169                    this.position--;
8170                }
8171            }
8172        },
8173        'N60': function(v, i) {
8174            if (this.values[this.index] == null || this.values[this.index] == '') {
8175                if (parseInt(v) > 5 && parseInt(v) < 10) {
8176                    this.date[i] = this.values[this.index] = '0' + v;
8177                    this.index++;
8178                } else if (parseInt(v) < 10) {
8179                    this.values[this.index] = v;
8180                }
8181            } else {
8182                if (parseInt(v) < 10) {
8183                    this.date[i] = this.values[this.index] += v;
8184                    this.index++;
8185                 }
8186            }
8187        },
8188        'MI': function(v) {
8189            parser.N60.call(this, v, 4);
8190        },
8191        'SS': function(v) {
8192            parser.N60.call(this, v, 5);
8193        },
8194        'AM/PM': function(v) {
8195            this.values[this.index] = '';
8196            if (v) {
8197                if (this.date[3] > 12) {
8198                    this.values[this.index] = 'PM';
8199                } else {
8200                    this.values[this.index] = 'AM';
8201                }
8202            }
8203            this.index++;
8204        },
8205        'WD': function(v) {
8206            if (typeof(this.values[this.index]) === 'undefined') {
8207                this.values[this.index] = '';
8208            }
8209            if (parseInt(v) >= 0 && parseInt(v) < 7) {
8210                this.values[this.index] = v;
8211            }
8212            if (this.value[this.index].length == 1) {
8213                this.index++;
8214            }
8215        },
8216        '0{1}(.{1}0+)?': function(v) {
8217            // Get decimal
8218            var decimal = getDecimal.call(this);
8219            // Negative number
8220            var neg = false;
8221            // Create if is blank
8222            if (isBlank(this.values[this.index])) {
8223                this.values[this.index] = '';
8224            } else {
8225                if (this.values[this.index] == '-') {
8226                    neg = true;
8227                }
8228            }
8229            var current = ParseValue.call(this, this.values[this.index], decimal);
8230            if (current) {
8231                this.values[this.index] = current.join(decimal);
8232            }
8233            // New entry
8234            if (parseInt(v) >= 0 && parseInt(v) < 10) {
8235                // Replace the zero for a number
8236                if (this.values[this.index] == '0' && v > 0) {
8237                    this.values[this.index] = '';
8238                } else if (this.values[this.index] == '-0' && v > 0) {
8239                    this.values[this.index] = '-';
8240                }
8241                // Don't add up zeros because does not mean anything here
8242                if ((this.values[this.index] != '0' && this.values[this.index] != '-0') || v == decimal) {
8243                    this.values[this.index] += v;
8244                }
8245            } else if (decimal && v == decimal) {
8246                if (this.values[this.index].indexOf(decimal) == -1) {
8247                    if (! this.values[this.index]) {
8248                        this.values[this.index] = '0';
8249                    }
8250                    this.values[this.index] += v;
8251                }
8252            } else if (v == '-') {
8253                // Negative signed
8254                neg = true;
8255            }
8256
8257            if (neg === true && this.values[this.index][0] !== '-') {
8258                this.values[this.index] = '-' + this.values[this.index];
8259            }
8260        },
8261        '0{1}(.{1}0+)?%': function(v) {
8262            parser['0{1}(.{1}0+)?'].call(this, v);
8263
8264            if (this.values[this.index].match(/[\-0-9]/g)) {
8265                if (this.values[this.index] && this.values[this.index].indexOf('%') == -1) {
8266                    this.values[this.index] += '%';
8267                }
8268            } else {
8269                this.values[this.index] = '';
8270            }
8271        },
8272        '#(.{1})##0?(.{1}0+)?( ?;(.*)?)?': function(v) {
8273            // Parse number
8274            parser['0{1}(.{1}0+)?'].call(this, v);
8275            // Get decimal
8276            var decimal = getDecimal.call(this);
8277            // Get separator
8278            var separator = this.tokens[this.index].substr(1,1);
8279            // Negative
8280            var negative = this.values[this.index][0] === '-' ? true : false;
8281            // Current value
8282            var current = ParseValue.call(this, this.values[this.index], decimal);
8283
8284            // Get main and decimal parts
8285            if (current !== '') {
8286                // Format number
8287                var n = current[0].match(/[0-9]/g);
8288                if (n) {
8289                    // Format
8290                    n = n.join('');
8291                    var t = [];
8292                    var s = 0;
8293                    for (var j = n.length - 1; j >= 0 ; j--) {
8294                        t.push(n[j]);
8295                        s++;
8296                        if (! (s % 3)) {
8297                            t.push(separator);
8298                        }
8299                    }
8300                    t = t.reverse();
8301                    current[0] = t.join('');
8302                    if (current[0].substr(0,1) == separator) {
8303                        current[0] = current[0].substr(1);
8304                    }
8305                } else {
8306                    current[0] = '';
8307                }
8308
8309                // Value
8310                this.values[this.index] = current.join(decimal);
8311
8312                // Negative
8313                if (negative) {
8314                    this.values[this.index] = '-' + this.values[this.index];
8315                }
8316            }
8317        },
8318        '0': function(v) {
8319            if (v.match(/[0-9]/g)) {
8320                this.values[this.index] = v;
8321                this.index++;
8322            }
8323        },
8324        '[0-9a-zA-Z$]+': function(v) {
8325            if (isBlank(this.values[this.index])) {
8326                this.values[this.index] = '';
8327            }
8328            var t = this.tokens[this.index];
8329            var s = this.values[this.index];
8330            var i = s.length;
8331
8332            if (t[i] == v) {
8333                this.values[this.index] += v;
8334
8335                if (this.values[this.index] == t) {
8336                    this.index++;
8337                }
8338            } else {
8339                this.values[this.index] = t;
8340                this.index++;
8341
8342                if (v.match(/[\-0-9]/g)) {
8343                    // Repeat the character
8344                    this.position--;
8345                }
8346            }
8347        },
8348        'A': function(v) {
8349            if (v.match(/[a-zA-Z]/gi)) {
8350                this.values[this.index] = v;
8351                this.index++;
8352            }
8353        },
8354        '.': function(v) {
8355            parser['[0-9a-zA-Z$]+'].call(this, v);
8356        },
8357        '@': function(v) {
8358            if (isBlank(this.values[this.index])) {
8359                this.values[this.index] = '';
8360            }
8361            this.values[this.index] += v;
8362        }
8363    }
8364
8365    /**
8366     * Get the tokens in the mask string
8367     */
8368    var getTokens = function(str) {
8369        if (this.type == 'general') {
8370            var t = [].concat(tokens.general);
8371        } else {
8372            var t = [].concat(tokens.currency, tokens.datetime, tokens.percentage, tokens.numeric, tokens.text, tokens.general);
8373        }
8374        // Expression to extract all tokens from the string
8375        var e = new RegExp(t.join('|'), 'gi');
8376        // Extract
8377        return str.match(e);
8378    }
8379
8380    /**
8381     * Get the method of one given token
8382     */
8383    var getMethod = function(str) {
8384        if (! this.type) {
8385            var types = Object.keys(tokens);
8386        } else if (this.type == 'text') {
8387            var types = [ 'text' ];
8388        } else if (this.type == 'general') {
8389            var types = [ 'general' ];
8390        } else if (this.type == 'datetime') {
8391            var types = [ 'numeric', 'datetime', 'general' ];
8392        } else {
8393            var types = [ 'currency', 'percentage', 'numeric', 'general' ];
8394        }
8395
8396        // Found
8397        for (var i = 0; i < types.length; i++) {
8398            var type = types[i];
8399            for (var j = 0; j < tokens[type].length; j++) {
8400                var e = new RegExp(tokens[type][j], 'gi');
8401                var r = str.match(e);
8402                if (r) {
8403                    return { type: type, method: tokens[type][j] }
8404                }
8405            }
8406        }
8407    }
8408
8409    /**
8410     * Identify each method for each token
8411     */
8412    var getMethods = function(t) {
8413        var result = [];
8414        for (var i = 0; i < t.length; i++) {
8415            var m = getMethod.call(this, t[i]);
8416            if (m) {
8417                result.push(m.method);
8418            } else {
8419                result.push(null);
8420            }
8421        }
8422
8423        // Compatibility with excel
8424        for (var i = 0; i < result.length; i++) {
8425            if (result[i] == 'MM') {
8426                // Not a month, correct to minutes
8427                if (result[i-1] && result[i-1].indexOf('H') >= 0) {
8428                    result[i] = 'MI';
8429                } else if (result[i-2] && result[i-2].indexOf('H') >= 0) {
8430                    result[i] = 'MI';
8431                } else if (result[i+1] && result[i+1].indexOf('S') >= 0) {
8432                    result[i] = 'MI';
8433                } else if (result[i+2] && result[i+2].indexOf('S') >= 0) {
8434                    result[i] = 'MI';
8435                }
8436            }
8437        }
8438
8439        return result;
8440    }
8441
8442    /**
8443     * Get the type for one given token
8444     */
8445    var getType = function(str) {
8446        var m = getMethod.call(this, str);
8447        if (m) {
8448            var type = m.type;
8449        }
8450
8451        if (type) {
8452            var numeric = 0;
8453            // Make sure the correct type
8454            var t = getTokens.call(this, str);
8455            for (var i = 0; i < t.length; i++) {
8456                m = getMethod.call(this, t[i]);
8457                if (m && isNumeric(m.type)) {
8458                    numeric++;
8459                }
8460            }
8461            if (numeric > 1) {
8462                type = 'general';
8463            }
8464        }
8465
8466        return type;
8467    }
8468
8469    /**
8470     * Parse character per character using the detected tokens in the mask
8471     */
8472    var parse = function() {
8473        // Parser method for this position
8474        if (typeof(parser[this.methods[this.index]]) == 'function') {
8475            parser[this.methods[this.index]].call(this, this.value[this.position]);
8476            this.position++;
8477        } else {
8478            this.values[this.index] = this.tokens[this.index];
8479            this.index++;
8480        }
8481    }
8482
8483    var isFormula = function(value) {
8484        var v = (''+value)[0];
8485        return v == '=' ? true : false;
8486    }
8487
8488    var toPlainString = function(num) {
8489        return (''+ +num).replace(/(-?)(\d*)\.?(\d*)e([+-]\d+)/,
8490          function(a,b,c,d,e) {
8491            return e < 0
8492              ? b + '0.' + Array(1-e-c.length).join(0) + c + d
8493              : b + c + d + Array(e-d.length+1).join(0);
8494          });
8495    }
8496
8497    /**
8498     * Mask function
8499     * @param {mixed|string} JS input or a string to be parsed
8500     * @param {object|string} When the first param is a string, the second is the mask or object with the mask options
8501     */
8502    var obj = function(e, config, returnObject) {
8503        // Options
8504        var r = null;
8505        var t = null;
8506        var o = {
8507            // Element
8508            input: null,
8509            // Current value
8510            value: null,
8511            // Mask options
8512            options: {},
8513            // New values for each token found
8514            values: [],
8515            // Token position
8516            index: 0,
8517            // Character position
8518            position: 0,
8519            // Date raw values
8520            date: [0,0,0,0,0,0],
8521            // Raw number for the numeric values
8522            number: 0,
8523        }
8524
8525        // This is a JavaScript Event
8526        if (typeof(e) == 'object') {
8527            // Element
8528            o.input = e.target;
8529            // Current value
8530            o.value = Value.call(e.target);
8531            // Current caret position
8532            o.caret = Caret.call(e.target);
8533            // Mask
8534            if (t = e.target.getAttribute('data-mask')) {
8535                o.mask = t;
8536            }
8537            // Type
8538            if (t = e.target.getAttribute('data-type')) {
8539                o.type = t;
8540            }
8541            // Options
8542            if (e.target.mask) {
8543                if (e.target.mask.options) {
8544                    o.options = e.target.mask.options;
8545                }
8546                if (e.target.mask.locale) {
8547                    o.locale = e.target.mask.locale;
8548                }
8549            } else {
8550                // Locale
8551                if (t = e.target.getAttribute('data-locale')) {
8552                    o.locale = t;
8553                    if (o.mask) {
8554                        o.options.style = o.mask;
8555                    }
8556                }
8557            }
8558            // Extra configuration
8559            if (e.target.attributes && e.target.attributes.length) {
8560                for (var i = 0; i < e.target.attributes.length; i++) {
8561                    var k = e.target.attributes[i].name;
8562                    var v = e.target.attributes[i].value;
8563                    if (k.substr(0,4) == 'data') {
8564                        o.options[k.substr(5)] = v;
8565                    }
8566                }
8567            }
8568        } else {
8569            // Options
8570            if (typeof(config) == 'string') {
8571                // Mask
8572                o.mask = config;
8573            } else {
8574                // Mask
8575                var k = Object.keys(config);
8576                for (var i = 0; i < k.length; i++) {
8577                    o[k[i]] = config[k[i]];
8578                }
8579            }
8580
8581            if (typeof(e) === 'number') {
8582                // Get decimal
8583                getDecimal.call(o, o.mask);
8584                // Replace to the correct decimal
8585                e = (''+e).replace('.', o.options.decimal);
8586            }
8587
8588            // Current
8589            o.value = e;
8590
8591            if (o.input) {
8592                // Value
8593                Value.call(o.input, e);
8594                // Focus
8595                jSuites.focus(o.input);
8596                // Caret
8597                o.caret = Caret.call(o.input);
8598            }
8599        }
8600
8601        // Mask detected start the process
8602        if (! isFormula(o.value) && (o.mask || o.locale)) {
8603            // Compatibility fixes
8604            if (o.mask) {
8605                // Remove []
8606                o.mask = o.mask.replace(new RegExp(/\[h]/),'|h|');
8607                o.mask = o.mask.replace(new RegExp(/\[.*?\]/),'');
8608                o.mask = o.mask.replace(new RegExp(/\|h\|/),'[h]');
8609                if (o.mask.indexOf(';') !== -1) {
8610                    var t = o.mask.split(';');
8611                    o.mask = t[0];
8612                }
8613                // Excel mask TODO: Improve
8614                if (o.mask.indexOf('##') !== -1) {
8615                    var d = o.mask.split(';');
8616                    if (d[0]) {
8617                        d[0] = d[0].replace('*', '\t');
8618                        d[0] = d[0].replace(new RegExp(/_-/g), ' ');
8619                        d[0] = d[0].replace(new RegExp(/_/g), '');
8620                        d[0] = d[0].replace('##0.###','##0.000');
8621                        d[0] = d[0].replace('##0.##','##0.00');
8622                        d[0] = d[0].replace('##0.#','##0.0');
8623                        d[0] = d[0].replace('##0,###','##0,000');
8624                        d[0] = d[0].replace('##0,##','##0,00');
8625                        d[0] = d[0].replace('##0,#','##0,0');
8626                    }
8627                    o.mask = d[0];
8628                }
8629                // Get type
8630                if (! o.type) {
8631                    o.type = getType.call(o, o.mask);
8632                }
8633                // Get tokens
8634                o.tokens = getTokens.call(o, o.mask);
8635            }
8636
8637            // On new input
8638            if (typeof(e) !== 'object'  || ! e.inputType || ! e.inputType.indexOf('insert') || ! e.inputType.indexOf('delete')) {
8639                // Start transformation
8640                if (o.locale) {
8641                    if (o.input) {
8642                        Format.call(o, o.input, e);
8643                    } else {
8644                        var newValue = FormatValue.call(o, o.value);
8645                    }
8646                } else {
8647                    // Get tokens
8648                    o.methods = getMethods.call(o, o.tokens);
8649                    o.event = e;
8650
8651                    // Go through all tokes
8652                    while (o.position < o.value.length && typeof(o.tokens[o.index]) !== 'undefined') {
8653                        // Get the appropriate parser
8654                        parse.call(o);
8655                    }
8656
8657                    // New value
8658                    var newValue = o.values.join('');
8659
8660                    // Add tokens to the end of string only if string is not empty
8661                    if (isNumeric(o.type) && newValue !== '') {
8662                        // Complement things in the end of the mask
8663                        while (typeof(o.tokens[o.index]) !== 'undefined') {
8664                            var t = getMethod.call(o, o.tokens[o.index]);
8665                            if (t && t.type == 'general') {
8666                                o.values[o.index] = o.tokens[o.index];
8667                            }
8668                            o.index++;
8669                        }
8670
8671                        var adjustNumeric = true;
8672                    } else {
8673                        var adjustNumeric = false;
8674                    }
8675
8676                    // New value
8677                    newValue = o.values.join('');
8678
8679                    // Reset value
8680                    if (o.input) {
8681                        t = newValue.length - o.value.length;
8682                        if (t > 0) {
8683                            var caret = o.caret + t;
8684                        } else {
8685                            var caret = o.caret;
8686                        }
8687                        Value.call(o.input, newValue, caret, adjustNumeric);
8688                    }
8689                }
8690            }
8691
8692            // Update raw data
8693            if (o.input) {
8694                var label = null;
8695                if (isNumeric(o.type)) {
8696                    // Extract the number
8697                    o.number = Extract.call(o, Value.call(o.input));
8698                    // Keep the raw data as a property of the tag
8699                    if (o.type == 'percentage') {
8700                        label = o.number / 100;
8701                    } else {
8702                        label = o.number;
8703                    }
8704                } else if (o.type == 'datetime') {
8705                    label = getDate.call(o);
8706
8707                    if (o.date[0] && o.date[1] && o.date[2]) {
8708                        o.input.setAttribute('data-completed', true);
8709                    }
8710                }
8711
8712                if (label) {
8713                    o.input.setAttribute('data-value', label);
8714                }
8715            }
8716
8717            if (newValue !== undefined) {
8718                if (returnObject) {
8719                    return o;
8720                } else {
8721                    return newValue;
8722                }
8723            }
8724        }
8725    }
8726
8727    // Get the type of the mask
8728    obj.getType = getType;
8729
8730    // Extract the tokens from a mask
8731    obj.prepare = function(str, o) {
8732        if (! o) {
8733            o = {};
8734        }
8735        return getTokens.call(o, str);
8736    }
8737
8738    /**
8739     * Apply the mask to a element (legacy)
8740     */
8741    obj.apply = function(e) {
8742        var v = Value.call(e.target);
8743        if (e.key.length == 1) {
8744            v += e.key;
8745        }
8746        Value.call(e.target, obj(v, e.target.getAttribute('data-mask')));
8747    }
8748
8749    /**
8750     * Legacy support
8751     */
8752    obj.run = function(value, mask, decimal) {
8753        return obj(value, { mask: mask, decimal: decimal });
8754    }
8755
8756    /**
8757     * Extract number from masked string
8758     */
8759    obj.extract = function(v, options, returnObject) {
8760        if (isBlank(v)) {
8761            return v;
8762        }
8763        if (typeof(options) != 'object') {
8764            return value;
8765        } else {
8766            options = Object.assign({}, options);
8767
8768            if (! options.options) {
8769                options.options = {};
8770            }
8771        }
8772
8773        // Compatibility
8774        if (! options.mask && options.format) {
8775            options.mask = options.format;
8776        }
8777
8778        // Remove []
8779        if (options.mask) {
8780            if (options.mask.indexOf(';') !== -1) {
8781                var t = options.mask.split(';');
8782                options.mask = t[0];
8783            }
8784            options.mask = options.mask.replace(new RegExp(/\[h]/),'|h|');
8785            options.mask = options.mask.replace(new RegExp(/\[.*?\]/),'');
8786            options.mask = options.mask.replace(new RegExp(/\|h\|/),'[h]');
8787        }
8788
8789        // Get decimal
8790        getDecimal.call(options, options.mask);
8791
8792        var type = null;
8793        if (options.type == 'percent' || options.options.style == 'percent') {
8794            type = 'percentage';
8795        } else if (options.mask) {
8796            type = getType.call(options, options.mask);
8797        }
8798
8799        if (type === 'general') {
8800            var o = obj(v, options, true);
8801
8802            value = v;
8803        } else if (type === 'datetime') {
8804            if (v instanceof Date) {
8805                var t = jSuites.calendar.getDateString(value, options.mask);
8806            }
8807
8808            var o = obj(v, options, true);
8809
8810            if (jSuites.isNumeric(v)) {
8811                value = v;
8812            } else {
8813                var value = extractDate.call(o);
8814            }
8815        } else {
8816            var value = Extract.call(options, v);
8817            // Percentage
8818            if (type == 'percentage') {
8819                value /= 100;
8820            }
8821            var o = options;
8822        }
8823
8824        o.value = value;
8825
8826        if (! o.type && type) {
8827            o.type = type;
8828        }
8829
8830        if (returnObject) {
8831            return o;
8832        } else {
8833            return value;
8834        }
8835    }
8836
8837    /**
8838     * Render
8839     */
8840    obj.render = function(value, options, fullMask) {
8841        if (isBlank(value)) {
8842            return value;
8843        }
8844
8845        if (typeof(options) != 'object') {
8846            return value;
8847        } else {
8848            options = Object.assign({}, options);
8849
8850            if (! options.options) {
8851                options.options = {};
8852            }
8853        }
8854
8855        // Compatibility
8856        if (! options.mask && options.format) {
8857            options.mask = options.format;
8858        }
8859
8860        // Remove []
8861        if (options.mask) {
8862            if (options.mask.indexOf(';') !== -1) {
8863                var t = options.mask.split(';');
8864                options.mask = t[0];
8865            }
8866            options.mask = options.mask.replace(new RegExp(/\[h]/),'|h|');
8867            options.mask = options.mask.replace(new RegExp(/\[.*?\]/),'');
8868            options.mask = options.mask.replace(new RegExp(/\|h\|/),'[h]');
8869        }
8870
8871        var type = null;
8872        if (options.type == 'percent' || options.options.style == 'percent') {
8873            type = 'percentage';
8874        } else if (options.mask) {
8875            type = getType.call(options, options.mask);
8876        } else if (value instanceof Date) {
8877            type = 'datetime';
8878        }
8879
8880        // Fill with blanks
8881        var fillWithBlanks = false;
8882
8883        if (type =='datetime' || options.type == 'calendar') {
8884            var t = jSuites.calendar.getDateString(value, options.mask);
8885            if (t) {
8886                value = t;
8887            }
8888            if (options.mask && fullMask) {
8889                fillWithBlanks = true;
8890            }
8891        } else {
8892            // Percentage
8893            if (type == 'percentage') {
8894                value *= 100;
8895            }
8896            // Number of decimal places
8897            if (typeof(value) === 'number') {
8898                var t = null;
8899                if (options.mask && fullMask && ((''+value).indexOf('e') === -1)) {
8900                    var d = getDecimal.call(options, options.mask);
8901                    if (options.mask.indexOf(d) !== -1) {
8902                        d = options.mask.split(d);
8903                        d = (''+d[1].match(/[0-9]+/g))
8904                        d = d.length;
8905                        t = value.toFixed(d);
8906                    } else {
8907                        t = value.toFixed(0);
8908                    }
8909                } else if (options.locale && fullMask) {
8910                    // Append zeros 
8911                    var d = (''+value).split('.');
8912                    if (options.options) {
8913                        if (typeof(d[1]) === 'undefined') {
8914                            d[1] = '';
8915                        }
8916                        var len = d[1].length;
8917                        if (options.options.minimumFractionDigits > len) {
8918                            for (var i = 0; i < options.options.minimumFractionDigits - len; i++) {
8919                                d[1] += '0';
8920                            }
8921                        }
8922                    }
8923                    if (! d[1].length) {
8924                        t = d[0]
8925                    } else {
8926                        t = d.join('.');
8927                    }
8928                    var len = d[1].length;
8929                    if (options.options && options.options.maximumFractionDigits < len) {
8930                        t = parseFloat(t).toFixed(options.options.maximumFractionDigits);
8931                    }
8932                } else {
8933                    t = toPlainString(value);
8934                }
8935
8936                if (t !== null) {
8937                    value = t;
8938                    // Get decimal
8939                    getDecimal.call(options, options.mask);
8940                    // Replace to the correct decimal
8941                    if (options.options.decimal) {
8942                        value = value.replace('.', options.options.decimal);
8943                    }
8944                }
8945            } else {
8946                if (options.mask && fullMask) {
8947                    fillWithBlanks = true;
8948                }
8949            }
8950        }
8951
8952        if (fillWithBlanks) {
8953            var s = options.mask.length - value.length;
8954            if (s > 0) {
8955                for (var i = 0; i < s; i++) {
8956                    value += ' ';
8957                }
8958            }
8959        }
8960
8961        value = obj(value, options);
8962
8963        // Numeric mask, number of zeros
8964        if (fullMask && type === 'numeric') {
8965            var maskZeros = options.mask.match(new RegExp(/^[0]+$/gm));
8966            if (maskZeros && maskZeros.length === 1) {
8967                var maskLength = maskZeros[0].length;
8968                if (maskLength > 3) {
8969                    value = '' + value;
8970                    while (value.length < maskLength) {
8971                        value = '0' + value;
8972                    }
8973                }
8974            }
8975        }
8976
8977        return value;
8978    }
8979
8980    obj.set = function(e, m) {
8981        if (m) {
8982            e.setAttribute('data-mask', m);
8983            // Reset the value
8984            var event = new Event('input', {
8985                bubbles: true,
8986                cancelable: true,
8987            });
8988            e.dispatchEvent(event);
8989        }
8990    }
8991
8992    if (typeof document !== 'undefined') {
8993        document.addEventListener('input', function(e) {
8994            if (e.target.getAttribute('data-mask') || e.target.mask) {
8995                obj(e);
8996            }
8997        });
8998    }
8999
9000    return obj;
9001})();
9002
9003jSuites.modal = (function(el, options) {
9004    var obj = {};
9005    obj.options = {};
9006
9007    // Default configuration
9008    var defaults = {
9009        url: null,
9010        onopen: null,
9011        onclose: null,
9012        onload: null,
vendor: 77,883 bytes, lines 9013-11549
9013        closed: false,
9014        width: null,
9015        height: null,
9016        title: null,
9017        padding: null,
9018        backdrop: true,
9019        icon: null,
9020    };
9021
9022    // Loop through our object
9023    for (var property in defaults) {
9024        if (options && options.hasOwnProperty(property)) {
9025            obj.options[property] = options[property];
9026        } else {
9027            obj.options[property] = defaults[property];
9028        }
9029    }
9030
9031    // Title
9032    if (! obj.options.title && el.getAttribute('title')) {
9033        obj.options.title = el.getAttribute('title');
9034    }
9035
9036    var temp = document.createElement('div');
9037    while (el.children[0]) {
9038        temp.appendChild(el.children[0]);
9039    }
9040
9041    obj.title = document.createElement('div');
9042    obj.title.className = 'jmodal_title';
9043    if (obj.options.icon) {
9044        obj.title.setAttribute('data-icon', obj.options.icon);
9045    }
9046
9047    obj.content = document.createElement('div');
9048    obj.content.className = 'jmodal_content';
9049    obj.content.innerHTML = el.innerHTML;
9050
9051    while (temp.children[0]) {
9052        obj.content.appendChild(temp.children[0]);
9053    }
9054
9055    obj.container = document.createElement('div');
9056    obj.container.className = 'jmodal';
9057    obj.container.appendChild(obj.title);
9058    obj.container.appendChild(obj.content);
9059
9060    if (obj.options.padding) {
9061        obj.content.style.padding = obj.options.padding;
9062    }
9063    if (obj.options.width) {
9064        obj.container.style.width = obj.options.width;
9065    }
9066    if (obj.options.height) {
9067        obj.container.style.height = obj.options.height;
9068    }
9069    if (obj.options.title) {
9070        var title = document.createElement('h4');
9071        title.innerText = obj.options.title;
9072        obj.title.appendChild(title);
9073    }
9074
9075    el.innerHTML = '';
9076    el.style.display = 'none';
9077    el.appendChild(obj.container);
9078
9079    // Backdrop
9080    if (obj.options.backdrop) {
9081        var backdrop = document.createElement('div');
9082        backdrop.className = 'jmodal_backdrop';
9083        backdrop.onclick = function () {
9084            obj.close();
9085        }
9086        el.appendChild(backdrop);
9087    }
9088
9089    obj.open = function() {
9090        el.style.display = 'block';
9091        // Fullscreen
9092        var rect = obj.container.getBoundingClientRect();
9093        if (jSuites.getWindowWidth() < rect.width) {
9094            obj.container.style.top = '';
9095            obj.container.style.left = '';
9096            obj.container.classList.add('jmodal_fullscreen');
9097            jSuites.animation.slideBottom(obj.container, 1);
9098        } else {
9099            if (obj.options.backdrop) {
9100                backdrop.style.display = 'block';
9101            }
9102        }
9103        // Event
9104        if (typeof(obj.options.onopen) == 'function') {
9105            obj.options.onopen(el, obj);
9106        }
9107    }
9108
9109    obj.resetPosition = function() {
9110        obj.container.style.top = '';
9111        obj.container.style.left = '';
9112    }
9113
9114    obj.isOpen = function() {
9115        return el.style.display != 'none' ? true : false;
9116    }
9117
9118    obj.close = function() {
9119        if (obj.isOpen()) {
9120            el.style.display = 'none';
9121            if (obj.options.backdrop) {
9122                // Backdrop
9123                backdrop.style.display = '';
9124            }
9125            // Remove fullscreen class
9126            obj.container.classList.remove('jmodal_fullscreen');
9127            // Event
9128            if (typeof(obj.options.onclose) == 'function') {
9129                obj.options.onclose(el, obj);
9130            }
9131        }
9132    }
9133
9134    if (! jSuites.modal.hasEvents) {
9135        //  Position
9136        var tracker = null;
9137
9138        document.addEventListener('keydown', function(e) {
9139            if (e.which == 27) {
9140                var modals = document.querySelectorAll('.jmodal');
9141                for (var i = 0; i < modals.length; i++) {
9142                    modals[i].parentNode.modal.close();
9143                }
9144            }
9145        });
9146
9147        document.addEventListener('mouseup', function(e) {
9148            var item = jSuites.findElement(e.target, 'jmodal');
9149            if (item) {
9150                // Get target info
9151                var rect = item.getBoundingClientRect();
9152
9153                if (e.changedTouches && e.changedTouches[0]) {
9154                    var x = e.changedTouches[0].clientX;
9155                    var y = e.changedTouches[0].clientY;
9156                } else {
9157                    var x = e.clientX;
9158                    var y = e.clientY;
9159                }
9160
9161                if (rect.width - (x - rect.left) < 50 && (y - rect.top) < 50) {
9162                    item.parentNode.modal.close();
9163                }
9164            }
9165
9166            if (tracker) {
9167                tracker.element.style.cursor = 'auto';
9168                tracker = null;
9169            }
9170        });
9171
9172        document.addEventListener('mousedown', function(e) {
9173            var item = jSuites.findElement(e.target, 'jmodal');
9174            if (item) {
9175                // Get target info
9176                var rect = item.getBoundingClientRect();
9177
9178                if (e.changedTouches && e.changedTouches[0]) {
9179                    var x = e.changedTouches[0].clientX;
9180                    var y = e.changedTouches[0].clientY;
9181                } else {
9182                    var x = e.clientX;
9183                    var y = e.clientY;
9184                }
9185
9186                if (rect.width - (x - rect.left) < 50 && (y - rect.top) < 50) {
9187                    // Do nothing
9188                } else {
9189                    if (y - rect.top < 50) {
9190                        if (document.selection) {
9191                            document.selection.empty();
9192                        } else if ( window.getSelection ) {
9193                            window.getSelection().removeAllRanges();
9194                        }
9195
9196                        tracker = {
9197                            left: rect.left,
9198                            top: rect.top,
9199                            x: e.clientX,
9200                            y: e.clientY,
9201                            width: rect.width,
9202                            height: rect.height,
9203                            element: item,
9204                        }
9205                    }
9206                }
9207            }
9208        });
9209
9210        document.addEventListener('mousemove', function(e) {
9211            if (tracker) {
9212                e = e || window.event;
9213                if (e.buttons) {
9214                    var mouseButton = e.buttons;
9215                } else if (e.button) {
9216                    var mouseButton = e.button;
9217                } else {
9218                    var mouseButton = e.which;
9219                }
9220
9221                if (mouseButton) {
9222                    tracker.element.style.top = (tracker.top + (e.clientY - tracker.y) + (tracker.height / 2)) + 'px';
9223                    tracker.element.style.left = (tracker.left + (e.clientX - tracker.x) + (tracker.width / 2)) + 'px';
9224                    tracker.element.style.cursor = 'move';
9225                } else {
9226                    tracker.element.style.cursor = 'auto';
9227                }
9228            }
9229        });
9230
9231        jSuites.modal.hasEvents = true;
9232    }
9233
9234    if (obj.options.url) {
9235        jSuites.ajax({
9236            url: obj.options.url,
9237            method: 'GET',
9238            dataType: 'text/html',
9239            success: function(data) {
9240                obj.content.innerHTML = data;
9241
9242                if (! obj.options.closed) {
9243                    obj.open();
9244                }
9245
9246                if (typeof(obj.options.onload) === 'function') {
9247                    obj.options.onload(obj);
9248                }
9249            }
9250        });
9251    } else {
9252        if (! obj.options.closed) {
9253            obj.open();
9254        }
9255
9256        if (typeof(obj.options.onload) === 'function') {
9257            obj.options.onload(obj);
9258        }
9259    }
9260
9261    // Keep object available from the node
9262    el.modal = obj;
9263
9264    return obj;
9265});
9266
9267
9268jSuites.notification = (function(options) {
9269    var obj = {};
9270    obj.options = {};
9271
9272    // Default configuration
9273    var defaults = {
9274        icon: null,
9275        name: 'Notification',
9276        date: null,
9277        error: null,
9278        title: null,
9279        message: null,
9280        timeout: 4000,
9281        autoHide: true,
9282        closeable: true,
9283    };
9284
9285    // Loop through our object
9286    for (var property in defaults) {
9287        if (options && options.hasOwnProperty(property)) {
9288            obj.options[property] = options[property];
9289        } else {
9290            obj.options[property] = defaults[property];
9291        }
9292    }
9293
9294    var notification = document.createElement('div');
9295    notification.className = 'jnotification';
9296
9297    if (obj.options.error) {
9298        notification.classList.add('jnotification-error');
9299    }
9300
9301    var notificationContainer = document.createElement('div');
9302    notificationContainer.className = 'jnotification-container';
9303    notification.appendChild(notificationContainer);
9304
9305    var notificationHeader = document.createElement('div');
9306    notificationHeader.className = 'jnotification-header';
9307    notificationContainer.appendChild(notificationHeader);
9308
9309    var notificationImage = document.createElement('div');
9310    notificationImage.className = 'jnotification-image';
9311    notificationHeader.appendChild(notificationImage);
9312
9313    if (obj.options.icon) {
9314        var notificationIcon = document.createElement('img');
9315        notificationIcon.src = obj.options.icon;
9316        notificationImage.appendChild(notificationIcon);
9317    }
9318
9319    var notificationName = document.createElement('div');
9320    notificationName.className = 'jnotification-name';
9321    notificationName.innerHTML = obj.options.name;
9322    notificationHeader.appendChild(notificationName);
9323
9324    if (obj.options.closeable == true) {
9325        var notificationClose = document.createElement('div');
9326        notificationClose.className = 'jnotification-close';
9327        notificationClose.onclick = function() {
9328            obj.hide();
9329        }
9330        notificationHeader.appendChild(notificationClose);
9331    }
9332
9333    var notificationDate = document.createElement('div');
9334    notificationDate.className = 'jnotification-date';
9335    notificationHeader.appendChild(notificationDate);
9336
9337    var notificationContent = document.createElement('div');
9338    notificationContent.className = 'jnotification-content';
9339    notificationContainer.appendChild(notificationContent);
9340
9341    if (obj.options.title) {
9342        var notificationTitle = document.createElement('div');
9343        notificationTitle.className = 'jnotification-title';
9344        notificationTitle.innerHTML = obj.options.title;
9345        notificationContent.appendChild(notificationTitle);
9346    }
9347
9348    var notificationMessage = document.createElement('div');
9349    notificationMessage.className = 'jnotification-message';
9350    notificationMessage.innerHTML = obj.options.message;
9351    notificationContent.appendChild(notificationMessage);
9352
9353    obj.show = function() {
9354        document.body.appendChild(notification);
9355        if (jSuites.getWindowWidth() > 800) { 
9356            jSuites.animation.fadeIn(notification);
9357        } else {
9358            jSuites.animation.slideTop(notification, 1);
9359        }
9360    }
9361
9362    obj.hide = function() {
9363        if (jSuites.getWindowWidth() > 800) { 
9364            jSuites.animation.fadeOut(notification, function() {
9365                if (notification.parentNode) {
9366                    notification.parentNode.removeChild(notification);
9367                    if (notificationTimeout) {
9368                        clearTimeout(notificationTimeout);
9369                    }
9370                }
9371            });
9372        } else {
9373            jSuites.animation.slideTop(notification, 0, function() {
9374                if (notification.parentNode) {
9375                    notification.parentNode.removeChild(notification);
9376                    if (notificationTimeout) {
9377                        clearTimeout(notificationTimeout);
9378                    }
9379                }
9380            });
9381        }
9382    };
9383
9384    obj.show();
9385
9386    if (obj.options.autoHide == true) {
9387        var notificationTimeout = setTimeout(function() {
9388            obj.hide();
9389        }, obj.options.timeout);
9390    }
9391
9392    if (jSuites.getWindowWidth() < 800) {
9393        notification.addEventListener("swipeup", function(e) {
9394            obj.hide();
9395            e.preventDefault();
9396            e.stopPropagation();
9397        });
9398    }
9399
9400    return obj;
9401});
9402
9403jSuites.notification.isVisible = function() {
9404    var j = document.querySelector('.jnotification');
9405    return j && j.parentNode ? true : false;
9406}
9407
9408// More palettes https://coolors.co/ or https://gka.github.io/palettes/#/10|s|003790,005647,ffffe0|ffffe0,ff005e,93003a|1|1
9409jSuites.palette = (function() {
9410    /**
9411     * Available palettes
9412     */
9413    var palette = {
9414        material: [
9415            [ "#ffebee", "#fce4ec", "#f3e5f5", "#e8eaf6", "#e3f2fd", "#e0f7fa", "#e0f2f1", "#e8f5e9", "#f1f8e9", "#f9fbe7", "#fffde7", "#fff8e1", "#fff3e0", "#fbe9e7", "#efebe9", "#fafafa", "#eceff1" ],
9416            [ "#ffcdd2", "#f8bbd0", "#e1bee7", "#c5cae9", "#bbdefb", "#b2ebf2", "#b2dfdb", "#c8e6c9", "#dcedc8", "#f0f4c3", "#fff9c4", "#ffecb3", "#ffe0b2", "#ffccbc", "#d7ccc8", "#f5f5f5", "#cfd8dc" ],
9417            [ "#ef9a9a", "#f48fb1", "#ce93d8", "#9fa8da", "#90caf9", "#80deea", "#80cbc4", "#a5d6a7", "#c5e1a5", "#e6ee9c", "#fff59d", "#ffe082", "#ffcc80", "#ffab91", "#bcaaa4", "#eeeeee", "#b0bec5" ],
9418            [ "#e57373", "#f06292", "#ba68c8", "#7986cb", "#64b5f6", "#4dd0e1", "#4db6ac", "#81c784", "#aed581", "#dce775", "#fff176", "#ffd54f", "#ffb74d", "#ff8a65", "#a1887f", "#e0e0e0", "#90a4ae" ],
9419            [ "#ef5350", "#ec407a", "#ab47bc", "#5c6bc0", "#42a5f5", "#26c6da", "#26a69a", "#66bb6a", "#9ccc65", "#d4e157", "#ffee58", "#ffca28", "#ffa726", "#ff7043", "#8d6e63", "#bdbdbd", "#78909c" ],
9420            [ "#f44336", "#e91e63", "#9c27b0", "#3f51b5", "#2196f3", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#795548", "#9e9e9e", "#607d8b" ],
9421            [ "#e53935", "#d81b60", "#8e24aa", "#3949ab", "#1e88e5", "#00acc1", "#00897b", "#43a047", "#7cb342", "#c0ca33", "#fdd835", "#ffb300", "#fb8c00", "#f4511e", "#6d4c41", "#757575", "#546e7a" ],
9422            [ "#d32f2f", "#c2185b", "#7b1fa2", "#303f9f", "#1976d2", "#0097a7", "#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19", "#5d4037", "#616161", "#455a64" ],
9423            [ "#c62828", "#ad1457", "#6a1b9a", "#283593", "#1565c0", "#00838f", "#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315", "#4e342e", "#424242", "#37474f" ],
9424            [ "#b71c1c", "#880e4f", "#4a148c", "#1a237e", "#0d47a1", "#006064", "#004d40", "#1b5e20", "#33691e", "#827717", "#f57f17", "#ff6f00", "#e65100", "#bf360c", "#3e2723", "#212121", "#263238" ],
9425        ],
9426        fire: [
9427            ["0b1a6d","840f38","b60718","de030b","ff0c0c","fd491c","fc7521","faa331","fbb535","ffc73a"],
9428            ["071147","5f0b28","930513","be0309","ef0000","fa3403","fb670b","f9991b","faad1e","ffc123"],
9429            ["03071e","370617","6a040f","9d0208","d00000","dc2f02","e85d04","f48c06","faa307","ffba08"],
9430            ["020619","320615","61040d","8c0207","bc0000","c82a02","d05203","db7f06","e19405","efab00"],
9431            ["020515","2d0513","58040c","7f0206","aa0000","b62602","b94903","c57205","ca8504","d89b00"],
9432        ],
9433        baby: [
9434            ["eddcd2","fff1e6","fde2e4","fad2e1","c5dedd","dbe7e4","f0efeb","d6e2e9","bcd4e6","99c1de"],
9435            ["e1c4b3","ffd5b5","fab6ba","f5a8c4","aacecd","bfd5cf","dbd9d0","baceda","9dc0db","7eb1d5"],
9436            ["daa990","ffb787","f88e95","f282a9","8fc4c3","a3c8be","cec9b3","9dbcce","82acd2","649dcb"],
9437            ["d69070","ff9c5e","f66770","f05f8f","74bbb9","87bfae","c5b993","83aac3","699bca","4d89c2"],
9438            ["c97d5d","f58443","eb4d57","e54a7b","66a9a7","78ae9c","b5a67e","7599b1","5c88b7","4978aa"],
9439        ],
9440        chart: [
9441            ['#C1D37F','#4C5454','#FFD275','#66586F','#D05D5B','#C96480','#95BF8F','#6EA240','#0F0F0E','#EB8258','#95A3B3','#995D81'],
9442        ],
9443    }
9444
9445    /**
9446     * Get a pallete
9447     */
9448    var component = function(o) {
9449        // Otherwise get palette value
9450        if (palette[o]) {
9451            return palette[o];
9452        } else {
9453            return palette.material;
9454        }
9455    }
9456
9457    component.get = function(o) {
9458        // Otherwise get palette value
9459        if (palette[o]) {
9460            return palette[o];
9461        } else {
9462            return palette;
9463        }
9464    }
9465
9466    component.set = function(o, v) {
9467        palette[o] = v;
9468    }
9469
9470    return component;
9471})();
9472
9473
9474jSuites.picker = (function(el, options) {
9475    // Already created, update options
9476    if (el.picker) {
9477        return el.picker.setOptions(options, true);
9478    }
9479
9480    // New instance
9481    var obj = { type: 'picker' };
9482    obj.options = {};
9483
9484    var dropdownHeader = null;
9485    var dropdownContent = null;
9486
9487    /**
9488     * The element passed is a DOM element
9489     */
9490    var isDOM = function(o) {
9491        return (o instanceof Element || o instanceof HTMLDocument);
9492    }
9493
9494    /**
9495     * Create the content options
9496     */
9497    var createContent = function() {
9498        dropdownContent.innerHTML = '';
9499
9500        // Create items
9501        var keys = Object.keys(obj.options.data);
9502
9503        // Go though all options
9504        for (var i = 0; i < keys.length; i++) {
9505            // Item
9506            var dropdownItem = document.createElement('div');
9507            dropdownItem.classList.add('jpicker-item');
9508            dropdownItem.k = keys[i];
9509            dropdownItem.v = obj.options.data[keys[i]];
9510            // Label
9511            var item = obj.getLabel(keys[i], dropdownItem);
9512            if (isDOM(item)) {
9513                dropdownItem.appendChild(item);
9514            } else {
9515                dropdownItem.innerHTML = item;
9516            }
9517            // Append
9518            dropdownContent.appendChild(dropdownItem);
9519        }
9520    }
9521
9522    /**
9523     * Set or reset the options for the picker
9524     */
9525    obj.setOptions = function(options, reset) {
9526        // Default configuration
9527        var defaults = {
9528            value: 0,
9529            data: null,
9530            render: null,
9531            onchange: null,
9532            onmouseover: null,
9533            onselect: null,
9534            onopen: null,
9535            onclose: null,
9536            onload: null,
9537            width: null,
9538            header: true,
9539            right: false,
9540            bottom: false,
9541            content: false,
9542            columns: null,
9543            grid: null,
9544            height: null,
9545        }
9546
9547        // Legacy purpose only
9548        if (options && options.options) {
9549            options.data = options.options;
9550        }
9551
9552        // Loop through the initial configuration
9553        for (var property in defaults) {
9554            if (options && options.hasOwnProperty(property)) {
9555                obj.options[property] = options[property];
9556            } else {
9557                if (typeof(obj.options[property]) == 'undefined' || reset === true) {
9558                    obj.options[property] = defaults[property];
9559                }
9560            }
9561        }
9562
9563        // Start using the options
9564        if (obj.options.header === false) {
9565            dropdownHeader.style.display = 'none';
9566        } else {
9567            dropdownHeader.style.display = '';
9568        }
9569
9570        // Width
9571        if (obj.options.width) {
9572            dropdownHeader.style.width = parseInt(obj.options.width) + 'px';
9573        } else {
9574            dropdownHeader.style.width = '';
9575        }
9576
9577        // Height
9578        if (obj.options.height) {
9579            dropdownContent.style.maxHeight = obj.options.height + 'px';
9580            dropdownContent.style.overflow = 'scroll';
9581        } else {
9582            dropdownContent.style.overflow = '';
9583        }
9584
9585        if (obj.options.columns > 0) {
9586            if (! obj.options.grid) {
9587                dropdownContent.classList.add('jpicker-columns');
9588                dropdownContent.style.width = obj.options.width ? obj.options.width : 36 * obj.options.columns + 'px';
9589            } else {
9590                dropdownContent.classList.add('jpicker-grid');
9591                dropdownContent.style.gridTemplateColumns = 'repeat(' + obj.options.grid + ', 1fr)';
9592            }
9593        }
9594
9595        if (isNaN(obj.options.value)) {
9596            obj.options.value = '0';
9597        }
9598
9599        // Create list from data
9600        createContent();
9601
9602        // Set value
9603        obj.setValue(obj.options.value);
9604
9605        // Set options all returns the own instance
9606        return obj;
9607    }
9608
9609    obj.getValue = function() {
9610        return obj.options.value;
9611    }
9612
9613    obj.setValue = function(v) {
9614        // Set label
9615        obj.setLabel(v);
9616
9617        // Update value
9618        obj.options.value = String(v);
9619
9620        // Lemonade JS
9621        if (el.value != obj.options.value) {
9622            el.value = obj.options.value;
9623            if (typeof(el.oninput) == 'function') {
9624                el.oninput({
9625                    type: 'input',
9626                    target: el,
9627                    value: el.value
9628                });
9629            }
9630        }
9631
9632        if (dropdownContent.children[v].getAttribute('type') !== 'generic') {
9633            obj.close();
9634        }
9635    }
9636
9637    obj.getLabel = function(v, item) {
9638        var label = obj.options.data[v] || null;
9639        if (typeof(obj.options.render) == 'function') {
9640            label = obj.options.render(label, item);
9641        }
9642        return label;
9643    }
9644
9645    obj.setLabel = function(v) {
9646        var item;
9647
9648        if (obj.options.content) {
9649            item = '<i class="material-icons">' + obj.options.content + '</i>';
9650        } else {
9651            item = obj.getLabel(v, null);
9652        }
9653        // Label
9654        if (isDOM(item)) {
9655            dropdownHeader.innerHTML = '';
9656            dropdownHeader.appendChild(item);
9657        } else {
9658            dropdownHeader.innerHTML = item;
9659        }
9660    }
9661
9662    obj.open = function() {
9663        if (! el.classList.contains('jpicker-focus')) {
9664            // Start tracking the element
9665            jSuites.tracking(obj, true);
9666
9667            // Open picker
9668            el.classList.add('jpicker-focus');
9669            el.focus();
9670
9671            var top = 0;
9672            var left = 0;
9673
9674            dropdownContent.style.marginLeft = '';
9675
9676            var rectHeader = dropdownHeader.getBoundingClientRect();
9677            var rectContent = dropdownContent.getBoundingClientRect();
9678
9679            if (window.innerHeight < rectHeader.bottom + rectContent.height || obj.options.bottom) {
9680                top = -1 * (rectContent.height + 4);
9681            } else {
9682                top = rectHeader.height + 4;
9683            }
9684
9685            if (obj.options.right === true) {
9686                left = -1 * rectContent.width + rectHeader.width;
9687            }
9688
9689            if (rectContent.left + left < 0) {
9690                left = left + rectContent.left + 10;
9691            }
9692            if (rectContent.left + rectContent.width > window.innerWidth) {
9693                left = -1 * (10 + rectContent.left + rectContent.width - window.innerWidth);
9694            }
9695
9696            dropdownContent.style.marginTop = parseInt(top) + 'px';
9697            dropdownContent.style.marginLeft = parseInt(left) + 'px';
9698
9699            //dropdownContent.style.marginTop
9700            if (typeof obj.options.onopen == 'function') {
9701                obj.options.onopen(el, obj);
9702            }
9703        }
9704    }
9705
9706    obj.close = function() {
9707        if (el.classList.contains('jpicker-focus')) {
9708            el.classList.remove('jpicker-focus');
9709
9710            // Start tracking the element
9711            jSuites.tracking(obj, false);
9712
9713            if (typeof obj.options.onclose == 'function') {
9714                obj.options.onclose(el, obj);
9715            }
9716        }
9717    }
9718
9719    /**
9720     * Create floating picker
9721     */
9722    var init = function() {
9723        // Class
9724        el.classList.add('jpicker');
9725        el.setAttribute('tabindex', '900');
9726        el.onmousedown = function(e) {
9727            if (! el.classList.contains('jpicker-focus')) {
9728                obj.open();
9729            }
9730        }
9731
9732        // Dropdown Header
9733        dropdownHeader = document.createElement('div');
9734        dropdownHeader.classList.add('jpicker-header');
9735
9736        // Dropdown content
9737        dropdownContent = document.createElement('div');
9738        dropdownContent.classList.add('jpicker-content');
9739        dropdownContent.onclick = function(e) {
9740            var item = jSuites.findElement(e.target, 'jpicker-item');
9741            if (item) {
9742                if (item.parentNode === dropdownContent) {
9743                    // Update label
9744                    obj.setValue(item.k);
9745                    // Call method
9746                    if (typeof(obj.options.onchange) == 'function') {
9747                        obj.options.onchange.call(obj, el, obj, item.v, item.v, item.k, e);
9748                    }
9749                }
9750            }
9751        }
9752
9753        // Append content and header
9754        el.appendChild(dropdownHeader);
9755        el.appendChild(dropdownContent);
9756
9757        // Default value
9758        el.value = options.value || 0;
9759
9760        // Set options
9761        obj.setOptions(options);
9762
9763        if (typeof(obj.options.onload) == 'function') {
9764            obj.options.onload(el, obj);
9765        }
9766
9767        // Change
9768        el.change = obj.setValue;
9769
9770        // Global generic value handler
9771        el.val = function(val) {
9772            if (val === undefined) {
9773                return obj.getValue();
9774            } else {
9775                obj.setValue(val);
9776            }
9777        }
9778
9779        // Reference
9780        el.picker = obj;
9781    }
9782
9783    init();
9784
9785    return obj;
9786});
9787
9788jSuites.progressbar = (function(el, options) {
9789    var obj = {};
9790    obj.options = {};
9791
9792    // Default configuration
9793    var defaults = {
9794        value: 0,
9795        onchange: null,
9796        width: null,
9797    };
9798
9799    // Loop through the initial configuration
9800    for (var property in defaults) {
9801        if (options && options.hasOwnProperty(property)) {
9802            obj.options[property] = options[property];
9803        } else {
9804            obj.options[property] = defaults[property];
9805        }
9806    }
9807
9808    // Class
9809    el.classList.add('jprogressbar');
9810    el.setAttribute('tabindex', 1);
9811    el.setAttribute('data-value', obj.options.value);
9812
9813    var bar = document.createElement('div');
9814    bar.style.width = obj.options.value + '%';
9815    bar.style.color = '#fff';
9816    el.appendChild(bar);
9817
9818    if (obj.options.width) {
9819        el.style.width = obj.options.width;
9820    }
9821
9822    // Set value
9823    obj.setValue = function(value) {
9824        value = parseInt(value);
9825        obj.options.value = value;
9826        bar.style.width = value + '%';
9827        el.setAttribute('data-value', value + '%');
9828
9829        if (value < 6) {
9830            el.style.color = '#000';
9831        } else {
9832            el.style.color = '#fff';
9833        }
9834
9835        // Update value
9836        obj.options.value = value;
9837
9838        if (typeof(obj.options.onchange) == 'function') {
9839            obj.options.onchange(el, value);
9840        }
9841
9842        // Lemonade JS
9843        if (el.value != obj.options.value) {
9844            el.value = obj.options.value;
9845            if (typeof(el.oninput) == 'function') {
9846                el.oninput({
9847                    type: 'input',
9848                    target: el,
9849                    value: el.value
9850                });
9851            }
9852        }
9853    }
9854
9855    obj.getValue = function() {
9856        return obj.options.value;
9857    }
9858
9859    var action = function(e) {
9860        if (e.which) {
9861            // Get target info
9862            var rect = el.getBoundingClientRect();
9863
9864            if (e.changedTouches && e.changedTouches[0]) {
9865                var x = e.changedTouches[0].clientX;
9866                var y = e.changedTouches[0].clientY;
9867            } else {
9868                var x = e.clientX;
9869                var y = e.clientY;
9870            }
9871
9872            obj.setValue(Math.round((x - rect.left) / rect.width * 100));
9873        }
9874    }
9875
9876    // Events
9877    if ('touchstart' in document.documentElement === true) {
9878        el.addEventListener('touchstart', action);
9879        el.addEventListener('touchend', action);
9880    } else {
9881        el.addEventListener('mousedown', action);
9882        el.addEventListener("mousemove", action);
9883    }
9884
9885    // Change
9886    el.change = obj.setValue;
9887
9888    // Global generic value handler
9889    el.val = function(val) {
9890        if (val === undefined) {
9891            return obj.getValue();
9892        } else {
9893            obj.setValue(val);
9894        }
9895    }
9896
9897    // Reference
9898    el.progressbar = obj;
9899
9900    return obj;
9901});
9902
9903jSuites.rating = (function(el, options) {
9904    // Already created, update options
9905    if (el.rating) {
9906        return el.rating.setOptions(options, true);
9907    }
9908
9909    // New instance
9910    var obj = {};
9911    obj.options = {};
9912
9913    obj.setOptions = function(options, reset) {
9914        // Default configuration
9915        var defaults = {
9916            number: 5,
9917            value: 0,
9918            tooltip: [ 'Very bad', 'Bad', 'Average', 'Good', 'Very good' ],
9919            onchange: null,
9920        };
9921
9922        // Loop through the initial configuration
9923        for (var property in defaults) {
9924            if (options && options.hasOwnProperty(property)) {
9925                obj.options[property] = options[property];
9926            } else {
9927                if (typeof(obj.options[property]) == 'undefined' || reset === true) {
9928                    obj.options[property] = defaults[property];
9929                }
9930            }
9931        }
9932
9933        // Make sure the container is empty
9934        el.innerHTML = '';
9935
9936        // Add elements
9937        for (var i = 0; i < obj.options.number; i++) {
9938            var div = document.createElement('div');
9939            div.setAttribute('data-index', (i + 1))
9940            div.setAttribute('title', obj.options.tooltip[i])
9941            el.appendChild(div);
9942        }
9943
9944        // Selected option
9945        if (obj.options.value) {
9946            for (var i = 0; i < obj.options.number; i++) {
9947                if (i < obj.options.value) {
9948                    el.children[i].classList.add('jrating-selected');
9949                }
9950            }
9951        }
9952
9953        return obj;
9954    }
9955
9956    // Set value
9957    obj.setValue = function(index) {
9958        for (var i = 0; i < obj.options.number; i++) {
9959            if (i < index) {
9960                el.children[i].classList.add('jrating-selected');
9961            } else {
9962                el.children[i].classList.remove('jrating-over');
9963                el.children[i].classList.remove('jrating-selected');
9964            }
9965        }
9966
9967        obj.options.value = index;
9968
9969        if (typeof(obj.options.onchange) == 'function') {
9970            obj.options.onchange(el, index);
9971        }
9972
9973        // Lemonade JS
9974        if (el.value != obj.options.value) {
9975            el.value = obj.options.value;
9976            if (typeof(el.oninput) == 'function') {
9977                el.oninput({
9978                    type: 'input',
9979                    target: el,
9980                    value: el.value
9981                });
9982            }
9983        }
9984    }
9985
9986    obj.getValue = function() {
9987        return obj.options.value;
9988    }
9989
9990    var init = function() {
9991        // Start plugin
9992        obj.setOptions(options);
9993
9994        // Class
9995        el.classList.add('jrating');
9996
9997        // Events
9998        el.addEventListener("click", function(e) {
9999            var index = e.target.getAttribute('data-index');
10000            if (index != undefined) {
10001                if (index == obj.options.value) {
10002                    obj.setValue(0);
10003                } else {
10004                    obj.setValue(index);
10005                }
10006            }
10007        });
10008
10009        el.addEventListener("mouseover", function(e) {
10010            var index = e.target.getAttribute('data-index');
10011            for (var i = 0; i < obj.options.number; i++) {
10012                if (i < index) {
10013                    el.children[i].classList.add('jrating-over');
10014                } else {
10015                    el.children[i].classList.remove('jrating-over');
10016                }
10017            }
10018        });
10019
10020        el.addEventListener("mouseout", function(e) {
10021            for (var i = 0; i < obj.options.number; i++) {
10022                el.children[i].classList.remove('jrating-over');
10023            }
10024        });
10025
10026        // Change
10027        el.change = obj.setValue;
10028
10029        // Global generic value handler
10030        el.val = function(val) {
10031            if (val === undefined) {
10032                return obj.getValue();
10033            } else {
10034                obj.setValue(val);
10035            }
10036        }
10037
10038        // Reference
10039        el.rating = obj;
10040    }
10041
10042    init();
10043
10044    return obj;
10045});
10046
10047
10048jSuites.search = (function(el, options) {
10049    if (el.search) {
10050        return el.search;
10051    }
10052
10053    var index =  null;
10054
10055    var select = function(e) {
10056        if (e.target.classList.contains('jsearch_item')) {
10057            var element = e.target;
10058        } else {
10059            var element = e.target.parentNode;
10060        }
10061
10062        obj.selectIndex(element);
10063        e.preventDefault();
10064    }
10065
10066    var createList = function(data) {
10067        // Reset container
10068        container.innerHTML = '';
10069        // Print results
10070        if (! data.length) {
10071            // Show container
10072            el.style.display = '';
10073        } else {
10074            // Show container
10075            el.style.display = 'block';
10076
10077            // Show items (only 10)
10078            var len = data.length < 11 ? data.length : 10;
10079            for (var i = 0; i < len; i++) {
10080                if (typeof(data[i]) == 'string') {
10081                    var text = data[i];
10082                    var value = data[i];
10083                } else {
10084                    // Legacy
10085                    var text = data[i].text;
10086                    if (! text && data[i].name) {
10087                        text = data[i].name;
10088                    }
10089                    var value = data[i].value;
10090                    if (! value && data[i].id) {
10091                        value = data[i].id;
10092                    }
10093                }
10094
10095                var div = document.createElement('div');
10096                div.setAttribute('data-value', value);
10097                div.setAttribute('data-text', text);
10098                div.className = 'jsearch_item';
10099
10100                if (data[i].id) {
10101                    div.setAttribute('id', data[i].id)
10102                }
10103
10104                if (obj.options.forceSelect && i == 0) {
10105                    div.classList.add('selected');
10106                }
10107                var img = document.createElement('img');
10108                if (data[i].image) {
10109                    img.src = data[i].image;
10110                } else {
10111                    img.style.display = 'none';
10112                }
10113                div.appendChild(img);
10114
10115                var item = document.createElement('div');
10116                item.innerHTML = text;
10117                div.appendChild(item);
10118
10119                // Append item to the container
10120                container.appendChild(div);
10121            }
10122        }
10123    }
10124
10125    var execute = function(str) {
10126        if (str != obj.terms) {
10127            // New terms
10128            obj.terms = str;
10129            // New index
10130            if (obj.options.forceSelect) {
10131                index = 0;
10132            } else {
10133                index = null;
10134            }
10135            // Array or remote search
10136            if (Array.isArray(obj.options.data)) {
10137                var test = function(o) {
10138                    if (typeof(o) == 'string') {
10139                        if ((''+o).toLowerCase().search(str.toLowerCase()) >= 0) {
10140                            return true;
10141                        }
10142                    } else {
10143                        for (var key in o) {
10144                            var value = o[key];
10145                            if ((''+value).toLowerCase().search(str.toLowerCase()) >= 0) {
10146                                return true;
10147                            }
10148                        }
10149                    }
10150                    return false;
10151                }
10152
10153                var results = obj.options.data.filter(function(item) {
10154                    return test(item);
10155                });
10156
10157                // Show items
10158                createList(results);
10159            } else {
10160                // Get remove results
10161                jSuites.ajax({
10162                    url: obj.options.data + str,
10163                    method: 'GET',
10164                    dataType: 'json',
10165                    success: function(data) {
10166                        // Show items
10167                        createList(data);
10168                    }
10169                });
10170            }
10171        }
10172    }
10173
10174    // Search timer
10175    var timer = null;
10176
10177    // Search methods
10178    var obj = function(str) {
10179        if (timer) {
10180            clearTimeout(timer);
10181        }
10182        timer = setTimeout(function() {
10183            execute(str);
10184        }, 500);
10185    }
10186    if(options.forceSelect === null) {
10187        options.forceSelect = true;
10188    }
10189    obj.options = {
10190        data: options.data || null,
10191        input: options.input || null,
10192        searchByNode: options.searchByNode || null,
10193        onselect: options.onselect || null,
10194        forceSelect: options.forceSelect,
10195        onbeforesearch: options.onbeforesearch || null,
10196    };
10197
10198    obj.selectIndex = function(item) {
10199        var id = item.getAttribute('id');
10200        var text = item.getAttribute('data-text');
10201        var value = item.getAttribute('data-value');
10202        // Onselect
10203        if (typeof(obj.options.onselect) == 'function') {
10204            obj.options.onselect(obj, text, value, id);
10205        }
10206        // Close container
10207        obj.close();
10208    }
10209
10210    obj.open = function() {
10211        el.style.display = 'block';
10212    }
10213
10214    obj.close = function() {
10215        if (timer) {
10216            clearTimeout(timer);
10217        }
10218        // Current terms
10219        obj.terms = '';
10220        // Remove results
10221        container.innerHTML = '';
10222        // Hide
10223        el.style.display = '';
10224    }
10225
10226    obj.isOpened = function() {
10227        return el.style.display ? true : false;
10228    }
10229
10230    obj.keydown = function(e) {
10231        if (obj.isOpened()) {
10232            if (e.key == 'Enter') {
10233                // Enter
10234                if (index!==null && container.children[index]) {
10235                    obj.selectIndex(container.children[index]);
10236                    e.preventDefault();
10237                } else {
10238                    obj.close();
10239                }
10240            } else if (e.key === 'ArrowUp') {
10241                // Up
10242                if (index!==null && container.children[0]) {
10243                    container.children[index].classList.remove('selected');
10244                    if(!obj.options.forceSelect && index === 0) {
10245                        index = null;
10246                    } else {
10247                        index = Math.max(0, index-1);
10248                        container.children[index].classList.add('selected');
10249                    }
10250                }
10251                e.preventDefault();
10252            } else if (e.key === 'ArrowDown') {
10253                // Down
10254                if(index == null) {
10255                    index = -1;
10256                } else {
10257                    container.children[index].classList.remove('selected');
10258                }
10259                if (index < 9 && container.children[index+1]) {
10260                    index++;
10261                }
10262                container.children[index].classList.add('selected');
10263                e.preventDefault();
10264            }
10265        }
10266    }
10267
10268    obj.keyup = function(e) {
10269        if (! obj.options.searchByNode && obj.options.input) {
10270            if (obj.options.input.tagName === 'DIV') {
10271                var terms = obj.options.input.innerText;
10272            } else {
10273                var terms = obj.options.input.value;
10274            }
10275        } else {
10276            // Current node
10277            var node = jSuites.getNode();
10278            if (node) {
10279                var terms = node.innerText;
10280            }
10281        }
10282
10283        if (typeof(obj.options.onbeforesearch) == 'function') {
10284            var ret = obj.options.onbeforesearch(obj, terms);
10285            if (ret) {
10286                terms = ret;
10287            } else {
10288                if (ret === false) {
10289                    // Ignore event
10290                    return;
10291                }
10292            }
10293        }
10294
10295        obj(terms);
10296    }
10297    
10298    // Add events
10299    if (obj.options.input) {
10300        obj.options.input.addEventListener("keyup", obj.keyup);
10301        obj.options.input.addEventListener("keydown", obj.keydown);
10302    }
10303
10304    // Append element
10305    var container = document.createElement('div');
10306    container.classList.add('jsearch_container');
10307    container.onmousedown = select;
10308    el.appendChild(container);
10309
10310    el.classList.add('jsearch');
10311    el.search = obj;
10312
10313    return obj;
10314});
10315
10316
10317jSuites.slider = (function(el, options) {
10318    var obj = {};
10319    obj.options = {};
10320    obj.currentImage = null;
10321
10322    if (options) {
10323        obj.options = options;
10324    }
10325
10326    // Focus
10327    el.setAttribute('tabindex', '900')
10328
10329    // Items
10330    obj.options.items = [];
10331
10332    if (! el.classList.contains('jslider')) {
10333        el.classList.add('jslider');
10334        el.classList.add('unselectable');
10335
10336        if (obj.options.height) {
10337            el.style.minHeight = obj.options.height;
10338        }
10339        if (obj.options.width) {
10340            el.style.width = obj.options.width;
10341        }
10342        if (obj.options.grid) {
10343            el.classList.add('jslider-grid');
10344            var number = el.children.length;
10345            if (number > 4) {
10346                el.setAttribute('data-total', number - 4);
10347            }
10348            el.setAttribute('data-number', (number > 4 ? 4 : number));
10349        }
10350
10351        // Add slider counter
10352        var counter = document.createElement('div');
10353        counter.classList.add('jslider-counter');
10354
10355        // Move children inside
10356        if (el.children.length > 0) {
10357            // Keep children items
10358            for (var i = 0; i < el.children.length; i++) {
10359                obj.options.items.push(el.children[i]);
10360                
10361                // counter click event
10362                var item = document.createElement('div');
10363                item.onclick = function() {
10364                    var index = Array.prototype.slice.call(counter.children).indexOf(this);
10365                    obj.show(obj.currentImage = obj.options.items[index]);
10366                }
10367                counter.appendChild(item);
10368            }
10369        }
10370        // Add caption
10371        var caption = document.createElement('div');
10372        caption.className = 'jslider-caption';
10373
10374        // Add close buttom
10375        var controls = document.createElement('div');
10376        var close = document.createElement('div');
10377        close.className = 'jslider-close';
10378        close.innerHTML = '';
10379        
10380        close.onclick = function() {
10381            obj.close();
10382        }
10383        controls.appendChild(caption);
10384        controls.appendChild(close);
10385    }
10386
10387    obj.updateCounter = function(index) {
10388        for (var i = 0; i < counter.children.length; i ++) {
10389            if (counter.children[i].classList.contains('jslider-counter-focus')) {
10390                counter.children[i].classList.remove('jslider-counter-focus');
10391                break;
10392            }
10393        }
10394        counter.children[index].classList.add('jslider-counter-focus');
10395    }
10396
10397    obj.show = function(target) {
10398        if (! target) {
10399            var target = el.children[0];
10400        }
10401
10402        // Focus element
10403        el.classList.add('jslider-focus');
10404        el.classList.remove('jslider-grid');
10405        el.appendChild(controls);
10406        el.appendChild(counter);
10407
10408        // Update counter
10409        var index = obj.options.items.indexOf(target);
10410        obj.updateCounter(index);
10411
10412        // Remove display
10413        for (var i = 0; i < el.children.length; i++) {
10414            el.children[i].style.display = '';
10415        }
10416        target.style.display = 'block';
10417
10418        // Is there any previous
10419        if (target.previousElementSibling) {
10420            el.classList.add('jslider-left');
10421        } else {
10422            el.classList.remove('jslider-left');
10423        }
10424
10425        // Is there any next
10426        if (target.nextElementSibling && target.nextElementSibling.tagName == 'IMG') {
10427            el.classList.add('jslider-right');
10428        } else {
10429            el.classList.remove('jslider-right');
10430        }
10431
10432        obj.currentImage = target;
10433
10434        // Vertical image
10435        if (obj.currentImage.offsetHeight > obj.currentImage.offsetWidth) {
10436            obj.currentImage.classList.add('jslider-vertical');
10437        }
10438
10439        controls.children[0].innerText = obj.currentImage.getAttribute('title');
10440    }
10441
10442    obj.open = function() {
10443        obj.show();
10444
10445        // Event
10446        if (typeof(obj.options.onopen) == 'function') {
10447            obj.options.onopen(el);
10448        }
10449    }
10450
10451    obj.close = function() {
10452        // Remove control classes
10453        el.classList.remove('jslider-focus');
10454        el.classList.remove('jslider-left');
10455        el.classList.remove('jslider-right');
10456        // Show as a grid depending on the configuration
10457        if (obj.options.grid) {
10458            el.classList.add('jslider-grid');
10459        }
10460        // Remove display
10461        for (var i = 0; i < el.children.length; i++) {
10462            el.children[i].style.display = '';
10463        }
10464        // Remove controls from the component
10465        counter.remove();
10466        controls.remove();
10467        // Current image
10468        obj.currentImage = null;
10469        // Event
10470        if (typeof(obj.options.onclose) == 'function') {
10471            obj.options.onclose(el);
10472        }
10473    }
10474
10475    obj.reset = function() {
10476        el.innerHTML = '';
10477    }
10478
10479    obj.next = function() {
10480        var nextImage = obj.currentImage.nextElementSibling;
10481        if (nextImage && nextImage.tagName === 'IMG') {
10482            obj.show(obj.currentImage.nextElementSibling);
10483        }
10484    }
10485    
10486    obj.prev = function() {
10487        if (obj.currentImage.previousElementSibling) {
10488            obj.show(obj.currentImage.previousElementSibling);
10489        }
10490    }
10491
10492    var mouseUp = function(e) {
10493        // Open slider
10494        if (e.target.tagName == 'IMG') {
10495            obj.show(e.target);
10496        } else if (! e.target.classList.contains('jslider-close') && ! (e.target.parentNode.classList.contains('jslider-counter') || e.target.classList.contains('jslider-counter'))){
10497            // Arrow controls
10498            var offsetX = e.offsetX || e.changedTouches[0].clientX;
10499            if (e.target.clientWidth - offsetX < 40) {
10500                // Show next image
10501                obj.next();
10502            } else if (offsetX < 40) {
10503                // Show previous image
10504                obj.prev();
10505            }
10506        }
10507    }
10508
10509    if ('ontouchend' in document.documentElement === true) {
10510        el.addEventListener('touchend', mouseUp);
10511    } else {
10512        el.addEventListener('mouseup', mouseUp);
10513    }
10514
10515    // Add global events
10516    el.addEventListener("swipeleft", function(e) {
10517        obj.next();
10518        e.preventDefault();
10519        e.stopPropagation();
10520    });
10521
10522    el.addEventListener("swiperight", function(e) {
10523        obj.prev();
10524        e.preventDefault();
10525        e.stopPropagation();
10526    });
10527
10528    el.addEventListener('keydown', function(e) {
10529        if (e.which == 27) {
10530            obj.close();
10531        }
10532    });
10533
10534    el.slider = obj;
10535
10536    return obj;
10537});
10538
10539jSuites.sorting = (function(el, options) {
10540    var obj = {};
10541    obj.options = {};
10542
10543    var defaults = {
10544        pointer: null,
10545        direction: null,
10546        ondragstart: null,
10547        ondragend: null,
10548        ondrop: null,
10549    }
10550
10551    var dragElement = null;
10552
10553    // Loop through the initial configuration
10554    for (var property in defaults) {
10555        if (options && options.hasOwnProperty(property)) {
10556            obj.options[property] = options[property];
10557        } else {
10558            obj.options[property] = defaults[property];
10559        }
10560    }
10561
10562    el.classList.add('jsorting');
10563
10564    el.addEventListener('dragstart', function(e) {
10565        var position = Array.prototype.indexOf.call(e.target.parentNode.children, e.target);
10566        dragElement = {
10567            element: e.target,
10568            o: position,
10569            d: position
10570        }
10571        e.target.style.opacity = '0.25';
10572
10573        if (typeof(obj.options.ondragstart) == 'function') {
10574            obj.options.ondragstart(el, e.target, e);
10575        }
10576    });
10577
10578    el.addEventListener('dragover', function(e) {
10579        e.preventDefault();
10580
10581        if (getElement(e.target) && dragElement) {
10582            if (e.target.getAttribute('draggable') == 'true' && dragElement.element != e.target) {
10583                if (! obj.options.direction) {
10584                    var condition = e.target.clientHeight / 2 > e.offsetY;
10585                } else {
10586                    var condition = e.target.clientWidth / 2 > e.offsetX;
10587                }
10588
10589                if (condition) {
10590                    e.target.parentNode.insertBefore(dragElement.element, e.target);
10591                } else {
10592                    e.target.parentNode.insertBefore(dragElement.element, e.target.nextSibling);
10593                }
10594
10595                dragElement.d = Array.prototype.indexOf.call(e.target.parentNode.children, dragElement.element);
10596            }
10597        }
10598    });
10599
10600    el.addEventListener('dragleave', function(e) {
10601        e.preventDefault();
10602    });
10603
10604    el.addEventListener('dragend', function(e) {
10605        e.preventDefault();
10606
10607        if (dragElement) {
10608            if (typeof(obj.options.ondragend) == 'function') {
10609                obj.options.ondragend(el, dragElement.element, e);
10610            }
10611
10612            // Cancelled put element to the original position
10613            if (dragElement.o < dragElement.d) {
10614                e.target.parentNode.insertBefore(dragElement.element, e.target.parentNode.children[dragElement.o]);
10615            } else {
10616                e.target.parentNode.insertBefore(dragElement.element, e.target.parentNode.children[dragElement.o].nextSibling);
10617            }
10618
10619            dragElement.element.style.opacity = '';
10620            dragElement = null;
10621        }
10622    });
10623
10624    el.addEventListener('drop', function(e) {
10625        e.preventDefault();
10626
10627        if (dragElement && (dragElement.o != dragElement.d)) {
10628            if (typeof(obj.options.ondrop) == 'function') {
10629                obj.options.ondrop(el, dragElement.o, dragElement.d, dragElement.element, e.target, e);
10630            }
10631        }
10632
10633        dragElement.element.style.opacity = '';
10634        dragElement = null;
10635    });
10636
10637    var getElement = function(element) {
10638        var sorting = false;
10639
10640        function path (element) {
10641            if (element.className) {
10642                if (element.classList.contains('jsorting')) {
10643                    sorting = true;
10644                }
10645            }
10646
10647            if (! sorting) {
10648                path(element.parentNode);
10649            }
10650        }
10651
10652        path(element);
10653
10654        return sorting;
10655    }
10656
10657    for (var i = 0; i < el.children.length; i++) {
10658        if (! el.children[i].hasAttribute('draggable')) {
10659            el.children[i].setAttribute('draggable', 'true');
10660        }
10661    }
10662
10663    el.val = function() {
10664        var id = null;
10665        var data = [];
10666        for (var i = 0; i < el.children.length; i++) {
10667            if (id = el.children[i].getAttribute('data-id')) {
10668                data.push(id);
10669            }
10670        }
10671        return data;
10672    }
10673
10674    return el;
10675});
10676
10677jSuites.tabs = (function(el, options) {
10678    var obj = {};
10679    obj.options = {};
10680
10681    // Default configuration
10682    var defaults = {
10683        data: [],
10684        position: null,
10685        allowCreate: false,
10686        allowChangePosition: false,
10687        onclick: null,
10688        onload: null,
10689        onchange: null,
10690        oncreate: null,
10691        ondelete: null,
10692        onbeforecreate: null,
10693        onchangeposition: null,
10694        animation: false,
10695        hideHeaders: false,
10696        padding: null,
10697        palette: null,
10698        maxWidth: null,
10699    }
10700
10701    // Loop through the initial configuration
10702    for (var property in defaults) {
10703        if (options && options.hasOwnProperty(property)) {
10704            obj.options[property] = options[property];
10705        } else {
10706            obj.options[property] = defaults[property];
10707        }
10708    }
10709
10710    // Class
10711    el.classList.add('jtabs');
10712
10713    var prev = null;
10714    var next = null;
10715    var border = null;
10716
10717    // Helpers
10718    var setBorder = function(index) {
10719        if (obj.options.animation) {
10720            setTimeout(function() {
10721                var rect = obj.headers.children[index].getBoundingClientRect();
10722
10723                if (obj.options.palette == 'modern') {
10724                    border.style.width = rect.width - 4 + 'px';
10725                    border.style.left = obj.headers.children[index].offsetLeft + 2 + 'px';
10726                } else {
10727                    border.style.width = rect.width + 'px';
10728                    border.style.left = obj.headers.children[index].offsetLeft + 'px';
10729                }
10730
10731                if (obj.options.position == 'bottom') {
10732                    border.style.top = '0px';
10733                } else {
10734                    border.style.bottom = '0px';
10735                }
10736            }, 150);
10737        }
10738    }
10739
10740    var updateControls = function(x) {
10741        if (typeof(obj.headers.scrollTo) == 'function') {
10742            obj.headers.scrollTo({
10743                left: x,
10744                behavior: 'smooth',
10745            });
10746        } else {
10747            obj.headers.scrollLeft = x;
10748        }
10749
10750        if (x <= 1) {
10751            prev.classList.add('disabled');
10752        } else {
10753            prev.classList.remove('disabled');
10754        }
10755
10756        if (x >= obj.headers.scrollWidth - obj.headers.offsetWidth) {
10757            next.classList.add('disabled');
10758        } else {
10759            next.classList.remove('disabled');
10760        }
10761
10762        if (obj.headers.scrollWidth <= obj.headers.offsetWidth) {
10763            prev.style.display = 'none';
10764            next.style.display = 'none';
10765        } else {
10766            prev.style.display = '';
10767            next.style.display = '';
10768        }
10769    }
10770
10771    obj.setBorder = setBorder;
10772
10773    // Set value
10774    obj.open = function(index) {
10775        var previous = null;
10776        for (var i = 0; i < obj.headers.children.length; i++) {
10777            if (obj.headers.children[i].classList.contains('jtabs-selected')) {
10778                // Current one
10779                previous = i;
10780            }
10781            // Remote selected
10782            obj.headers.children[i].classList.remove('jtabs-selected');
10783            if (obj.content.children[i]) {
10784                obj.content.children[i].classList.remove('jtabs-selected');
10785            }
10786        }
10787
10788        obj.headers.children[index].classList.add('jtabs-selected');
10789        if (obj.content.children[index]) {
10790            obj.content.children[index].classList.add('jtabs-selected');
10791        }
10792
10793        if (previous != index && typeof(obj.options.onchange) == 'function') {
10794            if (obj.content.children[index]) {
10795                obj.options.onchange(el, obj, index, obj.headers.children[index], obj.content.children[index]);
10796            }
10797        }
10798
10799        // Hide
10800        if (obj.options.hideHeaders == true && (obj.headers.children.length < 3 && obj.options.allowCreate == false)) {
10801            obj.headers.parentNode.style.display = 'none';
10802        } else {
10803            // Set border
10804            setBorder(index);
10805
10806            obj.headers.parentNode.style.display = '';
10807
10808            var x1 = obj.headers.children[index].offsetLeft;
10809            var x2 = x1 + obj.headers.children[index].offsetWidth;
10810            var r1 = obj.headers.scrollLeft;
10811            var r2 = r1 + obj.headers.offsetWidth;
10812
10813            if (! (r1 <= x1 && r2 >= x2)) {
10814                // Out of the viewport
10815                updateControls(x1 - 1);
10816            }
10817        }
10818    }
10819
10820    obj.selectIndex = function(a) {
10821        var index = Array.prototype.indexOf.call(obj.headers.children, a);
10822        if (index >= 0) {
10823            obj.open(index);
10824        }
10825
10826        return index;
10827    }
10828
10829    obj.rename = function(i, title) {
10830        if (! title) {
10831            title = prompt('New title', obj.headers.children[i].innerText);
10832        }
10833        obj.headers.children[i].innerText = title;
10834        obj.open(i);
10835    }
10836
10837    obj.create = function(title, url) {
10838        if (typeof(obj.options.onbeforecreate) == 'function') {
10839            var ret = obj.options.onbeforecreate(el);
10840            if (ret === false) {
10841                return false;
10842            } else {
10843                title = ret;
10844            }
10845        }
10846
10847        var div = obj.appendElement(title);
10848
10849        if (typeof(obj.options.oncreate) == 'function') {
10850            obj.options.oncreate(el, div)
10851        }
10852
10853        setBorder();
10854
10855        return div;
10856    }
10857
10858    obj.remove = function(index) {
10859        return obj.deleteElement(index);
10860    }
10861
10862    obj.nextNumber = function() {
10863        var num = 0;
10864        for (var i = 0; i < obj.headers.children.length; i++) {
10865            var tmp = obj.headers.children[i].innerText.match(/[0-9].*/);
10866            if (tmp > num) {
10867                num = parseInt(tmp);
10868            }
10869        }
10870        if (! num) {
10871            num = 1;
10872        } else {
10873            num++;
10874        }
10875
10876        return num;
10877    }
10878
10879    obj.deleteElement = function(index) {
10880        if (! obj.headers.children[index]) {
10881            return false;
10882        } else {
10883            obj.headers.removeChild(obj.headers.children[index]);
10884            obj.content.removeChild(obj.content.children[index]);
10885        }
10886
10887        obj.open(0);
10888
10889        if (typeof(obj.options.ondelete) == 'function') {
10890            obj.options.ondelete(el, index)
10891        }
10892    }
10893
10894    obj.appendElement = function(title, cb) {
10895        if (! title) {
10896            var title = prompt('Title?', '');
10897        }
10898
10899        if (title) {
10900            // Add content
10901            var div = document.createElement('div');
10902            obj.content.appendChild(div);
10903
10904            // Add headers
10905            var h = document.createElement('div');
10906            h.innerHTML = title;
10907            h.content = div;
10908            obj.headers.insertBefore(h, obj.headers.lastChild);
10909
10910            // Sortable
10911            if (obj.options.allowChangePosition) {
10912                h.setAttribute('draggable', 'true');
10913            }
10914            // Open new tab
10915            obj.selectIndex(h);
10916
10917            // Callback
10918            if (typeof(cb) == 'function') {
10919                cb(div, h);
10920            }
10921
10922            // Return element
10923            return div;
10924        }
10925    }
10926
10927    obj.getActive = function() {
10928        for (var i = 0; i < obj.headers.children.length; i++) {
10929            if (obj.headers.children[i].classList.contains('jtabs-selected')) {
10930                return i
10931            }
10932        }
10933        return 0;
10934    }
10935
10936    obj.updateContent = function(position, newContent) {
10937        if (typeof newContent !== 'string') {
10938            var contentItem = newContent;
10939        } else {
10940            var contentItem = document.createElement('div');
10941            contentItem.innerHTML = newContent;
10942        }
10943
10944        if (obj.content.children[position].classList.contains('jtabs-selected')) {
10945            newContent.classList.add('jtabs-selected');
10946        }
10947
10948        obj.content.replaceChild(newContent, obj.content.children[position]);
10949
10950        setBorder();
10951    }
10952
10953    obj.updatePosition = function(f, t) {
10954        // Ondrop update position of content
10955        if (f > t) {
10956            obj.content.insertBefore(obj.content.children[f], obj.content.children[t]);
10957        } else {
10958            obj.content.insertBefore(obj.content.children[f], obj.content.children[t].nextSibling);
10959        }
10960
10961        // Open destination tab
10962        obj.open(t);
10963
10964        // Call event
10965        if (typeof(obj.options.onchangeposition) == 'function') {
10966            obj.options.onchangeposition(obj.headers, f, t);
10967        }
10968    }
10969
10970    obj.move = function(f, t) {
10971        if (f > t) {
10972            obj.headers.insertBefore(obj.headers.children[f], obj.headers.children[t]);
10973        } else {
10974            obj.headers.insertBefore(obj.headers.children[f], obj.headers.children[t].nextSibling);
10975        }
10976
10977        obj.updatePosition(f, t);
10978    }
10979
10980    obj.setBorder = setBorder;
10981
10982    obj.init = function() {
10983        el.innerHTML = '';
10984
10985        // Make sure the component is blank
10986        obj.headers = document.createElement('div');
10987        obj.content = document.createElement('div');
10988        obj.headers.classList.add('jtabs-headers');
10989        obj.content.classList.add('jtabs-content');
10990
10991        if (obj.options.palette) {
10992            el.classList.add('jtabs-modern');
10993        } else {
10994            el.classList.remove('jtabs-modern');
10995        }
10996
10997        // Padding
10998        if (obj.options.padding) {
10999            obj.content.style.padding = parseInt(obj.options.padding) + 'px';
11000        }
11001
11002        // Header
11003        var header = document.createElement('div');
11004        header.className = 'jtabs-headers-container';
11005        header.appendChild(obj.headers);
11006        if (obj.options.maxWidth) {
11007            header.style.maxWidth = parseInt(obj.options.maxWidth) + 'px';
11008        }
11009
11010        // Controls
11011        var controls = document.createElement('div');
11012        controls.className = 'jtabs-controls';
11013        controls.setAttribute('draggable', 'false');
11014        header.appendChild(controls);
11015
11016        // Append DOM elements
11017        if (obj.options.position == 'bottom') {
11018            el.appendChild(obj.content);
11019            el.appendChild(header);
11020        } else {
11021            el.appendChild(header);
11022            el.appendChild(obj.content);
11023        }
11024
11025        // New button
11026        if (obj.options.allowCreate == true) {
11027            var add = document.createElement('div');
11028            add.className = 'jtabs-add';
11029            add.onclick = function() {
11030                obj.create();
11031            }
11032            controls.appendChild(add);
11033        }
11034
11035        prev = document.createElement('div');
11036        prev.className = 'jtabs-prev';
11037        prev.onclick = function() {
11038            updateControls(obj.headers.scrollLeft - obj.headers.offsetWidth);
11039        }
11040        controls.appendChild(prev);
11041
11042        next = document.createElement('div');
11043        next.className = 'jtabs-next';
11044        next.onclick = function() {
11045            updateControls(obj.headers.scrollLeft + obj.headers.offsetWidth);
11046        }
11047        controls.appendChild(next);
11048
11049        // Data
11050        for (var i = 0; i < obj.options.data.length; i++) {
11051            // Title
11052            if (obj.options.data[i].titleElement) {
11053                var headerItem = obj.options.data[i].titleElement;
11054            } else {
11055                var headerItem = document.createElement('div');
11056            }
11057            // Icon
11058            if (obj.options.data[i].icon) {
11059                var iconContainer = document.createElement('div');
11060                var icon = document.createElement('i');
11061                icon.classList.add('material-icons');
11062                icon.innerHTML = obj.options.data[i].icon;
11063                iconContainer.appendChild(icon);
11064                headerItem.appendChild(iconContainer);
11065            }
11066            // Title
11067            if (obj.options.data[i].title) {
11068                var title = document.createTextNode(obj.options.data[i].title);
11069                headerItem.appendChild(title);
11070            }
11071            // Width
11072            if (obj.options.data[i].width) {
11073                headerItem.style.width = obj.options.data[i].width;
11074            }
11075            // Content
11076            if (obj.options.data[i].contentElement) {
11077                var contentItem = obj.options.data[i].contentElement;
11078            } else {
11079                var contentItem = document.createElement('div');
11080                contentItem.innerHTML = obj.options.data[i].content;
11081            }
11082            obj.headers.appendChild(headerItem);
11083            obj.content.appendChild(contentItem);
11084        }
11085
11086        // Animation
11087        border = document.createElement('div');
11088        border.className = 'jtabs-border';
11089        obj.headers.appendChild(border);
11090
11091        if (obj.options.animation) {
11092            el.classList.add('jtabs-animation');
11093        }
11094
11095        // Events
11096        obj.headers.addEventListener("click", function(e) {
11097            if (e.target.parentNode.classList.contains('jtabs-headers')) {
11098                var target = e.target;
11099            } else {
11100                if (e.target.tagName == 'I') {
11101                    var target = e.target.parentNode.parentNode;
11102                } else {
11103                    var target = e.target.parentNode;
11104                }
11105            }
11106
11107            var index = obj.selectIndex(target);
11108
11109            if (typeof(obj.options.onclick) == 'function') {
11110                obj.options.onclick(el, obj, index, obj.headers.children[index], obj.content.children[index]);
11111            }
11112        });
11113
11114        obj.headers.addEventListener("contextmenu", function(e) {
11115            obj.selectIndex(e.target);
11116        });
11117
11118        if (obj.headers.children.length) {
11119            // Open first tab
11120            obj.open(0);
11121        }
11122
11123        // Update controls
11124        updateControls(0);
11125
11126        if (obj.options.allowChangePosition == true) {
11127            jSuites.sorting(obj.headers, {
11128                direction: 1,
11129                ondrop: function(a,b,c) {
11130                    obj.updatePosition(b,c);
11131                },
11132            });
11133        }
11134
11135        if (typeof(obj.options.onload) == 'function') {
11136            obj.options.onload(el, obj);
11137        }
11138    }
11139
11140    // Loading existing nodes as the data
11141    if (el.children[0] && el.children[0].children.length) {
11142        // Create from existing elements
11143        for (var i = 0; i < el.children[0].children.length; i++) {
11144            var item = obj.options.data && obj.options.data[i] ? obj.options.data[i] : {};
11145
11146            if (el.children[1] && el.children[1].children[i]) {
11147                item.titleElement = el.children[0].children[i];
11148                item.contentElement = el.children[1].children[i];
11149            } else {
11150                item.contentElement = el.children[0].children[i];
11151            }
11152
11153            obj.options.data[i] = item;
11154        }
11155    }
11156
11157    // Remote controller flag
11158    var loadingRemoteData = false;
11159
11160    // Create from data
11161    if (obj.options.data) {
11162        // Append children
11163        for (var i = 0; i < obj.options.data.length; i++) {
11164            if (obj.options.data[i].url) {
11165                jSuites.ajax({
11166                    url: obj.options.data[i].url,
11167                    type: 'GET',
11168                    dataType: 'text/html',
11169                    index: i,
11170                    success: function(result) {
11171                        obj.options.data[this.index].content = result;
11172                    },
11173                    complete: function() {
11174                        obj.init();
11175                    }
11176                });
11177
11178                // Flag loading
11179                loadingRemoteData = true;
11180            }
11181        }
11182    }
11183
11184    if (! loadingRemoteData) {
11185        obj.init();
11186    }
11187
11188    el.tabs = obj;
11189
11190    return obj;
11191});
11192
11193jSuites.tags = (function(el, options) {
11194    // Redefine configuration
11195    if (el.tags) {
11196        return el.tags.setOptions(options, true);
11197    }
11198
11199    var obj = { type:'tags' };
11200    obj.options = {};
11201
11202    // Limit
11203    var limit = function() {
11204        return obj.options.limit && el.children.length >= obj.options.limit ? true : false;
11205    }
11206
11207    // Search helpers
11208    var search = null;
11209    var searchContainer = null;
11210
11211    obj.setOptions = function(options, reset) {
11212        /**
11213         * @typedef {Object} defaults
11214         * @property {(string|Array)} value - Initial value of the compontent
11215         * @property {number} limit - Max number of tags inside the element
11216         * @property {string} search - The URL for suggestions
11217         * @property {string} placeholder - The default instruction text on the element
11218         * @property {validation} validation - Method to validate the tags
11219         * @property {requestCallback} onbeforechange - Method to be execute before any changes on the element
11220         * @property {requestCallback} onchange - Method to be execute after any changes on the element
11221         * @property {requestCallback} onfocus - Method to be execute when on focus
11222         * @property {requestCallback} onblur - Method to be execute when on blur
11223         * @property {requestCallback} onload - Method to be execute when the element is loaded
11224         */
11225        var defaults = {
11226            value: '',
11227            limit: null,
11228            limitMessage: null,
11229            search: null,
11230            placeholder: null,
11231            validation: null,
11232            onbeforepaste: null,
11233            onbeforechange: null,
11234            onlimit: null,
11235            onchange: null,
11236            onfocus: null,
11237            onblur: null,
11238            onload: null,
11239            colors: null,
11240        }
11241
11242        // Loop through though the default configuration
11243        for (var property in defaults) {
11244            if (options && options.hasOwnProperty(property)) {
11245                obj.options[property] = options[property];
11246            } else {
11247                if (typeof(obj.options[property]) == 'undefined' || reset === true) {
11248                    obj.options[property] = defaults[property];
11249                }
11250            }
11251        }
11252
11253        // Placeholder
11254        if (obj.options.placeholder) {
11255            el.setAttribute('data-placeholder', obj.options.placeholder);
11256        } else {
11257            el.removeAttribute('data-placeholder');
11258        }
11259        el.placeholder = obj.options.placeholder;
11260
11261        // Update value
11262        obj.setValue(obj.options.value);
11263
11264        // Validate items
11265        filter();
11266
11267        // Create search box
11268        if (obj.options.search) {
11269            if (! searchContainer) {
11270                searchContainer = document.createElement('div');
11271                el.parentNode.insertBefore(searchContainer, el.nextSibling);
11272
11273                // Create container
11274                search = jSuites.search(searchContainer, {
11275                    data: obj.options.search,
11276                    onselect: function(a,b,c) {
11277                        obj.selectIndex(b,c);
11278                    }
11279                });
11280            }
11281        } else {
11282            if (searchContainer) {
11283                search = null;
11284                searchContainer.remove();
11285                searchContainer = null;
11286            }
11287        }
11288
11289        return obj;
11290    }
11291
11292    /**
11293     * Add a new tag to the element
11294     * @param {(?string|Array)} value - The value of the new element
11295     */
11296    obj.add = function(value, focus) {
11297        if (typeof(obj.options.onbeforechange) == 'function') {
11298            var ret = obj.options.onbeforechange(el, obj, obj.options.value, value);
11299            if (ret === false) {
11300                return false;
11301            } else { 
11302                if (ret != null) {
11303                    value = ret;
11304                }
11305            }
11306        }
11307
11308        // Make sure search is closed
11309        if (search) {
11310            search.close();
11311        }
11312
11313        if (limit()) {
11314            if (typeof(obj.options.onlimit) == 'function') {
11315                obj.options.onlimit(obj, obj.options.limit);
11316            } else if (obj.options.limitMessage) {
11317                alert(obj.options.limitMessage + ' ' + obj.options.limit);
11318            }
11319        } else {
11320            // Get node
11321            var node = jSuites.getNode();
11322
11323            if (node && node.parentNode && node.parentNode.classList.contains('jtags') &&
11324                node.nextSibling && (! (node.nextSibling.innerText && node.nextSibling.innerText.trim()))) {
11325                div = node.nextSibling;
11326            } else {
11327                // Remove not used last item
11328                if (el.lastChild) {
11329                    if (! el.lastChild.innerText.trim()) {
11330                        el.removeChild(el.lastChild);
11331                    }
11332                }
11333
11334                // Mix argument string or array
11335                if (! value || typeof(value) == 'string') {
11336                    var div = createElement(value, value, node);
11337                } else {
11338                    for (var i = 0; i <= value.length; i++) {
11339                        if (! limit()) {
11340                            if (! value[i] || typeof(value[i]) == 'string') {
11341                                var t = value[i] || '';
11342                                var v = null;
11343                            } else {
11344                                var t = value[i].text;
11345                                var v = value[i].value;
11346                            }
11347
11348                            // Add element
11349                            var div = createElement(t, v);
11350                        }
11351                    }
11352                }
11353
11354                // Change
11355                change();
11356            }
11357
11358            // Place caret
11359            if (focus) {
11360                setFocus(div);
11361            }
11362        }
11363    }
11364
11365    obj.setLimit = function(limit) {
11366        obj.options.limit = limit;
11367        var n = el.children.length - limit;
11368        while (el.children.length > limit) {
11369            el.removeChild(el.lastChild);
11370        }
11371    }
11372
11373    // Remove a item node
11374    obj.remove = function(node) {
11375        // Remove node
11376        node.parentNode.removeChild(node);
11377        // Make sure element is not blank
11378        if (! el.children.length) {
11379            obj.add('', true);
11380        } else {
11381            change();
11382        }
11383    }
11384
11385    /**
11386     * Get all tags in the element
11387     * @return {Array} data - All tags as an array
11388     */
11389    obj.getData = function() {
11390        var data = [];
11391        for (var i = 0; i < el.children.length; i++) {
11392            // Get value
11393            var text = el.children[i].innerText.replace("\n", "");
11394            // Get id
11395            var value = el.children[i].getAttribute('data-value');
11396            if (! value) {
11397                value = text;
11398            }
11399            // Item
11400            if (text || value) {
11401                data.push({ text: text, value: value });
11402            }
11403        }
11404        return data;
11405    }
11406
11407    /**
11408     * Get the value of one tag. Null for all tags
11409     * @param {?number} index - Tag index number. Null for all tags.
11410     * @return {string} value - All tags separated by comma
11411     */
11412    obj.getValue = function(index) {
11413        var value = null;
11414
11415        if (index != null) {
11416            // Get one individual value
11417            value = el.children[index].getAttribute('data-value');
11418            if (! value) {
11419                value = el.children[index].innerText.replace("\n", "");
11420            }
11421        } else {
11422            // Get all
11423            var data = [];
11424            for (var i = 0; i < el.children.length; i++) {
11425                value = el.children[i].innerText.replace("\n", "");
11426                if (value) {
11427                    data.push(obj.getValue(i));
11428                }
11429            }
11430            value = data.join(',');
11431        }
11432
11433        return value;
11434    }
11435
11436    /**
11437     * Set the value of the element based on a string separeted by (,|;|\r\n)
11438     * @param {mixed} value - A string or array object with values
11439     */
11440    obj.setValue = function(mixed) {
11441        if (! mixed) {
11442            obj.reset();
11443        } else {
11444            if (el.value != mixed) {
11445                if (Array.isArray(mixed)) {
11446                    obj.add(mixed);
11447                } else {
11448                    // Remove whitespaces
11449                    var text = (''+mixed).trim();
11450                    // Tags
11451                    var data = extractTags(text);
11452                    // Reset
11453                    el.innerHTML = '';
11454                    // Add tags to the element
11455                    obj.add(data);
11456                }
11457            }
11458        }
11459    }
11460
11461    /**
11462     * Reset the data from the element
11463     */
11464    obj.reset = function() {
11465        // Empty class
11466        el.classList.add('jtags-empty');
11467        // Empty element
11468        el.innerHTML = '<div></div>';
11469        // Execute changes
11470        change();
11471    }
11472
11473    /**
11474     * Verify if all tags in the element are valid
11475     * @return {boolean}
11476     */
11477    obj.isValid = function() {
11478        var test = 0;
11479        for (var i = 0; i < el.children.length; i++) {
11480            if (el.children[i].classList.contains('jtags_error')) {
11481                test++;
11482            }
11483        }
11484        return test == 0 ? true : false;
11485    }
11486
11487    /**
11488     * Add one element from the suggestions to the element
11489     * @param {object} item - Node element in the suggestions container
11490     */ 
11491    obj.selectIndex = function(text, value) {
11492        var node = jSuites.getNode();
11493        if (node) {
11494            // Append text to the caret
11495            node.innerText = text;
11496            // Set node id
11497            if (value) {
11498                node.setAttribute('data-value', value);
11499            }
11500            // Remove any error
11501            node.classList.remove('jtags_error');
11502            if (! limit()) {
11503                // Add new item
11504                obj.add('', true);
11505            }
11506        }
11507    }
11508
11509    /**
11510     * Search for suggestions
11511     * @param {object} node - Target node for any suggestions
11512     */
11513    obj.search = function(node) {
11514        // Search for
11515        var terms = node.innerText;
11516    }
11517
11518    // Destroy tags element
11519    obj.destroy = function() {
11520        // Bind events
11521        el.removeEventListener('mouseup', tagsMouseUp);
11522        el.removeEventListener('keydown', tagsKeyDown);
11523        el.removeEventListener('keyup', tagsKeyUp);
11524        el.removeEventListener('paste', tagsPaste);
11525        el.removeEventListener('focus', tagsFocus);
11526        el.removeEventListener('blur', tagsBlur);
11527
11528        // Remove element
11529        el.parentNode.removeChild(el);
11530    }
11531
11532    var setFocus = function(node) {
11533        if (el.children.length) {
11534            var range = document.createRange();
11535            var sel = window.getSelection();
11536            if (! node) {
11537                var node = el.childNodes[el.childNodes.length-1];
11538            }
11539            range.setStart(node, node.length)
11540            range.collapse(true)
11541            sel.removeAllRanges()
11542            sel.addRange(range)
11543            el.scrollLeft = el.scrollWidth;
11544        }
11545    }
11546
11547    var createElement = function(label, value, node) {
11548        var div = document.createElement('div');
11549        div.innerHTML
vendor: 9,474 bytes, lines 11549-11882
11549 = label ? label : '';
11550        if (value) {
11551            div.setAttribute('data-value', value);
11552        }
11553
11554        if (node && node.parentNode.classList.contains('jtags')) {
11555            el.insertBefore(div, node.nextSibling);
11556        } else {
11557            el.appendChild(div);
11558        }
11559
11560        return div;
11561    }
11562
11563    var change = function() {
11564        // Value
11565        var value = obj.getValue();
11566
11567        if (value != obj.options.value) {
11568            obj.options.value = value;
11569            if (typeof(obj.options.onchange) == 'function') {
11570                obj.options.onchange(el, obj, obj.options.value);
11571            }
11572
11573            // Lemonade JS
11574            if (el.value != obj.options.value) {
11575                el.value = obj.options.value;
11576                if (typeof(el.oninput) == 'function') {
11577                    el.oninput({
11578                        type: 'input',
11579                        target: el,
11580                        value: el.value
11581                    });
11582                }
11583            }
11584        }
11585
11586        filter();
11587    }
11588
11589    /**
11590     * Filter tags
11591     */
11592    var filter = function() {
11593        for (var i = 0; i < el.children.length; i++) {
11594            if (el.children[i].tagName === 'DIV') {
11595                // Create label design
11596                if (!obj.getValue(i)) {
11597                    el.children[i].classList.remove('jtags_label');
11598                } else {
11599                    el.children[i].classList.add('jtags_label');
11600
11601                    // Validation in place
11602                    if (typeof (obj.options.validation) == 'function') {
11603                        if (obj.getValue(i)) {
11604                            if (!obj.options.validation(el.children[i], el.children[i].innerText, el.children[i].getAttribute('data-value'))) {
11605                                el.children[i].classList.add('jtags_error');
11606                            } else {
11607                                el.children[i].classList.remove('jtags_error');
11608                            }
11609                        } else {
11610                            el.children[i].classList.remove('jtags_error');
11611                        }
11612                    } else {
11613                        el.children[i].classList.remove('jtags_error');
11614                    }
11615                }
11616            }
11617        }
11618
11619        isEmpty();
11620    }
11621
11622    var isEmpty = function() {
11623        // Can't be empty
11624        if (! el.innerText.trim()) {
11625            if (! el.children.length || el.children[0].tagName === 'BR') {
11626                el.innerHTML = '';
11627                setFocus(createElement());
11628            }
11629        } else {
11630            el.classList.remove('jtags-empty');
11631        }
11632    }
11633
11634    /**
11635     * Extract tags from a string
11636     * @param {string} text - Raw string
11637     * @return {Array} data - Array with extracted tags
11638     */
11639    var extractTags = function(text) {
11640        /** @type {Array} */
11641        var data = [];
11642
11643        /** @type {string} */
11644        var word = '';
11645
11646        // Remove whitespaces
11647        text = text.trim();
11648
11649        if (text) {
11650            for (var i = 0; i < text.length; i++) {
11651                if (text[i] == ',' || text[i] == ';' || text[i] == '\n') {
11652                    if (word) {
11653                        data.push(word.trim());
11654                        word = '';
11655                    }
11656                } else {
11657                    word += text[i];
11658                }
11659            }
11660
11661            if (word) {
11662                data.push(word);
11663            }
11664        }
11665
11666        return data;
11667    }
11668
11669    /** @type {number} */
11670    var anchorOffset = 0;
11671
11672    /**
11673     * Processing event keydown on the element
11674     * @param e {object}
11675     */
11676    var tagsKeyDown = function(e) {
11677        // Anchoroffset
11678        anchorOffset = window.getSelection().anchorOffset;
11679
11680        // Verify if is empty
11681        isEmpty();
11682
11683        // Comma
11684        if (e.key === 'Tab'  || e.key === ';' || e.key === ',') {
11685            var n = window.getSelection().anchorOffset;
11686            if (n > 1) {
11687                if (limit()) {
11688                    if (typeof(obj.options.onlimit) == 'function') {
11689                        obj.options.onlimit(obj, obj.options.limit)
11690                    }
11691                } else {
11692                    obj.add('', true);
11693                }
11694            }
11695            e.preventDefault();
11696        } else if (e.key == 'Enter') {
11697            if (! search || ! search.isOpened()) {
11698                var n = window.getSelection().anchorOffset;
11699                if (n > 1) {
11700                    if (! limit()) {
11701                        obj.add('', true);
11702                    }
11703                }
11704                e.preventDefault();
11705            }
11706        } else if (e.key == 'Backspace') {
11707            // Back space - do not let last item to be removed
11708            if (el.children.length == 1 && window.getSelection().anchorOffset < 1) {
11709                e.preventDefault();
11710            }
11711        }
11712
11713        // Search events
11714        if (search) {
11715            search.keydown(e);
11716        }
11717
11718        // Verify if is empty
11719        isEmpty();
11720    }
11721
11722    /**
11723     * Processing event keyup on the element
11724     * @param e {object}
11725     */
11726    var tagsKeyUp = function(e) {
11727        if (e.which == 39) {
11728            // Right arrow
11729            var n = window.getSelection().anchorOffset;
11730            if (n > 1 && n == anchorOffset) {
11731                obj.add('', true);
11732            }
11733        } else if (e.which == 13 || e.which == 38 || e.which == 40) {
11734            e.preventDefault();
11735        } else {
11736            if (search) {
11737                search.keyup(e);
11738            }
11739        }
11740
11741        filter();
11742    }
11743
11744    /**
11745     * Processing event paste on the element
11746     * @param e {object}
11747     */
11748    var tagsPaste =  function(e) {
11749        if (e.clipboardData || e.originalEvent.clipboardData) {
11750            var text = (e.originalEvent || e).clipboardData.getData('text/plain');
11751        } else if (window.clipboardData) {
11752            var text = window.clipboardData.getData('Text');
11753        }
11754
11755        var data = extractTags(text);
11756
11757        if (typeof(obj.options.onbeforepaste) == 'function') {
11758            var ret = obj.options.onbeforepaste(el, obj, data);
11759            if (ret === false) {
11760                e.preventDefault();
11761                return false;
11762            } else {
11763                if (ret) {
11764                    data = ret;
11765                }
11766            }
11767        }
11768
11769        if (data.length > 1) {
11770            obj.add(data, true);
11771            e.preventDefault();
11772        } else if (data[0]) {
11773            document.execCommand('insertText', false, data[0])
11774            e.preventDefault();
11775        }
11776    }
11777
11778    /**
11779     * Processing event mouseup on the element
11780     * @param e {object}
11781     */
11782    var tagsMouseUp = function(e) {
11783        if (e.target.parentNode && e.target.parentNode.classList.contains('jtags')) {
11784            if (e.target.classList.contains('jtags_label') || e.target.classList.contains('jtags_error')) {
11785                var rect = e.target.getBoundingClientRect();
11786                if (rect.width - (e.clientX - rect.left) < 16) {
11787                    obj.remove(e.target);
11788                }
11789            }
11790        }
11791
11792        // Set focus in the last item
11793        if (e.target == el) {
11794            setFocus();
11795        }
11796    }
11797
11798    var tagsFocus = function() {
11799        if (! el.classList.contains('jtags-focus')) {
11800            if (! el.children.length || obj.getValue(el.children.length - 1)) {
11801                if (! limit()) {
11802                    createElement('');
11803                }
11804            }
11805
11806            if (typeof(obj.options.onfocus) == 'function') {
11807                obj.options.onfocus(el, obj, obj.getValue());
11808            }
11809
11810            el.classList.add('jtags-focus');
11811        }
11812    }
11813
11814    var tagsBlur = function() {
11815        if (el.classList.contains('jtags-focus')) {
11816            if (search) {
11817                search.close();
11818            }
11819
11820            for (var i = 0; i < el.children.length - 1; i++) {
11821                // Create label design
11822                if (! obj.getValue(i)) {
11823                    el.removeChild(el.children[i]);
11824                }
11825            }
11826
11827            change();
11828
11829            el.classList.remove('jtags-focus');
11830
11831            if (typeof(obj.options.onblur) == 'function') {
11832                obj.options.onblur(el, obj, obj.getValue());
11833            }
11834        }
11835    }
11836
11837    var init = function() {
11838        // Bind events
11839        if ('touchend' in document.documentElement === true) {
11840            el.addEventListener('touchend', tagsMouseUp);
11841        } else {
11842            el.addEventListener('mouseup', tagsMouseUp);
11843        }
11844
11845        el.addEventListener('keydown', tagsKeyDown);
11846        el.addEventListener('keyup', tagsKeyUp);
11847        el.addEventListener('paste', tagsPaste);
11848        el.addEventListener('focus', tagsFocus);
11849        el.addEventListener('blur', tagsBlur);
11850
11851        // Editable
11852        el.setAttribute('contenteditable', true);
11853
11854        // Prepare container
11855        el.classList.add('jtags');
11856
11857        // Initial options
11858        obj.setOptions(options);
11859
11860        if (typeof(obj.options.onload) == 'function') {
11861            obj.options.onload(el, obj);
11862        }
11863
11864        // Change methods
11865        el.change = obj.setValue;
11866
11867        // Global generic value handler
11868        el.val = function(val) {
11869            if (val === undefined) {
11870                return obj.getValue();
11871            } else {
11872                obj.setValue(val);
11873            }
11874        }
11875
11876        el.tags = obj;
11877    }
11878
11879    init();
11880
11881    return obj;
11882}
vendor: 7,559 bytes, lines 11882-12110
11882);
11883
11884jSuites.toolbar = (function(el, options) {
11885    // New instance
11886    var obj = { type:'toolbar' };
11887    obj.options = {};
11888
11889    // Default configuration
11890    var defaults = {
11891        app: null,
11892        container: false,
11893        badge: false,
11894        title: false,
11895        responsive: false,
11896        maxWidth: null,
11897        bottom: true,
11898        items: [],
11899    }
11900
11901    // Loop through our object
11902    for (var property in defaults) {
11903        if (options && options.hasOwnProperty(property)) {
11904            obj.options[property] = options[property];
11905        } else {
11906            obj.options[property] = defaults[property];
11907        }
11908    }
11909
11910    if (! el && options.app && options.app.el) {
11911        el = document.createElement('div');
11912        options.app.el.appendChild(el);
11913    }
11914
11915    // Arrow
11916    var toolbarArrow = document.createElement('div');
11917    toolbarArrow.classList.add('jtoolbar-item');
11918    toolbarArrow.classList.add('jtoolbar-arrow');
11919
11920    var toolbarFloating = document.createElement('div');
11921    toolbarFloating.classList.add('jtoolbar-floating');
11922    toolbarArrow.appendChild(toolbarFloating);
11923
11924    obj.selectItem = function(element) {
11925        var elements = toolbarContent.children;
11926        for (var i = 0; i < elements.length; i++) {
11927            if (element != elements[i]) {
11928                elements[i].classList.remove('jtoolbar-selected');
11929            }
11930        }
11931        element.classList.add('jtoolbar-selected');
11932    }
11933
11934    obj.hide = function() {
11935        jSuites.animation.slideBottom(el, 0, function() {
11936            el.style.display = 'none';
11937        });
11938    }
11939
11940    obj.show = function() {
11941        el.style.display = '';
11942        jSuites.animation.slideBottom(el, 1);
11943    }
11944
11945    obj.get = function() {
11946        return el;
11947    }
11948
11949    obj.setBadge = function(index, value) {
11950        toolbarContent.children[index].children[1].firstChild.innerHTML = value;
11951    }
11952
11953    obj.destroy = function() {
11954        toolbar.remove();
11955        el.innerHTML = '';
11956    }
11957
11958    obj.update = function(a, b) {
11959        for (var i = 0; i < toolbarContent.children.length; i++) {
11960            // Toolbar element
11961            var toolbarItem = toolbarContent.children[i];
11962            // State management
11963            if (typeof(toolbarItem.updateState) == 'function') {
11964                toolbarItem.updateState(el, obj, toolbarItem, a, b);
11965            }
11966        }
11967        for (var i = 0; i < toolbarFloating.children.length; i++) {
11968            // Toolbar element
11969            var toolbarItem = toolbarFloating.children[i];
11970            // State management
11971            if (typeof(toolbarItem.updateState) == 'function') {
11972                toolbarItem.updateState(el, obj, toolbarItem, a, b);
11973            }
11974        }
11975    }
11976
11977    obj.create = function(items) {
11978        // Reset anything in the toolbar
11979        toolbarContent.innerHTML = '';
11980        // Create elements in the toolbar
11981        for (var i = 0; i < items.length; i++) {
11982            var toolbarItem = document.createElement('div');
11983            toolbarItem.classList.add('jtoolbar-item');
11984
11985            if (items[i].width) {
11986                toolbarItem.style.width = parseInt(items[i].width) + 'px'; 
11987            }
11988
11989            if (items[i].k) {
11990                toolbarItem.k = items[i].k;
11991            }
11992
11993            if (items[i].tooltip) {
11994                toolbarItem.setAttribute('title', items[i].tooltip);
11995            }
11996
11997            // Id
11998            if (items[i].id) {
11999                toolbarItem.setAttribute('id', items[i].id);
12000            }
12001
12002            // Selected
12003            if (items[i].updateState) {
12004                toolbarItem.updateState = items[i].updateState;
12005            }
12006
12007            if (items[i].active) {
12008                toolbarItem.classList.add('jtoolbar-active');
12009            }
12010
12011            if (items[i].disabled) {
12012                toolbarItem.classList.add('jtoolbar-disabled');
12013            }
12014
12015            if (items[i].type == 'select' || items[i].type == 'dropdown') {
12016                jSuites.picker(toolbarItem, items[i]);
12017            } else if (items[i].type == 'divisor') {
12018                toolbarItem.classList.add('jtoolbar-divisor');
12019            } else if (items[i].type == 'label') {
12020                toolbarItem.classList.add('jtoolbar-label');
12021                toolbarItem.innerHTML = items[i].content;
12022            } else {
12023                // Material icons
12024                var toolbarIcon = document.createElement('i');
12025                if (typeof(items[i].class) === 'undefined') {
12026                    toolbarIcon.classList.add('material-icons');
12027                } else {
12028                    var c = items[i].class.split(' ');
12029                    for (var j = 0; j < c.length; j++) {
12030                        toolbarIcon.classList.add(c[j]);
12031                    }
12032                }
12033                toolbarIcon.innerHTML = items[i].content ? items[i].content : '';
12034                toolbarItem.appendChild(toolbarIcon);
12035
12036                // Badge options
12037                if (obj.options.badge == true) {
12038                    var toolbarBadge = document.createElement('div');
12039                    toolbarBadge.classList.add('jbadge');
12040                    var toolbarBadgeContent = document.createElement('div');
12041                    toolbarBadgeContent.innerHTML = items[i].badge ? items[i].badge : '';
12042                    toolbarBadge.appendChild(toolbarBadgeContent);
12043                    toolbarItem.appendChild(toolbarBadge);
12044                }
12045
12046                // Title
12047                if (items[i].title) {
12048                    if (obj.options.title == true) {
12049                        var toolbarTitle = document.createElement('span');
12050                        toolbarTitle.innerHTML = items[i].title;
12051                        toolbarItem.appendChild(toolbarTitle);
12052                    } else {
12053                        toolbarItem.setAttribute('title', items[i].title);
12054                    }
12055                }
12056
12057                if (obj.options.app && items[i].route) {
12058                    // Route
12059                    toolbarItem.route = items[i].route;
12060                    // Onclick for route
12061                    toolbarItem.onclick = function() {
12062                        obj.options.app.pages(this.route);
12063                    }
12064                    // Create pages
12065                    obj.options.app.pages(items[i].route, {
12066                        toolbarItem: toolbarItem,
12067                        closed: true
12068                    });
12069                }
12070            }
12071
12072            if (items[i].onclick) {
12073                toolbarItem.onclick = items[i].onclick.bind(items[i], el, obj, toolbarItem);
12074            }
12075
12076            toolbarContent.appendChild(toolbarItem);
12077        }
12078
12079        // Fits to the page
12080        setTimeout(function() {
12081            obj.refresh();
12082        }, 0);
12083    }
12084
12085    obj.open = function() {
12086        toolbarArrow.classList.add('jtoolbar-arrow-selected');
12087
12088        var rectElement = el.getBoundingClientRect();
12089        var rect = toolbarFloating.getBoundingClientRect();
12090        if (rect.bottom > window.innerHeight || obj.options.bottom) {
12091            toolbarFloating.style.bottom = '0';
12092        } else {
12093            toolbarFloating.style.removeProperty('bottom');
12094        }
12095
12096        toolbarFloating.style.right = '0';
12097
12098        toolbarArrow.children[0].focus();
12099        // Start tracking
12100        jSuites.tracking(obj, true);
12101    }
12102
12103    obj.close = function() {
12104        toolbarArrow.classList.remove('jtoolbar-arrow-selected')
12105        // End tracking
12106        jSuites.tracking(obj, false);
12107    }
12108
12109    obj.refresh = function() {
12110        if (obj.options.responsive == true) {
12111            // Width of the c
12112            var rect = el.parentNode.getBoundingClientRect();
12113            if (! obj.options.maxWidth) {
12114                obj.options.maxWidth = rect.width;
12115            }
12116            // Available parent space
12117            var available = parseInt(obj.options.maxWidth);
12118            // Remove arrow
12119            if (toolbarArrow.parentNode) {
12120                toolbarArrow.parentNode.removeChild(toolbarArrow);
12121            }
12122            // Move all items to the toolbar
12123            while (toolbarFloating.firstChild) {
12124                toolbarContent.appendChild(toolbarFloating.firstChild);
12125            }
12126            // Toolbar is larger than the parent, move elements to the floating element
12127            if (available < toolbarContent.offsetWidth) {
12128                // Give space to the floating element
12129                available -= 50;
12130                // Move to the floating option
12131                while (toolbarContent.lastChild && available < toolbarContent.offsetWidth) {
12132                    toolbarFloating.insertBefore(toolbarContent.lastChild, toolbarFloating.firstChild);
12133                }
12134            }
12135            // Show arrow
12136            if (toolbarFloating.children.length > 0) {
12137                toolbarContent.appendChild(toolbarArrow);
12138            }
12139        }
12140    }
12141
12142    obj.setReadonly = function(state) {
12143        state = state ? 'add' : 'remove';
12144        el.classList[state]('jtoolbar-disabled');
12145    }
12146
12147    el.onclick = function(e) {
12148        var element = jSuites.findElement(e.target, 'jtoolbar-item');
12149        if (element) {
12150            obj.selectItem(element);
12151        }
12152
12153        if (e.target.classList.contains('jtoolbar-arrow')) {
12154            obj.open();
12155        }
12156    }
12157
12158    window.addEventListener('resize', function() {
12159        obj.refresh();
12160    });
12161
12162    // Toolbar
12163    el.classList.add('jtoolbar');
12164    // Reset content
12165    el.innerHTML = '';
12166    // Container
12167    if (obj.options.container == true) {
12168        el.classList.add('jtoolbar-container');
12169    }
12170    // Content
12171    var toolbarContent = document.createElement('div');
12172    el.appendChild(toolbarContent);
12173    // Special toolbar for mobile applications
12174    if (obj.options.app) {
12175        el.classList.add('jtoolbar-mobile');
12176    }
12177    // Create toolbar
12178    obj.create(obj.options.items);
12179    // Shortcut
12180    el.toolbar = obj;
12181
12182    return obj;
12183});
12184
12185jSuites.validations = (function() {
12186    /**
12187     * Options: Object,
12188     * Properties:
12189     * Constraint,
12190     * Reference,
12191     * Value
12192     */
12193
12194    var isNumeric = function(num) {
12195        return !isNaN(num) && num !== null && num !== '';
12196    }
12197
12198    var numberCriterias = {
12199        'between': function(value, range) {
12200            return value >= range[0] && value <= range[1];
12201        },
12202        'not between': function(value, range) {
12203            return value < range[0] || value > range[1];
12204        },
12205        '<': function(value, range) {
12206            return value < range[0];
12207        },
12208        '<=': function(value, range) {
12209            return value <= range[0];
12210        },
12211        '>': function(value, range) {
12212            return value > range[0];
12213        },
12214        '>=': function(value, range) {
12215            return value >= range[0];
12216        },
12217        '=': function(value, range) {
12218            return value == range[0];
12219        },
12220        '!=': function(value, range) {
12221            return value != range[0];
12222        },
12223    }
12224
12225    var dateCriterias = {
12226        'valid date': function() {
12227            return true;
12228        },
12229        '=': function(value, range) {
12230            return value === range[0];
12231        },
12232        '<': function(value, range) {
12233            return value < range[0];
12234        },
12235        '<=': function(value, range) {
12236            return value <= range[0];
12237        },
12238        '>': function(value, range) {
12239            return value > range[0];
12240        },
12241        '>=': function(value, range) {
12242            return value >= range[0];
12243        },
12244        'between': function(value, range) {
12245            return value >= range[0] && value <= range[1];
12246        },
12247        'not between': function(value, range) {
12248            return value < range[0] || value > range[1];
12249        },
12250    }
12251
12252    var textCriterias = {
12253        'contains': function(value, range) {
12254            return value.includes(range[0]);
12255        },
12256        'not contains': function(value, range) {
12257            return !value.includes(range[0]);
12258        },
12259        'begins with': function(value, range) {
12260            return value.startsWith(range[0]);
12261        },
12262        'ends with': function(value, range) {
12263            return value.endsWith(range[0]);
12264        },
12265        '=': function(value, range) {
12266            return value === range[0];
12267        },
12268        'valid email': function(value) {
12269            var pattern = new RegExp(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
12270
12271            return pattern.test(value);
12272        },
12273        'valid url': function(value) {
12274            var pattern = new RegExp(/(((https?:\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]+)/ig);
12275
12276            return pattern.test(value);
12277        },
12278    }
12279
12280    // Component router
12281    var component = function(value, options) {
12282        if (typeof(component[options.type]) === 'function') {
12283            if (options.allowBlank && value === '') {
12284                return true;
12285            }
12286
12287            return component[options.type](value, options);
12288        }
12289        return null;
12290    }
12291    
12292    component.url = function() {
12293        var pattern = new RegExp(/(((https?:\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]+)/ig);
12294        return pattern.test(data) ? true : false;
12295    }
12296
12297    component.email = function(data) {
12298        var pattern = new RegExp(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
12299        return data && pattern.test(data) ? true : false; 
12300    }
12301    
12302    component.required = function(data) {
12303        return data.trim() ? true : false;
12304    }
12305
12306    component.exist = function(data, options) {
12307        return !!data.toString();
12308    }
12309
12310    component['not exist'] = function(data, options) {
12311        return !data.toString();
12312    }
12313
12314    component.empty = function(data) {
12315        return !data.toString();
12316    }
12317
12318    component.notEmpty = function(data) {
12319        return !!data.toString();
12320    }
12321
12322    component.number = function(data, options) {
12323       if (! isNumeric(data)) {
12324           return false;
12325       }
12326
12327       if (!options || !options.criteria) {
12328           return true;
12329       }
12330
12331       if (!numberCriterias[options.criteria]) {
12332           return false;
12333       }
12334
12335       var values = options.value.map(function(num) {
12336          return parseFloat(num);
12337       })
12338
12339       return numberCriterias[options.criteria](data, values);
12340   };
12341
12342    component.login = function(data) {
12343        var pattern = new RegExp(/^[a-zA-Z0-9\_\-\.\s+]+$/);
12344        return data && pattern.test(data) ? true : false;
12345    }
12346
12347    component.list = function(data, options) {
12348        var dataType = typeof data;
12349        if (dataType !== 'string' && dataType !== 'number') {
12350            return false;
12351        }
12352        if (typeof(options.value[0]) === 'string') {
12353            var list = options.value[0].split(',');
12354        } else {
12355            var list = options.value[0];
12356        }
12357
12358        var validOption = list.findIndex(function name(item) {
12359            return item == data;
12360        });
12361
12362        return validOption > -1;
12363    }
12364
12365    component.date = function(data, options) {
12366        if (new Date(data) == 'Invalid Date') {
12367            return false;
12368        }
12369
12370        if (!options || !options.criteria) {
12371            return true;
12372        }
12373
12374        if (!dateCriterias[options.criteria]) {
12375            return false;
12376        }
12377
12378        var values = options.value.map(function(date) {
12379            return new Date(date).getTime();
12380        });
12381
12382        return dateCriterias[options.criteria](new Date(data).getTime(), values);
12383    }
12384
12385    component.text = function(data, options) {
12386        if (typeof data !== 'string') {
12387            return false;
12388        }
12389
12390        if (!options || !options.criteria) {
12391            return true;
12392        }
12393
12394        if (!textCriterias[options.criteria]) {
12395            return false;
12396        }
12397
12398        return textCriterias[options.criteria](data, options.value);
12399    }
12400
12401    component.textLength = function(data, options) {
12402        data = data.toString();
12403
12404        return component.number(data.length, options);
12405    }
12406
12407    return component;
12408})();
12409
12410
12411
12412    return jSuites;
12413
12414})));

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.