Path: blob/trunk/third_party/closure/goog/debug/fancywindow.js
2868 views
// Copyright 2006 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Definition of the FancyWindow class. Please minimize16* dependencies this file has on other closure classes as any dependency it17* takes won't be able to use the logging infrastructure.18*19* This is a pretty hacky implementation, aimed at making debugging of large20* applications more manageable.21*22* @see ../demos/debug.html23*/242526goog.provide('goog.debug.FancyWindow');2728goog.require('goog.array');29goog.require('goog.asserts');30goog.require('goog.debug.DebugWindow');31goog.require('goog.debug.LogManager');32goog.require('goog.debug.Logger');33goog.require('goog.dom.DomHelper');34goog.require('goog.dom.TagName');35goog.require('goog.dom.safe');36goog.require('goog.html.SafeHtml');37goog.require('goog.html.SafeStyleSheet');38goog.require('goog.object');39goog.require('goog.string');40goog.require('goog.string.Const');41goog.require('goog.userAgent');42434445// TODO(mlourenco): Introduce goog.scope for goog.html.SafeHtml once b/1201441246// is fixed.47/**48* Provides a Fancy extension to the DebugWindow class. Allows filtering based49* on loggers and levels.50*51* @param {string=} opt_identifier Idenitifier for this logging class.52* @param {string=} opt_prefix Prefix pre-pended to messages.53* @constructor54* @extends {goog.debug.DebugWindow}55*/56goog.debug.FancyWindow = function(opt_identifier, opt_prefix) {57this.readOptionsFromLocalStorage_();58goog.debug.FancyWindow.base(this, 'constructor', opt_identifier, opt_prefix);59/** @private {goog.dom.DomHelper} */60this.dh_ = null;61};62goog.inherits(goog.debug.FancyWindow, goog.debug.DebugWindow);636465/**66* Constant indicating if we are able to use localStorage to persist filters67* @type {boolean}68*/69goog.debug.FancyWindow.HAS_LOCAL_STORE = (function() {7071try {72return !!window['localStorage'].getItem;73} catch (e) {74}75return false;76})();777879/**80* Constant defining the prefix to use when storing log levels81* @type {string}82*/83goog.debug.FancyWindow.LOCAL_STORE_PREFIX = 'fancywindow.sel.';848586/** @override */87goog.debug.FancyWindow.prototype.writeBufferToLog = function() {88this.lastCall = goog.now();89if (this.hasActiveWindow()) {90var logel = /** @type {!HTMLElement} */ (this.dh_.getElement('log'));9192// Work out if scrolling is needed before we add the content93var scroll =94logel.scrollHeight - (logel.scrollTop + logel.offsetHeight) <= 100;9596for (var i = 0; i < this.outputBuffer.length; i++) {97var div = this.dh_.createDom(goog.dom.TagName.DIV, 'logmsg');98goog.dom.safe.setInnerHtml(div, this.outputBuffer[i]);99logel.appendChild(div);100}101this.outputBuffer.length = 0;102this.resizeStuff_();103104if (scroll) {105logel.scrollTop = logel.scrollHeight;106}107}108};109110111/** @override */112goog.debug.FancyWindow.prototype.writeInitialDocument = function() {113if (!this.hasActiveWindow()) {114return;115}116117var doc = this.win.document;118doc.open();119goog.dom.safe.documentWrite(doc, this.getHtml_());120doc.close();121122(goog.userAgent.IE ? doc.body : this.win).onresize =123goog.bind(this.resizeStuff_, this);124125// Create a dom helper for the logging window126this.dh_ = new goog.dom.DomHelper(doc);127128// Don't use events system to reduce dependencies129this.dh_.getElement('openbutton').onclick =130goog.bind(this.openOptions_, this);131this.dh_.getElement('closebutton').onclick =132goog.bind(this.closeOptions_, this);133this.dh_.getElement('clearbutton').onclick = goog.bind(this.clear, this);134this.dh_.getElement('exitbutton').onclick = goog.bind(this.exit_, this);135136this.writeSavedMessages();137};138139140/**141* Show the options menu.142* @return {boolean} false.143* @private144*/145goog.debug.FancyWindow.prototype.openOptions_ = function() {146var el = goog.asserts.assert(this.dh_.getElement('optionsarea'));147goog.dom.safe.setInnerHtml(el, goog.html.SafeHtml.EMPTY);148149var loggers = goog.debug.FancyWindow.getLoggers_();150var dh = this.dh_;151for (var i = 0; i < loggers.length; i++) {152var logger = loggers[i];153var curlevel = logger.getLevel() ? logger.getLevel().name : 'INHERIT';154var div = dh.createDom(155goog.dom.TagName.DIV, {},156this.getDropDown_('sel' + logger.getName(), curlevel),157dh.createDom(goog.dom.TagName.SPAN, {}, logger.getName() || '(root)'));158el.appendChild(div);159}160161this.dh_.getElement('options').style.display = 'block';162return false;163};164165166/**167* Make a drop down for the log levels.168* @param {string} id Logger id.169* @param {string} selected What log level is currently selected.170* @return {Element} The newly created 'select' DOM element.171* @private172*/173goog.debug.FancyWindow.prototype.getDropDown_ = function(id, selected) {174var dh = this.dh_;175var sel = dh.createDom(goog.dom.TagName.SELECT, {'id': id});176var levels = goog.debug.Logger.Level.PREDEFINED_LEVELS;177for (var i = 0; i < levels.length; i++) {178var level = levels[i];179var option = dh.createDom(goog.dom.TagName.OPTION, {}, level.name);180if (selected == level.name) {181option.selected = true;182}183sel.appendChild(option);184}185sel.appendChild(186dh.createDom(187goog.dom.TagName.OPTION, {'selected': selected == 'INHERIT'},188'INHERIT'));189return sel;190};191192193/**194* Close the options menu.195* @return {boolean} The value false.196* @private197*/198goog.debug.FancyWindow.prototype.closeOptions_ = function() {199this.dh_.getElement('options').style.display = 'none';200var loggers = goog.debug.FancyWindow.getLoggers_();201var dh = this.dh_;202for (var i = 0; i < loggers.length; i++) {203var logger = loggers[i];204var sel = /** @type {!HTMLSelectElement} */ (205dh.getElement('sel' + logger.getName()));206var level = sel.options[sel.selectedIndex].text;207if (level == 'INHERIT') {208logger.setLevel(null);209} else {210logger.setLevel(goog.debug.Logger.Level.getPredefinedLevel(level));211}212}213this.writeOptionsToLocalStorage_();214return false;215};216217218/**219* Resizes the log elements220* @private221*/222goog.debug.FancyWindow.prototype.resizeStuff_ = function() {223var dh = this.dh_;224var logel = /** @type {!HTMLElement} */ (dh.getElement('log'));225var headel = /** @type {!HTMLElement} */ (dh.getElement('head'));226logel.style.top = headel.offsetHeight + 'px';227logel.style.height = (dh.getDocument().body.offsetHeight -228headel.offsetHeight - (goog.userAgent.IE ? 4 : 0)) +229'px';230};231232233/**234* Handles the user clicking the exit button, disabled the debug window and235* closes the popup.236* @param {Event} e Event object.237* @private238*/239goog.debug.FancyWindow.prototype.exit_ = function(e) {240this.setEnabled(false);241if (this.win) {242this.win.close();243}244};245246247/** @override */248goog.debug.FancyWindow.prototype.getStyleRules = function() {249var baseRules = goog.debug.FancyWindow.base(this, 'getStyleRules');250var extraRules = goog.html.SafeStyleSheet.fromConstant(251goog.string.Const.from(252'html,body{height:100%;width:100%;margin:0px;padding:0px;' +253'background-color:#FFF;overflow:hidden}' +254'*{}' +255'.logmsg{border-bottom:1px solid #CCC;padding:2px;font:90% monospace}' +256'#head{position:absolute;width:100%;font:x-small arial;' +257'border-bottom:2px solid #999;background-color:#EEE;}' +258'#head p{margin:0px 5px;}' +259'#log{position:absolute;width:100%;background-color:#FFF;}' +260'#options{position:absolute;right:0px;width:50%;height:100%;' +261'border-left:1px solid #999;background-color:#DDD;display:none;' +262'padding-left: 5px;font:normal small arial;overflow:auto;}' +263'#openbutton,#closebutton{text-decoration:underline;color:#00F;cursor:' +264'pointer;position:absolute;top:0px;right:5px;font:x-small arial;}' +265'#clearbutton{text-decoration:underline;color:#00F;cursor:' +266'pointer;position:absolute;top:0px;right:80px;font:x-small arial;}' +267'#exitbutton{text-decoration:underline;color:#00F;cursor:' +268'pointer;position:absolute;top:0px;right:50px;font:x-small arial;}' +269'select{font:x-small arial;margin-right:10px;}' +270'hr{border:0;height:5px;background-color:#8c8;color:#8c8;}'));271return goog.html.SafeStyleSheet.concat(baseRules, extraRules);272};273274275/**276* Return the default HTML for the debug window277* @return {!goog.html.SafeHtml} Html.278* @private279*/280goog.debug.FancyWindow.prototype.getHtml_ = function() {281var SafeHtml = goog.html.SafeHtml;282var head = SafeHtml.create(283'head', {},284SafeHtml.concat(285SafeHtml.create('title', {}, 'Logging: ' + this.identifier),286SafeHtml.createStyle(this.getStyleRules())));287288var body = SafeHtml.create(289'body', {},290SafeHtml.concat(291SafeHtml.create(292'div',293{'id': 'log', 'style': goog.string.Const.from('overflow:auto')}),294SafeHtml.create(295'div', {'id': 'head'},296SafeHtml.concat(297SafeHtml.create(298'p', {},299SafeHtml.create('b', {}, 'Logging: ' + this.identifier)),300SafeHtml.create('p', {}, this.welcomeMessage),301SafeHtml.create('span', {'id': 'clearbutton'}, 'clear'),302SafeHtml.create('span', {'id': 'exitbutton'}, 'exit'),303SafeHtml.create('span', {'id': 'openbutton'}, 'options'))),304SafeHtml.create(305'div', {'id': 'options'},306SafeHtml.concat(307SafeHtml.create(308'big', {}, SafeHtml.create('b', {}, 'Options:')),309SafeHtml.create('div', {'id': 'optionsarea'}),310SafeHtml.create(311'span', {'id': 'closebutton'}, 'save and close')))));312313return SafeHtml.create('html', {}, SafeHtml.concat(head, body));314};315316317/**318* Write logger levels to localStorage if possible.319* @private320*/321goog.debug.FancyWindow.prototype.writeOptionsToLocalStorage_ = function() {322if (!goog.debug.FancyWindow.HAS_LOCAL_STORE) {323return;324}325var loggers = goog.debug.FancyWindow.getLoggers_();326var storedKeys = goog.debug.FancyWindow.getStoredKeys_();327for (var i = 0; i < loggers.length; i++) {328var key = goog.debug.FancyWindow.LOCAL_STORE_PREFIX + loggers[i].getName();329var level = loggers[i].getLevel();330if (key in storedKeys) {331if (!level) {332window.localStorage.removeItem(key);333} else if (window.localStorage.getItem(key) != level.name) {334window.localStorage.setItem(key, level.name);335}336} else if (level) {337window.localStorage.setItem(key, level.name);338}339}340};341342343/**344* Sync logger levels with any values stored in localStorage.345* @private346*/347goog.debug.FancyWindow.prototype.readOptionsFromLocalStorage_ = function() {348if (!goog.debug.FancyWindow.HAS_LOCAL_STORE) {349return;350}351var storedKeys = goog.debug.FancyWindow.getStoredKeys_();352for (var key in storedKeys) {353var loggerName = key.replace(goog.debug.FancyWindow.LOCAL_STORE_PREFIX, '');354var logger = goog.debug.LogManager.getLogger(loggerName);355var curLevel = logger.getLevel();356var storedLevel = window.localStorage.getItem(key).toString();357if (!curLevel || curLevel.toString() != storedLevel) {358logger.setLevel(goog.debug.Logger.Level.getPredefinedLevel(storedLevel));359}360}361};362363364/**365* Helper function to create a list of locally stored keys. Used to avoid366* expensive localStorage.getItem() calls.367* @return {!Object} List of keys.368* @private369*/370goog.debug.FancyWindow.getStoredKeys_ = function() {371var storedKeys = {};372for (var i = 0, len = window.localStorage.length; i < len; i++) {373var key = window.localStorage.key(i);374if (key != null &&375goog.string.startsWith(376key, goog.debug.FancyWindow.LOCAL_STORE_PREFIX)) {377storedKeys[key] = true;378}379}380return storedKeys;381};382383384/**385* Gets a sorted array of all the loggers registered.386* @return {!Array<!goog.debug.Logger>} Array of logger instances.387* @private388*/389goog.debug.FancyWindow.getLoggers_ = function() {390var loggers = goog.object.getValues(goog.debug.LogManager.getLoggers());391392/**393* @param {!goog.debug.Logger} a394* @param {!goog.debug.Logger} b395* @return {number}396*/397var loggerSort = function(a, b) {398return goog.array.defaultCompare(a.getName(), b.getName());399};400goog.array.sort(loggers, loggerSort);401return loggers;402};403404405