Update wmts-hidpi, add nicer-api-docs

This commit is contained in:
Andreas Hocevar
2014-05-06 13:02:46 -05:00
parent b3ac1afd00
commit 1e25fc5585
2239 changed files with 3726515 additions and 37010 deletions

View File

@@ -0,0 +1,207 @@
// 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 Simple logger that logs to the window console if available.
*
* Has an autoInstall option which can be put into initialization code, which
* will start logging if "Debug=true" is in document.location.href
*
*/
goog.provide('goog.debug.Console');
goog.require('goog.debug.LogManager');
goog.require('goog.debug.Logger.Level');
goog.require('goog.debug.TextFormatter');
/**
* Create and install a log handler that logs to window.console if available
* @constructor
*/
goog.debug.Console = function() {
this.publishHandler_ = goog.bind(this.addLogRecord, this);
/**
* Formatter for formatted output.
* @type {!goog.debug.TextFormatter}
* @private
*/
this.formatter_ = new goog.debug.TextFormatter();
this.formatter_.showAbsoluteTime = false;
this.formatter_.showExceptionText = false;
this.isCapturing_ = false;
this.logBuffer_ = '';
/**
* Loggers that we shouldn't output.
* @type {!Object.<boolean>}
* @private
*/
this.filteredLoggers_ = {};
};
/**
* Returns the text formatter used by this console
* @return {!goog.debug.TextFormatter} The text formatter.
*/
goog.debug.Console.prototype.getFormatter = function() {
return this.formatter_;
};
/**
* Sets whether we are currently capturing logger output.
* @param {boolean} capturing Whether to capture logger output.
*/
goog.debug.Console.prototype.setCapturing = function(capturing) {
if (capturing == this.isCapturing_) {
return;
}
// attach or detach handler from the root logger
var rootLogger = goog.debug.LogManager.getRoot();
if (capturing) {
rootLogger.addHandler(this.publishHandler_);
} else {
rootLogger.removeHandler(this.publishHandler_);
this.logBuffer = '';
}
this.isCapturing_ = capturing;
};
/**
* Adds a log record.
* @param {goog.debug.LogRecord} logRecord The log entry.
*/
goog.debug.Console.prototype.addLogRecord = function(logRecord) {
// Check to see if the log record is filtered or not.
if (this.filteredLoggers_[logRecord.getLoggerName()]) {
return;
}
var record = this.formatter_.formatRecord(logRecord);
var console = goog.debug.Console.console_;
if (console) {
switch (logRecord.getLevel()) {
case goog.debug.Logger.Level.SHOUT:
goog.debug.Console.logToConsole_(console, 'info', record);
break;
case goog.debug.Logger.Level.SEVERE:
goog.debug.Console.logToConsole_(console, 'error', record);
break;
case goog.debug.Logger.Level.WARNING:
goog.debug.Console.logToConsole_(console, 'warn', record);
break;
default:
goog.debug.Console.logToConsole_(console, 'debug', record);
break;
}
} else if (window.opera) {
// window.opera.postError is considered an undefined property reference
// by JSCompiler, so it has to be referenced using array notation instead.
window.opera['postError'](record);
} else {
this.logBuffer_ += record;
}
};
/**
* Adds a logger name to be filtered.
* @param {string} loggerName the logger name to add.
*/
goog.debug.Console.prototype.addFilter = function(loggerName) {
this.filteredLoggers_[loggerName] = true;
};
/**
* Removes a logger name to be filtered.
* @param {string} loggerName the logger name to remove.
*/
goog.debug.Console.prototype.removeFilter = function(loggerName) {
delete this.filteredLoggers_[loggerName];
};
/**
* Global console logger instance
* @type {goog.debug.Console}
*/
goog.debug.Console.instance = null;
/**
* The console to which to log. This is a property so it can be mocked out in
* this unit test for goog.debug.Console.
* @type {Object}
* @private
*/
goog.debug.Console.console_ = window.console;
/**
* Sets the console to which to log.
* @param {!Object} console The console to which to log.
*/
goog.debug.Console.setConsole = function(console) {
goog.debug.Console.console_ = console;
};
/**
* Install the console and start capturing if "Debug=true" is in the page URL
*/
goog.debug.Console.autoInstall = function() {
if (!goog.debug.Console.instance) {
goog.debug.Console.instance = new goog.debug.Console();
}
if (window.location.href.indexOf('Debug=true') != -1) {
goog.debug.Console.instance.setCapturing(true);
}
};
/**
* Show an alert with all of the captured debug information.
* Information is only captured if console is not available
*/
goog.debug.Console.show = function() {
alert(goog.debug.Console.instance.logBuffer_);
};
/**
* Logs the record to the console using the given function. If the function is
* not available on the console object, the log function is used instead.
* @param {!Object} console The console object.
* @param {string} fnName The name of the function to use.
* @param {string} record The record to log.
* @private
*/
goog.debug.Console.logToConsole_ = function(console, fnName, record) {
if (console[fnName]) {
console[fnName](record);
} else {
console.log(record);
}
};

View File

@@ -0,0 +1,501 @@
// 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 Logging and debugging utilities.
*
* @see ../demos/debug.html
*/
goog.provide('goog.debug');
goog.require('goog.array');
goog.require('goog.string');
goog.require('goog.structs.Set');
goog.require('goog.userAgent');
/** @define {boolean} Whether logging should be enabled. */
goog.define('goog.debug.LOGGING_ENABLED', goog.DEBUG);
/**
* Catches onerror events fired by windows and similar objects.
* @param {function(Object)} logFunc The function to call with the error
* information.
* @param {boolean=} opt_cancel Whether to stop the error from reaching the
* browser.
* @param {Object=} opt_target Object that fires onerror events.
*/
goog.debug.catchErrors = function(logFunc, opt_cancel, opt_target) {
var target = opt_target || goog.global;
var oldErrorHandler = target.onerror;
var retVal = !!opt_cancel;
// Chrome interprets onerror return value backwards (http://crbug.com/92062)
// until it was fixed in webkit revision r94061 (Webkit 535.3). This
// workaround still needs to be skipped in Safari after the webkit change
// gets pushed out in Safari.
// See https://bugs.webkit.org/show_bug.cgi?id=67119
if (goog.userAgent.WEBKIT &&
!goog.userAgent.isVersionOrHigher('535.3')) {
retVal = !retVal;
}
target.onerror = function(message, url, line) {
if (oldErrorHandler) {
oldErrorHandler(message, url, line);
}
logFunc({
message: message,
fileName: url,
line: line
});
return retVal;
};
};
/**
* Creates a string representing an object and all its properties.
* @param {Object|null|undefined} obj Object to expose.
* @param {boolean=} opt_showFn Show the functions as well as the properties,
* default is false.
* @return {string} The string representation of {@code obj}.
*/
goog.debug.expose = function(obj, opt_showFn) {
if (typeof obj == 'undefined') {
return 'undefined';
}
if (obj == null) {
return 'NULL';
}
var str = [];
for (var x in obj) {
if (!opt_showFn && goog.isFunction(obj[x])) {
continue;
}
var s = x + ' = ';
/** @preserveTry */
try {
s += obj[x];
} catch (e) {
s += '*** ' + e + ' ***';
}
str.push(s);
}
return str.join('\n');
};
/**
* Creates a string representing a given primitive or object, and for an
* object, all its properties and nested objects. WARNING: If an object is
* given, it and all its nested objects will be modified. To detect reference
* cycles, this method identifies objects using goog.getUid() which mutates the
* object.
* @param {*} obj Object to expose.
* @param {boolean=} opt_showFn Also show properties that are functions (by
* default, functions are omitted).
* @return {string} A string representation of {@code obj}.
*/
goog.debug.deepExpose = function(obj, opt_showFn) {
var previous = new goog.structs.Set();
var str = [];
var helper = function(obj, space) {
var nestspace = space + ' ';
var indentMultiline = function(str) {
return str.replace(/\n/g, '\n' + space);
};
/** @preserveTry */
try {
if (!goog.isDef(obj)) {
str.push('undefined');
} else if (goog.isNull(obj)) {
str.push('NULL');
} else if (goog.isString(obj)) {
str.push('"' + indentMultiline(obj) + '"');
} else if (goog.isFunction(obj)) {
str.push(indentMultiline(String(obj)));
} else if (goog.isObject(obj)) {
if (previous.contains(obj)) {
// TODO(user): This is a bug; it falsely detects non-loops as loops
// when the reference tree contains two references to the same object.
str.push('*** reference loop detected ***');
} else {
previous.add(obj);
str.push('{');
for (var x in obj) {
if (!opt_showFn && goog.isFunction(obj[x])) {
continue;
}
str.push('\n');
str.push(nestspace);
str.push(x + ' = ');
helper(obj[x], nestspace);
}
str.push('\n' + space + '}');
}
} else {
str.push(obj);
}
} catch (e) {
str.push('*** ' + e + ' ***');
}
};
helper(obj, '');
return str.join('');
};
/**
* Recursively outputs a nested array as a string.
* @param {Array} arr The array.
* @return {string} String representing nested array.
*/
goog.debug.exposeArray = function(arr) {
var str = [];
for (var i = 0; i < arr.length; i++) {
if (goog.isArray(arr[i])) {
str.push(goog.debug.exposeArray(arr[i]));
} else {
str.push(arr[i]);
}
}
return '[ ' + str.join(', ') + ' ]';
};
/**
* Exposes an exception that has been caught by a try...catch and outputs the
* error with a stack trace.
* @param {Object} err Error object or string.
* @param {Function=} opt_fn Optional function to start stack trace from.
* @return {string} Details of exception.
*/
goog.debug.exposeException = function(err, opt_fn) {
/** @preserveTry */
try {
var e = goog.debug.normalizeErrorObject(err);
// Create the error message
var error = 'Message: ' + goog.string.htmlEscape(e.message) +
'\nUrl: <a href="view-source:' + e.fileName + '" target="_new">' +
e.fileName + '</a>\nLine: ' + e.lineNumber + '\n\nBrowser stack:\n' +
goog.string.htmlEscape(e.stack + '-> ') +
'[end]\n\nJS stack traversal:\n' + goog.string.htmlEscape(
goog.debug.getStacktrace(opt_fn) + '-> ');
return error;
} catch (e2) {
return 'Exception trying to expose exception! You win, we lose. ' + e2;
}
};
/**
* Normalizes the error/exception object between browsers.
* @param {Object} err Raw error object.
* @return {Object} Normalized error object.
*/
goog.debug.normalizeErrorObject = function(err) {
var href = goog.getObjectByName('window.location.href');
if (goog.isString(err)) {
return {
'message': err,
'name': 'Unknown error',
'lineNumber': 'Not available',
'fileName': href,
'stack': 'Not available'
};
}
var lineNumber, fileName;
var threwError = false;
try {
lineNumber = err.lineNumber || err.line || 'Not available';
} catch (e) {
// Firefox 2 sometimes throws an error when accessing 'lineNumber':
// Message: Permission denied to get property UnnamedClass.lineNumber
lineNumber = 'Not available';
threwError = true;
}
try {
fileName = err.fileName || err.filename || err.sourceURL ||
// $googDebugFname may be set before a call to eval to set the filename
// that the eval is supposed to present.
goog.global['$googDebugFname'] || href;
} catch (e) {
// Firefox 2 may also throw an error when accessing 'filename'.
fileName = 'Not available';
threwError = true;
}
// The IE Error object contains only the name and the message.
// The Safari Error object uses the line and sourceURL fields.
if (threwError || !err.lineNumber || !err.fileName || !err.stack ||
!err.message || !err.name) {
return {
'message': err.message || 'Not available',
'name': err.name || 'UnknownError',
'lineNumber': lineNumber,
'fileName': fileName,
'stack': err.stack || 'Not available'
};
}
// Standards error object
return err;
};
/**
* Converts an object to an Error if it's a String,
* adds a stacktrace if there isn't one,
* and optionally adds an extra message.
* @param {Error|string} err the original thrown object or string.
* @param {string=} opt_message optional additional message to add to the
* error.
* @return {Error} If err is a string, it is used to create a new Error,
* which is enhanced and returned. Otherwise err itself is enhanced
* and returned.
*/
goog.debug.enhanceError = function(err, opt_message) {
var error = typeof err == 'string' ? Error(err) : err;
if (!error.stack) {
error.stack = goog.debug.getStacktrace(arguments.callee.caller);
}
if (opt_message) {
// find the first unoccupied 'messageX' property
var x = 0;
while (error['message' + x]) {
++x;
}
error['message' + x] = String(opt_message);
}
return error;
};
/**
* Gets the current stack trace. Simple and iterative - doesn't worry about
* catching circular references or getting the args.
* @param {number=} opt_depth Optional maximum depth to trace back to.
* @return {string} A string with the function names of all functions in the
* stack, separated by \n.
*/
goog.debug.getStacktraceSimple = function(opt_depth) {
var sb = [];
var fn = arguments.callee.caller;
var depth = 0;
while (fn && (!opt_depth || depth < opt_depth)) {
sb.push(goog.debug.getFunctionName(fn));
sb.push('()\n');
/** @preserveTry */
try {
fn = fn.caller;
} catch (e) {
sb.push('[exception trying to get caller]\n');
break;
}
depth++;
if (depth >= goog.debug.MAX_STACK_DEPTH) {
sb.push('[...long stack...]');
break;
}
}
if (opt_depth && depth >= opt_depth) {
sb.push('[...reached max depth limit...]');
} else {
sb.push('[end]');
}
return sb.join('');
};
/**
* Max length of stack to try and output
* @type {number}
*/
goog.debug.MAX_STACK_DEPTH = 50;
/**
* Gets the current stack trace, either starting from the caller or starting
* from a specified function that's currently on the call stack.
* @param {Function=} opt_fn Optional function to start getting the trace from.
* If not provided, defaults to the function that called this.
* @return {string} Stack trace.
*/
goog.debug.getStacktrace = function(opt_fn) {
return goog.debug.getStacktraceHelper_(opt_fn || arguments.callee.caller, []);
};
/**
* Private helper for getStacktrace().
* @param {Function} fn Function to start getting the trace from.
* @param {Array} visited List of functions visited so far.
* @return {string} Stack trace starting from function fn.
* @private
*/
goog.debug.getStacktraceHelper_ = function(fn, visited) {
var sb = [];
// Circular reference, certain functions like bind seem to cause a recursive
// loop so we need to catch circular references
if (goog.array.contains(visited, fn)) {
sb.push('[...circular reference...]');
// Traverse the call stack until function not found or max depth is reached
} else if (fn && visited.length < goog.debug.MAX_STACK_DEPTH) {
sb.push(goog.debug.getFunctionName(fn) + '(');
var args = fn.arguments;
for (var i = 0; i < args.length; i++) {
if (i > 0) {
sb.push(', ');
}
var argDesc;
var arg = args[i];
switch (typeof arg) {
case 'object':
argDesc = arg ? 'object' : 'null';
break;
case 'string':
argDesc = arg;
break;
case 'number':
argDesc = String(arg);
break;
case 'boolean':
argDesc = arg ? 'true' : 'false';
break;
case 'function':
argDesc = goog.debug.getFunctionName(arg);
argDesc = argDesc ? argDesc : '[fn]';
break;
case 'undefined':
default:
argDesc = typeof arg;
break;
}
if (argDesc.length > 40) {
argDesc = argDesc.substr(0, 40) + '...';
}
sb.push(argDesc);
}
visited.push(fn);
sb.push(')\n');
/** @preserveTry */
try {
sb.push(goog.debug.getStacktraceHelper_(fn.caller, visited));
} catch (e) {
sb.push('[exception trying to get caller]\n');
}
} else if (fn) {
sb.push('[...long stack...]');
} else {
sb.push('[end]');
}
return sb.join('');
};
/**
* Set a custom function name resolver.
* @param {function(Function): string} resolver Resolves functions to their
* names.
*/
goog.debug.setFunctionResolver = function(resolver) {
goog.debug.fnNameResolver_ = resolver;
};
/**
* Gets a function name
* @param {Function} fn Function to get name of.
* @return {string} Function's name.
*/
goog.debug.getFunctionName = function(fn) {
if (goog.debug.fnNameCache_[fn]) {
return goog.debug.fnNameCache_[fn];
}
if (goog.debug.fnNameResolver_) {
var name = goog.debug.fnNameResolver_(fn);
if (name) {
goog.debug.fnNameCache_[fn] = name;
return name;
}
}
// Heuristically determine function name based on code.
var functionSource = String(fn);
if (!goog.debug.fnNameCache_[functionSource]) {
var matches = /function ([^\(]+)/.exec(functionSource);
if (matches) {
var method = matches[1];
goog.debug.fnNameCache_[functionSource] = method;
} else {
goog.debug.fnNameCache_[functionSource] = '[Anonymous]';
}
}
return goog.debug.fnNameCache_[functionSource];
};
/**
* Makes whitespace visible by replacing it with printable characters.
* This is useful in finding diffrences between the expected and the actual
* output strings of a testcase.
* @param {string} string whose whitespace needs to be made visible.
* @return {string} string whose whitespace is made visible.
*/
goog.debug.makeWhitespaceVisible = function(string) {
return string.replace(/ /g, '[_]')
.replace(/\f/g, '[f]')
.replace(/\n/g, '[n]\n')
.replace(/\r/g, '[r]')
.replace(/\t/g, '[t]');
};
/**
* Hash map for storing function names that have already been looked up.
* @type {Object}
* @private
*/
goog.debug.fnNameCache_ = {};
/**
* Resolves functions to their names. Resolved function names will be cached.
* @type {function(Function):string}
* @private
*/
goog.debug.fnNameResolver_;

View File

@@ -0,0 +1,615 @@
// 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 DebugWindow 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.
*
*/
goog.provide('goog.debug.DebugWindow');
goog.require('goog.debug.HtmlFormatter');
goog.require('goog.debug.LogManager');
goog.require('goog.debug.Logger');
goog.require('goog.structs.CircularBuffer');
goog.require('goog.userAgent');
/**
* Provides a debug DebugWindow that is bound to the goog.debug.Logger.
* It handles log messages and writes them to the DebugWindow. This doesn't
* provide a lot of functionality that the old Gmail logging infrastructure
* provided like saving debug logs for exporting to the server. Now that we
* have an event-based logging infrastructure, we can encapsulate that
* functionality in a separate class.
*
* @constructor
* @param {string=} opt_identifier Identifier for this logging class.
* @param {string=} opt_prefix Prefix prepended to messages.
*/
goog.debug.DebugWindow = function(opt_identifier, opt_prefix) {
/**
* Identifier for this logging class
* @protected {string}
*/
this.identifier = opt_identifier || '';
/**
* Array used to buffer log output
* @protected {!Array}
*/
this.outputBuffer = [];
/**
* Optional prefix to be prepended to error strings
* @private {string}
*/
this.prefix_ = opt_prefix || '';
/**
* Buffer for saving the last 1000 messages
* @private {!goog.structs.CircularBuffer}
*/
this.savedMessages_ =
new goog.structs.CircularBuffer(goog.debug.DebugWindow.MAX_SAVED);
/**
* Save the publish handler so it can be removed
* @private {!Function}
*/
this.publishHandler_ = goog.bind(this.addLogRecord, this);
/**
* Formatter for formatted output
* @private {goog.debug.Formatter}
*/
this.formatter_ = new goog.debug.HtmlFormatter(this.prefix_);
/**
* Loggers that we shouldn't output
* @private {!Object}
*/
this.filteredLoggers_ = {};
// enable by default
this.setCapturing(true);
/**
* Whether we are currently enabled. When the DebugWindow is enabled, it tries
* to keep its window open. When it's disabled, it can still be capturing log
* output if, but it won't try to write them to the DebugWindow window until
* it's enabled.
* @private {boolean}
*/
this.enabled_ = goog.debug.DebugWindow.isEnabled(this.identifier);
// timer to save the DebugWindow's window position in a cookie
goog.global.setInterval(goog.bind(this.saveWindowPositionSize_, this), 7500);
};
/**
* Max number of messages to be saved
* @type {number}
*/
goog.debug.DebugWindow.MAX_SAVED = 500;
/**
* How long to keep the cookies for in milliseconds
* @type {number}
*/
goog.debug.DebugWindow.COOKIE_TIME = 30 * 24 * 60 * 60 * 1000; // 30-days
/**
* HTML string printed when the debug window opens
* @type {string}
* @protected
*/
goog.debug.DebugWindow.prototype.welcomeMessage = 'LOGGING';
/**
* Whether to force enable the window on a severe log.
* @type {boolean}
* @private
*/
goog.debug.DebugWindow.prototype.enableOnSevere_ = false;
/**
* Reference to debug window
* @type {Window}
* @protected
*/
goog.debug.DebugWindow.prototype.win = null;
/**
* In the process of opening the window
* @type {boolean}
* @private
*/
goog.debug.DebugWindow.prototype.winOpening_ = false;
/**
* Whether we are currently capturing logger output.
*
* @type {boolean}
* @private
*/
goog.debug.DebugWindow.prototype.isCapturing_ = false;
/**
* Whether we already showed an alert that the DebugWindow was blocked.
* @type {boolean}
* @private
*/
goog.debug.DebugWindow.showedBlockedAlert_ = false;
/**
* Reference to timeout used to buffer the output stream.
* @type {?number}
* @private
*/
goog.debug.DebugWindow.prototype.bufferTimeout_ = null;
/**
* Timestamp for the last time the log was written to.
* @protected {number}
*/
goog.debug.DebugWindow.prototype.lastCall = goog.now();
/**
* Sets the welcome message shown when the window is first opened or reset.
*
* @param {string} msg An HTML string.
*/
goog.debug.DebugWindow.prototype.setWelcomeMessage = function(msg) {
this.welcomeMessage = msg;
};
/**
* Initializes the debug window.
*/
goog.debug.DebugWindow.prototype.init = function() {
if (this.enabled_) {
this.openWindow_();
}
};
/**
* Whether the DebugWindow is enabled. When the DebugWindow is enabled, it
* tries to keep its window open and logs all messages to the window. When the
* DebugWindow is disabled, it stops logging messages to its window.
*
* @return {boolean} Whether the DebugWindow is enabled.
*/
goog.debug.DebugWindow.prototype.isEnabled = function() {
return this.enabled_;
};
/**
* Sets whether the DebugWindow is enabled. When the DebugWindow is enabled, it
* tries to keep its window open and log all messages to the window. When the
* DebugWindow is disabled, it stops logging messages to its window. The
* DebugWindow also saves this state to a cookie so that it's persisted across
* application refreshes.
* @param {boolean} enable Whether the DebugWindow is enabled.
*/
goog.debug.DebugWindow.prototype.setEnabled = function(enable) {
this.enabled_ = enable;
if (this.enabled_) {
this.openWindow_();
}
this.setCookie_('enabled', enable ? '1' : '0');
};
/**
* Sets whether the debug window should be force enabled when a severe log is
* encountered.
* @param {boolean} enableOnSevere Whether to enable on severe logs..
*/
goog.debug.DebugWindow.prototype.setForceEnableOnSevere =
function(enableOnSevere) {
this.enableOnSevere_ = enableOnSevere;
};
/**
* Whether we are currently capturing logger output.
* @return {boolean} whether we are currently capturing logger output.
*/
goog.debug.DebugWindow.prototype.isCapturing = function() {
return this.isCapturing_;
};
/**
* Sets whether we are currently capturing logger output.
* @param {boolean} capturing Whether to capture logger output.
*/
goog.debug.DebugWindow.prototype.setCapturing = function(capturing) {
if (capturing == this.isCapturing_) {
return;
}
this.isCapturing_ = capturing;
// attach or detach handler from the root logger
var rootLogger = goog.debug.LogManager.getRoot();
if (capturing) {
rootLogger.addHandler(this.publishHandler_);
} else {
rootLogger.removeHandler(this.publishHandler_);
}
};
/**
* Gets the formatter for outputting to the debug window. The default formatter
* is an instance of goog.debug.HtmlFormatter
* @return {goog.debug.Formatter} The formatter in use.
*/
goog.debug.DebugWindow.prototype.getFormatter = function() {
return this.formatter_;
};
/**
* Sets the formatter for outputting to the debug window.
* @param {goog.debug.Formatter} formatter The formatter to use.
*/
goog.debug.DebugWindow.prototype.setFormatter = function(formatter) {
this.formatter_ = formatter;
};
/**
* Adds a separator to the debug window.
*/
goog.debug.DebugWindow.prototype.addSeparator = function() {
this.write_('<hr>');
};
/**
* @return {boolean} Whether there is an active window.
*/
goog.debug.DebugWindow.prototype.hasActiveWindow = function() {
return !!this.win && !this.win.closed;
};
/**
* Clears the contents of the debug window
* @protected
*/
goog.debug.DebugWindow.prototype.clear = function() {
this.savedMessages_.clear();
if (this.hasActiveWindow()) {
this.writeInitialDocument();
}
};
/**
* Adds a log record.
* @param {goog.debug.LogRecord} logRecord the LogRecord.
*/
goog.debug.DebugWindow.prototype.addLogRecord = function(logRecord) {
if (this.filteredLoggers_[logRecord.getLoggerName()]) {
return;
}
var html = this.formatter_.formatRecord(logRecord);
this.write_(html);
if (this.enableOnSevere_ &&
logRecord.getLevel().value >= goog.debug.Logger.Level.SEVERE.value) {
this.setEnabled(true);
}
};
/**
* Writes a message to the log, possibly opening up the window if it's enabled,
* or saving it if it's disabled.
* @param {string} html The HTML to write.
* @private
*/
goog.debug.DebugWindow.prototype.write_ = function(html) {
// If the logger is enabled, open window and write html message to log
// otherwise save it
if (this.enabled_) {
this.openWindow_();
this.savedMessages_.add(html);
this.writeToLog_(html);
} else {
this.savedMessages_.add(html);
}
};
/**
* Write to the buffer. If a message hasn't been sent for more than 750ms just
* write, otherwise delay for a minimum of 250ms.
* @param {string} html HTML to post to the log.
* @private
*/
goog.debug.DebugWindow.prototype.writeToLog_ = function(html) {
this.outputBuffer.push(html);
goog.global.clearTimeout(this.bufferTimeout_);
if (goog.now() - this.lastCall > 750) {
this.writeBufferToLog();
} else {
this.bufferTimeout_ =
goog.global.setTimeout(goog.bind(this.writeBufferToLog, this), 250);
}
};
/**
* Write to the log and maybe scroll into view.
* @protected
*/
goog.debug.DebugWindow.prototype.writeBufferToLog = function() {
this.lastCall = goog.now();
if (this.hasActiveWindow()) {
var body = this.win.document.body;
var scroll = body &&
body.scrollHeight - (body.scrollTop + body.clientHeight) <= 100;
this.win.document.write(this.outputBuffer.join(''));
this.outputBuffer.length = 0;
if (scroll) {
this.win.scrollTo(0, 1000000);
}
}
};
/**
* Writes all saved messages to the DebugWindow.
* @protected
*/
goog.debug.DebugWindow.prototype.writeSavedMessages = function() {
var messages = this.savedMessages_.getValues();
for (var i = 0; i < messages.length; i++) {
this.writeToLog_(messages[i]);
}
};
/**
* Opens the debug window if it is not already referenced
* @private
*/
goog.debug.DebugWindow.prototype.openWindow_ = function() {
if (this.hasActiveWindow() || this.winOpening_) {
return;
}
var winpos = this.getCookie_('dbg', '0,0,800,500').split(',');
var x = Number(winpos[0]);
var y = Number(winpos[1]);
var w = Number(winpos[2]);
var h = Number(winpos[3]);
this.winOpening_ = true;
this.win = window.open('', this.getWindowName_(), 'width=' + w +
',height=' + h + ',toolbar=no,resizable=yes,' +
'scrollbars=yes,left=' + x + ',top=' + y +
',status=no,screenx=' + x + ',screeny=' + y);
if (!this.win) {
if (!this.showedBlockedAlert_) {
// only show this once
alert('Logger popup was blocked');
this.showedBlockedAlert_ = true;
}
}
this.winOpening_ = false;
if (this.win) {
this.writeInitialDocument();
}
};
/**
* Gets a valid window name for the debug window. Replaces invalid characters in
* IE.
* @return {string} Valid window name.
* @private
*/
goog.debug.DebugWindow.prototype.getWindowName_ = function() {
return goog.userAgent.IE ?
this.identifier.replace(/[\s\-\.\,]/g, '_') : this.identifier;
};
/**
* @return {string} The style rule text, for inclusion in the initial HTML.
*/
goog.debug.DebugWindow.prototype.getStyleRules = function() {
return '*{font:normal 14px monospace;}' +
'.dbg-sev{color:#F00}' +
'.dbg-w{color:#E92}' +
'.dbg-sh{background-color:#fd4;font-weight:bold;color:#000}' +
'.dbg-i{color:#666}' +
'.dbg-f{color:#999}' +
'.dbg-ev{color:#0A0}' +
'.dbg-m{color:#990}';
};
/**
* Writes the initial HTML of the debug window.
* @protected
*/
goog.debug.DebugWindow.prototype.writeInitialDocument = function() {
if (!this.hasActiveWindow()) {
return;
}
this.win.document.open();
var html = '<style>' + this.getStyleRules() + '</style>' +
'<hr><div class="dbg-ev" style="text-align:center">' +
this.welcomeMessage + '<br><small>Logger: ' +
this.identifier + '</small></div><hr>';
this.writeToLog_(html);
this.writeSavedMessages();
};
/**
* Save persistent data (using cookies) for 1 month (cookie specific to this
* logger object).
* @param {string} key Data name.
* @param {string} value Data value.
* @private
*/
goog.debug.DebugWindow.prototype.setCookie_ = function(key, value) {
var fullKey = goog.debug.DebugWindow.getCookieKey_(this.identifier, key);
document.cookie = fullKey + '=' + encodeURIComponent(value) +
';path=/;expires=' +
(new Date(goog.now() + goog.debug.DebugWindow.COOKIE_TIME)).toUTCString();
};
/**
* Retrieve data (using cookies).
* @param {string} key Data name.
* @param {string=} opt_default Optional default value if cookie doesn't exist.
* @return {string} Cookie value.
* @private
*/
goog.debug.DebugWindow.prototype.getCookie_ = function(key, opt_default) {
return goog.debug.DebugWindow.getCookieValue_(
this.identifier, key, opt_default);
};
/**
* Creates a valid cookie key name which is scoped to the given identifier.
* Substitutes all occurences of invalid cookie name characters (whitespace,
* ';', and '=') with '_', which is a valid and readable alternative.
* @see goog.net.Cookies#isValidName
* @see <a href="http://tools.ietf.org/html/rfc2109">RFC 2109</a>
* @param {string} identifier Identifier for logging class.
* @param {string} key Data name.
* @return {string} Cookie key name.
* @private
*/
goog.debug.DebugWindow.getCookieKey_ = function(identifier, key) {
var fullKey = key + identifier;
return fullKey.replace(/[;=\s]/g, '_');
};
/**
* Retrieve data (using cookies).
* @param {string} identifier Identifier for logging class.
* @param {string} key Data name.
* @param {string=} opt_default Optional default value if cookie doesn't exist.
* @return {string} Cookie value.
* @private
*/
goog.debug.DebugWindow.getCookieValue_ = function(
identifier, key, opt_default) {
var fullKey = goog.debug.DebugWindow.getCookieKey_(identifier, key);
var cookie = String(document.cookie);
var start = cookie.indexOf(fullKey + '=');
if (start != -1) {
var end = cookie.indexOf(';', start);
return decodeURIComponent(cookie.substring(start + fullKey.length + 1,
end == -1 ? cookie.length : end));
} else {
return opt_default || '';
}
};
/**
* @param {string} identifier Identifier for logging class.
* @return {boolean} Whether the DebugWindow is enabled.
*/
goog.debug.DebugWindow.isEnabled = function(identifier) {
return goog.debug.DebugWindow.getCookieValue_(identifier, 'enabled') == '1';
};
/**
* Saves the window position size to a cookie
* @private
*/
goog.debug.DebugWindow.prototype.saveWindowPositionSize_ = function() {
if (!this.hasActiveWindow()) {
return;
}
var x = this.win.screenX || this.win.screenLeft || 0;
var y = this.win.screenY || this.win.screenTop || 0;
var w = this.win.outerWidth || 800;
var h = this.win.outerHeight || 500;
this.setCookie_('dbg', x + ',' + y + ',' + w + ',' + h);
};
/**
* Adds a logger name to be filtered.
* @param {string} loggerName the logger name to add.
*/
goog.debug.DebugWindow.prototype.addFilter = function(loggerName) {
this.filteredLoggers_[loggerName] = 1;
};
/**
* Removes a logger name to be filtered.
* @param {string} loggerName the logger name to remove.
*/
goog.debug.DebugWindow.prototype.removeFilter = function(loggerName) {
delete this.filteredLoggers_[loggerName];
};
/**
* Modify the size of the circular buffer. Allows the log to retain more
* information while the window is closed.
* @param {number} size New size of the circular buffer.
*/
goog.debug.DebugWindow.prototype.resetBufferWithNewSize = function(size) {
if (size > 0 && size < 50000) {
this.clear();
this.savedMessages_ = new goog.structs.CircularBuffer(size);
}
};

View File

@@ -0,0 +1,444 @@
// Copyright 2008 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 Runtime development CSS Compiler emulation, via javascript.
* This class provides an approximation to CSSCompiler's functionality by
* hacking the live CSSOM.
* This code is designed to be inserted in the DOM immediately after the last
* style block in HEAD when in development mode, i.e. you are not using a
* running instance of a CSS Compiler to pass your CSS through.
*/
goog.provide('goog.debug.DevCss');
goog.provide('goog.debug.DevCss.UserAgent');
goog.require('goog.cssom');
goog.require('goog.dom.classes');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.string');
goog.require('goog.userAgent');
/**
* A class for solving development CSS issues/emulating the CSS Compiler.
* @param {goog.debug.DevCss.UserAgent=} opt_userAgent The user agent, if not
* passed in, will be determined using goog.userAgent.
* @param {number|string=} opt_userAgentVersion The user agent's version.
* If not passed in, will be determined using goog.userAgent.
* @throws {Error} When userAgent detection fails.
* @constructor
*/
goog.debug.DevCss = function(opt_userAgent, opt_userAgentVersion) {
if (!opt_userAgent) {
// Walks through the known goog.userAgents.
if (goog.userAgent.IE) {
opt_userAgent = goog.debug.DevCss.UserAgent.IE;
} else if (goog.userAgent.GECKO) {
opt_userAgent = goog.debug.DevCss.UserAgent.GECKO;
} else if (goog.userAgent.WEBKIT) {
opt_userAgent = goog.debug.DevCss.UserAgent.WEBKIT;
} else if (goog.userAgent.MOBILE) {
opt_userAgent = goog.debug.DevCss.UserAgent.MOBILE;
} else if (goog.userAgent.OPERA) {
opt_userAgent = goog.debug.DevCss.UserAgent.OPERA;
}
}
switch (opt_userAgent) {
case goog.debug.DevCss.UserAgent.OPERA:
case goog.debug.DevCss.UserAgent.IE:
case goog.debug.DevCss.UserAgent.GECKO:
case goog.debug.DevCss.UserAgent.FIREFOX:
case goog.debug.DevCss.UserAgent.WEBKIT:
case goog.debug.DevCss.UserAgent.SAFARI:
case goog.debug.DevCss.UserAgent.MOBILE:
break;
default:
throw Error('Could not determine the user agent from known UserAgents');
}
/**
* One of goog.debug.DevCss.UserAgent.
* @type {string}
* @private
*/
this.userAgent_ = opt_userAgent;
/**
* @type {number|string}
* @private
*/
this.userAgentVersion_ = opt_userAgentVersion || goog.userAgent.VERSION;
this.generateUserAgentTokens_();
/**
* @type {boolean}
* @private
*/
this.isIe6OrLess_ = this.userAgent_ == goog.debug.DevCss.UserAgent.IE &&
goog.string.compareVersions('7', this.userAgentVersion_) > 0;
if (this.isIe6OrLess_) {
/**
* @type {Array.<{classNames,combinedClassName,els}>}
* @private
*/
this.ie6CombinedMatches_ = [];
}
};
/**
* Rewrites the CSSOM as needed to activate any useragent-specific selectors.
* @param {boolean=} opt_enableIe6ReadyHandler If true(the default), and the
* userAgent is ie6, we set a document "ready" event handler to walk the DOM
* and make combined selector className changes. Having this parameter also
* aids unit testing.
*/
goog.debug.DevCss.prototype.activateBrowserSpecificCssRules = function(
opt_enableIe6ReadyHandler) {
var enableIe6EventHandler = goog.isDef(opt_enableIe6ReadyHandler) ?
opt_enableIe6ReadyHandler : true;
var cssRules = goog.cssom.getAllCssStyleRules();
for (var i = 0, cssRule; cssRule = cssRules[i]; i++) {
this.replaceBrowserSpecificClassNames_(cssRule);
}
// Since we may have manipulated the rules above, we'll have to do a
// complete sweep again if we're in IE6. Luckily performance doesn't
// matter for this tool.
if (this.isIe6OrLess_) {
cssRules = goog.cssom.getAllCssStyleRules();
for (var i = 0, cssRule; cssRule = cssRules[i]; i++) {
this.replaceIe6CombinedSelectors_(cssRule);
}
}
// Add an event listener for document ready to rewrite any necessary
// combined classnames in IE6.
if (this.isIe6OrLess_ && enableIe6EventHandler) {
goog.events.listen(document, goog.events.EventType.LOAD, goog.bind(
this.addIe6CombinedClassNames_, this));
}
};
/**
* @type {Object}
* @private
*/
goog.debug.DevCss.prototype.userAgentTokens_ = {};
/**
* A list of possible user agent strings.
* @enum {string}
*/
goog.debug.DevCss.UserAgent = {
OPERA: 'OPERA',
IE: 'IE',
GECKO: 'GECKO',
FIREFOX: 'GECKO',
WEBKIT: 'WEBKIT',
SAFARI: 'WEBKIT',
MOBILE: 'MOBILE'
};
/**
* A list of strings that may be used for matching in CSS files/development.
* @enum {string}
* @private
*/
goog.debug.DevCss.CssToken_ = {
USERAGENT: 'USERAGENT',
SEPARATOR: '-',
LESS_THAN: 'LT',
GREATER_THAN: 'GT',
LESS_THAN_OR_EQUAL: 'LTE',
GREATER_THAN_OR_EQUAL: 'GTE',
IE6_SELECTOR_TEXT: 'goog-ie6-selector',
IE6_COMBINED_GLUE: '_'
};
/**
* Generates user agent token match strings with comparison and version bits.
* For example:
* userAgentTokens_.ANY will be like 'GECKO'
* userAgentTokens_.LESS_THAN will be like 'GECKO-LT3' etc...
* @private
*/
goog.debug.DevCss.prototype.generateUserAgentTokens_ = function() {
this.userAgentTokens_.ANY = goog.debug.DevCss.CssToken_.USERAGENT +
goog.debug.DevCss.CssToken_.SEPARATOR + this.userAgent_;
this.userAgentTokens_.EQUALS = this.userAgentTokens_.ANY +
goog.debug.DevCss.CssToken_.SEPARATOR;
this.userAgentTokens_.LESS_THAN = this.userAgentTokens_.ANY +
goog.debug.DevCss.CssToken_.SEPARATOR +
goog.debug.DevCss.CssToken_.LESS_THAN;
this.userAgentTokens_.LESS_THAN_OR_EQUAL = this.userAgentTokens_.ANY +
goog.debug.DevCss.CssToken_.SEPARATOR +
goog.debug.DevCss.CssToken_.LESS_THAN_OR_EQUAL;
this.userAgentTokens_.GREATER_THAN = this.userAgentTokens_.ANY +
goog.debug.DevCss.CssToken_.SEPARATOR +
goog.debug.DevCss.CssToken_.GREATER_THAN;
this.userAgentTokens_.GREATER_THAN_OR_EQUAL = this.userAgentTokens_.ANY +
goog.debug.DevCss.CssToken_.SEPARATOR +
goog.debug.DevCss.CssToken_.GREATER_THAN_OR_EQUAL;
};
/**
* Gets the version number bit from a selector matching userAgentToken.
* @param {string} selectorText The selector text of a CSS rule.
* @param {string} userAgentToken Includes the LTE/GTE bit to see if it matches.
* @return {string|undefined} The version number.
* @private
*/
goog.debug.DevCss.prototype.getVersionNumberFromSelectorText_ = function(
selectorText, userAgentToken) {
var regex = new RegExp(userAgentToken + '([\\d\\.]+)');
var matches = regex.exec(selectorText);
if (matches && matches.length == 2) {
return matches[1];
}
};
/**
* Extracts a rule version from the selector text, and if it finds one, calls
* compareVersions against it and the passed in token string to provide the
* value needed to determine if we have a match or not.
* @param {CSSRule} cssRule The rule to test against.
* @param {string} token The match token to test against the rule.
* @return {Array|undefined} A tuple with the result of the compareVersions call
* and the matched ruleVersion.
* @private
*/
goog.debug.DevCss.prototype.getRuleVersionAndCompare_ = function(cssRule,
token) {
if (!cssRule.selectorText.match(token)) {
return;
}
var ruleVersion = this.getVersionNumberFromSelectorText_(
cssRule.selectorText, token);
if (!ruleVersion) {
return;
}
var comparison = goog.string.compareVersions(this.userAgentVersion_,
ruleVersion);
return [comparison, ruleVersion];
};
/**
* Replaces a CSS selector if we have matches based on our useragent/version.
* Example: With a selector like ".USERAGENT-IE-LTE6 .class { prop: value }" if
* we are running IE6 we'll end up with ".class { prop: value }", thereby
* "activating" the selector.
* @param {CSSRule} cssRule The cssRule to potentially replace.
* @private
*/
goog.debug.DevCss.prototype.replaceBrowserSpecificClassNames_ = function(
cssRule) {
// If we don't match the browser token, we can stop now.
if (!cssRule.selectorText.match(this.userAgentTokens_.ANY)) {
return;
}
// We know it will begin as a classname.
var additionalRegexString;
// Tests "Less than or equals".
var compared = this.getRuleVersionAndCompare_(cssRule,
this.userAgentTokens_.LESS_THAN_OR_EQUAL);
if (compared && compared.length) {
if (compared[0] > 0) {
return;
}
additionalRegexString = this.userAgentTokens_.LESS_THAN_OR_EQUAL +
compared[1];
}
// Tests "Less than".
compared = this.getRuleVersionAndCompare_(cssRule,
this.userAgentTokens_.LESS_THAN);
if (compared && compared.length) {
if (compared[0] > -1) {
return;
}
additionalRegexString = this.userAgentTokens_.LESS_THAN + compared[1];
}
// Tests "Greater than or equals".
compared = this.getRuleVersionAndCompare_(cssRule,
this.userAgentTokens_.GREATER_THAN_OR_EQUAL);
if (compared && compared.length) {
if (compared[0] < 0) {
return;
}
additionalRegexString = this.userAgentTokens_.GREATER_THAN_OR_EQUAL +
compared[1];
}
// Tests "Greater than".
compared = this.getRuleVersionAndCompare_(cssRule,
this.userAgentTokens_.GREATER_THAN);
if (compared && compared.length) {
if (compared[0] < 1) {
return;
}
additionalRegexString = this.userAgentTokens_.GREATER_THAN + compared[1];
}
// Tests "Equals".
compared = this.getRuleVersionAndCompare_(cssRule,
this.userAgentTokens_.EQUALS);
if (compared && compared.length) {
if (compared[0] != 0) {
return;
}
additionalRegexString = this.userAgentTokens_.EQUALS + compared[1];
}
// If we got to here without generating the additionalRegexString, then
// we did not match any of our comparison token strings, and we want a
// general browser token replacement.
if (!additionalRegexString) {
additionalRegexString = this.userAgentTokens_.ANY;
}
// We need to match at least a single whitespace character to know that
// we are matching the entire useragent string token.
var regexString = '\\.' + additionalRegexString + '\\s+';
var re = new RegExp(regexString, 'g');
var currentCssText = goog.cssom.getCssTextFromCssRule(cssRule);
// Replacing the token with '' activates the selector for this useragent.
var newCssText = currentCssText.replace(re, '');
if (newCssText != currentCssText) {
goog.cssom.replaceCssRule(cssRule, newCssText);
}
};
/**
* Replaces IE6 combined selector rules with a workable development alternative.
* IE6 actually parses .class1.class2 {} to simply .class2 {} which is nasty.
* To fully support combined selectors in IE6 this function needs to be paired
* with a call to replace the relevant DOM elements classNames as well.
* @see {this.addIe6CombinedClassNames_}
* @param {CSSRule} cssRule The rule to potentially fix.
* @private
*/
goog.debug.DevCss.prototype.replaceIe6CombinedSelectors_ = function(cssRule) {
// This match only ever works in IE because other UA's won't have our
// IE6_SELECTOR_TEXT in the cssText property.
if (cssRule.style.cssText &&
cssRule.style.cssText.match(
goog.debug.DevCss.CssToken_.IE6_SELECTOR_TEXT)) {
var cssText = goog.cssom.getCssTextFromCssRule(cssRule);
var combinedSelectorText = this.getIe6CombinedSelectorText_(cssText);
if (combinedSelectorText) {
var newCssText = combinedSelectorText + '{' + cssRule.style.cssText + '}';
goog.cssom.replaceCssRule(cssRule, newCssText);
}
}
};
/**
* Gets the appropriate new combined selector text for IE6.
* Also adds an entry onto ie6CombinedMatches_ with relevant info for the
* likely following call to walk the DOM and rewrite the class attribute.
* Example: With a selector like
* ".class2 { -goog-ie6-selector: .class1.class2; prop: value }".
* this function will return:
* ".class1_class2 { prop: value }".
* @param {string} cssText The CSS selector text and css rule text combined.
* @return {?string} The rewritten css rule text.
* @private
*/
goog.debug.DevCss.prototype.getIe6CombinedSelectorText_ = function(cssText) {
var regex = new RegExp(goog.debug.DevCss.CssToken_.IE6_SELECTOR_TEXT +
'\\s*:\\s*\\"([^\\"]+)\\"', 'gi');
var matches = regex.exec(cssText);
if (matches) {
var combinedSelectorText = matches[1];
// To aid in later fixing the DOM, we need to split up the possible
// selector groups by commas.
var groupedSelectors = combinedSelectorText.split(/\s*\,\s*/);
for (var i = 0, selector; selector = groupedSelectors[i]; i++) {
// Strips off the leading ".".
var combinedClassName = selector.substr(1);
var classNames = combinedClassName.split(
goog.debug.DevCss.CssToken_.IE6_COMBINED_GLUE);
var entry = {
classNames: classNames,
combinedClassName: combinedClassName,
els: []
};
this.ie6CombinedMatches_.push(entry);
}
return combinedSelectorText;
}
return null;
};
/**
* Adds combined selectors with underscores to make them "work" in IE6.
* @see {this.replaceIe6CombinedSelectors_}
* @private
*/
goog.debug.DevCss.prototype.addIe6CombinedClassNames_ = function() {
if (!this.ie6CombinedMatches_.length) {
return;
}
var allEls = document.getElementsByTagName('*');
var matches = [];
// Match nodes for all classNames.
for (var i = 0, classNameEntry; classNameEntry =
this.ie6CombinedMatches_[i]; i++) {
for (var j = 0, el; el = allEls[j]; j++) {
var classNamesLength = classNameEntry.classNames.length;
for (var k = 0, className; className = classNameEntry.classNames[k];
k++) {
if (!goog.dom.classes.has(el, className)) {
break;
}
if (k == classNamesLength - 1) {
classNameEntry.els.push(el);
}
}
}
// Walks over our matching nodes and fixes them.
if (classNameEntry.els.length) {
for (var j = 0, el; el = classNameEntry.els[j]; j++) {
if (!goog.dom.classes.has(el, classNameEntry.combinedClassName)) {
goog.dom.classes.add(el, classNameEntry.combinedClassName);
}
}
}
}
};

View File

@@ -0,0 +1,26 @@
// Copyright 2008 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 Development CSS Compiler runtime execution.
*/
goog.provide('goog.debug.devCssRunner');
goog.require('goog.debug.DevCss');
(function() {
var devCssInstance = new goog.debug.DevCss();
devCssInstance.activateBrowserSpecificCssRules();
})();

View File

@@ -0,0 +1,141 @@
// 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 Simple logger that logs a Div Element.
*
*/
goog.provide('goog.debug.DivConsole');
goog.require('goog.debug.HtmlFormatter');
goog.require('goog.debug.LogManager');
goog.require('goog.style');
/**
* A class for visualising logger calls in a div element.
* @param {Element} element The element to append to.
* @constructor
*/
goog.debug.DivConsole = function(element) {
this.publishHandler_ = goog.bind(this.addLogRecord, this);
this.formatter_ = new goog.debug.HtmlFormatter();
this.formatter_.showAbsoluteTime = false;
this.isCapturing_ = false;
this.element_ = element;
this.elementOwnerDocument_ =
this.element_.ownerDocument || this.element_.document;
this.installStyles();
};
/**
* Installs styles for the log messages and its div
*/
goog.debug.DivConsole.prototype.installStyles = function() {
goog.style.installStyles(
'.dbg-sev{color:#F00}' +
'.dbg-w{color:#C40}' +
'.dbg-sh{font-weight:bold;color:#000}' +
'.dbg-i{color:#444}' +
'.dbg-f{color:#999}' +
'.dbg-ev{color:#0A0}' +
'.dbg-m{color:#990}' +
'.logmsg{border-bottom:1px solid #CCC;padding:2px}' +
'.logsep{background-color: #8C8;}' +
'.logdiv{border:1px solid #CCC;background-color:#FCFCFC;' +
'font:medium monospace}',
this.element_);
this.element_.className += ' logdiv';
};
/**
* Sets whether we are currently capturing logger output.
* @param {boolean} capturing Whether to capture logger output.
*/
goog.debug.DivConsole.prototype.setCapturing = function(capturing) {
if (capturing == this.isCapturing_) {
return;
}
// attach or detach handler from the root logger
var rootLogger = goog.debug.LogManager.getRoot();
if (capturing) {
rootLogger.addHandler(this.publishHandler_);
} else {
rootLogger.removeHandler(this.publishHandler_);
this.logBuffer = '';
}
this.isCapturing_ = capturing;
};
/**
* Adds a log record.
* @param {goog.debug.LogRecord} logRecord The log entry.
*/
goog.debug.DivConsole.prototype.addLogRecord = function(logRecord) {
var scroll = this.element_.scrollHeight - this.element_.scrollTop -
this.element_.clientHeight <= 100;
var div = this.elementOwnerDocument_.createElement('div');
div.className = 'logmsg';
div.innerHTML = this.formatter_.formatRecord(logRecord);
this.element_.appendChild(div);
if (scroll) {
this.element_.scrollTop = this.element_.scrollHeight;
}
};
/**
* Gets the formatter for outputting to the console. The default formatter
* is an instance of goog.debug.HtmlFormatter
* @return {goog.debug.Formatter} The formatter in use.
*/
goog.debug.DivConsole.prototype.getFormatter = function() {
return this.formatter_;
};
/**
* Sets the formatter for outputting to the console.
* @param {goog.debug.Formatter} formatter The formatter to use.
*/
goog.debug.DivConsole.prototype.setFormatter = function(formatter) {
this.formatter_ = formatter;
};
/**
* Adds a separator to the debug window.
*/
goog.debug.DivConsole.prototype.addSeparator = function() {
var div = this.elementOwnerDocument_.createElement('div');
div.className = 'logmsg logsep';
this.element_.appendChild(div);
};
/**
* Clears the console.
*/
goog.debug.DivConsole.prototype.clear = function() {
this.element_.innerHTML = '';
};

View File

@@ -0,0 +1,158 @@
// Copyright 2010 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 A global registry for entry points into a program,
* so that they can be instrumented. Each module should register their
* entry points with this registry. Designed to be compiled out
* if no instrumentation is requested.
*
* Entry points may be registered before or after a call to
* goog.debug.entryPointRegistry.monitorAll. If an entry point is registered
* later, the existing monitor will instrument the new entry point.
*
* @author nicksantos@google.com (Nick Santos)
*/
goog.provide('goog.debug.EntryPointMonitor');
goog.provide('goog.debug.entryPointRegistry');
goog.require('goog.asserts');
/**
* @interface
*/
goog.debug.EntryPointMonitor = function() {};
/**
* Instruments a function.
*
* @param {!Function} fn A function to instrument.
* @return {!Function} The instrumented function.
*/
goog.debug.EntryPointMonitor.prototype.wrap;
/**
* Try to remove an instrumentation wrapper created by this monitor.
* If the function passed to unwrap is not a wrapper created by this
* monitor, then we will do nothing.
*
* Notice that some wrappers may not be unwrappable. For example, if other
* monitors have applied their own wrappers, then it will be impossible to
* unwrap them because their wrappers will have captured our wrapper.
*
* So it is important that entry points are unwrapped in the reverse
* order that they were wrapped.
*
* @param {!Function} fn A function to unwrap.
* @return {!Function} The unwrapped function, or {@code fn} if it was not
* a wrapped function created by this monitor.
*/
goog.debug.EntryPointMonitor.prototype.unwrap;
/**
* An array of entry point callbacks.
* @type {!Array.<function(!Function)>}
* @private
*/
goog.debug.entryPointRegistry.refList_ = [];
/**
* Monitors that should wrap all the entry points.
* @type {!Array.<!goog.debug.EntryPointMonitor>}
* @private
*/
goog.debug.entryPointRegistry.monitors_ = [];
/**
* Whether goog.debug.entryPointRegistry.monitorAll has ever been called.
* Checking this allows the compiler to optimize out the registrations.
* @type {boolean}
* @private
*/
goog.debug.entryPointRegistry.monitorsMayExist_ = false;
/**
* Register an entry point with this module.
*
* The entry point will be instrumented when a monitor is passed to
* goog.debug.entryPointRegistry.monitorAll. If this has already occurred, the
* entry point is instrumented immediately.
*
* @param {function(!Function)} callback A callback function which is called
* with a transforming function to instrument the entry point. The callback
* is responsible for wrapping the relevant entry point with the
* transforming function.
*/
goog.debug.entryPointRegistry.register = function(callback) {
// Don't use push(), so that this can be compiled out.
goog.debug.entryPointRegistry.refList_[
goog.debug.entryPointRegistry.refList_.length] = callback;
// If no one calls monitorAll, this can be compiled out.
if (goog.debug.entryPointRegistry.monitorsMayExist_) {
var monitors = goog.debug.entryPointRegistry.monitors_;
for (var i = 0; i < monitors.length; i++) {
callback(goog.bind(monitors[i].wrap, monitors[i]));
}
}
};
/**
* Configures a monitor to wrap all entry points.
*
* Entry points that have already been registered are immediately wrapped by
* the monitor. When an entry point is registered in the future, it will also
* be wrapped by the monitor when it is registered.
*
* @param {!goog.debug.EntryPointMonitor} monitor An entry point monitor.
*/
goog.debug.entryPointRegistry.monitorAll = function(monitor) {
goog.debug.entryPointRegistry.monitorsMayExist_ = true;
var transformer = goog.bind(monitor.wrap, monitor);
for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
goog.debug.entryPointRegistry.refList_[i](transformer);
}
goog.debug.entryPointRegistry.monitors_.push(monitor);
};
/**
* Try to unmonitor all the entry points that have already been registered. If
* an entry point is registered in the future, it will not be wrapped by the
* monitor when it is registered. Note that this may fail if the entry points
* have additional wrapping.
*
* @param {!goog.debug.EntryPointMonitor} monitor The last monitor to wrap
* the entry points.
* @throws {Error} If the monitor is not the most recently configured monitor.
*/
goog.debug.entryPointRegistry.unmonitorAllIfPossible = function(monitor) {
var monitors = goog.debug.entryPointRegistry.monitors_;
goog.asserts.assert(monitor == monitors[monitors.length - 1],
'Only the most recent monitor can be unwrapped.');
var transformer = goog.bind(monitor.unwrap, monitor);
for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
goog.debug.entryPointRegistry.refList_[i](transformer);
}
monitors.length--;
};

View File

@@ -0,0 +1,51 @@
// Copyright 2009 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 Provides a base class for custom Error objects such that the
* stack is correctly maintained.
*
* You should never need to throw goog.debug.Error(msg) directly, Error(msg) is
* sufficient.
*
*/
goog.provide('goog.debug.Error');
/**
* Base class for custom error objects.
* @param {*=} opt_msg The message associated with the error.
* @constructor
* @extends {Error}
*/
goog.debug.Error = function(opt_msg) {
// Ensure there is a stack trace.
if (Error.captureStackTrace) {
Error.captureStackTrace(this, goog.debug.Error);
} else {
this.stack = new Error().stack || '';
}
if (opt_msg) {
this.message = String(opt_msg);
}
};
goog.inherits(goog.debug.Error, Error);
/** @override */
goog.debug.Error.prototype.name = 'CustomError';

View File

@@ -0,0 +1,363 @@
// Copyright 2007 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 Error handling utilities.
*
*/
goog.provide('goog.debug.ErrorHandler');
goog.provide('goog.debug.ErrorHandler.ProtectedFunctionError');
goog.require('goog.asserts');
goog.require('goog.debug');
goog.require('goog.debug.EntryPointMonitor');
goog.require('goog.debug.Trace');
/**
* The ErrorHandler can be used to to wrap functions with a try/catch
* statement. If an exception is thrown, the given error handler function will
* be called.
*
* When this object is disposed, it will stop handling exceptions and tracing.
* It will also try to restore window.setTimeout and window.setInterval
* if it wrapped them. Notice that in the general case, it is not technically
* possible to remove the wrapper, because functions have no knowledge of
* what they have been assigned to. So the app is responsible for other
* forms of unwrapping.
*
* @param {Function} handler Handler for exceptions.
* @constructor
* @extends {goog.Disposable}
* @implements {goog.debug.EntryPointMonitor}
*/
goog.debug.ErrorHandler = function(handler) {
goog.base(this);
/**
* Handler for exceptions, which can do logging, reporting, etc.
* @type {Function}
* @private
*/
this.errorHandlerFn_ = handler;
/**
* Whether errors should be wrapped in
* goog.debug.ErrorHandler.ProtectedFunctionError before rethrowing.
* @type {boolean}
* @private
*/
this.wrapErrors_ = true; // TODO(user) Change default.
/**
* Whether to add a prefix to all error messages. The prefix is
* goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX. This option
* only has an effect if this.wrapErrors_ is set to false.
* @type {boolean}
* @private
*/
this.prefixErrorMessages_ = false;
};
goog.inherits(goog.debug.ErrorHandler, goog.Disposable);
/**
* Whether to add tracers when instrumenting entry points.
* @type {boolean}
* @private
*/
goog.debug.ErrorHandler.prototype.addTracersToProtectedFunctions_ = false;
/**
* Enable tracers when instrumenting entry points.
* @param {boolean} newVal See above.
*/
goog.debug.ErrorHandler.prototype.setAddTracersToProtectedFunctions =
function(newVal) {
this.addTracersToProtectedFunctions_ = newVal;
};
/** @override */
goog.debug.ErrorHandler.prototype.wrap = function(fn) {
return this.protectEntryPoint(goog.asserts.assertFunction(fn));
};
/** @override */
goog.debug.ErrorHandler.prototype.unwrap = function(fn) {
goog.asserts.assertFunction(fn);
return fn[this.getFunctionIndex_(false)] || fn;
};
/**
* Private helper function to return a span that can be clicked on to display
* an alert with the current stack trace. Newlines are replaced with a
* placeholder so that they will not be html-escaped.
* @param {string} stackTrace The stack trace to create a span for.
* @return {string} A span which can be clicked on to show the stack trace.
* @private
*/
goog.debug.ErrorHandler.prototype.getStackTraceHolder_ = function(stackTrace) {
var buffer = [];
buffer.push('##PE_STACK_START##');
buffer.push(stackTrace.replace(/(\r\n|\r|\n)/g, '##STACK_BR##'));
buffer.push('##PE_STACK_END##');
return buffer.join('');
};
/**
* Get the index for a function. Used for internal indexing.
* @param {boolean} wrapper True for the wrapper; false for the wrapped.
* @return {string} The index where we should store the function in its
* wrapper/wrapped function.
* @private
*/
goog.debug.ErrorHandler.prototype.getFunctionIndex_ = function(wrapper) {
return (wrapper ? '__wrapper_' : '__protected_') + goog.getUid(this) + '__';
};
/**
* Installs exception protection for an entry point function. When an exception
* is thrown from a protected function, a handler will be invoked to handle it.
*
* @param {Function} fn An entry point function to be protected.
* @return {!Function} A protected wrapper function that calls the entry point
* function.
*/
goog.debug.ErrorHandler.prototype.protectEntryPoint = function(fn) {
var protectedFnName = this.getFunctionIndex_(true);
if (!fn[protectedFnName]) {
var wrapper = fn[protectedFnName] = this.getProtectedFunction(fn);
wrapper[this.getFunctionIndex_(false)] = fn;
}
return fn[protectedFnName];
};
/**
* Helps {@link #protectEntryPoint} by actually creating the protected
* wrapper function, after {@link #protectEntryPoint} determines that one does
* not already exist for the given function. Can be overriden by subclasses
* that may want to implement different error handling, or add additional
* entry point hooks.
* @param {!Function} fn An entry point function to be protected.
* @return {!Function} protected wrapper function.
* @protected
*/
goog.debug.ErrorHandler.prototype.getProtectedFunction = function(fn) {
var that = this;
var tracers = this.addTracersToProtectedFunctions_;
if (tracers) {
var stackTrace = goog.debug.getStacktraceSimple(15);
}
var googDebugErrorHandlerProtectedFunction = function() {
if (that.isDisposed()) {
return fn.apply(this, arguments);
}
if (tracers) {
var tracer = goog.debug.Trace.startTracer('protectedEntryPoint: ' +
that.getStackTraceHolder_(stackTrace));
}
try {
return fn.apply(this, arguments);
} catch (e) {
that.errorHandlerFn_(e);
if (!that.wrapErrors_) {
// Add the prefix to the existing message.
if (that.prefixErrorMessages_) {
if (typeof e === 'object') {
e.message =
goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX +
e.message;
} else {
e = goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX +
e;
}
}
if (goog.DEBUG) {
// Work around for https://code.google.com/p/v8/issues/detail?id=2625
// and https://code.google.com/p/chromium/issues/detail?id=237059
// Custom errors and errors with custom stack traces show the wrong
// stack trace
// If it has a stack and Error.captureStackTrace is supported (only
// supported in V8 as of May 2013) log the stack to the console.
if (e && e.stack && Error.captureStackTrace &&
goog.global['console']) {
goog.global['console']['error'](e.message, e.stack);
}
}
// Re-throw original error. This is great for debugging as it makes
// browser JS dev consoles show the correct error and stack trace.
throw e;
}
// Re-throw it since this may be expected by the caller.
throw new goog.debug.ErrorHandler.ProtectedFunctionError(e);
} finally {
if (tracers) {
goog.debug.Trace.stopTracer(tracer);
}
}
};
googDebugErrorHandlerProtectedFunction[this.getFunctionIndex_(false)] = fn;
return googDebugErrorHandlerProtectedFunction;
};
// TODO(user): Allow these functions to take in the window to protect.
/**
* Installs exception protection for window.setTimeout to handle exceptions.
*/
goog.debug.ErrorHandler.prototype.protectWindowSetTimeout =
function() {
this.protectWindowFunctionsHelper_('setTimeout');
};
/**
* Install exception protection for window.setInterval to handle exceptions.
*/
goog.debug.ErrorHandler.prototype.protectWindowSetInterval =
function() {
this.protectWindowFunctionsHelper_('setInterval');
};
/**
* Install exception protection for window.requestAnimationFrame to handle
* exceptions.
*/
goog.debug.ErrorHandler.prototype.protectWindowRequestAnimationFrame =
function() {
var win = goog.getObjectByName('window');
var fnNames = [
'requestAnimationFrame',
'mozRequestAnimationFrame',
'webkitAnimationFrame',
'msRequestAnimationFrame'
];
for (var i = 0; i < fnNames.length; i++) {
var fnName = fnNames[i];
if (fnNames[i] in win) {
win[fnName] = this.protectEntryPoint(win[fnName]);
}
}
};
/**
* Helper function for protecting setTimeout/setInterval.
* @param {string} fnName The name of the function we're protecting. Must
* be setTimeout or setInterval.
* @private
*/
goog.debug.ErrorHandler.prototype.protectWindowFunctionsHelper_ =
function(fnName) {
var win = goog.getObjectByName('window');
var originalFn = win[fnName];
var that = this;
win[fnName] = function(fn, time) {
// Don't try to protect strings. In theory, we could try to globalEval
// the string, but this seems to lead to permission errors on IE6.
if (goog.isString(fn)) {
fn = goog.partial(goog.globalEval, fn);
}
fn = that.protectEntryPoint(fn);
// IE doesn't support .call for setInterval/setTimeout, but it
// also doesn't care what "this" is, so we can just call the
// original function directly
if (originalFn.call) {
return originalFn.call(this, fn, time);
} else {
return originalFn(fn, time);
}
};
win[fnName][this.getFunctionIndex_(false)] = originalFn;
};
/**
* Set whether to wrap errors that occur in protected functions in a
* goog.debug.ErrorHandler.ProtectedFunctionError.
* @param {boolean} wrapErrors Whether to wrap errors.
*/
goog.debug.ErrorHandler.prototype.setWrapErrors = function(wrapErrors) {
this.wrapErrors_ = wrapErrors;
};
/**
* Set whether to add a prefix to all error messages that occur in protected
* functions.
* @param {boolean} prefixErrorMessages Whether to add a prefix to error
* messages.
*/
goog.debug.ErrorHandler.prototype.setPrefixErrorMessages =
function(prefixErrorMessages) {
this.prefixErrorMessages_ = prefixErrorMessages;
};
/** @override */
goog.debug.ErrorHandler.prototype.disposeInternal = function() {
// Try to unwrap window.setTimeout and window.setInterval.
var win = goog.getObjectByName('window');
win.setTimeout = this.unwrap(win.setTimeout);
win.setInterval = this.unwrap(win.setInterval);
goog.base(this, 'disposeInternal');
};
/**
* Error thrown to the caller of a protected entry point if the entry point
* throws an error.
* @param {*} cause The error thrown by the entry point.
* @constructor
* @extends {goog.debug.Error}
*/
goog.debug.ErrorHandler.ProtectedFunctionError = function(cause) {
var message = goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX +
(cause && cause.message ? String(cause.message) : String(cause));
goog.base(this, message);
/**
* The error thrown by the entry point.
* @type {*}
*/
this.cause = cause;
var stack = cause && cause.stack;
if (stack && goog.isString(stack)) {
this.stack = /** @type {string} */ (stack);
}
};
goog.inherits(goog.debug.ErrorHandler.ProtectedFunctionError, goog.debug.Error);
/**
* Text to prefix the message with.
* @type {string}
*/
goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX =
'Error in protected function: ';

View File

@@ -0,0 +1,38 @@
// Copyright 2008 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 File which defines dummy object to work around undefined
* properties compiler warning for weak dependencies on
* {@link goog.debug.ErrorHandler#protectEntryPoint}.
*
*/
goog.provide('goog.debug.errorHandlerWeakDep');
/**
* Dummy object to work around undefined properties compiler warning.
* @type {Object}
*/
goog.debug.errorHandlerWeakDep = {
/**
* @param {Function} fn An entry point function to be protected.
* @param {boolean=} opt_tracers Whether to install tracers around the
* fn.
* @return {Function} A protected wrapper function that calls the
* entry point function.
*/
protectEntryPoint: function(fn, opt_tracers) { return fn; }
};

View File

@@ -0,0 +1,376 @@
// Copyright 2009 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 ErrorReporter class, which creates an error
* handler that reports any errors raised to a URL.
*
*/
goog.provide('goog.debug.ErrorReporter');
goog.provide('goog.debug.ErrorReporter.ExceptionEvent');
goog.require('goog.asserts');
goog.require('goog.debug');
goog.require('goog.debug.ErrorHandler');
goog.require('goog.debug.entryPointRegistry');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.events.EventTarget');
goog.require('goog.log');
goog.require('goog.net.XhrIo');
goog.require('goog.object');
goog.require('goog.string');
goog.require('goog.uri.utils');
goog.require('goog.userAgent');
/**
* Constructs an error reporter. Internal Use Only. To install an error
* reporter see the {@see #install} method below.
*
* @param {string} handlerUrl The URL to which all errors will be reported.
* @param {function(!Error, !Object.<string, string>)=}
* opt_contextProvider When a report is to be sent to the server,
* this method will be called, and given an opportunity to modify the
* context object before submission to the server.
* @param {boolean=} opt_noAutoProtect Whether to automatically add handlers for
* onerror and to protect entry points. If apps have other error reporting
* facilities, it may make sense for them to set these up themselves and use
* the ErrorReporter just for transmission of reports.
* @constructor
* @extends {goog.events.EventTarget}
*/
goog.debug.ErrorReporter = function(
handlerUrl, opt_contextProvider, opt_noAutoProtect) {
goog.base(this);
/**
* Context provider, if one was provided.
* @type {?function(!Error, !Object.<string, string>)}
* @private
*/
this.contextProvider_ = opt_contextProvider || null;
/**
* The string prefix of any optional context parameters logged with the error.
* @private {string}
*/
this.contextPrefix_ = 'context.';
/**
* The number of bytes after which the ErrorReporter truncates the POST body.
* If null, the ErrorReporter won't truncate the body.
* @private {?number}
*/
this.truncationLimit_ = null;
/**
* XHR sender.
* @type {function(string, string, string, (Object|goog.structs.Map)=)}
* @private
*/
this.xhrSender_ = goog.debug.ErrorReporter.defaultXhrSender;
/**
* The URL at which all errors caught by this handler will be logged.
*
* @type {string}
* @private
*/
this.handlerUrl_ = handlerUrl;
if (!opt_noAutoProtect) {
this.setup_();
}
};
goog.inherits(goog.debug.ErrorReporter, goog.events.EventTarget);
/**
* Event broadcast when an exception is logged.
* @param {Error} error The exception that was was reported.
* @param {!Object.<string, string>} context The context values sent to the
* server alongside this error.
* @constructor
* @extends {goog.events.Event}
*/
goog.debug.ErrorReporter.ExceptionEvent = function(error, context) {
goog.events.Event.call(this, goog.debug.ErrorReporter.ExceptionEvent.TYPE);
/**
* The error that was reported.
* @type {Error}
*/
this.error = error;
/**
* Context values sent to the server alongside this report.
* @type {!Object.<string, string>}
*/
this.context = context;
};
goog.inherits(goog.debug.ErrorReporter.ExceptionEvent, goog.events.Event);
/**
* Event type for notifying of a logged exception.
* @type {string}
*/
goog.debug.ErrorReporter.ExceptionEvent.TYPE =
goog.events.getUniqueId('exception');
/**
* The internal error handler used to catch all errors.
*
* @type {goog.debug.ErrorHandler}
* @private
*/
goog.debug.ErrorReporter.prototype.errorHandler_ = null;
/**
* Extra headers for the error-reporting XHR.
* @type {Object|goog.structs.Map|undefined}
* @private
*/
goog.debug.ErrorReporter.prototype.extraHeaders_;
/**
* Logging object.
*
* @type {goog.log.Logger}
* @private
*/
goog.debug.ErrorReporter.logger_ =
goog.log.getLogger('goog.debug.ErrorReporter');
/**
* Installs an error reporter to catch all JavaScript errors raised.
*
* @param {string} loggingUrl The URL to which the errors caught will be
* reported.
* @param {function(!Error, !Object.<string, string>)=}
* opt_contextProvider When a report is to be sent to the server,
* this method will be called, and given an opportunity to modify the
* context object before submission to the server.
* @param {boolean=} opt_noAutoProtect Whether to automatically add handlers for
* onerror and to protect entry points. If apps have other error reporting
* facilities, it may make sense for them to set these up themselves and use
* the ErrorReporter just for transmission of reports.
* @return {goog.debug.ErrorReporter} The error reporter.
*/
goog.debug.ErrorReporter.install = function(
loggingUrl, opt_contextProvider, opt_noAutoProtect) {
var instance = new goog.debug.ErrorReporter(
loggingUrl, opt_contextProvider, opt_noAutoProtect);
return instance;
};
/**
* Default implemntation of XHR sender interface.
*
* @param {string} uri URI to make request to.
* @param {string} method Send method.
* @param {string} content Post data.
* @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
* request.
*/
goog.debug.ErrorReporter.defaultXhrSender = function(uri, method, content,
opt_headers) {
goog.net.XhrIo.send(uri, null, method, content, opt_headers);
};
/**
* Installs exception protection for an entry point function in addition
* to those that are protected by default.
* Has no effect in IE because window.onerror is used for reporting
* exceptions in that case.
*
* @param {Function} fn An entry point function to be protected.
* @return {Function} A protected wrapper function that calls the entry point
* function or null if the entry point could not be protected.
*/
goog.debug.ErrorReporter.prototype.protectAdditionalEntryPoint = function(fn) {
if (this.errorHandler_) {
return this.errorHandler_.protectEntryPoint(fn);
}
return null;
};
/**
* Add headers to the logging url.
* @param {Object|goog.structs.Map} loggingHeaders Extra headers to send
* to the logging URL.
*/
goog.debug.ErrorReporter.prototype.setLoggingHeaders =
function(loggingHeaders) {
this.extraHeaders_ = loggingHeaders;
};
/**
* Set the function used to send error reports to the server.
* @param {function(string, string, string, (Object|goog.structs.Map)=)}
* xhrSender If provided, this will be used to send a report to the
* server instead of the default method. The function will be given the URI,
* HTTP method request content, and (optionally) request headers to be
* added.
*/
goog.debug.ErrorReporter.prototype.setXhrSender = function(xhrSender) {
this.xhrSender_ = xhrSender;
};
/**
* Sets up the error reporter.
*
* @private
*/
goog.debug.ErrorReporter.prototype.setup_ = function() {
if (goog.userAgent.IE) {
// Use "onerror" because caught exceptions in IE don't provide line number.
goog.debug.catchErrors(
goog.bind(this.handleException, this), false, null);
} else {
// "onerror" doesn't work with FF2 or Chrome
this.errorHandler_ = new goog.debug.ErrorHandler(
goog.bind(this.handleException, this));
this.errorHandler_.protectWindowSetTimeout();
this.errorHandler_.protectWindowSetInterval();
goog.debug.entryPointRegistry.monitorAll(this.errorHandler_);
}
};
/**
* Handler for caught exceptions. Sends report to the LoggingServlet and
* notifies any listeners.
*
* @param {Object} e The exception.
* @param {!Object.<string, string>=} opt_context Context values to optionally
* include in the error report.
*/
goog.debug.ErrorReporter.prototype.handleException = function(e,
opt_context) {
var error = /** @type {!Error} */ (goog.debug.normalizeErrorObject(e));
// Construct the context, possibly from the one provided in the argument, and
// pass it to the context provider if there is one.
var context = opt_context ? goog.object.clone(opt_context) : {};
if (this.contextProvider_) {
try {
this.contextProvider_(error, context);
} catch (err) {
goog.log.error(goog.debug.ErrorReporter.logger_,
'Context provider threw an exception: ' + err.message);
}
}
// Truncate message to a reasonable length, since it will be sent in the URL.
var message = error.message.substring(0, 2000);
this.sendErrorReport(message, error.fileName, error.lineNumber, error.stack,
context);
try {
this.dispatchEvent(
new goog.debug.ErrorReporter.ExceptionEvent(error, context));
} catch (ex) {
// Swallow exception to avoid infinite recursion.
}
};
/**
* Sends an error report to the logging URL. This will not consult the context
* provider, the report will be sent exactly as specified.
*
* @param {string} message Error description.
* @param {string} fileName URL of the JavaScript file with the error.
* @param {number} line Line number of the error.
* @param {string=} opt_trace Call stack trace of the error.
* @param {!Object.<string, string>=} opt_context Context information to include
* in the request.
*/
goog.debug.ErrorReporter.prototype.sendErrorReport =
function(message, fileName, line, opt_trace, opt_context) {
try {
// Create the logging URL.
var requestUrl = goog.uri.utils.appendParams(this.handlerUrl_,
'script', fileName, 'error', message, 'line', line);
var queryMap = {};
queryMap['trace'] = opt_trace;
// Copy context into query data map
if (opt_context) {
for (var entry in opt_context) {
queryMap[this.contextPrefix_ + entry] = opt_context[entry];
}
}
// Copy query data map into request.
var queryData = goog.uri.utils.buildQueryDataFromMap(queryMap);
// Truncate if truncationLimit set.
if (goog.isNumber(this.truncationLimit_)) {
queryData = queryData.substring(0, this.truncationLimit_);
}
// Send the request with the contents of the error.
this.xhrSender_(requestUrl, 'POST', queryData, this.extraHeaders_);
} catch (e) {
var logMessage = goog.string.buildString(
'Error occurred in sending an error report.\n\n',
'script:', fileName, '\n',
'line:', line, '\n',
'error:', message, '\n',
'trace:', opt_trace);
goog.log.info(goog.debug.ErrorReporter.logger_, logMessage);
}
};
/**
* @param {string} prefix The prefix to appear prepended to all context
* variables in the error report body.
*/
goog.debug.ErrorReporter.prototype.setContextPrefix = function(prefix) {
this.contextPrefix_ = prefix;
};
/**
* @param {?number} limit Size in bytes to begin truncating POST body. Set to
* null to prevent truncation. The limit must be >= 0.
*/
goog.debug.ErrorReporter.prototype.setTruncationLimit = function(limit) {
goog.asserts.assert(!goog.isNumber(limit) || limit >= 0,
'Body limit must be valid number >= 0 or null');
this.truncationLimit_ = limit;
};
/** @override */
goog.debug.ErrorReporter.prototype.disposeInternal = function() {
goog.dispose(this.errorHandler_);
goog.base(this, 'disposeInternal');
};

View File

@@ -0,0 +1,362 @@
// 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.debug.DebugWindow');
goog.require('goog.debug.LogManager');
goog.require('goog.debug.Logger');
goog.require('goog.dom.DomHelper');
goog.require('goog.object');
goog.require('goog.string');
goog.require('goog.userAgent');
/**
* 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.base(this, 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');
div.innerHTML = 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();
doc.write(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');
el.innerHTML = '';
var loggers = goog.debug.FancyWindow.getLoggers_();
var dh = this.dh_;
for (var i = 0; i < loggers.length; i++) {
var logger = goog.debug.Logger.getLogger(loggers[i]);
var curlevel = logger.getLevel() ? logger.getLevel().name : 'INHERIT';
var div = dh.createDom('div', {},
this.getDropDown_('sel' + loggers[i], curlevel),
dh.createDom('span', {}, loggers[i] || '(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 = goog.debug.Logger.getLogger(loggers[i]);
var sel = dh.getElement('sel' + loggers[i]);
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;
};
/**
* Resize the lof 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() {
return goog.base(this, 'getStyleRules') +
'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 the default HTML for the debug window
* @return {string} Html.
* @private
*/
goog.debug.FancyWindow.prototype.getHtml_ = function() {
return '' +
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"' +
'"http://www.w3.org/TR/html4/loose.dtd">' +
'<html><head><title>Logging: ' + this.identifier + '</title>' +
'<style>' + this.getStyleRules() + '</style>' +
'</head><body>' +
'<div id="log" style="overflow:auto"></div>' +
'<div id="head">' +
'<p><b>Logging: ' + this.identifier + '</b></p><p>' +
this.welcomeMessage + '</p>' +
'<span id="clearbutton">clear</span>' +
'<span id="exitbutton">exit</span>' +
'<span id="openbutton">options</span>' +
'</div>' +
'<div id="options">' +
'<big><b>Options:</b></big>' +
'<div id="optionsarea"></div>' +
'<span id="closebutton">save and close</span>' +
'</div>' +
'</body></html>';
};
/**
* 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];
var level = goog.debug.Logger.getLogger(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.Logger.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} Array of logger idents, e.g. goog.net.XhrIo.
* @private
*/
goog.debug.FancyWindow.getLoggers_ = function() {
var loggers = goog.object.getKeys(goog.debug.LogManager.getLoggers());
loggers.sort();
return loggers;
};

View File

@@ -0,0 +1,316 @@
// 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 various formatters for logging. Please minimize
* dependencies this file has on other closure classes as any dependency it
* takes won't be able to use the logging infrastructure.
*
*/
goog.provide('goog.debug.Formatter');
goog.provide('goog.debug.HtmlFormatter');
goog.provide('goog.debug.TextFormatter');
goog.require('goog.debug.RelativeTimeProvider');
goog.require('goog.string');
/**
* Base class for Formatters. A Formatter is used to format a LogRecord into
* something that can be displayed to the user.
*
* @param {string=} opt_prefix The prefix to place before text records.
* @constructor
*/
goog.debug.Formatter = function(opt_prefix) {
this.prefix_ = opt_prefix || '';
/**
* A provider that returns the relative start time.
* @type {goog.debug.RelativeTimeProvider}
* @private
*/
this.startTimeProvider_ =
goog.debug.RelativeTimeProvider.getDefaultInstance();
};
/**
* Whether to show absolute time in the DebugWindow.
* @type {boolean}
*/
goog.debug.Formatter.prototype.showAbsoluteTime = true;
/**
* Whether to show relative time in the DebugWindow.
* @type {boolean}
*/
goog.debug.Formatter.prototype.showRelativeTime = true;
/**
* Whether to show the logger name in the DebugWindow.
* @type {boolean}
*/
goog.debug.Formatter.prototype.showLoggerName = true;
/**
* Whether to show the logger exception text.
* @type {boolean}
*/
goog.debug.Formatter.prototype.showExceptionText = false;
/**
* Whether to show the severity level.
* @type {boolean}
*/
goog.debug.Formatter.prototype.showSeverityLevel = false;
/**
* Formats a record.
* @param {goog.debug.LogRecord} logRecord the logRecord to format.
* @return {string} The formatted string.
*/
goog.debug.Formatter.prototype.formatRecord = goog.abstractMethod;
/**
* Sets the start time provider. By default, this is the default instance
* but can be changed.
* @param {goog.debug.RelativeTimeProvider} provider The provider to use.
*/
goog.debug.Formatter.prototype.setStartTimeProvider = function(provider) {
this.startTimeProvider_ = provider;
};
/**
* Returns the start time provider. By default, this is the default instance
* but can be changed.
* @return {goog.debug.RelativeTimeProvider} The start time provider.
*/
goog.debug.Formatter.prototype.getStartTimeProvider = function() {
return this.startTimeProvider_;
};
/**
* Resets the start relative time.
*/
goog.debug.Formatter.prototype.resetRelativeTimeStart = function() {
this.startTimeProvider_.reset();
};
/**
* Returns a string for the time/date of the LogRecord.
* @param {goog.debug.LogRecord} logRecord The record to get a time stamp for.
* @return {string} A string representation of the time/date of the LogRecord.
* @private
*/
goog.debug.Formatter.getDateTimeStamp_ = function(logRecord) {
var time = new Date(logRecord.getMillis());
return goog.debug.Formatter.getTwoDigitString_((time.getFullYear() - 2000)) +
goog.debug.Formatter.getTwoDigitString_((time.getMonth() + 1)) +
goog.debug.Formatter.getTwoDigitString_(time.getDate()) + ' ' +
goog.debug.Formatter.getTwoDigitString_(time.getHours()) + ':' +
goog.debug.Formatter.getTwoDigitString_(time.getMinutes()) + ':' +
goog.debug.Formatter.getTwoDigitString_(time.getSeconds()) + '.' +
goog.debug.Formatter.getTwoDigitString_(
Math.floor(time.getMilliseconds() / 10));
};
/**
* Returns the number as a two-digit string, meaning it prepends a 0 if the
* number if less than 10.
* @param {number} n The number to format.
* @return {string} A two-digit string representation of {@code n}.
* @private
*/
goog.debug.Formatter.getTwoDigitString_ = function(n) {
if (n < 10) {
return '0' + n;
}
return String(n);
};
/**
* Returns a string for the number of seconds relative to the start time.
* Prepads with spaces so that anything less than 1000 seconds takes up the
* same number of characters for better formatting.
* @param {goog.debug.LogRecord} logRecord The log to compare time to.
* @param {number} relativeTimeStart The start time to compare to.
* @return {string} The number of seconds of the LogRecord relative to the
* start time.
* @private
*/
goog.debug.Formatter.getRelativeTime_ = function(logRecord,
relativeTimeStart) {
var ms = logRecord.getMillis() - relativeTimeStart;
var sec = ms / 1000;
var str = sec.toFixed(3);
var spacesToPrepend = 0;
if (sec < 1) {
spacesToPrepend = 2;
} else {
while (sec < 100) {
spacesToPrepend++;
sec *= 10;
}
}
while (spacesToPrepend-- > 0) {
str = ' ' + str;
}
return str;
};
/**
* Formatter that returns formatted html. See formatRecord for the classes
* it uses for various types of formatted output.
*
* @param {string=} opt_prefix The prefix to place before text records.
* @constructor
* @extends {goog.debug.Formatter}
*/
goog.debug.HtmlFormatter = function(opt_prefix) {
goog.debug.Formatter.call(this, opt_prefix);
};
goog.inherits(goog.debug.HtmlFormatter, goog.debug.Formatter);
/**
* Whether to show the logger exception text
* @type {boolean}
* @override
*/
goog.debug.HtmlFormatter.prototype.showExceptionText = true;
/**
* Formats a record
* @param {goog.debug.LogRecord} logRecord the logRecord to format.
* @return {string} The formatted string as html.
* @override
*/
goog.debug.HtmlFormatter.prototype.formatRecord = function(logRecord) {
var className;
switch (logRecord.getLevel().value) {
case goog.debug.Logger.Level.SHOUT.value:
className = 'dbg-sh';
break;
case goog.debug.Logger.Level.SEVERE.value:
className = 'dbg-sev';
break;
case goog.debug.Logger.Level.WARNING.value:
className = 'dbg-w';
break;
case goog.debug.Logger.Level.INFO.value:
className = 'dbg-i';
break;
case goog.debug.Logger.Level.FINE.value:
default:
className = 'dbg-f';
break;
}
// Build message html
var sb = [];
sb.push(this.prefix_, ' ');
if (this.showAbsoluteTime) {
sb.push('[', goog.debug.Formatter.getDateTimeStamp_(logRecord), '] ');
}
if (this.showRelativeTime) {
sb.push('[',
goog.string.whitespaceEscape(
goog.debug.Formatter.getRelativeTime_(logRecord,
this.startTimeProvider_.get())),
's] ');
}
if (this.showLoggerName) {
sb.push('[', goog.string.htmlEscape(logRecord.getLoggerName()), '] ');
}
if (this.showSeverityLevel) {
sb.push('[', goog.string.htmlEscape(logRecord.getLevel().name), '] ');
}
sb.push('<span class="', className, '">',
goog.string.newLineToBr(goog.string.whitespaceEscape(
goog.string.htmlEscape(logRecord.getMessage()))));
if (this.showExceptionText && logRecord.getException()) {
sb.push('<br>',
goog.string.newLineToBr(goog.string.whitespaceEscape(
logRecord.getExceptionText() || '')));
}
sb.push('</span><br>');
return sb.join('');
};
/**
* Formatter that returns formatted plain text
*
* @param {string=} opt_prefix The prefix to place before text records.
* @constructor
* @extends {goog.debug.Formatter}
*/
goog.debug.TextFormatter = function(opt_prefix) {
goog.debug.Formatter.call(this, opt_prefix);
};
goog.inherits(goog.debug.TextFormatter, goog.debug.Formatter);
/**
* Formats a record as text
* @param {goog.debug.LogRecord} logRecord the logRecord to format.
* @return {string} The formatted string.
* @override
*/
goog.debug.TextFormatter.prototype.formatRecord = function(logRecord) {
// Build message html
var sb = [];
sb.push(this.prefix_, ' ');
if (this.showAbsoluteTime) {
sb.push('[', goog.debug.Formatter.getDateTimeStamp_(logRecord), '] ');
}
if (this.showRelativeTime) {
sb.push('[', goog.debug.Formatter.getRelativeTime_(logRecord,
this.startTimeProvider_.get()), 's] ');
}
if (this.showLoggerName) {
sb.push('[', logRecord.getLoggerName(), '] ');
}
if (this.showSeverityLevel) {
sb.push('[', logRecord.getLevel().name, '] ');
}
sb.push(logRecord.getMessage(), '\n');
if (this.showExceptionText && logRecord.getException()) {
sb.push(logRecord.getExceptionText(), '\n');
}
return sb.join('');
};

View File

@@ -0,0 +1,162 @@
// Copyright 2011 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 Displays frames per second (FPS) for the current window.
* Only supported in browsers that support requestAnimationFrame.
* See: https://developer.mozilla.org/en/DOM/window.requestAnimationFrame.
*
* @see ../demos/fpsdisplay.html
*/
goog.provide('goog.debug.FpsDisplay');
goog.require('goog.asserts');
goog.require('goog.async.AnimationDelay');
goog.require('goog.ui.Component');
/**
* Displays frames per seconds that the window this component is
* rendered in is animating at.
*
* @param {goog.dom.DomHelper=} opt_domHelper An optional dom helper.
* @constructor
* @extends {goog.ui.Component}
*/
goog.debug.FpsDisplay = function(opt_domHelper) {
goog.base(this, opt_domHelper);
};
goog.inherits(goog.debug.FpsDisplay, goog.ui.Component);
/**
* CSS class for the FPS display.
*/
goog.debug.FpsDisplay.CSS = goog.getCssName('goog-fps-display');
/**
* The number of samples per FPS report.
*/
goog.debug.FpsDisplay.SAMPLES = 10;
/**
* The current animation.
* @type {goog.debug.FpsDisplay.FpsAnimation_}
* @private
*/
goog.debug.FpsDisplay.prototype.animation_ = null;
/** @override */
goog.debug.FpsDisplay.prototype.createDom = function() {
this.setElementInternal(
this.getDomHelper().createDom('div', goog.debug.FpsDisplay.CSS));
};
/** @override */
goog.debug.FpsDisplay.prototype.enterDocument = function() {
goog.base(this, 'enterDocument');
this.animation_ = new goog.debug.FpsDisplay.FpsAnimation_(this.getElement());
this.delay_ = new goog.async.AnimationDelay(
this.handleDelay_, this.getDomHelper().getWindow(), this);
this.delay_.start();
};
/**
* @param {number} now The current time.
* @private
*/
goog.debug.FpsDisplay.prototype.handleDelay_ = function(now) {
if (this.isInDocument()) {
this.animation_.onAnimationFrame(now);
this.delay_.start();
}
};
/** @override */
goog.debug.FpsDisplay.prototype.exitDocument = function() {
goog.base(this, 'exitDocument');
this.animation_ = null;
goog.dispose(this.delay_);
};
/**
* @return {number} The average frames per second.
*/
goog.debug.FpsDisplay.prototype.getFps = function() {
goog.asserts.assert(
this.isInDocument(), 'Render the FPS display before querying FPS');
return this.animation_.lastFps_;
};
/**
* @param {Element} elem An element to hold the FPS count.
* @constructor
* @private
*/
goog.debug.FpsDisplay.FpsAnimation_ = function(elem) {
/**
* An element to hold the current FPS rate.
* @type {Element}
* @private
*/
this.element_ = elem;
/**
* The number of frames observed so far.
* @type {number}
* @private
*/
this.frameNumber_ = 0;
};
/**
* The last time which we reported FPS at.
* @type {number}
* @private
*/
goog.debug.FpsDisplay.FpsAnimation_.prototype.lastTime_ = 0;
/**
* The last average FPS.
* @type {number}
* @private
*/
goog.debug.FpsDisplay.FpsAnimation_.prototype.lastFps_ = -1;
/**
* @param {number} now The current time.
*/
goog.debug.FpsDisplay.FpsAnimation_.prototype.onAnimationFrame = function(now) {
var SAMPLES = goog.debug.FpsDisplay.SAMPLES;
if (this.frameNumber_ % SAMPLES == 0) {
this.lastFps_ = Math.round((1000 * SAMPLES) / (now - this.lastTime_));
this.element_.innerHTML = this.lastFps_;
this.lastTime_ = now;
}
this.frameNumber_++;
};

View File

@@ -0,0 +1,143 @@
// Copyright 2007 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 GcDiagnostics class.
*
*/
goog.provide('goog.debug.GcDiagnostics');
goog.require('goog.debug.Trace');
goog.require('goog.log');
goog.require('goog.userAgent');
/**
* Class used for singleton goog.debug.GcDiagnostics. Used to hook into
* the L2 ActiveX controller to profile garbage collection information in IE.
* Can be used in combination with tracers (goog.debug.Trace), to provide object
* allocation counts from within the tracers or used alone by invoking start and
* stop.
*
* See http://go/l2binary for the install.
* TODO(user): Move the L2 installer somewhere more general.
* @constructor
* @private
*/
goog.debug.GcDiagnostics_ = function() {};
/**
* Install the GcDiagnostics tool.
*/
goog.debug.GcDiagnostics_.prototype.install = function() {
if (goog.userAgent.IE) {
/** @preserveTry */
try {
var l2Helper = new ActiveXObject('L2.NativeHelper');
// If using tracers, use the higher precision timer provided by L2.
if (goog.debug.Trace_) {
goog.debug.Trace_.now = function() {
return l2Helper['getMilliSeconds']();
};
}
if (l2Helper['gcTracer']) {
l2Helper['gcTracer']['installGcTracing']();
this.gcTracer_ = l2Helper['gcTracer'];
if (goog.debug.Trace) {
// If tracers are in use, register the gcTracer so that per tracer
// allocations are recorded.
goog.debug.Trace.setGcTracer(this.gcTracer_);
}
}
goog.log.info(this.logger_, 'Installed L2 native helper');
} catch (e) {
goog.log.info(this.logger_, 'Failed to install L2 native helper: ' + e);
}
}
};
/**
* Logger for the gcDiagnotics
* @type {goog.log.Logger}
* @private
*/
goog.debug.GcDiagnostics_.prototype.logger_ =
goog.log.getLogger('goog.debug.GcDiagnostics');
/**
* Starts recording garbage collection information. If a trace is already in
* progress, it is ended.
*/
goog.debug.GcDiagnostics_.prototype.start = function() {
if (this.gcTracer_) {
if (this.gcTracer_['isTracing']()) {
this.gcTracer_['endGcTracing']();
}
this.gcTracer_['startGcTracing']();
}
};
/**
* Stops recording garbage collection information. Logs details on the garbage
* collections that occurred between start and stop. If tracers are in use,
* adds comments where each GC occurs.
*/
goog.debug.GcDiagnostics_.prototype.stop = function() {
if (this.gcTracer_ && this.gcTracer_['isTracing']()) {
var gcTracer = this.gcTracer_;
this.gcTracer_['endGcTracing']();
var numGCs = gcTracer['getNumTraces']();
goog.log.info(this.logger_, '*********GC TRACE*********');
goog.log.info(this.logger_, 'GC ran ' + numGCs + ' times.');
var totalTime = 0;
for (var i = 0; i < numGCs; i++) {
var trace = gcTracer['getTrace'](i);
var msStart = trace['gcStartTime'];
var msElapsed = trace['gcElapsedTime'];
var msRounded = Math.round(msElapsed * 10) / 10;
var s = 'GC ' + i + ': ' + msRounded + ' ms, ' +
'numVValAlloc=' + trace['numVValAlloc'] + ', ' +
'numVarAlloc=' + trace['numVarAlloc'] + ', ' +
'numBytesSysAlloc=' + trace['numBytesSysAlloc'];
if (goog.debug.Trace) {
goog.debug.Trace.addComment(s, null, msStart);
}
goog.log.info(this.logger_, s);
totalTime += msElapsed;
}
if (goog.debug.Trace) {
goog.debug.Trace.addComment('Total GC time was ' + totalTime + ' ms.');
}
goog.log.info(this.logger_, 'Total GC time was ' + totalTime + ' ms.');
goog.log.info(this.logger_, '*********GC TRACE*********');
}
};
/**
* Singleton GcDiagnostics object
* @type {goog.debug.GcDiagnostics_}
*/
goog.debug.GcDiagnostics = new goog.debug.GcDiagnostics_();

View File

@@ -0,0 +1,147 @@
// Copyright 2010 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 A buffer for log records. The purpose of this is to improve
* logging performance by re-using old objects when the buffer becomes full and
* to eliminate the need for each app to implement their own log buffer. The
* disadvantage to doing this is that log handlers cannot maintain references to
* log records and expect that they are not overwriten at a later point.
*
* @author agrieve@google.com (Andrew Grieve)
*/
goog.provide('goog.debug.LogBuffer');
goog.require('goog.asserts');
goog.require('goog.debug.LogRecord');
/**
* Creates the log buffer.
* @constructor
*/
goog.debug.LogBuffer = function() {
goog.asserts.assert(goog.debug.LogBuffer.isBufferingEnabled(),
'Cannot use goog.debug.LogBuffer without defining ' +
'goog.debug.LogBuffer.CAPACITY.');
this.clear();
};
/**
* A static method that always returns the same instance of LogBuffer.
* @return {!goog.debug.LogBuffer} The LogBuffer singleton instance.
*/
goog.debug.LogBuffer.getInstance = function() {
if (!goog.debug.LogBuffer.instance_) {
// This function is written with the return statement after the assignment
// to avoid the jscompiler StripCode bug described in http://b/2608064.
// After that bug is fixed this can be refactored.
goog.debug.LogBuffer.instance_ = new goog.debug.LogBuffer();
}
return goog.debug.LogBuffer.instance_;
};
/**
* @define {number} The number of log records to buffer. 0 means disable
* buffering.
*/
goog.define('goog.debug.LogBuffer.CAPACITY', 0);
/**
* The array to store the records.
* @type {!Array.<!goog.debug.LogRecord|undefined>}
* @private
*/
goog.debug.LogBuffer.prototype.buffer_;
/**
* The index of the most recently added record or -1 if there are no records.
* @type {number}
* @private
*/
goog.debug.LogBuffer.prototype.curIndex_;
/**
* Whether the buffer is at capacity.
* @type {boolean}
* @private
*/
goog.debug.LogBuffer.prototype.isFull_;
/**
* Adds a log record to the buffer, possibly overwriting the oldest record.
* @param {goog.debug.Logger.Level} level One of the level identifiers.
* @param {string} msg The string message.
* @param {string} loggerName The name of the source logger.
* @return {!goog.debug.LogRecord} The log record.
*/
goog.debug.LogBuffer.prototype.addRecord = function(level, msg, loggerName) {
var curIndex = (this.curIndex_ + 1) % goog.debug.LogBuffer.CAPACITY;
this.curIndex_ = curIndex;
if (this.isFull_) {
var ret = this.buffer_[curIndex];
ret.reset(level, msg, loggerName);
return ret;
}
this.isFull_ = curIndex == goog.debug.LogBuffer.CAPACITY - 1;
return this.buffer_[curIndex] =
new goog.debug.LogRecord(level, msg, loggerName);
};
/**
* @return {boolean} Whether the log buffer is enabled.
*/
goog.debug.LogBuffer.isBufferingEnabled = function() {
return goog.debug.LogBuffer.CAPACITY > 0;
};
/**
* Removes all buffered log records.
*/
goog.debug.LogBuffer.prototype.clear = function() {
this.buffer_ = new Array(goog.debug.LogBuffer.CAPACITY);
this.curIndex_ = -1;
this.isFull_ = false;
};
/**
* Calls the given function for each buffered log record, starting with the
* oldest one.
* @param {function(!goog.debug.LogRecord)} func The function to call.
*/
goog.debug.LogBuffer.prototype.forEachRecord = function(func) {
var buffer = this.buffer_;
// Corner case: no records.
if (!buffer[0]) {
return;
}
var curIndex = this.curIndex_;
var i = this.isFull_ ? curIndex : -1;
do {
i = (i + 1) % goog.debug.LogBuffer.CAPACITY;
func(/** @type {!goog.debug.LogRecord} */ (buffer[i]));
} while (i != curIndex);
};

View File

@@ -0,0 +1,853 @@
// 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 Logger 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.
*
* @see ../demos/debug.html
*/
goog.provide('goog.debug.LogManager');
goog.provide('goog.debug.Logger');
goog.provide('goog.debug.Logger.Level');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.debug');
goog.require('goog.debug.LogBuffer');
goog.require('goog.debug.LogRecord');
/**
* The Logger is an object used for logging debug messages. Loggers are
* normally named, using a hierarchical dot-separated namespace. Logger names
* can be arbitrary strings, but they should normally be based on the package
* name or class name of the logged component, such as goog.net.BrowserChannel.
*
* The Logger object is loosely based on the java class
* java.util.logging.Logger. It supports different levels of filtering for
* different loggers.
*
* The logger object should never be instantiated by application code. It
* should always use the goog.debug.Logger.getLogger function.
*
* @constructor
* @param {string} name The name of the Logger.
*/
goog.debug.Logger = function(name) {
/**
* Name of the Logger. Generally a dot-separated namespace
* @type {string}
* @private
*/
this.name_ = name;
};
/**
* Parent Logger.
* @type {goog.debug.Logger}
* @private
*/
goog.debug.Logger.prototype.parent_ = null;
/**
* Level that this logger only filters above. Null indicates it should
* inherit from the parent.
* @type {goog.debug.Logger.Level}
* @private
*/
goog.debug.Logger.prototype.level_ = null;
/**
* Map of children loggers. The keys are the leaf names of the children and
* the values are the child loggers.
* @type {Object}
* @private
*/
goog.debug.Logger.prototype.children_ = null;
/**
* Handlers that are listening to this logger.
* @type {Array.<Function>}
* @private
*/
goog.debug.Logger.prototype.handlers_ = null;
/**
* @define {boolean} Toggles whether loggers other than the root logger can have
* log handlers attached to them and whether they can have their log level
* set. Logging is a bit faster when this is set to false.
*/
goog.define('goog.debug.Logger.ENABLE_HIERARCHY', true);
if (!goog.debug.Logger.ENABLE_HIERARCHY) {
/**
* @type {!Array.<Function>}
* @private
*/
goog.debug.Logger.rootHandlers_ = [];
/**
* @type {goog.debug.Logger.Level}
* @private
*/
goog.debug.Logger.rootLevel_;
}
/**
* The Level class defines a set of standard logging levels that
* can be used to control logging output. The logging Level objects
* are ordered and are specified by ordered integers. Enabling logging
* at a given level also enables logging at all higher levels.
* <p>
* Clients should normally use the predefined Level constants such
* as Level.SEVERE.
* <p>
* The levels in descending order are:
* <ul>
* <li>SEVERE (highest value)
* <li>WARNING
* <li>INFO
* <li>CONFIG
* <li>FINE
* <li>FINER
* <li>FINEST (lowest value)
* </ul>
* In addition there is a level OFF that can be used to turn
* off logging, and a level ALL that can be used to enable
* logging of all messages.
*
* @param {string} name The name of the level.
* @param {number} value The numeric value of the level.
* @constructor
*/
goog.debug.Logger.Level = function(name, value) {
/**
* The name of the level
* @type {string}
*/
this.name = name;
/**
* The numeric value of the level
* @type {number}
*/
this.value = value;
};
/**
* @return {string} String representation of the logger level.
* @override
*/
goog.debug.Logger.Level.prototype.toString = function() {
return this.name;
};
/**
* OFF is a special level that can be used to turn off logging.
* This level is initialized to <CODE>Infinity</CODE>.
* @type {!goog.debug.Logger.Level}
*/
goog.debug.Logger.Level.OFF =
new goog.debug.Logger.Level('OFF', Infinity);
/**
* SHOUT is a message level for extra debugging loudness.
* This level is initialized to <CODE>1200</CODE>.
* @type {!goog.debug.Logger.Level}
*/
goog.debug.Logger.Level.SHOUT = new goog.debug.Logger.Level('SHOUT', 1200);
/**
* SEVERE is a message level indicating a serious failure.
* This level is initialized to <CODE>1000</CODE>.
* @type {!goog.debug.Logger.Level}
*/
goog.debug.Logger.Level.SEVERE = new goog.debug.Logger.Level('SEVERE', 1000);
/**
* WARNING is a message level indicating a potential problem.
* This level is initialized to <CODE>900</CODE>.
* @type {!goog.debug.Logger.Level}
*/
goog.debug.Logger.Level.WARNING = new goog.debug.Logger.Level('WARNING', 900);
/**
* INFO is a message level for informational messages.
* This level is initialized to <CODE>800</CODE>.
* @type {!goog.debug.Logger.Level}
*/
goog.debug.Logger.Level.INFO = new goog.debug.Logger.Level('INFO', 800);
/**
* CONFIG is a message level for static configuration messages.
* This level is initialized to <CODE>700</CODE>.
* @type {!goog.debug.Logger.Level}
*/
goog.debug.Logger.Level.CONFIG = new goog.debug.Logger.Level('CONFIG', 700);
/**
* FINE is a message level providing tracing information.
* This level is initialized to <CODE>500</CODE>.
* @type {!goog.debug.Logger.Level}
*/
goog.debug.Logger.Level.FINE = new goog.debug.Logger.Level('FINE', 500);
/**
* FINER indicates a fairly detailed tracing message.
* This level is initialized to <CODE>400</CODE>.
* @type {!goog.debug.Logger.Level}
*/
goog.debug.Logger.Level.FINER = new goog.debug.Logger.Level('FINER', 400);
/**
* FINEST indicates a highly detailed tracing message.
* This level is initialized to <CODE>300</CODE>.
* @type {!goog.debug.Logger.Level}
*/
goog.debug.Logger.Level.FINEST = new goog.debug.Logger.Level('FINEST', 300);
/**
* ALL indicates that all messages should be logged.
* This level is initialized to <CODE>0</CODE>.
* @type {!goog.debug.Logger.Level}
*/
goog.debug.Logger.Level.ALL = new goog.debug.Logger.Level('ALL', 0);
/**
* The predefined levels.
* @type {!Array.<!goog.debug.Logger.Level>}
* @final
*/
goog.debug.Logger.Level.PREDEFINED_LEVELS = [
goog.debug.Logger.Level.OFF,
goog.debug.Logger.Level.SHOUT,
goog.debug.Logger.Level.SEVERE,
goog.debug.Logger.Level.WARNING,
goog.debug.Logger.Level.INFO,
goog.debug.Logger.Level.CONFIG,
goog.debug.Logger.Level.FINE,
goog.debug.Logger.Level.FINER,
goog.debug.Logger.Level.FINEST,
goog.debug.Logger.Level.ALL];
/**
* A lookup map used to find the level object based on the name or value of
* the level object.
* @type {Object}
* @private
*/
goog.debug.Logger.Level.predefinedLevelsCache_ = null;
/**
* Creates the predefined levels cache and populates it.
* @private
*/
goog.debug.Logger.Level.createPredefinedLevelsCache_ = function() {
goog.debug.Logger.Level.predefinedLevelsCache_ = {};
for (var i = 0, level; level = goog.debug.Logger.Level.PREDEFINED_LEVELS[i];
i++) {
goog.debug.Logger.Level.predefinedLevelsCache_[level.value] = level;
goog.debug.Logger.Level.predefinedLevelsCache_[level.name] = level;
}
};
/**
* Gets the predefined level with the given name.
* @param {string} name The name of the level.
* @return {goog.debug.Logger.Level} The level, or null if none found.
*/
goog.debug.Logger.Level.getPredefinedLevel = function(name) {
if (!goog.debug.Logger.Level.predefinedLevelsCache_) {
goog.debug.Logger.Level.createPredefinedLevelsCache_();
}
return goog.debug.Logger.Level.predefinedLevelsCache_[name] || null;
};
/**
* Gets the highest predefined level <= #value.
* @param {number} value Level value.
* @return {goog.debug.Logger.Level} The level, or null if none found.
*/
goog.debug.Logger.Level.getPredefinedLevelByValue = function(value) {
if (!goog.debug.Logger.Level.predefinedLevelsCache_) {
goog.debug.Logger.Level.createPredefinedLevelsCache_();
}
if (value in goog.debug.Logger.Level.predefinedLevelsCache_) {
return goog.debug.Logger.Level.predefinedLevelsCache_[value];
}
for (var i = 0; i < goog.debug.Logger.Level.PREDEFINED_LEVELS.length; ++i) {
var level = goog.debug.Logger.Level.PREDEFINED_LEVELS[i];
if (level.value <= value) {
return level;
}
}
return null;
};
/**
* Finds or creates a logger for a named subsystem. If a logger has already been
* created with the given name it is returned. Otherwise a new logger is
* created. If a new logger is created its log level will be configured based
* on the LogManager configuration and it will configured to also send logging
* output to its parent's handlers. It will be registered in the LogManager
* global namespace.
*
* @param {string} name A name for the logger. This should be a dot-separated
* name and should normally be based on the package name or class name of the
* subsystem, such as goog.net.BrowserChannel.
* @return {!goog.debug.Logger} The named logger.
*/
goog.debug.Logger.getLogger = function(name) {
return goog.debug.LogManager.getLogger(name);
};
/**
* Logs a message to profiling tools, if available.
* {@see http://code.google.com/webtoolkit/speedtracer/logging-api.html}
* {@see http://msdn.microsoft.com/en-us/library/dd433074(VS.85).aspx}
* @param {string} msg The message to log.
*/
goog.debug.Logger.logToProfilers = function(msg) {
// Using goog.global, as loggers might be used in window-less contexts.
if (goog.global['console']) {
if (goog.global['console']['timeStamp']) {
// Logs a message to Firebug, Web Inspector, SpeedTracer, etc.
goog.global['console']['timeStamp'](msg);
} else if (goog.global['console']['markTimeline']) {
// TODO(user): markTimeline is deprecated. Drop this else clause entirely
// after Chrome M14 hits stable.
goog.global['console']['markTimeline'](msg);
}
}
if (goog.global['msWriteProfilerMark']) {
// Logs a message to the Microsoft profiler
goog.global['msWriteProfilerMark'](msg);
}
};
/**
* Gets the name of this logger.
* @return {string} The name of this logger.
*/
goog.debug.Logger.prototype.getName = function() {
return this.name_;
};
/**
* Adds a handler to the logger. This doesn't use the event system because
* we want to be able to add logging to the event system.
* @param {Function} handler Handler function to add.
*/
goog.debug.Logger.prototype.addHandler = function(handler) {
if (goog.debug.LOGGING_ENABLED) {
if (goog.debug.Logger.ENABLE_HIERARCHY) {
if (!this.handlers_) {
this.handlers_ = [];
}
this.handlers_.push(handler);
} else {
goog.asserts.assert(!this.name_,
'Cannot call addHandler on a non-root logger when ' +
'goog.debug.Logger.ENABLE_HIERARCHY is false.');
goog.debug.Logger.rootHandlers_.push(handler);
}
}
};
/**
* Removes a handler from the logger. This doesn't use the event system because
* we want to be able to add logging to the event system.
* @param {Function} handler Handler function to remove.
* @return {boolean} Whether the handler was removed.
*/
goog.debug.Logger.prototype.removeHandler = function(handler) {
if (goog.debug.LOGGING_ENABLED) {
var handlers = goog.debug.Logger.ENABLE_HIERARCHY ? this.handlers_ :
goog.debug.Logger.rootHandlers_;
return !!handlers && goog.array.remove(handlers, handler);
} else {
return false;
}
};
/**
* Returns the parent of this logger.
* @return {goog.debug.Logger} The parent logger or null if this is the root.
*/
goog.debug.Logger.prototype.getParent = function() {
return this.parent_;
};
/**
* Returns the children of this logger as a map of the child name to the logger.
* @return {!Object} The map where the keys are the child leaf names and the
* values are the Logger objects.
*/
goog.debug.Logger.prototype.getChildren = function() {
if (!this.children_) {
this.children_ = {};
}
return this.children_;
};
/**
* Set the log level specifying which message levels will be logged by this
* logger. Message levels lower than this value will be discarded.
* The level value Level.OFF can be used to turn off logging. If the new level
* is null, it means that this node should inherit its level from its nearest
* ancestor with a specific (non-null) level value.
*
* @param {goog.debug.Logger.Level} level The new level.
*/
goog.debug.Logger.prototype.setLevel = function(level) {
if (goog.debug.LOGGING_ENABLED) {
if (goog.debug.Logger.ENABLE_HIERARCHY) {
this.level_ = level;
} else {
goog.asserts.assert(!this.name_,
'Cannot call setLevel() on a non-root logger when ' +
'goog.debug.Logger.ENABLE_HIERARCHY is false.');
goog.debug.Logger.rootLevel_ = level;
}
}
};
/**
* Gets the log level specifying which message levels will be logged by this
* logger. Message levels lower than this value will be discarded.
* The level value Level.OFF can be used to turn off logging. If the level
* is null, it means that this node should inherit its level from its nearest
* ancestor with a specific (non-null) level value.
*
* @return {goog.debug.Logger.Level} The level.
*/
goog.debug.Logger.prototype.getLevel = function() {
return goog.debug.LOGGING_ENABLED ?
this.level_ : goog.debug.Logger.Level.OFF;
};
/**
* Returns the effective level of the logger based on its ancestors' levels.
* @return {goog.debug.Logger.Level} The level.
*/
goog.debug.Logger.prototype.getEffectiveLevel = function() {
if (!goog.debug.LOGGING_ENABLED) {
return goog.debug.Logger.Level.OFF;
}
if (!goog.debug.Logger.ENABLE_HIERARCHY) {
return goog.debug.Logger.rootLevel_;
}
if (this.level_) {
return this.level_;
}
if (this.parent_) {
return this.parent_.getEffectiveLevel();
}
goog.asserts.fail('Root logger has no level set.');
return null;
};
/**
* Checks if a message of the given level would actually be logged by this
* logger. This check is based on the Loggers effective level, which may be
* inherited from its parent.
* @param {goog.debug.Logger.Level} level The level to check.
* @return {boolean} Whether the message would be logged.
*/
goog.debug.Logger.prototype.isLoggable = function(level) {
return goog.debug.LOGGING_ENABLED &&
level.value >= this.getEffectiveLevel().value;
};
/**
* Logs a message. If the logger is currently enabled for the
* given message level then the given message is forwarded to all the
* registered output Handler objects.
* @param {goog.debug.Logger.Level} level One of the level identifiers.
* @param {string} msg The string message.
* @param {Error|Object=} opt_exception An exception associated with the
* message.
*/
goog.debug.Logger.prototype.log = function(level, msg, opt_exception) {
// java caches the effective level, not sure it's necessary here
if (goog.debug.LOGGING_ENABLED && this.isLoggable(level)) {
this.doLogRecord_(this.getLogRecord(level, msg, opt_exception));
}
};
/**
* Creates a new log record and adds the exception (if present) to it.
* @param {goog.debug.Logger.Level} level One of the level identifiers.
* @param {string} msg The string message.
* @param {Error|Object=} opt_exception An exception associated with the
* message.
* @return {!goog.debug.LogRecord} A log record.
*/
goog.debug.Logger.prototype.getLogRecord = function(level, msg, opt_exception) {
if (goog.debug.LogBuffer.isBufferingEnabled()) {
var logRecord =
goog.debug.LogBuffer.getInstance().addRecord(level, msg, this.name_);
} else {
logRecord = new goog.debug.LogRecord(level, String(msg), this.name_);
}
if (opt_exception) {
logRecord.setException(opt_exception);
logRecord.setExceptionText(
goog.debug.exposeException(opt_exception, arguments.callee.caller));
}
return logRecord;
};
/**
* Logs a message at the Logger.Level.SHOUT level.
* If the logger is currently enabled for the given message level then the
* given message is forwarded to all the registered output Handler objects.
* @param {string} msg The string message.
* @param {Error=} opt_exception An exception associated with the message.
*/
goog.debug.Logger.prototype.shout = function(msg, opt_exception) {
if (goog.debug.LOGGING_ENABLED) {
this.log(goog.debug.Logger.Level.SHOUT, msg, opt_exception);
}
};
/**
* Logs a message at the Logger.Level.SEVERE level.
* If the logger is currently enabled for the given message level then the
* given message is forwarded to all the registered output Handler objects.
* @param {string} msg The string message.
* @param {Error=} opt_exception An exception associated with the message.
*/
goog.debug.Logger.prototype.severe = function(msg, opt_exception) {
if (goog.debug.LOGGING_ENABLED) {
this.log(goog.debug.Logger.Level.SEVERE, msg, opt_exception);
}
};
/**
* Logs a message at the Logger.Level.WARNING level.
* If the logger is currently enabled for the given message level then the
* given message is forwarded to all the registered output Handler objects.
* @param {string} msg The string message.
* @param {Error=} opt_exception An exception associated with the message.
*/
goog.debug.Logger.prototype.warning = function(msg, opt_exception) {
if (goog.debug.LOGGING_ENABLED) {
this.log(goog.debug.Logger.Level.WARNING, msg, opt_exception);
}
};
/**
* Logs a message at the Logger.Level.INFO level.
* If the logger is currently enabled for the given message level then the
* given message is forwarded to all the registered output Handler objects.
* @param {string} msg The string message.
* @param {Error=} opt_exception An exception associated with the message.
*/
goog.debug.Logger.prototype.info = function(msg, opt_exception) {
if (goog.debug.LOGGING_ENABLED) {
this.log(goog.debug.Logger.Level.INFO, msg, opt_exception);
}
};
/**
* Logs a message at the Logger.Level.CONFIG level.
* If the logger is currently enabled for the given message level then the
* given message is forwarded to all the registered output Handler objects.
* @param {string} msg The string message.
* @param {Error=} opt_exception An exception associated with the message.
*/
goog.debug.Logger.prototype.config = function(msg, opt_exception) {
if (goog.debug.LOGGING_ENABLED) {
this.log(goog.debug.Logger.Level.CONFIG, msg, opt_exception);
}
};
/**
* Logs a message at the Logger.Level.FINE level.
* If the logger is currently enabled for the given message level then the
* given message is forwarded to all the registered output Handler objects.
* @param {string} msg The string message.
* @param {Error=} opt_exception An exception associated with the message.
*/
goog.debug.Logger.prototype.fine = function(msg, opt_exception) {
if (goog.debug.LOGGING_ENABLED) {
this.log(goog.debug.Logger.Level.FINE, msg, opt_exception);
}
};
/**
* Logs a message at the Logger.Level.FINER level.
* If the logger is currently enabled for the given message level then the
* given message is forwarded to all the registered output Handler objects.
* @param {string} msg The string message.
* @param {Error=} opt_exception An exception associated with the message.
*/
goog.debug.Logger.prototype.finer = function(msg, opt_exception) {
if (goog.debug.LOGGING_ENABLED) {
this.log(goog.debug.Logger.Level.FINER, msg, opt_exception);
}
};
/**
* Logs a message at the Logger.Level.FINEST level.
* If the logger is currently enabled for the given message level then the
* given message is forwarded to all the registered output Handler objects.
* @param {string} msg The string message.
* @param {Error=} opt_exception An exception associated with the message.
*/
goog.debug.Logger.prototype.finest = function(msg, opt_exception) {
if (goog.debug.LOGGING_ENABLED) {
this.log(goog.debug.Logger.Level.FINEST, msg, opt_exception);
}
};
/**
* Logs a LogRecord. If the logger is currently enabled for the
* given message level then the given message is forwarded to all the
* registered output Handler objects.
* @param {goog.debug.LogRecord} logRecord A log record to log.
*/
goog.debug.Logger.prototype.logRecord = function(logRecord) {
if (goog.debug.LOGGING_ENABLED && this.isLoggable(logRecord.getLevel())) {
this.doLogRecord_(logRecord);
}
};
/**
* Logs a LogRecord.
* @param {goog.debug.LogRecord} logRecord A log record to log.
* @private
*/
goog.debug.Logger.prototype.doLogRecord_ = function(logRecord) {
goog.debug.Logger.logToProfilers('log:' + logRecord.getMessage());
if (goog.debug.Logger.ENABLE_HIERARCHY) {
var target = this;
while (target) {
target.callPublish_(logRecord);
target = target.getParent();
}
} else {
for (var i = 0, handler; handler = goog.debug.Logger.rootHandlers_[i++]; ) {
handler(logRecord);
}
}
};
/**
* Calls the handlers for publish.
* @param {goog.debug.LogRecord} logRecord The log record to publish.
* @private
*/
goog.debug.Logger.prototype.callPublish_ = function(logRecord) {
if (this.handlers_) {
for (var i = 0, handler; handler = this.handlers_[i]; i++) {
handler(logRecord);
}
}
};
/**
* Sets the parent of this logger. This is used for setting up the logger tree.
* @param {goog.debug.Logger} parent The parent logger.
* @private
*/
goog.debug.Logger.prototype.setParent_ = function(parent) {
this.parent_ = parent;
};
/**
* Adds a child to this logger. This is used for setting up the logger tree.
* @param {string} name The leaf name of the child.
* @param {goog.debug.Logger} logger The child logger.
* @private
*/
goog.debug.Logger.prototype.addChild_ = function(name, logger) {
this.getChildren()[name] = logger;
};
/**
* There is a single global LogManager object that is used to maintain a set of
* shared state about Loggers and log services. This is loosely based on the
* java class java.util.logging.LogManager.
*/
goog.debug.LogManager = {};
/**
* Map of logger names to logger objects.
*
* @type {!Object}
* @private
*/
goog.debug.LogManager.loggers_ = {};
/**
* The root logger which is the root of the logger tree.
* @type {goog.debug.Logger}
* @private
*/
goog.debug.LogManager.rootLogger_ = null;
/**
* Initializes the LogManager if not already initialized.
*/
goog.debug.LogManager.initialize = function() {
if (!goog.debug.LogManager.rootLogger_) {
goog.debug.LogManager.rootLogger_ = new goog.debug.Logger('');
goog.debug.LogManager.loggers_[''] = goog.debug.LogManager.rootLogger_;
goog.debug.LogManager.rootLogger_.setLevel(goog.debug.Logger.Level.CONFIG);
}
};
/**
* Returns all the loggers.
* @return {!Object} Map of logger names to logger objects.
*/
goog.debug.LogManager.getLoggers = function() {
return goog.debug.LogManager.loggers_;
};
/**
* Returns the root of the logger tree namespace, the logger with the empty
* string as its name.
*
* @return {!goog.debug.Logger} The root logger.
*/
goog.debug.LogManager.getRoot = function() {
goog.debug.LogManager.initialize();
return /** @type {!goog.debug.Logger} */ (goog.debug.LogManager.rootLogger_);
};
/**
* Finds a named logger.
*
* @param {string} name A name for the logger. This should be a dot-separated
* name and should normally be based on the package name or class name of the
* subsystem, such as goog.net.BrowserChannel.
* @return {!goog.debug.Logger} The named logger.
*/
goog.debug.LogManager.getLogger = function(name) {
goog.debug.LogManager.initialize();
var ret = goog.debug.LogManager.loggers_[name];
return ret || goog.debug.LogManager.createLogger_(name);
};
/**
* Creates a function that can be passed to goog.debug.catchErrors. The function
* will log all reported errors using the given logger.
* @param {goog.debug.Logger=} opt_logger The logger to log the errors to.
* Defaults to the root logger.
* @return {function(Object)} The created function.
*/
goog.debug.LogManager.createFunctionForCatchErrors = function(opt_logger) {
return function(info) {
var logger = opt_logger || goog.debug.LogManager.getRoot();
logger.severe('Error: ' + info.message + ' (' + info.fileName +
' @ Line: ' + info.line + ')');
};
};
/**
* Creates the named logger. Will also create the parents of the named logger
* if they don't yet exist.
* @param {string} name The name of the logger.
* @return {!goog.debug.Logger} The named logger.
* @private
*/
goog.debug.LogManager.createLogger_ = function(name) {
// find parent logger
var logger = new goog.debug.Logger(name);
if (goog.debug.Logger.ENABLE_HIERARCHY) {
var lastDotIndex = name.lastIndexOf('.');
var parentName = name.substr(0, lastDotIndex);
var leafName = name.substr(lastDotIndex + 1);
var parentLogger = goog.debug.LogManager.getLogger(parentName);
// tell the parent about the child and the child about the parent
parentLogger.addChild_(leafName, logger);
logger.setParent_(parentLogger);
}
goog.debug.LogManager.loggers_[name] = logger;
return logger;
};

View File

@@ -0,0 +1,271 @@
// 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 LogRecord 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.
*
*/
goog.provide('goog.debug.LogRecord');
/**
* LogRecord objects are used to pass logging requests between
* the logging framework and individual log Handlers.
* @constructor
* @param {goog.debug.Logger.Level} level One of the level identifiers.
* @param {string} msg The string message.
* @param {string} loggerName The name of the source logger.
* @param {number=} opt_time Time this log record was created if other than now.
* If 0, we use #goog.now.
* @param {number=} opt_sequenceNumber Sequence number of this log record. This
* should only be passed in when restoring a log record from persistence.
*/
goog.debug.LogRecord = function(level, msg, loggerName,
opt_time, opt_sequenceNumber) {
this.reset(level, msg, loggerName, opt_time, opt_sequenceNumber);
};
/**
* Time the LogRecord was created.
* @type {number}
* @private
*/
goog.debug.LogRecord.prototype.time_;
/**
* Level of the LogRecord
* @type {goog.debug.Logger.Level}
* @private
*/
goog.debug.LogRecord.prototype.level_;
/**
* Message associated with the record
* @type {string}
* @private
*/
goog.debug.LogRecord.prototype.msg_;
/**
* Name of the logger that created the record.
* @type {string}
* @private
*/
goog.debug.LogRecord.prototype.loggerName_;
/**
* Sequence number for the LogRecord. Each record has a unique sequence number
* that is greater than all log records created before it.
* @type {number}
* @private
*/
goog.debug.LogRecord.prototype.sequenceNumber_ = 0;
/**
* Exception associated with the record
* @type {Object}
* @private
*/
goog.debug.LogRecord.prototype.exception_ = null;
/**
* Exception text associated with the record
* @type {?string}
* @private
*/
goog.debug.LogRecord.prototype.exceptionText_ = null;
/**
* @define {boolean} Whether to enable log sequence numbers.
*/
goog.define('goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS', true);
/**
* A sequence counter for assigning increasing sequence numbers to LogRecord
* objects.
* @type {number}
* @private
*/
goog.debug.LogRecord.nextSequenceNumber_ = 0;
/**
* Sets all fields of the log record.
* @param {goog.debug.Logger.Level} level One of the level identifiers.
* @param {string} msg The string message.
* @param {string} loggerName The name of the source logger.
* @param {number=} opt_time Time this log record was created if other than now.
* If 0, we use #goog.now.
* @param {number=} opt_sequenceNumber Sequence number of this log record. This
* should only be passed in when restoring a log record from persistence.
*/
goog.debug.LogRecord.prototype.reset = function(level, msg, loggerName,
opt_time, opt_sequenceNumber) {
if (goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS) {
this.sequenceNumber_ = typeof opt_sequenceNumber == 'number' ?
opt_sequenceNumber : goog.debug.LogRecord.nextSequenceNumber_++;
}
this.time_ = opt_time || goog.now();
this.level_ = level;
this.msg_ = msg;
this.loggerName_ = loggerName;
delete this.exception_;
delete this.exceptionText_;
};
/**
* Get the source Logger's name.
*
* @return {string} source logger name (may be null).
*/
goog.debug.LogRecord.prototype.getLoggerName = function() {
return this.loggerName_;
};
/**
* Get the exception that is part of the log record.
*
* @return {Object} the exception.
*/
goog.debug.LogRecord.prototype.getException = function() {
return this.exception_;
};
/**
* Set the exception that is part of the log record.
*
* @param {Object} exception the exception.
*/
goog.debug.LogRecord.prototype.setException = function(exception) {
this.exception_ = exception;
};
/**
* Get the exception text that is part of the log record.
*
* @return {?string} Exception text.
*/
goog.debug.LogRecord.prototype.getExceptionText = function() {
return this.exceptionText_;
};
/**
* Set the exception text that is part of the log record.
*
* @param {string} text The exception text.
*/
goog.debug.LogRecord.prototype.setExceptionText = function(text) {
this.exceptionText_ = text;
};
/**
* Get the source Logger's name.
*
* @param {string} loggerName source logger name (may be null).
*/
goog.debug.LogRecord.prototype.setLoggerName = function(loggerName) {
this.loggerName_ = loggerName;
};
/**
* Get the logging message level, for example Level.SEVERE.
* @return {goog.debug.Logger.Level} the logging message level.
*/
goog.debug.LogRecord.prototype.getLevel = function() {
return this.level_;
};
/**
* Set the logging message level, for example Level.SEVERE.
* @param {goog.debug.Logger.Level} level the logging message level.
*/
goog.debug.LogRecord.prototype.setLevel = function(level) {
this.level_ = level;
};
/**
* Get the "raw" log message, before localization or formatting.
*
* @return {string} the raw message string.
*/
goog.debug.LogRecord.prototype.getMessage = function() {
return this.msg_;
};
/**
* Set the "raw" log message, before localization or formatting.
*
* @param {string} msg the raw message string.
*/
goog.debug.LogRecord.prototype.setMessage = function(msg) {
this.msg_ = msg;
};
/**
* Get event time in milliseconds since 1970.
*
* @return {number} event time in millis since 1970.
*/
goog.debug.LogRecord.prototype.getMillis = function() {
return this.time_;
};
/**
* Set event time in milliseconds since 1970.
*
* @param {number} time event time in millis since 1970.
*/
goog.debug.LogRecord.prototype.setMillis = function(time) {
this.time_ = time;
};
/**
* Get the sequence number.
* <p>
* Sequence numbers are normally assigned in the LogRecord
* constructor, which assigns unique sequence numbers to
* each new LogRecord in increasing order.
* @return {number} the sequence number.
*/
goog.debug.LogRecord.prototype.getSequenceNumber = function() {
return this.sequenceNumber_;
};

View File

@@ -0,0 +1,121 @@
// Copyright 2011 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 Static methods for serializing and deserializing log
* messages. These methods are deliberately kept separate from logrecord.js
* and logger.js because they add dependencies on goog.json and goog.object.
*
*/
goog.provide('goog.debug.logRecordSerializer');
goog.require('goog.debug.LogRecord');
goog.require('goog.debug.Logger.Level');
goog.require('goog.json');
goog.require('goog.object');
/**
* Enumeration of object keys used when serializing a log message.
* @enum {string}
* @private
*/
goog.debug.logRecordSerializer.Param_ = {
TIME: 't',
LEVEL_NAME: 'ln',
LEVEL_VALUE: 'lv',
MSG: 'm',
LOGGER_NAME: 'n',
SEQUENCE_NUMBER: 's',
EXCEPTION: 'e',
EXCEPTION_TEXT: 'et'
};
/**
* Serializes a LogRecord to a JSON string. Note that any associated
* exception is likely to be lost.
* @param {goog.debug.LogRecord} record The record to serialize.
* @return {string} Serialized JSON string of the log message.
*/
goog.debug.logRecordSerializer.serialize = function(record) {
var param = goog.debug.logRecordSerializer.Param_;
return goog.json.serialize(goog.object.create(
param.TIME, record.getMillis(),
param.LEVEL_NAME, record.getLevel().name,
param.LEVEL_VALUE, record.getLevel().value,
param.MSG, record.getMessage(),
param.LOGGER_NAME, record.getLoggerName(),
param.SEQUENCE_NUMBER, record.getSequenceNumber(),
param.EXCEPTION, record.getException(),
param.EXCEPTION_TEXT, record.getExceptionText()));
};
/**
* Deserializes a JSON-serialized LogRecord.
* @param {string} s The JSON serialized record.
* @return {!goog.debug.LogRecord} The deserialized record.
*/
goog.debug.logRecordSerializer.parse = function(s) {
return goog.debug.logRecordSerializer.reconstitute_(goog.json.parse(s));
};
/**
* Deserializes a JSON-serialized LogRecord. Use this only if you're
* naive enough to blindly trust any JSON formatted input that comes
* your way.
* @param {string} s The JSON serialized record.
* @return {!goog.debug.LogRecord} The deserialized record.
*/
goog.debug.logRecordSerializer.unsafeParse = function(s) {
return goog.debug.logRecordSerializer.reconstitute_(goog.json.unsafeParse(s));
};
/**
* Common reconsitution method for for parse and unsafeParse.
* @param {Object} o The JSON object.
* @return {!goog.debug.LogRecord} The reconstituted record.
* @private
*/
goog.debug.logRecordSerializer.reconstitute_ = function(o) {
var param = goog.debug.logRecordSerializer.Param_;
var level = goog.debug.logRecordSerializer.getLevel_(
o[param.LEVEL_NAME], o[param.LEVEL_VALUE]);
var ret = new goog.debug.LogRecord(level, o[param.MSG],
o[param.LOGGER_NAME], o[param.TIME], o[param.SEQUENCE_NUMBER]);
ret.setException(o[param.EXCEPTION]);
ret.setExceptionText(o[param.EXCEPTION_TEXT]);
return ret;
};
/**
* @param {string} name The name of the log level to return.
* @param {number} value The numeric value of the log level to return.
* @return {goog.debug.Logger.Level} Returns a goog.debug.Logger.Level with
* the specified name and value. If the name and value match a predefined
* log level, that instance will be returned, otherwise a new one will be
* created.
* @private
*/
goog.debug.logRecordSerializer.getLevel_ = function(name, value) {
var level = goog.debug.Logger.Level.getPredefinedLevel(name);
return level && level.value == value ?
level : new goog.debug.Logger.Level(name, value);
};

View File

@@ -0,0 +1,178 @@
// Copyright 2011 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 JavaScript reflection tools. They should only be used for
* debugging non-compiled code or tests, because there is no guarantee that
* they work consistently in all browsers.
*
*/
goog.provide('goog.debug.reflect');
/**
* Maps the unique id of the known constructors to their full names.
* Initialized lazily.
* @type {Object.<number, string>}
* @private
*/
goog.debug.reflect.typeMap_ = null;
/**
* List of all known constructors. Initialized lazily.
* @type {Array.<!Function>}
* @private
*/
goog.debug.reflect.constructors_ = null;
/**
* Copy of {@code Object.prototype.toString} to use if it is overridden later.
* Although saving the original {@code toString} somewhat protects against
* third-party libraries which touch {@code Object.prototype}, the actual goal
* of this assignment is to allow overriding that method, thus more debug
* information can be exposed about objects.
* See {@link goog.debug.reflect.typeOf}.
* @private
*/
goog.debug.reflect.toString_ = Object.prototype.toString;
/**
* Registers a type which will be recognized by goog.debug.reflect.typeOf.
* @param {string} name Full name of the type.
* @param {!Function} ctor The constructor.
* @private
*/
goog.debug.reflect.registerType_ = function(name, ctor) {
goog.debug.reflect.constructors_.push(ctor);
goog.debug.reflect.typeMap_[goog.getUid(ctor)] = name;
};
/**
* Adds all known constructors to the type registry.
* @private
*/
goog.debug.reflect.init_ = function() {
if (goog.debug.reflect.typeMap_) {
return;
}
goog.debug.reflect.typeMap_ = {};
goog.debug.reflect.constructors_ = [];
var implicitNs = goog.getObjectByName('goog.implicitNamespaces_') || {};
for (var ns in implicitNs) {
if (implicitNs.hasOwnProperty(ns)) {
var nsObj = goog.getObjectByName(ns);
for (var name in nsObj) {
if (nsObj.hasOwnProperty(name) && goog.isFunction(nsObj[name])) {
goog.debug.reflect.registerType_(ns + '.' + name, nsObj[name]);
}
}
}
}
goog.debug.reflect.registerType_('Array', Array);
goog.debug.reflect.registerType_('Boolean', Boolean);
goog.debug.reflect.registerType_('Date', Date);
goog.debug.reflect.registerType_('Error', Error);
goog.debug.reflect.registerType_('Function', Function);
goog.debug.reflect.registerType_('Number', Number);
goog.debug.reflect.registerType_('Object', Object);
goog.debug.reflect.registerType_('String', String);
// The compiler gets upset if we alias regexp directly, because
// then it can't optimize regexps as well. Just be sneaky about it,
// because this is only for debugging.
goog.debug.reflect.registerType_('RegExp', goog.global['RegExp']);
};
/**
* Returns the name of a type of object.
* @param {!Function} classConstructor A object constructor to get the name of.
* @return {string|undefined} The string name of the class.
*/
goog.debug.reflect.className = function(classConstructor) {
goog.debug.reflect.init_();
if (goog.isDefAndNotNull(classConstructor)) {
return goog.debug.reflect.typeMap_[goog.getUid(classConstructor)];
} else {
return undefined;
}
};
/**
* Guesses the real type of the object, even if its {@code toString} method is
* overridden. Gives exact result for all goog.provided classes in non-compiled
* code, and some often used native classes in compiled code too. Not tested in
* multi-frame environment.
*
* Example use case to get better type information in the Watch tab of FireBug:
* <pre>
* Object.prototype.toString = function() {
* return goog.debug.reflect.typeOf(this);
* };
* </pre>
*
* @param {*} obj An arbitrary variable to get the type of.
* @return {string} The namespaced type of the argument or 'Object' if didn't
* manage to determine it. Warning: in IE7 ActiveX (including DOM) objects
* don't expose their type to JavaScript. Their {@code constructor}
* property is undefined and they are not even the instances of the
* {@code Object} type. This method will recognize them as 'ActiveXObject'.
*/
goog.debug.reflect.typeOf = function(obj) {
// Check primitive types.
if (!obj || goog.isNumber(obj) || goog.isString(obj) || goog.isBoolean(obj)) {
return goog.typeOf(obj);
}
// Check if the type is present in the registry.
goog.debug.reflect.init_();
if (obj.constructor) {
// Some DOM objects such as document don't have constructor in IE7.
var type = goog.debug.reflect.typeMap_[goog.getUid(obj.constructor)];
if (type) {
return type;
}
}
// In IE8 the internal 'class' property of ActiveXObjects is Object, but
// String(obj) tells their real type.
var isActiveXObject = goog.global.ActiveXObject &&
obj instanceof ActiveXObject;
var typeString = isActiveXObject ? String(obj) :
goog.debug.reflect.toString_.call(/** @type {Object} */ (obj));
var match = typeString.match(/^\[object (\w+)\]$/);
if (match) {
var name = match[1];
var ctor = goog.global[name];
try {
if (obj instanceof ctor) {
return name;
}
} catch (e) {
// instanceof may fail if the guessed name is not a real type.
}
}
// Fall back to Object or ActiveXObject.
return isActiveXObject ? 'ActiveXObject' : 'Object';
};

View File

@@ -0,0 +1,83 @@
// Copyright 2007 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 the goog.debug.RelativeTimeProvider class.
*
*/
goog.provide('goog.debug.RelativeTimeProvider');
/**
* A simple object to keep track of a timestamp considered the start of
* something. The main use is for the logger system to maintain a start time
* that is occasionally reset. For example, in Gmail, we reset this relative
* time at the start of a user action so that timings are offset from the
* beginning of the action. This class also provides a singleton as the default
* behavior for most use cases is to share the same start time.
*
* @constructor
*/
goog.debug.RelativeTimeProvider = function() {
/**
* The start time.
* @type {number}
* @private
*/
this.relativeTimeStart_ = goog.now();
};
/**
* Default instance.
* @type {goog.debug.RelativeTimeProvider}
* @private
*/
goog.debug.RelativeTimeProvider.defaultInstance_ =
new goog.debug.RelativeTimeProvider();
/**
* Sets the start time to the specified time.
* @param {number} timeStamp The start time.
*/
goog.debug.RelativeTimeProvider.prototype.set = function(timeStamp) {
this.relativeTimeStart_ = timeStamp;
};
/**
* Resets the start time to now.
*/
goog.debug.RelativeTimeProvider.prototype.reset = function() {
this.set(goog.now());
};
/**
* @return {number} The start time.
*/
goog.debug.RelativeTimeProvider.prototype.get = function() {
return this.relativeTimeStart_;
};
/**
* @return {goog.debug.RelativeTimeProvider} The default instance.
*/
goog.debug.RelativeTimeProvider.getDefaultInstance = function() {
return goog.debug.RelativeTimeProvider.defaultInstance_;
};

View File

@@ -0,0 +1,724 @@
// 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 Tracer class and associated classes.
*
* @see ../demos/tracer.html
*/
goog.provide('goog.debug.Trace');
goog.require('goog.array');
goog.require('goog.iter');
goog.require('goog.log');
goog.require('goog.structs.Map');
goog.require('goog.structs.SimplePool');
/**
* Class used for singleton goog.debug.Trace. Used for timing slow points in
* the code. Based on the java Tracer class but optimized for javascript.
* See com.google.common.tracing.Tracer.
* @constructor
* @private
*/
goog.debug.Trace_ = function() {
/**
* Events in order.
* @type {Array.<goog.debug.Trace_.Event_>}
* @private
*/
this.events_ = [];
/**
* Outstanding events that have started but haven't yet ended. The keys are
* numeric ids and the values are goog.debug.Trace_.Event_ objects.
* @type {goog.structs.Map}
* @private
*/
this.outstandingEvents_ = new goog.structs.Map();
/**
* Start time of the event trace
* @type {number}
* @private
*/
this.startTime_ = 0;
/**
* Cummulative overhead of calls to startTracer
* @type {number}
* @private
*/
this.tracerOverheadStart_ = 0;
/**
* Cummulative overhead of calls to endTracer
* @type {number}
* @private
*/
this.tracerOverheadEnd_ = 0;
/**
* Cummulative overhead of calls to addComment
* @type {number}
* @private
*/
this.tracerOverheadComment_ = 0;
/**
* Keeps stats on different types of tracers. The keys are strings and the
* values are goog.debug.Stat
* @type {goog.structs.Map}
* @private
*/
this.stats_ = new goog.structs.Map();
/**
* Total number of traces created in the trace.
* @type {number}
* @private
*/
this.tracerCount_ = 0;
/**
* Total number of comments created in the trace.
* @type {number}
* @private
*/
this.commentCount_ = 0;
/**
* Next id to use for the trace.
* @type {number}
* @private
*/
this.nextId_ = 1;
/**
* A pool for goog.debug.Trace_.Event_ objects so we don't keep creating and
* garbage collecting these (which is very expensive in IE6).
* @type {goog.structs.SimplePool}
* @private
*/
this.eventPool_ = new goog.structs.SimplePool(0, 4000);
this.eventPool_.createObject = function() {
return new goog.debug.Trace_.Event_();
};
/**
* A pool for goog.debug.Trace_.Stat_ objects so we don't keep creating and
* garbage collecting these (which is very expensive in IE6).
* @type {goog.structs.SimplePool}
* @private
*/
this.statPool_ = new goog.structs.SimplePool(0, 50);
this.statPool_.createObject = function() {
return new goog.debug.Trace_.Stat_();
};
var that = this;
this.idPool_ = new goog.structs.SimplePool(0, 2000);
// TODO(nicksantos): SimplePool is supposed to only return objects.
// Reconcile this so that we don't have to cast to number below.
this.idPool_.createObject = function() {
return String(that.nextId_++);
};
this.idPool_.disposeObject = function(obj) {};
/**
* Default threshold below which a tracer shouldn't be reported
* @type {number}
* @private
*/
this.defaultThreshold_ = 3;
};
/**
* Logger for the tracer
* @type {goog.log.Logger}
* @private
*/
goog.debug.Trace_.prototype.logger_ =
goog.log.getLogger('goog.debug.Trace');
/**
* Maximum size of the trace before we discard events
* @type {number}
*/
goog.debug.Trace_.prototype.MAX_TRACE_SIZE = 1000;
/**
* Event type supported by tracer
* @enum {number}
*/
goog.debug.Trace_.EventType = {
/**
* Start event type
*/
START: 0,
/**
* Stop event type
*/
STOP: 1,
/**
* Comment event type
*/
COMMENT: 2
};
/**
* Class to keep track of a stat of a single tracer type. Stores the count
* and cumulative time.
* @constructor
* @private
*/
goog.debug.Trace_.Stat_ = function() {
/**
* Number of tracers
* @type {number}
*/
this.count = 0;
/**
* Cumulative time of traces
* @type {number}
*/
this.time = 0;
/**
* Total number of allocations for this tracer type
* @type {number}
*/
this.varAlloc = 0;
};
/**
* @type {string|null|undefined}
*/
goog.debug.Trace_.Stat_.prototype.type;
/**
* @return {string} A string describing the tracer stat.
* @override
*/
goog.debug.Trace_.Stat_.prototype.toString = function() {
var sb = [];
sb.push(this.type, ' ', this.count, ' (', Math.round(this.time * 10) / 10,
' ms)');
if (this.varAlloc) {
sb.push(' [VarAlloc = ', this.varAlloc, ']');
}
return sb.join('');
};
/**
* Private class used to encapsulate a single event, either the start or stop
* of a tracer.
* @constructor
* @private
*/
goog.debug.Trace_.Event_ = function() {
// the fields are different for different events - see usage in code
};
/**
* @type {string|null|undefined}
*/
goog.debug.Trace_.Event_.prototype.type;
/**
* Returns a formatted string for the event.
* @param {number} startTime The start time of the trace to generate relative
* times.
* @param {number} prevTime The completion time of the previous event or -1.
* @param {string} indent Extra indent for the message
* if there was no previous event.
* @return {string} The formatted tracer string.
*/
goog.debug.Trace_.Event_.prototype.toTraceString = function(startTime, prevTime,
indent) {
var sb = [];
if (prevTime == -1) {
sb.push(' ');
} else {
sb.push(goog.debug.Trace_.longToPaddedString_(this.eventTime - prevTime));
}
sb.push(' ', goog.debug.Trace_.formatTime_(this.eventTime - startTime));
if (this.eventType == goog.debug.Trace_.EventType.START) {
sb.push(' Start ');
} else if (this.eventType == goog.debug.Trace_.EventType.STOP) {
sb.push(' Done ');
var delta = this.stopTime - this.startTime;
sb.push(goog.debug.Trace_.longToPaddedString_(delta), ' ms ');
} else {
sb.push(' Comment ');
}
sb.push(indent, this);
if (this.totalVarAlloc > 0) {
sb.push('[VarAlloc ', this.totalVarAlloc, '] ');
}
return sb.join('');
};
/**
* @return {string} A string describing the tracer event.
* @override
*/
goog.debug.Trace_.Event_.prototype.toString = function() {
if (this.type == null) {
return this.comment;
} else {
return '[' + this.type + '] ' + this.comment;
}
};
/**
* Add the ability to explicitly set the start time. This is useful for example
* for measuring initial load time where you can set a variable as soon as the
* main page of the app is loaded and then later call this function when the
* Tracer code has been loaded.
* @param {number} startTime The start time to set.
*/
goog.debug.Trace_.prototype.setStartTime = function(startTime) {
this.startTime_ = startTime;
};
/**
* Initializes and resets the current trace
* @param {number} defaultThreshold The default threshold below which the
* tracer output will be supressed. Can be overridden on a per-Tracer basis.
*/
goog.debug.Trace_.prototype.initCurrentTrace = function(defaultThreshold) {
this.reset(defaultThreshold);
};
/**
* Clears the current trace
*/
goog.debug.Trace_.prototype.clearCurrentTrace = function() {
this.reset(0);
};
/**
* Resets the trace.
* @param {number} defaultThreshold The default threshold below which the
* tracer output will be supressed. Can be overridden on a per-Tracer basis.
*/
goog.debug.Trace_.prototype.reset = function(defaultThreshold) {
this.defaultThreshold_ = defaultThreshold;
for (var i = 0; i < this.events_.length; i++) {
var id = /** @type {Object} */ (this.eventPool_).id;
if (id) {
this.idPool_.releaseObject(id);
}
this.eventPool_.releaseObject(this.events_[i]);
}
this.events_.length = 0;
this.outstandingEvents_.clear();
this.startTime_ = goog.debug.Trace_.now();
this.tracerOverheadStart_ = 0;
this.tracerOverheadEnd_ = 0;
this.tracerOverheadComment_ = 0;
this.tracerCount_ = 0;
this.commentCount_ = 0;
var keys = this.stats_.getKeys();
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var stat = this.stats_.get(key);
stat.count = 0;
stat.time = 0;
stat.varAlloc = 0;
this.statPool_.releaseObject(/** @type {Object} */ (stat));
}
this.stats_.clear();
};
/**
* Starts a tracer
* @param {string} comment A comment used to identify the tracer. Does not
* need to be unique.
* @param {string=} opt_type Type used to identify the tracer. If a Trace is
* given a type (the first argument to the constructor) and multiple Traces
* are done on that type then a "TOTAL line will be produced showing the
* total number of traces and the sum of the time
* ("TOTAL Database 2 (37 ms)" in our example). These traces should be
* mutually exclusive or else the sum won't make sense (the time will
* be double counted if the second starts before the first ends).
* @return {number} The identifier for the tracer that should be passed to the
* the stopTracer method.
*/
goog.debug.Trace_.prototype.startTracer = function(comment, opt_type) {
var tracerStartTime = goog.debug.Trace_.now();
var varAlloc = this.getTotalVarAlloc();
var outstandingEventCount = this.outstandingEvents_.getCount();
if (this.events_.length + outstandingEventCount > this.MAX_TRACE_SIZE) {
goog.log.warning(this.logger_,
'Giant thread trace. Clearing to avoid memory leak.');
// This is the more likely case. This usually means that we
// either forgot to clear the trace or else we are performing a
// very large number of events
if (this.events_.length > this.MAX_TRACE_SIZE / 2) {
for (var i = 0; i < this.events_.length; i++) {
var event = this.events_[i];
if (event.id) {
this.idPool_.releaseObject(event.id);
}
this.eventPool_.releaseObject(event);
}
this.events_.length = 0;
}
// This is less likely and probably indicates that a lot of traces
// aren't being closed. We want to avoid unnecessarily clearing
// this though in case the events do eventually finish.
if (outstandingEventCount > this.MAX_TRACE_SIZE / 2) {
this.outstandingEvents_.clear();
}
}
goog.debug.Logger.logToProfilers('Start : ' + comment);
var event = /** @type {goog.debug.Trace_.Event_} */ (
this.eventPool_.getObject());
event.totalVarAlloc = varAlloc;
event.eventType = goog.debug.Trace_.EventType.START;
event.id = Number(this.idPool_.getObject());
event.comment = comment;
event.type = opt_type;
this.events_.push(event);
this.outstandingEvents_.set(String(event.id), event);
this.tracerCount_++;
var now = goog.debug.Trace_.now();
event.startTime = event.eventTime = now;
this.tracerOverheadStart_ += now - tracerStartTime;
return event.id;
};
/**
* Stops a tracer
* @param {number|undefined|null} id The id of the tracer that is ending.
* @param {number=} opt_silenceThreshold Threshold below which the tracer is
* silenced.
* @return {?number} The elapsed time for the tracer or null if the tracer
* identitifer was not recognized.
*/
goog.debug.Trace_.prototype.stopTracer = function(id, opt_silenceThreshold) {
// this used to call goog.isDef(opt_silenceThreshold) but that causes an
// object allocation in IE for some reason (doh!). The following code doesn't
// cause an allocation
var now = goog.debug.Trace_.now();
var silenceThreshold;
if (opt_silenceThreshold === 0) {
silenceThreshold = 0;
} else if (opt_silenceThreshold) {
silenceThreshold = opt_silenceThreshold;
} else {
silenceThreshold = this.defaultThreshold_;
}
var startEvent = this.outstandingEvents_.get(String(id));
if (startEvent == null) {
return null;
}
this.outstandingEvents_.remove(String(id));
var stopEvent;
var elapsed = now - startEvent.startTime;
if (elapsed < silenceThreshold) {
var count = this.events_.length;
for (var i = count - 1; i >= 0; i--) {
var nextEvent = this.events_[i];
if (nextEvent == startEvent) {
this.events_.splice(i, 1);
this.idPool_.releaseObject(startEvent.id);
this.eventPool_.releaseObject(/** @type {Object} */ (startEvent));
break;
}
}
} else {
stopEvent = /** @type {goog.debug.Trace_.Event_} */ (
this.eventPool_.getObject());
stopEvent.eventType = goog.debug.Trace_.EventType.STOP;
stopEvent.startTime = startEvent.startTime;
stopEvent.comment = startEvent.comment;
stopEvent.type = startEvent.type;
stopEvent.stopTime = stopEvent.eventTime = now;
this.events_.push(stopEvent);
}
var type = startEvent.type;
var stat = null;
if (type) {
stat = this.getStat_(type);
stat.count++;
stat.time += elapsed;
}
if (stopEvent) {
goog.debug.Logger.logToProfilers('Stop : ' + stopEvent.comment);
stopEvent.totalVarAlloc = this.getTotalVarAlloc();
if (stat) {
stat.varAlloc += (stopEvent.totalVarAlloc - startEvent.totalVarAlloc);
}
}
var tracerFinishTime = goog.debug.Trace_.now();
this.tracerOverheadEnd_ += tracerFinishTime - now;
return elapsed;
};
/**
* Sets the ActiveX object that can be used to get GC tracing in IE6.
* @param {Object} gcTracer GCTracer ActiveX object.
*/
goog.debug.Trace_.prototype.setGcTracer = function(gcTracer) {
this.gcTracer_ = gcTracer;
};
/**
* Returns the total number of allocations since the GC stats were reset. Only
* works in IE.
* @return {number} The number of allocaitons or -1 if not supported.
*/
goog.debug.Trace_.prototype.getTotalVarAlloc = function() {
var gcTracer = this.gcTracer_;
// isTracing is defined on the ActiveX object.
if (gcTracer && gcTracer['isTracing']()) {
return gcTracer['totalVarAlloc'];
}
return -1;
};
/**
* Adds a comment to the trace. Makes it possible to see when a specific event
* happened in relation to the traces.
* @param {string} comment A comment that is inserted into the trace.
* @param {?string=} opt_type Type used to identify the tracer. If a comment is
* given a type and multiple comments are done on that type then a "TOTAL
* line will be produced showing the total number of comments of that type.
* @param {?number=} opt_timeStamp The timestamp to insert the comment. If not
* specified, the current time wil be used.
*/
goog.debug.Trace_.prototype.addComment = function(comment, opt_type,
opt_timeStamp) {
var now = goog.debug.Trace_.now();
var timeStamp = opt_timeStamp ? opt_timeStamp : now;
var eventComment = /** @type {goog.debug.Trace_.Event_} */ (
this.eventPool_.getObject());
eventComment.eventType = goog.debug.Trace_.EventType.COMMENT;
eventComment.eventTime = timeStamp;
eventComment.type = opt_type;
eventComment.comment = comment;
eventComment.totalVarAlloc = this.getTotalVarAlloc();
this.commentCount_++;
if (opt_timeStamp) {
var numEvents = this.events_.length;
for (var i = 0; i < numEvents; i++) {
var event = this.events_[i];
var eventTime = event.eventTime;
if (eventTime > timeStamp) {
goog.array.insertAt(this.events_, eventComment, i);
break;
}
}
if (i == numEvents) {
this.events_.push(eventComment);
}
} else {
this.events_.push(eventComment);
}
var type = eventComment.type;
if (type) {
var stat = this.getStat_(type);
stat.count++;
}
this.tracerOverheadComment_ += goog.debug.Trace_.now() - now;
};
/**
* Gets a stat object for a particular type. The stat object is created if it
* hasn't yet been.
* @param {string} type The type of stat.
* @return {goog.debug.Trace_.Stat_} The stat object.
* @private
*/
goog.debug.Trace_.prototype.getStat_ = function(type) {
var stat = this.stats_.get(type);
if (!stat) {
stat = /** @type {goog.debug.Trace_.Event_} */ (
this.statPool_.getObject());
stat.type = type;
this.stats_.set(type, stat);
}
return /** @type {goog.debug.Trace_.Stat_} */(stat);
};
/**
* Returns a formatted string for the current trace
* @return {string} A formatted string that shows the timings of the current
* trace.
*/
goog.debug.Trace_.prototype.getFormattedTrace = function() {
return this.toString();
};
/**
* Returns a formatted string that describes the thread trace.
* @return {string} A formatted string.
* @override
*/
goog.debug.Trace_.prototype.toString = function() {
var sb = [];
var etime = -1;
var indent = [];
for (var i = 0; i < this.events_.length; i++) {
var e = this.events_[i];
if (e.eventType == goog.debug.Trace_.EventType.STOP) {
indent.pop();
}
sb.push(' ', e.toTraceString(this.startTime_, etime, indent.join('')));
etime = e.eventTime;
sb.push('\n');
if (e.eventType == goog.debug.Trace_.EventType.START) {
indent.push('| ');
}
}
if (this.outstandingEvents_.getCount() != 0) {
var now = goog.debug.Trace_.now();
sb.push(' Unstopped timers:\n');
goog.iter.forEach(this.outstandingEvents_, function(startEvent) {
sb.push(' ', startEvent, ' (', now - startEvent.startTime,
' ms, started at ',
goog.debug.Trace_.formatTime_(startEvent.startTime),
')\n');
});
}
var statKeys = this.stats_.getKeys();
for (var i = 0; i < statKeys.length; i++) {
var stat = this.stats_.get(statKeys[i]);
if (stat.count > 1) {
sb.push(' TOTAL ', stat, '\n');
}
}
sb.push('Total tracers created ', this.tracerCount_, '\n',
'Total comments created ', this.commentCount_, '\n',
'Overhead start: ', this.tracerOverheadStart_, ' ms\n',
'Overhead end: ', this.tracerOverheadEnd_, ' ms\n',
'Overhead comment: ', this.tracerOverheadComment_, ' ms\n');
return sb.join('');
};
/**
* Converts 'v' to a string and pads it with up to 3 spaces for
* improved alignment. TODO there must be a better way
* @param {number} v A number.
* @return {string} A padded string.
* @private
*/
goog.debug.Trace_.longToPaddedString_ = function(v) {
v = Math.round(v);
// todo (pupius) - there should be a generic string in goog.string for this
var space = '';
if (v < 1000) space = ' ';
if (v < 100) space = ' ';
if (v < 10) space = ' ';
return space + v;
};
/**
* Return the sec.ms part of time (if time = "20:06:11.566", "11.566
* @param {number} time The time in MS.
* @return {string} A formatted string as sec.ms'.
* @private
*/
goog.debug.Trace_.formatTime_ = function(time) {
time = Math.round(time);
var sec = (time / 1000) % 60;
var ms = time % 1000;
// TODO their must be a nicer way to get zero padded integers
return String(100 + sec).substring(1, 3) + '.' +
String(1000 + ms).substring(1, 4);
};
/**
* Returns the current time. Done through a wrapper function so it can be
* overridden by application code. Gmail has an ActiveX extension that provides
* higher precision timing info.
* @return {number} The current time in milliseconds.
*/
goog.debug.Trace_.now = function() {
return goog.now();
};
/**
* Singleton trace object
* @type {goog.debug.Trace_}
*/
goog.debug.Trace = new goog.debug.Trace_();