PageSourceSearch

https://portal.aspi.mk/js/scripts.bundle.js

js aspi.mk collected 2026-10-02 10:59:40 UTC 307,579 bytes, 9,965 lines download raw bytes

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

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.