log4javascript

log4javascript is a logging framework for JavaScript based on log4j

Este script no debería instalarse directamente. Es una biblioteca que utilizan otros scripts mediante la meta-directiva de inclusión // @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);