1
0
mirror of https://github.com/maximebf/php-debugbar.git synced 2025-06-11 00:21:16 +02:00
Files
php-debugbar/debugbar/debugbar.js
2013-06-19 13:16:45 +09:00

580 lines
16 KiB
JavaScript

if (typeof(PhpDebugBar) == 'undefined') {
// namespace
var PhpDebugBar = {};
}
if (typeof(localStorage) == 'undefined') {
// provide mock localStorage object for dumb browsers
var localStorage = {
setItem: function(key, value) {},
getItem: function(key) { return null; }
};
}
/**
* 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.
*
* @constructor
*/
PhpDebugBar.DebugBar = (function($) {
/**
* Returns the value from an object property.
* Using dots in the key, it is possible to retreive nested property values
*
* @param {Object} dict
* @param {String} key
* @param {Object} default_value
* @return {Object}
*/
function getDictValue(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;
}
// ------------------------------------------------------------------
/**
* 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.
*
* @this {Tab}
* @constructor
* @param {String} title
* @param {Object} widget
*/
var Tab = function(title, widget) {
this.tab = $('<a href="javascript:" class="phpdebugbar-tab" />')
this.tabText = $('<span class="text" />').text(title).appendTo(this.tab);
this.badge = $('<span class="badge" />').appendTo(this.tab);
this.panel = $('<div class="phpdebugbar-panel" />');
this.replaceWidget(widget);
};
/**
* Sets the title of the tab
*
* @this {Tab}
* @param {String} text
*/
Tab.prototype.setTitle = function(text) {
this.tab.text(text);
};
/**
* Sets the badge value of the tab
*
* @this {Tab}
* @param {String} value
*/
Tab.prototype.setBadgeValue = function(value) {
if (value === null) {
this.badge.hide();
} else {
this.badge.text(value).show();
}
};
/**
* Replaces the widget inside the panel
*
* @this {Tab}
* @param {Object} new_widget
*/
Tab.prototype.replaceWidget = function(new_widget) {
this.panel.empty().append(new_widget.element);
this.widget = new_widget;
};
// ------------------------------------------------------------------
/**
* Indicator
*
* An indicator is a text and an icon to display single value information
* right inside the always visible part of the debug bar
*
* @this {Indicator}
* @constructor
* @param {String} icon
* @param {String} tooltip
* @param {String} position "right" or "left", default is "right"
*/
var Indicator = function(icon, tooltip, position) {
if (!position) {
position = 'right'
}
this.position = position;
this.element = $('<span class="phpdebugbar-indicator" />').css('float', position);
this.label = $('<span class="text" />').appendTo(this.element);
if (icon) {
$('<i class="icon-' + icon + '" />').insertBefore(this.label);
}
if (tooltip) {
this.element.append($('<span class="tooltip" />').text(tooltip));
}
};
/**
* Sets the text of the indicator
*
* @this {Indicator}
* @param {String} text
*/
Indicator.prototype.setText = function(text) {
this.element.find('.text').text(text);
};
/**
* Sets the tooltip of the indicator
*
* @this {Indicator}
* @param {String} text
*/
Indicator.prototype.setTooltip = function(text) {
this.element.find('.tooltip').text(text);
};
// ------------------------------------------------------------------
/**
* DebugBar
*
* @this {DebugBar}
* @constructor
*/
var DebugBar = function() {
this.controls = {};
this.dataMap = {};
this.datasets = {};
this.initUI();
this.init();
};
/**
* Initialiazes the UI
*
* @this {DebugBar}
*/
DebugBar.prototype.initUI = function() {
var self = this;
this.element = $('<div class="phpdebugbar" />').appendTo('body');
this.header = $('<div class="phpdebugbar-header" />').appendTo(this.element);
this.body = $('<div class="phpdebugbar-body" />').appendTo(this.element);
this.resizeHandle = $('<div class="phpdebugbar-resize-handle" />').appendTo(this.body);
this.firstPanelName = null;
this.activePanelName = null;
// allow resizing by dragging handle
this.body.drag('start', function(e, dd) {
dd.height = $(this).height();
}).drag(function(e, dd) {
var h = Math.max(100, dd.height - dd.deltaY);
$(this).css('height', h);
localStorage.setItem('phpdebugbar-height', h);
}, {handle: '.phpdebugbar-resize-handle'});
// close button
this.closeButton = $('<a class="phpdebugbar-close-btn" href="javascript:"><i class="icon-remove"></i></a>').appendTo(this.header);
this.closeButton.click(function() {
self.hidePanels();
});
// select box for data sets
this.datasetSelectBox = $('<select class="phpdebugbar-datasets-switcher" />').appendTo(this.header);
this.datasetSelectBox.change(function() {
self.dataChangeHandler(self.datasets[this.value]);
});
};
/**
* Custom initialiaze function for subsclasses
*
* @this {DebugBar}
*/
DebugBar.prototype.init = function() {};
/**
* 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}
*/
DebugBar.prototype.restoreState = function() {
// bar height
var height = localStorage.getItem('phpdebugbar-height');
if (height) {
this.body.css('height', height);
} else {
localStorage.setItem('phpdebugbar-height', this.body.height());
}
// bar visibility
var visible = localStorage.getItem('phpdebugbar-visible');
if (visible && visible == '1') {
this.showPanel(localStorage.getItem('phpdebugbar-panel'));
}
};
/**
* 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}
*/
DebugBar.prototype.createTab = function(name, widget, title) {
var tab = new Tab(title || (name.replace(/[_\-]/g, ' ').charAt(0).toUpperCase() + name.slice(1)), widget);
return this.addTab(name, tab);
};
/**
* Adds a new tab
*
* @this {DebugBar}
* @param {String} name Internal name
* @param {Tab} tab Tab object
* @return {Tab}
*/
DebugBar.prototype.addTab = function(name, tab) {
if (this.isControl(name)) {
throw new Exception(name + ' already exists');
}
var self = this;
tab.tab.appendTo(this.header).click(function() { self.showPanel(name); });
tab.panel.appendTo(this.body);
this.controls[name] = tab;
if (this.firstPanelName == null) {
this.firstPanelName = name;
}
return tab;
};
/**
* Returns a Tab object
*
* @this {DebugBar}
* @param {String} name
* @return {Tab}
*/
DebugBar.prototype.getTab = function(name) {
if (this.isTab(name)) {
return this.controls[name];
}
};
/**
* 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}
*/
DebugBar.prototype.createIndicator = function(name, icon, tooltip, position) {
var indicator = new Indicator(icon, tooltip, position);
return this.addIndicator(name, indicator);
};
/**
* Adds an indicator
*
* @this {DebugBar}
* @param {String} name Internal name
* @param {Indicator} indicator Indicator object
* @return {Indicator}
*/
DebugBar.prototype.addIndicator = function(name, indicator) {
if (this.isControl(name)) {
throw new Exception(name + ' already exists');
}
if (indicator.position == 'right') {
indicator.element.appendTo(this.header);
} else {
indicator.element.insertBefore(this.header.children().first())
}
this.controls[name] = indicator;
return indicator;
};
/**
* Returns an Indicator object
*
* @this {DebugBar}
* @param {String} name
* @return {Indicator}
*/
DebugBar.prototype.getIndicator = function(name) {
if (this.isIndicator(name)) {
return this.controls[name];
}
};
/**
* Adds a control
*
* @param {String} name
* @param {Object} control
* @return {Object}
*/
DebugBar.prototype.addControl = function(name, control) {
if (control instanceof Tab) {
this.addTab(name, control);
} else if (control instanceof Indicator) {
this.addIndicator(name, control);
} else {
throw new Exception("Unknown type of control");
}
return control;
};
/**
* Checks if there's a control under the specified name
*
* @this {DebugBar}
* @param {String} name
* @return {Boolean}
*/
DebugBar.prototype.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}
*/
DebugBar.prototype.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}
*/
DebugBar.prototype.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}
*/
DebugBar.prototype.reset = function() {
this.hidePanels();
this.body.find('.phpdebugbar-panel').remove();
this.header.find('.phpdebugbar-tab, .phpdebugbar-indicator').remove();
this.controls = {};
};
/**
* Open the debug bar and display a specified panel
*
* @this {DebugBar}
* @param {String} name If not specified, display the first panel
*/
DebugBar.prototype.showPanel = function(name) {
this.resizeHandle.show();
this.body.show();
this.closeButton.show();
if (!name) {
if (this.activePanelName) {
name = this.activePanelName;
} else {
name = this.firstPanelName;
}
}
this.header.find('.phpdebugbar-tab.active').removeClass('active');
this.body.find('.phpdebugbar-panel.active').removeClass('active');
if (this.isTab(name)) {
this.controls[name].tab.addClass('active');
this.controls[name].panel.addClass('active').show();
this.activePanelName = name;
}
localStorage.setItem('phpdebugbar-visible', '1');
localStorage.setItem('phpdebugbar-panel', name);
};
/**
* Shows the first panel
*
* @this {DebugBar}
*/
DebugBar.prototype.showFirstPanel = function() {
this.showPanel(this.firstPanelName);
};
/**
* Hide panels and "close" the debug bar
*
* @this {DebugBar}
*/
DebugBar.prototype.hidePanels = function() {
this.header.find('.phpdebugbar-tab.active').removeClass('active');
this.body.hide();
this.closeButton.hide();
this.resizeHandle.hide();
localStorage.setItem('phpdebugbar-visible', '0');
};
/**
* 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
*/
DebugBar.prototype.setDataMap = function(map) {
this.dataMap = map;
};
/**
* Same as setDataMap() but appends to the existing map
* rather than replacing it
*
* @this {DebugBar}
* @param {Object} map
*/
DebugBar.prototype.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
*/
DebugBar.prototype.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
* @return {String} Dataset's id
*/
DebugBar.prototype.addDataSet = function(data, id) {
id = id || ("Request #" + (Object.keys(this.datasets).length + 1));
this.datasets[id] = data;
this.datasetSelectBox.append($('<option value="' + id + '">' + id + '</option>'));
if (Object.keys(this.datasets).length > 1) {
this.datasetSelectBox.show();
}
this.showDataSet(id);
return id;
};
/**
* Returns the data from a dataset
*
* @this {DebugBar}
* @param {String} id
* @return {Object}
*/
DebugBar.prototype.getDataSet = function(id) {
return this.datasets[id];
};
/**
* Switch the currently displayed dataset
*
* @this {DebugBar}
* @param {String} id
*/
DebugBar.prototype.showDataSet = function(id) {
this.dataChangeHandler(this.datasets[id]);
this.datasetSelectBox.val(id);
};
/**
* Called when the current dataset is modified.
*
* @this {DebugBar}
* @param {Object} data
*/
DebugBar.prototype.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(':')[0];
self.getTab(key).setBadgeValue(d);
} else if (self.isIndicator(key)) {
self.getIndicator(key).setText(d);
} else {
self.getTab(key).widget.setData(d);
}
});
};
DebugBar.Tab = Tab;
DebugBar.Indicator = Indicator;
return DebugBar;
})(jQuery);