PageSourceSearch

https://ommolketab.ir/public/assets/js/scripts.bundle.js

js ommolketab.ir collected 2026-10-02 19:58:11 UTC 313,938 bytes, 9,857 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            var duration = duration ? duration : 500;
4402            var 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 (targetPos > scrollPos) {
4408                from = targetPos;
4409                to = scrollPos;
4410            } else {
4411                from = scrollPos;
4412                to = targetPos;
4413            }
4414
4415            if (offset) {
4416                to += offset;
4417            }
4418
4419            KTUtil.animate(from, to, duration, function(value) {
4420                document.documentElement.scrollTop = value;
4421                document.body.parentNode.scrollTop = value;
4422                document.body.scrollTop = value;
4423            }); //, easing, done
4424        },
4425
4426        scrollTop: function(offset, duration) {
4427            KTUtil.scrollTo(null, offset, duration);
4428        },
4429
4430        isArray: function(obj) {
4431            return obj && Array.isArray(obj);
4432        },
4433
4434        ready: function(callback) {
4435            if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading") {
4436                callback();
4437            } else {
4438                document.addEventListener('DOMContentLoaded', callback);
4439            }
4440        },
4441
4442        isEmpty: function(obj) {
4443            for (var prop in obj) {
4444                if (obj.hasOwnProperty(prop)) {
4445                    return false;
4446                }
4447            }
4448
4449            return true;
4450        },
4451
4452        numberString: function(nStr) {
4453            nStr += '';
4454            var x = nStr.split('.');
4455            var x1 = x[0];
4456            var x2 = x.length > 1 ? '.' + x[1] : '';
4457            var rgx = /(\d+)(\d{3})/;
4458            while (rgx.test(x1)) {
4459                x1 = x1.replace(rgx, '$1' + ',' + '$2');
4460            }
4461            return x1 + x2;
4462        },
4463
4464        detectIE: function() {
4465            var ua = window.navigator.userAgent;
4466
4467            // Test values; Uncomment to check result …
4468
4469            // IE 10
4470            // ua = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)';
4471
4472            // IE 11
4473            // ua = 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko';
4474
4475            // Edge 12 (Spartan)
4476            // 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';
4477
4478            // Edge 13
4479            // 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';
4480
4481            var msie = ua.indexOf('MSIE ');
4482            if (msie > 0) {
4483                // IE 10 or older => return version number
4484                return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
4485            }
4486
4487            var trident = ua.indexOf('Trident/');
4488            if (trident > 0) {
4489                // IE 11 => return version number
4490                var rv = ua.indexOf('rv:');
4491                return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
4492            }
4493
4494            var edge = ua.indexOf('Edge/');
4495            if (edge > 0) {
4496                // Edge (IE 12+) => return version number
4497                return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
4498            }
4499
4500            // other browser
4501            return false;
4502        },
4503
4504        isRTL: function() {
4505            return (KTUtil.attr(KTUtil.get('html'), 'direction') == 'rtl');
4506        },
4507
4508        //
4509
4510        // Scroller
4511        scrollInit: function(element, options) {
4512            if(!element) return;
4513            // Define init function
4514            function init() {
4515                var ps;
4516                var height;
4517
4518                if (options.height instanceof Function) {
4519                    height = parseInt(options.height.call());
4520                } else {
4521                    height = parseInt(options.height);
4522                }
4523
4524                // Destroy scroll on table and mobile modes
4525                if ((options.mobileNativeScroll || options.disableForMobile) && KTUtil.isInResponsiveRange('tablet-and-mobile')) {
4526                    ps = KTUtil.data(element).get('ps');
4527                    if (ps) {
4528                        if (options.resetHeightOnDestroy) {
4529                            KTUtil.css(element, 'height', 'auto');
4530                        } else {
4531                            KTUtil.css(element, 'overflow', 'auto');
4532                            if (height > 0) {
4533                                KTUtil.css(element, 'height', height + 'px');
4534                            }
4535                        }
4536
4537                        ps.destroy();
4538                        ps = KTUtil.data(element).remove('ps');
4539                    } else if (height > 0){
4540                        KTUtil.css(element, 'overflow', 'auto');
4541                        KTUtil.css(element, 'height', height + 'px');
4542                    }
4543
4544                    return;
4545                }
4546
4547                if (height > 0) {
4548                    KTUtil.css(element, 'height', height + 'px');
4549                }
4550
4551                if (options.desktopNativeScroll) {
4552                    KTUtil.css(element, 'overflow', 'auto');
4553                    return;
4554                }
4555
4556                // Init scroll
4557                KTUtil.css(element, 'overflow', 'hidden');
4558
4559                ps = KTUtil.data(element).get('ps');
4560                if (ps) {
4561                    ps.update();
4562                } else {
4563                    KTUtil.addClass(element, 'kt-scroll');
4564                    ps = new PerfectScrollbar(element, {
4565                        wheelSpeed: 0.5,
4566                        swipeEasing: true,
4567                        wheelPropagation: (options.windowScroll === false ? false : true),
4568                        minScrollbarLength: 40,
4569                        maxScrollbarLength: 300,
4570                        suppressScrollX: KTUtil.attr(element, 'data-scroll-x') != 'true' ? true : false
4571                    });
4572
4573                    KTUtil.data(element).set('ps', ps);
4574                }
4575
4576                // Remember scroll position in cookie
4577                var uid = KTUtil.attr(element, 'id');
4578
4579                if (options.rememberPosition === true && Cookies && uid) {
4580                    if (Cookies.get(uid)) {
4581                        var pos = parseInt(Cookies.get(uid));
4582
4583                        if (pos > 0) {
4584                            element.scrollTop = pos;
4585                        }
4586                    }
4587
4588                    element.addEventListener('ps-scroll-y', function() {
4589                        Cookies.set(uid, element.scrollTop);
4590                    });
4591                }
4592            }
4593
4594            // Init
4595            init();
4596
4597            // Handle window resize
4598            if (options.handleWindowResize) {
4599                KTUtil.addResizeHandler(function() {
4600                    init();
4601                });
4602            }
4603        },
4604
4605        scrollUpdate: function(element) {
4606            var ps = KTUtil.data(element).get('ps');
4607            if (ps) {
4608                ps.update();
4609            }
4610        },
4611
4612        scrollUpdateAll: function(parent) {
4613            var scrollers = KTUtil.findAll(parent, '.ps');
4614            for (var i = 0, len = scrollers.length; i < len; i++) {
4615                KTUtil.scrollUpdate(scrollers[i]);
4616            }
4617        },
4618
4619        scrollDestroy: function(element) {
4620            var ps = KTUtil.data(element).get('ps');
4621            if (ps) {
4622                ps.destroy();
4623                ps = KTUtil.data(element).remove('ps');
4624            }
4625        },
4626
4627        setHTML: function(el, html) {
4628            if (KTUtil.get(el)) {
4629                KTUtil.get(el).innerHTML = html;
4630            }
4631        },
4632
4633        getHTML: function(el) {
4634            if (KTUtil.get(el)) {
4635                return KTUtil.get(el).innerHTML;
4636            }
4637        },
4638
4639        getDocumentHeight: function() {
4640            var body = document.body;
4641            var html = document.documentElement;
4642
4643            return Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight );
4644        },
4645
4646        getScrollTop: function() {
4647            return  (document.scrollingElement || document.documentElement).scrollTop;
4648        }
4649    }
4650}();
4651
4652// webpack support
4653if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
4654    module.exports = KTUtil;
4655}
4656
4657// Initialize KTUtil class on document ready
4658KTUtil.ready(function() {
4659    KTUtil.init();
4660});
4661
4662// CSS3 Transitions only after page load(.kt-page-loading class added to body tag and remove with JS on page load)
4663window.onload = function() {
4664    KTUtil.removeClass(KTUtil.get('body'), 'kt-page--loading');
4665}
4666
4667// plugin setup
4668var KTWizard = function(elementId, options) {
4669    // Main object
4670    var the = this;
4671    var init = false;
4672
4673    // Get element object
4674    var element = KTUtil.get(elementId);
4675    var body = KTUtil.get('body');
4676
4677    if (!element) {
4678        return;
4679    }
4680
4681    // Default options
4682    var defaultOptions = {
4683        startStep: 1,
4684        clickableSteps: true
4685    };
4686
4687    ////////////////////////////
4688    // ** Private Methods  ** //
4689    ////////////////////////////
4690
4691    var Plugin = {
4692        /**
4693         * Construct
4694         */
4695
4696        construct: function(options) {
4697            if (KTUtil.data(element).has('wizard')) {
4698                the = KTUtil.data(element).get('wizard');
4699            } else {
4700                // reset menu
4701                Plugin.init(options);
4702
4703                // build menu
4704                Plugin.build();
4705
4706                KTUtil.data(element).set('wizard', the);
4707            }
4708
4709            return the;
4710        },
4711
4712        /**
4713         * Init wizard
4714         */
4715        init: function(options) {
4716            the.element = element;
4717            the.events = [];
4718
4719            // merge default and user defined options
4720            the.options = KTUtil.deepExtend({}, defaultOptions, options);
4721
4722            // Elements
4723            the.steps = KTUtil.findAll(element, '[data-ktwizard-type="step"]');
4724
4725            the.btnSubmit = KTUtil.find(element, '[data-ktwizard-type="action-submit"]');
4726            the.btnNext = KTUtil.find(element, '[data-ktwizard-type="action-next"]');
4727            the.btnPrev = KTUtil.find(element, '[data-ktwizard-type="action-prev"]');
4728            the.btnLast = KTUtil.find(element, '[data-ktwizard-type="action-last"]');
4729            the.btnFirst = KTUtil.find(element, '[data-ktwizard-type="action-first"]');
4730
4731            // Variables
4732            the.events = [];
4733            the.currentStep = 1;
4734            the.stopped = false;
4735            the.totalSteps = the.steps.length;
4736
4737            // Init current step
4738            if (the.options.startStep > 1) {
4739                Plugin.goTo(the.options.startStep);
4740            }
4741
4742            // Init UI
4743            Plugin.updateUI();
4744        },
4745
4746        /**
4747         * Build Form Wizard
4748         */
4749        build: function() {
4750            // Next button event handler
4751            KTUtil.addEvent(the.btnNext, 'click', function(e) {
4752                e.preventDefault();
4753                Plugin.goTo(Plugin.getNextStep(), true);
4754            });
4755
4756            // Prev button event handler
4757            KTUtil.addEvent(the.btnPrev, 'click', function(e) {
4758                e.preventDefault();
4759                Plugin.goTo(Plugin.getPrevStep(), true);
4760            });
4761
4762            // First button event handler
4763            KTUtil.addEvent(the.btnFirst, 'click', function(e) {
4764                e.preventDefault();
4765                Plugin.goTo(1, true);
4766            });
4767
4768            // Last button event handler
4769            KTUtil.addEvent(the.btnLast, 'click', function(e) {
4770                e.preventDefault();
4771                Plugin.goTo(the.totalSteps, true);
4772            });
4773
4774            if (the.options.clickableSteps === true) {
4775                KTUtil.on(element, '[data-ktwizard-type="step"]', 'click', function() {
4776                    var index = Array.prototype.indexOf.call(the.steps, this) + 1;
4777                    if (index !== the.currentStep) {
4778                        Plugin.goTo(index, true);
4779                    }
4780                });
4781            }
4782        },
4783
4784        /**
4785         * Handles wizard click wizard
4786         */
4787        goTo: function(number, eventHandle) {
4788            // Skip if this step is already shown
4789            if (number === the.currentStep || number > the.totalSteps || number < 0) {
4790                return;
4791            }
4792
4793            // Validate step number
4794            if (number) {
4795                number = parseInt(number);
4796            } else {
4797                number = Plugin.getNextStep();
4798            }
4799
4800            // Before next and prev events
4801            var callback;
4802
4803            if (eventHandle === true) {
4804                if (number > the.currentStep) {
4805                    callback = Plugin.eventTrigger('beforeNext');
4806                } else {
4807                    callback = Plugin.eventTrigger('beforePrev');
4808                }
4809            }
4810
4811            // Skip if stopped
4812            if (the.stopped === true) {
4813                the.stopped = false;
4814                return;
4815            }
4816
4817            // Continue if no exit
4818            if (callback !== false) {
4819                // Before change
4820                if (eventHandle === true) {
4821                    Plugin.eventTrigger('beforeChange');
4822                }
4823
4824                // Set current step
4825                the.currentStep = number;
4826
4827                Plugin.updateUI();
4828
4829                // Trigger change event
4830                if (eventHandle === true) {
4831                    Plugin.eventTrigger('change');
4832                }
4833            }
4834
4835            // After next and prev events
4836            if (eventHandle === true) {
4837                if (number > the.startStep) {
4838                    Plugin.eventTrigger('afterNext');
4839                } else {
4840                    Plugin.eventTrigger('afterPrev');
4841                }
4842            } else {
4843                // this function called by method, stop for the next call
4844                the.stopped = true;
4845            }
4846
4847            return the;
4848        },
4849
4850        /**
4851         * Cancel
4852         */
4853        stop: function() {
4854            the.stopped = true;
4855        },
4856
4857        /**
4858         * Resume
4859         */
4860        start: function() {
4861            the.stopped = false;
4862        },
4863
4864        /**
4865         * Check last step
4866         */
4867        isLastStep: function() {
4868            return the.currentStep === the.totalSteps;
4869        },
4870
4871        /**
4872         * Check first step
4873         */
4874        isFirstStep: function() {
4875            return the.currentStep === 1;
4876        },
4877
4878        /**
4879         * Check between step
4880         */
4881        isBetweenStep: function() {
4882            return Plugin.isLastStep() === false && Plugin.isFirstStep() === false;
4883        },
4884
4885        /**
4886         * Go to the first step
4887         */
4888        updateUI: function() {
4889            var stepType = '';
4890            var index = the.currentStep - 1;
4891
4892            if (Plugin.isLastStep()) {
4893                stepType = 'last';
4894            } else if (Plugin.isFirstStep()) {
4895                stepType = 'first';
4896            } else {
4897                stepType = 'between';
4898            }
4899
4900            KTUtil.attr(the.element, 'data-ktwizard-state', stepType);
4901
4902            // Steps
4903            var steps = KTUtil.findAll(the.element, '[data-ktwizard-type="step"]');
4904
4905            if (steps && steps.length > 0) {
4906                for (var i = 0, len = steps.length; i < len; i++) {
4907                    if (i == index) {
4908                        KTUtil.attr(steps[i], 'data-ktwizard-state', 'current');
4909                    } else {
4910                        if (i < index) {
4911                            KTUtil.attr(steps[i], 'data-ktwizard-state', 'done');
4912                        } else {
4913                            KTUtil.attr(steps[i], 'data-ktwizard-state', 'pending');
4914                        }
4915                    }
4916                }
4917            }
4918
4919            // Steps Info
4920            var stepsInfo = KTUtil.findAll(the.element, '[data-ktwizard-type="step-info"]');
4921            if (stepsInfo &&stepsInfo.length > 0) {
4922                for (var i = 0, len = stepsInfo.length; i < len; i++) {
4923                    if (i == index) {
4924                        KTUtil.attr(stepsInfo[i], 'data-ktwizard-state', 'current');
4925                    } else {
4926                        KTUtil.removeAttr(stepsInfo[i], 'data-ktwizard-state');
4927                    }
4928                }
4929            }
4930
4931            // Steps Content
4932            var stepsContent = KTUtil.findAll(the.element, '[data-ktwizard-type="step-content"]');
4933            if (stepsContent&& stepsContent.length > 0) {
4934                for (var i = 0, len = stepsContent.length; i < len; i++) {
4935                    if (i == index) {
4936                        KTUtil.attr(stepsContent[i], 'data-ktwizard-state', 'current');
4937                    } else {
4938                        KTUtil.removeAttr(stepsContent[i], 'data-ktwizard-state');
4939                    }
4940                }
4941            }
4942        },
4943
4944        /**
4945         * Get next step
4946         */
4947        getNextStep: function() {
4948            if (the.totalSteps >= (the.currentStep + 1)) {
4949                return the.currentStep + 1;
4950            } else {
4951                return the.totalSteps;
4952            }
4953        },
4954
4955        /**
4956         * Get prev step
4957         */
4958        getPrevStep: function() {
4959            if ((the.currentStep - 1) >= 1) {
4960                return the.currentStep - 1;
4961            } else {
4962                return 1;
4963            }
4964        },
4965
4966        /**
4967         * Trigger events
4968         */
4969        eventTrigger: function(name, nested) {
4970            //KTUtil.triggerCustomEvent(name);
4971            for (var i = 0; i < the.events.length; i++) {
4972                var event = the.events[i];
4973                if (event.name == name) {
4974                    if (event.one == true) {
4975                        if (event.fired == false) {
4976                            the.events[i].fired = true;
4977                            return event.handler.call(this, the);
4978                        }
4979                    } else {
4980                        return event.handler.call(this, the);
4981                    }
4982                }
4983            }
4984        },
4985
4986        addEvent: function(name, handler, one) {
4987            the.events.push({
4988                name: name,
4989                handler: handler,
4990                one: one,
4991                fired: false
4992            });
4993
4994            return the;
4995        }
4996    };
4997
4998    //////////////////////////
4999    // ** Public Methods ** //
5000    //////////////////////////
5001
5002    /**
5003     * Set default options
5004     */
5005
5006    the.setDefaults = function(options) {
5007        defaultOptions = options;
5008    };
5009
5010    /**
5011     * Go to the next step
5012     */
5013    the.goNext = function(eventHandle) {
5014        return Plugin.goTo(Plugin.getNextStep(), eventHandle);
5015    };
5016
5017    /**
5018     * Go to the prev step
5019     */
5020    the.goPrev = function(eventHandle) {
5021        return Plugin.goTo(Plugin.getPrevStep(),eventHandle);
5022    };
5023
5024    /**
5025     * Go to the last step
5026     */
5027    the.goLast = function(eventHandle) {
5028        return Plugin.goTo(the.totalSteps, eventHandle);
5029    };
5030
5031    /**
5032     * Go to the first step
5033     */
5034    the.goFirst = function(eventHandle) {
5035        return Plugin.goTo(1, eventHandle);
5036    };
5037
5038    /**
5039     * Go to a step
5040     */
5041    the.goTo = function(number, eventHandle) {
5042        return Plugin.goTo(number, eventHandle);
5043    };
5044
5045    /**
5046     * Cancel step
5047     */
5048    the.stop = function() {
5049        return Plugin.stop();
5050    };
5051
5052    /**
5053     * Resume step
5054     */
5055    the.start = function() {
5056        return Plugin.start();
5057    };
5058
5059    /**
5060     * Get current step number
5061     */
5062    the.getStep = function() {
5063        return the.currentStep;
5064    };
5065
5066    /**
5067     * Check last step
5068     */
5069    the.isLastStep = function() {
5070        return Plugin.isLastStep();
5071    };
5072
5073    /**
5074     * Check first step
5075     */
5076    the.isFirstStep = function() {
5077        return Plugin.isFirstStep();
5078    };
5079
5080    /**
5081     * Attach event
5082     */
5083    the.on = function(name, handler) {
5084        return Plugin.addEvent(name, handler);
5085    };
5086
5087    /**
5088     * Attach event that will be fired once
5089     */
5090    the.one = function(name, handler) {
5091        return Plugin.addEvent(name, handler, true);
5092    };
5093
5094    // Construct plugin
5095    Plugin.construct.apply(the, [options]);
5096
5097    return the;
5098};
5099
5100// webpack support
5101if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
5102    module.exports = KTWizard;
5103}
5104
5105'use strict';
5106(function($) {
5107
5108	var pluginName = 'KTDatatable';
5109	var pfx = 'kt-';
5110	var util = KTUtil;
5111	var app = KTApp;
5112
5113	if (typeof util === 'undefined') throw new Error('Util class is required and must be included before ' + pluginName);
5114
5115	// plugin setup
5116	$.fn[pluginName] = function(options) {
5117		if ($(this).length === 0) {
5118			console.warn('No ' + pluginName + ' element exist.');
5119			return;
5120		}
5121
5122		// global variables
5123		var datatable = this;
5124
5125		// debug enabled?
5126		// 1) state will be cleared on each refresh
5127		// 2) enable some logs
5128		// 3) etc.
5129		datatable.debug = false;
5130
5131		datatable.API = {
5132			record: null,
5133			value: null,
5134			params: null,
5135		};
5136
5137		var Plugin = {
5138			/********************
5139			 ** PRIVATE METHODS
5140			 ********************/
5141			isInit: false,
5142			cellOffset: 110,
5143			iconOffset: 15,
5144			stateId: 'meta',
5145			ajaxParams: {},
5146			pagingObject: {},
5147
5148			init: function(options) {
5149				var isHtmlTable = false;
5150				// data source option empty is normal table
5151				if (options.data.source === null) {
5152					Plugin.extractTable();
5153					isHtmlTable = true;
5154				}
5155
5156				Plugin.setupBaseDOM.call();
5157				Plugin.setupDOM(datatable.table);
5158
5159				// set custom query from options
5160				Plugin.setDataSourceQuery(Plugin.getOption('data.source.read.params.query'));
5161
5162				// on event after layout had done setup, show datatable
5163				$(datatable).on(pfx + 'datatable--on-layout-updated', Plugin.afterRender);
5164
5165				if (datatable.debug) {
5166					Plugin.stateRemove(Plugin.stateId);
5167				}
5168
5169				// initialize extensions
5170				$.each(Plugin.getOption('extensions'), function(extName, extOptions) {
5171					if (typeof $.fn[pluginName][extName] === 'function') {
5172						new $.fn[pluginName][extName](datatable, extOptions);
5173					}
5174				});
5175
5176				Plugin.spinnerCallback(true);
5177				// get data
5178				if (options.data.type === 'remote' || options.data.type === 'local') {
5179					if (options.data.saveState === false
5180						|| options.data.saveState.cookie === false
5181						&& options.data.saveState.webstorage === false) {
5182						Plugin.stateRemove(Plugin.stateId);
5183					}
5184					// get data for local datatable and local table
5185					if (options.data.type === 'local' && typeof options.data.source === 'object') {
5186						datatable.dataSet = datatable.originalDataSet = Plugin.dataMapCallback(options.data.source);
5187					}
5188					Plugin.dataRender();
5189				}
5190
5191                // if html table, remove and setup a new header
5192                if (isHtmlTable) {
5193                  $(datatable.tableHead).find('tr').remove();
5194                  $(datatable.tableFoot).find('tr').remove();
5195                }
5196
5197                Plugin.setHeadTitle();
5198                if (Plugin.getOption('layout.footer')) {
5199                  Plugin.setHeadTitle(datatable.tableFoot);
5200                }
5201
5202				// hide header
5203				if (typeof options.layout.header !== 'undefined' &&
5204					options.layout.header === false) {
5205					$(datatable.table).find('thead').remove();
5206				}
5207
5208				// hide footer
5209				if (typeof options.layout.footer !== 'undefined' &&
5210					options.layout.footer === false) {
5211					$(datatable.table).find('tfoot').remove();
5212				}
5213
5214				// for normal and local data type, run layoutUpdate
5215				if (options.data.type === null ||
5216					options.data.type === 'local') {
5217					Plugin.setupCellField.call();
5218					Plugin.setupTemplateCell.call();
5219
5220					// setup nested datatable, if option enabled
5221					Plugin.setupSubDatatable.call();
5222
5223					// setup extra system column properties
5224					Plugin.setupSystemColumn.call();
5225					Plugin.redraw();
5226				}
5227
5228				var width;
5229				var initialWidth = false;
5230				$(window).resize(function() {
5231					// issue: URL Bar Resizing on mobile, https://developers.google.com/web/updates/2016/12/url-bar-resizing
5232					// trigger datatable resize on width change only
5233					if ($(this).width() !== width) {
5234						width = $(this).width();
5235						Plugin.fullRender();
5236					}
5237					// get initial width
5238					if (!initialWidth) {
5239						width = $(this).width();
5240						initialWidth = true;
5241					}
5242				});
5243
5244				$(datatable).height('');
5245
5246				$(Plugin.getOption('search.input')).on('keyup', function(e) {
5247					if (Plugin.getOption('search.onEnter') && e.which !== 13) return;
5248					Plugin.search($(this).val());
5249				});
5250
5251				return datatable;
5252			},
5253
5254			/**
5255			 * Extract static HTML table content into datasource
5256			 */
5257			extractTable: function() {
5258				var columns = [];
5259				var headers = $(datatable).find('tr:first-child th').get().map(function(cell, i) {
5260					var field = $(cell).data('field');
5261					if (typeof field === 'undefined') {
5262						field = $(cell).text().trim();
5263					}
5264					var column = {field: field, title: field};
5265					for (var ii in options.columns) {
5266						if (options.columns[ii].field === field) {
5267							column = $.extend(true, {}, options.columns[ii], column);
5268						}
5269					}
5270					columns.push(column);
5271					return field;
5272				});
5273				// auto create columns config
5274				options.columns = columns;
5275
5276				var rowProp = [];
5277				var source = [];
5278
5279				$(datatable).find('tr').each(function() {
5280					if ($(this).find('td').length) {
5281						rowProp.push($(this).prop('attributes'));
5282					}
5283					var td = {};
5284					$(this).find('td').each(function(i, cell) {
5285						td[headers[i]] = cell.innerHTML.trim();
5286					});
5287					if (!util.isEmpty(td)) {
5288						source.push(td);
5289					}
5290				});
5291
5292				options.data.attr.rowProps = rowProp;
5293				options.data.source = source;
5294			},
5295
5296			/**
5297			 * One time layout update on init
5298			 */
5299			layoutUpdate: function() {
5300				// setup nested datatable, if option enabled
5301				Plugin.setupSubDatatable.call();
5302
5303				// setup extra system column properties
5304				Plugin.setupSystemColumn.call();
5305
5306				// setup cell hover event
5307				Plugin.setupHover.call();
5308
5309				if (typeof options.detail === 'undefined'
5310					// temporary disable lock column in subtable
5311					&& Plugin.getDepth() === 1) {
5312					// lock columns handler
5313					Plugin.lockTable.call();
5314				}
5315
5316				Plugin.resetScroll();
5317
5318				// check if not is a locked column
5319				if (!Plugin.isLocked()) {
5320					Plugin.redraw.call();
5321					// check if its not a subtable and has autoHide option enabled
5322					if (!Plugin.isSubtable() && Plugin.getOption('rows.autoHide') === true) {
5323						Plugin.autoHide();
5324					}
5325					// reset row
5326					$(datatable.table).find('.' + pfx + 'datatable__row').css('height', '');
5327				}
5328
5329				Plugin.columnHide.call();
5330
5331				Plugin.rowEvenOdd.call();
5332
5333				Plugin.sorting.call();
5334
5335				Plugin.scrollbar.call();
5336
5337				if (!Plugin.isInit) {
5338					// run once dropdown inside datatable
5339					Plugin.dropdownFix();
5340					$(datatable).trigger(pfx + 'datatable--on-init', {table: $(datatable.wrap).attr('id'), options: options});
5341					Plugin.isInit = true;
5342				}
5343
5344				$(datatable).trigger(pfx + 'datatable--on-layout-updated', {table: $(datatable.wrap).attr('id')});
5345			},
5346
5347			dropdownFix: function() {
5348				var dropdownMenu;
5349				$('body').on('show.bs.dropdown', '.' + pfx + 'datatable .' + pfx + 'datatable__body', function(e) {
5350					dropdownMenu = $(e.target).find('.dropdown-menu');
5351					$('body').append(dropdownMenu.detach());
5352					dropdownMenu.css('display', 'block');
5353					dropdownMenu.position({
5354						'my': 'right top',
5355						'at': 'right bottom',
5356						'of': $(e.relatedTarget),
5357					});
5358					// if datatable is inside modal
5359					if (datatable.closest('.modal').length) {
5360						// increase dropdown z-index
5361						dropdownMenu.css('z-index', '2000');
5362					}
5363				}).on('hide.bs.dropdown', '.' + pfx + 'datatable .' + pfx + 'datatable__body', function(e) {
5364					$(e.target).append(dropdownMenu.detach());
5365					dropdownMenu.hide();
5366				});
5367			},
5368
5369			lockTable: function() {
5370				var lock = {
5371					lockEnabled: false,
5372					init: function() {
5373						// check if table should be locked columns
5374						lock.lockEnabled = Plugin.lockEnabledColumns();
5375						if (lock.lockEnabled.left.length === 0 &&
5376							lock.lockEnabled.right.length === 0) {
5377							return;
5378						}
5379						lock.enable();
5380					},
5381					enable: function() {
5382						var enableLock = function(tablePart) {
5383							// check if already has lock column
5384							if ($(tablePart).find('.' + pfx + 'datatable__lock').length > 0) {
5385								Plugin.log('Locked container already exist in: ', tablePart);
5386								return;
5387							}
5388							// check if no rows exists
5389							if ($(tablePart).find('.' + pfx + 'datatable__row').length === 0) {
5390								Plugin.log('No row exist in: ', tablePart);
5391								return;
5392							}
5393
5394							// locked div container
5395							var lockLeft = $('<div/>').addClass(pfx + 'datatable__lock ' + pfx + 'datatable__lock--left');
5396							var lockScroll = $('<div/>').addClass(pfx + 'datatable__lock ' + pfx + 'datatable__lock--scroll');
5397							var lockRight = $('<div/>').addClass(pfx + 'datatable__lock ' + pfx + 'datatable__lock--right');
5398
5399							$(tablePart).find('.' + pfx + 'datatable__row').each(function() {
5400								// create new row for lock columns and pass the data
5401								var rowLeft = $('<tr/>').addClass(pfx + 'datatable__row').data('obj', $(this).data('obj')).appendTo(lockLeft);
5402								var rowScroll = $('<tr/>').addClass(pfx + 'datatable__row').data('obj', $(this).data('obj')).appendTo(lockScroll);
5403								var rowRight = $('<tr/>').addClass(pfx + 'datatable__row').data('obj', $(this).data('obj')).appendTo(lockRight);
5404								$(this).find('.' + pfx + 'datatable__cell').each(function() {
5405									var locked = $(this).data('locked');
5406									if (typeof locked !== 'undefined') {
5407										if (typeof locked.left !== 'undefined' || locked === true) {
5408											// default locked to left
5409											$(this).appendTo(rowLeft);
5410										}
5411										if (typeof locked.right !== 'undefined') {
5412											$(this).appendTo(rowRight);
5413										}
5414									} else {
5415										$(this).appendTo(rowScroll);
5416									}
5417								});
5418								// remove old row
5419								$(this).remove();
5420							});
5421
5422							if (lock.lockEnabled.left.length > 0) {
5423								$(datatable.wrap).addClass(pfx + 'datatable--lock');
5424								$(lockLeft).appendTo(tablePart);
5425							}
5426							if (lock.lockEnabled.left.length > 0 || lock.lockEnabled.right.length > 0) {
5427								$(lockScroll).appendTo(tablePart);
5428							}
5429							if (lock.lockEnabled.right.length > 0) {
5430								$(datatable.wrap).addClass(pfx + 'datatable--lock');
5431								$(lockRight).appendTo(tablePart);
5432							}
5433						};
5434
5435						$(datatable.table).find('thead,tbody,tfoot').each(function() {
5436							var tablePart = this;
5437							if ($(this).find('.' + pfx + 'datatable__lock').length === 0) {
5438								$(this).ready(function() {
5439									enableLock(tablePart);
5440								});
5441							}
5442						});
5443					},
5444				};
5445				lock.init();
5446				return lock;
5447			},
5448
5449			/**
5450			 * Render everything for resize
5451			 */
5452			fullRender: function() {
5453				$(datatable.tableHead).empty();
5454				Plugin.setHeadTitle();
5455				if (Plugin.getOption('layout.footer')) {
5456					$(datatable.tableFoot).empty();
5457					Plugin.setHeadTitle(datatable.tableFoot);
5458				}
5459
5460				Plugin.spinnerCallback(true);
5461				$(datatable.wrap).removeClass(pfx + 'datatable--loaded');
5462
5463				Plugin.insertData();
5464			},
5465
5466			lockEnabledColumns: function() {
5467				var screen = $(window).width();
5468				var columns = options.columns;
5469				var enabled = {left: [], right: []};
5470				$.each(columns, function(i, column) {
5471					if (typeof column.locked !== 'undefined') {
5472						if (typeof column.locked.left !== 'undefined') {
5473							if (util.getBreakpoint(column.locked.left) <= screen) {
5474								enabled['left'].push(column.locked.left);
5475							}
5476						}
5477						if (typeof column.locked.right !== 'undefined') {
5478							if (util.getBreakpoint(column.locked.right) <= screen) {
5479								enabled['right'].push(column.locked.right);
5480							}
5481						}
5482					}
5483				});
5484				return enabled;
5485			},
5486
5487			/**
5488			 * After render event, called by
5489			 * '+pfx+'-datatable--on-layout-updated
5490			 * @param e
5491			 * @param args
5492			 */
5493			afterRender: function(e, args) {
5494				$(datatable).ready(function() {
5495					// redraw locked columns table
5496					if (Plugin.isLocked()) {
5497						Plugin.redraw();
5498					}
5499
5500					$(datatable.tableBody).css('visibility', '');
5501					$(datatable.wrap).addClass(pfx + 'datatable--loaded');
5502
5503					Plugin.spinnerCallback(false);
5504				});
5505			},
5506
5507			hoverTimer: 0,
5508			isScrolling: false,
5509			setupHover: function() {
5510				$(window).scroll(function(e) {
5511					// stop hover when scrolling
5512					clearTimeout(Plugin.hoverTimer);
5513					Plugin.isScrolling = true;
5514				});
5515
5516				$(datatable.tableBody).find('.' + pfx + 'datatable__cell').off('mouseenter', 'mouseleave').on('mouseenter', function() {
5517					// reset scroll timer to hover class
5518					Plugin.hoverTimer = setTimeout(function() {
5519						Plugin.isScrolling = false;
5520					}, 200);
5521					if (Plugin.isScrolling) return;
5522
5523					// normal table
5524					var row = $(this).closest('.' + pfx + 'datatable__row').addClass(pfx + 'datatable__row--hover');
5525					var index = $(row).index() + 1;
5526
5527					// lock table
5528					$(row).closest('.' + pfx + 'datatable__lock').parent().find('.' + pfx + 'datatable__row:nth-child(' + index + ')').addClass(pfx + 'datatable__row--hover');
5529				}).on('mouseleave', function() {
5530					// normal table
5531					var row = $(this).closest('.' + pfx + 'datatable__row').removeClass(pfx + 'datatable__row--hover');
5532					var index = $(row).index() + 1;
5533
5534					// look table
5535					$(row).closest('.' + pfx + 'datatable__lock').parent().find('.' + pfx + 'datatable__row:nth-child(' + index + ')').removeClass(pfx + 'datatable__row--hover');
5536				});
5537			},
5538
5539			/**
5540			 * Adjust width of locked table containers by resize handler
5541			 * @returns {number}
5542			 */
5543			adjustLockContainer: function() {
5544				if (!Plugin.isLocked()) return 0;
5545
5546				// refer to head dimension
5547				var containerWidth = $(datatable.tableHead).width();
5548				var lockLeft = $(datatable.tableHead).find('.' + pfx + 'datatable__lock--left').width();
5549				var lockRight = $(datatable.tableHead).find('.' + pfx + 'datatable__lock--right').width();
5550
5551				if (typeof lockLeft === 'undefined') lockLeft = 0;
5552				if (typeof lockRight === 'undefined') lockRight = 0;
5553
5554				var lockScroll = Math.floor(containerWidth - lockLeft - lockRight);
5555				$(datatable.table).find('.' + pfx + 'datatable__lock--scroll').css('width', lockScroll);
5556
5557				return lockScroll;
5558			},
5559
5560			/**
5561			 * todo; not in use
5562			 */
5563			dragResize: function() {
5564				var pressed = false;
5565				var start = undefined;
5566				var startX, startWidth;
5567				$(datatable.tableHead).find('.' + pfx + 'datatable__cell').mou
5567sedown(function(e) {
5568					start = $(this);
5569					pressed = true;
5570					startX = e.pageX;
5571					startWidth = $(this).width();
5572					$(start).addClass(pfx + 'datatable__cell--resizing');
5573
5574				}).mousemove(function(e) {
5575					if (pressed) {
5576						var i = $(start).index();
5577						var tableBody = $(datatable.tableBody);
5578						var ifLocked = $(start).closest('.' + pfx + 'datatable__lock');
5579
5580						if (ifLocked) {
5581							var lockedIndex = $(ifLocked).index();
5582							tableBody = $(datatable.tableBody).find('.' + pfx + 'datatable__lock').eq(lockedIndex);
5583						}
5584
5585						$(tableBody).find('.' + pfx + 'datatable__row').each(function(tri, tr) {
5586							$(tr).find('.' + pfx + 'datatable__cell').eq(i).width(startWidth + (e.pageX - startX)).children().width(startWidth + (e.pageX - startX));
5587						});
5588
5589						$(start).children().css('width', startWidth + (e.pageX - startX));
5590					}
5591
5592				}).mouseup(function() {
5593					$(start).removeClass(pfx + 'datatable__cell--resizing');
5594					pressed = false;
5595				});
5596
5597				$(document).mouseup(function() {
5598					$(start).removeClass(pfx + 'datatable__cell--resizing');
5599					pressed = false;
5600				});
5601			},
5602
5603			/**
5604			 * To prepare placeholder for table before content is loading
5605			 */
5606			initHeight: function() {
5607				if (options.layout.height && options.layout.scroll) {
5608					var theadHeight = $(datatable.tableHead).find('.' + pfx + 'datatable__row').outerHeight();
5609					var tfootHeight = $(datatable.tableFoot).find('.' + pfx + 'datatable__row').outerHeight();
5610					var bodyHeight = options.layout.height;
5611					if (theadHeight > 0) {
5612						bodyHeight -= theadHeight;
5613					}
5614					if (tfootHeight > 0) {
5615						bodyHeight -= tfootHeight;
5616					}
5617
5618					// scrollbar offset
5619					bodyHeight -= 2;
5620
5621					$(datatable.tableBody).css('max-height', Math.floor(parseFloat(bodyHeight)));
5622
5623					// set scrollable area fixed height
5624					// $(datatable.tableBody).find('.' + pfx + 'datatable__lock--scroll').css('height', Math.floor(parseFloat(bodyHeight)));
5625				}
5626			},
5627
5628			/**
5629			 * Setup base DOM (table, thead, tbody, tfoot) and create if not
5630			 * exist.
5631			 */
5632			setupBaseDOM: function() {
5633				// keep original state before datatable initialize
5634				datatable.initialDatatable = $(datatable).clone();
5635
5636				// main element
5637				if ($(datatable).prop('tagName') === 'TABLE') {
5638					// if main init element is <table>, wrap with div
5639					datatable.table = $(datatable).removeClass(pfx + 'datatable').addClass(pfx + 'datatable__table');
5640					if ($(datatable.table).parents('.' + pfx + 'datatable').length === 0) {
5641						datatable.table.wrap($('<div/>').addClass(pfx + 'datatable').addClass(pfx + 'datatable--' + options.layout.theme));
5642						datatable.wrap = $(datatable.table).parent();
5643					}
5644				} else {
5645					// create table
5646					datatable.wrap = $(datatable).addClass(pfx + 'datatable').addClass(pfx + 'datatable--' + options.layout.theme);
5647					datatable.table = $('<table/>').addClass(pfx + 'datatable__table').appendTo(datatable);
5648				}
5649
5650				if (typeof options.layout.class !== 'undefined') {
5651					$(datatable.wrap).addClass(options.layout.class);
5652				}
5653
5654				$(datatable.table).removeClass(pfx + 'datatable--destroyed').css('display', 'block');
5655
5656				// force disable save state
5657				if (typeof $(datatable).attr('id') === 'undefined') {
5658					Plugin.setOption('data.saveState', false);
5659					$(datatable.table).attr('id', util.getUniqueID(pfx + 'datatable--'));
5660				}
5661
5662				// predefine table height
5663				if (Plugin.getOption('layout.minHeight'))
5664					$(datatable.table).css('min-height', Plugin.getOption('layout.minHeight'));
5665
5666				if (Plugin.getOption('layout.height'))
5667					$(datatable.table).css('max-height', Plugin.getOption('layout.height'));
5668
5669				// for normal table load
5670				if (options.data.type === null) {
5671					$(datatable.table).css('width', '').css('display', '');
5672				}
5673
5674				// create table head element
5675				datatable.tableHead = $(datatable.table).find('thead');
5676				if ($(datatable.tableHead).length === 0) {
5677					datatable.tableHead = $('<thead/>').prependTo(datatable.table);
5678				}
5679
5680				// create table head element
5681				datatable.tableBody = $(datatable.table).find('tbody');
5682				if ($(datatable.tableBody).length === 0) {
5683					datatable.tableBody = $('<tbody/>').appendTo(datatable.table);
5684				}
5685
5686				if (typeof options.layout.footer !== 'undefined' &&
5687					options.layout.footer) {
5688					// create table foot element
5689					datatable.tableFoot = $(datatable.table).find('tfoot');
5690					if ($(datatable.tableFoot).length === 0) {
5691						datatable.tableFoot = $('<tfoot/>').appendTo(datatable.table);
5692					}
5693				}
5694			},
5695
5696			/**
5697			 * Set column data before table manipulation.
5698			 */
5699			setupCellField: function(tableParts) {
5700				if (typeof tableParts === 'undefined') tableParts = $(datatable.table).children();
5701				var columns = options.columns;
5702				$.each(tableParts, function(part, tablePart) {
5703					$(tablePart).find('.' + pfx + 'datatable__row').each(function(tri, tr) {
5704						// prepare data
5705						$(tr).find('.' + pfx + 'datatable__cell').each(function(tdi, td) {
5706							if (typeof columns[tdi] !== 'undefined') {
5707								$(td).data(columns[tdi]);
5708							}
5709						});
5710					});
5711				});
5712			},
5713
5714			/**
5715			 * Set column template callback
5716			 * @param tablePart
5717			 */
5718			setupTemplateCell: function(tablePart) {
5719				if (typeof tablePart === 'undefined') tablePart = datatable.tableBody;
5720				var columns = options.columns;
5721				$(tablePart).find('.' + pfx + 'datatable__row').each(function(tri, tr) {
5722					// row data object, if any
5723					var obj = $(tr).data('obj');
5724					if (typeof obj === 'undefined') {
5725						return;
5726					}
5727
5728					// @deprecated in v5.0.6
5729					// obj['getIndex'] = function() {
5730					// 	return tri;
5731					// };
5732					// @deprecated in v5.0.6
5733					// obj['getDatatable'] = function() {
5734					// 	return datatable;
5735					// };
5736
5737					// @deprecated in v5.0.6
5738					var rowCallback = Plugin.getOption('rows.callback');
5739					if (typeof rowCallback === 'function') {
5740						rowCallback($(tr), obj, tri);
5741					}
5742					// before template row callback
5743					var beforeTemplate = Plugin.getOption('rows.beforeTemplate');
5744					if (typeof beforeTemplate === 'function') {
5745						beforeTemplate($(tr), obj, tri);
5746					}
5747					// if data object is undefined, collect from table
5748					if (typeof obj === 'undefined') {
5749						obj = {};
5750						$(tr).find('.' + pfx + 'datatable__cell').each(function(tdi, td) {
5751							// get column settings by field
5752							var column = $.grep(columns, function(n, i) {
5753								return $(td).data('field') === n.field;
5754							})[0];
5755							if (typeof column !== 'undefined') {
5756								obj[column['field']] = $(td).text();
5757							}
5758						});
5759					}
5760
5761					$(tr).find('.' + pfx + 'datatable__cell').each(function(tdi, td) {
5762						// get column settings by field
5763						var column = $.grep(columns, function(n, i) {
5764							return $(td).data('field') === n.field;
5765						})[0];
5766						if (typeof column !== 'undefined') {
5767							// column template
5768							if (typeof column.template !== 'undefined') {
5769								var finalValue = '';
5770								// template string
5771								if (typeof column.template === 'string') {
5772									finalValue = Plugin.dataPlaceholder(column.template, obj);
5773								}
5774								// template callback function
5775								if (typeof column.template === 'function') {
5776									finalValue = column.template(obj, tri, datatable);
5777								}
5778
5779								// sanitize using DOMPurify if installed
5780								if (typeof DOMPurify !== 'undefined') {
5781									finalValue = DOMPurify.sanitize(finalValue);
5782								}
5783
5784								var span = document.createElement('span');
5785								span.innerHTML = finalValue;
5786
5787								// insert to cell, wrap with span
5788								$(td).html(span);
5789
5790								// set span overflow
5791								if (typeof column.overflow !== 'undefined') {
5792									$(span).css('overflow', column.overflow);
5793									$(span).css('position', 'relative');
5794								}
5795							}
5796						}
5797					});
5798
5799					// after template row callback
5800					var afterTemplate = Plugin.getOption('rows.afterTemplate');
5801					if (typeof afterTemplate === 'function') {
5802						afterTemplate($(tr), obj, tri);
5803					}
5804				});
5805			},
5806
5807			/**
5808			 * Setup extra system column properties
5809			 * Note: selector checkbox, subtable toggle
5810			 */
5811			setupSystemColumn: function() {
5812				datatable.dataSet = datatable.dataSet || [];
5813				// no records available
5814				if (datatable.dataSet.length === 0) return;
5815
5816				var columns = options.columns;
5817				$(datatable.tableBody).find('.' + pfx + 'datatable__row').each(function(tri, tr) {
5818					$(tr).find('.' + pfx + 'datatable__cell').each(function(tdi, td) {
5819						// get column settings by field
5820						var column = $.grep(columns, function(n, i) {
5821							return $(td).data('field') === n.field;
5822						})[0];
5823						if (typeof column !== 'undefined') {
5824							var value = $(td).text();
5825
5826							// enable column selector
5827							if (typeof column.selector !== 'undefined' && column.selector !== false) {
5828								// check if checkbox exist
5829								if ($(td).find('.' + pfx + 'checkbox [type="checkbox"]').length > 0) return;
5830
5831								$(td).addClass(pfx + 'datatable__cell--check');
5832
5833								// append checkbox
5834								var chk = $('<label/>').
5835									addClass(pfx + 'checkbox ' + pfx + 'checkbox--single').
5836									append($('<input/>').attr('type', 'checkbox').attr('value', value).on('click', function() {
5837										if ($(this).is(':checked')) {
5838											// add checkbox active row class
5839											Plugin.setActive(this);
5840										} else {
5841											// add checkbox active row class
5842											Plugin.setInactive(this);
5843										}
5844									})).
5845									append('&nbsp;<span></span>');
5846
5847								// checkbox selector has outline style
5848								if (typeof column.selector.class !== 'undefined') {
5849									$(chk).addClass(column.selector.class);
5850								}
5851
5852								$(td).children().html(chk);
5853							}
5854
5855							// enable column subtable toggle
5856							if (typeof column.subtable !== 'undefined' && column.subtable) {
5857								// check if subtable toggle exist
5858								if ($(td).find('.' + pfx + 'datatable__toggle-subtable').length > 0) return;
5859								// append subtable toggle
5860								$(td).
5861									children().
5862									html($('<a/>').
5863										addClass(pfx + 'datatable__toggle-subtable').
5864										attr('href', '#').
5865										attr('data-value', value).
5866										append($('<i/>').addClass(Plugin.getOption('layout.icons.rowDetail.collapse'))));
5867							}
5868						}
5869					});
5870				});
5871
5872				// init checkbox for header/footer
5873				var initCheckbox = function(tr) {
5874					// get column settings by field
5875					var column = $.grep(columns, function(n, i) {
5876						return typeof n.selector !== 'undefined' && n.selector !== false;
5877					})[0];
5878
5879					if (typeof column !== 'undefined') {
5880						// enable column selector
5881						if (typeof column.selector !== 'undefined' && column.selector !== false) {
5882							var td = $(tr).find('[data-field="' + column.field + '"]');
5883							// check if checkbox exist
5884							if ($(td).find('.' + pfx + 'checkbox [type="checkbox"]').length > 0) return;
5885
5886							$(td).addClass(pfx + 'datatable__cell--check');
5887
5888							// append checkbox
5889							var chk = $('<label/>').
5890								addClass(pfx + 'checkbox ' + pfx + 'checkbox--single ' + pfx + 'checkbox--all').
5891								append($('<input/>').attr('type', 'checkbox').on('click', function() {
5892									if ($(this).is(':checked')) {
5893										Plugin.setActiveAll(true);
5894									} else {
5895										Plugin.setActiveAll(false);
5896									}
5897								})).
5898								append('&nbsp;<span></span>');
5899
5900							// checkbox selector has outline style
5901							if (typeof column.selector.class !== 'undefined') {
5902								$(chk).addClass(column.selector.class);
5903							}
5904
5905							$(td).children().html(chk);
5906						}
5907					}
5908				};
5909
5910				if (options.layout.header) {
5911					initCheckbox($(datatable.tableHead).find('.' + pfx + 'datatable__row').first());
5912				}
5913				if (options.layout.footer) {
5914					initCheckbox($(datatable.tableFoot).find('.' + pfx + 'datatable__row').first());
5915				}
5916			},
5917
5918            maxWidthList: {},
5919
5920			/**
5921			 * Adjust width to match container size
5922			 */
5923			adjustCellsWidth: function() {
5924				// get table width
5925				var containerWidth = $(datatable.tableBody).innerWidth() - Plugin.iconOffset;
5926
5927				// get total number of columns
5928				var columns = $(datatable.tableBody).
5929					find('.' + pfx + 'datatable__row:first-child').
5930					find('.' + pfx + 'datatable__cell').
5931					// exclude expand icon
5932					not('.' + pfx + 'datatable__toggle-detail').
5933					not(':hidden').length;
5934
5935				if (columns > 0) {
5936					//  remove reserved sort icon width
5937					containerWidth = containerWidth - (Plugin.iconOffset * columns);
5938					var minWidth = Math.floor(containerWidth / columns);
5939
5940					// minimum width
5941					if (minWidth <= Plugin.cellOffset) {
5942						minWidth = Plugin.cellOffset;
5943					}
5944
5945					$(datatable.table).find('.' + pfx + 'datatable__row').
5946							find('.' + pfx + 'datatable__cell').
5947							// exclude expand icon
5948							not('.' + pfx + 'datatable__toggle-detail').
5949							not(':hidden').each(function(tdi, td) {
5950
5951						var width = minWidth;
5952						var dataWidth = $(td).data('width');
5953
5954						if (typeof dataWidth !== 'undefined') {
5955							if (dataWidth === 'auto') {
5956								var field = $(td).data('field');
5957								if (Plugin.maxWidthList[field]) {
5958									width = Plugin.maxWidthList[field];
5959								}
5960								else {
5961									var cells = $(datatable.table).find('.' + pfx + 'datatable__cell[data-field="' + field + '"]');
5962									width = Plugin.maxWidthList[field] = Math.max.apply(null,
5963											$(cells).map(function() {
5964												return $(this).outerWidth();
5965											}).get());
5966								}
5967							}
5968							else {
5969								width = dataWidth;
5970							}
5971						}
5972						$(td).children().css('width', Math.ceil(width));
5973					});
5974				}
5975
5976				return datatable;
5977			},
5978
5979			/**
5980			 * Adjust height to match container size
5981			 */
5982			adjustCellsHeight: function() {
5983				$.each($(datatable.table).children(), function(part, tablePart) {
5984					var totalRows = $(tablePart).find('.' + pfx + 'datatable__row').first().parent().find('.' + pfx + 'datatable__row').length;
5985					for (var i = 1; i <= totalRows; i++) {
5986						var rows = $(tablePart).find('.' + pfx + 'datatable__row:nth-child(' + i + ')');
5987						if ($(rows).length > 0) {
5988							var maxHeight = Math.max.apply(null, $(rows).map(function() {
5989								return $(this).outerHeight();
5990							}).get());
5991							$(rows).css('height', Math.ceil(maxHeight));
5992						}
5993					}
5994				});
5995			},
5996
5997			/**
5998			 * Setup table DOM and classes
5999			 */
6000			setupDOM: function(table) {
6001				// set table classes
6002				$(table).find('> thead').addClass(pfx + 'datatable__head');
6003				$(table).find('> tbody').addClass(pfx + 'datatable__body');
6004				$(table).find('> tfoot').addClass(pfx + 'datatable__foot');
6005				$(table).find('tr').addClass(pfx + 'datatable__row');
6006				$(table).find('tr > th, tr > td').addClass(pfx + 'datatable__cell');
6007				$(table).find('tr > th, tr > td').each(function(i, td) {
6008					if ($(td).find('span').length === 0) {
6009						$(td).wrapInner($('<span/>').css('width', Plugin.cellOffset));
6010					}
6011				});
6012			},
6013
6014			/**
6015			 * Default scrollbar
6016			 * @returns {{tableLocked: null, init: init, onScrolling:
6017			 *     onScrolling}}
6018			 */
6019			scrollbar: function() {
6020				var scroll = {
6021					scrollable: null,
6022					tableLocked: null,
6023					initPosition: null,
6024					init: function() {
6025						var screen = util.getViewPort().width;
6026						// setup scrollable datatable
6027						if (options.layout.scroll) {
6028							// add scrollable datatable class
6029							$(datatable.wrap).addClass(pfx + 'datatable--scroll');
6030
6031							var scrollable = $(datatable.tableBody).find('.' + pfx + 'datatable__lock--scroll');
6032
6033							// check if scrollable area have rows
6034							if ($(scrollable).find('.' + pfx + 'datatable__row').length > 0 && $(scrollable).length > 0) {
6035								scroll.scrollHead = $(datatable.tableHead).find('> .' + pfx + 'datatable__lock--scroll > .' + pfx + 'datatable__row');
6036								scroll.scrollFoot = $(datatable.tableFoot).find('> .' + pfx + 'datatable__lock--scroll > .' + pfx + 'datatable__row');
6037								scroll.tableLocked = $(datatable.tableBody).find('.' + pfx + 'datatable__lock:not(.' + pfx + 'datatable__lock--scroll)');
6038								if (Plugin.getOption('layout.customScrollbar') && util.detectIE() != 10 && screen > util.getBreakpoint('lg')) {
6039									scroll.initCustomScrollbar(scrollable[0]);
6040								} else {
6041									scroll.initDefaultScrollbar(scrollable);
6042								}
6043							} else if ($(datatable.tableBody).find('.' + pfx + 'datatable__row').length > 0) {
6044								scroll.scrollHead = $(datatable.tableHead).find('> .' + pfx + 'datatable__row');
6045								scroll.scrollFoot = $(datatable.tableFoot).find('> .' + pfx + 'datatable__row');
6046								if (Plugin.getOption('layout.customScrollbar') && util.detectIE() != 10 && screen > util.getBreakpoint('lg')) {
6047									scroll.initCustomScrollbar(datatable.tableBody);
6048								} else {
6049									scroll.initDefaultScrollbar(datatable.tableBody);
6050								}
6051							}
6052						}
6053					},
6054					initDefaultScrollbar: function(scrollable) {
6055						// get initial scroll position
6056						scroll.initPosition = $(scrollable).scrollLeft();
6057						$(scrollable).css('overflow-y', 'auto').off().on('scroll', scroll.onScrolling);
6058						$(scrollable).css('overflow-x', 'auto');
6059					},
6060					onScrolling: function(e) {
6061						var left = $(this).scrollLeft();
6062						var top = $(this).scrollTop();
6063						if (util.isRTL()) {
6064							// deduct initial position for RTL
6065							left = left - scroll.initPosition;
6066						}
6067						$(scroll.scrollHead).css('left', -left);
6068						$(scroll.scrollFoot).css('left', -left);
6069						$(scroll.tableLocked).each(function(i, table) {
6070							if (Plugin.isLocked()) {
6071								// scrollbar offset
6072								top -= 1;
6073							}
6074							$(table).css('top', -top);
6075						});
6076					},
6077					initCustomScrollbar: function(scrollable) {
6078						scroll.scrollable = scrollable;
6079						// create a new instance for table body with scrollbar
6080						Plugin.initScrollbar(scrollable);
6081						// get initial scroll position
6082						scroll.initPosition = $(scrollable).scrollLeft();
6083						$(scrollable).off().on('scroll', scroll.onScrolling);
6084					},
6085				};
6086				scroll.init();
6087				return scroll;
6088			},
6089
6090			/**
6091			 * Init custom scrollbar and reset position
6092			 * @param element
6093			 * @param options
6094			 */
6095			initScrollbar: function(element, options) {
6096				if (!element || !element.nodeName) {
6097					return;
6098				}
6099				$(datatable.tableBody).css('overflow', '');
6100				var ps = $(element).data('ps');
6101				if (util.hasClass(element, 'ps') && typeof ps !== 'undefined') {
6102					ps.update();
6103				} else {
6104					ps = new PerfectScrollbar(element, Object.assign({}, {
6105						wheelSpeed: 0.5,
6106						swipeEasing: true,
6107						// wheelPropagation: false,
6108						minScrollbarLength: 40,
6109						maxScrollbarLength: 300,
6110						suppressScrollX: Plugin.getOption('rows.autoHide') && !Plugin.isLocked()
6111					}, options));
6112					$(element).data('ps', ps);
6113				}
6114
6115				// reset perfect scrollbar on resize
6116				$(window).resize(function() {
6117					ps.update();
6118				});
6119			},
6120
6121			/**
6122			 * Set column title from options.columns settings
6123			 */
6124			setHeadTitle: function(tablePart) {
6125				if (typeof tablePart === 'undefined') tablePart = datatable.tableHead;
6126				tablePart = $(tablePart)[0];
6127				var columns = options.columns;
6128				var row = tablePart.getElementsByTagName('tr')[0];
6129				var ths = tablePart.getElementsByTagName('td');
6130
6131				if (typeof row === 'undefined') {
6132					row = document.createElement('tr');
6133					tablePart.appendChild(row);
6134				}
6135
6136				$.each(columns, function(i, column) {
6137					var th = ths[i];
6138					if (typeof th === 'undefined') {
6139						th = document.createElement('th');
6140						row.appendChild(th);
6141					}
6142
6143					// set column title
6144					if (typeof column['title'] !== 'undefined') {
6145						th.innerHTML = column.title;
6146						th.setAttribute('data-field', column.field);
6147						util.addClass(th, column.class);
6148						// set disable autoHide or force enable
6149						if (typeof column.autoHide !== 'undefined') {
6150							if (column.autoHide !== true) {
6151								th.setAttribute('data-autohide-disabled', column.autoHide);
6152							} else {
6153								th.setAttribute('data-autohide-enabled', column.autoHide);
6154							}
6155						}
6156						$(th).data(column);
6157					}
6158
6159					// set header attr option
6160					if (typeof column.attr !== 'undefined') {
6161						$.each(column.attr, function(key, val) {
6162							th.setAttribute(key, val);
6163						});
6164					}
6165
6166					// apply text align to thead/tfoot
6167					if (typeof column.textAlign !== 'undefined') {
6168						var align = typeof datatable.textAlign[column.textAlign] !== 'undefined' ? datatable.textAlign[column.textAlign] : '';
6169						util.addClass(th, align);
6170					}
6171				});
6172				Plugin.setupDOM(tablePart);
6173			},
6174
6175			/**
6176			 * Initiate to get remote or local data via ajax
6177			 */
6178			dataRender: function(action) {
6179				$(datatable.table).siblings('.' + pfx + 'datatable__pager').removeClass(pfx + 'datatable--paging-loaded');
6180
6181				var buildMeta = function() {
6182					datatable.dataSet = datatable.dataSet || [];
6183					Plugin.localDataUpdate();
6184					// local pagination meta
6185					var meta = Plugin.getDataSourceParam('pagination');
6186					if (meta.perpage === 0) {
6187						meta.perpage = options.data.pageSize || 10;
6188					}
6189					meta.total = datatable.dataSet.length;
6190					var start = Math.max(meta.perpage * (meta.page - 1), 0);
6191					var end = Math.min(start + meta.perpage, meta.total);
6192					datatable.dataSet = $(datatable.dataSet).slice(start, end);
6193					return meta;
6194				};
6195
6196				var afterGetData = function(result) {
6197					var localPagingCallback = function(ctx, meta) {
6198						if (!$(ctx.pager).hasClass(pfx + 'datatable--paging-loaded')) {
6199							$(ctx.pager).remove();
6200							ctx.init(meta);
6201						}
6202						$(ctx.pager).off().on(pfx + 'datatable--on-goto-page', function(e) {
6203							$(ctx.pager).remove();
6204							ctx.init(meta);
6205						});
6206
6207						var start = Math.max(meta.perpage * (meta.page - 1), 0);
6208						var end = Math.min(start + meta.perpage, meta.total);
6209
6210						Plugin.localDataUpdate();
6211						datatable.dataSet = $(datatable.dataSet).slice(start, end);
6212
6213						// insert data into table content
6214						Plugin.insertData();
6215					};
6216
6217					$(datatable.wrap).removeClass(pfx + 'datatable--error');
6218					// pagination enabled
6219					if (options.pagination) {
6220						if (options.data.serverPaging && options.data.type !== 'local') {
6221							// server pagination
6222							var serverMeta = Plugin.getObject('meta', result || null);
6223							if (serverMeta !== null) {
6224								Plugin.pagingObject = Plugin.paging(serverMeta);
6225							} else {
6226								// no meta object from server response, fallback to local pagination
6227								Plugin.pagingObject = Plugin.paging(buildMeta(), localPagingCallback);
6228							}
6229						} else {
6230							// local pagination can be used by remote data also
6231							Plugin.pagingObject = Plugin.paging(buildMeta(), localPagingCallback);
6232						}
6233					} else {
6234						// pagination is disabled
6235						Plugin.localDataUpdate();
6236					}
6237					// insert data into table content
6238					Plugin.insertData();
6239				};
6240
6241				// get local datasource
6242				if (options.data.type === 'local'
6243					// for remote json datasource
6244					// || typeof options.data.source.read === 'undefined' && datatable.dataSet !== null
6245					// for remote datasource, server sorting is disabled and data already received from remote
6246					|| options.data.serverSorting === false && action === 'sort'
6247					|| options.data.serverFiltering === false && action === 'search'
6248				) {
6249					setTimeout(function() {
6250						afterGetData();
6251						Plugin.setAutoColumns();
6252					});
6253					return;
6254				}
6255
6256				// getting data from remote only
6257				Plugin.getData().done(afterGetData);
6258			},
6259
6260			/**
6261			 * Process ajax data
6262			 */
6263			insertData: function() {
6264				datatable.dataSet = datatable.dataSet || [];
6265				var params = Plugin.getDataSourceParam();
6266
6267				// get row attributes
6268				var pagination = params.pagination;
6269				var start = (Math.max(pagination.page, 1) - 1) * pagination.perpage;
6270				var end = Math.min(pagination.page, pagination.pages) * pagination.perpage;
6271				var rowProps = {};
6272				if (typeof options.data.attr.rowProps !== 'undefined' && options.data.attr.rowProps.length) {
6273					rowProps = options.data.attr.rowProps.slice(start, end);
6274				}
6275
6276				var tableBody = document.createElement('tbody');
6277				tableBody.style.visibility = 'hidden';
6278				var colLength = options.columns.length;
6279
6280				$.each(datatable.dataSet, function(rowIndex, row) {
6281					var tr = document.createElement('tr');
6282					tr.setAttribute('data-row', rowIndex);
6283					// keep data object to row
6284					$(tr).data('obj', row);
6285
6286					if (typeof rowProps[rowIndex] !== 'undefined') {
6287						$.each(rowProps[rowIndex], function() {
6288							tr.setAttribute(this.name, this.value);
6289						});
6290					}
6291
6292					var cellIndex = 0;
6293					var tds = [];
6294					for (var a = 0; a < colLength; a += 1) {
6295						var column = options.columns[a];
6296						var classes = [];
6297						// add sorted class to cells
6298						if (Plugin.getObject('sort.field', params) === column.field) {
6299							classes.push(pfx + 'datatable__cell--sorted');
6300						}
6301
6302						// apply text align
6303						if (typeof column.textAlign !== 'undefined') {
6304							var align = typeof datatable.textAlign[column.textAlign] !== 'undefined' ? datatable.textAlign[column.textAlign] : '';
6305							classes.push(align);
6306						}
6307
6308						// var classAttr = '';
6309						if (typeof column.class !== 'undefined') {
6310							classes.push(column.class);
6311						}
6312
6313						var td = document.createElement('td');
6314						util.addClass(td, classes.join(' '));
6315						td.setAttribute('data-field', column.field);
6316						// set disable autoHide or force enable
6317						if (typeof column.autoHide !== 'undefined') {
6318							if (column.autoHide !== true) {
6319								td.setAttribute('data-autohide-disabled', column.autoHide);
6320							} else {
6321								td.setAttribute('data-autohide-enabled', column.autoHide);
6322							}
6323						}
6324						td.innerHTML = Plugin.getObject(column.field, row);
6325						tr.appendChild(td);
6326					}
6327
6328					tableBody.appendChild(tr);
6329				});
6330
6331				// display no records message
6332				if (datatable.dataSet.length === 0) {
6333					var errorSpan = document.createElement('span');
6334					util.addClass(errorSpan, pfx + 'datatable--error');
6335					errorSpan.innerHTML = Plugin.getOption('translate.records.noRecords');
6336					tableBody.appendChild(errorSpan);
6337					$(datatable.wrap).addClass(pfx + 'datatable--error ' + pfx + 'datatable--loaded');
6338					Plugin.spinnerCallback(false);
6339				}
6340
6341				// replace existing table body
6342				$(datatable.tableBody).replaceWith(tableBody);
6343				datatable.tableBody = tableBody;
6344
6345				// layout update
6346				Plugin.setupDOM(datatable.table);
6347				Plugin.setupCellField([datatable.tableBody]);
6348				Plugin.setupTemplateCell(datatable.tableBody);
6349				Plugin.layoutUpdate();
6350			},
6351
6352			updateTableComponents: function() {
6353				datatable.tableHead = $(datatable.table).children('thead').get(0);
6354				datatable.tableBody = $(datatable.table).children('tbody').get(0);
6355				datatable.tableFoot = $(datatable.table).children('tfoot').get(0);
6356			},
6357
6358			/**
6359			 * Call ajax for raw JSON data
6360			 */
6361			getData: function() {
6362				// Plugin.spinnerCallback(true);
6363
6364				var ajaxParams = {
6365					dataType: 'json',
6366					method: 'POST',
6367					data: {},
6368					timeout: Plugin.getOption('data.source.read.timeout') || 30000,
6369				};
6370
6371				if (options.data.type === 'local') {
6372					ajaxParams.url = options.data.source;
6373				}
6374
6375				if (options.data.type === 'remote') {
6376					var data = Plugin.getDataSourceParam();
6377					// remove if server params is not enabled
6378					if (!Plugin.getOption('data.serverPaging')) {
6379						delete data['pagination'];
6380					}
6381					if (!Plugin.getOption('data.serverSorting')) {
6382						delete data['sort'];
6383					}
6384					ajaxParams.data = $.extend({}, ajaxParams.data, Plugin.getOption('data.source.read.params'), data);
6385					ajaxParams = $.extend({}, ajaxParams, Plugin.getOption('data.source.read'));
6386
6387					if (typeof ajaxParams.url !== 'string') ajaxParams.url = Plugin.getOption('data.source.read');
6388					if (typeof ajaxParams.url !== 'string') ajaxParams.url = Plugin.getOption('data.source');
6389					// ajaxParams.data = $.extend(ajaxParams.data, data.pagination);
6390				}
6391
6392				return $.ajax(ajaxParams).done(function(response, textStatus, jqXHR) {
6393					datatable.lastResponse = response;
6394					// extendible data map callback for custom datasource
6395					datatable.dataSet = datatable.originalDataSet = Plugin.dataMapCallback(response);
6396					Plugin.setAutoColumns();
6397					$(datatable).trigger(pfx + 'datatable--on-ajax-done', [datatable.dataSet]);
6398				}).fail(function(jqXHR, textStatus, errorThrown) {
6399					$(datatable).trigger(pfx + 'datatable--on-ajax-fail', [jqXHR]);
6400					$(datatable.tableBody).html($('<span/>').addClass(pfx + 'datatable--error').html(Plugin.getOption('translate.records.noRecords')));
6401					$(datatable.wrap).addClass(pfx + 'datatable--error ' + pfx + 'datatable--loaded');
6402					Plugin.spinnerCallback(false);
6403				}).always(function() {
6404				});
6405			},
6406
6407			/**
6408			 * Pagination object
6409			 * @param meta if null, local pagination, otherwise remote
6410			 *     pagination
6411			 * @param callback for update data when navigating page
6412			 */
6413			paging: function(meta, callback) {
6414				var pg = {
6415					meta: null,
6416					pager: null,
6417					paginateEvent: null,
6418					pagerLayout: {pagination: null, info: null},
6419					callback: null,
6420					init: function(meta) {
6421						pg.meta = meta;
6422
6423						// parse pagination meta to integer
6424						pg.meta.page = parseInt(pg.meta.page);
6425						pg.meta.pages = parseInt(pg.meta.pages);
6426						pg.meta.perpage = parseInt(pg.meta.perpage);
6427						pg.meta.total = parseInt(pg.meta.total);
6428
6429						// always recount total pages
6430						pg.meta.pages = Math.max(Math.ceil(pg.meta.total / pg.meta.perpage), 1);
6431
6432						// current page must be not over than total pages
6433						if (pg.meta.page > pg.meta.pages) pg.meta.page = pg.meta.pages;
6434
6435						// set unique event name between tables
6436						pg.paginateEvent = Plugin.getTablePrefix('paging');
6437
6438						pg.pager = $(datatable.table).siblings('.' + pfx + 'datatable__pager');
6439						if ($(pg.pager).hasClass(pfx + 'datatable--paging-loaded')) return;
6440
6441						// if class .'+pfx+'datatable--paging-loaded not exist, recreate pagination
6442						$(pg.pager).remove();
6443
6444						// if no pages available
6445						if (pg.meta.pages === 0) return;
6446
6447						// update datasource params
6448						Plugin.setDataSourceParam('pagination', {
6449							page: pg.meta.page,
6450							pages: pg.meta.pages,
6451							perpage: pg.meta.perpage,
6452							total: pg.meta.total,
6453						});
6454
6455						// default callback function, contains remote pagination handler
6456						pg.callback = pg.serverCallback;
6457						// custom callback function
6458						if (typeof callback === 'function') pg.callback = callback;
6459
6460						pg.addPaginateEvent();
6461						pg.populate();
6462
6463						pg.meta.page = Math.max(pg.meta.page || 1, pg.meta.page);
6464
6465						$(datatable).trigger(pg.paginateEvent, pg.meta);
6466
6467						pg.pagingBreakpoint.call();
6468						$(window).resize(pg.pagingBreakpoint);
6469					},
6470					serverCallback: function(ctx, meta) {
6471						Plugin.dataRender();
6472					},
6473					populate: function() {
6474						var icons = Plugin.getOption('layout.icons.pagination');
6475						var title = Plugin.getOption('translate.toolbar.pagination.items.default');
6476						// pager root element
6477						pg.pager = $('<div/>').addClass(pfx + 'datatable__pager ' + pfx + 'datatable--paging-loaded');
6478						// numbering links
6479						var pagerNumber = $('<ul/>').addClass(pfx + 'datatable__pager-nav');
6480						pg.pagerLayout['pagination'] = pagerNumber;
6481
6482						// pager first/previous button
6483						$('<li/>').
6484							append($('<a/>').
6485								attr('title', title.first).
6486								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--first').
6487								append($('<i/>').addClass(icons.first)).
6488								on('click', pg.gotoMorePage).
6489								attr('data-page', 1)).
6490							appendTo(pagerNumber);
6491						$('<li/>').
6492							append($('<a/>').
6493								attr('title', title.prev).
6494								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--prev').
6495								append($('<i/>').addClass(icons.prev)).
6496								on('click', pg.gotoMorePage)).
6497							appendTo(pagerNumber);
6498
6499						// more previous pages
6500						$('<li/>').
6501							append($('<a/>').
6502								attr('title', title.more).
6503								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--more-prev').
6504								html($('<i/>').addClass(icons.more)).
6505								on('click', pg.gotoMorePage)).
6506							appendTo(pagerNumber);
6507
6508						$('<li/>').append($('<input/>').attr('type', 'text').addClass(pfx + 'pager-input form-control').attr('title', title.input).on('keyup', function() {
6509							// on keyup update [data-page]
6510							$(this).attr('data-page', Math.abs($(this).val()));
6511						}).on('keypress', function(e) {
6512							// on keypressed enter button
6513							if (e.which === 13) pg.gotoMorePage(e);
6514						})).appendTo(pagerNumber);
6515
6516						var pagesNumber = Plugin.getOption('toolbar.items.pagination.pages.desktop.pagesNumber');
6517						var end = Math.ceil(pg.meta.page / pagesNumber) * pagesNumber;
6518						var start = end - pagesNumber;
6519						if (end > pg.meta.pages) {
6520							end = pg.meta.pages;
6521						}
6522						for (var x = start; x < end; x++) {
6523							var pageNumber = x + 1;
6524							$('<li/>').
6525								append($('<a/>').
6526									addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link-number').
6527									text(pageNumber).
6528									attr('data-page', pageNumber).
6529									attr('title', pageNumber).
6530									on('click', pg.gotoPage)).
6531								appendTo(pagerNumber);
6532						}
6533
6534						// more next pages
6535						$('<li/>').
6536							append($('<a/>').
6537								attr('title', title.more).
6538								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--more-next').
6539								html($('<i/>').addClass(icons.more)).
6540								on('click', pg.gotoMorePage)).
6541							appendTo(pagerNumber);
6542
6543						// pager next/last button
6544						$('<li/>').
6545							append($('<a/>').
6546								attr('title', title.next).
6547								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--next').
6548								append($('<i/>').addClass(icons.next)).
6549								on('click', pg.gotoMorePage)).
6550							appendTo(pagerNumber);
6551						$('<li/>').
6552							append($('<a/>').
6553								attr('title', title.last).
6554								addClass(pfx + 'datatable__pager-link ' + pfx + 'datatable__pager-link--last').
6555								append($('<i/>').addClass(icons.last)).
6556								on('click', pg.gotoMorePage).
6557								attr('data-page', pg.meta.pages)).
6558							appendTo(pagerNumber);
6559
6560						// page info
6561						if (Plugin.getOption('toolbar.items.info')) {
6562							pg.pagerLayout['info'] = $('<div/>').addClass(pfx + 'datatable__pager-info').append($('<span/>').addClass(pfx + 'datatable__pager-detail'));
6563						}
6564
6565						$.each(Plugin.getOption('toolbar.layout'), function(i, layout) {
6566							$(pg.pagerLayout[layout]).appendTo(pg.pager);
6567						});
6568
6569						// page size select
6570						var pageSizeSelect = $('<select/>').
6571							addClass('selectpicker ' + pfx + 'datatable__pager-size').
6572							attr('title', Plugin.getOption('translate.toolbar.pagination.items.default.select')).
6573							attr('data-width', '60px').
6574							attr('data-container', 'body').
6575							val(pg.meta.perpage).
6576							on('change', pg.updatePerpage).
6577							prependTo(pg.pagerLayout['info']);
6578
6579						var pageSizes = Plugin.getOption('toolbar.items.pagination.pageSizeSelect');
6580						// default value here, to fix override option by user
6581						if (pageSizes.length == 0) pageSizes = [10, 20, 30, 50, 100];
6582						$.each(pageSizes, function(i, size) {
6583							var display = size;
6584							if (size === -1) display = Plugin.getOption('translate.toolbar.pagination.items.default.all');
6585							$('<option/>').attr('value', size).html(display).appendTo(pageSizeSelect);
6586						});
6587
6588						// init selectpicker to dropdown
6589						$(datatable).ready(function() {
6590							$('.selectpicker').
6591								selectpicker().
6592								on('hide.bs.select', function() {
6593									// fix dropup arrow icon on hide
6594									$(this).closest('.bootstrap-select').removeClass('dropup');
6595								}).
6596								siblings('.dropdown-toggle').
6597								attr('title', Plugin.getOption('translate.toolbar.pagination.items.default.select'));
6598						});
6599
6600						pg.paste();
6601					},
6602					paste: function() {
6603						// insert pagination based on placement position, top|bottom
6604						$.each($.unique(Plugin.getOption('toolbar.placement')),
6605							function(i, position) {
6606								if (position === 'bottom') {
6607									$(pg.pager).clone(true).insertAfter(datatable.table);
6608								}
6609								if (position === 'top') {
6610									// pager top need some extra space
6611									$(pg.pager).clone(true).addClass(pfx + 'datatable__pager--top').insertBefore(datatable.table);
6612								}
6613							});
6614					},
6615					gotoMorePage: function(e) {
6616						e.preventDefault();
6617						// $(this) is a link of .'+pfx+'datatable__pager-link
6618
6619						if ($(this).attr('disabled') === 'disabled') return false;
6620
6621						var page = $(this).attr('data-page');
6622
6623						// event from text input
6624						if (typeof page === 'undefined') {
6625							page = $(e.target).attr('data-page');
6626						}
6627
6628						pg.openPage(parseInt(page));
6629						return false;
6630					},
6631					gotoPage: function(e) {
6632						e.preventDefault();
6633						// prevent from click same page number
6634						if ($(this).hasClass(pfx + 'datatable__pager-link--active')) return;
6635
6636						pg.openPage(parseInt($(this).data('page')));
6637					},
6638					openPage: function(page) {
6639						// currentPage is 1-based index
6640						pg.meta.page = parseInt(page);
6641
6642						$(datatable).trigger(pg.paginateEvent, pg.meta);
6643						pg.callback(pg, pg.meta);
6644
6645						// update page callback function
6646						$(pg.pager).trigger(pfx + 'datatable--on-goto-page', pg.meta);
6647					},
6648					updatePerpage: function(e) {
6649						e.preventDefault();
6650						// if (Plugin.getOption('layout.height') === null) {
6651						// fix white space, when perpage is set from many records to less records
6652						// $('html, body').animate({scrollTop: $(datatable).position().top});
6653						// }
6654
6655						pg.pager = $(datatable.table).siblings('.' + pfx + 'datatable__pager').removeClass(pfx + 'datatable--paging-loaded');
6656
6657						// on change select page size
6658						if (e.originalEvent) {
6659							pg.meta.perpage = parseInt($(this).val());
6660						}
6661
6662						$(pg.pager).find('select.' + pfx + 'datatable__pager-size').val(pg.meta.perpage).attr('data-selected', pg.meta.perpage);
6663
6664						// update datasource params
6665						Plugin.setDataSourceParam('pagination', {
6666							page: pg.meta.page,
6667							pages: pg.meta.pages,
6668							perpage: pg.meta.perpage,
6669							total: pg.meta.total,
6670						});
6671
6672						// update page callback function
6673						$(pg.pager).trigger(pfx + 'datatable--on-update-perpage', pg.meta);
6674						$(datatable).trigger(pg.paginateEvent, pg.meta);
6675						pg.callback(pg, pg.meta);
6676
6677						// update pagination info
6678						pg.updateInfo.call();
6679					},
6680					addPaginateEvent: function(e) {
6681						// pagination event
6682						$(datatable).off(pg.paginateEvent).on(pg.paginateEvent, function(e, meta) {
6683							Plugin.spinnerCallback(true);
6684
6685							pg.pager = $(datatable.table).siblings('.' + pfx + 'datatable__pager');
6686							var pagerNumber = $(pg.pager).find('.' + pfx + 'datatable__pager-nav');
6687
6688							// set sync active page class
6689							$(pagerNumber).find('.' + pfx + 'datatable__pager-link--active').removeClass(pfx + 'datatable__pager-link--active');
6690							$(pagerNumber).find('.' + pfx + 'datatable__pager-link-number[data-page="' + meta.page + '"]').addClass(pfx + 'datatable__pager-link--active');
6691
6692							// set next and previous link page number
6693							$(pagerNumber).find('.' + pfx + 'datatable__pager-link--prev').attr('data-page', Math.max(meta.page - 1, 1));
6694							$(pagerNumber).find('.' + pfx + 'datatable__pager-link--next').attr('data-page', Math.min(meta.page + 1, meta.pages));
6695
6696							// current page input value sync
6697							$(pg.pager).each(function() {
6698								$(this).find('.' + pfx + 'pager-input[type="text"]').prop('value', meta.page);
6699							});
6700
6701							$(pg.pager).find('.' + pfx + 'datatable__pager-nav').show();
6702							if (meta.pages <= 1) {
6703								// hide pager if has 1 page
6704								$(pg.pager).find('.' + pfx + 'datatable__pager-nav').hide();
6705							}
6706
6707							// update datasource params
6708							Plugin.setDataSourceParam('pagination', {
6709								page: pg.meta.page,
6710								pages: pg.meta.pages,
6711								perpage: pg.meta.perpage,
6712								total: pg.meta.total,
6713							});
6714
6715							$(pg.pager).find('select.' + pfx + 'datatable__pager-size').val(meta.perpage).attr('data-selected', meta.perpage);
6716
6717							// clear active rows
6718							$(datatable.table).find('.' + pfx + 'checkbox > [type="checkbox"]').prop('checked', false);
6719							$(datatable.table).find('.' + pfx + 'datatable__row--active').removeClass(pfx + 'datatable__row--active');
6720
6721							pg.updateInfo.call();
6722							pg.pagingBreakpoint.call();
6723							// Plugin.resetScroll();
6724						});
6725					},
6726					updateInfo: function() {
6727						var start = Math.max(pg.meta.perpage * (pg.meta.page - 1) + 1, 1);
6728						var end = Math.min(start + pg.meta.perpage - 1, pg.meta.total);
6729						// page info update
6730						$(pg.pager).find('.' + pfx + 'datatable__pager-info').find('.' + pfx + 'datatable__pager-detail').html(Plugin.dataPlaceholder(
6731							Plugin.getOption('translate.toolbar.pagination.items.info'), {
6732								// set start page 0 if the is no records. eg. Showing 0 - 0 of 0
6733								start: pg.meta.total === 0 ? 0 : start,
6734								end: pg.meta.perpage === -1 ? pg.meta.total : end,
6735								pageSize: pg.meta.perpage === -1 ||
6736								pg.meta.perpage >= pg.meta.total
6737									? pg.meta.total
6738									: pg.meta.perpage,
6739								total: pg.meta.total,
6740							}));
6741					},
6742
6743					/**
6744					 * Update pagination layout breakpoint
6745					 */
6746					pagingBreakpoint: function() {
6747						// keep page links reference
6748						var pagerNumber = $(datatable.table).siblings('.' + pfx + 'datatable__pager').find('.' + pfx + 'datatable__pager-nav');
6749						if ($(pagerNumber).length === 0) return;
6750
6751						var currentPage = Plugin.getCurrentPage();
6752						var pagerInput = $(pagerNumber).find('.' + pfx + 'pager-input').closest('li');
6753
6754						// reset
6755						$(pagerNumber).find('li').show();
6756
6757						// pagination update
6758						$.each(Plugin.getOption('toolbar.items.pagination.pages'),
6759							function(mode, option) {
6760								if (util.isInResponsiveRange(mode)) {
6761									switch (mode) {
6762										case 'desktop':
6763										case 'tablet':
6764											var end = Math.ceil(currentPage / option.pagesNumber) *
6765												option.pagesNumber;
6766											var start = end - option.pagesNumber;
6767											$(pagerInput).hide();
6768											pg.meta = Plugin.getDataSourceParam('pagination');
6769											pg.paginationUpdate();
6770											break;
6771
6772										case 'mobile':
6773											$(pagerInput).show();
6774											$(pagerNumber).find('.' + pfx + 'datatable__pager-link--more-prev').closest('li').hide();
6775											$(pagerNumber).find('.' + pfx + 'datatable__pager-link--more-next').closest('li').hide();
6776											$(pagerNumber).find('.' + pfx + 'datatable__pager-link-number').closest('li').hide();
6777											break;
6778									}
6779
6780									return false;
6781								}
6782							});
6783					},
6784
6785					/**
6786					 * Update pagination number and button display
6787					 */
6788					paginationUpdate: function() {
6789						var pager = $(datatable.table).siblings('.' + pfx + 'datatable__pager').find('.' + pfx + 'datatable__pager-nav'),
6790							pagerMorePrev = $(pager).find('.' + pfx + 'datatable__pager-link--more-prev'),
6791							pagerMoreNext = $(pager).find('.' + pfx + 'datatable__pager-link--more-next'),
6792							pagerFirst = $(pager).find('.' + pfx + 'datatable__pager-link--first'),
6793							pagerPrev = $(pager).find('.' + pfx + 'datatable__pager-link--prev'),
6794							pagerNext = $(pager).find('.' + pfx + 'datatable__pager-link--next'),
6795							pagerLast = $(pager).find('.' + pfx + 'datatable__pager-link--last');
6796
6797						// get visible page
6798						var pagerNumber = $(pager).find('.' + pfx + 'datatable__pager-link-number');
6799						// get page before of first visible
6800						var morePrevPage = Math.max($(pagerNumber).first().data('page') - 1, 1);
6801						$(pagerMorePrev).each(function(i, prev) {
6802							$(prev).attr('data-page', morePrevPage);
6803						});
6804						// show/hide <li>
6805						if (morePrevPage === 1) {
6806							$(pagerMorePrev).parent().hide();
6807						} else {
6808							$(pagerMorePrev).parent().show();
6809						}
6810
6811						// get page after of last visible
6812						var moreNextPage = Math.min($(pagerNumber).last().data('page') + 1,
6813							pg.meta.pages);
6814						$(pagerMoreNext).each(function(i, prev) {
6815							$(pagerMoreNext).attr('data-page', moreNextPage).show();
6816						});
6817
6818						// show/hide <li>
6819						if (moreNextPage === pg.meta.pages
6820							// missing dot fix when last hidden page is one left
6821							&& moreNextPage === $(pagerNumber).last().data('page')) {
6822							$(pagerMoreNext).parent().hide();
6823						} else {
6824							$(pagerMoreNext).parent().show();
6825						}
6826
6827						// begin/end of pages
6828						if (pg.meta.page === 1) {
6829							$(pagerFirst).attr('disabled', true).addClass(pfx + 'datatable__pager-link--disabled');
6830							$(pagerPrev).attr('disabled', true).addClass(pfx + 'datatable__pager-link--disabled');
6831						} else {
6832							$(pagerFirst).removeAttr('disabled').removeClass(pfx + 'datatable__pager-link--disabled');
6833							$(pagerPrev).removeAttr('disabled').removeClass(pfx + 'datatable__pager-link--disabled');
6834						}
6835						if (pg.meta.page === pg.meta.pages) {
6836							$(pagerNext).attr('disabled', true).addClass(pfx + 'datatable__pager-link--disabled');
6837							$(pagerLast).attr('disabled', true).addClass(pfx + 'datatable__pager-link--disabled');
6838						} else {
6839							$(pagerNext).removeAttr('disabled').removeClass(pfx + 'datatable__pager-link--disabled');
6840							$(pagerLast).removeAttr('disabled').removeClass(pfx + 'datatable__pager-link--disabled');
6841						}
6842
6843						// display more buttons
6844						var nav = Plugin.getOption('toolbar.items.pagination.navigation');
6845						if (!nav.first) $(pagerFirst).remove();
6846						if (!nav.prev) $(pagerPrev).remove();
6847						if (!nav.next) $(pagerNext).remove();
6848						if (!nav.last) $(pagerLast).remove();
6849						if (!nav.more) {
6850							$(pagerMorePrev).remove();
6851							$(pagerMoreNext).remove();
6852						}
6853					},
6854				};
6855				pg.init(meta);
6856				return pg;
6857			},
6858
6859			/**
6860			 * Hide/show table cell defined by
6861			 * options[columns][i][responsive][visible/hidden]
6862			 */
6863			columnHide: function() {
6864				var screen = util.getViewPort().width;
6865				// foreach columns setting
6866				$.each(options.columns, function(i, column) {
6867					if (typeof column.responsive !== 'undefined' || typeof column.visible !== 'undefined') {
6868						var field = column.field;
6869						var tds = $.grep($(datatable.table).find('.' + pfx + 'datatable__cell'), function(n, i) {
6870							return field === $(n).data('field');
6871						});
6872
6873						setTimeout(function () {
6874							// hide by force
6875							if (Plugin.getObject('visible', column) === false) {
6876								$(tds).hide();
6877							} else {
6878								// show/hide by responsive breakpoint
6879								if (util.getBreakpoint(Plugin.getObject('responsive.hidden', column)) >= screen) {
6880									$(tds).hide();
6881								} else {
6882									$(tds).show();
6883								}
6884								if (util.getBreakpoint(Plugin.getObject('responsive.visible', column)) <= screen) {
6885									$(tds).show();
6886								} else {
6887									$(tds).hide();
6888								}
6889							}
6890						});
6891					}
6892				});
6893			},
6894
6895			/**
6896			 * Setup sub datatable
6897			 */
6898			setupSubDatatable: function() {
6899				var subTableCallback = Plugin.getOption('detail.content');
6900				if (typeof subTableCallback !== 'function') return;
6901
6902				// subtable already exist
6903				if ($(datatable.table).find('.' + pfx + 'datatable__subtable').length > 0) return;
6904
6905				$(datatable.wrap).addClass(pfx + 'datatable--subtable');
6906
6907				options.columns[0]['subtable'] = true;
6908
6909				// toggle on open sub table
6910				var toggleSubTable = function(e) {
6911					e.preventDefault();
6912					// get parent row of this subtable
6913					var parentRow = $(this).closest('.' + pfx + 'datatable__row');
6914
6915					// get subtable row for sub table
6916					var subTableRow = $(parentRow).next('.' + pfx + 'datatable__row-subtable');
6917					if ($(subTableRow).length === 0) {
6918						// prepare DOM for sub table, each <tr> as parent and add <tr> as child table
6919						subTableRow = $('<tr/>').
6920							addClass(pfx + 'datatable__row-subtable ' + pfx + 'datatable__row-loading').
6921							hide().
6922							append($('<td/>').addClass(pfx + 'datatable__subtable').attr('colspan', Plugin.getTotalColumns()));
6923						$(parentRow).after(subTableRow);
6924						// add class to even row
6925						if ($(parentRow).hasClass(pfx + 'datatable__row--even')) {
6926							$(subTableRow).addClass(pfx + 'datatable__row-subtable--even');
6927						}
6928					}
6929
6930					$(subTableRow).toggle();
6931
6932					var subTable = $(subTableRow).find('.' + pfx + 'datatable__subtable');
6933
6934					// get id from first column of parent row
6935					var primaryKey = $(this).closest('[data-field]:first-child').find('.' + pfx + 'datatable__toggle-subtable').data('value');
6936
6937					var icon = $(this).find('i').removeAttr('class');
6938
6939					// prevent duplicate datatable init
6940					if ($(parentRow).hasClass(pfx + 'datatable__row--subtable-expanded')) {
6941						$(icon).addClass(Plugin.getOption('layout.icons.rowDetail.collapse'));
6942						// remove expand class from parent row
6943						$(parentRow).removeClass(pfx + 'datatable__row--subtable-expanded');
6944						// trigger event on collapse
6945						$(datatable).trigger(pfx + 'datatable--on-collapse-subtable', [parentRow]);
6946					} else {
6947						// expand and run callback function
6948						$(icon).addClass(Plugin.getOption('layout.icons.rowDetail.expand'));
6949						// add expand class to parent row
6950						$(parentRow).addClass(pfx + 'datatable__row--subtable-expanded');
6951						// trigger event on expand
6952						$(datatable).trigger(pfx + 'datatable--on-expand-subtable', [parentRow]);
6953					}
6954
6955					// prevent duplicate datatable init
6956					if ($(subTable).find('.' + pfx + 'datatable').length === 0) {
6957						// get data by primary id
6958						$.map(datatable.dataSet, function(n, i) {
6959							// primary id must be at the first column, otherwise e.data will be undefined
6960							if (primaryKey === n[options.columns[0].field]) {
6961								e.data = n;
6962								return true;
6963							}
6964							return false;
6965						});
6966
6967						// deprecated in v5.0.6
6968						e.detailCell = subTable;
6969
6970						e.parentRow = parentRow;
6971						e.subTable = subTable;
6972
6973						// run callback with event
6974						subTableCallback(e);
6975
6976						$(subTable).children('.' + pfx + 'datatable').on(pfx + 'datatable--on-init', function(e) {
6977							$(subTableRow).removeClass(pfx + 'datatable__row-loading');
6978						});
6979						if (Plugin.getOption('data.type') === 'local') {
6980							$(subTableRow).removeClass(pfx + 'datatable__row-loading');
6981						}
6982					}
6983				};
6984
6985				var columns = options.columns;
6986				$(datatable.tableBody).find('.' + pfx + 'datatable__row').each(function(tri, tr) {
6987					$(tr).find('.' + pfx + 'datatable__cell').each(function(tdi, td) {
6988						// get column settings by field
6989						var column = $.grep(columns, function(n, i) {
6990							return $(td).data('field') === n.field;
6991						})[0];
6992						if (typeof column !== 'undefined') {
6993							var value = $(td).text();
6994							// enable column subtable toggle
6995							if (typeof column.subtable !== 'undefined' && column.subtable) {
6996								// check if subtable toggle exist
6997								if ($(td).find('.' + pfx + 'datatable__toggle-subtable').length > 0) return;
6998								// append subtable toggle
6999								$(td).
7000									html($('<a/>').
7001										addClass(pfx + 'datatable__toggle-subtable').
7002										attr('href', '#').
7003										attr('data-value', value).
7004										attr('title', Plugin.getOption('detail.title')).
7005										on('click', toggleSubTable).
7006										append($('<i/>').css('width', $(td).data('width')).addClass(Plugin.getOption('layout.icons.rowDetail.collapse'))));
7007							}
7008						}
7009					});
7010				});
7011
7012				// $(datatable.tableHead).find('.'+pfx+'-datatable__row').first()
7013			},
7014
7015			/**
7016			 * Datasource mapping callback
7017			 */
7018			dataMapCallback: function(raw) {
7019				// static dataset array
7020				var dataSet = raw;
7021				// dataset mapping callback
7022				if (typeof Plugin.getOption('data.source.read.map') === 'function') {
7023					return Plugin.getOption('data.source.read.map')(raw);
7024				} else {
7025					// default data mapping fallback
7026					if (typeof raw !== 'undefined' && typeof raw.data !== 'undefined') {
7027						dataSet = raw.data;
7028					}
7029				}
7030				return dataSet;
7031			},
7032
7033			isSpinning: false,
7034			/**
7035			 * BlockUI spinner callback
7036			 * @param block
7037			 * @param target
7038			 */
7039			spinnerCallback: function(block, target) {
7040				if (typeof target === 'undefined') target = datatable;
7041				// get spinner options
7042				var spinnerOptions = Plugin.getOption('layout.spinner');
7043				// spinner is disabled
7044				if (typeof spinnerOptions === 'undefined' || !spinnerOptions) {
7045					return;
7046				}
7047				if (block) {
7048					if (!Plugin.isSpinning) {
7049						if (typeof spinnerOptions.message !== 'undefined' && spinnerOptions.message === true) {
7050							// use default spinner message from translation
7051							spinnerOptions.message = Plugin.getOption('translate.records.processing');
7052						}
7053						Plugin.isSpinning = true;
7054						if (typeof app !== 'undefined') {
7055							app.block(target, spinnerOptions);
7056						}
7057					}
7058				} else {
7059					Plugin.isSpinning = false;
7060					if (typeof app !== 'undefined') {
7061						app.unblock(target);
7062					}
7063				}
7064			},
7065
7066			/**
7067			 * Default sort callback function
7068			 * @param data
7069			 * @param sort
7070			 * @param column
7071			 * @returns {*|Array.<T>|{sort, field}|{asc, desc}}
7072			 */
7073			sortCallback: function(data, sort, column) {
7074				var type = column['type'] || 'string';
7075				var format = column['format'] || '';
7076				var field = column['field'];
7077
7078				return $(data).sort(function(a, b) {
7079					var aField = a[field];
7080					var bField = b[field];
7081
7082					switch (type) {
7083						case 'date':
7084							if (typeof moment === 'undefined') {
7085								throw new Error('Moment.js is required.');
7086							}
7087							var diff = moment(aField, format).diff(moment(bField, format));
7088							if (sort === 'asc') {
7089								return diff > 0 ? 1 : diff < 0 ? -1 : 0;
7090							} else {
7091								return diff < 0 ? 1 : diff > 0 ? -1 : 0;
7092							}
7093							break;
7094
7095						case 'number':
7096							if (isNaN(parseFloat(aField)) && aField != null) {
7097								aField = Number(aField.replace(/[^0-9\.-]+/g, ''));
7098							}
7099							if (isNaN(parseFloat(bField)) && bField != null) {
7100								bField = Number(bField.replace(/[^0-9\.-]+/g, ''));
7101							}
7102							aField = parseFloat(aField);
7103							bField = parseFloat(bField);
7104							if (sort === 'asc') {
7105								return aField > bField ? 1 : aField < bField ? -1 : 0;
7106							} else {
7107								return aField < bField ? 1 : aField > bField ? -1 : 0;
7108							}
7109							break;
7110
7111						case 'html':
7112							return $(data).sort(function(a, b) {
7113								// get the text only from html
7114								aField = $(a[field]).text();
7115								bField = $(b[field]).text();
7116								// sort
7117								if (sort === 'asc') {
7118									return aField > bField ? 1 : aField < bField ? -1 : 0;
7119								} else {
7120									return aField < bField ? 1 : aField > bField ? -1 : 0;
7121								}
7122							});
7123							break;
7124
7125						case 'string':
7126						default:
7127							if (sort === 'asc') {
7128								return aField > bField ? 1 : aField < bField ? -1 : 0;
7129							} else {
7130								return aField < bField ? 1 : aField > bField ? -1 : 0;
7131							}
7132							break;
7133					}
7134				});
7135			},
7136
7137			/**
7138			 * Custom debug log
7139			 * @param text
7140			 * @param obj
7141			 */
7142			log: function(text, obj) {
7143				if (typeof obj === 'undefined') obj = '';
7144				if (datatable.debug) {
7145					console.log(text, obj);
7146				}
7147			},
7148
7149			/**
7150			 * Auto hide columnds overflow in row
7151			 */
7152			autoHide: function() {
7153				var hiddenExist = false;
7154				// force hide enabled
7155				var hidDefault = $(datatable.table).find('[data-autohide-enabled]');
7156				if (hidDefault.length) {
7157					hiddenExist = true;
7158					hidDefault.hide();
7159				}
7160
7161				var toggleHiddenColumns = function(e) {
7162					e.preventDefault();
7163
7164					var row = $(this).closest('.' + pfx + 'datatable__row');
7165					var detailRow = $(row).next();
7166
7167					if (!$(detailRow).hasClass(pfx + 'datatable__row-detail')) {
7168						$(this).find('i').removeClass(Plugin.getOption('layout.icons.rowDetail.collapse')).addClass(Plugin.getOption('layout.icons.rowDetail.expand'));
7169
7170						var hiddenCells = $(row).find('.' + pfx + 'datatable__cell:hidden');
7171						var clonedCells = hiddenCells.clone().show();
7172
7173						detailRow = $('<tr/>').addClass(pfx + 'datatable__row-detail').insertAfter(row);
7174						var detailRowTd = $('<td/>').addClass(pfx + 'datatable__detail').attr('colspan', Plugin.getTotalColumns()).appendTo(detailRow);
7175
7176						var detailSubTable = $('<table/>');
7177						$(clonedCells).each(function() {
7178							var field = $(this).data('field');
7179							var column = $.grep(options.columns, function(n, i) {
7180								return field === n.field;
7181							})[0];
7182							if (typeof column === 'undefined' || column.visible !== false) {
7183								$(detailSubTable).
7184										append($('<tr class="' + pfx + 'datatable__row"></tr>').
7185												append($('<td class="' + pfx + 'datatable__cell"></td>').append($('<span/>').append(column.title))).
7186												append(this));
7187							}
7188						});
7189						$(detailRowTd).append(detailSubTable);
7190
7191					} else {
7192						$(this).find('i').removeClass(Plugin.getOption('layout.icons.rowDetail.expand')).addClass(Plugin.getOption('layout.icons.rowDetail.collapse'));
7193						$(detailRow).remove();
7194					}
7195				};
7196
7197				setTimeout(function () {
7198					$(datatable.table).find('.' + pfx + 'datatable__cell').show();
7199					$(datatable.tableBody).each(function() {
7200						var recursive = 0;
7201						while ($(this)[0].offsetWidth < $(this)[0].scrollWidth && recursive < options.columns.length) {
7202							$(datatable.table).find('.' + pfx + 'datatable__row').each(function(i) {
7203								var cell = $(this).find('.' + pfx + 'datatable__cell:not(:hidden):not([data-autohide-disabled])').last();
7204								$(cell).hide();
7205								hiddenExist = true;
7206							});
7207							recursive++;
7208						}
7209					});
7210
7211					if (hiddenExist) {
7212						// toggle show hidden columns
7213						$(datatable.tableBody).find('.' + pfx + 'datatable__row').each(function() {
7214							// if no toggle yet
7215							if($(this).find('.' + pfx + 'datatable__toggle-detail').length === 0) {
7216								// add toggle
7217								$(this).prepend($('<td/>').
7218										addClass(pfx + 'datatable__cell ' + pfx + 'datatable__toggle-detail').
7219										append($('<a/>').
7220											addClass(pfx + 'datatable__toggle-detail').
7221											attr('href', '').
7222											on('click', toggleHiddenColumns).
7223											append('<i class="' + Plugin.getOption('layout.icons.rowDetail.collapse') + '"></i>')));
7224							}
7225
7226							// check if subtable toggle exist
7227							if ($(datatable.tableHead).find('.' + pfx + 'datatable__toggle-detail').length === 0) {
7228								// add empty column to the header and footer
7229								$(datatable.tableHead).
7230									find('.' + pfx + 'datatable__row').
7231									first().
7232									prepend('<th class="' + pfx + 'datatable__cell ' + pfx + 'datatable__toggle-detail"><span></span></th>');
7233								$(datatable.tableFoot).
7234									find('.' + pfx + 'datatable__row').
7235									first().
7236									prepend('<th class="' + pfx + 'datatable__cell ' + pfx + 'datatable__toggle-detail"><span></span></th>');
7237							} else {
7238								$(datatable.tableHead).find('.' + pfx + 'datatable__toggle-detail').find('span');
7239							}
7240						});
7241					}
7242				});
7243
7244				Plugin.adjustCellsWidth.call();
7245			},
7246
7247			/**
7248			 * To enable auto columns features for remote data source
7249			 */
7250			setAutoColumns: function() {
7251				if (Plugin.getOption('data.autoColumns')) {
7252					$.each(datatable.dataSet[0], function(k, v) {
7253						var found = $.grep(options.columns, function(n, i) {
7254							return k === n.field;
7255						});
7256						if (found.length === 0) {
7257							options.columns.push({field: k, title: k});
7258						}
7259					});
7260					$(datatable.tableHead).find('.' + pfx + 'datatable__row').remove();
7261					Plugin.setHeadTitle();
7262					if (Plugin.getOption('layout.footer')) {
7263						$(datatable.tableFoot).find('.' + pfx + 'datatable__row').remove();
7264						Plugin.setHeadTitle(datatable.tableFoot);
7265					}
7266				}
7267			},
7268
7269			/********************
7270			 ** HELPERS
7271			 ********************/
7272
7273			/**
7274			 * Check if table is a locked colums table
7275			 */
7276			isLocked: function() {
7277				var isLocked = Plugin.lockEnabledColumns();
7278				return isLocked.left.length > 0 || isLocked.right.length > 0;
7279			},
7280
7281			isSubtable: function() {
7282				return util.hasClass(datatable.wrap[0], pfx + 'datatable--subtable') || false;
7283			},
7284
7285			/**
7286			 * Get total extra space of an element for width calculation,
7287			 * including padding, margin, border
7288			 * @param element
7289			 * @returns {number}
7290			 */
7291			getExtraSpace: function(element) {
7292				var padding = parseInt($(element).css('paddingRight')) +
7293					parseInt($(element).css('paddingLeft'));
7294				var margin = parseInt($(element).css('marginRight')) +
7295					parseInt($(element).css('marginLeft'));
7296				var border = Math.ceil(
7297					$(element).css('border-right-width').replace('px', ''));
7298				return padding + margin + border;
7299			},
7300
7301			/**
7302			 * Insert data of array into {{ }} template placeholder
7303			 * @param template
7304			 * @param data
7305			 * @returns {*}
7306			 */
7307			dataPlaceholder: function(template, data) {
7308				var result = template;
7309				$.each(data, function(key, val) {
7310					result = result.replace('{{' + key + '}}', val);
7311				});
7312				return result;
7313			},
7314
7315			/**
7316			 * Get table unique ID
7317			 * Note: table unique change each time refreshed
7318			 * @param suffix
7319			 * @returns {*}
7320			 */
7321			getTableId: function(suffix) {
7322				if (typeof suffix === 'undefined') suffix = '';
7323				var id = $(datatable).attr('id');
7324				if (typeof id === 'undefined') {
7325					id = $(datatable).attr('class').split(' ')[0];
7326				}
7327				return id + suffix;
7328			},
7329
7330			/**
7331			 * Get table prefix with depth number
7332			 */
7333			getTablePrefix: function(suffix) {
7334				if (typeof suffix !== 'undefined') suffix = '-' + suffix;
7335				return Plugin.getTableId() + '-' + Plugin.getDepth() + suffix;
7336			},
7337
7338			/**
7339			 * Get current table depth of sub table
7340			 * @returns {number}
7341			 */
7342			getDepth: function() {
7343				var depth = 0;
7344				var table = datatable.table;
7345				do {
7346					table = $(table).parents('.' + pfx + 'datatable__table');
7347					depth++;
7348				} while ($(table).length > 0);
7349				return depth;
7350			},
7351
7352			/**
7353			 * Keep state item
7354			 * @param key
7355			 * @param value
7356			 */
7357			stateKeep: function(key, value) {
7358				key = Plugin.getTablePrefix(key);
7359				if (Plugin.getOption('data.saveState') === false) return;
7360				if (Plugin.getOption('data.saveState.webstorage') && localStorage) {
7361					localStorage.setItem(key, JSON.stringify(value));
7362				}
7363				if (Plugin.getOption('data.saveState.cookie')) {
7364					Cookies.set(key, JSON.stringify(value));
7365				}
7366			},
7367
7368			/**
7369			 * Get state item
7370			 * @param key
7371			 * @param defValue
7372			 */
7373			stateGet: function(key, defValue) {
7374				key = Plugin.getTablePrefix(key);
7375				if (Plugin.getOption('data.saveState') === false) return;
7376				var value = null;
7377				if (Plugin.getOption('data.saveState.webstorage') && localStorage) {
7378					value = localStorage.getItem(key);
7379				} else {
7380					value = Cookies.get(key);
7381				}
7382				if (typeof value !== 'undefined' && value !== null) {
7383					return JSON.parse(value);
7384				}
7385			},
7386
7387			/**
7388			 * Update data in state without clear existing
7389			 * @param key
7390			 * @param value
7391			 */
7392			stateUpdate: function(key, value) {
7393				var ori = Plugin.stateGet(key);
7394				if (typeof ori === 'undefined' || ori === null) ori = {};
7395				Plugin.stateKeep(key, $.extend({}, ori, value));
7396			},
7397
7398			/**
7399			 * Remove state item
7400			 * @param key
7401			 */
7402			stateRemove: function(key) {
7403				key = Plugin.getTablePrefix(key);
7404				if (localStorage) {
7405					localStorage.removeItem(key);
7406				}
7407				Cookies.remove(key);
7408			},
7409
7410			/**
7411			 * Get total columns.
7412			 */
7413			getTotalColumns: function(tablePart) {
7414				if (typeof tablePart === 'undefined') tablePart = datatable.tableBody;
7415				return $(tablePart).find('.' + pfx + 'datatable__row').first().find('.' + pfx + 'datatable__cell').length;
7416			},
7417
7418			/**
7419			 * Get table row. Useful to get row when current table is in lock
7420			 * mode. Can be used for both lock and normal table mode. By
7421			 * default, returning result will be in a list of <td>.
7422			 * @param tablePart
7423			 * @param row 1-based index
7424			 * @param tdOnly Optional. Default true
7425			 * @returns {*}
7426			 */
7427			getOneRow: function(tablePart, row, tdOnly) {
7428				if (typeof tdOnly === 'undefined') tdOnly = true;
7429				// get list of <tr>
7430				var result = $(tablePart).find('.' + pfx + 'datatable__row:not(.' + pfx + 'datatable__row-detail):nth-child(' + row + ')');
7431				if (tdOnly) {
7432					// get list of <td> or <th>
7433					result = result.find('.' + pfx + 'datatable__cell');
7434				}
7435				return result;
7436			},
7437
7438			/**
7439			 * Sort table row at HTML level by column index.
7440			 * todo; Not in use.
7441			 * @param header Header sort clicked
7442			 * @param sort asc|desc. Optional. Default asc
7443			 * @param int Boolean. Optional. Comparison value parse to integer.
7444			 *     Default false
7445			 */
7446			sortColumn: function(header, sort, int) {
7447				if (typeof sort === 'undefined') sort = 'asc'; // desc
7448				if (typeof int === 'undefined') int = false;
7449
7450				var column = $(header).index();
7451				var rows = $(datatable.tableBody).find('.' + pfx + 'datatable__row');
7452				var hIndex = $(header).closest('.' + pfx + 'datatable__lock').index();
7453				if (hIndex !== -1) {
7454					rows = $(datatable.tableBody).find('.' + pfx + 'datatable__lock:nth-child(' + (hIndex + 1) + ')').find('.' + pfx + 'datatable__row');
7455				}
7456
7457				var container = $(rows).parent();
7458				$(rows).sort(function(a, b) {
7459					var tda = $(a).find('td:nth-child(' + column + ')').text();
7460					var tdb = $(b).find('td:nth-child(' + column + ')').text();
7461
7462					if (int) {
7463						// useful for integer type sorting
7464						tda = parseInt(tda);
7465						tdb = parseInt(tdb);
7466					}
7467
7468					if (sort === 'asc') {
7469						return tda > tdb ? 1 : tda < tdb ? -1 : 0;
7470					} else {
7471						return tda < tdb ? 1 : tda > tdb ? -1 : 0;
7472					}
7473				}).appendTo(container);
7474			},
7475
7476			/**
7477			 * Perform sort remote and local
7478			 */
7479			sorting: function() {
7480				var sortObj = {
7481					init: function() {
7482						if (options.sortable) {
7483							$(datatable.tableHead).
7484								find('.' + pfx + 'datatable__cell:not(.' + pfx + 'datatable__cell--check)').
7485								addClass(pfx + 'datatable__cell--sort').
7486								off('click').
7487								on('click', sortObj.sortClick);
7488							// first init
7489							sortObj.setIcon();
7490						}
7491					},
7492					setIcon: function() {
7493						var meta = Plugin.getDataSourceParam('sort');
7494						if ($.isEmptyObject(meta)) return;
7495
7496						var column = Plugin.getColumnByField(meta.field);
7497						// sort is disabled for this column
7498						if (typeof column !== 'undefined' && typeof column.sortable !== 'undefined' && column.sortable === false) return;
7499
7500						// sort icon beside column header
7501						var td = $(datatable.tableHead).find('.' + pfx + 'datatable__cell[data-field="' + meta.field + '"]').attr('data-sort', meta.sort);
7502						var sorting = $(td).find('span');
7503						var icon = $(sorting).find('i');
7504
7505						var icons = Plugin.getOption('layout.icons.sort');
7506						// update sort icon; desc & asc
7507						if ($(icon).length > 0) {
7508							$(icon).removeAttr('class').addClass(icons[meta.sort]);
7509						} else {
7510							$(sorting).append($('<i/>').addClass(icons[meta.sort]));
7511						}
7512
7513						// set sorted class to header on init
7514						$(td).addClass(pfx + 'datatable__cell--sorted');
7515					},
7516					sortClick: function(e) {
7517						var meta = Plugin.getDataSourceParam('sort');
7518						var field = $(this).data('field');
7519						var column = Plugin.getColumnByField(field);
7520						// sort is disabled for this column
7521						if (typeof column.sortable !== 'undefined' && column.sortable === false) return;
7522
7523						// set sorted class to header
7524						$(datatable.tableHead).find('th').removeClass(pfx + 'datatable__cell--sorted');
7525						util.addClass(this, pfx + 'datatable__cell--sorted');
7526
7527						$(datatable.tableHead).find('.' + pfx + 'datatable__cell > span > i').remove();
7528
7529						if (options.sortable) {
7530							Plugin.spinnerCallback(true);
7531
7532							var sort = 'desc';
7533							if (Plugin.getObject('field', meta) === field) {
7534								sort = Plugin.getObject('sort', meta);
7535							}
7536
7537							// toggle sort
7538							sort = typeof sort === 'undefined' || sort === 'desc'
7539								? 'asc'
7540								: 'desc';
7541
7542							// update field and sort params
7543							meta = {field: field, sort: sort};
7544							Plugin.setDataSourceParam('sort', meta);
7545
7546							sortObj.setIcon();
7547
7548							setTimeout(function() {
7549								Plugin.dataRender('sort');
7550								$(datatable).trigger(pfx + 'datatable--on-sort', meta);
7551							}, 300);
7552						}
7553					},
7554				};
7555				sortObj.init();
7556			},
7557
7558			/**
7559			 * Update JSON data list linked with sort, filter and pagination.
7560			 * Call this method, before using dataSet variable.
7561			 * @returns {*|null}
7562			 */
7563			localDataUpdate: function() {
7564				var params = Plugin.getDataSourceParam();
7565				if (typeof datatable.originalDataSet === 'undefined') {
7566					datatable.originalDataSet = datatable.dataSet;
7567				}
7568
7569				var field = Plugin.getObject('sort.field', params);
7570				var sort = Plugin.getObject('sort.sort', params);
7571				var column = Plugin.getColumnByField(field);
7572				if (typeof column !== 'undefined' && Plugin.getOption('data.serverSorting') !== true) {
7573					if (typeof column.sortCallback === 'function') {
7574						datatable.dataSet = column.sortCallback(datatable.originalDataSet, sort, column);
7575					} else {
7576						datatable.dataSet = Plugin.sortCallback(datatable.originalDataSet, sort, column);
7577					}
7578				} else {
7579					datatable.dataSet = datatable.originalDataSet;
7580				}
7581
7582				// if server filter enable, don't pass local filter
7583				if (typeof params.query === 'object' && !Plugin.getOption('data.serverFiltering')) {
7584					params.query = params.query || {};
7585
7586					var nestedSearch = function(obj) {
7587						for (var field in obj) {
7588							if (!obj.hasOwnProperty(field)) continue;
7589							if (typeof obj[field] === 'string') {
7590								if (obj[field].toLowerCase() == search || obj[field].toLowerCase().indexOf(search) !== -1) {
7591									return true;
7592								}
7593							} else if (typeof obj[field] === 'number') {
7594								if (obj[field] === search) {
7595									return true;
7596								}
7597							} else if (typeof obj[field] === 'object') {
7598								if (nestedSearch(obj[field])) {
7599									return true;
7600								}
7601							}
7602						}
7603						return false;
7604					};
7605
7606					var search = $(Plugin.getOption('search.input')).val();
7607					if (typeof search !== 'undefined' && search !== '') {
7608						search = search.toLowerCase();
7609						datatable.dataSet = $.grep(datatable.dataSet, nestedSearch);
7610						// remove generalSearch as we don't need this for next columns filter
7611						delete params.query[Plugin.getGeneralSearchKey()];
7612					}
7613
7614					// remove empty element from array
7615					$.each(params.query, function(k, v) {
7616						if (v === '') {
7617							delete params.query[k];
7618						}
7619					});
7620
7621					// filter array by query
7622					datatable.dataSet = Plugin.filterArray(datatable.dataSet, params.query);
7623
7624					// reset array index
7625					datatable.dataSet = datatable.dataSet.filter(function() {
7626						return true;
7627					});
7628				}
7629
7630				return datatable.dataSet;
7631			},
7632
7633			/**
7634			 * Utility helper to filter array by object pair of {key:value}
7635			 * @param list
7636			 * @param args
7637			 * @param operator
7638			 * @returns {*}
7639			 */
7640			filterArray: function(list, args, operator) {
7641				if (typeof list !== 'object') {
7642					return [];
7643				}
7644
7645				if (typeof operator === 'undefined') operator = 'AND';
7646
7647				if (typeof args !== 'object') {
7648					return list;
7649				}
7650
7651				operator = operator.toUpperCase();
7652
7653				if ($.inArray(operator, ['AND', 'OR', 'NOT']) === -1) {
7654					return [];
7655				}
7656
7657				var count = Object.keys(args).length;
7658				var filtered = [];
7659
7660				$.each(list, function(key, obj) {
7661					var to_match = obj;
7662
7663					var matched = 0;
7664					$.each(args, function(m_key, m_value) {
7665						m_value = m_value instanceof Array ? m_value : [m_value];
7666						var match_property = Plugin.getObject(m_key, to_match);
7667						if (typeof match_property !== 'undefined' && match_property) {
7668							var lhs = match_property.toString().toLowerCase();
7669							m_value.forEach(function(item, index) {
7670								if (item.toString().toLowerCase() == lhs || lhs.indexOf(item.toString().toLowerCase()) !== -1) {
7671									matched++;
7672								}
7673							});
7674						}
7675					});
7676
7677					if (('AND' == operator && matched == count) ||
7678						('OR' == operator && matched > 0) ||
7679						('NOT' == operator && 0 == matched)) {
7680						filtered[key] = obj;
7681					}
7682				});
7683
7684				list = filtered;
7685
7686				return list;
7687			},
7688
7689			/**
7690			 * Reset lock column scroll to 0 when resize
7691			 */
7692			resetScroll: function() {
7693				if (typeof options.detail === 'undefined' && Plugin.getDepth() === 1) {
7694					$(datatable.table).find('.' + pfx + 'datatable__row').css('left', 0);
7695					$(datatable.table).find('.' + pfx + 'datatable__lock').css('top', 0);
7696					$(datatable.tableBody).scrollTop(0);
7697				}
7698			},
7699
7700			/**
7701			 * Get column options by field
7702			 * @param field
7703			 * @returns {boolean}
7704			 */
7705			getColumnByField: function(field) {
7706				if (typeof field === 'undefined') return;
7707				var result;
7708				$.each(options.columns, function(i, column) {
7709					if (field === column.field) {
7710						result = column;
7711						return false;
7712					}
7713				});
7714				return result;
7715			},
7716
7717			/**
7718			 * Get default sort column
7719			 */
7720			getDefaultSortColumn: function() {
7721				var result;
7722				$.each(options.columns, function(i, column) {
7723					if (typeof column.sortable !== 'undefined'
7724						&& $.inArray(column.sortable, ['asc', 'desc']) !== -1) {
7725						result = {sort: column.sortable, field: column.field};
7726						return false;
7727					}
7728				});
7729				return result;
7730			},
7731
7732			/**
7733			 * Helper to get element dimensions, when the element is hidden
7734			 * @param element
7735			 * @param includeMargin
7736			 * @returns {{width: number, height: number, innerWidth: number,
7737			 *     innerHeight: number, outerWidth: number, outerHeight:
7738			 *     number}}
7739			 */
7740			getHiddenDimensions: function(element, includeMargin) {
7741				var props = {
7742						position: 'absolute',
7743						visibility: 'hidden',
7744						display: 'block',
7745					},
7746					dim = {
7747						width: 0,
7748						height: 0,
7749						innerWidth: 0,
7750						innerHeight: 0,
7751						outerWidth: 0,
7752						outerHeight: 0,
7753					},
7754					hiddenParents = $(element).parents().addBack().not(':visible');
7755				includeMargin = (typeof includeMargin === 'boolean')
7756					? includeMargin
7757					: false;
7758
7759				var oldProps = [];
7760				hiddenParents.each(function() {
7761					var old = {};
7762
7763					for (var name in props) {
7764						old[name] = this.style[name];
7765						this.style[name] = props[name];
7766					}
7767
7768					oldProps.push(old);
7769				});
7770
7771				dim.width = $(element).width();
7772				dim.outerWidth = $(element).outerWidth(includeMargin);
7773				dim.innerWidth = $(element).innerWidth();
7774				dim.height = $(element).height();
7775				dim.innerHeight = $(element).innerHeight();
7776				dim.outerHeight = $(element).outerHeight(includeMargin);
7777
7778				hiddenParents.each(function(i) {
7779					var old = oldProps[i];
7780					for (var name in props) {
7781						this.style[name] = old[name];
7782					}
7783				});
7784
7785				return dim;
7786			},
7787
7788			getGeneralSearchKey: function() {
7789				var searchInput = $(Plugin.getOption('search.input'));
7790				return $(searchInput).prop('name') || $(searchInput).prop('id');
7791			},
7792
7793			/**
7794			 * Get value by dot notation path string and to prevent undefined
7795			 * errors
7796			 * @param path String Dot notation path in string
7797			 * @param object Object to iterate
7798			 * @returns {*}
7799			 */
7800			getObject: function(path, object) {
7801				return path.split('.').reduce(function(obj, i) {
7802					return obj !== null && typeof obj[i] !== 'undefined' ? obj[i] : null;
7803				}, object);
7804			},
7805
7806			/**
7807			 * Extend object
7808			 * @param obj
7809			 * @param path
7810			 * @param value
7811			 * @returns {*}
7812			 */
7813			extendObj: function(obj, path, value) {
7814				var levels = path.split('.'),
7815					i = 0;
7816
7817				function createLevel(child) {
7818					var name = levels[i++];
7819					if (typeof child[name] !== 'undefined' && child[name] !== null) {
7820						if (typeof child[name] !== 'object' &&
7821							typeof child[name] !== 'function') {
7822							child[name] = {};
7823						}
7824					} else {
7825						child[name] = {};
7826					}
7827					if (i === levels.length) {
7828						child[name] = value;
7829					} else {
7830						createLevel(child[name]);
7831					}
7832				}
7833
7834				createLevel(obj);
7835				return obj;
7836			},
7837
7838			rowEvenOdd: function() {
7839				// row even class
7840				$(datatable.tableBody).find('.' + pfx + 'datatable__row').removeClass(pfx + 'datatable__row--even');
7841				if ($(datatable.wrap).hasClass(pfx + 'datatable--subtable')) {
7842					$(datatable.tableBody).find('.' + pfx + 'datatable__row:not(.' + pfx + 'datatable__row-detail):even').addClass(pfx + 'datatable__row--even');
7843				} else {
7844					$(datatable.tableBody).find('.' + pfx + 'datatable__row:nth-child(even)').addClass(pfx + 'datatable__row--even');
7845				}
7846			},
7847
7848			/********************
7849			 ** PUBLIC API METHODS
7850			 ********************/
7851
7852			// delay timer
7853			timer: 0,
7854
7855			/**
7856			 * Redraw datatable by recalculating its DOM elements, etc.
7857			 * @returns {jQuery}
7858			 */
7859			redraw: function() {
7860				Plugin.adjustCellsWidth.call();
7861				if (Plugin.isLocked()) {
7862					// fix hiding cell width issue
7863					Plugin.scrollbar();
7864					Plugin.resetScroll();
7865					Plugin.adjustCellsHeight.call();
7866				}
7867				Plugin.adjustLockContainer.call();
7868				Plugin.initHeight.call();
7869				return datatable;
7870			},
7871
7872			/**
7873			 * Shortcode to reload
7874			 * @returns {jQuery}
7875			 */
7876			load: function() {
7877				Plugin.reload();
7878				return datatable;
7879			},
7880
7881			/**
7882			 * Datasource reload
7883			 * @returns {jQuery}
7884			 */
7885			reload: function() {
7886				var delay = (function() {
7887					return function(callback, ms) {
7888						clearTimeout(Plugin.timer);
7889						Plugin.timer = setTimeout(callback, ms);
7890					};
7891				})();
7892				delay(function() {
7893					// local only. remote pagination will skip this block
7894					if (!options.data.serverFiltering) {
7895						Plugin.localDataUpdate();
7896					}
7897					Plugin.dataRender();
7898					$(datatable).trigger(pfx + 'datatable--on-reloaded');
7899				}, Plugin.getOption('search.delay'));
7900				return datatable;
7901			},
7902
7903			/**
7904			 * Get record by record ID
7905			 * @param id
7906			 * @returns {jQuery}
7907			 */
7908			getRecord: function(id) {
7909				if (typeof datatable.tableBody === 'undefined') datatable.tableBody = $(datatable.table).children('tbody');
7910				$(datatable.tableBody).find('.' + pfx + 'datatable__cell:first-child').each(function(i, cell) {
7911					if (id == $(cell).text()) {
7912						var rowNumber = $(cell).closest('.' + pfx + 'datatable__row').index() + 1;
7913						datatable.API.record = datatable.API.value = Plugin.getOneRow(datatable.tableBody, rowNumber);
7914						return datatable;
7915					}
7916				});
7917				return datatable;
7918			},
7919
7920			/**
7921			 * @deprecated in v5.0.6
7922			 * Get column of current record ID
7923			 * @param columnName
7924			 * @returns {jQuery}
7925			 */
7926			getColumn: function(columnName) {
7927				Plugin.setSelectedRecords();
7928				datatable.API.value = $(datatable.API.record).find('[data-field="' + columnName + '"]');
7929				return datatable;
7930			},
7931
7932			/**
7933			 * Destroy datatable to original DOM state before datatable was
7934			 * initialized
7935			 * @returns {jQuery}
7936			 */
7937			destroy: function() {
7938				$(datatable).parent().find('.' + pfx + 'datatable__pager').remove();
7939				var initialDatatable = $(datatable.initialDatatable).addClass(pfx + 'datatable--destroyed').show();
7940				$(datatable).replaceWith(initialDatatable);
7941				datatable = initialDatatable;
7942				$(datatable).trigger(pfx + 'datatable--on-destroy');
7943				Plugin.isInit = false;
7944				initialDatatable = null;
7945				return initialDatatable;
7946			},
7947
7948			/**
7949			 * Sort by column field
7950			 * @param field
7951			 * @param sort
7952			 */
7953			sort: function(field, sort) {
7954				// toggle sort
7955				sort = typeof sort === 'undefined' ? 'asc' : sort;
7956
7957				Plugin.spinnerCallback(true);
7958
7959				// update field and sort params
7960				var meta = {field: field, sort: sort};
7961				Plugin.setDataSourceParam('sort', meta);
7962
7963				setTimeout(function() {
7964					Plugin.dataRender('sort');
7965					$(datatable).trigger(pfx + 'datatable--on-sort', meta);
7966					$(datatable.tableHead).find('.' + pfx + 'datatable__cell > span > i').remove();
7967				}, 300);
7968
7969				return datatable;
7970			},
7971
7972			/**
7973			 * @deprecated in v5.0.6
7974			 * Get current selected column value
7975			 * @returns {jQuery}
7976			 */
7977			getValue: function() {
7978				return $(datatable.API.value).text();
7979			},
7980
7981			/**
7982			 * Set checkbox active
7983			 * @param cell JQuery selector or checkbox ID
7984			 */
7985			setActive: function(cell) {
7986				if (typeof cell === 'string') {
7987					// set by checkbox id
7988					cell = $(datatable.tableBody).find('.' + pfx + 'checkbox--single > [type="checkbox"][value="' + cell + '"]');
7989				}
7990
7991				$(cell).prop('checked', true);
7992
7993				var ids = [];
7994				$(cell).each(function(i, td) {
7995					// normal table
7996					var row = $(td).closest('tr').addClass(pfx + 'datatable__row--active');
7997
7998					var id = $(td).attr('value');
7999					if (typeof id !== 'undefined') {
8000						ids.push(id);
8001					}
8002				});
8003
8004				$(datatable).trigger(pfx + 'datatable--on-check', [ids]);
8005			},
8006
8007			/**
8008			 * Set checkbox inactive
8009			 * @param cell JQuery selector or checkbox ID
8010			 */
8011			setInactive: function(cell) {
8012				if (typeof cell === 'string') {
8013					// set by checkbox id
8014					cell = $(datatable.tableBody).find('.' + pfx + 'checkbox--single > [type="checkbox"][value="' + cell + '"]');
8015				}
8016
8017				$(cell).prop('checked', false);
8018
8019				var ids = [];
8020				$(cell).each(function(i, td) {
8021					// normal table
8022					var row = $(td).closest('tr').removeClass(pfx + 'datatable__row--active');
8023
8024					var id = $(td).attr('value');
8025					if (typeof id !== 'undefined') {
8026						ids.push(id);
8027					}
8028				});
8029
8030				$(datatable).trigger(pfx + 'datatable--on-uncheck', [ids]);
8031			},
8032
8033			/**
8034			 * Set all checkboxes active or inactive
8035			 * @param active
8036			 */
8037			setActiveAll: function(active) {
8038				var checkboxes = $(datatable.table).
8039					find('> tbody, > thead').
8040					find('tr').not('.' + pfx + 'datatable__row-subtable').
8041					find('.' + pfx + 'datatable__cell--check [type="checkbox"]');
8042				if (active) {
8043					Plugin.setActive(checkboxes);
8044				} else {
8045					Plugin.setInactive(checkboxes);
8046				}
8047			},
8048
8049			/**
8050			 * @deprecated in v5.0.6
8051			 * Get selected rows which are active
8052			 * @returns {jQuery}
8053			 */
8054			setSelectedRecords: function() {
8055				datatable.API.record = $(datatable.tableBody).find('.' + pfx + 'datatable__row--active');
8056				return datatable;
8057			},
8058
8059			/**
8060			 * Get selected records
8061			 * @returns {null}
8062			 */
8063			getSelectedRecords: function() {
8064				// support old method
8065				Plugin.setSelectedRecords();
8066				datatable.API.record = datatable.rows('.' + pfx + 'datatable__row--active').nodes();
8067				return datatable.API.record;
8068			},
8069
8070			/**
8071			 * Get options by dots notation path
8072			 * @param path String Dot notation path in string
8073			 * @returns {*}
8074			 */
8075			getOption: function(path) {
8076				return Plugin.getObject(path, options);
8077			},
8078
8079			/**
8080			 * Set global options nodes by dots notation path
8081			 * @param path
8082			 * @param object
8083			 */
8084			setOption: function(path, object) {
8085				options = Plugin.extendObj(options, path, object);
8086			},
8087
8088			/**
8089			 * Search filter for local & remote
8090			 * @param value
8091			 * @param columns. Optional list of columns to be filtered.
8092			 */
8093			search: function(value, columns) {
8094				if (typeof columns !== 'undefined') columns = $.makeArray(columns);
8095				var delay = (function() {
8096					return function(callback, ms) {
8097						clearTimeout(Plugin.timer);
8098						Plugin.timer = setTimeout(callback, ms);
8099					};
8100				})();
8101
8102				delay(function() {
8103					// get query parameters
8104					var query = Plugin.getDataSourceQuery();
8105
8106					// search not by columns
8107					if (typeof columns === 'undefined' && typeof value !== 'undefined') {
8108						var key = Plugin.getGeneralSearchKey();
8109						query[key] = value;
8110					}
8111
8112					// search by columns, support multiple columns
8113					if (typeof columns === 'object') {
8114						$.each(columns, function(k, column) {
8115							query[column] = value;
8116						});
8117						// remove empty element from arrays
8118						$.each(query, function(k, v) {
8119							if (v === '' || $.isEmptyObject(v)) {
8120								delete query[k];
8121							}
8122						});
8123					}
8124
8125					Plugin.setDataSourceQuery(query);
8126
8127					// reset pagination to 1 when doing seearching
8128					datatable.setDataSourceParam('pagination', Object.assign({}, datatable.getDataSourceParam('pagination'), {page: 1}));
8129
8130					// local filter only. remote pagination will skip this block
8131					if (!options.data.serverFiltering) {
8132						Plugin.localDataUpdate();
8133					}
8134					Plugin.dataRender('search');
8135				}, Plugin.getOption('search.delay'));
8136			},
8137
8138			/**
8139			 * Set datasource params extract
8140			 * @param param
8141			 * @param value
8142			 */
8143			setDataSourceParam: function(param, value) {
8144				datatable.API.params = $.extend({}, {
8145					pagination: {page: 1, perpage: Plugin.getOption('data.pageSize')},
8146					sort: Plugin.getDefaultSortColumn(),
8147					query: {},
8148				}, datatable.API.params, Plugin.stateGet(Plugin.stateId));
8149
8150				datatable.API.params = Plugin.extendObj(datatable.API.params, param, value);
8151
8152				Plugin.stateKeep(Plugin.stateId, datatable.API.params);
8153			},
8154
8155			/**
8156			 * Get datasource params
8157			 * @param param
8158			 */
8159			getDataSourceParam: function(param) {
8160				datatable.API.params = $.extend({}, {
8161					pagination: {page: 1, perpage: Plugin.getOption('data.pageSize')},
8162					sort: Plugin.getDefaultSortColumn(),
8163					query: {},
8164				}, datatable.API.params, Plugin.stateGet(Plugin.stateId));
8165
8166				if (typeof param === 'string') {
8167					return Plugin.getObject(param, datatable.API.params);
8168				}
8169
8170				return datatable.API.params;
8171			},
8172
8173			/**
8174			 * Shortcode to datatable.getDataSourceParam('query');
8175			 * @returns {*}
8176			 */
8177			getDataSourceQuery: function() {
8178				return Plugin.getDataSourceParam('query') || {};
8179			},
8180
8181			/**
8182			 * Shortcode to datatable.setDataSourceParam('query', query);
8183			 * @param query
8184			 */
8185			setDataSourceQuery: function(query) {
8186				Plugin.setDataSourceParam('query', query);
8187			},
8188
8189			/**
8190			 * Get current page number
8191			 * @returns {number}
8192			 */
8193			getCurrentPage: function() {
8194				return $(datatable.table).
8195					siblings('.' + pfx + 'datatable__pager').
8196					last().
8197					find('.' + pfx + 'datatable__pager-nav').
8198					find('.' + pfx + 'datatable__pager-link.' + pfx + 'datatable__pager-link--active').
8199					data('page') || 1;
8200			},
8201
8202			/**
8203			 * Get selected dropdown page size
8204			 * @returns {*|number}
8205			 */
8206			getPageSize: function() {
8207				return $(datatable.table).siblings('.' + pfx + 'datatable__pager').last().find('select.' + pfx + 'datatable__pager-size').val() || 10;
8208			},
8209
8210			/**
8211			 * Get total rows
8212			 */
8213			getTotalRows: function() {
8214				return datatable.API.params.pagination.total;
8215			},
8216
8217			/**
8218			 * Get full dataset in grid
8219			 * @returns {*|null|Array}
8220			 */
8221			getDataSet: function() {
8222				return datatable.originalDataSet;
8223			},
8224
8225			nodeTr: [],
8226			nodeTd: [],
8227			nodeCols: [],
8228			recentNode: [],
8229
8230			table: function() {
8231				if (typeof datatable.table !== 'undefined') {
8232					return datatable.table;
8233				}
8234			},
8235
8236			/**
8237			 * Select a single row from the table
8238			 * @param selector
8239			 * @returns {jQuery}
8240			 */
8241			row: function(selector) {
8242				Plugin.rows(selector);
8243				Plugin.nodeTr = Plugin.recentNode = $(Plugin.nodeTr).first();
8244				return datatable;
8245			},
8246
8247			/**
8248			 * Select multiple rows from the table
8249			 * @param selector
8250			 * @returns {jQuery}
8251			 */
8252			rows: function(selector) {
8253				if (Plugin.isLocked()) {
8254					Plugin.nodeTr = Plugin.recentNode = $(datatable.tableBody).find(selector).filter('.' + pfx + 'datatable__lock--scroll > .' + pfx + 'datatable__row');
8255				} else {
8256					Plugin.nodeTr = Plugin.recentNode = $(datatable.tableBody).find(selector).filter('.' + pfx + 'datatable__row');
8257				}
8258				return datatable;
8259			},
8260
8261			/**
8262			 * Select a single column from the table
8263			 * @param index zero-based index
8264			 * @returns {jQuery}
8265			 */
8266			column: function(index) {
8267				Plugin.nodeCols = Plugin.recentNode = $(datatable.tableBody).find('.' + pfx + 'datatable__cell:nth-child(' + (index + 1) + ')');
8268				return datatable;
8269			},
8270
8271			/**
8272			 * Select multiple columns from the table
8273			 * @param selector
8274			 * @returns {jQuery}
8275			 */
8276			columns: function(selector) {
8277				var context = datatable.table;
8278				if (Plugin.nodeTr === Plugin.recentNode) {
8279					context = Plugin.nodeTr;
8280				}
8281				var columns = $(context).find('.' + pfx + 'datatable__cell[data-field="' + selector + '"]');
8282				if (columns.length > 0) {
8283					Plugin.nodeCols = Plugin.recentNode = columns;
8284				} else {
8285					Plugin.nodeCols = Plugin.recentNode = $(context).find(selector).filter('.' + pfx + 'datatable__cell');
8286				}
8287				return datatable;
8288			},
8289
8290			cell: function(selector) {
8291				Plugin.cells(selector);
8292				Plugin.nodeTd = Plugin.recentNode = $(Plugin.nodeTd).first();
8293				return datatable;
8294			},
8295
8296			cells: function(selector) {
8297				var cells = $(datatable.tableBody).find('.' + pfx + 'datatable__cell');
8298				if (typeof selector !== 'undefined') {
8299					cells = $(cells).filter(selector);
8300				}
8301				Plugin.nodeTd = Plugin.recentNode = cells;
8302				return datatable;
8303			},
8304
8305			/**
8306			 * Delete the selected row from the table
8307			 * @returns {jQuery}
8308			 */
8309			remove: function() {
8310				if ($(Plugin.nodeTr.length) && Plugin.nodeTr === Plugin.recentNode) {
8311					$(Plugin.nodeTr).remove();
8312				}
8313				Plugin.layoutUpdate();
8314				return datatable;
8315			},
8316
8317			/**
8318			 * Show or hide the columns or rows
8319			 */
8320			visible: function(bool) {
8321				if ($(Plugin.recentNode.length)) {
8322					var locked = Plugin.lockEnabledColumns();
8323					if (Plugin.recentNode === Plugin.nodeCols) {
8324						var index = Plugin.recentNode.index();
8325
8326						if (Plugin.isLocked()) {
8327							var scrollColumns = $(Plugin.recentNode).closest('.' + pfx + 'datatable__lock--scroll').length;
8328							if (scrollColumns) {
8329								// is at center of scrollable area
8330								index += locked.left.length + 1;
8331							} else if ($(Plugin.recentNode).closest('.' + pfx + 'datatable__lock--right').length) {
8332								// is at the right locked table
8333								index += locked.left.length + scrollColumns + 1;
8334							}
8335						}
8336					}
8337
8338					if (bool) {
8339						if (Plugin.recentNode === Plugin.nodeCols) {
8340							delete options.columns[index].visible;
8341						}
8342						$(Plugin.recentNode).show();
8343					} else {
8344						if (Plugin.recentNode === Plugin.nodeCols) {
8345							Plugin.setOption('columns.' + (index) + '.visible', false);
8346						}
8347						$(Plugin.recentNode).hide();
8348					}
8349					Plugin.columnHide();
8350					Plugin.redraw();
8351				}
8352			},
8353
8354			/**
8355			 * Get the the DOM element for the selected rows or columns
8356			 * @returns {Array}
8357			 */
8358			nodes: function() {
8359				return Plugin.recentNode;
8360			},
8361
8362			/**
8363			 * will be implemented soon
8364			 * @returns {jQuery}
8365			 */
8366			dataset: function() {
8367				return datatable;
8368			},
8369
8370			/**
8371			 * Open page by number
8372			 * @param page number
8373			 */
8374			gotoPage: function (page) {
8375				if (typeof Plugin.pagingObject !== 'undefined') {
8376					Plugin.isInit = true;
8377					Plugin.pagingObject.openPage(page);
8378				}
8379			},
8380
8381		};
8382
8383		/**
8384		 * Public API methods can be used directly by datatable
8385		 */
8386		$.each(Plugin, function(funcName, func) {
8387			datatable[funcName] = func;
8388		});
8389
8390		// initialize main datatable plugin
8391		if (typeof options !== 'undefined') {
8392			if (typeof options === 'string') {
8393				var method = options;
8394				datatable = $(this).data(pluginName);
8395				if (typeof datatable !== 'undefined') {
8396					options = datatable.options;
8397					Plugin[method].apply(this, Array.prototype.slice.call(arguments, 1));
8398				}
8399			} else {
8400				if (!datatable.data(pluginName) && !$(this).hasClass(pfx + 'datatable--loaded')) {
8401					datatable.dataSet = null;
8402					datatable.textAlign = {
8403						left: pfx + 'datatable__cell--left',
8404						center: pfx + 'datatable__cell--center',
8405						right: pfx + 'datatable__cell--right',
8406					};
8407
8408					// merge default and user defined options
8409					options = $.extend(true, {}, $.fn[pluginName].defaults, options);
8410
8411					datatable.options = options;
8412
8413					// init plugin process
8414					Plugin.init.apply(this, [options]);
8415
8416					$(datatable.wrap).data(pluginName, datatable);
8417				}
8418			}
8419		} else {
8420			// get existing instance datatable
8421			datatable = $(this).data(pluginName);
8422			if (typeof datatable === 'undefined') {
8423				$.error(pluginName + ' not initialized');
8424			}
8425			options = datatable.options;
8426		}
8427
8428		return datatable;
8429	};
8430
8431	// default options
8432	$.fn[pluginName].defaults = {
8433		// datasource definition
8434		data: {
8435			type: 'local',
8436			source: null,
8437			pageSize: 10, // display records per page
8438			saveState: {
8439				// save datatable state(pagination, filtering, sorting, etc) in cookie or browser webstorage
8440				cookie: false,
8441				webstorage: true,
8442			},
8443
8444			serverPaging: false,
8445			serverFiltering: false,
8446			serverSorting: false,
8447
8448			autoColumns: false,
8449			attr: {
8450				rowProps: [],
8451			},
8452		},
8453
8454		// layout definition
8455		layout: {
8456			theme: 'default', // datatable will support multiple themes and designs
8457			class: pfx + 'datatable--brand', // custom wrapper class
8458			scroll: false, // enable/disable datatable scroll both horizontal and vertical when needed.
8459			height: null, // datatable's body's fixed height
8460			minHeight: null,
8461			footer: false, // display/hide footer
8462			header: true, // display/hide header
8463			customScrollbar: true, // set false to disable custom scrollbar
8464
8465			// datatable spinner
8466			spinner: {
8467				overlayColor: '#000000',
8468				opacity: 0,
8469				type: 'loader',
8470				state: 'brand',
8471				message: true,
8472			},
8473
8474			// datatable UI icons
8475			icons: {
8476				sort: {asc: 'flaticon2-arrow-up', desc: 'flaticon2-arrow-down'},
8477				pagination: {
8478					next: 'flaticon2-next',
8479					prev: 'flaticon2-back',
8480					first: 'flaticon2-fast-back',
8481					last: 'flaticon2-fast-next',
8482					more: 'flaticon-more-1',
8483				},
8484				rowDetail: {expand: 'fa fa-caret-down', collapse: 'fa fa-caret-right'},
8485			},
8486		},
8487
8488		// column sorting
8489		sortable: true,
8490
8491		// resize column size with mouse drag coming soon)
8492		resizable: false,
8493
8494		// column based filtering (coming soon)
8495		filterable: false,
8496
8497		pagination: true,
8498
8499		// inline and bactch editing (cooming soon)
8500		editable: false,
8501
8502		// columns definition
8503		columns: [],
8504
8505		search: {
8506			// enable trigger search by keyup enter
8507			onEnter: false,
8508			// input text for search
8509			input: null,
8510			// search delay in milliseconds
8511			delay: 400,
8512		},
8513
8514		rows: {
8515			// deprecated
8516			callback: function() {
8517			},
8518			// call before row template
8519			beforeTemplate: function() {
8520			},
8521			// call after row template
8522			afterTemplate: function() {
8523			},
8524			autoHide: true,
8525		},
8526
8527		// toolbar
8528		toolbar: {
8529			// place pagination and displayInfo blocks according to the array order
8530			layout: ['pagination', 'info'],
8531
8532			// toolbar placement can be at top or bottom or both top and bottom repeated
8533			placement: ['bottom'],  //'top', 'bottom'
8534
8535			// toolbar items
8536			items: {
8537				// pagination
8538				pagination: {
8539					// pagination type(default or scroll)
8540					type: 'default',
8541
8542					// number of pages to display by breakpoints
8543					pages: {
8544						desktop: {
8545							layout: 'default',
8546							pagesNumber: 5,
8547						},
8548						tablet: {
8549							layout: 'default',
8550							pagesNumber: 3,
8551						},
8552						mobile: {
8553							layout: 'compact',
8554						},
8555					},
8556
8557					// navigation buttons
8558					navigation: {
8559						prev: true, // display prev button
8560						next: true, // display next button
8561						first: true, // display first button
8562						last: true, // display last button
8563						more: false // display more button
8564					},
8565
8566					// page size select
8567					pageSizeSelect: [], // display dropdown to select pagination size. -1 is used for "ALl" option
8568				},
8569
8570				// records info
8571				info: true,
8572			},
8573		},
8574
8575		// here we will keep all strings and message used by datatable UI so developer can easiliy translate to any language.
8576		// By default the stirngs will be in the plugin source and here can override it
8577		translate: {
8578			records: {
8579				processing: 'Please wait...',
8580				noRecords: 'No records found',
8581			},
8582			toolbar: {
8583				pagination: {
8584					items: {
8585						default: {
8586							first: 'First',
8587							prev: 'Previous',
8588							next: 'Next',
8589							last: 'Last',
8590							more: 'More pages',
8591							input: 'Page number',
8592							select: 'Select page size',
8593							all: 'all',
8594						},
8595						info: 'Showing {{start}} - {{end}} of {{total}}',
8596					},
8597				},
8598			},
8599		},
8600
8601		extensions: {},
8602	};
8603
8604}(jQuery));
8605
8606"use strict";
8607(function($) {
8608
8609	var pluginName = 'KTDatatable';
8610	var pfx = 'kt-';
8611
8612	$.fn[pluginName] = $.fn[pluginName] || {};
8613
8614	/**
8615	 * @param datatable Main datatable plugin instance
8616	 * @param options Extension options
8617	 * @returns {*}
8618	 */
8619	$.fn[pluginName].checkbox = function(datatable, options) {
8620		var Extension = {
8621			selectedAllRows: false,
8622			selectedRows: [],
8623			unselectedRows: [],
8624
8625			init: function() {
8626				if (Extension.selectorEnabled()) {
8627					// reset
8628					datatable.setDataSourceParam(options.vars.selectedAllRows, false);
8629					datatable.stateRemove('checkbox');
8630
8631					// requestIds is not null
8632					if (options.vars.requestIds) {
8633						// request ids in response
8634						datatable.setDataSourceParam(options.vars.requestIds, true);
8635					}
8636
8637					// remove selected checkbox on datatable reload
8638					$(datatable).on(pfx + 'datatable--on-reloaded', function() {
8639						datatable.stateRemove('checkbox');
8640						datatable.setDataSourceParam(options.vars.selectedAllRows, false);
8641						Extension.selectedAllRows = false;
8642						Extension.selectedRows = [];
8643						Extension.unselectedRows = [];
8644					});
8645
8646					// select all on extension init
8647					Extension.selectedAllRows = datatable.getDataSourceParam(options.vars.selectedAllRows);
8648
8649					$(datatable).on(pfx + 'datatable--on-layout-updated', function(e, args) {
8650						if (args.table != $(datatable.wrap).attr('id')) {
8651							return;
8652						}
8653						datatable.ready(function() {
8654							Extension.initVars();
8655							Extension.initEvent();
8656							Extension.initSelect();
8657						});
8658					});
8659
8660					$(datatable).on(pfx + 'datatable--on-check', function(e, ids) {
8661						ids.forEach(function(id) {
8662							Extension.selectedRows.push(id);
8663							// // remove from unselected rows
8664							Extension.unselectedRows = Extension.remove(Extension.unselectedRows, id);
8665						});
8666						var storage = {};
8667						storage['selectedRows'] = $.unique(Extension.selectedRows);
8668						storage['unselectedRows'] = $.unique(Extension.unselectedRows);
8669						datatable.stateKeep('checkbox', storage);
8670					});
8671					$(datatable).on(pfx + 'datatable--on-uncheck', function(e, ids) {
8672						ids.forEach(function(id) {
8673							Extension.unselectedRows.push(id);
8674							// // remove from selected rows
8675							Extension.selectedRows = Extension.remove(Extension.selectedRows, id);
8676						});
8677						var storage = {};
8678						storage['selectedRows'] = $.unique(Extension.selectedRows);
8679						storage['unselectedRows'] = $.unique(Extension.unselectedRows);
8680						datatable.stateKeep('checkbox', storage);
8681					});
8682				}
8683			},
8684
8685			/**
8686			 * Init checkbox clicks event
8687			 */
8688			initEvent: function() {
8689				// select all checkbox click
8690				$(datatable.tableHead).find('.' + pfx + 'checkbox--all > [type="checkbox"]').click(function(e) {
8691					// clear selected and unselected rows
8692					Extension.selectedRows = Extension.unselectedRows = [];
8693					datatable.stateRemove('checkbox');
8694
8695					// select all rows
8696					if ($(this).is(':checked')) {
8697						Extension.selectedAllRows = true;
8698					}
8699					else {
8700						Extension.selectedAllRows = false;
8701					}
8702
8703					// local select all current page rows
8704					if (!options.vars.requestIds) {
8705						if ($(this).is(':checked')) {
8706							Extension.selectedRows = $.makeArray($(datatable.tableBody).find('.' + pfx + 'checkbox--single > [type="checkbox"]').map(function(i, chk) {
8707								return $(chk).val();
8708							}));
8709						}
8710						var storage = {};
8711						storage['selectedRows'] = $.unique(Extension.selectedRows);
8712						datatable.stateKeep('checkbox', storage);
8713					}
8714
8715					// keep selectedAllRows in datasource params
8716					datatable.setDataSourceParam(options.vars.selectedAllRows, Extension.selectedAllRows);
8717
8718					$(datatable).trigger(pfx + 'datatable--on-click-checkbox', [$(this)]);
8719				});
8720
8721				// single row checkbox click
8722				$(datatable.tableBody).find('.' + pfx + 'checkbox--single > [type="checkbox"]').click(function(e) {
8723					var id = $(this).val();
8724					if ($(this).is(':checked')) {
8725						Extension.selectedRows.push(id);
8726						// remove from unselected rows
8727						Extension.unselectedRows = Extension.remove(Extension.unselectedRows, id);
8728					}
8729					else {
8730						Extension.unselectedRows.push(id);
8731						// remove from selected rows
8732						Extension.selectedRows = Extension.remove(Extension.selectedRows, id);
8733					}
8734
8735					// local checkbox header check
8736					if (!options.vars.requestIds && Extension.selectedRows.length < 1) {
8737						// remove select all checkbox, if there is no checked checkbox left
8738						$(datatable.tableHead).find('.' + pfx + 'checkbox--all > [type="checkbox"]').prop('checked', false);
8739					}
8740
8741					var storage = {};
8742					storage['selectedRows'] = $.unique(Extension.selectedRows);
8743					storage['unselectedRows'] = $.unique(Extension.unselectedRows);
8744					datatable.stateKeep('checkbox', storage);
8745
8746					$(datatable).trigger(pfx + 'datatable--on-click-checkbox', [$(this)]);
8747				});
8748			},
8749
8750			initSelect: function() {
8751				// selected all rows from server
8752				if (Extension.selectedAllRows && options.vars.requestIds) {
8753					if (!datatable.hasClass(pfx + 'datatable--error')) {
8754						// set header select all checkbox checked
8755						$(datatable.tableHead).find('.' + pfx + 'checkbox--all > [type="checkbox"]').prop('checked', true);
8756					}
8757
8758					// set all checkbox in table body
8759					datatable.setActiveAll(true);
8760
8761					// remove unselected rows
8762					Extension.unselectedRows.forEach(function(id) {
8763						datatable.setInactive(id);
8764					});
8765
8766				}
8767				else {
8768					// single check for server and local
8769					Extension.selectedRows.forEach(function(id) {
8770						datatable.setActive(id);
8771					});
8772
8773					// local checkbox; check if all checkboxes of currect page are checked
8774					if (!datatable.hasClass(pfx + 'datatable--error') && $(datatable.tableBody).find('.' + pfx + 'checkbox--single > [type="checkbox"]').not(':checked').length < 1) {
8775						// set header select all checkbox checked
8776						$(datatable.tableHead).find('.' + pfx + 'checkbox--all > [type="checkbox"]').prop('checked', true);
8777					}
8778				}
8779			},
8780
8781			/**
8782			 * Check if selector is enabled from options
8783			 */
8784			selectorEnabled: function() {
8785				return $.grep(datatable.options.columns, function(n, i) {
8786					return n.selector || false;
8787				})[0];
8788			},
8789
8790			initVars: function() {
8791				// get single select/unselect from localstorage
8792				var storage = datatable.stateGet('checkbox');
8793				if (typeof storage !== 'undefined') {
8794					Extension.selectedRows = storage['selectedRows'] || [];
8795					Extension.unselectedRows = storage['unselectedRows'] || [];
8796				}
8797			},
8798
8799			getSelectedId: function(path) {
8800				Extension.initVars();
8801
8802				// server selected all rows
8803				if (Extension.selectedAllRows && options.vars.requestIds) {
8804					if (typeof path === 'undefined') {
8805						path = options.vars.rowIds;
8806					}
8807
8808					// if selected all rows, return id from response meta
8809					var selectedAllRows = datatable.getObject(path, datatable.lastResponse) || [];
8810
8811					if (selectedAllRows.length > 0) {
8812						// remove single unselected rows from selectedAllRows ids from server response emta
8813						Extension.unselectedRows.forEach(function(id) {
8814							selectedAllRows = Extension.remove(selectedAllRows, parseInt(id));
8815						});
8816					}
8817					return selectedAllRows;
8818				}
8819
8820				// else return single checked selected rows
8821				return Extension.selectedRows;
8822			},
8823
8824			remove: function(array, element) {
8825				return array.filter(function(e) {
8826					return e !== element;
8827				});
8828			},
8829		};
8830
8831		// make the extension accessible from datatable init
8832		datatable.checkbox = function() {
8833			return Extension;
8834		};
8835
8836		if (typeof options === 'object') {
8837			options = $.extend(true, {}, $.fn[pluginName].checkbox.default, options);
8838			Extension.init.apply(this, [options]);
8839		}
8840
8841		return datatable;
8842	};
8843
8844	$.fn[pluginName].checkbox.default = {
8845		vars: {
8846			// select all rows flag to be sent to the server
8847			selectedAllRows: 'selectedAllRows',
8848			// request id parameter's name
8849			requestIds: 'requestIds',
8850			// response path to all rows id
8851			rowIds: 'meta.rowIds',
8852		},
8853	};
8854
8855}(jQuery));
8856var defaults = {
8857	layout: {
8858		icons: {
8859			pagination: {
8860				next: 'flaticon2-next',
8861				prev: 'flaticon2-back',
8862				first: 'flaticon2-fast-back',
8863				last: 'flaticon2-fast-next',
8864				more: 'flaticon-more-1',
8865			},
8866			rowDetail: {expand: 'fa fa-caret-down', collapse: 'fa fa-caret-right'},
8867		}
8868	}
8869};
8870
8871if (KTUtil.isRTL()) {
8872	defaults = {
8873		layout: {
8874			icons: {
8875				pagination: {
8876					next: 'flaticon2-back',
8877					prev: 'flaticon2-next',
8878					first: 'flaticon2-fast-next',
8879					last: 'flaticon2-fast-back',
8880				},
8881				rowDetail: {collapse: 'fa fa-caret-down', expand: 'fa fa-caret-right'},
8882			}
8883		}
8884	}
8885}
8886
8887$.extend(true, $.fn.KTDatatable.defaults, defaults);
8888"use strict";
8889
8890// Class definition
8891var KTChat = function () {
8892	var initChat = function (parentEl) {
8893		var messageListEl = KTUtil.find(parentEl, '.kt-scroll');
8894
8895		if (!messageListEl) {
8896			return;
8897		}
8898
8899		// initialize perfect scrollbar(see:  https://github.com/utatti/perfect-scrollbar)
8900		KTUtil.scrollInit(messageListEl, {
8901			windowScroll: false, // allow browser scroll when the scroll reaches the end of the side
8902			mobileNativeScroll: true,  // enable native scroll for mobile
8903			desktopNativeScroll: false, // disable native scroll and use custom scroll for desktop
8904			resetHeightOnDestroy: true,  // reset css height on scroll feature destroyed
8905			handleWindowResize: true, // recalculate hight on window resize
8906			rememberPosition: true, // remember scroll position in cookie
8907			height: function() {  // calculate height
8908				var height;
8909
8910				// Mobile mode
8911				if (KTUtil.isInResponsiveRange('tablet-and-mobile')) {
8912					return KTUtil.hasAttr(messageListEl, 'data-mobile-height') ? parseInt(KTUtil.attr(messageListEl, 'data-mobile-height')) : 300;
8913				}
8914
8915				// Desktop mode
8916				if (KTUtil.isInResponsiveRange('desktop') && KTUtil.hasAttr(messageListEl, 'data-height')) {
8917					return parseInt(KTUtil.attr(messageListEl, 'data-height'));
8918				}
8919
8920				var chatEl = KTUtil.find(parentEl, '.kt-chat');
8921				var portletHeadEl = KTUtil.find(parentEl, '.kt-portlet > .kt-portlet__head');
8922				var portletBodyEl = KTUtil.find(parentEl, '.kt-portlet > .kt-portlet__body');
8923				var portletFootEl = KTUtil.find(parentEl, '.kt-portlet > .kt-portlet__foot');
8924
8925				if (KTUtil.isInResponsiveRange('desktop')) {
8926					height = KTLayout.getContentHeight();
8927				} else {
8928					height = KTUtil.getViewPort().height;
8929				}
8930
8931				if (chatEl) {
8932					height = height - parseInt(KTUtil.css(chatEl, 'margin-top')) - parseInt(KTUtil.css(chatEl, 'margin-bottom'));
8933					height = height - parseInt(KTUtil.css(chatEl, 'padding-top')) - parseInt(KTUtil.css(chatEl, 'padding-bottom'));
8934				}
8935
8936				if (portletHeadEl) {
8937					height = height - parseInt(KTUtil.css(portletHeadEl, 'height'));
8938					height = height - parseInt(KTUtil.css(portletHeadEl, 'margin-top')) - parseInt(KTUtil.css(portletHeadEl, 'margin-bottom'));
8939				}
8940
8941				if (portletBodyEl) {
8942					height = height - parseInt(KTUtil.css(portletBodyEl, 'margin-top')) - parseInt(KTUtil.css(portletBodyEl, 'margin-bottom'));
8943					height = height - parseInt(KTUtil.css(portletBodyEl, 'padding-top')) - parseInt(KTUtil.css(portletBodyEl, 'padding-bottom'));
8944				}
8945
8946				if (portletFootEl) {
8947					height = height - parseInt(KTUtil.css(portletFootEl, 'height'));
8948					height = height - parseInt(KTUtil.css(portletFootEl, 'margin-top')) - parseInt(KTUtil.css(portletFootEl, 'margin-bottom'));
8949				}
8950
8951				// remove additional space
8952				height = height - 5;
8953
8954				return height;
8955			}
8956		});
8957
8958		// messaging
8959		var handleMessaging = function() {
8960			var scrollEl = KTUtil.find(parentEl, '.kt-scroll');
8961			var messagesEl = KTUtil.find(parentEl, '.kt-chat__messages');
8962            var textarea = KTUtil.find(parentEl, '.kt-chat__input textarea');
8963
8964            if (textarea.value.length === 0 ) {
8965                return;
8966            }
8967
8968			var node = document.createElement("DIV");
8969			KTUtil.addClass(node, 'kt-chat__message kt-chat__message--brand kt-chat__message--right');
8970
8971			var html =
8972				'<div class="kt-chat__user">' +
8973					'<span class="kt-chat__datetime">Just now</span>' +
8974					'<a href="#" class="kt-chat__username">Jason Muller</span></a>' +
8975					'<span class="kt-media kt-media--circle kt-media--sm">' +
8976						'<img src="./assets/media/users/100_12.jpg" alt="image">'  +
8977					'</span>' +
8978				'</div>' +
8979				'<div class="kt-chat__text kt-bg-light-brand">' +
8980					textarea.value
8981				'</div>';
8982
8983			KTUtil.setHTML(node, html);
8984			messagesEl.appendChild(node);
8985			textarea.value = '';
8986			scrollEl.scrollTop = parseInt(KTUtil.css(messagesEl, 'height'));
8987
8988			var ps;
8989			if (ps = KTUtil.data(scrollEl).get('ps')) {
8990				ps.update();
8991			}
8992
8993			setTimeout(function() {
8994				var node = document.createElement("DIV");
8995				KTUtil.addClass(node, 'kt-chat__message kt-chat__message--success');
8996
8997				var html =
8998					'<div class="kt-chat__user">' +
8999						'<span class="kt-media kt-media--circle kt-media--sm">' +
9000							'<img src="./assets/media/users/100_13.jpg" alt="image">'  +
9001						'</span>' +
9002						'<a href="#" class="kt-chat__username">Max Born</span></a>' +
9003						'<span class="kt-chat__datetime">Just now</span>' +
9004					'</div>' +
9005					'<div class="kt-chat__text kt-bg-light-success">' +
9006					'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.' +
9007					'</div>';
9008
9009				KTUtil.setHTML(node, html);
9010				messagesEl.appendChild(node);
9011				textarea.value = '';
9012				scrollEl.scrollTop = parseInt(KTUtil.css(messagesEl, 'height'));
9013
9014				var ps;
9015				if (ps = KTUtil.data(scrollEl).get('ps')) {
9016					ps.update();
9017				}
9018			}, 2000);
9019		}
9020
9021		// attach events
9022		KTUtil.on(parentEl, '.kt-chat__input textarea', 'keydown', function(e) {
9023			if (e.keyCode == 13) {
9024				handleMessaging();
9025				e.preventDefault();
9026
9027				return false;
9028			}
9029		});
9030
9031		KTUtil.on(parentEl, '.kt-chat__input .kt-chat__reply', 'click', function(e) {
9032			handleMessaging();
9033		});
9034	}
9035
9036	return {
9037		// public functions
9038		init: function() {
9039			// init modal chat example
9040			initChat( KTUtil.getByID('kt_chat_modal'));
9041
9042			// trigger click to show popup modal chat on page load
9043			if (encodeURI(window.location.hostname) == 'keenthemes.com' || encodeURI(window.location.hostname) == 'www.keenthemes.com') {
9044				setTimeout(function() {
9045		            if (!Cookies.get('kt_app_chat_shown')) {
9046		                var expires = new Date(new Date().getTime() + 60 * 60 * 1000); // expire in 60 minutes from now
9047		                Cookies.set('kt_app_chat_shown', 1, { expires: expires });
9048		                KTUtil.getByID('kt_app_chat_launch_btn').click();
9049		            }
9050		        }, 2000);
9051	        }
9052        },
9053
9054        setup: function(element) {
9055            initChat(element);
9056        }
9057	};
9058}();
9059
9060// webpack support
9061if (typeof module !== 'undefined') {
9062	module.exports = KTChat;
9063}
9064
9065KTUtil.ready(function() {
9066	KTChat.init();
9067});
9068
9069"use strict";
9070
9071var KTDemoPanel = function() {
9072    var demoPanel;
9073    var offcanvas;
9074
9075    var init = function() {
9076        offcanvas = new KTOffcanvas(demoPanel, {
9077            overlay: true,
9078            baseClass: 'kt-demo-panel',
9079            closeBy: 'kt_demo_panel_close',
9080            toggleBy: 'kt_demo_panel_toggle'
9081        });
9082
9083        var head = KTUtil.find(demoPanel, '.kt-demo-panel__head');
9084        var body = KTUtil.find(demoPanel, '.kt-demo-panel__body');
9085
9086        KTUtil.scrollInit(body, {
9087            disableForMobile: true,
9088            resetHeightOnDestroy: true,
9089            handleWindowResize: true,
9090            height: function() {
9091                var height = parseInt(KTUtil.getViewPort().height);
9092
9093                if (head) {
9094                    height = height - parseInt(KTUtil.actualHeight(head));
9095                    height = height - parseInt(KTUtil.css(head, 'marginBottom'));
9096                }
9097
9098                height = height - parseInt(KTUtil.css(demoPanel, 'paddingTop'));
9099                height = height - parseInt(KTUtil.css(demoPanel, 'paddingBottom'));
9100
9101                return height;
9102            }
9103        });
9104
9105        if (typeof offcanvas !== 'undefined' && offcanvas.length === 0) {
9106            offcanvas.on('hide', function() {
9107                var expires = new Date(new Date().getTime() + 60 * 60 * 1000); // expire in 60 minutes from now
9108                Cookies.set('kt_demo_panel_shown', 1, {expires: expires});
9109            });
9110        }
9111    }
9112
9113    var remind = function() {
9114        if (!(encodeURI(window.location.hostname) == 'keenthemes.com' || encodeURI(window.location.hostname) == 'www.keenthemes.com')) {
9115            return;
9116        }
9117
9118        setTimeout(function() {
9119            if (!Cookies.get('kt_demo_panel_shown')) {
9120                var expires = new Date(new Date().getTime() + 15 * 60 * 1000); // expire in 15 minutes from now
9121                Cookies.set('kt_demo_panel_shown', 1, { expires: expires });
9122                offcanvas.show();
9123            }
9124        }, 4000);
9125    }
9126
9127    return {
9128        init: function() {
9129            demoPanel = KTUtil.getByID('kt_demo_panel');
9130
9131            init();
9132            remind();
9133        }
9134    };
9135}();
9136
9137$(document).ready(function() {
9138    KTDemoPanel.init();
9139});
9140
9141"use strict";
9142
9143var KTLayout = function() {
9144    var body;
9145
9146    var header;
9147    var headerMenu;
9148    var headerMenuOffcanvas;
9149    var mobileHeaderTopbarToggle;
9150
9151    var asideMenu;
9152    var asideMenuOffcanvas;
9153
9154    var scrollTop;
9155
9156    var pageStickyPortlet;
9157
9158    // Header
9159    var initHeader = function() {
9160        var tmp;
9161        var headerEl = KTUtil.get('kt_header');
9162        var options = {
9163            classic: {
9164                desktop: true,
9165                mobile: false
9166            },
9167            offset: {},
9168            minimize: {}
9169        };
9170
9171        options.minimize.mobile = false;
9172
9173        if (KTUtil.attr(headerEl, 'data-ktheader-minimize') == 'on') {
9174            options.minimize.desktop = {};
9175            options.minimize.desktop.on = 'kt-header--minimize';
9176            options.offset.desktop = parseInt(KTUtil.css(headerEl, 'height')) - 10;
9177        } else {
9178            options.minimize.desktop = false;
9179        }
9180
9181        header = new KTHeader('kt_header', options);
9182
9183        if (asideMenu) {
9184            header.on('minimizeOn', function() {
9185                asideMenu.scrollReInit();
9186            });
9187
9188            header.on('minimizeOff', function() {
9189                asideMenu.scrollReInit();
9190            });
9191        }
9192    }
9193
9194    // Header Menu
9195    var initHeaderMenu = function() {
9196        // init aside left offcanvas
9197        headerMenuOffcanvas = new KTOffcanvas('kt_header_menu_wrapper', {
9198            overlay: true,
9199            baseClass: 'kt-header-menu-wrapper',
9200            closeBy: 'kt_header_menu_mobile_close_btn',
9201            toggleBy: {
9202                target: 'kt_header_mobile_toggler',
9203                state: 'kt-header-mobile__toolbar-toggler--active'
9204            }
9205        });
9206
9207        headerMenu = new KTMenu('kt_header_menu', {
9208            submenu: {
9209                desktop: 'dropdown',
9210                tablet: 'accordion',
9211                mobile: 'accordion'
9212            },
9213            accordion: {
9214                slideSpeed: 200, // accordion toggle slide speed in milliseconds
9215                expandAll: false // allow having multiple expanded accordions in the menu
9216            }
9217        });
9218    }
9219
9220    // Header Topbar
9221    var initHeaderTopbar = function() {
9222        mobileHeaderTopbarToggle = new KTToggle('kt_header_mobile_topbar_toggler', {
9223            target: 'body',
9224            targetState: 'kt-header__topbar--mobile-on',
9225            togglerState: 'kt-header-mobile__toolbar-topbar-toggler--active'
9226        });
9227    }
9228
9229    // Aside
9230    var initAside = function() {
9231        // init aside left offcanvas
9232        var asidBrandHover = false;
9233        var aside = KTUtil.get('kt_aside');
9234        var asideBrand = KTUtil.get('kt_aside_brand');
9235        var asideOffcanvasClass = KTUtil.hasClass(aside, 'kt-aside--offcanvas-default') ? 'kt-aside--offcanvas-default' : 'kt-aside';
9236
9237        var asideMenuOffcanvas = new KTOffcanvas('kt_aside', {
9238            baseClass: asideOffcanvasClass,
9239            overlay: true,
9240            closeBy: 'kt_aside_close_btn',
9241            toggleBy: [{
9242                target: 'kt_aside_mobile_toggler',
9243                state: 'kt-aside-toggler--active'
9244            },{
9245                target: 'kt_aside_toggler',
9246                state: 'kt-aside-toggler--active'
9247            }]
9248        });
9249    }
9250
9251    // Aside menu
9252    var initAsideMenu = function() {
9253        // Init aside menu
9254        var menu = KTUtil.get('kt_aside_menu');
9255        var menuDesktopMode = (KTUtil.attr(menu, 'data-ktmenu-dropdown') === '1' ? 'dropdown' : 'accordion');
9256
9257        // Init scrollable menu container
9258        var scroll;
9259        if (KTUtil.attr(menu, 'data-ktmenu-scroll') === '1') {
9260            scroll = {
9261                rememberPosition: true, // remember position on page reload
9262                height: function() {  // calculate available scrollable area height
9263                    var height;
9264
9265                    if (KTUtil.isInResponsiveRange('desktop')) {
9266                        height = parseInt(KTUtil.getViewPort().height);
9267                        height = height - parseInt(KTUtil.css(menu, 'marginTop')) - parseInt(KTUtil.css(menu, 'marginBottom'));
9268                    } else {
9269                        height = parseInt(KTUtil.getViewPort().height);
9270                    }
9271
9272                    return height;
9273                }
9274            };
9275        }
9276
9277        // Init aside menu
9278        asideMenu = new KTMenu('kt_aside_menu', {
9279            // vertical scroll
9280            scroll: scroll,
9281
9282            // submenu setup
9283            submenu: {
9284                desktop: menuDesktopMode,
9285                tablet: 'accordion', // menu set to accordion in tablet mode
9286                mobile: 'accordion' // menu set to accordion in mobile mode
9287            },
9288
9289            //accordion setup
9290            accordion: {
9291                expandAll: false // allow having multiple expanded accordions in the menu
9292            }
9293        });
9294    }
9295
9296    // Scrolltop
9297    var initScrolltop = function() {
9298        var scrolltop = new KTScrolltop('kt_scrolltop', {
9299            offset: 200,
9300            speed: 400
9301        });
9302    }
9303
9304    // Init page sticky portlet
9305    var initPageStickyPortlet = function() {
9306        return new KTPortlet('kt_page_portlet', {
9307            sticky: {
9308                offset: parseInt(KTUtil.css( KTUtil.get('kt_header'), 'height')) + 200,
9309                zIndex: 90,
9310                position: {
9311                    top: function() {
9312                        if (KTUtil.isInResponsiveRange('desktop')) {
9313                            return 60;
9313 //parseInt(KTUtil.actualHeight( KTUtil.get('kt_header'), 'height') );
9314                        } else {
9315                            return parseInt(KTUtil.css( KTUtil.get('kt_header_mobile'), 'height') );
9316                        }
9317                    },
9318                    left: function(portlet) {
9319						var porletEl = portlet.getSelf();
9320
9321						return KTUtil.offset(porletEl).left;
9322					},
9323					right: function(portlet) {
9324                        var porletEl = portlet.getSelf();
9325
9326                        var portletWidth = parseInt(KTUtil.css(porletEl, 'width'));
9327						var bodyWidth = parseInt(KTUtil.css(KTUtil.get('body'), 'width'));
9328						var portletOffsetLeft = KTUtil.offset(porletEl).left;
9329
9330						return bodyWidth - portletWidth - portletOffsetLeft;
9331					}
9332                }
9333            }
9334        });
9335    }
9336
9337	// Calculate content available full height
9338	var getContentHeight = function() {
9339		var height;
9340
9341		height = KTUtil.getViewPort().height;
9342
9343		if (KTUtil.getByID('kt_header')) {
9344            height = height - KTUtil.actualHeight('kt_header');
9345		}
9346
9347		if (KTUtil.getByID('kt_subheader')) {
9348            height = height - KTUtil.actualHeight('kt_subheader');
9349		}
9350
9351		if (KTUtil.getByID('kt_footer')) {
9352			height = height - parseInt(KTUtil.css('kt_footer', 'height'));
9353		}
9354
9355		if (KTUtil.getByID('kt_content')) {
9356			height = height - parseInt(KTUtil.css('kt_content', 'padding-top')) - parseInt(KTUtil.css('kt_content', 'padding-bottom'));
9357		}
9358
9359		return height;
9360	}
9361
9362    return {
9363        init: function() {
9364            body = KTUtil.get('body');
9365
9366            this.initHeader();
9367            this.initAside();
9368            this.initPageStickyPortlet();
9369
9370            // Non functional links notice(can be removed in production)
9371			$('#kt_aside_menu, #kt_header_menu').on('click', '.kt-menu__link[href="#"]', function(e) {
9372				swal.fire("", "You have clicked on a non-functional dummy link!");
9373
9374				e.preventDefault();
9375			});
9376        },
9377
9378        initHeader: function() {
9379            initHeader();
9380            initHeaderMenu();
9381            initHeaderTopbar();
9382            initScrolltop();
9383        },
9384
9385        initAside: function() {
9386            initAside();
9387            initAsideMenu();
9388        },
9389
9390        getAsideMenu: function() {
9391            return asideMenu;
9392        },
9393
9394        initPageStickyPortlet: function() {
9395            if (!KTUtil.get('kt_page_portlet')) {
9396                return;
9397            }
9398
9399            pageStickyPortlet = initPageStickyPortlet();
9400            pageStickyPortlet.initSticky();
9401
9402            KTUtil.addResizeHandler(function(){
9403                pageStickyPortlet.updateSticky();
9404            });
9405
9406            initPageStickyPortlet();
9407        },
9408
9409        closeMobileAsideMenuOffcanvas: function() {
9410            if (KTUtil.isMobileDevice()) {
9411                asideMenuOffcanvas.hide();
9412            }
9413        },
9414
9415        closeMobileHeaderMenuOffcanvas: function() {
9416            if (KTUtil.isMobileDevice()) {
9417                headerMenuOffcanvas.hide();
9418            }
9419        },
9420
9421        getContentHeight: function() {
9422			return getContentHeight();
9423		}
9424    };
9425}();
9426
9427// webpack support
9428if (typeof module !== 'undefined') {
9429    module.exports = KTLayout;
9430}
9431
9432$(document).ready(function() {
9433    KTLayout.init();
9434});
9435
9436"use strict";
9437
9438var KTOffcanvasPanel = function() {
9439    var notificationPanel;
9440    var quickActionsPanel;
9441    var profilePanel;
9442    var searchPanel;
9443
9444    var initNotifications = function() {
9445        var head = KTUtil.find(notificationPanel, '.kt-offcanvas-panel__head');
9446        var body = KTUtil.find(notificationPanel, '.kt-offcanvas-panel__body');
9447
9448        var offcanvas = new KTOffcanvas(notificationPanel, {
9449            overlay: true,
9450            baseClass: 'kt-offcanvas-panel',
9451            closeBy: 'kt_offcanvas_toolbar_notifications_close',
9452            toggleBy: 'kt_offcanvas_toolbar_notifications_toggler_btn'
9453        });
9454
9455        KTUtil.scrollInit(body, {
9456            disableForMobile: true,
9457            resetHeightOnDestroy: true,
9458            handleWindowResize: true,
9459            height: function() {
9460                var height = parseInt(KTUtil.getViewPort().height);
9461
9462                if (head) {
9463                    height = height - parseInt(KTUtil.actualHeight(head));
9464                    height = height - parseInt(KTUtil.css(head, 'marginBottom'));
9465                }
9466
9467                height = height - parseInt(KTUtil.css(notificationPanel, 'paddingTop'));
9468                height = height - parseInt(KTUtil.css(notificationPanel, 'paddingBottom'));
9469
9470                return height;
9471            }
9472        });
9473    }
9474
9475    var initQucikActions = function() {
9476        var head = KTUtil.find(quickActionsPanel, '.kt-offcanvas-panel__head');
9477        var body = KTUtil.find(quickActionsPanel, '.kt-offcanvas-panel__body');
9478
9479        var offcanvas = new KTOffcanvas(quickActionsPanel, {
9480            overlay: true,
9481            baseClass: 'kt-offcanvas-panel',
9482            closeBy: 'kt_offcanvas_toolbar_quick_actions_close',
9483            toggleBy: 'kt_offcanvas_toolbar_quick_actions_toggler_btn'
9484        });
9485
9486        KTUtil.scrollInit(body, {
9487            disableForMobile: true,
9488            resetHeightOnDestroy: true,
9489            handleWindowResize: true,
9490            height: function() {
9491                var height = parseInt(KTUtil.getViewPort().height);
9492
9493                if (head) {
9494                    height = height - parseInt(KTUtil.actualHeight(head));
9495                    height = height - parseInt(KTUtil.css(head, 'marginBottom'));
9496                }
9497
9498                height = height - parseInt(KTUtil.css(quickActionsPanel, 'paddingTop'));
9499                height = height - parseInt(KTUtil.css(quickActionsPanel, 'paddingBottom'));
9500
9501                return height;
9502            }
9503        });
9504    }
9505
9506    var initProfile = function() {
9507        var head = KTUtil.find(profilePanel, '.kt-offcanvas-panel__head');
9508        var body = KTUtil.find(profilePanel, '.kt-offcanvas-panel__body');
9509
9510        var offcanvas = new KTOffcanvas(profilePanel, {
9511            overlay: true,
9512            baseClass: 'kt-offcanvas-panel',
9513            closeBy: 'kt_offcanvas_toolbar_profile_close',
9514            toggleBy: 'kt_offcanvas_toolbar_profile_toggler_btn'
9515        });
9516
9517        KTUtil.scrollInit(body, {
9518            disableForMobile: true,
9519            resetHeightOnDestroy: true,
9520            handleWindowResize: true,
9521            height: function() {
9522                var height = parseInt(KTUtil.getViewPort().height);
9523
9524                if (head) {
9525                    height = height - parseInt(KTUtil.actualHeight(head));
9526                    height = height - parseInt(KTUtil.css(head, 'marginBottom'));
9527                }
9528
9529                height = height - parseInt(KTUtil.css(profilePanel, 'paddingTop'));
9530                height = height - parseInt(KTUtil.css(profilePanel, 'paddingBottom'));
9531
9532                return height;
9533            }
9534        });
9535    }
9536
9537    var initSearch = function() {
9538        var head = KTUtil.find(searchPanel, '.kt-offcanvas-panel__head');
9539        var body = KTUtil.find(searchPanel, '.kt-offcanvas-panel__body');
9540        var search = KTUtil.get('kt_quick_search_offcanvas');
9541        var form = KTUtil.find(search, '.kt-quick-search__form');
9542        var wrapper = KTUtil.find(search, '.kt-quick-search__wrapper');
9543
9544        var offcanvas = new KTOffcanvas(searchPanel, {
9545            overlay: true,
9546            baseClass: 'kt-offcanvas-panel',
9547            closeBy: 'kt_offcanvas_toolbar_search_close',
9548            toggleBy: 'kt_offcanvas_toolbar_search_toggler_btn'
9549        });
9550
9551        KTUtil.scrollInit(wrapper, {
9552            disableForMobile: true,
9553            resetHeightOnDestroy: true,
9554            handleWindowResize: true,
9555            height: function() {
9556                var height = parseInt(KTUtil.getViewPort().height);
9557
9558                height = height - parseInt(KTUtil.actualHeight(form));
9559                height = height - parseInt(KTUtil.css(form, 'marginBottom'));
9560
9561                if (head) {
9562                    height = height - parseInt(KTUtil.actualHeight(head));
9563                    height = height - parseInt(KTUtil.css(head, 'marginBottom'));
9564                }
9565
9566                height = height - parseInt(KTUtil.css(searchPanel, 'paddingTop'));
9567                height = height - parseInt(KTUtil.css(searchPanel, 'paddingBottom'));
9568
9569                return height;
9570            }
9571        });
9572    }
9573
9574    return {
9575        init: function() {
9576            notificationPanel = KTUtil.get('kt_offcanvas_toolbar_notifications');
9577            quickActionsPanel = KTUtil.get('kt_offcanvas_toolbar_quick_actions');
9578            profilePanel = KTUtil.get('kt_offcanvas_toolbar_profile');
9579            searchPanel = KTUtil.get('kt_offcanvas_toolbar_search');
9580
9581            initNotifications();
9582            initQucikActions();
9583            initProfile();
9584            initSearch();
9585        }
9586    };
9587}();
9588
9589// Init on page load completed
9590KTUtil.ready(function() {
9591    KTOffcanvasPanel.init();
9592});
9593
9594"use strict";
9595
9596var KTQuickPanel = function() {
9597    var panel;
9598    var notificationPanel;
9599    var logsPanel;
9600    var settingsPanel;
9601
9602    var getContentHeight = function() {
9603        var height;
9604        var nav = KTUtil.find(panel, '.kt-quick-panel__nav');
9605        var content = KTUtil.find(panel, '.kt-quick-panel__content');
9606
9607        height = parseInt(KTUtil.getViewPort().height) - parseInt(KTUtil.actualHeight(nav)) - (2 * parseInt(KTUtil.css(nav, 'padding-top'))) - 10;
9608
9609        return height;
9610    }
9611
9612    var initOffcanvas = function() {
9613        new KTOffcanvas(panel, {
9614            overlay: true,
9615            baseClass: 'kt-quick-panel',
9616            closeBy: 'kt_quick_panel_close_btn',
9617            toggleBy: 'kt_quick_panel_toggler_btn'
9618        });
9619    }
9620
9621    var initNotifications = function() {
9622        KTUtil.scrollInit(notificationPanel, {
9623            mobileNativeScroll: true,
9624            resetHeightOnDestroy: true,
9625            handleWindowResize: true,
9626            height: function() {
9627                return getContentHeight();
9628            }
9629        });
9630    }
9631
9632    var initLogs = function() {
9633        KTUtil.scrollInit(logsPanel, {
9634            mobileNativeScroll: true,
9635            resetHeightOnDestroy: true,
9636            handleWindowResize: true,
9637            height: function() {
9638                return getContentHeight();
9639            }
9640        });
9641    }
9642
9643    var initSettings = function() {
9644        KTUtil.scrollInit(settingsPanel, {
9645            mobileNativeScroll: true,
9646            resetHeightOnDestroy: true,
9647            handleWindowResize: true,
9648            height: function() {
9649                return getContentHeight();
9650            }
9651        });
9652    }
9653
9654    var updatePerfectScrollbars = function() {
9655        $(panel).find('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
9656            KTUtil.scrollUpdate(notificationPanel);
9657            KTUtil.scrollUpdate(logsPanel);
9658            KTUtil.scrollUpdate(settingsPanel);
9659        });
9660    }
9661
9662    return {
9663        init: function() {
9664            panel = KTUtil.get('kt_quick_panel');
9665            notificationPanel = KTUtil.get('kt_quick_panel_tab_notifications');
9666            logsPanel = KTUtil.get('kt_quick_panel_tab_logs');
9667            settingsPanel = KTUtil.get('kt_quick_panel_tab_settings');
9668
9669            initOffcanvas();
9670            initNotifications();
9671            initLogs();
9672            initSettings();
9673            updatePerfectScrollbars();
9674        }
9675    };
9676}();
9677
9678$(document).ready(function() {
9679    KTQuickPanel.init();
9680});
9681
9682"use strict";
9683
9684var KTQuickSearch = function() {
9685    var target;
9686    var form;
9687    var input;
9688    var closeIcon;
9689    var resultWrapper;
9690    var resultDropdown;
9691    var resultDropdownToggle;
9692    var inputGroup;
9693    var query = '';
9694
9695    var hasResult = false;
9696    var timeout = false;
9697    var isProcessing = false;
9698    var requestTimeout = 200; // ajax request fire timeout in milliseconds
9699    var spinnerClass = 'kt-spinner kt-spinner--input kt-spinner--sm kt-spinner--brand kt-spinner--right';
9700    var resultClass = 'kt-quick-search--has-result';
9701    var minLength = 2;
9702
9703    var showProgress = function() {
9704        isProcessing = true;
9705        KTUtil.addClass(inputGroup, spinnerClass);
9706
9707        if (closeIcon) {
9708            KTUtil.hide(closeIcon);
9709        }
9710    }
9711
9712    var hideProgress = function() {
9713        isProcessing = false;
9714        KTUtil.removeClass(inputGroup, spinnerClass);
9715
9716        if (closeIcon) {
9717            if (input.value.length < minLength) {
9718                KTUtil.hide(closeIcon);
9719            } else {
9720                KTUtil.show(closeIcon, 'flex');
9721            }
9722        }
9723    }
9724
9725    var showDropdown = function() {
9726        if (resultDropdownToggle && !KTUtil.hasClass(resultDropdown, 'show')) {
9727            $(resultDropdownToggle).dropdown('toggle');
9728            $(resultDropdownToggle).dropdown('update');
9729        }
9730    }
9731
9732    var hideDropdown = function() {
9733        if (resultDropdownToggle && KTUtil.hasClass(resultDropdown, 'show')) {
9734            $(resultDropdownToggle).dropdown('toggle');
9735        }
9736    }
9737
9738    var processSearch = function() {
9739        if (hasResult && query === input.value) {
9740            hideProgress();
9741            KTUtil.addClass(target, resultClass);
9742            showDropdown();
9743            KTUtil.scrollUpdate(resultWrapper);
9744
9745            return;
9746        }
9747
9748        query = input.value;
9749
9750        KTUtil.removeClass(target, resultClass);
9751        showProgress();
9752        hideDropdown();
9753
9754        setTimeout(function() {
9755            $.ajax({
9756                url: 'https://keenthemes.com/metronic/tools/preview/api/quick_search.php',
9757                data: {
9758                    query: query
9759                },
9760                dataType: 'html',
9761                success: function(res) {
9762                    hasResult = true;
9763                    hideProgress();
9764                    KTUtil.addClass(target, resultClass);
9765                    KTUtil.setHTML(resultWrapper, res);
9766                    showDropdown();
9767                    KTUtil.scrollUpdate(resultWrapper);
9768                },
9769                error: function(res) {
9770                    hasResult = false;
9771                    hideProgress();
9772                    KTUtil.addClass(target, resultClass);
9773                    KTUtil.setHTML(resultWrapper, '<span class="kt-quick-search__message">Connection error. Pleae try again later.</div>');
9774                    showDropdown();
9775                    KTUtil.scrollUpdate(resultWrapper);
9776                }
9777            });
9778        }, 1000);
9779    }
9780
9781    var handleCancel = function(e) {
9782        input.value = '';
9783        query = '';
9784        hasResult = false;
9785        KTUtil.hide(closeIcon);
9786        KTUtil.removeClass(target, resultClass);
9787        hideDropdown();
9788    }
9789
9790    var handleSearch = function() {
9791        if (input.value.length < minLength) {
9792            hideProgress();
9793            hideDropdown();
9794
9795            return;
9796        }
9797
9798        if (isProcessing == true) {
9799            return;
9800        }
9801
9802        if (timeout) {
9803            clearTimeout(timeout);
9804        }
9805
9806        timeout = setTimeout(function() {
9807            processSearch();
9808        }, requestTimeout);
9809    }
9810
9811    return {
9812        init: function(element) {
9813            // Init
9814            target = element;
9815            form = KTUtil.find(target, '.kt-quick-search__form');
9816            input = KTUtil.find(target, '.kt-quick-search__input');
9817            closeIcon = KTUtil.find(target, '.kt-quick-search__close');
9818            resultWrapper = KTUtil.find(target, '.kt-quick-search__wrapper');
9819            resultDropdown = KTUtil.find(target, '.dropdown-menu');
9820            resultDropdownToggle = KTUtil.find(target, '[data-toggle="dropdown"]');
9821            inputGroup = KTUtil.find(target, '.input-group');
9822
9823            // Attach input keyup handler
9824            KTUtil.addEvent(input, 'keyup', handleSearch);
9825            KTUtil.addEvent(input, 'focus', handleSearch);
9826
9827            // Prevent enter click
9828            form.onkeypress = function(e) {
9829                var key = e.charCode || e.keyCode || 0;
9830                if (key == 13) {
9831                    e.preventDefault();
9832                }
9833            }
9834
9835            KTUtil.addEvent(closeIcon, 'click', handleCancel);
9836        }
9837    };
9838};
9839
9840var KTQuickSearchInline = KTQuickSearch;
9841var KTQuickSearchOffcanvas = KTQuickSearch;
9842
9843// Init on page load completed
9844
9845KTUtil.ready(function() {
9846    if (KTUtil.get('kt_quick_search_dropdown')) {
9847        KTQuickSearch().init(KTUtil.get('kt_quick_search_dropdown'));
9848    }
9849
9850    if (KTUtil.get('kt_quick_search_inline')) {
9851        KTQuickSearchInline().init(KTUtil.get('kt_quick_search_inline'));
9852    }
9853
9854    if (KTUtil.get('kt_quick_search_offcanvas')) {
9855        KTQuickSearchOffcanvas().init(KTUtil.get('kt_quick_search_offcanvas'));
9856    }
9857});

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.