log4javascript_uncompressed.js 98 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199
  1. /**
  2. * Copyright 2006 Tim Down.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /**
  17. * log4javascript
  18. *
  19. * log4javascript is a logging framework for JavaScript based on log4j
  20. * for Java. This file contains all core log4javascript code and is the only
  21. * file required to use log4javascript. If you wish to disable log4javascript
  22. * in production code, replace log4javascript.js with the stub file
  23. * log4javascript_stub.js, included in the distribution. Also included in the
  24. * distribution is log4javascript.js, a compressed but functionally
  25. * identical version of this file.
  26. *
  27. * Author: Tim Down <tim@timdown.co.uk>
  28. * Version: 1.3.1
  29. * Last modified: 8/11/2006
  30. * Website: http://www.timdown.co.uk/log4javascript
  31. */
  32. /* ------------------------------------------------------------------------- */
  33. // Array-related stuff
  34. // Next three methods are solely for IE5, which is missing them
  35. if (!Array.prototype.push) {
  36. Array.prototype.push = function() {
  37. for (var i = 0; i < arguments.length; i++){
  38. this[this.length] = arguments[i];
  39. }
  40. return this.length;
  41. };
  42. }
  43. if (!Array.prototype.shift) {
  44. Array.prototype.shift = function() {
  45. if (this.length > 0) {
  46. var firstItem = this[0];
  47. for (var i = 0; i < this.length - 1; i++) {
  48. this[i] = this[i + 1];
  49. }
  50. this.length = this.length - 1;
  51. return firstItem;
  52. }
  53. };
  54. }
  55. if (!Array.prototype.splice) {
  56. Array.prototype.splice = function(startIndex, deleteCount) {
  57. var itemsAfterDeleted = this.slice(startIndex + deleteCount);
  58. var itemsDeleted = this.slice(startIndex, startIndex + deleteCount);
  59. this.length = startIndex;
  60. // Copy the arguments into a proper Array object
  61. var argumentsArray = [];
  62. for (var i = 0; i < arguments.length; i++) {
  63. argumentsArray[i] = arguments[i];
  64. }
  65. var itemsToAppend = (argumentsArray.length > 2) ?
  66. itemsAfterDeleted = argumentsArray.slice(2).concat(itemsAfterDeleted) : itemsAfterDeleted;
  67. for (i = 0; i < itemsToAppend.length; i++) {
  68. this.push(itemsToAppend[i]);
  69. }
  70. return itemsDeleted;
  71. };
  72. }
  73. /* ------------------------------------------------------------------------- */
  74. var log4javascript;
  75. var SimpleDateFormat;
  76. (function() {
  77. function isUndefined(obj) {
  78. return typeof obj == "undefined";
  79. }
  80. // Date-related stuff
  81. (function() {
  82. 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']+)/;
  83. var monthNames = ["January", "February", "March", "April", "May", "June",
  84. "July", "August", "September", "October", "November", "December"];
  85. var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  86. var TEXT2 = 0, TEXT3 = 1, NUMBER = 2, YEAR = 3, MONTH = 4, TIMEZONE = 5;
  87. var types = {
  88. G : TEXT2,
  89. y : YEAR,
  90. Y : YEAR,
  91. M : MONTH,
  92. w : NUMBER,
  93. W : NUMBER,
  94. D : NUMBER,
  95. d : NUMBER,
  96. F : NUMBER,
  97. E : TEXT3,
  98. a : TEXT2,
  99. H : NUMBER,
  100. k : NUMBER,
  101. K : NUMBER,
  102. h : NUMBER,
  103. m : NUMBER,
  104. s : NUMBER,
  105. S : NUMBER,
  106. Z : TIMEZONE
  107. };
  108. var ONE_DAY = 24 * 60 * 60 * 1000;
  109. var ONE_WEEK = 7 * ONE_DAY;
  110. var DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK = 1;
  111. Date.prototype.getDifference = function(date) {
  112. return this.getTime() - date.getTime();
  113. };
  114. Date.prototype.isBefore = function(d) {
  115. return this.getTime() < d.getTime();
  116. };
  117. Date.prototype.getWeekInYear = function(minimalDaysInFirstWeek) {
  118. if (isUndefined(this.minimalDaysInFirstWeek)) {
  119. minimalDaysInFirstWeek = DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK;
  120. }
  121. var previousSunday = new Date(this.getTime() - this.getDay() * ONE_DAY);
  122. previousSunday = new Date(previousSunday.getFullYear(), previousSunday.getMonth(), previousSunday.getDate());
  123. var startOfYear = new Date(this.getFullYear(), 0, 1);
  124. var numberOfSundays = previousSunday.isBefore(startOfYear) ?
  125. 0 : 1 + Math.floor((previousSunday.getTime() - startOfYear.getTime()) / ONE_WEEK);
  126. var numberOfDaysInFirstWeek = 7 - startOfYear.getDay();
  127. var weekInYear = numberOfSundays;
  128. if (numberOfDaysInFirstWeek >= minimalDaysInFirstWeek) {
  129. weekInYear++;
  130. }
  131. return weekInYear;
  132. };
  133. Date.prototype.getWeekInMonth = function(minimalDaysInFirstWeek) {
  134. if (isUndefined(this.minimalDaysInFirstWeek)) {
  135. minimalDaysInFirstWeek = DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK;
  136. }
  137. var previousSunday = new Date(this.getTime() - this.getDay() * ONE_DAY);
  138. previousSunday = new Date(previousSunday.getFullYear(), previousSunday.getMonth(), previousSunday.getDate());
  139. var startOfMonth = new Date(this.getFullYear(), this.getMonth(), 1);
  140. var numberOfSundays = previousSunday.isBefore(startOfMonth) ?
  141. 0 : 1 + Math.floor((previousSunday.getTime() - startOfMonth.getTime()) / ONE_WEEK);
  142. var numberOfDaysInFirstWeek = 7 - startOfMonth.getDay();
  143. var weekInMonth = numberOfSundays;
  144. if (numberOfDaysInFirstWeek >= minimalDaysInFirstWeek) {
  145. weekInMonth++;
  146. }
  147. return weekInMonth;
  148. };
  149. Date.prototype.getDayInYear = function() {
  150. var startOfYear = new Date(this.getFullYear(), 0, 1);
  151. return 1 + Math.floor((this.getTime() - startOfYear.getTime()) / ONE_DAY);
  152. };
  153. /* ----------------------------------------------------------------- */
  154. SimpleDateFormat = function(formatString) {
  155. this.formatString = formatString;
  156. };
  157. /**
  158. * Sets the minimum number of days in a week in order for that week to
  159. * be considered as belonging to a particular month or year
  160. */
  161. SimpleDateFormat.prototype.setMinimalDaysInFirstWeek = function(days) {
  162. this.minimalDaysInFirstWeek = days;
  163. };
  164. SimpleDateFormat.prototype.getMinimalDaysInFirstWeek = function(days) {
  165. return isUndefined(this.minimalDaysInFirstWeek) ?
  166. DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK : this.minimalDaysInFirstWeek;
  167. };
  168. SimpleDateFormat.prototype.format = function(date) {
  169. var formattedString = "";
  170. var result;
  171. var padWithZeroes = function(str, len) {
  172. while (str.length < len) {
  173. str = "0" + str;
  174. }
  175. return str;
  176. };
  177. var formatText = function(data, numberOfLetters, minLength) {
  178. return (numberOfLetters >= 4) ? data : data.substr(0, Math.max(minLength, numberOfLetters));
  179. };
  180. var formatNumber = function(data, numberOfLetters) {
  181. var dataString = "" + data;
  182. // Pad with 0s as necessary
  183. return padWithZeroes(dataString, numberOfLetters);
  184. };
  185. var searchString = this.formatString;
  186. while ((result = regex.exec(searchString))) {
  187. var matchedString = result[0];
  188. var quotedString = result[1];
  189. var patternLetters = result[2];
  190. var otherLetters = result[3];
  191. var otherCharacters = result[4];
  192. // If the pattern matched is quoted string, output the text between the quotes
  193. if (quotedString) {
  194. if (quotedString == "''") {
  195. formattedString += "'";
  196. } else {
  197. formattedString += quotedString.substring(1, quotedString.length - 1);
  198. }
  199. } else if (otherLetters) {
  200. // Swallow non-pattern letters by doing nothing here
  201. } else if (otherCharacters) {
  202. // Simply output other characters
  203. formattedString += otherCharacters;
  204. } else if (patternLetters) {
  205. // Replace pattern letters
  206. var patternLetter = patternLetters.charAt(0);
  207. var numberOfLetters = patternLetters.length;
  208. var rawData = "";
  209. switch (patternLetter) {
  210. case "G":
  211. rawData = "AD";
  212. break;
  213. case "y":
  214. rawData = date.getFullYear();
  215. break;
  216. case "M":
  217. rawData = date.getMonth();
  218. break;
  219. case "w":
  220. rawData = date.getWeekInYear(this.getMinimalDaysInFirstWeek());
  221. break;
  222. case "W":
  223. rawData = date.getWeekInMonth(this.getMinimalDaysInFirstWeek());
  224. break;
  225. case "D":
  226. rawData = date.getDayInYear();
  227. break;
  228. case "d":
  229. rawData = date.getDate();
  230. break;
  231. case "F":
  232. rawData = 1 + Math.floor((date.getDate() - 1) / 7);
  233. break;
  234. case "E":
  235. rawData = dayNames[date.getDay()];
  236. break;
  237. case "a":
  238. rawData = (date.getHours() >= 12) ? "PM" : "AM";
  239. break;
  240. case "H":
  241. rawData = date.getHours();
  242. break;
  243. case "k":
  244. rawData = 1 + date.getHours();
  245. break;
  246. case "K":
  247. rawData = date.getHours() % 12;
  248. break;
  249. case "h":
  250. rawData = 1 + (date.getHours() % 12);
  251. break;
  252. case "m":
  253. rawData = date.getMinutes();
  254. break;
  255. case "s":
  256. rawData = date.getSeconds();
  257. break;
  258. case "S":
  259. rawData = date.getMilliseconds();
  260. break;
  261. case "Z":
  262. rawData = date.getTimezoneOffset(); // This returns the number of minutes since GMT was this time.
  263. break;
  264. }
  265. // Format the raw data depending on the type
  266. switch (types[patternLetter]) {
  267. case TEXT2:
  268. formattedString += formatText(rawData, numberOfLetters, 2);
  269. break;
  270. case TEXT3:
  271. formattedString += formatText(rawData, numberOfLetters, 3);
  272. break;
  273. case NUMBER:
  274. formattedString += formatNumber(rawData, numberOfLetters);
  275. break;
  276. case YEAR:
  277. if (numberOfLetters <= 2) {
  278. // Output a 2-digit year
  279. var dataString = "" + rawData;
  280. formattedString += dataString.substr(2, 2);
  281. } else {
  282. formattedString += formatNumber(rawData, numberOfLetters);
  283. }
  284. break;
  285. case MONTH:
  286. if (numberOfLetters >= 3) {
  287. formattedString += formatText(monthNames[rawData], numberOfLetters, numberOfLetters);
  288. } else {
  289. // NB. Months returned by getMonth are zero-based
  290. formattedString += formatNumber(rawData + 1, numberOfLetters);
  291. }
  292. break;
  293. case TIMEZONE:
  294. var isPositive = (rawData > 0);
  295. // The following line looks like a mistake but isn't
  296. // because of the way getTimezoneOffset measures.
  297. var prefix = isPositive ? "-" : "+";
  298. var absData = Math.abs(rawData);
  299. // Hours
  300. var hours = "" + Math.floor(absData / 60);
  301. hours = padWithZeroes(hours, 2);
  302. // Minutes
  303. var minutes = "" + (absData % 60);
  304. minutes = padWithZeroes(minutes, 2);
  305. formattedString += prefix + hours + minutes;
  306. break;
  307. }
  308. }
  309. searchString = searchString.substr(result.index + result[0].length);
  310. }
  311. return formattedString;
  312. };
  313. })();
  314. /* ------------------------------------------------------------------------- */
  315. var applicationStartDate = new Date();
  316. var uniqueId = "log4javascript_" + applicationStartDate.getTime() + "_" +
  317. Math.floor(Math.random() * 100000000);
  318. var emptyFunction = function() {};
  319. var newLine = "\r\n";
  320. // Create logging object; this will be assigned properties and returned
  321. log4javascript = {};
  322. log4javascript.version = "1.3.1";
  323. // Returns a nicely formatted representation of an error
  324. function getExceptionStringRep(ex) {
  325. if (ex) {
  326. var exStr = "Exception: ";
  327. if (ex.message) {
  328. exStr += ex.message;
  329. } else if (ex.description) {
  330. exStr += ex.description;
  331. }
  332. if (ex.lineNumber) {
  333. exStr += " on line number " + ex.lineNumber;
  334. }
  335. if (ex.fileName) {
  336. exStr += " in file " + ex.fileName;
  337. }
  338. if (showStackTraces && ex.stack) {
  339. exStr += newLine + "Stack trace:" + newLine + ex.stack;
  340. }
  341. return exStr;
  342. }
  343. return null;
  344. }
  345. function formatObjectExpansion(obj, depth, indentation) {
  346. var i, output, childDepth, childIndentation, childLines;
  347. if ((obj instanceof Array) && depth > 0) {
  348. if (!indentation) {
  349. indentation = "";
  350. }
  351. output = "[" + newLine;
  352. childDepth = depth - 1;
  353. childIndentation = indentation + " ";
  354. childLines = [];
  355. for (i = 0; i < obj.length; i++) {
  356. childLines.push(childIndentation + formatObjectExpansion(obj[i], childDepth, childIndentation));
  357. }
  358. output += childLines.join("," + newLine) + newLine + indentation + "]";
  359. return output;
  360. } else if (typeof obj == "object" && depth > 0) {
  361. if (!indentation) {
  362. indentation = "";
  363. }
  364. output = "" + "{" + newLine;
  365. childDepth = depth - 1;
  366. childIndentation = indentation + " ";
  367. childLines = [];
  368. for (i in obj) {
  369. childLines.push(childIndentation + i + ": " + formatObjectExpansion(obj[i], childDepth, childIndentation));
  370. }
  371. output += childLines.join("," + newLine) + newLine + indentation + "}";
  372. return output;
  373. } else if (typeof obj == "string") {
  374. return obj;
  375. } else {
  376. return obj.toString();
  377. }
  378. }
  379. function escapeNewLines(str) {
  380. return str.replace(/\r\n|\r|\n/g, "\\r\\n");
  381. }
  382. function urlEncode(str) {
  383. return escape(str).replace(/\+/g, "%2B").replace(/"/g, "%22").replace(/'/g, "%27").replace(/\//g, "%2F");
  384. }
  385. function bool(obj) {
  386. return Boolean(obj);
  387. }
  388. function array_remove(arr, val) {
  389. var index = -1;
  390. for (var i = 0; i < arr.length; i++) {
  391. if (arr[i] === val) {
  392. index = i;
  393. break;
  394. }
  395. }
  396. if (index >= 0) {
  397. arr.splice(index, 1);
  398. return true;
  399. } else {
  400. return false;
  401. }
  402. }
  403. function extractBooleanFromParam(param, defaultValue) {
  404. if (isUndefined(param)) {
  405. return defaultValue;
  406. } else {
  407. return bool(param);
  408. }
  409. }
  410. function extractStringFromParam(param, defaultValue) {
  411. if (isUndefined(param)) {
  412. return defaultValue;
  413. } else {
  414. return String(param);
  415. }
  416. }
  417. function extractIntFromParam(param, defaultValue) {
  418. if (isUndefined(param)) {
  419. return defaultValue;
  420. } else {
  421. try {
  422. var value = parseInt(param, 10);
  423. return isNaN(value) ? defaultValue : value;
  424. } catch (ex) {
  425. logLog.warn("Invalid int param " + param, ex);
  426. return defaultValue;
  427. }
  428. }
  429. }
  430. function extractFunctionFromParam(param, defaultValue) {
  431. if (typeof param == "function") {
  432. return param;
  433. } else {
  434. return defaultValue;
  435. }
  436. }
  437. /* --------------------------------------------------------------------- */
  438. // Simple logging for log4javascript itself
  439. var logLog = {
  440. quietMode: false,
  441. setQuietMode: function(quietMode) {
  442. this.quietMode = bool(quietMode);
  443. },
  444. numberOfErrors: 0,
  445. alertAllErrors: false,
  446. setAlertAllErrors: function(alertAllErrors) {
  447. this.alertAllErrors = alertAllErrors;
  448. },
  449. debug: function(message, exception) {
  450. },
  451. warn: function(message, exception) {
  452. },
  453. error: function(message, exception) {
  454. if (++this.numberOfErrors == 1 || this.alertAllErrors) {
  455. if (!this.quietMode) {
  456. var alertMessage = "log4javascript error: " + message;
  457. if (exception) {
  458. alertMessage += newLine + newLine + "Original error: " + getExceptionStringRep(exception);
  459. }
  460. alert(alertMessage);
  461. }
  462. }
  463. }
  464. };
  465. log4javascript.logLog = logLog;
  466. /* --------------------------------------------------------------------- */
  467. var errorListeners = [];
  468. log4javascript.addErrorListener = function(listener) {
  469. if (typeof listener == "function") {
  470. errorListeners.push(listener);
  471. } else {
  472. handleError("addErrorListener: listener supplied was not a function");
  473. }
  474. };
  475. log4javascript.removeErrorListener = function(listener) {
  476. array_remove(errorListeners, listener);
  477. };
  478. function handleError(message, exception) {
  479. logLog.error(message, exception);
  480. for (var i = 0; i < errorListeners.length; i++) {
  481. errorListeners[i](message, exception);
  482. }
  483. }
  484. /* --------------------------------------------------------------------- */
  485. var enabled = (typeof log4javascript_disabled != "undefined") &&
  486. log4javascript_disabled ? false : true;
  487. log4javascript.setEnabled = function(enable) {
  488. enabled = bool(enable);
  489. };
  490. log4javascript.isEnabled = function() {
  491. return enabled;
  492. };
  493. // This evaluates the given expression in the current scope, thus allowing
  494. // scripts to access private variables. Particularly useful for testing
  495. log4javascript.evalInScope = function(expr) {
  496. return eval(expr);
  497. };
  498. var showStackTraces = false;
  499. log4javascript.setShowStackTraces = function(show) {
  500. showStackTraces = bool(show);
  501. };
  502. /* --------------------------------------------------------------------- */
  503. function Logger(name) {
  504. this.name = name;
  505. var appenders = [];
  506. var loggerLevel = Level.DEBUG;
  507. // Create methods that use the appenders variable in this scope
  508. this.addAppender = function(appender) {
  509. if (appender instanceof log4javascript.Appender) {
  510. appenders.push(appender);
  511. } else {
  512. handleError("Logger.addAppender: appender supplied is not a subclass of Appender");
  513. }
  514. };
  515. this.removeAppender = function(appender) {
  516. array_remove(appenders, appender);
  517. };
  518. this.removeAllAppenders = function(appender) {
  519. appenders.length = 0;
  520. };
  521. this.log = function(level, message, exception) {
  522. if (level.isGreaterOrEqual(loggerLevel)) {
  523. var loggingEvent = new LoggingEvent(
  524. this, new Date(), level, message, exception);
  525. for (var i = 0; i < appenders.length; i++) {
  526. appenders[i].doAppend(loggingEvent);
  527. }
  528. }
  529. };
  530. this.setLevel = function(level) {
  531. loggerLevel = level;
  532. };
  533. this.getLevel = function() {
  534. return loggerLevel;
  535. };
  536. }
  537. Logger.prototype = {
  538. trace: function(message, exception) {
  539. this.log(Level.TRACE, message, exception);
  540. },
  541. debug: function(message, exception) {
  542. this.log(Level.DEBUG, message, exception);
  543. },
  544. info: function(message, exception) {
  545. this.log(Level.INFO, message, exception);
  546. },
  547. warn: function(message, exception) {
  548. this.log(Level.WARN, message, exception);
  549. },
  550. error: function(message, exception) {
  551. this.log(Level.ERROR, message, exception);
  552. },
  553. fatal: function(message, exception) {
  554. this.log(Level.FATAL, message, exception);
  555. }
  556. };
  557. Logger.prototype.trace.isEntryPoint = true;
  558. Logger.prototype.debug.isEntryPoint = true;
  559. Logger.prototype.info.isEntryPoint = true;
  560. Logger.prototype.warn.isEntryPoint = true;
  561. Logger.prototype.error.isEntryPoint = true;
  562. Logger.prototype.fatal.isEntryPoint = true;
  563. /* --------------------------------------------------------------------- */
  564. // Hashtable of loggers keyed by logger name
  565. var loggers = {};
  566. log4javascript.getLogger = function(loggerName) {
  567. // Use default logger if loggerName is not specified or invalid
  568. if (!(typeof loggerName == "string")) {
  569. loggerName = "[anonymous]";
  570. }
  571. // Create the logger for this name if it doesn't already exist
  572. if (!loggers[loggerName]) {
  573. loggers[loggerName] = new Logger(loggerName);
  574. }
  575. return loggers[loggerName];
  576. };
  577. var defaultLogger = null;
  578. log4javascript.getDefaultLogger = function() {
  579. if (!defaultLogger) {
  580. defaultLogger = log4javascript.getLogger("[default]");
  581. var a = new log4javascript.PopUpAppender();
  582. defaultLogger.addAppender(a);
  583. }
  584. return defaultLogger;
  585. };
  586. var nullLogger = null;
  587. log4javascript.getNullLogger = function() {
  588. if (!nullLogger) {
  589. nullLogger = log4javascript.getLogger("[null]");
  590. }
  591. return nullLogger;
  592. };
  593. /* --------------------------------------------------------------------- */
  594. var Level = function(level, name) {
  595. this.level = level;
  596. this.name = name;
  597. };
  598. Level.prototype = {
  599. toString: function() {
  600. return this.name;
  601. },
  602. equals: function(level) {
  603. return this.level == level.level;
  604. },
  605. isGreaterOrEqual: function(level) {
  606. return this.level >= level.level;
  607. }
  608. };
  609. Level.ALL = new Level(Number.MIN_VALUE, "ALL");
  610. Level.TRACE = new Level(10000, "TRACE");
  611. Level.DEBUG = new Level(20000, "DEBUG");
  612. Level.INFO = new Level(30000, "INFO");
  613. Level.WARN = new Level(40000, "WARN");
  614. Level.ERROR = new Level(50000, "ERROR");
  615. Level.FATAL = new Level(60000, "FATAL");
  616. Level.OFF = new Level(Number.MAX_VALUE, "OFF");
  617. log4javascript.Level = Level;
  618. /* --------------------------------------------------------------------- */
  619. var LoggingEvent = function(logger, timeStamp, level, message,
  620. exception) {
  621. this.logger = logger;
  622. this.timeStamp = timeStamp;
  623. this.timeStampInSeconds = Math.floor(timeStamp.getTime() / 1000);
  624. this.level = level;
  625. this.message = message;
  626. this.exception = exception;
  627. };
  628. LoggingEvent.prototype.getThrowableStrRep = function() {
  629. return this.exception ?
  630. getExceptionStringRep(this.exception) : "";
  631. };
  632. log4javascript.LoggingEvent = LoggingEvent;
  633. /* --------------------------------------------------------------------- */
  634. // Layout "abstract class"
  635. var Layout = function() {
  636. };
  637. Layout.prototype = {
  638. defaults: {
  639. loggerKey: "logger",
  640. timeStampKey: "timestamp",
  641. levelKey: "level",
  642. messageKey: "message",
  643. exceptionKey: "exception",
  644. urlKey: "url"
  645. },
  646. loggerKey: "logger",
  647. timeStampKey: "timestamp",
  648. levelKey: "level",
  649. messageKey: "message",
  650. exceptionKey: "exception",
  651. urlKey: "url",
  652. batchHeader: "",
  653. batchFooter: "",
  654. batchSeparator: "",
  655. format: function(loggingEvent) {
  656. handleError("Layout.format: layout supplied has no format() method");
  657. },
  658. ignoresThrowable: function() {
  659. handleError("Layout.ignoresThrowable: layout supplied has no ignoresThrowable() method");
  660. },
  661. getContentType: function() {
  662. return "text/plain";
  663. },
  664. allowBatching: function() {
  665. return true;
  666. },
  667. getDataValues: function(loggingEvent) {
  668. var dataValues = [
  669. [this.loggerKey, loggingEvent.logger.name],
  670. [this.timeStampKey, loggingEvent.timeStampInSeconds],
  671. [this.levelKey, loggingEvent.level.name],
  672. [this.urlKey, window.location.href],
  673. [this.messageKey, loggingEvent.message]
  674. ];
  675. if (loggingEvent.exception) {
  676. dataValues.push([this.exceptionKey, getExceptionStringRep(loggingEvent.exception)]);
  677. }
  678. if (this.hasCustomFields()) {
  679. for (var i = 0; i < this.customFields.length; i++) {
  680. dataValues.push([this.customFields[i].name, this.customFields[i].value]);
  681. }
  682. }
  683. return dataValues;
  684. },
  685. setKeys: function(loggerKey, timeStampKey, levelKey, messageKey,
  686. exceptionKey, urlKey) {
  687. this.loggerKey = extractStringFromParam(loggerKey, this.defaults.loggerKey);
  688. this.timeStampKey = extractStringFromParam(timeStampKey, this.defaults.timeStampKey);
  689. this.levelKey = extractStringFromParam(levelKey, this.defaults.levelKey);
  690. this.messageKey = extractStringFromParam(messageKey, this.defaults.messageKey);
  691. this.exceptionKey = extractStringFromParam(exceptionKey, this.defaults.exceptionKey);
  692. this.urlKey = extractStringFromParam(urlKey, this.defaults.urlKey);
  693. },
  694. setCustomField: function(name, value) {
  695. var fieldUpdated = false;
  696. for (var i = 0; i < this.customFields.length; i++) {
  697. if (this.customFields[i].name === name) {
  698. this.customFields[i].value = value;
  699. fieldUpdated = true;
  700. }
  701. }
  702. if (!fieldUpdated) {
  703. this.customFields.push({"name": name, "value": value});
  704. }
  705. },
  706. hasCustomFields: function() {
  707. return (this.customFields.length > 0);
  708. }
  709. };
  710. log4javascript.Layout = Layout;
  711. /* --------------------------------------------------------------------- */
  712. // SimpleLayout
  713. var SimpleLayout = function() {
  714. this.customFields = [];
  715. };
  716. SimpleLayout.prototype = new Layout();
  717. SimpleLayout.prototype.format = function(loggingEvent) {
  718. return loggingEvent.level.name + " - " + loggingEvent.message;
  719. };
  720. SimpleLayout.prototype.ignoresThrowable = function(loggingEvent) {
  721. return true;
  722. };
  723. log4javascript.SimpleLayout = SimpleLayout;
  724. /* --------------------------------------------------------------------- */
  725. // NullLayout
  726. var NullLayout = function() {
  727. this.customFields = [];
  728. };
  729. NullLayout.prototype = new Layout();
  730. NullLayout.prototype.format = function(loggingEvent) {
  731. return loggingEvent.message;
  732. };
  733. NullLayout.prototype.ignoresThrowable = function(loggingEvent) {
  734. return true;
  735. };
  736. log4javascript.NullLayout = NullLayout;
  737. /* --------------------------------------------------------------------- */
  738. // XmlLayout
  739. var XmlLayout = function() {
  740. this.customFields = [];
  741. };
  742. XmlLayout.prototype = new Layout();
  743. XmlLayout.prototype.getContentType = function() {
  744. return "text/xml";
  745. };
  746. XmlLayout.prototype.escapeCdata = function(str) {
  747. return str.replace(/\]\]>/, "]]>]]&gt;<![CDATA[");
  748. };
  749. XmlLayout.prototype.format = function(loggingEvent) {
  750. var str = "<log4javascript:event logger=\"" + loggingEvent.logger.name +
  751. "\" timestamp=\"" + loggingEvent.timeStampInSeconds +
  752. "\" level=\"" + loggingEvent.level.name +
  753. "\">" + newLine + "<log4javascript:message><![CDATA[" +
  754. this.escapeCdata(loggingEvent.message.toString()) +
  755. "]]></log4javascript:message>" + newLine;
  756. if (this.hasCustomFields()) {
  757. for (var i = 0; i < this.customFields.length; i++) {
  758. str += "<log4javascript:customfield name=\"" +
  759. this.customFields[i].name + "\"><![CDATA[" +
  760. this.customFields[i].value.toString() +
  761. "]]></log4javascript:customfield>" + newLine;
  762. }
  763. }
  764. if (loggingEvent.exception) {
  765. str += "<log4javascript:exception><![CDATA[" +
  766. getExceptionStringRep(loggingEvent.exception) +
  767. "]]></log4javascript:exception>" + newLine;
  768. }
  769. str += "</log4javascript:event>" + newLine + newLine;
  770. return str;
  771. };
  772. XmlLayout.prototype.ignoresThrowable = function(loggingEvent) {
  773. return false;
  774. };
  775. log4javascript.XmlLayout = XmlLayout;
  776. /* --------------------------------------------------------------------- */
  777. // JsonLayout
  778. var JsonLayout = function(readable, loggerKey, timeStampKey,
  779. levelKey, messageKey, exceptionKey, urlKey) {
  780. this.readable = bool(readable);
  781. this.batchHeader = this.readable ? "[" + newLine : "[";
  782. this.batchFooter = this.readable ? "]" + newLine : "]";
  783. this.batchSeparator = this.readable ? "," + newLine : ",";
  784. this.setKeys(loggerKey, timeStampKey, levelKey, messageKey,
  785. exceptionKey, urlKey);
  786. this.propertySeparator = this.readable ? ", " : ",";
  787. this.colon = this.readable ? ": " : ":";
  788. this.customFields = [];
  789. };
  790. JsonLayout.prototype = new Layout();
  791. JsonLayout.prototype.setReadable = function(readable) {
  792. this.readable = bool(readable);
  793. };
  794. JsonLayout.prototype.isReadable = function() {
  795. return this.readable;
  796. };
  797. JsonLayout.prototype.format = function(loggingEvent) {
  798. var dataValues = this.getDataValues(loggingEvent);
  799. var str = "{";
  800. if (this.readable) {
  801. str += newLine;
  802. }
  803. for (var i = 0; i < dataValues.length; i++) {
  804. if (this.readable) {
  805. str += "\t";
  806. }
  807. // Check the type of the data value to decide whether quotation marks
  808. // are required
  809. var valType = typeof dataValues[i][1];
  810. var val = (valType != "number" && valType != "boolean") ?
  811. "\"" + escapeNewLines(dataValues[i][1].toString().replace(/\"/g, "\\\"")) + "\"" :
  812. dataValues[i][1];
  813. str += "\"" + dataValues[i][0] + "\"" + this.colon + val;
  814. if (i < dataValues.length - 1) {
  815. str += this.propertySeparator;
  816. }
  817. if (this.readable) {
  818. str += newLine;
  819. }
  820. }
  821. str += "}";
  822. if (this.readable) {
  823. str += newLine;
  824. }
  825. return str;
  826. };
  827. JsonLayout.prototype.ignoresThrowable = function(loggingEvent) {
  828. return false;
  829. };
  830. log4javascript.JsonLayout = JsonLayout;
  831. /* --------------------------------------------------------------------- */
  832. // HttpPostDataLayout
  833. var HttpPostDataLayout = function(loggerKey, timeStampKey,
  834. levelKey, messageKey, exceptionKey, urlKey) {
  835. this.setKeys(loggerKey, timeStampKey, levelKey, messageKey,
  836. exceptionKey, urlKey);
  837. this.customFields = [];
  838. };
  839. HttpPostDataLayout.prototype = new Layout();
  840. // Disable batching
  841. HttpPostDataLayout.prototype.allowBatching = function() {
  842. return false;
  843. };
  844. HttpPostDataLayout.prototype.format = function(loggingEvent) {
  845. var dataValues = this.getDataValues(loggingEvent);
  846. var queryBits = [];
  847. for (var i = 0; i < dataValues.length; i++) {
  848. queryBits.push(urlEncode(dataValues[i][0]) + "=" + urlEncode(dataValues[i][1]));
  849. }
  850. return queryBits.join("&");
  851. };
  852. HttpPostDataLayout.prototype.ignoresThrowable = function(loggingEvent) {
  853. return false;
  854. };
  855. log4javascript.HttpPostDataLayout = HttpPostDataLayout;
  856. /* --------------------------------------------------------------------- */
  857. // PatternLayout
  858. var PatternLayout = function(pattern) {
  859. if (pattern) {
  860. this.pattern = pattern;
  861. } else {
  862. this.pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
  863. }
  864. this.customFields = [];
  865. };
  866. PatternLayout.TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n";
  867. PatternLayout.DEFAULT_CONVERSION_PATTERN = "%m%n";
  868. PatternLayout.ISO8601_DATEFORMAT = "yyyy-MM-dd HH:mm:ss,SSS";
  869. PatternLayout.DATETIME_DATEFORMAT = "dd MMM yyyy HH:mm:ss,SSS";
  870. PatternLayout.ABSOLUTETIME_DATEFORMAT = "HH:mm:ss,SSS";
  871. PatternLayout.prototype = new Layout();
  872. PatternLayout.prototype.format = function(loggingEvent) {
  873. var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([cdfmMnpr%])(\{([^\}]+)\})?|([^%]+)/;
  874. var formattedString = "";
  875. var result;
  876. var searchString = this.pattern;
  877. // Cannot use regex global flag since it doesn't work with exec in IE5
  878. while ((result = regex.exec(searchString))) {
  879. var matchedString = result[0];
  880. var padding = result[1];
  881. var truncation = result[2];
  882. var conversionCharacter = result[3];
  883. var specifier = result[5];
  884. var text = result[6];
  885. // Check if the pattern matched was just normal text
  886. if (text) {
  887. formattedString += "" + text;
  888. } else {
  889. // Create a raw replacement string based on the conversion
  890. // character and specifier
  891. var replacement = "";
  892. switch(conversionCharacter) {
  893. case "c": // Logger name
  894. var loggerName = loggingEvent.logger.name;
  895. if (specifier) {
  896. var precision = parseInt(specifier, 10);
  897. var loggerNameBits = loggingEvent.logger.name.split(".");
  898. if (precision >= loggerNameBits.length) {
  899. replacement = loggerName;
  900. } else {
  901. replacement = loggerNameBits.slice(loggerNameBits.length - precision).join(".");
  902. }
  903. } else {
  904. replacement = loggerName;
  905. }
  906. break;
  907. case "d": // Date
  908. var dateFormat = PatternLayout.ISO8601_DATEFORMAT;
  909. if (specifier) {
  910. dateFormat = specifier;
  911. // Pick up special cases
  912. if (dateFormat == "ISO8601") {
  913. dateFormat = PatternLayout.ISO8601_DATEFORMAT;
  914. } else if (dateFormat == "ABSOLUTE") {
  915. dateFormat = PatternLayout.ABSOLUTETIME_DATEFORMAT;
  916. } else if (dateFormat == "DATE") {
  917. dateFormat = PatternLayout.DATETIME_DATEFORMAT;
  918. }
  919. }
  920. // Format the date
  921. replacement = (new SimpleDateFormat(dateFormat)).format(loggingEvent.timeStamp);
  922. break;
  923. case "f": // Custom field
  924. if (this.hasCustomFields()) {
  925. var fieldIndex = 0;
  926. if (specifier) {
  927. fieldIndex = parseInt(specifier, 10);
  928. if (isNaN(fieldIndex)) {
  929. handleError("PatternLayout.format: invalid specifier '" +
  930. specifier + "' for conversion character 'f' - should be a number");
  931. } else if (fieldIndex === 0) {
  932. handleError("PatternLayout.format: invalid specifier '" +
  933. specifier + "' for conversion character 'f' - must be greater than zero");
  934. } else if (fieldIndex > this.customFields.length) {
  935. handleError("PatternLayout.format: invalid specifier '" +
  936. specifier + "' for conversion character 'f' - there aren't that many custom fields");
  937. } else {
  938. fieldIndex = fieldIndex - 1;
  939. }
  940. }
  941. replacement = this.customFields[fieldIndex].value;
  942. }
  943. break;
  944. case "m": // Message
  945. if (specifier) {
  946. var depth = parseInt(specifier, 10);
  947. if (isNaN(depth)) {
  948. handleError("PatternLayout.format: invalid specifier '" +
  949. specifier + "' for conversion character 'm' - should be a number");
  950. replacement = loggingEvent.message;
  951. } else {
  952. replacement = formatObjectExpansion(loggingEvent.message, depth);
  953. }
  954. } else {
  955. replacement = loggingEvent.message;
  956. }
  957. break;
  958. case "n": // New line
  959. replacement = newLine;
  960. break;
  961. case "p": // Level
  962. replacement = loggingEvent.level.name;
  963. break;
  964. case "r": // Milliseconds since log4javascript startup
  965. replacement = "" + loggingEvent.timeStamp.getDifference(applicationStartDate);
  966. break;
  967. case "%": // Literal % sign
  968. replacement = "%";
  969. break;
  970. default:
  971. replacement = matchedString;
  972. break;
  973. }
  974. // Format the replacement according to any padding or
  975. // truncation specified
  976. var len;
  977. // First, truncation
  978. if (truncation) {
  979. len = parseInt(truncation.substr(1), 10);
  980. var strLen = replacement.length;
  981. if (len < strLen) {
  982. replacement = replacement.substring(strLen - len, strLen);
  983. }
  984. }
  985. // Next, padding
  986. if (padding) {
  987. if (padding.charAt(0) == "-") {
  988. len = parseInt(padding.substr(1), 10);
  989. // Right pad with spaces
  990. while (replacement.length < len) {
  991. replacement += " ";
  992. }
  993. } else {
  994. len = parseInt(padding, 10);
  995. // Left pad with spaces
  996. while (replacement.length < len) {
  997. replacement = " " + replacement;
  998. }
  999. }
  1000. }
  1001. formattedString += replacement;
  1002. }
  1003. searchString = searchString.substr(result.index + result[0].length);
  1004. }
  1005. return formattedString;
  1006. };
  1007. PatternLayout.prototype.ignoresThrowable = function(loggingEvent) {
  1008. return true;
  1009. };
  1010. log4javascript.PatternLayout = PatternLayout;
  1011. /* --------------------------------------------------------------------- */
  1012. // Appender "abstract class"
  1013. var Appender = function() {};
  1014. // Performs threshold checks before delegating actual logging to the
  1015. // subclass's specific append method.
  1016. Appender.prototype = {
  1017. layout: new PatternLayout(),
  1018. threshold: Level.ALL,
  1019. doAppend: function(loggingEvent) {
  1020. if (enabled && loggingEvent.level.level >= this.threshold.level) {
  1021. this.append(loggingEvent);
  1022. }
  1023. },
  1024. append: function(loggingEvent) {},
  1025. setLayout: function(layout) {
  1026. if (layout instanceof Layout) {
  1027. this.layout = layout;
  1028. } else {
  1029. handleError("Appender.setLayout: layout supplied to " +
  1030. this.toString() + " is not a subclass of Layout");
  1031. }
  1032. },
  1033. getLayout: function() {
  1034. return this.layout;
  1035. },
  1036. setThreshold: function(threshold) {
  1037. if (threshold instanceof Level) {
  1038. this.threshold = threshold;
  1039. } else {
  1040. handleError("Appender.setThreshold: threshold supplied to " +
  1041. this.toString() + " is not a subclass of Level");
  1042. }
  1043. },
  1044. getThreshold: function() {
  1045. return this.threshold;
  1046. },
  1047. toString: function() {
  1048. return "[Base Appender]";
  1049. }
  1050. };
  1051. log4javascript.Appender = Appender;
  1052. /* --------------------------------------------------------------------- */
  1053. // AlertAppender
  1054. var AlertAppender = function(layout) {
  1055. if (layout) {
  1056. this.setLayout(layout);
  1057. }
  1058. };
  1059. AlertAppender.prototype = new Appender();
  1060. AlertAppender.prototype.layout = new SimpleLayout();
  1061. AlertAppender.prototype.append = function(loggingEvent) {
  1062. var formattedMessage = this.getLayout().format(loggingEvent);
  1063. if (this.getLayout().ignoresThrowable()) {
  1064. formattedMessage += loggingEvent.getThrowableStrRep();
  1065. }
  1066. alert(formattedMessage);
  1067. };
  1068. AlertAppender.prototype.toString = function() {
  1069. return "[AlertAppender]";
  1070. };
  1071. log4javascript.AlertAppender = AlertAppender;
  1072. /* --------------------------------------------------------------------- */
  1073. // AjaxAppender
  1074. var AjaxAppender = function(url, layout, timed, waitForResponse,
  1075. batchSize, timerInterval, requestSuccessCallback, failCallback) {
  1076. var appender = this;
  1077. var isSupported = true;
  1078. if (!url) {
  1079. handleError("AjaxAppender: URL must be specified in constructor");
  1080. isSupported = false;
  1081. }
  1082. timed = extractBooleanFromParam(timed, this.defaults.timed);
  1083. waitForResponse = extractBooleanFromParam(waitForResponse, this.defaults.waitForResponse);
  1084. batchSize = extractIntFromParam(batchSize, this.defaults.batchSize);
  1085. timerInterval = extractIntFromParam(timerInterval, this.defaults.timerInterval);
  1086. requestSuccessCallback = extractFunctionFromParam(requestSuccessCallback, this.defaults.requestSuccessCallback);
  1087. failCallback = extractFunctionFromParam(failCallback, this.defaults.failCallback);
  1088. var sessionId = null;
  1089. var queuedLoggingEvents = [];
  1090. var queuedRequests = [];
  1091. var sending = false;
  1092. var initialized = false;
  1093. // Configuration methods. The function scope is used to prevent
  1094. // direct alteration to the appender configuration properties.
  1095. function checkCanConfigure(configOptionName) {
  1096. if (initialized) {
  1097. handleError("AjaxAppender: configuration option '" + configOptionName + "' may not be set after the appender has been initialized");
  1098. return false;
  1099. }
  1100. return true;
  1101. }
  1102. this.getSessionId = function() { return sessionId; };
  1103. this.setSessionId = function(sessionIdParam) {
  1104. sessionId = extractStringFromParam(sessionIdParam, null);
  1105. this.layout.setCustomField("sessionid", sessionId);
  1106. };
  1107. this.setLayout = function(layout) {
  1108. if (checkCanConfigure("layout")) {
  1109. this.layout = layout;
  1110. // Set the session id as a custom field on the layout, if not already present
  1111. if (sessionId !== null) {
  1112. this.setSessionId(sessionId);
  1113. }
  1114. }
  1115. };
  1116. if (layout) {
  1117. this.setLayout(layout);
  1118. }
  1119. this.isTimed = function() { return timed; };
  1120. this.setTimed = function(timedParam) {
  1121. if (checkCanConfigure("timed")) {
  1122. timed = bool(timedParam);
  1123. }
  1124. };
  1125. this.getTimerInterval = function() { return timerInterval; };
  1126. this.setTimerInterval = function(timerIntervalParam) {
  1127. if (checkCanConfigure("timerInterval")) {
  1128. timerInterval = extractIntFromParam(timerIntervalParam, timerInterval);
  1129. }
  1130. };
  1131. this.isWaitForResponse = function() { return waitForResponse; };
  1132. this.setWaitForResponse = function(waitForResponseParam) {
  1133. if (checkCanConfigure("waitForResponse")) {
  1134. waitForResponse = bool(waitForResponseParam);
  1135. }
  1136. };
  1137. this.getBatchSize = function() { return batchSize; };
  1138. this.setBatchSize = function(batchSizeParam) {
  1139. if (checkCanConfigure("batchSize")) {
  1140. batchSize = extractIntFromParam(batchSizeParam, batchSize);
  1141. }
  1142. };
  1143. this.setRequestSuccessCallback = function(requestSuccessCallbackParam) {
  1144. requestSuccessCallback = extractFunctionFromParam(requestSuccessCallbackParam, requestSuccessCallback);
  1145. };
  1146. this.setFailCallback = function(failCallbackParam) {
  1147. failCallback = extractFunctionFromParam(failCallbackParam, failCallback);
  1148. };
  1149. // Internal functions
  1150. function sendAll() {
  1151. if (isSupported && enabled) {
  1152. sending = true;
  1153. var currentRequestBatch;
  1154. if (waitForResponse) {
  1155. // Send the first request then use this function as the callback once
  1156. // the response comes back
  1157. if (queuedRequests.length > 0) {
  1158. currentRequestBatch = queuedRequests.shift();
  1159. sendRequest(preparePostData(currentRequestBatch), sendAll);
  1160. } else {
  1161. sending = false;
  1162. if (timed) {
  1163. scheduleSending();
  1164. }
  1165. }
  1166. } else {
  1167. // Rattle off all the requests without waiting to see the response
  1168. while ((currentRequestBatch = queuedRequests.shift())) {
  1169. sendRequest(preparePostData(currentRequestBatch));
  1170. }
  1171. sending = false;
  1172. if (timed) {
  1173. scheduleSending();
  1174. }
  1175. }
  1176. }
  1177. }
  1178. this.sendAll = sendAll;
  1179. function preparePostData(batchedLoggingEvents) {
  1180. // Format the logging events
  1181. var formattedMessages = [];
  1182. var currentLoggingEvent;
  1183. var postData = "";
  1184. while ((currentLoggingEvent = batchedLoggingEvents.shift())) {
  1185. var currentFormattedMessage = appender.getLayout().format(currentLoggingEvent);
  1186. if (appender.getLayout().ignoresThrowable()) {
  1187. currentFormattedMessage += loggingEvent.getThrowableStrRep();
  1188. }
  1189. formattedMessages.push(currentFormattedMessage);
  1190. }
  1191. // Create the post data string
  1192. if (batchedLoggingEvents.length == 1) {
  1193. postData = formattedMessages.join("");
  1194. } else {
  1195. postData = appender.getLayout().batchHeader +
  1196. formattedMessages.join(appender.getLayout().batchSeparator) +
  1197. appender.getLayout().batchFooter;
  1198. }
  1199. return postData;
  1200. }
  1201. function scheduleSending() {
  1202. setTimeout(sendAll, timerInterval);
  1203. }
  1204. function getXmlHttp() {
  1205. var xmlHttp = null;
  1206. if (typeof XMLHttpRequest == "object" || typeof XMLHttpRequest == "function") {
  1207. xmlHttp = new XMLHttpRequest();
  1208. } else {
  1209. try {
  1210. xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
  1211. } catch (e2){
  1212. try {
  1213. xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
  1214. } catch (e3) {
  1215. var msg = "AjaxAppender: could not create XMLHttpRequest object. AjaxAppender disabled";
  1216. handleError(msg);
  1217. isSupported = false;
  1218. if (failCallback) {
  1219. failCallback(msg);
  1220. }
  1221. }
  1222. }
  1223. }
  1224. return xmlHttp;
  1225. }
  1226. function sendRequest(postData, successCallback) {
  1227. try {
  1228. var xmlHttp = getXmlHttp();
  1229. if (isSupported) {
  1230. if (xmlHttp.overrideMimeType) {
  1231. xmlHttp.overrideMimeType(appender.getLayout().getContentType());
  1232. }
  1233. xmlHttp.onreadystatechange = function() {
  1234. if (xmlHttp.readyState == 4) {
  1235. var success = (isUndefined(xmlHttp.status) || xmlHttp.status === 0 ||
  1236. (xmlHttp.status >= 200 && xmlHttp.status < 300));
  1237. if (success) {
  1238. if (requestSuccessCallback) {
  1239. requestSuccessCallback(xmlHttp);
  1240. }
  1241. if (successCallback) {
  1242. successCallback(xmlHttp);
  1243. }
  1244. } else {
  1245. var msg = "AjaxAppender.append: XMLHttpRequest request to URL " +
  1246. url + " returned status code " + xmlHttp.status;
  1247. handleError(msg);
  1248. if (failCallback) {
  1249. failCallback(msg);
  1250. }
  1251. }
  1252. xmlHttp.onreadystatechange = emptyFunction;
  1253. xmlHttp = null;
  1254. }
  1255. };
  1256. xmlHttp.open("POST", url, true);
  1257. try {
  1258. xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  1259. } catch (headerEx) {
  1260. var msg = "AjaxAppender.append: your browser's XMLHttpRequest implementation" +
  1261. " does not support setRequestHeader, therefore cannot post data. AjaxAppender disabled";
  1262. handleError(msg);
  1263. isSupported = false;
  1264. if (failCallback) {
  1265. failCallback(msg);
  1266. }
  1267. return;
  1268. }
  1269. xmlHttp.send(postData);
  1270. }
  1271. } catch (ex) {
  1272. var msg = "AjaxAppender.append: error sending log message to " + url;
  1273. handleError(msg, ex);
  1274. if (failCallback) {
  1275. failCallback(msg + ". Details: " + getExceptionStringRep(ex));
  1276. }
  1277. }
  1278. }
  1279. this.append = function(loggingEvent) {
  1280. if (isSupported) {
  1281. if (!initialized) {
  1282. init();
  1283. }
  1284. queuedLoggingEvents.push(loggingEvent);
  1285. var actualBatchSize = this.getLayout().allowBatching() ? batchSize : 1;
  1286. if (queuedLoggingEvents.length >= actualBatchSize) {
  1287. var currentLoggingEvent;
  1288. var postData = "";
  1289. var batchedLoggingEvents = [];
  1290. while ((currentLoggingEvent = queuedLoggingEvents.shift())) {
  1291. batchedLoggingEvents.push(currentLoggingEvent);
  1292. }
  1293. // Queue this batch of log entries
  1294. queuedRequests.push(batchedLoggingEvents);
  1295. // If using a timer, the queue of requests will be processed by the
  1296. // timer function, so nothing needs to be done here.
  1297. if (!timed) {
  1298. if (!waitForResponse || (waitForResponse && !sending)) {
  1299. sendAll();
  1300. }
  1301. }
  1302. }
  1303. }
  1304. };
  1305. function init() {
  1306. initialized = true;
  1307. // Start timer
  1308. if (timed) {
  1309. scheduleSending();
  1310. }
  1311. }
  1312. };
  1313. AjaxAppender.prototype = new Appender();
  1314. AjaxAppender.prototype.defaults = {
  1315. waitForResponse: false,
  1316. timed: false,
  1317. timerInterval: 1000,
  1318. batchSize: 1,
  1319. requestSuccessCallback: null,
  1320. failCallback: null
  1321. };
  1322. AjaxAppender.prototype.layout = new HttpPostDataLayout();
  1323. AjaxAppender.prototype.toString = function() {
  1324. return "[AjaxAppender]";
  1325. };
  1326. log4javascript.AjaxAppender = AjaxAppender;
  1327. /* --------------------------------------------------------------------- */
  1328. // BaseConsoleAppender
  1329. // Create an anonymous function to protect base console methods
  1330. (function() {
  1331. var getConsoleHtmlLines = function() {
  1332. return [
  1333. '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
  1334. '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">',
  1335. ' <head>',
  1336. ' <title>log4javascript</title>',
  1337. ' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',
  1338. ' <script type="text/javascript">',
  1339. ' //<![CDATA[',
  1340. ' var loggingEnabled = true;',
  1341. '',
  1342. ' function toggleLoggingEnabled() {',
  1343. ' setLoggingEnabled($("enableLogging").checked);',
  1344. ' }',
  1345. '',
  1346. ' function setLoggingEnabled(enable) {',
  1347. ' loggingEnabled = enable;',
  1348. ' }',
  1349. '',
  1350. ' var newestAtTop = false;',
  1351. '',
  1352. ' function setNewestAtTop(isNewestAtTop) {',
  1353. ' var oldNewestAtTop = newestAtTop;',
  1354. ' newestAtTop = Boolean(isNewestAtTop);',
  1355. ' if (oldNewestAtTop != newestAtTop) {',
  1356. ' // Invert the order of the log entries',
  1357. ' var lc = getLogContainer();',
  1358. ' var numberOfEntries = lc.childNodes.length;',
  1359. ' var node = null;',
  1360. '',
  1361. ' // Remove all the log container nodes',
  1362. ' var logContainerChildNodes = [];',
  1363. ' while ((node = lc.firstChild)) {',
  1364. ' lc.removeChild(node);',
  1365. ' logContainerChildNodes.push(node);',
  1366. ' }',
  1367. '',
  1368. ' // Put them all back in reverse order',
  1369. ' while ((node = logContainerChildNodes.pop())) {',
  1370. ' lc.appendChild(node);',
  1371. ' }',
  1372. '',
  1373. ' // Reassemble the matches array',
  1374. ' if (currentSearch) {',
  1375. ' var currentMatch = currentSearch.matches[currentMatchIndex];',
  1376. ' var matchIndex = 0;',
  1377. ' var matches = [];',
  1378. ' var actOnLogEntry = function(logEntry) {',
  1379. ' var logEntryMatches = logEntry.getSearchMatches();',
  1380. ' for (var i = 0; i < logEntryMatches.length; i++) {',
  1381. ' matches[matchIndex] = logEntryMatches[i];',
  1382. ' if (currentMatch && logEntryMatches[i].equals(currentMatch)) {',
  1383. ' currentMatchIndex = matchIndex;',
  1384. ' }',
  1385. ' matchIndex++;',
  1386. ' }',
  1387. ' };',
  1388. ' var i;',
  1389. ' if (newestAtTop) {',
  1390. ' for (i = logEntries.length - 1; i >= 0; i--) {',
  1391. ' actOnLogEntry(logEntries[i]);',
  1392. ' }',
  1393. ' } else {',
  1394. ' for (i = 0; i < logEntries.length; i++) {',
  1395. ' actOnLogEntry(logEntries[i]);',
  1396. ' }',
  1397. ' }',
  1398. ' currentSearch.matches = matches;',
  1399. ' if (currentMatch) {',
  1400. ' currentMatch.setCurrent();',
  1401. ' }',
  1402. ' } else if (scrollToLatest) {',
  1403. ' doScrollToLatest();',
  1404. ' }',
  1405. ' }',
  1406. ' $("newestAtTop").checked = isNewestAtTop;',
  1407. ' }',
  1408. '',
  1409. ' function toggleNewestAtTop() {',
  1410. ' var isNewestAtTop = $("newestAtTop").checked;',
  1411. ' setNewestAtTop(isNewestAtTop);',
  1412. ' }',
  1413. '',
  1414. ' var scrollToLatest = true;',
  1415. '',
  1416. ' function setScrollToLatest(isScrollToLatest) {',
  1417. ' scrollToLatest = isScrollToLatest;',
  1418. ' if (scrollToLatest) {',
  1419. ' doScrollToLatest();',
  1420. ' }',
  1421. ' $("scrollToLatest").checked = isScrollToLatest;',
  1422. ' }',
  1423. '',
  1424. ' function toggleScrollToLatest() {',
  1425. ' var isScrollToLatest = $("scrollToLatest").checked;',
  1426. ' setScrollToLatest(isScrollToLatest);',
  1427. ' }',
  1428. '',
  1429. ' function doScrollToLatest() {',
  1430. ' var l = getLogContainer();',
  1431. ' if (typeof l.scrollTop != "undefined") {',
  1432. ' if (newestAtTop) {',
  1433. ' l.scrollTop = 0;',
  1434. ' } else {',
  1435. ' var latestLogEntry = l.lastChild;',
  1436. ' if (latestLogEntry) {',
  1437. ' l.scrollTop = l.scrollHeight;',
  1438. ' }',
  1439. ' }',
  1440. ' }',
  1441. ' }',
  1442. '',
  1443. ' var maxMessages = null;',
  1444. '',
  1445. ' function setMaxMessages(max) {',
  1446. ' maxMessages = max;',
  1447. ' pruneLogEntries();',
  1448. ' }',
  1449. '',
  1450. ' var logQueuedEventsTimer = null;',
  1451. ' var logEntries = [];',
  1452. ' var isCssWrapSupported;',
  1453. ' var renderDelay = 100;',
  1454. '',
  1455. ' function log(logLevel, formattedMessage) {',
  1456. ' if (loggingEnabled) {',
  1457. ' var logEntry = new LogEntry(logLevel, formattedMessage);',
  1458. ' logEntries.push(logEntry);',
  1459. ' if (loaded) {',
  1460. ' if (logQueuedEventsTimer !== null) {',
  1461. ' clearTimeout(logQueuedEventsTimer);',
  1462. ' }',
  1463. ' setTimeout(renderQueuedLogEntries, renderDelay);',
  1464. ' }',
  1465. ' }',
  1466. ' }',
  1467. '',
  1468. ' function renderQueuedLogEntries() {',
  1469. ' logQueuedEventsTimer = null;',
  1470. ' var pruned = pruneLogEntries();',
  1471. '',
  1472. ' // Render any unrendered log entries and apply the current search to them',
  1473. ' var initiallyHasMatches = currentSearch ? currentSearch.hasMatches() : false;',
  1474. ' for (var i = 0; i < logEntries.length; i++) {',
  1475. ' if (!logEntries[i].isRendered) {',
  1476. ' logEntries[i].render();',
  1477. ' logEntries[i].appendToLog();',
  1478. ' if (currentSearch) {',
  1479. ' currentSearch.applyTo(logEntries[i]);',
  1480. ' }',
  1481. ' }',
  1482. ' }',
  1483. ' if (currentSearch) {',
  1484. ' if (pruned) {',
  1485. ' if (currentSearch.hasMatches()) {',
  1486. ' if (currentMatchIndex === null) {',
  1487. ' setCurrentMatchIndex(0);',
  1488. ' }',
  1489. ' displayMatches();',
  1490. ' } else {',
  1491. ' displayNoMatches();',
  1492. ' }',
  1493. ' } else if (!initiallyHasMatches && currentSearch.hasMatches()) {',
  1494. ' setCurrentMatchIndex(0);',
  1495. ' displayMatches();',
  1496. ' }',
  1497. ' }',
  1498. ' if (scrollToLatest) {',
  1499. ' doScrollToLatest();',
  1500. ' }',
  1501. ' }',
  1502. '',
  1503. ' function pruneLogEntries() {',
  1504. ' if ((maxMessages !== null) && (logEntries.length > maxMessages)) {',
  1505. ' var numberToDelete = logEntries.length - maxMessages;',
  1506. ' for (var i = 0; i < numberToDelete; i++) {',
  1507. ' logEntries[i].remove();',
  1508. ' }',
  1509. ' logEntries = array_removeFromStart(logEntries, numberToDelete);',
  1510. ' if (currentSearch) {',
  1511. ' currentSearch.removePrunedMatches();',
  1512. ' }',
  1513. ' return true;',
  1514. ' }',
  1515. ' return false;',
  1516. ' }',
  1517. '',
  1518. ' function LogEntry(level, formattedMessage) {',
  1519. ' this.level = level;',
  1520. ' this.formattedMessage = formattedMessage;',
  1521. ' this.isRendered = false;',
  1522. ' }',
  1523. '',
  1524. ' LogEntry.prototype = {',
  1525. ' render: function() {',
  1526. ' this.mainDiv = document.createElement("div");',
  1527. ' this.mainDiv.className = "logentry " + this.level.name;',
  1528. ' ',
  1529. ' // Support for the CSS attribute white-space in IE for Windows is',
  1530. ' // non-existent pre version 6 and slightly odd in 6, so instead',
  1531. ' // use two different HTML elements',
  1532. ' if (isCssWrapSupported) {',
  1533. ' this.mainDiv.appendChild(document.createTextNode(this.formattedMessage));',
  1534. ' } else {',
  1535. ' this.formattedMessage = this.formattedMessage.replace(/\\r\\n/g, "\\r"); // Workaround for IE\'s treatment of white space',
  1536. ' this.unwrappedPre = this.mainDiv.appendChild(document.createElement("pre"));',
  1537. ' this.unwrappedPre.appendChild(document.createTextNode(this.formattedMessage));',
  1538. ' this.unwrappedPre.className = "unwrapped";',
  1539. ' this.wrappedSpan = this.mainDiv.appendChild(document.createElement("span"));',
  1540. ' this.wrappedSpan.appendChild(document.createTextNode(this.formattedMessage));',
  1541. ' this.wrappedSpan.className = "wrapped";',
  1542. ' }',
  1543. ' this.content = this.formattedMessage;',
  1544. ' this.isRendered = true;',
  1545. ' },',
  1546. ' ',
  1547. ' appendToLog: function() {',
  1548. ' var lc = getLogContainer();',
  1549. ' if (newestAtTop && lc.hasChildNodes()) {',
  1550. ' lc.insertBefore(this.mainDiv, lc.firstChild);',
  1551. ' } else {',
  1552. ' getLogContainer().appendChild(this.mainDiv);',
  1553. ' }',
  1554. ' },',
  1555. ' ',
  1556. ' setContent: function(content) {',
  1557. ' if (content != this.content) {',
  1558. ' if (getLogContainer().currentStyle) {',
  1559. ' if (content === this.formattedMessage) {',
  1560. ' this.unwrappedPre.innerHTML = "";',
  1561. ' this.unwrappedPre.appendChild(document.createTextNode(this.formattedMessage));',
  1562. ' this.wrappedSpan.innerHTML = "";',
  1563. ' this.wrappedSpan.appendChild(document.createTextNode(this.formattedMessage));',
  1564. ' } else {',
  1565. ' content = content.replace(/\\r\\n/g, "\\r"); // Workaround for IE\'s treatment of white space',
  1566. ' this.unwrappedPre.innerHTML = content;',
  1567. ' this.wrappedSpan.innerHTML = content;',
  1568. ' }',
  1569. ' } else {',
  1570. ' if (content === this.formattedMessage) {',
  1571. ' this.mainDiv.innerHTML = "";',
  1572. ' this.mainDiv.appendChild(document.createTextNode(this.formattedMessage));',
  1573. ' } else {',
  1574. ' this.mainDiv.innerHTML = content;',
  1575. ' }',
  1576. ' }',
  1577. ' this.content = content;',
  1578. ' }',
  1579. ' },',
  1580. '',
  1581. ' getSearchMatches: function() {',
  1582. ' var matches = [];',
  1583. ' if (isCssWrapSupported) {',
  1584. ' var els = getElementsByClass(this.mainDiv, "searchterm", "span");',
  1585. ' for (var i = 0; i < els.length; i++) {',
  1586. ' matches[i] = new Match(this.level, els[i]);',
  1587. ' }',
  1588. ' } else {',
  1589. ' var unwrappedEls = getElementsByClass(this.unwrappedPre, "searchterm", "span");',
  1590. ' var wrappedEls = getElementsByClass(this.wrappedSpan, "searchterm", "span");',
  1591. ' for (i = 0; i < unwrappedEls.length; i++) {',
  1592. ' matches[i] = new Match(this.level, null, unwrappedEls[i], wrappedEls[i]);',
  1593. ' }',
  1594. ' }',
  1595. ' return matches;',
  1596. ' },',
  1597. '',
  1598. ' remove: function() {',
  1599. ' if (this.isRendered) {',
  1600. ' this.mainDiv.parentNode.removeChild(this.mainDiv);',
  1601. ' this.mainDiv = null;',
  1602. ' }',
  1603. ' }',
  1604. ' };',
  1605. '',
  1606. ' function mainPageReloaded() {',
  1607. ' var separator = document.createElement("div");',
  1608. ' separator.className = "separator";',
  1609. ' separator.innerHTML = "&nbsp;";',
  1610. ' getLogContainer().appendChild(separator);',
  1611. ' }',
  1612. '',
  1613. ' window.onload = function() {',
  1614. ' isCssWrapSupported = (typeof getLogContainer().currentStyle == "undefined");',
  1615. ' setLogContainerHeight();',
  1616. ' toggleLoggingEnabled();',
  1617. ' toggleSearchEnabled();',
  1618. ' toggleSearchFilter();',
  1619. ' toggleSearchHighlight();',
  1620. ' applyFilters();',
  1621. ' toggleWrap();',
  1622. ' toggleNewestAtTop();',
  1623. ' toggleScrollToLatest();',
  1624. ' //doSearch();',
  1625. ' renderQueuedLogEntries();',
  1626. ' loaded = true;',
  1627. ' // Workaround to make sure log div starts at the correct size',
  1628. ' setTimeout(setLogContainerHeight, 20);',
  1629. '',
  1630. ' // Remove "Close" button if not in pop-up mode',
  1631. ' if (window != top) {',
  1632. ' $("closeButton").style.display = "none";',
  1633. ' }',
  1634. ' };',
  1635. '',
  1636. ' var loaded = false;',
  1637. '',
  1638. ' var logLevels = ["TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"];',
  1639. '',
  1640. ' function getCheckBox(logLevel) {',
  1641. ' return $("switch_" + logLevel);',
  1642. ' }',
  1643. '',
  1644. ' function getLogContainer() {',
  1645. ' return $("log");',
  1646. ' }',
  1647. '',
  1648. ' function applyFilters() {',
  1649. ' for (var i = 0; i < logLevels.length; i++) {',
  1650. ' if (getCheckBox(logLevels[i]).checked) {',
  1651. ' addClass(getLogContainer(), logLevels[i]);',
  1652. ' } else {',
  1653. ' removeClass(getLogContainer(), logLevels[i]);',
  1654. ' }',
  1655. ' }',
  1656. ' updateSearchFromFilters();',
  1657. ' }',
  1658. '',
  1659. ' function toggleAllLevels() {',
  1660. ' var turnOn = $("switch_ALL").checked;',
  1661. ' for (var i = 0; i < logLevels.length; i++) {',
  1662. ' getCheckBox(logLevels[i]).checked = turnOn;',
  1663. ' if (turnOn) {',
  1664. ' addClass(getLogContainer(), logLevels[i]);',
  1665. ' } else {',
  1666. ' removeClass(getLogContainer(), logLevels[i]);',
  1667. ' }',
  1668. ' }',
  1669. ' }',
  1670. '',
  1671. ' function checkAllLevels() {',
  1672. ' for (var i = 0; i < logLevels.length; i++) {',
  1673. ' if (!getCheckBox(logLevels[i]).checked) {',
  1674. ' getCheckBox("ALL").checked = false;',
  1675. ' return;',
  1676. ' }',
  1677. ' }',
  1678. ' getCheckBox("ALL").checked = true;',
  1679. ' }',
  1680. '',
  1681. ' function clearLog() {',
  1682. ' getLogContainer().innerHTML = "";',
  1683. ' logEntries = [];',
  1684. ' doSearch();',
  1685. ' }',
  1686. '',
  1687. ' function toggleWrap() {',
  1688. ' var enable = $("wrap").checked;',
  1689. ' if (enable) {',
  1690. ' addClass(getLogContainer(), "wrap");',
  1691. ' } else {',
  1692. ' removeClass(getLogContainer(), "wrap");',
  1693. ' }',
  1694. ' refreshCurrentMatch();',
  1695. ' }',
  1696. '',
  1697. ' /* ------------------------------------------------------------------- */',
  1698. '',
  1699. ' // Search',
  1700. '',
  1701. ' var searchTimer = null;',
  1702. '',
  1703. ' function scheduleSearch() {',
  1704. ' try {',
  1705. ' clearTimeout(searchTimer);',
  1706. ' } catch (ex) {',
  1707. ' // Do nothing',
  1708. ' }',
  1709. ' searchTimer = setTimeout(doSearch, 500);',
  1710. ' }',
  1711. '',
  1712. ' function Search(searchTerm, isRegex, searchRegex, isCaseSensitive) {',
  1713. ' this.searchTerm = searchTerm;',
  1714. ' this.isRegex = isRegex;',
  1715. ' this.searchRegex = searchRegex;',
  1716. ' this.isCaseSensitive = isCaseSensitive;',
  1717. ' this.matches = [];',
  1718. ' }',
  1719. '',
  1720. ' Search.prototype = {',
  1721. ' hasMatches: function() {',
  1722. ' return this.matches.length > 0;',
  1723. ' },',
  1724. '',
  1725. ' hasVisibleMatches: function() {',
  1726. ' if (this.hasMatches()) {',
  1727. ' for (var i = 0; i <= this.matches.length; i++) {',
  1728. ' if (this.matches[i].isVisible()) {',
  1729. ' return true;',
  1730. ' }',
  1731. ' }',
  1732. ' }',
  1733. ' return false;',
  1734. ' },',
  1735. '',
  1736. ' match: function(logEntry) {',
  1737. ' var entryText = logEntry.formattedMessage;',
  1738. ' var matchesSearch = false;',
  1739. ' if (this.isRegex) {',
  1740. ' matchesSearch = this.searchRegex.test(entryText);',
  1741. ' } else if (this.isCaseSensitive) {',
  1742. ' matchesSearch = (entryText.indexOf(this.searchTerm) > -1);',
  1743. ' } else {',
  1744. ' matchesSearch = (entryText.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1);',
  1745. ' }',
  1746. ' return matchesSearch;',
  1747. ' },',
  1748. ' ',
  1749. ' getNextVisibleMatchIndex: function() {',
  1750. ' for (var i = currentMatchIndex + 1; i < this.matches.length; i++) {',
  1751. ' if (this.matches[i].isVisible()) {',
  1752. ' return i;',
  1753. ' }',
  1754. ' }',
  1755. ' // Start again from the first match',
  1756. ' for (var i = 0; i <= currentMatchIndex; i++) {',
  1757. ' if (this.matches[i].isVisible()) {',
  1758. ' return i;',
  1759. ' }',
  1760. ' }',
  1761. ' return -1;',
  1762. ' },',
  1763. '',
  1764. ' getPreviousVisibleMatchIndex: function() {',
  1765. ' for (var i = currentMatchIndex - 1; i >= 0; i--) {',
  1766. ' if (this.matches[i].isVisible()) {',
  1767. ' return i;',
  1768. ' }',
  1769. ' }',
  1770. ' // Start again from the last match',
  1771. ' for (var i = this.matches.length - 1; i >= currentMatchIndex; i--) {',
  1772. ' if (this.matches[i].isVisible()) {',
  1773. ' return i;',
  1774. ' }',
  1775. ' }',
  1776. ' return -1;',
  1777. ' },',
  1778. '',
  1779. ' applyTo: function(logEntry) {',
  1780. ' var doesMatch = this.match(logEntry);',
  1781. ' if (doesMatch) {',
  1782. ' replaceClass(logEntry.mainDiv, "searchmatch", "searchnonmatch");',
  1783. ' var logEntryContent;',
  1784. ' if (this.isRegex) {',
  1785. ' var flags = this.isCaseSensitive ? "g" : "gi";',
  1786. ' var capturingRegex = new RegExp("(" + this.searchRegex.source + ")", flags);',
  1787. ' logEntryContent = logEntry.formattedMessage.replace(capturingRegex, "<span class=\\\"searchterm\\\">$1</span>");',
  1788. ' } else {',
  1789. ' logEntryContent = "";',
  1790. ' var searchTermReplacementStartTag = "<span class=\\\"searchterm\\\">";',
  1791. ' var searchTermReplacementEndTag = "</span>";',
  1792. ' var searchTermReplacementLength = searchTermReplacementStartTag.length + this.searchTerm.length + searchTermReplacementEndTag.length;',
  1793. ' var searchTermLength = this.searchTerm.length;',
  1794. ' var startIndex = 0;',
  1795. ' var searchIndex;',
  1796. ' var searchTermLowerCase = this.searchTerm.toLowerCase();',
  1797. ' var logTextLowerCase = logEntry.formattedMessage.toLowerCase();',
  1798. ' while ((searchIndex = logTextLowerCase.indexOf(searchTermLowerCase, startIndex)) > -1) {',
  1799. ' var searchTermReplacement = searchTermReplacementStartTag + logEntry.formattedMessage.substr(searchIndex, this.searchTerm.length) + searchTermReplacementEndTag;',
  1800. ' logEntryContent += logEntry.formattedMessage.substring(startIndex, searchIndex) + searchTermReplacement;',
  1801. ' startIndex = searchIndex + searchTermLength;',
  1802. ' }',
  1803. ' logEntryContent += logEntry.formattedMessage.substr(startIndex);',
  1804. ' }',
  1805. ' logEntry.setContent(logEntryContent);',
  1806. ' var logEntryMatches = logEntry.getSearchMatches();',
  1807. ' this.matches = this.matches.concat(logEntryMatches);',
  1808. ' } else {',
  1809. ' replaceClass(logEntry.mainDiv, "searchnonmatch", "searchmatch");',
  1810. ' logEntry.setContent(logEntry.formattedMessage);',
  1811. ' }',
  1812. ' return doesMatch;',
  1813. ' },',
  1814. '',
  1815. ' removePrunedMatches: function() {',
  1816. ' var matchesToRemoveCount = 0;',
  1817. ' var currentMatchRemoved = false;',
  1818. ' for (var i = 0; i < this.matches.length; i++) {',
  1819. ' if (this.matches[i].isOrphan()) {',
  1820. ' this.matches[i].remove();',
  1821. ' if (i === currentMatchIndex) {',
  1822. ' currentMatchRemoved = true;',
  1823. ' }',
  1824. ' matchesToRemoveCount++;',
  1825. ' }',
  1826. ' }',
  1827. ' if (matchesToRemoveCount > 0) {',
  1828. ' array_removeFromStart(this.matches, matchesToRemoveCount);',
  1829. ' var newMatchIndex = currentMatchRemoved ? 0 :',
  1830. ' currentMatchIndex - matchesToRemoveCount;',
  1831. ' if (this.hasMatches()) {',
  1832. ' setCurrentMatchIndex(newMatchIndex);',
  1833. ' } else {',
  1834. ' currentMatchIndex = null;',
  1835. ' }',
  1836. ' }',
  1837. ' }',
  1838. ' };',
  1839. '',
  1840. ' function getPageOffsetTop(el) {',
  1841. ' var currentEl = el;',
  1842. ' var y = 0;',
  1843. ' while (currentEl) {',
  1844. ' y += currentEl.offsetTop;',
  1845. ' currentEl = currentEl.offsetParent;',
  1846. ' }',
  1847. ' return y;',
  1848. ' }',
  1849. '',
  1850. ' function scrollIntoView(el) {',
  1851. ' getLogContainer().scrollLeft = el.offsetLeft;',
  1852. ' getLogContainer().scrollTop = getPageOffsetTop(el) - getToolBarsHeight();',
  1853. ' }',
  1854. '',
  1855. ' function Match(logEntryLevel, spanInMainDiv, spanInUnwrappedPre, spanInWrappedSpan) {',
  1856. ' this.logEntryLevel = logEntryLevel;',
  1857. ' this.spanInMainDiv = spanInMainDiv;',
  1858. ' if (!isCssWrapSupported) {',
  1859. ' this.spanInUnwrappedPre = spanInUnwrappedPre;',
  1860. ' this.spanInWrappedSpan = spanInWrappedSpan;',
  1861. ' }',
  1862. ' this.mainSpan = isCssWrapSupported ? spanInMainDiv : spanInUnwrappedPre;',
  1863. ' }',
  1864. '',
  1865. ' Match.prototype = {',
  1866. ' equals: function(match) {',
  1867. ' return this.mainSpan === match.mainSpan;',
  1868. ' },',
  1869. '',
  1870. ' setCurrent: function() {',
  1871. ' if (isCssWrapSupported) {',
  1872. ' addClass(this.spanInMainDiv, "currentmatch");',
  1873. ' scrollIntoView(this.spanInMainDiv);',
  1874. ' } else {',
  1875. ' addClass(this.spanInUnwrappedPre, "currentmatch");',
  1876. ' addClass(this.spanInWrappedSpan, "currentmatch");',
  1877. ' // Scroll the visible one into view',
  1878. ' var elementToScroll = $("wrap").checked ? this.spanInWrappedSpan : this.spanInUnwrappedPre;',
  1879. ' scrollIntoView(elementToScroll);',
  1880. ' }',
  1881. ' },',
  1882. '',
  1883. ' setNotCurrent: function() {',
  1884. ' if (isCssWrapSupported) {',
  1885. ' removeClass(this.spanInMainDiv, "currentmatch");',
  1886. ' } else {',
  1887. ' removeClass(this.spanInUnwrappedPre, "currentmatch");',
  1888. ' removeClass(this.spanInWrappedSpan, "currentmatch");',
  1889. ' }',
  1890. ' },',
  1891. '',
  1892. ' isOrphan: function() {',
  1893. ' return isOrphan(this.mainSpan);',
  1894. ' },',
  1895. '',
  1896. ' isVisible: function() {',
  1897. ' return getCheckBox(this.logEntryLevel).checked;',
  1898. ' },',
  1899. '',
  1900. ' remove: function() {',
  1901. ' if (isCssWrapSupported) {',
  1902. ' this.spanInMainDiv = null;',
  1903. ' } else {',
  1904. ' this.spanInUnwrappedPre = null;',
  1905. ' this.spanInWrappedSpan = null;',
  1906. ' }',
  1907. ' }',
  1908. ' };',
  1909. '',
  1910. ' var currentSearch = null;',
  1911. ' var currentMatchIndex = null;',
  1912. '',
  1913. ' function doSearch() {',
  1914. ' var searchBox = $("searchBox");',
  1915. ' var searchTerm = searchBox.value;',
  1916. ' var isRegex = $("searchRegex").checked;',
  1917. ' var isCaseSensitive = $("searchCaseSensitive").checked;',
  1918. ' var i;',
  1919. '',
  1920. ' if (searchTerm === "") {',
  1921. ' $("searchReset").disabled = true;',
  1922. ' $("searchNav").style.display = "none";',
  1923. ' removeClass(document.body, "searching");',
  1924. ' removeClass(searchBox, "hasmatches");',
  1925. ' removeClass(searchBox, "nomatches");',
  1926. ' for (i = 0; i < logEntries.length; i++) {',
  1927. ' removeClass(logEntries[i].mainDiv, "searchmatch");',
  1928. ' removeClass(logEntries[i].mainDiv, "searchnonmatch");',
  1929. ' logEntries[i].setContent(logEntries[i].formattedMessage);',
  1930. ' }',
  1931. ' currentSearch = null;',
  1932. ' setLogContainerHeight();',
  1933. ' } else {',
  1934. ' $("searchReset").disabled = false;',
  1935. ' $("searchNav").style.display = "block";',
  1936. ' var searchRegex;',
  1937. ' var regexValid;',
  1938. ' if (isRegex) {',
  1939. ' try {',
  1940. ' searchRegex = isCaseSensitive ? new RegExp(searchTerm, "g") : new RegExp(searchTerm, "gi");',
  1941. ' regexValid = true;',
  1942. ' replaceClass(searchBox, "validregex", "invalidregex");',
  1943. ' searchBox.title = "Valid regex";',
  1944. ' } catch (ex) {',
  1945. ' regexValid = false;',
  1946. ' replaceClass(searchBox, "invalidregex", "validregex");',
  1947. ' searchBox.title = "Invalid regex: " + (ex.message ? ex.message : (ex.description ? ex.description : "unknown error"));',
  1948. ' return;',
  1949. ' }',
  1950. ' } else {',
  1951. ' searchBox.title = "";',
  1952. ' removeClass(searchBox, "validregex");',
  1953. ' removeClass(searchBox, "invalidregex");',
  1954. ' }',
  1955. ' addClass(document.body, "searching");',
  1956. ' currentSearch = new Search(searchTerm, isRegex, searchRegex, isCaseSensitive);',
  1957. ' for (i = 0; i < logEntries.length; i++) {',
  1958. ' currentSearch.applyTo(logEntries[i]);',
  1959. ' }',
  1960. ' setLogContainerHeight();',
  1961. '',
  1962. ' // Highlight the first search match',
  1963. ' if (currentSearch.hasMatches()) {',
  1964. ' setCurrentMatchIndex(0);',
  1965. ' displayMatches();',
  1966. ' } else {',
  1967. ' displayNoMatches();',
  1968. ' }',
  1969. ' }',
  1970. ' }',
  1971. ' ',
  1972. ' function updateSearchFromFilters() {',
  1973. ' if (currentSearch && currentSearch.hasMatches()) {',
  1974. ' var currentMatch = currentSearch.matches[currentMatchIndex];',
  1975. ' if (currentMatch.isVisible()) {',
  1976. ' displayMatches();',
  1977. ' setCurrentMatchIndex(currentMatchIndex);',
  1978. ' } else {',
  1979. ' currentMatch.setNotCurrent();',
  1980. ' // Find the next visible match, if one exists',
  1981. ' var nextVisibleMatchIndex = currentSearch.getNextVisibleMatchIndex();',
  1982. ' if (nextVisibleMatchIndex > -1) {',
  1983. ' setCurrentMatchIndex(nextVisibleMatchIndex);',
  1984. ' displayMatches();',
  1985. ' } else {',
  1986. ' displayNoMatches();',
  1987. ' }',
  1988. ' }',
  1989. ' }',
  1990. ' }',
  1991. '',
  1992. ' function refreshCurrentMatch() {',
  1993. ' if (currentSearch && currentSearch.hasMatches()) {',
  1994. ' setCurrentMatchIndex(currentMatchIndex);',
  1995. ' }',
  1996. ' }',
  1997. '',
  1998. ' function displayMatches() {',
  1999. ' replaceClass($("searchBox"), "hasmatches", "nomatches");',
  2000. ' $("searchBox").title = "" + currentSearch.matches.length + " matches found";',
  2001. ' $("searchNav").style.display = "block";',
  2002. ' setLogContainerHeight();',
  2003. ' }',
  2004. '',
  2005. ' function displayNoMatches() {',
  2006. ' replaceClass($("searchBox"), "nomatches", "hasmatches");',
  2007. ' $("searchBox").title = "No matches found";',
  2008. ' $("searchNav").style.display = "none";',
  2009. ' setLogContainerHeight();',
  2010. ' }',
  2011. '',
  2012. ' function toggleSearchEnabled(enable) {',
  2013. ' enable = (typeof enable == "undefined") ? !$("searchDisable").checked : enable;',
  2014. ' $("searchBox").disabled = !enable;',
  2015. ' $("searchReset").disabled = !enable;',
  2016. ' $("searchRegex").disabled = !enable;',
  2017. ' $("searchNext").disabled = !enable;',
  2018. ' $("searchPrevious").disabled = !enable;',
  2019. ' $("searchCaseSensitive").disabled = !enable;',
  2020. ' $("searchNav").style.display = (enable && ($("searchBox").value !== "")) ?',
  2021. ' "block" : "none";',
  2022. ' if (enable) {',
  2023. ' removeClass($("search"), "greyedout");',
  2024. ' addClass(document.body, "searching");',
  2025. ' if ($("searchHighlight").checked) {',
  2026. ' addClass(getLogContainer(), "searchhighlight");',
  2027. ' } else {',
  2028. ' removeClass(getLogContainer(), "searchhighlight");',
  2029. ' }',
  2030. ' if ($("searchFilter").checked) {',
  2031. ' addClass(getLogContainer(), "searchfilter");',
  2032. ' } else {',
  2033. ' removeClass(getLogContainer(), "searchfilter");',
  2034. ' }',
  2035. ' $("searchDisable").checked = !enable;',
  2036. ' } else {',
  2037. ' addClass($("search"), "greyedout");',
  2038. ' removeClass(document.body, "searching");',
  2039. ' removeClass(getLogContainer(), "searchhighlight");',
  2040. ' removeClass(getLogContainer(), "searchfilter");',
  2041. ' }',
  2042. ' setLogContainerHeight();',
  2043. ' }',
  2044. '',
  2045. ' function toggleSearchFilter() {',
  2046. ' var enable = $("searchFilter").checked;',
  2047. ' if (enable) {',
  2048. ' addClass(getLogContainer(), "searchfilter");',
  2049. ' } else {',
  2050. ' removeClass(getLogContainer(), "searchfilter");',
  2051. ' }',
  2052. ' refreshCurrentMatch();',
  2053. ' }',
  2054. '',
  2055. ' function toggleSearchHighlight() {',
  2056. ' var enable = $("searchHighlight").checked;',
  2057. ' if (enable) {',
  2058. ' addClass(getLogContainer(), "searchhighlight");',
  2059. ' } else {',
  2060. ' removeClass(getLogContainer(), "searchhighlight");',
  2061. ' }',
  2062. ' }',
  2063. '',
  2064. ' function clearSearch() {',
  2065. ' $("searchBox").value = "";',
  2066. ' doSearch();',
  2067. ' }',
  2068. '',
  2069. ' function searchNext() {',
  2070. ' try {',
  2071. ' if (currentSearch !== null && currentMatchIndex !== null) {',
  2072. ' currentSearch.matches[currentMatchIndex].setNotCurrent();',
  2073. ' var nextMatchIndex = currentSearch.getNextVisibleMatchIndex();',
  2074. ' if (nextMatchIndex > currentMatchIndex || confirm("Reached the end of the page. Start from the top?")) {',
  2075. ' setCurrentMatchIndex(nextMatchIndex);',
  2076. ' }',
  2077. ' }',
  2078. ' } catch (err) {',
  2079. ' alert("currentMatchIndex is " + currentMatchIndex);',
  2080. ' }',
  2081. ' }',
  2082. '',
  2083. ' function searchPrevious() {',
  2084. ' if (currentSearch !== null && currentMatchIndex !== null) {',
  2085. ' currentSearch.matches[currentMatchIndex].setNotCurrent();',
  2086. ' var previousMatchIndex = currentSearch.getPreviousVisibleMatchIndex();',
  2087. ' if (previousMatchIndex < currentMatchIndex || confirm("Reached the start of the page. Continue from the bottom?")) {',
  2088. ' setCurrentMatchIndex(previousMatchIndex);',
  2089. ' }',
  2090. ' }',
  2091. ' }',
  2092. '',
  2093. ' function setCurrentMatchIndex(index) {',
  2094. ' currentMatchIndex = index;',
  2095. ' currentSearch.matches[currentMatchIndex].setCurrent();',
  2096. ' }',
  2097. '',
  2098. ' /* ------------------------------------------------------------------------- */',
  2099. '',
  2100. ' // CSS Utilities',
  2101. '',
  2102. ' function addClass(el, cssClass) {',
  2103. ' if (!hasClass(el, cssClass)) {',
  2104. ' if (el.className) {',
  2105. ' el.className += " " + cssClass;',
  2106. ' } else {',
  2107. ' el.className = cssClass;',
  2108. ' }',
  2109. ' }',
  2110. ' }',
  2111. '',
  2112. ' function hasClass(el, cssClass) {',
  2113. ' if (el.className) {',
  2114. ' var classNames = el.className.split(" ");',
  2115. ' return array_contains(classNames, cssClass);',
  2116. ' }',
  2117. ' return false;',
  2118. ' }',
  2119. '',
  2120. ' function removeClass(el, cssClass) {',
  2121. ' if (hasClass(el, cssClass)) {',
  2122. ' // Rebuild the className property',
  2123. ' var existingClasses = el.className.split(" ");',
  2124. ' var newClasses = [];',
  2125. ' for (var i = 0; i < existingClasses.length; i++) {',
  2126. ' if (existingClasses[i] != cssClass) {',
  2127. ' newClasses[newClasses.length] = existingClasses[i];',
  2128. ' }',
  2129. ' }',
  2130. ' el.className = newClasses.join(" ");',
  2131. ' }',
  2132. ' }',
  2133. '',
  2134. ' function replaceClass(el, newCssClass, oldCssClass) {',
  2135. ' removeClass(el, oldCssClass);',
  2136. ' addClass(el, newCssClass);',
  2137. ' }',
  2138. '',
  2139. ' /* ------------------------------------------------------------------------- */',
  2140. '',
  2141. ' // Other utility functions',
  2142. '',
  2143. ' function getElementsByClass(el, cssClass, tagName) {',
  2144. ' var elements = el.getElementsByTagName(tagName);',
  2145. ' var matches = [];',
  2146. ' for (var i = 0; i < elements.length; i++) {',
  2147. ' if (hasClass(elements[i], cssClass)) {',
  2148. ' matches.push(elements[i]);',
  2149. ' }',
  2150. ' }',
  2151. ' return matches;',
  2152. ' }',
  2153. '',
  2154. ' // Syntax borrowed from Prototype library',
  2155. ' function $(id) {',
  2156. ' return document.getElementById(id);',
  2157. ' }',
  2158. '',
  2159. ' function isOrphan(node) {',
  2160. ' var currentNode = node;',
  2161. ' while (currentNode) {',
  2162. ' if (currentNode == document.body) {',
  2163. ' return false;',
  2164. ' }',
  2165. ' currentNode = currentNode.parentNode;',
  2166. ' }',
  2167. ' return true;',
  2168. ' }',
  2169. '',
  2170. ' function getWindowWidth() {',
  2171. ' if (window.innerWidth) {',
  2172. ' return window.innerWidth;',
  2173. ' } else if (document.documentElement && document.documentElement.clientWidth) {',
  2174. ' return document.documentElement.clientWidth;',
  2175. ' } else if (document.body) {',
  2176. ' return document.body.clientWidth;',
  2177. ' }',
  2178. ' return 0;',
  2179. ' }',
  2180. '',
  2181. ' function getWindowHeight() {',
  2182. ' if (window.innerHeight) {',
  2183. ' return window.innerHeight;',
  2184. ' } else if (document.documentElement && document.documentElement.clientHeight) {',
  2185. ' return document.documentElement.clientHeight;',
  2186. ' } else if (document.body) {',
  2187. ' return document.body.clientHeight;',
  2188. ' }',
  2189. ' return 0;',
  2190. ' }',
  2191. '',
  2192. ' function getToolBarsHeight() {',
  2193. ' return $("switches").offsetHeight;',
  2194. ' }',
  2195. '',
  2196. ' function setLogContainerHeight() {',
  2197. ' var windowHeight = getWindowHeight();',
  2198. ' $("body").style.height = getWindowHeight() + "px";',
  2199. ' getLogContainer().style.height = "" +',
  2200. ' (windowHeight - getToolBarsHeight()) + "px";',
  2201. ' }',
  2202. ' window.onresize = setLogContainerHeight;',
  2203. '',
  2204. ' if (!Array.prototype.push) {',
  2205. ' Array.prototype.push = function() {',
  2206. ' for (var i = 0; i < arguments.length; i++){',
  2207. ' this[this.length] = arguments[i];',
  2208. ' }',
  2209. ' return this.length;',
  2210. ' };',
  2211. ' }',
  2212. '',
  2213. ' if (!Array.prototype.pop) {',
  2214. ' Array.prototype.pop = function() {',
  2215. ' if (this.length > 0) {',
  2216. ' var val = this[this.length - 1];',
  2217. ' this.length = this.length - 1;',
  2218. ' return val;',
  2219. ' }',
  2220. ' };',
  2221. ' }',
  2222. '',
  2223. ' if (!Array.prototype.shift) {',
  2224. ' Array.prototype.shift = function() {',
  2225. ' if (this.length > 0) {',
  2226. ' var firstItem = this[0];',
  2227. ' for (var i = 0; i < this.length - 1; i++) {',
  2228. ' this[i] = this[i + 1];',
  2229. ' }',
  2230. ' this.length = this.length - 1;',
  2231. ' return firstItem;',
  2232. ' }',
  2233. ' };',
  2234. ' }',
  2235. '',
  2236. ' function array_removeFromStart(array, numberToRemove) {',
  2237. ' if (Array.prototype.splice) {',
  2238. ' array.splice(0, numberToRemove);',
  2239. ' } else {',
  2240. ' for (var i = numberToRemove; i < array.length; i++) {',
  2241. ' array[i - numberToRemove] = array[i];',
  2242. ' }',
  2243. ' array.length = array.length - numberToRemove;',
  2244. ' }',
  2245. ' return array;',
  2246. ' }',
  2247. '',
  2248. ' function array_contains(arr, val) {',
  2249. ' for (var i = 0; i < arr.length; i++) {',
  2250. ' if (arr[i] == val) {',
  2251. ' return true;',
  2252. ' }',
  2253. ' }',
  2254. ' return false;',
  2255. ' }',
  2256. ' //]]>',
  2257. ' </script>',
  2258. ' <style type="text/css">',
  2259. ' body {',
  2260. ' background-color: white;',
  2261. ' color: black;',
  2262. ' padding: 0px;',
  2263. ' margin: 0px;',
  2264. ' font-family: tahoma, verdana, arial, helvetica, sans-serif;',
  2265. ' overflow: hidden;',
  2266. ' }',
  2267. '',
  2268. ' div#switchesContainer input {',
  2269. ' margin-bottom: 0px;',
  2270. ' }',
  2271. '',
  2272. ' div#switches div.toolbar {',
  2273. ' border-top: solid #ffffff 1px;',
  2274. ' border-bottom: solid #aca899 1px;',
  2275. ' background-color: #f1efe7;',
  2276. ' padding: 3px 5px;',
  2277. ' font-size: 68.75%;',
  2278. ' }',
  2279. '',
  2280. ' div#switches div.toolbar, div#search input {',
  2281. ' font-family: tahoma, verdana, arial, helvetica, sans-serif;',
  2282. ' }',
  2283. '',
  2284. ' div#switches input.button {',
  2285. ' padding: 0px 5px;',
  2286. ' font-size: 100%;',
  2287. ' }',
  2288. '',
  2289. ' div#switches input#clearButton {',
  2290. ' margin-left: 20px;',
  2291. ' }',
  2292. '',
  2293. ' div#levels label {',
  2294. ' font-weight: bold;',
  2295. ' }',
  2296. '',
  2297. ' div#levels label, div#options label {',
  2298. ' margin-right: 5px;',
  2299. ' }',
  2300. '',
  2301. ' div#levels label#wrapLabel {',
  2302. ' font-weight: normal;',
  2303. ' }',
  2304. '',
  2305. ' div#search {',
  2306. ' padding: 5px 0px;',
  2307. ' }',
  2308. '',
  2309. ' div#search label {',
  2310. ' margin-right: 10px;',
  2311. ' }',
  2312. '',
  2313. ' div#search label.searchboxlabel {',
  2314. ' margin-right: 0px;',
  2315. ' }',
  2316. '',
  2317. ' div#search input {',
  2318. ' font-size: 100%;',
  2319. ' }',
  2320. '',
  2321. ' div#search input.validregex {',
  2322. ' color: green;',
  2323. ' }',
  2324. '',
  2325. ' div#search input.invalidregex {',
  2326. ' color: red;',
  2327. ' }',
  2328. '',
  2329. ' div#search input.nomatches {',
  2330. ' color: white;',
  2331. ' background-color: #ff6666;',
  2332. ' }',
  2333. '',
  2334. ' div#search input.nomatches {',
  2335. ' color: white;',
  2336. ' background-color: #ff6666;',
  2337. ' }',
  2338. '',
  2339. ' *.greyedout {',
  2340. ' color: gray;',
  2341. ' }',
  2342. '',
  2343. ' *.greyedout *.alwaysenabled {',
  2344. ' color: black;',
  2345. ' }',
  2346. '',
  2347. ' div#log {',
  2348. ' font-family: Courier New, Courier;',
  2349. ' font-size: 75%;',
  2350. ' width: 100%;',
  2351. ' overflow: auto;',
  2352. ' }',
  2353. '',
  2354. ' *.logentry {',
  2355. ' overflow: visible;',
  2356. ' display: none;',
  2357. ' white-space: pre;',
  2358. ' }',
  2359. '',
  2360. ' *.logentry pre.unwrapped {',
  2361. ' display: inline;',
  2362. ' }',
  2363. '',
  2364. ' *.logentry span.wrapped {',
  2365. ' display: none;',
  2366. ' }',
  2367. '',
  2368. ' body.searching *.logentry span.currentmatch {',
  2369. ' color: white !important;',
  2370. ' background-color: green !important;',
  2371. ' }',
  2372. '',
  2373. ' body.searching div.searchhighlight *.logentry span.searchterm {',
  2374. ' /*font-weight: bold;*/',
  2375. ' color: black;',
  2376. ' background-color: yellow;',
  2377. ' }',
  2378. '',
  2379. ' div.wrap *.logentry {',
  2380. ' white-space: normal !important;',
  2381. ' border-width: 0px 0px 1px 0px;',
  2382. ' border-color: #dddddd;',
  2383. ' border-style: dotted;',
  2384. ' }',
  2385. '',
  2386. ' div.wrap *.logentry pre.unwrapped {',
  2387. ' display: none;',
  2388. ' }',
  2389. '',
  2390. ' div.wrap *.logentry span.wrapped {',
  2391. ' display: inline;',
  2392. ' }',
  2393. '',
  2394. ' div.searchfilter *.searchnonmatch {',
  2395. ' display: none !important;',
  2396. ' }',
  2397. '',
  2398. ' div#log *.TRACE, label#label_TRACE {',
  2399. ' color: #666666;',
  2400. ' }',
  2401. '',
  2402. ' div#log *.DEBUG, label#label_DEBUG {',
  2403. ' color: green;',
  2404. ' }',
  2405. '',
  2406. ' div#log *.INFO, label#label_INFO {',
  2407. ' color: #000099;',
  2408. ' }',
  2409. '',
  2410. ' div#log *.WARN, label#label_WARN {',
  2411. ' color: #999900;',
  2412. ' }',
  2413. '',
  2414. ' div#log *.ERROR, label#label_ERROR {',
  2415. ' color: red;',
  2416. ' }',
  2417. '',
  2418. ' div#log *.FATAL, label#label_FATAL {',
  2419. ' color: #660066;',
  2420. ' }',
  2421. '',
  2422. ' div.TRACE#log *.TRACE,',
  2423. ' div.DEBUG#log *.DEBUG,',
  2424. ' div.INFO#log *.INFO,',
  2425. ' div.WARN#log *.WARN,',
  2426. ' div.ERROR#log *.ERROR,',
  2427. ' div.FATAL#log *.FATAL {',
  2428. ' display: block;',
  2429. ' }',
  2430. '',
  2431. ' div#log div.separator {',
  2432. ' background-color: #cccccc;',
  2433. ' margin: 5px 0px;',
  2434. ' line-height: 1px;',
  2435. ' }',
  2436. ' </style>',
  2437. ' </head>',
  2438. '',
  2439. ' <body id="body">',
  2440. ' <div id="switchesContainer">',
  2441. ' <div id="switches">',
  2442. ' <div id="levels" class="toolbar">',
  2443. ' Filters:',
  2444. ' <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>',
  2445. ' <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>',
  2446. ' <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>',
  2447. ' <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>',
  2448. ' <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>',
  2449. ' <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>',
  2450. ' <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>',
  2451. ' </div>',
  2452. ' <div id="search" class="toolbar">',
  2453. ' <label for="searchBox" class="searchboxlabel">Search:</label> <input type="text" id="searchBox" onclick="toggleSearchEnabled(true)" onkeyup="scheduleSearch()" size="20" />',
  2454. ' <input type="button" id="searchReset" disabled="disabled" value="Reset" onclick="clearSearch()" class="button" title="Reset the search" />',
  2455. ' <input type="checkbox" id="searchRegex" onclick="doSearch()" title="If checked, search is treated as a regular expression" /><label for="searchRegex">Regex</label>',
  2456. ' <input type="checkbox" id="searchCaseSensitive" onclick="doSearch()" title="If checked, search is case sensitive" /><label for="searchCaseSensitive">Match case</label>',
  2457. ' <input type="checkbox" id="searchDisable" onclick="toggleSearchEnabled()" title="Enable/disable search" /><label for="searchDisable" class="alwaysenabled">Disable</label>',
  2458. ' <div id="searchNav">',
  2459. ' <input type="button" id="searchNext" disabled="disabled" value="Next" onclick="searchNext()" class="button" title="Go to the next matching log entry" />',
  2460. ' <input type="button" id="searchPrevious" disabled="disabled" value="Previous" onclick="searchPrevious()" class="button" title="Go to the previous matching log entry" />',
  2461. ' <input type="checkbox" id="searchFilter" onclick="toggleSearchFilter()" title="If checked, non-matching log entries are filtered out" /><label for="searchFilter">Filter</label>',
  2462. ' <input type="checkbox" id="searchHighlight" onclick="toggleSearchHighlight()" title="Highlight matched search terms" /><label for="searchHighlight" class="alwaysenabled">Highlight all</label>',
  2463. ' </div>',
  2464. ' </div>',
  2465. ' <div id="options" class="toolbar">',
  2466. ' Options:',
  2467. ' <input type="checkbox" id="enableLogging" onclick="toggleLoggingEnabled()" checked="checked" title="Enable/disable logging" /><label for="enableLogging" id="wrapLabel">Log</label>',
  2468. ' <input type="checkbox" id="wrap" onclick="toggleWrap()" title="Enable / disable word wrap" /><label for="wrap" id="wrapLabel">Wrap</label>',
  2469. ' <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>',
  2470. ' <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>',
  2471. ' <input type="button" id="clearButton" value="Clear" onclick="clearLog()" class="button" title="Clear all log messages" />',
  2472. ' <input type="button" id="closeButton" value="Close" onclick="window.close()" class="button" title="Close the window" />',
  2473. ' </div>',
  2474. ' </div>',
  2475. '',
  2476. ' </div>',
  2477. ' <div id="log" class="TRACE DEBUG INFO WARN ERROR FATAL"></div>',
  2478. ' </body>',
  2479. '</html>'
  2480. ];
  2481. };
  2482. function ConsoleAppender() {}
  2483. var consoleAppenderIdCounter = 1;
  2484. ConsoleAppender.prototype = new Appender();
  2485. ConsoleAppender.prototype.create = function(inPage, containerElement,
  2486. layout, lazyInit, focusConsoleWindow, useOldPopUp,
  2487. complainAboutPopUpBlocking, newestMessageAtTop,
  2488. scrollToLatestMessage, initiallyMinimized, width, height,
  2489. reopenWhenClosed, maxMessages) {
  2490. var appender = this;
  2491. // Common properties
  2492. if (layout) {
  2493. this.setLayout(layout);
  2494. } else {
  2495. this.setLayout(this.defaults.layout);
  2496. }
  2497. var initialized = false;
  2498. var consoleWindowLoaded = false;
  2499. var queuedLoggingEvents = [];
  2500. var isSupported = true;
  2501. var consoleAppenderId = consoleAppenderIdCounter++;
  2502. // Params
  2503. lazyInit = extractBooleanFromParam(lazyInit, true);
  2504. newestMessageAtTop = extractBooleanFromParam(newestMessageAtTop, this.defaults.newestMessageAtTop);
  2505. scrollToLatestMessage = extractBooleanFromParam(scrollToLatestMessage, this.defaults.scrollToLatestMessage);
  2506. width = width ? width : this.defaults.width;
  2507. height = height ? height : this.defaults.height;
  2508. maxMessages = maxMessages ? maxMessages : this.defaults.maxMessages;
  2509. // Functions whose implementations vary between subclasses
  2510. var init, safeToAppend, getConsoleWindow;
  2511. // Configuration methods. The function scope is used to prevent
  2512. // direct alteration to the appender configuration properties.
  2513. var appenderName = inPage ? "InPageAppender" : "PopUpAppender";
  2514. var checkCanConfigure = function(configOptionName) {
  2515. if (initialized) {
  2516. handleError(appenderName + ": configuration option '" + configOptionName + "' may not be set after the appender has been initialized");
  2517. return false;
  2518. }
  2519. return true;
  2520. };
  2521. this.isNewestMessageAtTop = function() { return newestMessageAtTop; };
  2522. this.setNewestMessageAtTop = function(newestMessageAtTopParam) {
  2523. newestMessageAtTop = bool(newestMessageAtTopParam);
  2524. if (consoleWindowLoaded && isSupported) {
  2525. getConsoleWindow().setNewestAtTop(newestMessageAtTop);
  2526. }
  2527. };
  2528. this.isScrollToLatestMessage = function() { return scrollToLatestMessage; };
  2529. this.setScrollToLatestMessage = function(scrollToLatestMessageParam) {
  2530. scrollToLatestMessage = bool(scrollToLatestMessageParam);
  2531. if (consoleWindowLoaded && isSupported) {
  2532. getConsoleWindow().setScrollToLatest(scrollToLatestMessage);
  2533. }
  2534. };
  2535. this.getWidth = function() { return width; };
  2536. this.setWidth = function(widthParam) {
  2537. if (checkCanConfigure("width")) {
  2538. width = extractStringFromParam(widthParam, width);
  2539. }
  2540. };
  2541. this.getHeight = function() { return height; };
  2542. this.setHeight = function(heightParam) {
  2543. if (checkCanConfigure("height")) {
  2544. height = extractStringFromParam(heightParam, height);
  2545. }
  2546. };
  2547. this.getMaxMessages = function() { return maxMessages; };
  2548. this.setMaxMessages = function(maxMessagesParam) {
  2549. maxMessages = extractIntFromParam(maxMessagesParam, maxMessages);
  2550. if (consoleWindowLoaded && isSupported) {
  2551. getConsoleWindow().setMaxMessages(maxMessages);
  2552. }
  2553. };
  2554. // Common methods
  2555. this.append = function(loggingEvent) {
  2556. if (isSupported) {
  2557. queuedLoggingEvents.push(loggingEvent);
  2558. // Force a check of whether the window is closed
  2559. var isSafeToAppend = safeToAppend();
  2560. if (!initialized || (consoleClosed && reopenWhenClosed)) {
  2561. init();
  2562. }
  2563. if (safeToAppend()) {
  2564. appendQueuedLoggingEvents();
  2565. }
  2566. }
  2567. };
  2568. var appendQueuedLoggingEvents = function(loggingEvent) {
  2569. while (queuedLoggingEvents.length > 0) {
  2570. var currentLoggingEvent = queuedLoggingEvents.shift();
  2571. var formattedMessage = appender.getLayout().format(currentLoggingEvent);
  2572. if (appender.getLayout().ignoresThrowable()) {
  2573. formattedMessage += currentLoggingEvent.getThrowableStrRep();
  2574. }
  2575. getConsoleWindow().log(currentLoggingEvent.level, formattedMessage);
  2576. }
  2577. if (focusConsoleWindow) {
  2578. getConsoleWindow().focus();
  2579. }
  2580. };
  2581. var writeHtml = function(doc) {
  2582. var lines = getConsoleHtmlLines();
  2583. doc.open();
  2584. for (var i = 0; i < lines.length; i++) {
  2585. doc.writeln(lines[i]);
  2586. }
  2587. doc.close();
  2588. };
  2589. var consoleClosed = false;
  2590. var pollConsoleWindow = function(windowTest, successCallback, errorMessage) {
  2591. function pollConsoleWindowLoaded() {
  2592. try {
  2593. // Test if the console has been closed while polling
  2594. if (consoleClosed) {
  2595. clearInterval(poll);
  2596. }
  2597. if (windowTest(getConsoleWindow())) {
  2598. clearInterval(poll);
  2599. successCallback();
  2600. }
  2601. } catch (ex) {
  2602. clearInterval(poll);
  2603. isSupported = false;
  2604. handleError(errorMessage, ex);
  2605. }
  2606. }
  2607. // Poll the pop-up since the onload event is not reliable
  2608. var poll = setInterval(pollConsoleWindowLoaded, 100);
  2609. };
  2610. // Define methods and properties that vary between subclasses
  2611. if (inPage) {
  2612. // InPageAppender
  2613. // Extract params
  2614. if (!containerElement || !containerElement.appendChild) {
  2615. isSupported = false;
  2616. handleError("InPageAppender.init: a container DOM element must be supplied for the console window");
  2617. return;
  2618. }
  2619. initiallyMinimized = extractBooleanFromParam(initiallyMinimized, appender.defaults.initiallyMinimized);
  2620. // Configuration methods. The function scope is used to prevent
  2621. // direct alteration to the appender configuration properties.
  2622. this.isInitiallyMinimized = function() { return initiallyMinimized; };
  2623. this.setInitiallyMinimized = function(initiallyMinimizedParam) {
  2624. if (checkCanConfigure("initiallyMinimized")) {
  2625. initiallyMinimized = bool(initiallyMinimizedParam);
  2626. }
  2627. };
  2628. // Define useful variables
  2629. var minimized = false;
  2630. var iframeContainerDiv;
  2631. var iframeId = uniqueId + "_InPageAppender_" + consoleAppenderId;
  2632. this.hide = function() {
  2633. iframeContainerDiv.style.display = "none";
  2634. minimized = true;
  2635. };
  2636. this.show = function() {
  2637. iframeContainerDiv.style.display = "block";
  2638. minimized = false;
  2639. };
  2640. this.isVisible = function() {
  2641. return !minimized;
  2642. };
  2643. this.close = function() {
  2644. if (!consoleClosed) {
  2645. iframeContainerDiv.parentNode.removeChild(iframeContainerDiv);
  2646. consoleClosed = true;
  2647. }
  2648. };
  2649. // Create init, getConsoleWindow and safeToAppend functions
  2650. init = function() {
  2651. var initErrorMessage = "InPageAppender.init: unable to create console iframe";
  2652. function finalInit() {
  2653. try {
  2654. getConsoleWindow().setNewestAtTop(newestMessageAtTop);
  2655. getConsoleWindow().setScrollToLatest(scrollToLatestMessage);
  2656. getConsoleWindow().setMaxMessages(maxMessages);
  2657. consoleWindowLoaded = true;
  2658. appendQueuedLoggingEvents();
  2659. if (initiallyMinimized) {
  2660. appender.hide();
  2661. }
  2662. } catch (ex) {
  2663. isSupported = false;
  2664. handleError(initErrorMessage, ex);
  2665. }
  2666. }
  2667. function writeToDocument() {
  2668. try {
  2669. var windowTest = function(win) { return bool(win.loaded); };
  2670. writeHtml(getConsoleWindow().document);
  2671. if (windowTest(getConsoleWindow())) {
  2672. finalInit();
  2673. } else {
  2674. pollConsoleWindow(windowTest, finalInit, initErrorMessage);
  2675. }
  2676. } catch (ex) {
  2677. isSupported = false;
  2678. handleError(initErrorMessage, ex);
  2679. }
  2680. }
  2681. minimized = initiallyMinimized;
  2682. iframeContainerDiv = containerElement.appendChild(document.createElement("div"));
  2683. iframeContainerDiv.style.width = width;
  2684. iframeContainerDiv.style.height = height;
  2685. iframeContainerDiv.style.border = "solid gray 1px";
  2686. // Adding an iframe using the DOM would be preferable, but it doesn't work
  2687. // in IE5 on Windows, or in Konqueror prior to version 3.5 - in Konqueror
  2688. // it creates the iframe fine but I haven't been able to find a way to obtain
  2689. // the iframe's window object
  2690. var iframeHtml = "<iframe id='" + iframeId + "' name='" + iframeId +
  2691. "' width='100%' height='100%' frameborder='0'" +
  2692. "scrolling='no'></iframe>";
  2693. iframeContainerDiv.innerHTML = iframeHtml;
  2694. consoleClosed = false;
  2695. // Write the console HTML to the iframe
  2696. var iframeDocumentExistsTest = function(win) { return bool(win) && bool(win.document); };
  2697. if (iframeDocumentExistsTest(getConsoleWindow())) {
  2698. writeToDocument();
  2699. } else {
  2700. pollConsoleWindow(iframeDocumentExistsTest, writeToDocument, initErrorMessage);
  2701. }
  2702. initialized = true;
  2703. };
  2704. getConsoleWindow = function() {
  2705. var iframe = window.frames[iframeId];
  2706. if (iframe) {
  2707. return iframe;
  2708. }
  2709. };
  2710. safeToAppend = function() {
  2711. if (isSupported && !consoleClosed) {
  2712. if (!consoleWindowLoaded && getConsoleWindow() && getConsoleWindow().loaded) {
  2713. consoleWindowLoaded = true;
  2714. }
  2715. return consoleWindowLoaded;
  2716. }
  2717. return false;
  2718. };
  2719. } else {
  2720. // PopUpAppender
  2721. // Extract params
  2722. useOldPopUp = extractBooleanFromParam(useOldPopUp, appender.defaults.useOldPopUp);
  2723. complainAboutPopUpBlocking = extractBooleanFromParam(complainAboutPopUpBlocking, appender.defaults.complainAboutPopUpBlocking);
  2724. reopenWhenClosed = extractBooleanFromParam(reopenWhenClosed, this.defaults.reopenWhenClosed);
  2725. // Configuration methods. The function scope is used to prevent
  2726. // direct alteration to the appender configuration properties.
  2727. this.isUseOldPopUp = function() { return useOldPopUp; };
  2728. this.setUseOldPopUp = function(useOldPopUpParam) {
  2729. if (checkCanConfigure("useOldPopUp")) {
  2730. useOldPopUp = bool(useOldPopUpParam);
  2731. }
  2732. };
  2733. this.isComplainAboutPopUpBlocking = function() { return complainAboutPopUpBlocking; };
  2734. this.setComplainAboutPopUpBlocking = function(complainAboutPopUpBlockingParam) {
  2735. if (checkCanConfigure("complainAboutPopUpBlocking")) {
  2736. complainAboutPopUpBlocking = bool(complainAboutPopUpBlockingParam);
  2737. }
  2738. };
  2739. this.isFocusPopUp = function() { return focusConsoleWindow; };
  2740. this.setFocusPopUp = function(focusPopUpParam) {
  2741. // This property can be safely altered after logging has started
  2742. focusConsoleWindow = bool(focusPopUpParam);
  2743. };
  2744. this.isReopenWhenClosed = function() { return reopenWhenClosed; };
  2745. this.setReopenWhenClosed = function(reopenWhenClosedParam) {
  2746. // This property can be safely altered after logging has started
  2747. reopenWhenClosed = bool(reopenWhenClosedParam);
  2748. };
  2749. this.close = function() {
  2750. try {
  2751. popUp.close();
  2752. } catch (e) {
  2753. // Do nothing
  2754. }
  2755. consoleClosed = true;
  2756. };
  2757. // Define useful variables
  2758. var popUp;
  2759. // Create init, getConsoleWindow and safeToAppend functions
  2760. init = function() {
  2761. var windowProperties = "width=" + width + ",height=" + height + ",status,resizable";
  2762. var windowName = "PopUp_" + location.host.replace(/[^a-z0-9]/gi, "_") + "_" + consoleAppenderId;
  2763. if (!useOldPopUp) {
  2764. // Ensure a previous window isn't used by using a unique name
  2765. windowName = windowName + "_" + uniqueId;
  2766. }
  2767. function finalInit() {
  2768. consoleWindowLoaded = true;
  2769. getConsoleWindow().setNewestAtTop(newestMessageAtTop);
  2770. getConsoleWindow().setScrollToLatest(scrollToLatestMessage);
  2771. getConsoleWindow().setMaxMessages(maxMessages);
  2772. appendQueuedLoggingEvents();
  2773. }
  2774. try {
  2775. popUp = window.open("", windowName, windowProperties);
  2776. consoleClosed = false;
  2777. if (popUp) {
  2778. if (useOldPopUp && popUp.loaded) {
  2779. popUp.mainPageReloaded();
  2780. finalInit();
  2781. } else {
  2782. writeHtml(popUp.document);
  2783. // Check if the pop-up window object is available
  2784. var popUpLoadedTest = function(win) { return bool(win) && win.loaded; };
  2785. if (popUp.loaded) {
  2786. finalInit();
  2787. } else {
  2788. pollConsoleWindow(popUpLoadedTest, finalInit, "PopUpAppender.init: unable to create console window");
  2789. }
  2790. }
  2791. } else {
  2792. isSupported = false;
  2793. logLog.warn("PopUpAppender.init: pop-ups blocked, please unblock to use PopUpAppender");
  2794. if (complainAboutPopUpBlocking) {
  2795. handleError("log4javascript: pop-up windows appear to be blocked. Please unblock them to use pop-up logging.");
  2796. }
  2797. }
  2798. } catch (ex) {
  2799. handleError("PopUpAppender.init: error creating pop-up", ex);
  2800. }
  2801. initialized = true;
  2802. };
  2803. getConsoleWindow = function() {
  2804. return popUp;
  2805. };
  2806. safeToAppend = function() {
  2807. if (isSupported && !isUndefined(popUp) && !consoleClosed) {
  2808. if (popUp.closed ||
  2809. (consoleWindowLoaded && isUndefined(popUp.closed))) { // Extra check for Opera
  2810. consoleClosed = true;
  2811. logLog.debug("PopUpAppender: pop-up closed");
  2812. return false;
  2813. }
  2814. if (!consoleWindowLoaded && popUp.loaded) {
  2815. consoleWindowLoaded = true;
  2816. }
  2817. }
  2818. return isSupported && consoleWindowLoaded && !consoleClosed;
  2819. };
  2820. }
  2821. if (enabled && !lazyInit) {
  2822. init();
  2823. }
  2824. // Expose getConsoleWindow so that automated tests can check the DOM
  2825. this.getConsoleWindow = getConsoleWindow;
  2826. };
  2827. /* ----------------------------------------------------------------- */
  2828. var PopUpAppender = function(lazyInit, layout, focusPopUp,
  2829. useOldPopUp, complainAboutPopUpBlocking, newestMessageAtTop,
  2830. scrollToLatestMessage, reopenWhenClosed, width, height,
  2831. maxMessages) {
  2832. var focusConsoleWindow = extractBooleanFromParam(focusPopUp, this.defaults.focusPopUp);
  2833. this.create(false, null, layout, lazyInit, focusConsoleWindow,
  2834. useOldPopUp, complainAboutPopUpBlocking,
  2835. newestMessageAtTop, scrollToLatestMessage, null, width, height,
  2836. reopenWhenClosed, maxMessages);
  2837. };
  2838. PopUpAppender.prototype = new ConsoleAppender();
  2839. PopUpAppender.prototype.defaults = {
  2840. layout: new PatternLayout("%d{HH:mm:ss} %-5p - %m{1}%n"),
  2841. focusPopUp: false,
  2842. lazyInit: true,
  2843. useOldPopUp: true,
  2844. complainAboutPopUpBlocking: true,
  2845. newestMessageAtTop: false,
  2846. scrollToLatestMessage: true,
  2847. width: "600",
  2848. height: "400",
  2849. reopenWhenClosed: false,
  2850. maxMessages: null
  2851. };
  2852. PopUpAppender.prototype.toString = function() {
  2853. return "[PopUpAppender]";
  2854. };
  2855. log4javascript.PopUpAppender = PopUpAppender;
  2856. /* ----------------------------------------------------------------- */
  2857. var InPageAppender = function(containerElement, lazyInit,
  2858. layout, initiallyMinimized, newestMessageAtTop,
  2859. scrollToLatestMessage, width, height, maxMessages) {
  2860. this.create(true, containerElement, layout, lazyInit, false,
  2861. null, null, newestMessageAtTop, scrollToLatestMessage,
  2862. initiallyMinimized, width, height, null, maxMessages);
  2863. };
  2864. InPageAppender.prototype = new ConsoleAppender();
  2865. InPageAppender.prototype.defaults = {
  2866. layout: new PatternLayout("%d{HH:mm:ss} %-5p - %m{1}%n"),
  2867. initiallyMinimized: false,
  2868. lazyInit: true,
  2869. newestMessageAtTop: false,
  2870. scrollToLatestMessage: true,
  2871. width: "100%",
  2872. height: "250px",
  2873. maxMessages: null
  2874. };
  2875. InPageAppender.prototype.toString = function() {
  2876. return "[InPageAppender]";
  2877. };
  2878. log4javascript.InPageAppender = InPageAppender;
  2879. // Next line for backwards compatibility
  2880. log4javascript.InlineAppender = InPageAppender;
  2881. })();
  2882. /* --------------------------------------------------------------------- */
  2883. // BrowserConsoleAppender (only works in Opera and Safari and Firefox with
  2884. // FireBug extension)
  2885. var BrowserConsoleAppender = function(layout) {
  2886. if (layout) {
  2887. this.setLayout(layout);
  2888. }
  2889. };
  2890. BrowserConsoleAppender.prototype = new log4javascript.Appender();
  2891. BrowserConsoleAppender.prototype.layout = new NullLayout();
  2892. BrowserConsoleAppender.prototype.threshold = Level.DEBUG;
  2893. BrowserConsoleAppender.prototype.append = function(loggingEvent) {
  2894. var appender = this;
  2895. var getFormattedMessage = function() {
  2896. var layout = appender.getLayout();
  2897. var formattedMessage = layout.format(loggingEvent);
  2898. if (layout.ignoresThrowable() && loggingEvent.exception) {
  2899. formattedMessage += loggingEvent.getThrowableStrRep();
  2900. }
  2901. return formattedMessage;
  2902. };
  2903. if ((typeof opera != "undefined") && opera.postError) { // Opera
  2904. opera.postError(getFormattedMessage());
  2905. } else if (window.console && window.console.log) { // Safari and FireBug
  2906. var formattedMesage = getFormattedMessage();
  2907. // Log to FireBug using its logging methods or revert to the console.log
  2908. // method in Safari
  2909. if (window.console.debug && Level.DEBUG.isGreaterOrEqual(loggingEvent.level)) {
  2910. window.console.debug(formattedMesage);
  2911. } else if (window.console.info && Level.INFO.equals(loggingEvent.level)) {
  2912. window.console.info(formattedMesage);
  2913. } else if (window.console.warn && Level.WARN.equals(loggingEvent.level)) {
  2914. window.console.warn(formattedMesage);
  2915. } else if (window.console.error && loggingEvent.level.isGreaterOrEqual(Level.ERROR)) {
  2916. window.console.error(formattedMesage);
  2917. } else {
  2918. window.console.log(formattedMesage);
  2919. }
  2920. }
  2921. };
  2922. BrowserConsoleAppender.prototype.toString = function() {
  2923. return "[BrowserConsoleAppender]";
  2924. };
  2925. log4javascript.BrowserConsoleAppender = BrowserConsoleAppender;
  2926. })();