/**
* Copyright 2006 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.
*/
/**
* log4javascript
*
* log4javascript is a logging framework for JavaScript based on log4j
* for Java. This file contains all core log4javascript code and is the only
* file required to use log4javascript. If you wish to disable log4javascript
* in production code, replace log4javascript.js with the stub file
* log4javascript_stub.js, included in the distribution. Also included in the
* distribution is log4javascript.js, a compressed but functionally
* identical version of this file.
*
* Author: Tim Down <tim@timdown.co.uk>
* Version: 1.3.1
* Last modified: 8/11/2006
* Website: http://www.timdown.co.uk/log4javascript
*/
/* ------------------------------------------------------------------------- */
// Array-related stuff
// Next three methods are solely for IE5, which is missing them
if (!Array.prototype.push) {
Array.prototype.push = function() {
for (var i = 0; i < arguments.length; i++){
this[this.length] = arguments[i];
}
return this.length;
};
}
if (!Array.prototype.shift) {
Array.prototype.shift = function() {
if (this.length > 0) {
var firstItem = this[0];
for (var i = 0; i < this.length - 1; i++) {
this[i] = this[i + 1];
}
this.length = this.length - 1;
return firstItem;
}
};
}
if (!Array.prototype.splice) {
Array.prototype.splice = function(startIndex, deleteCount) {
var itemsAfterDeleted = this.slice(startIndex + deleteCount);
var itemsDeleted = this.slice(startIndex, startIndex + deleteCount);
this.length = startIndex;
// Copy the arguments into a proper Array object
var argumentsArray = [];
for (var i = 0; i < arguments.length; i++) {
argumentsArray[i] = arguments[i];
}
var itemsToAppend = (argumentsArray.length > 2) ?
itemsAfterDeleted = argumentsArray.slice(2).concat(itemsAfterDeleted) : itemsAfterDeleted;
for (i = 0; i < itemsToAppend.length; i++) {
this.push(itemsToAppend[i]);
}
return itemsDeleted;
};
}
/* ------------------------------------------------------------------------- */
var log4javascript;
var SimpleDateFormat;
(function() {
function isUndefined(obj) {
return typeof obj == "undefined";
}
// Date-related stuff
(function() {
var regex = /('[^']*')|(G+|y+|M+|w+|W+|D+|d+|F+|E+|a+|H+|k+|K+|h+|m+|s+|S+|Z+)|([a-zA-Z]+)|([^a-zA-Z']+)/;
var monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
var TEXT2 = 0, TEXT3 = 1, NUMBER = 2, YEAR = 3, MONTH = 4, TIMEZONE = 5;
var types = {
G : TEXT2,
y : YEAR,
Y : YEAR,
M : MONTH,
w : NUMBER,
W : NUMBER,
D : NUMBER,
d : NUMBER,
F : NUMBER,
E : TEXT3,
a : TEXT2,
H : NUMBER,
k : NUMBER,
K : NUMBER,
h : NUMBER,
m : NUMBER,
s : NUMBER,
S : NUMBER,
Z : TIMEZONE
};
var ONE_DAY = 24 * 60 * 60 * 1000;
var ONE_WEEK = 7 * ONE_DAY;
var DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK = 1;
Date.prototype.getDifference = function(date) {
return this.getTime() - date.getTime();
};
Date.prototype.isBefore = function(d) {
return this.getTime() < d.getTime();
};
Date.prototype.getWeekInYear = function(minimalDaysInFirstWeek) {
if (isUndefined(this.minimalDaysInFirstWeek)) {
minimalDaysInFirstWeek = DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK;
}
var previousSunday = new Date(this.getTime() - this.getDay() * ONE_DAY);
previousSunday = new Date(previousSunday.getFullYear(), previousSunday.getMonth(), previousSunday.getDate());
var startOfYear = new Date(this.getFullYear(), 0, 1);
var numberOfSundays = previousSunday.isBefore(startOfYear) ?
0 : 1 + Math.floor((previousSunday.getTime() - startOfYear.getTime()) / ONE_WEEK);
var numberOfDaysInFirstWeek = 7 - startOfYear.getDay();
var weekInYear = numberOfSundays;
if (numberOfDaysInFirstWeek >= minimalDaysInFirstWeek) {
weekInYear++;
}
return weekInYear;
};
Date.prototype.getWeekInMonth = function(minimalDaysInFirstWeek) {
if (isUndefined(this.minimalDaysInFirstWeek)) {
minimalDaysInFirstWeek = DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK;
}
var previousSunday = new Date(this.getTime() - this.getDay() * ONE_DAY);
previousSunday = new Date(previousSunday.getFullYear(), previousSunday.getMonth(), previousSunday.getDate());
var startOfMonth = new Date(this.getFullYear(), this.getMonth(), 1);
var numberOfSundays = previousSunday.isBefore(startOfMonth) ?
0 : 1 + Math.floor((previousSunday.getTime() - startOfMonth.getTime()) / ONE_WEEK);
var numberOfDaysInFirstWeek = 7 - startOfMonth.getDay();
var weekInMonth = numberOfSundays;
if (numberOfDaysInFirstWeek >= minimalDaysInFirstWeek) {
weekInMonth++;
}
return weekInMonth;
};
Date.prototype.getDayInYear = function() {
var startOfYear = new Date(this.getFullYear(), 0, 1);
return 1 + Math.floor((this.getTime() - startOfYear.getTime()) / ONE_DAY);
};
/* ----------------------------------------------------------------- */
SimpleDateFormat = function(formatString) {
this.formatString = formatString;
};
/**
* Sets the minimum number of days in a week in order for that week to
* be considered as belonging to a particular month or year
*/
SimpleDateFormat.prototype.setMinimalDaysInFirstWeek = function(days) {
this.minimalDaysInFirstWeek = days;
};
SimpleDateFormat.prototype.getMinimalDaysInFirstWeek = function(days) {
return isUndefined(this.minimalDaysInFirstWeek) ?
DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK : this.minimalDaysInFirstWeek;
};
SimpleDateFormat.prototype.format = function(date) {
var formattedString = "";
var result;
var padWithZeroes = function(str, len) {
while (str.length < len) {
str = "0" + str;
}
return str;
};
var formatText = function(data, numberOfLetters, minLength) {
return (numberOfLetters >= 4) ? data : data.substr(0, Math.max(minLength, numberOfLetters));
};
var formatNumber = function(data, numberOfLetters) {
var dataString = "" + data;
// Pad with 0s as necessary
return padWithZeroes(dataString, numberOfLetters);
};
var searchString = this.formatString;
while ((result = regex.exec(searchString))) {
var matchedString = result[0];
var quotedString = result[1];
var patternLetters = result[2];
var otherLetters = result[3];
var otherCharacters = result[4];
// If the pattern matched is quoted string, output the text between the quotes
if (quotedString) {
if (quotedString == "''") {
formattedString += "'";
} else {
formattedString += quotedString.substring(1, quotedString.length - 1);
}
} else if (otherLetters) {
// Swallow non-pattern letters by doing nothing here
} else if (otherCharacters) {
// Simply output other characters
formattedString += otherCharacters;
} else if (patternLetters) {
// Replace pattern letters
var patternLetter = patternLetters.charAt(0);
var numberOfLetters = patternLetters.length;
var rawData = "";
switch (patternLetter) {
case "G":
rawData = "AD";
break;
case "y":
rawData = date.getFullYear();
break;
case "M":
rawData = date.getMonth();
break;
case "w":
rawData = date.getWeekInYear(this.getMinimalDaysInFirstWeek());
break;
case "W":
rawData = date.getWeekInMonth(this.getMinimalDaysInFirstWeek());
break;
case "D":
rawData = date.getDayInYear();
break;
case "d":
rawData = date.getDate();
break;
case "F":
rawData = 1 + Math.floor((date.getDate() - 1) / 7);
break;
case "E":
rawData = dayNames[date.getDay()];
break;
case "a":
rawData = (date.getHours() >= 12) ? "PM" : "AM";
break;
case "H":
rawData = date.getHours();
break;
case "k":
rawData = 1 + date.getHours();
break;
case "K":
rawData = date.getHours() % 12;
break;
case "h":
rawData = 1 + (date.getHours() % 12);
break;
case "m":
rawData = date.getMinutes();
break;
case "s":
rawData = date.getSeconds();
break;
case "S":
rawData = date.getMilliseconds();
break;
case "Z":
rawData = date.getTimezoneOffset(); // This returns the number of minutes since GMT was this time.
break;
}
// Format the raw data depending on the type
switch (types[patternLetter]) {
case TEXT2:
formattedString += formatText(rawData, numberOfLetters, 2);
break;
case TEXT3:
formattedString += formatText(rawData, numberOfLetters, 3);
break;
case NUMBER:
formattedString += formatNumber(rawData, numberOfLetters);
break;
case YEAR:
if (numberOfLetters <= 2) {
// Output a 2-digit year
var dataString = "" + rawData;
formattedString += dataString.substr(2, 2);
} else {
formattedString += formatNumber(rawData, numberOfLetters);
}
break;
case MONTH:
if (numberOfLetters >= 3) {
formattedString += formatText(monthNames[rawData], numberOfLetters, numberOfLetters);
} else {
// NB. Months returned by getMonth are zero-based
formattedString += formatNumber(rawData + 1, numberOfLetters);
}
break;
case TIMEZONE:
var isPositive = (rawData > 0);
// The following line looks like a mistake but isn't
// because of the way getTimezoneOffset measures.
var prefix = isPositive ? "-" : "+";
var absData = Math.abs(rawData);
// Hours
var hours = "" + Math.floor(absData / 60);
hours = padWithZeroes(hours, 2);
// Minutes
var minutes = "" + (absData % 60);
minutes = padWithZeroes(minutes, 2);
formattedString += prefix + hours + minutes;
break;
}
}
searchString = searchString.substr(result.index + result[0].length);
}
return formattedString;
};
})();
/* ------------------------------------------------------------------------- */
var applicationStartDate = new Date();
var uniqueId = "log4javascript_" + applicationStartDate.getTime() + "_" +
Math.floor(Math.random() * 100000000);
var emptyFunction = function() {};
var newLine = "\r\n";
// Create logging object; this will be assigned properties and returned
log4javascript = {};
log4javascript.version = "1.3.1";
// Returns a nicely formatted representation of an error
function getExceptionStringRep(ex) {
if (ex) {
var exStr = "Exception: ";
if (ex.message) {
exStr += ex.message;
} else if (ex.description) {
exStr += ex.description;
}
if (ex.lineNumber) {
exStr += " on line number " + ex.lineNumber;
}
if (ex.fileName) {
exStr += " in file " + ex.fileName;
}
if (showStackTraces && ex.stack) {
exStr += newLine + "Stack trace:" + newLine + ex.stack;
}
return exStr;
}
return null;
}
function formatObjectExpansion(obj, depth, indentation) {
var i, output, childDepth, childIndentation, childLines;
if ((obj instanceof Array) && depth > 0) {
if (!indentation) {
indentation = "";
}
output = "[" + newLine;
childDepth = depth - 1;
childIndentation = indentation + " ";
childLines = [];
for (i = 0; i < obj.length; i++) {
childLines.push(childIndentation + formatObjectExpansion(obj[i], childDepth, childIndentation));
}
output += childLines.join("," + newLine) + newLine + indentation + "]";
return output;
} else if (typeof obj == "object" && depth > 0) {
if (!indentation) {
indentation = "";
}
output = "" + "{" + newLine;
childDepth = depth - 1;
childIndentation = indentation + " ";
childLines = [];
for (i in obj) {
childLines.push(childIndentation + i + ": " + formatObjectExpansion(obj[i], childDepth, childIndentation));
}
output += childLines.join("," + newLine) + newLine + indentation + "}";
return output;
} else if (typeof obj == "string") {
return obj;
} else {
return obj.toString();
}
}
function escapeNewLines(str) {
return str.replace(/\r\n|\r|\n/g, "\\r\\n");
}
function urlEncode(str) {
return escape(str).replace(/\+/g, "%2B").replace(/"/g, "%22").replace(/'/g, "%27").replace(/\//g, "%2F");
}
function bool(obj) {
return Boolean(obj);
}
function array_remove(arr, val) {
var index = -1;
for (var i = 0; i < arr.length; i++) {
if (arr[i] === val) {
index = i;
break;
}
}
if (index >= 0) {
arr.splice(index, 1);
return true;
} else {
return false;
}
}
function extractBooleanFromParam(param, defaultValue) {
if (isUndefined(param)) {
return defaultValue;
} else {
return bool(param);
}
}
function extractStringFromParam(param, defaultValue) {
if (isUndefined(param)) {
return defaultValue;
} else {
return String(param);
}
}
function extractIntFromParam(param, defaultValue) {
if (isUndefined(param)) {
return defaultValue;
} else {
try {
var value = parseInt(param, 10);
return isNaN(value) ? defaultValue : value;
} catch (ex) {
logLog.warn("Invalid int param " + param, ex);
return defaultValue;
}
}
}
function extractFunctionFromParam(param, defaultValue) {
if (typeof param == "function") {
return param;
} else {
return defaultValue;
}
}
/* --------------------------------------------------------------------- */
// Simple logging for log4javascript itself
var logLog = {
quietMode: false,
setQuietMode: function(quietMode) {
this.quietMode = bool(quietMode);
},
numberOfErrors: 0,
alertAllErrors: false,
setAlertAllErrors: function(alertAllErrors) {
this.alertAllErrors = alertAllErrors;
},
debug: function(message, exception) {
},
warn: function(message, exception) {
},
error: function(message, exception) {
if (++this.numberOfErrors == 1 || this.alertAllErrors) {
if (!this.quietMode) {
var alertMessage = "log4javascript error: " + message;
if (exception) {
alertMessage += newLine + newLine + "Original error: " + getExceptionStringRep(exception);
}
alert(alertMessage);
}
}
}
};
log4javascript.logLog = logLog;
/* --------------------------------------------------------------------- */
var errorListeners = [];
log4javascript.addErrorListener = function(listener) {
if (typeof listener == "function") {
errorListeners.push(listener);
} else {
handleError("addErrorListener: listener supplied was not a function");
}
};
log4javascript.removeErrorListener = function(listener) {
array_remove(errorListeners, listener);
};
function handleError(message, exception) {
logLog.error(message, exception);
for (var i = 0; i < errorListeners.length; i++) {
errorListeners[i](message, exception);
}
}
/* --------------------------------------------------------------------- */
var enabled = (typeof log4javascript_disabled != "undefined") &&
log4javascript_disabled ? false : true;
log4javascript.setEnabled = function(enable) {
enabled = bool(enable);
};
log4javascript.isEnabled = function() {
return enabled;
};
// This evaluates the given expression in the current scope, thus allowing
// scripts to access private variables. Particularly useful for testing
log4javascript.evalInScope = function(expr) {
return eval(expr);
};
var showStackTraces = false;
log4javascript.setShowStackTraces = function(show) {
showStackTraces = bool(show);
};
/* --------------------------------------------------------------------- */
function Logger(name) {
this.name = name;
var appenders = [];
var loggerLevel = Level.DEBUG;
// Create methods that use the appenders variable in this scope
this.addAppender = function(appender) {
if (appender instanceof log4javascript.Appender) {
appenders.push(appender);
} else {
handleError("Logger.addAppender: appender supplied is not a subclass of Appender");
}
};
this.removeAppender = function(appender) {
array_remove(appenders, appender);
};
this.removeAllAppenders = function(appender) {
appenders.length = 0;
};
this.log = function(level, message, exception) {
if (level.isGreaterOrEqual(loggerLevel)) {
var loggingEvent = new LoggingEvent(
this, new Date(), level, message, exception);
for (var i = 0; i < appenders.length; i++) {
appenders[i].doAppend(loggingEvent);
}
}
};
this.setLevel = function(level) {
loggerLevel = level;
};
this.getLevel = function() {
return loggerLevel;
};
}
Logger.prototype = {
trace: function(message, exception) {
this.log(Level.TRACE, message, exception);
},
debug: function(message, exception) {
this.log(Level.DEBUG, message, exception);
},
info: function(message, exception) {
this.log(Level.INFO, message, exception);
},
warn: function(message, exception) {
this.log(Level.WARN, message, exception);
},
error: function(message, exception) {
this.log(Level.ERROR, message, exception);
},
fatal: function(message, exception) {
this.log(Level.FATAL, message, exception);
}
};
Logger.prototype.trace.isEntryPoint = true;
Logger.prototype.debug.isEntryPoint = true;
Logger.prototype.info.isEntryPoint = true;
Logger.prototype.warn.isEntryPoint = true;
Logger.prototype.error.isEntryPoint = true;
Logger.prototype.fatal.isEntryPoint = true;
/* --------------------------------------------------------------------- */
// Hashtable of loggers keyed by logger name
var loggers = {};
log4javascript.getLogger = function(loggerName) {
// Use default logger if loggerName is not specified or invalid
if (!(typeof loggerName == "string")) {
loggerName = "[anonymous]";
}
// Create the logger for this name if it doesn't already exist
if (!loggers[loggerName]) {
loggers[loggerName] = new Logger(loggerName);
}
return loggers[loggerName];
};
var defaultLogger = null;
log4javascript.getDefaultLogger = function() {
if (!defaultLogger) {
defaultLogger = log4javascript.getLogger("[default]");
var a = new log4javascript.PopUpAppender();
defaultLogger.addAppender(a);
}
return defaultLogger;
};
var nullLogger = null;
log4javascript.getNullLogger = function() {
if (!nullLogger) {
nullLogger = log4javascript.getLogger("[null]");
}
return nullLogger;
};
/* --------------------------------------------------------------------- */
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;
/* --------------------------------------------------------------------- */
var LoggingEvent = function(logger, timeStamp, level, message,
exception) {
this.logger = logger;
this.timeStamp = timeStamp;
this.timeStampInSeconds = Math.floor(timeStamp.getTime() / 1000);
this.level = level;
this.message = message;
this.exception = exception;
};
LoggingEvent.prototype.getThrowableStrRep = function() {
return this.exception ?
getExceptionStringRep(this.exception) : "";
};
log4javascript.LoggingEvent = LoggingEvent;
/* --------------------------------------------------------------------- */
// Layout "abstract class"
var Layout = function() {
};
Layout.prototype = {
defaults: {
loggerKey: "logger",
timeStampKey: "timestamp",
levelKey: "level",
messageKey: "message",
exceptionKey: "exception",
urlKey: "url"
},
loggerKey: "logger",
timeStampKey: "timestamp",
levelKey: "level",
messageKey: "message",
exceptionKey: "exception",
urlKey: "url",
batchHeader: "",
batchFooter: "",
batchSeparator: "",
format: function(loggingEvent) {
handleError("Layout.format: layout supplied has no format() method");
},
ignoresThrowable: function() {
handleError("Layout.ignoresThrowable: layout supplied has no ignoresThrowable() method");
},
getContentType: function() {
return "text/plain";
},
allowBatching: function() {
return true;
},
getDataValues: function(loggingEvent) {
var dataValues = [
[this.loggerKey, loggingEvent.logger.name],
[this.timeStampKey, loggingEvent.timeStampInSeconds],
[this.levelKey, loggingEvent.level.name],
[this.urlKey, window.location.href],
[this.messageKey, loggingEvent.message]
];
if (loggingEvent.exception) {
dataValues.push([this.exceptionKey, getExceptionStringRep(loggingEvent.exception)]);
}
if (this.hasCustomFields()) {
for (var i = 0; i < this.customFields.length; i++) {
dataValues.push([this.customFields[i].name, this.customFields[i].value]);
}
}
return dataValues;
},
setKeys: function(loggerKey, timeStampKey, levelKey, messageKey,
exceptionKey, urlKey) {
this.loggerKey = extractStringFromParam(loggerKey, this.defaults.loggerKey);
this.timeStampKey = extractStringFromParam(timeStampKey, this.defaults.timeStampKey);
this.levelKey = extractStringFromParam(levelKey, this.defaults.levelKey);
this.messageKey = extractStringFromParam(messageKey, this.defaults.messageKey);
this.exceptionKey = extractStringFromParam(exceptionKey, this.defaults.exceptionKey);
this.urlKey = extractStringFromParam(urlKey, this.defaults.urlKey);
},
setCustomField: function(name, value) {
var fieldUpdated = false;
for (var i = 0; i < this.customFields.length; i++) {
if (this.customFields[i].name === name) {
this.customFields[i].value = value;
fieldUpdated = true;
}
}
if (!fieldUpdated) {
this.customFields.push({"name": name, "value": value});
}
},
hasCustomFields: function() {
return (this.customFields.length > 0);
}
};
log4javascript.Layout = Layout;
/* --------------------------------------------------------------------- */
// SimpleLayout
var SimpleLayout = function() {
this.customFields = [];
};
SimpleLayout.prototype = new Layout();
SimpleLayout.prototype.format = function(loggingEvent) {
return loggingEvent.level.name + " - " + loggingEvent.message;
};
SimpleLayout.prototype.ignoresThrowable = function(loggingEvent) {
return true;
};
log4javascript.SimpleLayout = SimpleLayout;
/* --------------------------------------------------------------------- */
// NullLayout
var NullLayout = function() {
this.customFields = [];
};
NullLayout.prototype = new Layout();
NullLayout.prototype.format = function(loggingEvent) {
return loggingEvent.message;
};
NullLayout.prototype.ignoresThrowable = function(loggingEvent) {
return true;
};
log4javascript.NullLayout = NullLayout;
/* --------------------------------------------------------------------- */
// XmlLayout
var XmlLayout = function() {
this.customFields = [];
};
XmlLayout.prototype = new Layout();
XmlLayout.prototype.getContentType = function() {
return "text/xml";
};
XmlLayout.prototype.escapeCdata = function(str) {
return str.replace(/\]\]>/, "]]>]]><![CDATA[");
};
XmlLayout.prototype.format = function(loggingEvent) {
var str = "<log4javascript:event logger=\"" + loggingEvent.logger.name +
"\" timestamp=\"" + loggingEvent.timeStampInSeconds +
"\" level=\"" + loggingEvent.level.name +
"\">" + newLine + "<log4javascript:message><![CDATA[" +
this.escapeCdata(loggingEvent.message.toString()) +
"]]></log4javascript:message>" + newLine;
if (this.hasCustomFields()) {
for (var i = 0; i < this.customFields.length; i++) {
str += "<log4javascript:customfield name=\"" +
this.customFields[i].name + "\"><![CDATA[" +
this.customFields[i].value.toString() +
"]]></log4javascript:customfield>" + newLine;
}
}
if (loggingEvent.exception) {
str += "<log4javascript:exception><![CDATA[" +
getExceptionStringRep(loggingEvent.exception) +
"]]></log4javascript:exception>" + newLine;
}
str += "</log4javascript:event>" + newLine + newLine;
return str;
};
XmlLayout.prototype.ignoresThrowable = function(loggingEvent) {
return false;
};
log4javascript.XmlLayout = XmlLayout;
/* --------------------------------------------------------------------- */
// JsonLayout
var JsonLayout = function(readable, loggerKey, timeStampKey,
levelKey, messageKey, exceptionKey, urlKey) {
this.readable = bool(readable);
this.batchHeader = this.readable ? "[" + newLine : "[";
this.batchFooter = this.readable ? "]" + newLine : "]";
this.batchSeparator = this.readable ? "," + newLine : ",";
this.setKeys(loggerKey, timeStampKey, levelKey, messageKey,
exceptionKey, urlKey);
this.propertySeparator = this.readable ? ", " : ",";
this.colon = this.readable ? ": " : ":";
this.customFields = [];
};
JsonLayout.prototype = new Layout();
JsonLayout.prototype.setReadable = function(readable) {
this.readable = bool(readable);
};
JsonLayout.prototype.isReadable = function() {
return this.readable;
};
JsonLayout.prototype.format = function(loggingEvent) {
var dataValues = this.getDataValues(loggingEvent);
var str = "{";
if (this.readable) {
str += newLine;
}
for (var i = 0; i < dataValues.length; i++) {
if (this.readable) {
str += "\t";
}
// Check the type of the data value to decide whether quotation marks
// are required
var valType = typeof dataValues[i][1];
var val = (valType != "number" && valType != "boolean") ?
"\"" + escapeNewLines(dataValues[i][1].toString().replace(/\"/g, "\\\"")) + "\"" :
dataValues[i][1];
str += "\"" + dataValues[i][0] + "\"" + this.colon + val;
if (i < dataValues.length - 1) {
str += this.propertySeparator;
}
if (this.readable) {
str += newLine;
}
}
str += "}";
if (this.readable) {
str += newLine;
}
return str;
};
JsonLayout.prototype.ignoresThrowable = function(loggingEvent) {
return false;
};
log4javascript.JsonLayout = JsonLayout;
/* --------------------------------------------------------------------- */
// HttpPostDataLayout
var HttpPostDataLayout = function(loggerKey, timeStampKey,
levelKey, messageKey, exceptionKey, urlKey) {
this.setKeys(loggerKey, timeStampKey, levelKey, messageKey,
exceptionKey, urlKey);
this.customFields = [];
};
HttpPostDataLayout.prototype = new Layout();
// Disable batching
HttpPostDataLayout.prototype.allowBatching = function() {
return false;
};
HttpPostDataLayout.prototype.format = function(loggingEvent) {
var dataValues = this.getDataValues(loggingEvent);
var queryBits = [];
for (var i = 0; i < dataValues.length; i++) {
queryBits.push(urlEncode(dataValues[i][0]) + "=" + urlEncode(dataValues[i][1]));
}
return queryBits.join("&");
};
HttpPostDataLayout.prototype.ignoresThrowable = function(loggingEvent) {
return false;
};
log4javascript.HttpPostDataLayout = HttpPostDataLayout;
/* --------------------------------------------------------------------- */
// PatternLayout
var PatternLayout = function(pattern) {
if (pattern) {
this.pattern = pattern;
} else {
this.pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
}
this.customFields = [];
};
PatternLayout.TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n";
PatternLayout.DEFAULT_CONVERSION_PATTERN = "%m%n";
PatternLayout.ISO8601_DATEFORMAT = "yyyy-MM-dd HH:mm:ss,SSS";
PatternLayout.DATETIME_DATEFORMAT = "dd MMM yyyy HH:mm:ss,SSS";
PatternLayout.ABSOLUTETIME_DATEFORMAT = "HH:mm:ss,SSS";
PatternLayout.prototype = new Layout();
PatternLayout.prototype.format = function(loggingEvent) {
var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([cdfmMnpr%])(\{([^\}]+)\})?|([^%]+)/;
var formattedString = "";
var result;
var searchString = this.pattern;
// Cannot use regex global flag since it doesn't work with exec in IE5
while ((result = regex.exec(searchString))) {
var matchedString = result[0];
var padding = result[1];
var truncation = result[2];
var conversionCharacter = result[3];
var specifier = result[5];
var text = result[6];
// Check if the pattern matched was just normal text
if (text) {
formattedString += "" + text;
} else {
// Create a raw replacement string based on the conversion
// character and specifier
var replacement = "";
switch(conversionCharacter) {
case "c": // Logger name
var loggerName = loggingEvent.logger.name;
if (specifier) {
var precision = parseInt(specifier, 10);
var loggerNameBits = loggingEvent.logger.name.split(".");
if (precision >= loggerNameBits.length) {
replacement = loggerName;
} else {
replacement = loggerNameBits.slice(loggerNameBits.length - precision).join(".");
}
} else {
replacement = loggerName;
}
break;
case "d": // Date
var dateFormat = PatternLayout.ISO8601_DATEFORMAT;
if (specifier) {
dateFormat = specifier;
// Pick up special cases
if (dateFormat == "ISO8601") {
dateFormat = PatternLayout.ISO8601_DATEFORMAT;
} else if (dateFormat == "ABSOLUTE") {
dateFormat = PatternLayout.ABSOLUTETIME_DATEFORMAT;
} else if (dateFormat == "DATE") {
dateFormat = PatternLayout.DATETIME_DATEFORMAT;
}
}
// Format the date
replacement = (new SimpleDateFormat(dateFormat)).format(loggingEvent.timeStamp);
break;
case "f": // Custom field
if (this.hasCustomFields()) {
var fieldIndex = 0;
if (specifier) {
fieldIndex = parseInt(specifier, 10);
if (isNaN(fieldIndex)) {
handleError("PatternLayout.format: invalid specifier '" +
specifier + "' for conversion character 'f' - should be a number");
} else if (fieldIndex === 0) {
handleError("PatternLayout.format: invalid specifier '" +
specifier + "' for conversion character 'f' - must be greater than zero");
} else if (fieldIndex > this.customFields.length) {
handleError("PatternLayout.format: invalid specifier '" +
specifier + "' for conversion character 'f' - there aren't that many custom fields");
} else {
fieldIndex = fieldIndex - 1;
}
}
replacement = this.customFields[fieldIndex].value;
}
break;
case "m": // Message
if (specifier) {
var depth = parseInt(specifier, 10);
if (isNaN(depth)) {
handleError("PatternLayout.format: invalid specifier '" +
specifier + "' for conversion character 'm' - should be a number");
replacement = loggingEvent.message;
} else {
replacement = formatObjectExpansion(loggingEvent.message, depth);
}
} else {
replacement = loggingEvent.message;
}
break;
case "n": // New line
replacement = newLine;
break;
case "p": // Level
replacement = loggingEvent.level.name;
break;
case "r": // Milliseconds since log4javascript startup
replacement = "" + loggingEvent.timeStamp.getDifference(applicationStartDate);
break;
case "%": // Literal % sign
replacement = "%";
break;
default:
replacement = matchedString;
break;
}
// Format the replacement according to any padding or
// truncation specified
var len;
// First, truncation
if (truncation) {
len = parseInt(truncation.substr(1), 10);
var strLen = replacement.length;
if (len < strLen) {
replacement = replacement.substring(strLen - len, strLen);
}
}
// Next, padding
if (padding) {
if (padding.charAt(0) == "-") {
len = parseInt(padding.substr(1), 10);
// Right pad with spaces
while (replacement.length < len) {
replacement += " ";
}
} else {
len = parseInt(padding, 10);
// Left pad with spaces
while (replacement.length < len) {
replacement = " " + replacement;
}
}
}
formattedString += replacement;
}
searchString = searchString.substr(result.index + result[0].length);
}
return formattedString;
};
PatternLayout.prototype.ignoresThrowable = function(loggingEvent) {
return true;
};
log4javascript.PatternLayout = PatternLayout;
/* --------------------------------------------------------------------- */
// Appender "abstract class"
var Appender = function() {};
// Performs threshold checks before delegating actual logging to the
// subclass's specific append method.
Appender.prototype = {
layout: new PatternLayout(),
threshold: Level.ALL,
doAppend: function(loggingEvent) {
if (enabled && loggingEvent.level.level >= this.threshold.level) {
this.append(loggingEvent);
}
},
append: function(loggingEvent) {},
setLayout: function(layout) {
if (layout instanceof Layout) {
this.layout = layout;
} else {
handleError("Appender.setLayout: layout supplied to " +
this.toString() + " is not a subclass of Layout");
}
},
getLayout: function() {
return this.layout;
},
setThreshold: function(threshold) {
if (threshold instanceof Level) {
this.threshold = threshold;
} else {
handleError("Appender.setThreshold: threshold supplied to " +
this.toString() + " is not a subclass of Level");
}
},
getThreshold: function() {
return this.threshold;
},
toString: function() {
return "[Base Appender]";
}
};
log4javascript.Appender = Appender;
/* --------------------------------------------------------------------- */
// AlertAppender
var AlertAppender = function(layout) {
if (layout) {
this.setLayout(layout);
}
};
AlertAppender.prototype = new Appender();
AlertAppender.prototype.layout = new SimpleLayout();
AlertAppender.prototype.append = function(loggingEvent) {
var formattedMessage = this.getLayout().format(loggingEvent);
if (this.getLayout().ignoresThrowable()) {
formattedMessage += loggingEvent.getThrowableStrRep();
}
alert(formattedMessage);
};
AlertAppender.prototype.toString = function() {
return "[AlertAppender]";
};
log4javascript.AlertAppender = AlertAppender;
/* --------------------------------------------------------------------- */
// AjaxAppender
var AjaxAppender = function(url, layout, timed, waitForResponse,
batchSize, timerInterval, requestSuccessCallback, failCallback) {
var appender = this;
var isSupported = true;
if (!url) {
handleError("AjaxAppender: URL must be specified in constructor");
isSupported = false;
}
timed = extractBooleanFromParam(timed, this.defaults.timed);
waitForResponse = extractBooleanFromParam(waitForResponse, this.defaults.waitForResponse);
batchSize = extractIntFromParam(batchSize, this.defaults.batchSize);
timerInterval = extractIntFromParam(timerInterval, this.defaults.timerInterval);
requestSuccessCallback = extractFunctionFromParam(requestSuccessCallback, this.defaults.requestSuccessCallback);
failCallback = extractFunctionFromParam(failCallback, this.defaults.failCallback);
var sessionId = null;
var queuedLoggingEvents = [];
var queuedRequests = [];
var sending = false;
var initialized = false;
// Configuration methods. The function scope is used to prevent
// direct alteration to the appender configuration properties.
function checkCanConfigure(configOptionName) {
if (initialized) {
handleError("AjaxAppender: configuration option '" + configOptionName + "' may not be set after the appender has been initialized");
return false;
}
return true;
}
this.getSessionId = function() { return sessionId; };
this.setSessionId = function(sessionIdParam) {
sessionId = extractStringFromParam(sessionIdParam, null);
this.layout.setCustomField("sessionid", sessionId);
};
this.setLayout = function(layout) {
if (checkCanConfigure("layout")) {
this.layout = layout;
// Set the session id as a custom field on the layout, if not already present
if (sessionId !== null) {
this.setSessionId(sessionId);
}
}
};
if (layout) {
this.setLayout(layout);
}
this.isTimed = function() { return timed; };
this.setTimed = function(timedParam) {
if (checkCanConfigure("timed")) {
timed = bool(timedParam);
}
};
this.getTimerInterval = function() { return timerInterval; };
this.setTimerInterval = function(timerIntervalParam) {
if (checkCanConfigure("timerInterval")) {
timerInterval = extractIntFromParam(timerIntervalParam, timerInterval);
}
};
this.isWaitForResponse = function() { return waitForResponse; };
this.setWaitForResponse = function(waitForResponseParam) {
if (checkCanConfigure("waitForResponse")) {
waitForResponse = bool(waitForResponseParam);
}
};
this.getBatchSize = function() { return batchSize; };
this.setBatchSize = function(batchSizeParam) {
if (checkCanConfigure("batchSize")) {
batchSize = extractIntFromParam(batchSizeParam, batchSize);
}
};
this.setRequestSuccessCallback = function(requestSuccessCallbackParam) {
requestSuccessCallback = extractFunctionFromParam(requestSuccessCallbackParam, requestSuccessCallback);
};
this.setFailCallback = function(failCallbackParam) {
failCallback = extractFunctionFromParam(failCallbackParam, failCallback);
};
// Internal functions
function sendAll() {
if (isSupported && enabled) {
sending = true;
var currentRequestBatch;
if (waitForResponse) {
// Send the first request then use this function as the callback once
// the response comes back
if (queuedRequests.length > 0) {
currentRequestBatch = queuedRequests.shift();
sendRequest(preparePostData(currentRequestBatch), sendAll);
} else {
sending = false;
if (timed) {
scheduleSending();
}
}
} else {
// Rattle off all the requests without waiting to see the response
while ((currentRequestBatch = queuedRequests.shift())) {
sendRequest(preparePostData(currentRequestBatch));
}
sending = false;
if (timed) {
scheduleSending();
}
}
}
}
this.sendAll = sendAll;
function preparePostData(batchedLoggingEvents) {
// Format the logging events
var formattedMessages = [];
var currentLoggingEvent;
var postData = "";
while ((currentLoggingEvent = batchedLoggingEvents.shift())) {
var currentFormattedMessage = appender.getLayout().format(currentLoggingEvent);
if (appender.getLayout().ignoresThrowable()) {
currentFormattedMessage += loggingEvent.getThrowableStrRep();
}
formattedMessages.push(currentFormattedMessage);
}
// Create the post data string
if (batchedLoggingEvents.length == 1) {
postData = formattedMessages.join("");
} else {
postData = appender.getLayout().batchHeader +
formattedMessages.join(appender.getLayout().batchSeparator) +
appender.getLayout().batchFooter;
}
return postData;
}
function scheduleSending() {
setTimeout(sendAll, timerInterval);
}
function getXmlHttp() {
var xmlHttp = null;
if (typeof XMLHttpRequest == "object" || typeof XMLHttpRequest == "function") {
xmlHttp = new XMLHttpRequest();
} else {
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e2){
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e3) {
var msg = "AjaxAppender: could not create XMLHttpRequest object. AjaxAppender disabled";
handleError(msg);
isSupported = false;
if (failCallback) {
failCallback(msg);
}
}
}
}
return xmlHttp;
}
function sendRequest(postData, successCallback) {
try {
var xmlHttp = getXmlHttp();
if (isSupported) {
if (xmlHttp.overrideMimeType) {
xmlHttp.overrideMimeType(appender.getLayout().getContentType());
}
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4) {
var success = (isUndefined(xmlHttp.status) || xmlHttp.status === 0 ||
(xmlHttp.status >= 200 && xmlHttp.status < 300));
if (success) {
if (requestSuccessCallback) {
requestSuccessCallback(xmlHttp);
}
if (successCallback) {
successCallback(xmlHttp);
}
} else {
var msg = "AjaxAppender.append: XMLHttpRequest request to URL " +
url + " returned status code " + xmlHttp.status;
handleError(msg);
if (failCallback) {
failCallback(msg);
}
}
xmlHttp.onreadystatechange = emptyFunction;
xmlHttp = null;
}
};
xmlHttp.open("POST", url, true);
try {
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
} catch (headerEx) {
var msg = "AjaxAppender.append: your browser's XMLHttpRequest implementation" +
" does not support setRequestHeader, therefore cannot post data. AjaxAppender disabled";
handleError(msg);
isSupported = false;
if (failCallback) {
failCallback(msg);
}
return;
}
xmlHttp.send(postData);
}
} catch (ex) {
var msg = "AjaxAppender.append: error sending log message to " + url;
handleError(msg, ex);
if (failCallback) {
failCallback(msg + ". Details: " + getExceptionStringRep(ex));
}
}
}
this.append = function(loggingEvent) {
if (isSupported) {
if (!initialized) {
init();
}
queuedLoggingEvents.push(loggingEvent);
var actualBatchSize = this.getLayout().allowBatching() ? batchSize : 1;
if (queuedLoggingEvents.length >= actualBatchSize) {
var currentLoggingEvent;
var postData = "";
var batchedLoggingEvents = [];
while ((currentLoggingEvent = queuedLoggingEvents.shift())) {
batchedLoggingEvents.push(currentLoggingEvent);
}
// Queue this batch of log entries
queuedRequests.push(batchedLoggingEvents);
// If using a timer, the queue of requests will be processed by the
// timer function, so nothing needs to be done here.
if (!timed) {
if (!waitForResponse || (waitForResponse && !sending)) {
sendAll();
}
}
}
}
};
function init() {
initialized = true;
// Start timer
if (timed) {
scheduleSending();
}
}
};
AjaxAppender.prototype = new Appender();
AjaxAppender.prototype.defaults = {
waitForResponse: false,
timed: false,
timerInterval: 1000,
batchSize: 1,
requestSuccessCallback: null,
failCallback: null
};
AjaxAppender.prototype.layout = new HttpPostDataLayout();
AjaxAppender.prototype.toString = function() {
return "[AjaxAppender]";
};
log4javascript.AjaxAppender = AjaxAppender;
/* --------------------------------------------------------------------- */
// BaseConsoleAppender
// Create an anonymous function to protect base console methods
(function() {
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" />',
' <script type="text/javascript">',
' //<![CDATA[',
' var loggingEnabled = true;',
'',
' function toggleLoggingEnabled() {',
' setLoggingEnabled($("enableLogging").checked);',
' }',
'',
' function setLoggingEnabled(enable) {',
' loggingEnabled = enable;',
' }',
'',
' var newestAtTop = false;',
'',
' function setNewestAtTop(isNewestAtTop) {',
' var oldNewestAtTop = newestAtTop;',
' newestAtTop = Boolean(isNewestAtTop);',
' if (oldNewestAtTop != newestAtTop) {',
' // Invert the order of the log entries',
' var lc = getLogContainer();',
' var numberOfEntries = lc.childNodes.length;',
' var node = null;',
'',
' // Remove all the log container nodes',
' var logContainerChildNodes = [];',
' while ((node = lc.firstChild)) {',
' lc.removeChild(node);',
' logContainerChildNodes.push(node);',
' }',
'',
' // Put them all back in reverse order',
' while ((node = logContainerChildNodes.pop())) {',
' lc.appendChild(node);',
' }',
'',
' // Reassemble the matches array',
' if (currentSearch) {',
' var currentMatch = currentSearch.matches[currentMatchIndex];',
' var matchIndex = 0;',
' var matches = [];',
' var actOnLogEntry = function(logEntry) {',
' var logEntryMatches = logEntry.getSearchMatches();',
' for (var i = 0; i < logEntryMatches.length; i++) {',
' matches[matchIndex] = logEntryMatches[i];',
' if (currentMatch && logEntryMatches[i].equals(currentMatch)) {',
' currentMatchIndex = matchIndex;',
' }',
' matchIndex++;',
' }',
' };',
' var i;',
' if (newestAtTop) {',
' for (i = logEntries.length - 1; i >= 0; i--) {',
' actOnLogEntry(logEntries[i]);',
' }',
' } else {',
' for (i = 0; i < logEntries.length; i++) {',
' actOnLogEntry(logEntries[i]);',
' }',
' }',
' currentSearch.matches = matches;',
' if (currentMatch) {',
' currentMatch.setCurrent();',
' }',
' } else if (scrollToLatest) {',
' doScrollToLatest();',
' }',
' }',
' $("newestAtTop").checked = isNewestAtTop;',
' }',
'',
' function toggleNewestAtTop() {',
' var isNewestAtTop = $("newestAtTop").checked;',
' setNewestAtTop(isNewestAtTop);',
' }',
'',
' var scrollToLatest = true;',
'',
' function setScrollToLatest(isScrollToLatest) {',
' scrollToLatest = isScrollToLatest;',
' if (scrollToLatest) {',
' doScrollToLatest();',
' }',
' $("scrollToLatest").checked = isScrollToLatest;',
' }',
'',
' function toggleScrollToLatest() {',
' var isScrollToLatest = $("scrollToLatest").checked;',
' setScrollToLatest(isScrollToLatest);',
' }',
'',
' function doScrollToLatest() {',
' var l = getLogContainer();',
' if (typeof l.scrollTop != "undefined") {',
' if (newestAtTop) {',
' l.scrollTop = 0;',
' } else {',
' var latestLogEntry = l.lastChild;',
' if (latestLogEntry) {',
' l.scrollTop = l.scrollHeight;',
' }',
' }',
' }',
' }',
'',
' var maxMessages = null;',
'',
' function setMaxMessages(max) {',
' maxMessages = max;',
' pruneLogEntries();',
' }',
'',
' var logQueuedEventsTimer = null;',
' var logEntries = [];',
' var isCssWrapSupported;',
' var renderDelay = 100;',
'',
' function log(logLevel, formattedMessage) {',
' if (loggingEnabled) {',
' var logEntry = new LogEntry(logLevel, formattedMessage);',
' logEntries.push(logEntry);',
' if (loaded) {',
' if (logQueuedEventsTimer !== null) {',
' clearTimeout(logQueuedEventsTimer);',
' }',
' setTimeout(renderQueuedLogEntries, renderDelay);',
' }',
' }',
' }',
'',
' function renderQueuedLogEntries() {',
' logQueuedEventsTimer = null;',
' var pruned = pruneLogEntries();',
'',
' // Render any unrendered log entries and apply the current search to them',
' var initiallyHasMatches = currentSearch ? currentSearch.hasMatches() : false;',
' for (var i = 0; i < logEntries.length; i++) {',
' if (!logEntries[i].isRendered) {',
' logEntries[i].render();',
' logEntries[i].appendToLog();',
' if (currentSearch) {',
' currentSearch.applyTo(logEntries[i]);',
' }',
' }',
' }',
' if (currentSearch) {',
' if (pruned) {',
' if (currentSearch.hasMatches()) {',
' if (currentMatchIndex === null) {',
' setCurrentMatchIndex(0);',
' }',
' displayMatches();',
' } else {',
' displayNoMatches();',
' }',
' } else if (!initiallyHasMatches && currentSearch.hasMatches()) {',
' setCurrentMatchIndex(0);',
' displayMatches();',
' }',
' }',
' if (scrollToLatest) {',
' doScrollToLatest();',
' }',
' }',
'',
' function pruneLogEntries() {',
' if ((maxMessages !== null) && (logEntries.length > maxMessages)) {',
' var numberToDelete = logEntries.length - maxMessages;',
' for (var i = 0; i < numberToDelete; i++) {',
' logEntries[i].remove();',
' }',
' logEntries = array_removeFromStart(logEntries, numberToDelete);',
' if (currentSearch) {',
' currentSearch.removePrunedMatches();',
' }',
' return true;',
' }',
' return false;',
' }',
'',
' function LogEntry(level, formattedMessage) {',
' this.level = level;',
' this.formattedMessage = formattedMessage;',
' this.isRendered = false;',
' }',
'',
' LogEntry.prototype = {',
' render: function() {',
' this.mainDiv = document.createElement("div");',
' this.mainDiv.className = "logentry " + this.level.name;',
' ',
' // Support for the CSS attribute white-space in IE for Windows is',
' // non-existent pre version 6 and slightly odd in 6, so instead',
' // use two different HTML elements',
' if (isCssWrapSupported) {',
' this.mainDiv.appendChild(document.createTextNode(this.formattedMessage));',
' } else {',
' this.formattedMessage = this.formattedMessage.replace(/\\r\\n/g, "\\r"); // Workaround for IE\'s treatment of white space',
' this.unwrappedPre = this.mainDiv.appendChild(document.createElement("pre"));',
' this.unwrappedPre.appendChild(document.createTextNode(this.formattedMessage));',
' this.unwrappedPre.className = "unwrapped";',
' this.wrappedSpan = this.mainDiv.appendChild(document.createElement("span"));',
' this.wrappedSpan.appendChild(document.createTextNode(this.formattedMessage));',
' this.wrappedSpan.className = "wrapped";',
' }',
' this.content = this.formattedMessage;',
' this.isRendered = true;',
' },',
' ',
' appendToLog: function() {',
' var lc = getLogContainer();',
' if (newestAtTop && lc.hasChildNodes()) {',
' lc.insertBefore(this.mainDiv, lc.firstChild);',
' } else {',
' getLogContainer().appendChild(this.mainDiv);',
' }',
' },',
' ',
' setContent: function(content) {',
' if (content != this.content) {',
' if (getLogContainer().currentStyle) {',
' if (content === this.formattedMessage) {',
' this.unwrappedPre.innerHTML = "";',
' this.unwrappedPre.appendChild(document.createTextNode(this.formattedMessage));',
' this.wrappedSpan.innerHTML = "";',
' this.wrappedSpan.appendChild(document.createTextNode(this.formattedMessage));',
' } else {',
' content = content.replace(/\\r\\n/g, "\\r"); // Workaround for IE\'s treatment of white space',
' this.unwrappedPre.innerHTML = content;',
' this.wrappedSpan.innerHTML = content;',
' }',
' } else {',
' if (content === this.formattedMessage) {',
' this.mainDiv.innerHTML = "";',
' this.mainDiv.appendChild(document.createTextNode(this.formattedMessage));',
' } else {',
' this.mainDiv.innerHTML = content;',
' }',
' }',
' this.content = content;',
' }',
' },',
'',
' getSearchMatches: function() {',
' var matches = [];',
' if (isCssWrapSupported) {',
' var els = getElementsByClass(this.mainDiv, "searchterm", "span");',
' for (var i = 0; i < els.length; i++) {',
' matches[i] = new Match(this.level, els[i]);',
' }',
' } else {',
' var unwrappedEls = getElementsByClass(this.unwrappedPre, "searchterm", "span");',
' var wrappedEls = getElementsByClass(this.wrappedSpan, "searchterm", "span");',
' for (i = 0; i < unwrappedEls.length; i++) {',
' matches[i] = new Match(this.level, null, unwrappedEls[i], wrappedEls[i]);',
' }',
' }',
' return matches;',
' },',
'',
' remove: function() {',
' if (this.isRendered) {',
' this.mainDiv.parentNode.removeChild(this.mainDiv);',
' this.mainDiv = null;',
' }',
' }',
' };',
'',
' function mainPageReloaded() {',
' var separator = document.createElement("div");',
' separator.className = "separator";',
' separator.innerHTML = " ";',
' getLogContainer().appendChild(separator);',
' }',
'',
' window.onload = function() {',
' isCssWrapSupported = (typeof getLogContainer().currentStyle == "undefined");',
' setLogContainerHeight();',
' toggleLoggingEnabled();',
' toggleSearchEnabled();',
' toggleSearchFilter();',
' toggleSearchHighlight();',
' applyFilters();',
' toggleWrap();',
' toggleNewestAtTop();',
' toggleScrollToLatest();',
' //doSearch();',
' renderQueuedLogEntries();',
' loaded = true;',
' // Workaround to make sure log div starts at the correct size',
' setTimeout(setLogContainerHeight, 20);',
'',
' // Remove "Close" button if not in pop-up mode',
' if (window != top) {',
' $("closeButton").style.display = "none";',
' }',
' };',
'',
' var loaded = false;',
'',
' var logLevels = ["TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"];',
'',
' function getCheckBox(logLevel) {',
' return $("switch_" + logLevel);',
' }',
'',
' function getLogContainer() {',
' return $("log");',
' }',
'',
' function applyFilters() {',
' for (var i = 0; i < logLevels.length; i++) {',
' if (getCheckBox(logLevels[i]).checked) {',
' addClass(getLogContainer(), logLevels[i]);',
' } else {',
' removeClass(getLogContainer(), logLevels[i]);',
' }',
' }',
' updateSearchFromFilters();',
' }',
'',
' function toggleAllLevels() {',
' var turnOn = $("switch_ALL").checked;',
' for (var i = 0; i < logLevels.length; i++) {',
' getCheckBox(logLevels[i]).checked = turnOn;',
' if (turnOn) {',
' addClass(getLogContainer(), logLevels[i]);',
' } else {',
' removeClass(getLogContainer(), logLevels[i]);',
' }',
' }',
' }',
'',
' function checkAllLevels() {',
' for (var i = 0; i < logLevels.length; i++) {',
' if (!getCheckBox(logLevels[i]).checked) {',
' getCheckBox("ALL").checked = false;',
' return;',
' }',
' }',
' getCheckBox("ALL").checked = true;',
' }',
'',
' function clearLog() {',
' getLogContainer().innerHTML = "";',
' logEntries = [];',
' doSearch();',
' }',
'',
' function toggleWrap() {',
' var enable = $("wrap").checked;',
' if (enable) {',
' addClass(getLogContainer(), "wrap");',
' } else {',
' removeClass(getLogContainer(), "wrap");',
' }',
' refreshCurrentMatch();',
' }',
'',
'/doc/t/ti/timdown/log4javascript/131/lib/___/_____________________________________________________________________/index.html',
'',
' // Search',
'',
' var searchTimer = null;',
'',
' function scheduleSearch() {',
' try {',
' clearTimeout(searchTimer);',
' } catch (ex) {',
' // Do nothing',
' }',
' searchTimer = setTimeout(doSearch, 500);',
' }',
'',
' function Search(searchTerm, isRegex, searchRegex, isCaseSensitive) {',
' this.searchTerm = searchTerm;',
' this.isRegex = isRegex;',
' this.searchRegex = searchRegex;',
' this.isCaseSensitive = isCaseSensitive;',
' this.matches = [];',
' }',
'',
' Search.prototype = {',
' hasMatches: function() {',
' return this.matches.length > 0;',
' },',
'',
' hasVisibleMatches: function() {',
' if (this.hasMatches()) {',
' for (var i = 0; i <= this.matches.length; i++) {',
' if (this.matches[i].isVisible()) {',
' return true;',
' }',
' }',
' }',
' return false;',
' },',
'',
' match: function(logEntry) {',
' var entryText = logEntry.formattedMessage;',
' var matchesSearch = false;',
' if (this.isRegex) {',
' matchesSearch = this.searchRegex.test(entryText);',
' } else if (this.isCaseSensitive) {',
' matchesSearch = (entryText.indexOf(this.searchTerm) > -1);',
' } else {',
' matchesSearch = (entryText.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1);',
' }',
' return matchesSearch;',
' },',
' ',
' getNextVisibleMatchIndex: function() {',
' for (var i = currentMatchIndex + 1; i < this.matches.length; i++) {',
' if (this.matches[i].isVisible()) {',
' return i;',
' }',
' }',
' // Start again from the first match',
' for (var i = 0; i <= currentMatchIndex; i++) {',
' if (this.matches[i].isVisible()) {',
' return i;',
' }',
' }',
' return -1;',
' },',
'',
' getPreviousVisibleMatchIndex: function() {',
' for (var i = currentMatchIndex - 1; i >= 0; i--) {',
' if (this.matches[i].isVisible()) {',
' return i;',
' }',
' }',
' // Start again from the last match',
' for (var i = this.matches.length - 1; i >= currentMatchIndex; i--) {',
' if (this.matches[i].isVisible()) {',
' return i;',
' }',
' }',
' return -1;',
' },',
'',
' applyTo: function(logEntry) {',
' var doesMatch = this.match(logEntry);',
' if (doesMatch) {',
' replaceClass(logEntry.mainDiv, "searchmatch", "searchnonmatch");',
' var logEntryContent;',
' if (this.isRegex) {',
' var flags = this.isCaseSensitive ? "g" : "gi";',
' var capturingRegex = new RegExp("(" + this.searchRegex.source + ")", flags);',
' logEntryContent = logEntry.formattedMessage.replace(capturingRegex, "<span class=\\\"searchterm\\\">$1</span>");',
' } else {',
' logEntryContent = "";',
' var searchTermReplacementStartTag = "<span class=\\\"searchterm\\\">";',
' var searchTermReplacementEndTag = "</span>";',
' var searchTermReplacementLength = searchTermReplacementStartTag.length + this.searchTerm.length + searchTermReplacementEndTag.length;',
' var searchTermLength = this.searchTerm.length;',
' var startIndex = 0;',
' var searchIndex;',
' var searchTermLowerCase = this.searchTerm.toLowerCase();',
' var logTextLowerCase = logEntry.formattedMessage.toLowerCase();',
' while ((searchIndex = logTextLowerCase.indexOf(searchTermLowerCase, startIndex)) > -1) {',
' var searchTermReplacement = searchTermReplacementStartTag + logEntry.formattedMessage.substr(searchIndex, this.searchTerm.length) + searchTermReplacementEndTag;',
' logEntryContent += logEntry.formattedMessage.substring(startIndex, searchIndex) + searchTermReplacement;',
' startIndex = searchIndex + searchTermLength;',
' }',
' logEntryContent += logEntry.formattedMessage.substr(startIndex);',
' }',
' logEntry.setContent(logEntryContent);',
' var logEntryMatches = logEntry.getSearchMatches();',
' this.matches = this.matches.concat(logEntryMatches);',
' } else {',
' replaceClass(logEntry.mainDiv, "searchnonmatch", "searchmatch");',
' logEntry.setContent(logEntry.formattedMessage);',
' }',
' return doesMatch;',
' },',
'',
' removePrunedMatches: function() {',
' var matchesToRemoveCount = 0;',
' var currentMatchRemoved = false;',
' for (var i = 0; i < this.matches.length; i++) {',
' if (this.matches[i].isOrphan()) {',
' this.matches[i].remove();',
' if (i === currentMatchIndex) {',
' currentMatchRemoved = true;',
' }',
' matchesToRemoveCount++;',
' }',
' }',
' if (matchesToRemoveCount > 0) {',
' array_removeFromStart(this.matches, matchesToRemoveCount);',
' var newMatchIndex = currentMatchRemoved ? 0 :',
' currentMatchIndex - matchesToRemoveCount;',
' if (this.hasMatches()) {',
' setCurrentMatchIndex(newMatchIndex);',
' } else {',
' currentMatchIndex = null;',
' }',
' }',
' }',
' };',
'',
' function getPageOffsetTop(el) {',
' var currentEl = el;',
' var y = 0;',
' while (currentEl) {',
' y += currentEl.offsetTop;',
' currentEl = currentEl.offsetParent;',
' }',
' return y;',
' }',
'',
' function scrollIntoView(el) {',
' getLogContainer().scrollLeft = el.offsetLeft;',
' getLogContainer().scrollTop = getPageOffsetTop(el) - getToolBarsHeight();',
' }',
'',
' function Match(logEntryLevel, spanInMainDiv, spanInUnwrappedPre, spanInWrappedSpan) {',
' this.logEntryLevel = logEntryLevel;',
' this.spanInMainDiv = spanInMainDiv;',
' if (!isCssWrapSupported) {',
' this.spanInUnwrappedPre = spanInUnwrappedPre;',
' this.spanInWrappedSpan = spanInWrappedSpan;',
' }',
' this.mainSpan = isCssWrapSupported ? spanInMainDiv : spanInUnwrappedPre;',
' }',
'',
' Match.prototype = {',
' equals: function(match) {',
' return this.mainSpan === match.mainSpan;',
' },',
'',
' setCurrent: function() {',
' if (isCssWrapSupported) {',
' addClass(this.spanInMainDiv, "currentmatch");',
' scrollIntoView(this.spanInMainDiv);',
' } else {',
' addClass(this.spanInUnwrappedPre, "currentmatch");',
' addClass(this.spanInWrappedSpan, "currentmatch");',
' // Scroll the visible one into view',
' var elementToScroll = $("wrap").checked ? this.spanInWrappedSpan : this.spanInUnwrappedPre;',
' scrollIntoView(elementToScroll);',
' }',
' },',
'',
' setNotCurrent: function() {',
' if (isCssWrapSupported) {',
' removeClass(this.spanInMainDiv, "currentmatch");',
' } else {',
' removeClass(this.spanInUnwrappedPre, "currentmatch");',
' removeClass(this.spanInWrappedSpan, "currentmatch");',
' }',
' },',
'',
' isOrphan: function() {',
' return isOrphan(this.mainSpan);',
' },',
'',
' isVisible: function() {',
' return getCheckBox(this.logEntryLevel).checked;',
' },',
'',
' remove: function() {',
' if (isCssWrapSupported) {',
' this.spanInMainDiv = null;',
' } else {',
' this.spanInUnwrappedPre = null;',
' this.spanInWrappedSpan = null;',
' }',
' }',
' };',
'',
' var currentSearch = null;',
' var currentMatchIndex = null;',
'',
' function doSearch() {',
' var searchBox = $("searchBox");',
' var searchTerm = searchBox.value;',
' var isRegex = $("searchRegex").checked;',
' var isCaseSensitive = $("searchCaseSensitive").checked;',
' var i;',
'',
' if (searchTerm === "") {',
' $("searchReset").disabled = true;',
' $("searchNav").style.display = "none";',
' removeClass(document.body, "searching");',
' removeClass(searchBox, "hasmatches");',
' removeClass(searchBox, "nomatches");',
' for (i = 0; i < logEntries.length; i++) {',
' removeClass(logEntries[i].mainDiv, "searchmatch");',
' removeClass(logEntries[i].mainDiv, "searchnonmatch");',
' logEntries[i].setContent(logEntries[i].formattedMessage);',
' }',
' currentSearch = null;',
' setLogContainerHeight();',
' } else {',
' $("searchReset").disabled = false;',
' $("searchNav").style.display = "block";',
' var searchRegex;',
' var regexValid;',
' if (isRegex) {',
' try {',
' searchRegex = isCaseSensitive ? new RegExp(searchTerm, "g") : new RegExp(searchTerm, "gi");',
' regexValid = true;',
' replaceClass(searchBox, "validregex", "invalidregex");',
' searchBox.title = "Valid regex";',
' } catch (ex) {',
' regexValid = false;',
' replaceClass(searchBox, "invalidregex", "validregex");',
' searchBox.title = "Invalid regex: " + (ex.message ? ex.message : (ex.description ? ex.description : "unknown error"));',
' return;',
' }',
' } else {',
' searchBox.title = "";',
' removeClass(searchBox, "validregex");',
' removeClass(searchBox, "invalidregex");',
' }',
' addClass(document.body, "searching");',
' currentSearch = new Search(searchTerm, isRegex, searchRegex, isCaseSensitive);',
' for (i = 0; i < logEntries.length; i++) {',
' currentSearch.applyTo(logEntries[i]);',
' }',
' setLogContainerHeight();',
'',
' // Highlight the first search match',
' if (currentSearch.hasMatches()) {',
' setCurrentMatchIndex(0);',
' displayMatches();',
' } else {',
' displayNoMatches();',
' }',
' }',
' }',
' ',
' function updateSearchFromFilters() {',
' if (currentSearch && currentSearch.hasMatches()) {',
' var currentMatch = currentSearch.matches[currentMatchIndex];',
' if (currentMatch.isVisible()) {',
' displayMatches();',
' setCurrentMatchIndex(currentMatchIndex);',
' } else {',
' currentMatch.setNotCurrent();',
' // Find the next visible match, if one exists',
' var nextVisibleMatchIndex = currentSearch.getNextVisibleMatchIndex();',
' if (nextVisibleMatchIndex > -1) {',
' setCurrentMatchIndex(nextVisibleMatchIndex);',
' displayMatches();',
' } else {',
' displayNoMatches();',
' }',
' }',
' }',
' }',
'',
' function refreshCurrentMatch() {',
' if (currentSearch && currentSearch.hasMatches()) {',
' setCurrentMatchIndex(currentMatchIndex);',
' }',
' }',
'',
' function displayMatches() {',
' replaceClass($("searchBox"), "hasmatches", "nomatches");',
' $("searchBox").title = "" + currentSearch.matches.length + " matches found";',
' $("searchNav").style.display = "block";',
' setLogContainerHeight();',
' }',
'',
' function displayNoMatches() {',
' replaceClass($("searchBox"), "nomatches", "hasmatches");',
' $("searchBox").title = "No matches found";',
' $("searchNav").style.display = "none";',
' setLogContainerHeight();',
' }',
'',
' function toggleSearchEnabled(enable) {',
' enable = (typeof enable == "undefined") ? !$("searchDisable").checked : enable;',
' $("searchBox").disabled = !enable;',
' $("searchReset").disabled = !enable;',
' $("searchRegex").disabled = !enable;',
' $("searchNext").disabled = !enable;',
' $("searchPrevious").disabled = !enable;',
' $("searchCaseSensitive").disabled = !enable;',
' $("searchNav").style.display = (enable && ($("searchBox").value !== "")) ?',
' "block" : "none";',
' if (enable) {',
' removeClass($("search"), "greyedout");',
' addClass(document.body, "searching");',
' if ($("searchHighlight").checked) {',
' addClass(getLogContainer(), "searchhighlight");',
' } else {',
' removeClass(getLogContainer(), "searchhighlight");',
' }',
' if ($("searchFilter").checked) {',
' addClass(getLogContainer(), "searchfilter");',
' } else {',
' removeClass(getLogContainer(), "searchfilter");',
' }',
' $("searchDisable").checked = !enable;',
' } else {',
' addClass($("search"), "greyedout");',
' removeClass(document.body, "searching");',
' removeClass(getLogContainer(), "searchhighlight");',
' removeClass(getLogContainer(), "searchfilter");',
' }',
' setLogContainerHeight();',
' }',
'',
' function toggleSearchFilter() {',
' var enable = $("searchFilter").checked;',
' if (enable) {',
' addClass(getLogContainer(), "searchfilter");',
' } else {',
' removeClass(getLogContainer(), "searchfilter");',
' }',
' refreshCurrentMatch();',
' }',
'',
' function toggleSearchHighlight() {',
' var enable = $("searchHighlight").checked;',
' if (enable) {',
' addClass(getLogContainer(), "searchhighlight");',
' } else {',
' removeClass(getLogContainer(), "searchhighlight");',
' }',
' }',
'',
' function clearSearch() {',
' $("searchBox").value = "";',
' doSearch();',
' }',
'',
' function searchNext() {',
' try {',
' if (currentSearch !== null && currentMatchIndex !== null) {',
' currentSearch.matches[currentMatchIndex].setNotCurrent();',
' var nextMatchIndex = currentSearch.getNextVisibleMatchIndex();',
' if (nextMatchIndex > currentMatchIndex || confirm("Reached the end of the page. Start from the top?")) {',
' setCurrentMatchIndex(nextMatchIndex);',
' }',
' }',
' } catch (err) {',
' alert("currentMatchIndex is " + currentMatchIndex);',
' }',
' }',
'',
' function searchPrevious() {',
' if (currentSearch !== null && currentMatchIndex !== null) {',
' currentSearch.matches[currentMatchIndex].setNotCurrent();',
' var previousMatchIndex = currentSearch.getPreviousVisibleMatchIndex();',
' if (previousMatchIndex < currentMatchIndex || confirm("Reached the start of the page. Continue from the bottom?")) {',
' setCurrentMatchIndex(previousMatchIndex);',
' }',
' }',
' }',
'',
' function setCurrentMatchIndex(index) {',
' currentMatchIndex = index;',
' currentSearch.matches[currentMatchIndex].setCurrent();',
' }',
'',
'/doc/t/ti/timdown/log4javascript/131/lib/___/___________________________________________________________________________/index.html',
'',
' // CSS Utilities',
'',
' function addClass(el, cssClass) {',
' if (!hasClass(el, cssClass)) {',
' if (el.className) {',
' el.className += " " + cssClass;',
' } else {',
' el.className = cssClass;',
' }',
' }',
' }',
'',
' function hasClass(el, cssClass) {',
' if (el.className) {',
' var classNames = el.className.split(" ");',
' return array_contains(classNames, cssClass);',
' }',
' return false;',
' }',
'',
' function removeClass(el, cssClass) {',
' if (hasClass(el, cssClass)) {',
' // Rebuild the className property',
' var existingClasses = el.className.split(" ");',
' var newClasses = [];',
' for (var i = 0; i < existingClasses.length; i++) {',
' if (existingClasses[i] != cssClass) {',
' newClasses[newClasses.length] = existingClasses[i];',
' }',
' }',
' el.className = newClasses.join(" ");',
' }',
' }',
'',
' function replaceClass(el, newCssClass, oldCssClass) {',
' removeClass(el, oldCssClass);',
' addClass(el, newCssClass);',
' }',
'',
'/doc/t/ti/timdown/log4javascript/131/lib/___/___________________________________________________________________________/index.html',
'',
' // Other utility functions',
'',
' function getElementsByClass(el, cssClass, tagName) {',
' var elements = el.getElementsByTagName(tagName);',
' var matches = [];',
' for (var i = 0; i < elements.length; i++) {',
' if (hasClass(elements[i], cssClass)) {',
' matches.push(elements[i]);',
' }',
' }',
' return matches;',
' }',
'',
' // Syntax borrowed from Prototype library',
' function $(id) {',
' return document.getElementById(id);',
' }',
'',
' function isOrphan(node) {',
' var currentNode = node;',
' while (currentNode) {',
' if (currentNode == document.body) {',
' return false;',
' }',
' currentNode = currentNode.parentNode;',
' }',
' return true;',
' }',
'',
' function getWindowWidth() {',
' if (window.innerWidth) {',
' return window.innerWidth;',
' } else if (document.documentElement && document.documentElement.clientWidth) {',
' return document.documentElement.clientWidth;',
' } else if (document.body) {',
' return document.body.clientWidth;',
' }',
' return 0;',
' }',
'',
' 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 getToolBarsHeight() {',
' return $("switches").offsetHeight;',
' }',
'',
' function setLogContainerHeight() {',
' var windowHeight = getWindowHeight();',
' $("body").style.height = getWindowHeight() + "px";',
' getLogContainer().style.height = "" +',
' (windowHeight - getToolBarsHeight()) + "px";',
' }',
' window.onresize = setLogContainerHeight;',
'',
' if (!Array.prototype.push) {',
' Array.prototype.push = function() {',
' for (var i = 0; i < arguments.length; i++){',
' this[this.length] = arguments[i];',
' }',
' return this.length;',
' };',
' }',
'',
' if (!Array.prototype.pop) {',
' Array.prototype.pop = function() {',
' if (this.length > 0) {',
' var val = this[this.length - 1];',
' this.length = this.length - 1;',
' return val;',
' }',
' };',
' }',
'',
' if (!Array.prototype.shift) {',
' Array.prototype.shift = function() {',
' if (this.length > 0) {',
' var firstItem = this[0];',
' for (var i = 0; i < this.length - 1; i++) {',
' this[i] = this[i + 1];',
' }',
' this.length = this.length - 1;',
' return firstItem;',
' }',
' };',
' }',
'',
' function array_removeFromStart(array, numberToRemove) {',
' if (Array.prototype.splice) {',
' array.splice(0, numberToRemove);',
' } else {',
' for (var i = numberToRemove; i < array.length; i++) {',
' array[i - numberToRemove] = array[i];',
' }',
' array.length = array.length - numberToRemove;',
' }',
' return array;',
' }',
'',
' function array_contains(arr, val) {',
' for (var i = 0; i < arr.length; i++) {',
' if (arr[i] == val) {',
' return true;',
' }',
' }',
' return false;',
' }',
' //]]>',
' </script>',
' <style type="text/css">',
' body {',
' background-color: white;',
' color: black;',
' padding: 0px;',
' margin: 0px;',
' font-family: tahoma, verdana, arial, helvetica, sans-serif;',
' overflow: hidden;',
' }',
'',
' div#switchesContainer input {',
' margin-bottom: 0px;',
' }',
'',
' div#switches div.toolbar {',
' border-top: solid #ffffff 1px;',
' border-bottom: solid #aca899 1px;',
' background-color: #f1efe7;',
' padding: 3px 5px;',
' font-size: 68.75%;',
' }',
'',
' div#switches div.toolbar, div#search input {',
' font-family: tahoma, verdana, arial, helvetica, sans-serif;',
' }',
'',
' div#switches input.button {',
' padding: 0px 5px;',
' font-size: 100%;',
' }',
'',
' div#switches input#clearButton {',
' margin-left: 20px;',
' }',
'',
' div#levels label {',
' font-weight: bold;',
' }',
'',
' div#levels label, div#options label {',
' margin-right: 5px;',
' }',
'',
' div#levels label#wrapLabel {',
' font-weight: normal;',
' }',
'',
' div#search {',
' padding: 5px 0px;',
' }',
'',
' div#search label {',
' margin-right: 10px;',
' }',
'',
' div#search label.searchboxlabel {',
' margin-right: 0px;',
' }',
'',
' div#search input {',
' font-size: 100%;',
' }',
'',
' div#search input.validregex {',
' color: green;',
' }',
'',
' div#search input.invalidregex {',
' color: red;',
' }',
'',
' div#search input.nomatches {',
' color: white;',
' background-color: #ff6666;',
' }',
'',
' div#search input.nomatches {',
' color: white;',
' background-color: #ff6666;',
' }',
'',
' *.greyedout {',
' color: gray;',
' }',
'',
' *.greyedout *.alwaysenabled {',
' color: black;',
' }',
'',
' div#log {',
' font-family: Courier New, Courier;',
' font-size: 75%;',
' width: 100%;',
' overflow: auto;',
' }',
'',
' *.logentry {',
' overflow: visible;',
' display: none;',
' white-space: pre;',
' }',
'',
' *.logentry pre.unwrapped {',
' display: inline;',
' }',
'',
' *.logentry span.wrapped {',
' display: none;',
' }',
'',
' body.searching *.logentry span.currentmatch {',
' color: white !important;',
' background-color: green !important;',
' }',
'',
' body.searching div.searchhighlight *.logentry span.searchterm {',
'/doc/t/ti/timdown/log4javascript/131/lib/____/font_weight_bold/index.html',
' color: black;',
' background-color: yellow;',
' }',
'',
' div.wrap *.logentry {',
' white-space: normal !important;',
' border-width: 0px 0px 1px 0px;',
' border-color: #dddddd;',
' border-style: dotted;',
' }',
'',
' div.wrap *.logentry pre.unwrapped {',
' display: none;',
' }',
'',
' div.wrap *.logentry span.wrapped {',
' display: inline;',
' }',
'',
' div.searchfilter *.searchnonmatch {',
' display: none !important;',
' }',
'',
' div#log *.TRACE, label#label_TRACE {',
' color: #666666;',
' }',
'',
' div#log *.DEBUG, label#label_DEBUG {',
' color: green;',
' }',
'',
' div#log *.INFO, label#label_INFO {',
' color: #000099;',
' }',
'',
' div#log *.WARN, label#label_WARN {',
' color: #999900;',
' }',
'',
' div#log *.ERROR, label#label_ERROR {',
' color: red;',
' }',
'',
' div#log *.FATAL, label#label_FATAL {',
' color: #660066;',
' }',
'',
' div.TRACE#log *.TRACE,',
' div.DEBUG#log *.DEBUG,',
' div.INFO#log *.INFO,',
' div.WARN#log *.WARN,',
' div.ERROR#log *.ERROR,',
' div.FATAL#log *.FATAL {',
' display: block;',
' }',
'',
' div#log div.separator {',
' background-color: #cccccc;',
' margin: 5px 0px;',
' line-height: 1px;',
' }',
' </style>',
' </head>',
'',
' <body id="body">',
' <div id="switchesContainer">',
' <div id="switches">',
' <div id="levels" class="toolbar">',
' Filters:',
' <input type="checkbox" id="switch_TRACE" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide trace messages" /><label for="switch_TRACE" id="label_TRACE">trace</label>',
' <input type="checkbox" id="switch_DEBUG" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide debug messages" /><label for="switch_DEBUG" id="label_DEBUG">debug</label>',
' <input type="checkbox" id="switch_INFO" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide info messages" /><label for="switch_INFO" id="label_INFO">info</label>',
' <input type="checkbox" id="switch_WARN" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide warn messages" /><label for="switch_WARN" id="label_WARN">warn</label>',
' <input type="checkbox" id="switch_ERROR" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide error messages" /><label for="switch_ERROR" id="label_ERROR">error</label>',
' <input type="checkbox" id="switch_FATAL" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide fatal messages" /><label for="switch_FATAL" id="label_FATAL">fatal</label>',
' <input type="checkbox" id="switch_ALL" onclick="toggleAllLevels(); applyFilters()" checked="checked" title="Show/hide all messages" /><label for="switch_ALL" id="label_ALL">all</label>',
' </div>',
' <div id="search" class="toolbar">',
' <label for="searchBox" class="searchboxlabel">Search:</label> <input type="text" id="searchBox" onclick="toggleSearchEnabled(true)" onkeyup="scheduleSearch()" size="20" />',
' <input type="button" id="searchReset" disabled="disabled" value="Reset" onclick="clearSearch()" class="button" title="Reset the search" />',
' <input type="checkbox" id="searchRegex" onclick="doSearch()" title="If checked, search is treated as a regular expression" /><label for="searchRegex">Regex</label>',
' <input type="checkbox" id="searchCaseSensitive" onclick="doSearch()" title="If checked, search is case sensitive" /><label for="searchCaseSensitive">Match case</label>',
' <input type="checkbox" id="searchDisable" onclick="toggleSearchEnabled()" title="Enable/disable search" /><label for="searchDisable" class="alwaysenabled">Disable</label>',
' <div id="searchNav">',
' <input type="button" id="searchNext" disabled="disabled" value="Next" onclick="searchNext()" class="button" title="Go to the next matching log entry" />',
' <input type="button" id="searchPrevious" disabled="disabled" value="Previous" onclick="searchPrevious()" class="button" title="Go to the previous matching log entry" />',
' <input type="checkbox" id="searchFilter" onclick="toggleSearchFilter()" title="If checked, non-matching log entries are filtered out" /><label for="searchFilter">Filter</label>',
' <input type="checkbox" id="searchHighlight" onclick="toggleSearchHighlight()" title="Highlight matched search terms" /><label for="searchHighlight" class="alwaysenabled">Highlight all</label>',
' </div>',
' </div>',
' <div id="options" class="toolbar">',
' Options:',
' <input type="checkbox" id="enableLogging" onclick="toggleLoggingEnabled()" checked="checked" title="Enable/disable logging" /><label for="enableLogging" id="wrapLabel">Log</label>',
' <input type="checkbox" id="wrap" onclick="toggleWrap()" title="Enable / disable word wrap" /><label for="wrap" id="wrapLabel">Wrap</label>',
' <input type="checkbox" id="newestAtTop" onclick="toggleNewestAtTop()" title="If checked, causes newest messages to appear at the top" /><label for="newestAtTop" id="newestAtTopLabel">Newest at the top</label>',
' <input type="checkbox" id="scrollToLatest" onclick="toggleScrollToLatest()" checked="checked" title="If checked, window automatically scrolls to a new message when it is added" /><label for="scrollToLatest" id="scrollToLatestLabel">Scroll to latest</label>',
' <input type="button" id="clearButton" value="Clear" onclick="clearLog()" class="button" title="Clear all log messages" />',
' <input type="button" id="closeButton" value="Close" onclick="window.close()" class="button" title="Close the window" />',
' </div>',
' </div>',
'',
' </div>',
' <div id="log" class="TRACE DEBUG INFO WARN ERROR FATAL"></div>',
' </body>',
'</html>'
];
};
function ConsoleAppender() {}
var consoleAppenderIdCounter = 1;
ConsoleAppender.prototype = new Appender();
ConsoleAppender.prototype.create = function(inPage, containerElement,
layout, lazyInit, focusConsoleWindow, useOldPopUp,
complainAboutPopUpBlocking, newestMessageAtTop,
scrollToLatestMessage, initiallyMinimized, width, height,
reopenWhenClosed, maxMessages) {
var appender = this;
// Common properties
if (layout) {
this.setLayout(layout);
} else {
this.setLayout(this.defaults.layout);
}
var initialized = false;
var consoleWindowLoaded = false;
var queuedLoggingEvents = [];
var isSupported = true;
var consoleAppenderId = consoleAppenderIdCounter++;
// Params
lazyInit = extractBooleanFromParam(lazyInit, true);
newestMessageAtTop = extractBooleanFromParam(newestMessageAtTop, this.defaults.newestMessageAtTop);
scrollToLatestMessage = extractBooleanFromParam(scrollToLatestMessage, this.defaults.scrollToLatestMessage);
width = width ? width : this.defaults.width;
height = height ? height : this.defaults.height;
maxMessages = maxMessages ? maxMessages : this.defaults.maxMessages;
// Functions whose implementations vary between subclasses
var init, safeToAppend, getConsoleWindow;
// Configuration methods. The function scope is used to prevent
// direct alteration to the appender configuration properties.
var appenderName = inPage ? "InPageAppender" : "PopUpAppender";
var checkCanConfigure = function(configOptionName) {
if (initialized) {
handleError(appenderName + ": configuration option '" + configOptionName + "' may not be set after the appender has been initialized");
return false;
}
return true;
};
this.isNewestMessageAtTop = function() { return newestMessageAtTop; };
this.setNewestMessageAtTop = function(newestMessageAtTopParam) {
newestMessageAtTop = bool(newestMessageAtTopParam);
if (consoleWindowLoaded && isSupported) {
getConsoleWindow().setNewestAtTop(newestMessageAtTop);
}
};
this.isScrollToLatestMessage = function() { return scrollToLatestMessage; };
this.setScrollToLatestMessage = function(scrollToLatestMessageParam) {
scrollToLatestMessage = bool(scrollToLatestMessageParam);
if (consoleWindowLoaded && isSupported) {
getConsoleWindow().setScrollToLatest(scrollToLatestMessage);
}
};
this.getWidth = function() { return width; };
this.setWidth = function(widthParam) {
if (checkCanConfigure("width")) {
width = extractStringFromParam(widthParam, width);
}
};
this.getHeight = function() { return height; };
this.setHeight = function(heightParam) {
if (checkCanConfigure("height")) {
height = extractStringFromParam(heightParam, height);
}
};
this.getMaxMessages = function() { return maxMessages; };
this.setMaxMessages = function(maxMessagesParam) {
maxMessages = extractIntFromParam(maxMessagesParam, maxMessages);
if (consoleWindowLoaded && isSupported) {
getConsoleWindow().setMaxMessages(maxMessages);
}
};
// Common methods
this.append = function(loggingEvent) {
if (isSupported) {
queuedLoggingEvents.push(loggingEvent);
// Force a check of whether the window is closed
var isSafeToAppend = safeToAppend();
if (!initialized || (consoleClosed && reopenWhenClosed)) {
init();
}
if (safeToAppend()) {
appendQueuedLoggingEvents();
}
}
};
var appendQueuedLoggingEvents = function(loggingEvent) {
while (queuedLoggingEvents.length > 0) {
var currentLoggingEvent = queuedLoggingEvents.shift();
var formattedMessage = appender.getLayout().format(currentLoggingEvent);
if (appender.getLayout().ignoresThrowable()) {
formattedMessage += currentLoggingEvent.getThrowableStrRep();
}
getConsoleWindow().log(currentLoggingEvent.level, formattedMessage);
}
if (focusConsoleWindow) {
getConsoleWindow().focus();
}
};
var writeHtml = function(doc) {
var lines = getConsoleHtmlLines();
doc.open();
for (var i = 0; i < lines.length; i++) {
doc.writeln(lines[i]);
}
doc.close();
};
var consoleClosed = false;
var pollConsoleWindow = function(windowTest, successCallback, errorMessage) {
function pollConsoleWindowLoaded() {
try {
// Test if the console has been closed while polling
if (consoleClosed) {
clearInterval(poll);
}
if (windowTest(getConsoleWindow())) {
clearInterval(poll);
successCallback();
}
} catch (ex) {
clearInterval(poll);
isSupported = false;
handleError(errorMessage, ex);
}
}
// Poll the pop-up since the onload event is not reliable
var poll = setInterval(pollConsoleWindowLoaded, 100);
};
// Define methods and properties that vary between subclasses
if (inPage) {
// InPageAppender
// Extract params
if (!containerElement || !containerElement.appendChild) {
isSupported = false;
handleError("InPageAppender.init: a container DOM element must be supplied for the console window");
return;
}
initiallyMinimized = extractBooleanFromParam(initiallyMinimized, appender.defaults.initiallyMinimized);
// Configuration methods. The function scope is used to prevent
// direct alteration to the appender configuration properties.
this.isInitiallyMinimized = function() { return initiallyMinimized; };
this.setInitiallyMinimized = function(initiallyMinimizedParam) {
if (checkCanConfigure("initiallyMinimized")) {
initiallyMinimized = bool(initiallyMinimizedParam);
}
};
// Define useful variables
var minimized = false;
var iframeContainerDiv;
var iframeId = uniqueId + "_InPageAppender_" + consoleAppenderId;
this.hide = function() {
iframeContainerDiv.style.display = "none";
minimized = true;
};
this.show = function() {
iframeContainerDiv.style.display = "block";
minimized = false;
};
this.isVisible = function() {
return !minimized;
};
this.close = function() {
if (!consoleClosed) {
iframeContainerDiv.parentNode.removeChild(iframeContainerDiv);
consoleClosed = true;
}
};
// Create init, getConsoleWindow and safeToAppend functions
init = function() {
var initErrorMessage = "InPageAppender.init: unable to create console iframe";
function finalInit() {
try {
getConsoleWindow().setNewestAtTop(newestMessageAtTop);
getConsoleWindow().setScrollToLatest(scrollToLatestMessage);
getConsoleWindow().setMaxMessages(maxMessages);
consoleWindowLoaded = true;
appendQueuedLoggingEvents();
if (initiallyMinimized) {
appender.hide();
}
} catch (ex) {
isSupported = false;
handleError(initErrorMessage, ex);
}
}
function writeToDocument() {
try {
var windowTest = function(win) { return bool(win.loaded); };
writeHtml(getConsoleWindow().document);
if (windowTest(getConsoleWindow())) {
finalInit();
} else {
pollConsoleWindow(windowTest, finalInit, initErrorMessage);
}
} catch (ex) {
isSupported = false;
handleError(initErrorMessage, ex);
}
}
minimized = initiallyMinimized;
iframeContainerDiv = containerElement.appendChild(document.createElement("div"));
iframeContainerDiv.style.width = width;
iframeContainerDiv.style.height = height;
iframeContainerDiv.style.border = "solid gray 1px";
// Adding an iframe using the DOM would be preferable, but it doesn't work
// in IE5 on Windows, or in Konqueror prior to version 3.5 - in Konqueror
// it creates the iframe fine but I haven't been able to find a way to obtain
// the iframe's window object
var iframeHtml = "<iframe id='" + iframeId + "' name='" + iframeId +
"' width='100%' height='100%' frameborder='0'" +
"scrolling='no'></iframe>";
iframeContainerDiv.innerHTML = iframeHtml;
consoleClosed = false;
// Write the console HTML to the iframe
var iframeDocumentExistsTest = function(win) { return bool(win) && bool(win.document); };
if (iframeDocumentExistsTest(getConsoleWindow())) {
writeToDocument();
} else {
pollConsoleWindow(iframeDocumentExistsTest, writeToDocument, initErrorMessage);
}
initialized = true;
};
getConsoleWindow = function() {
var iframe = window.frames[iframeId];
if (iframe) {
return iframe;
}
};
safeToAppend = function() {
if (isSupported && !consoleClosed) {
if (!consoleWindowLoaded && getConsoleWindow() && getConsoleWindow().loaded) {
consoleWindowLoaded = true;
}
return consoleWindowLoaded;
}
return false;
};
} else {
// PopUpAppender
// Extract params
useOldPopUp = extractBooleanFromParam(useOldPopUp, appender.defaults.useOldPopUp);
complainAboutPopUpBlocking = extractBooleanFromParam(complainAboutPopUpBlocking, appender.defaults.complainAboutPopUpBlocking);
reopenWhenClosed = extractBooleanFromParam(reopenWhenClosed, this.defaults.reopenWhenClosed);
// Configuration methods. The function scope is used to prevent
// direct alteration to the appender configuration properties.
this.isUseOldPopUp = function() { return useOldPopUp; };
this.setUseOldPopUp = function(useOldPopUpParam) {
if (checkCanConfigure("useOldPopUp")) {
useOldPopUp = bool(useOldPopUpParam);
}
};
this.isComplainAboutPopUpBlocking = function() { return complainAboutPopUpBlocking; };
this.setComplainAboutPopUpBlocking = function(complainAboutPopUpBlockingParam) {
if (checkCanConfigure("complainAboutPopUpBlocking")) {
complainAboutPopUpBlocking = bool(complainAboutPopUpBlockingParam);
}
};
this.isFocusPopUp = function() { return focusConsoleWindow; };
this.setFocusPopUp = function(focusPopUpParam) {
// This property can be safely altered after logging has started
focusConsoleWindow = bool(focusPopUpParam);
};
this.isReopenWhenClosed = function() { return reopenWhenClosed; };
this.setReopenWhenClosed = function(reopenWhenClosedParam) {
// This property can be safely altered after logging has started
reopenWhenClosed = bool(reopenWhenClosedParam);
};
this.close = function() {
try {
popUp.close();
} catch (e) {
// Do nothing
}
consoleClosed = true;
};
// Define useful variables
var popUp;
// Create init, getConsoleWindow and safeToAppend functions
init = function() {
var windowProperties = "width=" + width + ",height=" + height + ",status,resizable";
var windowName = "PopUp_" + location.host.replace(/[^a-z0-9]/gi, "_") + "_" + consoleAppenderId;
if (!useOldPopUp) {
// Ensure a previous window isn't used by using a unique name
windowName = windowName + "_" + uniqueId;
}
function finalInit() {
consoleWindowLoaded = true;
getConsoleWindow().setNewestAtTop(newestMessageAtTop);
getConsoleWindow().setScrollToLatest(scrollToLatestMessage);
getConsoleWindow().setMaxMessages(maxMessages);
appendQueuedLoggingEvents();
}
try {
popUp = window.open("", windowName, windowProperties);
consoleClosed = false;
if (popUp) {
if (useOldPopUp && popUp.loaded) {
popUp.mainPageReloaded();
finalInit();
} else {
writeHtml(popUp.document);
// Check if the pop-up window object is available
var popUpLoadedTest = function(win) { return bool(win) && win.loaded; };
if (popUp.loaded) {
finalInit();
} else {
pollConsoleWindow(popUpLoadedTest, finalInit, "PopUpAppender.init: unable to create console window");
}
}
} else {
isSupported = false;
logLog.warn("PopUpAppender.init: pop-ups blocked, please unblock to use PopUpAppender");
if (complainAboutPopUpBlocking) {
handleError("log4javascript: pop-up windows appear to be blocked. Please unblock them to use pop-up logging.");
}
}
} catch (ex) {
handleError("PopUpAppender.init: error creating pop-up", ex);
}
initialized = true;
};
getConsoleWindow = function() {
return popUp;
};
safeToAppend = function() {
if (isSupported && !isUndefined(popUp) && !consoleClosed) {
if (popUp.closed ||
(consoleWindowLoaded && isUndefined(popUp.closed))) { // Extra check for Opera
consoleClosed = true;
logLog.debug("PopUpAppender: pop-up closed");
return false;
}
if (!consoleWindowLoaded && popUp.loaded) {
consoleWindowLoaded = true;
}
}
return isSupported && consoleWindowLoaded && !consoleClosed;
};
}
if (enabled && !lazyInit) {
init();
}
// Expose getConsoleWindow so that automated tests can check the DOM
this.getConsoleWindow = getConsoleWindow;
};
/* ----------------------------------------------------------------- */
var PopUpAppender = function(lazyInit, layout, focusPopUp,
useOldPopUp, complainAboutPopUpBlocking, newestMessageAtTop,
scrollToLatestMessage, reopenWhenClosed, width, height,
maxMessages) {
var focusConsoleWindow = extractBooleanFromParam(focusPopUp, this.defaults.focusPopUp);
this.create(false, null, layout, lazyInit, focusConsoleWindow,
useOldPopUp, complainAboutPopUpBlocking,
newestMessageAtTop, scrollToLatestMessage, null, width, height,
reopenWhenClosed, maxMessages);
};
PopUpAppender.prototype = new ConsoleAppender();
PopUpAppender.prototype.defaults = {
layout: new PatternLayout("%d{HH:mm:ss} %-5p - %m{1}%n"),
focusPopUp: false,
lazyInit: true,
useOldPopUp: true,
complainAboutPopUpBlocking: true,
newestMessageAtTop: false,
scrollToLatestMessage: true,
width: "600",
height: "400",
reopenWhenClosed: false,
maxMessages: null
};
PopUpAppender.prototype.toString = function() {
return "[PopUpAppender]";
};
log4javascript.PopUpAppender = PopUpAppender;
/* ----------------------------------------------------------------- */
var InPageAppender = function(containerElement, lazyInit,
layout, initiallyMinimized, newestMessageAtTop,
scrollToLatestMessage, width, height, maxMessages) {
this.create(true, containerElement, layout, lazyInit, false,
null, null, newestMessageAtTop, scrollToLatestMessage,
initiallyMinimized, width, height, null, maxMessages);
};
InPageAppender.prototype = new ConsoleAppender();
InPageAppender.prototype.defaults = {
layout: new PatternLayout("%d{HH:mm:ss} %-5p - %m{1}%n"),
initiallyMinimized: false,
lazyInit: true,
newestMessageAtTop: false,
scrollToLatestMessage: true,
width: "100%",
height: "250px",
maxMessages: null
};
InPageAppender.prototype.toString = function() {
return "[InPageAppender]";
};
log4javascript.InPageAppender = InPageAppender;
// Next line for backwards compatibility
log4javascript.InlineAppender = InPageAppender;
})();
/* --------------------------------------------------------------------- */
// BrowserConsoleAppender (only works in Opera and Safari and Firefox with
// FireBug extension)
var BrowserConsoleAppender = function(layout) {
if (layout) {
this.setLayout(layout);
}
};
BrowserConsoleAppender.prototype = new log4javascript.Appender();
BrowserConsoleAppender.prototype.layout = new NullLayout();
BrowserConsoleAppender.prototype.threshold = Level.DEBUG;
BrowserConsoleAppender.prototype.append = function(loggingEvent) {
var appender = this;
var getFormattedMessage = function() {
var layout = appender.getLayout();
var formattedMessage = layout.format(loggingEvent);
if (layout.ignoresThrowable() && loggingEvent.exception) {
formattedMessage += loggingEvent.getThrowableStrRep();
}
return formattedMessage;
};
if ((typeof opera != "undefined") && opera.postError) { // Opera
opera.postError(getFormattedMessage());
} else if (window.console && window.console.log) { // Safari and FireBug
var formattedMesage = getFormattedMessage();
// Log to FireBug using its logging methods or revert to the console.log
// method in Safari
if (window.console.debug && Level.DEBUG.isGreaterOrEqual(loggingEvent.level)) {
window.console.debug(formattedMesage);
} else if (window.console.info && Level.INFO.equals(loggingEvent.level)) {
window.console.info(formattedMesage);
} else if (window.console.warn && Level.WARN.equals(loggingEvent.level)) {
window.console.warn(formattedMesage);
} else if (window.console.error && loggingEvent.level.isGreaterOrEqual(Level.ERROR)) {
window.console.error(formattedMesage);
} else {
window.console.log(formattedMesage);
}
}
};
BrowserConsoleAppender.prototype.toString = function() {
return "[BrowserConsoleAppender]";
};
log4javascript.BrowserConsoleAppender = BrowserConsoleAppender;
})();