function DebugToolbar(cfg) {
var $toolbar, $window, $toolbarFrame, $windowFrame;
var displayedScreen, displayedPage;
function init() {
if (typeof cfg == 'undefined') {
cfg = parent.__debugtoolbarCfg;
}
if (typeof cfg == 'string') {
/* JSON configuration */
cfg = $.parseJSON(cfg);
}
$toolbarFrame = $('#debugtoolbar_toolbar_frame', parent.document);
$windowFrame = $('#debugtoolbar_window_frame', parent.document);
$toolbarFrame.show();
/* Toolbar container element */
$toolbar = $('#toolbar');
/* Window container element */
$window = $('#window', $windowFrame[0].contentDocument);
if (cfg['screens'])
for (var name in cfg['screens'])
if (cfg['screens'][name])
addScreen(name, cfg['screens'][name]);
var toolbar = cfg['toolbar'];
var $buttons = $([]);
/* Add a span element for the logo image */
if (toolbar['logo'])
$('<span class="logo" />').appendTo($toolbar);
if (toolbar['buttons']) {
$buttons = $('<div class="buttons" />').appendTo($toolbar);
for (var name in toolbar['buttons'])
if (toolbar['buttons'][name])
addButton(name, toolbar['buttons'][name]);
if (toolbar['buttons']['close'])
$('<span class="button close" />').appendTo($toolbar);
}
/* Standard actions */
/* Expand/collapse toolbar when the logo is clicked */
$('.logo').click(function () {
$buttons.toggle();
resizeToolbar();
if (displayedScreen) {
$windowFrame.fadeOut(300);
displayedScreen = false;
}
});
/* Alignment */
$('.button.align').click(function () {
if ($(this).hasClass('left')) {
$toolbarFrame.css({ right: '5px', left: 'auto' });
$(this).attr('title', 'Move the toolbar to the left');
}
else {
$toolbarFrame.css({ left: '5px', right: 'auto' });
$(this).attr('title', 'Move the toolbar to the right');
}
$(this).toggleClass('left right');
});
/* Close */
$('.button.close').click(function () {
if (displayedScreen) {
$windowFrame.fadeOut(300, function () {
$windowFrame.remove();
$toolbarFrame.remove();
});
displayedScreen = false;
}
else {
$windowFrame.remove();
$toolbarFrame.remove();
}
});
/* Buttons are initially hidden */
$buttons.hide();
resizeToolbar();
/* Adjust info window size when main browser window is resized */
$(parent).resize(resizeWindow);
}
function addButton(name, options) {
var $button;
switch (name) {
case 'align':
/* Right/left toolbar alignment button */
$button = $('<span class="button align" />')
.addClass('right');
break;
case 'close':
/* Close button gets special treatment -- see init() */
break;
default:
$button = $('<span class="button" />').
addClass(name);
$button.data('name', name);
if (options['text'])
$button.text(options['text']);
if (cfg['screens'][name])
$button.click(function () {
displayScreen($(this).data('name'));
});
break;
}
if ($button)
$button.appendTo($('.buttons', $toolbar));
}
function addScreen(name, options) {
var $screen;
$screen = $('<div class="screen" />')
.addClass(name);
if (options['title'])
$screen.append($('<h1>' + options['title'] + '</h1>'));
if (options['pages']) {
var $pages = $('<div class="pages scrolled" />').appendTo($screen);
var pageCount = 0;
for (var name in options['pages']) {
addPage($pages, name, options['pages'][name]);
pageCount++;
}
if (pageCount > 1) {
/* There's more than one page -- add a menu to switch pages */
$('<ul class="page-list" />').insertBefore($pages);
for (var name in options['pages'])
$('<li />').appendTo($('.page-list', $screen))
.data('page', name)
.addClass(name)
.text(options['pages'][name]['name'] || name)
.click(function () {
displayPage($(this).data('page'));
});
}
}
$screen.appendTo($window);
}
function addPage($parent, name, options) {
if (pageHandlers[options['type']]) {
var page = new pageHandlers[options['type']](name, options);
$parent.append(page.$page.hide());
}
}
function resizeWindow() {
$windowFrame.width($(parent).width() - 10 + 'px');
$windowFrame.height($(parent).height() * 0.7 + 'px');
$('.screen', $window).each(function () {
$(this).css('height', $windowFrame.height() - 10 + 'px');
$('.pages', $(this)).each(function () {
$(this).css('height', $(this).parent().height() -
$(this).position().top + 'px');
});
});
}
function displayScreen(screen) {
if ($windowFrame.is(':visible')) {
}
else {
/* Show the information window */
var top = $toolbarFrame.offset().top -
$(parent.document).scrollTop() +
$toolbarFrame.outerHeight() + 5;
$windowFrame.hide();
$windowFrame.css({ left: '5px', right: '5px',
top: top + 'px'}).fadeIn(300);
}
var $screen = $('.screen.' + screen, $window);
$('.screen:not(.' + screen + ')', $window).hide();
$screen.show();
displayedScreen = screen;
/* If none of the pages is displayed, display the first one */
if ($('.pages .page:visible', $screen).length == 0)
displayPage($('.pages .page', $screen).eq(0).data('name'));
windowDisplayed = true;
resizeWindow();
}
function displayPage(page, screen) {
if (!screen)
screen = displayedScreen;
var $screen = $('.screen.' + screen, $window);
$('.page', $screen).hide();
$('.page.' + page, $screen).show();
$('.page-list li', $screen).removeClass('active');
$('.page-list li.' + page, $screen).addClass('active');
}
function resizeToolbar() {
var frame = $('#debugtoolbar_toolbar_frame', parent.document)[0];
frame.width = $('#toolbar').width();
frame.height = $('#toolbar').height();
}
init(cfg);
}
var pageHandlers = {};
var Widget = Class.extend({
init: function () {
this.$elem = $('<div class="widget" />');
},
get: function () {
return this.$elem;
}
});
var TextWidget = Widget.extend({
init: function (content) {
this._super();
this.$elem.addClass('text');
this.$elem.text(content);
}
});
var DataStructureWidget = Widget.extend({
init: function (data) {
this._super();
this.$elem.addClass('data-structure');
this.$elem.append(this.htmlize(data));
$('li div.field', this.$elem).each(function () {
if ($(this).nextAll('div.sub').length > 0) {
$('<span class="expand"></span>').prependTo($(this))
.click(function () {
$(this).parent().nextAll('div.sub').toggle();
var expanded = $(this).parent().nextAll('div.sub')
.is(':visible');
$(this).parent().toggleClass('expanded', expanded);
});
}
else {
$('<span class="placeholder" />').prependTo($(this));
}
});
},
htmlizeValue: function (item) {
function htmlizeShort(value) {
if (typeof value['html'] != 'undefined')
return $(value['html']);
else
return $('<div class="value" />').text(value);
}
switch (item['type']) {
case 'list':
var $ret = $('<div class="sub list" />')
.hide()
.append(this.htmlize(item));
if (typeof item['short_value'] != 'undefined')
$ret = $ret.clone().before(htmlizeShort(item['short_value']));
return $ret;
case 'map':
var $ret = $('<div class="sub map" />')
.hide()
.append(this.htmlize(item));
if (item['short_value'])
$ret = $ret.clone().before(htmlizeShort(item['short_value']));
return $ret;
case 'number':
return $('<div class="value value-number" />')
.text(item['value']);
case 'string':
return $('<div class="value value-string" />')
.text(item['value']);
default:
return $('<div class="value" />').text(item['value']);
}
},
htmlize: function (data) {
var $ul = $('<ul />');
switch (data['type']) {
case 'list':
case 'map':
/*
* li
* div.field
* span.name foo
* [... value ...]
*/
var name;
for (name in data['value']) {
var $li = $('<li />').appendTo($ul);
$('<div class="field" />')
.append($('<span class="name" />').text(name))
.appendTo($li);
$li.append(this.htmlizeValue(data['value'][name]));
}
if (!name)
/* Empty list/map */
$('<li />').append($('<div class="value value-empty" />')
.text('empty')).appendTo($ul);
break;
}
return $ul;
}
});
/*
*
*/
var Page = Class.extend({
init: function (name, options) {
this.name = name;
this.options = options;
this.$page = $('<div class="page" />').addClass(name);
this.$page.data('name', name);
}
});
var TextPage = Page.extend({
init: function (name, options) {
this._super(name, options);
this.$page.append((new TextWidget(options['content'])).get());
}
});
/*
* Page to display complex data structures
*/
var DataStructurePage = Page.extend({
widgetClass: DataStructureWidget,
init: function (name, options) {
this._super(name, options);
this.$page.addClass('data-structure');
this.$page.append((new this.widgetClass(options['data'])).get());
},
});
/* Perl-specific */
/*
* Widget to display complex data structures (Perl flavor)
*/
var DataStructurePerlWidget = DataStructureWidget.extend({
init: function (data) {
this._super(data);
},
htmlizeValue: function (item) {
switch (item['type']) {
case 'perl/undefined':
return $('<div class="value value-undefined">undefined</div>');
case 'perl/cyclic-ref':
return $('<div class="value value-empty">cyclic reference</div>');
default:
return this._super(item);
}
}
});
var DataStructurePerlPage = DataStructurePage.extend({
widgetClass: DataStructurePerlWidget,
init: function (name, options) {
this._super(name, options);
}
});
var RoutesPage = Page.extend({
init: function (name, options) {
this._super(name, options);
this.render();
},
render: function () {
for (var type in this.options['routes']) {
this.$page.append($('<h2 />').text(type));
var routes = this.options['routes'][type];
for (var i = 0; i < routes.length; i++) {
var routeData = {
'type': 'map',
'value': { }
};
routeData.value[routes[i].pattern] = routes[i].data;
routeData.value[routes[i].pattern]['short_value'] = '';
var $widget = (new DataStructurePerlWidget(routeData))
.get();
$('> ul > li > .field', $widget)
.addClass('pattern wide');
if (routes[i].matching)
$('> ul > li > .field', $widget).addClass('matching');
this.$page.append($widget);
}
}
}
});
var TemplatesPage = Page.extend({
init: function (name, options) {
this._super(name, options);
this.render();
},
render: function () {
for (var i = 0; i < this.options['views'].length; i++) {
var view = this.options['views'][i];
this.$page.append($('<h2 />').text(view['template'])
.append($('<span class="engine" />').text(view['engine'])));
var $widget = (new DataStructurePerlWidget(view['tokens'])).get();
this.$page.append($widget);
}
}
});
var DatabaseQueriesPage = Page.extend({
init: function (name, options) {
this._super(name, options);
this.render();
},
render: function () {
var queries = this.options['queries'];
var $ul = $('<ul class="queries" />');
for (var i = 0; i < queries.length; i++) {
$ul.append($('<li />').append($('<pre />')
.append($(prettyPrintOne(queries[i].query, 'sql')))));
}
this.$page.append($ul);
}
});
/* --- */
pageHandlers['text'] = TextPage;
pageHandlers['data-structure'] = DataStructurePage;
/* Perl and/or Dancer-specific pages */
pageHandlers['data-structure/perl'] = DataStructurePerlPage;
pageHandlers['routes'] = RoutesPage;
pageHandlers['templates'] = TemplatesPage;
pageHandlers['database-queries'] = DatabaseQueriesPage;
/* --- */