log4javascript

log4javascript is a logging framework for JavaScript based on log4j

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greatest.deepsurf.us/scripts/8159/37575/log4javascript.js

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