blob: 55679f8a63ac4019c7142b48d4ba22797569f5ef [file] [log] [blame]
Siobhan Tullye18b3442014-02-23 14:23:34 -05001<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
3 <head>
4 <title>log4javascript</title>
5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6 <!-- Make IE8 behave like IE7, having gone to all the trouble of making IE work -->
7 <meta http-equiv="X-UA-Compatible" content="IE=7" />
8 <script type="text/javascript">var isIe = false, isIePre7 = false;</script>
9 <!--[if IE]><script type="text/javascript">isIe = true</script><![endif]-->
10 <!--[if lt IE 7]><script type="text/javascript">isIePre7 = true</script><![endif]-->
11 <script type="text/javascript">
12 //<![CDATA[
13 var loggingEnabled = true;
14 var logQueuedEventsTimer = null;
15 var logEntries = [];
16 var logEntriesAndSeparators = [];
17 var logItems = [];
18 var renderDelay = 100;
19 var unrenderedLogItemsExist = false;
20 var rootGroup, currentGroup = null;
21 var loaded = false;
22 var currentLogItem = null;
23 var logMainContainer;
24
25 function copyProperties(obj, props) {
26 for (var i in props) {
27 obj[i] = props[i];
28 }
29 }
30
31 /*----------------------------------------------------------------*/
32
33 function LogItem() {
34 }
35
36 LogItem.prototype = {
37 mainContainer: null,
38 wrappedContainer: null,
39 unwrappedContainer: null,
40 group: null,
41
42 appendToLog: function() {
43 for (var i = 0, len = this.elementContainers.length; i < len; i++) {
44 this.elementContainers[i].appendToLog();
45 }
46 this.group.update();
47 },
48
49 doRemove: function(doUpdate, removeFromGroup) {
50 if (this.rendered) {
51 for (var i = 0, len = this.elementContainers.length; i < len; i++) {
52 this.elementContainers[i].remove();
53 }
54 this.unwrappedElementContainer = null;
55 this.wrappedElementContainer = null;
56 this.mainElementContainer = null;
57 }
58 if (this.group && removeFromGroup) {
59 this.group.removeChild(this, doUpdate);
60 }
61 if (this === currentLogItem) {
62 currentLogItem = null;
63 }
64 },
65
66 remove: function(doUpdate, removeFromGroup) {
67 this.doRemove(doUpdate, removeFromGroup);
68 },
69
70 render: function() {},
71
72 accept: function(visitor) {
73 visitor.visit(this);
74 },
75
76 getUnwrappedDomContainer: function() {
77 return this.group.unwrappedElementContainer.contentDiv;
78 },
79
80 getWrappedDomContainer: function() {
81 return this.group.wrappedElementContainer.contentDiv;
82 },
83
84 getMainDomContainer: function() {
85 return this.group.mainElementContainer.contentDiv;
86 }
87 };
88
89 LogItem.serializedItemKeys = {LOG_ENTRY: 0, GROUP_START: 1, GROUP_END: 2};
90
91 /*----------------------------------------------------------------*/
92
93 function LogItemContainerElement() {
94 }
95
96 LogItemContainerElement.prototype = {
97 appendToLog: function() {
98 var insertBeforeFirst = (newestAtTop && this.containerDomNode.hasChildNodes());
99 if (insertBeforeFirst) {
100 this.containerDomNode.insertBefore(this.mainDiv, this.containerDomNode.firstChild);
101 } else {
102 this.containerDomNode.appendChild(this.mainDiv);
103 }
104 }
105 };
106
107 /*----------------------------------------------------------------*/
108
109 function SeparatorElementContainer(containerDomNode) {
110 this.containerDomNode = containerDomNode;
111 this.mainDiv = document.createElement("div");
112 this.mainDiv.className = "separator";
113 this.mainDiv.innerHTML = "&nbsp;";
114 }
115
116 SeparatorElementContainer.prototype = new LogItemContainerElement();
117
118 SeparatorElementContainer.prototype.remove = function() {
119 this.mainDiv.parentNode.removeChild(this.mainDiv);
120 this.mainDiv = null;
121 };
122
123 /*----------------------------------------------------------------*/
124
125 function Separator() {
126 this.rendered = false;
127 }
128
129 Separator.prototype = new LogItem();
130
131 copyProperties(Separator.prototype, {
132 render: function() {
133 var containerDomNode = this.group.contentDiv;
134 if (isIe) {
135 this.unwrappedElementContainer = new SeparatorElementContainer(this.getUnwrappedDomContainer());
136 this.wrappedElementContainer = new SeparatorElementContainer(this.getWrappedDomContainer());
137 this.elementContainers = [this.unwrappedElementContainer, this.wrappedElementContainer];
138 } else {
139 this.mainElementContainer = new SeparatorElementContainer(this.getMainDomContainer());
140 this.elementContainers = [this.mainElementContainer];
141 }
142 this.content = this.formattedMessage;
143 this.rendered = true;
144 }
145 });
146
147 /*----------------------------------------------------------------*/
148
149 function GroupElementContainer(group, containerDomNode, isRoot, isWrapped) {
150 this.group = group;
151 this.containerDomNode = containerDomNode;
152 this.isRoot = isRoot;
153 this.isWrapped = isWrapped;
154 this.expandable = false;
155
156 if (this.isRoot) {
157 if (isIe) {
158 this.contentDiv = logMainContainer.appendChild(document.createElement("div"));
159 this.contentDiv.id = this.isWrapped ? "log_wrapped" : "log_unwrapped";
160 } else {
161 this.contentDiv = logMainContainer;
162 }
163 } else {
164 var groupElementContainer = this;
165
166 this.mainDiv = document.createElement("div");
167 this.mainDiv.className = "group";
168
169 this.headingDiv = this.mainDiv.appendChild(document.createElement("div"));
170 this.headingDiv.className = "groupheading";
171
172 this.expander = this.headingDiv.appendChild(document.createElement("span"));
173 this.expander.className = "expander unselectable greyedout";
174 this.expander.unselectable = true;
175 var expanderText = this.group.expanded ? "-" : "+";
176 this.expanderTextNode = this.expander.appendChild(document.createTextNode(expanderText));
177
178 this.headingDiv.appendChild(document.createTextNode(" " + this.group.name));
179
180 this.contentDiv = this.mainDiv.appendChild(document.createElement("div"));
181 var contentCssClass = this.group.expanded ? "expanded" : "collapsed";
182 this.contentDiv.className = "groupcontent " + contentCssClass;
183
184 this.expander.onclick = function() {
185 if (groupElementContainer.group.expandable) {
186 groupElementContainer.group.toggleExpanded();
187 }
188 };
189 }
190 }
191
192 GroupElementContainer.prototype = new LogItemContainerElement();
193
194 copyProperties(GroupElementContainer.prototype, {
195 toggleExpanded: function() {
196 if (!this.isRoot) {
197 var oldCssClass, newCssClass, expanderText;
198 if (this.group.expanded) {
199 newCssClass = "expanded";
200 oldCssClass = "collapsed";
201 expanderText = "-";
202 } else {
203 newCssClass = "collapsed";
204 oldCssClass = "expanded";
205 expanderText = "+";
206 }
207 replaceClass(this.contentDiv, newCssClass, oldCssClass);
208 this.expanderTextNode.nodeValue = expanderText;
209 }
210 },
211
212 remove: function() {
213 if (!this.isRoot) {
214 this.headingDiv = null;
215 this.expander.onclick = null;
216 this.expander = null;
217 this.expanderTextNode = null;
218 this.contentDiv = null;
219 this.containerDomNode = null;
220 this.mainDiv.parentNode.removeChild(this.mainDiv);
221 this.mainDiv = null;
222 }
223 },
224
225 reverseChildren: function() {
226 // Invert the order of the log entries
227 var node = null;
228
229 // Remove all the log container nodes
230 var childDomNodes = [];
231 while ((node = this.contentDiv.firstChild)) {
232 this.contentDiv.removeChild(node);
233 childDomNodes.push(node);
234 }
235
236 // Put them all back in reverse order
237 while ((node = childDomNodes.pop())) {
238 this.contentDiv.appendChild(node);
239 }
240 },
241
242 update: function() {
243 if (!this.isRoot) {
244 if (this.group.expandable) {
245 removeClass(this.expander, "greyedout");
246 } else {
247 addClass(this.expander, "greyedout");
248 }
249 }
250 },
251
252 clear: function() {
253 if (this.isRoot) {
254 this.contentDiv.innerHTML = "";
255 }
256 }
257 });
258
259 /*----------------------------------------------------------------*/
260
261 function Group(name, isRoot, initiallyExpanded) {
262 this.name = name;
263 this.group = null;
264 this.isRoot = isRoot;
265 this.initiallyExpanded = initiallyExpanded;
266 this.elementContainers = [];
267 this.children = [];
268 this.expanded = initiallyExpanded;
269 this.rendered = false;
270 this.expandable = false;
271 }
272
273 Group.prototype = new LogItem();
274
275 copyProperties(Group.prototype, {
276 addChild: function(logItem) {
277 this.children.push(logItem);
278 logItem.group = this;
279 },
280
281 render: function() {
282 if (isIe) {
283 var unwrappedDomContainer, wrappedDomContainer;
284 if (this.isRoot) {
285 unwrappedDomContainer = logMainContainer;
286 wrappedDomContainer = logMainContainer;
287 } else {
288 unwrappedDomContainer = this.getUnwrappedDomContainer();
289 wrappedDomContainer = this.getWrappedDomContainer();
290 }
291 this.unwrappedElementContainer = new GroupElementContainer(this, unwrappedDomContainer, this.isRoot, false);
292 this.wrappedElementContainer = new GroupElementContainer(this, wrappedDomContainer, this.isRoot, true);
293 this.elementContainers = [this.unwrappedElementContainer, this.wrappedElementContainer];
294 } else {
295 var mainDomContainer = this.isRoot ? logMainContainer : this.getMainDomContainer();
296 this.mainElementContainer = new GroupElementContainer(this, mainDomContainer, this.isRoot, false);
297 this.elementContainers = [this.mainElementContainer];
298 }
299 this.rendered = true;
300 },
301
302 toggleExpanded: function() {
303 this.expanded = !this.expanded;
304 for (var i = 0, len = this.elementContainers.length; i < len; i++) {
305 this.elementContainers[i].toggleExpanded();
306 }
307 },
308
309 expand: function() {
310 if (!this.expanded) {
311 this.toggleExpanded();
312 }
313 },
314
315 accept: function(visitor) {
316 visitor.visitGroup(this);
317 },
318
319 reverseChildren: function() {
320 if (this.rendered) {
321 for (var i = 0, len = this.elementContainers.length; i < len; i++) {
322 this.elementContainers[i].reverseChildren();
323 }
324 }
325 },
326
327 update: function() {
328 var previouslyExpandable = this.expandable;
329 this.expandable = (this.children.length !== 0);
330 if (this.expandable !== previouslyExpandable) {
331 for (var i = 0, len = this.elementContainers.length; i < len; i++) {
332 this.elementContainers[i].update();
333 }
334 }
335 },
336
337 flatten: function() {
338 var visitor = new GroupFlattener();
339 this.accept(visitor);
340 return visitor.logEntriesAndSeparators;
341 },
342
343 removeChild: function(child, doUpdate) {
344 array_remove(this.children, child);
345 child.group = null;
346 if (doUpdate) {
347 this.update();
348 }
349 },
350
351 remove: function(doUpdate, removeFromGroup) {
352 for (var i = 0, len = this.children.length; i < len; i++) {
353 this.children[i].remove(false, false);
354 }
355 this.children = [];
356 this.update();
357 if (this === currentGroup) {
358 currentGroup = this.group;
359 }
360 this.doRemove(doUpdate, removeFromGroup);
361 },
362
363 serialize: function(items) {
364 items.push([LogItem.serializedItemKeys.GROUP_START, this.name]);
365 for (var i = 0, len = this.children.length; i < len; i++) {
366 this.children[i].serialize(items);
367 }
368 if (this !== currentGroup) {
369 items.push([LogItem.serializedItemKeys.GROUP_END]);
370 }
371 },
372
373 clear: function() {
374 for (var i = 0, len = this.elementContainers.length; i < len; i++) {
375 this.elementContainers[i].clear();
376 }
377 }
378 });
379
380 /*----------------------------------------------------------------*/
381
382 function LogEntryElementContainer() {
383 }
384
385 LogEntryElementContainer.prototype = new LogItemContainerElement();
386
387 copyProperties(LogEntryElementContainer.prototype, {
388 remove: function() {
389 this.doRemove();
390 },
391
392 doRemove: function() {
393 this.mainDiv.parentNode.removeChild(this.mainDiv);
394 this.mainDiv = null;
395 this.contentElement = null;
396 this.containerDomNode = null;
397 },
398
399 setContent: function(content, wrappedContent) {
400 if (content === this.formattedMessage) {
401 this.contentElement.innerHTML = "";
402 this.contentElement.appendChild(document.createTextNode(this.formattedMessage));
403 } else {
404 this.contentElement.innerHTML = content;
405 }
406 },
407
408 setSearchMatch: function(isMatch) {
409 var oldCssClass = isMatch ? "searchnonmatch" : "searchmatch";
410 var newCssClass = isMatch ? "searchmatch" : "searchnonmatch";
411 replaceClass(this.mainDiv, newCssClass, oldCssClass);
412 },
413
414 clearSearch: function() {
415 removeClass(this.mainDiv, "searchmatch");
416 removeClass(this.mainDiv, "searchnonmatch");
417 }
418 });
419
420 /*----------------------------------------------------------------*/
421
422 function LogEntryWrappedElementContainer(logEntry, containerDomNode) {
423 this.logEntry = logEntry;
424 this.containerDomNode = containerDomNode;
425 this.mainDiv = document.createElement("div");
426 this.mainDiv.appendChild(document.createTextNode(this.logEntry.formattedMessage));
427 this.mainDiv.className = "logentry wrapped " + this.logEntry.level;
428 this.contentElement = this.mainDiv;
429 }
430
431 LogEntryWrappedElementContainer.prototype = new LogEntryElementContainer();
432
433 LogEntryWrappedElementContainer.prototype.setContent = function(content, wrappedContent) {
434 if (content === this.formattedMessage) {
435 this.contentElement.innerHTML = "";
436 this.contentElement.appendChild(document.createTextNode(this.formattedMessage));
437 } else {
438 this.contentElement.innerHTML = wrappedContent;
439 }
440 };
441
442 /*----------------------------------------------------------------*/
443
444 function LogEntryUnwrappedElementContainer(logEntry, containerDomNode) {
445 this.logEntry = logEntry;
446 this.containerDomNode = containerDomNode;
447 this.mainDiv = document.createElement("div");
448 this.mainDiv.className = "logentry unwrapped " + this.logEntry.level;
449 this.pre = this.mainDiv.appendChild(document.createElement("pre"));
450 this.pre.appendChild(document.createTextNode(this.logEntry.formattedMessage));
451 this.pre.className = "unwrapped";
452 this.contentElement = this.pre;
453 }
454
455 LogEntryUnwrappedElementContainer.prototype = new LogEntryElementContainer();
456
457 LogEntryUnwrappedElementContainer.prototype.remove = function() {
458 this.doRemove();
459 this.pre = null;
460 };
461
462 /*----------------------------------------------------------------*/
463
464 function LogEntryMainElementContainer(logEntry, containerDomNode) {
465 this.logEntry = logEntry;
466 this.containerDomNode = containerDomNode;
467 this.mainDiv = document.createElement("div");
468 this.mainDiv.className = "logentry nonielogentry " + this.logEntry.level;
469 this.contentElement = this.mainDiv.appendChild(document.createElement("span"));
470 this.contentElement.appendChild(document.createTextNode(this.logEntry.formattedMessage));
471 }
472
473 LogEntryMainElementContainer.prototype = new LogEntryElementContainer();
474
475 /*----------------------------------------------------------------*/
476
477 function LogEntry(level, formattedMessage) {
478 this.level = level;
479 this.formattedMessage = formattedMessage;
480 this.rendered = false;
481 }
482
483 LogEntry.prototype = new LogItem();
484
485 copyProperties(LogEntry.prototype, {
486 render: function() {
487 var logEntry = this;
488 var containerDomNode = this.group.contentDiv;
489
490 // Support for the CSS attribute white-space in IE for Windows is
491 // non-existent pre version 6 and slightly odd in 6, so instead
492 // use two different HTML elements
493 if (isIe) {
494 this.formattedMessage = this.formattedMessage.replace(/\r\n/g, "\r"); // Workaround for IE's treatment of white space
495 this.unwrappedElementContainer = new LogEntryUnwrappedElementContainer(this, this.getUnwrappedDomContainer());
496 this.wrappedElementContainer = new LogEntryWrappedElementContainer(this, this.getWrappedDomContainer());
497 this.elementContainers = [this.unwrappedElementContainer, this.wrappedElementContainer];
498 } else {
499 this.mainElementContainer = new LogEntryMainElementContainer(this, this.getMainDomContainer());
500 this.elementContainers = [this.mainElementContainer];
501 }
502 this.content = this.formattedMessage;
503 this.rendered = true;
504 },
505
506 setContent: function(content, wrappedContent) {
507 if (content != this.content) {
508 if (isIe && (content !== this.formattedMessage)) {
509 content = content.replace(/\r\n/g, "\r"); // Workaround for IE's treatment of white space
510 }
511 for (var i = 0, len = this.elementContainers.length; i < len; i++) {
512 this.elementContainers[i].setContent(content, wrappedContent);
513 }
514 this.content = content;
515 }
516 },
517
518 getSearchMatches: function() {
519 var matches = [];
520 var i, len;
521 if (isIe) {
522 var unwrappedEls = getElementsByClass(this.unwrappedElementContainer.mainDiv, "searchterm", "span");
523 var wrappedEls = getElementsByClass(this.wrappedElementContainer.mainDiv, "searchterm", "span");
524 for (i = 0, len = unwrappedEls.length; i < len; i++) {
525 matches[i] = new Match(this.level, null, unwrappedEls[i], wrappedEls[i]);
526 }
527 } else {
528 var els = getElementsByClass(this.mainElementContainer.mainDiv, "searchterm", "span");
529 for (i = 0, len = els.length; i < len; i++) {
530 matches[i] = new Match(this.level, els[i]);
531 }
532 }
533 return matches;
534 },
535
536 setSearchMatch: function(isMatch) {
537 for (var i = 0, len = this.elementContainers.length; i < len; i++) {
538 this.elementContainers[i].setSearchMatch(isMatch);
539 }
540 },
541
542 clearSearch: function() {
543 for (var i = 0, len = this.elementContainers.length; i < len; i++) {
544 this.elementContainers[i].clearSearch();
545 }
546 },
547
548 accept: function(visitor) {
549 visitor.visitLogEntry(this);
550 },
551
552 serialize: function(items) {
553 items.push([LogItem.serializedItemKeys.LOG_ENTRY, this.level, this.formattedMessage]);
554 }
555 });
556
557 /*----------------------------------------------------------------*/
558
559 function LogItemVisitor() {
560 }
561
562 LogItemVisitor.prototype = {
563 visit: function(logItem) {
564 },
565
566 visitParent: function(logItem) {
567 if (logItem.group) {
568 logItem.group.accept(this);
569 }
570 },
571
572 visitChildren: function(logItem) {
573 for (var i = 0, len = logItem.children.length; i < len; i++) {
574 logItem.children[i].accept(this);
575 }
576 },
577
578 visitLogEntry: function(logEntry) {
579 this.visit(logEntry);
580 },
581
582 visitSeparator: function(separator) {
583 this.visit(separator);
584 },
585
586 visitGroup: function(group) {
587 this.visit(group);
588 }
589 };
590
591 /*----------------------------------------------------------------*/
592
593 function GroupFlattener() {
594 this.logEntriesAndSeparators = [];
595 }
596
597 GroupFlattener.prototype = new LogItemVisitor();
598
599 GroupFlattener.prototype.visitGroup = function(group) {
600 this.visitChildren(group);
601 };
602
603 GroupFlattener.prototype.visitLogEntry = function(logEntry) {
604 this.logEntriesAndSeparators.push(logEntry);
605 };
606
607 GroupFlattener.prototype.visitSeparator = function(separator) {
608 this.logEntriesAndSeparators.push(separator);
609 };
610
611 /*----------------------------------------------------------------*/
612
613 window.onload = function() {
614 // Sort out document.domain
615 if (location.search) {
616 var queryBits = unescape(location.search).substr(1).split("&"), nameValueBits;
617 for (var i = 0, len = queryBits.length; i < len; i++) {
618 nameValueBits = queryBits[i].split("=");
619 if (nameValueBits[0] == "log4javascript_domain") {
620 document.domain = nameValueBits[1];
621 break;
622 }
623 }
624 }
625
626 // Create DOM objects
627 logMainContainer = $("log");
628 if (isIePre7) {
629 addClass(logMainContainer, "oldIe");
630 }
631
632 rootGroup = new Group("root", true);
633 rootGroup.render();
634 currentGroup = rootGroup;
635
636 setCommandInputWidth();
637 setLogContainerHeight();
638 toggleLoggingEnabled();
639 toggleSearchEnabled();
640 toggleSearchFilter();
641 toggleSearchHighlight();
642 applyFilters();
643 checkAllLevels();
644 toggleWrap();
645 toggleNewestAtTop();
646 toggleScrollToLatest();
647 renderQueuedLogItems();
648 loaded = true;
649 $("command").value = "";
650 $("command").autocomplete = "off";
651 $("command").onkeydown = function(evt) {
652 evt = getEvent(evt);
653 if (evt.keyCode == 10 || evt.keyCode == 13) { // Return/Enter
654 evalCommandLine();
655 stopPropagation(evt);
656 } else if (evt.keyCode == 27) { // Escape
657 this.value = "";
658 this.focus();
659 } else if (evt.keyCode == 38 && commandHistory.length > 0) { // Up
660 currentCommandIndex = Math.max(0, currentCommandIndex - 1);
661 this.value = commandHistory[currentCommandIndex];
662 moveCaretToEnd(this);
663 } else if (evt.keyCode == 40 && commandHistory.length > 0) { // Down
664 currentCommandIndex = Math.min(commandHistory.length - 1, currentCommandIndex + 1);
665 this.value = commandHistory[currentCommandIndex];
666 moveCaretToEnd(this);
667 }
668 };
669
670 // Prevent the keypress moving the caret in Firefox
671 $("command").onkeypress = function(evt) {
672 evt = getEvent(evt);
673 if (evt.keyCode == 38 && commandHistory.length > 0 && evt.preventDefault) { // Up
674 evt.preventDefault();
675 }
676 };
677
678 // Prevent the keyup event blurring the input in Opera
679 $("command").onkeyup = function(evt) {
680 evt = getEvent(evt);
681 if (evt.keyCode == 27 && evt.preventDefault) { // Up
682 evt.preventDefault();
683 this.focus();
684 }
685 };
686
687 // Add document keyboard shortcuts
688 document.onkeydown = function keyEventHandler(evt) {
689 evt = getEvent(evt);
690 switch (evt.keyCode) {
691 case 69: // Ctrl + shift + E: re-execute last command
692 if (evt.shiftKey && (evt.ctrlKey || evt.metaKey)) {
693 evalLastCommand();
694 cancelKeyEvent(evt);
695 return false;
696 }
697 break;
698 case 75: // Ctrl + shift + K: focus search
699 if (evt.shiftKey && (evt.ctrlKey || evt.metaKey)) {
700 focusSearch();
701 cancelKeyEvent(evt);
702 return false;
703 }
704 break;
705 case 40: // Ctrl + shift + down arrow: focus command line
706 case 76: // Ctrl + shift + L: focus command line
707 if (evt.shiftKey && (evt.ctrlKey || evt.metaKey)) {
708 focusCommandLine();
709 cancelKeyEvent(evt);
710 return false;
711 }
712 break;
713 }
714 };
715
716 // Workaround to make sure log div starts at the correct size
717 setTimeout(setLogContainerHeight, 20);
718
719 setShowCommandLine(showCommandLine);
720 doSearch();
721 };
722
723 window.onunload = function() {
724 if (mainWindowExists()) {
725 appender.unload();
726 }
727 appender = null;
728 };
729
730 /*----------------------------------------------------------------*/
731
732 function toggleLoggingEnabled() {
733 setLoggingEnabled($("enableLogging").checked);
734 }
735
736 function setLoggingEnabled(enable) {
737 loggingEnabled = enable;
738 }
739
740 var appender = null;
741
742 function setAppender(appenderParam) {
743 appender = appenderParam;
744 }
745
746 function setShowCloseButton(showCloseButton) {
747 $("closeButton").style.display = showCloseButton ? "inline" : "none";
748 }
749
750 function setShowHideButton(showHideButton) {
751 $("hideButton").style.display = showHideButton ? "inline" : "none";
752 }
753
754 var newestAtTop = false;
755
756 /*----------------------------------------------------------------*/
757
758 function LogItemContentReverser() {
759 }
760
761 LogItemContentReverser.prototype = new LogItemVisitor();
762
763 LogItemContentReverser.prototype.visitGroup = function(group) {
764 group.reverseChildren();
765 this.visitChildren(group);
766 };
767
768 /*----------------------------------------------------------------*/
769
770 function setNewestAtTop(isNewestAtTop) {
771 var oldNewestAtTop = newestAtTop;
772 var i, iLen, j, jLen;
773 newestAtTop = Boolean(isNewestAtTop);
774 if (oldNewestAtTop != newestAtTop) {
775 var visitor = new LogItemContentReverser();
776 rootGroup.accept(visitor);
777
778 // Reassemble the matches array
779 if (currentSearch) {
780 var currentMatch = currentSearch.matches[currentMatchIndex];
781 var matchIndex = 0;
782 var matches = [];
783 var actOnLogEntry = function(logEntry) {
784 var logEntryMatches = logEntry.getSearchMatches();
785 for (j = 0, jLen = logEntryMatches.length; j < jLen; j++) {
786 matches[matchIndex] = logEntryMatches[j];
787 if (currentMatch && logEntryMatches[j].equals(currentMatch)) {
788 currentMatchIndex = matchIndex;
789 }
790 matchIndex++;
791 }
792 };
793 if (newestAtTop) {
794 for (i = logEntries.length - 1; i >= 0; i--) {
795 actOnLogEntry(logEntries[i]);
796 }
797 } else {
798 for (i = 0, iLen = logEntries.length; i < iLen; i++) {
799 actOnLogEntry(logEntries[i]);
800 }
801 }
802 currentSearch.matches = matches;
803 if (currentMatch) {
804 currentMatch.setCurrent();
805 }
806 } else if (scrollToLatest) {
807 doScrollToLatest();
808 }
809 }
810 $("newestAtTop").checked = isNewestAtTop;
811 }
812
813 function toggleNewestAtTop() {
814 var isNewestAtTop = $("newestAtTop").checked;
815 setNewestAtTop(isNewestAtTop);
816 }
817
818 var scrollToLatest = true;
819
820 function setScrollToLatest(isScrollToLatest) {
821 scrollToLatest = isScrollToLatest;
822 if (scrollToLatest) {
823 doScrollToLatest();
824 }
825 $("scrollToLatest").checked = isScrollToLatest;
826 }
827
828 function toggleScrollToLatest() {
829 var isScrollToLatest = $("scrollToLatest").checked;
830 setScrollToLatest(isScrollToLatest);
831 }
832
833 function doScrollToLatest() {
834 var l = logMainContainer;
835 if (typeof l.scrollTop != "undefined") {
836 if (newestAtTop) {
837 l.scrollTop = 0;
838 } else {
839 var latestLogEntry = l.lastChild;
840 if (latestLogEntry) {
841 l.scrollTop = l.scrollHeight;
842 }
843 }
844 }
845 }
846
847 var closeIfOpenerCloses = true;
848
849 function setCloseIfOpenerCloses(isCloseIfOpenerCloses) {
850 closeIfOpenerCloses = isCloseIfOpenerCloses;
851 }
852
853 var maxMessages = null;
854
855 function setMaxMessages(max) {
856 maxMessages = max;
857 pruneLogEntries();
858 }
859
860 var showCommandLine = false;
861
862 function setShowCommandLine(isShowCommandLine) {
863 showCommandLine = isShowCommandLine;
864 if (loaded) {
865 $("commandLine").style.display = showCommandLine ? "block" : "none";
866 setCommandInputWidth();
867 setLogContainerHeight();
868 }
869 }
870
871 function focusCommandLine() {
872 if (loaded) {
873 $("command").focus();
874 }
875 }
876
877 function focusSearch() {
878 if (loaded) {
879 $("searchBox").focus();
880 }
881 }
882
883 function getLogItems() {
884 var items = [];
885 for (var i = 0, len = logItems.length; i < len; i++) {
886 logItems[i].serialize(items);
887 }
888 return items;
889 }
890
891 function setLogItems(items) {
892 var loggingReallyEnabled = loggingEnabled;
893 // Temporarily turn logging on
894 loggingEnabled = true;
895 for (var i = 0, len = items.length; i < len; i++) {
896 switch (items[i][0]) {
897 case LogItem.serializedItemKeys.LOG_ENTRY:
898 log(items[i][1], items[i][2]);
899 break;
900 case LogItem.serializedItemKeys.GROUP_START:
901 group(items[i][1]);
902 break;
903 case LogItem.serializedItemKeys.GROUP_END:
904 groupEnd();
905 break;
906 }
907 }
908 loggingEnabled = loggingReallyEnabled;
909 }
910
911 function log(logLevel, formattedMessage) {
912 if (loggingEnabled) {
913 var logEntry = new LogEntry(logLevel, formattedMessage);
914 logEntries.push(logEntry);
915 logEntriesAndSeparators.push(logEntry);
916 logItems.push(logEntry);
917 currentGroup.addChild(logEntry);
918 if (loaded) {
919 if (logQueuedEventsTimer !== null) {
920 clearTimeout(logQueuedEventsTimer);
921 }
922 logQueuedEventsTimer = setTimeout(renderQueuedLogItems, renderDelay);
923 unrenderedLogItemsExist = true;
924 }
925 }
926 }
927
928 function renderQueuedLogItems() {
929 logQueuedEventsTimer = null;
930 var pruned = pruneLogEntries();
931
932 // Render any unrendered log entries and apply the current search to them
933 var initiallyHasMatches = currentSearch ? currentSearch.hasMatches() : false;
934 for (var i = 0, len = logItems.length; i < len; i++) {
935 if (!logItems[i].rendered) {
936 logItems[i].render();
937 logItems[i].appendToLog();
938 if (currentSearch && (logItems[i] instanceof LogEntry)) {
939 currentSearch.applyTo(logItems[i]);
940 }
941 }
942 }
943 if (currentSearch) {
944 if (pruned) {
945 if (currentSearch.hasVisibleMatches()) {
946 if (currentMatchIndex === null) {
947 setCurrentMatchIndex(0);
948 }
949 displayMatches();
950 } else {
951 displayNoMatches();
952 }
953 } else if (!initiallyHasMatches && currentSearch.hasVisibleMatches()) {
954 setCurrentMatchIndex(0);
955 displayMatches();
956 }
957 }
958 if (scrollToLatest) {
959 doScrollToLatest();
960 }
961 unrenderedLogItemsExist = false;
962 }
963
964 function pruneLogEntries() {
965 if ((maxMessages !== null) && (logEntriesAndSeparators.length > maxMessages)) {
966 var numberToDelete = logEntriesAndSeparators.length - maxMessages;
967 var prunedLogEntries = logEntriesAndSeparators.slice(0, numberToDelete);
968 if (currentSearch) {
969 currentSearch.removeMatches(prunedLogEntries);
970 }
971 var group;
972 for (var i = 0; i < numberToDelete; i++) {
973 group = logEntriesAndSeparators[i].group;
974 array_remove(logItems, logEntriesAndSeparators[i]);
975 array_remove(logEntries, logEntriesAndSeparators[i]);
976 logEntriesAndSeparators[i].remove(true, true);
977 if (group.children.length === 0 && group !== currentGroup && group !== rootGroup) {
978 array_remove(logItems, group);
979 group.remove(true, true);
980 }
981 }
982 logEntriesAndSeparators = array_removeFromStart(logEntriesAndSeparators, numberToDelete);
983 return true;
984 }
985 return false;
986 }
987
988 function group(name, startExpanded) {
989 if (loggingEnabled) {
990 initiallyExpanded = (typeof startExpanded === "undefined") ? true : Boolean(startExpanded);
991 var newGroup = new Group(name, false, initiallyExpanded);
992 currentGroup.addChild(newGroup);
993 currentGroup = newGroup;
994 logItems.push(newGroup);
995 if (loaded) {
996 if (logQueuedEventsTimer !== null) {
997 clearTimeout(logQueuedEventsTimer);
998 }
999 logQueuedEventsTimer = setTimeout(renderQueuedLogItems, renderDelay);
1000 unrenderedLogItemsExist = true;
1001 }
1002 }
1003 }
1004
1005 function groupEnd() {
1006 currentGroup = (currentGroup === rootGroup) ? rootGroup : currentGroup.group;
1007 }
1008
1009 function mainPageReloaded() {
1010 currentGroup = rootGroup;
1011 var separator = new Separator();
1012 logEntriesAndSeparators.push(separator);
1013 logItems.push(separator);
1014 currentGroup.addChild(separator);
1015 }
1016
1017 function closeWindow() {
1018 if (appender && mainWindowExists()) {
1019 appender.close(true);
1020 } else {
1021 window.close();
1022 }
1023 }
1024
1025 function hide() {
1026 if (appender && mainWindowExists()) {
1027 appender.hide();
1028 }
1029 }
1030
1031 var mainWindow = window;
1032 var windowId = "log4javascriptConsoleWindow_" + new Date().getTime() + "_" + ("" + Math.random()).substr(2);
1033
1034 function setMainWindow(win) {
1035 mainWindow = win;
1036 mainWindow[windowId] = window;
1037 // If this is a pop-up, poll the opener to see if it's closed
1038 if (opener && closeIfOpenerCloses) {
1039 pollOpener();
1040 }
1041 }
1042
1043 function pollOpener() {
1044 if (closeIfOpenerCloses) {
1045 if (mainWindowExists()) {
1046 setTimeout(pollOpener, 500);
1047 } else {
1048 closeWindow();
1049 }
1050 }
1051 }
1052
1053 function mainWindowExists() {
1054 try {
1055 return (mainWindow && !mainWindow.closed &&
1056 mainWindow[windowId] == window);
1057 } catch (ex) {}
1058 return false;
1059 }
1060
1061 var logLevels = ["TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"];
1062
1063 function getCheckBox(logLevel) {
1064 return $("switch_" + logLevel);
1065 }
1066
1067 function getIeWrappedLogContainer() {
1068 return $("log_wrapped");
1069 }
1070
1071 function getIeUnwrappedLogContainer() {
1072 return $("log_unwrapped");
1073 }
1074
1075 function applyFilters() {
1076 for (var i = 0; i < logLevels.length; i++) {
1077 if (getCheckBox(logLevels[i]).checked) {
1078 addClass(logMainContainer, logLevels[i]);
1079 } else {
1080 removeClass(logMainContainer, logLevels[i]);
1081 }
1082 }
1083 updateSearchFromFilters();
1084 }
1085
1086 function toggleAllLevels() {
1087 var turnOn = $("switch_ALL").checked;
1088 for (var i = 0; i < logLevels.length; i++) {
1089 getCheckBox(logLevels[i]).checked = turnOn;
1090 if (turnOn) {
1091 addClass(logMainContainer, logLevels[i]);
1092 } else {
1093 removeClass(logMainContainer, logLevels[i]);
1094 }
1095 }
1096 }
1097
1098 function checkAllLevels() {
1099 for (var i = 0; i < logLevels.length; i++) {
1100 if (!getCheckBox(logLevels[i]).checked) {
1101 getCheckBox("ALL").checked = false;
1102 return;
1103 }
1104 }
1105 getCheckBox("ALL").checked = true;
1106 }
1107
1108 function clearLog() {
1109 rootGroup.clear();
1110 currentGroup = rootGroup;
1111 logEntries = [];
1112 logItems = [];
1113 logEntriesAndSeparators = [];
1114 doSearch();
1115 }
1116
1117 function toggleWrap() {
1118 var enable = $("wrap").checked;
1119 if (enable) {
1120 addClass(logMainContainer, "wrap");
1121 } else {
1122 removeClass(logMainContainer, "wrap");
1123 }
1124 refreshCurrentMatch();
1125 }
1126
1127 /* ------------------------------------------------------------------- */
1128
1129 // Search
1130
1131 var searchTimer = null;
1132
1133 function scheduleSearch() {
1134 try {
1135 clearTimeout(searchTimer);
1136 } catch (ex) {
1137 // Do nothing
1138 }
1139 searchTimer = setTimeout(doSearch, 500);
1140 }
1141
1142 function Search(searchTerm, isRegex, searchRegex, isCaseSensitive) {
1143 this.searchTerm = searchTerm;
1144 this.isRegex = isRegex;
1145 this.searchRegex = searchRegex;
1146 this.isCaseSensitive = isCaseSensitive;
1147 this.matches = [];
1148 }
1149
1150 Search.prototype = {
1151 hasMatches: function() {
1152 return this.matches.length > 0;
1153 },
1154
1155 hasVisibleMatches: function() {
1156 if (this.hasMatches()) {
1157 for (var i = 0; i < this.matches.length; i++) {
1158 if (this.matches[i].isVisible()) {
1159 return true;
1160 }
1161 }
1162 }
1163 return false;
1164 },
1165
1166 match: function(logEntry) {
1167 var entryText = String(logEntry.formattedMessage);
1168 var matchesSearch = false;
1169 if (this.isRegex) {
1170 matchesSearch = this.searchRegex.test(entryText);
1171 } else if (this.isCaseSensitive) {
1172 matchesSearch = (entryText.indexOf(this.searchTerm) > -1);
1173 } else {
1174 matchesSearch = (entryText.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1);
1175 }
1176 return matchesSearch;
1177 },
1178
1179 getNextVisibleMatchIndex: function() {
1180 for (var i = currentMatchIndex + 1; i < this.matches.length; i++) {
1181 if (this.matches[i].isVisible()) {
1182 return i;
1183 }
1184 }
1185 // Start again from the first match
1186 for (i = 0; i <= currentMatchIndex; i++) {
1187 if (this.matches[i].isVisible()) {
1188 return i;
1189 }
1190 }
1191 return -1;
1192 },
1193
1194 getPreviousVisibleMatchIndex: function() {
1195 for (var i = currentMatchIndex - 1; i >= 0; i--) {
1196 if (this.matches[i].isVisible()) {
1197 return i;
1198 }
1199 }
1200 // Start again from the last match
1201 for (var i = this.matches.length - 1; i >= currentMatchIndex; i--) {
1202 if (this.matches[i].isVisible()) {
1203 return i;
1204 }
1205 }
1206 return -1;
1207 },
1208
1209 applyTo: function(logEntry) {
1210 var doesMatch = this.match(logEntry);
1211 if (doesMatch) {
1212 logEntry.group.expand();
1213 logEntry.setSearchMatch(true);
1214 var logEntryContent;
1215 var wrappedLogEntryContent;
1216 var searchTermReplacementStartTag = "<span class=\"searchterm\">";
1217 var searchTermReplacementEndTag = "<" + "/span>";
1218 var preTagName = isIe ? "pre" : "span";
1219 var preStartTag = "<" + preTagName + " class=\"pre\">";
1220 var preEndTag = "<" + "/" + preTagName + ">";
1221 var startIndex = 0;
1222 var searchIndex, matchedText, textBeforeMatch;
1223 if (this.isRegex) {
1224 var flags = this.isCaseSensitive ? "g" : "gi";
1225 var capturingRegex = new RegExp("(" + this.searchRegex.source + ")", flags);
1226
1227 // Replace the search term with temporary tokens for the start and end tags
1228 var rnd = ("" + Math.random()).substr(2);
1229 var startToken = "%%s" + rnd + "%%";
1230 var endToken = "%%e" + rnd + "%%";
1231 logEntryContent = logEntry.formattedMessage.replace(capturingRegex, startToken + "$1" + endToken);
1232
1233 // Escape the HTML to get rid of angle brackets
1234 logEntryContent = escapeHtml(logEntryContent);
1235
1236 // Substitute the proper HTML back in for the search match
1237 var result;
1238 var searchString = logEntryContent;
1239 logEntryContent = "";
1240 wrappedLogEntryContent = "";
1241 while ((searchIndex = searchString.indexOf(startToken, startIndex)) > -1) {
1242 var endTokenIndex = searchString.indexOf(endToken, searchIndex);
1243 matchedText = searchString.substring(searchIndex + startToken.length, endTokenIndex);
1244 textBeforeMatch = searchString.substring(startIndex, searchIndex);
1245 logEntryContent += preStartTag + textBeforeMatch + preEndTag;
1246 logEntryContent += searchTermReplacementStartTag + preStartTag + matchedText +
1247 preEndTag + searchTermReplacementEndTag;
1248 if (isIe) {
1249 wrappedLogEntryContent += textBeforeMatch + searchTermReplacementStartTag +
1250 matchedText + searchTermReplacementEndTag;
1251 }
1252 startIndex = endTokenIndex + endToken.length;
1253 }
1254 logEntryContent += preStartTag + searchString.substr(startIndex) + preEndTag;
1255 if (isIe) {
1256 wrappedLogEntryContent += searchString.substr(startIndex);
1257 }
1258 } else {
1259 logEntryContent = "";
1260 wrappedLogEntryContent = "";
1261 var searchTermReplacementLength = searchTermReplacementStartTag.length +
1262 this.searchTerm.length + searchTermReplacementEndTag.length;
1263 var searchTermLength = this.searchTerm.length;
1264 var searchTermLowerCase = this.searchTerm.toLowerCase();
1265 var logTextLowerCase = logEntry.formattedMessage.toLowerCase();
1266 while ((searchIndex = logTextLowerCase.indexOf(searchTermLowerCase, startIndex)) > -1) {
1267 matchedText = escapeHtml(logEntry.formattedMessage.substr(searchIndex, this.searchTerm.length));
1268 textBeforeMatch = escapeHtml(logEntry.formattedMessage.substring(startIndex, searchIndex));
1269 var searchTermReplacement = searchTermReplacementStartTag +
1270 preStartTag + matchedText + preEndTag + searchTermReplacementEndTag;
1271 logEntryContent += preStartTag + textBeforeMatch + preEndTag + searchTermReplacement;
1272 if (isIe) {
1273 wrappedLogEntryContent += textBeforeMatch + searchTermReplacementStartTag +
1274 matchedText + searchTermReplacementEndTag;
1275 }
1276 startIndex = searchIndex + searchTermLength;
1277 }
1278 var textAfterLastMatch = escapeHtml(logEntry.formattedMessage.substr(startIndex));
1279 logEntryContent += preStartTag + textAfterLastMatch + preEndTag;
1280 if (isIe) {
1281 wrappedLogEntryContent += textAfterLastMatch;
1282 }
1283 }
1284 logEntry.setContent(logEntryContent, wrappedLogEntryContent);
1285 var logEntryMatches = logEntry.getSearchMatches();
1286 this.matches = this.matches.concat(logEntryMatches);
1287 } else {
1288 logEntry.setSearchMatch(false);
1289 logEntry.setContent(logEntry.formattedMessage, logEntry.formattedMessage);
1290 }
1291 return doesMatch;
1292 },
1293
1294 removeMatches: function(logEntries) {
1295 var matchesToRemoveCount = 0;
1296 var currentMatchRemoved = false;
1297 var matchesToRemove = [];
1298 var i, iLen, j, jLen;
1299
1300 // Establish the list of matches to be removed
1301 for (i = 0, iLen = this.matches.length; i < iLen; i++) {
1302 for (j = 0, jLen = logEntries.length; j < jLen; j++) {
1303 if (this.matches[i].belongsTo(logEntries[j])) {
1304 matchesToRemove.push(this.matches[i]);
1305 if (i === currentMatchIndex) {
1306 currentMatchRemoved = true;
1307 }
1308 }
1309 }
1310 }
1311
1312 // Set the new current match index if the current match has been deleted
1313 // This will be the first match that appears after the first log entry being
1314 // deleted, if one exists; otherwise, it's the first match overall
1315 var newMatch = currentMatchRemoved ? null : this.matches[currentMatchIndex];
1316 if (currentMatchRemoved) {
1317 for (i = currentMatchIndex, iLen = this.matches.length; i < iLen; i++) {
1318 if (this.matches[i].isVisible() && !array_contains(matchesToRemove, this.matches[i])) {
1319 newMatch = this.matches[i];
1320 break;
1321 }
1322 }
1323 }
1324
1325 // Remove the matches
1326 for (i = 0, iLen = matchesToRemove.length; i < iLen; i++) {
1327 array_remove(this.matches, matchesToRemove[i]);
1328 matchesToRemove[i].remove();
1329 }
1330
1331 // Set the new match, if one exists
1332 if (this.hasVisibleMatches()) {
1333 if (newMatch === null) {
1334 setCurrentMatchIndex(0);
1335 } else {
1336 // Get the index of the new match
1337 var newMatchIndex = 0;
1338 for (i = 0, iLen = this.matches.length; i < iLen; i++) {
1339 if (newMatch === this.matches[i]) {
1340 newMatchIndex = i;
1341 break;
1342 }
1343 }
1344 setCurrentMatchIndex(newMatchIndex);
1345 }
1346 } else {
1347 currentMatchIndex = null;
1348 displayNoMatches();
1349 }
1350 }
1351 };
1352
1353 function getPageOffsetTop(el, container) {
1354 var currentEl = el;
1355 var y = 0;
1356 while (currentEl && currentEl != container) {
1357 y += currentEl.offsetTop;
1358 currentEl = currentEl.offsetParent;
1359 }
1360 return y;
1361 }
1362
1363 function scrollIntoView(el) {
1364 var logContainer = logMainContainer;
1365 // Check if the whole width of the element is visible and centre if not
1366 if (!$("wrap").checked) {
1367 var logContainerLeft = logContainer.scrollLeft;
1368 var logContainerRight = logContainerLeft + logContainer.offsetWidth;
1369 var elLeft = el.offsetLeft;
1370 var elRight = elLeft + el.offsetWidth;
1371 if (elLeft < logContainerLeft || elRight > logContainerRight) {
1372 logContainer.scrollLeft = elLeft - (logContainer.offsetWidth - el.offsetWidth) / 2;
1373 }
1374 }
1375 // Check if the whole height of the element is visible and centre if not
1376 var logContainerTop = logContainer.scrollTop;
1377 var logContainerBottom = logContainerTop + logContainer.offsetHeight;
1378 var elTop = getPageOffsetTop(el) - getToolBarsHeight();
1379 var elBottom = elTop + el.offsetHeight;
1380 if (elTop < logContainerTop || elBottom > logContainerBottom) {
1381 logContainer.scrollTop = elTop - (logContainer.offsetHeight - el.offsetHeight) / 2;
1382 }
1383 }
1384
1385 function Match(logEntryLevel, spanInMainDiv, spanInUnwrappedPre, spanInWrappedDiv) {
1386 this.logEntryLevel = logEntryLevel;
1387 this.spanInMainDiv = spanInMainDiv;
1388 if (isIe) {
1389 this.spanInUnwrappedPre = spanInUnwrappedPre;
1390 this.spanInWrappedDiv = spanInWrappedDiv;
1391 }
1392 this.mainSpan = isIe ? spanInUnwrappedPre : spanInMainDiv;
1393 }
1394
1395 Match.prototype = {
1396 equals: function(match) {
1397 return this.mainSpan === match.mainSpan;
1398 },
1399
1400 setCurrent: function() {
1401 if (isIe) {
1402 addClass(this.spanInUnwrappedPre, "currentmatch");
1403 addClass(this.spanInWrappedDiv, "currentmatch");
1404 // Scroll the visible one into view
1405 var elementToScroll = $("wrap").checked ? this.spanInWrappedDiv : this.spanInUnwrappedPre;
1406 scrollIntoView(elementToScroll);
1407 } else {
1408 addClass(this.spanInMainDiv, "currentmatch");
1409 scrollIntoView(this.spanInMainDiv);
1410 }
1411 },
1412
1413 belongsTo: function(logEntry) {
1414 if (isIe) {
1415 return isDescendant(this.spanInUnwrappedPre, logEntry.unwrappedPre);
1416 } else {
1417 return isDescendant(this.spanInMainDiv, logEntry.mainDiv);
1418 }
1419 },
1420
1421 setNotCurrent: function() {
1422 if (isIe) {
1423 removeClass(this.spanInUnwrappedPre, "currentmatch");
1424 removeClass(this.spanInWrappedDiv, "currentmatch");
1425 } else {
1426 removeClass(this.spanInMainDiv, "currentmatch");
1427 }
1428 },
1429
1430 isOrphan: function() {
1431 return isOrphan(this.mainSpan);
1432 },
1433
1434 isVisible: function() {
1435 return getCheckBox(this.logEntryLevel).checked;
1436 },
1437
1438 remove: function() {
1439 if (isIe) {
1440 this.spanInUnwrappedPre = null;
1441 this.spanInWrappedDiv = null;
1442 } else {
1443 this.spanInMainDiv = null;
1444 }
1445 }
1446 };
1447
1448 var currentSearch = null;
1449 var currentMatchIndex = null;
1450
1451 function doSearch() {
1452 var searchBox = $("searchBox");
1453 var searchTerm = searchBox.value;
1454 var isRegex = $("searchRegex").checked;
1455 var isCaseSensitive = $("searchCaseSensitive").checked;
1456 var i;
1457
1458 if (searchTerm === "") {
1459 $("searchReset").disabled = true;
1460 $("searchNav").style.display = "none";
1461 removeClass(document.body, "searching");
1462 removeClass(searchBox, "hasmatches");
1463 removeClass(searchBox, "nomatches");
1464 for (i = 0; i < logEntries.length; i++) {
1465 logEntries[i].clearSearch();
1466 logEntries[i].setContent(logEntries[i].formattedMessage, logEntries[i].formattedMessage);
1467 }
1468 currentSearch = null;
1469 setLogContainerHeight();
1470 } else {
1471 $("searchReset").disabled = false;
1472 $("searchNav").style.display = "block";
1473 var searchRegex;
1474 var regexValid;
1475 if (isRegex) {
1476 try {
1477 searchRegex = isCaseSensitive ? new RegExp(searchTerm, "g") : new RegExp(searchTerm, "gi");
1478 regexValid = true;
1479 replaceClass(searchBox, "validregex", "invalidregex");
1480 searchBox.title = "Valid regex";
1481 } catch (ex) {
1482 regexValid = false;
1483 replaceClass(searchBox, "invalidregex", "validregex");
1484 searchBox.title = "Invalid regex: " + (ex.message ? ex.message : (ex.description ? ex.description : "unknown error"));
1485 return;
1486 }
1487 } else {
1488 searchBox.title = "";
1489 removeClass(searchBox, "validregex");
1490 removeClass(searchBox, "invalidregex");
1491 }
1492 addClass(document.body, "searching");
1493 currentSearch = new Search(searchTerm, isRegex, searchRegex, isCaseSensitive);
1494 for (i = 0; i < logEntries.length; i++) {
1495 currentSearch.applyTo(logEntries[i]);
1496 }
1497 setLogContainerHeight();
1498
1499 // Highlight the first search match
1500 if (currentSearch.hasVisibleMatches()) {
1501 setCurrentMatchIndex(0);
1502 displayMatches();
1503 } else {
1504 displayNoMatches();
1505 }
1506 }
1507 }
1508
1509 function updateSearchFromFilters() {
1510 if (currentSearch) {
1511 if (currentSearch.hasMatches()) {
1512 if (currentMatchIndex === null) {
1513 currentMatchIndex = 0;
1514 }
1515 var currentMatch = currentSearch.matches[currentMatchIndex];
1516 if (currentMatch.isVisible()) {
1517 displayMatches();
1518 setCurrentMatchIndex(currentMatchIndex);
1519 } else {
1520 currentMatch.setNotCurrent();
1521 // Find the next visible match, if one exists
1522 var nextVisibleMatchIndex = currentSearch.getNextVisibleMatchIndex();
1523 if (nextVisibleMatchIndex > -1) {
1524 setCurrentMatchIndex(nextVisibleMatchIndex);
1525 displayMatches();
1526 } else {
1527 displayNoMatches();
1528 }
1529 }
1530 } else {
1531 displayNoMatches();
1532 }
1533 }
1534 }
1535
1536 function refreshCurrentMatch() {
1537 if (currentSearch && currentSearch.hasVisibleMatches()) {
1538 setCurrentMatchIndex(currentMatchIndex);
1539 }
1540 }
1541
1542 function displayMatches() {
1543 replaceClass($("searchBox"), "hasmatches", "nomatches");
1544 $("searchBox").title = "" + currentSearch.matches.length + " matches found";
1545 $("searchNav").style.display = "block";
1546 setLogContainerHeight();
1547 }
1548
1549 function displayNoMatches() {
1550 replaceClass($("searchBox"), "nomatches", "hasmatches");
1551 $("searchBox").title = "No matches found";
1552 $("searchNav").style.display = "none";
1553 setLogContainerHeight();
1554 }
1555
1556 function toggleSearchEnabled(enable) {
1557 enable = (typeof enable == "undefined") ? !$("searchDisable").checked : enable;
1558 $("searchBox").disabled = !enable;
1559 $("searchReset").disabled = !enable;
1560 $("searchRegex").disabled = !enable;
1561 $("searchNext").disabled = !enable;
1562 $("searchPrevious").disabled = !enable;
1563 $("searchCaseSensitive").disabled = !enable;
1564 $("searchNav").style.display = (enable && ($("searchBox").value !== "") &&
1565 currentSearch && currentSearch.hasVisibleMatches()) ?
1566 "block" : "none";
1567 if (enable) {
1568 removeClass($("search"), "greyedout");
1569 addClass(document.body, "searching");
1570 if ($("searchHighlight").checked) {
1571 addClass(logMainContainer, "searchhighlight");
1572 } else {
1573 removeClass(logMainContainer, "searchhighlight");
1574 }
1575 if ($("searchFilter").checked) {
1576 addClass(logMainContainer, "searchfilter");
1577 } else {
1578 removeClass(logMainContainer, "searchfilter");
1579 }
1580 $("searchDisable").checked = !enable;
1581 } else {
1582 addClass($("search"), "greyedout");
1583 removeClass(document.body, "searching");
1584 removeClass(logMainContainer, "searchhighlight");
1585 removeClass(logMainContainer, "searchfilter");
1586 }
1587 setLogContainerHeight();
1588 }
1589
1590 function toggleSearchFilter() {
1591 var enable = $("searchFilter").checked;
1592 if (enable) {
1593 addClass(logMainContainer, "searchfilter");
1594 } else {
1595 removeClass(logMainContainer, "searchfilter");
1596 }
1597 refreshCurrentMatch();
1598 }
1599
1600 function toggleSearchHighlight() {
1601 var enable = $("searchHighlight").checked;
1602 if (enable) {
1603 addClass(logMainContainer, "searchhighlight");
1604 } else {
1605 removeClass(logMainContainer, "searchhighlight");
1606 }
1607 }
1608
1609 function clearSearch() {
1610 $("searchBox").value = "";
1611 doSearch();
1612 }
1613
1614 function searchNext() {
1615 if (currentSearch !== null && currentMatchIndex !== null) {
1616 currentSearch.matches[currentMatchIndex].setNotCurrent();
1617 var nextMatchIndex = currentSearch.getNextVisibleMatchIndex();
1618 if (nextMatchIndex > currentMatchIndex || confirm("Reached the end of the page. Start from the top?")) {
1619 setCurrentMatchIndex(nextMatchIndex);
1620 }
1621 }
1622 }
1623
1624 function searchPrevious() {
1625 if (currentSearch !== null && currentMatchIndex !== null) {
1626 currentSearch.matches[currentMatchIndex].setNotCurrent();
1627 var previousMatchIndex = currentSearch.getPreviousVisibleMatchIndex();
1628 if (previousMatchIndex < currentMatchIndex || confirm("Reached the start of the page. Continue from the bottom?")) {
1629 setCurrentMatchIndex(previousMatchIndex);
1630 }
1631 }
1632 }
1633
1634 function setCurrentMatchIndex(index) {
1635 currentMatchIndex = index;
1636 currentSearch.matches[currentMatchIndex].setCurrent();
1637 }
1638
1639 /* ------------------------------------------------------------------------- */
1640
1641 // CSS Utilities
1642
1643 function addClass(el, cssClass) {
1644 if (!hasClass(el, cssClass)) {
1645 if (el.className) {
1646 el.className += " " + cssClass;
1647 } else {
1648 el.className = cssClass;
1649 }
1650 }
1651 }
1652
1653 function hasClass(el, cssClass) {
1654 if (el.className) {
1655 var classNames = el.className.split(" ");
1656 return array_contains(classNames, cssClass);
1657 }
1658 return false;
1659 }
1660
1661 function removeClass(el, cssClass) {
1662 if (hasClass(el, cssClass)) {
1663 // Rebuild the className property
1664 var existingClasses = el.className.split(" ");
1665 var newClasses = [];
1666 for (var i = 0, len = existingClasses.length; i < len; i++) {
1667 if (existingClasses[i] != cssClass) {
1668 newClasses[newClasses.length] = existingClasses[i];
1669 }
1670 }
1671 el.className = newClasses.join(" ");
1672 }
1673 }
1674
1675 function replaceClass(el, newCssClass, oldCssClass) {
1676 removeClass(el, oldCssClass);
1677 addClass(el, newCssClass);
1678 }
1679
1680 /* ------------------------------------------------------------------------- */
1681
1682 // Other utility functions
1683
1684 function getElementsByClass(el, cssClass, tagName) {
1685 var elements = el.getElementsByTagName(tagName);
1686 var matches = [];
1687 for (var i = 0, len = elements.length; i < len; i++) {
1688 if (hasClass(elements[i], cssClass)) {
1689 matches.push(elements[i]);
1690 }
1691 }
1692 return matches;
1693 }
1694
1695 // Syntax borrowed from Prototype library
1696 function $(id) {
1697 return document.getElementById(id);
1698 }
1699
1700 function isDescendant(node, ancestorNode) {
1701 while (node != null) {
1702 if (node === ancestorNode) {
1703 return true;
1704 }
1705 node = node.parentNode;
1706 }
1707 return false;
1708 }
1709
1710 function isOrphan(node) {
1711 var currentNode = node;
1712 while (currentNode) {
1713 if (currentNode == document.body) {
1714 return false;
1715 }
1716 currentNode = currentNode.parentNode;
1717 }
1718 return true;
1719 }
1720
1721 function escapeHtml(str) {
1722 return str.replace(/&/g, "&amp;").replace(/[<]/g, "&lt;").replace(/>/g, "&gt;");
1723 }
1724
1725 function getWindowWidth() {
1726 if (window.innerWidth) {
1727 return window.innerWidth;
1728 } else if (document.documentElement && document.documentElement.clientWidth) {
1729 return document.documentElement.clientWidth;
1730 } else if (document.body) {
1731 return document.body.clientWidth;
1732 }
1733 return 0;
1734 }
1735
1736 function getWindowHeight() {
1737 if (window.innerHeight) {
1738 return window.innerHeight;
1739 } else if (document.documentElement && document.documentElement.clientHeight) {
1740 return document.documentElement.clientHeight;
1741 } else if (document.body) {
1742 return document.body.clientHeight;
1743 }
1744 return 0;
1745 }
1746
1747 function getToolBarsHeight() {
1748 return $("switches").offsetHeight;
1749 }
1750
1751 function getChromeHeight() {
1752 var height = getToolBarsHeight();
1753 if (showCommandLine) {
1754 height += $("commandLine").offsetHeight;
1755 }
1756 return height;
1757 }
1758
1759 function setLogContainerHeight() {
1760 if (logMainContainer) {
1761 var windowHeight = getWindowHeight();
1762 $("body").style.height = getWindowHeight() + "px";
1763 logMainContainer.style.height = "" +
1764 Math.max(0, windowHeight - getChromeHeight()) + "px";
1765 }
1766 }
1767
1768 function setCommandInputWidth() {
1769 if (showCommandLine) {
1770 $("command").style.width = "" + Math.max(0, $("commandLineContainer").offsetWidth -
1771 ($("evaluateButton").offsetWidth + 13)) + "px";
1772 }
1773 }
1774
1775 window.onresize = function() {
1776 setCommandInputWidth();
1777 setLogContainerHeight();
1778 };
1779
1780 if (!Array.prototype.push) {
1781 Array.prototype.push = function() {
1782 for (var i = 0, len = arguments.length; i < len; i++){
1783 this[this.length] = arguments[i];
1784 }
1785 return this.length;
1786 };
1787 }
1788
1789 if (!Array.prototype.pop) {
1790 Array.prototype.pop = function() {
1791 if (this.length > 0) {
1792 var val = this[this.length - 1];
1793 this.length = this.length - 1;
1794 return val;
1795 }
1796 };
1797 }
1798
1799 if (!Array.prototype.shift) {
1800 Array.prototype.shift = function() {
1801 if (this.length > 0) {
1802 var firstItem = this[0];
1803 for (var i = 0, len = this.length - 1; i < len; i++) {
1804 this[i] = this[i + 1];
1805 }
1806 this.length = this.length - 1;
1807 return firstItem;
1808 }
1809 };
1810 }
1811
1812 if (!Array.prototype.splice) {
1813 Array.prototype.splice = function(startIndex, deleteCount) {
1814 var itemsAfterDeleted = this.slice(startIndex + deleteCount);
1815 var itemsDeleted = this.slice(startIndex, startIndex + deleteCount);
1816 this.length = startIndex;
1817 // Copy the arguments into a proper Array object
1818 var argumentsArray = [];
1819 for (var i = 0, len = arguments.length; i < len; i++) {
1820 argumentsArray[i] = arguments[i];
1821 }
1822 var itemsToAppend = (argumentsArray.length > 2) ?
1823 itemsAfterDeleted = argumentsArray.slice(2).concat(itemsAfterDeleted) : itemsAfterDeleted;
1824 for (i = 0, len = itemsToAppend.length; i < len; i++) {
1825 this.push(itemsToAppend[i]);
1826 }
1827 return itemsDeleted;
1828 };
1829 }
1830
1831 function array_remove(arr, val) {
1832 var index = -1;
1833 for (var i = 0, len = arr.length; i < len; i++) {
1834 if (arr[i] === val) {
1835 index = i;
1836 break;
1837 }
1838 }
1839 if (index >= 0) {
1840 arr.splice(index, 1);
1841 return index;
1842 } else {
1843 return false;
1844 }
1845 }
1846
1847 function array_removeFromStart(array, numberToRemove) {
1848 if (Array.prototype.splice) {
1849 array.splice(0, numberToRemove);
1850 } else {
1851 for (var i = numberToRemove, len = array.length; i < len; i++) {
1852 array[i - numberToRemove] = array[i];
1853 }
1854 array.length = array.length - numberToRemove;
1855 }
1856 return array;
1857 }
1858
1859 function array_contains(arr, val) {
1860 for (var i = 0, len = arr.length; i < len; i++) {
1861 if (arr[i] == val) {
1862 return true;
1863 }
1864 }
1865 return false;
1866 }
1867
1868 function getErrorMessage(ex) {
1869 if (ex.message) {
1870 return ex.message;
1871 } else if (ex.description) {
1872 return ex.description;
1873 }
1874 return "" + ex;
1875 }
1876
1877 function moveCaretToEnd(input) {
1878 if (input.setSelectionRange) {
1879 input.focus();
1880 var length = input.value.length;
1881 input.setSelectionRange(length, length);
1882 } else if (input.createTextRange) {
1883 var range = input.createTextRange();
1884 range.collapse(false);
1885 range.select();
1886 }
1887 input.focus();
1888 }
1889
1890 function stopPropagation(evt) {
1891 if (evt.stopPropagation) {
1892 evt.stopPropagation();
1893 } else if (typeof evt.cancelBubble != "undefined") {
1894 evt.cancelBubble = true;
1895 }
1896 }
1897
1898 function getEvent(evt) {
1899 return evt ? evt : event;
1900 }
1901
1902 function getTarget(evt) {
1903 return evt.target ? evt.target : evt.srcElement;
1904 }
1905
1906 function getRelatedTarget(evt) {
1907 if (evt.relatedTarget) {
1908 return evt.relatedTarget;
1909 } else if (evt.srcElement) {
1910 switch(evt.type) {
1911 case "mouseover":
1912 return evt.fromElement;
1913 case "mouseout":
1914 return evt.toElement;
1915 default:
1916 return evt.srcElement;
1917 }
1918 }
1919 }
1920
1921 function cancelKeyEvent(evt) {
1922 evt.returnValue = false;
1923 stopPropagation(evt);
1924 }
1925
1926 function evalCommandLine() {
1927 var expr = $("command").value;
1928 evalCommand(expr);
1929 $("command").value = "";
1930 }
1931
1932 function evalLastCommand() {
1933 if (lastCommand != null) {
1934 evalCommand(lastCommand);
1935 }
1936 }
1937
1938 var lastCommand = null;
1939 var commandHistory = [];
1940 var currentCommandIndex = 0;
1941
1942 function evalCommand(expr) {
1943 if (appender) {
1944 appender.evalCommandAndAppend(expr);
1945 } else {
1946 var prefix = ">>> " + expr + "\r\n";
1947 try {
1948 log("INFO", prefix + eval(expr));
1949 } catch (ex) {
1950 log("ERROR", prefix + "Error: " + getErrorMessage(ex));
1951 }
1952 }
1953 // Update command history
1954 if (expr != commandHistory[commandHistory.length - 1]) {
1955 commandHistory.push(expr);
1956 // Update the appender
1957 if (appender) {
1958 appender.storeCommandHistory(commandHistory);
1959 }
1960 }
1961 currentCommandIndex = (expr == commandHistory[currentCommandIndex]) ? currentCommandIndex + 1 : commandHistory.length;
1962 lastCommand = expr;
1963 }
1964 //]]>
1965 </script>
1966 <style type="text/css">
1967 body {
1968 background-color: white;
1969 color: black;
1970 padding: 0;
1971 margin: 0;
1972 font-family: tahoma, verdana, arial, helvetica, sans-serif;
1973 overflow: hidden;
1974 }
1975
1976 div#switchesContainer input {
1977 margin-bottom: 0;
1978 }
1979
1980 div.toolbar {
1981 border-top: solid #ffffff 1px;
1982 border-bottom: solid #aca899 1px;
1983 background-color: #f1efe7;
1984 padding: 3px 5px;
1985 font-size: 68.75%;
1986 }
1987
1988 div.toolbar, div#search input {
1989 font-family: tahoma, verdana, arial, helvetica, sans-serif;
1990 }
1991
1992 div.toolbar input.button {
1993 padding: 0 5px;
1994 font-size: 100%;
1995 }
1996
1997 div.toolbar input.hidden {
1998 display: none;
1999 }
2000
2001 div#switches input#clearButton {
2002 margin-left: 20px;
2003 }
2004
2005 div#levels label {
2006 font-weight: bold;
2007 }
2008
2009 div#levels label, div#options label {
2010 margin-right: 5px;
2011 }
2012
2013 div#levels label#wrapLabel {
2014 font-weight: normal;
2015 }
2016
2017 div#search label {
2018 margin-right: 10px;
2019 }
2020
2021 div#search label.searchboxlabel {
2022 margin-right: 0;
2023 }
2024
2025 div#search input {
2026 font-size: 100%;
2027 }
2028
2029 div#search input.validregex {
2030 color: green;
2031 }
2032
2033 div#search input.invalidregex {
2034 color: red;
2035 }
2036
2037 div#search input.nomatches {
2038 color: white;
2039 background-color: #ff6666;
2040 }
2041
2042 div#search input.nomatches {
2043 color: white;
2044 background-color: #ff6666;
2045 }
2046
2047 div#searchNav {
2048 display: none;
2049 }
2050
2051 div#commandLine {
2052 display: none;
2053 }
2054
2055 div#commandLine input#command {
2056 font-size: 100%;
2057 font-family: Courier New, Courier;
2058 }
2059
2060 div#commandLine input#evaluateButton {
2061 }
2062
2063 *.greyedout {
2064 color: gray !important;
2065 border-color: gray !important;
2066 }
2067
2068 *.greyedout *.alwaysenabled { color: black; }
2069
2070 *.unselectable {
2071 -khtml-user-select: none;
2072 -moz-user-select: none;
2073 user-select: none;
2074 }
2075
2076 div#log {
2077 font-family: Courier New, Courier;
2078 font-size: 75%;
2079 width: 100%;
2080 overflow: auto;
2081 clear: both;
2082 position: relative;
2083 }
2084
2085 div.group {
2086 border-color: #cccccc;
2087 border-style: solid;
2088 border-width: 1px 0 1px 1px;
2089 overflow: visible;
2090 }
2091
2092 div.oldIe div.group, div.oldIe div.group *, div.oldIe *.logentry {
2093 height: 1%;
2094 }
2095
2096 div.group div.groupheading span.expander {
2097 border: solid black 1px;
2098 font-family: Courier New, Courier;
2099 font-size: 0.833em;
2100 background-color: #eeeeee;
2101 position: relative;
2102 top: -1px;
2103 color: black;
2104 padding: 0 2px;
2105 cursor: pointer;
2106 cursor: hand;
2107 height: 1%;
2108 }
2109
2110 div.group div.groupcontent {
2111 margin-left: 10px;
2112 padding-bottom: 2px;
2113 overflow: visible;
2114 }
2115
2116 div.group div.expanded {
2117 display: block;
2118 }
2119
2120 div.group div.collapsed {
2121 display: none;
2122 }
2123
2124 *.logentry {
2125 overflow: visible;
2126 display: none;
2127 white-space: pre;
2128 }
2129
2130 span.pre {
2131 white-space: pre;
2132 }
2133
2134 pre.unwrapped {
2135 display: inline !important;
2136 }
2137
2138 pre.unwrapped pre.pre, div.wrapped pre.pre {
2139 display: inline;
2140 }
2141
2142 div.wrapped pre.pre {
2143 white-space: normal;
2144 }
2145
2146 div.wrapped {
2147 display: none;
2148 }
2149
2150 body.searching *.logentry span.currentmatch {
2151 color: white !important;
2152 background-color: green !important;
2153 }
2154
2155 body.searching div.searchhighlight *.logentry span.searchterm {
2156 color: black;
2157 background-color: yellow;
2158 }
2159
2160 div.wrap *.logentry {
2161 white-space: normal !important;
2162 border-width: 0 0 1px 0;
2163 border-color: #dddddd;
2164 border-style: dotted;
2165 }
2166
2167 div.wrap #log_wrapped, #log_unwrapped {
2168 display: block;
2169 }
2170
2171 div.wrap #log_unwrapped, #log_wrapped {
2172 display: none;
2173 }
2174
2175 div.wrap *.logentry span.pre {
2176 overflow: visible;
2177 white-space: normal;
2178 }
2179
2180 div.wrap *.logentry pre.unwrapped {
2181 display: none;
2182 }
2183
2184 div.wrap *.logentry span.wrapped {
2185 display: inline;
2186 }
2187
2188 div.searchfilter *.searchnonmatch {
2189 display: none !important;
2190 }
2191
2192 div#log *.TRACE, label#label_TRACE {
2193 color: #666666;
2194 }
2195
2196 div#log *.DEBUG, label#label_DEBUG {
2197 color: green;
2198 }
2199
2200 div#log *.INFO, label#label_INFO {
2201 color: #000099;
2202 }
2203
2204 div#log *.WARN, label#label_WARN {
2205 color: #999900;
2206 }
2207
2208 div#log *.ERROR, label#label_ERROR {
2209 color: red;
2210 }
2211
2212 div#log *.FATAL, label#label_FATAL {
2213 color: #660066;
2214 }
2215
2216 div.TRACE#log *.TRACE,
2217 div.DEBUG#log *.DEBUG,
2218 div.INFO#log *.INFO,
2219 div.WARN#log *.WARN,
2220 div.ERROR#log *.ERROR,
2221 div.FATAL#log *.FATAL {
2222 display: block;
2223 }
2224
2225 div#log div.separator {
2226 background-color: #cccccc;
2227 margin: 5px 0;
2228 line-height: 1px;
2229 }
2230 </style>
2231 </head>
2232
2233 <body id="body">
2234 <div id="switchesContainer">
2235 <div id="switches">
2236 <div id="levels" class="toolbar">
2237 Filters:
2238 <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>
2239 <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>
2240 <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>
2241 <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>
2242 <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>
2243 <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>
2244 <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>
2245 </div>
2246 <div id="search" class="toolbar">
2247 <label for="searchBox" class="searchboxlabel">Search:</label> <input type="text" id="searchBox" onclick="toggleSearchEnabled(true)" onkeyup="scheduleSearch()" size="20" />
2248 <input type="button" id="searchReset" disabled="disabled" value="Reset" onclick="clearSearch()" class="button" title="Reset the search" />
2249 <input type="checkbox" id="searchRegex" onclick="doSearch()" title="If checked, search is treated as a regular expression" /><label for="searchRegex">Regex</label>
2250 <input type="checkbox" id="searchCaseSensitive" onclick="doSearch()" title="If checked, search is case sensitive" /><label for="searchCaseSensitive">Match case</label>
2251 <input type="checkbox" id="searchDisable" onclick="toggleSearchEnabled()" title="Enable/disable search" /><label for="searchDisable" class="alwaysenabled">Disable</label>
2252 <div id="searchNav">
2253 <input type="button" id="searchNext" disabled="disabled" value="Next" onclick="searchNext()" class="button" title="Go to the next matching log entry" />
2254 <input type="button" id="searchPrevious" disabled="disabled" value="Previous" onclick="searchPrevious()" class="button" title="Go to the previous matching log entry" />
2255 <input type="checkbox" id="searchFilter" onclick="toggleSearchFilter()" title="If checked, non-matching log entries are filtered out" /><label for="searchFilter">Filter</label>
2256 <input type="checkbox" id="searchHighlight" onclick="toggleSearchHighlight()" title="Highlight matched search terms" /><label for="searchHighlight" class="alwaysenabled">Highlight all</label>
2257 </div>
2258 </div>
2259 <div id="options" class="toolbar">
2260 Options:
2261 <input type="checkbox" id="enableLogging" onclick="toggleLoggingEnabled()" checked="checked" title="Enable/disable logging" /><label for="enableLogging" id="enableLoggingLabel">Log</label>
2262 <input type="checkbox" id="wrap" onclick="toggleWrap()" title="Enable / disable word wrap" /><label for="wrap" id="wrapLabel">Wrap</label>
2263 <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>
2264 <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>
2265 <input type="button" id="clearButton" value="Clear" onclick="clearLog()" class="button" title="Clear all log messages" />
2266 <input type="button" id="hideButton" value="Hide" onclick="hide()" class="hidden button" title="Hide the console" />
2267 <input type="button" id="closeButton" value="Close" onclick="closeWindow()" class="hidden button" title="Close the window" />
2268 </div>
2269 </div>
2270 </div>
2271 <div id="log" class="TRACE DEBUG INFO WARN ERROR FATAL"></div>
2272 <div id="commandLine" class="toolbar">
2273 <div id="commandLineContainer">
2274 <input type="text" id="command" title="Enter a JavaScript command here and hit return or press 'Evaluate'" />
2275 <input type="button" id="evaluateButton" value="Evaluate" class="button" title="Evaluate the command" onclick="evalCommandLine()" />
2276 </div>
2277 </div>
2278 </body>
2279</html>