/** | |
* Copyright 2013 Tim Down. | |
* | |
* 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. | |
*/ | |
if (!Array.prototype.shift) { | |
Array.prototype.shift = function() { | |
if (this.length > 0) { | |
var firstItem = this[0]; | |
for (var i = 0, len = this.length - 1; i < len; i++) { | |
this[i] = this[i + 1]; | |
} | |
this.length--; | |
return firstItem; | |
} | |
}; | |
} | |
var log4javascript; | |
(function() { | |
var newLine = "\r\n"; | |
function Log4JavaScript() {} | |
log4javascript = new Log4JavaScript(); | |
log4javascript.version = "1.4.6"; | |
log4javascript.edition = "log4javascript_lite"; | |
function getExceptionMessage(ex) { | |
if (ex.message) { | |
return ex.message; | |
} else if (ex.description) { | |
return ex.description; | |
} else { | |
return String(ex); | |
} | |
} | |
// Gets the portion of the URL after the last slash | |
function getUrlFileName(url) { | |
var lastSlashIndex = Math.max(url.lastIndexOf("/"), url.lastIndexOf("\\")); | |
return url.substr(lastSlashIndex + 1); | |
} | |
// Returns a nicely formatted representation of an error | |
function getExceptionStringRep(ex) { | |
if (ex) { | |
var exStr = "Exception: " + getExceptionMessage(ex); | |
try { | |
if (ex.lineNumber) { | |
exStr += " on line number " + ex.lineNumber; | |
} | |
if (ex.fileName) { | |
exStr += " in file " + getUrlFileName(ex.fileName); | |
} | |
} catch (localEx) { | |
} | |
if (showStackTraces && ex.stack) { | |
exStr += newLine + "Stack trace:" + newLine + ex.stack; | |
} | |
return exStr; | |
} | |
return null; | |
} | |
function isError(err) { | |
return (err instanceof Error); | |
} | |
function bool(obj) { | |
return Boolean(obj); | |
} | |
var enabled = (typeof log4javascript_disabled != "undefined") && | |
log4javascript_disabled ? false : true; | |
log4javascript.setEnabled = function(enable) { | |
enabled = bool(enable); | |
}; | |
log4javascript.isEnabled = function() { | |
return enabled; | |
}; | |
var showStackTraces = false; | |
log4javascript.setShowStackTraces = function(show) { | |
showStackTraces = bool(show); | |
}; | |
/* ---------------------------------------------------------------------- */ | |
// Levels | |
var Level = function(level, name) { | |
this.level = level; | |
this.name = name; | |
}; | |
Level.prototype = { | |
toString: function() { | |
return this.name; | |
}, | |
equals: function(level) { | |
return this.level == level.level; | |
}, | |
isGreaterOrEqual: function(level) { | |
return this.level >= level.level; | |
} | |
}; | |
Level.ALL = new Level(Number.MIN_VALUE, "ALL"); | |
Level.TRACE = new Level(10000, "TRACE"); | |
Level.DEBUG = new Level(20000, "DEBUG"); | |
Level.INFO = new Level(30000, "INFO"); | |
Level.WARN = new Level(40000, "WARN"); | |
Level.ERROR = new Level(50000, "ERROR"); | |
Level.FATAL = new Level(60000, "FATAL"); | |
Level.OFF = new Level(Number.MAX_VALUE, "OFF"); | |
log4javascript.Level = Level; | |
/* ---------------------------------------------------------------------- */ | |
// Appenders | |
function Appender() { | |
var getConsoleHtmlLines = function() { | |
return [ | |
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">', | |
'<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">', | |
' <head>', | |
' <title>log4javascript</title>', | |
' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />', | |
' <!-- Make IE8 behave like IE7, having gone to all the trouble of making IE work -->', | |
' <meta http-equiv="X-UA-Compatible" content="IE=7" />', | |
' <script type="text/javascript">', | |
' //<![CDATA[', | |
' var loggingEnabled = true;', | |
' var messagesBeforeDocLoaded = [];', | |
'', | |
' function toggleLoggingEnabled() {', | |
' setLoggingEnabled($("enableLogging").checked);', | |
' }', | |
'', | |
' function setLoggingEnabled(enable) {', | |
' loggingEnabled = enable;', | |
' }', | |
'', | |
' function scrollToLatestEntry() {', | |
' var l = getLogContainer();', | |
' if (typeof l.scrollTop != "undefined") {', | |
' var latestLogEntry = l.lastChild;', | |
' if (latestLogEntry) {', | |
' l.scrollTop = l.scrollHeight;', | |
' }', | |
' }', | |
' }', | |
'', | |
' function log(logLevel, formattedMessage) {', | |
' if (loggingEnabled) {', | |
' if (loaded) {', | |
' doLog(logLevel, formattedMessage);', | |
' } else {', | |
' messagesBeforeDocLoaded.push([logLevel, formattedMessage]);', | |
' }', | |
' }', | |
' }', | |
'', | |
' function doLog(logLevel, formattedMessage) {', | |
' var logEntry = document.createElement("div");', | |
' logEntry.appendChild(document.createTextNode(formattedMessage));', | |
' logEntry.className = "logentry " + logLevel.name;', | |
' getLogContainer().appendChild(logEntry);', | |
' scrollToLatestEntry();', | |
' }', | |
'', | |
' function mainPageReloaded() {', | |
' var separator = document.createElement("div");', | |
' separator.className = "separator";', | |
' separator.innerHTML = " ";', | |
' getLogContainer().appendChild(separator);', | |
' }', | |
'', | |
' var loaded = false;', | |
' var logLevels = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"];', | |
'', | |
' window.onload = function() {', | |
' setLogContainerHeight();', | |
' toggleLoggingEnabled();', | |
' for (var i = 0; i < messagesBeforeDocLoaded.length; i++) {', | |
' doLog(messagesBeforeDocLoaded[i][0], messagesBeforeDocLoaded[i][1]);', | |
' }', | |
' messagesBeforeDocLoaded = [];', | |
' loaded = true;', | |
'', | |
' // Workaround to make sure log div starts at the correct size', | |
' setTimeout(setLogContainerHeight, 20);', | |
' };', | |
'', | |
' function getLogContainer() {', | |
' return $("log");', | |
' }', | |
'', | |
' function clearLog() {', | |
' getLogContainer().innerHTML = "";', | |
' }', | |
'', | |
' /* ------------------------------------------------------------------------- */', | |
'', | |
' // Other utility functions', | |
'', | |
' // Syntax borrowed from Prototype library', | |
' function $(id) {', | |
' return document.getElementById(id);', | |
' }', | |
'', | |
' function getWindowHeight() {', | |
' if (window.innerHeight) {', | |
' return window.innerHeight;', | |
' } else if (document.documentElement && document.documentElement.clientHeight) {', | |
' return document.documentElement.clientHeight;', | |
' } else if (document.body) {', | |
' return document.body.clientHeight;', | |
' }', | |
' return 0;', | |
' }', | |
'', | |
' function getChromeHeight() {', | |
' return $("toolbar").offsetHeight;', | |
' }', | |
'', | |
' function setLogContainerHeight() {', | |
' var windowHeight = getWindowHeight();', | |
' $("body").style.height = getWindowHeight() + "px";', | |
' getLogContainer().style.height = "" +', | |
' Math.max(0, windowHeight - getChromeHeight()) + "px";', | |
' }', | |
'', | |
' window.onresize = function() {', | |
' setLogContainerHeight();', | |
' };', | |
'', | |
' //]]>', | |
' </script>', | |
' <style type="text/css">', | |
' body {', | |
' background-color: white;', | |
' color: black;', | |
' padding: 0;', | |
' margin: 0;', | |
' font-family: tahoma, verdana, arial, helvetica, sans-serif;', | |
' overflow: hidden;', | |
' }', | |
' ', | |
' div#toolbar {', | |
' border-top: solid #ffffff 1px;', | |
' border-bottom: solid #aca899 1px;', | |
' background-color: #f1efe7;', | |
' padding: 3px 5px;', | |
' font-size: 68.75%;', | |
' }', | |
'', | |
' div#toolbar input.button {', | |
' padding: 0 5px;', | |
' font-size: 100%;', | |
' }', | |
'', | |
' div#log {', | |
' font-family: Courier New, Courier;', | |
' font-size: 75%;', | |
' width: 100%;', | |
' overflow: auto;', | |
' clear: both;', | |
' }', | |
'', | |
' *.logentry {', | |
' overflow: visible;', | |
' white-space: pre;', | |
' }', | |
'', | |
' *.TRACE {', | |
' color: #666666;', | |
' }', | |
'', | |
' *.DEBUG {', | |
' color: green;', | |
' }', | |
'', | |
' *.INFO {', | |
' color: #000099;', | |
' }', | |
'', | |
' *.WARN {', | |
' color: #999900;', | |
' }', | |
'', | |
' *.ERROR {', | |
' color: red;', | |
' }', | |
'', | |
' *.FATAL {', | |
' color: #660066;', | |
' }', | |
'', | |
' div#log div.separator {', | |
' background-color: #cccccc;', | |
' margin: 5px 0;', | |
' line-height: 1px;', | |
' }', | |
' </style>', | |
' </head>', | |
'', | |
' <body id="body">', | |
' <div id="toolbar">', | |
' Options:', | |
' <input type="checkbox" id="enableLogging" onclick="toggleLoggingEnabled()" class="stateful" checked="checked" title="Enable/disable logging" /><label for="enableLogging" id="enableLoggingLabel">Enable logging</label>', | |
' <input type="button" id="clearButton" value="Clear" onclick="clearLog()" class="stateful button" title="Clear all log messages" />', | |
' <input type="button" id="closeButton" value="Close" onclick="window.close()" class="stateful button" title="Close the window" />', | |
' </div>', | |
' <div id="log" class="TRACE DEBUG INFO WARN ERROR FATAL"></div>', | |
' </body>', | |
'</html>' | |
]; | |
}; | |
var popUp = null; | |
var popUpsBlocked = false; | |
var popUpClosed = false; | |
var popUpLoaded = false; | |
var complainAboutPopUpBlocking = true; | |
var initialized = false; | |
var isSupported = true; | |
var width = 600; | |
var height = 400; | |
var focusPopUp = false; | |
var queuedLoggingEvents = new Array(); | |
function isLoaded(win) { | |
try { | |
return bool(win.loaded); | |
} catch (ex) { | |
return false; | |
} | |
} | |
function finalInit() { | |
popUpLoaded = true; | |
appendQueuedLoggingEvents(); | |
} | |
function writeHtml(doc) { | |
var lines = getConsoleHtmlLines(); | |
doc.open(); | |
for (var i = 0, len = lines.length; i < len; i++) { | |
doc.writeln(lines[i]); | |
} | |
doc.close(); | |
} | |
function pollConsoleWindow() { | |
function pollConsoleWindowLoaded() { | |
if (popUpLoaded) { | |
clearInterval(poll); | |
} else if (bool(popUp) && isLoaded(popUp)) { | |
clearInterval(poll); | |
finalInit(); | |
} | |
} | |
// Poll the pop-up since the onload event is not reliable | |
var poll = setInterval(pollConsoleWindowLoaded, 100); | |
} | |
function init() { | |
var windowProperties = "width=" + width + ",height=" + height + ",status,resizable"; | |
var windowName = "log4javascriptLitePopUp" + location.host.replace(/[^a-z0-9]/gi, "_"); | |
popUp = window.open("", windowName, windowProperties); | |
popUpClosed = false; | |
if (popUp) { | |
if (isLoaded(popUp)) { | |
popUp.mainPageReloaded(); | |
finalInit(); | |
} else { | |
writeHtml(popUp.document); | |
// Check if the pop-up window object is available | |
if (isLoaded(popUp)) { | |
finalInit(); | |
} else { | |
pollConsoleWindow(); | |
} | |
} | |
} else { | |
isSupported = false; | |
if (complainAboutPopUpBlocking) { | |
alert("log4javascript: pop-up windows appear to be blocked. Please unblock them to use pop-up logging."); | |
} | |
} | |
initialized = true; | |
} | |
function safeToAppend() { | |
if (!popUpsBlocked && !popUpClosed) { | |
if (popUp.closed) { | |
popUpClosed = true; | |
return false; | |
} | |
if (!popUpLoaded && popUp.loaded) { | |
popUpLoaded = true; | |
} | |
} | |
return !popUpsBlocked && popUpLoaded && !popUpClosed; | |
} | |
function padWithZeroes(num, len) { | |
var str = "" + num; | |
while (str.length < len) { | |
str = "0" + str; | |
} | |
return str; | |
} | |
function padWithSpaces(str, len) { | |
while (str.length < len) { | |
str += " "; | |
} | |
return str; | |
} | |
this.append = function(loggingEvent) { | |
if (!initialized) { | |
init(); | |
} | |
queuedLoggingEvents.push(loggingEvent); | |
if (safeToAppend()) { | |
appendQueuedLoggingEvents(); | |
} | |
}; | |
function appendQueuedLoggingEvents() { | |
if (safeToAppend()) { | |
while (queuedLoggingEvents.length > 0) { | |
var currentLoggingEvent = queuedLoggingEvents.shift(); | |
var date = currentLoggingEvent.timeStamp; | |
var formattedDate = padWithZeroes(date.getHours(), 2) + ":" + | |
padWithZeroes(date.getMinutes(), 2) + ":" + padWithZeroes(date.getSeconds(), 2); | |
var formattedMessage = formattedDate + " " + padWithSpaces(currentLoggingEvent.level.name, 5) + | |
" - " + currentLoggingEvent.getCombinedMessages(); | |
var throwableStringRep = currentLoggingEvent.getThrowableStrRep(); | |
if (throwableStringRep) { | |
formattedMessage += newLine + throwableStringRep; | |
} | |
popUp.log(currentLoggingEvent.level, formattedMessage); | |
} | |
if (focusPopUp) { | |
popUp.focus(); | |
} | |
} | |
} | |
} | |
log4javascript.Appender = Appender; | |
/* ---------------------------------------------------------------------- */ | |
// Loggers | |
function Logger() { | |
var appender = new Appender(); | |
var loggerLevel = Level.ALL; | |
this.log = function(level, params) { | |
if (enabled && level.isGreaterOrEqual(this.getLevel())) { | |
// Check whether last param is an exception | |
var exception; | |
var finalParamIndex = params.length - 1; | |
var lastParam = params[params.length - 1]; | |
if (params.length > 1 && isError(lastParam)) { | |
exception = lastParam; | |
finalParamIndex--; | |
} | |
// Construct genuine array for the params | |
var messages = []; | |
for (var i = 0; i <= finalParamIndex; i++) { | |
messages[i] = params[i]; | |
} | |
var loggingEvent = new LoggingEvent( | |
this, new Date(), level, messages, exception); | |
appender.append(loggingEvent); | |
} | |
}; | |
this.setLevel = function(level) { | |
loggerLevel = level; | |
}; | |
this.getLevel = function() { | |
return loggerLevel; | |
}; | |
} | |
Logger.prototype = { | |
trace: function() { | |
this.log(Level.TRACE, arguments); | |
}, | |
debug: function() { | |
this.log(Level.DEBUG, arguments); | |
}, | |
info: function() { | |
this.log(Level.INFO, arguments); | |
}, | |
warn: function() { | |
this.log(Level.WARN, arguments); | |
}, | |
error: function() { | |
this.log(Level.ERROR, arguments); | |
}, | |
fatal: function() { | |
this.log(Level.FATAL, arguments); | |
}, | |
isEnabledFor: function(level) { | |
return level.isGreaterOrEqual(this.getLevel()); | |
}, | |
isTraceEnabled: function() { | |
return this.isEnabledFor(Level.TRACE); | |
}, | |
isDebugEnabled: function() { | |
return this.isEnabledFor(Level.DEBUG); | |
}, | |
isInfoEnabled: function() { | |
return this.isEnabledFor(Level.INFO); | |
}, | |
isWarnEnabled: function() { | |
return this.isEnabledFor(Level.WARN); | |
}, | |
isErrorEnabled: function() { | |
return this.isEnabledFor(Level.ERROR); | |
}, | |
isFatalEnabled: function() { | |
return this.isEnabledFor(Level.FATAL); | |
} | |
}; | |
/* ---------------------------------------------------------------------- */ | |
// Logger access methods | |
var defaultLogger = null; | |
log4javascript.getDefaultLogger = function() { | |
if (!defaultLogger) { | |
defaultLogger = new Logger(); | |
} | |
return defaultLogger; | |
}; | |
log4javascript.getLogger = log4javascript.getDefaultLogger; | |
var nullLogger = null; | |
log4javascript.getNullLogger = function() { | |
if (!nullLogger) { | |
nullLogger = new Logger(); | |
nullLogger.setLevel(Level.OFF); | |
} | |
return nullLogger; | |
}; | |
/* ---------------------------------------------------------------------- */ | |
// Logging events | |
var LoggingEvent = function(logger, timeStamp, level, messages, | |
exception) { | |
this.logger = logger; | |
this.timeStamp = timeStamp; | |
this.level = level; | |
this.messages = messages; | |
this.exception = exception; | |
}; | |
LoggingEvent.prototype = { | |
getThrowableStrRep: function() { | |
return this.exception ? | |
getExceptionStringRep(this.exception) : ""; | |
}, | |
getCombinedMessages: function() { | |
return (this.messages.length === 1) ? this.messages[0] : | |
this.messages.join(newLine); | |
} | |
}; | |
log4javascript.LoggingEvent = LoggingEvent; | |
// Ensure that the log4javascript object is available in the window. This | |
// is necessary for log4javascript to be available in IE if loaded using | |
// Dojo's module system | |
window.log4javascript = log4javascript; | |
})(); |