Added in support for new HPC Dashboard View, ability to retrieve HPC Dashboard view...
[plstackapi.git] / planetstack / core / static / log4javascript-1.4.6 / js / log4javascript_uncompressed.js
1 /**\r
2  * Copyright 2013 Tim Down.\r
3  *\r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *      http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /**\r
18  * log4javascript\r
19  *\r
20  * log4javascript is a logging framework for JavaScript based on log4j\r
21  * for Java. This file contains all core log4javascript code and is the only\r
22  * file required to use log4javascript, unless you require support for\r
23  * document.domain, in which case you will also need console.html, which must be\r
24  * stored in the same directory as the main log4javascript.js file.\r
25  *\r
26  * Author: Tim Down <tim@log4javascript.org>\r
27  * Version: 1.4.6\r
28  * Edition: log4javascript\r
29  * Build date: 19 March 2013\r
30  * Website: http://log4javascript.org\r
31  */\r
32 \r
33 /* -------------------------------------------------------------------------- */\r
34 // Array-related stuff\r
35 \r
36 // Next three methods are solely for IE5, which is missing them\r
37 if (!Array.prototype.push) {\r
38         Array.prototype.push = function() {\r
39                 for (var i = 0, len = arguments.length; i < len; i++){\r
40                         this[this.length] = arguments[i];\r
41                 }\r
42                 return this.length;\r
43         };\r
44 }\r
45 \r
46 if (!Array.prototype.shift) {\r
47         Array.prototype.shift = function() {\r
48                 if (this.length > 0) {\r
49                         var firstItem = this[0];\r
50                         for (var i = 0, len = this.length - 1; i < len; i++) {\r
51                                 this[i] = this[i + 1];\r
52                         }\r
53                         this.length = this.length - 1;\r
54                         return firstItem;\r
55                 }\r
56         };\r
57 }\r
58 \r
59 if (!Array.prototype.splice) {\r
60         Array.prototype.splice = function(startIndex, deleteCount) {\r
61                 var itemsAfterDeleted = this.slice(startIndex + deleteCount);\r
62                 var itemsDeleted = this.slice(startIndex, startIndex + deleteCount);\r
63                 this.length = startIndex;\r
64                 // Copy the arguments into a proper Array object\r
65                 var argumentsArray = [];\r
66                 for (var i = 0, len = arguments.length; i < len; i++) {\r
67                         argumentsArray[i] = arguments[i];\r
68                 }\r
69                 var itemsToAppend = (argumentsArray.length > 2) ?\r
70                         itemsAfterDeleted = argumentsArray.slice(2).concat(itemsAfterDeleted) : itemsAfterDeleted;\r
71                 for (i = 0, len = itemsToAppend.length; i < len; i++) {\r
72                         this.push(itemsToAppend[i]);\r
73                 }\r
74                 return itemsDeleted;\r
75         };\r
76 }\r
77 \r
78 /* -------------------------------------------------------------------------- */\r
79 \r
80 var log4javascript = (function() {\r
81 \r
82         function isUndefined(obj) {\r
83                 return typeof obj == "undefined";\r
84         }\r
85 \r
86         /* ---------------------------------------------------------------------- */\r
87         // Custom event support\r
88 \r
89         function EventSupport() {}\r
90 \r
91         EventSupport.prototype = {\r
92                 eventTypes: [],\r
93                 eventListeners: {},\r
94                 setEventTypes: function(eventTypesParam) {\r
95                         if (eventTypesParam instanceof Array) {\r
96                                 this.eventTypes = eventTypesParam;\r
97                                 this.eventListeners = {};\r
98                                 for (var i = 0, len = this.eventTypes.length; i < len; i++) {\r
99                                         this.eventListeners[this.eventTypes[i]] = [];\r
100                                 }\r
101                         } else {\r
102                                 handleError("log4javascript.EventSupport [" + this + "]: setEventTypes: eventTypes parameter must be an Array");\r
103                         }\r
104                 },\r
105 \r
106                 addEventListener: function(eventType, listener) {\r
107                         if (typeof listener == "function") {\r
108                                 if (!array_contains(this.eventTypes, eventType)) {\r
109                                         handleError("log4javascript.EventSupport [" + this + "]: addEventListener: no event called '" + eventType + "'");\r
110                                 }\r
111                                 this.eventListeners[eventType].push(listener);\r
112                         } else {\r
113                                 handleError("log4javascript.EventSupport [" + this + "]: addEventListener: listener must be a function");\r
114                         }\r
115                 },\r
116 \r
117                 removeEventListener: function(eventType, listener) {\r
118                         if (typeof listener == "function") {\r
119                                 if (!array_contains(this.eventTypes, eventType)) {\r
120                                         handleError("log4javascript.EventSupport [" + this + "]: removeEventListener: no event called '" + eventType + "'");\r
121                                 }\r
122                                 array_remove(this.eventListeners[eventType], listener);\r
123                         } else {\r
124                                 handleError("log4javascript.EventSupport [" + this + "]: removeEventListener: listener must be a function");\r
125                         }\r
126                 },\r
127 \r
128                 dispatchEvent: function(eventType, eventArgs) {\r
129                         if (array_contains(this.eventTypes, eventType)) {\r
130                                 var listeners = this.eventListeners[eventType];\r
131                                 for (var i = 0, len = listeners.length; i < len; i++) {\r
132                                         listeners[i](this, eventType, eventArgs);\r
133                                 }\r
134                         } else {\r
135                                 handleError("log4javascript.EventSupport [" + this + "]: dispatchEvent: no event called '" + eventType + "'");\r
136                         }\r
137                 }\r
138         };\r
139 \r
140         /* -------------------------------------------------------------------------- */\r
141 \r
142         var applicationStartDate = new Date();\r
143         var uniqueId = "log4javascript_" + applicationStartDate.getTime() + "_" +\r
144                 Math.floor(Math.random() * 100000000);\r
145         var emptyFunction = function() {};\r
146         var newLine = "\r\n";\r
147         var pageLoaded = false;\r
148 \r
149         // Create main log4javascript object; this will be assigned public properties\r
150         function Log4JavaScript() {}\r
151         Log4JavaScript.prototype = new EventSupport();\r
152 \r
153         log4javascript = new Log4JavaScript();\r
154         log4javascript.version = "1.4.6";\r
155         log4javascript.edition = "log4javascript";\r
156 \r
157         /* -------------------------------------------------------------------------- */\r
158         // Utility functions\r
159 \r
160         function toStr(obj) {\r
161                 if (obj && obj.toString) {\r
162                         return obj.toString();\r
163                 } else {\r
164                         return String(obj);\r
165                 }\r
166         }\r
167 \r
168         function getExceptionMessage(ex) {\r
169                 if (ex.message) {\r
170                         return ex.message;\r
171                 } else if (ex.description) {\r
172                         return ex.description;\r
173                 } else {\r
174                         return toStr(ex);\r
175                 }\r
176         }\r
177 \r
178         // Gets the portion of the URL after the last slash\r
179         function getUrlFileName(url) {\r
180                 var lastSlashIndex = Math.max(url.lastIndexOf("/"), url.lastIndexOf("\\"));\r
181                 return url.substr(lastSlashIndex + 1);\r
182         }\r
183 \r
184         // Returns a nicely formatted representation of an error\r
185         function getExceptionStringRep(ex) {\r
186                 if (ex) {\r
187                         var exStr = "Exception: " + getExceptionMessage(ex);\r
188                         try {\r
189                                 if (ex.lineNumber) {\r
190                                         exStr += " on line number " + ex.lineNumber;\r
191                                 }\r
192                                 if (ex.fileName) {\r
193                                         exStr += " in file " + getUrlFileName(ex.fileName);\r
194                                 }\r
195                         } catch (localEx) {\r
196                                 logLog.warn("Unable to obtain file and line information for error");\r
197                         }\r
198                         if (showStackTraces && ex.stack) {\r
199                                 exStr += newLine + "Stack trace:" + newLine + ex.stack;\r
200                         }\r
201                         return exStr;\r
202                 }\r
203                 return null;\r
204         }\r
205 \r
206         function bool(obj) {\r
207                 return Boolean(obj);\r
208         }\r
209 \r
210         function trim(str) {\r
211                 return str.replace(/^\s+/, "").replace(/\s+$/, "");\r
212         }\r
213 \r
214         function splitIntoLines(text) {\r
215                 // Ensure all line breaks are \n only\r
216                 var text2 = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");\r
217                 return text2.split("\n");\r
218         }\r
219 \r
220         var urlEncode = (typeof window.encodeURIComponent != "undefined") ?\r
221                 function(str) {\r
222                         return encodeURIComponent(str);\r
223                 }: \r
224                 function(str) {\r
225                         return escape(str).replace(/\+/g, "%2B").replace(/"/g, "%22").replace(/'/g, "%27").replace(/\//g, "%2F").replace(/=/g, "%3D");\r
226                 };\r
227 \r
228         var urlDecode = (typeof window.decodeURIComponent != "undefined") ?\r
229                 function(str) {\r
230                         return decodeURIComponent(str);\r
231                 }: \r
232                 function(str) {\r
233                         return unescape(str).replace(/%2B/g, "+").replace(/%22/g, "\"").replace(/%27/g, "'").replace(/%2F/g, "/").replace(/%3D/g, "=");\r
234                 };\r
235 \r
236         function array_remove(arr, val) {\r
237                 var index = -1;\r
238                 for (var i = 0, len = arr.length; i < len; i++) {\r
239                         if (arr[i] === val) {\r
240                                 index = i;\r
241                                 break;\r
242                         }\r
243                 }\r
244                 if (index >= 0) {\r
245                         arr.splice(index, 1);\r
246                         return true;\r
247                 } else {\r
248                         return false;\r
249                 }\r
250         }\r
251 \r
252         function array_contains(arr, val) {\r
253                 for(var i = 0, len = arr.length; i < len; i++) {\r
254                         if (arr[i] == val) {\r
255                                 return true;\r
256                         }\r
257                 }\r
258                 return false;\r
259         }\r
260 \r
261         function extractBooleanFromParam(param, defaultValue) {\r
262                 if (isUndefined(param)) {\r
263                         return defaultValue;\r
264                 } else {\r
265                         return bool(param);\r
266                 }\r
267         }\r
268 \r
269         function extractStringFromParam(param, defaultValue) {\r
270                 if (isUndefined(param)) {\r
271                         return defaultValue;\r
272                 } else {\r
273                         return String(param);\r
274                 }\r
275         }\r
276 \r
277         function extractIntFromParam(param, defaultValue) {\r
278                 if (isUndefined(param)) {\r
279                         return defaultValue;\r
280                 } else {\r
281                         try {\r
282                                 var value = parseInt(param, 10);\r
283                                 return isNaN(value) ? defaultValue : value;\r
284                         } catch (ex) {\r
285                                 logLog.warn("Invalid int param " + param, ex);\r
286                                 return defaultValue;\r
287                         }\r
288                 }\r
289         }\r
290 \r
291         function extractFunctionFromParam(param, defaultValue) {\r
292                 if (typeof param == "function") {\r
293                         return param;\r
294                 } else {\r
295                         return defaultValue;\r
296                 }\r
297         }\r
298 \r
299         function isError(err) {\r
300                 return (err instanceof Error);\r
301         }\r
302 \r
303         if (!Function.prototype.apply){\r
304                 Function.prototype.apply = function(obj, args) {\r
305                         var methodName = "__apply__";\r
306                         if (typeof obj[methodName] != "undefined") {\r
307                                 methodName += String(Math.random()).substr(2);\r
308                         }\r
309                         obj[methodName] = this;\r
310 \r
311                         var argsStrings = [];\r
312                         for (var i = 0, len = args.length; i < len; i++) {\r
313                                 argsStrings[i] = "args[" + i + "]";\r
314                         }\r
315                         var script = "obj." + methodName + "(" + argsStrings.join(",") + ")";\r
316                         var returnValue = eval(script);\r
317                         delete obj[methodName];\r
318                         return returnValue;\r
319                 };\r
320         }\r
321 \r
322         if (!Function.prototype.call){\r
323                 Function.prototype.call = function(obj) {\r
324                         var args = [];\r
325                         for (var i = 1, len = arguments.length; i < len; i++) {\r
326                                 args[i - 1] = arguments[i];\r
327                         }\r
328                         return this.apply(obj, args);\r
329                 };\r
330         }\r
331 \r
332         function getListenersPropertyName(eventName) {\r
333                 return "__log4javascript_listeners__" + eventName;\r
334         }\r
335 \r
336         function addEvent(node, eventName, listener, useCapture, win) {\r
337                 win = win ? win : window;\r
338                 if (node.addEventListener) {\r
339                         node.addEventListener(eventName, listener, useCapture);\r
340                 } else if (node.attachEvent) {\r
341                         node.attachEvent("on" + eventName, listener);\r
342                 } else {\r
343                         var propertyName = getListenersPropertyName(eventName);\r
344                         if (!node[propertyName]) {\r
345                                 node[propertyName] = [];\r
346                                 // Set event handler\r
347                                 node["on" + eventName] = function(evt) {\r
348                                         evt = getEvent(evt, win);\r
349                                         var listenersPropertyName = getListenersPropertyName(eventName);\r
350 \r
351                                         // Clone the array of listeners to leave the original untouched\r
352                                         var listeners = this[listenersPropertyName].concat([]);\r
353                                         var currentListener;\r
354 \r
355                                         // Call each listener in turn\r
356                                         while ((currentListener = listeners.shift())) {\r
357                                                 currentListener.call(this, evt);\r
358                                         }\r
359                                 };\r
360                         }\r
361                         node[propertyName].push(listener);\r
362                 }\r
363         }\r
364 \r
365         function removeEvent(node, eventName, listener, useCapture) {\r
366                 if (node.removeEventListener) {\r
367                         node.removeEventListener(eventName, listener, useCapture);\r
368                 } else if (node.detachEvent) {\r
369                         node.detachEvent("on" + eventName, listener);\r
370                 } else {\r
371                         var propertyName = getListenersPropertyName(eventName);\r
372                         if (node[propertyName]) {\r
373                                 array_remove(node[propertyName], listener);\r
374                         }\r
375                 }\r
376         }\r
377 \r
378         function getEvent(evt, win) {\r
379                 win = win ? win : window;\r
380                 return evt ? evt : win.event;\r
381         }\r
382 \r
383         function stopEventPropagation(evt) {\r
384                 if (evt.stopPropagation) {\r
385                         evt.stopPropagation();\r
386                 } else if (typeof evt.cancelBubble != "undefined") {\r
387                         evt.cancelBubble = true;\r
388                 }\r
389                 evt.returnValue = false;\r
390         }\r
391 \r
392         /* ---------------------------------------------------------------------- */\r
393         // Simple logging for log4javascript itself\r
394 \r
395         var logLog = {\r
396                 quietMode: false,\r
397 \r
398                 debugMessages: [],\r
399 \r
400                 setQuietMode: function(quietMode) {\r
401                         this.quietMode = bool(quietMode);\r
402                 },\r
403 \r
404                 numberOfErrors: 0,\r
405 \r
406                 alertAllErrors: false,\r
407 \r
408                 setAlertAllErrors: function(alertAllErrors) {\r
409                         this.alertAllErrors = alertAllErrors;\r
410                 },\r
411 \r
412                 debug: function(message) {\r
413                         this.debugMessages.push(message);\r
414                 },\r
415 \r
416                 displayDebug: function() {\r
417                         alert(this.debugMessages.join(newLine));\r
418                 },\r
419 \r
420                 warn: function(message, exception) {\r
421                 },\r
422 \r
423                 error: function(message, exception) {\r
424                         if (++this.numberOfErrors == 1 || this.alertAllErrors) {\r
425                                 if (!this.quietMode) {\r
426                                         var alertMessage = "log4javascript error: " + message;\r
427                                         if (exception) {\r
428                                                 alertMessage += newLine + newLine + "Original error: " + getExceptionStringRep(exception);\r
429                                         }\r
430                                         alert(alertMessage);\r
431                                 }\r
432                         }\r
433                 }\r
434         };\r
435         log4javascript.logLog = logLog;\r
436 \r
437         log4javascript.setEventTypes(["load", "error"]);\r
438 \r
439         function handleError(message, exception) {\r
440                 logLog.error(message, exception);\r
441                 log4javascript.dispatchEvent("error", { "message": message, "exception": exception });\r
442         }\r
443 \r
444         log4javascript.handleError = handleError;\r
445 \r
446         /* ---------------------------------------------------------------------- */\r
447 \r
448         var enabled = !((typeof log4javascript_disabled != "undefined") &&\r
449                                         log4javascript_disabled);\r
450 \r
451         log4javascript.setEnabled = function(enable) {\r
452                 enabled = bool(enable);\r
453         };\r
454 \r
455         log4javascript.isEnabled = function() {\r
456                 return enabled;\r
457         };\r
458 \r
459         var useTimeStampsInMilliseconds = true;\r
460 \r
461         log4javascript.setTimeStampsInMilliseconds = function(timeStampsInMilliseconds) {\r
462                 useTimeStampsInMilliseconds = bool(timeStampsInMilliseconds);\r
463         };\r
464 \r
465         log4javascript.isTimeStampsInMilliseconds = function() {\r
466                 return useTimeStampsInMilliseconds;\r
467         };\r
468         \r
469 \r
470         // This evaluates the given expression in the current scope, thus allowing\r
471         // scripts to access private variables. Particularly useful for testing\r
472         log4javascript.evalInScope = function(expr) {\r
473                 return eval(expr);\r
474         };\r
475 \r
476         var showStackTraces = false;\r
477 \r
478         log4javascript.setShowStackTraces = function(show) {\r
479                 showStackTraces = bool(show);\r
480         };\r
481 \r
482         /* ---------------------------------------------------------------------- */\r
483         // Levels\r
484 \r
485         var Level = function(level, name) {\r
486                 this.level = level;\r
487                 this.name = name;\r
488         };\r
489 \r
490         Level.prototype = {\r
491                 toString: function() {\r
492                         return this.name;\r
493                 },\r
494                 equals: function(level) {\r
495                         return this.level == level.level;\r
496                 },\r
497                 isGreaterOrEqual: function(level) {\r
498                         return this.level >= level.level;\r
499                 }\r
500         };\r
501 \r
502         Level.ALL = new Level(Number.MIN_VALUE, "ALL");\r
503         Level.TRACE = new Level(10000, "TRACE");\r
504         Level.DEBUG = new Level(20000, "DEBUG");\r
505         Level.INFO = new Level(30000, "INFO");\r
506         Level.WARN = new Level(40000, "WARN");\r
507         Level.ERROR = new Level(50000, "ERROR");\r
508         Level.FATAL = new Level(60000, "FATAL");\r
509         Level.OFF = new Level(Number.MAX_VALUE, "OFF");\r
510 \r
511         log4javascript.Level = Level;\r
512 \r
513         /* ---------------------------------------------------------------------- */\r
514         // Timers\r
515 \r
516         function Timer(name, level) {\r
517                 this.name = name;\r
518                 this.level = isUndefined(level) ? Level.INFO : level;\r
519                 this.start = new Date();\r
520         }\r
521 \r
522         Timer.prototype.getElapsedTime = function() {\r
523                 return new Date().getTime() - this.start.getTime();\r
524         };\r
525 \r
526         /* ---------------------------------------------------------------------- */\r
527         // Loggers\r
528 \r
529         var anonymousLoggerName = "[anonymous]";\r
530         var defaultLoggerName = "[default]";\r
531         var nullLoggerName = "[null]";\r
532         var rootLoggerName = "root";\r
533 \r
534         function Logger(name) {\r
535                 this.name = name;\r
536                 this.parent = null;\r
537                 this.children = [];\r
538 \r
539                 var appenders = [];\r
540                 var loggerLevel = null;\r
541                 var isRoot = (this.name === rootLoggerName);\r
542                 var isNull = (this.name === nullLoggerName);\r
543 \r
544                 var appenderCache = null;\r
545                 var appenderCacheInvalidated = false;\r
546                 \r
547                 this.addChild = function(childLogger) {\r
548                         this.children.push(childLogger);\r
549                         childLogger.parent = this;\r
550                         childLogger.invalidateAppenderCache();\r
551                 };\r
552 \r
553                 // Additivity\r
554                 var additive = true;\r
555                 this.getAdditivity = function() {\r
556                         return additive;\r
557                 };\r
558 \r
559                 this.setAdditivity = function(additivity) {\r
560                         var valueChanged = (additive != additivity);\r
561                         additive = additivity;\r
562                         if (valueChanged) {\r
563                                 this.invalidateAppenderCache();\r
564                         }\r
565                 };\r
566 \r
567                 // Create methods that use the appenders variable in this scope\r
568                 this.addAppender = function(appender) {\r
569                         if (isNull) {\r
570                                 handleError("Logger.addAppender: you may not add an appender to the null logger");\r
571                         } else {\r
572                                 if (appender instanceof log4javascript.Appender) {\r
573                                         if (!array_contains(appenders, appender)) {\r
574                                                 appenders.push(appender);\r
575                                                 appender.setAddedToLogger(this);\r
576                                                 this.invalidateAppenderCache();\r
577                                         }\r
578                                 } else {\r
579                                         handleError("Logger.addAppender: appender supplied ('" +\r
580                                                 toStr(appender) + "') is not a subclass of Appender");\r
581                                 }\r
582                         }\r
583                 };\r
584 \r
585                 this.removeAppender = function(appender) {\r
586                         array_remove(appenders, appender);\r
587                         appender.setRemovedFromLogger(this);\r
588                         this.invalidateAppenderCache();\r
589                 };\r
590 \r
591                 this.removeAllAppenders = function() {\r
592                         var appenderCount = appenders.length;\r
593                         if (appenderCount > 0) {\r
594                                 for (var i = 0; i < appenderCount; i++) {\r
595                                         appenders[i].setRemovedFromLogger(this);\r
596                                 }\r
597                                 appenders.length = 0;\r
598                                 this.invalidateAppenderCache();\r
599                         }\r
600                 };\r
601 \r
602                 this.getEffectiveAppenders = function() {\r
603                         if (appenderCache === null || appenderCacheInvalidated) {\r
604                                 // Build appender cache\r
605                                 var parentEffectiveAppenders = (isRoot || !this.getAdditivity()) ?\r
606                                         [] : this.parent.getEffectiveAppenders();\r
607                                 appenderCache = parentEffectiveAppenders.concat(appenders);\r
608                                 appenderCacheInvalidated = false;\r
609                         }\r
610                         return appenderCache;\r
611                 };\r
612                 \r
613                 this.invalidateAppenderCache = function() {\r
614                         appenderCacheInvalidated = true;\r
615                         for (var i = 0, len = this.children.length; i < len; i++) {\r
616                                 this.children[i].invalidateAppenderCache();\r
617                         }\r
618                 };\r
619 \r
620                 this.log = function(level, params) {\r
621                         if (enabled && level.isGreaterOrEqual(this.getEffectiveLevel())) {\r
622                                 // Check whether last param is an exception\r
623                                 var exception;\r
624                                 var finalParamIndex = params.length - 1;\r
625                                 var lastParam = params[finalParamIndex];\r
626                                 if (params.length > 1 && isError(lastParam)) {\r
627                                         exception = lastParam;\r
628                                         finalParamIndex--;\r
629                                 }\r
630 \r
631                                 // Construct genuine array for the params\r
632                                 var messages = [];\r
633                                 for (var i = 0; i <= finalParamIndex; i++) {\r
634                                         messages[i] = params[i];\r
635                                 }\r
636 \r
637                                 var loggingEvent = new LoggingEvent(\r
638                                         this, new Date(), level, messages, exception);\r
639 \r
640                                 this.callAppenders(loggingEvent);\r
641                         }\r
642                 };\r
643 \r
644                 this.callAppenders = function(loggingEvent) {\r
645                         var effectiveAppenders = this.getEffectiveAppenders();\r
646                         for (var i = 0, len = effectiveAppenders.length; i < len; i++) {\r
647                                 effectiveAppenders[i].doAppend(loggingEvent);\r
648                         }\r
649                 };\r
650 \r
651                 this.setLevel = function(level) {\r
652                         // Having a level of null on the root logger would be very bad.\r
653                         if (isRoot && level === null) {\r
654                                 handleError("Logger.setLevel: you cannot set the level of the root logger to null");\r
655                         } else if (level instanceof Level) {\r
656                                 loggerLevel = level;\r
657                         } else {\r
658                                 handleError("Logger.setLevel: level supplied to logger " +\r
659                                         this.name + " is not an instance of log4javascript.Level");\r
660                         }\r
661                 };\r
662 \r
663                 this.getLevel = function() {\r
664                         return loggerLevel;\r
665                 };\r
666 \r
667                 this.getEffectiveLevel = function() {\r
668                         for (var logger = this; logger !== null; logger = logger.parent) {\r
669                                 var level = logger.getLevel();\r
670                                 if (level !== null) {\r
671                                         return level;\r
672                                 }\r
673                         }\r
674                 };\r
675 \r
676                 this.group = function(name, initiallyExpanded) {\r
677                         if (enabled) {\r
678                                 var effectiveAppenders = this.getEffectiveAppenders();\r
679                                 for (var i = 0, len = effectiveAppenders.length; i < len; i++) {\r
680                                         effectiveAppenders[i].group(name, initiallyExpanded);\r
681                                 }\r
682                         }\r
683                 };\r
684 \r
685                 this.groupEnd = function() {\r
686                         if (enabled) {\r
687                                 var effectiveAppenders = this.getEffectiveAppenders();\r
688                                 for (var i = 0, len = effectiveAppenders.length; i < len; i++) {\r
689                                         effectiveAppenders[i].groupEnd();\r
690                                 }\r
691                         }\r
692                 };\r
693 \r
694                 var timers = {};\r
695 \r
696                 this.time = function(name, level) {\r
697                         if (enabled) {\r
698                                 if (isUndefined(name)) {\r
699                                         handleError("Logger.time: a name for the timer must be supplied");\r
700                                 } else if (level && !(level instanceof Level)) {\r
701                                         handleError("Logger.time: level supplied to timer " +\r
702                                                 name + " is not an instance of log4javascript.Level");\r
703                                 } else {\r
704                                         timers[name] = new Timer(name, level);\r
705                                 }\r
706                         }\r
707                 };\r
708 \r
709                 this.timeEnd = function(name) {\r
710                         if (enabled) {\r
711                                 if (isUndefined(name)) {\r
712                                         handleError("Logger.timeEnd: a name for the timer must be supplied");\r
713                                 } else if (timers[name]) {\r
714                                         var timer = timers[name];\r
715                                         var milliseconds = timer.getElapsedTime();\r
716                                         this.log(timer.level, ["Timer " + toStr(name) + " completed in " + milliseconds + "ms"]);\r
717                                         delete timers[name];\r
718                                 } else {\r
719                                         logLog.warn("Logger.timeEnd: no timer found with name " + name);\r
720                                 }\r
721                         }\r
722                 };\r
723 \r
724                 this.assert = function(expr) {\r
725                         if (enabled && !expr) {\r
726                                 var args = [];\r
727                                 for (var i = 1, len = arguments.length; i < len; i++) {\r
728                                         args.push(arguments[i]);\r
729                                 }\r
730                                 args = (args.length > 0) ? args : ["Assertion Failure"];\r
731                                 args.push(newLine);\r
732                                 args.push(expr);\r
733                                 this.log(Level.ERROR, args);\r
734                         }\r
735                 };\r
736 \r
737                 this.toString = function() {\r
738                         return "Logger[" + this.name + "]";\r
739                 };\r
740         }\r
741 \r
742         Logger.prototype = {\r
743                 trace: function() {\r
744                         this.log(Level.TRACE, arguments);\r
745                 },\r
746 \r
747                 debug: function() {\r
748                         this.log(Level.DEBUG, arguments);\r
749                 },\r
750 \r
751                 info: function() {\r
752                         this.log(Level.INFO, arguments);\r
753                 },\r
754 \r
755                 warn: function() {\r
756                         this.log(Level.WARN, arguments);\r
757                 },\r
758 \r
759                 error: function() {\r
760                         this.log(Level.ERROR, arguments);\r
761                 },\r
762 \r
763                 fatal: function() {\r
764                         this.log(Level.FATAL, arguments);\r
765                 },\r
766 \r
767                 isEnabledFor: function(level) {\r
768                         return level.isGreaterOrEqual(this.getEffectiveLevel());\r
769                 },\r
770 \r
771                 isTraceEnabled: function() {\r
772                         return this.isEnabledFor(Level.TRACE);\r
773                 },\r
774 \r
775                 isDebugEnabled: function() {\r
776                         return this.isEnabledFor(Level.DEBUG);\r
777                 },\r
778 \r
779                 isInfoEnabled: function() {\r
780                         return this.isEnabledFor(Level.INFO);\r
781                 },\r
782 \r
783                 isWarnEnabled: function() {\r
784                         return this.isEnabledFor(Level.WARN);\r
785                 },\r
786 \r
787                 isErrorEnabled: function() {\r
788                         return this.isEnabledFor(Level.ERROR);\r
789                 },\r
790 \r
791                 isFatalEnabled: function() {\r
792                         return this.isEnabledFor(Level.FATAL);\r
793                 }\r
794         };\r
795 \r
796         Logger.prototype.trace.isEntryPoint = true;\r
797         Logger.prototype.debug.isEntryPoint = true;\r
798         Logger.prototype.info.isEntryPoint = true;\r
799         Logger.prototype.warn.isEntryPoint = true;\r
800         Logger.prototype.error.isEntryPoint = true;\r
801         Logger.prototype.fatal.isEntryPoint = true;\r
802 \r
803         /* ---------------------------------------------------------------------- */\r
804         // Logger access methods\r
805 \r
806         // Hashtable of loggers keyed by logger name\r
807         var loggers = {};\r
808         var loggerNames = [];\r
809 \r
810         var ROOT_LOGGER_DEFAULT_LEVEL = Level.DEBUG;\r
811         var rootLogger = new Logger(rootLoggerName);\r
812         rootLogger.setLevel(ROOT_LOGGER_DEFAULT_LEVEL);\r
813 \r
814         log4javascript.getRootLogger = function() {\r
815                 return rootLogger;\r
816         };\r
817 \r
818         log4javascript.getLogger = function(loggerName) {\r
819                 // Use default logger if loggerName is not specified or invalid\r
820                 if (!(typeof loggerName == "string")) {\r
821                         loggerName = anonymousLoggerName;\r
822                         logLog.warn("log4javascript.getLogger: non-string logger name " +\r
823                                 toStr(loggerName) + " supplied, returning anonymous logger");\r
824                 }\r
825 \r
826                 // Do not allow retrieval of the root logger by name\r
827                 if (loggerName == rootLoggerName) {\r
828                         handleError("log4javascript.getLogger: root logger may not be obtained by name");\r
829                 }\r
830 \r
831                 // Create the logger for this name if it doesn't already exist\r
832                 if (!loggers[loggerName]) {\r
833                         var logger = new Logger(loggerName);\r
834                         loggers[loggerName] = logger;\r
835                         loggerNames.push(loggerName);\r
836 \r
837                         // Set up parent logger, if it doesn't exist\r
838                         var lastDotIndex = loggerName.lastIndexOf(".");\r
839                         var parentLogger;\r
840                         if (lastDotIndex > -1) {\r
841                                 var parentLoggerName = loggerName.substring(0, lastDotIndex);\r
842                                 parentLogger = log4javascript.getLogger(parentLoggerName); // Recursively sets up grandparents etc.\r
843                         } else {\r
844                                 parentLogger = rootLogger;\r
845                         }\r
846                         parentLogger.addChild(logger);\r
847                 }\r
848                 return loggers[loggerName];\r
849         };\r
850 \r
851         var defaultLogger = null;\r
852         log4javascript.getDefaultLogger = function() {\r
853                 if (!defaultLogger) {\r
854                         defaultLogger = log4javascript.getLogger(defaultLoggerName);\r
855                         var a = new log4javascript.PopUpAppender();\r
856                         defaultLogger.addAppender(a);\r
857                 }\r
858                 return defaultLogger;\r
859         };\r
860 \r
861         var nullLogger = null;\r
862         log4javascript.getNullLogger = function() {\r
863                 if (!nullLogger) {\r
864                         nullLogger = new Logger(nullLoggerName);\r
865                         nullLogger.setLevel(Level.OFF);\r
866                 }\r
867                 return nullLogger;\r
868         };\r
869 \r
870         // Destroys all loggers\r
871         log4javascript.resetConfiguration = function() {\r
872                 rootLogger.setLevel(ROOT_LOGGER_DEFAULT_LEVEL);\r
873                 loggers = {};\r
874         };\r
875 \r
876         /* ---------------------------------------------------------------------- */\r
877         // Logging events\r
878 \r
879         var LoggingEvent = function(logger, timeStamp, level, messages,\r
880                         exception) {\r
881                 this.logger = logger;\r
882                 this.timeStamp = timeStamp;\r
883                 this.timeStampInMilliseconds = timeStamp.getTime();\r
884                 this.timeStampInSeconds = Math.floor(this.timeStampInMilliseconds / 1000);\r
885                 this.milliseconds = this.timeStamp.getMilliseconds();\r
886                 this.level = level;\r
887                 this.messages = messages;\r
888                 this.exception = exception;\r
889         };\r
890 \r
891         LoggingEvent.prototype = {\r
892                 getThrowableStrRep: function() {\r
893                         return this.exception ?\r
894                                 getExceptionStringRep(this.exception) : "";\r
895                 },\r
896                 getCombinedMessages: function() {\r
897                         return (this.messages.length == 1) ? this.messages[0] :\r
898                                    this.messages.join(newLine);\r
899                 },\r
900                 toString: function() {\r
901                         return "LoggingEvent[" + this.level + "]";\r
902                 }\r
903         };\r
904 \r
905         log4javascript.LoggingEvent = LoggingEvent;\r
906 \r
907         /* ---------------------------------------------------------------------- */\r
908         // Layout prototype\r
909 \r
910         var Layout = function() {\r
911         };\r
912 \r
913         Layout.prototype = {\r
914                 defaults: {\r
915                         loggerKey: "logger",\r
916                         timeStampKey: "timestamp",\r
917                         millisecondsKey: "milliseconds",\r
918                         levelKey: "level",\r
919                         messageKey: "message",\r
920                         exceptionKey: "exception",\r
921                         urlKey: "url"\r
922                 },\r
923                 loggerKey: "logger",\r
924                 timeStampKey: "timestamp",\r
925                 millisecondsKey: "milliseconds",\r
926                 levelKey: "level",\r
927                 messageKey: "message",\r
928                 exceptionKey: "exception",\r
929                 urlKey: "url",\r
930                 batchHeader: "",\r
931                 batchFooter: "",\r
932                 batchSeparator: "",\r
933                 returnsPostData: false,\r
934                 overrideTimeStampsSetting: false,\r
935                 useTimeStampsInMilliseconds: null,\r
936 \r
937                 format: function() {\r
938                         handleError("Layout.format: layout supplied has no format() method");\r
939                 },\r
940 \r
941                 ignoresThrowable: function() {\r
942                         handleError("Layout.ignoresThrowable: layout supplied has no ignoresThrowable() method");\r
943                 },\r
944 \r
945                 getContentType: function() {\r
946                         return "text/plain";\r
947                 },\r
948 \r
949                 allowBatching: function() {\r
950                         return true;\r
951                 },\r
952 \r
953                 setTimeStampsInMilliseconds: function(timeStampsInMilliseconds) {\r
954                         this.overrideTimeStampsSetting = true;\r
955                         this.useTimeStampsInMilliseconds = bool(timeStampsInMilliseconds);\r
956                 },\r
957 \r
958                 isTimeStampsInMilliseconds: function() {\r
959                         return this.overrideTimeStampsSetting ?\r
960                                 this.useTimeStampsInMilliseconds : useTimeStampsInMilliseconds;\r
961                 },\r
962 \r
963                 getTimeStampValue: function(loggingEvent) {\r
964                         return this.isTimeStampsInMilliseconds() ?\r
965                                 loggingEvent.timeStampInMilliseconds : loggingEvent.timeStampInSeconds;\r
966                 },\r
967 \r
968                 getDataValues: function(loggingEvent, combineMessages) {\r
969                         var dataValues = [\r
970                                 [this.loggerKey, loggingEvent.logger.name],\r
971                                 [this.timeStampKey, this.getTimeStampValue(loggingEvent)],\r
972                                 [this.levelKey, loggingEvent.level.name],\r
973                                 [this.urlKey, window.location.href],\r
974                                 [this.messageKey, combineMessages ? loggingEvent.getCombinedMessages() : loggingEvent.messages]\r
975                         ];\r
976                         if (!this.isTimeStampsInMilliseconds()) {\r
977                                 dataValues.push([this.millisecondsKey, loggingEvent.milliseconds]);\r
978                         }\r
979                         if (loggingEvent.exception) {\r
980                                 dataValues.push([this.exceptionKey, getExceptionStringRep(loggingEvent.exception)]);\r
981                         }\r
982                         if (this.hasCustomFields()) {\r
983                                 for (var i = 0, len = this.customFields.length; i < len; i++) {\r
984                                         var val = this.customFields[i].value;\r
985 \r
986                                         // Check if the value is a function. If so, execute it, passing it the\r
987                                         // current layout and the logging event\r
988                                         if (typeof val === "function") {\r
989                                                 val = val(this, loggingEvent);\r
990                                         }\r
991                                         dataValues.push([this.customFields[i].name, val]);\r
992                                 }\r
993                         }\r
994                         return dataValues;\r
995                 },\r
996 \r
997                 setKeys: function(loggerKey, timeStampKey, levelKey, messageKey,\r
998                                 exceptionKey, urlKey, millisecondsKey) {\r
999                         this.loggerKey = extractStringFromParam(loggerKey, this.defaults.loggerKey);\r
1000                         this.timeStampKey = extractStringFromParam(timeStampKey, this.defaults.timeStampKey);\r
1001                         this.levelKey = extractStringFromParam(levelKey, this.defaults.levelKey);\r
1002                         this.messageKey = extractStringFromParam(messageKey, this.defaults.messageKey);\r
1003                         this.exceptionKey = extractStringFromParam(exceptionKey, this.defaults.exceptionKey);\r
1004                         this.urlKey = extractStringFromParam(urlKey, this.defaults.urlKey);\r
1005                         this.millisecondsKey = extractStringFromParam(millisecondsKey, this.defaults.millisecondsKey);\r
1006                 },\r
1007 \r
1008                 setCustomField: function(name, value) {\r
1009                         var fieldUpdated = false;\r
1010                         for (var i = 0, len = this.customFields.length; i < len; i++) {\r
1011                                 if (this.customFields[i].name === name) {\r
1012                                         this.customFields[i].value = value;\r
1013                                         fieldUpdated = true;\r
1014                                 }\r
1015                         }\r
1016                         if (!fieldUpdated) {\r
1017                                 this.customFields.push({"name": name, "value": value});\r
1018                         }\r
1019                 },\r
1020 \r
1021                 hasCustomFields: function() {\r
1022                         return (this.customFields.length > 0);\r
1023                 },\r
1024 \r
1025                 toString: function() {\r
1026                         handleError("Layout.toString: all layouts must override this method");\r
1027                 }\r
1028         };\r
1029 \r
1030         log4javascript.Layout = Layout;\r
1031 \r
1032         /* ---------------------------------------------------------------------- */\r
1033         // Appender prototype\r
1034 \r
1035         var Appender = function() {};\r
1036 \r
1037         Appender.prototype = new EventSupport();\r
1038 \r
1039         Appender.prototype.layout = new PatternLayout();\r
1040         Appender.prototype.threshold = Level.ALL;\r
1041         Appender.prototype.loggers = [];\r
1042 \r
1043         // Performs threshold checks before delegating actual logging to the\r
1044         // subclass's specific append method.\r
1045         Appender.prototype.doAppend = function(loggingEvent) {\r
1046                 if (enabled && loggingEvent.level.level >= this.threshold.level) {\r
1047                         this.append(loggingEvent);\r
1048                 }\r
1049         };\r
1050 \r
1051         Appender.prototype.append = function(loggingEvent) {};\r
1052 \r
1053         Appender.prototype.setLayout = function(layout) {\r
1054                 if (layout instanceof Layout) {\r
1055                         this.layout = layout;\r
1056                 } else {\r
1057                         handleError("Appender.setLayout: layout supplied to " +\r
1058                                 this.toString() + " is not a subclass of Layout");\r
1059                 }\r
1060         };\r
1061 \r
1062         Appender.prototype.getLayout = function() {\r
1063                 return this.layout;\r
1064         };\r
1065 \r
1066         Appender.prototype.setThreshold = function(threshold) {\r
1067                 if (threshold instanceof Level) {\r
1068                         this.threshold = threshold;\r
1069                 } else {\r
1070                         handleError("Appender.setThreshold: threshold supplied to " +\r
1071                                 this.toString() + " is not a subclass of Level");\r
1072                 }\r
1073         };\r
1074 \r
1075         Appender.prototype.getThreshold = function() {\r
1076                 return this.threshold;\r
1077         };\r
1078 \r
1079         Appender.prototype.setAddedToLogger = function(logger) {\r
1080                 this.loggers.push(logger);\r
1081         };\r
1082 \r
1083         Appender.prototype.setRemovedFromLogger = function(logger) {\r
1084                 array_remove(this.loggers, logger);\r
1085         };\r
1086 \r
1087         Appender.prototype.group = emptyFunction;\r
1088         Appender.prototype.groupEnd = emptyFunction;\r
1089 \r
1090         Appender.prototype.toString = function() {\r
1091                 handleError("Appender.toString: all appenders must override this method");\r
1092         };\r
1093 \r
1094         log4javascript.Appender = Appender;\r
1095 \r
1096         /* ---------------------------------------------------------------------- */\r
1097         // SimpleLayout \r
1098 \r
1099         function SimpleLayout() {\r
1100                 this.customFields = [];\r
1101         }\r
1102 \r
1103         SimpleLayout.prototype = new Layout();\r
1104 \r
1105         SimpleLayout.prototype.format = function(loggingEvent) {\r
1106                 return loggingEvent.level.name + " - " + loggingEvent.getCombinedMessages();\r
1107         };\r
1108 \r
1109         SimpleLayout.prototype.ignoresThrowable = function() {\r
1110             return true;\r
1111         };\r
1112 \r
1113         SimpleLayout.prototype.toString = function() {\r
1114             return "SimpleLayout";\r
1115         };\r
1116 \r
1117         log4javascript.SimpleLayout = SimpleLayout;\r
1118         /* ----------------------------------------------------------------------- */\r
1119         // NullLayout \r
1120 \r
1121         function NullLayout() {\r
1122                 this.customFields = [];\r
1123         }\r
1124 \r
1125         NullLayout.prototype = new Layout();\r
1126 \r
1127         NullLayout.prototype.format = function(loggingEvent) {\r
1128                 return loggingEvent.messages;\r
1129         };\r
1130 \r
1131         NullLayout.prototype.ignoresThrowable = function() {\r
1132             return true;\r
1133         };\r
1134 \r
1135         NullLayout.prototype.toString = function() {\r
1136             return "NullLayout";\r
1137         };\r
1138 \r
1139         log4javascript.NullLayout = NullLayout;\r
1140 /* ---------------------------------------------------------------------- */\r
1141         // XmlLayout\r
1142 \r
1143         function XmlLayout(combineMessages) {\r
1144                 this.combineMessages = extractBooleanFromParam(combineMessages, true);\r
1145                 this.customFields = [];\r
1146         }\r
1147 \r
1148         XmlLayout.prototype = new Layout();\r
1149 \r
1150         XmlLayout.prototype.isCombinedMessages = function() {\r
1151                 return this.combineMessages;\r
1152         };\r
1153 \r
1154         XmlLayout.prototype.getContentType = function() {\r
1155                 return "text/xml";\r
1156         };\r
1157 \r
1158         XmlLayout.prototype.escapeCdata = function(str) {\r
1159                 return str.replace(/\]\]>/, "]]>]]&gt;<![CDATA[");\r
1160         };\r
1161 \r
1162         XmlLayout.prototype.format = function(loggingEvent) {\r
1163                 var layout = this;\r
1164                 var i, len;\r
1165                 function formatMessage(message) {\r
1166                         message = (typeof message === "string") ? message : toStr(message);\r
1167                         return "<log4javascript:message><![CDATA[" +\r
1168                                 layout.escapeCdata(message) + "]]></log4javascript:message>";\r
1169                 }\r
1170 \r
1171                 var str = "<log4javascript:event logger=\"" + loggingEvent.logger.name +\r
1172                         "\" timestamp=\"" + this.getTimeStampValue(loggingEvent) + "\"";\r
1173                 if (!this.isTimeStampsInMilliseconds()) {\r
1174                         str += " milliseconds=\"" + loggingEvent.milliseconds + "\"";\r
1175                 }\r
1176                 str += " level=\"" + loggingEvent.level.name + "\">" + newLine;\r
1177                 if (this.combineMessages) {\r
1178                         str += formatMessage(loggingEvent.getCombinedMessages());\r
1179                 } else {\r
1180                         str += "<log4javascript:messages>" + newLine;\r
1181                         for (i = 0, len = loggingEvent.messages.length; i < len; i++) {\r
1182                                 str += formatMessage(loggingEvent.messages[i]) + newLine;\r
1183                         }\r
1184                         str += "</log4javascript:messages>" + newLine;\r
1185                 }\r
1186                 if (this.hasCustomFields()) {\r
1187                         for (i = 0, len = this.customFields.length; i < len; i++) {\r
1188                                 str += "<log4javascript:customfield name=\"" +\r
1189                                         this.customFields[i].name + "\"><![CDATA[" +\r
1190                                         this.customFields[i].value.toString() +\r
1191                                         "]]></log4javascript:customfield>" + newLine;\r
1192                         }\r
1193                 }\r
1194                 if (loggingEvent.exception) {\r
1195                         str += "<log4javascript:exception><![CDATA[" +\r
1196                                 getExceptionStringRep(loggingEvent.exception) +\r
1197                                 "]]></log4javascript:exception>" + newLine;\r
1198                 }\r
1199                 str += "</log4javascript:event>" + newLine + newLine;\r
1200                 return str;\r
1201         };\r
1202 \r
1203         XmlLayout.prototype.ignoresThrowable = function() {\r
1204             return false;\r
1205         };\r
1206 \r
1207         XmlLayout.prototype.toString = function() {\r
1208             return "XmlLayout";\r
1209         };\r
1210 \r
1211         log4javascript.XmlLayout = XmlLayout;\r
1212         /* ---------------------------------------------------------------------- */\r
1213         // JsonLayout related\r
1214 \r
1215         function escapeNewLines(str) {\r
1216                 return str.replace(/\r\n|\r|\n/g, "\\r\\n");\r
1217         }\r
1218 \r
1219         function JsonLayout(readable, combineMessages) {\r
1220                 this.readable = extractBooleanFromParam(readable, false);\r
1221                 this.combineMessages = extractBooleanFromParam(combineMessages, true);\r
1222                 this.batchHeader = this.readable ? "[" + newLine : "[";\r
1223                 this.batchFooter = this.readable ? "]" + newLine : "]";\r
1224                 this.batchSeparator = this.readable ? "," + newLine : ",";\r
1225                 this.setKeys();\r
1226                 this.colon = this.readable ? ": " : ":";\r
1227                 this.tab = this.readable ? "\t" : "";\r
1228                 this.lineBreak = this.readable ? newLine : "";\r
1229                 this.customFields = [];\r
1230         }\r
1231 \r
1232         /* ---------------------------------------------------------------------- */\r
1233         // JsonLayout\r
1234 \r
1235         JsonLayout.prototype = new Layout();\r
1236 \r
1237         JsonLayout.prototype.isReadable = function() {\r
1238                 return this.readable;\r
1239         };\r
1240 \r
1241         JsonLayout.prototype.isCombinedMessages = function() {\r
1242                 return this.combineMessages;\r
1243         };\r
1244 \r
1245     JsonLayout.prototype.format = function(loggingEvent) {\r
1246         var layout = this;\r
1247         var dataValues = this.getDataValues(loggingEvent, this.combineMessages);\r
1248         var str = "{" + this.lineBreak;\r
1249         var i, len;\r
1250 \r
1251         function formatValue(val, prefix, expand) {\r
1252             // Check the type of the data value to decide whether quotation marks\r
1253             // or expansion are required\r
1254             var formattedValue;\r
1255             var valType = typeof val;\r
1256             if (val instanceof Date) {\r
1257                 formattedValue = String(val.getTime());\r
1258             } else if (expand && (val instanceof Array)) {\r
1259                 formattedValue = "[" + layout.lineBreak;\r
1260                 for (var i = 0, len = val.length; i < len; i++) {\r
1261                     var childPrefix = prefix + layout.tab;\r
1262                     formattedValue += childPrefix + formatValue(val[i], childPrefix, false);\r
1263                     if (i < val.length - 1) {\r
1264                         formattedValue += ",";\r
1265                     }\r
1266                     formattedValue += layout.lineBreak;\r
1267                 }\r
1268                 formattedValue += prefix + "]";\r
1269             } else if (valType !== "number" && valType !== "boolean") {\r
1270                 formattedValue = "\"" + escapeNewLines(toStr(val).replace(/\"/g, "\\\"")) + "\"";\r
1271             } else {\r
1272                 formattedValue = val;\r
1273             }\r
1274             return formattedValue;\r
1275         }\r
1276 \r
1277         for (i = 0, len = dataValues.length - 1; i <= len; i++) {\r
1278             str += this.tab + "\"" + dataValues[i][0] + "\"" + this.colon + formatValue(dataValues[i][1], this.tab, true);\r
1279             if (i < len) {\r
1280                 str += ",";\r
1281             }\r
1282             str += this.lineBreak;\r
1283         }\r
1284 \r
1285         str += "}" + this.lineBreak;\r
1286         return str;\r
1287     };\r
1288 \r
1289         JsonLayout.prototype.ignoresThrowable = function() {\r
1290             return false;\r
1291         };\r
1292 \r
1293         JsonLayout.prototype.toString = function() {\r
1294             return "JsonLayout";\r
1295         };\r
1296 \r
1297         JsonLayout.prototype.getContentType = function() {\r
1298                 return "application/json";\r
1299         };\r
1300 \r
1301         log4javascript.JsonLayout = JsonLayout;\r
1302         /* ---------------------------------------------------------------------- */\r
1303         // HttpPostDataLayout\r
1304 \r
1305         function HttpPostDataLayout() {\r
1306                 this.setKeys();\r
1307                 this.customFields = [];\r
1308                 this.returnsPostData = true;\r
1309         }\r
1310 \r
1311         HttpPostDataLayout.prototype = new Layout();\r
1312 \r
1313         // Disable batching\r
1314         HttpPostDataLayout.prototype.allowBatching = function() {\r
1315                 return false;\r
1316         };\r
1317 \r
1318         HttpPostDataLayout.prototype.format = function(loggingEvent) {\r
1319                 var dataValues = this.getDataValues(loggingEvent);\r
1320                 var queryBits = [];\r
1321                 for (var i = 0, len = dataValues.length; i < len; i++) {\r
1322                         var val = (dataValues[i][1] instanceof Date) ?\r
1323                                 String(dataValues[i][1].getTime()) : dataValues[i][1];\r
1324                         queryBits.push(urlEncode(dataValues[i][0]) + "=" + urlEncode(val));\r
1325                 }\r
1326                 return queryBits.join("&");\r
1327         };\r
1328 \r
1329         HttpPostDataLayout.prototype.ignoresThrowable = function(loggingEvent) {\r
1330             return false;\r
1331         };\r
1332 \r
1333         HttpPostDataLayout.prototype.toString = function() {\r
1334             return "HttpPostDataLayout";\r
1335         };\r
1336 \r
1337         log4javascript.HttpPostDataLayout = HttpPostDataLayout;\r
1338         /* ---------------------------------------------------------------------- */\r
1339         // formatObjectExpansion\r
1340 \r
1341         function formatObjectExpansion(obj, depth, indentation) {\r
1342                 var objectsExpanded = [];\r
1343 \r
1344                 function doFormat(obj, depth, indentation) {\r
1345                         var i, j, len, childDepth, childIndentation, childLines, expansion,\r
1346                                 childExpansion;\r
1347 \r
1348                         if (!indentation) {\r
1349                                 indentation = "";\r
1350                         }\r
1351 \r
1352                         function formatString(text) {\r
1353                                 var lines = splitIntoLines(text);\r
1354                                 for (var j = 1, jLen = lines.length; j < jLen; j++) {\r
1355                                         lines[j] = indentation + lines[j];\r
1356                                 }\r
1357                                 return lines.join(newLine);\r
1358                         }\r
1359 \r
1360                         if (obj === null) {\r
1361                                 return "null";\r
1362                         } else if (typeof obj == "undefined") {\r
1363                                 return "undefined";\r
1364                         } else if (typeof obj == "string") {\r
1365                                 return formatString(obj);\r
1366                         } else if (typeof obj == "object" && array_contains(objectsExpanded, obj)) {\r
1367                                 try {\r
1368                                         expansion = toStr(obj);\r
1369                                 } catch (ex) {\r
1370                                         expansion = "Error formatting property. Details: " + getExceptionStringRep(ex);\r
1371                                 }\r
1372                                 return expansion + " [already expanded]";\r
1373                         } else if ((obj instanceof Array) && depth > 0) {\r
1374                                 objectsExpanded.push(obj);\r
1375                                 expansion = "[" + newLine;\r
1376                                 childDepth = depth - 1;\r
1377                                 childIndentation = indentation + "  ";\r
1378                                 childLines = [];\r
1379                                 for (i = 0, len = obj.length; i < len; i++) {\r
1380                                         try {\r
1381                                                 childExpansion = doFormat(obj[i], childDepth, childIndentation);\r
1382                                                 childLines.push(childIndentation + childExpansion);\r
1383                                         } catch (ex) {\r
1384                                                 childLines.push(childIndentation + "Error formatting array member. Details: " +\r
1385                                                         getExceptionStringRep(ex) + "");\r
1386                                         }\r
1387                                 }\r
1388                                 expansion += childLines.join("," + newLine) + newLine + indentation + "]";\r
1389                                 return expansion;\r
1390             } else if (Object.prototype.toString.call(obj) == "[object Date]") {\r
1391                 return obj.toString();\r
1392                         } else if (typeof obj == "object" && depth > 0) {\r
1393                                 objectsExpanded.push(obj);\r
1394                                 expansion = "{" + newLine;\r
1395                                 childDepth = depth - 1;\r
1396                                 childIndentation = indentation + "  ";\r
1397                                 childLines = [];\r
1398                                 for (i in obj) {\r
1399                                         try {\r
1400                                                 childExpansion = doFormat(obj[i], childDepth, childIndentation);\r
1401                                                 childLines.push(childIndentation + i + ": " + childExpansion);\r
1402                                         } catch (ex) {\r
1403                                                 childLines.push(childIndentation + i + ": Error formatting property. Details: " +\r
1404                                                         getExceptionStringRep(ex));\r
1405                                         }\r
1406                                 }\r
1407                                 expansion += childLines.join("," + newLine) + newLine + indentation + "}";\r
1408                                 return expansion;\r
1409                         } else {\r
1410                                 return formatString(toStr(obj));\r
1411                         }\r
1412                 }\r
1413                 return doFormat(obj, depth, indentation);\r
1414         }\r
1415         /* ---------------------------------------------------------------------- */\r
1416         // Date-related stuff\r
1417 \r
1418         var SimpleDateFormat;\r
1419 \r
1420         (function() {\r
1421                 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']+)/;\r
1422                 var monthNames = ["January", "February", "March", "April", "May", "June",\r
1423                         "July", "August", "September", "October", "November", "December"];\r
1424                 var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];\r
1425                 var TEXT2 = 0, TEXT3 = 1, NUMBER = 2, YEAR = 3, MONTH = 4, TIMEZONE = 5;\r
1426                 var types = {\r
1427                         G : TEXT2,\r
1428                         y : YEAR,\r
1429                         M : MONTH,\r
1430                         w : NUMBER,\r
1431                         W : NUMBER,\r
1432                         D : NUMBER,\r
1433                         d : NUMBER,\r
1434                         F : NUMBER,\r
1435                         E : TEXT3,\r
1436                         a : TEXT2,\r
1437                         H : NUMBER,\r
1438                         k : NUMBER,\r
1439                         K : NUMBER,\r
1440                         h : NUMBER,\r
1441                         m : NUMBER,\r
1442                         s : NUMBER,\r
1443                         S : NUMBER,\r
1444                         Z : TIMEZONE\r
1445                 };\r
1446                 var ONE_DAY = 24 * 60 * 60 * 1000;\r
1447                 var ONE_WEEK = 7 * ONE_DAY;\r
1448                 var DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK = 1;\r
1449 \r
1450                 var newDateAtMidnight = function(year, month, day) {\r
1451                         var d = new Date(year, month, day, 0, 0, 0);\r
1452                         d.setMilliseconds(0);\r
1453                         return d;\r
1454                 };\r
1455 \r
1456                 Date.prototype.getDifference = function(date) {\r
1457                         return this.getTime() - date.getTime();\r
1458                 };\r
1459 \r
1460                 Date.prototype.isBefore = function(d) {\r
1461                         return this.getTime() < d.getTime();\r
1462                 };\r
1463 \r
1464                 Date.prototype.getUTCTime = function() {\r
1465                         return Date.UTC(this.getFullYear(), this.getMonth(), this.getDate(), this.getHours(), this.getMinutes(),\r
1466                                         this.getSeconds(), this.getMilliseconds());\r
1467                 };\r
1468 \r
1469                 Date.prototype.getTimeSince = function(d) {\r
1470                         return this.getUTCTime() - d.getUTCTime();\r
1471                 };\r
1472 \r
1473                 Date.prototype.getPreviousSunday = function() {\r
1474                         // Using midday avoids any possibility of DST messing things up\r
1475                         var midday = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 12, 0, 0);\r
1476                         var previousSunday = new Date(midday.getTime() - this.getDay() * ONE_DAY);\r
1477                         return newDateAtMidnight(previousSunday.getFullYear(), previousSunday.getMonth(),\r
1478                                         previousSunday.getDate());\r
1479                 };\r
1480 \r
1481                 Date.prototype.getWeekInYear = function(minimalDaysInFirstWeek) {\r
1482                         if (isUndefined(this.minimalDaysInFirstWeek)) {\r
1483                                 minimalDaysInFirstWeek = DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK;\r
1484                         }\r
1485                         var previousSunday = this.getPreviousSunday();\r
1486                         var startOfYear = newDateAtMidnight(this.getFullYear(), 0, 1);\r
1487                         var numberOfSundays = previousSunday.isBefore(startOfYear) ?\r
1488                                 0 : 1 + Math.floor(previousSunday.getTimeSince(startOfYear) / ONE_WEEK);\r
1489                         var numberOfDaysInFirstWeek =  7 - startOfYear.getDay();\r
1490                         var weekInYear = numberOfSundays;\r
1491                         if (numberOfDaysInFirstWeek < minimalDaysInFirstWeek) {\r
1492                                 weekInYear--;\r
1493                         }\r
1494                         return weekInYear;\r
1495                 };\r
1496 \r
1497                 Date.prototype.getWeekInMonth = function(minimalDaysInFirstWeek) {\r
1498                         if (isUndefined(this.minimalDaysInFirstWeek)) {\r
1499                                 minimalDaysInFirstWeek = DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK;\r
1500                         }\r
1501                         var previousSunday = this.getPreviousSunday();\r
1502                         var startOfMonth = newDateAtMidnight(this.getFullYear(), this.getMonth(), 1);\r
1503                         var numberOfSundays = previousSunday.isBefore(startOfMonth) ?\r
1504                                 0 : 1 + Math.floor(previousSunday.getTimeSince(startOfMonth) / ONE_WEEK);\r
1505                         var numberOfDaysInFirstWeek =  7 - startOfMonth.getDay();\r
1506                         var weekInMonth = numberOfSundays;\r
1507                         if (numberOfDaysInFirstWeek >= minimalDaysInFirstWeek) {\r
1508                                 weekInMonth++;\r
1509                         }\r
1510                         return weekInMonth;\r
1511                 };\r
1512 \r
1513                 Date.prototype.getDayInYear = function() {\r
1514                         var startOfYear = newDateAtMidnight(this.getFullYear(), 0, 1);\r
1515                         return 1 + Math.floor(this.getTimeSince(startOfYear) / ONE_DAY);\r
1516                 };\r
1517 \r
1518                 /* ------------------------------------------------------------------ */\r
1519 \r
1520                 SimpleDateFormat = function(formatString) {\r
1521                         this.formatString = formatString;\r
1522                 };\r
1523 \r
1524                 /**\r
1525                  * Sets the minimum number of days in a week in order for that week to\r
1526                  * be considered as belonging to a particular month or year\r
1527                  */\r
1528                 SimpleDateFormat.prototype.setMinimalDaysInFirstWeek = function(days) {\r
1529                         this.minimalDaysInFirstWeek = days;\r
1530                 };\r
1531 \r
1532                 SimpleDateFormat.prototype.getMinimalDaysInFirstWeek = function() {\r
1533                         return isUndefined(this.minimalDaysInFirstWeek) ?\r
1534                                 DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK : this.minimalDaysInFirstWeek;\r
1535                 };\r
1536 \r
1537                 var padWithZeroes = function(str, len) {\r
1538                         while (str.length < len) {\r
1539                                 str = "0" + str;\r
1540                         }\r
1541                         return str;\r
1542                 };\r
1543 \r
1544                 var formatText = function(data, numberOfLetters, minLength) {\r
1545                         return (numberOfLetters >= 4) ? data : data.substr(0, Math.max(minLength, numberOfLetters));\r
1546                 };\r
1547 \r
1548                 var formatNumber = function(data, numberOfLetters) {\r
1549                         var dataString = "" + data;\r
1550                         // Pad with 0s as necessary\r
1551                         return padWithZeroes(dataString, numberOfLetters);\r
1552                 };\r
1553 \r
1554                 SimpleDateFormat.prototype.format = function(date) {\r
1555                         var formattedString = "";\r
1556                         var result;\r
1557                         var searchString = this.formatString;\r
1558                         while ((result = regex.exec(searchString))) {\r
1559                                 var quotedString = result[1];\r
1560                                 var patternLetters = result[2];\r
1561                                 var otherLetters = result[3];\r
1562                                 var otherCharacters = result[4];\r
1563 \r
1564                                 // If the pattern matched is quoted string, output the text between the quotes\r
1565                                 if (quotedString) {\r
1566                                         if (quotedString == "''") {\r
1567                                                 formattedString += "'";\r
1568                                         } else {\r
1569                                                 formattedString += quotedString.substring(1, quotedString.length - 1);\r
1570                                         }\r
1571                                 } else if (otherLetters) {\r
1572                                         // Swallow non-pattern letters by doing nothing here\r
1573                                 } else if (otherCharacters) {\r
1574                                         // Simply output other characters\r
1575                                         formattedString += otherCharacters;\r
1576                                 } else if (patternLetters) {\r
1577                                         // Replace pattern letters\r
1578                                         var patternLetter = patternLetters.charAt(0);\r
1579                                         var numberOfLetters = patternLetters.length;\r
1580                                         var rawData = "";\r
1581                                         switch(patternLetter) {\r
1582                                                 case "G":\r
1583                                                         rawData = "AD";\r
1584                                                         break;\r
1585                                                 case "y":\r
1586                                                         rawData = date.getFullYear();\r
1587                                                         break;\r
1588                                                 case "M":\r
1589                                                         rawData = date.getMonth();\r
1590                                                         break;\r
1591                                                 case "w":\r
1592                                                         rawData = date.getWeekInYear(this.getMinimalDaysInFirstWeek());\r
1593                                                         break;\r
1594                                                 case "W":\r
1595                                                         rawData = date.getWeekInMonth(this.getMinimalDaysInFirstWeek());\r
1596                                                         break;\r
1597                                                 case "D":\r
1598                                                         rawData = date.getDayInYear();\r
1599                                                         break;\r
1600                                                 case "d":\r
1601                                                         rawData = date.getDate();\r
1602                                                         break;\r
1603                                                 case "F":\r
1604                                                         rawData = 1 + Math.floor((date.getDate() - 1) / 7);\r
1605                                                         break;\r
1606                                                 case "E":\r
1607                                                         rawData = dayNames[date.getDay()];\r
1608                                                         break;\r
1609                                                 case "a":\r
1610                                                         rawData = (date.getHours() >= 12) ? "PM" : "AM";\r
1611                                                         break;\r
1612                                                 case "H":\r
1613                                                         rawData = date.getHours();\r
1614                                                         break;\r
1615                                                 case "k":\r
1616                                                         rawData = date.getHours() || 24;\r
1617                                                         break;\r
1618                                                 case "K":\r
1619                                                         rawData = date.getHours() % 12;\r
1620                                                         break;\r
1621                                                 case "h":\r
1622                                                         rawData = (date.getHours() % 12) || 12;\r
1623                                                         break;\r
1624                                                 case "m":\r
1625                                                         rawData = date.getMinutes();\r
1626                                                         break;\r
1627                                                 case "s":\r
1628                                                         rawData = date.getSeconds();\r
1629                                                         break;\r
1630                                                 case "S":\r
1631                                                         rawData = date.getMilliseconds();\r
1632                                                         break;\r
1633                                                 case "Z":\r
1634                                                         rawData = date.getTimezoneOffset(); // This returns the number of minutes since GMT was this time.\r
1635                                                         break;\r
1636                                         }\r
1637                                         // Format the raw data depending on the type\r
1638                                         switch(types[patternLetter]) {\r
1639                                                 case TEXT2:\r
1640                                                         formattedString += formatText(rawData, numberOfLetters, 2);\r
1641                                                         break;\r
1642                                                 case TEXT3:\r
1643                                                         formattedString += formatText(rawData, numberOfLetters, 3);\r
1644                                                         break;\r
1645                                                 case NUMBER:\r
1646                                                         formattedString += formatNumber(rawData, numberOfLetters);\r
1647                                                         break;\r
1648                                                 case YEAR:\r
1649                                                         if (numberOfLetters <= 3) {\r
1650                                                                 // Output a 2-digit year\r
1651                                                                 var dataString = "" + rawData;\r
1652                                                                 formattedString += dataString.substr(2, 2);\r
1653                                                         } else {\r
1654                                                                 formattedString += formatNumber(rawData, numberOfLetters);\r
1655                                                         }\r
1656                                                         break;\r
1657                                                 case MONTH:\r
1658                                                         if (numberOfLetters >= 3) {\r
1659                                                                 formattedString += formatText(monthNames[rawData], numberOfLetters, numberOfLetters);\r
1660                                                         } else {\r
1661                                                                 // NB. Months returned by getMonth are zero-based\r
1662                                                                 formattedString += formatNumber(rawData + 1, numberOfLetters);\r
1663                                                         }\r
1664                                                         break;\r
1665                                                 case TIMEZONE:\r
1666                                                         var isPositive = (rawData > 0);\r
1667                                                         // The following line looks like a mistake but isn't\r
1668                                                         // because of the way getTimezoneOffset measures.\r
1669                                                         var prefix = isPositive ? "-" : "+";\r
1670                                                         var absData = Math.abs(rawData);\r
1671 \r
1672                                                         // Hours\r
1673                                                         var hours = "" + Math.floor(absData / 60);\r
1674                                                         hours = padWithZeroes(hours, 2);\r
1675                                                         // Minutes\r
1676                                                         var minutes = "" + (absData % 60);\r
1677                                                         minutes = padWithZeroes(minutes, 2);\r
1678 \r
1679                                                         formattedString += prefix + hours + minutes;\r
1680                                                         break;\r
1681                                         }\r
1682                                 }\r
1683                                 searchString = searchString.substr(result.index + result[0].length);\r
1684                         }\r
1685                         return formattedString;\r
1686                 };\r
1687         })();\r
1688 \r
1689         log4javascript.SimpleDateFormat = SimpleDateFormat;\r
1690 \r
1691         /* ---------------------------------------------------------------------- */\r
1692         // PatternLayout\r
1693 \r
1694         function PatternLayout(pattern) {\r
1695                 if (pattern) {\r
1696                         this.pattern = pattern;\r
1697                 } else {\r
1698                         this.pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;\r
1699                 }\r
1700                 this.customFields = [];\r
1701         }\r
1702 \r
1703         PatternLayout.TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n";\r
1704         PatternLayout.DEFAULT_CONVERSION_PATTERN = "%m%n";\r
1705         PatternLayout.ISO8601_DATEFORMAT = "yyyy-MM-dd HH:mm:ss,SSS";\r
1706         PatternLayout.DATETIME_DATEFORMAT = "dd MMM yyyy HH:mm:ss,SSS";\r
1707         PatternLayout.ABSOLUTETIME_DATEFORMAT = "HH:mm:ss,SSS";\r
1708 \r
1709         PatternLayout.prototype = new Layout();\r
1710 \r
1711         PatternLayout.prototype.format = function(loggingEvent) {\r
1712                 var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([acdfmMnpr%])(\{([^\}]+)\})?|([^%]+)/;\r
1713                 var formattedString = "";\r
1714                 var result;\r
1715                 var searchString = this.pattern;\r
1716 \r
1717                 // Cannot use regex global flag since it doesn't work with exec in IE5\r
1718                 while ((result = regex.exec(searchString))) {\r
1719                         var matchedString = result[0];\r
1720                         var padding = result[1];\r
1721                         var truncation = result[2];\r
1722                         var conversionCharacter = result[3];\r
1723                         var specifier = result[5];\r
1724                         var text = result[6];\r
1725 \r
1726                         // Check if the pattern matched was just normal text\r
1727                         if (text) {\r
1728                                 formattedString += "" + text;\r
1729                         } else {\r
1730                                 // Create a raw replacement string based on the conversion\r
1731                                 // character and specifier\r
1732                                 var replacement = "";\r
1733                                 switch(conversionCharacter) {\r
1734                                         case "a": // Array of messages\r
1735                                         case "m": // Message\r
1736                                                 var depth = 0;\r
1737                                                 if (specifier) {\r
1738                                                         depth = parseInt(specifier, 10);\r
1739                                                         if (isNaN(depth)) {\r
1740                                                                 handleError("PatternLayout.format: invalid specifier '" +\r
1741                                                                         specifier + "' for conversion character '" + conversionCharacter +\r
1742                                                                         "' - should be a number");\r
1743                                                                 depth = 0;\r
1744                                                         }\r
1745                                                 }\r
1746                                                 var messages = (conversionCharacter === "a") ? loggingEvent.messages[0] : loggingEvent.messages;\r
1747                                                 for (var i = 0, len = messages.length; i < len; i++) {\r
1748                                                         if (i > 0 && (replacement.charAt(replacement.length - 1) !== " ")) {\r
1749                                                                 replacement += " ";\r
1750                                                         }\r
1751                                                         if (depth === 0) {\r
1752                                                                 replacement += messages[i];\r
1753                                                         } else {\r
1754                                                                 replacement += formatObjectExpansion(messages[i], depth);\r
1755                                                         }\r
1756                                                 }\r
1757                                                 break;\r
1758                                         case "c": // Logger name\r
1759                                                 var loggerName = loggingEvent.logger.name;\r
1760                                                 if (specifier) {\r
1761                                                         var precision = parseInt(specifier, 10);\r
1762                                                         var loggerNameBits = loggingEvent.logger.name.split(".");\r
1763                                                         if (precision >= loggerNameBits.length) {\r
1764                                                                 replacement = loggerName;\r
1765                                                         } else {\r
1766                                                                 replacement = loggerNameBits.slice(loggerNameBits.length - precision).join(".");\r
1767                                                         }\r
1768                                                 } else {\r
1769                                                         replacement = loggerName;\r
1770                                                 }\r
1771                                                 break;\r
1772                                         case "d": // Date\r
1773                                                 var dateFormat = PatternLayout.ISO8601_DATEFORMAT;\r
1774                                                 if (specifier) {\r
1775                                                         dateFormat = specifier;\r
1776                                                         // Pick up special cases\r
1777                                                         if (dateFormat == "ISO8601") {\r
1778                                                                 dateFormat = PatternLayout.ISO8601_DATEFORMAT;\r
1779                                                         } else if (dateFormat == "ABSOLUTE") {\r
1780                                                                 dateFormat = PatternLayout.ABSOLUTETIME_DATEFORMAT;\r
1781                                                         } else if (dateFormat == "DATE") {\r
1782                                                                 dateFormat = PatternLayout.DATETIME_DATEFORMAT;\r
1783                                                         }\r
1784                                                 }\r
1785                                                 // Format the date\r
1786                                                 replacement = (new SimpleDateFormat(dateFormat)).format(loggingEvent.timeStamp);\r
1787                                                 break;\r
1788                                         case "f": // Custom field\r
1789                                                 if (this.hasCustomFields()) {\r
1790                                                         var fieldIndex = 0;\r
1791                                                         if (specifier) {\r
1792                                                                 fieldIndex = parseInt(specifier, 10);\r
1793                                                                 if (isNaN(fieldIndex)) {\r
1794                                                                         handleError("PatternLayout.format: invalid specifier '" +\r
1795                                                                                 specifier + "' for conversion character 'f' - should be a number");\r
1796                                                                 } else if (fieldIndex === 0) {\r
1797                                                                         handleError("PatternLayout.format: invalid specifier '" +\r
1798                                                                                 specifier + "' for conversion character 'f' - must be greater than zero");\r
1799                                                                 } else if (fieldIndex > this.customFields.length) {\r
1800                                                                         handleError("PatternLayout.format: invalid specifier '" +\r
1801                                                                                 specifier + "' for conversion character 'f' - there aren't that many custom fields");\r
1802                                                                 } else {\r
1803                                                                         fieldIndex = fieldIndex - 1;\r
1804                                                                 }\r
1805                                                         }\r
1806                             var val = this.customFields[fieldIndex].value;\r
1807                             if (typeof val == "function") {\r
1808                                 val = val(this, loggingEvent);\r
1809                             }\r
1810                             replacement = val;\r
1811                                                 }\r
1812                                                 break;\r
1813                                         case "n": // New line\r
1814                                                 replacement = newLine;\r
1815                                                 break;\r
1816                                         case "p": // Level\r
1817                                                 replacement = loggingEvent.level.name;\r
1818                                                 break;\r
1819                                         case "r": // Milliseconds since log4javascript startup\r
1820                                                 replacement = "" + loggingEvent.timeStamp.getDifference(applicationStartDate);\r
1821                                                 break;\r
1822                                         case "%": // Literal % sign\r
1823                                                 replacement = "%";\r
1824                                                 break;\r
1825                                         default:\r
1826                                                 replacement = matchedString;\r
1827                                                 break;\r
1828                                 }\r
1829                                 // Format the replacement according to any padding or\r
1830                                 // truncation specified\r
1831                                 var l;\r
1832 \r
1833                                 // First, truncation\r
1834                                 if (truncation) {\r
1835                                         l = parseInt(truncation.substr(1), 10);\r
1836                                         var strLen = replacement.length;\r
1837                                         if (l < strLen) {\r
1838                                                 replacement = replacement.substring(strLen - l, strLen);\r
1839                                         }\r
1840                                 }\r
1841                                 // Next, padding\r
1842                                 if (padding) {\r
1843                                         if (padding.charAt(0) == "-") {\r
1844                                                 l = parseInt(padding.substr(1), 10);\r
1845                                                 // Right pad with spaces\r
1846                                                 while (replacement.length < l) {\r
1847                                                         replacement += " ";\r
1848                                                 }\r
1849                                         } else {\r
1850                                                 l = parseInt(padding, 10);\r
1851                                                 // Left pad with spaces\r
1852                                                 while (replacement.length < l) {\r
1853                                                         replacement = " " + replacement;\r
1854                                                 }\r
1855                                         }\r
1856                                 }\r
1857                                 formattedString += replacement;\r
1858                         }\r
1859                         searchString = searchString.substr(result.index + result[0].length);\r
1860                 }\r
1861                 return formattedString;\r
1862         };\r
1863 \r
1864         PatternLayout.prototype.ignoresThrowable = function() {\r
1865             return true;\r
1866         };\r
1867 \r
1868         PatternLayout.prototype.toString = function() {\r
1869             return "PatternLayout";\r
1870         };\r
1871 \r
1872         log4javascript.PatternLayout = PatternLayout;\r
1873         /* ---------------------------------------------------------------------- */\r
1874         // AlertAppender\r
1875 \r
1876         function AlertAppender() {}\r
1877 \r
1878         AlertAppender.prototype = new Appender();\r
1879 \r
1880         AlertAppender.prototype.layout = new SimpleLayout();\r
1881 \r
1882         AlertAppender.prototype.append = function(loggingEvent) {\r
1883                 var formattedMessage = this.getLayout().format(loggingEvent);\r
1884                 if (this.getLayout().ignoresThrowable()) {\r
1885                         formattedMessage += loggingEvent.getThrowableStrRep();\r
1886                 }\r
1887                 alert(formattedMessage);\r
1888         };\r
1889 \r
1890         AlertAppender.prototype.toString = function() {\r
1891                 return "AlertAppender";\r
1892         };\r
1893 \r
1894         log4javascript.AlertAppender = AlertAppender;\r
1895         /* ---------------------------------------------------------------------- */\r
1896         // BrowserConsoleAppender (only works in Opera and Safari and Firefox with\r
1897         // Firebug extension)\r
1898 \r
1899         function BrowserConsoleAppender() {}\r
1900 \r
1901         BrowserConsoleAppender.prototype = new log4javascript.Appender();\r
1902         BrowserConsoleAppender.prototype.layout = new NullLayout();\r
1903         BrowserConsoleAppender.prototype.threshold = Level.DEBUG;\r
1904 \r
1905         BrowserConsoleAppender.prototype.append = function(loggingEvent) {\r
1906                 var appender = this;\r
1907 \r
1908                 var getFormattedMessage = function() {\r
1909                         var layout = appender.getLayout();\r
1910                         var formattedMessage = layout.format(loggingEvent);\r
1911                         if (layout.ignoresThrowable() && loggingEvent.exception) {\r
1912                                 formattedMessage += loggingEvent.getThrowableStrRep();\r
1913                         }\r
1914                         return formattedMessage;\r
1915                 };\r
1916 \r
1917                 if ((typeof opera != "undefined") && opera.postError) { // Opera\r
1918                         opera.postError(getFormattedMessage());\r
1919                 } else if (window.console && window.console.log) { // Safari and Firebug\r
1920                         var formattedMesage = getFormattedMessage();\r
1921                         // Log to Firebug using its logging methods or revert to the console.log\r
1922                         // method in Safari\r
1923                         if (window.console.debug && Level.DEBUG.isGreaterOrEqual(loggingEvent.level)) {\r
1924                                 window.console.debug(formattedMesage);\r
1925                         } else if (window.console.info && Level.INFO.equals(loggingEvent.level)) {\r
1926                                 window.console.info(formattedMesage);\r
1927                         } else if (window.console.warn && Level.WARN.equals(loggingEvent.level)) {\r
1928                                 window.console.warn(formattedMesage);\r
1929                         } else if (window.console.error && loggingEvent.level.isGreaterOrEqual(Level.ERROR)) {\r
1930                                 window.console.error(formattedMesage);\r
1931                         } else {\r
1932                                 window.console.log(formattedMesage);\r
1933                         }\r
1934                 }\r
1935         };\r
1936 \r
1937         BrowserConsoleAppender.prototype.group = function(name) {\r
1938                 if (window.console && window.console.group) {\r
1939                         window.console.group(name);\r
1940                 }\r
1941         };\r
1942 \r
1943         BrowserConsoleAppender.prototype.groupEnd = function() {\r
1944                 if (window.console && window.console.groupEnd) {\r
1945                         window.console.groupEnd();\r
1946                 }\r
1947         };\r
1948 \r
1949         BrowserConsoleAppender.prototype.toString = function() {\r
1950                 return "BrowserConsoleAppender";\r
1951         };\r
1952 \r
1953         log4javascript.BrowserConsoleAppender = BrowserConsoleAppender;\r
1954         /* ---------------------------------------------------------------------- */\r
1955         // AjaxAppender related\r
1956 \r
1957         var xmlHttpFactories = [\r
1958                 function() { return new XMLHttpRequest(); },\r
1959                 function() { return new ActiveXObject("Msxml2.XMLHTTP"); },\r
1960                 function() { return new ActiveXObject("Microsoft.XMLHTTP"); }\r
1961         ];\r
1962 \r
1963         var getXmlHttp = function(errorHandler) {\r
1964                 // This is only run the first time; the value of getXmlHttp gets\r
1965                 // replaced with the factory that succeeds on the first run\r
1966                 var xmlHttp = null, factory;\r
1967                 for (var i = 0, len = xmlHttpFactories.length; i < len; i++) {\r
1968                         factory = xmlHttpFactories[i];\r
1969                         try {\r
1970                                 xmlHttp = factory();\r
1971                                 getXmlHttp = factory;\r
1972                                 return xmlHttp;\r
1973                         } catch (e) {\r
1974                         }\r
1975                 }\r
1976                 // If we're here, all factories have failed, so throw an error\r
1977                 if (errorHandler) {\r
1978                         errorHandler();\r
1979                 } else {\r
1980                         handleError("getXmlHttp: unable to obtain XMLHttpRequest object");\r
1981                 }\r
1982         };\r
1983 \r
1984         function isHttpRequestSuccessful(xmlHttp) {\r
1985                 return isUndefined(xmlHttp.status) || xmlHttp.status === 0 ||\r
1986                         (xmlHttp.status >= 200 && xmlHttp.status < 300) ||\r
1987                         xmlHttp.status == 1223 /* Fix for IE */;\r
1988         }\r
1989 \r
1990         /* ---------------------------------------------------------------------- */\r
1991         // AjaxAppender\r
1992 \r
1993         function AjaxAppender(url) {\r
1994                 var appender = this;\r
1995                 var isSupported = true;\r
1996                 if (!url) {\r
1997                         handleError("AjaxAppender: URL must be specified in constructor");\r
1998                         isSupported = false;\r
1999                 }\r
2000 \r
2001                 var timed = this.defaults.timed;\r
2002                 var waitForResponse = this.defaults.waitForResponse;\r
2003                 var batchSize = this.defaults.batchSize;\r
2004                 var timerInterval = this.defaults.timerInterval;\r
2005                 var requestSuccessCallback = this.defaults.requestSuccessCallback;\r
2006                 var failCallback = this.defaults.failCallback;\r
2007                 var postVarName = this.defaults.postVarName;\r
2008                 var sendAllOnUnload = this.defaults.sendAllOnUnload;\r
2009                 var contentType = this.defaults.contentType;\r
2010                 var sessionId = null;\r
2011 \r
2012                 var queuedLoggingEvents = [];\r
2013                 var queuedRequests = [];\r
2014                 var headers = [];\r
2015                 var sending = false;\r
2016                 var initialized = false;\r
2017 \r
2018                 // Configuration methods. The function scope is used to prevent\r
2019                 // direct alteration to the appender configuration properties.\r
2020                 function checkCanConfigure(configOptionName) {\r
2021                         if (initialized) {\r
2022                                 handleError("AjaxAppender: configuration option '" +\r
2023                                         configOptionName +\r
2024                                         "' may not be set after the appender has been initialized");\r
2025                                 return false;\r
2026                         }\r
2027                         return true;\r
2028                 }\r
2029 \r
2030                 this.getSessionId = function() { return sessionId; };\r
2031                 this.setSessionId = function(sessionIdParam) {\r
2032                         sessionId = extractStringFromParam(sessionIdParam, null);\r
2033                         this.layout.setCustomField("sessionid", sessionId);\r
2034                 };\r
2035 \r
2036                 this.setLayout = function(layoutParam) {\r
2037                         if (checkCanConfigure("layout")) {\r
2038                                 this.layout = layoutParam;\r
2039                                 // Set the session id as a custom field on the layout, if not already present\r
2040                                 if (sessionId !== null) {\r
2041                                         this.setSessionId(sessionId);\r
2042                                 }\r
2043                         }\r
2044                 };\r
2045 \r
2046                 this.isTimed = function() { return timed; };\r
2047                 this.setTimed = function(timedParam) {\r
2048                         if (checkCanConfigure("timed")) {\r
2049                                 timed = bool(timedParam);\r
2050                         }\r
2051                 };\r
2052 \r
2053                 this.getTimerInterval = function() { return timerInterval; };\r
2054                 this.setTimerInterval = function(timerIntervalParam) {\r
2055                         if (checkCanConfigure("timerInterval")) {\r
2056                                 timerInterval = extractIntFromParam(timerIntervalParam, timerInterval);\r
2057                         }\r
2058                 };\r
2059 \r
2060                 this.isWaitForResponse = function() { return waitForResponse; };\r
2061                 this.setWaitForResponse = function(waitForResponseParam) {\r
2062                         if (checkCanConfigure("waitForResponse")) {\r
2063                                 waitForResponse = bool(waitForResponseParam);\r
2064                         }\r
2065                 };\r
2066 \r
2067                 this.getBatchSize = function() { return batchSize; };\r
2068                 this.setBatchSize = function(batchSizeParam) {\r
2069                         if (checkCanConfigure("batchSize")) {\r
2070                                 batchSize = extractIntFromParam(batchSizeParam, batchSize);\r
2071                         }\r
2072                 };\r
2073 \r
2074                 this.isSendAllOnUnload = function() { return sendAllOnUnload; };\r
2075                 this.setSendAllOnUnload = function(sendAllOnUnloadParam) {\r
2076                         if (checkCanConfigure("sendAllOnUnload")) {\r
2077                                 sendAllOnUnload = extractBooleanFromParam(sendAllOnUnloadParam, sendAllOnUnload);\r
2078                         }\r
2079                 };\r
2080 \r
2081                 this.setRequestSuccessCallback = function(requestSuccessCallbackParam) {\r
2082                         requestSuccessCallback = extractFunctionFromParam(requestSuccessCallbackParam, requestSuccessCallback);\r
2083                 };\r
2084 \r
2085                 this.setFailCallback = function(failCallbackParam) {\r
2086                         failCallback = extractFunctionFromParam(failCallbackParam, failCallback);\r
2087                 };\r
2088 \r
2089                 this.getPostVarName = function() { return postVarName; };\r
2090                 this.setPostVarName = function(postVarNameParam) {\r
2091                         if (checkCanConfigure("postVarName")) {\r
2092                                 postVarName = extractStringFromParam(postVarNameParam, postVarName);\r
2093                         }\r
2094                 };\r
2095 \r
2096                 this.getHeaders = function() { return headers; };\r
2097                 this.addHeader = function(name, value) {\r
2098                         if (name.toLowerCase() == "content-type") {\r
2099                                 contentType = value;\r
2100                         } else {\r
2101                                 headers.push( { name: name, value: value } );\r
2102                         }\r
2103                 };\r
2104 \r
2105                 // Internal functions\r
2106                 function sendAll() {\r
2107                         if (isSupported && enabled) {\r
2108                                 sending = true;\r
2109                                 var currentRequestBatch;\r
2110                                 if (waitForResponse) {\r
2111                                         // Send the first request then use this function as the callback once\r
2112                                         // the response comes back\r
2113                                         if (queuedRequests.length > 0) {\r
2114                                                 currentRequestBatch = queuedRequests.shift();\r
2115                                                 sendRequest(preparePostData(currentRequestBatch), sendAll);\r
2116                                         } else {\r
2117                                                 sending = false;\r
2118                                                 if (timed) {\r
2119                                                         scheduleSending();\r
2120                                                 }\r
2121                                         }\r
2122                                 } else {\r
2123                                         // Rattle off all the requests without waiting to see the response\r
2124                                         while ((currentRequestBatch = queuedRequests.shift())) {\r
2125                                                 sendRequest(preparePostData(currentRequestBatch));\r
2126                                         }\r
2127                                         sending = false;\r
2128                                         if (timed) {\r
2129                                                 scheduleSending();\r
2130                                         }\r
2131                                 }\r
2132                         }\r
2133                 }\r
2134 \r
2135                 this.sendAll = sendAll;\r
2136 \r
2137                 // Called when the window unloads. At this point we're past caring about\r
2138                 // waiting for responses or timers or incomplete batches - everything\r
2139                 // must go, now\r
2140                 function sendAllRemaining() {\r
2141                         var sendingAnything = false;\r
2142                         if (isSupported && enabled) {\r
2143                                 // Create requests for everything left over, batched as normal\r
2144                                 var actualBatchSize = appender.getLayout().allowBatching() ? batchSize : 1;\r
2145                                 var currentLoggingEvent;\r
2146                                 var batchedLoggingEvents = [];\r
2147                                 while ((currentLoggingEvent = queuedLoggingEvents.shift())) {\r
2148                                         batchedLoggingEvents.push(currentLoggingEvent);\r
2149                                         if (queuedLoggingEvents.length >= actualBatchSize) {\r
2150                                                 // Queue this batch of log entries\r
2151                                                 queuedRequests.push(batchedLoggingEvents);\r
2152                                                 batchedLoggingEvents = [];\r
2153                                         }\r
2154                                 }\r
2155                                 // If there's a partially completed batch, add it\r
2156                                 if (batchedLoggingEvents.length > 0) {\r
2157                                         queuedRequests.push(batchedLoggingEvents);\r
2158                                 }\r
2159                                 sendingAnything = (queuedRequests.length > 0);\r
2160                                 waitForResponse = false;\r
2161                                 timed = false;\r
2162                                 sendAll();\r
2163                         }\r
2164                         return sendingAnything;\r
2165                 }\r
2166 \r
2167                 this.sendAllRemaining = sendAllRemaining;\r
2168 \r
2169                 function preparePostData(batchedLoggingEvents) {\r
2170                         // Format the logging events\r
2171                         var formattedMessages = [];\r
2172                         var currentLoggingEvent;\r
2173                         var postData = "";\r
2174                         while ((currentLoggingEvent = batchedLoggingEvents.shift())) {\r
2175                                 var currentFormattedMessage = appender.getLayout().format(currentLoggingEvent);\r
2176                                 if (appender.getLayout().ignoresThrowable()) {\r
2177                                         currentFormattedMessage += currentLoggingEvent.getThrowableStrRep();\r
2178                                 }\r
2179                                 formattedMessages.push(currentFormattedMessage);\r
2180                         }\r
2181                         // Create the post data string\r
2182                         if (batchedLoggingEvents.length == 1) {\r
2183                                 postData = formattedMessages.join("");\r
2184                         } else {\r
2185                                 postData = appender.getLayout().batchHeader +\r
2186                                         formattedMessages.join(appender.getLayout().batchSeparator) +\r
2187                                         appender.getLayout().batchFooter;\r
2188                         }\r
2189                         if (contentType == appender.defaults.contentType) {\r
2190                                 postData = appender.getLayout().returnsPostData ? postData :\r
2191                                         urlEncode(postVarName) + "=" + urlEncode(postData);\r
2192                                 // Add the layout name to the post data\r
2193                                 if (postData.length > 0) {\r
2194                                         postData += "&";\r
2195                                 }\r
2196                                 postData += "layout=" + urlEncode(appender.getLayout().toString());\r
2197                         }\r
2198                         return postData;\r
2199                 }\r
2200 \r
2201                 function scheduleSending() {\r
2202                         window.setTimeout(sendAll, timerInterval);\r
2203                 }\r
2204 \r
2205                 function xmlHttpErrorHandler() {\r
2206                         var msg = "AjaxAppender: could not create XMLHttpRequest object. AjaxAppender disabled";\r
2207                         handleError(msg);\r
2208                         isSupported = false;\r
2209                         if (failCallback) {\r
2210                                 failCallback(msg);\r
2211                         }\r
2212                 }\r
2213 \r
2214                 function sendRequest(postData, successCallback) {\r
2215                         try {\r
2216                                 var xmlHttp = getXmlHttp(xmlHttpErrorHandler);\r
2217                                 if (isSupported) {\r
2218                                         if (xmlHttp.overrideMimeType) {\r
2219                                                 xmlHttp.overrideMimeType(appender.getLayout().getContentType());\r
2220                                         }\r
2221                                         xmlHttp.onreadystatechange = function() {\r
2222                                                 if (xmlHttp.readyState == 4) {\r
2223                                                         if (isHttpRequestSuccessful(xmlHttp)) {\r
2224                                                                 if (requestSuccessCallback) {\r
2225                                                                         requestSuccessCallback(xmlHttp);\r
2226                                                                 }\r
2227                                                                 if (successCallback) {\r
2228                                                                         successCallback(xmlHttp);\r
2229                                                                 }\r
2230                                                         } else {\r
2231                                                                 var msg = "AjaxAppender.append: XMLHttpRequest request to URL " +\r
2232                                                                         url + " returned status code " + xmlHttp.status;\r
2233                                                                 handleError(msg);\r
2234                                                                 if (failCallback) {\r
2235                                                                         failCallback(msg);\r
2236                                                                 }\r
2237                                                         }\r
2238                                                         xmlHttp.onreadystatechange = emptyFunction;\r
2239                                                         xmlHttp = null;\r
2240                                                 }\r
2241                                         };\r
2242                                         xmlHttp.open("POST", url, true);\r
2243                                         try {\r
2244                                                 for (var i = 0, header; header = headers[i++]; ) {\r
2245                                                         xmlHttp.setRequestHeader(header.name, header.value);\r
2246                                                 }\r
2247                                                 xmlHttp.setRequestHeader("Content-Type", contentType);\r
2248                                         } catch (headerEx) {\r
2249                                                 var msg = "AjaxAppender.append: your browser's XMLHttpRequest implementation" +\r
2250                                                         " does not support setRequestHeader, therefore cannot post data. AjaxAppender disabled";\r
2251                                                 handleError(msg);\r
2252                                                 isSupported = false;\r
2253                                                 if (failCallback) {\r
2254                                                         failCallback(msg);\r
2255                                                 }\r
2256                                                 return;\r
2257                                         }\r
2258                                         xmlHttp.send(postData);\r
2259                                 }\r
2260                         } catch (ex) {\r
2261                                 var errMsg = "AjaxAppender.append: error sending log message to " + url;\r
2262                                 handleError(errMsg, ex);\r
2263                                 isSupported = false;\r
2264                                 if (failCallback) {\r
2265                                         failCallback(errMsg + ". Details: " + getExceptionStringRep(ex));\r
2266                                 }\r
2267                         }\r
2268                 }\r
2269 \r
2270                 this.append = function(loggingEvent) {\r
2271                         if (isSupported) {\r
2272                                 if (!initialized) {\r
2273                                         init();\r
2274                                 }\r
2275                                 queuedLoggingEvents.push(loggingEvent);\r
2276                                 var actualBatchSize = this.getLayout().allowBatching() ? batchSize : 1;\r
2277 \r
2278                                 if (queuedLoggingEvents.length >= actualBatchSize) {\r
2279                                         var currentLoggingEvent;\r
2280                                         var batchedLoggingEvents = [];\r
2281                                         while ((currentLoggingEvent = queuedLoggingEvents.shift())) {\r
2282                                                 batchedLoggingEvents.push(currentLoggingEvent);\r
2283                                         }\r
2284                                         // Queue this batch of log entries\r
2285                                         queuedRequests.push(batchedLoggingEvents);\r
2286 \r
2287                                         // If using a timer, the queue of requests will be processed by the\r
2288                                         // timer function, so nothing needs to be done here.\r
2289                                         if (!timed && (!waitForResponse || (waitForResponse && !sending))) {\r
2290                                                 sendAll();\r
2291                                         }\r
2292                                 }\r
2293                         }\r
2294                 };\r
2295 \r
2296                 function init() {\r
2297                         initialized = true;\r
2298                         // Add unload event to send outstanding messages\r
2299                         if (sendAllOnUnload) {\r
2300                                 var oldBeforeUnload = window.onbeforeunload;\r
2301                                 window.onbeforeunload = function() {\r
2302                                         if (oldBeforeUnload) {\r
2303                                                 oldBeforeUnload();\r
2304                                         }\r
2305                                         if (sendAllRemaining()) {\r
2306                                                 return "Sending log messages";\r
2307                                         }\r
2308                                 };\r
2309                         }\r
2310                         // Start timer\r
2311                         if (timed) {\r
2312                                 scheduleSending();\r
2313                         }\r
2314                 }\r
2315         }\r
2316 \r
2317         AjaxAppender.prototype = new Appender();\r
2318 \r
2319         AjaxAppender.prototype.defaults = {\r
2320                 waitForResponse: false,\r
2321                 timed: false,\r
2322                 timerInterval: 1000,\r
2323                 batchSize: 1,\r
2324                 sendAllOnUnload: false,\r
2325                 requestSuccessCallback: null,\r
2326                 failCallback: null,\r
2327                 postVarName: "data",\r
2328                 contentType: "application/x-www-form-urlencoded"\r
2329         };\r
2330 \r
2331         AjaxAppender.prototype.layout = new HttpPostDataLayout();\r
2332 \r
2333         AjaxAppender.prototype.toString = function() {\r
2334                 return "AjaxAppender";\r
2335         };\r
2336 \r
2337         log4javascript.AjaxAppender = AjaxAppender;\r
2338         /* ---------------------------------------------------------------------- */\r
2339         // PopUpAppender and InPageAppender related\r
2340 \r
2341         function setCookie(name, value, days, path) {\r
2342             var expires;\r
2343             path = path ? "; path=" + path : "";\r
2344                 if (days) {\r
2345                         var date = new Date();\r
2346                         date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));\r
2347                         expires = "; expires=" + date.toGMTString();\r
2348                 } else {\r
2349                     expires = "";\r
2350             }\r
2351                 document.cookie = escape(name) + "=" + escape(value) + expires + path;\r
2352         }\r
2353 \r
2354         function getCookie(name) {\r
2355                 var nameEquals = escape(name) + "=";\r
2356                 var ca = document.cookie.split(";");\r
2357                 for (var i = 0, len = ca.length; i < len; i++) {\r
2358                         var c = ca[i];\r
2359                         while (c.charAt(0) === " ") {\r
2360                             c = c.substring(1, c.length);\r
2361                         }\r
2362                         if (c.indexOf(nameEquals) === 0) {\r
2363                             return unescape(c.substring(nameEquals.length, c.length));\r
2364                 }\r
2365                 }\r
2366                 return null;\r
2367         }\r
2368 \r
2369         // Gets the base URL of the location of the log4javascript script.\r
2370         // This is far from infallible.\r
2371         function getBaseUrl() {\r
2372                 var scripts = document.getElementsByTagName("script");\r
2373                 for (var i = 0, len = scripts.length; i < len; ++i) {\r
2374                         if (scripts[i].src.indexOf("log4javascript") != -1) {\r
2375                                 var lastSlash = scripts[i].src.lastIndexOf("/");\r
2376                                 return (lastSlash == -1) ? "" : scripts[i].src.substr(0, lastSlash + 1);\r
2377                         }\r
2378                 }\r
2379         return null;\r
2380     }\r
2381 \r
2382         function isLoaded(win) {\r
2383                 try {\r
2384                         return bool(win.loaded);\r
2385                 } catch (ex) {\r
2386                         return false;\r
2387                 }\r
2388         }\r
2389 \r
2390         /* ---------------------------------------------------------------------- */\r
2391         // ConsoleAppender (prototype for PopUpAppender and InPageAppender)\r
2392 \r
2393         var ConsoleAppender;\r
2394 \r
2395         // Create an anonymous function to protect base console methods\r
2396         (function() {\r
2397                 var getConsoleHtmlLines = function() {\r
2398                         return [\r
2399 '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',\r
2400 '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">',\r
2401 '       <head>',\r
2402 '               <title>log4javascript</title>',\r
2403 '               <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',\r
2404 '               <!-- Make IE8 behave like IE7, having gone to all the trouble of making IE work -->',\r
2405 '               <meta http-equiv="X-UA-Compatible" content="IE=7" />',\r
2406 '               <script type="text/javascript">var isIe = false, isIePre7 = false;</script>',\r
2407 '               <!--[if IE]><script type="text/javascript">isIe = true</script><![endif]-->',\r
2408 '               <!--[if lt IE 7]><script type="text/javascript">isIePre7 = true</script><![endif]-->',\r
2409 '               <script type="text/javascript">',\r
2410 '                       //<![CDATA[',\r
2411 '                       var loggingEnabled = true;',\r
2412 '                       var logQueuedEventsTimer = null;',\r
2413 '                       var logEntries = [];',\r
2414 '                       var logEntriesAndSeparators = [];',\r
2415 '                       var logItems = [];',\r
2416 '                       var renderDelay = 100;',\r
2417 '                       var unrenderedLogItemsExist = false;',\r
2418 '                       var rootGroup, currentGroup = null;',\r
2419 '                       var loaded = false;',\r
2420 '                       var currentLogItem = null;',\r
2421 '                       var logMainContainer;',\r
2422 '',\r
2423 '                       function copyProperties(obj, props) {',\r
2424 '                               for (var i in props) {',\r
2425 '                                       obj[i] = props[i];',\r
2426 '                               }',\r
2427 '                       }',\r
2428 '',\r
2429 '                       /*----------------------------------------------------------------*/',\r
2430 '',\r
2431 '                       function LogItem() {',\r
2432 '                       }',\r
2433 '',\r
2434 '                       LogItem.prototype = {',\r
2435 '                               mainContainer: null,',\r
2436 '                               wrappedContainer: null,',\r
2437 '                               unwrappedContainer: null,',\r
2438 '                               group: null,',\r
2439 '',\r
2440 '                               appendToLog: function() {',\r
2441 '                                       for (var i = 0, len = this.elementContainers.length; i < len; i++) {',\r
2442 '                                               this.elementContainers[i].appendToLog();',\r
2443 '                                       }',\r
2444 '                                       this.group.update();',\r
2445 '                               },',\r
2446 '',\r
2447 '                               doRemove: function(doUpdate, removeFromGroup) {',\r
2448 '                                       if (this.rendered) {',\r
2449 '                                               for (var i = 0, len = this.elementContainers.length; i < len; i++) {',\r
2450 '                                                       this.elementContainers[i].remove();',\r
2451 '                                               }',\r
2452 '                                               this.unwrappedElementContainer = null;',\r
2453 '                                               this.wrappedElementContainer = null;',\r
2454 '                                               this.mainElementContainer = null;',\r
2455 '                                       }',\r
2456 '                                       if (this.group && removeFromGroup) {',\r
2457 '                                               this.group.removeChild(this, doUpdate);',\r
2458 '                                       }',\r
2459 '                                       if (this === currentLogItem) {',\r
2460 '                                               currentLogItem = null;',\r
2461 '                                       }',\r
2462 '                               },',\r
2463 '',\r
2464 '                               remove: function(doUpdate, removeFromGroup) {',\r
2465 '                                       this.doRemove(doUpdate, removeFromGroup);',\r
2466 '                               },',\r
2467 '',\r
2468 '                               render: function() {},',\r
2469 '',\r
2470 '                               accept: function(visitor) {',\r
2471 '                                       visitor.visit(this);',\r
2472 '                               },',\r
2473 '',\r
2474 '                               getUnwrappedDomContainer: function() {',\r
2475 '                                       return this.group.unwrappedElementContainer.contentDiv;',\r
2476 '                               },',\r
2477 '',\r
2478 '                               getWrappedDomContainer: function() {',\r
2479 '                                       return this.group.wrappedElementContainer.contentDiv;',\r
2480 '                               },',\r
2481 '',\r
2482 '                               getMainDomContainer: function() {',\r
2483 '                                       return this.group.mainElementContainer.contentDiv;',\r
2484 '                               }',\r
2485 '                       };',\r
2486 '',\r
2487 '                       LogItem.serializedItemKeys = {LOG_ENTRY: 0, GROUP_START: 1, GROUP_END: 2};',\r
2488 '',\r
2489 '                       /*----------------------------------------------------------------*/',\r
2490 '',\r
2491 '                       function LogItemContainerElement() {',\r
2492 '                       }',\r
2493 '',\r
2494 '                       LogItemContainerElement.prototype = {',\r
2495 '                               appendToLog: function() {',\r
2496 '                                       var insertBeforeFirst = (newestAtTop && this.containerDomNode.hasChildNodes());',\r
2497 '                                       if (insertBeforeFirst) {',\r
2498 '                                               this.containerDomNode.insertBefore(this.mainDiv, this.containerDomNode.firstChild);',\r
2499 '                                       } else {',\r
2500 '                                               this.containerDomNode.appendChild(this.mainDiv);',\r
2501 '                                       }',\r
2502 '                               }',\r
2503 '                       };',\r
2504 '',\r
2505 '                       /*----------------------------------------------------------------*/',\r
2506 '',\r
2507 '                       function SeparatorElementContainer(containerDomNode) {',\r
2508 '                               this.containerDomNode = containerDomNode;',\r
2509 '                               this.mainDiv = document.createElement("div");',\r
2510 '                               this.mainDiv.className = "separator";',\r
2511 '                               this.mainDiv.innerHTML = "&nbsp;";',\r
2512 '                       }',\r
2513 '',\r
2514 '                       SeparatorElementContainer.prototype = new LogItemContainerElement();',\r
2515 '',\r
2516 '                       SeparatorElementContainer.prototype.remove = function() {',\r
2517 '                               this.mainDiv.parentNode.removeChild(this.mainDiv);',\r
2518 '                               this.mainDiv = null;',\r
2519 '                       };',\r
2520 '',\r
2521 '                       /*----------------------------------------------------------------*/',\r
2522 '',\r
2523 '                       function Separator() {',\r
2524 '                               this.rendered = false;',\r
2525 '                       }',\r
2526 '',\r
2527 '                       Separator.prototype = new LogItem();',\r
2528 '',\r
2529 '                       copyProperties(Separator.prototype, {',\r
2530 '                               render: function() {',\r
2531 '                                       var containerDomNode = this.group.contentDiv;',\r
2532 '                                       if (isIe) {',\r
2533 '                                               this.unwrappedElementContainer = new SeparatorElementContainer(this.getUnwrappedDomContainer());',\r
2534 '                                               this.wrappedElementContainer = new SeparatorElementContainer(this.getWrappedDomContainer());',\r
2535 '                                               this.elementContainers = [this.unwrappedElementContainer, this.wrappedElementContainer];',\r
2536 '                                       } else {',\r
2537 '                                               this.mainElementContainer = new SeparatorElementContainer(this.getMainDomContainer());',\r
2538 '                                               this.elementContainers = [this.mainElementContainer];',\r
2539 '                                       }',\r
2540 '                                       this.content = this.formattedMessage;',\r
2541 '                                       this.rendered = true;',\r
2542 '                               }',\r
2543 '                       });',\r
2544 '',\r
2545 '                       /*----------------------------------------------------------------*/',\r
2546 '',\r
2547 '                       function GroupElementContainer(group, containerDomNode, isRoot, isWrapped) {',\r
2548 '                               this.group = group;',\r
2549 '                               this.containerDomNode = containerDomNode;',\r
2550 '                               this.isRoot = isRoot;',\r
2551 '                               this.isWrapped = isWrapped;',\r
2552 '                               this.expandable = false;',\r
2553 '',\r
2554 '                               if (this.isRoot) {',\r
2555 '                                       if (isIe) {',\r
2556 '                                               this.contentDiv = logMainContainer.appendChild(document.createElement("div"));',\r
2557 '                                               this.contentDiv.id = this.isWrapped ? "log_wrapped" : "log_unwrapped";',\r
2558 '                                       } else {',\r
2559 '                                               this.contentDiv = logMainContainer;',\r
2560 '                                       }',\r
2561 '                               } else {',\r
2562 '                                       var groupElementContainer = this;',\r
2563 '                                       ',\r
2564 '                                       this.mainDiv = document.createElement("div");',\r
2565 '                                       this.mainDiv.className = "group";',\r
2566 '',\r
2567 '                                       this.headingDiv = this.mainDiv.appendChild(document.createElement("div"));',\r
2568 '                                       this.headingDiv.className = "groupheading";',\r
2569 '',\r
2570 '                                       this.expander = this.headingDiv.appendChild(document.createElement("span"));',\r
2571 '                                       this.expander.className = "expander unselectable greyedout";',\r
2572 '                                       this.expander.unselectable = true;',\r
2573 '                                       var expanderText = this.group.expanded ? "-" : "+";',\r
2574 '                                       this.expanderTextNode = this.expander.appendChild(document.createTextNode(expanderText));',\r
2575 '                                       ',\r
2576 '                                       this.headingDiv.appendChild(document.createTextNode(" " + this.group.name));',\r
2577 '',\r
2578 '                                       this.contentDiv = this.mainDiv.appendChild(document.createElement("div"));',\r
2579 '                                       var contentCssClass = this.group.expanded ? "expanded" : "collapsed";',\r
2580 '                                       this.contentDiv.className = "groupcontent " + contentCssClass;',\r
2581 '',\r
2582 '                                       this.expander.onclick = function() {',\r
2583 '                                               if (groupElementContainer.group.expandable) {',\r
2584 '                                                       groupElementContainer.group.toggleExpanded();',\r
2585 '                                               }',\r
2586 '                                       };',\r
2587 '                               }',\r
2588 '                       }',\r
2589 '',\r
2590 '                       GroupElementContainer.prototype = new LogItemContainerElement();',\r
2591 '',\r
2592 '                       copyProperties(GroupElementContainer.prototype, {',\r
2593 '                               toggleExpanded: function() {',\r
2594 '                                       if (!this.isRoot) {',\r
2595 '                                               var oldCssClass, newCssClass, expanderText;',\r
2596 '                                               if (this.group.expanded) {',\r
2597 '                                                       newCssClass = "expanded";',\r
2598 '                                                       oldCssClass = "collapsed";',\r
2599 '                                                       expanderText = "-";',\r
2600 '                                               } else {',\r
2601 '                                                       newCssClass = "collapsed";',\r
2602 '                                                       oldCssClass = "expanded";',\r
2603 '                                                       expanderText = "+";',\r
2604 '                                               }',\r
2605 '                                               replaceClass(this.contentDiv, newCssClass, oldCssClass);',\r
2606 '                                               this.expanderTextNode.nodeValue = expanderText;',\r
2607 '                                       }',\r
2608 '                               },',\r
2609 '',\r
2610 '                               remove: function() {',\r
2611 '                                       if (!this.isRoot) {',\r
2612 '                                               this.headingDiv = null;',\r
2613 '                                               this.expander.onclick = null;',\r
2614 '                                               this.expander = null;',\r
2615 '                                               this.expanderTextNode = null;',\r
2616 '                                               this.contentDiv = null;',\r
2617 '                                               this.containerDomNode = null;',\r
2618 '                                               this.mainDiv.parentNode.removeChild(this.mainDiv);',\r
2619 '                                               this.mainDiv = null;',\r
2620 '                                       }',\r
2621 '                               },',\r
2622 '',\r
2623 '                               reverseChildren: function() {',\r
2624 '                                       // Invert the order of the log entries',\r
2625 '                                       var node = null;',\r
2626 '',\r
2627 '                                       // Remove all the log container nodes',\r
2628 '                                       var childDomNodes = [];',\r
2629 '                                       while ((node = this.contentDiv.firstChild)) {',\r
2630 '                                               this.contentDiv.removeChild(node);',\r
2631 '                                               childDomNodes.push(node);',\r
2632 '                                       }',\r
2633 '',\r
2634 '                                       // Put them all back in reverse order',\r
2635 '                                       while ((node = childDomNodes.pop())) {',\r
2636 '                                               this.contentDiv.appendChild(node);',\r
2637 '                                       }',\r
2638 '                               },',\r
2639 '',\r
2640 '                               update: function() {',\r
2641 '                                       if (!this.isRoot) {',\r
2642 '                                               if (this.group.expandable) {',\r
2643 '                                                       removeClass(this.expander, "greyedout");',\r
2644 '                                               } else {',\r
2645 '                                                       addClass(this.expander, "greyedout");',\r
2646 '                                               }',\r
2647 '                                       }',\r
2648 '                               },',\r
2649 '',\r
2650 '                               clear: function() {',\r
2651 '                                       if (this.isRoot) {',\r
2652 '                                               this.contentDiv.innerHTML = "";',\r
2653 '                                       }',\r
2654 '                               }',\r
2655 '                       });',\r
2656 '',\r
2657 '                       /*----------------------------------------------------------------*/',\r
2658 '',\r
2659 '                       function Group(name, isRoot, initiallyExpanded) {',\r
2660 '                               this.name = name;',\r
2661 '                               this.group = null;',\r
2662 '                               this.isRoot = isRoot;',\r
2663 '                               this.initiallyExpanded = initiallyExpanded;',\r
2664 '                               this.elementContainers = [];',\r
2665 '                               this.children = [];',\r
2666 '                               this.expanded = initiallyExpanded;',\r
2667 '                               this.rendered = false;',\r
2668 '                               this.expandable = false;',\r
2669 '                       }',\r
2670 '',\r
2671 '                       Group.prototype = new LogItem();',\r
2672 '',\r
2673 '                       copyProperties(Group.prototype, {',\r
2674 '                               addChild: function(logItem) {',\r
2675 '                                       this.children.push(logItem);',\r
2676 '                                       logItem.group = this;',\r
2677 '                               },',\r
2678 '',\r
2679 '                               render: function() {',\r
2680 '                                       if (isIe) {',\r
2681 '                                               var unwrappedDomContainer, wrappedDomContainer;',\r
2682 '                                               if (this.isRoot) {',\r
2683 '                                                       unwrappedDomContainer = logMainContainer;',\r
2684 '                                                       wrappedDomContainer = logMainContainer;',\r
2685 '                                               } else {',\r
2686 '                                                       unwrappedDomContainer = this.getUnwrappedDomContainer();',\r
2687 '                                                       wrappedDomContainer = this.getWrappedDomContainer();',\r
2688 '                                               }',\r
2689 '                                               this.unwrappedElementContainer = new GroupElementContainer(this, unwrappedDomContainer, this.isRoot, false);',\r
2690 '                                               this.wrappedElementContainer = new GroupElementContainer(this, wrappedDomContainer, this.isRoot, true);',\r
2691 '                                               this.elementContainers = [this.unwrappedElementContainer, this.wrappedElementContainer];',\r
2692 '                                       } else {',\r
2693 '                                               var mainDomContainer = this.isRoot ? logMainContainer : this.getMainDomContainer();',\r
2694 '                                               this.mainElementContainer = new GroupElementContainer(this, mainDomContainer, this.isRoot, false);',\r
2695 '                                               this.elementContainers = [this.mainElementContainer];',\r
2696 '                                       }',\r
2697 '                                       this.rendered = true;',\r
2698 '                               },',\r
2699 '',\r
2700 '                               toggleExpanded: function() {',\r
2701 '                                       this.expanded = !this.expanded;',\r
2702 '                                       for (var i = 0, len = this.elementContainers.length; i < len; i++) {',\r
2703 '                                               this.elementContainers[i].toggleExpanded();',\r
2704 '                                       }',\r
2705 '                               },',\r
2706 '',\r
2707 '                               expand: function() {',\r
2708 '                                       if (!this.expanded) {',\r
2709 '                                               this.toggleExpanded();',\r
2710 '                                       }',\r
2711 '                               },',\r
2712 '',\r
2713 '                               accept: function(visitor) {',\r
2714 '                                       visitor.visitGroup(this);',\r
2715 '                               },',\r
2716 '',\r
2717 '                               reverseChildren: function() {',\r
2718 '                                       if (this.rendered) {',\r
2719 '                                               for (var i = 0, len = this.elementContainers.length; i < len; i++) {',\r
2720 '                                                       this.elementContainers[i].reverseChildren();',\r
2721 '                                               }',\r
2722 '                                       }',\r
2723 '                               },',\r
2724 '',\r
2725 '                               update: function() {',\r
2726 '                                       var previouslyExpandable = this.expandable;',\r
2727 '                                       this.expandable = (this.children.length !== 0);',\r
2728 '                                       if (this.expandable !== previouslyExpandable) {',\r
2729 '                                               for (var i = 0, len = this.elementContainers.length; i < len; i++) {',\r
2730 '                                                       this.elementContainers[i].update();',\r
2731 '                                               }',\r
2732 '                                       }',\r
2733 '                               },',\r
2734 '',\r
2735 '                               flatten: function() {',\r
2736 '                                       var visitor = new GroupFlattener();',\r
2737 '                                       this.accept(visitor);',\r
2738 '                                       return visitor.logEntriesAndSeparators;',\r
2739 '                               },',\r
2740 '',\r
2741 '                               removeChild: function(child, doUpdate) {',\r
2742 '                                       array_remove(this.children, child);',\r
2743 '                                       child.group = null;',\r
2744 '                                       if (doUpdate) {',\r
2745 '                                               this.update();',\r
2746 '                                       }',\r
2747 '                               },',\r
2748 '',\r
2749 '                               remove: function(doUpdate, removeFromGroup) {',\r
2750 '                                       for (var i = 0, len = this.children.length; i < len; i++) {',\r
2751 '                                               this.children[i].remove(false, false);',\r
2752 '                                       }',\r
2753 '                                       this.children = [];',\r
2754 '                                       this.update();',\r
2755 '                                       if (this === currentGroup) {',\r
2756 '                                               currentGroup = this.group;',\r
2757 '                                       }',\r
2758 '                                       this.doRemove(doUpdate, removeFromGroup);',\r
2759 '                               },',\r
2760 '',\r
2761 '                               serialize: function(items) {',\r
2762 '                                       items.push([LogItem.serializedItemKeys.GROUP_START, this.name]);',\r
2763 '                                       for (var i = 0, len = this.children.length; i < len; i++) {',\r
2764 '                                               this.children[i].serialize(items);',\r
2765 '                                       }',\r
2766 '                                       if (this !== currentGroup) {',\r
2767 '                                               items.push([LogItem.serializedItemKeys.GROUP_END]);',\r
2768 '                                       }',\r
2769 '                               },',\r
2770 '',\r
2771 '                               clear: function() {',\r
2772 '                                       for (var i = 0, len = this.elementContainers.length; i < len; i++) {',\r
2773 '                                               this.elementContainers[i].clear();',\r
2774 '                                       }',\r
2775 '                               }',\r
2776 '                       });',\r
2777 '',\r
2778 '                       /*----------------------------------------------------------------*/',\r
2779 '',\r
2780 '                       function LogEntryElementContainer() {',\r
2781 '                       }',\r
2782 '',\r
2783 '                       LogEntryElementContainer.prototype = new LogItemContainerElement();',\r
2784 '',\r
2785 '                       copyProperties(LogEntryElementContainer.prototype, {',\r
2786 '                               remove: function() {',\r
2787 '                                       this.doRemove();',\r
2788 '                               },',\r
2789 '',\r
2790 '                               doRemove: function() {',\r
2791 '                                       this.mainDiv.parentNode.removeChild(this.mainDiv);',\r
2792 '                                       this.mainDiv = null;',\r
2793 '                                       this.contentElement = null;',\r
2794 '                                       this.containerDomNode = null;',\r
2795 '                               },',\r
2796 '',\r
2797 '                               setContent: function(content, wrappedContent) {',\r
2798 '                                       if (content === this.formattedMessage) {',\r
2799 '                                               this.contentElement.innerHTML = "";',\r
2800 '                                               this.contentElement.appendChild(document.createTextNode(this.formattedMessage));',\r
2801 '                                       } else {',\r
2802 '                                               this.contentElement.innerHTML = content;',\r
2803 '                                       }',\r
2804 '                               },',\r
2805 '',\r
2806 '                               setSearchMatch: function(isMatch) {',\r
2807 '                                       var oldCssClass = isMatch ? "searchnonmatch" : "searchmatch";',\r
2808 '                                       var newCssClass = isMatch ? "searchmatch" : "searchnonmatch";',\r
2809 '                                       replaceClass(this.mainDiv, newCssClass, oldCssClass);',\r
2810 '                               },',\r
2811 '',\r
2812 '                               clearSearch: function() {',\r
2813 '                                       removeClass(this.mainDiv, "searchmatch");',\r
2814 '                                       removeClass(this.mainDiv, "searchnonmatch");',\r
2815 '                               }',\r
2816 '                       });',\r
2817 '',\r
2818 '                       /*----------------------------------------------------------------*/',\r
2819 '',\r
2820 '                       function LogEntryWrappedElementContainer(logEntry, containerDomNode) {',\r
2821 '                               this.logEntry = logEntry;',\r
2822 '                               this.containerDomNode = containerDomNode;',\r
2823 '                               this.mainDiv = document.createElement("div");',\r
2824 '                               this.mainDiv.appendChild(document.createTextNode(this.logEntry.formattedMessage));',\r
2825 '                               this.mainDiv.className = "logentry wrapped " + this.logEntry.level;',\r
2826 '                               this.contentElement = this.mainDiv;',\r
2827 '                       }',\r
2828 '',\r
2829 '                       LogEntryWrappedElementContainer.prototype = new LogEntryElementContainer();',\r
2830 '',\r
2831 '                       LogEntryWrappedElementContainer.prototype.setContent = function(content, wrappedContent) {',\r
2832 '                               if (content === this.formattedMessage) {',\r
2833 '                                       this.contentElement.innerHTML = "";',\r
2834 '                                       this.contentElement.appendChild(document.createTextNode(this.formattedMessage));',\r
2835 '                               } else {',\r
2836 '                                       this.contentElement.innerHTML = wrappedContent;',\r
2837 '                               }',\r
2838 '                       };',\r
2839 '',\r
2840 '                       /*----------------------------------------------------------------*/',\r
2841 '',\r
2842 '                       function LogEntryUnwrappedElementContainer(logEntry, containerDomNode) {',\r
2843 '                               this.logEntry = logEntry;',\r
2844 '                               this.containerDomNode = containerDomNode;',\r
2845 '                               this.mainDiv = document.createElement("div");',\r
2846 '                               this.mainDiv.className = "logentry unwrapped " + this.logEntry.level;',\r
2847 '                               this.pre = this.mainDiv.appendChild(document.createElement("pre"));',\r
2848 '                               this.pre.appendChild(document.createTextNode(this.logEntry.formattedMessage));',\r
2849 '                               this.pre.className = "unwrapped";',\r
2850 '                               this.contentElement = this.pre;',\r
2851 '                       }',\r
2852 '',\r
2853 '                       LogEntryUnwrappedElementContainer.prototype = new LogEntryElementContainer();',\r
2854 '',\r
2855 '                       LogEntryUnwrappedElementContainer.prototype.remove = function() {',\r
2856 '                               this.doRemove();',\r
2857 '                               this.pre = null;',\r
2858 '                       };',\r
2859 '',\r
2860 '                       /*----------------------------------------------------------------*/',\r
2861 '',\r
2862 '                       function LogEntryMainElementContainer(logEntry, containerDomNode) {',\r
2863 '                               this.logEntry = logEntry;',\r
2864 '                               this.containerDomNode = containerDomNode;',\r
2865 '                               this.mainDiv = document.createElement("div");',\r
2866 '                               this.mainDiv.className = "logentry nonielogentry " + this.logEntry.level;',\r
2867 '                               this.contentElement = this.mainDiv.appendChild(document.createElement("span"));',\r
2868 '                               this.contentElement.appendChild(document.createTextNode(this.logEntry.formattedMessage));',\r
2869 '                       }',\r
2870 '',\r
2871 '                       LogEntryMainElementContainer.prototype = new LogEntryElementContainer();',\r
2872 '',\r
2873 '                       /*----------------------------------------------------------------*/',\r
2874 '',\r
2875 '                       function LogEntry(level, formattedMessage) {',\r
2876 '                               this.level = level;',\r
2877 '                               this.formattedMessage = formattedMessage;',\r
2878 '                               this.rendered = false;',\r
2879 '                       }',\r
2880 '',\r
2881 '                       LogEntry.prototype = new LogItem();',\r
2882 '',\r
2883 '                       copyProperties(LogEntry.prototype, {',\r
2884 '                               render: function() {',\r
2885 '                                       var logEntry = this;',\r
2886 '                                       var containerDomNode = this.group.contentDiv;',\r
2887 '',\r
2888 '                                       // Support for the CSS attribute white-space in IE for Windows is',\r
2889 '                                       // non-existent pre version 6 and slightly odd in 6, so instead',\r
2890 '                                       // use two different HTML elements',\r
2891 '                                       if (isIe) {',\r
2892 '                                               this.formattedMessage = this.formattedMessage.replace(/\\r\\n/g, "\\r"); // Workaround for IE\'s treatment of white space',\r
2893 '                                               this.unwrappedElementContainer = new LogEntryUnwrappedElementContainer(this, this.getUnwrappedDomContainer());',\r
2894 '                                               this.wrappedElementContainer = new LogEntryWrappedElementContainer(this, this.getWrappedDomContainer());',\r
2895 '                                               this.elementContainers = [this.unwrappedElementContainer, this.wrappedElementContainer];',\r
2896 '                                       } else {',\r
2897 '                                               this.mainElementContainer = new LogEntryMainElementContainer(this, this.getMainDomContainer());',\r
2898 '                                               this.elementContainers = [this.mainElementContainer];',\r
2899 '                                       }',\r
2900 '                                       this.content = this.formattedMessage;',\r
2901 '                                       this.rendered = true;',\r
2902 '                               },',\r
2903 '',\r
2904 '                               setContent: function(content, wrappedContent) {',\r
2905 '                                       if (content != this.content) {',\r
2906 '                                               if (isIe && (content !== this.formattedMessage)) {',\r
2907 '                                                       content = content.replace(/\\r\\n/g, "\\r"); // Workaround for IE\'s treatment of white space',\r
2908 '                                               }',\r
2909 '                                               for (var i = 0, len = this.elementContainers.length; i < len; i++) {',\r
2910 '                                                       this.elementContainers[i].setContent(content, wrappedContent);',\r
2911 '                                               }',\r
2912 '                                               this.content = content;',\r
2913 '                                       }',\r
2914 '                               },',\r
2915 '',\r
2916 '                               getSearchMatches: function() {',\r
2917 '                                       var matches = [];',\r
2918 '                                       var i, len;',\r
2919 '                                       if (isIe) {',\r
2920 '                                               var unwrappedEls = getElementsByClass(this.unwrappedElementContainer.mainDiv, "searchterm", "span");',\r
2921 '                                               var wrappedEls = getElementsByClass(this.wrappedElementContainer.mainDiv, "searchterm", "span");',\r
2922 '                                               for (i = 0, len = unwrappedEls.length; i < len; i++) {',\r
2923 '                                                       matches[i] = new Match(this.level, null, unwrappedEls[i], wrappedEls[i]);',\r
2924 '                                               }',\r
2925 '                                       } else {',\r
2926 '                                               var els = getElementsByClass(this.mainElementContainer.mainDiv, "searchterm", "span");',\r
2927 '                                               for (i = 0, len = els.length; i < len; i++) {',\r
2928 '                                                       matches[i] = new Match(this.level, els[i]);',\r
2929 '                                               }',\r
2930 '                                       }',\r
2931 '                                       return matches;',\r
2932 '                               },',\r
2933 '',\r
2934 '                               setSearchMatch: function(isMatch) {',\r
2935 '                                       for (var i = 0, len = this.elementContainers.length; i < len; i++) {',\r
2936 '                                               this.elementContainers[i].setSearchMatch(isMatch);',\r
2937 '                                       }',\r
2938 '                               },',\r
2939 '',\r
2940 '                               clearSearch: function() {',\r
2941 '                                       for (var i = 0, len = this.elementContainers.length; i < len; i++) {',\r
2942 '                                               this.elementContainers[i].clearSearch();',\r
2943 '                                       }',\r
2944 '                               },',\r
2945 '',\r
2946 '                               accept: function(visitor) {',\r
2947 '                                       visitor.visitLogEntry(this);',\r
2948 '                               },',\r
2949 '',\r
2950 '                               serialize: function(items) {',\r
2951 '                                       items.push([LogItem.serializedItemKeys.LOG_ENTRY, this.level, this.formattedMessage]);',\r
2952 '                               }',\r
2953 '                       });',\r
2954 '',\r
2955 '                       /*----------------------------------------------------------------*/',\r
2956 '',\r
2957 '                       function LogItemVisitor() {',\r
2958 '                       }',\r
2959 '',\r
2960 '                       LogItemVisitor.prototype = {',\r
2961 '                               visit: function(logItem) {',\r
2962 '                               },',\r
2963 '',\r
2964 '                               visitParent: function(logItem) {',\r
2965 '                                       if (logItem.group) {',\r
2966 '                                               logItem.group.accept(this);',\r
2967 '                                       }',\r
2968 '                               },',\r
2969 '',\r
2970 '                               visitChildren: function(logItem) {',\r
2971 '                                       for (var i = 0, len = logItem.children.length; i < len; i++) {',\r
2972 '                                               logItem.children[i].accept(this);',\r
2973 '                                       }',\r
2974 '                               },',\r
2975 '',\r
2976 '                               visitLogEntry: function(logEntry) {',\r
2977 '                                       this.visit(logEntry);',\r
2978 '                               },',\r
2979 '',\r
2980 '                               visitSeparator: function(separator) {',\r
2981 '                                       this.visit(separator);',\r
2982 '                               },',\r
2983 '',\r
2984 '                               visitGroup: function(group) {',\r
2985 '                                       this.visit(group);',\r
2986 '                               }',\r
2987 '                       };',\r
2988 '',\r
2989 '                       /*----------------------------------------------------------------*/',\r
2990 '',\r
2991 '                       function GroupFlattener() {',\r
2992 '                               this.logEntriesAndSeparators = [];',\r
2993 '                       }',\r
2994 '',\r
2995 '                       GroupFlattener.prototype = new LogItemVisitor();',\r
2996 '',\r
2997 '                       GroupFlattener.prototype.visitGroup = function(group) {',\r
2998 '                               this.visitChildren(group);',\r
2999 '                       };',\r
3000 '',\r
3001 '                       GroupFlattener.prototype.visitLogEntry = function(logEntry) {',\r
3002 '                               this.logEntriesAndSeparators.push(logEntry);',\r
3003 '                       };',\r
3004 '',\r
3005 '                       GroupFlattener.prototype.visitSeparator = function(separator) {',\r
3006 '                               this.logEntriesAndSeparators.push(separator);',\r
3007 '                       };',\r
3008 '',\r
3009 '                       /*----------------------------------------------------------------*/',\r
3010 '',\r
3011 '                       window.onload = function() {',\r
3012 '                               // Sort out document.domain',\r
3013 '                               if (location.search) {',\r
3014 '                                       var queryBits = unescape(location.search).substr(1).split("&"), nameValueBits;',\r
3015 '                                       for (var i = 0, len = queryBits.length; i < len; i++) {',\r
3016 '                                               nameValueBits = queryBits[i].split("=");',\r
3017 '                                               if (nameValueBits[0] == "log4javascript_domain") {',\r
3018 '                                                       document.domain = nameValueBits[1];',\r
3019 '                                                       break;',\r
3020 '                                               }',\r
3021 '                                       }',\r
3022 '                               }',\r
3023 '',\r
3024 '                               // Create DOM objects',\r
3025 '                               logMainContainer = $("log");',\r
3026 '                               if (isIePre7) {',\r
3027 '                                       addClass(logMainContainer, "oldIe");',\r
3028 '                               }',\r
3029 '',\r
3030 '                               rootGroup = new Group("root", true);',\r
3031 '                               rootGroup.render();',\r
3032 '                               currentGroup = rootGroup;',\r
3033 '                               ',\r
3034 '                               setCommandInputWidth();',\r
3035 '                               setLogContainerHeight();',\r
3036 '                               toggleLoggingEnabled();',\r
3037 '                               toggleSearchEnabled();',\r
3038 '                               toggleSearchFilter();',\r
3039 '                               toggleSearchHighlight();',\r
3040 '                               applyFilters();',\r
3041 '                               checkAllLevels();',\r
3042 '                               toggleWrap();',\r
3043 '                               toggleNewestAtTop();',\r
3044 '                               toggleScrollToLatest();',\r
3045 '                               renderQueuedLogItems();',\r
3046 '                               loaded = true;',\r
3047 '                               $("command").value = "";',\r
3048 '                               $("command").autocomplete = "off";',\r
3049 '                               $("command").onkeydown = function(evt) {',\r
3050 '                                       evt = getEvent(evt);',\r
3051 '                                       if (evt.keyCode == 10 || evt.keyCode == 13) { // Return/Enter',\r
3052 '                                               evalCommandLine();',\r
3053 '                                               stopPropagation(evt);',\r
3054 '                                       } else if (evt.keyCode == 27) { // Escape',\r
3055 '                                               this.value = "";',\r
3056 '                                               this.focus();',\r
3057 '                                       } else if (evt.keyCode == 38 && commandHistory.length > 0) { // Up',\r
3058 '                                               currentCommandIndex = Math.max(0, currentCommandIndex - 1);',\r
3059 '                                               this.value = commandHistory[currentCommandIndex];',\r
3060 '                                               moveCaretToEnd(this);',\r
3061 '                                       } else if (evt.keyCode == 40 && commandHistory.length > 0) { // Down',\r
3062 '                                               currentCommandIndex = Math.min(commandHistory.length - 1, currentCommandIndex + 1);',\r
3063 '                                               this.value = commandHistory[currentCommandIndex];',\r
3064 '                                               moveCaretToEnd(this);',\r
3065 '                                       }',\r
3066 '                               };',\r
3067 '',\r
3068 '                               // Prevent the keypress moving the caret in Firefox',\r
3069 '                               $("command").onkeypress = function(evt) {',\r
3070 '                                       evt = getEvent(evt);',\r
3071 '                                       if (evt.keyCode == 38 && commandHistory.length > 0 && evt.preventDefault) { // Up',\r
3072 '                                               evt.preventDefault();',\r
3073 '                                       }',\r
3074 '                               };',\r
3075 '',\r
3076 '                               // Prevent the keyup event blurring the input in Opera',\r
3077 '                               $("command").onkeyup = function(evt) {',\r
3078 '                                       evt = getEvent(evt);',\r
3079 '                                       if (evt.keyCode == 27 && evt.preventDefault) { // Up',\r
3080 '                                               evt.preventDefault();',\r
3081 '                                               this.focus();',\r
3082 '                                       }',\r
3083 '                               };',\r
3084 '',\r
3085 '                               // Add document keyboard shortcuts',\r
3086 '                               document.onkeydown = function keyEventHandler(evt) {',\r
3087 '                                       evt = getEvent(evt);',\r
3088 '                                       switch (evt.keyCode) {',\r
3089 '                                               case 69: // Ctrl + shift + E: re-execute last command',\r
3090 '                                                       if (evt.shiftKey && (evt.ctrlKey || evt.metaKey)) {',\r
3091 '                                                               evalLastCommand();',\r
3092 '                                                               cancelKeyEvent(evt);',\r
3093 '                                                               return false;',\r
3094 '                                                       }',\r
3095 '                                                       break;',\r
3096 '                                               case 75: // Ctrl + shift + K: focus search',\r
3097 '                                                       if (evt.shiftKey && (evt.ctrlKey || evt.metaKey)) {',\r
3098 '                                                               focusSearch();',\r
3099 '                                                               cancelKeyEvent(evt);',\r
3100 '                                                               return false;',\r
3101 '                                                       }',\r
3102 '                                                       break;',\r
3103 '                                               case 40: // Ctrl + shift + down arrow: focus command line',\r
3104 '                                               case 76: // Ctrl + shift + L: focus command line',\r
3105 '                                                       if (evt.shiftKey && (evt.ctrlKey || evt.metaKey)) {',\r
3106 '                                                               focusCommandLine();',\r
3107 '                                                               cancelKeyEvent(evt);',\r
3108 '                                                               return false;',\r
3109 '                                                       }',\r
3110 '                                                       break;',\r
3111 '                                       }',\r
3112 '                               };',\r
3113 '',\r
3114 '                               // Workaround to make sure log div starts at the correct size',\r
3115 '                               setTimeout(setLogContainerHeight, 20);',\r
3116 '',\r
3117 '                               setShowCommandLine(showCommandLine);',\r
3118 '                               doSearch();',\r
3119 '                       };',\r
3120 '',\r
3121 '                       window.onunload = function() {',\r
3122 '                               if (mainWindowExists()) {',\r
3123 '                                       appender.unload();',\r
3124 '                               }',\r
3125 '                               appender = null;',\r
3126 '                       };',\r
3127 '',\r
3128 '                       /*----------------------------------------------------------------*/',\r
3129 '',\r
3130 '                       function toggleLoggingEnabled() {',\r
3131 '                               setLoggingEnabled($("enableLogging").checked);',\r
3132 '                       }',\r
3133 '',\r
3134 '                       function setLoggingEnabled(enable) {',\r
3135 '                               loggingEnabled = enable;',\r
3136 '                       }',\r
3137 '',\r
3138 '                       var appender = null;',\r
3139 '',\r
3140 '                       function setAppender(appenderParam) {',\r
3141 '                               appender = appenderParam;',\r
3142 '                       }',\r
3143 '',\r
3144 '                       function setShowCloseButton(showCloseButton) {',\r
3145 '                               $("closeButton").style.display = showCloseButton ? "inline" : "none";',\r
3146 '                       }',\r
3147 '',\r
3148 '                       function setShowHideButton(showHideButton) {',\r
3149 '                               $("hideButton").style.display = showHideButton ? "inline" : "none";',\r
3150 '                       }',\r
3151 '',\r
3152 '                       var newestAtTop = false;',\r
3153 '',\r
3154 '                       /*----------------------------------------------------------------*/',\r
3155 '',\r
3156 '                       function LogItemContentReverser() {',\r
3157 '                       }',\r
3158 '                       ',\r
3159 '                       LogItemContentReverser.prototype = new LogItemVisitor();',\r
3160 '                       ',\r
3161 '                       LogItemContentReverser.prototype.visitGroup = function(group) {',\r
3162 '                               group.reverseChildren();',\r
3163 '                               this.visitChildren(group);',\r
3164 '                       };',\r
3165 '',\r
3166 '                       /*----------------------------------------------------------------*/',\r
3167 '',\r
3168 '                       function setNewestAtTop(isNewestAtTop) {',\r
3169 '                               var oldNewestAtTop = newestAtTop;',\r
3170 '                               var i, iLen, j, jLen;',\r
3171 '                               newestAtTop = Boolean(isNewestAtTop);',\r
3172 '                               if (oldNewestAtTop != newestAtTop) {',\r
3173 '                                       var visitor = new LogItemContentReverser();',\r
3174 '                                       rootGroup.accept(visitor);',\r
3175 '',\r
3176 '                                       // Reassemble the matches array',\r
3177 '                                       if (currentSearch) {',\r
3178 '                                               var currentMatch = currentSearch.matches[currentMatchIndex];',\r
3179 '                                               var matchIndex = 0;',\r
3180 '                                               var matches = [];',\r
3181 '                                               var actOnLogEntry = function(logEntry) {',\r
3182 '                                                       var logEntryMatches = logEntry.getSearchMatches();',\r
3183 '                                                       for (j = 0, jLen = logEntryMatches.length; j < jLen; j++) {',\r
3184 '                                                               matches[matchIndex] = logEntryMatches[j];',\r
3185 '                                                               if (currentMatch && logEntryMatches[j].equals(currentMatch)) {',\r
3186 '                                                                       currentMatchIndex = matchIndex;',\r
3187 '                                                               }',\r
3188 '                                                               matchIndex++;',\r
3189 '                                                       }',\r
3190 '                                               };',\r
3191 '                                               if (newestAtTop) {',\r
3192 '                                                       for (i = logEntries.length - 1; i >= 0; i--) {',\r
3193 '                                                               actOnLogEntry(logEntries[i]);',\r
3194 '                                                       }',\r
3195 '                                               } else {',\r
3196 '                                                       for (i = 0, iLen = logEntries.length; i < iLen; i++) {',\r
3197 '                                                               actOnLogEntry(logEntries[i]);',\r
3198 '                                                       }',\r
3199 '                                               }',\r
3200 '                                               currentSearch.matches = matches;',\r
3201 '                                               if (currentMatch) {',\r
3202 '                                                       currentMatch.setCurrent();',\r
3203 '                                               }',\r
3204 '                                       } else if (scrollToLatest) {',\r
3205 '                                               doScrollToLatest();',\r
3206 '                                       }',\r
3207 '                               }',\r
3208 '                               $("newestAtTop").checked = isNewestAtTop;',\r
3209 '                       }',\r
3210 '',\r
3211 '                       function toggleNewestAtTop() {',\r
3212 '                               var isNewestAtTop = $("newestAtTop").checked;',\r
3213 '                               setNewestAtTop(isNewestAtTop);',\r
3214 '                       }',\r
3215 '',\r
3216 '                       var scrollToLatest = true;',\r
3217 '',\r
3218 '                       function setScrollToLatest(isScrollToLatest) {',\r
3219 '                               scrollToLatest = isScrollToLatest;',\r
3220 '                               if (scrollToLatest) {',\r
3221 '                                       doScrollToLatest();',\r
3222 '                               }',\r
3223 '                               $("scrollToLatest").checked = isScrollToLatest;',\r
3224 '                       }',\r
3225 '',\r
3226 '                       function toggleScrollToLatest() {',\r
3227 '                               var isScrollToLatest = $("scrollToLatest").checked;',\r
3228 '                               setScrollToLatest(isScrollToLatest);',\r
3229 '                       }',\r
3230 '',\r
3231 '                       function doScrollToLatest() {',\r
3232 '                               var l = logMainContainer;',\r
3233 '                               if (typeof l.scrollTop != "undefined") {',\r
3234 '                                       if (newestAtTop) {',\r
3235 '                                               l.scrollTop = 0;',\r
3236 '                                       } else {',\r
3237 '                                               var latestLogEntry = l.lastChild;',\r
3238 '                                               if (latestLogEntry) {',\r
3239 '                                                       l.scrollTop = l.scrollHeight;',\r
3240 '                                               }',\r
3241 '                                       }',\r
3242 '                               }',\r
3243 '                       }',\r
3244 '',\r
3245 '                       var closeIfOpenerCloses = true;',\r
3246 '',\r
3247 '                       function setCloseIfOpenerCloses(isCloseIfOpenerCloses) {',\r
3248 '                               closeIfOpenerCloses = isCloseIfOpenerCloses;',\r
3249 '                       }',\r
3250 '',\r
3251 '                       var maxMessages = null;',\r
3252 '',\r
3253 '                       function setMaxMessages(max) {',\r
3254 '                               maxMessages = max;',\r
3255 '                               pruneLogEntries();',\r
3256 '                       }',\r
3257 '',\r
3258 '                       var showCommandLine = false;',\r
3259 '',\r
3260 '                       function setShowCommandLine(isShowCommandLine) {',\r
3261 '                               showCommandLine = isShowCommandLine;',\r
3262 '                               if (loaded) {',\r
3263 '                                       $("commandLine").style.display = showCommandLine ? "block" : "none";',\r
3264 '                                       setCommandInputWidth();',\r
3265 '                                       setLogContainerHeight();',\r
3266 '                               }',\r
3267 '                       }',\r
3268 '',\r
3269 '                       function focusCommandLine() {',\r
3270 '                               if (loaded) {',\r
3271 '                                       $("command").focus();',\r
3272 '                               }',\r
3273 '                       }',\r
3274 '',\r
3275 '                       function focusSearch() {',\r
3276 '                               if (loaded) {',\r
3277 '                                       $("searchBox").focus();',\r
3278 '                               }',\r
3279 '                       }',\r
3280 '',\r
3281 '                       function getLogItems() {',\r
3282 '                               var items = [];',\r
3283 '                               for (var i = 0, len = logItems.length; i < len; i++) {',\r
3284 '                                       logItems[i].serialize(items);',\r
3285 '                               }',\r
3286 '                               return items;',\r
3287 '                       }',\r
3288 '',\r
3289 '                       function setLogItems(items) {',\r
3290 '                               var loggingReallyEnabled = loggingEnabled;',\r
3291 '                               // Temporarily turn logging on',\r
3292 '                               loggingEnabled = true;',\r
3293 '                               for (var i = 0, len = items.length; i < len; i++) {',\r
3294 '                                       switch (items[i][0]) {',\r
3295 '                                               case LogItem.serializedItemKeys.LOG_ENTRY:',\r
3296 '                                                       log(items[i][1], items[i][2]);',\r
3297 '                                                       break;',\r
3298 '                                               case LogItem.serializedItemKeys.GROUP_START:',\r
3299 '                                                       group(items[i][1]);',\r
3300 '                                                       break;',\r
3301 '                                               case LogItem.serializedItemKeys.GROUP_END:',\r
3302 '                                                       groupEnd();',\r
3303 '                                                       break;',\r
3304 '                                       }',\r
3305 '                               }',\r
3306 '                               loggingEnabled = loggingReallyEnabled;',\r
3307 '                       }',\r
3308 '',\r
3309 '                       function log(logLevel, formattedMessage) {',\r
3310 '                               if (loggingEnabled) {',\r
3311 '                                       var logEntry = new LogEntry(logLevel, formattedMessage);',\r
3312 '                                       logEntries.push(logEntry);',\r
3313 '                                       logEntriesAndSeparators.push(logEntry);',\r
3314 '                                       logItems.push(logEntry);',\r
3315 '                                       currentGroup.addChild(logEntry);',\r
3316 '                                       if (loaded) {',\r
3317 '                                               if (logQueuedEventsTimer !== null) {',\r
3318 '                                                       clearTimeout(logQueuedEventsTimer);',\r
3319 '                                               }',\r
3320 '                                               logQueuedEventsTimer = setTimeout(renderQueuedLogItems, renderDelay);',\r
3321 '                                               unrenderedLogItemsExist = true;',\r
3322 '                                       }',\r
3323 '                               }',\r
3324 '                       }',\r
3325 '',\r
3326 '                       function renderQueuedLogItems() {',\r
3327 '                               logQueuedEventsTimer = null;',\r
3328 '                               var pruned = pruneLogEntries();',\r
3329 '',\r
3330 '                               // Render any unrendered log entries and apply the current search to them',\r
3331 '                               var initiallyHasMatches = currentSearch ? currentSearch.hasMatches() : false;',\r
3332 '                               for (var i = 0, len = logItems.length; i < len; i++) {',\r
3333 '                                       if (!logItems[i].rendered) {',\r
3334 '                                               logItems[i].render();',\r
3335 '                                               logItems[i].appendToLog();',\r
3336 '                                               if (currentSearch && (logItems[i] instanceof LogEntry)) {',\r
3337 '                                                       currentSearch.applyTo(logItems[i]);',\r
3338 '                                               }',\r
3339 '                                       }',\r
3340 '                               }',\r
3341 '                               if (currentSearch) {',\r
3342 '                                       if (pruned) {',\r
3343 '                                               if (currentSearch.hasVisibleMatches()) {',\r
3344 '                                                       if (currentMatchIndex === null) {',\r
3345 '                                                               setCurrentMatchIndex(0);',\r
3346 '                                                       }',\r
3347 '                                                       displayMatches();',\r
3348 '                                               } else {',\r
3349 '                                                       displayNoMatches();',\r
3350 '                                               }',\r
3351 '                                       } else if (!initiallyHasMatches && currentSearch.hasVisibleMatches()) {',\r
3352 '                                               setCurrentMatchIndex(0);',\r
3353 '                                               displayMatches();',\r
3354 '                                       }',\r
3355 '                               }',\r
3356 '                               if (scrollToLatest) {',\r
3357 '                                       doScrollToLatest();',\r
3358 '                               }',\r
3359 '                               unrenderedLogItemsExist = false;',\r
3360 '                       }',\r
3361 '',\r
3362 '                       function pruneLogEntries() {',\r
3363 '                               if ((maxMessages !== null) && (logEntriesAndSeparators.length > maxMessages)) {',\r
3364 '                                       var numberToDelete = logEntriesAndSeparators.length - maxMessages;',\r
3365 '                                       var prunedLogEntries = logEntriesAndSeparators.slice(0, numberToDelete);',\r
3366 '                                       if (currentSearch) {',\r
3367 '                                               currentSearch.removeMatches(prunedLogEntries);',\r
3368 '                                       }',\r
3369 '                                       var group;',\r
3370 '                                       for (var i = 0; i < numberToDelete; i++) {',\r
3371 '                                               group = logEntriesAndSeparators[i].group;',\r
3372 '                                               array_remove(logItems, logEntriesAndSeparators[i]);',\r
3373 '                                               array_remove(logEntries, logEntriesAndSeparators[i]);',\r
3374 '                                               logEntriesAndSeparators[i].remove(true, true);',\r
3375 '                                               if (group.children.length === 0 && group !== currentGroup && group !== rootGroup) {',\r
3376 '                                                       array_remove(logItems, group);',\r
3377 '                                                       group.remove(true, true);',\r
3378 '                                               }',\r
3379 '                                       }',\r
3380 '                                       logEntriesAndSeparators = array_removeFromStart(logEntriesAndSeparators, numberToDelete);',\r
3381 '                                       return true;',\r
3382 '                               }',\r
3383 '                               return false;',\r
3384 '                       }',\r
3385 '',\r
3386 '                       function group(name, startExpanded) {',\r
3387 '                               if (loggingEnabled) {',\r
3388 '                                       initiallyExpanded = (typeof startExpanded === "undefined") ? true : Boolean(startExpanded);',\r
3389 '                                       var newGroup = new Group(name, false, initiallyExpanded);',\r
3390 '                                       currentGroup.addChild(newGroup);',\r
3391 '                                       currentGroup = newGroup;',\r
3392 '                                       logItems.push(newGroup);',\r
3393 '                                       if (loaded) {',\r
3394 '                                               if (logQueuedEventsTimer !== null) {',\r
3395 '                                                       clearTimeout(logQueuedEventsTimer);',\r
3396 '                                               }',\r
3397 '                                               logQueuedEventsTimer = setTimeout(renderQueuedLogItems, renderDelay);',\r
3398 '                                               unrenderedLogItemsExist = true;',\r
3399 '                                       }',\r
3400 '                               }',\r
3401 '                       }',\r
3402 '',\r
3403 '                       function groupEnd() {',\r
3404 '                               currentGroup = (currentGroup === rootGroup) ? rootGroup : currentGroup.group;',\r
3405 '                       }',\r
3406 '',\r
3407 '                       function mainPageReloaded() {',\r
3408 '                               currentGroup = rootGroup;',\r
3409 '                               var separator = new Separator();',\r
3410 '                               logEntriesAndSeparators.push(separator);',\r
3411 '                               logItems.push(separator);',\r
3412 '                               currentGroup.addChild(separator);',\r
3413 '                       }',\r
3414 '',\r
3415 '                       function closeWindow() {',\r
3416 '                               if (appender && mainWindowExists()) {',\r
3417 '                                       appender.close(true);',\r
3418 '                               } else {',\r
3419 '                                       window.close();',\r
3420 '                               }',\r
3421 '                       }',\r
3422 '',\r
3423 '                       function hide() {',\r
3424 '                               if (appender && mainWindowExists()) {',\r
3425 '                                       appender.hide();',\r
3426 '                               }',\r
3427 '                       }',\r
3428 '',\r
3429 '                       var mainWindow = window;',\r
3430 '                       var windowId = "log4javascriptConsoleWindow_" + new Date().getTime() + "_" + ("" + Math.random()).substr(2);',\r
3431 '',\r
3432 '                       function setMainWindow(win) {',\r
3433 '                               mainWindow = win;',\r
3434 '                               mainWindow[windowId] = window;',\r
3435 '                               // If this is a pop-up, poll the opener to see if it\'s closed',\r
3436 '                               if (opener && closeIfOpenerCloses) {',\r
3437 '                                       pollOpener();',\r
3438 '                               }',\r
3439 '                       }',\r
3440 '',\r
3441 '                       function pollOpener() {',\r
3442 '                               if (closeIfOpenerCloses) {',\r
3443 '                                       if (mainWindowExists()) {',\r
3444 '                                               setTimeout(pollOpener, 500);',\r
3445 '                                       } else {',\r
3446 '                                               closeWindow();',\r
3447 '                                       }',\r
3448 '                               }',\r
3449 '                       }',\r
3450 '',\r
3451 '                       function mainWindowExists() {',\r
3452 '                               try {',\r
3453 '                                       return (mainWindow && !mainWindow.closed &&',\r
3454 '                                               mainWindow[windowId] == window);',\r
3455 '                               } catch (ex) {}',\r
3456 '                               return false;',\r
3457 '                       }',\r
3458 '',\r
3459 '                       var logLevels = ["TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"];',\r
3460 '',\r
3461 '                       function getCheckBox(logLevel) {',\r
3462 '                               return $("switch_" + logLevel);',\r
3463 '                       }',\r
3464 '',\r
3465 '                       function getIeWrappedLogContainer() {',\r
3466 '                               return $("log_wrapped");',\r
3467 '                       }',\r
3468 '',\r
3469 '                       function getIeUnwrappedLogContainer() {',\r
3470 '                               return $("log_unwrapped");',\r
3471 '                       }',\r
3472 '',\r
3473 '                       function applyFilters() {',\r
3474 '                               for (var i = 0; i < logLevels.length; i++) {',\r
3475 '                                       if (getCheckBox(logLevels[i]).checked) {',\r
3476 '                                               addClass(logMainContainer, logLevels[i]);',\r
3477 '                                       } else {',\r
3478 '                                               removeClass(logMainContainer, logLevels[i]);',\r
3479 '                                       }',\r
3480 '                               }',\r
3481 '                               updateSearchFromFilters();',\r
3482 '                       }',\r
3483 '',\r
3484 '                       function toggleAllLevels() {',\r
3485 '                               var turnOn = $("switch_ALL").checked;',\r
3486 '                               for (var i = 0; i < logLevels.length; i++) {',\r
3487 '                                       getCheckBox(logLevels[i]).checked = turnOn;',\r
3488 '                                       if (turnOn) {',\r
3489 '                                               addClass(logMainContainer, logLevels[i]);',\r
3490 '                                       } else {',\r
3491 '                                               removeClass(logMainContainer, logLevels[i]);',\r
3492 '                                       }',\r
3493 '                               }',\r
3494 '                       }',\r
3495 '',\r
3496 '                       function checkAllLevels() {',\r
3497 '                               for (var i = 0; i < logLevels.length; i++) {',\r
3498 '                                       if (!getCheckBox(logLevels[i]).checked) {',\r
3499 '                                               getCheckBox("ALL").checked = false;',\r
3500 '                                               return;',\r
3501 '                                       }',\r
3502 '                               }',\r
3503 '                               getCheckBox("ALL").checked = true;',\r
3504 '                       }',\r
3505 '',\r
3506 '                       function clearLog() {',\r
3507 '                               rootGroup.clear();',\r
3508 '                               currentGroup = rootGroup;',\r
3509 '                               logEntries = [];',\r
3510 '                               logItems = [];',\r
3511 '                               logEntriesAndSeparators = [];',\r
3512 '                               doSearch();',\r
3513 '                       }',\r
3514 '',\r
3515 '                       function toggleWrap() {',\r
3516 '                               var enable = $("wrap").checked;',\r
3517 '                               if (enable) {',\r
3518 '                                       addClass(logMainContainer, "wrap");',\r
3519 '                               } else {',\r
3520 '                                       removeClass(logMainContainer, "wrap");',\r
3521 '                               }',\r
3522 '                               refreshCurrentMatch();',\r
3523 '                       }',\r
3524 '',\r
3525 '                       /* ------------------------------------------------------------------- */',\r
3526 '',\r
3527 '                       // Search',\r
3528 '',\r
3529 '                       var searchTimer = null;',\r
3530 '',\r
3531 '                       function scheduleSearch() {',\r
3532 '                               try {',\r
3533 '                                       clearTimeout(searchTimer);',\r
3534 '                               } catch (ex) {',\r
3535 '                                       // Do nothing',\r
3536 '                               }',\r
3537 '                               searchTimer = setTimeout(doSearch, 500);',\r
3538 '                       }',\r
3539 '',\r
3540 '                       function Search(searchTerm, isRegex, searchRegex, isCaseSensitive) {',\r
3541 '                               this.searchTerm = searchTerm;',\r
3542 '                               this.isRegex = isRegex;',\r
3543 '                               this.searchRegex = searchRegex;',\r
3544 '                               this.isCaseSensitive = isCaseSensitive;',\r
3545 '                               this.matches = [];',\r
3546 '                       }',\r
3547 '',\r
3548 '                       Search.prototype = {',\r
3549 '                               hasMatches: function() {',\r
3550 '                                       return this.matches.length > 0;',\r
3551 '                               },',\r
3552 '',\r
3553 '                               hasVisibleMatches: function() {',\r
3554 '                                       if (this.hasMatches()) {',\r
3555 '                                               for (var i = 0; i < this.matches.length; i++) {',\r
3556 '                                                       if (this.matches[i].isVisible()) {',\r
3557 '                                                               return true;',\r
3558 '                                                       }',\r
3559 '                                               }',\r
3560 '                                       }',\r
3561 '                                       return false;',\r
3562 '                               },',\r
3563 '',\r
3564 '                               match: function(logEntry) {',\r
3565 '                                       var entryText = String(logEntry.formattedMessage);',\r
3566 '                                       var matchesSearch = false;',\r
3567 '                                       if (this.isRegex) {',\r
3568 '                                               matchesSearch = this.searchRegex.test(entryText);',\r
3569 '                                       } else if (this.isCaseSensitive) {',\r
3570 '                                               matchesSearch = (entryText.indexOf(this.searchTerm) > -1);',\r
3571 '                                       } else {',\r
3572 '                                               matchesSearch = (entryText.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1);',\r
3573 '                                       }',\r
3574 '                                       return matchesSearch;',\r
3575 '                               },',\r
3576 '',\r
3577 '                               getNextVisibleMatchIndex: function() {',\r
3578 '                                       for (var i = currentMatchIndex + 1; i < this.matches.length; i++) {',\r
3579 '                                               if (this.matches[i].isVisible()) {',\r
3580 '                                                       return i;',\r
3581 '                                               }',\r
3582 '                                       }',\r
3583 '                                       // Start again from the first match',\r
3584 '                                       for (i = 0; i <= currentMatchIndex; i++) {',\r
3585 '                                               if (this.matches[i].isVisible()) {',\r
3586 '                                                       return i;',\r
3587 '                                               }',\r
3588 '                                       }',\r
3589 '                                       return -1;',\r
3590 '                               },',\r
3591 '',\r
3592 '                               getPreviousVisibleMatchIndex: function() {',\r
3593 '                                       for (var i = currentMatchIndex - 1; i >= 0; i--) {',\r
3594 '                                               if (this.matches[i].isVisible()) {',\r
3595 '                                                       return i;',\r
3596 '                                               }',\r
3597 '                                       }',\r
3598 '                                       // Start again from the last match',\r
3599 '                                       for (var i = this.matches.length - 1; i >= currentMatchIndex; i--) {',\r
3600 '                                               if (this.matches[i].isVisible()) {',\r
3601 '                                                       return i;',\r
3602 '                                               }',\r
3603 '                                       }',\r
3604 '                                       return -1;',\r
3605 '                               },',\r
3606 '',\r
3607 '                               applyTo: function(logEntry) {',\r
3608 '                                       var doesMatch = this.match(logEntry);',\r
3609 '                                       if (doesMatch) {',\r
3610 '                                               logEntry.group.expand();',\r
3611 '                                               logEntry.setSearchMatch(true);',\r
3612 '                                               var logEntryContent;',\r
3613 '                                               var wrappedLogEntryContent;',\r
3614 '                                               var searchTermReplacementStartTag = "<span class=\\\"searchterm\\\">";',\r
3615 '                                               var searchTermReplacementEndTag = "<" + "/span>";',\r
3616 '                                               var preTagName = isIe ? "pre" : "span";',\r
3617 '                                               var preStartTag = "<" + preTagName + " class=\\\"pre\\\">";',\r
3618 '                                               var preEndTag = "<" + "/" + preTagName + ">";',\r
3619 '                                               var startIndex = 0;',\r
3620 '                                               var searchIndex, matchedText, textBeforeMatch;',\r
3621 '                                               if (this.isRegex) {',\r
3622 '                                                       var flags = this.isCaseSensitive ? "g" : "gi";',\r
3623 '                                                       var capturingRegex = new RegExp("(" + this.searchRegex.source + ")", flags);',\r
3624 '',\r
3625 '                                                       // Replace the search term with temporary tokens for the start and end tags',\r
3626 '                                                       var rnd = ("" + Math.random()).substr(2);',\r
3627 '                                                       var startToken = "%%s" + rnd + "%%";',\r
3628 '                                                       var endToken = "%%e" + rnd + "%%";',\r
3629 '                                                       logEntryContent = logEntry.formattedMessage.replace(capturingRegex, startToken + "$1" + endToken);',\r
3630 '',\r
3631 '                                                       // Escape the HTML to get rid of angle brackets',\r
3632 '                                                       logEntryContent = escapeHtml(logEntryContent);',\r
3633 '',\r
3634 '                                                       // Substitute the proper HTML back in for the search match',\r
3635 '                                                       var result;',\r
3636 '                                                       var searchString = logEntryContent;',\r
3637 '                                                       logEntryContent = "";',\r
3638 '                                                       wrappedLogEntryContent = "";',\r
3639 '                                                       while ((searchIndex = searchString.indexOf(startToken, startIndex)) > -1) {',\r
3640 '                                                               var endTokenIndex = searchString.indexOf(endToken, searchIndex);',\r
3641 '                                                               matchedText = searchString.substring(searchIndex + startToken.length, endTokenIndex);',\r
3642 '                                                               textBeforeMatch = searchString.substring(startIndex, searchIndex);',\r
3643 '                                                               logEntryContent += preStartTag + textBeforeMatch + preEndTag;',\r
3644 '                                                               logEntryContent += searchTermReplacementStartTag + preStartTag + matchedText +',\r
3645 '                                                                       preEndTag + searchTermReplacementEndTag;',\r
3646 '                                                               if (isIe) {',\r
3647 '                                                                       wrappedLogEntryContent += textBeforeMatch + searchTermReplacementStartTag +',\r
3648 '                                                                               matchedText + searchTermReplacementEndTag;',\r
3649 '                                                               }',\r
3650 '                                                               startIndex = endTokenIndex + endToken.length;',\r
3651 '                                                       }',\r
3652 '                                                       logEntryContent += preStartTag + searchString.substr(startIndex) + preEndTag;',\r
3653 '                                                       if (isIe) {',\r
3654 '                                                               wrappedLogEntryContent += searchString.substr(startIndex);',\r
3655 '                                                       }',\r
3656 '                                               } else {',\r
3657 '                                                       logEntryContent = "";',\r
3658 '                                                       wrappedLogEntryContent = "";',\r
3659 '                                                       var searchTermReplacementLength = searchTermReplacementStartTag.length +',\r
3660 '                                                               this.searchTerm.length + searchTermReplacementEndTag.length;',\r
3661 '                                                       var searchTermLength = this.searchTerm.length;',\r
3662 '                                                       var searchTermLowerCase = this.searchTerm.toLowerCase();',\r
3663 '                                                       var logTextLowerCase = logEntry.formattedMessage.toLowerCase();',\r
3664 '                                                       while ((searchIndex = logTextLowerCase.indexOf(searchTermLowerCase, startIndex)) > -1) {',\r
3665 '                                                               matchedText = escapeHtml(logEntry.formattedMessage.substr(searchIndex, this.searchTerm.length));',\r
3666 '                                                               textBeforeMatch = escapeHtml(logEntry.formattedMessage.substring(startIndex, searchIndex));',\r
3667 '                                                               var searchTermReplacement = searchTermReplacementStartTag +',\r
3668 '                                                                       preStartTag + matchedText + preEndTag + searchTermReplacementEndTag;',\r
3669 '                                                               logEntryContent += preStartTag + textBeforeMatch + preEndTag + searchTermReplacement;',\r
3670 '                                                               if (isIe) {',\r
3671 '                                                                       wrappedLogEntryContent += textBeforeMatch + searchTermReplacementStartTag +',\r
3672 '                                                                               matchedText + searchTermReplacementEndTag;',\r
3673 '                                                               }',\r
3674 '                                                               startIndex = searchIndex + searchTermLength;',\r
3675 '                                                       }',\r
3676 '                                                       var textAfterLastMatch = escapeHtml(logEntry.formattedMessage.substr(startIndex));',\r
3677 '                                                       logEntryContent += preStartTag + textAfterLastMatch + preEndTag;',\r
3678 '                                                       if (isIe) {',\r
3679 '                                                               wrappedLogEntryContent += textAfterLastMatch;',\r
3680 '                                                       }',\r
3681 '                                               }',\r
3682 '                                               logEntry.setContent(logEntryContent, wrappedLogEntryContent);',\r
3683 '                                               var logEntryMatches = logEntry.getSearchMatches();',\r
3684 '                                               this.matches = this.matches.concat(logEntryMatches);',\r
3685 '                                       } else {',\r
3686 '                                               logEntry.setSearchMatch(false);',\r
3687 '                                               logEntry.setContent(logEntry.formattedMessage, logEntry.formattedMessage);',\r
3688 '                                       }',\r
3689 '                                       return doesMatch;',\r
3690 '                               },',\r
3691 '',\r
3692 '                               removeMatches: function(logEntries) {',\r
3693 '                                       var matchesToRemoveCount = 0;',\r
3694 '                                       var currentMatchRemoved = false;',\r
3695 '                                       var matchesToRemove = [];',\r
3696 '                                       var i, iLen, j, jLen;',\r
3697 '',\r
3698 '                                       // Establish the list of matches to be removed',\r
3699 '                                       for (i = 0, iLen = this.matches.length; i < iLen; i++) {',\r
3700 '                                               for (j = 0, jLen = logEntries.length; j < jLen; j++) {',\r
3701 '                                                       if (this.matches[i].belongsTo(logEntries[j])) {',\r
3702 '                                                               matchesToRemove.push(this.matches[i]);',\r
3703 '                                                               if (i === currentMatchIndex) {',\r
3704 '                                                                       currentMatchRemoved = true;',\r
3705 '                                                               }',\r
3706 '                                                       }',\r
3707 '                                               }',\r
3708 '                                       }',\r
3709 '',\r
3710 '                                       // Set the new current match index if the current match has been deleted',\r
3711 '                                       // This will be the first match that appears after the first log entry being',\r
3712 '                                       // deleted, if one exists; otherwise, it\'s the first match overall',\r
3713 '                                       var newMatch = currentMatchRemoved ? null : this.matches[currentMatchIndex];',\r
3714 '                                       if (currentMatchRemoved) {',\r
3715 '                                               for (i = currentMatchIndex, iLen = this.matches.length; i < iLen; i++) {',\r
3716 '                                                       if (this.matches[i].isVisible() && !array_contains(matchesToRemove, this.matches[i])) {',\r
3717 '                                                               newMatch = this.matches[i];',\r
3718 '                                                               break;',\r
3719 '                                                       }',\r
3720 '                                               }',\r
3721 '                                       }',\r
3722 '',\r
3723 '                                       // Remove the matches',\r
3724 '                                       for (i = 0, iLen = matchesToRemove.length; i < iLen; i++) {',\r
3725 '                                               array_remove(this.matches, matchesToRemove[i]);',\r
3726 '                                               matchesToRemove[i].remove();',\r
3727 '                                       }',\r
3728 '',\r
3729 '                                       // Set the new match, if one exists',\r
3730 '                                       if (this.hasVisibleMatches()) {',\r
3731 '                                               if (newMatch === null) {',\r
3732 '                                                       setCurrentMatchIndex(0);',\r
3733 '                                               } else {',\r
3734 '                                                       // Get the index of the new match',\r
3735 '                                                       var newMatchIndex = 0;',\r
3736 '                                                       for (i = 0, iLen = this.matches.length; i < iLen; i++) {',\r
3737 '                                                               if (newMatch === this.matches[i]) {',\r
3738 '                                                                       newMatchIndex = i;',\r
3739 '                                                                       break;',\r
3740 '                                                               }',\r
3741 '                                                       }',\r
3742 '                                                       setCurrentMatchIndex(newMatchIndex);',\r
3743 '                                               }',\r
3744 '                                       } else {',\r
3745 '                                               currentMatchIndex = null;',\r
3746 '                                               displayNoMatches();',\r
3747 '                                       }',\r
3748 '                               }',\r
3749 '                       };',\r
3750 '',\r
3751 '                       function getPageOffsetTop(el, container) {',\r
3752 '                               var currentEl = el;',\r
3753 '                               var y = 0;',\r
3754 '                               while (currentEl && currentEl != container) {',\r
3755 '                                       y += currentEl.offsetTop;',\r
3756 '                                       currentEl = currentEl.offsetParent;',\r
3757 '                               }',\r
3758 '                               return y;',\r
3759 '                       }',\r
3760 '',\r
3761 '                       function scrollIntoView(el) {',\r
3762 '                               var logContainer = logMainContainer;',\r
3763 '                               // Check if the whole width of the element is visible and centre if not',\r
3764 '                               if (!$("wrap").checked) {',\r
3765 '                                       var logContainerLeft = logContainer.scrollLeft;',\r
3766 '                                       var logContainerRight = logContainerLeft  + logContainer.offsetWidth;',\r
3767 '                                       var elLeft = el.offsetLeft;',\r
3768 '                                       var elRight = elLeft + el.offsetWidth;',\r
3769 '                                       if (elLeft < logContainerLeft || elRight > logContainerRight) {',\r
3770 '                                               logContainer.scrollLeft = elLeft - (logContainer.offsetWidth - el.offsetWidth) / 2;',\r
3771 '                                       }',\r
3772 '                               }',\r
3773 '                               // Check if the whole height of the element is visible and centre if not',\r
3774 '                               var logContainerTop = logContainer.scrollTop;',\r
3775 '                               var logContainerBottom = logContainerTop  + logContainer.offsetHeight;',\r
3776 '                               var elTop = getPageOffsetTop(el) - getToolBarsHeight();',\r
3777 '                               var elBottom = elTop + el.offsetHeight;',\r
3778 '                               if (elTop < logContainerTop || elBottom > logContainerBottom) {',\r
3779 '                                       logContainer.scrollTop = elTop - (logContainer.offsetHeight - el.offsetHeight) / 2;',\r
3780 '                               }',\r
3781 '                       }',\r
3782 '',\r
3783 '                       function Match(logEntryLevel, spanInMainDiv, spanInUnwrappedPre, spanInWrappedDiv) {',\r
3784 '                               this.logEntryLevel = logEntryLevel;',\r
3785 '                               this.spanInMainDiv = spanInMainDiv;',\r
3786 '                               if (isIe) {',\r
3787 '                                       this.spanInUnwrappedPre = spanInUnwrappedPre;',\r
3788 '                                       this.spanInWrappedDiv = spanInWrappedDiv;',\r
3789 '                               }',\r
3790 '                               this.mainSpan = isIe ? spanInUnwrappedPre : spanInMainDiv;',\r
3791 '                       }',\r
3792 '',\r
3793 '                       Match.prototype = {',\r
3794 '                               equals: function(match) {',\r
3795 '                                       return this.mainSpan === match.mainSpan;',\r
3796 '                               },',\r
3797 '',\r
3798 '                               setCurrent: function() {',\r
3799 '                                       if (isIe) {',\r
3800 '                                               addClass(this.spanInUnwrappedPre, "currentmatch");',\r
3801 '                                               addClass(this.spanInWrappedDiv, "currentmatch");',\r
3802 '                                               // Scroll the visible one into view',\r
3803 '                                               var elementToScroll = $("wrap").checked ? this.spanInWrappedDiv : this.spanInUnwrappedPre;',\r
3804 '                                               scrollIntoView(elementToScroll);',\r
3805 '                                       } else {',\r
3806 '                                               addClass(this.spanInMainDiv, "currentmatch");',\r
3807 '                                               scrollIntoView(this.spanInMainDiv);',\r
3808 '                                       }',\r
3809 '                               },',\r
3810 '',\r
3811 '                               belongsTo: function(logEntry) {',\r
3812 '                                       if (isIe) {',\r
3813 '                                               return isDescendant(this.spanInUnwrappedPre, logEntry.unwrappedPre);',\r
3814 '                                       } else {',\r
3815 '                                               return isDescendant(this.spanInMainDiv, logEntry.mainDiv);',\r
3816 '                                       }',\r
3817 '                               },',\r
3818 '',\r
3819 '                               setNotCurrent: function() {',\r
3820 '                                       if (isIe) {',\r
3821 '                                               removeClass(this.spanInUnwrappedPre, "currentmatch");',\r
3822 '                                               removeClass(this.spanInWrappedDiv, "currentmatch");',\r
3823 '                                       } else {',\r
3824 '                                               removeClass(this.spanInMainDiv, "currentmatch");',\r
3825 '                                       }',\r
3826 '                               },',\r
3827 '',\r
3828 '                               isOrphan: function() {',\r
3829 '                                       return isOrphan(this.mainSpan);',\r
3830 '                               },',\r
3831 '',\r
3832 '                               isVisible: function() {',\r
3833 '                                       return getCheckBox(this.logEntryLevel).checked;',\r
3834 '                               },',\r
3835 '',\r
3836 '                               remove: function() {',\r
3837 '                                       if (isIe) {',\r
3838 '                                               this.spanInUnwrappedPre = null;',\r
3839 '                                               this.spanInWrappedDiv = null;',\r
3840 '                                       } else {',\r
3841 '                                               this.spanInMainDiv = null;',\r
3842 '                                       }',\r
3843 '                               }',\r
3844 '                       };',\r
3845 '',\r
3846 '                       var currentSearch = null;',\r
3847 '                       var currentMatchIndex = null;',\r
3848 '',\r
3849 '                       function doSearch() {',\r
3850 '                               var searchBox = $("searchBox");',\r
3851 '                               var searchTerm = searchBox.value;',\r
3852 '                               var isRegex = $("searchRegex").checked;',\r
3853 '                               var isCaseSensitive = $("searchCaseSensitive").checked;',\r
3854 '                               var i;',\r
3855 '',\r
3856 '                               if (searchTerm === "") {',\r
3857 '                                       $("searchReset").disabled = true;',\r
3858 '                                       $("searchNav").style.display = "none";',\r
3859 '                                       removeClass(document.body, "searching");',\r
3860 '                                       removeClass(searchBox, "hasmatches");',\r
3861 '                                       removeClass(searchBox, "nomatches");',\r
3862 '                                       for (i = 0; i < logEntries.length; i++) {',\r
3863 '                                               logEntries[i].clearSearch();',\r
3864 '                                               logEntries[i].setContent(logEntries[i].formattedMessage, logEntries[i].formattedMessage);',\r
3865 '                                       }',\r
3866 '                                       currentSearch = null;',\r
3867 '                                       setLogContainerHeight();',\r
3868 '                               } else {',\r
3869 '                                       $("searchReset").disabled = false;',\r
3870 '                                       $("searchNav").style.display = "block";',\r
3871 '                                       var searchRegex;',\r
3872 '                                       var regexValid;',\r
3873 '                                       if (isRegex) {',\r
3874 '                                               try {',\r
3875 '                                                       searchRegex = isCaseSensitive ? new RegExp(searchTerm, "g") : new RegExp(searchTerm, "gi");',\r
3876 '                                                       regexValid = true;',\r
3877 '                                                       replaceClass(searchBox, "validregex", "invalidregex");',\r
3878 '                                                       searchBox.title = "Valid regex";',\r
3879 '                                               } catch (ex) {',\r
3880 '                                                       regexValid = false;',\r
3881 '                                                       replaceClass(searchBox, "invalidregex", "validregex");',\r
3882 '                                                       searchBox.title = "Invalid regex: " + (ex.message ? ex.message : (ex.description ? ex.description : "unknown error"));',\r
3883 '                                                       return;',\r
3884 '                                               }',\r
3885 '                                       } else {',\r
3886 '                                               searchBox.title = "";',\r
3887 '                                               removeClass(searchBox, "validregex");',\r
3888 '                                               removeClass(searchBox, "invalidregex");',\r
3889 '                                       }',\r
3890 '                                       addClass(document.body, "searching");',\r
3891 '                                       currentSearch = new Search(searchTerm, isRegex, searchRegex, isCaseSensitive);',\r
3892 '                                       for (i = 0; i < logEntries.length; i++) {',\r
3893 '                                               currentSearch.applyTo(logEntries[i]);',\r
3894 '                                       }',\r
3895 '                                       setLogContainerHeight();',\r
3896 '',\r
3897 '                                       // Highlight the first search match',\r
3898 '                                       if (currentSearch.hasVisibleMatches()) {',\r
3899 '                                               setCurrentMatchIndex(0);',\r
3900 '                                               displayMatches();',\r
3901 '                                       } else {',\r
3902 '                                               displayNoMatches();',\r
3903 '                                       }',\r
3904 '                               }',\r
3905 '                       }',\r
3906 '',\r
3907 '                       function updateSearchFromFilters() {',\r
3908 '                               if (currentSearch) {',\r
3909 '                                       if (currentSearch.hasMatches()) {',\r
3910 '                                               if (currentMatchIndex === null) {',\r
3911 '                                                       currentMatchIndex = 0;',\r
3912 '                                               }',\r
3913 '                                               var currentMatch = currentSearch.matches[currentMatchIndex];',\r
3914 '                                               if (currentMatch.isVisible()) {',\r
3915 '                                                       displayMatches();',\r
3916 '                                                       setCurrentMatchIndex(currentMatchIndex);',\r
3917 '                                               } else {',\r
3918 '                                                       currentMatch.setNotCurrent();',\r
3919 '                                                       // Find the next visible match, if one exists',\r
3920 '                                                       var nextVisibleMatchIndex = currentSearch.getNextVisibleMatchIndex();',\r
3921 '                                                       if (nextVisibleMatchIndex > -1) {',\r
3922 '                                                               setCurrentMatchIndex(nextVisibleMatchIndex);',\r
3923 '                                                               displayMatches();',\r
3924 '                                                       } else {',\r
3925 '                                                               displayNoMatches();',\r
3926 '                                                       }',\r
3927 '                                               }',\r
3928 '                                       } else {',\r
3929 '                                               displayNoMatches();',\r
3930 '                                       }',\r
3931 '                               }',\r
3932 '                       }',\r
3933 '',\r
3934 '                       function refreshCurrentMatch() {',\r
3935 '                               if (currentSearch && currentSearch.hasVisibleMatches()) {',\r
3936 '                                       setCurrentMatchIndex(currentMatchIndex);',\r
3937 '                               }',\r
3938 '                       }',\r
3939 '',\r
3940 '                       function displayMatches() {',\r
3941 '                               replaceClass($("searchBox"), "hasmatches", "nomatches");',\r
3942 '                               $("searchBox").title = "" + currentSearch.matches.length + " matches found";',\r
3943 '                               $("searchNav").style.display = "block";',\r
3944 '                               setLogContainerHeight();',\r
3945 '                       }',\r
3946 '',\r
3947 '                       function displayNoMatches() {',\r
3948 '                               replaceClass($("searchBox"), "nomatches", "hasmatches");',\r
3949 '                               $("searchBox").title = "No matches found";',\r
3950 '                               $("searchNav").style.display = "none";',\r
3951 '                               setLogContainerHeight();',\r
3952 '                       }',\r
3953 '',\r
3954 '                       function toggleSearchEnabled(enable) {',\r
3955 '                               enable = (typeof enable == "undefined") ? !$("searchDisable").checked : enable;',\r
3956 '                               $("searchBox").disabled = !enable;',\r
3957 '                               $("searchReset").disabled = !enable;',\r
3958 '                               $("searchRegex").disabled = !enable;',\r
3959 '                               $("searchNext").disabled = !enable;',\r
3960 '                               $("searchPrevious").disabled = !enable;',\r
3961 '                               $("searchCaseSensitive").disabled = !enable;',\r
3962 '                               $("searchNav").style.display = (enable && ($("searchBox").value !== "") &&',\r
3963 '                                               currentSearch && currentSearch.hasVisibleMatches()) ?',\r
3964 '                                       "block" : "none";',\r
3965 '                               if (enable) {',\r
3966 '                                       removeClass($("search"), "greyedout");',\r
3967 '                                       addClass(document.body, "searching");',\r
3968 '                                       if ($("searchHighlight").checked) {',\r
3969 '                                               addClass(logMainContainer, "searchhighlight");',\r
3970 '                                       } else {',\r
3971 '                                               removeClass(logMainContainer, "searchhighlight");',\r
3972 '                                       }',\r
3973 '                                       if ($("searchFilter").checked) {',\r
3974 '                                               addClass(logMainContainer, "searchfilter");',\r
3975 '                                       } else {',\r
3976 '                                               removeClass(logMainContainer, "searchfilter");',\r
3977 '                                       }',\r
3978 '                                       $("searchDisable").checked = !enable;',\r
3979 '                               } else {',\r
3980 '                                       addClass($("search"), "greyedout");',\r
3981 '                                       removeClass(document.body, "searching");',\r
3982 '                                       removeClass(logMainContainer, "searchhighlight");',\r
3983 '                                       removeClass(logMainContainer, "searchfilter");',\r
3984 '                               }',\r
3985 '                               setLogContainerHeight();',\r
3986 '                       }',\r
3987 '',\r
3988 '                       function toggleSearchFilter() {',\r
3989 '                               var enable = $("searchFilter").checked;',\r
3990 '                               if (enable) {',\r
3991 '                                       addClass(logMainContainer, "searchfilter");',\r
3992 '                               } else {',\r
3993 '                                       removeClass(logMainContainer, "searchfilter");',\r
3994 '                               }',\r
3995 '                               refreshCurrentMatch();',\r
3996 '                       }',\r
3997 '',\r
3998 '                       function toggleSearchHighlight() {',\r
3999 '                               var enable = $("searchHighlight").checked;',\r
4000 '                               if (enable) {',\r
4001 '                                       addClass(logMainContainer, "searchhighlight");',\r
4002 '                               } else {',\r
4003 '                                       removeClass(logMainContainer, "searchhighlight");',\r
4004 '                               }',\r
4005 '                       }',\r
4006 '',\r
4007 '                       function clearSearch() {',\r
4008 '                               $("searchBox").value = "";',\r
4009 '                               doSearch();',\r
4010 '                       }',\r
4011 '',\r
4012 '                       function searchNext() {',\r
4013 '                               if (currentSearch !== null && currentMatchIndex !== null) {',\r
4014 '                                       currentSearch.matches[currentMatchIndex].setNotCurrent();',\r
4015 '                                       var nextMatchIndex = currentSearch.getNextVisibleMatchIndex();',\r
4016 '                                       if (nextMatchIndex > currentMatchIndex || confirm("Reached the end of the page. Start from the top?")) {',\r
4017 '                                               setCurrentMatchIndex(nextMatchIndex);',\r
4018 '                                       }',\r
4019 '                               }',\r
4020 '                       }',\r
4021 '',\r
4022 '                       function searchPrevious() {',\r
4023 '                               if (currentSearch !== null && currentMatchIndex !== null) {',\r
4024 '                                       currentSearch.matches[currentMatchIndex].setNotCurrent();',\r
4025 '                                       var previousMatchIndex = currentSearch.getPreviousVisibleMatchIndex();',\r
4026 '                                       if (previousMatchIndex < currentMatchIndex || confirm("Reached the start of the page. Continue from the bottom?")) {',\r
4027 '                                               setCurrentMatchIndex(previousMatchIndex);',\r
4028 '                                       }',\r
4029 '                               }',\r
4030 '                       }',\r
4031 '',\r
4032 '                       function setCurrentMatchIndex(index) {',\r
4033 '                               currentMatchIndex = index;',\r
4034 '                               currentSearch.matches[currentMatchIndex].setCurrent();',\r
4035 '                       }',\r
4036 '',\r
4037 '                       /* ------------------------------------------------------------------------- */',\r
4038 '',\r
4039 '                       // CSS Utilities',\r
4040 '',\r
4041 '                       function addClass(el, cssClass) {',\r
4042 '                               if (!hasClass(el, cssClass)) {',\r
4043 '                                       if (el.className) {',\r
4044 '                                               el.className += " " + cssClass;',\r
4045 '                                       } else {',\r
4046 '                                               el.className = cssClass;',\r
4047 '                                       }',\r
4048 '                               }',\r
4049 '                       }',\r
4050 '',\r
4051 '                       function hasClass(el, cssClass) {',\r
4052 '                               if (el.className) {',\r
4053 '                                       var classNames = el.className.split(" ");',\r
4054 '                                       return array_contains(classNames, cssClass);',\r
4055 '                               }',\r
4056 '                               return false;',\r
4057 '                       }',\r
4058 '',\r
4059 '                       function removeClass(el, cssClass) {',\r
4060 '                               if (hasClass(el, cssClass)) {',\r
4061 '                                       // Rebuild the className property',\r
4062 '                                       var existingClasses = el.className.split(" ");',\r
4063 '                                       var newClasses = [];',\r
4064 '                                       for (var i = 0, len = existingClasses.length; i < len; i++) {',\r
4065 '                                               if (existingClasses[i] != cssClass) {',\r
4066 '                                                       newClasses[newClasses.length] = existingClasses[i];',\r
4067 '                                               }',\r
4068 '                                       }',\r
4069 '                                       el.className = newClasses.join(" ");',\r
4070 '                               }',\r
4071 '                       }',\r
4072 '',\r
4073 '                       function replaceClass(el, newCssClass, oldCssClass) {',\r
4074 '                               removeClass(el, oldCssClass);',\r
4075 '                               addClass(el, newCssClass);',\r
4076 '                       }',\r
4077 '',\r
4078 '                       /* ------------------------------------------------------------------------- */',\r
4079 '',\r
4080 '                       // Other utility functions',\r
4081 '',\r
4082 '                       function getElementsByClass(el, cssClass, tagName) {',\r
4083 '                               var elements = el.getElementsByTagName(tagName);',\r
4084 '                               var matches = [];',\r
4085 '                               for (var i = 0, len = elements.length; i < len; i++) {',\r
4086 '                                       if (hasClass(elements[i], cssClass)) {',\r
4087 '                                               matches.push(elements[i]);',\r
4088 '                                       }',\r
4089 '                               }',\r
4090 '                               return matches;',\r
4091 '                       }',\r
4092 '',\r
4093 '                       // Syntax borrowed from Prototype library',\r
4094 '                       function $(id) {',\r
4095 '                               return document.getElementById(id);',\r
4096 '                       }',\r
4097 '',\r
4098 '                       function isDescendant(node, ancestorNode) {',\r
4099 '                               while (node != null) {',\r
4100 '                                       if (node === ancestorNode) {',\r
4101 '                                               return true;',\r
4102 '                                       }',\r
4103 '                                       node = node.parentNode;',\r
4104 '                               }',\r
4105 '                               return false;',\r
4106 '                       }',\r
4107 '',\r
4108 '                       function isOrphan(node) {',\r
4109 '                               var currentNode = node;',\r
4110 '                               while (currentNode) {',\r
4111 '                                       if (currentNode == document.body) {',\r
4112 '                                               return false;',\r
4113 '                                       }',\r
4114 '                                       currentNode = currentNode.parentNode;',\r
4115 '                               }',\r
4116 '                               return true;',\r
4117 '                       }',\r
4118 '',\r
4119 '                       function escapeHtml(str) {',\r
4120 '                               return str.replace(/&/g, "&amp;").replace(/[<]/g, "&lt;").replace(/>/g, "&gt;");',\r
4121 '                       }',\r
4122 '',\r
4123 '                       function getWindowWidth() {',\r
4124 '                               if (window.innerWidth) {',\r
4125 '                                       return window.innerWidth;',\r
4126 '                               } else if (document.documentElement && document.documentElement.clientWidth) {',\r
4127 '                                       return document.documentElement.clientWidth;',\r
4128 '                               } else if (document.body) {',\r
4129 '                                       return document.body.clientWidth;',\r
4130 '                               }',\r
4131 '                               return 0;',\r
4132 '                       }',\r
4133 '',\r
4134 '                       function getWindowHeight() {',\r
4135 '                               if (window.innerHeight) {',\r
4136 '                                       return window.innerHeight;',\r
4137 '                               } else if (document.documentElement && document.documentElement.clientHeight) {',\r
4138 '                                       return document.documentElement.clientHeight;',\r
4139 '                               } else if (document.body) {',\r
4140 '                                       return document.body.clientHeight;',\r
4141 '                               }',\r
4142 '                               return 0;',\r
4143 '                       }',\r
4144 '',\r
4145 '                       function getToolBarsHeight() {',\r
4146 '                               return $("switches").offsetHeight;',\r
4147 '                       }',\r
4148 '',\r
4149 '                       function getChromeHeight() {',\r
4150 '                               var height = getToolBarsHeight();',\r
4151 '                               if (showCommandLine) {',\r
4152 '                                       height += $("commandLine").offsetHeight;',\r
4153 '                               }',\r
4154 '                               return height;',\r
4155 '                       }',\r
4156 '',\r
4157 '                       function setLogContainerHeight() {',\r
4158 '                               if (logMainContainer) {',\r
4159 '                                       var windowHeight = getWindowHeight();',\r
4160 '                                       $("body").style.height = getWindowHeight() + "px";',\r
4161 '                                       logMainContainer.style.height = "" +',\r
4162 '                                               Math.max(0, windowHeight - getChromeHeight()) + "px";',\r
4163 '                               }',\r
4164 '                       }',\r
4165 '',\r
4166 '                       function setCommandInputWidth() {',\r
4167 '                               if (showCommandLine) {',\r
4168 '                                       $("command").style.width = "" + Math.max(0, $("commandLineContainer").offsetWidth -',\r
4169 '                                               ($("evaluateButton").offsetWidth + 13)) + "px";',\r
4170 '                               }',\r
4171 '                       }',\r
4172 '',\r
4173 '                       window.onresize = function() {',\r
4174 '                               setCommandInputWidth();',\r
4175 '                               setLogContainerHeight();',\r
4176 '                       };',\r
4177 '',\r
4178 '                       if (!Array.prototype.push) {',\r
4179 '                               Array.prototype.push = function() {',\r
4180 '                               for (var i = 0, len = arguments.length; i < len; i++){',\r
4181 '                                   this[this.length] = arguments[i];',\r
4182 '                               }',\r
4183 '                               return this.length;',\r
4184 '                               };',\r
4185 '                       }',\r
4186 '',\r
4187 '                       if (!Array.prototype.pop) {',\r
4188 '                               Array.prototype.pop = function() {',\r
4189 '                                       if (this.length > 0) {',\r
4190 '                                               var val = this[this.length - 1];',\r
4191 '                                               this.length = this.length - 1;',\r
4192 '                                               return val;',\r
4193 '                                       }',\r
4194 '                               };',\r
4195 '                       }',\r
4196 '',\r
4197 '                       if (!Array.prototype.shift) {',\r
4198 '                               Array.prototype.shift = function() {',\r
4199 '                                       if (this.length > 0) {',\r
4200 '                                               var firstItem = this[0];',\r
4201 '                                               for (var i = 0, len = this.length - 1; i < len; i++) {',\r
4202 '                                                       this[i] = this[i + 1];',\r
4203 '                                               }',\r
4204 '                                               this.length = this.length - 1;',\r
4205 '                                               return firstItem;',\r
4206 '                                       }',\r
4207 '                               };',\r
4208 '                       }',\r
4209 '',\r
4210 '                       if (!Array.prototype.splice) {',\r
4211 '                               Array.prototype.splice = function(startIndex, deleteCount) {',\r
4212 '                                       var itemsAfterDeleted = this.slice(startIndex + deleteCount);',\r
4213 '                                       var itemsDeleted = this.slice(startIndex, startIndex + deleteCount);',\r
4214 '                                       this.length = startIndex;',\r
4215 '                                       // Copy the arguments into a proper Array object',\r
4216 '                                       var argumentsArray = [];',\r
4217 '                                       for (var i = 0, len = arguments.length; i < len; i++) {',\r
4218 '                                               argumentsArray[i] = arguments[i];',\r
4219 '                                       }',\r
4220 '                                       var itemsToAppend = (argumentsArray.length > 2) ?',\r
4221 '                                               itemsAfterDeleted = argumentsArray.slice(2).concat(itemsAfterDeleted) : itemsAfterDeleted;',\r
4222 '                                       for (i = 0, len = itemsToAppend.length; i < len; i++) {',\r
4223 '                                               this.push(itemsToAppend[i]);',\r
4224 '                                       }',\r
4225 '                                       return itemsDeleted;',\r
4226 '                               };',\r
4227 '                       }',\r
4228 '',\r
4229 '                       function array_remove(arr, val) {',\r
4230 '                               var index = -1;',\r
4231 '                               for (var i = 0, len = arr.length; i < len; i++) {',\r
4232 '                                       if (arr[i] === val) {',\r
4233 '                                               index = i;',\r
4234 '                                               break;',\r
4235 '                                       }',\r
4236 '                               }',\r
4237 '                               if (index >= 0) {',\r
4238 '                                       arr.splice(index, 1);',\r
4239 '                                       return index;',\r
4240 '                               } else {',\r
4241 '                                       return false;',\r
4242 '                               }',\r
4243 '                       }',\r
4244 '',\r
4245 '                       function array_removeFromStart(array, numberToRemove) {',\r
4246 '                               if (Array.prototype.splice) {',\r
4247 '                                       array.splice(0, numberToRemove);',\r
4248 '                               } else {',\r
4249 '                                       for (var i = numberToRemove, len = array.length; i < len; i++) {',\r
4250 '                                               array[i - numberToRemove] = array[i];',\r
4251 '                                       }',\r
4252 '                                       array.length = array.length - numberToRemove;',\r
4253 '                               }',\r
4254 '                               return array;',\r
4255 '                       }',\r
4256 '',\r
4257 '                       function array_contains(arr, val) {',\r
4258 '                               for (var i = 0, len = arr.length; i < len; i++) {',\r
4259 '                                       if (arr[i] == val) {',\r
4260 '                                               return true;',\r
4261 '                                       }',\r
4262 '                               }',\r
4263 '                               return false;',\r
4264 '                       }',\r
4265 '',\r
4266 '                       function getErrorMessage(ex) {',\r
4267 '                               if (ex.message) {',\r
4268 '                                       return ex.message;',\r
4269 '                               } else if (ex.description) {',\r
4270 '                                       return ex.description;',\r
4271 '                               }',\r
4272 '                               return "" + ex;',\r
4273 '                       }',\r
4274 '',\r
4275 '                       function moveCaretToEnd(input) {',\r
4276 '                               if (input.setSelectionRange) {',\r
4277 '                                       input.focus();',\r
4278 '                                       var length = input.value.length;',\r
4279 '                                       input.setSelectionRange(length, length);',\r
4280 '                               } else if (input.createTextRange) {',\r
4281 '                                       var range = input.createTextRange();',\r
4282 '                                       range.collapse(false);',\r
4283 '                                       range.select();',\r
4284 '                               }',\r
4285 '                               input.focus();',\r
4286 '                       }',\r
4287 '',\r
4288 '                       function stopPropagation(evt) {',\r
4289 '                               if (evt.stopPropagation) {',\r
4290 '                                       evt.stopPropagation();',\r
4291 '                               } else if (typeof evt.cancelBubble != "undefined") {',\r
4292 '                                       evt.cancelBubble = true;',\r
4293 '                               }',\r
4294 '                       }',\r
4295 '',\r
4296 '                       function getEvent(evt) {',\r
4297 '                               return evt ? evt : event;',\r
4298 '                       }',\r
4299 '',\r
4300 '                       function getTarget(evt) {',\r
4301 '                               return evt.target ? evt.target : evt.srcElement;',\r
4302 '                       }',\r
4303 '',\r
4304 '                       function getRelatedTarget(evt) {',\r
4305 '                               if (evt.relatedTarget) {',\r
4306 '                                       return evt.relatedTarget;',\r
4307 '                               } else if (evt.srcElement) {',\r
4308 '                                       switch(evt.type) {',\r
4309 '                                               case "mouseover":',\r
4310 '                                                       return evt.fromElement;',\r
4311 '                                               case "mouseout":',\r
4312 '                                                       return evt.toElement;',\r
4313 '                                               default:',\r
4314 '                                                       return evt.srcElement;',\r
4315 '                                       }',\r
4316 '                               }',\r
4317 '                       }',\r
4318 '',\r
4319 '                       function cancelKeyEvent(evt) {',\r
4320 '                               evt.returnValue = false;',\r
4321 '                               stopPropagation(evt);',\r
4322 '                       }',\r
4323 '',\r
4324 '                       function evalCommandLine() {',\r
4325 '                               var expr = $("command").value;',\r
4326 '                               evalCommand(expr);',\r
4327 '                               $("command").value = "";',\r
4328 '                       }',\r
4329 '',\r
4330 '                       function evalLastCommand() {',\r
4331 '                               if (lastCommand != null) {',\r
4332 '                                       evalCommand(lastCommand);',\r
4333 '                               }',\r
4334 '                       }',\r
4335 '',\r
4336 '                       var lastCommand = null;',\r
4337 '                       var commandHistory = [];',\r
4338 '                       var currentCommandIndex = 0;',\r
4339 '',\r
4340 '                       function evalCommand(expr) {',\r
4341 '                               if (appender) {',\r
4342 '                                       appender.evalCommandAndAppend(expr);',\r
4343 '                               } else {',\r
4344 '                                       var prefix = ">>> " + expr + "\\r\\n";',\r
4345 '                                       try {',\r
4346 '                                               log("INFO", prefix + eval(expr));',\r
4347 '                                       } catch (ex) {',\r
4348 '                                               log("ERROR", prefix + "Error: " + getErrorMessage(ex));',\r
4349 '                                       }',\r
4350 '                               }',\r
4351 '                               // Update command history',\r
4352 '                               if (expr != commandHistory[commandHistory.length - 1]) {',\r
4353 '                                       commandHistory.push(expr);',\r
4354 '                                       // Update the appender',\r
4355 '                                       if (appender) {',\r
4356 '                                               appender.storeCommandHistory(commandHistory);',\r
4357 '                                       }',\r
4358 '                               }',\r
4359 '                               currentCommandIndex = (expr == commandHistory[currentCommandIndex]) ? currentCommandIndex + 1 : commandHistory.length;',\r
4360 '                               lastCommand = expr;',\r
4361 '                       }',\r
4362 '                       //]]>',\r
4363 '               </script>',\r
4364 '               <style type="text/css">',\r
4365 '                       body {',\r
4366 '                               background-color: white;',\r
4367 '                               color: black;',\r
4368 '                               padding: 0;',\r
4369 '                               margin: 0;',\r
4370 '                               font-family: tahoma, verdana, arial, helvetica, sans-serif;',\r
4371 '                               overflow: hidden;',\r
4372 '                       }',\r
4373 '',\r
4374 '                       div#switchesContainer input {',\r
4375 '                               margin-bottom: 0;',\r
4376 '                       }',\r
4377 '',\r
4378 '                       div.toolbar {',\r
4379 '                               border-top: solid #ffffff 1px;',\r
4380 '                               border-bottom: solid #aca899 1px;',\r
4381 '                               background-color: #f1efe7;',\r
4382 '                               padding: 3px 5px;',\r
4383 '                               font-size: 68.75%;',\r
4384 '                       }',\r
4385 '',\r
4386 '                       div.toolbar, div#search input {',\r
4387 '                               font-family: tahoma, verdana, arial, helvetica, sans-serif;',\r
4388 '                       }',\r
4389 '',\r
4390 '                       div.toolbar input.button {',\r
4391 '                               padding: 0 5px;',\r
4392 '                               font-size: 100%;',\r
4393 '                       }',\r
4394 '',\r
4395 '                       div.toolbar input.hidden {',\r
4396 '                               display: none;',\r
4397 '                       }',\r
4398 '',\r
4399 '                       div#switches input#clearButton {',\r
4400 '                               margin-left: 20px;',\r
4401 '                       }',\r
4402 '',\r
4403 '                       div#levels label {',\r
4404 '                               font-weight: bold;',\r
4405 '                       }',\r
4406 '',\r
4407 '                       div#levels label, div#options label {',\r
4408 '                               margin-right: 5px;',\r
4409 '                       }',\r
4410 '',\r
4411 '                       div#levels label#wrapLabel {',\r
4412 '                               font-weight: normal;',\r
4413 '                       }',\r
4414 '',\r
4415 '                       div#search label {',\r
4416 '                               margin-right: 10px;',\r
4417 '                       }',\r
4418 '',\r
4419 '                       div#search label.searchboxlabel {',\r
4420 '                               margin-right: 0;',\r
4421 '                       }',\r
4422 '',\r
4423 '                       div#search input {',\r
4424 '                               font-size: 100%;',\r
4425 '                       }',\r
4426 '',\r
4427 '                       div#search input.validregex {',\r
4428 '                               color: green;',\r
4429 '                       }',\r
4430 '',\r
4431 '                       div#search input.invalidregex {',\r
4432 '                               color: red;',\r
4433 '                       }',\r
4434 '',\r
4435 '                       div#search input.nomatches {',\r
4436 '                               color: white;',\r
4437 '                               background-color: #ff6666;',\r
4438 '                       }',\r
4439 '',\r
4440 '                       div#search input.nomatches {',\r
4441 '                               color: white;',\r
4442 '                               background-color: #ff6666;',\r
4443 '                       }',\r
4444 '',\r
4445 '                       div#searchNav {',\r
4446 '                               display: none;',\r
4447 '                       }',\r
4448 '',\r
4449 '                       div#commandLine {',\r
4450 '                               display: none;',\r
4451 '                       }',\r
4452 '',\r
4453 '                       div#commandLine input#command {',\r
4454 '                               font-size: 100%;',\r
4455 '                               font-family: Courier New, Courier;',\r
4456 '                       }',\r
4457 '',\r
4458 '                       div#commandLine input#evaluateButton {',\r
4459 '                       }',\r
4460 '',\r
4461 '                       *.greyedout {',\r
4462 '                               color: gray !important;',\r
4463 '                               border-color: gray !important;',\r
4464 '                       }',\r
4465 '',\r
4466 '                       *.greyedout *.alwaysenabled { color: black; }',\r
4467 '',\r
4468 '                       *.unselectable {',\r
4469 '                               -khtml-user-select: none;',\r
4470 '                               -moz-user-select: none;',\r
4471 '                               user-select: none;',\r
4472 '                       }',\r
4473 '',\r
4474 '                       div#log {',\r
4475 '                               font-family: Courier New, Courier;',\r
4476 '                               font-size: 75%;',\r
4477 '                               width: 100%;',\r
4478 '                               overflow: auto;',\r
4479 '                               clear: both;',\r
4480 '                               position: relative;',\r
4481 '                       }',\r
4482 '',\r
4483 '                       div.group {',\r
4484 '                               border-color: #cccccc;',\r
4485 '                               border-style: solid;',\r
4486 '                               border-width: 1px 0 1px 1px;',\r
4487 '                               overflow: visible;',\r
4488 '                       }',\r
4489 '',\r
4490 '                       div.oldIe div.group, div.oldIe div.group *, div.oldIe *.logentry {',\r
4491 '                               height: 1%;',\r
4492 '                       }',\r
4493 '',\r
4494 '                       div.group div.groupheading span.expander {',\r
4495 '                               border: solid black 1px;',\r
4496 '                               font-family: Courier New, Courier;',\r
4497 '                               font-size: 0.833em;',\r
4498 '                               background-color: #eeeeee;',\r
4499 '                               position: relative;',\r
4500 '                               top: -1px;',\r
4501 '                               color: black;',\r
4502 '                               padding: 0 2px;',\r
4503 '                               cursor: pointer;',\r
4504 '                               cursor: hand;',\r
4505 '                               height: 1%;',\r
4506 '                       }',\r
4507 '',\r
4508 '                       div.group div.groupcontent {',\r
4509 '                               margin-left: 10px;',\r
4510 '                               padding-bottom: 2px;',\r
4511 '                               overflow: visible;',\r
4512 '                       }',\r
4513 '',\r
4514 '                       div.group div.expanded {',\r
4515 '                               display: block;',\r
4516 '                       }',\r
4517 '',\r
4518 '                       div.group div.collapsed {',\r
4519 '                               display: none;',\r
4520 '                       }',\r
4521 '',\r
4522 '                       *.logentry {',\r
4523 '                               overflow: visible;',\r
4524 '                               display: none;',\r
4525 '                               white-space: pre;',\r
4526 '                       }',\r
4527 '',\r
4528 '                       span.pre {',\r
4529 '                               white-space: pre;',\r
4530 '                       }',\r
4531 '                       ',\r
4532 '                       pre.unwrapped {',\r
4533 '                               display: inline !important;',\r
4534 '                       }',\r
4535 '',\r
4536 '                       pre.unwrapped pre.pre, div.wrapped pre.pre {',\r
4537 '                               display: inline;',\r
4538 '                       }',\r
4539 '',\r
4540 '                       div.wrapped pre.pre {',\r
4541 '                               white-space: normal;',\r
4542 '                       }',\r
4543 '',\r
4544 '                       div.wrapped {',\r
4545 '                               display: none;',\r
4546 '                       }',\r
4547 '',\r
4548 '                       body.searching *.logentry span.currentmatch {',\r
4549 '                               color: white !important;',\r
4550 '                               background-color: green !important;',\r
4551 '                       }',\r
4552 '',\r
4553 '                       body.searching div.searchhighlight *.logentry span.searchterm {',\r
4554 '                               color: black;',\r
4555 '                               background-color: yellow;',\r
4556 '                       }',\r
4557 '',\r
4558 '                       div.wrap *.logentry {',\r
4559 '                               white-space: normal !important;',\r
4560 '                               border-width: 0 0 1px 0;',\r
4561 '                               border-color: #dddddd;',\r
4562 '                               border-style: dotted;',\r
4563 '                       }',\r
4564 '',\r
4565 '                       div.wrap #log_wrapped, #log_unwrapped {',\r
4566 '                               display: block;',\r
4567 '                       }',\r
4568 '',\r
4569 '                       div.wrap #log_unwrapped, #log_wrapped {',\r
4570 '                               display: none;',\r
4571 '                       }',\r
4572 '',\r
4573 '                       div.wrap *.logentry span.pre {',\r
4574 '                               overflow: visible;',\r
4575 '                               white-space: normal;',\r
4576 '                       }',\r
4577 '',\r
4578 '                       div.wrap *.logentry pre.unwrapped {',\r
4579 '                               display: none;',\r
4580 '                       }',\r
4581 '',\r
4582 '                       div.wrap *.logentry span.wrapped {',\r
4583 '                               display: inline;',\r
4584 '                       }',\r
4585 '',\r
4586 '                       div.searchfilter *.searchnonmatch {',\r
4587 '                               display: none !important;',\r
4588 '                       }',\r
4589 '',\r
4590 '                       div#log *.TRACE, label#label_TRACE {',\r
4591 '                               color: #666666;',\r
4592 '                       }',\r
4593 '',\r
4594 '                       div#log *.DEBUG, label#label_DEBUG {',\r
4595 '                               color: green;',\r
4596 '                       }',\r
4597 '',\r
4598 '                       div#log *.INFO, label#label_INFO {',\r
4599 '                               color: #000099;',\r
4600 '                       }',\r
4601 '',\r
4602 '                       div#log *.WARN, label#label_WARN {',\r
4603 '                               color: #999900;',\r
4604 '                       }',\r
4605 '',\r
4606 '                       div#log *.ERROR, label#label_ERROR {',\r
4607 '                               color: red;',\r
4608 '                       }',\r
4609 '',\r
4610 '                       div#log *.FATAL, label#label_FATAL {',\r
4611 '                               color: #660066;',\r
4612 '                       }',\r
4613 '',\r
4614 '                       div.TRACE#log *.TRACE,',\r
4615 '                       div.DEBUG#log *.DEBUG,',\r
4616 '                       div.INFO#log *.INFO,',\r
4617 '                       div.WARN#log *.WARN,',\r
4618 '                       div.ERROR#log *.ERROR,',\r
4619 '                       div.FATAL#log *.FATAL {',\r
4620 '                               display: block;',\r
4621 '                       }',\r
4622 '',\r
4623 '                       div#log div.separator {',\r
4624 '                               background-color: #cccccc;',\r
4625 '                               margin: 5px 0;',\r
4626 '                               line-height: 1px;',\r
4627 '                       }',\r
4628 '               </style>',\r
4629 '       </head>',\r
4630 '',\r
4631 '       <body id="body">',\r
4632 '               <div id="switchesContainer">',\r
4633 '                       <div id="switches">',\r
4634 '                               <div id="levels" class="toolbar">',\r
4635 '                                       Filters:',\r
4636 '                                       <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>',\r
4637 '                                       <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>',\r
4638 '                                       <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>',\r
4639 '                                       <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>',\r
4640 '                                       <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>',\r
4641 '                                       <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>',\r
4642 '                                       <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>',\r
4643 '                               </div>',\r
4644 '                               <div id="search" class="toolbar">',\r
4645 '                                       <label for="searchBox" class="searchboxlabel">Search:</label> <input type="text" id="searchBox" onclick="toggleSearchEnabled(true)" onkeyup="scheduleSearch()" size="20" />',\r
4646 '                                       <input type="button" id="searchReset" disabled="disabled" value="Reset" onclick="clearSearch()" class="button" title="Reset the search" />',\r
4647 '                                       <input type="checkbox" id="searchRegex" onclick="doSearch()" title="If checked, search is treated as a regular expression" /><label for="searchRegex">Regex</label>',\r
4648 '                                       <input type="checkbox" id="searchCaseSensitive" onclick="doSearch()" title="If checked, search is case sensitive" /><label for="searchCaseSensitive">Match case</label>',\r
4649 '                                       <input type="checkbox" id="searchDisable" onclick="toggleSearchEnabled()" title="Enable/disable search" /><label for="searchDisable" class="alwaysenabled">Disable</label>',\r
4650 '                                       <div id="searchNav">',\r
4651 '                                               <input type="button" id="searchNext" disabled="disabled" value="Next" onclick="searchNext()" class="button" title="Go to the next matching log entry" />',\r
4652 '                                               <input type="button" id="searchPrevious" disabled="disabled" value="Previous" onclick="searchPrevious()" class="button" title="Go to the previous matching log entry" />',\r
4653 '                                               <input type="checkbox" id="searchFilter" onclick="toggleSearchFilter()" title="If checked, non-matching log entries are filtered out" /><label for="searchFilter">Filter</label>',\r
4654 '                                               <input type="checkbox" id="searchHighlight" onclick="toggleSearchHighlight()" title="Highlight matched search terms" /><label for="searchHighlight" class="alwaysenabled">Highlight all</label>',\r
4655 '                                       </div>',\r
4656 '                               </div>',\r
4657 '                               <div id="options" class="toolbar">',\r
4658 '                                       Options:',\r
4659 '                                       <input type="checkbox" id="enableLogging" onclick="toggleLoggingEnabled()" checked="checked" title="Enable/disable logging" /><label for="enableLogging" id="enableLoggingLabel">Log</label>',\r
4660 '                                       <input type="checkbox" id="wrap" onclick="toggleWrap()" title="Enable / disable word wrap" /><label for="wrap" id="wrapLabel">Wrap</label>',\r
4661 '                                       <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>',\r
4662 '                                       <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>',\r
4663 '                                       <input type="button" id="clearButton" value="Clear" onclick="clearLog()" class="button" title="Clear all log messages"  />',\r
4664 '                                       <input type="button" id="hideButton" value="Hide" onclick="hide()" class="hidden button" title="Hide the console" />',\r
4665 '                                       <input type="button" id="closeButton" value="Close" onclick="closeWindow()" class="hidden button" title="Close the window" />',\r
4666 '                               </div>',\r
4667 '                       </div>',\r
4668 '               </div>',\r
4669 '               <div id="log" class="TRACE DEBUG INFO WARN ERROR FATAL"></div>',\r
4670 '               <div id="commandLine" class="toolbar">',\r
4671 '                       <div id="commandLineContainer">',\r
4672 '                               <input type="text" id="command" title="Enter a JavaScript command here and hit return or press \'Evaluate\'" />',\r
4673 '                               <input type="button" id="evaluateButton" value="Evaluate" class="button" title="Evaluate the command" onclick="evalCommandLine()" />',\r
4674 '                       </div>',\r
4675 '               </div>',\r
4676 '       </body>',\r
4677 '</html>',\r
4678 ''\r
4679 ];\r
4680                 };\r
4681 \r
4682                 var defaultCommandLineFunctions = [];\r
4683 \r
4684                 ConsoleAppender = function() {};\r
4685 \r
4686                 var consoleAppenderIdCounter = 1;\r
4687                 ConsoleAppender.prototype = new Appender();\r
4688 \r
4689                 ConsoleAppender.prototype.create = function(inPage, container,\r
4690                                 lazyInit, initiallyMinimized, useDocumentWrite, width, height, focusConsoleWindow) {\r
4691                         var appender = this;\r
4692 \r
4693                         // Common properties\r
4694                         var initialized = false;\r
4695                         var consoleWindowCreated = false;\r
4696                         var consoleWindowLoaded = false;\r
4697                         var consoleClosed = false;\r
4698 \r
4699                         var queuedLoggingEvents = [];\r
4700                         var isSupported = true;\r
4701                         var consoleAppenderId = consoleAppenderIdCounter++;\r
4702 \r
4703                         // Local variables\r
4704                         initiallyMinimized = extractBooleanFromParam(initiallyMinimized, this.defaults.initiallyMinimized);\r
4705                         lazyInit = extractBooleanFromParam(lazyInit, this.defaults.lazyInit);\r
4706                         useDocumentWrite = extractBooleanFromParam(useDocumentWrite, this.defaults.useDocumentWrite);\r
4707                         var newestMessageAtTop = this.defaults.newestMessageAtTop;\r
4708                         var scrollToLatestMessage = this.defaults.scrollToLatestMessage;\r
4709                         width = width ? width : this.defaults.width;\r
4710                         height = height ? height : this.defaults.height;\r
4711                         var maxMessages = this.defaults.maxMessages;\r
4712                         var showCommandLine = this.defaults.showCommandLine;\r
4713                         var commandLineObjectExpansionDepth = this.defaults.commandLineObjectExpansionDepth;\r
4714                         var showHideButton = this.defaults.showHideButton;\r
4715             var showCloseButton = this.defaults.showCloseButton;\r
4716             var showLogEntryDeleteButtons = this.defaults.showLogEntryDeleteButtons;\r
4717 \r
4718                         this.setLayout(this.defaults.layout);\r
4719 \r
4720                         // Functions whose implementations vary between subclasses\r
4721                         var init, createWindow, safeToAppend, getConsoleWindow, open;\r
4722 \r
4723                         // Configuration methods. The function scope is used to prevent\r
4724                         // direct alteration to the appender configuration properties.\r
4725                         var appenderName = inPage ? "InPageAppender" : "PopUpAppender";\r
4726                         var checkCanConfigure = function(configOptionName) {\r
4727                                 if (consoleWindowCreated) {\r
4728                                         handleError(appenderName + ": configuration option '" + configOptionName + "' may not be set after the appender has been initialized");\r
4729                                         return false;\r
4730                                 }\r
4731                                 return true;\r
4732                         };\r
4733 \r
4734                         var consoleWindowExists = function() {\r
4735                                 return (consoleWindowLoaded && isSupported && !consoleClosed);\r
4736                         };\r
4737 \r
4738                         this.isNewestMessageAtTop = function() { return newestMessageAtTop; };\r
4739                         this.setNewestMessageAtTop = function(newestMessageAtTopParam) {\r
4740                                 newestMessageAtTop = bool(newestMessageAtTopParam);\r
4741                                 if (consoleWindowExists()) {\r
4742                                         getConsoleWindow().setNewestAtTop(newestMessageAtTop);\r
4743                                 }\r
4744                         };\r
4745 \r
4746                         this.isScrollToLatestMessage = function() { return scrollToLatestMessage; };\r
4747                         this.setScrollToLatestMessage = function(scrollToLatestMessageParam) {\r
4748                                 scrollToLatestMessage = bool(scrollToLatestMessageParam);\r
4749                                 if (consoleWindowExists()) {\r
4750                                         getConsoleWindow().setScrollToLatest(scrollToLatestMessage);\r
4751                                 }\r
4752                         };\r
4753 \r
4754                         this.getWidth = function() { return width; };\r
4755                         this.setWidth = function(widthParam) {\r
4756                                 if (checkCanConfigure("width")) {\r
4757                                         width = extractStringFromParam(widthParam, width);\r
4758                                 }\r
4759                         };\r
4760 \r
4761                         this.getHeight = function() { return height; };\r
4762                         this.setHeight = function(heightParam) {\r
4763                                 if (checkCanConfigure("height")) {\r
4764                                         height = extractStringFromParam(heightParam, height);\r
4765                                 }\r
4766                         };\r
4767 \r
4768                         this.getMaxMessages = function() { return maxMessages; };\r
4769                         this.setMaxMessages = function(maxMessagesParam) {\r
4770                                 maxMessages = extractIntFromParam(maxMessagesParam, maxMessages);\r
4771                                 if (consoleWindowExists()) {\r
4772                                         getConsoleWindow().setMaxMessages(maxMessages);\r
4773                                 }\r
4774                         };\r
4775 \r
4776                         this.isShowCommandLine = function() { return showCommandLine; };\r
4777                         this.setShowCommandLine = function(showCommandLineParam) {\r
4778                                 showCommandLine = bool(showCommandLineParam);\r
4779                                 if (consoleWindowExists()) {\r
4780                                         getConsoleWindow().setShowCommandLine(showCommandLine);\r
4781                                 }\r
4782                         };\r
4783 \r
4784                         this.isShowHideButton = function() { return showHideButton; };\r
4785                         this.setShowHideButton = function(showHideButtonParam) {\r
4786                                 showHideButton = bool(showHideButtonParam);\r
4787                                 if (consoleWindowExists()) {\r
4788                                         getConsoleWindow().setShowHideButton(showHideButton);\r
4789                                 }\r
4790                         };\r
4791 \r
4792                         this.isShowCloseButton = function() { return showCloseButton; };\r
4793                         this.setShowCloseButton = function(showCloseButtonParam) {\r
4794                                 showCloseButton = bool(showCloseButtonParam);\r
4795                                 if (consoleWindowExists()) {\r
4796                                         getConsoleWindow().setShowCloseButton(showCloseButton);\r
4797                                 }\r
4798                         };\r
4799 \r
4800                         this.getCommandLineObjectExpansionDepth = function() { return commandLineObjectExpansionDepth; };\r
4801                         this.setCommandLineObjectExpansionDepth = function(commandLineObjectExpansionDepthParam) {\r
4802                                 commandLineObjectExpansionDepth = extractIntFromParam(commandLineObjectExpansionDepthParam, commandLineObjectExpansionDepth);\r
4803                         };\r
4804 \r
4805                         var minimized = initiallyMinimized;\r
4806                         this.isInitiallyMinimized = function() { return initiallyMinimized; };\r
4807                         this.setInitiallyMinimized = function(initiallyMinimizedParam) {\r
4808                                 if (checkCanConfigure("initiallyMinimized")) {\r
4809                                         initiallyMinimized = bool(initiallyMinimizedParam);\r
4810                                         minimized = initiallyMinimized;\r
4811                                 }\r
4812                         };\r
4813 \r
4814                         this.isUseDocumentWrite = function() { return useDocumentWrite; };\r
4815                         this.setUseDocumentWrite = function(useDocumentWriteParam) {\r
4816                                 if (checkCanConfigure("useDocumentWrite")) {\r
4817                                         useDocumentWrite = bool(useDocumentWriteParam);\r
4818                                 }\r
4819                         };\r
4820 \r
4821                         // Common methods\r
4822                         function QueuedLoggingEvent(loggingEvent, formattedMessage) {\r
4823                                 this.loggingEvent = loggingEvent;\r
4824                                 this.levelName = loggingEvent.level.name;\r
4825                                 this.formattedMessage = formattedMessage;\r
4826                         }\r
4827 \r
4828                         QueuedLoggingEvent.prototype.append = function() {\r
4829                                 getConsoleWindow().log(this.levelName, this.formattedMessage);\r
4830                         };\r
4831 \r
4832                         function QueuedGroup(name, initiallyExpanded) {\r
4833                                 this.name = name;\r
4834                                 this.initiallyExpanded = initiallyExpanded;\r
4835                         }\r
4836 \r
4837                         QueuedGroup.prototype.append = function() {\r
4838                                 getConsoleWindow().group(this.name, this.initiallyExpanded);\r
4839                         };\r
4840 \r
4841                         function QueuedGroupEnd() {}\r
4842 \r
4843                         QueuedGroupEnd.prototype.append = function() {\r
4844                                 getConsoleWindow().groupEnd();\r
4845                         };\r
4846 \r
4847                         var checkAndAppend = function() {\r
4848                                 // Next line forces a check of whether the window has been closed\r
4849                                 safeToAppend();\r
4850                                 if (!initialized) {\r
4851                                         init();\r
4852                                 } else if (consoleClosed && reopenWhenClosed) {\r
4853                                         createWindow();\r
4854                                 }\r
4855                                 if (safeToAppend()) {\r
4856                                         appendQueuedLoggingEvents();\r
4857                                 }\r
4858                         };\r
4859 \r
4860                         this.append = function(loggingEvent) {\r
4861                                 if (isSupported) {\r
4862                                         // Format the message\r
4863                                         var formattedMessage = appender.getLayout().format(loggingEvent);\r
4864                                         if (this.getLayout().ignoresThrowable()) {\r
4865                                                 formattedMessage += loggingEvent.getThrowableStrRep();\r
4866                                         }\r
4867                                         queuedLoggingEvents.push(new QueuedLoggingEvent(loggingEvent, formattedMessage));\r
4868                                         checkAndAppend();\r
4869                                 }\r
4870                         };\r
4871 \r
4872             this.group = function(name, initiallyExpanded) {\r
4873                                 if (isSupported) {\r
4874                                         queuedLoggingEvents.push(new QueuedGroup(name, initiallyExpanded));\r
4875                                         checkAndAppend();\r
4876                                 }\r
4877                         };\r
4878 \r
4879             this.groupEnd = function() {\r
4880                                 if (isSupported) {\r
4881                                         queuedLoggingEvents.push(new QueuedGroupEnd());\r
4882                                         checkAndAppend();\r
4883                                 }\r
4884                         };\r
4885 \r
4886                         var appendQueuedLoggingEvents = function() {\r
4887                                 var currentLoggingEvent;\r
4888                                 while (queuedLoggingEvents.length > 0) {\r
4889                                         queuedLoggingEvents.shift().append();\r
4890                                 }\r
4891                                 if (focusConsoleWindow) {\r
4892                                         getConsoleWindow().focus();\r
4893                                 }\r
4894                         };\r
4895 \r
4896                         this.setAddedToLogger = function(logger) {\r
4897                                 this.loggers.push(logger);\r
4898                                 if (enabled && !lazyInit) {\r
4899                                         init();\r
4900                                 }\r
4901                         };\r
4902 \r
4903                         this.clear = function() {\r
4904                                 if (consoleWindowExists()) {\r
4905                                         getConsoleWindow().clearLog();\r
4906                                 }\r
4907                                 queuedLoggingEvents.length = 0;\r
4908                         };\r
4909 \r
4910                         this.focus = function() {\r
4911                                 if (consoleWindowExists()) {\r
4912                                         getConsoleWindow().focus();\r
4913                                 }\r
4914                         };\r
4915 \r
4916                         this.focusCommandLine = function() {\r
4917                                 if (consoleWindowExists()) {\r
4918                                         getConsoleWindow().focusCommandLine();\r
4919                                 }\r
4920                         };\r
4921 \r
4922                         this.focusSearch = function() {\r
4923                                 if (consoleWindowExists()) {\r
4924                                         getConsoleWindow().focusSearch();\r
4925                                 }\r
4926                         };\r
4927 \r
4928                         var commandWindow = window;\r
4929 \r
4930                         this.getCommandWindow = function() { return commandWindow; };\r
4931                         this.setCommandWindow = function(commandWindowParam) {\r
4932                                 commandWindow = commandWindowParam;\r
4933                         };\r
4934 \r
4935                         this.executeLastCommand = function() {\r
4936                                 if (consoleWindowExists()) {\r
4937                                         getConsoleWindow().evalLastCommand();\r
4938                                 }\r
4939                         };\r
4940 \r
4941                         var commandLayout = new PatternLayout("%m");\r
4942                         this.getCommandLayout = function() { return commandLayout; };\r
4943                         this.setCommandLayout = function(commandLayoutParam) {\r
4944                                 commandLayout = commandLayoutParam;\r
4945                         };\r
4946 \r
4947                         this.evalCommandAndAppend = function(expr) {\r
4948                                 var commandReturnValue = { appendResult: true, isError: false };\r
4949                                 var commandOutput = "";\r
4950                                 // Evaluate the command\r
4951                                 try {\r
4952                                         var result, i;\r
4953                                         // The next three lines constitute a workaround for IE. Bizarrely, iframes seem to have no\r
4954                                         // eval method on the window object initially, but once execScript has been called on\r
4955                                         // it once then the eval method magically appears. See http://www.thismuchiknow.co.uk/?p=25\r
4956                                         if (!commandWindow.eval && commandWindow.execScript) {\r
4957                                                 commandWindow.execScript("null");\r
4958                                         }\r
4959 \r
4960                                         var commandLineFunctionsHash = {};\r
4961                                         for (i = 0, len = commandLineFunctions.length; i < len; i++) {\r
4962                                                 commandLineFunctionsHash[commandLineFunctions[i][0]] = commandLineFunctions[i][1];\r
4963                                         }\r
4964 \r
4965                                         // Keep an array of variables that are being changed in the command window so that they\r
4966                                         // can be restored to their original values afterwards\r
4967                                         var objectsToRestore = [];\r
4968                                         var addObjectToRestore = function(name) {\r
4969                                                 objectsToRestore.push([name, commandWindow[name]]);\r
4970                                         };\r
4971 \r
4972                                         addObjectToRestore("appender");\r
4973                                         commandWindow.appender = appender;\r
4974 \r
4975                                         addObjectToRestore("commandReturnValue");\r
4976                                         commandWindow.commandReturnValue = commandReturnValue;\r
4977 \r
4978                                         addObjectToRestore("commandLineFunctionsHash");\r
4979                                         commandWindow.commandLineFunctionsHash = commandLineFunctionsHash;\r
4980 \r
4981                                         var addFunctionToWindow = function(name) {\r
4982                                                 addObjectToRestore(name);\r
4983                                                 commandWindow[name] = function() {\r
4984                                                         return this.commandLineFunctionsHash[name](appender, arguments, commandReturnValue);\r
4985                                                 };\r
4986                                         };\r
4987 \r
4988                                         for (i = 0, len = commandLineFunctions.length; i < len; i++) {\r
4989                                                 addFunctionToWindow(commandLineFunctions[i][0]);\r
4990                                         }\r
4991 \r
4992                                         // Another bizarre workaround to get IE to eval in the global scope\r
4993                                         if (commandWindow === window && commandWindow.execScript) {\r
4994                                                 addObjectToRestore("evalExpr");\r
4995                                                 addObjectToRestore("result");\r
4996                                                 window.evalExpr = expr;\r
4997                                                 commandWindow.execScript("window.result=eval(window.evalExpr);");\r
4998                                                 result = window.result;\r
4999                                         } else {\r
5000                                                 result = commandWindow.eval(expr);\r
5001                                         }\r
5002                                         commandOutput = isUndefined(result) ? result : formatObjectExpansion(result, commandLineObjectExpansionDepth);\r
5003 \r
5004                                         // Restore variables in the command window to their original state\r
5005                                         for (i = 0, len = objectsToRestore.length; i < len; i++) {\r
5006                                                 commandWindow[objectsToRestore[i][0]] = objectsToRestore[i][1];\r
5007                                         }\r
5008                                 } catch (ex) {\r
5009                                         commandOutput = "Error evaluating command: " + getExceptionStringRep(ex);\r
5010                                         commandReturnValue.isError = true;\r
5011                                 }\r
5012                                 // Append command output\r
5013                                 if (commandReturnValue.appendResult) {\r
5014                                         var message = ">>> " + expr;\r
5015                                         if (!isUndefined(commandOutput)) {\r
5016                                                 message += newLine + commandOutput;\r
5017                                         }\r
5018                                         var level = commandReturnValue.isError ? Level.ERROR : Level.INFO;\r
5019                                         var loggingEvent = new LoggingEvent(null, new Date(), level, [message], null);\r
5020                                         var mainLayout = this.getLayout();\r
5021                                         this.setLayout(commandLayout);\r
5022                                         this.append(loggingEvent);\r
5023                                         this.setLayout(mainLayout);\r
5024                                 }\r
5025                         };\r
5026 \r
5027                         var commandLineFunctions = defaultCommandLineFunctions.concat([]);\r
5028 \r
5029                         this.addCommandLineFunction = function(functionName, commandLineFunction) {\r
5030                                 commandLineFunctions.push([functionName, commandLineFunction]);\r
5031                         };\r
5032 \r
5033                         var commandHistoryCookieName = "log4javascriptCommandHistory";\r
5034                         this.storeCommandHistory = function(commandHistory) {\r
5035                                 setCookie(commandHistoryCookieName, commandHistory.join(","));\r
5036                         };\r
5037 \r
5038                         var writeHtml = function(doc) {\r
5039                                 var lines = getConsoleHtmlLines();\r
5040                                 doc.open();\r
5041                                 for (var i = 0, len = lines.length; i < len; i++) {\r
5042                                         doc.writeln(lines[i]);\r
5043                                 }\r
5044                                 doc.close();\r
5045                         };\r
5046 \r
5047                         // Set up event listeners\r
5048                         this.setEventTypes(["load", "unload"]);\r
5049 \r
5050                         var consoleWindowLoadHandler = function() {\r
5051                                 var win = getConsoleWindow();\r
5052                                 win.setAppender(appender);\r
5053                                 win.setNewestAtTop(newestMessageAtTop);\r
5054                                 win.setScrollToLatest(scrollToLatestMessage);\r
5055                                 win.setMaxMessages(maxMessages);\r
5056                                 win.setShowCommandLine(showCommandLine);\r
5057                                 win.setShowHideButton(showHideButton);\r
5058                                 win.setShowCloseButton(showCloseButton);\r
5059                                 win.setMainWindow(window);\r
5060 \r
5061                                 // Restore command history stored in cookie\r
5062                                 var storedValue = getCookie(commandHistoryCookieName);\r
5063                                 if (storedValue) {\r
5064                                         win.commandHistory = storedValue.split(",");\r
5065                                         win.currentCommandIndex = win.commandHistory.length;\r
5066                                 }\r
5067 \r
5068                                 appender.dispatchEvent("load", { "win" : win });\r
5069                         };\r
5070 \r
5071                         this.unload = function() {\r
5072                                 logLog.debug("unload " + this + ", caller: " + this.unload.caller);\r
5073                                 if (!consoleClosed) {\r
5074                                         logLog.debug("really doing unload " + this);\r
5075                                         consoleClosed = true;\r
5076                                         consoleWindowLoaded = false;\r
5077                                         consoleWindowCreated = false;\r
5078                                         appender.dispatchEvent("unload", {});\r
5079                                 }\r
5080                         };\r
5081 \r
5082                         var pollConsoleWindow = function(windowTest, interval, successCallback, errorMessage) {\r
5083                                 function doPoll() {\r
5084                                         try {\r
5085                                                 // Test if the console has been closed while polling\r
5086                                                 if (consoleClosed) {\r
5087                                                         clearInterval(poll);\r
5088                                                 }\r
5089                                                 if (windowTest(getConsoleWindow())) {\r
5090                                                         clearInterval(poll);\r
5091                                                         successCallback();\r
5092                                                 }\r
5093                                         } catch (ex) {\r
5094                                                 clearInterval(poll);\r
5095                                                 isSupported = false;\r
5096                                                 handleError(errorMessage, ex);\r
5097                                         }\r
5098                                 }\r
5099 \r
5100                                 // Poll the pop-up since the onload event is not reliable\r
5101                                 var poll = setInterval(doPoll, interval);\r
5102                         };\r
5103 \r
5104                         var getConsoleUrl = function() {\r
5105                                 var documentDomainSet = (document.domain != location.hostname);\r
5106                                 return useDocumentWrite ? "" : getBaseUrl() + "console_uncompressed.html" +\r
5107                                                                                            (documentDomainSet ? "?log4javascript_domain=" + escape(document.domain) : "");\r
5108                         };\r
5109 \r
5110                         // Define methods and properties that vary between subclasses\r
5111                         if (inPage) {\r
5112                                 // InPageAppender\r
5113 \r
5114                                 var containerElement = null;\r
5115 \r
5116                                 // Configuration methods. The function scope is used to prevent\r
5117                                 // direct alteration to the appender configuration properties.\r
5118                                 var cssProperties = [];\r
5119                                 this.addCssProperty = function(name, value) {\r
5120                                         if (checkCanConfigure("cssProperties")) {\r
5121                                                 cssProperties.push([name, value]);\r
5122                                         }\r
5123                                 };\r
5124 \r
5125                                 // Define useful variables\r
5126                                 var windowCreationStarted = false;\r
5127                                 var iframeContainerDiv;\r
5128                                 var iframeId = uniqueId + "_InPageAppender_" + consoleAppenderId;\r
5129 \r
5130                                 this.hide = function() {\r
5131                                         if (initialized && consoleWindowCreated) {\r
5132                                                 if (consoleWindowExists()) {\r
5133                                                         getConsoleWindow().$("command").blur();\r
5134                                                 }\r
5135                                                 iframeContainerDiv.style.display = "none";\r
5136                                                 minimized = true;\r
5137                                         }\r
5138                                 };\r
5139 \r
5140                                 this.show = function() {\r
5141                                         if (initialized) {\r
5142                                                 if (consoleWindowCreated) {\r
5143                                                         iframeContainerDiv.style.display = "block";\r
5144                                                         this.setShowCommandLine(showCommandLine); // Force IE to update\r
5145                                                         minimized = false;\r
5146                                                 } else if (!windowCreationStarted) {\r
5147                                                         createWindow(true);\r
5148                                                 }\r
5149                                         }\r
5150                                 };\r
5151 \r
5152                                 this.isVisible = function() {\r
5153                                         return !minimized && !consoleClosed;\r
5154                                 };\r
5155 \r
5156                                 this.close = function(fromButton) {\r
5157                                         if (!consoleClosed && (!fromButton || confirm("This will permanently remove the console from the page. No more messages will be logged. Do you wish to continue?"))) {\r
5158                                                 iframeContainerDiv.parentNode.removeChild(iframeContainerDiv);\r
5159                                                 this.unload();\r
5160                                         }\r
5161                                 };\r
5162 \r
5163                                 // Create open, init, getConsoleWindow and safeToAppend functions\r
5164                                 open = function() {\r
5165                                         var initErrorMessage = "InPageAppender.open: unable to create console iframe";\r
5166 \r
5167                                         function finalInit() {\r
5168                                                 try {\r
5169                                                         if (!initiallyMinimized) {\r
5170                                                                 appender.show();\r
5171                                                         }\r
5172                                                         consoleWindowLoadHandler();\r
5173                                                         consoleWindowLoaded = true;\r
5174                                                         appendQueuedLoggingEvents();\r
5175                                                 } catch (ex) {\r
5176                                                         isSupported = false;\r
5177                                                         handleError(initErrorMessage, ex);\r
5178                                                 }\r
5179                                         }\r
5180 \r
5181                                         function writeToDocument() {\r
5182                                                 try {\r
5183                                                         var windowTest = function(win) { return isLoaded(win); };\r
5184                                                         if (useDocumentWrite) {\r
5185                                                                 writeHtml(getConsoleWindow().document);\r
5186                                                         }\r
5187                                                         if (windowTest(getConsoleWindow())) {\r
5188                                                                 finalInit();\r
5189                                                         } else {\r
5190                                                                 pollConsoleWindow(windowTest, 100, finalInit, initErrorMessage);\r
5191                                                         }\r
5192                                                 } catch (ex) {\r
5193                                                         isSupported = false;\r
5194                                                         handleError(initErrorMessage, ex);\r
5195                                                 }\r
5196                                         }\r
5197 \r
5198                                         minimized = false;\r
5199                                         iframeContainerDiv = containerElement.appendChild(document.createElement("div"));\r
5200 \r
5201                                         iframeContainerDiv.style.width = width;\r
5202                                         iframeContainerDiv.style.height = height;\r
5203                                         iframeContainerDiv.style.border = "solid gray 1px";\r
5204 \r
5205                                         for (var i = 0, len = cssProperties.length; i < len; i++) {\r
5206                                                 iframeContainerDiv.style[cssProperties[i][0]] = cssProperties[i][1];\r
5207                                         }\r
5208 \r
5209                                         var iframeSrc = useDocumentWrite ? "" : " src='" + getConsoleUrl() + "'";\r
5210 \r
5211                                         // Adding an iframe using the DOM would be preferable, but it doesn't work\r
5212                                         // in IE5 on Windows, or in Konqueror prior to version 3.5 - in Konqueror\r
5213                                         // it creates the iframe fine but I haven't been able to find a way to obtain\r
5214                                         // the iframe's window object\r
5215                                         iframeContainerDiv.innerHTML = "<iframe id='" + iframeId + "' name='" + iframeId +\r
5216                                                 "' width='100%' height='100%' frameborder='0'" + iframeSrc +\r
5217                                                 " scrolling='no'></iframe>";\r
5218                                         consoleClosed = false;\r
5219 \r
5220                                         // Write the console HTML to the iframe\r
5221                                         var iframeDocumentExistsTest = function(win) {\r
5222                                                 try {\r
5223                                                         return bool(win) && bool(win.document);\r
5224                                                 } catch (ex) {\r
5225                                                         return false;\r
5226                                                 }\r
5227                                         };\r
5228                                         if (iframeDocumentExistsTest(getConsoleWindow())) {\r
5229                                                 writeToDocument();\r
5230                                         } else {\r
5231                                                 pollConsoleWindow(iframeDocumentExistsTest, 100, writeToDocument, initErrorMessage);\r
5232                                         }\r
5233                                         consoleWindowCreated = true;\r
5234                                 };\r
5235 \r
5236                                 createWindow = function(show) {\r
5237                                         if (show || !initiallyMinimized) {\r
5238                                                 var pageLoadHandler = function() {\r
5239                                                         if (!container) {\r
5240                                                                 // Set up default container element\r
5241                                                                 containerElement = document.createElement("div");\r
5242                                                                 containerElement.style.position = "fixed";\r
5243                                                                 containerElement.style.left = "0";\r
5244                                                                 containerElement.style.right = "0";\r
5245                                                                 containerElement.style.bottom = "0";\r
5246                                                                 document.body.appendChild(containerElement);\r
5247                                                                 appender.addCssProperty("borderWidth", "1px 0 0 0");\r
5248                                                                 appender.addCssProperty("zIndex", 1000000); // Can't find anything authoritative that says how big z-index can be\r
5249                                                                 open();\r
5250                                                         } else {\r
5251                                                                 try {\r
5252                                                                         var el = document.getElementById(container);\r
5253                                                                         if (el.nodeType == 1) {\r
5254                                                                                 containerElement = el;\r
5255                                                                         }\r
5256                                                                         open();\r
5257                                                                 } catch (ex) {\r
5258                                                                         handleError("InPageAppender.init: invalid container element '" + container + "' supplied", ex);\r
5259                                                                 }\r
5260                                                         }\r
5261                                                 };\r
5262 \r
5263                                                 // Test the type of the container supplied. First, check if it's an element\r
5264                                                 if (pageLoaded && container && container.appendChild) {\r
5265                                                         containerElement = container;\r
5266                                                         open();\r
5267                                                 } else if (pageLoaded) {\r
5268                                                         pageLoadHandler();\r
5269                                                 } else {\r
5270                                                         log4javascript.addEventListener("load", pageLoadHandler);\r
5271                                                 }\r
5272                                                 windowCreationStarted = true;\r
5273                                         }\r
5274                                 };\r
5275 \r
5276                                 init = function() {\r
5277                                         createWindow();\r
5278                                         initialized = true;\r
5279                                 };\r
5280 \r
5281                                 getConsoleWindow = function() {\r
5282                                         var iframe = window.frames[iframeId];\r
5283                                         if (iframe) {\r
5284                                                 return iframe;\r
5285                                         }\r
5286                                 };\r
5287 \r
5288                                 safeToAppend = function() {\r
5289                                         if (isSupported && !consoleClosed) {\r
5290                                                 if (consoleWindowCreated && !consoleWindowLoaded && getConsoleWindow() && isLoaded(getConsoleWindow())) {\r
5291                                                         consoleWindowLoaded = true;\r
5292                                                 }\r
5293                                                 return consoleWindowLoaded;\r
5294                                         }\r
5295                                         return false;\r
5296                                 };\r
5297                         } else {\r
5298                                 // PopUpAppender\r
5299 \r
5300                                 // Extract params\r
5301                                 var useOldPopUp = appender.defaults.useOldPopUp;\r
5302                                 var complainAboutPopUpBlocking = appender.defaults.complainAboutPopUpBlocking;\r
5303                                 var reopenWhenClosed = this.defaults.reopenWhenClosed;\r
5304 \r
5305                                 // Configuration methods. The function scope is used to prevent\r
5306                                 // direct alteration to the appender configuration properties.\r
5307                                 this.isUseOldPopUp = function() { return useOldPopUp; };\r
5308                                 this.setUseOldPopUp = function(useOldPopUpParam) {\r
5309                                         if (checkCanConfigure("useOldPopUp")) {\r
5310                                                 useOldPopUp = bool(useOldPopUpParam);\r
5311                                         }\r
5312                                 };\r
5313 \r
5314                                 this.isComplainAboutPopUpBlocking = function() { return complainAboutPopUpBlocking; };\r
5315                                 this.setComplainAboutPopUpBlocking = function(complainAboutPopUpBlockingParam) {\r
5316                                         if (checkCanConfigure("complainAboutPopUpBlocking")) {\r
5317                                                 complainAboutPopUpBlocking = bool(complainAboutPopUpBlockingParam);\r
5318                                         }\r
5319                                 };\r
5320 \r
5321                                 this.isFocusPopUp = function() { return focusConsoleWindow; };\r
5322                                 this.setFocusPopUp = function(focusPopUpParam) {\r
5323                                         // This property can be safely altered after logging has started\r
5324                                         focusConsoleWindow = bool(focusPopUpParam);\r
5325                                 };\r
5326 \r
5327                                 this.isReopenWhenClosed = function() { return reopenWhenClosed; };\r
5328                                 this.setReopenWhenClosed = function(reopenWhenClosedParam) {\r
5329                                         // This property can be safely altered after logging has started\r
5330                                         reopenWhenClosed = bool(reopenWhenClosedParam);\r
5331                                 };\r
5332 \r
5333                                 this.close = function() {\r
5334                                         logLog.debug("close " + this);\r
5335                                         try {\r
5336                                                 popUp.close();\r
5337                                                 this.unload();\r
5338                                         } catch (ex) {\r
5339                                                 // Do nothing\r
5340                                         }\r
5341                                 };\r
5342 \r
5343                                 this.hide = function() {\r
5344                                         logLog.debug("hide " + this);\r
5345                                         if (consoleWindowExists()) {\r
5346                                                 this.close();\r
5347                                         }\r
5348                                 };\r
5349 \r
5350                                 this.show = function() {\r
5351                                         logLog.debug("show " + this);\r
5352                                         if (!consoleWindowCreated) {\r
5353                                                 open();\r
5354                                         }\r
5355                                 };\r
5356 \r
5357                                 this.isVisible = function() {\r
5358                                         return safeToAppend();\r
5359                                 };\r
5360 \r
5361                                 // Define useful variables\r
5362                                 var popUp;\r
5363 \r
5364                                 // Create open, init, getConsoleWindow and safeToAppend functions\r
5365                                 open = function() {\r
5366                                         var windowProperties = "width=" + width + ",height=" + height + ",status,resizable";\r
5367                                         var frameInfo = "";\r
5368                                         try {\r
5369                                                 var frameEl = window.frameElement;\r
5370                                                 if (frameEl) {\r
5371                                                         frameInfo = "_" + frameEl.tagName + "_" + (frameEl.name || frameEl.id || "");\r
5372                                                 }\r
5373                                         } catch (e) {\r
5374                                                 frameInfo = "_inaccessibleParentFrame";\r
5375                                         }\r
5376                                         var windowName = "PopUp_" + location.host.replace(/[^a-z0-9]/gi, "_") + "_" + consoleAppenderId + frameInfo;\r
5377                                         if (!useOldPopUp || !useDocumentWrite) {\r
5378                                                 // Ensure a previous window isn't used by using a unique name\r
5379                                                 windowName = windowName + "_" + uniqueId;\r
5380                                         }\r
5381 \r
5382                                         var checkPopUpClosed = function(win) {\r
5383                                                 if (consoleClosed) {\r
5384                                                         return true;\r
5385                                                 } else {\r
5386                                                         try {\r
5387                                                                 return bool(win) && win.closed;\r
5388                                                         } catch(ex) {}\r
5389                                                 }\r
5390                                                 return false;\r
5391                                         };\r
5392 \r
5393                                         var popUpClosedCallback = function() {\r
5394                                                 if (!consoleClosed) {\r
5395                                                         appender.unload();\r
5396                                                 }\r
5397                                         };\r
5398 \r
5399                                         function finalInit() {\r
5400                                                 getConsoleWindow().setCloseIfOpenerCloses(!useOldPopUp || !useDocumentWrite);\r
5401                                                 consoleWindowLoadHandler();\r
5402                                                 consoleWindowLoaded = true;\r
5403                                                 appendQueuedLoggingEvents();\r
5404                                                 pollConsoleWindow(checkPopUpClosed, 500, popUpClosedCallback,\r
5405                                                                 "PopUpAppender.checkPopUpClosed: error checking pop-up window");\r
5406                                         }\r
5407 \r
5408                                         try {\r
5409                                                 popUp = window.open(getConsoleUrl(), windowName, windowProperties);\r
5410                                                 consoleClosed = false;\r
5411                                                 consoleWindowCreated = true;\r
5412                                                 if (popUp && popUp.document) {\r
5413                                                         if (useDocumentWrite && useOldPopUp && isLoaded(popUp)) {\r
5414                                                                 popUp.mainPageReloaded();\r
5415                                                                 finalInit();\r
5416                                                         } else {\r
5417                                                                 if (useDocumentWrite) {\r
5418                                                                         writeHtml(popUp.document);\r
5419                                                                 }\r
5420                                                                 // Check if the pop-up window object is available\r
5421                                                                 var popUpLoadedTest = function(win) { return bool(win) && isLoaded(win); };\r
5422                                                                 if (isLoaded(popUp)) {\r
5423                                                                         finalInit();\r
5424                                                                 } else {\r
5425                                                                         pollConsoleWindow(popUpLoadedTest, 100, finalInit,\r
5426                                                                                         "PopUpAppender.init: unable to create console window");\r
5427                                                                 }\r
5428                                                         }\r
5429                                                 } else {\r
5430                                                         isSupported = false;\r
5431                                                         logLog.warn("PopUpAppender.init: pop-ups blocked, please unblock to use PopUpAppender");\r
5432                                                         if (complainAboutPopUpBlocking) {\r
5433                                                                 handleError("log4javascript: pop-up windows appear to be blocked. Please unblock them to use pop-up logging.");\r
5434                                                         }\r
5435                                                 }\r
5436                                         } catch (ex) {\r
5437                                                 handleError("PopUpAppender.init: error creating pop-up", ex);\r
5438                                         }\r
5439                                 };\r
5440 \r
5441                                 createWindow = function() {\r
5442                                         if (!initiallyMinimized) {\r
5443                                                 open();\r
5444                                         }\r
5445                                 };\r
5446 \r
5447                                 init = function() {\r
5448                                         createWindow();\r
5449                                         initialized = true;\r
5450                                 };\r
5451 \r
5452                                 getConsoleWindow = function() {\r
5453                                         return popUp;\r
5454                                 };\r
5455 \r
5456                                 safeToAppend = function() {\r
5457                                         if (isSupported && !isUndefined(popUp) && !consoleClosed) {\r
5458                                                 if (popUp.closed ||\r
5459                                                                 (consoleWindowLoaded && isUndefined(popUp.closed))) { // Extra check for Opera\r
5460                                                         appender.unload();\r
5461                                                         logLog.debug("PopUpAppender: pop-up closed");\r
5462                                                         return false;\r
5463                                                 }\r
5464                                                 if (!consoleWindowLoaded && isLoaded(popUp)) {\r
5465                                                         consoleWindowLoaded = true;\r
5466                                                 }\r
5467                                         }\r
5468                                         return isSupported && consoleWindowLoaded && !consoleClosed;\r
5469                                 };\r
5470                         }\r
5471 \r
5472                         // Expose getConsoleWindow so that automated tests can check the DOM\r
5473                         this.getConsoleWindow = getConsoleWindow;\r
5474                 };\r
5475 \r
5476                 ConsoleAppender.addGlobalCommandLineFunction = function(functionName, commandLineFunction) {\r
5477                         defaultCommandLineFunctions.push([functionName, commandLineFunction]);\r
5478                 };\r
5479 \r
5480                 /* ------------------------------------------------------------------ */\r
5481 \r
5482                 function PopUpAppender(lazyInit, initiallyMinimized, useDocumentWrite,\r
5483                                                            width, height) {\r
5484                         this.create(false, null, lazyInit, initiallyMinimized,\r
5485                                         useDocumentWrite, width, height, this.defaults.focusPopUp);\r
5486                 }\r
5487 \r
5488                 PopUpAppender.prototype = new ConsoleAppender();\r
5489 \r
5490                 PopUpAppender.prototype.defaults = {\r
5491                         layout: new PatternLayout("%d{HH:mm:ss} %-5p - %m{1}%n"),\r
5492                         initiallyMinimized: false,\r
5493                         focusPopUp: false,\r
5494                         lazyInit: true,\r
5495                         useOldPopUp: true,\r
5496                         complainAboutPopUpBlocking: true,\r
5497                         newestMessageAtTop: false,\r
5498                         scrollToLatestMessage: true,\r
5499                         width: "600",\r
5500                         height: "400",\r
5501                         reopenWhenClosed: false,\r
5502                         maxMessages: null,\r
5503                         showCommandLine: true,\r
5504                         commandLineObjectExpansionDepth: 1,\r
5505                         showHideButton: false,\r
5506                         showCloseButton: true,\r
5507             showLogEntryDeleteButtons: true,\r
5508             useDocumentWrite: true\r
5509                 };\r
5510 \r
5511                 PopUpAppender.prototype.toString = function() {\r
5512                         return "PopUpAppender";\r
5513                 };\r
5514 \r
5515                 log4javascript.PopUpAppender = PopUpAppender;\r
5516 \r
5517                 /* ------------------------------------------------------------------ */\r
5518 \r
5519                 function InPageAppender(container, lazyInit, initiallyMinimized,\r
5520                                                                 useDocumentWrite, width, height) {\r
5521                         this.create(true, container, lazyInit, initiallyMinimized,\r
5522                                         useDocumentWrite, width, height, false);\r
5523                 }\r
5524 \r
5525                 InPageAppender.prototype = new ConsoleAppender();\r
5526 \r
5527                 InPageAppender.prototype.defaults = {\r
5528                         layout: new PatternLayout("%d{HH:mm:ss} %-5p - %m{1}%n"),\r
5529                         initiallyMinimized: false,\r
5530                         lazyInit: true,\r
5531                         newestMessageAtTop: false,\r
5532                         scrollToLatestMessage: true,\r
5533                         width: "100%",\r
5534                         height: "220px",\r
5535                         maxMessages: null,\r
5536                         showCommandLine: true,\r
5537                         commandLineObjectExpansionDepth: 1,\r
5538                         showHideButton: false,\r
5539                         showCloseButton: false,\r
5540             showLogEntryDeleteButtons: true,\r
5541             useDocumentWrite: true\r
5542                 };\r
5543 \r
5544                 InPageAppender.prototype.toString = function() {\r
5545                         return "InPageAppender";\r
5546                 };\r
5547 \r
5548                 log4javascript.InPageAppender = InPageAppender;\r
5549 \r
5550                 // Next line for backwards compatibility\r
5551                 log4javascript.InlineAppender = InPageAppender;\r
5552         })();\r
5553         /* ---------------------------------------------------------------------- */\r
5554         // Console extension functions\r
5555 \r
5556         function padWithSpaces(str, len) {\r
5557                 if (str.length < len) {\r
5558                         var spaces = [];\r
5559                         var numberOfSpaces = Math.max(0, len - str.length);\r
5560                         for (var i = 0; i < numberOfSpaces; i++) {\r
5561                                 spaces[i] = " ";\r
5562                         }\r
5563                         str += spaces.join("");\r
5564                 }\r
5565                 return str;\r
5566         }\r
5567 \r
5568         (function() {\r
5569                 function dir(obj) {\r
5570                         var maxLen = 0;\r
5571                         // Obtain the length of the longest property name\r
5572                         for (var p in obj) {\r
5573                                 maxLen = Math.max(toStr(p).length, maxLen);\r
5574                         }\r
5575                         // Create the nicely formatted property list\r
5576                         var propList = [];\r
5577                         for (p in obj) {\r
5578                                 var propNameStr = "  " + padWithSpaces(toStr(p), maxLen + 2);\r
5579                                 var propVal;\r
5580                                 try {\r
5581                                         propVal = splitIntoLines(toStr(obj[p])).join(padWithSpaces(newLine, maxLen + 6));\r
5582                                 } catch (ex) {\r
5583                                         propVal = "[Error obtaining property. Details: " + getExceptionMessage(ex) + "]";\r
5584                                 }\r
5585                                 propList.push(propNameStr + propVal);\r
5586                         }\r
5587                         return propList.join(newLine);\r
5588                 }\r
5589 \r
5590                 var nodeTypes = {\r
5591                         ELEMENT_NODE: 1,\r
5592                         ATTRIBUTE_NODE: 2,\r
5593                         TEXT_NODE: 3,\r
5594                         CDATA_SECTION_NODE: 4,\r
5595                         ENTITY_REFERENCE_NODE: 5,\r
5596                         ENTITY_NODE: 6,\r
5597                         PROCESSING_INSTRUCTION_NODE: 7,\r
5598                         COMMENT_NODE: 8,\r
5599                         DOCUMENT_NODE: 9,\r
5600                         DOCUMENT_TYPE_NODE: 10,\r
5601                         DOCUMENT_FRAGMENT_NODE: 11,\r
5602                         NOTATION_NODE: 12\r
5603                 };\r
5604 \r
5605                 var preFormattedElements = ["script", "pre"];\r
5606 \r
5607                 // This should be the definitive list, as specified by the XHTML 1.0 Transitional DTD\r
5608                 var emptyElements = ["br", "img", "hr", "param", "link", "area", "input", "col", "base", "meta"];\r
5609                 var indentationUnit = "  ";\r
5610 \r
5611                 // Create and return an XHTML string from the node specified\r
5612                 function getXhtml(rootNode, includeRootNode, indentation, startNewLine, preformatted) {\r
5613                         includeRootNode = (typeof includeRootNode == "undefined") ? true : !!includeRootNode;\r
5614                         if (typeof indentation != "string") {\r
5615                                 indentation = "";\r
5616                         }\r
5617                         startNewLine = !!startNewLine;\r
5618                         preformatted = !!preformatted;\r
5619                         var xhtml;\r
5620 \r
5621                         function isWhitespace(node) {\r
5622                                 return ((node.nodeType == nodeTypes.TEXT_NODE) && /^[ \t\r\n]*$/.test(node.nodeValue));\r
5623                         }\r
5624 \r
5625                         function fixAttributeValue(attrValue) {\r
5626                                 return attrValue.toString().replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/"/g, "&quot;");\r
5627                         }\r
5628 \r
5629                         function getStyleAttributeValue(el) {\r
5630                                 var stylePairs = el.style.cssText.split(";");\r
5631                                 var styleValue = "";\r
5632                                 var isFirst = true;\r
5633                                 for (var j = 0, len = stylePairs.length; j < len; j++) {\r
5634                                         var nameValueBits = stylePairs[j].split(":");\r
5635                                         var props = [];\r
5636                                         if (!/^\s*$/.test(nameValueBits[0])) {\r
5637                                                 props.push(trim(nameValueBits[0]).toLowerCase() + ":" + trim(nameValueBits[1]));\r
5638                                         }\r
5639                                         styleValue = props.join(";");\r
5640                                 }\r
5641                                 return styleValue;\r
5642                         }\r
5643 \r
5644                         function getNamespace(el) {\r
5645                                 if (el.prefix) {\r
5646                                         return el.prefix;\r
5647                                 } else if (el.outerHTML) {\r
5648                                         var regex = new RegExp("<([^:]+):" + el.tagName + "[^>]*>", "i");\r
5649                                         if (regex.test(el.outerHTML)) {\r
5650                                                 return RegExp.$1.toLowerCase();\r
5651                                         }\r
5652                                 }\r
5653                 return "";\r
5654                         }\r
5655 \r
5656                         var lt = "<";\r
5657                         var gt = ">";\r
5658 \r
5659                         if (includeRootNode && rootNode.nodeType != nodeTypes.DOCUMENT_FRAGMENT_NODE) {\r
5660                                 switch (rootNode.nodeType) {\r
5661                                         case nodeTypes.ELEMENT_NODE:\r
5662                                                 var tagName = rootNode.tagName.toLowerCase();\r
5663                                                 xhtml = startNewLine ? newLine + indentation : "";\r
5664                                                 xhtml += lt;\r
5665                                                 // Allow for namespaces, where present\r
5666                                                 var prefix = getNamespace(rootNode);\r
5667                                                 var hasPrefix = !!prefix;\r
5668                                                 if (hasPrefix) {\r
5669                                                         xhtml += prefix + ":";\r
5670                                                 }\r
5671                                                 xhtml += tagName;\r
5672                                                 for (i = 0, len = rootNode.attributes.length; i < len; i++) {\r
5673                                                         var currentAttr = rootNode.attributes[i];\r
5674                                                         // Check the attribute is valid.\r
5675                                                         if (!   currentAttr.specified ||\r
5676                                                                         currentAttr.nodeValue === null ||\r
5677                                                                         currentAttr.nodeName.toLowerCase() === "style" ||\r
5678                                                                         typeof currentAttr.nodeValue !== "string" ||\r
5679                                                                         currentAttr.nodeName.indexOf("_moz") === 0) {\r
5680                                                                 continue;\r
5681                                                         }\r
5682                                                         xhtml += " " + currentAttr.nodeName.toLowerCase() + "=\"";\r
5683                                                         xhtml += fixAttributeValue(currentAttr.nodeValue);\r
5684                                                         xhtml += "\"";\r
5685                                                 }\r
5686                                                 // Style needs to be done separately as it is not reported as an\r
5687                                                 // attribute in IE\r
5688                                                 if (rootNode.style.cssText) {\r
5689                                                         var styleValue = getStyleAttributeValue(rootNode);\r
5690                                                         if (styleValue !== "") {\r
5691                                                                 xhtml += " style=\"" + getStyleAttributeValue(rootNode) + "\"";\r
5692                                                         }\r
5693                                                 }\r
5694                                                 if (array_contains(emptyElements, tagName) ||\r
5695                                                                 (hasPrefix && !rootNode.hasChildNodes())) {\r
5696                                                         xhtml += "/" + gt;\r
5697                                                 } else {\r
5698                                                         xhtml += gt;\r
5699                                                         // Add output for childNodes collection (which doesn't include attribute nodes)\r
5700                                                         var childStartNewLine = !(rootNode.childNodes.length === 1 &&\r
5701                                                                 rootNode.childNodes[0].nodeType === nodeTypes.TEXT_NODE);\r
5702                                                         var childPreformatted = array_contains(preFormattedElements, tagName);\r
5703                                                         for (var i = 0, len = rootNode.childNodes.length; i < len; i++) {\r
5704                                                                 xhtml += getXhtml(rootNode.childNodes[i], true, indentation + indentationUnit,\r
5705                                                                         childStartNewLine, childPreformatted);\r
5706                                                         }\r
5707                                                         // Add the end tag\r
5708                                                         var endTag = lt + "/" + tagName + gt;\r
5709                                                         xhtml += childStartNewLine ? newLine + indentation + endTag : endTag;\r
5710                                                 }\r
5711                                                 return xhtml;\r
5712                                         case nodeTypes.TEXT_NODE:\r
5713                                                 if (isWhitespace(rootNode)) {\r
5714                                                         xhtml = "";\r
5715                                                 } else {\r
5716                                                         if (preformatted) {\r
5717                                                                 xhtml = rootNode.nodeValue;\r
5718                                                         } else {\r
5719                                                                 // Trim whitespace from each line of the text node\r
5720                                                                 var lines = splitIntoLines(trim(rootNode.nodeValue));\r
5721                                                                 var trimmedLines = [];\r
5722                                                                 for (var i = 0, len = lines.length; i < len; i++) {\r
5723                                                                         trimmedLines[i] = trim(lines[i]);\r
5724                                                                 }\r
5725                                                                 xhtml = trimmedLines.join(newLine + indentation);\r
5726                                                         }\r
5727                                                         if (startNewLine) {\r
5728                                                                 xhtml = newLine + indentation + xhtml;\r
5729                                                         }\r
5730                                                 }\r
5731                                                 return xhtml;\r
5732                                         case nodeTypes.CDATA_SECTION_NODE:\r
5733                                                 return "<![CDA" + "TA[" + rootNode.nodeValue + "]" + "]>" + newLine;\r
5734                                         case nodeTypes.DOCUMENT_NODE:\r
5735                                                 xhtml = "";\r
5736                                                 // Add output for childNodes collection (which doesn't include attribute nodes)\r
5737                                                 for (var i = 0, len = rootNode.childNodes.length; i < len; i++) {\r
5738                                                         xhtml += getXhtml(rootNode.childNodes[i], true, indentation);\r
5739                                                 }\r
5740                                                 return xhtml;\r
5741                                         default:\r
5742                                                 return "";\r
5743                                 }\r
5744                         } else {\r
5745                                 xhtml = "";\r
5746                                 // Add output for childNodes collection (which doesn't include attribute nodes)\r
5747                                 for (var i = 0, len = rootNode.childNodes.length; i < len; i++) {\r
5748                                         xhtml += getXhtml(rootNode.childNodes[i], true, indentation + indentationUnit);\r
5749                                 }\r
5750                                 return xhtml;\r
5751                         }\r
5752                 }\r
5753 \r
5754                 function createCommandLineFunctions() {\r
5755                         ConsoleAppender.addGlobalCommandLineFunction("$", function(appender, args, returnValue) {\r
5756                                 return document.getElementById(args[0]);\r
5757                         });\r
5758 \r
5759                         ConsoleAppender.addGlobalCommandLineFunction("dir", function(appender, args, returnValue) {\r
5760                                 var lines = [];\r
5761                                 for (var i = 0, len = args.length; i < len; i++) {\r
5762                                         lines[i] = dir(args[i]);\r
5763                                 }\r
5764                                 return lines.join(newLine + newLine);\r
5765                         });\r
5766 \r
5767                         ConsoleAppender.addGlobalCommandLineFunction("dirxml", function(appender, args, returnValue) {\r
5768                                 var lines = [];\r
5769                                 for (var i = 0, len = args.length; i < len; i++) {\r
5770                                         var win = appender.getCommandWindow();\r
5771                                         lines[i] = getXhtml(args[i]);\r
5772                                 }\r
5773                                 return lines.join(newLine + newLine);\r
5774                         });\r
5775 \r
5776                         ConsoleAppender.addGlobalCommandLineFunction("cd", function(appender, args, returnValue) {\r
5777                                 var win, message;\r
5778                                 if (args.length === 0 || args[0] === "") {\r
5779                                         win = window;\r
5780                                         message = "Command line set to run in main window";\r
5781                                 } else {\r
5782                                         if (args[0].window == args[0]) {\r
5783                                                 win = args[0];\r
5784                                                 message = "Command line set to run in frame '" + args[0].name + "'";\r
5785                                         } else {\r
5786                                                 win = window.frames[args[0]];\r
5787                                                 if (win) {\r
5788                                                         message = "Command line set to run in frame '" + args[0] + "'";\r
5789                                                 } else {\r
5790                                                         returnValue.isError = true;\r
5791                                                         message = "Frame '" + args[0] + "' does not exist";\r
5792                                                         win = appender.getCommandWindow();\r
5793                                                 }\r
5794                                         }\r
5795                                 }\r
5796                                 appender.setCommandWindow(win);\r
5797                                 return message;\r
5798                         });\r
5799 \r
5800                         ConsoleAppender.addGlobalCommandLineFunction("clear", function(appender, args, returnValue) {\r
5801                                 returnValue.appendResult = false;\r
5802                                 appender.clear();\r
5803                         });\r
5804 \r
5805                         ConsoleAppender.addGlobalCommandLineFunction("keys", function(appender, args, returnValue) {\r
5806                                 var keys = [];\r
5807                                 for (var k in args[0]) {\r
5808                                         keys.push(k);\r
5809                                 }\r
5810                                 return keys;\r
5811                         });\r
5812 \r
5813                         ConsoleAppender.addGlobalCommandLineFunction("values", function(appender, args, returnValue) {\r
5814                                 var values = [];\r
5815                                 for (var k in args[0]) {\r
5816                                         try {\r
5817                                                 values.push(args[0][k]);\r
5818                                         } catch (ex) {\r
5819                                                 logLog.warn("values(): Unable to obtain value for key " + k + ". Details: " + getExceptionMessage(ex));\r
5820                                         }\r
5821                                 }\r
5822                                 return values;\r
5823                         });\r
5824 \r
5825                         ConsoleAppender.addGlobalCommandLineFunction("expansionDepth", function(appender, args, returnValue) {\r
5826                                 var expansionDepth = parseInt(args[0], 10);\r
5827                                 if (isNaN(expansionDepth) || expansionDepth < 0) {\r
5828                                         returnValue.isError = true;\r
5829                                         return "" + args[0] + " is not a valid expansion depth";\r
5830                                 } else {\r
5831                                         appender.setCommandLineObjectExpansionDepth(expansionDepth);\r
5832                                         return "Object expansion depth set to " + expansionDepth;\r
5833                                 }\r
5834                         });\r
5835                 }\r
5836 \r
5837                 function init() {\r
5838                         // Add command line functions\r
5839                         createCommandLineFunctions();\r
5840                 }\r
5841 \r
5842                 /* ------------------------------------------------------------------ */\r
5843 \r
5844                 init();\r
5845         })();\r
5846 \r
5847         /* ---------------------------------------------------------------------- */\r
5848         // Main load\r
5849 \r
5850    log4javascript.setDocumentReady = function() {\r
5851        pageLoaded = true;\r
5852        log4javascript.dispatchEvent("load", {});\r
5853    };\r
5854 \r
5855     if (window.addEventListener) {\r
5856         window.addEventListener("load", log4javascript.setDocumentReady, false);\r
5857     } else if (window.attachEvent) {\r
5858         window.attachEvent("onload", log4javascript.setDocumentReady);\r
5859     } else {\r
5860         var oldOnload = window.onload;\r
5861         if (typeof window.onload != "function") {\r
5862             window.onload = log4javascript.setDocumentReady;\r
5863         } else {\r
5864             window.onload = function(evt) {\r
5865                 if (oldOnload) {\r
5866                     oldOnload(evt);\r
5867                 }\r
5868                 log4javascript.setDocumentReady();\r
5869             };\r
5870         }\r
5871     }\r
5872 \r
5873     // Ensure that the log4javascript object is available in the window. This\r
5874     // is necessary for log4javascript to be available in IE if loaded using\r
5875     // Dojo's module system\r
5876     window.log4javascript = log4javascript;\r
5877 \r
5878     return log4javascript;\r
5879 })();