PageSourceSearch

https://reservation.kapmer.fr/billetterie/assets/js/scripts.bundle.js

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

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.