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_production_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_production\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_production";\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         // AjaxAppender related\r
1875 \r
1876         var xmlHttpFactories = [\r
1877                 function() { return new XMLHttpRequest(); },\r
1878                 function() { return new ActiveXObject("Msxml2.XMLHTTP"); },\r
1879                 function() { return new ActiveXObject("Microsoft.XMLHTTP"); }\r
1880         ];\r
1881 \r
1882         var getXmlHttp = function(errorHandler) {\r
1883                 // This is only run the first time; the value of getXmlHttp gets\r
1884                 // replaced with the factory that succeeds on the first run\r
1885                 var xmlHttp = null, factory;\r
1886                 for (var i = 0, len = xmlHttpFactories.length; i < len; i++) {\r
1887                         factory = xmlHttpFactories[i];\r
1888                         try {\r
1889                                 xmlHttp = factory();\r
1890                                 getXmlHttp = factory;\r
1891                                 return xmlHttp;\r
1892                         } catch (e) {\r
1893                         }\r
1894                 }\r
1895                 // If we're here, all factories have failed, so throw an error\r
1896                 if (errorHandler) {\r
1897                         errorHandler();\r
1898                 } else {\r
1899                         handleError("getXmlHttp: unable to obtain XMLHttpRequest object");\r
1900                 }\r
1901         };\r
1902 \r
1903         function isHttpRequestSuccessful(xmlHttp) {\r
1904                 return isUndefined(xmlHttp.status) || xmlHttp.status === 0 ||\r
1905                         (xmlHttp.status >= 200 && xmlHttp.status < 300) ||\r
1906                         xmlHttp.status == 1223 /* Fix for IE */;\r
1907         }\r
1908 \r
1909         /* ---------------------------------------------------------------------- */\r
1910         // AjaxAppender\r
1911 \r
1912         function AjaxAppender(url) {\r
1913                 var appender = this;\r
1914                 var isSupported = true;\r
1915                 if (!url) {\r
1916                         handleError("AjaxAppender: URL must be specified in constructor");\r
1917                         isSupported = false;\r
1918                 }\r
1919 \r
1920                 var timed = this.defaults.timed;\r
1921                 var waitForResponse = this.defaults.waitForResponse;\r
1922                 var batchSize = this.defaults.batchSize;\r
1923                 var timerInterval = this.defaults.timerInterval;\r
1924                 var requestSuccessCallback = this.defaults.requestSuccessCallback;\r
1925                 var failCallback = this.defaults.failCallback;\r
1926                 var postVarName = this.defaults.postVarName;\r
1927                 var sendAllOnUnload = this.defaults.sendAllOnUnload;\r
1928                 var contentType = this.defaults.contentType;\r
1929                 var sessionId = null;\r
1930 \r
1931                 var queuedLoggingEvents = [];\r
1932                 var queuedRequests = [];\r
1933                 var headers = [];\r
1934                 var sending = false;\r
1935                 var initialized = false;\r
1936 \r
1937                 // Configuration methods. The function scope is used to prevent\r
1938                 // direct alteration to the appender configuration properties.\r
1939                 function checkCanConfigure(configOptionName) {\r
1940                         if (initialized) {\r
1941                                 handleError("AjaxAppender: configuration option '" +\r
1942                                         configOptionName +\r
1943                                         "' may not be set after the appender has been initialized");\r
1944                                 return false;\r
1945                         }\r
1946                         return true;\r
1947                 }\r
1948 \r
1949                 this.getSessionId = function() { return sessionId; };\r
1950                 this.setSessionId = function(sessionIdParam) {\r
1951                         sessionId = extractStringFromParam(sessionIdParam, null);\r
1952                         this.layout.setCustomField("sessionid", sessionId);\r
1953                 };\r
1954 \r
1955                 this.setLayout = function(layoutParam) {\r
1956                         if (checkCanConfigure("layout")) {\r
1957                                 this.layout = layoutParam;\r
1958                                 // Set the session id as a custom field on the layout, if not already present\r
1959                                 if (sessionId !== null) {\r
1960                                         this.setSessionId(sessionId);\r
1961                                 }\r
1962                         }\r
1963                 };\r
1964 \r
1965                 this.isTimed = function() { return timed; };\r
1966                 this.setTimed = function(timedParam) {\r
1967                         if (checkCanConfigure("timed")) {\r
1968                                 timed = bool(timedParam);\r
1969                         }\r
1970                 };\r
1971 \r
1972                 this.getTimerInterval = function() { return timerInterval; };\r
1973                 this.setTimerInterval = function(timerIntervalParam) {\r
1974                         if (checkCanConfigure("timerInterval")) {\r
1975                                 timerInterval = extractIntFromParam(timerIntervalParam, timerInterval);\r
1976                         }\r
1977                 };\r
1978 \r
1979                 this.isWaitForResponse = function() { return waitForResponse; };\r
1980                 this.setWaitForResponse = function(waitForResponseParam) {\r
1981                         if (checkCanConfigure("waitForResponse")) {\r
1982                                 waitForResponse = bool(waitForResponseParam);\r
1983                         }\r
1984                 };\r
1985 \r
1986                 this.getBatchSize = function() { return batchSize; };\r
1987                 this.setBatchSize = function(batchSizeParam) {\r
1988                         if (checkCanConfigure("batchSize")) {\r
1989                                 batchSize = extractIntFromParam(batchSizeParam, batchSize);\r
1990                         }\r
1991                 };\r
1992 \r
1993                 this.isSendAllOnUnload = function() { return sendAllOnUnload; };\r
1994                 this.setSendAllOnUnload = function(sendAllOnUnloadParam) {\r
1995                         if (checkCanConfigure("sendAllOnUnload")) {\r
1996                                 sendAllOnUnload = extractBooleanFromParam(sendAllOnUnloadParam, sendAllOnUnload);\r
1997                         }\r
1998                 };\r
1999 \r
2000                 this.setRequestSuccessCallback = function(requestSuccessCallbackParam) {\r
2001                         requestSuccessCallback = extractFunctionFromParam(requestSuccessCallbackParam, requestSuccessCallback);\r
2002                 };\r
2003 \r
2004                 this.setFailCallback = function(failCallbackParam) {\r
2005                         failCallback = extractFunctionFromParam(failCallbackParam, failCallback);\r
2006                 };\r
2007 \r
2008                 this.getPostVarName = function() { return postVarName; };\r
2009                 this.setPostVarName = function(postVarNameParam) {\r
2010                         if (checkCanConfigure("postVarName")) {\r
2011                                 postVarName = extractStringFromParam(postVarNameParam, postVarName);\r
2012                         }\r
2013                 };\r
2014 \r
2015                 this.getHeaders = function() { return headers; };\r
2016                 this.addHeader = function(name, value) {\r
2017                         if (name.toLowerCase() == "content-type") {\r
2018                                 contentType = value;\r
2019                         } else {\r
2020                                 headers.push( { name: name, value: value } );\r
2021                         }\r
2022                 };\r
2023 \r
2024                 // Internal functions\r
2025                 function sendAll() {\r
2026                         if (isSupported && enabled) {\r
2027                                 sending = true;\r
2028                                 var currentRequestBatch;\r
2029                                 if (waitForResponse) {\r
2030                                         // Send the first request then use this function as the callback once\r
2031                                         // the response comes back\r
2032                                         if (queuedRequests.length > 0) {\r
2033                                                 currentRequestBatch = queuedRequests.shift();\r
2034                                                 sendRequest(preparePostData(currentRequestBatch), sendAll);\r
2035                                         } else {\r
2036                                                 sending = false;\r
2037                                                 if (timed) {\r
2038                                                         scheduleSending();\r
2039                                                 }\r
2040                                         }\r
2041                                 } else {\r
2042                                         // Rattle off all the requests without waiting to see the response\r
2043                                         while ((currentRequestBatch = queuedRequests.shift())) {\r
2044                                                 sendRequest(preparePostData(currentRequestBatch));\r
2045                                         }\r
2046                                         sending = false;\r
2047                                         if (timed) {\r
2048                                                 scheduleSending();\r
2049                                         }\r
2050                                 }\r
2051                         }\r
2052                 }\r
2053 \r
2054                 this.sendAll = sendAll;\r
2055 \r
2056                 // Called when the window unloads. At this point we're past caring about\r
2057                 // waiting for responses or timers or incomplete batches - everything\r
2058                 // must go, now\r
2059                 function sendAllRemaining() {\r
2060                         var sendingAnything = false;\r
2061                         if (isSupported && enabled) {\r
2062                                 // Create requests for everything left over, batched as normal\r
2063                                 var actualBatchSize = appender.getLayout().allowBatching() ? batchSize : 1;\r
2064                                 var currentLoggingEvent;\r
2065                                 var batchedLoggingEvents = [];\r
2066                                 while ((currentLoggingEvent = queuedLoggingEvents.shift())) {\r
2067                                         batchedLoggingEvents.push(currentLoggingEvent);\r
2068                                         if (queuedLoggingEvents.length >= actualBatchSize) {\r
2069                                                 // Queue this batch of log entries\r
2070                                                 queuedRequests.push(batchedLoggingEvents);\r
2071                                                 batchedLoggingEvents = [];\r
2072                                         }\r
2073                                 }\r
2074                                 // If there's a partially completed batch, add it\r
2075                                 if (batchedLoggingEvents.length > 0) {\r
2076                                         queuedRequests.push(batchedLoggingEvents);\r
2077                                 }\r
2078                                 sendingAnything = (queuedRequests.length > 0);\r
2079                                 waitForResponse = false;\r
2080                                 timed = false;\r
2081                                 sendAll();\r
2082                         }\r
2083                         return sendingAnything;\r
2084                 }\r
2085 \r
2086                 this.sendAllRemaining = sendAllRemaining;\r
2087 \r
2088                 function preparePostData(batchedLoggingEvents) {\r
2089                         // Format the logging events\r
2090                         var formattedMessages = [];\r
2091                         var currentLoggingEvent;\r
2092                         var postData = "";\r
2093                         while ((currentLoggingEvent = batchedLoggingEvents.shift())) {\r
2094                                 var currentFormattedMessage = appender.getLayout().format(currentLoggingEvent);\r
2095                                 if (appender.getLayout().ignoresThrowable()) {\r
2096                                         currentFormattedMessage += currentLoggingEvent.getThrowableStrRep();\r
2097                                 }\r
2098                                 formattedMessages.push(currentFormattedMessage);\r
2099                         }\r
2100                         // Create the post data string\r
2101                         if (batchedLoggingEvents.length == 1) {\r
2102                                 postData = formattedMessages.join("");\r
2103                         } else {\r
2104                                 postData = appender.getLayout().batchHeader +\r
2105                                         formattedMessages.join(appender.getLayout().batchSeparator) +\r
2106                                         appender.getLayout().batchFooter;\r
2107                         }\r
2108                         if (contentType == appender.defaults.contentType) {\r
2109                                 postData = appender.getLayout().returnsPostData ? postData :\r
2110                                         urlEncode(postVarName) + "=" + urlEncode(postData);\r
2111                                 // Add the layout name to the post data\r
2112                                 if (postData.length > 0) {\r
2113                                         postData += "&";\r
2114                                 }\r
2115                                 postData += "layout=" + urlEncode(appender.getLayout().toString());\r
2116                         }\r
2117                         return postData;\r
2118                 }\r
2119 \r
2120                 function scheduleSending() {\r
2121                         window.setTimeout(sendAll, timerInterval);\r
2122                 }\r
2123 \r
2124                 function xmlHttpErrorHandler() {\r
2125                         var msg = "AjaxAppender: could not create XMLHttpRequest object. AjaxAppender disabled";\r
2126                         handleError(msg);\r
2127                         isSupported = false;\r
2128                         if (failCallback) {\r
2129                                 failCallback(msg);\r
2130                         }\r
2131                 }\r
2132 \r
2133                 function sendRequest(postData, successCallback) {\r
2134                         try {\r
2135                                 var xmlHttp = getXmlHttp(xmlHttpErrorHandler);\r
2136                                 if (isSupported) {\r
2137                                         if (xmlHttp.overrideMimeType) {\r
2138                                                 xmlHttp.overrideMimeType(appender.getLayout().getContentType());\r
2139                                         }\r
2140                                         xmlHttp.onreadystatechange = function() {\r
2141                                                 if (xmlHttp.readyState == 4) {\r
2142                                                         if (isHttpRequestSuccessful(xmlHttp)) {\r
2143                                                                 if (requestSuccessCallback) {\r
2144                                                                         requestSuccessCallback(xmlHttp);\r
2145                                                                 }\r
2146                                                                 if (successCallback) {\r
2147                                                                         successCallback(xmlHttp);\r
2148                                                                 }\r
2149                                                         } else {\r
2150                                                                 var msg = "AjaxAppender.append: XMLHttpRequest request to URL " +\r
2151                                                                         url + " returned status code " + xmlHttp.status;\r
2152                                                                 handleError(msg);\r
2153                                                                 if (failCallback) {\r
2154                                                                         failCallback(msg);\r
2155                                                                 }\r
2156                                                         }\r
2157                                                         xmlHttp.onreadystatechange = emptyFunction;\r
2158                                                         xmlHttp = null;\r
2159                                                 }\r
2160                                         };\r
2161                                         xmlHttp.open("POST", url, true);\r
2162                                         try {\r
2163                                                 for (var i = 0, header; header = headers[i++]; ) {\r
2164                                                         xmlHttp.setRequestHeader(header.name, header.value);\r
2165                                                 }\r
2166                                                 xmlHttp.setRequestHeader("Content-Type", contentType);\r
2167                                         } catch (headerEx) {\r
2168                                                 var msg = "AjaxAppender.append: your browser's XMLHttpRequest implementation" +\r
2169                                                         " does not support setRequestHeader, therefore cannot post data. AjaxAppender disabled";\r
2170                                                 handleError(msg);\r
2171                                                 isSupported = false;\r
2172                                                 if (failCallback) {\r
2173                                                         failCallback(msg);\r
2174                                                 }\r
2175                                                 return;\r
2176                                         }\r
2177                                         xmlHttp.send(postData);\r
2178                                 }\r
2179                         } catch (ex) {\r
2180                                 var errMsg = "AjaxAppender.append: error sending log message to " + url;\r
2181                                 handleError(errMsg, ex);\r
2182                                 isSupported = false;\r
2183                                 if (failCallback) {\r
2184                                         failCallback(errMsg + ". Details: " + getExceptionStringRep(ex));\r
2185                                 }\r
2186                         }\r
2187                 }\r
2188 \r
2189                 this.append = function(loggingEvent) {\r
2190                         if (isSupported) {\r
2191                                 if (!initialized) {\r
2192                                         init();\r
2193                                 }\r
2194                                 queuedLoggingEvents.push(loggingEvent);\r
2195                                 var actualBatchSize = this.getLayout().allowBatching() ? batchSize : 1;\r
2196 \r
2197                                 if (queuedLoggingEvents.length >= actualBatchSize) {\r
2198                                         var currentLoggingEvent;\r
2199                                         var batchedLoggingEvents = [];\r
2200                                         while ((currentLoggingEvent = queuedLoggingEvents.shift())) {\r
2201                                                 batchedLoggingEvents.push(currentLoggingEvent);\r
2202                                         }\r
2203                                         // Queue this batch of log entries\r
2204                                         queuedRequests.push(batchedLoggingEvents);\r
2205 \r
2206                                         // If using a timer, the queue of requests will be processed by the\r
2207                                         // timer function, so nothing needs to be done here.\r
2208                                         if (!timed && (!waitForResponse || (waitForResponse && !sending))) {\r
2209                                                 sendAll();\r
2210                                         }\r
2211                                 }\r
2212                         }\r
2213                 };\r
2214 \r
2215                 function init() {\r
2216                         initialized = true;\r
2217                         // Add unload event to send outstanding messages\r
2218                         if (sendAllOnUnload) {\r
2219                                 var oldBeforeUnload = window.onbeforeunload;\r
2220                                 window.onbeforeunload = function() {\r
2221                                         if (oldBeforeUnload) {\r
2222                                                 oldBeforeUnload();\r
2223                                         }\r
2224                                         if (sendAllRemaining()) {\r
2225                                                 return "Sending log messages";\r
2226                                         }\r
2227                                 };\r
2228                         }\r
2229                         // Start timer\r
2230                         if (timed) {\r
2231                                 scheduleSending();\r
2232                         }\r
2233                 }\r
2234         }\r
2235 \r
2236         AjaxAppender.prototype = new Appender();\r
2237 \r
2238         AjaxAppender.prototype.defaults = {\r
2239                 waitForResponse: false,\r
2240                 timed: false,\r
2241                 timerInterval: 1000,\r
2242                 batchSize: 1,\r
2243                 sendAllOnUnload: false,\r
2244                 requestSuccessCallback: null,\r
2245                 failCallback: null,\r
2246                 postVarName: "data",\r
2247                 contentType: "application/x-www-form-urlencoded"\r
2248         };\r
2249 \r
2250         AjaxAppender.prototype.layout = new HttpPostDataLayout();\r
2251 \r
2252         AjaxAppender.prototype.toString = function() {\r
2253                 return "AjaxAppender";\r
2254         };\r
2255 \r
2256         log4javascript.AjaxAppender = AjaxAppender;\r
2257 \r
2258         /* ---------------------------------------------------------------------- */\r
2259         // Main load\r
2260 \r
2261    log4javascript.setDocumentReady = function() {\r
2262        pageLoaded = true;\r
2263        log4javascript.dispatchEvent("load", {});\r
2264    };\r
2265 \r
2266     if (window.addEventListener) {\r
2267         window.addEventListener("load", log4javascript.setDocumentReady, false);\r
2268     } else if (window.attachEvent) {\r
2269         window.attachEvent("onload", log4javascript.setDocumentReady);\r
2270     } else {\r
2271         var oldOnload = window.onload;\r
2272         if (typeof window.onload != "function") {\r
2273             window.onload = log4javascript.setDocumentReady;\r
2274         } else {\r
2275             window.onload = function(evt) {\r
2276                 if (oldOnload) {\r
2277                     oldOnload(evt);\r
2278                 }\r
2279                 log4javascript.setDocumentReady();\r
2280             };\r
2281         }\r
2282     }\r
2283 \r
2284     // Ensure that the log4javascript object is available in the window. This\r
2285     // is necessary for log4javascript to be available in IE if loaded using\r
2286     // Dojo's module system\r
2287     window.log4javascript = log4javascript;\r
2288 \r
2289     return log4javascript;\r
2290 })();