PageSourceSearch

https://job.villedegrandbassam.ci/assets/demo/default/base/scripts.bundle.js

js villedegrandbassam.ci collected 2026-10-02 22:48:00 UTC 291,471 bytes, 9,164 lines download raw bytes

1"use strict";
2/**
3 * @class KTUtil  base utilize class that privides helper functions
4 */
5// Polyfill
6// matches polyfill
7this.Element && function(ElementPrototype) {
8    ElementPrototype.matches = ElementPrototype.matches ||
9        ElementPrototype.matchesSelector ||
10        ElementPrototype.webkitMatchesSelector ||
11        ElementPrototype.msMatchesSelector ||
12        function(selector) {
13            var node = this,
14                nodes = (node.parentNode || node.document).querySelectorAll(selector),
15                i = -1;
16            while (nodes[++i] && nodes[i] != node);
17            return !!nodes[i];
18        }
19}(Element.prototype);
20
21// closest polyfill
22this.Element && function(ElementPrototype) {
23    ElementPrototype.closest = ElementPrototype.closest ||
24        function(selector) {
25            var el = this;
26            while (el.matches && !el.matches(selector)) el = el.parentNode;
27            return el.matches ? el : null;
28        }
29}(Element.prototype);
30
31// remove polyfill
32if (!('remove' in Element.prototype)) {
33    Element.prototype.remove = function() {
34        if (this.parentNode) {
35            this.parentNode.removeChild(this);
36        }
37    };
38}
39
40// matches polyfill
41this.Element && function(ElementPrototype) {
42    ElementPrototype.matches = ElementPrototype.matches ||
43        ElementPrototype.matchesSelector ||
44        ElementPrototype.webkitMatchesSelector ||
45        ElementPrototype.msMatchesSelector ||
46        function(selector) {
47            var node = this,
48                nodes = (node.parentNode || node.document).querySelectorAll(selector),
49                i = -1;
50            while (nodes[++i] && nodes[i] != node);
51            return !!nodes[i];
52        }
53}(Element.prototype);
54
55//
56// requestAnimationFrame polyfill by Erik Möller.
57//  With fixes from Paul Irish and Tino Zijdel
58//
59//  http://paulirish.com/2011/requestanimationframe-for-smart-animating/
60//  http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
61//
62//  MIT license
63//
64(function() {
65    var lastTime = 0;
66    var vendors = ['webkit', 'moz'];
67    for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
68        window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
69        window.cancelAnimationFrame =
70            window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
71    }
72
73    if (!window.requestAnimationFrame)
74        window.requestAnimationFrame = function(callback) {
75            var currTime = new Date().getTime();
76            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
77            var id = window.setTimeout(function() {
78                callback(currTime + timeToCall);
79            }, timeToCall);
80            lastTime = currTime + timeToCall;
81            return id;
82        };
83
84    if (!window.cancelAnimationFrame)
85        window.cancelAnimationFrame = function(id) {
86            clearTimeout(id);
87        };
88}());
89
90// Source: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/prepend()/prepend().md
91(function(arr) {
92    arr.forEach(function(item) {
93        if (item.hasOwnProperty('prepend')) {
94            return;
95        }
96        Object.defineProperty(item, 'prepend', {
97            configurable: true,
98            enumerable: true,
99            writable: true,
100            value: function prepend() {
101                var argArr = Array.prototype.slice.call(arguments),
102                    docFrag = document.createDocumentFragment();
103
104                argArr.forEach(function(argItem) {
105                    var isNode = argItem instanceof Node;
106                    docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem)));
107                });
108
109                this.insertBefore(docFrag, this.firstChild);
110            }
111        });
112    });
113})([Element.prototype, Document.prototype, DocumentFragment.prototype]);
114
115// Global variables 
116window.KTUtilElementDataStore = {};
117window.KTUtilElementDataStoreID = 0;
118window.KTUtilDelegatedEventHandlers = {};
119
120var KTUtil = function() {
121
122    var resizeHandlers = [];
123
124    /** @type {object} breakpoints The device width breakpoints **/
125    var breakpoints = {
126        sm: 544, // Small screen / phone           
127        md: 768, // Medium screen / tablet            
128        lg: 1024, // Large screen / desktop        
129        xl: 1200 // Extra large screen / wide desktop
130    };
131
132    /**
133     * Handle window resize event with some 
134     * delay to attach event handlers upon resize complete 
135     */
136    var _windowResizeHandler = function() {
137        var _runResizeHandlers = function() {
138            // reinitialize other subscribed elements
139            for (var i = 0; i < resizeHandlers.length; i++) {
140                var each = resizeHandlers[i];
141                each.call();
142            }
143        };
144
145        var timeout = false; // holder for timeout id
146        var delay = 250; // delay after event is "complete" to run callback
147
148        window.addEventListener('resize', function() {
149            clearTimeout(timeout);
150            timeout = setTimeout(function() {
151                _runResizeHandlers();
152            }, delay); // wait 50ms until window resize finishes.
153        });
154    };
155
156    return {
157        /**
158         * Class main initializer.
159         * @param {object} options.
160         * @returns null
161         */
162        //main function to initiate the theme
163        init: function(options) {
164            if (options && options.breakpoints) {
165                breakpoints = options.breakpoints;
166            }
167
168            _windowResizeHandler();
169        },
170
171        /**
172         * Adds window resize event handler.
173         * @param {function} callback function.
174         */
175        addResizeHandler: function(callback) {
176            resizeHandlers.push(callback);
177        },
178
179        /**
180         * Removes window resize event handler.
181         * @param {function} callback function.
182         */
183        removeResizeHandler: function(callback) {
184            for (var i = 0; i < resizeHandlers.length; i++) {
185                if (callback === resizeHandlers[i]) {
186                    delete resizeHandlers[i];
187                }
188            }
189        },
190
191        /**
192         * Trigger window resize handlers.
193         */
194        runResizeHandlers: function() {
195            _runResizeHandlers();
196        },
197
198        resize: function() {
199            if (typeof(Event) === 'function') {
200                // modern browsers
201                window.dispatchEvent(new Event('resize'));
202            } else {
203                // for IE and other old browsers
204                // causes deprecation warning on modern browsers
205                var evt = window.document.createEvent('UIEvents'); 
206                evt.initUIEvent('resize', true, false, window, 0); 
207                window.dispatchEvent(evt);
208            }
209        },
210
211        /**
212         * Get GET parameter value from URL.
213         * @param {string} paramName Parameter name.
214         * @returns {string}  
215         */
216        getURLParam: function(paramName) {
217            var searchString = window.location.search.substring(1),
218                i, val, params = searchString.split("&");
219
220            for (i = 0; i < params.length; i++) {
221                val = params[i].split("=");
222                if (val[0] == paramName) {
223                    return unescape(val[1]);
224                }
225            }
226
227            return null;
228        },
229
230        /**
231         * Checks whether current device is mobile touch.
232         * @returns {boolean}  
233         */
234        isMobileDevice: function() {
235            return (this.getViewPort().width < this.getBreakpoint('lg') ? true : false);
236        },
237
238        /**
239         * Checks whether current device is desktop.
240         * @returns {boolean}  
241         */
242        isDesktopDevice: function() {
243            return KTUtil.isMobileDevice() ? false : true;
244        },
245
246        /**
247         * Gets browser window viewport size. Ref:
248         * http://andylangton.co.uk/articles/javascript/get-viewport-size-javascript/
249         * @returns {object}  
250         */
251        getViewPort: function() {
252            var e = window,
253                a = 'inner';
254            if (!('innerWidth' in window)) {
255                a = 'client';
256                e = document.documentElement || document.body;
257            }
258
259            return {
260                width: e[a + 'Width'],
261                height: e[a + 'Height']
262            };
263        },
264
265        /**
266         * Checks whether given device mode is currently activated.
267         * @param {string} mode Responsive mode name(e.g: desktop,
268         *     desktop-and-tablet, tablet, tablet-and-mobile, mobile)
269         * @returns {boolean}  
270         */
271        isInResponsiveRange: function(mode) {
272            var breakpoint = this.getViewPort().width;
273
274            if (mode == 'general') {
275                return true;
276            } else if (mode == 'desktop' && breakpoint >
276= (this.getBreakpoint('lg') + 1)) {
277                return true;
278            } else if (mode == 'tablet' && (breakpoint >= (this.getBreakpoint('md') + 1) && breakpoint < this.getBreakpoint('lg'))) {
279                return true;
280            } else if (mode == 'mobile' && breakpoint <= this.getBreakpoint('md')) {
281                return true;
282            } else if (mode == 'desktop-and-tablet' && breakpoint >= (this.getBreakpoint('md') + 1)) {
283                return true;
284            } else if (mode == 'tablet-and-mobile' && breakpoint <= this.getBreakpoint('lg')) {
285                return true;
286            } else if (mode == 'minimal-desktop-and-below' && breakpoint <= this.getBreakpoint('xl')) {
287                return true;
288            }
289
290            return false;
291        },
292
293        /**
294         * Generates unique ID for give prefix.
295         * @param {string} prefix Prefix for generated ID
296         * @returns {boolean}  
297         */
298        getUniqueID: function(prefix) {
299            return prefix + Math.floor(Math.random() * (new Date()).getTime());
300        },
301
302        /**
303         * Gets window width for give breakpoint mode.
304         * @param {string} mode Responsive mode name(e.g: xl, lg, md, sm)
305         * @returns {number}  
306         */
307        getBreakpoint: function(mode) {
308            return breakpoints[mode];
309        },
310
311        /**
312         * Checks whether object has property matchs given key path.
313         * @param {object} obj Object contains values paired with given key path
314         * @param {string} keys Keys path seperated with dots
315         * @returns {object}  
316         */
317        isset: function(obj, keys) {
318            var stone;
319
320            keys = keys || '';
321
322            if (keys.indexOf('[') !== -1) {
323                throw new Error('Unsupported object path notation.');
324            }
325
326            keys = keys.split('.');
327
328            do {
329                if (obj === undefined) {
330                    return false;
331                }
332
333                stone = keys.shift();
334
335                if (!obj.hasOwnProperty(stone)) {
336                    return false;
337                }
338
339                obj = obj[stone];
340
341            } while (keys.length);
342
343            return true;
344        },
345
346        /**
347         * Gets highest z-index of the given element parents
348         * @param {object} el jQuery element object
349         * @returns {number}  
350         */
351        getHighestZindex: function(el) {
352            var elem = KTUtil.get(el),
353                position, value;
354
355            while (elem && elem !== document) {
356                // Ignore z-index if position is set to a value where z-index is ignored by the browser
357                // This makes behavior of this function consistent across browsers
358                // WebKit always returns auto if the element is positioned
359                position = KTUtil.css(elem, 'position');
360
361                if (position === "absolute" || position === "relative" || position === "fixed") {
362                    // IE returns 0 when zIndex is not specified
363                    // other browsers return a string
364                    // we ignore the case of nested elements with an explicit value of 0
365                    // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
366                    value = parseInt(KTUtil.css(elem, 'z-index'));
367
368                    if (!isNaN(value) && value !== 0) {
369                        return value;
370                    }
371                }
372
373                elem = elem.parentNode;
374            }
375
376            return null;
377        },
378
379        /**
380         * Checks whether the element has any parent with fixed positionfreg
381         * @param {object} el jQuery element object
382         * @returns {boolean}  
383         */
384        hasFixedPositionedParent: function(el) {
385            while (el && el !== document) {
386                position = KTUtil.css(el, 'position');
387
388                if (position === "fixed") {
389                    return true;
390                }
391
392                el = el.parentNode;
393            }
394
395            return false;
396        },
397
398        /**
399         * Simulates delay
400         */
401        sleep: function(milliseconds) {
402            var start = new Date().getTime();
403            for (var i = 0; i < 1e7; i++) {
404                if ((new Date().getTime() - start) > milliseconds) {
405                    break;
406                }
407            }
408        },
409
410        /**
411         * Gets randomly generated integer value within given min and max range
412         * @param {number} min Range start value
413         * @param {number} max Range end value
414         * @returns {number}
415         */
416        getRandomInt: function(min, max) {
417            return Math.floor(Math.random() * (max - min + 1)) + min;
418        },
419
420        /**
421         * Checks whether Angular library is included
422         * @returns {boolean}  
423         */
424        isAngularVersion: function() {
425            return window.Zone !== undefined ? true : false;
426        },
427
428        // jQuery Workarounds
429
430        // Deep extend:  $.extend(true, {}, objA, objB);
431        deepExtend: function(out) {
432            out = out || {};
433
434            for (var i = 1; i < arguments.length; i++) {
435                var obj = arguments[i];
436
437                if (!obj)
438                    continue;
439
440                for (var key in obj) {
441                    if (obj.hasOwnProperty(key)) {
442                        if (typeof obj[key] === 'object')
443                            out[key] = KTUtil.deepExtend(out[key], obj[key]);
444                        else
445                            out[key] = obj[key];
446                    }
447                }
448            }
449
450            return out;
451        },
452
453        // extend:  $.extend({}, objA, objB); 
454        extend: function(out) {
455            out = out || {};
456
457            for (var i = 1; i < arguments.length; i++) {
458                if (!arguments[i])
459                    continue;
460
461                for (var key in arguments[i]) {
462                    if (arguments[i].hasOwnProperty(key))
463                        out[key] = arguments[i][key];
464                }
465            }
466
467            return out;
468        },
469
470        get: function(query) {
471            var el;
472
473            if (query === document) {
474                return document;
475            }
476
477            if (!!(query && query.nodeType === 1)) {
478                return query;
479            }
480
481            if (el = document.getElementById(query)) {
482                return el;
483            } else if (el = document.getElementsByTagName(query)) {
484                return el[0];
485            } else if (el = document.getElementsByClassName(query)) {
486                return el[0];
487            } else {
488                return null;
489            }
490        },
491
492        getByID: function(query) {
493            if (!!(query && query.nodeType === 1)) {
494                return query;
495            }
496
497            return document.getElementById(query);
498        },
499
500        getByTag: function(query) {
501            var el;
502            
503            if (el = document.getElementsByTagName(query)) {
504                return el[0];
505            } else {
506                return null;
507            }
508        },
509
510        getByClass: function(query) {
511            var el;
512            
513            if (el = document.getElementsByClassName(query)) {
514                return el[0];
515            } else {
516                return null;
517            }
518        },
519
520        /**
521         * Checks whether the element has given classes
522         * @param {object} el jQuery element object
523         * @param {string} Classes string
524         * @returns {boolean}  
525         */
526        hasClasses: function(el, classes) {
527            if (!el) {
528                return;
529            }
530
531            var classesArr = classes.split(" ");
532
533            for (var i = 0; i < classesArr.length; i++) {
534                if (KTUtil.hasClass(el, KTUtil.trim(classesArr[i])) == false) {
535                    return false;
536                }
537            }
538
539            return true;
540        },
541
542        hasClass: function(el, className) {
543            if (!el) {
544                return;
545            }
546
547            return el.classList ? el.classList.contains(className) : new RegExp('\\b' + className + '\\b').test(el.className);
548        },
549
550        addClass: function(el, className) {
551            if (!el || typeof className === 'undefined') {
552                return;
553            }
554
555            var classNames = className.split(' ');
556
557            if (el.classList) {
558                for (var i = 0; i < classNames.length; i++) {
559                    if (classNames[i] && classNames[i].length > 0) {
560                        el.classList.add(KTUtil.trim(classNames[i]));
561                    }
562                }
563            } else if (!KTUtil.hasClass(el, className)) {
564                for (var i = 0; i < classNames.length; i++) {
565                    el.className += ' ' + KTUtil.trim(classNames[i]);
566                }
567            }
568        },
569
570        removeClass: function(el, className) {
571          if (!el || typeof className === 'undefined') {
572                return;
573            }
574
575            var classNames = className.split(' ');
576
577            if (el.classList) {
578                for (var i = 0; i < classNames.length; i++) {
579                    el.classList.remove(KTUtil.trim(classNames[i]));
580                }
581            } else if (KTUtil.hasClass(el, className)) {
582                for (var i = 0; i < classNames.length; i++) {
583                    el.className = el.className.replace(new RegExp('\\b' + KTUtil.trim(classNames[i]) + '\\b', 'g'), '');
584                }
585            }
586        },
587
588        triggerCustomEvent: function(el, eventName, data) {
589            if (window.CustomEvent) {
590                var event = new CustomEvent(eventName, {
591                    detail: data
592                });
593            } else {
594                var event = document.createEvent('CustomEvent');
595                event.initCustomEvent(eventName, true, true, data);
596            }
597
598            el.dispatchEvent(event);
599        },
600
601        triggerEvent: function(node, eventName) {
602            // Make sure we use the ownerDocument from the provided node to avoid cross-window problems
603            var doc;
604            if (node.ownerDocument) {
605                doc = node.ownerDocument;
606            } else if (node.nodeType == 9) {
607                // the node may be the document itself, nodeType 9 = DOCUMENT_NODE
608                doc = node;
609            } else {
610                throw new Error("Invalid node passed to fireEvent: " + node.id);
611            }
612
613            if (node.dispatchEvent) {
614                // Gecko-style approach (now the standard) takes more work
615                var eventClass = "";
616
617                // Different events have different event classes.
618                // If this switch statement can't map an eventName to an eventClass,
619                // the event firing is going to fail.
620                switch (eventName) {
621                case "click": // Dispatching of 'click' appears to not work correctly in Safari. Use 'mousedown' or 'mouseup' instead.
622                case "mouseenter":
623                case "mouseleave":
624                case "mousedown":
625                case "mouseup":
626                    eventClass = "MouseEvents";
627                    break;
628
629                case "focus":
630                case "change":
631                case "blur":
632                case "select":
633                    eventClass = "HTMLEvents";
634                    break;
635
636                default:
637                    throw "fireEvent: Couldn't find an event class for event '" + eventName + "'.";
638                    break;
639                }
640                var event = doc.createEvent(eventClass);
641
642                var bubbles = eventName == "change" ? false : true;
643                event.initEvent(eventName, bubbles, true); // All events created as bubbling and cancelable.
644
645                event.synthetic = true; // allow detection of synthetic events
646                // The second parameter says go ahead with the default action
647                node.dispatchEvent(event, true);
648            } else if (node.fireEvent) {
649                // IE-old school style
650                var event = doc.createEventObject();
651                event.synthetic = true; // allow detection of synthetic events
652                node.fireEvent("on" + eventName, event);
653            }
654        },
655
656        index: function( elm ){ 
657            elm = KTUtil.get(elm);
658            var c = elm.parentNode.children, i = 0;
659            for(; i < c.length; i++ )
660                if( c[i] == elm ) return i;
661        },
662
663        trim: function(string) {
664            return string.trim();
665        },
666
667        eventTriggered: function(e) {
668            if (e.currentTarget.dataset.triggered) {
669                return true;
670            } else {
671                e.currentTarget.dataset.triggered = true;
672
673                return false;
674            }
675        },
676
677        remove: function(el) {
678            if (el && el.parentNode) {
679                el.parentNode.removeChild(el);
680            }
681        },
682
683        find: function(parent, query) {
684            parent = KTUtil.get(parent);
685            if (parent) {
686                return parent.querySelector(query);
687            }            
688        },
689
690        findAll: function(parent, query) {
691            parent = KTUtil.get(parent);
692            if (parent) {
693                return parent.querySelectorAll(query);
694            } 
695        },
696
697        insertAfter: function(el, referenceNode) {
698            return referenceNode.parentNode.insertBefore(el, referenceNode.nextSibling);
699        },
700
701        parents: function(el, query) {
702            function collectionHas(a, b) { //helper function (see below)
703                for (var i = 0, len = a.length; i < len; i++) {
704                    if (a[i] == b) return true;
705                }
706
707                return false;
708            }
709
710            function findParentBySelector(el, selector) {
711                var all = document.querySelectorAll(selector);
712                var cur = el.parentNode;
713
714                while (cur && !collectionHas(all, cur)) { //keep going up until you find a match
715                    cur = cur.parentNode; //go up
716                }
717
718                return cur; //will return null if not found
719            }
720
721            return findParentBySelector(el, query);
722        },
723
724        children: function(el, selector, log) {
725            if (!el || !el.childNodes) {
726                return;
727            }
728
729            var result = [],
730                i = 0,
731                l = el.childNodes.length;
732
733            for (var i; i < l; ++i) {
734                if (el.childNodes[i].nodeType == 1 && KTUtil.matches(el.childNodes[i], selector, log)) {
735                    result.push(el.childNodes[i]);
736                }
737            }
738
739            return result;
740        },
741
742        child: function(el, selector, log) {
743            var children = KTUtil.children(el, selector, log);
744
745            return children ? children[0] : null;
746        },
747
748        matches: function(el, selector, log) {
749            var p = Element.prototype;
750            var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) {
751                return [].indexOf.call(document.querySelectorAll(s), this) !== -1;
752            };
753
754            if (el && el.tagName) {
755                return f.call(el, selector);
756            } else {
757                return false;
758            }
759        },
760
761        data: function(element) {
762            element = KTUtil.get(element);
763
764            return {
765                set: function(name, data) {
766                    if (element === undefined) {
767                        return;
768                    }
769
770                    if (element.customDataTag === undefined) {
771                        KTUtilElementDataStoreID++;
772                        element.customDataTag = KTUtilElementDataStoreID;
773                    }
774
775                    if (KTUtilElementDataStore[element.customDataTag] === undefined) {
776                        KTUtilElementDataStore[element.customDataTag] = {};
777                    }
778
779                    KTUtilElementDataStore[element.customDataTag][name] = data;
780                },
781
782                get: function(name) {
783                    if (element === undefined) {
784                        return;
785                    }
786
787                    if (element.customDataTag === undefined) { 
788                        return null;
789                    }
790
791                    return this.has(name) ? KTUtilElementDataStore[element.customDataTag][name] : null;
792                },
793
794                has: function(name) {
795                    if (element === undefined) {
796                        return false;
797                    }
798                    
799                    if (element.customDataTag === undefined) { 
800                        return false;
801                    }
802
803                    return (KTUtilElementDataStore[element.customDataTag] && KTUtilElementDataStore[element.customDataTag][name]) ? true : false;
804                },
805
806                remove: function(name) {
807                    if (element && this.has(name)) {
808                        delete KTUtilElementDataStore[element.customDataTag][name];
809                    }
810                }
811            };
812        },
813
814        outerWidth: function(el, margin) {
815            var width;
816
817            if (margin === true) {
818                var width = parseFloat(el.offsetWidth);
819                width += parseFloat(KTUtil.css(el, 'margin-left')) + parseFloat(KTUtil.css(el, 'margin-right'));
820
821                return parseFloat(width);
822            } else {
823                var width = parseFloat(el.offsetWidth);
824
825                return width;
826            }
827        },
828
829        offset: function(elem) {
830            var rect, win;
831            elem = KTUtil.get(elem);
832
833            if ( !elem ) {
834                return;
835            }
836
837            // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
838            // Support: IE <=11 only
839            // Running getBoundingClientRect on a
840            // disconnected node in IE throws an error
841
842            if ( !elem.getClientRects().length ) {
843                return { top: 0, left: 0 };
844            }
845
846            // Get document-relative position by adding viewport scroll to viewport-relative gBCR
847            rect = elem.getBoundingClientRect();
848            win = elem.ownerDocument.defaultView;
849
850            return {
851                top: rect.top + win.pageYOffset,
852                left: rect.left + win.pageXOffset
853            };
854        },
855
856        height: function(el) {
857            return KTUtil.css(el, 'height');
858        },
859
860        visible: function(el) {
861            return !(el.offsetWidth === 0 && el.offsetHeight === 0);
862        },
863
864        attr: function(el, name, value) {
865            el = KTUtil.get(el);
866
867            if (el == undefined) {
868                return;
869            }
870
871            if (value !== undefined) {
872                el.setAttribute(name, value);
873            } else {
874                return el.getAttribute(name);
875            }
876        },
877
878        hasAttr: function(el, name) {
879            el = KTUtil.get(el);
880
881            if (el == undefined) {
882                return;
883            }
884
885            return el.getAttribute(name) ? true : false;
886        },
887
888        removeAttr: function(el, name) {
889            el = KTUtil.get(el);
890
891            if (el == undefined) {
892                return;
893            }
894
895            el.removeAttribute(name);
896        },
897
898        animate: function(from, to, duration, update, easing, done) {
899            /**
900             * TinyAnimate.easings
901             *  Adapted from jQuery Easing
902             */
903            var easings = {};
904            var easing;
905
906            easings.linear = function(t, b, c, d) {
907                return c * t / d + b;
908            };
909
910            easing = easings.linear;
911
912            // Early bail out if called incorrectly
913            if (typeof from !== 'number' ||
914                typeof to !== 'number' ||
915                typeof duration !== 'number' ||
916                typeof update !== 'function') {
917                return;
918            }
919
920            // Create mock done() function if necessary
921            if (typeof done !== 'function') {
922                done = function() {};
923            }
924
925            // Pick implementation (requestAnimationFrame | setTimeout)
926            var rAF = window.requestAnimationFrame || function(callback) {
927                window.setTimeout(callback, 1000 / 50);
928            };
929
930            // Animation loop
931            var canceled = false;
932            var change = to - from;
933
934            function loop(timestamp) {
935                var time = (timestamp || +new Date()) - start;
936
937                if (time >= 0) {
938                    update(easing(time, from, change, duration));
939                }
940                if (time >= 0 && time >= duration) {
941                    update(to);
942                    done();
943                } else {
944                    rAF(loop);
945                }
946            }
947
948            update(from);
949
950            // Start animation loop
951            var start = window.performance && window.performance.now ? window.performance.now() : +new Date();
952
953            rAF(loop);
954        },
955
956        actualCss: function(el, prop, cache) {
957            el = KTUtil.get(el);
958            var css = '';
959            
960            if (el instanceof HTMLElement === false) {
961                return;
962            }
963
964            if (!el.getAttribute('kt-hidden-' + prop) || cache === false) {
965                var value;
966
967                // the element is hidden so:
968                // making the el block so we can meassure its height but still be hidden
969                css = el.style.cssText;
970                el.style.cssText = 'position: absolute; visibility: hidden; display: block;';
971
972                if (prop == 'width') {
973                    value = el.offsetWidth;
974                } else if (prop == 'height') {
975                    value = el.offsetHeight;
976                }
977
978                el.style.cssText = css;
979
980                // store it in cache
981                el.setAttribute('kt-hidden-' + prop, value);
982
983                return parseFloat(value);
984            } else {
985                // store it in cache
986                return parseFloat(el.getAttribute('kt-hidden-' + prop));
987            }
988        },
989
990        actualHeight: function(el, cache) {
991            return KTUtil.actualCss(el, 'height', cache);
992        },
993
994        actualWidth: function(el, cache) {
995            return KTUtil.actualCss(el, 'width', cache);
996        },
997
998        getScroll: function(element, method) {
999            // The passed in `method` value should be 'Top' or 'Left'
1000            method = 'scroll' + method;
1001            return (element == window || element == document) ? (
1002                self[(method == 'scrollTop') ? 'pageYOffset' : 'pageXOffset'] ||
1003                (browserSupportsBoxModel && document.documentElement[method]) ||
1004                document.body[method]
1005            ) : element[method];
1006        },
1007
1008        css: function(el, styleProp, value) {
1009            el = KTUtil.get(el);
1010
1011            if (!el) {
1012                return;
1013            }
1014
1015            if (value !== undefined) {
1016                el.style[styleProp] = value;
1017            } else {
1018                var value, defaultView = (el.ownerDocument || document).defaultView;
1019                // W3C standard way:
1020                if (defaultView && defaultView.getComputedStyle) {
1021                    // sanitize property name to css notation
1022                    // (hyphen separated words eg. font-Size)
1023                    styleProp = styleProp.replace(/([A-Z])/g, "-$1").toLowerCase();
1024                    return defaultView.getComputedStyle(el, null).getPropertyValue(styleProp);
1025                } else if (el.currentStyle) { // IE
1026                    // sanitize property name to camelCase
1027                    styleProp = styleProp.replace(/\-(\w)/g, function(str, letter) {
1028                        return letter.toUpperCase();
1029                    });
1030                    value = el.currentStyle[styleProp];
1031                    // convert other units to pixels on IE
1032                    if (/^\d+(em|pt|%|ex)?$/i.test(value)) {
1033                        return (function(value) {
1034                            var oldLeft = el.style.left,
1035                                oldRsLeft = el.runtimeStyle.left;
1036                            el.runtimeStyle.left = el.currentStyle.left;
1037                            el.style.left = value || 0;
1038                            value = el.style.pixelLeft + "px";
1039                            el.style.left = oldLeft;
1040                            el.runtimeStyle.left = oldRsLeft;
1041                            return value;
1042                        })(value);
1043                    }
1044                    return value;
1045                }
1046            }
1047        },
1048
1049        slide: function(el, dir, speed, callback, recalcMaxHeight) {
1050            if (!el || (dir == 'up' && KTUtil.visible(el) === false) || (dir == 'down' && KTUtil.visible(el) === true)) {
1051                return;
1052            }
1053
1054            speed = (speed ? speed : 600);
1055            var calcHeight = KTUtil.actualHeight(el);
1056            var calcPaddingTop = false;
1057            var calcPaddingBottom = false;
1058
1059            if (KTUtil.css(el, 'padding-top') && KTUtil.data(el).has('slide-pad
1059ding-top') !== true) {
1060                KTUtil.data(el).set('slide-padding-top', KTUtil.css(el, 'padding-top'));
1061            }
1062
1063            if (KTUtil.css(el, 'padding-bottom') && KTUtil.data(el).has('slide-padding-bottom') !== true) {
1064                KTUtil.data(el).set('slide-padding-bottom', KTUtil.css(el, 'padding-bottom'));
1065            }
1066
1067            if (KTUtil.data(el).has('slide-padding-top')) {
1068                calcPaddingTop = parseInt(KTUtil.data(el).get('slide-padding-top'));
1069            }
1070
1071            if (KTUtil.data(el).has('slide-padding-bottom')) {
1072                calcPaddingBottom = parseInt(KTUtil.data(el).get('slide-padding-bottom'));
1073            }
1074
1075            if (dir == 'up') { // up          
1076                el.style.cssText = 'display: block; overflow: hidden;';
1077
1078                if (calcPaddingTop) {
1079                    KTUtil.animate(0, calcPaddingTop, speed, function(value) {
1080                        el.style.paddingTop = (calcPaddingTop - value) + 'px';
1081                    }, 'linear');
1082                }
1083
1084                if (calcPaddingBottom) {
1085                    KTUtil.animate(0, calcPaddingBottom, speed, function(value) {
1086                        el.style.paddingBottom = (calcPaddingBottom - value) + 'px';
1087                    }, 'linear');
1088                }
1089
1090                KTUtil.animate(0, calcHeight, speed, function(value) {
1091                    el.style.height = (calcHeight - value) + 'px';
1092                }, 'linear', function() {
1093                    callback();
1094                    el.style.height = '';
1095                    el.style.display = 'none';
1096                });
1097
1098
1099            } else if (dir == 'down') { // down
1100                el.style.cssText = 'display: block; overflow: hidden;';
1101
1102                if (calcPaddingTop) {
1103                    KTUtil.animate(0, calcPaddingTop, speed, function(value) {
1104                        el.style.paddingTop = value + 'px';
1105                    }, 'linear', function() {
1106                        el.style.paddingTop = '';
1107                    });
1108                }
1109
1110                if (calcPaddingBottom) {
1111                    KTUtil.animate(0, calcPaddingBottom, speed, function(value) {
1112                        el.style.paddingBottom = value + 'px';
1113                    }, 'linear', function() {
1114                        el.style.paddingBottom = '';
1115                    });
1116                }
1117
1118                KTUtil.animate(0, calcHeight, speed, function(value) {
1119                    el.style.height = value + 'px';
1120                }, 'linear', function() {
1121                    callback();
1122                    el.style.height = '';
1123                    el.style.display = '';
1124                    el.style.overflow = '';
1125                });
1126            }
1127        },
1128
1129        slideUp: function(el, speed, callback) {
1130            KTUtil.slide(el, 'up', speed, callback);
1131        },
1132
1133        slideDown: function(el, speed, callback) {
1134            KTUtil.slide(el, 'down', speed, callback);
1135        },
1136
1137        show: function(el, display) {
1138            el.style.display = (display ? display : 'block');
1139        },
1140
1141        hide: function(el) {
1142            el.style.display = 'none';
1143        },
1144
1145        addEvent: function(el, type, handler, one) {
1146            el = KTUtil.get(el);
1147            if (typeof el !== 'undefined') {
1148                el.addEventListener(type, handler);
1149            }
1150        },
1151
1152        removeEvent: function(el, type, handler) {
1153            el = KTUtil.get(el);
1154            el.removeEventListener(type, handler);
1155        },
1156
1157        on: function(element, selector, event, handler) {
1158            if (!selector) {
1159                return;
1160            }
1161
1162            var eventId = KTUtil.getUniqueID('event');
1163
1164            KTUtilDelegatedEventHandlers[eventId] = function(e) {
1165                var targets = element.querySelectorAll(selector);
1166                var target = e.target;
1167
1168                while (target && target !== element) {
1169                    for (var i = 0, j = targets.length; i < j; i++) {
1170                        if (target === targets[i]) {
1171                            handler.call(target, e);
1172                        }
1173                    }
1174
1175                    target = target.parentNode;
1176                }
1177            }
1178
1179            KTUtil.addEvent(element, event, KTUtilDelegatedEventHandlers[eventId]);
1180
1181            return eventId;
1182        },
1183
1184        off: function(element, event, eventId) {
1185            if (!element || !KTUtilDelegatedEventHandlers[eventId]) {
1186                return;
1187            }
1188
1189            KTUtil.removeEvent(element, event, KTUtilDelegatedEventHandlers[eventId]);
1190
1191            delete KTUtilDelegatedEventHandlers[eventId];
1192        },
1193
1194        one: function onetime(el, type, callback) {
1195            el = KTUtil.get(el);
1196
1197            el.addEventListener(type, function callee(e) {
1198                // remove event
1199                if (e.target && e.target.removeEventListener) {
1200                    e.target.removeEventListener(e.type, callee);                    
1201                }
1202                
1203                // call handler
1204                return callback(e);
1205            });
1206        },
1207
1208        hash: function(str) {
1209            var hash = 0,
1210                i, chr;
1211
1212            if (str.length === 0) return hash;
1213            for (i = 0; i < str.length; i++) {
1214                chr = str.charCodeAt(i);
1215                hash = ((hash << 5) - hash) + chr;
1216                hash |= 0; // Convert to 32bit integer
1217            }
1218
1219            return hash;
1220        },
1221
1222        animateClass: function(el, animationName, callback) {
1223            var animation;
1224            var animations = {
1225                animation: 'animationend',
1226                OAnimation: 'oAnimationEnd',
1227                MozAnimation: 'mozAnimationEnd',
1228                WebkitAnimation: 'webkitAnimationEnd',
1229                msAnimation: 'msAnimationEnd',
1230            };
1231
1232            for (var t in animations) {
1233                if (el.style[t] !== undefined) {
1234                    animation = animations[t];
1235                }
1236            }
1237
1238            KTUtil.addClass(el, 'animated ' + animationName);
1239
1240            KTUtil.one(el, animation, function() {
1241                KTUtil.removeClass(el, 'animated ' + animationName);
1242            });
1243
1244            if (callback) {
1245                KTUtil.one(el, animation, callback);
1246            }
1247        },
1248
1249        transitionEnd: function(el, callback) {
1250            var transition;
1251            var transitions = {
1252                transition: 'transitionend',
1253                OTransition: 'oTransitionEnd',
1254                MozTransition: 'mozTransitionEnd',
1255                WebkitTransition: 'webkitTransitionEnd',
1256                msTransition: 'msTransitionEnd'
1257            };
1258
1259            for (var t in transitions) {
1260                if (el.style[t] !== undefined) {
1261                    transition = transitions[t];
1262                }
1263            }
1264
1265            KTUtil.one(el, transition, callback);
1266        },
1267
1268        animationEnd: function(el, callback) {
1269            var animation;
1270            var animations = {
1271                animation: 'animationend',
1272                OAnimation: 'oAnimationEnd',
1273                MozAnimation: 'mozAnimationEnd',
1274                WebkitAnimation: 'webkitAnimationEnd',
1275                msAnimation: 'msAnimationEnd'
1276            };
1277
1278            for (var t in animations) {
1279                if (el.style[t] !== undefined) {
1280                    animation = animations[t];
1281                }
1282            }
1283
1284            KTUtil.one(el, animation, callback);
1285        },
1286
1287        animateDelay: function(el, value) {
1288            var vendors = ['webkit-', 'moz-', 'ms-', 'o-', ''];
1289            for (var i = 0; i < vendors.length; i++) {
1290                KTUtil.css(el, vendors[i] + 'animation-delay', value);
1291            }
1292        },
1293
1294        animateDuration: function(el, value) {
1295            var vendors = ['webkit-', 'moz-', 'ms-', 'o-', ''];
1296            for (var i = 0; i < vendors.length; i++) {
1297                KTUtil.css(el, vendors[i] + 'animation-duration', value);
1298            }
1299        },
1300
1301        scrollTo: function(target, offset, duration) {
1302            var duration = duration ? duration : 500;
1303            var target = KTUtil.get(target);
1304            var targetPos = target ? KTUtil.offset(target).top : 0;
1305            var scrollPos = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
1306            var from, to;
1307
1308            if (targetPos > scrollPos) {
1309                from = targetPos;
1310                to = scrollPos;
1311            } else {
1312                from = scrollPos;
1313                to = targetPos;
1314            }
1315
1316            if (offset) {
1317                to += offset;
1318            }
1319
1320            KTUtil.animate(from, to, duration, function(value) {
1321                document.documentElement.scrollTop = value;
1322                document.body.parentNode.scrollTop = value;
1323                document.body.scrollTop = value;
1324            }); //, easing, done
1325        },
1326
1327        scrollTop: function(offset, duration) {
1328            KTUtil.scrollTo(null, offset, duration);
1329        },
1330
1331        isArray: function(obj) {
1332            return obj && Array.isArray(obj);
1333        },
1334
1335        ready: function(callback) {
1336            if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading") {
1337                callback();
1338            } else {
1339                document.addEventListener('DOMContentLoaded', callback);
1340            }
1341        },
1342
1343        isEmpty: function(obj) {
1344            for (var prop in obj) {
1345                if (obj.hasOwnProperty(prop)) {
1346                    return false;
1347                }
1348            }
1349
1350            return true;
1351        },
1352
1353        numberString: function(nStr) {
1354            nStr += '';
1355            var x = nStr.split('.');
1356            var x1 = x[0];
1357            var x2 = x.length > 1 ? '.' + x[1] : '';
1358            var rgx = /(\d+)(\d{3})/;
1359            while (rgx.test(x1)) {
1360                x1 = x1.replace(rgx, '$1' + ',' + '$2');
1361            }
1362            return x1 + x2;
1363        },
1364
1365        detectIE: function() {
1366            var ua = window.navigator.userAgent;
1367
1368            // Test values; Uncomment to check result …
1369
1370            // IE 10
1371            // ua = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)';
1372
1373            // IE 11
1374            // ua = 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko';
1375
1376            // Edge 12 (Spartan)
1377            // ua = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0';
1378
1379            // Edge 13
1380            // ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586';
1381
1382            var msie = ua.indexOf('MSIE ');
1383            if (msie > 0) {
1384                // IE 10 or older => return version number
1385                return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
1386            }
1387
1388            var trident = ua.indexOf('Trident/');
1389            if (trident > 0) {
1390                // IE 11 => return version number
1391                var rv = ua.indexOf('rv:');
1392                return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
1393            }
1394
1395            var edge = ua.indexOf('Edge/');
1396            if (edge > 0) {
1397                // Edge (IE 12+) => return version number
1398                return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
1399            }
1400
1401            // other browser
1402            return false;
1403        },
1404
1405        isRTL: function() {
1406            return (KTUtil.attr(KTUtil.get('html'), 'direction') == 'rtl');
1407        },
1408
1409        // Scroller
1410        scrollInit: function(element, options) {
1411            if(!element) return;
1412            // Define init function
1413            function init() {
1414                var ps;
1415                var height;
1416
1417                if (options.height instanceof Function) {
1418                    height = parseInt(options.height.call());
1419                } else {
1420                    height = parseInt(options.height);
1421                }
1422
1423                // Destroy scroll on table and mobile modes
1424                if (options.disableForMobile && KTUtil.isInResponsiveRange('tablet-and-mobile')) {
1425                    if (ps = KTUtil.data(element).get('ps')) {
1426                        if (options.resetHeightOnDestroy) {
1427                            KTUtil.css(element, 'height', 'auto');
1428                        } else {
1429                            KTUtil.css(element, 'overflow', 'auto');
1430                            if (height > 0) {
1431                                KTUtil.css(element, 'height', height + 'px');
1432                            }
1433                        }
1434
1435                        ps.destroy();
1436                        ps = KTUtil.data(element).remove('ps');
1437                    } else if (height > 0){
1438                        KTUtil.css(element, 'overflow', 'auto');
1439                        KTUtil.css(element, 'height', height + 'px');
1440                    }
1441
1442                    return;
1443                }
1444
1445                if (height > 0) {
1446                    KTUtil.css(element, 'height', height + 'px');
1447                }
1448
1449                KTUtil.css(element, 'overflow', 'hidden');
1450
1451                // Init scroll
1452                if (ps = KTUtil.data(element).get('ps')) {
1453                    ps.update();
1454                } else {
1455                    KTUtil.addClass(element, 'kt-scroll');
1456                    ps = new PerfectScrollbar(element, {
1457                        wheelSpeed: 0.5,
1458                        swipeEasing: true,
1459                        wheelPropagation: false,
1460                        minScrollbarLength: 40,
1461                        maxScrollbarLength: 300, 
1462                        suppressScrollX: KTUtil.attr(element, 'data-scroll-x') != 'true' ? true : false
1463                    });
1464
1465                    KTUtil.data(element).set('ps', ps);
1466                }
1467            }
1468
1469            // Init
1470            init();
1471
1472            // Handle window resize
1473            if (options.handleWindowResize) {
1474                KTUtil.addResizeHandler(function() {
1475                    init();
1476                });
1477            }
1478        },
1479
1480        scrollUpdate: function(element) {
1481            var ps;
1482            if (ps = KTUtil.data(element).get('ps')) {
1483                ps.update();
1484            }
1485        },
1486
1487        scrollUpdateAll: function(parent) {
1488            var scrollers = KTUtil.findAll(parent, '.ps');
1489            for (var i = 0, len = scrollers.length; i < len; i++) {
1490                KTUtil.scrollerUpdate(scrollers[i]);
1491            }
1492        },
1493
1494        scrollDestroy: function(element) {
1495            var ps;
1496            if (ps = KTUtil.data(element).get('ps')) {
1497                ps.destroy();
1498                ps = KTUtil.data(element).remove('ps');
1499            }
1500        },
1501
1502        setHTML: function(el, html) {
1503            if (KTUtil.get(el)) {
1504                KTUtil.get(el).innerHTML = html;
1505            }
1506        },
1507
1508        getHTML: function(el) {
1509            if (KTUtil.get(el)) {
1510                return KTUtil.get(el).innerHTML;
1511            }
1512        } 
1513    }
1514}();
1515
1516// Initialize KTUtil class on document ready
1517KTUtil.ready(function() {
1518    KTUtil.init();
1519});
1520
1521// CSS3 Transitions only after page load(.kt-page-loading class added to body tag and remove with JS on page load
1521)
1522window.onload = function() {    
1523    KTUtil.removeClass(KTUtil.get('body'), 'kt-page--loading');
1524}
1525"use strict";
1526
1527/**
1528 * @class KApp
1529 */
1530
1531var KTApp = function() {
1532    /** @type {object} colors State colors **/
1533    var colors = {};
1534
1535    var initTooltip = function(el) {
1536        var skin = el.data('skin') ? 'tooltip-' + el.data('skin') : '';
1537        var width = el.data('width') == 'auto' ? 'tooltop-auto-width' : '';
1538        var triggerValue = el.data('trigger') ? el.data('trigger') : 'hover';
1539        var placement = el.data('placement') ? el.data('placement') : 'left';
1540
1541        el.tooltip({
1542            trigger: triggerValue,
1543            template: '<div class="tooltip ' + skin + ' ' + width + '" role="tooltip">\
1544                <div class="arrow"></div>\
1545                <div class="tooltip-inner"></div>\
1546            </div>'
1547        });
1548    }
1549
1550    var initTooltips = function() {
1551        // init bootstrap tooltips
1552        $('[data-toggle="kt-tooltip"]').each(function() {
1553            initTooltip($(this));
1554        });
1555    }
1556
1557    var initPopover = function(el) {
1558        var skin = el.data('skin') ? 'popover-' + el.data('skin') : '';
1559        var triggerValue = el.data('trigger') ? el.data('trigger') : 'hover';
1560
1561        el.popover({
1562            trigger: triggerValue,
1563            template: '\
1564            <div class="popover ' + skin + '" role="tooltip">\
1565                <div class="arrow"></div>\
1566                <h3 class="popover-header"></h3>\
1567                <div class="popover-body"></div>\
1568            </div>'
1569        });
1570    }
1571
1572    var initPopovers = function() {
1573        // init bootstrap popover
1574        $('[data-toggle="kt-popover"]').each(function() {
1575            initPopover($(this));
1576        });
1577    }
1578
1579    var initFileInput = function() {
1580        // init bootstrap popover
1581        $('.custom-file-input').on('change', function() {
1582            var fileName = $(this).val();
1583            $(this).next('.custom-file-label').addClass("selected").html(fileName);
1584        });
1585    }
1586
1587    var initPortlet = function(el, options) {
1588        // init portlet tools
1589        var el = $(el);
1590        var portlet = new KTPortlet(el[0], options);
1591    }
1592
1593    var initPortlets = function() {
1594        // init portlet tools
1595        $('[data-ktportlet="true"]').each(function() {
1596            var el = $(this);
1597
1598            if (el.data('data-ktportlet-initialized') !== true) {
1599                initPortlet(el, {});
1600                el.data('data-ktportlet-initialized', true);
1601            }
1602        });
1603    }
1604
1605    var initScroll = function() {
1606        $('[data-scroll="true"]').each(function() {
1607            var el = $(this);
1608            KTUtil.scrollInit(this, {
1609                disableForMobile: true,
1610                handleWindowResize: true,
1611                height: function() {
1612                    if (KTUtil.isInResponsiveRange('tablet-and-mobile') && el.data('mobile-height')) {
1613                        return el.data('mobile-height');
1614                    } else {
1615                        return el.data('height');
1616                    }
1617                }
1618            });
1619        });
1620    }
1621
1622    var initAlerts = function() {
1623        // init bootstrap popover
1624        $('body').on('click', '[data-close=alert]', function() {
1625            $(this).closest('.alert').hide();
1626        });
1627    }
1628
1629    var initSticky = function() {
1630        var sticky = new Sticky('[data-sticky="true"]');
1631    }
1632
1633    var initAbsoluteDropdown = function(dropdown) {
1634        var dropdownMenu;
1635
1636        if (!dropdown) {
1637            return;
1638        }
1639
1640        dropdown.on('show.bs.dropdown', function(e) {
1641            dropdownMenu = $(e.target).find('.dropdown-menu');
1642            $('body').append(dropdownMenu.detach());
1643            dropdownMenu.css('display', 'block');
1644            dropdownMenu.position({
1645                'my': 'right top',
1646                'at': 'right bottom',
1647                'of': $(e.relatedTarget)
1648            });
1649        });
1650
1651        dropdown.on('hide.bs.dropdown', function(e) {
1652            $(e.target).append(dropdownMenu.detach());
1653            dropdownMenu.hide();
1654        });
1655    }
1656
1657    return {
1658        init: function(options) {
1659            if (options && options.colors) {
1660                colors = options.colors;
1661            }
1662
1663            KTApp.initComponents();
1664        },
1665
1666        initComponents: function() {
1667            initScroll();
1668            initTooltips();
1669            initPopovers();
1670            initAlerts();
1671            initPortlets();
1672            initFileInput();
1673            initSticky();
1674        },
1675
1676        initTooltips: function() {
1677            initTooltips();
1678        },
1679
1680        initTooltip: function(el) {
1681            initTooltip(el);
1682        },
1683
1684        initPopovers: function() {
1685            initPopovers();
1686        },
1687
1688        initPopover: function(el) {
1689            initPopover(el);
1690        },
1691
1692        initPortlet: function(el, options) {
1693            initPortlet(el, options);
1694        },
1695
1696        initPortlets: function() {
1697            initPortlets();
1698        },
1699
1700        initSticky: function() {
1701            initSticky();
1702        },
1703
1704        initAbsoluteDropdown: function(dropdown) {
1705            initAbsoluteDropdown(dropdown);
1706        },
1707
1708        block: function(target, options) {
1709            var el = $(target);
1710
1711            options = $.extend(true, {
1712                opacity: 0.03,
1713                overlayColor: '#000000',
1714                type: '',
1715                size: '',
1716                state: 'brand',
1717                centerX: true,
1718                centerY: true,
1719                message: '',
1720                shadow: true,
1721                width: 'auto'
1722            }, options);
1723
1724            var html;
1725            var version = options.type ? 'kt-spinner--' + options.type : '';
1726            var state = options.state ? 'kt-spinner--' + options.state : '';
1727            var size = options.size ? 'kt-spinner--' + options.size : '';
1728            var spinner = '<div class="kt-spinner ' + version + ' ' + state 
1728+ ' ' + size + '"></div';
1729
1730            if (options.message && options.message.length > 0) {
1731                var classes = 'blockui ' + (options.shadow === false ? 'blockui' : '');
1732
1733                html = '<div class="' + classes + '"><span>' + options.message + '</span><span>' + spinner + '</span></div>';
1734
1735                var el = document.createElement('div');
1736                KTUtil.get('body').prepend(el);
1737                KTUtil.addClass(el, classes);
1738                el.innerHTML = '<span>' + options.message + '</span><span>' + spinner + '</span>';
1739                options.width = KTUtil.actualWidth(el) + 10;
1740                KTUtil.remove(el);
1741
1742                if (target == 'body') {
1743                    html = '<div class="' + classes + '" style="margin-left:-' + (options.width / 2) + 'px;"><span>' + options.message + '</span><span>' + spinner + '</span></div>';
1744                }
1745            } else {
1746                html = spinner;
1747            }
1748
1749            var params = {
1750                message: html,
1751                centerY: options.centerY,
1752                centerX: options.centerX,
1753                css: {
1754                    top: '30%',
1755                    left: '50%',
1756                    border: '0',
1757                    padding: '0',
1758                    backgroundColor: 'none',
1759                    width: options.width
1760                },
1761                overlayCSS: {
1762                    backgroundColor: options.overlayColor,
1763                    opacity: options.opacity,
1764                    cursor: 'wait',
1765                    zIndex: '10'
1766                },
1767                onUnblock: function() {
1768                    if (el && el[0]) {
1769                        KTUtil.css(el[0], 'position', '');
1770                        KTUtil.css(el[0], 'zoom', '');
1771                    }
1772                }
1773            };
1774
1775            if (target == 'body') {
1776                params.css.top = '50%';
1777                $.blockUI(params);
1778            } else {
1779                var el = $(target);
1780                el.block(params);
1781            }
1782        },
1783
1784        unblock: function(target) {
1785            if (target && target != 'body') {
1786                $(target).unblock();
1787            } else {
1788                $.unblockUI();
1789            }
1790        },
1791
1792        blockPage: function(options) {
1793            return KTApp.block('body', options);
1794        },
1795
1796        unblockPage: function() {
1797            return KTApp.unblock('body');
1798        },
1799
1800        progress: function(target, options) {
1801            var skin = (options && options.skin) ? options.skin : 'light';
1802            var alignment = (options && options.alignment) ? options.alignment : 'right';
1803            var size = (options && options.size) ? 'kt-spinner--' + options.size : '';
1804            var classes = 'kt-spinner ' + 'kt-spinner--' + skin + ' kt-spinner--' + alignment + ' kt-spinner--' + size; 
1805
1806            KTApp.unprogress(target);
1807
1808            $(target).addClass(classes);
1809            $(target).data('progress-classes', classes);
1810        },
1811
1812        unprogress: function(target) {
1813            $(target).removeClass($(target).data('progress-classes'));
1814        },
1815
1816        getStateColor: function(name) {
1817            return colors["state"][name];
1818        },
1819
1820        getBaseColor: function(type, level) {
1821            return colors["base"][type][level - 1];
1822        }
1823    };
1824}();
1825
1826// Initialize KTApp class on document ready
1827$(document).ready(function() {
1828    KTApp.init(KTAppOptions);
1829});
1830"use strict";
1831
1832// plugin setup
1833var KTDialog = function(options) {
1834    // Main object
1835    var the = this;
1836
1837    // Get element object
1838    var element;
1839    var body = KTUtil.get('body');  
1840
1841    // Default options
1842    var defaultOptions = {
1843        'placement' : 'top center',
1844        'type'  : 'loader',
1845        'width' : 100,
1846        'state' : 'default',
1847        'message' : 'Loading...' 
1848    };    
1849
1850    ////////////////////////////
1851    // ** Private Methods  ** //
1852    ////////////////////////////
1853
1854    var Plugin = {
1855        /**
1856         * Construct
1857         */
1858
1859        construct: function(options) {
1860            Plugin.init(options);
1861
1862            return the;
1863        },
1864
1865        /**
1866         * Handles subtoggle click toggle
1867         */
1868        init: function(options) {
1869            the.events = [];
1870
1871            // merge default and user defined options
1872            the.options = KTUtil.deepExtend({}, defaultOptions, options);
1873
1874            the.state = false;
1875        },
1876
1877        /**
1878         * Show dialog
1879         */
1880        show: function() {
1881            Plugin.eventTrigger('show');
1882
1883            element = document.createElement("DIV");
1884            KTUtil.setHTML(element, the.options.message);
1885            
1886            KTUtil.addClass(element, 'kt-dialog kt-dialog--shown');
1887            KTUtil.addClass(element, 'kt-dialog--' + the.options.state);
1888            KTUtil.addClass(element, 'kt-dialog--' + the.options.type); 
1889
1890            if (the.options.placement == 'top center') {
1891                KTUtil.addClass(element, 'kt-dialog--top-center');
1892            }
1893
1894            body.appendChild(element);
1895
1896            the.state = 'shown';
1897
1898            Plugin.eventTrigger('shown');
1899
1900            return the;
1901        },
1902
1903        /**
1904         * Hide dialog
1905         */
1906        hide: function() {
1907            if (element) {
1908                Plugin.eventTrigger('hide');
1909
1910                element.remove();
1911                the.state = 'hidden';
1912
1913                Plugin.eventTrigger('hidden');
1914            }
1915
1916            return the;
1917        },
1918
1919        /**
1920         * Trigger events
1921         */
1922        eventTrigger: function(name) {
1923            for (var i = 0; i < the.events.length; i++) {
1924                var event = the.events[i];
1925
1926                if (event.name == name) {
1927                    if (event.one == true) {
1928                        if (event.fired == false) {
1929                            the.events[i].fired = true;                            
1930                            event.handler.call(this, the);
1931                        }
1932                    } else {
1933                        event.handler.call(this, the);
1934                    }
1935                }
1936            }
1937        },
1938
1939        addEvent: function(name, handler, one) {
1940            the.events.push({
1941                name: name,
1942                handler: handler,
1943                one: one,
1944                fired: false
1945            });
1946
1947            return the;
1948        }
1949    };
1950
1951    //////////////////////////
1952    // ** Public Methods ** //
1953    //////////////////////////
1954
1955    /**
1956     * Set default options 
1957     */
1958
1959    the.setDefaults = function(options) {
1960        defaultOptions = options;
1961    };
1962
1963    /**
1964     * Check shown state 
1965     */
1966    the.shown = function() {
1967        return the.state == 'shown';
1968    };
1969
1970    /**
1971     * Check hidden state 
1972     */
1973    the.hidden = function() {
1974        return the.state == 'hidden';
1975    };
1976
1977    /**
1978     * Show dialog 
1979     */
1980    the.show = function() {
1981        return Plugin.show();
1982    };
1983
1984    /**
1985     * Hide dialog
1986     */
1987    the.hide = function() {
1988        return Plugin.hide();
1989    };
1990
1991    /**
1992     * Attach event
1993     * @returns {KTToggle}
1994     */
1995    the.on = function(name, handler) {
1996        return Plugin.addEvent(name, handler);
1997    };
1998
1999    /**
2000     * Attach event that will be fired once
2001     * @returns {KTToggle}
2002     */
2003    the.one = function(name, handler) {
2004        return Plugin.addEvent(name, handler, true);
2005    };
2006
2007    // Construct plugin
2008    Plugin.construct.apply(the, [options]);
2009
2010    return the;
2011};
2012'use strict';
2013(function($) {
2014
2015	var pluginName = 'KTDatatable';
2016	var pfx = 'kt-';
2017	var util = KTUtil;
2018	var app = KTApp;
2019
2020	if (typeof util === 'undefined') throw new Error('Util class is required and must be included before ' + pluginName);
2021
2022	// plugin setup
2023	$.fn[pluginName] = function(options) {
2024		if ($(this).length === 0) {
2025			console.log('No ' + pluginName + ' element exist.');
2026			return;
2027		}
2028
2029		// global variables
2030		var datatable = this;
2031
2032		// debug enabled?
2033		// 1) state will be cleared on each refresh
2034		// 2) enable some logs
2035		// 3) etc.
2036		datatable.debug = false;
2037
2038		datatable.API = {
2039			record: null,
2040			value: null,
2041			params: null,
2042		};
2043
2044		var Plugin = {
2045			/********************
2046			 ** PRIVATE METHODS
2047			 ********************/
2048			isInit: false,
2049			cellOffset: 110,
2050			iconOffset: 15,
2051			stateId: 'meta',
2052			ajaxParams: {},
2053			pagingObject: {},
2054
2055			init: function(options) {
2056				var isHtmlTable = false;
2057				// data source option empty is normal table
2058				if (options.data.source === null) {
2059					Plugin.extractTable();
2060					isHtmlTable = true;
2061				}
2062
2063				Plugin.setupBaseDOM.call();
2064				Plugin.setupDOM(datatable.table);
2065				// Plugin.spinnerCallback(true);
2066
2067				// set custom query from options
2068				Plugin.setDataSourceQuery(Plugin.getOption('data.source.read.params.query'));
2069
2070				// on event after layout had done setup, show datatable
2071				$(datatable).on(pfx + 'datatable--on-layout-updated', Plugin.afterRender);
2072
2073				if (datatable.debug) Plugin.stateRemove(Plugin.stateId);
2074
2075				// initialize extensions
2076				$.each(Plugin.getOption('extensions'), function(extName, extOptions) {
2077					if (typeof $.fn[pluginName][extName] === 'function')
2078						new $.fn[pluginName][extName](datatable, extOptions);
2079				});
2080
2081				// get data
2082				if (options.data.type === 'remote' || options.data.type === 'local') {
2083					if (options.data.saveState === false
2084						|| options.data.saveState.cookie === false
2085						&& options.data.saveState.webstorage === false) {
2086						Plugin.stateRemove(Plugin.stateId);
2087					}
2088					// get data for local datatable and local table
2089					if (options.data.type === 'local' && typeof options.data.source === 'object') {
2090						datatable.dataSet = datatable.originalDataSet = Plugin.dataMapCallback(options.data.source);
2091					}
2092					Plugin.dataRender();
2093				}
2094
2095        // if html table, remove and setup a new header
2096        if (isHtmlTable) {
2097          $(datatable.tableHead).find('tr').remove();
2098          $(datatable.tableFoot).find('tr').remove();
2099        }
2100
2101        Plugin.setHeadTitle();
2102        if (Plugin.getOption('layout.footer')) {
2103          Plugin.setHeadTitle(datatable.tableFoot);
2104        }
2105
2106				// hide header
2107				if (typeof options.layout.header !== 'undefined' &&
2108					options.layout.header === false) {
2109					$(datatable.table).find('thead').remove();
2110				}
2111
2112				// hide footer
2113				if (typeof options.layout.footer !== 'undefined' &&
2114					options.layout.footer === false) {
2115					$(datatable.table).find('tfoot').remove();
2116				}
2117
2118				// for normal and local data type, run layoutUpdate
2119				if (options.data.type === null ||
2120					options.data.type === 'local') {
2121					Plugin.setupCellField.call();
2122					Plugin.setupTemplateCell.call();
2123
2124					// setup nested datatable, if option enabled
2125					Plugin.setupSubDatatable.call();
2126
2127					// setup extra system column properties
2128					Plugin.setupSystemColumn.call();
2129					Plugin.redraw();
2130				}
2131
2132				var width;
2133				var initialWidth = false;
2134				$(window).resize(function() {
2135					// get initial width
2136					if (!initialWidth) {
2137						width = $(this).width();
2138						initialWidth = true;
2139					}
2140					// issue: URL Bar Resizing on mobile, https://developers.google.com/web/updates/2016/12/url-bar-resizing
2141					// trigger datatable resize on width change only
2142					if ($(this).width() !== width) {
2143						width = $(this).width();
2144						Plugin.fullRender();
2145					}
2146				});
2147
2148				$(datatable).height('');
2149
2150				$(Plugin.getOption('search.input')).on('keyup', function(e) {
2151					if (Plugin.getOption('search.onEnter') && e.which !== 13) return;
2152					Plugin.search($(this).val());
2153				});
2154
2155				return datatable;
2156			},
2157
2158			/**
2159			 * Extract static HTML table content into datasource
2160			 */
2161			extractTable: function() {
2162				var columns = [];
2163				var headers = $(datatable).find('tr:first-child th').get().map(function(cell, i) {
2164					var field = $(cell).data('field');
2165					if (typeof field === 'undefined') {
2166						field = $(cell).text().trim();
2167					}
2168					var column = {field: field, title: field};
2169					for (var ii in options.columns) {
2170						if (options.columns[ii].field === field) {
2171							column = $.extend(true, {}, options.columns[ii], column);
2172						}
2173					}
2174					columns.push(column);
2175					return field;
2176				});
2177				// auto create columns config
2178				options.columns = columns;
2179
2180				var rowProp = [];
2181				var source = [];
2182
2183				$(datatable).find('tr').each(function() {
2184					if ($(this).find('td').length) {
2185						rowProp.push($(this).prop('attributes'));
2186					}
2187					var td = {};
2188					$(this).find('td').each(function(i, cell) {
2189						td[headers[i]] = cell.innerHTML.trim();
2190					});
2191					if (!util.isEmpty(td)) {
2192						source.push(td);
2193					}
2194				});
2195
2196				options.data.attr.rowProps = rowProp;
2197				options.data.source = source;
2198			},
2199
2200			/**
2201			 * One time layout update on init
2202			 */
2203			layoutUpdate: function() {
2204				// setup nested datatable, if option enabled
2205				Plugin.setupSubDatatable.call();
2206
2207				// setup extra system column properties
2208				Plugin.setupSystemColumn.call();
2209
2210				// setup cell hover event
2211				Plugin.setupHover.call();
2212
2213				if (typeof options.detail === 'undefined'
2214					// temporary disable lock column in subtable
2215					&& Plugin.getDepth() === 1) {
2216					// lock columns handler
2217					Plugin.lockTable.call();
2218				}
2219
2220				Plugin.columnHide.call();
2221
2222				Plugin.resetScroll();
2223
2224				// check if not is a locked column
2225				if (!Plugin.isLocked()) {
2226					Plugin.redraw.call();
2227					// check if its not a subtable and has autoHide option enabled
2228					if (!Plugin.isSubtable() && Plugin.getOption('rows.autoHide') === true) {
2229						Plugin.autoHide();
2230					}
2231					// reset row
2232					$(datatable.table).find('.' + pfx + 'datatable__row').css('height', '');
2233				}
2234
2235				Plugin.rowEvenOdd.call();
2236
2237				Plugin.sorting.call();
2238
2239				Plugin.scrollbar.call();
2240
2241				if (!Plugin.isInit) {
2242					// run once dropdown inside datatable
2243					Plugin.dropdownFix();
2244					$(datatable).trigger(pfx + 'datatable--on-init', {table: $(datatable.wrap).attr('id'), options: options});
2245					Plugin.isInit = true;
2246				}
2247
2248				$(datatable).trigger(pfx + 'datatable--on-layout-updated', {table: $(datatable.wrap).attr('id')});
2249			},
2250
2251			lockTable: function() {
2252				var lock = {
2253					lockEnabled: false,
2254					init: function() {
2255						// check if table should be locked columns
2256						lock.lockEnabled = Plugin.lockEnabledColumns();
2257						if (lock.lockEnabled.left.length === 0 &&
2258							lock.lockEnabled.right.length === 0) {
2259							return;
2260						}
2261						lock.enable();
2262					},
2263					enable: function() {
2264						var enableLock = function(tablePart) {
2265							// check if already has lock column
2266							if ($(tablePart).find('.' + pfx + 'datatable__lock').length > 0) {
2267								Plugin.log('Locked container already exist in: ', tablePart);
2268								return;
2269							}
2270							// check if no rows exists
2271							if ($(tablePart).find('.' + pfx + 'datatable__row').length === 0) {
2272								Plugin.log('No row exist in: ', tablePart);
2273								return;
2274							}
2275
2276							// locked div container
2277							var lockLeft = $('<div/>').addClass(pfx + 'datatable__lock ' + pfx + 'datatable__lock--left');
2278							var lockScroll = $('<div/>').addClass(pfx + 'datatable__lock ' + pfx + 'datatable__lock--scroll');
2279							var lockRight = $('<div/>').addClass(pfx + 'datatable__lock ' + pfx + 'datatable__lock--right');
2280
2281							$(tablePart).find('.' + pfx + 'datatable__row').each(function() {
2282								// create new row for lock columns and pass the data
2283								var rowLeft = $('<tr/>').addClass(pfx + 'datatable__row').data('obj', $(this).data('obj')).appendTo(lockLeft);
2284								var rowScroll = $('<tr/>').addClass(pfx + 'datatable__row').data('obj', $(this).data('obj')).appendTo(lockScroll);
2285								var rowRight = $('<tr/>').addClass(pfx + 'datatable__row').data('obj', $(this).data('obj')).appendTo(lockRight);
2286								$(this).find('.' + pfx + 'datatable__cell').each(function() {
2287									var locked = $(this).data('locked');
2288									if (typeof locked !== 'undefined') {
2289										if (typeof locked.left !== 'undefined' || locked === true) {
2290											// default locked to left
2291											$(this).appendTo(rowLeft);
2292										}
2293										if (typeof locked.right !== 'undefined') {
2294											$(this).appendTo(rowRight);
2295										}
2296									} else {
2297										$(this).appendTo(rowScroll);
2298									}
2299								});
2300								// remove old row
2301								$(this).remove();
2302							});
2303
2304							if (lock.lockEnabled.left.length > 0) {
2305								$(datatable.wrap).addClass(pfx + 'datatable--lock');
2306								$(lockLeft).appendTo(tablePart);
2307							}
2308							if (lock.lockEnabled.left.length > 0 || lock.lockEnabled.right.length > 0) {
2309								$(lockScroll).appendTo(tablePart);
2310							}
2311							if (lock.lockEnabled.right.length > 0) {
2312								$(datatable.wrap).addClass(pfx + 'datatable--lock');
2313								$(lockRight).appendTo(tablePart);
2314							}
2315						};
2316
2317						$(datatable.table).find('thead,tbody,tfoot').each(function() {
2318							var tablePart = this;
2319							if ($(this).find('.' + pfx + 'datatable__lock').length === 0) {
2320								$(this).ready(function() {
2321									enableLock(tablePart);
2322								});
2323							}
2324						});
2325					},
2326				};
2327				lock.init();
2328				return lock;
2329			},
2330
2331			/**
2332			 * Render everything for resize
2333			 */
2334			fullRender: function() {
2335				$(datatable.tableHead).empty();
2336				Plugin.setHeadTitle();
2337				if (Plugin.getOption('layout.footer')) {
2338					$(datatable.tableFoot).empty();
2339					Plugin.setHeadTitle(datatable.tableFoot);
2340				}
2341
2342				Plugin.spinnerCallback(true);
2343				$(datatable.wrap).removeClass(pfx + 'datatable--loaded');
2344
2345				Plugin.insertData();
2346			},
2347
2348			lockEnabledColumns: function() {
2349				var screen = $(window).width();
2350				var columns = options.columns;
2351				var enabled = {left: [], right: []};
2352				$.each(columns, function(i, column) {
2353					if (typeof column.locked !== 'undefined') {
2354						if (typeof column.locked.left !== 'undefined') {
2355							if (util.getBreakpoint(column.locked.left) <= screen) {
2356								enabled['left'].push(column.locked.left);
2357							}
2358						}
2359						if (typeof column.locked.right !== 'undefined') {
2360							if (util.getBreakpoint(column.locked.right) <= screen) {
2361								enabled['right'].push(column.locked.right);
2362							}
2363						}
2364					}
2365				});
2366				return enabled;
2367			},
2368
2369			/**
2370			 * After render event, called by
2371			 * '+pfx+'-datatable--on-layout-updated
2372			 * @param e
2373			 * @param args
2374			 */
2375			afterRender: function(e, args) {
2376				$(datatable).ready(function() {
2377					// redraw locked columns table
2378					if (Plugin.isLocked()) {
2379						Plugin.redraw();
2380					}
2381
2382					$(datatable.tableBody).css('visibility', '');
2383					$(datatable.wrap).addClass(pfx + 'datatable--loaded');
2384
2385					Plugin.spinnerCallback(false);
2386				});
2387			},
2388
2389			dropdownFix: function() {
2390				var dropdownMenu;
2391				$('body').on('show.bs.dropdown', '.' + pfx + 'datatable .' + pfx + 'datatable__body', function(e) {
2392					dropdownMenu = $(e.target).find('.dropdown-menu');
2393					$('body').append(dropdownMenu.detach());
2394					dropdownMenu.css('display', 'block');
2395					dropdownMenu.position({
2396						'my': 'right top',
2397						'at': 'right bottom',
2398						'of': $(e.relatedTarget),
2399					});
2400					// if datatable is inside modal
2401					if (datatable.closest('.modal').length) {
2402						// increase dropdown z-index
2403						dropdownMenu.css('z-index', '2000');
2404					}
2405				}).on('hide.bs.dropdown', '.' + pfx + 'datatable .' + pfx + 'datatable__body', function(e) {
2406					$(e.target).append(dropdownMenu.detach());
2407					dropdownMenu.hide();
2408				});
2409			},
2410
2411			hoverTimer: 0,
2412			isScrolling: false,
2413			setupHover: function() {
2414				$(window).scroll(function(e) {
2415					// stop hover when scrolling
2416					clearTimeout(Plugin.hoverTimer);
2417					Plugin.isScrolling = true;
2418				});
2419
2420				$(datatable.tableBody).find('.' + pfx + 'datatable__cell').off('mouseenter', 'mouseleave').on('mouseenter', function() {
2421					// reset scroll timer to hover class
2422					Plugin.hoverTimer = setTimeout(function() {
2423						Plugin.isScrolling = false;
2424					}, 200);
2425					if (Plugin.isScrolling) return;
2426
2427					// normal table
2428					var row = $(this).closest('.' + pfx + 'datatable__row').addClass(pfx + 'datatable__row--hover');
2429					var index = $(row).index() + 1;
2430
2431					// lock table
2432					$(row).closest('.' + pfx + 'datatable__lock').parent().find('.' + pfx + 'datatable__row:nth-child(' + index + ')').addClass(pfx + 'datatable__row--hover');
2433				}).on('mouseleave', function() {
2434					// normal table
2435					var row = $(this).closest('.' + pfx + 'datatable__row').removeClass(pfx + 'datatable__row--hover');
2436					var index = $(row).index() + 1;
2437
2438					// look table
2439					$(row).closest('.' + pfx + 'datatable__lock').parent().find('.' + pfx + 'datatable__row:nth-child(' + index + ')').removeClass(pfx + 'datatable__row--hover');
2440				});
2441			},
2442
2443			/**
2444			 * Adjust width of locked table containers by resize handler
2445			 * @returns {number}
2446			 */
2447			adjustLockContainer: function() {
2448				if (!Plugin.isLocked()) return 0;
2449
2450				// refer to head dimension
2451				var containerWidth = $(datatable.tableHead).width();
2452				var lockLeft = $(datatable.tableHead).find('.' + pfx + 'datatable__lock--left').width();
2453				var lockRight = $(datatable.tableHead).find('.' + pfx + 'datatable__lock--right').width();
2454
2455				if (typeof lockLeft === 'undefined') lockLeft = 0;
2456				if (typeof lockRight === 'undefined') lockRight = 0;
2457
2458				var lockScroll = Math.floor(containerWidth - lockLeft - lockRight);
2459				$(datatable.table).find('.' + pfx + 'datatable__lock--scroll').css('width', lockScroll);
2460
2461				return lockScroll;
2462			},
2463
2464			/**
2465			 * todo; not in use
2466			 */
2467			dragResize: function() {
2468				var pressed = false;
2469				var start = undefined;
2470				var startX, startWidth;
2471				$(datatable.tableHead).find('.' + pfx + 'datatable__cell').mousedown(function(e) {
2472					start = $(this);
2473					pressed = true;
2474					startX = e.pageX;
2475					startWidth = $(this).width();
2476					$(start).addClass(pfx + 'datatable__cell--resizing');
2477
2478				}).mousemove(function(e) {
2479					if (pressed) {
2480						var i = $(start).index();
2481						var tableBody = $(datatable.tableBody);
2482						var ifLocked = $(start).closest('.' + pfx + 'datatable__lock');
2483
2484						if (ifLocked) {
2485							var lockedIndex = $(ifLocked).index();
2486							tableBody = $(datatable.tableBody).find('.' + pfx + 'datatable__lock').eq(lockedIndex);
2487						}
2488
2489						$(tableBody).find('.' + pfx + 'datatable__row').each(function(tri, tr) {
2490							$(tr).find('.' + pfx + 'datatable__cell').eq(i).width(startWidth + (e.pageX - startX)).children().width(startWidth + (e.pageX - startX));
2491						});
2492
2493						$(start).children().css('width', startWidth + (e.pageX - startX));
2494					}
2495
2496				}).mouseup(function() {
2497					$(start).removeClass(pfx + 'datatable__cell--resizing');
2498					pressed = false;
2499				});
2500
2501				$(document).mouseup(function() {
2502					$(start).removeClass(pfx + 'datatable__cell--resizing');
2503					pressed = false;
2504				});
2505			},
2506
2507			/**
2508			 * To prepare placeholder for table before content is loading
2509			 */
2510			initHeight: function() {
2511				if (options.layout.height && options.layout.scroll) {
2512					var theadHeight = $(datatable.tableHead).find('.' + pfx + 'datatable__row').outerHeight();
2513					var tfootHeight = $(datatable.tableFoot).find('.' + pfx + 'datatable__row').outerHeight();
2514					var bodyHeight = options.layout.height;
2515					if (theadHeight > 0) {
2516						bodyHeight -= theadHeight;
2517					}
2518					if (tfootHeight > 0) {
2519						bodyHeight -= tfootHeight;
2520					}
2521
2522					// scrollbar offset
2523					bodyHeight -= 2;
2524
2525					$(datatable.tableBody).css('max-height', bodyHeight);
2526
2527					// set scrollable area fixed height
2528					$(datatable.tableBody).find('.' + pfx + 'datatable__lock--scroll').css('height', bodyHeight);
2529				}
2530			},
2531
2532			/**
2533			 * Setup base DOM (table, thead, tbody, tfoot) and create if not
2534			 * exist.
2535			 */
2536			setupBaseDOM: function() {
2537				// keep original state before datatable initialize
2538				datatable.initialDatatable = $(datatable).clone();
2539
2540				// main element
2541				if ($(datatable).prop('tagName') === 'TABLE') {
2542					// if main init element is <table>, wrap with div
2543					datatable.table = $(datatable).removeClass(pfx + 'datatable').addClass(pfx + 'datatable__table');
2544					if ($(datatable.table).parents('.' + pfx + 'datatable').length === 0) {
2545						datatable.table.wrap($('<div/>').addClass(pfx + 'datatable').addClass(pfx + 'datatable--' + options.layout.theme));
2546						datatable.wrap = $(datatable.table).parent();
2547					}
2548				} else {
2549					// create table
2550					datatable.wrap = $(datatable).addClass(pfx + 'datatable').addClass(pfx + 'datatable--' + options.layout.theme);
2551					datatable.table = $('<table/>').addClass(pfx + 'datatable__table').appendTo(datatable);
2552				}
2553
2554				if (typeof options.layout.class !== 'undefined') {
2555					$(datatable.wrap).addClass(options.layout.class);
2556				}
2557
2558				$(datatable.table).removeClass(pfx + 'datatable--destroyed').css('display', 'block');
2559
2560				// force disable save state
2561				if (typeof $(datatable).attr('id') === 'undefined') {
2562					Plugin.setOption('data.saveState', false);
2563					$(datatable.table).attr('id', util.getUniqueID(pfx + 'datatable--'));
2564				}
2565
2566				// predefine table height
2567				if (Plugin.getOption('layout.minHeight'))
2568					$(datatable.table).css('min-height', Plugin.getOption('layout.minHeight'));
2569
2570				if (Plugin.getOption('layout.height'))
2571					$(datatable.table).css('max-height', Plugin.getOption('layout.height'));
2572
2573				// for normal table load
2574				if (options.data.type === null) {
2575					$(datatable.table).css('width', '').css('display', '');
2576				}
2577
2578				// create table head element
2579				datatable.tableHead = $(datatable.table).find('thead');
2580				if ($(datatable.tableHead).length === 0) {
2581					datatable.tableHead = $('<thead/>').prependTo(datatable.table);
2582				}
2583
2584				// create table head element
2585				datatable.tableBody = $(datatable.table).find('tbody');
2586				if ($(datatable.tableBody).length === 0) {
2587					datatable.tableBody = $('<tbody/>').appendTo(datatable.table);
2588				}
2589
2590				if (typeof options.layout.footer !== 'undefined' &&
2591					options.layout.footer) {
2592					// create table foot element
2593					datatable.tableFoot = $(datatable.table).find('tfoot');
2594					if ($(datatable.tableFoot).length === 0) {
2595						datatable.tableFoot = $('<tfoot/>').appendTo(datatable.table);
2596					}
2597				}
2598			},
2599
2600			/**
2601			 * Set column data before table manipulation.
2602			 */
2603			setupCellField: function(tableParts) {
2604				if (typeof tableParts === 'undefined') tableParts = $(datatable.table).children();
2605				var columns = options.columns;
2606				$.each(tableParts, function(part, tablePart) {
2607					$(tablePart).find('.' + pfx + 'datatable__row').each(function(tri, tr) {
2608						// prepare data
2609						$(tr).find('.' + pfx + 'datatable__cell').each(function(tdi, td) {
2610							if (typeof columns[tdi] !== 'undefined') {
2611								$(td).data(columns[tdi]);
2612							}
2613						});
2614					});
2615				});
2616			},
2617
2618			/**
2619			 * Set column template callback
2620			 * @param tablePart
2621			 */
2622			setupTemplateCell: function(tablePart) {
2623				if (typeof tablePart === 'undefined') tablePart = datatable.tableBody;
2624				var columns = options.columns;
2625				$(tablePart).find('.' + pfx + 'datatable__row').each(function(tri, tr) {
2626					// row data object, if any
2627					var obj = $(tr).data('obj');
2628					if (typeof obj === 'undefined') {
2629						return;
2630					}
2631
2632					// @deprecated in v5.0.6
2633					// obj['getIndex'] = function() {
2634					// 	return tri;
2635					// };
2636					// @deprecated in v5.0.6
2637					// obj['getDatatable'] = function() {
2638					// 	return datatable;
2639					// };
2640
2641					// @deprecated in v5.0.6
2642					var rowCallback = Plugin.getOption('rows.callback');
2643					if (typeof rowCallback === 'function') {
2644						rowCallback($(tr), obj, tri);
2645					}
2646					// before template row callback
2647					var beforeTemplate = Plugin.getOption('rows.beforeTemplate');
2648					if (typeof beforeTemplate === 'function') {
2649						beforeTemplate($(tr), obj, tri);
2650					}
2651					// if data object is undefined, collect from table
2652					if (typeof obj === 'undefined') {
2653						obj = {};
2654						$(tr).find('.' + pfx + 'datatable__cell').each(function(tdi, td) {
2655							// get column settings by field
2656							var column = $.grep(columns, function(n, i) {
2657								return $(td).data('field') === n.field;
2658							})[0];
2659							if (typeof column !== 'undefined') {
2660								obj[column['field']] = $(td).text();
2661							}
2662						});
2663					}
2664
2665					$(tr).find('.' + pfx + 'datatable__cell').each(function(tdi, td) {
2666						// get column settings by field
2667						var column = $.grep(columns, function(n, i) {
2668							return $(td).data('field') === n.field;
2669						})[0];
2670						if (typeof column !== 'undefined') {
2671							// column template
2672							if (typeof column.template !== 'undefined') {
2673								var finalValue = '';
2674								// template string
2675								if (typeof column.template === 'string') {
2676									finalValue = Plugin.dataPlaceholder(column.template, obj);
2677								}
2678								// template callback function
2679								if (typeof column.template === 'function') {
2680									finalValue = column.template(obj, tri, datatable);
2681								}
2682
2683								// sanitize using DOMPurify if installed
2684								if (typeof DOMPurify !== 'undefined') {
2685									finalValue = DOMPurify.sanitize(finalValue);
2686								}
2687
2688								var span = document.createElement('span');
2689								span.innerHTML = finalValue;
2690
2691								// insert to cell, wrap with span
2692								$(td).html(span);
2693
2694								// set span overflow
2695								if (typeof column.overflow !== 'undefined') {
2696									$(span).css('overflow', column.overflow);
2697									$(span).css('position', 'relative');
2698								}
2699							}
2700						}
2701					});
2702
2703					// after template row callback
2704					var afterTemplate = Plugin.getOption('rows.afterTemplate');
2705					if (typeof afterTemplate === 'function') {
2706						afterTemplate($(tr), obj, tri);
2707					}
2708				});
2709			},
2710
2711			/**
2712			 * Setup extra system column properties
2713			 * Note: selector checkbox, subtable toggle
2714			 */
2715			setupSystemColumn: function() {
2716				datatable.dataSet = datatable.dataSet || [];
2717				// no records available
2718				if (datatable.dataSet.length === 0) return;
2719
2720				var columns = options.columns;
2721				$(datatable.tableBody).find('.' + pfx + 'datatable__row').each(function(tri, tr) {
2722					$(tr).find('.' + pfx + 'datatable__cell').each(function(tdi, td) {
2723						// get column settings by field
2724						var column = $.grep(columns, function(n, i) {
2725							return $(td).data('field') === n.field;
2726						})[0];
2727						if (typeof column !== 'undefined') {
2728							var value = $(td).text();
2729
2730							// enable column selector
2731							if (typeof column.selector !== 'undefined' && column.selector !== false) {
2732								// check if checkbox exist
2733								if ($(td).find('.' + pfx + 'checkbox [type="checkbox"]').length > 0) return;
2734
2735								$(td).addClass(pfx + 'datatable__cell--check');
2736
2737								// append checkbox
2738								var chk = $('<label/>').
2739									addClass(pfx + 'checkbox ' + pfx + 'checkbox--single').
2740									append($('<input/>').attr('type', 'checkbox').attr('value', value).on('click', function() {
2741										if ($(this).is(':checked')) {
2742											// add checkbox active row class
2743											Plugin.setActive(this);
2744										} else {
2745											// add checkbox active row class
2746											Plugin.setInactive(this);
2747										}
2748									})).
2749									append('&nbsp;<span></span>');
2750
2751								// checkbox selector has outline style
2752								if (typeof column.selector.class !== 'undefined') {
2753									$(chk).addClass(column.selector.class);
2754								}
2755
2756								$(td).children().html(chk);
2757							}
2758
2759							// enable column subtable toggle
2760							if (typeof column.subtable !== 'undefined' && column.subtable) {
2761								// check if subtable toggle exist
2762								if ($(td).find('.' + pfx + 'datatable__toggle-subtable').length > 0) return;
2763								// append subtable toggle
2764								$(td).
2765									children().
2766									html($('<a/>').
2767										addClass(pfx + 'datatable__toggle-subtable').
2768										attr('href', '#').
2769										attr('data-value', value).
2770										append($('<i/>').addClass(Plugin.getOption('layout.icons.rowDetail.collapse'))));
2771							}
2772						}
2773					});
2774				});
2775
2776				// init checkbox for header/footer
2777				var initCheckbox = function(tr) {
2778					// get column settings by field
2779					var column = $.grep(columns, function(n, i) {
2780						return typeof n.selector !== 'undefined' && n.selector !== false;
2781					})[0];
2782
2783					if (typeof column !== 'undefined') {
2784						// enable column selector
2785						if (typeof column.selector !== 'undefined' && column.selector !== false) {
2786							var td = $(tr).find('[data-field="' + column.field + '"]');
2787							// check if checkbox exist
2788							if ($(td).find('.' + pfx + 'checkbox [type="checkbox"]').length > 0) return;
2789
2790							$(td).addClass(pfx + 'datatable__cell--check');
2791
2792							// append checkbox
2793							var chk = $('<label/>').
2794								addClass(pfx + 'checkbox ' + pfx + 'checkbox--single ' + pfx + 'checkbox--all').
2795								append($('<input/>').attr('type', 'checkbox').on('click', function() {
2796									if ($(this).is(':checked')) {
2797										Plugin.setActiveAll(true);
2798									} else {
2799										Plugin.setActiveAll(false);
2800									}
2801								})).
2802								append('&nbsp;<span></span>');
2803
2804							// checkbox selector has outline style
2805							if (typeof column.selector.class !== 'undefined') {
2806								$(chk).addClass(column.selector.class);
2807							}
2808
2809							$(td).children().html(chk);
2810						}
2811					}
2812				};
2813
2814				if (options.layout.header) {
2815					initCheckbox($(datatable.tableHead).find('.' + pfx + 'datatable__row').first());
2816				}
2817				if (options.layout.footer) {
2818					initCheckbox($(datatable.tableFoot).find('.' + pfx + 'datatable__row').first());
2819				}
2820			},
2821
2822			/**
2823			 * Adjust width to match container size
2824			 */
2825			adjustCellsWidth: function() {
2826				// get table width
2827				var containerWidth = $(datatable.tableBody).innerWidth() - Plugin.iconOffset;
2828
2829				// get total number of columns
2830				var columns = $(datatable.tableBody).
2831					find('.' + pfx + 'datatable__row:first-child').
2832					find('.' + pfx + 'datatable__cell').
2833					// exclude expand icon
2834					not('.' + pfx + 'datatable__toggle-detail').
2835					not(':hidden').length;
2836
2837				if (columns > 0) {
2838					//  remove reserved sort icon width
2839					containerWidth = containerWidth - (Plugin.iconOffset * columns);
2840					var minWidth = Math.floor(containerWidth / columns);
2841
2842					// minimum width
2843					if (minWidth <= Plugin.cellOffset) {
2844						minWidth = Plugin.cellOffset;
2845					}
2846
2847					var maxWidthList = {};
2848					$(datatable.table).find('.' + pfx + 'datatable__row').
2849							find('.' + pfx + 'datatable__cell').
2850							// exclude expand icon
2851							not('.' + pfx + 'datatable__toggle-detail').
2852							not(':hidden').each(function(tdi, td) {
2853
2854						var width = minWidth;
2855						var dataWidth = $(td).data('width');
2856
2857						if (typeof dataWidth !== 'undefined') {
2858
2859							if (dataWidth === 'auto') {
2860								var field = $(td).data('field');
2861								if (maxWidthList[field]) {
2862									width = maxWidthList[field];
2863								}
2864								else {
2865									var cells = $(datatable.tableBody).find('.' + pfx + 'datatable__cell[data-field="' + field + '"]');
2866									width = maxWidthList[field] = Math.max.apply(null,
2867											$(cells).map(function() {
2868												return $(this).outerWidth();
2869											}).get());
2870								}
2871							}
2872							else {
2873								width = dataWidth;
2874							}
2875						}
2876						$(td).children().css('width', Math.ceil(width));
2877					});
2878				}
2879
2880				return datatable;
2881			},
2882
2883			/**
2884			 * Adjust height to match container size
2885			 */
2886			adjustCellsHeight: function() {
2887				$.each($(datatable.table).children(), function(part, tablePart) {
2888					var totalRows = $(tablePart).find('.' + pfx + 'datatable__row').first().parent().find('.' + pfx + 'datatable__row').length;
2889					for (var i = 1; i <= totalRows; i++) {
2890						var rows = $(tablePart).find('.' + pfx + 'datatable__row:nth-child(' + i + ')');
2891						if ($(rows).length > 0) {
2892							var maxHeight = Math.max.apply(null, $(rows).map(function() {
2893								return $(this).outerHeight();
2894							}).get());
2895							$(rows).css('height', Math.ceil(maxHeight));
2896						}
2897					}
2898				});
2899			},
2900
2901			/**
2902			 * Setup table DOM and classes
2903			 */
2904			setupDOM: function(table) {
2905				// set table classes
2906				$(table).find('> thead').addClass(pfx + 'datatable__head');
2907				$(table).find('> tbody').addClass(pfx + 'datatable__body');
2908				$(table).find('> tfoot').addClass(pfx + 'datatable__foot');
2909				$(table).find('tr').addClass(pfx + 'datatable__row');
2910				$(table).find('tr > th, tr > td').addClass(pfx + 'datatable__cell');
2911				$(table).find('tr > th, tr > td').each(function(i, td) {
2912					if ($(td).find('span').length === 0) {
2913						$(td).wrapInner($('<span/>').css('width', Plugin.cellOffset));
2914					}
2915				});
2916			},
2917
2918			/**
2919			 * Default scrollbar
2920			 * @returns {{tableLocked: null, init: init, onScrolling:
2921			 *     onScrolling}}
2922			 */
2923			scrollbar: function() {
2924				var scroll = {
2925					scrollable: null,
2926					tableLocked: null,
2927					initPosition: null,
2928					init: function() {
2929						var screen = util.getViewPort().width;
2930						// setup scrollable datatable
2931						if (options.layout.scroll) {
2932							// add scrollable datatable class
2933							$(datatable.wrap).addClass(pfx + 'datatable--scroll');
2934
2935							var scrollable = $(datatable.tableBody).find('.' + pfx + 'datatable__lock--scroll');
2936
2937							// check if scrollable area have rows
2938							if ($(scrollable).find('.' + pfx + 'datatable__row').length > 0 && $(scrollable).length > 0) {
2939								scroll.scrollHead = $(datatable.tableHead).find('> .' + pfx + 'datatable__lock--scroll > .' + pfx + 'datatable__row');
2940								scroll.scrollFoot = $(datatable.tableFoot).find('> .' + pfx + 'datatable__lock--scroll > .' + pfx + 'datatable__row');
2941								scroll.tableLocked = $(datatable.tableBody).find('.' + pfx + 'datatable__lock:not(.' + pfx + 'datatable__lock--scroll)');
2942								if (Plugin.getOption('layout.customScrollbar') && util.detectIE() != 10 && screen > util.getBreakpoint('lg')) {
2943									scroll.initCustomScrollbar(scrollable[0]);
2944								} else {
2945									scroll.initDefaultScrollbar(scrollable);
2946								}
2947							} else if ($(datatable.tableBody).find('.' + pfx + 'datatable__row').length > 0) {
2948								scroll.scrollHead = $(datatable.tableHead).find('> .' + pfx + 'datatable__row');
2949								scroll.scrollFoot = $(datatable.tableFoot).find('> .' + pfx + 'datatable__row');
2950								if (Plugin.getOption('layout.customScrollbar') && util.detectIE() != 10 && screen > util.getBreakpoint('lg')) {
2951									scroll.initCustomScrollbar(datatable.tableBody);
2952								} else {
2953									scroll.initDefaultScrollbar(datatable.tableBody);
2954								}
2955							}
2956						}
2957					},
2958					initDefaultScrollbar: function(scrollable) {
2959						// get initial scroll position
2960						scroll.initPosition = $(scrollable).scrollLeft();
2961						$(scrollable).css('overflow-y', 'auto').off().on('scroll', scroll.onScrolling);
2962						if (Plugin.getOption('rows.autoHide') !== true) {
2963							$(scrollable).css('overflow-x', 'auto');
2964						}
2965					},
2966					onScrolling: function(e) {
2967						var left = $(this).scrollLeft();
2968						var top = $(this).scrollTop();
2969						if (util.isRTL()) {
2970							// deduct initial position for RTL
2971							left = left - scroll.initPosition;
2972						}
2973						$(scroll.scrollHead).css('left', -left);
2974						$(scroll.scrollFoot).css('left', -left);
2975						$(scroll.tableLocked).each(function(i, table) {
2976							if (Plugin.isLocked()) {
2977								// scrollbar offset
2978								top -= 1;
2979							}
2980							$(table).css('top', -top);
2981						});
2982					},
2983					initCustomScrollbar: function(scrollable) {
2984						scroll.scrollable = scrollable;
2985						// create a new instance for table body with scrollbar
2986						Plugin.initScrollbar(scrollable);
2987						// get initial scroll position
2988						scroll.initPosition = $(scrollable).scrollLeft();
2989						$(scrollable).off().on('scroll', scroll.onScrolling);
2990					},
2991				};
2992				scroll.init();
2993				return scroll;
2994			},
2995
2996			/**
2997			 * Init custom scrollbar and reset position
2998			 * @param element
2999			 * @param options
3000			 */
3001			initScrollbar: function(element, options) {
3002				if (!element || !element.nodeName) {
3003					return;
3004				}
3005				$(datatable.tableBody).css('overflow', '');
3006				if (util.hasClass(element, 'ps')) {
3007					$(element).data('ps').update();
3008				} else {
3009					var ps = new PerfectScrollbar(element, Object.assign({}, {
3010						wheelSpeed: 0.5,
3011						swipeEasing: true,
3012						// wheelPropagation: false,
3013						minScrollbarLength: 40,
3014						maxScrollbarLength: 300,
3015						suppressScrollX: Plugin.getOption('rows.autoHide') && !Plugin.isLocked()
3016					}, options));
3017					$(element).data('ps', ps);
3018
3019					// reset perfect scrollbar on resize
3020					$(window).resize(function() {
3021						ps.update();
3022					});
3023				}
3024			},
3025
3026			/**
3027			 * Set column title from options.columns settings
3028			 */
3029			setHeadTitle: function(tablePart) {
3030				if (typeof tablePart === 'undefined') tablePart = datatable.tableHead;
3031				tablePart = $(tablePart)[0];
3032				var columns = options.columns;
3033				var row = tablePart.getElementsByTagName('tr')[0];
3034				var ths = tablePart.getElementsByTagName('td');
3035
3036				if (typeof row === 'undefined') {
3037					row = document.createElement('tr');
3038					tablePart.appendChild(row);
3039				}
3040
3041				$.each(columns, function(i, column) {
3042					var th = ths[i];
3043					if (typeof th === 'undefined') {
3044						th = document.createElement('th');
3045						row.appendChild(th);
3046					}
3047
3048					// set column title
3049					if (typeof column['title'] !== 'undefined') {
3050						th.innerHTML = column.title;
3051						th.setAttribute('data-field', column.field);
3052						util.addClass(th, column.class);
3053						// set disable autoHide or force enable
3054						if (typeof column.autoHide !== 'undefined') {
3055							if (column.autoHide !== true) {
3056								th.setAttribute('data-autohide-disabled', column.autoHide);
3057							} else {
3058								th.setAttribute('data-autohide-enabled', column.autoHide);
3059							}
3060						}
3061						$(th).data(column);
3062					}
3063
3064					// set header attr option
3065					if (typeof column.attr !== 'undefined') {
3066						$.each(column.attr, function(key, val) {
3067							th.setAttribute(key, val);
3068						});
3069					}
3070
3071					// apply text align to thead/tfoot
3072					if (typeof column.textAlign !== 'undefined') {
3073						var align = typeof datatable.textAlign[column.textAlign] !== 'undefined' ? datatable.textAlign[column.textAlign] : '';
3074						util.addClass(th, align);
3075					}
3076				});
3077				Plugin.setupDOM(tablePart);
3078			},
3079
3080			/**
3081			 * Initiate to get remote or local data via ajax
3082			 */
3083			dataRender: function(action) {
3084				$(datatable.table).siblings('.' + pfx + 'datatable__pager').removeClass(pfx + 'datatable--paging-loaded');
3085
3086				var buildMeta = function() {
3087					datatable.dataSet = datatable.dataSet || [];
3088					Plugin.localDataUpdate();
3089					// local pagination meta
3090					var meta = Plugin.getDataSourceParam('pagination');
3091					if (meta.perpage === 0) {
3092						meta.perpage = options.data.pageSize || 10;
3093					}
3094					meta.total = datatable.dataSet.length;
3095					var start = Math.max(meta.perpage * (meta.page - 1), 0);
3096					var end = Math.min(start + meta.perpage, meta.total);
3097					datatable.dataSet = $(datatable.dataSet).slice(start, end);
3098					return meta;
3099				};
3100
3101				var afterGetData = function(result) {
3102					var localPagingCallback = function(ctx, meta) {
3103						if (!$(ctx.pager).hasClass(pfx + 'datatable--paging-loaded')) {
3104							$(ctx.pager).remove();
3105							ctx.init(meta);
3106						}
3107						$(ctx.pager).off().on(pfx + 'datatable--on-goto-page', function(e) {
3108							$(ctx.pager).remove();
3109							ctx.init(meta);
3110						});
3111
3112						var start = Math.max(meta.perpage * (meta.page - 1), 0);
3113						var end = Math.min(start + meta.perpage, meta.total);
3114
3115						Plugin.localDataUpdate();
3116						datatable.dataSet = $(datatable.dataSet).slice(start, end);
3117
3118						// insert data into table content
3119						Plugin.insertData();
3120					};
3121
3122					$(datatable.wrap).removeClass(pfx + 'datatable--error');
3123					// pagination enabled
3124					if (options.pagination) {
3125						if (options.data.serverPaging && options.data.type !== 'local') {
3126							// server pagination
3127							var serverMeta = Plugin.getObject('meta', result || null);
3128							if (serverMeta !== null) {
3129								Plugin.pagingObject = Plugin.paging(serverMeta);
3130							} else {
3131								// no meta object from server response, fallback to local pagination
3132								Plugin.pagingObject = Plugin.paging(buildMeta(), localPagingCallback);
3133							}
3134						} else {
3135							// local pagination can be used by remote data also
3136							Plugin.pagingObject = Plugin.paging(buildMeta(), localPagingCallback);
3137						}
3138					} else {
3139						// pagination is disabled
3140						Plugin.localDataUpdate();
3141					}
3142					// insert data into table content
3143					Plugin.insertData();
3144				};
3145
3146				// get local datasource
3147				if (options.data.type === 'local'
3148					// for remote json datasource
3149					// || typeof options.data.source.read === 'undefined' && datatable.dataSet !== null
3150					// for remote datasource, server sorting is disabled and data already received from remote
3151					|| options.data.serverSorting === false && action === 'sort'
3152					|| options.data.serverFiltering === false && action === 'search'
3153				) {
3154					setTimeout(function() {
3155						afterGetData();
3156						Plugin.setAutoColumns();
3157					});
3158					return;
3159				}
3160
3161				// getting data from remote only
3162				Plugin.getData().done(afterGetData);
3163			},
3164
3165			/**
3166			 * Process ajax data
3167			 */
3168			insertData: function() {
3169				datatable.dataSet = datatable.dataSet || [];
3170				var params = Plugin.getDataSourceParam();
3171
3172				// get row attributes
3173				var pagination = params.pagination;
3174				var start = (Math.max(pagination.page, 1) - 1) * pagination.perpage;
3175				var end = Math.min(pagination.page, pagination.pages) * pagination.perpage;
3176				var rowProps = {};
3177				if (typeof options.data.attr.rowProps !== 'undefined' && options.data.attr.rowProps.length) {
3178					rowProps = options.data.attr.rowProps.slice(start, end);
3179				}
3180
3181				var tableBody = document.createElement('tbody');
3182				tableBody.style.visibility = 'hidden';
3183				var colLength = options.columns.length;
3184
3185				$.each(datatable.dataSet, function(rowIndex, row) {
3186					var tr = document.createElement('tr');
3187					tr.setAttribute('data-row', rowIndex);
3188					// keep data object to row
3189					$(tr).data('obj', row);
3190
3191					if (typeof rowProps[rowIndex] !== 'undefined') {
3192						$.each(rowProps[rowIndex], function() {
3193							tr.setAttribute(this.name, this.value);
3194						});
3195					}
3196
3197					var cellIndex = 0;
3198					var tds = [];
3199					for (var a = 0; a < colLength; a += 1) {
3200						var column = options.columns[a];
3201						var classes = [];
3202						// add sorted class to cells
3203						if (Plugin.getObject('sort.field', params) === column.field) {
3204							classes.push(pfx + 'datatable__cell--sorted');
3205						}
3206
3207						// apply text align
3208						if (typeof column.textAlign !== 'undefined') {
3209							var align = typeof datatable.textAlign[column.textAlign] !== 'undefined' ? datatable.textAlign[column.textAlign] : '';
3210							classes.push(align);
3211						}
3212
3213						// var classAttr = '';
3214						if (typeof column.class !== 'undefined') {
3215							classes.push(column.class);
3216						}
3217
3218						var td = document.createElement('td');
3219						util.addClass(td, classes.join(' '));
3220						td.setAttribute('data-field', column.field);
3221						// set disable autoHide or force enable
3222						if (typeof column.autoHide !== 'undefined') {
3223							if (column.autoHide !== true) {
3224								td.setAttribute('data-autohide-disabled', column.autoHide);
3225							} else {
3226								td.setAttribute('data-autohide-enabled', column.autoHide);
3227							}
3228						}
3229						td.innerHTML = Plugin.getObject(column.field, row);
3230						tr.appendChild(td);
3231					}
3232
3233					tableBody.appendChild(tr);
3234				});
3235
3236				// display no records message
3237				if (datatable.dataSet.length === 0) {
3238					var errorSpan = document.createElement('span');
3239					util.addClass(errorSpan, pfx + 'datatable--error');
3240					errorSpan.innerHTML = Plugin.getOption('translate.records.noRecords');
3241					tableBody.appendChild(errorSpan);
3242					$(datatable.wrap).addClass(pfx + 'datatable--error ' + pfx + 'datatable--loaded');
3243					Plugin.spinnerCallback(false);
3244				}
3245
3246				// replace existing table body
3247				$(datatable.tableBody).replaceWith(tableBody);
3248				datatable.tableBody = tableBody;
3249
3250				// layout update
3251				Plugin.setupDOM(datatable.table);
3252				Plugin.setupCellField([datatable.tableBody]);
3253				Plugin.setupTemplateCell(datatable.tableBody);
3254				Plugin.layoutUpdate();
3255			},
3256
3257			updateTableComponents: function() {
3258				datatable.tableHead = $(datatable.table).children('thead');
3259				datatable.tableBody = $(datatable.table).children('tbody');
3260				datatable.tableFoot = $(datatable.table).children('tfoot');
3261			},
3262
3263			/**
3264			 * Call ajax for raw JSON data
3265			 */
3266			getData: function() {
3267				// Plugin.spinnerCallback(true);
3268
3269				var ajaxParams = {
3270					dataType: 'json',
3271					method: 'POST',
3272					data: {},
3273					timeout: Plugin.getOption('data.source.read.timeout') || 30000,
3274				};
3275
3276				if (options.data.type === 'local') {
3277					ajaxParams.url = options.data.source;
3278				}
3279
3280				if (options.data.type === 'remote') {
3281					var data = Plugin.getDataSourceParam();
3282					// remove if server params is not enabled
3283					if (!Plugin.getOption('data.serverPaging')) {
3284						delete data['pagination'];
3285					}
3286					if (!Plugin.getOption('data.serverSorting')) {
3287						delete data['sort'];
3288					}
3289					ajaxParams.data = $.extend({}, ajaxParams.data, data, Plugin.getOption('data.source.read.params'));
3290					ajaxParams = $.extend({}, ajaxParams, Plugin.getOption('data.source.read'));
3291
3292					if (typeof ajaxParams.url !== 'string') ajaxParams.url = Plugin.getOption('data.source.read');
3293					if (typeof ajaxParams.url !== 'string') ajaxParams.url = Plugin.getOption('data.source');
3294					// ajaxParams.data = $.extend(ajaxParams.data, data.pagination);
3295				}
3296
3297				return $.ajax(ajaxParams).done(function(response, textStatus, jqXHR) {
3298					datatable.lastResponse = response;
3299					// extendible data map callback for custom datasource
3300					datatable.dataSet = datatable.originalDataSet = Plugin.dataMapCallback(response);
3301					Plugin.setAutoColumns();
3302					$(datatable).trigger(pfx + 'datatable--on-ajax-done', [datatable.dataSet]);
3303				}).fail(function(jqXHR, textStatus, errorThrown) {
3304					$(datatable).trigger(pfx + 'datatable--on-ajax-fail', [jqXHR]);
3305					$(datatable.tableBody).html($('<span/>').addClass(pfx + 'datatable--error').html(Plugin.getOption('translate.records.noRecords')));
3306					$(datatable.wrap).addClass(pfx + 'datatable--error ' + pfx + 'datatable--loaded');
3307					Plugin.spinnerCallback(false);
3308				}).always(function() {
3309				});
3310			},
3311
3312			/**
3313			 * Pagination object
3314			 * @param meta if null, local pagination, otherwise remote
3315			 *     pagination
3316			 * @param callback for update data when navigating page
3317			 */
3318			paging: function(meta, callback) {
3319				var pg = {
3320					meta: null,
3321					pager: null,
3322					paginateEvent: null,
3323					pagerLayout: {pagination: null, info: null},
3324					callback: null,
3325					init: function(meta) {
3326						pg.meta = meta;
3327
3328						// parse pagination meta to integer
3329						pg.meta.page = parseInt(pg.meta.page);
3330						pg.meta.pages = parseInt(pg.meta.pages);
3331						pg.meta.perpage = parseInt(pg.meta.perpage);
3332						pg.meta.total = parseInt(pg.meta.total);
3333
3334						// always recount total pages
3335						pg.meta.pages = Math.max(Math.ceil(pg.meta.total / pg.meta.perpage), 1);
3336
3337						// current page must be not over than total pages
3338						if (pg.meta.page > pg.meta.pages) pg.meta.page = pg.meta.pages;
3339
3340						// set unique event name between tables
3341						pg.paginateEvent = Plugin.getTablePrefix();
3342
3343						pg.pager = $(datatable.table).siblings('.' + pfx + 'datatable__pager');
3344						if ($(pg.pager).hasClass(pfx + 'datatable--paging-loaded')) return;
3345
3346						// if class .'+pfx+'datatable--paging-loaded not exist, recreate pagination
3347						$(pg.pager).remove();
3348
3349						// if no pages available
3350						if (pg.meta.pages === 0) return;
3351
3352						// update datasource params
3353						Plugin.setDataSourceParam('pagination', {
3354							page: pg.meta.page,
3355							pages: pg.meta.pages,
3356							perpage: pg.meta.perpage,
3357							total: pg.meta.total,
3358						});
3359
3360						// default callback function, contains remote pagination handler
3361						pg.callback = pg.serverCallback;
3362						// custom callback function
3363						if (typeof callback === 'function') pg.callback = callback;
3364
3365						pg.addPaginateEvent();
3366						pg.populate();
3367
3368						pg.meta.page = Math.max(pg.meta.page || 1, pg.meta.page);
3369
3370						$(datatable).trigger(pg.paginateEvent, pg.meta);
3371
3372						pg.pagingBreakpoint.call();
3373						$(window).resize(pg.pagingBreakpoint);
3374					},
3375					serverCallback: function(ctx, meta) {
3376						Plugin.dataRender();
3377					},
3378					populate: function() {
3379						var icons = Plugin.getOption('layout.icons.pagination');
3380						var title = Plugin.getOption('translate.toolbar.pagination.items.default');
3381						// pager root element
3382						pg.pager = $('<div/>').addClass(pfx + 'datatable__pager ' + pfx + 'datatable--paging-loaded');
3383						// numbering links
3384						var pagerNumber = $('<ul/>').addClass(pfx + 'datatable__pager-nav');
3385						pg.pagerLayout['pagination'] = pagerNumber;
3386
3387						// pager first/previous button
3388						$('<li/>').
3389							append($('<a/>').
3390								attr('title', title.first).
3391								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--first').
3392								append($('<i/>').addClass(icons.first)).
3393								on('click', pg.gotoMorePage).
3394								attr('data-page', 1)).
3395							appendTo(pagerNumber);
3396						$('<li/>').
3397							append($('<a/>').
3398								attr('title', title.prev).
3399								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--prev').
3400								append($('<i/>').addClass(icons.prev)).
3401								on('click', pg.gotoMorePage)).
3402							appendTo(pagerNumber);
3403
3404						// more previous pages
3405						$('<li/>').
3406							append($('<a/>').
3407								attr('title', title.more).
3408								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--more-prev').
3409								html($('<i/>').addClass(icons.more)).
3410								on('click', pg.gotoMorePage)).
3411							appendTo(pagerNumber);
3412
3413						$('<li/>').append($('<input/>').attr('type', 'text').addClass(pfx + 'pager-input form-control').attr('title', title.input).on('keyup', function() {
3414							// on keyup update [data-page]
3415							$(this).attr('data-page', Math.abs($(this).val()));
3416						}).on('keypress', function(e) {
3417							// on keypressed enter button
3418							if (e.which === 13) pg.gotoMorePage(e);
3419						})).appendTo(pagerNumber);
3420
3421						var pagesNumber = Plugin.getOption('toolbar.items.pagination.pages.desktop.pagesNumber');
3422						var end = Math.ceil(pg.meta.page / pagesNumber) * pagesNumber;
3423						var start = end - pagesNumber;
3424						if (end > pg.meta.pages) {
3425							end = pg.meta.pages;
3426						}
3427						for (var x = start; x < end; x++) {
3428							var pageNumber = x + 1;
3429							$('<li/>').
3430								append($('<a/>').
3431									addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link-number').
3432									text(pageNumber).
3433									attr('data-page', pageNumber).
3434									attr('title', pageNumber).
3435									on('click', pg.gotoPage)).
3436								appendTo(pagerNumber);
3437						}
3438
3439						// more next pages
3440						$('<li/>').
3441							append($('<a/>').
3442								attr('title', title.more).
3443								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--more-next').
3444								html($('<i/>').addClass(icons.more)).
3445								on('click', pg.gotoMorePage)).
3446							appendTo(pagerNumber);
3447
3448						// pager next/last button
3449						$('<li/>').
3450							append($('<a/>').
3451								attr('title', title.next).
3452								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--next').
3453								append($('<i/>').addClass(icons.next)).
3454								on('click', pg.gotoMorePage)).
3455							appendTo(pagerNumber);
3456						$('<li/>').
3457							append($('<a/>').
3458								attr('title', title.last).
3459								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--last').
3460								append($('<i/>').addClass(icons.last)).
3461								on('click', pg.gotoMorePage).
3462								attr('data-page', pg.meta.pages)).
3463							appendTo(pagerNumber);
3464
3465						// page info
3466						if (Plugin.getOption('toolbar.items.info')) {
3467							pg.pagerLayout['info'] = $('<div/>').addClass(pfx + 'datatable__pager-info').append($('<span/>').addClass(pfx + 'datatable__pager-detail'));
3468						}
3469
3470						$.each(Plugin.getOption('toolbar.layout'), function(i, layout) {
3471							$(pg.pagerLayout[layout]).appendTo(pg.pager);
3472						});
3473
3474						// page size select
3475						var pageSizeSelect = $('<select/>').
3476							addClass('selectpicker ' + pfx + 'datatable__pager-size').
3477							attr('title', Plugin.getOption('translate.toolbar.pagination.items.default.select')).
3478							attr('data-width', '60px').
3479							val(pg.meta.perpage).
3480							on('change', pg.updatePerpage).
3481							prependTo(pg.pagerLayout['info']);
3482
3483						var pageSizes = Plugin.getOption('toolbar.items.pagination.pageSizeSelect');
3484						// default value here, to fix override option by user
3485						if (pageSizes.length == 0) pageSizes = [10, 20, 30, 50, 100];
3486						$.each(pageSizes, function(i, size) {
3487							var display = size;
3488							if (size === -1) display = Plugin.getOption('translate.toolbar.pagination.items.default.all');
3489							$('<option/>').attr('value', size).html(display).appendTo(pageSizeSelect);
3490						});
3491
3492						// init selectpicker to dropdown
3493						$(datatable).ready(function() {
3494							$('.selectpicker').
3495								selectpicker().
3496								on('hide.bs.select', function() {
3497									// fix dropup arrow icon on hide
3498									$(this).closest('.bootstrap-select').removeClass('dropup');
3499								}).
3500								siblings('.dropdown-toggle').
3501								attr('title', Plugin.getOption('translate.toolbar.pagination.items.default.select'));
3502						});
3503
3504						pg.paste();
3505					},
3506					paste: function() {
3507						// insert pagination based on placement position, top|bottom
3508						$.each($.unique(Plugin.getOption('toolbar.placement')),
3509							function(i, position) {
3510								if (position === 'bottom') {
3511									$(pg.pager).clone(true).insertAfter(datatable.table);
3512								}
3513								if (position === 'top') {
3514									// pager top need some extra space
3515									$(pg.pager).clone(true).addClass(pfx + 'datatable__pager--top').insertBefore(datatable.table);
3516								}
3517							});
3518					},
3519					gotoMorePage: function(e) {
3520						e.preventDefault();
3521						// $(this) is a link of .'+pfx+'datatable__pager-link
3522
3523						if ($(this).attr('disabled') === 'disabled') return false;
3524
3525						var page = $(this).attr('data-page');
3526
3527						// event from text input
3528						if (typeof page === 'undefined') {
3529							page = $(e.target).attr('data-page');
3530						}
3531
3532						pg.openPage(parseInt(page));
3533						return false;
3534					},
3535					gotoPage: function(e) {
3536						e.preventDefault();
3537						// prevent from click same page number
3538						if ($(this).hasClass(pfx + 'datatable__pager-link--active')) return;
3539
3540						pg.openPage(parseInt($(this).data('page')));
3541					},
3542					openPage: function(page) {
3543						// currentPage is 1-based index
3544						pg.meta.page = parseInt(page);
3545
3546						$(datatable).trigger(pg.paginateEvent, pg.meta);
3547						pg.callback(pg, pg.meta);
3548
3549						// update page callback function
3550						$(pg.pager).trigger(pfx + 'datatable--on-goto-page', pg.meta);
3551					},
3552					updatePerpage: function(e) {
3553						e.preventDefault();
3554						// if (Plugin.getOption('layout.height') === null) {
3555						// fix white space, when perpage is set from many records to less records
3556						// $('html, body').animate({scrollTop: $(datatable).position().top});
3557						// }
3558
3559						pg.pager = $(datatable.table).siblings('.' + pfx + 'datatable__pager').removeClass(pfx + 'datatable--paging-loaded');
3560
3561						// on change select page size
3562						if (e.originalEvent) {
3563							pg.meta.perpage = parseInt($(this).val());
3564						}
3565
3566						$(pg.pager).find('select.' + pfx + 'datatable__pager-size').val(pg.meta.perpage).attr('data-selected', pg.meta.perpage);
3567
3568						// update datasource params
3569						Plugin.setDataSourceParam('pagination', {
3570							page: pg.meta.page,
3571							pages: pg.meta.pages,
3572							perpage: pg.meta.perpage,
3573							total: pg.meta.total,
3574						});
3575
3576						// update page callback function
3577						$(pg.pager).trigger(pfx + 'datatable--on-update-perpage', pg.meta);
3578						$(datatable).trigger(pg.paginateEvent, pg.meta);
3579						pg.callback(pg, pg.meta);
3580
3581						// update pagination info
3582						pg.updateInfo.call();
3583					},
3584					addPaginateEvent: function(e) {
3585						// pagination event
3586						$(datatable).off(pg.paginateEvent).on(pg.paginateEvent, function(e, meta) {
3587							Plugin.spinnerCallback(true);
3588
3589							pg.pager = $(datatable.table).siblings('.' + pfx + 'datatable__pager');
3590							var pagerNumber = $(pg.pager).find('.' + pfx + 'datatable__pager-nav');
3591
3592							// set sync active page class
3593							$(pagerNumber).find('.' + pfx + 'datatable__pager-link--active').removeClass(pfx + 'datatable__pager-link--active');
3594							$(pagerNumber).find('.' + pfx + 'datatable__pager-link-number[data-page="' + meta.page + '"]').addClass(pfx + 'datatable__pager-link--active');
3595
3596							// set next and previous link page number
3597							$(pagerNumber).find('.' + pfx + 'datatable__pager-link--prev').attr('data-page', Math.max(meta.page - 1, 1));
3598							$(pagerNumber).find('.' + pfx + 'datatable__pager-link--next').attr('data-page', Math.min(meta.page + 1, meta.pages));
3599
3600							// current page input value sync
3601							$(pg.pager).each(function() {
3602								$(this).find('.' + pfx + 'pager-input[type="text"]').prop('value', meta.page);
3603							});
3604
3605							$(pg.pager).find('.' + pfx + 'datatable__pager-nav').show();
3606							if (meta.pages <= 1) {
3607								// hide pager if has 1 page
3608								$(pg.pager).find('.' + pfx + 'datatable__pager-nav').hide();
3609							}
3610
3611							// update datasource params
3612							Plugin.setDataSourceParam('pagination', {
3613								page: pg.meta.page,
3614								pages: pg.meta.pages,
3615								perpage: pg.meta.perpage,
3616								total: pg.meta.total,
3617							});
3618
3619							$(pg.pager).find('select.' + pfx + 'datatable__pager-size').val(meta.perpage).attr('data-selected', meta.perpage);
3620
3621							// clear active rows
3622							$(datatable.table).find('.' + pfx + 'checkbox > [type="checkbox"]').prop('checked', false);
3623							$(datatable.table).find('.' + pfx + 'datatable__row--active').removeClass(pfx + 'datatable__row--active');
3624
3625							pg.updateInfo.call();
3626							pg.pagingBreakpoint.call();
3627							// Plugin.resetScroll();
3628						});
3629					},
3630					updateInfo: function() {
3631						var start = Math.max(pg.meta.perpage * (pg.meta.page - 1) + 1, 1);
3632						var end = Math.min(start + pg.meta.perpage - 1, pg.meta.total);
3633						// page info update
3634						$(pg.pager).find('.' + pfx + 'datatable__pager-info').find('.' + pfx + 'datatable__pager-detail').html(Plugin.dataPlaceholder(
3635							Plugin.getOption('translate.toolbar.pagination.items.info'), {
3636								start: start,
3637								end: pg.meta.perpage === -1 ? pg.meta.total : end,
3638								pageSize: pg.meta.perpage === -1 ||
3639								pg.meta.perpage >= pg.meta.total
3640									? pg.meta.total
3641									: pg.meta.perpage,
3642								total: pg.meta.total,
3643							}));
3644					},
3645
3646					/**
3647					 * Update pagination layout breakpoint
3648					 */
3649					pagingBreakpoint: function() {
3650						// keep page links reference
3651						var pagerNumber = $(datatable.table).siblings('.' + pfx + 'datatable__pager').find('.' + pfx + 'datatable__pager-nav');
3652						if ($(pagerNumber).length === 0) return;
3653
3654						var currentPage = Plugin.getCurrentPage();
3655						var pagerInput = $(pagerNumber).find('.' + pfx + 'pager-input').closest('li');
3656
3657						// reset
3658						$(pagerNumber).find('li').show();
3659
3660						// pagination update
3661						$.each(Plugin.getOption('toolbar.items.pagination.pages'),
3662							function(mode, option) {
3663								if (util.isInResponsiveRange(mode)) {
3664									switch (mode) {
3665										case 'desktop':
3666										case 'tablet':
3667											var end = Math.ceil(currentPage / option.pagesNumber) *
3668												option.pagesNumber;
3669											var start = end - option.pagesNumber;
3670											$(pagerInput).hide();
3671											pg.meta = Plugin.getDataSourceParam('pagination');
3672											pg.paginationUpdate();
3673											break;
3674
3675										case 'mobile':
3676											$(pagerInput).show();
3677											$(pagerNumber).find('.' + pfx + 'datatable__pager-link--more-prev').closest('li').hide();
3678											$(pagerNumber).find('.' + pfx + 'datatable__pager-link--more-next').closest('li').hide();
3679											$(pagerNumber).find('.' + pfx + 'datatable__pager-link-number').closest('li').hide();
3680											break;
3681									}
3682
3683									return false;
3684								}
3685							});
3686					},
3687
3688					/**
3689					 * Update pagination number and button display
3690					 */
3691					paginationUpdate: function() {
3692						var pager = $(datatable.table).siblings('.' + pfx + 'datatable__pager').find('.' + pfx + 'datatable__pager-nav'),
3693							pagerMorePrev = $(pager).find('.' + pfx + 'datatable__pager-link--more-prev'),
3694							pagerMoreNext = $(pager).find('.' + pfx + 'datatable__pager-link--more-next'),
3695							pagerFirst = $(pager).find('.' + pfx + 'datatable__pager-link--first'),
3696							pagerPrev = $(pager).find('.' + pfx + 'datatable__pager-link--prev'),
3697							pagerNext = $(pager).find('.' + pfx + 'datatable__pager-link--next'),
3698							pagerLast = $(pager).find('.' + pfx + 'datatable__pager-link--last');
3699
3700						// get visible page
3701						var pagerNumber = $(pager).find('.' + pfx + 'datatable__pager-link-number');
3702						// get page before of first visible
3703						var morePrevPage = Math.max($(pagerNumber).first().data('page') - 1,
3704							1);
3705						$(pagerMorePrev).each(function(i, prev) {
3706							$(prev).attr('data-page', morePrevPage);
3707						});
3708						// show/hide <li>
3709						if (morePrevPage === 1) {
3710							$(pagerMorePrev).parent().hide();
3711						} else {
3712							$(pagerMorePrev).parent().show();
3713						}
3714
3715						// get page after of last visible
3716						var moreNextPage = Math.min($(pagerNumber).last().data('page') + 1,
3717							pg.meta.pages);
3718						$(pagerMoreNext).each(function(i, prev) {
3719							$(pagerMoreNext).attr('data-page', moreNextPage).show();
3720						});
3721
3722						// show/hide <li>
3723						if (moreNextPage === pg.meta.pages
3724							// missing dot fix when last hidden page is one left
3725							&& moreNextPage === $(pagerNumber).last().data('page')) {
3726							$(pagerMoreNext).parent().hide();
3727						} else {
3728							$(pagerMoreNext).parent().show();
3729						}
3730
3731						// begin/end of pages
3732						if (pg.meta.page === 1) {
3733							$(pagerFirst).attr('disabled', true).addClass(pfx + 'datatable__pager-link--disabled');
3734							$(pagerPrev).attr('disabled', true).addClass(pfx + 'datatable__pager-link--disabled');
3735						} else {
3736							$(pagerFirst).removeAttr('disabled').removeClass(pfx + 'datatable__pager-link--disabled');
3737							$(pagerPrev).removeAttr('disabled').removeClass(pfx + 'datatable__pager-link--disabled');
3738						}
3739						if (pg.meta.page === pg.meta.pages) {
3740							$(pagerNext).attr('disabled', true).addClass(pfx + 'datatable__pager-link--disabled');
3741							$(pagerLast).attr('disabled', true).addClass(pfx + 'datatable__pager-link--disabled');
3742						} else {
3743							$(pagerNext).removeAttr('disabled').removeClass(pfx + 'datatable__pager-link--disabled');
3744							$(pagerLast).removeAttr('disabled').removeClass(pfx + 'datatable__pager-link--disabled');
3745						}
3746
3747						// display more buttons
3748						var nav = Plugin.getOption('toolbar.items.pagination.navigation');
3749						if (!nav.first) $(pagerFirst).remove();
3750						if (!nav.prev) $(pagerPrev).remove();
3751						if (!nav.next) $(pagerNext).remove();
3752						if (!nav.last) $(pagerLast).remove();
3753						if (!nav.more) {
3754							$(pagerMorePrev).remove();
3755							$(pagerMoreNext).remove();
3756						}
3757					},
3758				};
3759				pg.init(meta);
3760				return pg;
3761			},
3762
3763			/**
3764			 * Hide/show table cell defined by
3765			 * options[columns][i][responsive][visible/hidden]
3766			 */
3767			columnHide: function() {
3768				var screen = util.getViewPort().width;
3769				// foreach columns setting
3770				$.each(options.columns, function(i, column) {
3771					if (typeof column.responsive !== 'undefined') {
3772						var field = column.field;
3773						var tds = $.grep($(datatable.table).find('.' + pfx + 'datatable__cell'), function(n, i) {
3774							return field === $(n).data('field');
3775						});
3776						if (util.getBreakpoint(column.responsive.hidden) >= screen) {
3777							$(tds).hide();
3778						} else {
3779							$(tds).show();
3780						}
3781						if (util.getBreakpoint(column.responsive.visible) <= screen) {
3782							$(tds).show();
3783						} else {
3784							$(tds).hide();
3785						}
3786					}
3787				});
3788			},
3789
3790			/**
3791			 * Setup sub datatable
3792			 */
3793			setupSubDatatable: function() {
3794				var subTableCallback = Plugin.getOption('detail.content');
3795				if (typeof subTableCallback !== 'function') return;
3796
3797				// subtable already exist
3798				if ($(datatable.table).find('.' + pfx + 'datatable__subtable').length > 0) return;
3799
3800				$(datatable.wrap).addClass(pfx + 'datatable--subtable');
3801
3802				options.columns[0]['subtable'] = true;
3803
3804				// toggle on open sub table
3805				var toggleSubTable = function(e) {
3806					e.preventDefault();
3807					// get parent row of this subtable
3808					var parentRow = $(this).closest('.' + pfx + 'datatable__row');
3809
3810					// get subtable row for sub table
3811					var subTableRow = $(parentRow).next('.' + pfx + 'datatable__row-subtable');
3812					if ($(subTableRow).length === 0) {
3813						// prepare DOM for sub table, each <tr> as parent and add <tr> as child table
3814						subTableRow = $('<tr/>').
3815							addClass(pfx + 'datatable__row-subtable ' + pfx + 'datatable__row-loading').
3816							hide().
3817							append($('<td/>').addClass(pfx + 'datatable__subtable').attr('colspan', Plugin.getTotalColumns()));
3818						$(parentRow).after(subTableRow);
3819						// add class to even row
3820						if ($(parentRow).hasClass(pfx + 'datatable__row--even')) {
3821							$(subTableRow).addClass(pfx + 'datatable__row-subtable--even');
3822						}
3823					}
3824
3825					$(subTableRow).toggle();
3826
3827					var subTable = $(subTableRow).find('.' + pfx + 'datatable__subtable');
3828
3829					// get id from first column of parent row
3830					var primaryKey = $(this).closest('[data-field]:first-child').find('.' + pfx + 'datatable__toggle-subtable').data('value');
3831
3832					var icon = $(this).find('i').removeAttr('class');
3833
3834					// prevent duplicate datatable init
3835					if ($(parentRow).hasClass(pfx + 'datatable__row--subtable-expanded')) {
3836						$(icon).addClass(Plugin.getOption('layout.icons.rowDetail.collapse'));
3837						// remove expand class from parent row
3838						$(parentRow).removeClass(pfx + 'datatable__row--subtable-expanded');
3839						// trigger event on collapse
3840						$(datatable).trigger(pfx + 'datatable--on-collapse-subtable', [parentRow]);
3841					} else {
3842						// expand and run callback function
3843						$(icon).addClass(Plugin.getOption('layout.icons.rowDetail.expand'));
3844						// add expand class to parent row
3845						$(parentRow).addClass(pfx + 'datatable__row--subtable-expanded');
3846						// trigger event on expand
3847						$(datatable).trigger(pfx + 'datatable--on-expand-subtable', [parentRow]);
3848					}
3849
3850					// prevent duplicate datatable init
3851					if ($(subTable).find('.' + pfx + 'datatable').length === 0) {
3852						// get data by primary id
3853						$.map(datatable.dataSet, function(n, i) {
3854							// primary id must be at the first column, otherwise e.data will be undefined
3855							if (primaryKey === n[options.columns[0].field]) {
3856								e.data = n;
3857								return true;
3858							}
3859							return false;
3860						});
3861
3862						// deprecated in v5.0.6
3863						e.detailCell = subTable;
3864
3865						e.parentRow = parentRow;
3866						e.subTable = subTable;
3867
3868						// run callback with event
3869						subTableCallback(e);
3870
3871						$(subTable).children('.' + pfx + 'datatable').on(pfx + 'datatable--on-init', function(e) {
3872							$(subTableRow).removeClass(pfx + 'datatable__row-loading');
3873						});
3874						if (Plugin.getOption('data.type') === 'local') {
3875							$(subTableRow).removeClass(pfx + 'datatable__row-loading');
3876						}
3877					}
3878				};
3879
3880				var columns = options.columns;
3881				$(datatable.tableBody).find('.' + pfx + 'datatable__row').each(function(tri, tr) {
3882					$(tr).find('.' + pfx + 'datatable__cell').each(function(tdi, td) {
3883						// get column settings by field
3884						var column = $.grep(columns, function(n, i) {
3885							return $(td).data('field') === n.field;
3886						})[0];
3887						if (typeof column !== 'undefined') {
3888							var value = $(td).text();
3889							// enable column subtable toggle
3890							if (typeof column.subtable !== 'undefined' && column.subtable) {
3891								// check if subtable toggle exist
3892								if ($(td).find('.' + pfx + 'datatable__toggle-subtable').length > 0) return;
3893								// append subtable toggle
3894								$(td).
3895									html($('<a/>').
3896										addClass(pfx + 'datatable__toggle-subtable').
3897										attr('href', '#').
3898										attr('data-value', value).
3899										attr('title', Plugin.getOption('detail.title')).
3900										on('click', toggleSubTable).
3901										append($('<i/>').css('width', $(td).data('width')).addClass(Plugin.getOption('layout.icons.rowDetail.collapse'))));
3902							}
3903						}
3904					});
3905				});
3906
3907				// $(datatable.tableHead).find('.'+pfx+'-datatable__row').first()
3908			},
3909
3910			/**
3911			 * Datasource mapping callback
3912			 */
3913			dataMapCallback: function(raw) {
3914				// static dataset array
3915				var dataSet = raw;
3916				// dataset mapping callback
3917				if (typeof Plugin.getOption('data.source.read.map') === 'function') {
3918					return Plugin.getOption('data.source.read.map')(raw);
3919				} else {
3920					// default data mapping fallback
3921					if (typeof raw !== 'undefined' && typeof raw.data !== 'undefined') {
3922						dataSet = raw.data;
3923					}
3924				}
3925				return dataSet;
3926			},
3927
3928			isSpinning: false,
3929			/**
3930			 * BlockUI spinner callback
3931			 * @param block
3932			 * @param target
3933			 */
3934			spinnerCallback: function(block, target) {
3935				if (typeof target === 'undefined') target = datatable;
3936				// get spinner options
3937				var spinnerOptions = Plugin.getOption('layout.spinner');
3938				// spinner is disabled
3939				if (typeof spinnerOptions === 'undefined' || !spinnerOptions) {
3940					return;
3941				}
3942				if (block) {
3943					if (!Plugin.isSpinning) {
3944						if (typeof spinnerOptions.message !== 'undefined' && spinnerOptions.message === true) {
3945							// use default spinner message from translation
3946							spinnerOptions.message = Plugin.getOption('translate.records.processing');
3947						}
3948						Plugin.isSpinning = true;
3949						if (typeof app !== 'undefined') {
3950							app.block(target, spinnerOptions);
3951						}
3952					}
3953				} else {
3954					Plugin.isSpinning = false;
3955					if (typeof app !== 'undefined') {
3956						app.unblock(target);
3957					}
3958				}
3959			},
3960
3961			/**
3962			 * Default sort callback function
3963			 * @param data
3964			 * @param sort
3965			 * @param column
3966			 * @returns {*|Array.<T>|{sort, field}|{asc, desc}}
3967			 */
3968			sortCallback: function(data, sort, column) {
3969				var type = column['type'] || 'string';
3970				var format = column['format'] || '';
3971				var field = column['field'];
3972
3973				return $(data).sort(function(a, b) {
3974					var aField = a[field];
3975					var bField = b[field];
3976
3977					switch (type) {
3978						case 'date':
3979							if (typeof moment === 'undefined') {
3980								throw new Error('Moment.js is required.');
3981							}
3982							var diff = moment(aField, format).diff(moment(bField, format));
3983							if (sort === 'asc') {
3984								return diff > 0 ? 1 : diff < 0 ? -1 : 0;
3985							} else {
3986								return diff < 0 ? 1 : diff > 0 ? -1 : 0;
3987							}
3988							break;
3989
3990						case 'number':
3991							if (isNaN(parseFloat(aField)) && aField != null) {
3992								aField = Number(aField.replace(/[^0-9\.-]+/g, ''));
3993							}
3994							if (isNaN(parseFloat(bField)) && bField != null) {
3995								bField = Number(bField.replace(/[^0-9\.-]+/g, ''));
3996							}
3997							aField = parseFloat(aField);
3998							bField = parseFloat(bField);
3999							if (sort === 'asc') {
4000								return aField > bField ? 1 : aField < bField ? -1 : 0;
4001							} else {
4002								return aField < bField ? 1 : aField > bField ? -1 : 0;
4003							}
4004							break;
4005
4006						case 'string':
4007						default:
4008							if (sort === 'asc') {
4009								return aField > bField ? 1 : aField < bField ? -1 : 0;
4010							} else {
4011								return aField < bField ? 1 : aField > bField ? -1 : 0;
4012							}
4013							break;
4014					}
4015				});
4016			},
4017
4018			/**
4019			 * Custom debug log
4020			 * @param text
4021			 * @param obj
4022			 */
4023			log: function(text, obj) {
4024				if (typeof obj === 'undefined') obj = '';
4025				if (datatable.debug) {
4026					console.log(text, obj);
4027				}
4028			},
4029
4030			/**
4031			 * Auto hide columnds overflow in row
4032			 */
4033			autoHide: function() {
4034				var hiddenExist = false;
4035				// force hide enabled
4036				var hidDefault = $(datatable.table).find('[data-autohide-enabled]');
4037				if (hidDefault.length) {
4038					hiddenExist = true;
4039					hidDefault.hide();
4040				}
4041
4042				var toggleHiddenColumns = function(e) {
4043					e.preventDefault();
4044
4045					var row = $(this).closest('.' + pfx + 'datatable__row');
4046					var detailRow = $(row).next();
4047
4048					if (!$(detailRow).hasClass(pfx + 'datatable__row-detail')) {
4049						$(this).find('i').removeClass(Plugin.getOption('layout.icons.rowDetail.collapse')).addClass(Plugin.getOption('layout.icons.rowDetail.expand'));
4050
4051						var hiddenCells = $(row).find('.' + pfx + 'datatable__cell:hidden');
4052						var clonedCells = hiddenCells.clone().show();
4053
4054						detailRow = $('<tr/>').addClass(pfx + 'datatable__row-detail').insertAfter(row);
4055						var detailRowTd = $('<td/>').addClass(pfx + 'datatable__detail').attr('colspan', Plugin.getTotalColumns()).appendTo(detailRow);
4056
4057						var detailSubTable = $('<table/>');
4058						$(clonedCells).each(function() {
4059							var field = $(this).data('field');
4060							var column = $.grep(options.columns, function(n, i) {
4061								return field === n.field;
4062							})[0];
4063							$(detailSubTable).
4064								append($('<tr class="' + pfx + 'datatable__row"></tr>').
4065									append($('<td class="' + pfx + 'datatable__cell"></td>').append($('<span/>').append(column.title))).
4066									append(this));
4067						});
4068						$(detailRowTd).append(detailSubTable);
4069
4070					} else {
4071						$(this).find('i').removeClass(Plugin.getOption('layout.icons.rowDetail.expand')).addClass(Plugin.getOption('layout.icons.rowDetail.collapse'));
4072						$(detailRow).remove();
4073					}
4074				};
4075
4076				setTimeout(function () {
4077					$(datatable.table).find('.' + pfx + 'datatable__cell').show();
4078					$(datatable.tableBody).each(function() {
4079						var recursive = 0;
4080						while ($(this)[0].offsetWidth < $(this)[0].scrollWidth && recursive < options.columns.length) {
4081							$(datatable.table).find('.' + pfx + 'datatable__row').each(function(i) {
4082								var cell = $(this).find('.' + pfx + 'datatable__cell:not(:hidden):not([data-autohide-disabled])').last();
4083								$(cell).hide();
4084								hiddenExist = true;
4085							});
4086							recursive++;
4087						}
4088					});
4089
4090					if (hiddenExist) {
4091						// toggle show hidden columns
4092						$(datatable.tableBody).find('.' + pfx + 'datatable__row').each(function() {
4093							// if no toggle yet
4094							if($(this).find('.' + pfx + 'datatable__toggle-detail').length === 0) {
4095								// add toggle
4096								$(this).prepend($('<td/>').
4097										addClass(pfx + 'datatable__cell ' + pfx + 'datatable__toggle-detail').
4098										append($('<a/>').
4099											addClass(pfx + 'datatable__toggle-detail').
4100											attr('href', '').
4101											on('click', toggleHiddenColumns).
4102											append('<i class="' + Plugin.getOption('layout.icons.rowDetail.collapse') + '"></i>')));
4103							}
4104
4105							// check if subtable toggle exist
4106							if ($(datatable.tableHead).find('.' + pfx + 'datatable__toggle-detail').length === 0) {
4107								// add empty column to the header and footer
4108								$(datatable.tableHead).
4109									find('.' + pfx + 'datatable__row').
4110									first().
4111									prepend('<th class="' + pfx + 'datatable__cell ' + pfx + 'datatable__toggle-detail"><span></span></th>');
4112								$(datatable.tableFoot).
4113									find('.' + pfx + 'datatable__row').
4114									first().
4115									prepend('<th class="' + pfx + 'datatable__cell ' + pfx + 'datatable__toggle-detail"><span></span></th>');
4116							} else {
4117								$(datatable.tableHead).find('.' + pfx + 'datatable__toggle-detail').find('span');
4118							}
4119						});
4120					}
4121				});
4122
4123				Plugin.adjustCellsWidth.call();
4124			},
4125
4126			/**
4127			 * To enable auto columns features for remote data source
4128			 */
4129			setAutoColumns: function() {
4130				if (Plugin.getOption('data.autoColumns')) {
4131					$.each(datatable.dataSet[0], function(k, v) {
4132						var found = $.grep(options.columns, function(n, i) {
4133							return k === n.field;
4134						});
4135						if (found.length === 0) {
4136							options.columns.push({field: k, title: k});
4137						}
4138					});
4139					$(datatable.tableHead).find('.' + pfx + 'datatable__row').remove();
4140					Plugin.setHeadTitle();
4141					if (Plugin.getOption('layout.footer')) {
4142						$(datatable.tableFoot).find('.' + pfx + 'datatable__row').remove();
4143						Plugin.setHeadTitle(datatable.tableFoot);
4144					}
4145				}
4146			},
4147
4148			/********************
4149			 ** HELPERS
4150			 ********************/
4151
4152			/**
4153			 * Check if table is a locked colums table
4154			 */
4155			isLocked: function() {
4156				var isLocked = Plugin.lockEnabledColumns();
4157				return isLocked.left.length > 0 || isLocked.right.length > 0;
4158			},
4159
4160			isSubtable: function() {
4161				return util.hasClass(datatable.wrap[0], pfx + 'datatable--subtable') || false;
4162			},
4163
4164			/**
4165			 * Get total extra space of an element for width calculation,
4166			 * including padding, margin, border
4167			 * @param element
4168			 * @returns {number}
4169			 */
4170			getExtraSpace: function(element) {
4171				var padding = parseInt($(element).css('paddingRight')) +
4172					parseInt($(element).css('paddingLeft'));
4173				var margin = parseInt($(element).css('marginRight')) +
4174					parseInt($(element).css('marginLeft'));
4175				var border = Math.ceil(
4176					$(element).css('border-right-width').replace('px', ''));
4177				return padding + margin + border;
4178			},
4179
4180			/**
4181			 * Insert data of array into {{ }} template placeholder
4182			 * @param template
4183			 * @param data
4184			 * @returns {*}
4185			 */
4186			dataPlaceholder: function(template, data) {
4187				var result = template;
4188				$.each(data, function(key, val) {
4189					result = result.replace('{{' + key + '}}', val);
4190				});
4191				return result;
4192			},
4193
4194			/**
4195			 * Get table unique ID
4196			 * Note: table unique change each time refreshed
4197			 * @param suffix
4198			 * @returns {*}
4199			 */
4200			getTableId: function(suffix) {
4201				if (typeof suffix === 'undefined') suffix = '';
4202				var id = $(datatable).attr('id');
4203				if (typeof id === 'undefined') {
4204					id = $(datatable).attr('class').split(' ')[0];
4205				}
4206				return id + suffix;
4207			},
4208
4209			/**
4210			 * Get table prefix with depth number
4211			 */
4212			getTablePrefix: function(suffix) {
4213				if (typeof suffix !== 'undefined') suffix = '-' + suffix;
4214				return Plugin.getTableId() + '-' + Plugin.getDepth() + suffix;
4215			},
4216
4217			/**
4218			 * Get current table depth of sub table
4219			 * @returns {number}
4220			 */
4221			getDepth: function() {
4222				var depth = 0;
4223				var table = datatable.table;
4224				do {
4225					table = $(table).parents('.' + pfx + 'datatable__table');
4226					depth++;
4227				} while ($(table).length > 0);
4228				return depth;
4229			},
4230
4231			/**
4232			 * Keep state item
4233			 * @param key
4234			 * @param value
4235			 */
4236			stateKeep: function(key, value) {
4237				key = Plugin.getTablePrefix(key);
4238				if (Plugin.getOption('data.saveState') === false) return;
4239				if (Plugin.getOption('data.saveState.webstorage') && localStorage) {
4240					localStorage.setItem(key, JSON.stringify(value));
4241				}
4242				if (Plugin.getOption('data.saveState.cookie')) {
4243					Cookies.set(key, JSON.stringify(value));
4244				}
4245			},
4246
4247			/**
4248			 * Get state item
4249			 * @param key
4250			 * @param defValue
4251			 */
4252			stateGet: function(key, defValue) {
4253				key = Plugin.getTablePrefix(key);
4254				if (Plugin.getOption('data.saveState') === false) return;
4255				var value = null;
4256				if (Plugin.getOption('data.saveState.webstorage') && localStorage) {
4257					value = localStorage.getItem(key);
4258				} else {
4259					value = Cookies.get(key);
4260				}
4261				if (typeof value !== 'undefined' && value !== null) {
4262					return JSON.parse(value);
4263				}
4264			},
4265
4266			/**
4267			 * Update data in state without clear existing
4268			 * @param key
4269			 * @param value
4270			 */
4271			stateUpdate: function(key, value) {
4272				var ori = Plugin.stateGet(key);
4273				if (typeof ori === 'undefined' || ori === null) ori = {};
4274				Plugin.stateKeep(key, $.extend({}, ori, value));
4275			},
4276
4277			/**
4278			 * Remove state item
4279			 * @param key
4280			 */
4281			stateRemove: function(key) {
4282				key = Plugin.getTablePrefix(key);
4283				if (localStorage) {
4284					localStorage.removeItem(key);
4285				}
4286				Cookies.remove(key);
4287			},
4288
4289			/**
4290			 * Get total columns.
4291			 */
4292			getTotalColumns: function(tablePart) {
4293				if (typeof tablePart === 'undefined') tablePart = datatable.tableBody;
4294				return $(tablePart).find('.' + pfx + 'datatable__row').first().find('.' + pfx + 'datatable__cell').length;
4295			},
4296
4297			/**
4298			 * Get table row. Useful to get row when current table is in lock
4299			 * mode. Can be used for both lock and normal table mode. By
4300			 * default, returning result will be in a list of <td>.
4301			 * @param tablePart
4302			 * @param row 1-based index
4303			 * @param tdOnly Optional. Default true
4304			 * @returns {*}
4305			 */
4306			getOneRow: function(tablePart, row, tdOnly) {
4307				if (typeof tdOnly === 'undefined') tdOnly = true;
4308				// get list of <tr>
4309				var result = $(tablePart).find('.' + pfx + 'datatable__row:not(.' + pfx + 'datatable__row-detail):nth-child(' + row + ')');
4310				if (tdOnly) {
4311					// get list of <td> or <th>
4312					result = result.find('.' + pfx + 'datatable__cell');
4313				}
4314				return result;
4315			},
4316
4317			/**
4318			 * Sort table row at HTML level by column index.
4319			 * todo; Not in use.
4320			 * @param header Header sort clicked
4321			 * @param sort asc|desc. Optional. Default asc
4322			 * @param int Boolean. Optional. Comparison value parse to integer.
4323			 *     Default false
4324			 */
4325			sortColumn: function(header, sort, int) {
4326				if (typeof sort === 'undefined') sort = 'asc'; // desc
4327				if (typeof int === 'undefined') int = false;
4328
4329				var column = $(header).index();
4330				var rows = $(datatable.tableBody).find('.' + pfx + 'datatable__row');
4331				var hIndex = $(header).closest('.' + pfx + 'datatable__lock').index();
4332				if (hIndex !== -1) {
4333					rows = $(datatable.tableBody).find('.' + pfx + 'datatable__lock:nth-child(' + (hIndex + 1) + ')').find('.' + pfx + 'datatable__row');
4334				}
4335
4336				var container = $(rows).parent();
4337				$(rows).sort(function(a, b) {
4338					var tda = $(a).find('td:nth-child(' + column + ')').text();
4339					var tdb = $(b).find('td:nth-child(' + column + ')').text();
4340
4341					if (int) {
4342						// useful for integer type sorting
4343						tda = parseInt(tda);
4344						tdb = parseInt(tdb);
4345					}
4346
4347					if (sort === 'asc') {
4348						return tda > tdb ? 1 : tda < tdb ? -1 : 0;
4349					} else {
4350						return tda < tdb ? 1 : tda > tdb ? -1 : 0;
4351					}
4352				}).appendTo(container);
4353			},
4354
4355			/**
4356			 * Perform sort remote and local
4357			 */
4358			sorting: function() {
4359				var sortObj = {
4360					init: function() {
4361						if (options.sortable) {
4362							$(datatable.tableHead).
4363								find('.' + pfx + 'datatable__cell:not(.' + pfx + 'datatable__cell--check)').
4364								addClass(pfx + 'datatable__cell--sort').
4365								off('click').
4366								on('click', sortObj.sortClick);
4367							// first init
4368							sortObj.setIcon();
4369						}
4370					},
4371					setIcon: function() {
4372						var meta = Plugin.getDataSourceParam('sort');
4373						if ($.isEmptyObject(meta)) return;
4374
4375						var column = Plugin.getColumnByField(meta.field);
4376						// sort is disabled for this column
4377						if (typeof column !== 'undefined' && typeof column.sortable !== 'undefined' && column.sortable === false) return;
4378
4379						// sort icon beside column header
4380						var td = $(datatable.tableHead).find('.' + pfx + 'datatable__cell[data-field="' + meta.field + '"]').attr('data-sort', meta.sort);
4381						var sorting = $(td).find('span');
4382						var icon = $(sorting).find('i');
4383
4384						var icons = Plugin.getOption('layout.icons.sort');
4385						// update sort icon; desc & asc
4386						if ($(icon).length > 0) {
4387							$(icon).removeAttr('class').addClass(icons[meta.sort]);
4388						} else {
4389							$(sorting).append($('<i/>').addClass(icons[meta.sort]));
4390						}
4391
4392						// set sorted class to header on init
4393						$(td).addClass(pfx + 'datatable__cell--sorted');
4394					},
4395					sortClick: function(e) {
4396						var meta = Plugin.getDataSourceParam('sort');
4397						var field = $(this).data('field');
4398						var column = Plugin.getColumnByField(field);
4399						// sort is disabled for this column
4400						if (typeof column.sortable !== 'undefined' && column.sortable === false) return;
4401
4402						// set sorted class to header
4403						$(datatable.tableHead).find('th').removeClass(pfx + 'datatable__cell--sorted');
4404						util.addClass(this, pfx + 'datatable__cell--sorted');
4405
4406						$(datatable.tableHead).find('.' + pfx + 'datatable__cell > span > i').remove();
4407
4408						if (options.sortable) {
4409							Plugin.spinnerCallback(true);
4410
4411							var sort = 'desc';
4412							if (Plugin.getObject('field', meta) === field) {
4413								sort = Plugin.getObject('sort', meta);
4414							}
4415
4416							// toggle sort
4417							sort = typeof sort === 'undefined' || sort === 'desc'
4418								? 'asc'
4419								: 'desc';
4420
4421							// update field and sort params
4422							meta = {field: field, sort: sort};
4423							Plugin.setDataSourceParam('sort', meta);
4424
4425							sortObj.setIcon();
4426
4427							setTimeout(function() {
4428								Plugin.dataRender('sort');
4429								$(datatable).trigger(pfx + 'datatable--on-sort', meta);
4430							}, 300);
4431						}
4432					},
4433				};
4434				sortObj.init();
4435			},
4436
4437			/**
4438			 * Update JSON data list linked with sort, filter and pagination.
4439			 * Call this method, before using dataSet variable.
4440			 * @returns {*|null}
4441			 */
4442			localDataUpdate: function() {
4443				var params = Plugin.getDataSourceParam();
4444				if (typeof datatable.originalDataSet === 'undefined') {
4445					datatable.originalDataSet = datatable.dataSet;
4446				}
4447
4448				var field = Plugin.getObject('sort.field', params);
4449				var sort = Plugin.getObject('sort.sort', params);
4450				var column = Plugin.getColumnByField(field);
4451				if (typeof column !== 'undefined' && Plugin.getOption('data.serverSorting') !== true) {
4452					if (typeof column.sortCallback === 'function') {
4453						datatable.dataSet = column.sortCallback(datatable.originalDataSet, sort, column);
4454					} else {
4455						datatable.dataSet = Plugin.sortCallback(datatable.originalDataSet, sort, column);
4456					}
4457				} else {
4458					datatable.dataSet = datatable.originalDataSet;
4459				}
4460
4461				// if server filter enable, don't pass local filter
4462				if (typeof params.query === 'object' && !Plugin.getOption('data.serverFiltering')) {
4463					params.query = params.query || {};
4464
4465					var nestedSearch = function(obj) {
4466						for (var field in obj) {
4467							if (!obj.hasOwnProperty(field)) continue;
4468							if (typeof obj[field] === 'string') {
4469								if (obj[field].toLowerCase() == search || obj[field].toLowerCase().indexOf(search) !== -1) {
4470									return true;
4471								}
4472							} else if (typeof obj[field] === 'number') {
4473								if (obj[field] === search) {
4474									return true;
4475								}
4476							} else if (typeof obj[field] === 'object') {
4477								if (nestedSearch(obj[field])) {
4478									return true;
4479								}
4480							}
4481						}
4482						return false;
4483					};
4484
4485					var search = $(Plugin.getOption('search.input')).val();
4486					if (typeof search !== 'undefined' && search !== '') {
4487						search = search.toLowerCase();
4488						datatable.dataSet = $.grep(datatable.dataSet, nestedSearch);
4489						// remove generalSearch as we don't need this for next columns filter
4490						delete params.query[Plugin.getGeneralSearchKey()];
4491					}
4492
4493					// remove empty element from array
4494					$.each(params.query, function(k, v) {
4495						if (v === '') {
4496							delete params.query[k];
4497						}
4498					});
4499
4500					// filter array by query
4501					datatable.dataSet = Plugin.filterArray(datatable.dataSet, params.query);
4502
4503					// reset array index
4504					datatable.dataSet = datatable.dataSet.filter(function() {
4505						return true;
4506					});
4507				}
4508
4509				return datatable.dataSet;
4510			},
4511
4512			/**
4513			 * Utility helper to filter array by object pair of {key:value}
4514			 * @param list
4515			 * @param args
4516			 * @param operator
4517			 * @returns {*}
4518			 */
4519			filterArray: function(list, args, operator) {
4520				if (typeof list !== 'object') {
4521					return [];
4522				}
4523
4524				if (typeof operator === 'undefined') operator = 'AND';
4525
4526				if (typeof args !== 'object') {
4527					return list;
4528				}
4529
4530				operator = operator.toUpperCase();
4531
4532				if ($.inArray(operator, ['AND', 'OR', 'NOT']) === -1) {
4533					return [];
4534				}
4535
4536				var count = Object.keys(args).length;
4537				var filtered = [];
4538
4539				$.each(list, function(key, obj) {
4540					var to_match = obj;
4541
4542					var matched = 0;
4543					$.each(args, function(m_key, m_value) {
4544						m_value = m_value instanceof Array ? m_value : [m_value];
4545						var match_property = Plugin.getObject(m_key, to_match);
4546						if (typeof match_property !== 'undefined' && match_property) {
4547							var lhs = match_property.toString().toLowerCase();
4548							m_value.forEach(function(item, index) {
4549								if (item.toString().toLowerCase() == lhs || lhs.indexOf(item.toString().toLowerCase()) !== -1) {
4550									matched++;
4551								}
4552							});
4553						}
4554					});
4555
4556					if (('AND' == operator && matched == count) ||
4557						('OR' == operator && matched > 0) ||
4558						('NOT' == operator && 0 == matched)) {
4559						filtered[key] = obj;
4560					}
4561				});
4562
4563				list = filtered;
4564
4565				return list;
4566			},
4567
4568			/**
4569			 * Reset lock column scroll to 0 when resize
4570			 */
4571			resetScroll: function() {
4572				if (typeof options.detail === 'undefined' && Plugin.getDepth() === 1) {
4573					$(datatable.table).find('.' + pfx + 'datatable__row').css('left', 0);
4574					$(datatable.table).find('.' + pfx + 'datatable__lock').css('top', 0);
4575					$(datatable.tableBody).scrollTop(0);
4576				}
4577			},
4578
4579			/**
4580			 * Get column options by field
4581			 * @param field
4582			 * @returns {boolean}
4583			 */
4584			getColumnByField: function(field) {
4585				if (typeof field === 'undefined') return;
4586				var result;
4587				$.each(options.columns, function(i, column) {
4588					if (field === column.field) {
4589						result = column;
4590						return false;
4591					}
4592				});
4593				return result;
4594			},
4595
4596			/**
4597			 * Get default sort column
4598			 */
4599			getDefaultSortColumn: function() {
4600				var result;
4601				$.each(options.columns, function(i, column) {
4602					if (typeof column.sortable !== 'undefined'
4603						&& $.inArray(column.sortable, ['asc', 'desc']) !== -1) {
4604						result = {sort: column.sortable, field: column.field};
4605						return false;
4606					}
4607				});
4608				return result;
4609			},
4610
4611			/**
4612			 * Helper to get element dimensions, when the element is hidden
4613			 * @param element
4614			 * @param includeMargin
4615			 * @returns {{width: number, height: number, innerWidth: number,
4616			 *     innerHeight: number, outerWidth: number, outerHeight:
4617			 *     number}}
4618			 */
4619			getHiddenDimensions: function(element, includeMargin) {
4620				var props = {
4621						position: 'absolute',
4622						visibility: 'hidden',
4623						display: 'block',
4624					},
4625					dim = {
4626						width: 0,
4627						height: 0,
4628						innerWidth: 0,
4629						innerHeight: 0,
4630						outerWidth: 0,
4631						outerHeight: 0,
4632					},
4633					hiddenParents = $(element).parents().addBack().not(':visible');
4634				includeMargin = (typeof includeMargin === 'boolean')
4635					? includeMargin
4636					: false;
4637
4638				var oldProps = [];
4639				hiddenParents.each(function() {
4640					var old = {};
4641
4642					for (var name in props) {
4643						old[name] = this.style[name];
4644						this.style[name] = props[name];
4645					}
4646
4647					oldProps.push(old);
4648				});
4649
4650				dim.width = $(element).width();
4651				dim.outerWidth = $(element).outerWidth(includeMargin);
4652				dim.innerWidth = $(element).innerWidth();
4653				dim.height = $(element).height();
4654				dim.innerHeight = $(element).innerHeight();
4655				dim.outerHeight = $(element).outerHeight(includeMargin);
4656
4657				hiddenParents.each(function(i) {
4658					var old = oldProps[i];
4659					for (var name in props) {
4660						this.style[name] = old[name];
4661					}
4662				});
4663
4664				return dim;
4665			},
4666
4667			getGeneralSearchKey: function() {
4668				var searchInput = $(Plugin.getOption('search.input'));
4669				return $(searchInput).prop('name') || $(searchInput).prop('id');
4670			},
4671
4672			/**
4673			 * Get value by dot notation path string and to prevent undefined
4674			 * errors
4675			 * @param path String Dot notation path in string
4676			 * @param object Object to iterate
4677			 * @returns {*}
4678			 */
4679			getObject: function(path, object) {
4680				return path.split('.').reduce(function(obj, i) {
4681					return obj !== null && typeof obj[i] !== 'undefined' ? obj[i] : null;
4682				}, object);
4683			},
4684
4685			/**
4686			 * Extend object
4687			 * @param obj
4688			 * @param path
4689			 * @param value
4690			 * @returns {*}
4691			 */
4692			extendObj: function(obj, path, value) {
4693				var levels = path.split('.'),
4694					i = 0;
4695
4696				function createLevel(child) {
4697					var name = levels[i++];
4698					if (typeof child[name] !== 'undefined' && child[name] !== null) {
4699						if (typeof child[name] !== 'object' &&
4700							typeof child[name] !== 'function') {
4701							child[name] = {};
4702						}
4703					} else {
4704						child[name] = {};
4705					}
4706					if (i === levels.length) {
4707						child[name] = value;
4708					} else {
4709						createLevel(child[name]);
4710					}
4711				}
4712
4713				createLevel(obj);
4714				return obj;
4715			},
4716
4717			rowEvenOdd: function() {
4718				// row even class
4719				$(datatable.tableBody).find('.' + pfx + 'datatable__row').removeClass(pfx + 'datatable__row--even');
4720				if ($(datatable.wrap).hasClass(pfx + 'datatable--subtable')) {
4721					$(datatable.tableBody).find('.' + pfx + 'datatable__row:not(.' + pfx + 'datatable__row-detail):even').addClass(pfx + 'datatable__row--even');
4722				} else {
4723					$(datatable.tableBody).find('.' + pfx + 'datatable__row:nth-child(even)').addClass(pfx + 'datatable__row--even');
4724				}
4725			},
4726
4727			/********************
4728			 ** PUBLIC API METHODS
4729			 ********************/
4730
4731			// delay timer
4732			timer: 0,
4733
4734			/**
4735			 * Redraw datatable by recalculating its DOM elements, etc.
4736			 * @returns {jQuery}
4737			 */
4738			redraw: function() {
4739				Plugin.adjustCellsWidth.call();
4740				if (Plugin.isLocked()) {
4741					// fix hiding cell width issue
4742					Plugin.scrollbar();
4743					Plugin.resetScroll();
4744					Plugin.adjustCellsHeight.call();
4745				}
4746				Plugin.adjustLockContainer.call();
4747				Plugin.initHeight.call();
4748				return datatable;
4749			},
4750
4751			/**
4752			 * Shortcode to reload
4753			 * @returns {jQuery}
4754			 */
4755			load: function() {
4756				Plugin.reload();
4757				return datatable;
4758			},
4759
4760			/**
4761			 * Datasource reload
4762			 * @returns {jQuery}
4763			 */
4764			reload: function() {
4765				var delay = (function() {
4766					return function(callback, ms) {
4767						clearTimeout(Plugin.timer);
4768						Plugin.timer = setTimeout(callback, ms);
4769					};
4770				})();
4771				delay(function() {
4772					// local only. remote pagination will skip this block
4773					if (!options.data.serverFiltering) {
4774						Plugin.localDataUpdate();
4775					}
4776					Plugin.dataRender();
4777					$(datatable).trigger(pfx + 'datatable--on-reloaded');
4778				}, Plugin.getOption('search.delay'));
4779				return datatable;
4780			},
4781
4782			/**
4783			 * Get record by record ID
4784			 * @param id
4785			 * @returns {jQuery}
4786			 */
4787			getRecord: function(id) {
4788				if (typeof datatable.tableBody === 'undefined') datatable.tableBody = $(datatable.table).children('tbody');
4789				$(datatable.tableBody).find('.' + pfx + 'datatable__cell:first-child').each(function(i, cell) {
4790					if (id == $(cell).text()) {
4791						var rowNumber = $(cell).closest('.' + pfx + 'datatable__row').index() + 1;
4792						datatable.API.record = datatable.API.value = Plugin.getOneRow(datatable.tableBody, rowNumber);
4793						return datatable;
4794					}
4795				});
4796				return datatable;
4797			},
4798
4799			/**
4800			 * @deprecated in v5.0.6
4801			 * Get column of current record ID
4802			 * @param columnName
4803			 * @returns {jQuery}
4804			 */
4805			getColumn: function(columnName) {
4806				Plugin.setSelectedRecords();
4807				datatable.API.value = $(datatable.API.record).find('[data-field="' + columnName + '"]');
4808				return datatable;
4809			},
4810
4811			/**
4812			 * Destroy datatable to original DOM state before datatable was
4813			 * initialized
4814			 * @returns {jQuery}
4815			 */
4816			destroy: function() {
4817				$(datatable).parent().find('.' + pfx + 'datatable__pager').remove();
4818				var initialDatatable = $(datatable.initialDatatable).addClass(pfx + 'datatable--destroyed').show();
4819				$(datatable).replaceWith(initialDatatable);
4820				datatable = initialDatatable;
4821				$(datatable).trigger(pfx + 'datatable--on-destroy');
4822				Plugin.isInit = false;
4823				initialDatatable = null;
4824				return initialDatatable;
4825			},
4826
4827			/**
4828			 * Sort by column field
4829			 * @param field
4830			 * @param sort
4831			 */
4832			sort: function(field, sort) {
4833				// toggle sort
4834				sort = typeof sort === 'undefined' ? 'asc' : sort;
4835
4836				Plugin.spinnerCallback(true);
4837
4838				// update field and sort params
4839				var meta = {field: field, sort: sort};
4840				Plugin.setDataSourceParam('sort', meta);
4841
4842				setTimeout(function() {
4843					Plugin.dataRender('sort');
4844					$(datatable).trigger(pfx + 'datatable--on-sort', meta);
4845					$(datatable.tableHead).find('.' + pfx + 'datatable__cell > span > i').remove();
4846				}, 300);
4847
4848				return datatable;
4849			},
4850
4851			/**
4852			 * @deprecated in v5.0.6
4853			 * Get current selected column value
4854			 * @returns {jQuery}
4855			 */
4856			getValue: function() {
4857				return $(datatable.API.value).text();
4858			},
4859
4860			/**
4861			 * Set checkbox active
4862			 * @param cell JQuery selector or checkbox ID
4863			 */
4864			setActive: function(cell) {
4865				if (typeof cell === 'string') {
4866					// set by checkbox id
4867					cell = $(datatable.tableBody).find('.' + pfx + 'checkbox--single > [type="checkbox"][value="' + cell + '"]');
4868				}
4869
4870				$(cell).prop('checked', true);
4871
4872				var ids = [];
4873				$(cell).each(function(i, td) {
4874					// normal table
4875					var row = $(td).closest('tr').addClass(pfx + 'datatable__row--active');
4876					var colIndex = $(row).index() + 1;
4877
4878					// lock table
4879					$(row).closest('tbody').find('tr:nth-child(' + colIndex + ')').not('.' + pfx + 'datatable__row-subtable').addClass(pfx + 'datatable__row--active');
4880
4881					var id = $(td).attr('value');
4882					if (typeof id !== 'undefined') {
4883						ids.push(id);
4884					}
4885				});
4886
4887				$(datatable).trigger(pfx + 'datatable--on-check', [ids]);
4888			},
4889
4890			/**
4891			 * Set checkbox inactive
4892			 * @param cell JQuery selector or checkbox ID
4893			 */
4894			setInactive: function(cell) {
4895				if (typeof cell === 'string') {
4896					// set by checkbox id
4897					cell = $(datatable.tableBody).find('.' + pfx + 'checkbox--single > [type="checkbox"][value="' + cell + '"]');
4898				}
4899
4900				$(cell).prop('checked', false);
4901
4902				var ids = [];
4903				$(cell).each(function(i, td) {
4904					// normal table
4905					var row = $(td).closest('tr').removeClass(pfx + 'datatable__row--active');
4906					var colIndex = $(row).index() + 1;
4907
4908					// lock table
4909					$(row).closest('tbody').find('tr:nth-child(' + colIndex + ')').not('.' + pfx + 'datatable__row-subtable').removeClass(pfx + 'datatable__row--active');
4910
4911					var id = $(td).attr('value');
4912					if (typeof id !== 'undefined') {
4913						ids.push(id);
4914					}
4915				});
4916
4917				$(datatable).trigger(pfx + 'datatable--on-uncheck', [ids]);
4918			},
4919
4920			/**
4921			 * Set all checkboxes active or inactive
4922			 * @param active
4923			 */
4924			setActiveAll: function(active) {
4925				var checkboxes = $(datatable.table).
4926					find('> tbody, > thead').
4927					find('tr').not('.' + pfx + 'datatable__row-subtable').
4928					find('.' + pfx + 'datatable__cell--check [type="checkbox"]');
4929				if (active) {
4930					Plugin.setActive(checkboxes);
4931				} else {
4932					Plugin.setInactive(checkboxes);
4933				}
4934			},
4935
4936			/**
4937			 * @deprecated in v5.0.6
4938			 * Get selected rows which are active
4939			 * @returns {jQuery}
4940			 */
4941			setSelectedRecords: function() {
4942				datatable.API.record = $(datatable.tableBody).find('.' + pfx + 'datatable__row--active');
4943				return datatable;
4944			},
4945
4946			/**
4947			 * Get selected records
4948			 * @returns {null}
4949			 */
4950			getSelectedRecords: function() {
4951				// support old method
4952				Plugin.setSelectedRecords();
4953				datatable.API.record = datatable.rows('.' + pfx + 'datatable__row--active').nodes();
4954				return datatable.API.record;
4955			},
4956
4957			/**
4958			 * Get options by dots notation path
4959			 * @param path String Dot notation path in string
4960			 * @returns {*}
4961			 */
4962			getOption: function(path) {
4963				return Plugin.getObject(path, options);
4964			},
4965
4966			/**
4967			 * Set global options nodes by dots notation path
4968			 * @param path
4969			 * @param object
4970			 */
4971			setOption: function(path, object) {
4972				options = Plugin.extendObj(options, path, object);
4973			},
4974
4975			/**
4976			 * Search filter for local & remote
4977			 * @param value
4978			 * @param columns. Optional list of columns to be filtered.
4979			 */
4980			search: function(value, columns) {
4981				if (typeof columns !== 'undefined') columns = $.makeArray(columns);
4982				var delay = (function() {
4983					return function(callback, ms) {
4984						clearTimeout(Plugin.timer);
4985						Plugin.timer = setTimeout(callback, ms);
4986					};
4987				})();
4988
4989				delay(function() {
4990					// get query parameters
4991					var query = Plugin.getDataSourceQuery();
4992
4993					// search not by columns
4994					if (typeof columns === 'undefined' && typeof value !== 'undefined') {
4995						var key = Plugin.getGeneralSearchKey();
4996						query[key] = value;
4997					}
4998
4999					// search by columns, support multiple columns
5000					if (typeof columns === 'object') {
5001						$.each(columns, function(k, column) {
5002							query[column] = value;
5003						});
5004						// remove empty element from arrays
5005						$.each(query, function(k, v) {
5006							if (v === '' || $.isEmptyObject(v)) {
5007								delete query[k];
5008							}
5009						});
5010					}
5011
5012					Plugin.setDataSourceQuery(query);
5013
5014					// local filter only. remote pagination will skip this block
5015					if (!options.data.serverFiltering) {
5016						Plugin.localDataUpdate();
5017					}
5018					Plugin.dataRender('search');
5019				}, Plugin.getOption('search.delay'));
5020			},
5021
5022			/**
5023			 * Set datasource params extract
5024			 * @param param
5025			 * @param value
5026			 */
5027			setDataSourceParam: function(param, value) {
5028				datatable.API.params = $.extend({}, {
5029					pagination: {page: 1, perpage: Plugin.getOption('data.pageSize')},
5030					sort: Plugin.getDefaultSortColumn(),
5031					query: {},
5032				}, datatable.API.params, Plugin.stateGet(Plugin.stateId));
5033
5034				datatable.API.params = Plugin.extendObj(datatable.API.params, param, value);
5035
5036				Plugin.stateKeep(Plugin.stateId, datatable.API.params);
5037			},
5038
5039			/**
5040			 * Get datasource params
5041			 * @param param
5042			 */
5043			getDataSourceParam: function(param) {
5044				datatable.API.params = $.extend({}, {
5045					pagination: {page: 1, perpage: Plugin.getOption('data.pageSize')},
5046					sort: Plugin.getDefaultSortColumn(),
5047					query: {},
5048				}, datatable.API.params, Plugin.stateGet(Plugin.stateId));
5049
5050				if (typeof param === 'string') {
5051					return Plugin.getObject(param, datatable.API.params);
5052				}
5053
5054				return datatable.API.params;
5055			},
5056
5057			/**
5058			 * Shortcode to datatable.getDataSourceParam('query');
5059			 * @returns {*}
5060			 */
5061			getDataSourceQuery: function() {
5062				return Plugin.getDataSourceParam('query') || {};
5063			},
5064
5065			/**
5066			 * Shortcode to datatable.setDataSourceParam('query', query);
5067			 * @param query
5068			 */
5069			setDataSourceQuery: function(query) {
5070				Plugin.setDataSourceParam('query', query);
5071			},
5072
5073			/**
5074			 * Get current page number
5075			 * @returns {number}
5076			 */
5077			getCurrentPage: function() {
5078				return $(datatable.table).
5079					siblings('.' + pfx + 'datatable__pager').
5080					last().
5081					find('.' + pfx + 'datatable__pager-nav').
5082					find('.' + pfx + 'datatable__pager-link.' + pfx + 'datatable__pager-link--active').
5083					data('page') || 1;
5084			},
5085
5086			/**
5087			 * Get selected dropdown page size
5088			 * @returns {*|number}
5089			 */
5090			getPageSize: function() {
5091				return $(datatable.table).siblings('.' + pfx + 'datatable__pager').last().find('select.' + pfx + 'datatable__pager-size').val() || 10;
5092			},
5093
5094			/**
5095			 * Get total rows
5096			 */
5097			getTotalRows: function() {
5098				return datatable.API.params.pagination.total;
5099			},
5100
5101			/**
5102			 * Get full dataset in grid
5103			 * @returns {*|null|Array}
5104			 */
5105			getDataSet: function() {
5106				return datatable.originalDataSet;
5107			},
5108
5109			/**
5110			 * @deprecated in v5.0.6
5111			 * Hide column by column's field name
5112			 * @param fieldName
5113			 */
5114			hideColumn: function(fieldName) {
5115				// add hide option for this column
5116				$.map(options.columns, function(column) {
5117					if (fieldName === column.field) {
5118						column.responsive = {hidden: 'xl'};
5119					}
5120					return column;
5121				});
5122				// hide current displayed column
5123				var tds = $.grep($(datatable.table).find('.' + pfx + 'datatable__cell'), function(n, i) {
5124					return fieldName === $(n).data('field');
5125				});
5126				$(tds).hide();
5127			},
5128
5129			/**
5130			 * @deprecated in v5.0.6
5131			 * Show column by column's field name
5132			 * @param fieldName
5133			 */
5134			showColumn: function(fieldName) {
5135				// add hide option for this column
5136				$.map(options.columns, function(column) {
5137					if (fieldName === column.field) {
5138						delete column.responsive;
5139					}
5140					return column;
5141				});
5142				// hide current displayed column
5143				var tds = $.grep($(datatable.table).find('.' + pfx + 'datatable__cell'), function(n, i) {
5144					return fieldName === $(n).data('field');
5145				});
5146				$(tds).show();
5147			},
5148
5149			nodeTr: [],
5150			nodeTd: [],
5151			nodeCols: [],
5152			recentNode: [],
5153
5154			table: function() {
5155				if (typeof datatable.table !== 'undefined') {
5156					return datatable.table;
5157				}
5158			},
5159
5160			/**
5161			 * Select a single row from the table
5162			 * @param selector
5163			 * @returns {jQuery}
5164			 */
5165			row: function(selector) {
5166				Plugin.rows(selector);
5167				Plugin.nodeTr = Plugin.recentNode = $(Plugin.nodeTr).first();
5168				return datatable;
5169			},
5170
5171			/**
5172			 * Select multiple rows from the table
5173			 * @param selector
5174			 * @returns {jQuery}
5175			 */
5176			rows: function(selector) {
5177				if (Plugin.isLocked()) {
5178					Plugin.nodeTr = Plugin.recentNode = $(datatable.tableBody).find(selector).filter('.' + pfx + 'datatable__lock--scroll > .' + pfx + 'datatable__row');
5179				} else {
5180					Plugin.nodeTr = Plugin.recentNode = $(datatable.tableBody).find(selector).filter('.' + pfx + 'datatable__row');
5181				}
5182				return datatable;
5183			},
5184
5185			/**
5186			 * Select a single column from the table
5187			 * @param index zero-based index
5188			 * @returns {jQuery}
5189			 */
5190			column: function(index) {
5191				Plugin.nodeCols = Plugin.recentNode = $(datatable.tableBody).find('.' + pfx + 'datatable__cell:nth-child(' + (index + 1) + ')');
5192				return datatable;
5193			},
5194
5195			/**
5196			 * Select multiple columns from the table
5197			 * @param selector
5198			 * @returns {jQuery}
5199			 */
5200			columns: function(selector) {
5201				var context = datatable.table;
5202				if (Plugin.nodeTr === Plugin.recentNode) {
5203					context = Plugin.nodeTr;
5204				}
5205				var columns = $(context).find('.' + pfx + 'datatable__cell[data-field="' + selector + '"]');
5206				if (columns.length > 0) {
5207					Plugin.nodeCols = Plugin.recentNode = columns;
5208				} else {
5209					Plugin.nodeCols = Plugin.recentNode = $(context).find(selector).filter('.' + pfx + 'datatable__cell');
5210				}
5211				return datatable;
5212			},
5213
5214			cell: function(selector) {
5215				Plugin.cells(selector);
5216				Plugin.nodeTd = Plugin.recentNode = $(Plugin.nodeTd).first();
5217				return datatable;
5218			},
5219
5220			cells: function(selector) {
5221				var cells = $(datatable.tableBody).find('.' + pfx + 'datatable__cell');
5222				if (typeof selector !== 'undefined') {
5223					cells = $(cells).filter(selector);
5224				}
5225				Plugin.nodeTd = Plugin.recentNode = cells;
5226				return datatable;
5227			},
5228
5229			/**
5230			 * Delete the selected row from the table
5231			 * @returns {jQuery}
5232			 */
5233			remove: function() {
5234				if ($(Plugin.nodeTr.length) && Plugin.nodeTr === Plugin.recentNode) {
5235					$(Plugin.nodeTr).remove();
5236				}
5237				Plugin.layoutUpdate();
5238				return datatable;
5239			},
5240
5241			/**
5242			 * Show or hide the columns or rows
5243			 */
5244			visible: function(bool) {
5245				if ($(Plugin.recentNode.length)) {
5246					var locked = Plugin.lockEnabledColumns();
5247					if (Plugin.recentNode === Plugin.nodeCols) {
5248						var index = Plugin.recentNode.index();
5249
5250						if (Plugin.isLocked()) {
5251							var scrollColumns = $(Plugin.recentNode).closest('.' + pfx + 'datatable__lock--scroll').length;
5252							if (scrollColumns) {
5253								// is at center of scrollable area
5254								index += locked.left.length + 1;
5255							} else if ($(Plugin.recentNode).closest('.' + pfx + 'datatable__lock--right').length) {
5256								// is at the right locked table
5257								index += locked.left.length + scrollColumns + 1;
5258							}
5259						}
5260					}
5261
5262					if (bool) {
5263						if (Plugin.recentNode === Plugin.nodeCols) {
5264							delete options.columns[index].responsive;
5265						}
5266						$(Plugin.recentNode).show();
5267					} else {
5268						if (Plugin.recentNode === Plugin.nodeCols) {
5269							Plugin.setOption('columns.' + index + '.responsive', {hidden: 'xl'});
5270						}
5271						$(Plugin.recentNode).hide();
5272					}
5273					Plugin.redraw();
5274				}
5275			},
5276
5277			/**
5278			 * Get the the DOM element for the selected rows or columns
5279			 * @returns {Array}
5280			 */
5281			nodes: function() {
5282				return Plugin.recentNode;
5283			},
5284
5285			/**
5286			 * will be implemented soon
5287			 * @returns {jQuery}
5288			 */
5289			dataset: function() {
5290				return datatable;
5291			},
5292
5293			/**
5294			 * Open page by number
5295			 * @param page number
5296			 */
5297			gotoPage: function (page) {
5298				Plugin.pagingObject.openPage(page);
5299			},
5300
5301		};
5302
5303		/**
5304		 * Public API methods can be used directly by datatable
5305		 */
5306		$.each(Plugin, function(funcName, func) {
5307			datatable[funcName] = func;
5308		});
5309
5310		// initialize main datatable plugin
5311		if (typeof options !== 'undefined') {
5312			if (typeof options === 'string') {
5313				var method = options;
5314				datatable = $(this).data(pluginName);
5315				if (typeof datatable !== 'undefined') {
5316					options = datatable.options;
5317					Plugin[method].apply(this, Array.prototype.slice.call(arguments, 1));
5318				}
5319			} else {
5320				if (!datatable.data(pluginName) && !$(this).hasClass(pfx + 'datatable--loaded')) {
5321					datatable.dataSet = null;
5322					datatable.textAlign = {
5323						left: pfx + 'datatable__cell--left',
5324						center: pfx + 'datatable__cell--center',
5325						right: pfx + 'datatable__cell--right',
5326					};
5327
5328					// merge default and user defined options
5329					options = $.extend(true, {}, $.fn[pluginName].defaults, options);
5330
5331					datatable.options = options;
5332
5333					// init plugin process
5334					Plugin.init.apply(this, [options]);
5335
5336					$(datatable.wrap).data(pluginName, datatable);
5337				}
5338			}
5339		} else {
5340			// get existing instance datatable
5341			datatable = $(this).data(pluginName);
5342			if (typeof datatable === 'undefined') {
5343				$.error(pluginName + ' not initialized');
5344			}
5345			options = datatable.options;
5346		}
5347
5348		return datatable;
5349	};
5350
5351	// default options
5352	$.fn[pluginName].defaults = {
5353		// datasource definition
5354		data: {
5355			type: 'local',
5356			source: null,
5357			pageSize: 10, // display records per page
5358			saveState: {
5359				// save datatable state(pagination, filtering, sorting, etc) in cookie or browser webstorage
5360				cookie: false,
5361				webstorage: true,
5362			},
5363
5364			serverPaging: false,
5365			serverFiltering: false,
5366			serverSorting: false,
5367
5368			autoColumns: false,
5369			attr: {
5370				rowProps: [],
5371			},
5372		},
5373
5374		// layout definition
5375		layout: {
5376			theme: 'default', // datatable will support multiple themes and designs
5377			class: pfx + 'datatable--brand', // custom wrapper class
5378			scroll: false, // enable/disable datatable scroll both horizontal and vertical when needed.
5379			height: null, // datatable's body's fixed height
5380			minHeight: 300,
5381			footer: false, // display/hide footer
5382			header: true, // display/hide header
5383			customScrollbar: true, // set false to disable custom scrollbar
5384
5385			// datatable spinner
5386			spinner: {
5387				overlayColor: '#000000',
5388				opacity: 0,
5389				type: 'loader',
5390				state: 'brand',
5391				message: true,
5392			},
5393
5394			// datatable UI icons
5395			icons: {
5396				sort: {asc: 'flaticon2-arrow-up', desc: 'flaticon2-arrow-down'},
5397				pagination: {
5398					next: 'flaticon2-next',
5399					prev: 'flaticon2-back',
5400					first: 'flaticon2-fast-back',
5401					last: 'flaticon2-fast-next',
5402					more: 'flaticon-more-1',
5403				},
5404				rowDetail: {expand: 'fa fa-caret-down', collapse: 'fa fa-caret-right'},
5405			},
5406		},
5407
5408		// column sorting
5409		sortable: true,
5410
5411		// resize column size with mouse drag coming soon)
5412		resizable: false,
5413
5414		// column based filtering (coming soon)
5415		filterable: false,
5416
5417		pagination: true,
5418
5419		// inline and bactch editing (cooming soon)
5420		editable: false,
5421
5422		// columns definition
5423		columns: [],
5424
5425		search: {
5426			// enable trigger search by keyup enter
5427			onEnter: false,
5428			// input text for search
5429			input: null,
5430			// search delay in milliseconds
5431			delay: 400,
5432		},
5433
5434		rows: {
5435			// deprecated
5436			callback: function() {
5437			},
5438			// call before row template
5439			beforeTemplate: function() {
5440			},
5441			// call after row template
5442			afterTemplate: function() {
5443			},
5444			autoHide: true,
5445		},
5446
5447		// toolbar
5448		toolbar: {
5449			// place pagination and displayInfo blocks according to the array order
5450			layout: ['pagination', 'info'],
5451
5452			// toolbar placement can be at top or bottom or both top and bottom repeated
5453			placement: ['bottom'],  //'top', 'bottom'
5454
5455			// toolbar items
5456			items: {
5457				// pagination
5458				pagination: {
5459					// pagination type(default or scroll)
5460					type: 'default',
5461
5462					// number of pages to display by breakpoints
5463					pages: {
5464						desktop: {
5465							layout: 'default',
5466							pagesNumber: 5,
5467						},
5468						tablet: {
5469							layout: 'default',
5470							pagesNumber: 3,
5471						},
5472						mobile: {
5473							layout: 'compact',
5474						},
5475					},
5476
5477					// navigation buttons
5478					navigation: {
5479						prev: true, // display prev button
5480						next: true, // display next button
5481						first: true, // display first button
5482						last: true, // display last button
5483						more: false // display more button
5484					},
5485
5486					// page size select
5487					pageSizeSelect: [], // display dropdown to select pagination size. -1 is used for "ALl" option
5488				},
5489
5490				// records info
5491				info: true,
5492			},
5493		},
5494
5495		// here we will keep all strings and message used by datatable UI so developer can easiliy translate to any language.
5496		// By default the stirngs will be in the plugin source and here can override it
5497		translate: {
5498			records: {
5499				processing: 'Please wait...',
5500				noRecords: 'No records found',
5501			},
5502			toolbar: {
5503				pagination: {
5504					items: {
5505						default: {
5506							first: 'First',
5507							prev: 'Previous',
5508							next: 'Next',
5509							last: 'Last',
5510							more: 'More pages',
5511							input: 'Page number',
5512							select: 'Select page size',
5513							all: 'all',
5514						},
5515						info: 'Showing {{start}} - {{end}} of {{total}}',
5516					},
5517				},
5518			},
5519		},
5520
5521		extensions: {},
5522	};
5523
5524}(jQuery));
5525"use strict";
5526(function($) {
5527
5528	var pluginName = 'KTDatatable';
5529	var pfx = 'kt-';
5530
5531	$.fn[pluginName] = $.fn[pluginName] || {};
5532
5533	/**
5534	 * @param datatable Main datatable plugin instance
5535	 * @param options Extension options
5536	 * @returns {*}
5537	 */
5538	$.fn[pluginName].checkbox = function(datatable, options) {
5539		var Extension = {
5540			selectedAllRows: false,
5541			selectedRows: [],
5542			unselectedRows: [],
5543
5544			init: function() {
5545				if (Extension.selectorEnabled()) {
5546					// reset
5547					datatable.setDataSourceParam(options.vars.selectedAllRows, false);
5548					datatable.stateRemove('checkbox');
5549
5550					// requestIds is not null
5551					if (options.vars.requestIds) {
5552						// request ids in response
5553						datatable.setDataSourceParam(options.vars.requestIds, true);
5554					}
5555
5556					// remove selected checkbox on datatable reload
5557					$(datatable).on(pfx + 'datatable--on-reloaded', function() {
5558						datatable.stateRemove('checkbox');
5559						datatable.setDataSourceParam(options.vars.selectedAllRows, false);
5560						Extension.selectedAllRows = false;
5561						Extension.selectedRows = [];
5562						Extension.unselectedRows = [];
5563					});
5564
5565					// select all on extension init
5566					Extension.selectedAllRows = datatable.getDataSourceParam(options.vars.selectedAllRows);
5567
5568					$(datatable).on(pfx + 'datatable--on-layout-updated', function(e, args) {
5569						if (args.table != $(datatable.wrap).attr('id')) {
5570							return;
5571						}
5572						datatable.ready(function() {
5573							Extension.initVars();
5574							Extension.initEvent();
5575							Extension.initSelect();
5576						});
5577					});
5578
5579					$(datatable).on(pfx + 'datatable--on-check', function(e, ids) {
5580						ids.forEach(function(id) {
5581							Extension.selectedRows.push(id);
5582							// // remove from unselected rows
5583							Extension.unselectedRows = Extension.remove(Extension.unselectedRows, id);
5584						});
5585						var storage = {};
5586						storage['selectedRows'] = $.unique(Extension.selectedRows);
5587						storage['unselectedRows'] = $.unique(Extension.unselectedRows);
5588						datatable.stateKeep('checkbox', storage);
5589					});
5590					$(datatable).on(pfx + 'datatable--on-uncheck', function(e, ids) {
5591						ids.forEach(function(id) {
5592							Extension.unselectedRows.push(id);
5593							// // remove from selected rows
5594							Extension.selectedRows = Extension.remove(Extension.selectedRows, id);
5595						});
5596						var storage = {};
5597						storage['selectedRows'] = $.unique(Extension.selectedRows);
5598						storage['unselectedRows'] = $.unique(Extension.unselectedRows);
5599						datatable.stateKeep('checkbox', storage);
5600					});
5601				}
5602			},
5603
5604			/**
5605			 * Init checkbox clicks event
5606			 */
5607			initEvent: function() {
5608				// select all checkbox click
5609				$(datatable.tableHead).find('.' + pfx + 'checkbox--all > [type="checkbox"]').click(function(e) {
5610					// clear selected and unselected rows
5611					Extension.selectedRows = Extension.unselectedRows = [];
5612					datatable.stateRemove('checkbox');
5613
5614					// select all rows
5615					if ($(this).is(':checked')) {
5616						Extension.selectedAllRows = true;
5617					}
5618					else {
5619						Extension.selectedAllRows = false;
5620					}
5621
5622					// local select all current page rows
5623					if (!options.vars.requestIds) {
5624						if ($(this).is(':checked')) {
5625							Extension.selectedRows = $.makeArray($(datatable.tableBody).find('.' + pfx + 'checkbox--single > [type="checkbox"]').map(function(i, chk) {
5626								return $(chk).val();
5627							}));
5628						}
5629						var storage = {};
5630						storage['selectedRows'] = $.unique(Extension.selectedRows);
5631						datatable.stateKeep('checkbox', storage);
5632					}
5633
5634					// keep selectedAllRows in datasource params
5635					datatable.setDataSourceParam(options.vars.selectedAllRows, Extension.selectedAllRows);
5636
5637					$(datatable).trigger(pfx + 'datatable--on-click-checkbox', [$(this)]);
5638				});
5639
5640				// single row checkbox click
5641				$(datatable.tableBody).find('.' + pfx + 'checkbox--single > [type="checkbox"]').click(function(e) {
5642					var id = $(this).val();
5643					if ($(this).is(':checked')) {
5644						Extension.selectedRows.push(id);
5645						// remove from unselected rows
5646						Extension.unselectedRows = Extension.remove(Extension.unselectedRows, id);
5647					}
5648					else {
5649						Extension.unselectedRows.push(id);
5650						// remove from selected rows
5651						Extension.selectedRows = Extension.remove(Extension.selectedRows, id);
5652					}
5653
5654					// local checkbox header check
5655					if (!options.vars.requestIds && Extension.selectedRows.length < 1) {
5656						// remove select all checkbox, if there is no checked checkbox left
5657						$(datatable.tableHead).find('.' + pfx + 'checkbox--all > [type="checkbox"]').prop('checked', false);
5658					}
5659
5660					var storage = {};
5661					storage['selectedRows'] = $.unique(Extension.selectedRows);
5662					storage['unselectedRows'] = $.unique(Extension.unselectedRows);
5663					datatable.stateKeep('checkbox', storage);
5664
5665					$(datatable).trigger(pfx + 'datatable--on-click-checkbox', [$(this)]);
5666				});
5667			},
5668
5669			initSelect: function() {
5670				// selected all rows from server
5671				if (Extension.selectedAllRows && options.vars.requestIds) {
5672					if (!datatable.hasClass(pfx + 'datatable--error')) {
5673						// set header select all checkbox checked
5674						$(datatable.tableHead).find('.' + pfx + 'checkbox--all > [type="checkbox"]').prop('checked', true);
5675					}
5676
5677					// set all checkbox in table body
5678					datatable.setActiveAll(true);
5679
5680					// remove unselected rows
5681					Extension.unselectedRows.forEach(function(id) {
5682						datatable.setInactive(id);
5683					});
5684
5685				}
5686				else {
5687					// single check for server and local
5688					Extension.selectedRows.forEach(function(id) {
5689						datatable.setActive(id);
5690					});
5691
5692					// local checkbox; check if all checkboxes of currect page are checked
5693					if (!datatable.hasClass(pfx + 'datatable--error') && $(datatable.tableBody).find('.' + pfx + 'checkbox--single > [type="checkbox"]').not(':checked').length < 1) {
5694						// set header select all checkbox checked
5695						$(datatable.tableHead).find('.' + pfx + 'checkbox--all > [type="checkbox"]').prop('checked', true);
5696					}
5697				}
5698			},
5699
5700			/**
5701			 * Check if selector is enabled from options
5702			 */
5703			selectorEnabled: function() {
5704				return $.grep(datatable.options.columns, function(n, i) {
5705					return n.selector || false;
5706				})[0];
5707			},
5708
5709			initVars: function() {
5710				// get single select/unselect from localstorage
5711				var storage = datatable.stateGet('checkbox');
5712				if (typeof storage !== 'undefined') {
5713					Extension.selectedRows = storage['selectedRows'] || [];
5714					Extension.unselectedRows = storage['unselectedRows'] || [];
5715				}
5716			},
5717
5718			getSelectedId: function(path) {
5719				Extension.initVars();
5720
5721				// server selected all rows
5722				if (Extension.selectedAllRows && options.vars.requestIds) {
5723					if (typeof path === 'undefined') {
5724						path = options.vars.rowIds;
5725					}
5726
5727					// if selected all rows, return id from response meta
5728					var selectedAllRows = datatable.getObject(path, datatable.lastResponse) || [];
5729
5730					if (selectedAllRows.length > 0) {
5731						// remove single unselected rows from selectedAllRows ids from server response emta
5732						Extension.unselectedRows.forEach(function(id) {
5733							selectedAllRows = Extension.remove(selectedAllRows, parseInt(id));
5734						});
5735					}
5736					return selectedAllRows;
5737				}
5738
5739				// else return single checked selected rows
5740				return Extension.selectedRows;
5741			},
5742
5743			remove: function(array, element) {
5744				return array.filter(function(e) {
5745					return e !== element;
5746				});
5747			},
5748		};
5749
5750		// make the extension accessible from datatable init
5751		datatable.checkbox = function() {
5752			return Extension;
5753		};
5754
5755		if (typeof options === 'object') {
5756			options = $.extend(true, {}, $.fn[pluginName].checkbox.default, options);
5757			Extension.init.apply(this, [options]);
5758		}
5759
5760		return datatable;
5761	};
5762
5763	$.fn[pluginName].checkbox.default = {
5764		vars: {
5765			// select all rows flag to be sent to the server
5766			selectedAllRows: 'selectedAllRows',
5767			// request id parameter's name
5768			requestIds: 'requestIds',
5769			// response path to all rows id
5770			rowIds: 'meta.rowIds',
5771		},
5772	};
5773
5774}(jQuery));
5775var defaults = {
5776	layout: {
5777		icons: {
5778			pagination: {
5779				next: 'flaticon2-next',
5780				prev: 'flaticon2-back',
5781				first: 'flaticon2-fast-back',
5782				last: 'flaticon2-fast-next',
5783				more: 'flaticon-more-1',
5784			},
5785			rowDetail: {expand: 'fa fa-caret-down', collapse: 'fa fa-caret-right'},
5786		}
5787	}
5788};
5789
5790if (KTUtil.isRTL()) {
5791	defaults = {
5792		layout: {
5793			icons: {
5794				pagination: {
5795					next: 'flaticon2-back',
5796					prev: 'flaticon2-next',
5797					first: 'flaticon2-fast-next',
5798					last: 'flaticon2-fast-back',
5799				},
5800				rowDetail: {collapse: 'fa fa-caret-down', expand: 'fa fa-caret-right'},
5801			}
5802		}
5803	}
5804}
5805
5806$.extend(true, $.fn.KTDatatable.defaults, defaults);
5807"use strict";
5808var KTHeader = function(elementId, options) {
5809    // Main object
5810    var the = this;
5811    var init = false;
5812
5813    // Get element object
5814    var element = KTUtil.get(elementId);
5815    var body = KTUtil.get('body');
5816
5817    if (element === undefined) {
5818        return;
5819    }
5820
5821    // Default options
5822    var defaultOptions = {
5823        classic: false,
5824        offset: {
5825            mobile: 150,
5826            desktop: 200
5827        },
5828        minimize: {
5829            mobile: false,
5830            desktop: false
5831        }
5832    };
5833
5834    ////////////////////////////
5835    // ** Private Methods  ** //
5836    ////////////////////////////
5837
5838    var Plugin = {
5839        /**
5840         * Run plugin
5841         * @returns {KTHeader}
5842         */
5843        construct: function(options) {
5844            if (KTUtil.data(element).has('header')) {
5845                the = KTUtil.data(element).get('header');
5846            } else {
5847                // reset header
5848                Plugin.init(options);
5849
5850                // build header
5851                Plugin.build();
5852
5853                KTUtil.data(element).set('header', the);
5854            }
5855
5856            return the;
5857        },
5858
5859        /**
5860         * Handles subheader click toggle
5861         * @returns {KTHeader}
5862         */
5863        init: function(options) {
5864            the.events = [];
5865
5866            // merge default and user defined options
5867            the.options = KTUtil.deepExtend({}, defaultOptions, options);
5868        },
5869
5870        /**
5871         * Reset header
5872         * @returns {KTHeader}
5873         */
5874        build: function() {
5875            var lastScrollTop = 0;
5876            var eventTriggerState = true;
5877            var viewportHeight = KTUtil.getViewPort().height;
5878
5879            if (the.options.minimize.mobile === false && the.options.minimize.desktop === false) {
5880                return;
5881            }
5882
5883            window.addEventListener('scroll', function() {
5884                var offset = 0, on, off, st;
5885
5886                if (KTUtil.isInResponsiveRange('desktop')) {
5887                    offset = the.options.offset.desktop;
5888                    on = the.options.minimize.desktop.on;
5889                    off = the.options.minimize.desktop.off;
5890                } else if (KTUtil.isInResponsiveRange('tablet-and-mobile')) {
5891                    offset = the.options.offset.mobile;
5892                    on = the.options.minimize.mobile.on;
5893                    off = the.options.minimize.mobile.off;
5894                }
5895
5896                st = window.pageYOffset;
5897
5898                if (
5899                    (KTUtil.isInResponsiveRange('tablet-and-mobile') && the.options.classic && the.options.classic.mobile) ||
5900                    (KTUtil.isInResponsiveRange('desktop') && the.options.classic && the.options.classic.desktop)
5901
5902                ) {
5903                    if (st > offset) { // down scroll mode
5904                        KTUtil.addClass(body, on);
5905                        KTUtil.removeClass(body, off);
5906                        
5907                        if (eventTriggerState) {
5908                            Plugin.eventTrigger('minimizeOn', the);
5909                            eventTriggerState = false;
5910                        }
5911                    } else { // back scroll mode
5912                        KTUtil.addClass(body, off);
5913                        KTUtil.removeClass(body, on);
5914
5915                        if (eventTriggerState == false) {
5916                            Plugin.eventTrigger('minimizeOff', the);
5917                            eventTriggerState = true; 
5918                        }
5919                    }
5920                } else {
5921                    if (st > offset && lastScrollTop < st) { // down scroll mode
5922                        KTUtil.addClass(body, on);
5923                        KTUtil.removeClass(body, off);
5924
5925                        if (eventTriggerState) {
5926                            Plugin.eventTrigger('minimizeOn', the);
5927                            eventTriggerState = false;
5928                        }
5929                    } else { // back scroll mode
5930                        KTUtil.addClass(body, off);
5931                        KTUtil.removeClass(body, on);
5932
5933                        if (eventTriggerState == false) {
5934                            Plugin.eventTrigger('minimizeOff', the);
5935                            eventTriggerState = true;
5936                        }
5937                    }
5938
5939                    lastScrollTop = st;
5940                }
5941            });
5942        },
5943
5944        /**
5945         * Trigger events
5946         */
5947        eventTrigger: function(name, args) {
5948            for (var i = 0; i < the.events.length; i++) {
5949                var event = the.events[i];
5950                if (event.name == name) {
5951                    if (event.one == true) {
5952                        if (event.fired == false) {
5953                            the.events[i].fired = true;
5954                            event.handler.call(this, the, args);
5955                        }
5956                    } else {
5957                        event.handler.call(this, the, args);
5958                    }
5959                }
5960            }
5961        },
5962
5963        addEvent: function(name, handler, one) {
5964            the.events.push({
5965                name: name,
5966                handler: handler,
5967                one: one,
5968                fired: false
5969            });
5970        }
5971    };
5972
5973    //////////////////////////
5974    // ** Public Methods ** //
5975    //////////////////////////
5976
5977    /**
5978     * Set default options 
5979     */
5980
5981    the.setDefaults = function(options) {
5982        defaultOptions = options;
5983    };
5984
5985    /**
5986     * Register event
5987     */
5988    the.on = function(name, handler) {
5989        return Plugin.addEvent(name, handler);
5990    };
5991
5992    ///////////////////////////////
5993    // ** Plugin Construction ** //
5994    ///////////////////////////////
5995
5996    // Run plugin
5997    Plugin.construct.apply(the, [options]);
5998
5999    // Init done
6000    init = true;
6001
6002    // Return plugin instance
6003    return the;
6004};
6005"use strict";
6006var KTMenu = function(elementId, options) {
6007    // Main object
6008    var the = this;
6009    var init = false;
6010
6011    // Get element object
6012    var element = KTUtil.get(elementId);
6013    var body = KTUtil.get('body');  
6014
6015    if (!element) {
6016        return;
6017    }
6018
6019    // Default options
6020    var defaultOptions = {       
6021        // accordion submenu mode
6022        accordion: {
6023            slideSpeed: 200, // accordion toggle slide speed in milliseconds
6024            autoScroll: false, // enable auto scrolling(focus) to the clicked menu item
6025            autoScrollSpeed: 1200,
6026            expandAll: true // allow having multiple expanded accordions in the menu
6027        },
6028
6029        // dropdown submenu mode
6030        dropdown: {
6031            timeout: 500 // timeout in milliseconds to show and hide the hoverable submenu dropdown
6032        }
6033    };
6034
6035    ////////////////////////////
6036    // ** Private Methods  ** //
6037    ////////////////////////////
6038
6039    var Plugin = {
6040        /**
6041         * Run plugin
6042         * @returns {KTMenu}
6043         */
6044        construct: function(options) {
6045            if (KTUtil.data(element).has('menu')) {
6046                the = KTUtil.data(element).get('menu');
6047            } else {
6048                // reset menu
6049                Plugin.init(options);
6050
6051                // reset menu
6052                Plugin.reset();
6053
6054                // build menu
6055                Plugin.build();
6056
6057                KTUtil.data(element).set('menu', the);
6058            }
6059
6060            return the;
6061        },
6062
6063        /**
6064         * Handles submenu click toggle
6065         * @returns {KTMenu}
6066         */
6067        init: function(options) {
6068            the.events = [];
6069
6070            the.eventHandlers = {};
6071
6072            // merge default and user defined options
6073            the.options = KTUtil.deepExtend({}, defaultOptions, options);
6074
6075            // pause menu
6076            the.pauseDropdownHoverTime = 0;
6077
6078            the.uid = KTUtil.getUniqueID();
6079        },
6080
6081        update: function(options) {
6082            // merge default and user defined options
6083            the.options = KTUtil.deepExtend({}, defaultOptions, options);
6084
6085            // pause menu
6086            the.pauseDropdownHoverTime = 0;
6087
6088             // reset menu
6089            Plugin.reset();
6090
6091            the.eventHandlers = {};
6092
6093            // build menu
6094            Plugin.build();
6095
6096            KTUtil.data(element).set('menu', the);
6097        },
6098
6099        reload: function() {
6100             // reset menu
6101            Plugin.reset();
6102
6103            // build menu
6104            Plugin.build();
6105
6106            // reset submenu props
6107            Plugin.resetSubmenuProps();
6108        },
6109
6110        /**
6111         * Reset menu
6112         * @returns {KTMenu}
6113         */
6114        build: function() {
6115            // General accordion submenu toggle
6116            the.eventHandlers['event_1'] = KTUtil.on( element, '.kt-menu__toggle', 'click', Plugin.handleSubmenuAccordion);
6117
6118            // Dropdown mode(hoverable)
6119            if (Plugin.getSubmenuMode() === 'dropdown' || Plugin.isConditionalSubmenuDropdown()) {
6120                // dropdown submenu - hover toggle
6121                the.eventHandlers['event_2'] = KTUtil.on( element, '[data-ktmenu-submenu-toggle="hover"]', 'mouseover', Plugin.handleSubmenuDrodownHoverEnter);
6122                the.eventHandlers['event_3'] = KTUtil.on( element, '[data-ktmenu-submenu-toggle="hover"]', 'mouseout', Plugin.handleSubmenuDrodownHoverExit);
6123
6124                // dropdown submenu - click toggle
6125                the.eventHandlers['event_4'] = KTUtil.on( element, '[data-ktmenu-submenu-toggle="click"] > .kt-menu__toggle, [data-ktmenu-submenu-toggle="click"] > .kt-menu__link .kt-menu__toggle', 'click', Plugin.handleSubmenuDropdownClick);
6126                the.eventHandlers['event_5'] = KTUtil.on( element, '[data-ktmenu-submenu-toggle="tab"] > .kt-menu__toggle, [data-ktmenu-submenu-toggle="tab"] > .kt-menu__link .kt-menu__toggle', 'click', Plugin.handleSubmenuDropdownTabClick);
6127            }
6128
6129            // General link click
6130            // the.eventHandlers['event_6'] = KTUtil.on(element, '.kt-menu__item:not(.kt-menu__item--submenu) > .kt-menu__link:not(.kt-menu__toggle):not(.kt-menu__link--toggle-skip)', 'click', Plugin.handleLinkClick);
6131
6132            // Init scrollable menu
6133            if (the.options.scroll && the.options.scroll.height) {
6134                Plugin.scrollInit();
6135            }
6136        },
6137
6138        /**
6139         * Reset menu
6140         * @returns {KTMenu}
6141         */
6142        reset: function() { 
6143            KTUtil.off( element, 'click', the.eventHandlers['event_1']);
6144
6145            // dropdown submenu - hover toggle
6146            KTUtil.off( element, 'mouseover', the.eventHandlers['event_2']);
6147            KTUtil.off( element, 'mouseout', the.eventHandlers['event_3']);
6148
6149            // dropdown submenu - click toggle
6150            KTUtil.off( element, 'click', the.eventHandlers['event_4']);
6151            KTUtil.off( element, 'click', the.eventHandlers['event_5']);
6152            
6153            KTUtil.off(element, 'click', the.eventHandlers['event_6']);
6154        },
6155
6156        /**
6157         * Init scroll menu
6158         *
6159        */
6160        scrollInit: function() {
6161            if ( the.options.scroll && the.options.scroll.height ) {
6162                KTUtil.scrollDestroy(element);
6163                KTUtil.scrollInit(element, {disableForMobile: true, resetHeightOnDestroy: true, handleWindowResize: true, height: the.options.scroll.height});
6164            } else {
6165                KTUtil.scrollDestroy(element);
6166            }           
6167        },
6168
6169        /**
6170         * Update scroll menu
6171        */
6172        scrollUpdate: function() {
6173            if ( the.options.scroll && the.options.scroll.height ) {
6174                KTUtil.scrollUpdate(element);
6175            }
6176        },
6177
6178        /**
6179         * Scroll top
6180        */
6181        scrollTop: function() {
6182            if ( the.options.scroll && the.options.scroll.height ) {
6183                KTUtil.scrollTop(element);
6184            }
6185        },
6186
6187        /**
6188         * Get submenu mode for current breakpoint and menu state
6189         * @returns {KTMenu}
6190         */
6191        getSubmenuMode: function(el) {
6192            if ( KTUtil.isInResponsiveRange('desktop') ) {
6193                if (el && KTUtil.hasAttr(el, 'data-ktmenu-submenu-toggle')) {
6194                    return KTUtil.attr(el, 'data-ktmenu-submenu-toggle');
6195                }
6196
6197                if ( KTUtil.isset(the.options.submenu, 'desktop.state.body') ) {
6198                    if ( KTUtil.hasClasses(body, the.options.submenu.desktop.state.body) ) {
6199                        return the.options.submenu.desktop.state.mode;
6200                    } else {
6201                        return the.options.submenu.desktop.default;
6202                    }
6203                } else if ( KTUtil.isset(the.options.submenu, 'desktop') ) {
6204                    return the.options.submenu.desktop;
6205                }
6206            } else if ( KTUtil.isInResponsiveRange('tablet') && KTUtil.isset(the.options.submenu, 'tablet') ) {
6207                return the.options.submenu.tablet;
6208            } else if ( KTUtil.isInResponsiveRange('mobile') && KTUtil.isset(the.options.submenu, 'mobile') ) {
6209                return the.options.submenu.mobile;
6210            } else {
6211                return false;
6212            }
6213        },
6214
6215        /**
6216         * Get submenu mode for current breakpoint and menu state
6217         * @returns {KTMenu}
6218         */
6219        isConditionalSubmenuDropdown: function() {
6220            if ( KTUtil.isInResponsiveRange('desktop') && KTUtil.isset(the.options.submenu, 'desktop.state.body') ) {
6221                return true;
6222            } else {
6223                return false;
6224            }
6225        },
6226
6227
6228        /**
6229         * Reset submenu attributes
6230         * @returns {KTMenu}
6231         */
6232        resetSubmenuProps: function(e) {
6233            var submenus = KTUtil.findAll(element, '.kt-menu__submenu');
6234            if ( submenus ) {
6235                for (var i = 0, len = submenus.length; i < len; i++) {
6236                    KTUtil.css(submenus[0], 'display', '');
6237                    KTUtil.css(submenus[0], 'overflow', '');                                        
6238                }
6239            }
6240        },
6241
6242        /**
6243         * Handles submenu hover toggle
6244         * @returns {KTMenu}
6245         */
6246        handleSubmenuDrodownHoverEnter: function(e) {
6247            if ( Plugin.getSubmenuMode(this) === 'accordion' ) {
6248                return;
6249            }
6250
6251            if ( the.resumeDropdownHover() === false ) {
6252                return;
6253            }
6254
6255            var item = this;
6256
6257            if ( item.getAttribute('data-hover') == '1' ) {
6258                item.removeAttribute('data-hover');
6259                clearTimeout( item.getAttribute('data-timeout') );
6260                item.removeAttribute('data-timeout');
6261                //Plugin.hideSubmenuDropdown(item, false);
6262            }
6263
6264            // console.log('test!');
6265
6266            Plugin.showSubmenuDropdown(item);
6267        },
6268
6269        /**
6270         * Handles submenu hover toggle
6271         * @returns {KTMenu}
6272         */
6273        handleSubmenuDrodownHoverExit: function(e) {
6274            if ( the.resumeDropdownHover() === false ) {
6275                return;
6276            }
6277
6278            if ( Plugin.getSubmenuMode(this) === 'accordion' ) {
6279                return;
6280            }
6281
6282            var item = this;
6283            var time = the.options.dropdown.timeout;
6284
6285            var timeout = setTimeout(function() {
6286                if ( item.getAttribute('data-hover') == '1' ) {
6287                    Plugin.hideSubmenuDropdown(item, true);
6288                } 
6289            }, time);
6290
6291            item.setAttribute('data-hover', '1');
6292            item.setAttribute('data-timeout', timeout);  
6293        },
6294
6295        /**
6296         * Handles submenu click toggle
6297         * @returns {KTMenu}
6298         */
6299        handleSubmenuDropdownClick: function(e) {
6300            if ( Plugin.getSubmenuMode(this) === 'accordion' ) {
6301                return;
6302            }
6303 
6304            var item = this.closest('.kt-menu__item');
6305
6306            if ( item.getAttribute('data-ktmenu-submenu-mode') == 'accordion' ) {
6307                return;
6308            }
6309
6310            if ( KTUtil.hasClass(item, 'kt-menu__item--hover') === false ) {
6311                KTUtil.addClass(item, 'kt-menu__item--open-dropdown');
6312                Plugin.showSubmenuDropdown(item);
6313            } else {
6314                KTUtil.removeClass(item, 'kt-menu__item--open-dropdown' );
6315                Plugin.hideSubmenuDropdown(item, true);
6316            }
6317
6318            e.preventDefault();
6319        },
6320
6321        /**
6322         * Handles tab click toggle
6323         * @returns {KTMenu}
6324         */
6325        handleSubmenuDropdownTabClick: function(e) {
6326            if (Plugin.getSubmenuMode(this) === 'accordion') {
6327                return;
6328            }
6329
6330            var item = this.closest('.kt-menu__item');
6331
6332            if (item.getAttribute('data-ktmenu-submenu-mode') == 'accordion') {
6333                return;
6334            }
6335
6336            if (KTUtil.hasClass(item, 'kt-menu__item--hover') == false) {
6337                KTUtil.addClass(item, 'kt-menu__item--open-dropdown');
6338                Plugin.showSubmenuDropdown(item);
6339            }
6340
6341            e.preventDefault();
6342        },
6343
6344        /**
6345         * Handles submenu dropdown close on link click
6346         * @returns {KTMenu}
6347         */
6348        handleSubmenuDropdownClose: function(e, el) {
6349            // exit if its not submenu dropdown mode
6350            if (Plugin.getSubmenuMode(el) === 'accordion') {
6351                return;
6352            }
6353
6354            var shown = element.querySelectorAll('.kt-menu__item.kt-menu__item--submenu.kt-menu__item--hover:not(.kt-menu__item--tabs)');
6355
6356            // check if currently clicked link's parent item ha
6357            if (shown.length > 0 && KTUtil.hasClass(el, 'kt-menu__toggle') === false && el.querySelectorAll('.kt-menu__toggle').length === 0) {
6358                // close opened dropdown menus
6359                for (var i = 0, len = shown.length; i < len; i++) {
6360                    Plugin.hideSubmenuDropdown(shown[0], true);
6361                }
6362            }
6363        },
6364
6365        /**
6366         * helper functions
6367         * @returns {KTMenu}
6368         */
6369        handleSubmenuAccordion: function(e, el) {
6370            var query;
6371            var item = el ? el : this;
6372
6373            if ( Plugin.getSubmenuMode(el) === 'dropdown' && (query = item.closest('.kt-menu__item') ) ) {
6374                if (query.getAttribute('data-ktmenu-submenu-mode') != 'accordion' ) {
6375                    e.preventDefault();
6376                    return;
6377                }
6378            }
6379
6380            var li = item.closest('.kt-menu__item');
6381            var submenu = KTUtil.child(li, '.kt-menu__submenu, .kt-menu__inner');
6382
6383            if (KTUtil.hasClass(item.closest('.kt-menu__item'), 'kt-menu__item--open-always')) {
6384                return;
6385            }
6386
6387            if ( li && submenu ) {
6388                e.preventDefault();
6389                var speed = the.options.accordion.slideSpeed;
6390                var hasClosables = false;
6391
6392                if ( KTUtil.hasClass(li, 'kt-menu__item--open') === false ) {
6393                    // hide other accordions                    
6394                    if ( the.options.accordion.expandAll === false ) {
6395                        var subnav = item.closest('.kt-menu__nav, .kt-menu__subnav');
6396                        var closables = KTUtil.children(subnav, '.kt-menu__item.kt-menu__item--open.kt-menu__item--submenu:not(.kt-menu__item--here):not(.kt-menu__item--open-always)');
6397
6398                        if ( subnav && closables ) {
6399                            for (var i = 0, len = closables.length; i < len; i++) {
6400                                var el_ = closables[0];
6401                                var submenu_ = KTUtil.child(el_, '.kt-menu__submenu');
6402                                if ( submenu_ ) {
6403                                    KTUtil.slideUp(submenu_, speed, function() {
6404                                        Plugin.scrollUpdate();
6405                                        KTUtil.removeClass(el_, 'kt-menu__item--open');
6406                                    });                    
6407                                }
6408                            }
6409                        }
6410                    }
6411
6412                    KTUtil.slideDown(submenu, speed, function() {
6413                        Plugin.scrollToItem(item);
6414                        Plugin.scrollUpdate();
6415                        
6416                        Plugin.eventTrigger('submenuToggle', submenu);
6417                    });
6418                
6419                    KTUtil.addClass(li, 'kt-menu__item--open');
6420
6421                } else {
6422                    KTUtil.slideUp(submenu, speed, function() {
6423                        Plugin.scrollToItem(item);
6424                        Plugin.eventTrigger('submenuToggle', submenu);
6425                    });
6426
6427                    KTUtil.removeClass(li, 'kt-menu__item--open');
6428                }
6429            }
6430        },
6431
6432        /**
6433         * scroll to item function
6434         * @returns {KTMenu}
6435         */
6436        scrollToItem: function(item) {
6437            // handle auto scroll for accordion submenus
6438            if ( KTUtil.isInResponsiveRange('desktop') && the.options.accordion.autoScroll && element.getAttribute('data-ktmenu-scroll') !== '1' ) {
6439                KTUtil.scrollTo(item, the.options.accordion.autoScrollSpeed);
6440            }
6441        },
6442
6443        /**
6444         * helper functions
6445         * @returns {KTMenu}
6446         */
6447        hideSubmenuDropdown: function(item, classAlso) {
6448            // remove submenu activation class
6449            if ( classAlso ) {
6450                KTUtil.removeClass(item, 'kt-menu__item--hover');
6451                KTUtil.removeClass(item, 'kt-menu__item--active-tab');
6452            }
6453
6454            // clear timeout
6455            item.removeAttribute('data-hover');
6456
6457            if ( item.getAttribute('data-ktmenu-dropdown-toggle-class') ) {
6458                KTUtil.removeClass(body, item.getAttribute('data-ktmenu-dropdown-toggle-class'));
6459            }
6460
6461            var timeout = item.getAttribute('data-timeout');
6462            item.removeAttribute('data-timeout');
6463            clearTimeout(timeout);
6464        },
6465
6466        /**
6467         * helper functions
6468         * @returns {KTMenu}
6469         */
6470        showSubmenuDropdown: function(item) {
6471            // close active submenus
6472            var list = element.querySelectorAll('.kt-menu__item--submenu.kt-menu__item--hover, .kt-menu__item--submenu.kt-menu__item--active-tab');
6473
6474            if ( list ) {
6475                for (var i = 0, len = list.length; i < len; i++) {
6476                    var el = list[i];
6477                    if ( item !== el && el.contains(item) === false && item.contains(el) === false ) {
6478                        Plugin.hideSubmenuDropdown(el, true);
6479                    }
6480                }
6481            } 
6482
6483            // add submenu activation class
6484            KTUtil.addClass(item, 'kt-menu__item--hover');
6485            
6486            if ( item.getAttribute('data-ktmenu-dropdown-toggle-class') ) {
6487                KTUtil.addClass(body, item.getAttribute('data-ktmenu-dropdown-toggle-class'));
6488            }
6489        },
6490
6491        /**
6492         * Handles submenu slide toggle
6493         * @returns {KTMenu}
6494         */
6495        createSubmenuDropdownClickDropoff: function(el) {
6496            var query;
6497            var zIndex = (query = KTUtil.child(el, '.kt-menu__submenu') ? KTUtil.css(query, 'z-index') : 0) - 1;
6498
6499            var dropoff = document.createElement('<div class="kt-menu__dropoff" style="background: transparent; position: fixed; top: 0; bottom: 0; left: 0; right: 0; z-index: ' + zIndex + '"></div>');
6500
6501            body.appendChild(dropoff);
6502
6503            KTUtil.addEvent(dropoff, 'click', function(e) {
6504                e.stopPropagation();
6505                e.preventDefault();
6506                KTUtil.remove(this);
6507                Plugin.hideSubmenuDropdown(el, true);
6508            });
6509        },
6510
6511        /**
6512         * Handles submenu hover toggle
6513         * @returns {KTMenu}
6514         */
6515        pauseDropdownHover: function(time) {
6516            var date = new Date();
6517
6518            the.pauseDropdownHoverTime = date.getTime() + time;
6519        },
6520
6521        /**
6522         * Handles submenu hover toggle
6523         * @returns {KTMenu}
6524         */
6525        resumeDropdownHover: function() {
6526            var date = new Date();
6527
6528            return (date.getTime() > the.pauseDropdownHoverTime ? true : false);
6529        },
6530
6531        /**
6532         * Reset menu's current active item
6533         * @returns {KTMenu}
6534         */
6535        resetActiveItem: function(item) {
6536            var list;
6537            var parents;
6538
6539            list = element.querySelectorAll('.kt-menu__item--active');
6540            
6541            for (var i = 0, len = list.length; i < len; i++) {
6542                var el = list[0];
6543                KTUtil.removeClass(el, 'kt-menu__item--active');
6544                KTUtil.hide( KTUtil.child(el, '.kt-menu__submenu') );
6545                parents = KTUtil.parents(el, '.kt-menu__item--submenu');
6546
6547                for (var i_ = 0, len_ = parents.length; i_ < len_; i_++) {
6548                    var el_ = parents[i];
6549                    KTUtil.removeClass(el_, 'kt-menu__item--open');
6550                    KTUtil.hide( KTUtil.child(el_, '.kt-menu__submenu') );
6551                }
6552            }
6553
6554            // close open submenus
6555            if ( the.options.accordion.expandAll === false ) {
6556                if ( list = element.querySelectorAll('.kt-menu__item--open') ) {
6557                    for (var i = 0, len = list.length; i < len; i++) {
6558                        KTUtil.removeClass(parents[0], 'kt-menu__item--open');
6559                    }
6560                }
6561            }
6562        },
6563
6564        /**
6565         * Sets menu's active item
6566         * @returns {KTMenu}
6567         */
6568        setActiveItem: function(item) {
6569            // reset current active item
6570            Plugin.resetActiveItem();
6571
6572            KTUtil.addClass(item, 'kt-menu__item--active');
6573            
6574            var parents = KTUtil.parents(item, '.kt-menu__item--submenu');
6575            for (var i = 0, len = parents.length; i < len; i++) {
6576                KTUtil.addClass(parents[i], 'kt-menu__item--open');
6577            }
6578        },
6579
6580        /**
6581         * Returns page breadcrumbs for the menu's active item
6582         * @returns {KTMenu}
6583         */
6584        getBreadcrumbs: function(item) {
6585            var query;
6586            var breadcrumbs = [];
6587            var link = KTUtil.child(item, '.kt-menu__link');
6588
6589            breadcrumbs.push({
6590                text: (query = KTUtil.child(link, '.kt-menu__link-text') ? query.innerHTML : ''),
6591                title: link.getAttribute('title'),
6592                href: link.getAttribute('href')
6593            });
6594
6595            var parents = KTUtil.parents(item, '.kt-menu__item--submenu');
6596            for (var i = 0, len = parents.length; i < len; i++) {
6597                var submenuLink = KTUtil.child(parents[i], '.kt-menu__link');
6598
6599                breadcrumbs.push({
6600                    text: (query = KTUtil.child(submenuLink, '.kt-menu__link-text') ? query.innerHTML : ''),
6601                    title: submenuLink.getAttribute('title'),
6602                    href: submenuLink.getAttribute('href')
6603                });
6604            }
6605
6606            return  breadcrumbs.reverse();
6607        },
6608
6609        /**
6610         * Returns page title for the menu's active item
6611         * @returns {KTMenu}
6612         */
6613        getPageTitle: function(item) {
6614            var query;
6615
6616            return (query = KTUtil.child(item, '.kt-menu__link-text') ? query.innerHTML : '');
6617        },
6618
6619        /**
6620         * Trigger events
6621         */
6622        eventTrigger: function(name, args) {
6623            for (var i = 0; i < the.events.length; i++ ) {
6624                var event = the.events[i];
6625                if ( event.name == name ) {
6626                    if ( event.one == true ) {
6627                        if ( event.fired == false ) {
6628                            the.events[i].fired = true;
6629                            event.handler.call(this, the, args);
6630                        }
6631                    } else {
6632                        event.handler.call(this, the, args);
6633                    }
6634                }
6635            }
6636        },
6637
6638        addEvent: function(name, handler, one) {
6639            the.events.push({
6640                name: name,
6641                handler: handler,
6642                one: one,
6643                fired: false
6644            });
6645        },
6646
6647        removeEvent: function(name) {
6648            if (the.events[name]) {
6649                delete the.events[name];
6650            }
6651        }
6652    };
6653
6654    //////////////////////////
6655    // ** Public Methods ** //
6656    //////////////////////////
6657
6658    /**
6659     * Set default options 
6660     */
6661
6662    the.setDefaults = function(options) {
6663        defaultOptions = options;
6664    };
6665
6666    /**
6667     * Update scroll
6668     */
6669    the.scrollUpdate = function() {
6670        return Plugin.scrollUpdate();
6671    };
6672
6673    /**
6674     * Re-init scroll
6675     */
6676    the.scrollReInit = function() {
6677        return Plugin.scrollInit();
6678    };
6679
6680    /**
6681     * Scroll top
6682     */
6683    the.scrollTop = function() {
6684        return Plugin.scrollTop();
6685    };
6686
6687    /**
6688     * Set active menu item
6689     */
6690    the.setActiveItem = function(item) {
6691        return Plugin.setActiveItem(item);
6692    };
6693
6694    the.reload = function() {
6695        return Plugin.reload();
6696    };
6697
6698    the.update = function(options) {
6699        return Plugin.update(options);
6700    };
6701
6702    /**
6703     * Set breadcrumb for menu item
6704     */
6705    the.getBreadcrumbs = function(item) {
6706        return Plugin.getBreadcrumbs(item);
6707    };
6708
6709    /**
6710     * Set page title for menu item
6711     */
6712    the.getPageTitle = function(item) {
6713        return Plugin.getPageTitle(item);
6714    };
6715
6716    /**
6717     * Get submenu mode
6718     */
6719    the.getSubmenuMode = function(el) {
6720        return Plugin.getSubmenuMode(el);
6721    };
6722
6723    /**
6724     * Hide dropdown submenu
6725     * @returns {jQuery}
6726     */
6727    the.hideDropdown = function(item) {
6728        Plugin.hideSubmenuDropdown(item, true);
6729    };
6730
6731    /**
6732     * Disable menu for given time
6733     * @returns {jQuery}
6734     */
6735    the.pauseDropdownHover = function(time) {
6736        Plugin.pauseDropdownHover(time);
6737    };
6738
6739    /**
6740     * Disable menu for given time
6741     * @returns {jQuery}
6742     */
6743    the.resumeDropdownHover = function() {
6744        return Plugin.resumeDropdownHover();
6745    };
6746
6747    /**
6748     * Register event
6749     */
6750    the.on = function(name, handler) {
6751        return Plugin.addEvent(name, handler);
6752    };
6753
6754    the.off = function(name) {
6755        return Plugin.removeEvent(name);
6756    };
6757
6758    the.one = function(name, handler) {
6759        return Plugin.addEvent(name, handler, true);
6760    };
6761
6762    ///////////////////////////////
6763    // ** Plugin Construction ** //
6764    ///////////////////////////////
6765
6766    // Run plugin
6767    Plugin.construct.apply(the, [options]);
6768
6769    // Handle plugin on window resize
6770    KTUtil.addResizeHandler(function() {
6771        if (init) {
6772            the.reload();
6773        }  
6774    });
6775
6776    // Init done
6777    init = true;
6778
6779    // Return plugin instance
6780    return the;
6781};
6782
6783// Plugin global lazy initialization
6784document.addEventListener("click", function (e) {
6785    var body = KTUtil.get('body');
6786    var query;
6787    if ( query = body.querySelectorAll('.kt-menu__nav .kt-menu__item.kt-menu__item--submenu.kt-menu__item--hover:not(.kt-menu__item--tabs)[data-ktmenu-submenu-toggle="click"]') ) {
6788        for (var i = 0, len = query.length; i < len; i++) {
6789            var element = query[i].closest('.kt-menu__nav').parentNode;
6790
6791            if ( element ) {
6792                var the = KTUtil.data(element).get('menu');
6793
6794                if ( !the ) {
6795                    break;
6796                }
6797
6798                if ( !the || the.getSubmenuMode() !== 'dropdown' ) {
6799                    break;
6800                }
6801
6802                if ( e.target !== element && element.contains(e.target) === false ) {
6803                    var items;
6804                    if ( items = element.querySelectorAll('.kt-menu__item--submenu.kt-menu__item--hover:not(.kt-menu__item--tabs)[data-ktmenu-submenu-toggle="click"]') ) {
6805                        for (var j = 0, cnt = items.length; j < cnt; j++) {
6806                            the.hideDropdown(items[j]);
6807                        }
6808                    }
6809                }
6810            }            
6811        }
6812    } 
6813});
6814"use strict";
6815var KTOffcanvas = function(elementId, options) {
6816    // Main object
6817    var the = this;
6818    var init = false;
6819
6820    // Get element object
6821    var element = KTUtil.get(elementId);
6822    var body = KTUtil.get('body');
6823
6824    if (!element) {
6825        return;
6826    }
6827
6828    // Default options
6829    var defaultOptions = {};
6830
6831    ////////////////////////////
6832    // ** Private Methods  ** //
6833    ////////////////////////////
6834
6835    var Plugin = {
6836        construct: function(options) {
6837            if (KTUtil.data(element).has('offcanvas')) {
6838                the = KTUtil.data(element).get('offcanvas');
6839            } else {
6840                // reset offcanvas
6841                Plugin.init(options);
6842                
6843                // build offcanvas
6844                Plugin.build();
6845
6846                KTUtil.data(element).set('offcanvas', the);
6847            }
6848
6849            return the;
6850        },
6851
6852        init: function(options) {
6853            the.events = [];
6854
6855            // merge default and user defined options
6856            the.options = KTUtil.deepExtend({}, defaultOptions, options);
6857            the.overlay;
6858
6859            the.classBase = the.options.baseClass;
6860            the.classShown = the.classBase + '--on';
6861            the.classOverlay = the.classBase + '-overlay';
6862
6863            the.state = KTUtil.hasClass(element, the.classShown) ? 'shown' : 'hidden';
6864        },
6865
6866        build: function() {
6867            // offcanvas toggle
6868            if (the.options.toggleBy) {
6869                if (typeof the.options.toggleBy === 'string') { 
6870                    KTUtil.addEvent( the.options.toggleBy, 'click', function(e) {
6871                        e.preventDefault();
6872                        Plugin.toggle();
6873                    }); 
6874                } else if (the.options.toggleBy && the.options.toggleBy[0] && the.options.toggleBy[0].target) {
6875                    for (var i in the.options.toggleBy) { 
6876                        KTUtil.addEvent( the.options.toggleBy[i].target, 'click', function(e) {
6877                            e.preventDefault();
6878                            Plugin.toggle();
6879                        }); 
6880                    }
6881                } else if (the.options.toggleBy && the.options.toggleBy.target) {
6882                    KTUtil.addEvent( the.options.toggleBy.target, 'click', function(e) {
6883                        e.preventDefault();
6884                        Plugin.toggle();
6885                    }); 
6886                } 
6887            }
6888
6889            // offcanvas close
6890            var closeBy = KTUtil.get(the.options.closeBy);
6891            if (closeBy) {
6892                KTUtil.addEvent(closeBy, 'click', function(e) {
6893                    e.preventDefault();
6894                    Plugin.hide();
6895                });
6896            }
6897        },
6898
6899        isShown: function(target) {
6900            return (the.state == 'shown' ? true : false);
6901        },
6902
6903        toggle: function() {;
6904            Plugin.eventTrigger('toggle'); 
6905
6906            if (the.state == 'shown') {
6907                Plugin.hide(this);
6908            } else {
6909                Plugin.show(this);
6910            }
6911        },
6912
6913        show: function(target) {
6914            if (the.state == 'shown') {
6915                return;
6916            }
6917
6918            Plugin.eventTrigger('beforeShow');
6919
6920            Plugin.togglerClass(target, 'show');
6921
6922            // Offcanvas panel
6923            KTUtil.addClass(body, the.classShown);
6924            KTUtil.addClass(element, the.classShown);
6925
6926            the.state = 'shown';
6927
6928            if (the.options.overlay) {
6929                the.overlay = KTUtil.insertAfter(document.createElement('DIV') , element );
6930                KTUtil.addClass(the.overlay, the.classOverlay);
6931                KTUtil.addEvent(the.overlay, 'click', function(e) {
6932                    e.stopPropagation();
6933                    e.preventDefault();
6934                    Plugin.hide(target);       
6935                });
6936            }
6937
6938            Plugin.eventTrigger('afterShow');
6939        },
6940
6941        hide: function(target) {
6942            if (the.state == 'hidden') {
6943                return;
6944            }
6945
6946            Plugin.eventTrigger('beforeHide');
6947
6948            Plugin.togglerClass(target, 'hide');
6949
6950            KTUtil.removeClass(body, the.classShown);
6951            KTUtil.removeClass(element, the.classShown);
6952
6953            the.state = 'hidden';
6954
6955            if (the.options.overlay && the.overlay) {
6956                KTUtil.remove(the.overlay);
6957            }
6958
6959            Plugin.eventTrigger('afterHide');
6960        },
6961
6962        togglerClass: function(target, mode) {
6963            // Toggler
6964            var id = KTUtil.attr(target, 'id');
6965            var toggleBy;
6966
6967            if (the.options.toggleBy && the.options.toggleBy[0] && the.options.toggleBy[0].target) {
6968                for (var i in the.options.toggleBy) {
6969                    if (the.options.toggleBy[i].target === id) {
6970                        toggleBy = the.options.toggleBy[i];
6971                    }        
6972                }
6973            } else if (the.options.toggleBy && the.options.toggleBy.target) {
6974                toggleBy = the.options.toggleBy;
6975            }
6976
6977            if (toggleBy) {                
6978                var el = KTUtil.get(toggleBy.target);
6979                
6980                if (mode === 'show') {
6981                    KTUtil.addClass(el, toggleBy.state);
6982                }
6983
6984                if (mode === 'hide') {
6985                    KTUtil.removeClass(el, toggleBy.state);
6986                }
6987            }
6988        },
6989
6990        eventTrigger: function(name, args) {
6991            for (var i = 0; i < the.events.length; i++) {
6992                var event = the.events[i];
6993                if (event.name == name) {
6994                    if (event.one == true) {
6995                        if (event.fired == false) {
6996                            the.events[i].fired = true;
6997                            event.handler.call(this, the, args);
6998                        }
6999                    } else {
7000                        event.handler.call(this, the, args);
7001                    }
7002                }
7003            }
7004        },
7005
7006        addEvent: function(name, handler, one) {
7007            the.events.push({
7008                name: name,
7009                handler: handler,
7010                one: one,
7011                fired: false
7012            });
7013        }
7014    };
7015
7016    //////////////////////////
7017    // ** Public Methods ** //
7018    //////////////////////////
7019    the.setDefaults = function(options) {
7020        defaultOptions = options;
7021    };
7022
7023    the.isShown = function() {
7024        return Plugin.isShown();
7025    };
7026
7027    the.hide = function() {
7028        return Plugin.hide();
7029    };
7030
7031    the.show = function() {
7032        return Plugin.show();
7033    };
7034
7035    the.on = function(name, handler) {
7036        return Plugin.addEvent(name, handler);
7037    };
7038
7039    the.one = function(name, handler) {
7040        return Plugin.addEvent(name, handler, true);
7041    };
7042
7043    ///////////////////////////////
7044    // ** Plugin Construction ** //
7045    ///////////////////////////////
7046
7047    // Run plugin
7048    Plugin.construct.apply(the, [options]);
7049
7050    // Init done
7051    init = true;
7052
7053    // Return plugin instance
7054    return the;
7055};
7056"use strict";
7057// plugin setup
7058var KTPortlet = function(elementId, options) {
7059    // Main object
7060    var the = this;
7061    var init = false;
7062
7063    // Get element object
7064    var element = KTUtil.get(elementId);
7065    var body = KTUtil.get('body');
7066
7067    if (!element) {
7068        return;
7069    }
7070
7071    // Default options
7072    var defaultOptions = {
7073        bodyToggleSpeed: 400,
7074        tooltips: true,
7075        tools: {
7076            toggle: {
7077                collapse: 'Collapse',
7078                expand: 'Expand'
7079            },
7080            reload: 'Reload',
7081            remove: 'Remove',
7082            fullscreen: {
7083                on: 'Fullscreen',
7084                off: 'Exit Fullscreen'
7085            }
7086        },
7087        sticky: {
7088            offset: 300,
7089            zIndex: 101
7090        }
7091    };
7092
7093    ////////////////////////////
7094    // ** Private Methods  ** //
7095    ////////////////////////////
7096
7097    var Plugin = {
7098        /**
7099         * Construct
7100         */
7101
7102        construct: function(options) {
7103            if (KTUtil.data(element).has('portlet')) {
7104                the = KTUtil.data(element).get('portlet');
7105            } else {
7106                // reset menu
7107                Plugin.init(options);
7108
7109                // build menu
7110                Plugin.build();
7111
7112                KTUtil.data(element).set('portlet', the);
7113            }
7114
7115            return the;
7116        },
7117
7118        /**
7119         * Init portlet
7120         */
7121        init: function(options) {
7122            the.element = element;
7123            the.events = [];
7124
7125            // merge default and user defined options
7126            the.options = KTUtil.deepExtend({}, defaultOptions, options);
7127            the.head = KTUtil.child(element, '.kt-portlet__head');
7128            the.foot = KTUtil.child(element, '.kt-portlet__foot');
7129
7130            if (KTUtil.child(element, '.kt-portlet__body')) {
7131                the.body = KTUtil.child(element, '.kt-portlet__body');
7132            } else if (KTUtil.child(element, '.kt-form')) {
7133                the.body = KTUtil.child(element, '.kt-form');
7134            }
7135        },
7136
7137        /**
7138         * Build Form Wizard
7139         */
7140        build: function() {
7141            // Remove
7142            var remove = KTUtil.find(the.head, '[data-ktportlet-tool=remove]');
7143            if (remove) {
7144                KTUtil.addEvent(remove, 'click', function(e) {
7145                    e.preventDefault();
7146                    Plugin.remove();
7147                });
7148            }
7149
7150            // Reload
7151            var reload = KTUtil.find(the.head, '[data-ktportlet-tool=reload]');
7152            if (reload) {
7153                KTUtil.addEvent(reload, 'click', function(e) {
7154                    e.preventDefault();
7155                    Plugin.reload();
7156                });
7157            }
7158
7159            // Toggle
7160            var toggle = KTUtil.find(the.head, '[data-ktportlet-tool=toggle]');
7161            if (toggle) {
7162                KTUtil.addEvent(toggle, 'click', function(e) {
7163                    e.preventDefault();
7164                    Plugin.toggle();
7165                });
7166            }
7167
7168            //== Fullscreen
7169            var fullscreen = KTUtil.find(the.head, '[data-ktportlet-tool=fullscreen]');
7170            if (fullscreen) {
7171                KTUtil.addEvent(fullscreen, 'click', function(e) {
7172                    e.preventDefault();
7173                    Plugin.fullscreen();
7174                });
7175            }
7176
7177            Plugin.setupTooltips();
7178        },
7179
7180        /**
7181         * Enable stickt mode
7182         */
7183        initSticky: function() {
7184            var lastScrollTop = 0;
7185            var offset = the.options.sticky.offset;
7186
7187            if (!the.head) {
7188                return;
7189            }
7190
7191	        window.addEventListener('scroll', Plugin.onScrollSticky);
7192        },
7193
7194	    /**
7195	     * Window scroll handle event for sticky portlet
7196	     */
7197	    onScrollSticky: function(e) {
7198		    var offset = the.options.sticky.offset;
7199		    if(isNaN(offset)) return;
7200
7201		    var st = document.documentElement.scrollTop;
7202
7203		    if (st >= offset && KTUtil.hasClass(body, 'kt-portlet--sticky') === false) {
7204			    Plugin.eventTrigger('stickyOn');
7205
7206			    KTUtil.addClass(body, 'kt-portlet--sticky');
7207			    KTUtil.addClass(element, 'kt-portlet--sticky');
7208
7209			    Plugin.updateSticky();
7210
7211		    } else if ((st*1.5) <= offset && KTUtil.hasClass(body, 'kt-portlet--sticky')) {
7212			    // back scroll mode
7213			    Plugin.eventTrigger('stickyOff');
7214
7215			    KTUtil.removeClass(body, 'kt-portlet--sticky');
7216			    KTUtil.removeClass(element, 'kt-portlet--sticky');
7217
7218			    Plugin.resetSticky();
7219		    }
7220	    },
7221
7222        updateSticky: function() {
7223            if (!the.head) {
7224                return;
7225            }
7226
7227            var top;
7228
7229            if (KTUtil.hasClass(body, 'kt-portlet--sticky')) {
7230                if (the.options.sticky.position.top instanceof Function) {
7231                    top = parseInt(the.options.sticky.position.top.call());
7232                } else {
7233                    top = parseInt(the.options.sticky.position.top);
7234                }
7235
7236                var left;
7237                if (the.options.sticky.position.left instanceof Function) {
7238                    left = parseInt(the.options.sticky.position.left.call());
7239                } else {
7240                    left = parseInt(the.options.sticky.position.left);
7241                }
7242
7243                var right;
7244                if (the.options.sticky.position.right instanceof Function) {
7245                    right = parseInt(the.options.sticky.position.right.call());
7246                } else {
7247                    right = parseInt(the.options.sticky.position.right);
7248                }
7249
7250                KTUtil.css(the.head, 'z-index', the.options.sticky.zIndex);
7251                KTUtil.css(the.head, 'top', top + 'px');
7252                KTUtil.css(the.head, 'left', left + 'px');
7253                KTUtil.css(the.head, 'right', right + 'px');
7254            }
7255        },
7256
7257        resetSticky: function() {
7258            if (!the.head) {
7259                return;
7260            }
7261
7262            if (KTUtil.hasClass(body, 'kt-portlet--sticky') === false) {
7263                KTUtil.css(the.head, 'z-index', '');
7264                KTUtil.css(the.head, 'top', '');
7265                KTUtil.css(the.head, 'left', '');
7266                KTUtil.css(the.head, 'right', '');
7267            }
7268        },
7269
7270        /**
7271         * Remove portlet
7272         */
7273        remove: function() {
7274            if (Plugin.eventTrigger('beforeRemove') === false) {
7275                return;
7276            }
7277
7278            if (KTUtil.hasClass(body, 'kt-portlet--fullscreen') && KTUtil.hasClass(element, 'kt-portlet--fullscreen')) {
7279                Plugin.fullscreen('off');
7280            }
7281
7282            Plugin.removeTooltips();
7283
7284            KTUtil.remove(element);
7285
7286            Plugin.eventTrigger('afterRemove');
7287        },
7288
7289        /**
7290         * Set content
7291         */
7292        setContent: function(html) {
7293            if (html) {
7294                the.body.innerHTML = html;
7295            }
7296        },
7297
7298        /**
7299         * Get body
7300         */
7301        getBody: function() {
7302            return the.body;
7303        },
7304
7305        /**
7306         * Get self
7307         */
7308        getSelf: function() {
7309            return element;
7310        },
7311
7312        /**
7313         * Setup tooltips
7314         */
7315        setupTooltips: function() {
7316            if (the.options.tooltips) {
7317                var collapsed = KTUtil.hasClass(element, 'kt-portlet--collapse') || KTUtil.hasClass(element, 'kt-portlet--collapsed');
7318                var fullscreenOn = KTUtil.hasClass(body, 'kt-portlet--fullscreen') && KTUtil.hasClass(element, 'kt-portlet--fullscreen');
7319
7320                //== Remove
7321                var remove = KTUtil.find(the.head, '[data-ktportlet-tool=remove]');
7322                if (remove) {
7323                    var placement = (fullscreenOn ? 'bottom' : 'top');
7324                    var tip = new Tooltip(remove, {
7325                        title: the.options.tools.remove,
7326                        placement: placement,
7327                        offset: (fullscreenOn ? '0,10px,0,0' : '0,5px'),
7328                        trigger: 'hover',
7329                        template: '<div class="tooltip tooltip-portlet tooltip bs-tooltip-' 
7329+ placement + '" role="tooltip">\
7330                            <div class="tooltip-arrow arrow"></div>\
7331                            <div class="tooltip-inner"></div>\
7332                        </div>'
7333                    });
7334
7335                    KTUtil.data(remove).set('tooltip', tip);
7336                }
7337
7338                //== Reload
7339                var reload = KTUtil.find(the.head, '[data-ktportlet-tool=reload]');
7340                if (reload) {
7341                    var placement = (fullscreenOn ? 'bottom' : 'top');
7342                    var tip = new Tooltip(reload, {
7343                        title: the.options.tools.reload,
7344                        placement: placement,
7345                        offset: (fullscreenOn ? '0,10px,0,0' : '0,5px'),
7346                        trigger: 'hover',
7347                        template: '<div class="tooltip tooltip-portlet tooltip bs-tooltip-' + placement + '" role="tooltip">\
7348                            <div class="tooltip-arrow arrow"></div>\
7349                            <div class="tooltip-inner"></div>\
7350                        </div>'
7351                    });
7352
7353                    KTUtil.data(reload).set('tooltip', tip);
7354                }
7355
7356                //== Toggle
7357                var toggle = KTUtil.find(the.head, '[data-ktportlet-tool=toggle]');
7358                if (toggle) {
7359                    var placement = (fullscreenOn ? 'bottom' : 'top');
7360                    var tip = new Tooltip(toggle, {
7361                        title: (collapsed ? the.options.tools.toggle.expand : the.options.tools.toggle.collapse),
7362                        placement: placement,
7363                        offset: (fullscreenOn ? '0,10px,0,0' : '0,5px'),
7364                        trigger: 'hover',
7365                        template: '<div class="tooltip tooltip-portlet tooltip bs-tooltip-' + placement + '" role="tooltip">\
7366                            <div class="tooltip-arrow arrow"></div>\
7367                            <div class="tooltip-inner"></div>\
7368                        </div>'
7369                    });
7370
7371                    KTUtil.data(toggle).set('tooltip', tip);
7372                }
7373
7374                //== Fullscreen
7375                var fullscreen = KTUtil.find(the.head, '[data-ktportlet-tool=fullscreen]');
7376                if (fullscreen) {
7377                    var placement = (fullscreenOn ? 'bottom' : 'top');
7378                    var tip = new Tooltip(fullscreen, {
7379                        title: (fullscreenOn ? the.options.tools.fullscreen.off : the.options.tools.fullscreen.on),
7380                        placement: placement,
7381                        offset: (fullscreenOn ? '0,10px,0,0' : '0,5px'),
7382                        trigger: 'hover',
7383                        template: '<div class="tooltip tooltip-portlet tooltip bs-tooltip-' + placement + '" role="tooltip">\
7384                            <div class="tooltip-arrow arrow"></div>\
7385                            <div class="tooltip-inner"></div>\
7386                        </div>'
7387                    });
7388
7389                    KTUtil.data(fullscreen).set('tooltip', tip);
7390                }
7391            }
7392        },
7393
7394        /**
7395         * Setup tooltips
7396         */
7397        removeTooltips: function() {
7398            if (the.options.tooltips) {
7399                //== Remove
7400                var remove = KTUtil.find(the.head, '[data-ktportlet-tool=remove]');
7401                if (remove && KTUtil.data(remove).has('tooltip')) {
7402                    KTUtil.data(remove).get('tooltip').dispose();
7403                }
7404
7405                //== Reload
7406                var reload = KTUtil.find(the.head, '[data-ktportlet-tool=reload]');
7407                if (reload && KTUtil.data(reload).has('tooltip')) {
7408                    KTUtil.data(reload).get('tooltip').dispose();
7409                }
7410
7411                //== Toggle
7412                var toggle = KTUtil.find(the.head, '[data-ktportlet-tool=toggle]');
7413                if (toggle && KTUtil.data(toggle).has('tooltip')) {
7414                    KTUtil.data(toggle).get('tooltip').dispose();
7415                }
7416
7417                //== Fullscreen
7418                var fullscreen = KTUtil.find(the.head, '[data-ktportlet-tool=fullscreen]');
7419                if (fullscreen && KTUtil.data(fullscreen).has('tooltip')) {
7420                    KTUtil.data(fullscreen).get('tooltip').dispose();
7421                }
7422            }
7423        },
7424
7425        /**
7426         * Reload
7427         */
7428        reload: function() {
7429            Plugin.eventTrigger('reload');
7430        },
7431
7432        /**
7433         * Toggle
7434         */
7435        toggle: function() {
7436            if (KTUtil.hasClass(element, 'kt-portlet--collapse') || KTUtil.hasClass(element, 'kt-portlet--collapsed')) {
7437                Plugin.expand();
7438            } else {
7439                Plugin.collapse();
7440            }
7441        },
7442
7443        /**
7444         * Collapse
7445         */
7446        collapse: function() {
7447            if (Plugin.eventTrigger('beforeCollapse') === false) {
7448                return;
7449            }
7450
7451            KTUtil.slideUp(the.body, the.options.bodyToggleSpeed, function() {
7452                Plugin.eventTrigger('afterCollapse');
7453            });
7454
7455            KTUtil.addClass(element, 'kt-portlet--collapse');
7456
7457            var toggle = KTUtil.find(the.head, '[data-ktportlet-tool=toggle]');
7458            if (toggle && KTUtil.data(toggle).has('tooltip')) {
7459                KTUtil.data(toggle).get('tooltip').updateTitleContent(the.options.tools.toggle.expand);
7460            }
7461        },
7462
7463        /**
7464         * Expand
7465         */
7466        expand: function() {
7467            if (Plugin.eventTrigger('beforeExpand') === false) {
7468                return;
7469            }
7470
7471            KTUtil.slideDown(the.body, the.options.bodyToggleSpeed, function() {
7472                Plugin.eventTrigger('afterExpand');
7473            });
7474
7475            KTUtil.removeClass(element, 'kt-portlet--collapse');
7476            KTUtil.removeClass(element, 'kt-portlet--collapsed');
7477
7478            var toggle = KTUtil.find(the.head, '[data-ktportlet-tool=toggle]');
7479            if (toggle && KTUtil.data(toggle).has('tooltip')) {
7480                KTUtil.data(toggle).get('tooltip').updateTitleContent(the.options.tools.toggle.collapse);
7481            }
7482        },
7483
7484        /**
7485         * fullscreen
7486         */
7487        fullscreen: function(mode) {
7488            var d = {};
7489            var speed = 300;
7490
7491            if (mode === 'off' || (KTUtil.hasClass(body, 'kt-portlet--fullscreen') && KTUtil.hasClass(element, 'kt-portlet--fullscreen'))) {
7492                Plugin.eventTrigger('beforeFullscreenOff');
7493
7494                KTUtil.removeClass(body, 'kt-portlet--fullscreen');
7495                KTUtil.removeClass(element, 'kt-portlet--fullscreen');
7496
7497                Plugin.removeTooltips();
7498                Plugin.setupTooltips();
7499
7500                if (the.foot) {
7501                    KTUtil.css(the.body, 'margin-bottom', '');
7502                    KTUtil.css(the.foot, 'margin-top', '');
7503                }
7504
7505                Plugin.eventTrigger('afterFullscreenOff');
7506            } else {
7507                Plugin.eventTrigger('beforeFullscreenOn');
7508
7509                KTUtil.addClass(element, 'kt-portlet--fullscreen');
7510                KTUtil.addClass(body, 'kt-portlet--fullscreen');
7511
7512                Plugin.removeTooltips();
7513                Plugin.setupTooltips();
7514
7515
7516                if (the.foot) {
7517                    var height1 = parseInt(KTUtil.css(the.foot, 'height'));
7518                    var height2 = parseInt(KTUtil.css(the.foot, 'height')) + parseInt(KTUtil.css(the.head, 'height'));
7519                    KTUtil.css(the.body, 'margin-bottom', height1 + 'px');
7520                    KTUtil.css(the.foot, 'margin-top', '-' + height2 + 'px');
7521                }
7522
7523                Plugin.eventTrigger('afterFullscreenOn');
7524            }
7525        },
7526
7527        /**
7528         * Trigger events
7529         */
7530        eventTrigger: function(name) {
7531            //KTUtil.triggerCustomEvent(name);
7532            for (var i = 0; i < the.events.length; i++) {
7533                var event = the.events[i];
7534                if (event.name == name) {
7535                    if (event.one == true) {
7536                        if (event.fired == false) {
7537                            the.events[i].fired = true;
7538                            event.handler.call(this, the);
7539                        }
7540                    } else {
7541                        event.handler.call(this, the);
7542                    }
7543                }
7544            }
7545        },
7546
7547        addEvent: function(name, handler, one) {
7548            the.events.push({
7549                name: name,
7550                handler: handler,
7551                one: one,
7552                fired: false
7553            });
7554
7555            return the;
7556        }
7557    };
7558
7559    //////////////////////////
7560    // ** Public Methods ** //
7561    //////////////////////////
7562
7563    /**
7564     * Set default options
7565     */
7566
7567    the.setDefaults = function(options) {
7568        defaultOptions = options;
7569    };
7570
7571    /**
7572     * Remove portlet
7573     * @returns {KTPortlet}
7574     */
7575    the.remove = function() {
7576        return Plugin.remove(html);
7577    };
7578
7579    /**
7580     * Remove portlet
7581     * @returns {KTPortlet}
7582     */
7583    the.initSticky = function() {
7584        return Plugin.initSticky();
7585    };
7586
7587    /**
7588     * Remove portlet
7589     * @returns {KTPortlet}
7590     */
7591    the.updateSticky = function() {
7592        return Plugin.updateSticky();
7593    };
7594
7595    /**
7596     * Remove portlet
7597     * @returns {KTPortlet}
7598     */
7599    the.resetSticky = function() {
7600        return Plugin.resetSticky();
7601    };
7602
7603	/**
7604	 * Destroy sticky portlet
7605	 */
7606	the.destroySticky = function() {
7607		Plugin.resetSticky();
7608		window.removeEventListener('scroll', Plugin.onScrollSticky);
7609	};
7610
7611    /**
7612     * Reload portlet
7613     * @returns {KTPortlet}
7614     */
7615    the.reload = function() {
7616        return Plugin.reload();
7617    };
7618
7619    /**
7620     * Set portlet content
7621     * @returns {KTPortlet}
7622     */
7623    the.setContent = function(html) {
7624        return Plugin.setContent(html);
7625    };
7626
7627    /**
7628     * Toggle portlet
7629     * @returns {KTPortlet}
7630     */
7631    the.toggle = function() {
7632        return Plugin.toggle();
7633    };
7634
7635    /**
7636     * Collapse portlet
7637     * @returns {KTPortlet}
7638     */
7639    the.collapse = function() {
7640        return Plugin.collapse();
7641    };
7642
7643    /**
7644     * Expand portlet
7645     * @returns {KTPortlet}
7646     */
7647    the.expand = function() {
7648        return Plugin.expand();
7649    };
7650
7651    /**
7652     * Fullscreen portlet
7653     * @returns {MPortlet}
7654     */
7655    the.fullscreen = function() {
7656        return Plugin.fullscreen('on');
7657    };
7658
7659    /**
7660     * Fullscreen portlet
7661     * @returns {MPortlet}
7662     */
7663    the.unFullscreen = function() {
7664        return Plugin.fullscreen('off');
7665    };
7666
7667    /**
7668     * Get portletbody
7669     * @returns {jQuery}
7670     */
7671    the.getBody = function() {
7672        return Plugin.getBody();
7673    };
7674
7675    /**
7676     * Get portletbody
7677     * @returns {jQuery}
7678     */
7679    the.getSelf = function() {
7680        return Plugin.getSelf();
7681    };
7682
7683    /**
7684     * Attach event
7685     */
7686    the.on = function(name, handler) {
7687        return Plugin.addEvent(name, handler);
7688    };
7689
7690    /**
7691     * Attach event that will be fired once
7692     */
7693    the.one = function(name, handler) {
7694        return Plugin.addEvent(name, handler, true);
7695    };
7696
7697    // Construct plugin
7698    Plugin.construct.apply(the, [options]);
7699
7700    return the;
7701};
7702"use strict";
7703var KTScrolltop = function(elementId, options) {
7704    // Main object
7705    var the = this;
7706    var init = false;
7707
7708    // Get element object
7709    var element = KTUtil.get(elementId);
7710    var body = KTUtil.get('body');
7711
7712    if (!element) {
7713        return;
7714    }
7715
7716    // Default options
7717    var defaultOptions = {
7718        offset: 300,
7719        speed: 600,
7720        toggleClass: 'kt-scrolltop--on'
7721    };
7722
7723    ////////////////////////////
7724    // ** Private Methods  ** //
7725    ////////////////////////////
7726
7727    var Plugin = {
7728        /**
7729         * Run plugin
7730         * @returns {mscrolltop}
7731         */
7732        construct: function(options) {
7733            if (KTUtil.data(element).has('scrolltop')) {
7734                the = KTUtil.data(element).get('scrolltop');
7735            } else {
7736                // reset scrolltop
7737                Plugin.init(options);
7738
7739                // build scrolltop
7740                Plugin.build();
7741
7742                KTUtil.data(element).set('scrolltop', the);
7743            }
7744
7745            return the;
7746        },
7747
7748        /**
7749         * Handles subscrolltop click toggle
7750         * @returns {mscrolltop}
7751         */
7752        init: function(options) {
7753            the.events = [];
7754
7755            // merge default and user defined options
7756            the.options = KTUtil.deepExtend({}, defaultOptions, options);
7757        },
7758
7759        build: function() {
7760            // handle window scroll
7761            if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
7762                window.addEventListener('touchend', function() {
7763                    Plugin.handle();
7764                });
7765
7766                window.addEventListener('touchcancel', function() {
7767                    Plugin.handle();
7768                });
7769
7770                window.addEventListener('touchleave', function() {
7771                    Plugin.handle();
7772                });
7773            } else {
7774                window.addEventListener('scroll', function() { 
7775                    Plugin.handle();
7776                });
7777            }
7778
7779            // handle button click 
7780            KTUtil.addEvent(element, 'click', Plugin.scroll);
7781        },
7782
7783        /**
7784         * Handles scrolltop click scrollTop
7785         */
7786        handle: function() {
7787            var pos = window.pageYOffset; // current vertical position
7788            if (pos > the.options.offset) {
7789                KTUtil.addClass(body, the.options.toggleClass);
7790            } else {
7791                KTUtil.removeClass(body, the.options.toggleClass);
7792            }
7793        },
7794
7795        /**
7796         * Handles scrolltop click scrollTop
7797         */
7798        scroll: function(e) {
7799            e.preventDefault();
7800
7801            KTUtil.scrollTop(0, the.options.speed);
7802        },
7803
7804
7805        /**
7806         * Trigger events
7807         */
7808        eventTrigger: function(name, args) {
7809            for (var i = 0; i < the.events.length; i++) {
7810                var event = the.events[i];
7811                if (event.name == name) {
7812                    if (event.one == true) {
7813                        if (event.fired == false) {
7814                            the.events[i].fired = true;
7815                            event.handler.call(this, the, args);
7816                        }
7817                    } else {
7818                        event.handler.call(this, the, args);
7819                    }
7820                }
7821            }
7822        },
7823
7824        addEvent: function(name, handler, one) {
7825            the.events.push({
7826                name: name,
7827                handler: handler,
7828                one: one,
7829                fired: false
7830            });
7831        }
7832    };
7833
7834    //////////////////////////
7835    // ** Public Methods ** //
7836    //////////////////////////
7837
7838    /**
7839     * Set default options 
7840     */
7841
7842    the.setDefaults = function(options) {
7843        defaultOptions = options;
7844    };
7845
7846    /**
7847     * Get subscrolltop mode
7848     */
7849    the.on = function(name, handler) {
7850        return Plugin.addEvent(name, handler);
7851    };
7852
7853    /**
7854     * Set scrolltop content
7855     * @returns {mscrolltop}
7856     */
7857    the.one = function(name, handler) {
7858        return Plugin.addEvent(name, handler, true);
7859    };
7860
7861    ///////////////////////////////
7862    // ** Plugin Construction ** //
7863    ///////////////////////////////
7864
7865    // Run plugin
7866    Plugin.construct.apply(the, [options]);
7867
7868    // Init done
7869    init = true;
7870
7871    // Return plugin instance
7872    return the;
7873};
7874"use strict";
7875
7876// plugin setup
7877var KTToggle = function(elementId, options) {
7878    // Main object
7879    var the = this;
7880    var init = false;
7881
7882    // Get element object
7883    var element = KTUtil.get(elementId);
7884    var body = KTUtil.get('body');  
7885
7886    if (!element) {
7887        return;
7888    }
7889
7890    // Default options
7891    var defaultOptions = {
7892        togglerState: '',
7893        targetState: ''
7894    };    
7895
7896    ////////////////////////////
7897    // ** Private Methods  ** //
7898    ////////////////////////////
7899
7900    var Plugin = {
7901        /**
7902         * Construct
7903         */
7904
7905        construct: function(options) {
7906            if (KTUtil.data(element).has('toggle')) {
7907                the = KTUtil.data(element).get('toggle');
7908            } else {
7909                // reset menu
7910                Plugin.init(options);
7911
7912                // build menu
7913                Plugin.build();
7914
7915                KTUtil.data(element).set('toggle', the);
7916            }
7917
7918            return the;
7919        },
7920
7921        /**
7922         * Handles subtoggle click toggle
7923         */
7924        init: function(options) {
7925            the.element = element;
7926            the.events = [];
7927
7928            // merge default and user defined options
7929            the.options = KTUtil.deepExtend({}, defaultOptions, options);
7930
7931            the.target = KTUtil.get(the.options.target);
7932            the.targetState = the.options.targetState;
7933            the.togglerState = the.options.togglerState;
7934
7935            the.state = KTUtil.hasClasses(the.target, the.targetState) ? 'on' : 'off';
7936        },
7937
7938        /**
7939         * Setup toggle
7940         */
7941        build: function() {
7942            KTUtil.addEvent(element, 'mouseup', Plugin.toggle);
7943        },
7944        
7945        /**
7946         * Handles offcanvas click toggle
7947         */
7948        toggle: function(e) {
7949            Plugin.eventTrigger('beforeToggle');
7950
7951            if (the.state == 'off') {
7952                Plugin.toggleOn();
7953            } else {
7954                Plugin.toggleOff();
7955            }
7956
7957            Plugin.eventTrigger('afterToggle');
7958
7959            e.preventDefault();
7960
7961            return the;
7962        },
7963
7964        /**
7965         * Handles toggle click toggle
7966         */
7967        toggleOn: function() {
7968            Plugin.eventTrigger('beforeOn');
7969
7970            KTUtil.addClass(the.target, the.targetState);
7971
7972            if (the.togglerState) {
7973                KTUtil.addClass(element, the.togglerState);
7974            }
7975
7976            the.state = 'on';
7977
7978            Plugin.eventTrigger('afterOn');
7979
7980            Plugin.eventTrigger('toggle');
7981
7982            return the;
7983        },
7984
7985        /**
7986         * Handles toggle click toggle
7987         */
7988        toggleOff: function() {
7989            Plugin.eventTrigger('beforeOff');
7990
7991            KTUtil.removeClass(the.target, the.targetState);
7992
7993            if (the.togglerState) {
7994                KTUtil.removeClass(element, the.togglerState);
7995            }
7996
7997            the.state = 'off';
7998
7999            Plugin.eventTrigger('afterOff');
8000
8001            Plugin.eventTrigger('toggle');
8002
8003            return the;
8004        },
8005
8006        /**
8007         * Trigger events
8008         */
8009        eventTrigger: function(name) {
8010            for (var i = 0; i < the.events.length; i++) {
8011                var event = the.events[i];
8012
8013                if (event.name == name) {
8014                    if (event.one == true) {
8015                        if (event.fired == false) {
8016                            the.events[i].fired = true;                            
8017                            event.handler.call(this, the);
8018                        }
8019                    } else {
8020                        event.handler.call(this, the);
8021                    }
8022                }
8023            }
8024        },
8025
8026        addEvent: function(name, handler, one) {
8027            the.events.push({
8028                name: name,
8029                handler: handler,
8030                one: one,
8031                fired: false
8032            });
8033
8034            return the;
8035        }
8036    };
8037
8038    //////////////////////////
8039    // ** Public Methods ** //
8040    //////////////////////////
8041
8042    /**
8043     * Set default options 
8044     */
8045
8046    the.setDefaults = function(options) {
8047        defaultOptions = options;
8048    };
8049
8050    /**
8051     * Get toggle state 
8052     */
8053    the.getState = function() {
8054        return the.state;
8055    };
8056
8057    /**
8058     * Toggle 
8059     */
8060    the.toggle = function() {
8061        return Plugin.toggle();
8062    };
8063
8064    /**
8065     * Toggle on 
8066     */
8067    the.toggleOn = function() {
8068        return Plugin.toggleOn();
8069    };
8070
8071    /**
8072     * Toggle off 
8073     */
8074    the.toggleOff = function() {
8075        return Plugin.toggleOff();
8076    };
8077
8078    /**
8079     * Attach event
8080     * @returns {KTToggle}
8081     */
8082    the.on = function(name, handler) {
8083        return Plugin.addEvent(name, handler);
8084    };
8085
8086    /**
8087     * Attach event that will be fired once
8088     * @returns {KTToggle}
8089     */
8090    the.one = function(name, handler) {
8091        return Plugin.addEvent(name, handler, true);
8092    };
8093
8094    // Construct plugin
8095    Plugin.construct.apply(the, [options]);
8096
8097    return the;
8098};
8099// plugin setup
8100var KTWizard = function(elementId, options) {
8101    // Main object
8102    var the = this;
8103    var init = false;
8104
8105    // Get element object
8106    var element = KTUtil.get(elementId);
8107    var body = KTUtil.get('body');
8108
8109    if (!element) {
8110        return; 
8111    }
8112
8113    // Default options
8114    var defaultOptions = {
8115        startStep: 1,
8116        manualStepForward: false
8117    };
8118
8119    ////////////////////////////
8120    // ** Private Methods  ** //
8121    ////////////////////////////
8122
8123    var Plugin = {
8124        /**
8125         * Construct
8126         */
8127
8128        construct: function(options) {
8129            if (KTUtil.data(element).has('wizard')) {
8130                the = KTUtil.data(element).get('wizard');
8131            } else {
8132                // reset menu
8133                Plugin.init(options);
8134
8135                // build menu
8136                Plugin.build();
8137
8138                KTUtil.data(element).set('wizard', the);
8139            }
8140
8141            return the;
8142        },
8143
8144        /**
8145         * Init wizard
8146         */
8147        init: function(options) {
8148            the.element = element;
8149            the.events = [];
8150
8151            // merge default and user defined options
8152            the.options = KTUtil.deepExtend({}, defaultOptions, options);
8153
8154            // Elements
8155            the.steps = KTUtil.findAll(element, '[data-ktwizard-type="step"]');
8156
8157            the.btnSubmit = KTUtil.find(element, '[data-ktwizard-type="action-submit"]');
8158            the.btnNext = KTUtil.find(element, '[data-ktwizard-type="action-next"]');
8159            the.btnPrev = KTUtil.find(element, '[data-ktwizard-type="action-prev"]');
8160            the.btnLast = KTUtil.find(element, '[data-ktwizard-type="action-last"]');
8161            the.btnFirst = KTUtil.find(element, '[data-ktwizard-type="action-first"]');
8162
8163            // Variables
8164            the.events = [];
8165            the.currentStep = 1;
8166            the.stopped = false;
8167            the.totalSteps = the.steps.length;
8168
8169            // Init current step
8170            if (the.options.startStep > 1) {
8171                Plugin.goTo(the.options.startStep);
8172            }
8173
8174            // Init UI
8175            Plugin.updateUI();
8176        },
8177
8178        /**
8179         * Build Form Wizard
8180         */
8181        build: function() {
8182            // Next button event handler
8183            KTUtil.addEvent(the.btnNext, 'click', function(e) {
8184                e.preventDefault();
8185                Plugin.goNext();
8186            });
8187
8188            // Prev button event handler
8189            KTUtil.addEvent(the.btnPrev, 'click', function(e) {
8190                e.preventDefault();
8191                Plugin.goPrev();
8192            });
8193
8194            // First button event handler
8195            KTUtil.addEvent(the.btnFirst, 'click', function(e) {
8196                e.preventDefault();
8197                Plugin.goFirst();
8198            });
8199
8200            // Last button event handler
8201            KTUtil.addEvent(the.btnLast, 'click', function(e) {
8202                e.preventDefault();
8203                Plugin.goLast();
8204            });
8205
8206            KTUtil.on(element, 'a[data-ktwizard-type="step"]', 'click', function() {
8207                var index = KTUtil.index(this) + 1;
8208                if (index !== the.currentStep) {
8209                    Plugin.goTo(index);
8210                }                
8211            });
8212        },
8213
8214        /**
8215         * Handles wizard click wizard
8216         */
8217        goTo: function(number) {
8218            // Skip if this step is already shown
8219            if (number === the.currentStep || number > the.totalSteps || number < 0) {
8220                return;
8221            }
8222
8223            // Validate step number
8224            if (number) {
8225                number = parseInt(number);
8226            } else {
8227                number = Plugin.getNextStep();
8228            }
8229
8230            // Before next and prev events
8231            var callback;
8232
8233            if (number > the.currentStep) {
8234                callback = Plugin.eventTrigger('beforeNext');
8235            } else {
8236                callback = Plugin.eventTrigger('beforePrev');
8237            }
8238            
8239            // Skip if stopped
8240            if (the.stopped === true) {
8241                the.stopped = false;
8242                return;
8243            }
8244
8245            // Continue if no exit
8246            if (callback !== false) {
8247                // Before change
8248                Plugin.eventTrigger('beforeChange');
8249
8250                // Set current step 
8251                the.currentStep = number;
8252
8253                Plugin.updateUI();
8254
8255                // Trigger change event
8256                Plugin.eventTrigger('change');
8257            }
8258
8259            // After next and prev events
8260            if (number > the.startStep) {
8261                Plugin.eventTrigger('afterNext');
8262            } else {
8263                Plugin.eventTrigger('afterPrev');
8264            }
8265
8266            return the;
8267        },
8268
8269        /**
8270         * Cancel
8271         */
8272        stop: function() {
8273            the.stopped = true;
8274        },
8275
8276        /**
8277         * Resume
8278         */
8279        start: function() {
8280            the.stopped = false;
8281        },
8282
8283        /**
8284         * Check last step
8285         */
8286        isLastStep: function() {
8287            return the.currentStep === the.totalSteps;
8288        },
8289
8290        /**
8291         * Check first step
8292         */
8293        isFirstStep: function() {
8294            return the.currentStep === 1;
8295        },
8296
8297        /**
8298         * Check between step
8299         */
8300        isBetweenStep: function() {
8301            return Plugin.isLastStep() === false && Plugin.isFirstStep() === false;
8302        },
8303
8304        /**
8305         * Go to the next step
8306         */
8307        goNext: function() {
8308            return Plugin.goTo(Plugin.getNextStep());
8309        },
8310
8311        /**
8312         * Go to the prev step
8313         */
8314        goPrev: function() {
8315            return Plugin.goTo(Plugin.getPrevStep());
8316        },
8317
8318        /**
8319         * Go to the last step
8320         */
8321        goLast: function() {
8322            return Plugin.goTo(the.totalSteps);
8323        },
8324
8325        /**
8326         * Go to the first step
8327         */
8328        goFirst: function() {
8329            return Plugin.goTo(1);
8330        },
8331
8332        /**
8333         * Go to the first step
8334         */
8335        updateUI: function() {
8336            var stepType = '';
8337            var index = the.currentStep - 1;
8338
8339            if (Plugin.isLastStep()) {
8340                stepType = 'last';
8341            } else if (Plugin.isFirstStep()) {
8342                stepType = 'first';
8343            } else {
8344                stepType = 'between';
8345            }
8346
8347            KTUtil.attr(the.element, 'data-ktwizard-state', stepType);
8348
8349            // Steps
8350            var steps = KTUtil.findAll(the.element, '[data-ktwizard-type="step"]');
8351
8352            if (steps && steps.length > 0) {
8353                for (var i = 0, len = steps.length; i < len; i++) {
8354                    if (i == index) {
8355                        KTUtil.attr(steps[i], 'data-ktwizard-state', 'current');
8356                    } else {
8357                        if (i < index) {
8358                            KTUtil.attr(steps[i], 'data-ktwizard-state', 'done');
8359                        } else {
8360                            KTUtil.attr(steps[i], 'data-ktwizard-state', 'pending');
8361                        }
8362                    }
8363                }
8364            }
8365
8366            // Steps Info
8367            var stepsInfo = KTUtil.findAll(the.element, '[data-ktwizard-type="step-info"]');
8368            if (stepsInfo &&stepsInfo.length > 0) {
8369                for (var i = 0, len = stepsInfo.length; i < len; i++) {
8370                    if (i == index) {
8371                        KTUtil.attr(stepsInfo[i], 'data-ktwizard-state', 'current');
8372                    } else {
8373                        KTUtil.removeAttr(stepsInfo[i], 'data-ktwizard-state');
8374                    }
8375                }
8376            }  
8377
8378            // Steps Content
8379            var stepsContent = KTUtil.findAll(the.element, '[data-ktwizard-type="step-content"]');
8380            if (stepsContent&& stepsContent.length > 0) {
8381                for (var i = 0, len = stepsContent.length; i < len; i++) {
8382                    if (i == index) {
8383                        KTUtil.attr(stepsContent[i], 'data-ktwizard-state', 'current');
8384                    } else {
8385                        KTUtil.removeAttr(stepsContent[i], 'data-ktwizard-state');
8386                    }
8387                }
8388            }            
8389        },
8390
8391        /**
8392         * Get next step
8393         */
8394        getNextStep: function() {
8395            if (the.totalSteps >= (the.currentStep + 1)) {
8396                return the.currentStep + 1;
8397            } else {
8398                return the.totalSteps;
8399            }
8400        },
8401
8402        /**
8403         * Get prev step
8404         */
8405        getPrevStep: function() {
8406            if ((the.currentStep - 1) >= 1) {
8407                return the.currentStep - 1;
8408            } else {
8409                return 1;
8410            }
8411        },
8412
8413        /**
8414         * Trigger events
8415         */
8416        eventTrigger: function(name) {
8417            //KTUtil.triggerCustomEvent(name);
8418            for (var i = 0; i < the.events.length; i++) {
8419                var event = the.events[i];
8420                if (event.name == name) {
8421                    if (event.one == true) {
8422                        if (event.fired == false) {
8423                            the.events[i].fired = true;
8424                            event.handler.call(this, the);
8425                        }
8426                    } else {
8427                        event.handler.call(this, the);
8428                    }
8429                }
8430            }
8431        },
8432
8433        addEvent: function(name, handler, one) {
8434            the.events.push({
8435                name: name,
8436                handler: handler,
8437                one: one,
8438                fired: false
8439            });
8440
8441            return the;
8442        }
8443    };
8444
8445    //////////////////////////
8446    // ** Public Methods ** //
8447    //////////////////////////
8448
8449    /**
8450     * Set default options 
8451     */
8452
8453    the.setDefaults = function(options) {
8454        defaultOptions = options;
8455    };
8456
8457    /**
8458     * Go to the next step 
8459     */
8460    the.goNext = function() {
8461        return Plugin.goNext();
8462    };
8463
8464    /**
8465     * Go to the prev step 
8466     */
8467    the.goPrev = function() {
8468        return Plugin.goPrev();
8469    };
8470
8471    /**
8472     * Go to the last step 
8473     */
8474    the.goLast = function() {
8475        return Plugin.goLast();
8476    };
8477
8478    /**
8479     * Cancel step 
8480     */
8481    the.stop = function() {
8482        return Plugin.stop();
8483    };
8484
8485    /**
8486     * Resume step 
8487     */
8488    the.start = function() {
8489        return Plugin.start();
8490    };
8491
8492    /**
8493     * Go to the first step 
8494     */
8495    the.goFirst = function() {
8496        return Plugin.goFirst();
8497    };
8498
8499    /**
8500     * Go to a step
8501     */
8502    the.goTo = function(number) {
8503        return Plugin.goTo(number);
8504    };
8505
8506    /**
8507     * Get current step number 
8508     */
8509    the.getStep = function() {
8510        return the.currentStep;
8511    };
8512
8513    /**
8514     * Check last step 
8515     */
8516    the.isLastStep = function() {
8517        return Plugin.isLastStep();
8518    };
8519
8520    /**
8521     * Check first step 
8522     */
8523    the.isFirstStep = function() {
8524        return Plugin.isFirstStep();
8525    };
8526    
8527    /**
8528     * Attach event
8529     */
8530    the.on = function(name, handler) {
8531        return Plugin.addEvent(name, handler);
8532    };
8533
8534    /**
8535     * Attach event that will be fired once
8536     */
8537    the.one = function(name, handler) {
8538        return Plugin.addEvent(name, handler, true);
8539    };
8540
8541    // Construct plugin
8542    Plugin.construct.apply(the, [options]);
8543
8544    return the;
8545};
8546// plugin setup
8547var KTAvatar = function(elementId, options) {
8548    // Main object
8549    var the = this;
8550    var init = false;
8551
8552    // Get element object
8553    var element = KTUtil.get(elementId);
8554    var body = KTUtil.get('body');
8555
8556    if (!element) {
8557        return; 
8558    }
8559
8560    // Default options
8561    var defaultOptions = {
8562    };
8563
8564    ////////////////////////////
8565    // ** Private Methods  ** //
8566    ////////////////////////////
8567
8568    var Plugin = {
8569        /**
8570         * Construct
8571         */
8572
8573        construct: function(options) {
8574            if (KTUtil.data(element).has('avatar')) {
8575                the = KTUtil.data(element).get('avatar');
8576            } else {
8577                // reset menu
8578                Plugin.init(options);
8579
8580                // build menu
8581                Plugin.build();
8582
8583                KTUtil.data(element).set('avatar', the);
8584            }
8585
8586            return the;
8587        },
8588
8589        /**
8590         * Init avatar
8591         */
8592        init: function(options) {
8593            the.element = element;
8594            the.events = [];
8595
8596            the.input = KTUtil.find(element, 'input[type="file"]');
8597            the.holder = KTUtil.find(element, '.kt-avatar__holder');
8598            the.cancel = KTUtil.find(element, '.kt-avatar__cancel');
8599            the.src = KTUtil.css(the.holder, 'backgroundImage');
8600
8601            // merge default and user defined options
8602            the.options = KTUtil.deepExtend({}, defaultOptions, options);
8603        },
8604
8605        /**
8606         * Build Form Wizard
8607         */
8608        build: function() {
8609            // Handle avatar change
8610            KTUtil.addEvent(the.input, 'change', function(e) {
8611                e.preventDefault();
8612
8613	            if (the.input && the.input.files && the.input.files[0]) {
8614	                var reader = new FileReader();
8615	                reader.onload = function(e) {
8616	                    KTUtil.css(the.holder, 'background-image', 'url('+e.target.result +')');
8617	                }
8618	                reader.readAsDataURL(the.input.files[0]);
8619
8620	                KTUtil.addClass(the.element, 'kt-avatar--changed');
8621	            }
8622            });
8623
8624            // Handle avatar cancel
8625            KTUtil.addEvent(the.cancel, 'click', function(e) {
8626                e.preventDefault();
8627
8628	            KTUtil.removeClass(the.element, 'kt-avatar--changed');
8629	            KTUtil.css(the.holder, 'background-image', the.src);
8630	            the.input.value = "";
8631            });
8632        },
8633
8634        /**
8635         * Trigger events
8636         */
8637        eventTrigger: function(name) {
8638            //KTUtil.triggerCustomEvent(name);
8639            for (var i = 0; i < the.events.length; i++) {
8640                var event = the.events[i];
8641                if (event.name == name) {
8642                    if (event.one == true) {
8643                        if (event.fired == false) {
8644                            the.events[i].fired = true;
8645                            event.handler.call(this, the);
8646                        }
8647                    } else {
8648                        event.handler.call(this, the);
8649                    }
8650                }
8651            }
8652        },
8653
8654        addEvent: function(name, handler, one) {
8655            the.events.push({
8656                name: name,
8657                handler: handler,
8658                one: one,
8659                fired: false
8660            });
8661
8662            return the;
8663        }
8664    };
8665
8666    //////////////////////////
8667    // ** Public Methods ** //
8668    //////////////////////////
8669
8670    /**
8671     * Set default options 
8672     */
8673
8674    the.setDefaults = function(options) {
8675        defaultOptions = options;
8676    };
8677    
8678    /**
8679     * Attach event
8680     */
8681    the.on = function(name, handler) {
8682        return Plugin.addEvent(name, handler);
8683    };
8684
8685    /**
8686     * Attach event that will be fired once
8687     */
8688    the.one = function(name, handler) {
8689        return Plugin.addEvent(name, handler, true);
8690    };
8691
8692    // Construct plugin
8693    Plugin.construct.apply(the, [options]);
8694
8695    return the;
8696};
8697"use strict";
8698
8699var KTLayout = function() {
8700    var body;
8701
8702    var header;
8703    var headerMenu;
8704    var headerMenuOffcanvas;
8705
8706    var asideMenu;
8707    var asideMenuOffcanvas;
8708    var asideToggler;
8709
8710    var asideSecondary;
8711    var asideSecondaryToggler;
8712
8713    var scrollTop;
8714
8715    var pageStickyPortlet;
8716
8717    // Header
8718    var initHeader = function() {
8719        var tmp;
8720        var headerEl = KTUtil.get('kt_header');
8721        var options = {
8722            offset: {},
8723            minimize: {
8724                /*
8725                desktop: {
8726                    on: 'kt-header--minimize'
8727                },
8728                */
8729                desktop: false,
8730                mobile: false
8731            }
8732        };
8733
8734        if (tmp = KTUtil.attr(headerEl, 'data-ktheader-minimize-offset')) {
8735            options.offset.desktop = tmp;
8736        }
8737
8738        if (tmp = KTUtil.attr(headerEl, 'data-ktheader-minimize-mobile-offset')) {
8739            options.offset.mobile = tmp;
8740        }
8741
8742        header = new KTHeader('kt_header', options);
8743    }
8744
8745    // Header Menu
8746    var initHeaderMenu = function() {
8747        // init aside left offcanvas
8748        headerMenuOffcanvas = new KTOffcanvas('kt_header_menu_wrapper', {
8749            overlay: true,
8750            baseClass: 'kt-header-menu-wrapper',
8751            closeBy: 'kt_header_menu_mobile_close_btn',
8752            toggleBy: {
8753                target: 'kt_header_mobile_toggler',
8754                state: 'kt-header-mobile__toolbar-toggler--active'
8755            }
8756        });
8757
8758        headerMenu = new KTMenu('kt_header_menu', {
8759            submenu: {
8760                desktop: 'dropdown',
8761                tablet: 'accordion',
8762                mobile: 'accordion'
8763            },
8764            accordion: {
8765                slideSpeed: 200, // accordion toggle slide speed in milliseconds
8766                expandAll: false // allow having multiple expanded accordions in the menu
8767            }
8768        });
8769    }
8770
8771    // Header Topbar
8772    var initHeaderTopbar = function() {
8773        asideToggler = new KTToggle('kt_header_mobile_topbar_toggler', {
8774            target: 'body',
8775            targetState: 'kt-header__topbar--mobile-on',
8776            togglerState: 'kt-header-mobile__toolbar-topbar-toggler--active'
8777        });
8778    }
8779
8780    // Aside
8781    var initAside = function() {
8782        // init aside left offcanvas
8783        var asidBrandHover = false;
8784        var aside = KTUtil.get('kt_aside');
8785        var asideBrand = KTUtil.get('kt_aside_brand');
8786        var asideOffcanvasClass = KTUtil.hasClass(aside, 'kt-aside--offcanvas-default') ? 'kt-aside--offcanvas-default' : 'kt-aside';
8787
8788        asideMenuOffcanvas = new KTOffcanvas('kt_aside', {
8789            baseClass: asideOffcanvasClass,
8790            overlay: true,
8791            closeBy: 'kt_aside_close_btn',
8792            toggleBy: {
8793                target: 'kt_aside_mobile_toggler',
8794                state: 'kt-header-mobile__toolbar-toggler--active'
8795            }
8796        });
8797
8798        // Handle minimzied aside hover
8799        if (KTUtil.hasClass(body, 'kt-aside--fixed')) {
8800            var insideTm;
8801            var outsideTm;
8802
8803            KTUtil.addEvent(aside, 'mouseenter', function(e) {
8804                e.preventDefault();
8805
8806                if (KTUtil.isInResponsiveRange('desktop') === false) {
8807                    return;
8808                }
8809
8810                if (outsideTm) {
8811                    clearTimeout(outsideTm);
8812                    outsideTm = null;
8813                }
8814
8815                insideTm = setTimeout(function() {
8816                    if (KTUtil.hasClass(body, 'kt-aside--minimize') && KTUtil.isInResponsiveRange('desktop')) {
8817                        KTUtil.removeClass(body, 'kt-aside--minimize');
8818                        
8819                        // Minimizing class
8820                        KTUtil.addClass(body, 'kt-aside--minimizing');
8821                        KTUtil.transitionEnd(body, function() {
8822                            KTUtil.removeClass(body, 'kt-aside--minimizing');
8823                        });
8824
8825                        // Hover class
8826                        KTUtil.addClass(body, 'kt-aside--minimize-hover');
8827                        asideMenu.scrollUpdate();
8828                        asideMenu.scrollTop();
8829                    }
8830                }, 50);
8831            });
8832
8833            KTUtil.addEvent(aside, 'mouseleave', function(e) {
8834                e.preventDefault();
8835
8836                if (KTUtil.isInResponsiveRange('desktop') === false) {
8837                    return;
8838                }
8839
8840                if (insideTm) {
8841                    clearTimeout(insideTm);
8842                    insideTm = null;
8843                }
8844
8845                outsideTm = setTimeout(function() {
8846                    if (KTUtil.hasClass(body, 'kt-aside--minimize-hover') && KTUtil.isInResponsiveRange('desktop')) {
8847                        KTUtil.removeClass(body, 'kt-aside--minimize-hover');
8848                        KTUtil.addClass(body, 'kt-aside--minimize');
8849
8850                        // Minimizing class
8851                        KTUtil.addClass(body, 'kt-aside--minimizing');
8852                        KTUtil.transitionEnd(body, function() {
8853                            KTUtil.removeClass(body, 'kt-aside--minimizing');
8854                        });
8855
8856                        // Hover class
8857                        asideMenu.scrollUpdate();
8858                        asideMenu.scrollTop();
8859                    }
8860                }, 100);
8861            });
8862        }
8863    }
8864
8865    // Aside menu
8866    var initAsideMenu = function() {
8867        // Init aside menu
8868        var menu = KTUtil.get('kt_aside_menu');
8869        var menuDesktopMode = (KTUtil.attr(menu, 'data-ktmenu-dropdown') === '1' ? 'dropdown' : 'accordion');
8870
8871        var scroll;
8872        if (KTUtil.attr(menu, 'data-ktmenu-scroll') === '1') {
8873            scroll = {
8874                height: function() {
8875                    var height;
8876
8877                    if (KTUtil.isInResponsiveRange('desktop')) {
8878                        height =  
8879                            parseInt(KTUtil.getViewPort().height) - 
8880                            parseInt(KTUtil.actualHeight('kt_aside_brand')) -
8881                            parseInt(KTUtil.getByID('kt_aside_footer') ? KTUtil.actualHeight('kt_aside_footer') : 0);
8882                    } else {
8883                        height =  
8884                            parseInt(KTUtil.getViewPort().height) - 
8885                            parseInt(KTUtil.getByID('kt_aside_footer') ? KTUtil.actualHeight('kt_aside_footer') : 0);
8886                    }
8887
8888                    height = height - (parseInt(KTUtil.css(menu, 'marginBottom')) + parseInt(KTUtil.css(menu, 'marginTop')));
8889
8890                    return height;
8891                }
8892            };
8893        }
8894
8895        asideMenu = new KTMenu('kt_aside_menu', {
8896            // vertical scroll
8897            scroll: scroll,
8898
8899            // submenu setup
8900            submenu: {
8901                desktop: menuDesktopMode,
8902                tablet: 'accordion', // menu set to accordion in tablet mode
8903                mobile: 'accordion' // menu set to accordion in mobile mode
8904            },
8905
8906            //accordion setup
8907            accordion: {
8908                expandAll: false // allow having multiple expanded accordions in the menu
8909            }
8910        });
8911    }
8912
8913    // Sidebar toggle
8914    var initAsideToggler = function() {
8915        if (!KTUtil.get('kt_aside_toggler')) {
8916            return;
8917        }
8918
8919        asideToggler = new KTToggle('kt_aside_toggler', {
8920            target: 'body',
8921            targetState: 'kt-aside--minimize',
8922            togglerState: 'kt-aside__brand-aside-toggler--active'
8923        }); 
8924
8925        asideToggler.on('toggle', function(toggle) {  
8926            KTUtil.addClass(body, 'kt-aside--minimizing');
8927
8928            if (KTUtil.get('kt_page_portlet')) {
8929                pageStickyPortlet.updateSticky();      
8930            } 
8931
8932            KTUtil.transitionEnd(body, function() {
8933                KTUtil.removeClass(body, 'kt-aside--minimizing');
8934            });
8935
8936            headerMenu.pauseDropdownHover(800);
8937            asideMenu.pauseDropdownHover(800);
8938
8939            // Remember state in cookie
8940            Cookies.set('kt_aside_toggle_state', toggle.getState());
8941            // to set default minimized left aside use this cookie value in y
8941our 
8942            // server side code and add "kt-brand--minimize kt-aside--minimize" classes to
8943            // the body tag in order to initialize the minimized left aside mode during page loading.
8944        });
8945
8946        asideToggler.on('beforeToggle', function(toggle) {   
8947            var body = KTUtil.get('body'); 
8948            if (KTUtil.hasClass(body, 'kt-aside--minimize') === false && KTUtil.hasClass(body, 'kt-aside--minimize-hover')) {
8949                KTUtil.removeClass(body, 'kt-aside--minimize-hover');
8950            }
8951        });
8952    }
8953
8954    // Aside secondary
8955    var initAsideSecondary = function() {
8956        if (!KTUtil.get('kt_aside_secondary')) {
8957            return;
8958        }
8959
8960        asideSecondaryToggler = new KTToggle('kt_aside_secondary_toggler', {
8961            target: 'body',
8962            targetState: 'kt-aside-secondary--expanded'
8963        });
8964
8965        asideSecondaryToggler.on('toggle', function(toggle) {
8966            if (KTUtil.get('kt_page_portlet')) {
8967                pageStickyPortlet.updateSticky();      
8968            } 
8969        });
8970    }
8971
8972    // Scrolltop
8973    var initScrolltop = function() {
8974        var scrolltop = new KTScrolltop('kt_scrolltop', {
8975            offset: 300,
8976            speed: 600
8977        });
8978    }
8979
8980    // Init page sticky portlet
8981    var initPageStickyPortlet = function() {
8982        var asideWidth = 255;
8983        var asideMinimizeWidth = 78;
8984        var asideSecondaryWidth = 60;
8985        var asideSecondaryExpandedWidth = 310;
8986
8987        return new KTPortlet('kt_page_portlet', {
8988            sticky: {
8989                offset: parseInt(KTUtil.css( KTUtil.get('kt_header'), 'height')),
8990                zIndex: 90,
8991                position: {
8992                    top: function() {
8993                        var h = 0;
8994
8995                        if (KTUtil.isInResponsiveRange('desktop')) {
8996                            if (KTUtil.hasClass(body, 'kt-header--fixed')) {
8997                                h = h + parseInt(KTUtil.css( KTUtil.get('kt_header'), 'height') );
8998                            }
8999                            
9000                            if (KTUtil.hasClass(body, 'kt-subheader--fixed') && KTUtil.get('kt_subheader')) {
9001                                h = h + parseInt(KTUtil.css( KTUtil.get('kt_subheader'), 'height') );
9002                            }
9003                        } else {
9004                            if (KTUtil.hasClass(body, 'kt-header-mobile--fixed')) {
9005                                h = h + parseInt(KTUtil.css( KTUtil.get('kt_header_mobile'), 'height') );
9006                            }
9007                        }    
9008                        
9009                        return h;                        
9010                    },
9011                    left: function() {
9012                        var left = 0;
9013
9014                        if (KTUtil.isInResponsiveRange('desktop')) {
9015                            if (KTUtil.hasClass(body, 'kt-aside--minimize')) {
9016                                left += asideMinimizeWidth;
9017                            } else if (KTUtil.hasClass(body, 'kt-aside--enabled')) {
9018                                left += asideWidth;
9019                            }
9020                        }
9021
9022                        left += parseInt(KTUtil.css( KTUtil.get('kt_content'), 'paddingLeft'));
9023
9024                        return left; 
9025                    },
9026                    right: function() {
9027                        var right = 0;
9028
9029                        if (KTUtil.isInResponsiveRange('desktop')) {                            
9030                            if (KTUtil.hasClass(body, 'kt-aside-secondary--enabled')) {
9031                                if (KTUtil.hasClass(body, 'kt-aside-secondary--expanded')) {
9032                                    right += asideSecondaryExpandedWidth + asideSecondaryWidth;
9033                                } else {
9034                                    right += asideSecondaryWidth; 
9035                                }
9036                            } else {
9037                                right += parseInt(KTUtil.css( KTUtil.get('kt_content'), 'paddingRight'));
9038                            }
9039                        }
9040
9041                        if (KTUtil.get('kt_aside_secondary')) {
9042                            right += parseInt(KTUtil.css( KTUtil.get('kt_content'), 'paddingRight') );
9043                        }
9044
9045                        return right;
9046                    }
9047                }
9048            }
9049        });
9050    }
9051
9052    return {
9053        init: function() {
9054            body = KTUtil.get('body');
9055
9056            this.initHeader();
9057            this.initAside();
9058            this.initAsideSecondary();
9059            this.initPageStickyPortlet();
9060
9061            // Non functional links notice(can be removed in production)
9062            $('#kt_aside_menu, #kt_header_menu').on('click', '.kt-menu__link[href="#"]', function(e) {
9063                swal.fire("", "You have clicked on a non-functional dummy link!");
9064
9065                e.preventDefault();
9066            });
9067        },
9068
9069        initHeader: function() {
9070            initHeader();
9071            initHeaderMenu();
9072            initHeaderTopbar();
9073            initScrolltop();
9074        },
9075
9076        initAside: function() { 
9077            initAside();
9078            initAsideMenu();
9079            initAsideToggler();
9080            
9081            this.onAsideToggle(function(e) {
9082                // Update sticky portlet
9083                if (pageStickyPortlet) {
9084                    pageStickyPortlet.updateSticky();
9085                }
9086
9087                // Reload datatable
9088                var datatables = $('.kt-datatable');
9089                if (datatables) {
9090                    datatables.each(function() {
9091                        $(this).KTDatatable('redraw');
9092                    });
9093                }                
9094            });
9095        },
9096
9097        initAsideSecondary: function() { 
9098            initAsideSecondary();
9099        },
9100
9101        initPageStickyPortlet: function() {
9102            if (!KTUtil.get('kt_page_portlet')) {
9103                return;
9104            }
9105            
9106            pageStickyPortlet = initPageStickyPortlet();
9107            pageStickyPortlet.initSticky();
9108            
9109            KTUtil.addResizeHandler(function(){
9110                pageStickyPortlet.updateSticky();
9111            });
9112
9113            initPageStickyPortlet();
9114        },
9115
9116        getAsideMenu: function() {
9117            return asideMenu;
9118        },
9119
9120        onAsideToggle: function(handler) {
9121            if (typeof asideToggler.element !== 'undefined') {
9122                asideToggler.on('toggle', handler);
9123            }
9124        },
9125
9126        getAsideToggler: function() {
9127            return asideToggler;
9128        },
9129
9130        openAsideSecondary: function() {
9131            asideSecondaryToggler.toggleOn();
9132        },
9133
9134        closeAsideSecondary: function() {
9135            asideSecondaryToggler.toggleOff();
9136        },
9137
9138        getAsideSecondaryToggler: function() {
9139            return asideSecondaryToggler;
9140        },
9141
9142        onAsideSecondaryToggle: function(handler) {
9143            if (asideSecondaryToggler) {
9144                asideSecondaryToggler.on('toggle', handler);
9145            }
9146        },
9147
9148        closeMobileAsideMenuOffcanvas: function() {
9149            if (KTUtil.isMobileDevice()) {
9150                asideMenuOffcanvas.hide();
9151            }
9152        },
9153
9154        closeMobileHeaderMenuOffcanvas: function() {
9155            if (KTUtil.isMobileDevice()) {
9156                headerMenuOffcanvas.hide();
9157            }
9158        }
9159    };
9160}();
9161
9162$(document).ready(function() {
9163    KTLayout.init();
9164});

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.