| // Copyright 2006 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| /** |
| * @fileoverview Definition of the FancyWindow class. Please minimize |
| * dependencies this file has on other closure classes as any dependency it |
| * takes won't be able to use the logging infrastructure. |
| * |
| * This is a pretty hacky implementation, aimed at making debugging of large |
| * applications more manageable. |
| * |
| * @see ../demos/debug.html |
| */ |
| |
| |
| goog.provide('goog.debug.FancyWindow'); |
| |
| goog.require('goog.array'); |
| goog.require('goog.debug.DebugWindow'); |
| goog.require('goog.debug.LogManager'); |
| goog.require('goog.debug.Logger'); |
| goog.require('goog.dom.DomHelper'); |
| goog.require('goog.dom.safe'); |
| goog.require('goog.html.SafeHtml'); |
| goog.require('goog.html.SafeStyleSheet'); |
| goog.require('goog.object'); |
| goog.require('goog.string'); |
| goog.require('goog.string.Const'); |
| goog.require('goog.userAgent'); |
| |
| |
| |
| // TODO(user): Introduce goog.scope for goog.html.SafeHtml once b/12014412 |
| // is fixed. |
| /** |
| * Provides a Fancy extension to the DebugWindow class. Allows filtering based |
| * on loggers and levels. |
| * |
| * @param {string=} opt_identifier Idenitifier for this logging class. |
| * @param {string=} opt_prefix Prefix pre-pended to messages. |
| * @constructor |
| * @extends {goog.debug.DebugWindow} |
| */ |
| goog.debug.FancyWindow = function(opt_identifier, opt_prefix) { |
| this.readOptionsFromLocalStorage_(); |
| goog.debug.FancyWindow.base(this, 'constructor', opt_identifier, opt_prefix); |
| }; |
| goog.inherits(goog.debug.FancyWindow, goog.debug.DebugWindow); |
| |
| |
| /** |
| * Constant indicating if we are able to use localStorage to persist filters |
| * @type {boolean} |
| */ |
| goog.debug.FancyWindow.HAS_LOCAL_STORE = (function() { |
| /** @preserveTry */ |
| try { |
| return !!window['localStorage'].getItem; |
| } catch (e) {} |
| return false; |
| })(); |
| |
| |
| /** |
| * Constant defining the prefix to use when storing log levels |
| * @type {string} |
| */ |
| goog.debug.FancyWindow.LOCAL_STORE_PREFIX = 'fancywindow.sel.'; |
| |
| |
| /** @override */ |
| goog.debug.FancyWindow.prototype.writeBufferToLog = function() { |
| this.lastCall = goog.now(); |
| if (this.hasActiveWindow()) { |
| var logel = this.dh_.getElement('log'); |
| |
| // Work out if scrolling is needed before we add the content |
| var scroll = |
| logel.scrollHeight - (logel.scrollTop + logel.offsetHeight) <= 100; |
| |
| for (var i = 0; i < this.outputBuffer.length; i++) { |
| var div = this.dh_.createDom('div', 'logmsg'); |
| goog.dom.safe.setInnerHtml(div, this.outputBuffer[i]); |
| logel.appendChild(div); |
| } |
| this.outputBuffer.length = 0; |
| this.resizeStuff_(); |
| |
| if (scroll) { |
| logel.scrollTop = logel.scrollHeight; |
| } |
| } |
| }; |
| |
| |
| /** @override */ |
| goog.debug.FancyWindow.prototype.writeInitialDocument = function() { |
| if (!this.hasActiveWindow()) { |
| return; |
| } |
| |
| var doc = this.win.document; |
| doc.open(); |
| goog.dom.safe.documentWrite(doc, this.getHtml_()); |
| doc.close(); |
| |
| (goog.userAgent.IE ? doc.body : this.win).onresize = |
| goog.bind(this.resizeStuff_, this); |
| |
| // Create a dom helper for the logging window |
| this.dh_ = new goog.dom.DomHelper(doc); |
| |
| // Don't use events system to reduce dependencies |
| this.dh_.getElement('openbutton').onclick = |
| goog.bind(this.openOptions_, this); |
| this.dh_.getElement('closebutton').onclick = |
| goog.bind(this.closeOptions_, this); |
| this.dh_.getElement('clearbutton').onclick = |
| goog.bind(this.clear, this); |
| this.dh_.getElement('exitbutton').onclick = |
| goog.bind(this.exit_, this); |
| |
| this.writeSavedMessages(); |
| }; |
| |
| |
| /** |
| * Show the options menu. |
| * @return {boolean} false. |
| * @private |
| */ |
| goog.debug.FancyWindow.prototype.openOptions_ = function() { |
| var el = this.dh_.getElement('optionsarea'); |
| goog.dom.safe.setInnerHtml(el, goog.html.SafeHtml.EMPTY); |
| |
| var loggers = goog.debug.FancyWindow.getLoggers_(); |
| var dh = this.dh_; |
| for (var i = 0; i < loggers.length; i++) { |
| var logger = loggers[i]; |
| var curlevel = logger.getLevel() ? logger.getLevel().name : 'INHERIT'; |
| var div = dh.createDom('div', {}, |
| this.getDropDown_('sel' + logger.getName(), curlevel), |
| dh.createDom('span', {}, logger.getName() || '(root)')); |
| el.appendChild(div); |
| } |
| |
| this.dh_.getElement('options').style.display = 'block'; |
| return false; |
| }; |
| |
| |
| /** |
| * Make a drop down for the log levels. |
| * @param {string} id Logger id. |
| * @param {string} selected What log level is currently selected. |
| * @return {Element} The newly created 'select' DOM element. |
| * @private |
| */ |
| goog.debug.FancyWindow.prototype.getDropDown_ = function(id, selected) { |
| var dh = this.dh_; |
| var sel = dh.createDom('select', {'id': id}); |
| var levels = goog.debug.Logger.Level.PREDEFINED_LEVELS; |
| for (var i = 0; i < levels.length; i++) { |
| var level = levels[i]; |
| var option = dh.createDom('option', {}, level.name); |
| if (selected == level.name) { |
| option.selected = true; |
| } |
| sel.appendChild(option); |
| } |
| sel.appendChild(dh.createDom('option', |
| {'selected': selected == 'INHERIT'}, 'INHERIT')); |
| return sel; |
| }; |
| |
| |
| /** |
| * Close the options menu. |
| * @return {boolean} The value false. |
| * @private |
| */ |
| goog.debug.FancyWindow.prototype.closeOptions_ = function() { |
| this.dh_.getElement('options').style.display = 'none'; |
| var loggers = goog.debug.FancyWindow.getLoggers_(); |
| var dh = this.dh_; |
| for (var i = 0; i < loggers.length; i++) { |
| var logger = loggers[i]; |
| var sel = dh.getElement('sel' + logger.getName()); |
| var level = sel.options[sel.selectedIndex].text; |
| if (level == 'INHERIT') { |
| logger.setLevel(null); |
| } else { |
| logger.setLevel(goog.debug.Logger.Level.getPredefinedLevel(level)); |
| } |
| } |
| this.writeOptionsToLocalStorage_(); |
| return false; |
| }; |
| |
| |
| /** |
| * Resizes the log elements |
| * @private |
| */ |
| goog.debug.FancyWindow.prototype.resizeStuff_ = function() { |
| var dh = this.dh_; |
| var logel = dh.getElement('log'); |
| var headel = dh.getElement('head'); |
| logel.style.top = headel.offsetHeight + 'px'; |
| logel.style.height = (dh.getDocument().body.offsetHeight - |
| headel.offsetHeight - (goog.userAgent.IE ? 4 : 0)) + 'px'; |
| }; |
| |
| |
| /** |
| * Handles the user clicking the exit button, disabled the debug window and |
| * closes the popup. |
| * @param {Event} e Event object. |
| * @private |
| */ |
| goog.debug.FancyWindow.prototype.exit_ = function(e) { |
| this.setEnabled(false); |
| if (this.win) { |
| this.win.close(); |
| } |
| }; |
| |
| |
| /** @override */ |
| goog.debug.FancyWindow.prototype.getStyleRules = function() { |
| var baseRules = goog.debug.FancyWindow.base(this, 'getStyleRules'); |
| var extraRules = goog.html.SafeStyleSheet.fromConstant(goog.string.Const.from( |
| 'html,body{height:100%;width:100%;margin:0px;padding:0px;' + |
| 'background-color:#FFF;overflow:hidden}' + |
| '*{}' + |
| '.logmsg{border-bottom:1px solid #CCC;padding:2px;font:90% monospace}' + |
| '#head{position:absolute;width:100%;font:x-small arial;' + |
| 'border-bottom:2px solid #999;background-color:#EEE;}' + |
| '#head p{margin:0px 5px;}' + |
| '#log{position:absolute;width:100%;background-color:#FFF;}' + |
| '#options{position:absolute;right:0px;width:50%;height:100%;' + |
| 'border-left:1px solid #999;background-color:#DDD;display:none;' + |
| 'padding-left: 5px;font:normal small arial;overflow:auto;}' + |
| '#openbutton,#closebutton{text-decoration:underline;color:#00F;cursor:' + |
| 'pointer;position:absolute;top:0px;right:5px;font:x-small arial;}' + |
| '#clearbutton{text-decoration:underline;color:#00F;cursor:' + |
| 'pointer;position:absolute;top:0px;right:80px;font:x-small arial;}' + |
| '#exitbutton{text-decoration:underline;color:#00F;cursor:' + |
| 'pointer;position:absolute;top:0px;right:50px;font:x-small arial;}' + |
| 'select{font:x-small arial;margin-right:10px;}' + |
| 'hr{border:0;height:5px;background-color:#8c8;color:#8c8;}')); |
| return goog.html.SafeStyleSheet.concat(baseRules, extraRules); |
| }; |
| |
| |
| /** |
| * Return the default HTML for the debug window |
| * @return {!goog.html.SafeHtml} Html. |
| * @private |
| */ |
| goog.debug.FancyWindow.prototype.getHtml_ = function() { |
| var SafeHtml = goog.html.SafeHtml; |
| var head = SafeHtml.create('head', {}, SafeHtml.concat( |
| SafeHtml.create('title', {}, 'Logging: ' + this.identifier), |
| SafeHtml.createStyle(this.getStyleRules()))); |
| |
| var body = SafeHtml.create('body', {}, SafeHtml.concat( |
| SafeHtml.create('div', |
| {'id': 'log', 'style': goog.string.Const.from('overflow:auto')}), |
| SafeHtml.create('div', {'id': 'head'}, SafeHtml.concat( |
| SafeHtml.create('p', {}, |
| SafeHtml.create('b', {}, 'Logging: ' + this.identifier)), |
| SafeHtml.create('p', {}, this.welcomeMessage), |
| SafeHtml.create('span', {'id': 'clearbutton'}, 'clear'), |
| SafeHtml.create('span', {'id': 'exitbutton'}, 'exit'), |
| SafeHtml.create('span', {'id': 'openbutton'}, 'options'))), |
| SafeHtml.create('div', {'id': 'options'}, SafeHtml.concat( |
| SafeHtml.create('big', {}, |
| SafeHtml.create('b', {}, 'Options:')), |
| SafeHtml.create('div', {'id': 'optionsarea'}), |
| SafeHtml.create('span', {'id': 'closebutton'}, 'save and close'))))); |
| |
| return SafeHtml.create('html', {}, SafeHtml.concat(head, body)); |
| }; |
| |
| |
| /** |
| * Write logger levels to localStorage if possible. |
| * @private |
| */ |
| goog.debug.FancyWindow.prototype.writeOptionsToLocalStorage_ = function() { |
| if (!goog.debug.FancyWindow.HAS_LOCAL_STORE) { |
| return; |
| } |
| var loggers = goog.debug.FancyWindow.getLoggers_(); |
| var storedKeys = goog.debug.FancyWindow.getStoredKeys_(); |
| for (var i = 0; i < loggers.length; i++) { |
| var key = goog.debug.FancyWindow.LOCAL_STORE_PREFIX + loggers[i].getName(); |
| var level = loggers[i].getLevel(); |
| if (key in storedKeys) { |
| if (!level) { |
| window.localStorage.removeItem(key); |
| } else if (window.localStorage.getItem(key) != level.name) { |
| window.localStorage.setItem(key, level.name); |
| } |
| } else if (level) { |
| window.localStorage.setItem(key, level.name); |
| } |
| } |
| }; |
| |
| |
| /** |
| * Sync logger levels with any values stored in localStorage. |
| * @private |
| */ |
| goog.debug.FancyWindow.prototype.readOptionsFromLocalStorage_ = function() { |
| if (!goog.debug.FancyWindow.HAS_LOCAL_STORE) { |
| return; |
| } |
| var storedKeys = goog.debug.FancyWindow.getStoredKeys_(); |
| for (var key in storedKeys) { |
| var loggerName = key.replace(goog.debug.FancyWindow.LOCAL_STORE_PREFIX, ''); |
| var logger = goog.debug.LogManager.getLogger(loggerName); |
| var curLevel = logger.getLevel(); |
| var storedLevel = window.localStorage.getItem(key).toString(); |
| if (!curLevel || curLevel.toString() != storedLevel) { |
| logger.setLevel(goog.debug.Logger.Level.getPredefinedLevel(storedLevel)); |
| } |
| } |
| }; |
| |
| |
| /** |
| * Helper function to create a list of locally stored keys. Used to avoid |
| * expensive localStorage.getItem() calls. |
| * @return {!Object} List of keys. |
| * @private |
| */ |
| goog.debug.FancyWindow.getStoredKeys_ = function() { |
| var storedKeys = {}; |
| for (var i = 0, len = window.localStorage.length; i < len; i++) { |
| var key = window.localStorage.key(i); |
| if (key != null && goog.string.startsWith( |
| key, goog.debug.FancyWindow.LOCAL_STORE_PREFIX)) { |
| storedKeys[key] = true; |
| } |
| } |
| return storedKeys; |
| }; |
| |
| |
| /** |
| * Gets a sorted array of all the loggers registered. |
| * @return {!Array<!goog.debug.Logger>} Array of logger instances. |
| * @private |
| */ |
| goog.debug.FancyWindow.getLoggers_ = function() { |
| var loggers = goog.object.getValues(goog.debug.LogManager.getLoggers()); |
| |
| /** |
| * @param {!goog.debug.Logger} a |
| * @param {!goog.debug.Logger} b |
| * @return {number} |
| */ |
| var loggerSort = function(a, b) { |
| return goog.array.defaultCompare(a.getName(), b.getName()); |
| }; |
| goog.array.sort(loggers, loggerSort); |
| return loggers; |
| }; |