Merge commit 'catalyst/MOODLE_19_STABLE' into mdl19-linuxchix
[moodle-linuxchix.git] / lib / yui / logger / logger.js
blobe778559510c59775f69590966fcbf5fdab072796
1 /*
2 Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.5.2
6 */
7 /****************************************************************************/
8 /****************************************************************************/
9 /****************************************************************************/
11 /**
12  * The LogMsg class defines a single log message.
13  *
14  * @class LogMsg
15  * @constructor
16  * @param oConfigs {Object} Object literal of configuration params.
17  */
18 YAHOO.widget.LogMsg = function(oConfigs) {
19     // Parse configs
20     /**
21      * Log message.
22      *
23      * @property msg
24      * @type String
25      */
26     this.msg =
27     /**
28      * Log timestamp.
29      *
30      * @property time
31      * @type Date
32      */
33     this.time =
35     /**
36      * Log category.
37      *
38      * @property category
39      * @type String
40      */
41     this.category =
43     /**
44      * Log source. The first word passed in as the source argument.
45      *
46      * @property source
47      * @type String
48      */
49     this.source =
51     /**
52      * Log source detail. The remainder of the string passed in as the source argument, not
53      * including the first word (if any).
54      *
55      * @property sourceDetail
56      * @type String
57      */
58     this.sourceDetail = null;
60     if (oConfigs && (oConfigs.constructor == Object)) {
61         for(var param in oConfigs) {
62             this[param] = oConfigs[param];
63         }
64     }
67 /****************************************************************************/
68 /****************************************************************************/
69 /****************************************************************************/
71 /**
72  * The LogWriter class provides a mechanism to log messages through
73  * YAHOO.widget.Logger from a named source.
74  *
75  * @class LogWriter
76  * @constructor
77  * @param sSource {String} Source of LogWriter instance.
78  */
79 YAHOO.widget.LogWriter = function(sSource) {
80     if(!sSource) {
81         YAHOO.log("Could not instantiate LogWriter due to invalid source.",
82             "error", "LogWriter");
83         return;
84     }
85     this._source = sSource;
86  };
88 /////////////////////////////////////////////////////////////////////////////
90 // Public methods
92 /////////////////////////////////////////////////////////////////////////////
94  /**
95  * Public accessor to the unique name of the LogWriter instance.
96  *
97  * @method toString
98  * @return {String} Unique name of the LogWriter instance.
99  */
100 YAHOO.widget.LogWriter.prototype.toString = function() {
101     return "LogWriter " + this._sSource;
105  * Logs a message attached to the source of the LogWriter.
107  * @method log
108  * @param sMsg {String} The log message.
109  * @param sCategory {String} Category name.
110  */
111 YAHOO.widget.LogWriter.prototype.log = function(sMsg, sCategory) {
112     YAHOO.widget.Logger.log(sMsg, sCategory, this._source);
116  * Public accessor to get the source name.
118  * @method getSource
119  * @return {String} The LogWriter source.
120  */
121 YAHOO.widget.LogWriter.prototype.getSource = function() {
122     return this._sSource;
126  * Public accessor to set the source name.
128  * @method setSource
129  * @param sSource {String} Source of LogWriter instance.
130  */
131 YAHOO.widget.LogWriter.prototype.setSource = function(sSource) {
132     if(!sSource) {
133         YAHOO.log("Could not set source due to invalid source.", "error", this.toString());
134         return;
135     }
136     else {
137         this._sSource = sSource;
138     }
141 /////////////////////////////////////////////////////////////////////////////
143 // Private member variables
145 /////////////////////////////////////////////////////////////////////////////
148  * Source of the LogWriter instance.
150  * @property _source
151  * @type String
152  * @private
153  */
154 YAHOO.widget.LogWriter.prototype._source = null;
159 /****************************************************************************/
160 /****************************************************************************/
161 /****************************************************************************/
164  * The LogReader class provides UI to read messages logged to YAHOO.widget.Logger.
166  * @class LogReader
167  * @constructor
168  * @param elContainer {HTMLElement} (optional) DOM element reference of an existing DIV.
169  * @param elContainer {String} (optional) String ID of an existing DIV.
170  * @param oConfigs {Object} (optional) Object literal of configuration params.
171  */
172 YAHOO.widget.LogReader = function(elContainer, oConfigs) {
173     this._sName = YAHOO.widget.LogReader._index;
174     YAHOO.widget.LogReader._index++;
175     
176     // Internal vars
177     this._buffer = []; // output buffer
178     this._filterCheckboxes = {}; // pointers to checkboxes
179     this._lastTime = YAHOO.widget.Logger.getStartTime(); // timestamp of last log message to console
181     // Parse config vars here
182     if (oConfigs && (oConfigs.constructor == Object)) {
183         for(var param in oConfigs) {
184             this[param] = oConfigs[param];
185         }
186     }
188     this._initContainerEl(elContainer);
189     if(!this._elContainer) {
190         YAHOO.log("Could not instantiate LogReader due to an invalid container element " +
191                 elContainer, "error", this.toString());
192         return;
193     }
194     
195     this._initHeaderEl();
196     this._initConsoleEl();
197     this._initFooterEl();
199     this._initDragDrop();
201     this._initCategories();
202     this._initSources();
204     // Subscribe to Logger custom events
205     YAHOO.widget.Logger.newLogEvent.subscribe(this._onNewLog, this);
206     YAHOO.widget.Logger.logResetEvent.subscribe(this._onReset, this);
208     YAHOO.widget.Logger.categoryCreateEvent.subscribe(this._onCategoryCreate, this);
209     YAHOO.widget.Logger.sourceCreateEvent.subscribe(this._onSourceCreate, this);
211     this._filterLogs();
212     YAHOO.log("LogReader initialized", null, this.toString());
215 /////////////////////////////////////////////////////////////////////////////
217 // Static member variables
219 /////////////////////////////////////////////////////////////////////////////
220 YAHOO.lang.augmentObject(YAHOO.widget.LogReader, {
221     /**
222      * Internal class member to index multiple LogReader instances.
223      *
224      * @property _memberName
225      * @static
226      * @type Number
227      * @default 0
228      * @private
229      */
230     _index : 0,
232     /**
233      * Node template for the log entries
234      * @property ENTRY_TEMPLATE
235      * @static
236      * @type {HTMLElement}
237      * @default PRE.yui-log-entry element
238      */
239     ENTRY_TEMPLATE : (function () {
240         var t = document.createElement('pre');
241         YAHOO.util.Dom.addClass(t,'yui-log-entry');
242         return t;
243     })(),
245     /**
246      * Template used for innerHTML of verbose entry output.
247      * @property VERBOSE_TEMPLATE
248      * @static
249      * @default "<span class='{category}'>{label}</span>{totalTime}ms (+{elapsedTime}) {localTime}:</p><p>{sourceAndDetail}</p><p>{message}</p>"
250      */
251     VERBOSE_TEMPLATE : "<span class='{category}'>{label}</span>{totalTime}ms (+{elapsedTime}) {localTime}:</p><p>{sourceAndDetail}</p><p>{message}</p>",
253     /**
254      * Template used for innerHTML of compact entry output.
255      * @property BASIC_TEMPLATE
256      * @static
257      * @default "<p><span class='{category}'>{label}</span>{totalTime}ms (+{elapsedTime}) {localTime}: {sourceAndDetail}: {message}</p>"
258      */
259     BASIC_TEMPLATE : "<p><span class='{category}'>{label}</span>{totalTime}ms (+{elapsedTime}) {localTime}: {sourceAndDetail}: {message}</p>"
262 /////////////////////////////////////////////////////////////////////////////
264 // Public member variables
266 /////////////////////////////////////////////////////////////////////////////
268 YAHOO.widget.LogReader.prototype = {
269     /**
270      * Whether or not LogReader is enabled to output log messages.
271      *
272      * @property logReaderEnabled
273      * @type Boolean
274      * @default true
275      */
276     logReaderEnabled : true,
278     /**
279      * Public member to access CSS width of the LogReader container.
280      *
281      * @property width
282      * @type String
283      */
284     width : null,
286     /**
287      * Public member to access CSS height of the LogReader container.
288      *
289      * @property height
290      * @type String
291      */
292     height : null,
294     /**
295      * Public member to access CSS top position of the LogReader container.
296      *
297      * @property top
298      * @type String
299      */
300     top : null,
302     /**
303      * Public member to access CSS left position of the LogReader container.
304      *
305      * @property left
306      * @type String
307      */
308     left : null,
310     /**
311      * Public member to access CSS right position of the LogReader container.
312      *
313      * @property right
314      * @type String
315      */
316     right : null,
318     /**
319      * Public member to access CSS bottom position of the LogReader container.
320      *
321      * @property bottom
322      * @type String
323      */
324     bottom : null,
326     /**
327      * Public member to access CSS font size of the LogReader container.
328      *
329      * @property fontSize
330      * @type String
331      */
332     fontSize : null,
334     /**
335      * Whether or not the footer UI is enabled for the LogReader.
336      *
337      * @property footerEnabled
338      * @type Boolean
339      * @default true
340      */
341     footerEnabled : true,
343     /**
344      * Whether or not output is verbose (more readable). Setting to true will make
345      * output more compact (less readable).
346      *
347      * @property verboseOutput
348      * @type Boolean
349      * @default true
350      */
351     verboseOutput : true,
353     /**
354      * Custom output format for log messages.  Defaults to null, which falls
355      * back to verboseOutput param deciding between LogReader.VERBOSE_TEMPLATE
356      * and LogReader.BASIC_TEMPLATE.  Use bracketed place holders to mark where
357      * message info should go.  Available place holder names include:
358      * <ul>
359      *  <li>category</li>
360      *  <li>label</li>
361      *  <li>sourceAndDetail</li>
362      *  <li>message</li>
363      *  <li>localTime</li>
364      *  <li>elapsedTime</li>
365      *  <li>totalTime</li>
366      * </ul>
367      *
368      * @property entryFormat
369      * @type String
370      * @default null
371      */
372     entryFormat : null,
374     /**
375      * Whether or not newest message is printed on top.
376      *
377      * @property newestOnTop
378      * @type Boolean
379      */
380     newestOnTop : true,
382     /**
383      * Output timeout buffer in milliseconds.
384      *
385      * @property outputBuffer
386      * @type Number
387      * @default 100
388      */
389     outputBuffer : 100,
391     /**
392      * Maximum number of messages a LogReader console will display.
393      *
394      * @property thresholdMax
395      * @type Number
396      * @default 500
397      */
398     thresholdMax : 500,
400     /**
401      * When a LogReader console reaches its thresholdMax, it will clear out messages
402      * and print out the latest thresholdMin number of messages.
403      *
404      * @property thresholdMin
405      * @type Number
406      * @default 100
407      */
408     thresholdMin : 100,
410     /**
411      * True when LogReader is in a collapsed state, false otherwise.
412      *
413      * @property isCollapsed
414      * @type Boolean
415      * @default false
416      */
417     isCollapsed : false,
419     /**
420      * True when LogReader is in a paused state, false otherwise.
421      *
422      * @property isPaused
423      * @type Boolean
424      * @default false
425      */
426     isPaused : false,
428     /**
429      * Enables draggable LogReader if DragDrop Utility is present.
430      *
431      * @property draggable
432      * @type Boolean
433      * @default true
434      */
435     draggable : true,
437     /////////////////////////////////////////////////////////////////////////////
438     //
439     // Public methods
440     //
441     /////////////////////////////////////////////////////////////////////////////
443      /**
444      * Public accessor to the unique name of the LogReader instance.
445      *
446      * @method toString
447      * @return {String} Unique name of the LogReader instance.
448      */
449     toString : function() {
450         return "LogReader instance" + this._sName;
451     },
452     /**
453      * Pauses output of log messages. While paused, log messages are not lost, but
454      * get saved to a buffer and then output upon resume of LogReader.
455      *
456      * @method pause
457      */
458     pause : function() {
459         this.isPaused = true;
460         this._btnPause.value = "Resume";
461         this._timeout = null;
462         this.logReaderEnabled = false;
463     },
465     /**
466      * Resumes output of log messages, including outputting any log messages that
467      * have been saved to buffer while paused.
468      *
469      * @method resume
470      */
471     resume : function() {
472         this.isPaused = false;
473         this._btnPause.value = "Pause";
474         this.logReaderEnabled = true;
475         this._printBuffer();
476     },
478     /**
479      * Hides UI of LogReader. Logging functionality is not disrupted.
480      *
481      * @method hide
482      */
483     hide : function() {
484         this._elContainer.style.display = "none";
485     },
487     /**
488      * Shows UI of LogReader. Logging functionality is not disrupted.
489      *
490      * @method show
491      */
492     show : function() {
493         this._elContainer.style.display = "block";
494     },
496     /**
497      * Collapses UI of LogReader. Logging functionality is not disrupted.
498      *
499      * @method collapse
500      */
501     collapse : function() {
502         this._elConsole.style.display = "none";
503         if(this._elFt) {
504             this._elFt.style.display = "none";
505         }
506         this._btnCollapse.value = "Expand";
507         this.isCollapsed = true;
508     },
510     /**
511      * Expands UI of LogReader. Logging functionality is not disrupted.
512      *
513      * @method expand
514      */
515     expand : function() {
516         this._elConsole.style.display = "block";
517         if(this._elFt) {
518             this._elFt.style.display = "block";
519         }
520         this._btnCollapse.value = "Collapse";
521         this.isCollapsed = false;
522     },
524     /**
525      * Returns related checkbox element for given filter (i.e., category or source).
526      *
527      * @method getCheckbox
528      * @param {String} Category or source name.
529      * @return {Array} Array of all filter checkboxes.
530      */
531     getCheckbox : function(filter) {
532         return this._filterCheckboxes[filter];
533     },
535     /**
536      * Returns array of enabled categories.
537      *
538      * @method getCategories
539      * @return {String[]} Array of enabled categories.
540      */
541     getCategories : function() {
542         return this._categoryFilters;
543     },
545     /**
546      * Shows log messages associated with given category.
547      *
548      * @method showCategory
549      * @param {String} Category name.
550      */
551     showCategory : function(sCategory) {
552         var filtersArray = this._categoryFilters;
553         // Don't do anything if category is already enabled
554         // Use Array.indexOf if available...
555         if(filtersArray.indexOf) {
556              if(filtersArray.indexOf(sCategory) >  -1) {
557                 return;
558             }
559         }
560         // ...or do it the old-fashioned way
561         else {
562             for(var i=0; i<filtersArray.length; i++) {
563                if(filtersArray[i] === sCategory){
564                     return;
565                 }
566             }
567         }
569         this._categoryFilters.push(sCategory);
570         this._filterLogs();
571         var elCheckbox = this.getCheckbox(sCategory);
572         if(elCheckbox) {
573             elCheckbox.checked = true;
574         }
575     },
577     /**
578      * Hides log messages associated with given category.
579      *
580      * @method hideCategory
581      * @param {String} Category name.
582      */
583     hideCategory : function(sCategory) {
584         var filtersArray = this._categoryFilters;
585         for(var i=0; i<filtersArray.length; i++) {
586             if(sCategory == filtersArray[i]) {
587                 filtersArray.splice(i, 1);
588                 break;
589             }
590         }
591         this._filterLogs();
592         var elCheckbox = this.getCheckbox(sCategory);
593         if(elCheckbox) {
594             elCheckbox.checked = false;
595         }
596     },
598     /**
599      * Returns array of enabled sources.
600      *
601      * @method getSources
602      * @return {Array} Array of enabled sources.
603      */
604     getSources : function() {
605         return this._sourceFilters;
606     },
608     /**
609      * Shows log messages associated with given source.
610      *
611      * @method showSource
612      * @param {String} Source name.
613      */
614     showSource : function(sSource) {
615         var filtersArray = this._sourceFilters;
616         // Don't do anything if category is already enabled
617         // Use Array.indexOf if available...
618         if(filtersArray.indexOf) {
619              if(filtersArray.indexOf(sSource) >  -1) {
620                 return;
621             }
622         }
623         // ...or do it the old-fashioned way
624         else {
625             for(var i=0; i<filtersArray.length; i++) {
626                if(sSource == filtersArray[i]){
627                     return;
628                 }
629             }
630         }
631         filtersArray.push(sSource);
632         this._filterLogs();
633         var elCheckbox = this.getCheckbox(sSource);
634         if(elCheckbox) {
635             elCheckbox.checked = true;
636         }
637     },
639     /**
640      * Hides log messages associated with given source.
641      *
642      * @method hideSource
643      * @param {String} Source name.
644      */
645     hideSource : function(sSource) {
646         var filtersArray = this._sourceFilters;
647         for(var i=0; i<filtersArray.length; i++) {
648             if(sSource == filtersArray[i]) {
649                 filtersArray.splice(i, 1);
650                 break;
651             }
652         }
653         this._filterLogs();
654         var elCheckbox = this.getCheckbox(sSource);
655         if(elCheckbox) {
656             elCheckbox.checked = false;
657         }
658     },
660     /**
661      * Does not delete any log messages, but clears all printed log messages from
662      * the console. Log messages will be printed out again if user re-filters. The
663      * static method YAHOO.widget.Logger.reset() should be called in order to
664      * actually delete log messages.
665      *
666      * @method clearConsole
667      */
668     clearConsole : function() {
669         // Clear the buffer of any pending messages
670         this._timeout = null;
671         this._buffer = [];
672         this._consoleMsgCount = 0;
674         var elConsole = this._elConsole;
675         elConsole.innerHTML = '';
676     },
678     /**
679      * Updates title to given string.
680      *
681      * @method setTitle
682      * @param sTitle {String} New title.
683      */
684     setTitle : function(sTitle) {
685         this._title.innerHTML = this.html2Text(sTitle);
686     },
688     /**
689      * Gets timestamp of the last log.
690      *
691      * @method getLastTime
692      * @return {Date} Timestamp of the last log.
693      */
694     getLastTime : function() {
695         return this._lastTime;
696     },
698     formatMsg : function (entry) {
699         var Static      = YAHOO.widget.LogReader,
700             entryFormat = this.entryFormat || (this.verboseOutput ?
701                           Static.VERBOSE_TEMPLATE : Static.BASIC_TEMPLATE),
702             info        = {
703                 category : entry.category,
705                 // Label for color-coded display
706                 label : entry.category.substring(0,4).toUpperCase(),
708                 sourceAndDetail : entry.sourceDetail ?
709                                   entry.source + " " + entry.sourceDetail :
710                                   entry.source,
712                 // Escape HTML entities in the log message itself for output
713                 // to console
714                 message : this.html2Text(entry.msg || entry.message || '')
715             };
717         // Add time info
718         if (entry.time && entry.time.getTime) {
719             info.localTime = entry.time.toLocaleTimeString ?
720                              entry.time.toLocaleTimeString() :
721                              entry.time.toString();
723             // Calculate the elapsed time to be from the last item that
724             // passed through the filter, not the absolute previous item
725             // in the stack
726             info.elapsedTime = entry.time.getTime() - this.getLastTime();
728             info.totalTime = entry.time.getTime() -
729                                YAHOO.widget.Logger.getStartTime();
730         }
732         var msg = Static.ENTRY_TEMPLATE.cloneNode(true);
733         if (this.verboseOutput) {
734             msg.className += ' yui-log-verbose';
735         }
737         msg.innerHTML = YAHOO.lang.substitute(entryFormat, info);
739         return msg;
740     },
742     /**
743      * Converts input chars "<", ">", and "&" to HTML entities.
744      *
745      * @method html2Text
746      * @param sHtml {String} String to convert.
747      * @private
748      */
749     html2Text : function(sHtml) {
750         if(sHtml) {
751             sHtml += "";
752             return sHtml.replace(/&/g, "&#38;").replace(/</g, "&#60;").replace(/>/g, "&#62;");
753         }
754         return "";
755     },
757 /////////////////////////////////////////////////////////////////////////////
759 // Private member variables
761 /////////////////////////////////////////////////////////////////////////////
763     /**
764      * Name of LogReader instance.
765      *
766      * @property _sName
767      * @type String
768      * @private
769      */
770     _sName : null,
772     //TODO: remove
773     /**
774      * A class member shared by all LogReaders if a container needs to be
775      * created during instantiation. Will be null if a container element never needs to
776      * be created on the fly, such as when the implementer passes in their own element.
777      *
778      * @property _elDefaultContainer
779      * @type HTMLElement
780      * @private
781      */
782     //YAHOO.widget.LogReader._elDefaultContainer = null;
784     /**
785      * Buffer of log message objects for batch output.
786      *
787      * @property _buffer
788      * @type Object[]
789      * @private
790      */
791     _buffer : null,
793     /**
794      * Number of log messages output to console.
795      *
796      * @property _consoleMsgCount
797      * @type Number
798      * @default 0
799      * @private
800      */
801     _consoleMsgCount : 0,
803     /**
804      * Date of last output log message.
805      *
806      * @property _lastTime
807      * @type Date
808      * @private
809      */
810     _lastTime : null,
812     /**
813      * Batched output timeout ID.
814      *
815      * @property _timeout
816      * @type Number
817      * @private
818      */
819     _timeout : null,
821     /**
822      * Hash of filters and their related checkbox elements.
823      *
824      * @property _filterCheckboxes
825      * @type Object
826      * @private
827      */
828     _filterCheckboxes : null,
830     /**
831      * Array of filters for log message categories.
832      *
833      * @property _categoryFilters
834      * @type String[]
835      * @private
836      */
837     _categoryFilters : null,
839     /**
840      * Array of filters for log message sources.
841      *
842      * @property _sourceFilters
843      * @type String[]
844      * @private
845      */
846     _sourceFilters : null,
848     /**
849      * LogReader container element.
850      *
851      * @property _elContainer
852      * @type HTMLElement
853      * @private
854      */
855     _elContainer : null,
857     /**
858      * LogReader header element.
859      *
860      * @property _elHd
861      * @type HTMLElement
862      * @private
863      */
864     _elHd : null,
866     /**
867      * LogReader collapse element.
868      *
869      * @property _elCollapse
870      * @type HTMLElement
871      * @private
872      */
873     _elCollapse : null,
875     /**
876      * LogReader collapse button element.
877      *
878      * @property _btnCollapse
879      * @type HTMLElement
880      * @private
881      */
882     _btnCollapse : null,
884     /**
885      * LogReader title header element.
886      *
887      * @property _title
888      * @type HTMLElement
889      * @private
890      */
891     _title : null,
893     /**
894      * LogReader console element.
895      *
896      * @property _elConsole
897      * @type HTMLElement
898      * @private
899      */
900     _elConsole : null,
902     /**
903      * LogReader footer element.
904      *
905      * @property _elFt
906      * @type HTMLElement
907      * @private
908      */
909     _elFt : null,
911     /**
912      * LogReader buttons container element.
913      *
914      * @property _elBtns
915      * @type HTMLElement
916      * @private
917      */
918     _elBtns : null,
920     /**
921      * Container element for LogReader category filter checkboxes.
922      *
923      * @property _elCategoryFilters
924      * @type HTMLElement
925      * @private
926      */
927     _elCategoryFilters : null,
929     /**
930      * Container element for LogReader source filter checkboxes.
931      *
932      * @property _elSourceFilters
933      * @type HTMLElement
934      * @private
935      */
936     _elSourceFilters : null,
938     /**
939      * LogReader pause button element.
940      *
941      * @property _btnPause
942      * @type HTMLElement
943      * @private
944      */
945     _btnPause : null,
947     /**
948      * Clear button element.
949      *
950      * @property _btnClear
951      * @type HTMLElement
952      * @private
953      */
954     _btnClear : null,
956     /////////////////////////////////////////////////////////////////////////////
957     //
958     // Private methods
959     //
960     /////////////////////////////////////////////////////////////////////////////
962     /**
963      * Initializes the primary container element.
964      *
965      * @method _initContainerEl
966      * @param elContainer {HTMLElement} Container element by reference or string ID.
967      * @private
968      */
969     _initContainerEl : function(elContainer) {
970         // Validate container
971         elContainer = YAHOO.util.Dom.get(elContainer);
972         // Attach to existing container...
973         if(elContainer && elContainer.tagName && (elContainer.tagName.toLowerCase() == "div")) {
974             this._elContainer = elContainer;
975             YAHOO.util.Dom.addClass(this._elContainer,"yui-log");
976         }
977         // ...or create container from scratch
978         else {
979             this._elContainer = document.body.appendChild(document.createElement("div"));
980             //this._elContainer.id = "yui-log" + this._sName;
981             YAHOO.util.Dom.addClass(this._elContainer,"yui-log");
982             YAHOO.util.Dom.addClass(this._elContainer,"yui-log-container");
984             //YAHOO.widget.LogReader._elDefaultContainer = this._elContainer;
986             // If implementer has provided container values, trust and set those
987             var containerStyle = this._elContainer.style;
988             if(this.width) {
989                 containerStyle.width = this.width;
990             }
991             if(this.right) {
992                 containerStyle.right = this.right;
993             }
994             if(this.top) {
995                 containerStyle.top = this.top;
996             }
997              if(this.left) {
998                 containerStyle.left = this.left;
999                 containerStyle.right = "auto";
1000             }
1001             if(this.bottom) {
1002                 containerStyle.bottom = this.bottom;
1003                 containerStyle.top = "auto";
1004             }
1005            if(this.fontSize) {
1006                 containerStyle.fontSize = this.fontSize;
1007             }
1008             // For Opera
1009             if(navigator.userAgent.toLowerCase().indexOf("opera") != -1) {
1010                 document.body.style += '';
1011             }
1012         }
1013     },
1015     /**
1016      * Initializes the header element.
1017      *
1018      * @method _initHeaderEl
1019      * @private
1020      */
1021     _initHeaderEl : function() {
1022         var oSelf = this;
1024         // Destroy header
1025         if(this._elHd) {
1026             // Unhook DOM events
1027             YAHOO.util.Event.purgeElement(this._elHd, true);
1029             // Remove DOM elements
1030             this._elHd.innerHTML = "";
1031         }
1032         
1033         // Create header
1034         this._elHd = this._elContainer.appendChild(document.createElement("div"));
1035         this._elHd.id = "yui-log-hd" + this._sName;
1036         this._elHd.className = "yui-log-hd";
1038         this._elCollapse = this._elHd.appendChild(document.createElement("div"));
1039         this._elCollapse.className = "yui-log-btns";
1041         this._btnCollapse = document.createElement("input");
1042         this._btnCollapse.type = "button";
1043         //this._btnCollapse.style.fontSize =
1044         //    YAHOO.util.Dom.getStyle(this._elContainer,"fontSize");
1045         this._btnCollapse.className = "yui-log-button";
1046         this._btnCollapse.value = "Collapse";
1047         this._btnCollapse = this._elCollapse.appendChild(this._btnCollapse);
1048         YAHOO.util.Event.addListener(
1049             oSelf._btnCollapse,'click',oSelf._onClickCollapseBtn,oSelf);
1051         this._title = this._elHd.appendChild(document.createElement("h4"));
1052         this._title.innerHTML = "Logger Console";
1053     },
1055     /**
1056      * Initializes the console element.
1057      *
1058      * @method _initConsoleEl
1059      * @private
1060      */
1061     _initConsoleEl : function() {
1062         // Destroy console
1063         if(this._elConsole) {
1064             // Unhook DOM events
1065             YAHOO.util.Event.purgeElement(this._elConsole, true);
1067             // Remove DOM elements
1068             this._elConsole.innerHTML = "";
1069         }
1071         // Ceate console
1072         this._elConsole = this._elContainer.appendChild(document.createElement("div"));
1073         this._elConsole.className = "yui-log-bd";
1075         // If implementer has provided console, trust and set those
1076         if(this.height) {
1077             this._elConsole.style.height = this.height;
1078         }
1079     },
1081     /**
1082      * Initializes the footer element.
1083      *
1084      * @method _initFooterEl
1085      * @private
1086      */
1087     _initFooterEl : function() {
1088         var oSelf = this;
1090         // Don't create footer elements if footer is disabled
1091         if(this.footerEnabled) {
1092             // Destroy console
1093             if(this._elFt) {
1094                 // Unhook DOM events
1095                 YAHOO.util.Event.purgeElement(this._elFt, true);
1097                 // Remove DOM elements
1098                 this._elFt.innerHTML = "";
1099             }
1101             this._elFt = this._elContainer.appendChild(document.createElement("div"));
1102             this._elFt.className = "yui-log-ft";
1104             this._elBtns = this._elFt.appendChild(document.createElement("div"));
1105             this._elBtns.className = "yui-log-btns";
1107             this._btnPause = document.createElement("input");
1108             this._btnPause.type = "button";
1109             //this._btnPause.style.fontSize =
1110             //    YAHOO.util.Dom.getStyle(this._elContainer,"fontSize");
1111             this._btnPause.className = "yui-log-button";
1112             this._btnPause.value = "Pause";
1113             this._btnPause = this._elBtns.appendChild(this._btnPause);
1114             YAHOO.util.Event.addListener(
1115                 oSelf._btnPause,'click',oSelf._onClickPauseBtn,oSelf);
1117             this._btnClear = document.createElement("input");
1118             this._btnClear.type = "button";
1119             //this._btnClear.style.fontSize =
1120             //    YAHOO.util.Dom.getStyle(this._elContainer,"fontSize");
1121             this._btnClear.className = "yui-log-button";
1122             this._btnClear.value = "Clear";
1123             this._btnClear = this._elBtns.appendChild(this._btnClear);
1124             YAHOO.util.Event.addListener(
1125                 oSelf._btnClear,'click',oSelf._onClickClearBtn,oSelf);
1127             this._elCategoryFilters = this._elFt.appendChild(document.createElement("div"));
1128             this._elCategoryFilters.className = "yui-log-categoryfilters";
1129             this._elSourceFilters = this._elFt.appendChild(document.createElement("div"));
1130             this._elSourceFilters.className = "yui-log-sourcefilters";
1131         }
1132     },
1134     /**
1135      * Initializes Drag and Drop on the header element.
1136      *
1137      * @method _initDragDrop
1138      * @private
1139      */
1140     _initDragDrop : function() {
1141         // If Drag and Drop utility is available...
1142         // ...and draggable is true...
1143         // ...then make the header draggable
1144         if(YAHOO.util.DD && this.draggable && this._elHd) {
1145             var ylog_dd = new YAHOO.util.DD(this._elContainer);
1146             ylog_dd.setHandleElId(this._elHd.id);
1147             //TODO: use class name
1148             this._elHd.style.cursor = "move";
1149         }
1150     },
1152     /**
1153      * Initializes category filters.
1154      *
1155      * @method _initCategories
1156      * @private
1157      */
1158     _initCategories : function() {
1159         // Initialize category filters
1160         this._categoryFilters = [];
1161         var aInitialCategories = YAHOO.widget.Logger.categories;
1163         for(var j=0; j < aInitialCategories.length; j++) {
1164             var sCategory = aInitialCategories[j];
1166             // Add category to the internal array of filters
1167             this._categoryFilters.push(sCategory);
1169             // Add checkbox element if UI is enabled
1170             if(this._elCategoryFilters) {
1171                 this._createCategoryCheckbox(sCategory);
1172             }
1173         }
1174     },
1176     /**
1177      * Initializes source filters.
1178      *
1179      * @method _initSources
1180      * @private
1181      */
1182     _initSources : function() {
1183         // Initialize source filters
1184         this._sourceFilters = [];
1185         var aInitialSources = YAHOO.widget.Logger.sources;
1187         for(var j=0; j < aInitialSources.length; j++) {
1188             var sSource = aInitialSources[j];
1190             // Add source to the internal array of filters
1191             this._sourceFilters.push(sSource);
1193             // Add checkbox element if UI is enabled
1194             if(this._elSourceFilters) {
1195                 this._createSourceCheckbox(sSource);
1196             }
1197         }
1198     },
1200     /**
1201      * Creates the UI for a category filter in the LogReader footer element.
1202      *
1203      * @method _createCategoryCheckbox
1204      * @param sCategory {String} Category name.
1205      * @private
1206      */
1207     _createCategoryCheckbox : function(sCategory) {
1208         var oSelf = this;
1210         if(this._elFt) {
1211             var elParent = this._elCategoryFilters;
1212             var elFilter = elParent.appendChild(document.createElement("span"));
1213             elFilter.className = "yui-log-filtergrp";
1214             
1215             // Append el at the end so IE 5.5 can set "type" attribute
1216             // and THEN set checked property
1217             var chkCategory = document.createElement("input");
1218             chkCategory.id = "yui-log-filter-" + sCategory + this._sName;
1219             chkCategory.className = "yui-log-filter-" + sCategory;
1220             chkCategory.type = "checkbox";
1221             chkCategory.category = sCategory;
1222             chkCategory = elFilter.appendChild(chkCategory);
1223             chkCategory.checked = true;
1225             // Subscribe to the click event
1226             YAHOO.util.Event.addListener(chkCategory,'click',oSelf._onCheckCategory,oSelf);
1228             // Create and class the text label
1229             var lblCategory = elFilter.appendChild(document.createElement("label"));
1230             lblCategory.htmlFor = chkCategory.id;
1231             lblCategory.className = sCategory;
1232             lblCategory.innerHTML = sCategory;
1233             
1234             this._filterCheckboxes[sCategory] = chkCategory;
1235         }
1236     },
1238     /**
1239      * Creates a checkbox in the LogReader footer element to filter by source.
1240      *
1241      * @method _createSourceCheckbox
1242      * @param sSource {String} Source name.
1243      * @private
1244      */
1245     _createSourceCheckbox : function(sSource) {
1246         var oSelf = this;
1248         if(this._elFt) {
1249             var elParent = this._elSourceFilters;
1250             var elFilter = elParent.appendChild(document.createElement("span"));
1251             elFilter.className = "yui-log-filtergrp";
1253             // Append el at the end so IE 5.5 can set "type" attribute
1254             // and THEN set checked property
1255             var chkSource = document.createElement("input");
1256             chkSource.id = "yui-log-filter" + sSource + this._sName;
1257             chkSource.className = "yui-log-filter" + sSource;
1258             chkSource.type = "checkbox";
1259             chkSource.source = sSource;
1260             chkSource = elFilter.appendChild(chkSource);
1261             chkSource.checked = true;
1263             // Subscribe to the click event
1264             YAHOO.util.Event.addListener(chkSource,'click',oSelf._onCheckSource,oSelf);
1266             // Create and class the text label
1267             var lblSource = elFilter.appendChild(document.createElement("label"));
1268             lblSource.htmlFor = chkSource.id;
1269             lblSource.className = sSource;
1270             lblSource.innerHTML = sSource;
1271             
1272             this._filterCheckboxes[sSource] = chkSource;
1273         }
1274     },
1276     /**
1277      * Reprints all log messages in the stack through filters.
1278      *
1279      * @method _filterLogs
1280      * @private
1281      */
1282     _filterLogs : function() {
1283         // Reprint stack with new filters
1284         if (this._elConsole !== null) {
1285             this.clearConsole();
1286             this._printToConsole(YAHOO.widget.Logger.getStack());
1287         }
1288     },
1290     /**
1291      * Sends buffer of log messages to output and clears buffer.
1292      *
1293      * @method _printBuffer
1294      * @private
1295      */
1296     _printBuffer : function() {
1297         this._timeout = null;
1299         if(this._elConsole !== null) {
1300             var thresholdMax = this.thresholdMax;
1301             thresholdMax = (thresholdMax && !isNaN(thresholdMax)) ? thresholdMax : 500;
1302             if(this._consoleMsgCount < thresholdMax) {
1303                 var entries = [];
1304                 for (var i=0; i<this._buffer.length; i++) {
1305                     entries[i] = this._buffer[i];
1306                 }
1307                 this._buffer = [];
1308                 this._printToConsole(entries);
1309             }
1310             else {
1311                 this._filterLogs();
1312             }
1313             
1314             if(!this.newestOnTop) {
1315                 this._elConsole.scrollTop = this._elConsole.scrollHeight;
1316             }
1317         }
1318     },
1320     /**
1321      * Cycles through an array of log messages, and outputs each one to the console
1322      * if its category has not been filtered out.
1323      *
1324      * @method _printToConsole
1325      * @param aEntries {Object[]} Array of LogMsg objects to output to console.
1326      * @private
1327      */
1328     _printToConsole : function(aEntries) {
1329         // Manage the number of messages displayed in the console
1330         var entriesLen         = aEntries.length,
1331             df                 = document.createDocumentFragment(),
1332             msgHTML            = [],
1333             thresholdMin       = this.thresholdMin,
1334             sourceFiltersLen   = this._sourceFilters.length,
1335             categoryFiltersLen = this._categoryFilters.length,
1336             entriesStartIndex,
1337             i, j, msg, before;
1339         if(isNaN(thresholdMin) || (thresholdMin > this.thresholdMax)) {
1340             thresholdMin = 0;
1341         }
1342         entriesStartIndex = (entriesLen > thresholdMin) ? (entriesLen - thresholdMin) : 0;
1343         
1344         // Iterate through all log entries 
1345         for(i=entriesStartIndex; i<entriesLen; i++) {
1346             // Print only the ones that filter through
1347             var okToPrint = false;
1348             var okToFilterCats = false;
1350             // Get log message details
1351             var entry = aEntries[i];
1352             var source = entry.source;
1353             var category = entry.category;
1355             for(j=0; j<sourceFiltersLen; j++) {
1356                 if(source == this._sourceFilters[j]) {
1357                     okToFilterCats = true;
1358                     break;
1359                 }
1360             }
1361             if(okToFilterCats) {
1362                 for(j=0; j<categoryFiltersLen; j++) {
1363                     if(category == this._categoryFilters[j]) {
1364                         okToPrint = true;
1365                         break;
1366                     }
1367                 }
1368             }
1369             if(okToPrint) {
1370                 msg = this.formatMsg(entry);
1371                 if (typeof msg === 'string') {
1372                     msgHTML[msgHTML.length] = msg;
1373                 } else {
1374                     df.insertBefore(msg, this.newestOnTop ?
1375                         df.firstChild || null : null);
1376                 }
1377                 this._consoleMsgCount++;
1378                 this._lastTime = entry.time.getTime();
1379             }
1380         }
1382         if (msgHTML.length) {
1383             msgHTML.splice(0,0,this._elConsole.innerHTML);
1384             this._elConsole.innerHTML = this.newestOnTop ?
1385                                             msgHTML.reverse().join('') :
1386                                             msgHTML.join('');
1387         } else if (df.firstChild) {
1388             this._elConsole.insertBefore(df, this.newestOnTop ?
1389                         this._elConsole.firstChild || null : null);
1390         }
1391     },
1393 /////////////////////////////////////////////////////////////////////////////
1395 // Private event handlers
1397 /////////////////////////////////////////////////////////////////////////////
1399     /**
1400      * Handles Logger's categoryCreateEvent.
1401      *
1402      * @method _onCategoryCreate
1403      * @param sType {String} The event.
1404      * @param aArgs {Object[]} Data passed from event firer.
1405      * @param oSelf {Object} The LogReader instance.
1406      * @private
1407      */
1408     _onCategoryCreate : function(sType, aArgs, oSelf) {
1409         var category = aArgs[0];
1410         
1411         // Add category to the internal array of filters
1412         oSelf._categoryFilters.push(category);
1414         if(oSelf._elFt) {
1415             oSelf._createCategoryCheckbox(category);
1416         }
1417     },
1419     /**
1420      * Handles Logger's sourceCreateEvent.
1421      *
1422      * @method _onSourceCreate
1423      * @param sType {String} The event.
1424      * @param aArgs {Object[]} Data passed from event firer.
1425      * @param oSelf {Object} The LogReader instance.
1426      * @private
1427      */
1428     _onSourceCreate : function(sType, aArgs, oSelf) {
1429         var source = aArgs[0];
1430         
1431         // Add source to the internal array of filters
1432         oSelf._sourceFilters.push(source);
1434         if(oSelf._elFt) {
1435             oSelf._createSourceCheckbox(source);
1436         }
1437     },
1439     /**
1440      * Handles check events on the category filter checkboxes.
1441      *
1442      * @method _onCheckCategory
1443      * @param v {HTMLEvent} The click event.
1444      * @param oSelf {Object} The LogReader instance.
1445      * @private
1446      */
1447     _onCheckCategory : function(v, oSelf) {
1448         var category = this.category;
1449         if(!this.checked) {
1450             oSelf.hideCategory(category);
1451         }
1452         else {
1453             oSelf.showCategory(category);
1454         }
1455     },
1457     /**
1458      * Handles check events on the category filter checkboxes.
1459      *
1460      * @method _onCheckSource
1461      * @param v {HTMLEvent} The click event.
1462      * @param oSelf {Object} The LogReader instance.
1463      * @private
1464      */
1465     _onCheckSource : function(v, oSelf) {
1466         var source = this.source;
1467         if(!this.checked) {
1468             oSelf.hideSource(source);
1469         }
1470         else {
1471             oSelf.showSource(source);
1472         }
1473     },
1475     /**
1476      * Handles click events on the collapse button.
1477      *
1478      * @method _onClickCollapseBtn
1479      * @param v {HTMLEvent} The click event.
1480      * @param oSelf {Object} The LogReader instance
1481      * @private
1482      */
1483     _onClickCollapseBtn : function(v, oSelf) {
1484         if(!oSelf.isCollapsed) {
1485             oSelf.collapse();
1486         }
1487         else {
1488             oSelf.expand();
1489         }
1490     },
1492     /**
1493      * Handles click events on the pause button.
1494      *
1495      * @method _onClickPauseBtn
1496      * @param v {HTMLEvent} The click event.
1497      * @param oSelf {Object} The LogReader instance.
1498      * @private
1499      */
1500     _onClickPauseBtn : function(v, oSelf) {
1501         if(!oSelf.isPaused) {
1502             oSelf.pause();
1503         }
1504         else {
1505             oSelf.resume();
1506         }
1507     },
1509     /**
1510      * Handles click events on the clear button.
1511      *
1512      * @method _onClickClearBtn
1513      * @param v {HTMLEvent} The click event.
1514      * @param oSelf {Object} The LogReader instance.
1515      * @private
1516      */
1517     _onClickClearBtn : function(v, oSelf) {
1518         oSelf.clearConsole();
1519     },
1521     /**
1522      * Handles Logger's newLogEvent.
1523      *
1524      * @method _onNewLog
1525      * @param sType {String} The event.
1526      * @param aArgs {Object[]} Data passed from event firer.
1527      * @param oSelf {Object} The LogReader instance.
1528      * @private
1529      */
1530     _onNewLog : function(sType, aArgs, oSelf) {
1531         var logEntry = aArgs[0];
1532         oSelf._buffer.push(logEntry);
1534         if (oSelf.logReaderEnabled === true && oSelf._timeout === null) {
1535             oSelf._timeout = setTimeout(function(){oSelf._printBuffer();}, oSelf.outputBuffer);
1536         }
1537     },
1539     /**
1540      * Handles Logger's resetEvent.
1541      *
1542      * @method _onReset
1543      * @param sType {String} The event.
1544      * @param aArgs {Object[]} Data passed from event firer.
1545      * @param oSelf {Object} The LogReader instance.
1546      * @private
1547      */
1548     _onReset : function(sType, aArgs, oSelf) {
1549         oSelf._filterLogs();
1550     }
1553  /**
1554  * The Logger widget provides a simple way to read or write log messages in
1555  * JavaScript code. Integration with the YUI Library's debug builds allow
1556  * implementers to access under-the-hood events, errors, and debugging messages.
1557  * Output may be read through a LogReader console and/or output to a browser
1558  * console.
1560  * @module logger
1561  * @requires yahoo, event, dom
1562  * @optional dragdrop
1563  * @namespace YAHOO.widget
1564  * @title Logger Widget
1565  */
1567 /****************************************************************************/
1568 /****************************************************************************/
1569 /****************************************************************************/
1571 // Define once
1572 if(!YAHOO.widget.Logger) {
1573     /**
1574      * The singleton Logger class provides core log management functionality. Saves
1575      * logs written through the global YAHOO.log function or written by a LogWriter
1576      * instance. Provides access to logs for reading by a LogReader instance or
1577      * native browser console such as the Firebug extension to Firefox or Safari's
1578      * JavaScript console through integration with the console.log() method.
1579      *
1580      * @class Logger
1581      * @static
1582      */
1583     YAHOO.widget.Logger = {
1584         // Initialize properties
1585         loggerEnabled: true,
1586         _browserConsoleEnabled: false,
1587         categories: ["info","warn","error","time","window"],
1588         sources: ["global"],
1589         _stack: [], // holds all log msgs
1590         maxStackEntries: 2500,
1591         _startTime: new Date().getTime(), // static start timestamp
1592         _lastTime: null, // timestamp of last logged message
1593         _windowErrorsHandled: false,
1594         _origOnWindowError: null
1595     };
1597     /////////////////////////////////////////////////////////////////////////////
1598     //
1599     // Public properties
1600     //
1601     /////////////////////////////////////////////////////////////////////////////
1602     /**
1603      * True if Logger is enabled, false otherwise.
1604      *
1605      * @property loggerEnabled
1606      * @type Boolean
1607      * @static
1608      * @default true
1609      */
1611     /**
1612      * Array of categories.
1613      *
1614      * @property categories
1615      * @type String[]
1616      * @static
1617      * @default ["info","warn","error","time","window"]
1618      */
1620     /**
1621      * Array of sources.
1622      *
1623      * @property sources
1624      * @type String[]
1625      * @static
1626      * @default ["global"]
1627      */
1629     /**
1630      * Upper limit on size of internal stack.
1631      *
1632      * @property maxStackEntries
1633      * @type Number
1634      * @static
1635      * @default 2500
1636      */
1638     /////////////////////////////////////////////////////////////////////////////
1639     //
1640     // Private properties
1641     //
1642     /////////////////////////////////////////////////////////////////////////////
1643     /**
1644      * Internal property to track whether output to browser console is enabled.
1645      *
1646      * @property _browserConsoleEnabled
1647      * @type Boolean
1648      * @static
1649      * @default false
1650      * @private
1651      */
1653     /**
1654      * Array to hold all log messages.
1655      *
1656      * @property _stack
1657      * @type Array
1658      * @static
1659      * @private
1660      */
1661     /**
1662      * Static timestamp of Logger initialization.
1663      *
1664      * @property _startTime
1665      * @type Date
1666      * @static
1667      * @private
1668      */
1669     /**
1670      * Timestamp of last logged message.
1671      *
1672      * @property _lastTime
1673      * @type Date
1674      * @static
1675      * @private
1676      */
1677     /////////////////////////////////////////////////////////////////////////////
1678     //
1679     // Public methods
1680     //
1681     /////////////////////////////////////////////////////////////////////////////
1682     /**
1683      * Saves a log message to the stack and fires newLogEvent. If the log message is
1684      * assigned to an unknown category, creates a new category. If the log message is
1685      * from an unknown source, creates a new source.  If browser console is enabled,
1686      * outputs the log message to browser console.
1687      *
1688      * @method log
1689      * @param sMsg {String} The log message.
1690      * @param sCategory {String} Category of log message, or null.
1691      * @param sSource {String} Source of LogWriter, or null if global.
1692      */
1693     YAHOO.widget.Logger.log = function(sMsg, sCategory, sSource) {
1694         if(this.loggerEnabled) {
1695             if(!sCategory) {
1696                 sCategory = "info"; // default category
1697             }
1698             else {
1699                 sCategory = sCategory.toLocaleLowerCase();
1700                 if(this._isNewCategory(sCategory)) {
1701                     this._createNewCategory(sCategory);
1702                 }
1703             }
1704             var sClass = "global"; // default source
1705             var sDetail = null;
1706             if(sSource) {
1707                 var spaceIndex = sSource.indexOf(" ");
1708                 if(spaceIndex > 0) {
1709                     // Substring until first space
1710                     sClass = sSource.substring(0,spaceIndex);
1711                     // The rest of the source
1712                     sDetail = sSource.substring(spaceIndex,sSource.length);
1713                 }
1714                 else {
1715                     sClass = sSource;
1716                 }
1717                 if(this._isNewSource(sClass)) {
1718                     this._createNewSource(sClass);
1719                 }
1720             }
1722             var timestamp = new Date();
1723             var logEntry = new YAHOO.widget.LogMsg({
1724                 msg: sMsg,
1725                 time: timestamp,
1726                 category: sCategory,
1727                 source: sClass,
1728                 sourceDetail: sDetail
1729             });
1731             var stack = this._stack;
1732             var maxStackEntries = this.maxStackEntries;
1733             if(maxStackEntries && !isNaN(maxStackEntries) &&
1734                 (stack.length >= maxStackEntries)) {
1735                 stack.shift();
1736             }
1737             stack.push(logEntry);
1738             this.newLogEvent.fire(logEntry);
1740             if(this._browserConsoleEnabled) {
1741                 this._printToBrowserConsole(logEntry);
1742             }
1743             return true;
1744         }
1745         else {
1746             return false;
1747         }
1748     };
1750     /**
1751      * Resets internal stack and startTime, enables Logger, and fires logResetEvent.
1752      *
1753      * @method reset
1754      */
1755     YAHOO.widget.Logger.reset = function() {
1756         this._stack = [];
1757         this._startTime = new Date().getTime();
1758         this.loggerEnabled = true;
1759         this.log("Logger reset");
1760         this.logResetEvent.fire();
1761     };
1763     /**
1764      * Public accessor to internal stack of log message objects.
1765      *
1766      * @method getStack
1767      * @return {Object[]} Array of log message objects.
1768      */
1769     YAHOO.widget.Logger.getStack = function() {
1770         return this._stack;
1771     };
1773     /**
1774      * Public accessor to internal start time.
1775      *
1776      * @method getStartTime
1777      * @return {Date} Internal date of when Logger singleton was initialized.
1778      */
1779     YAHOO.widget.Logger.getStartTime = function() {
1780         return this._startTime;
1781     };
1783     /**
1784      * Disables output to the browser's global console.log() function, which is used
1785      * by the Firebug extension to Firefox as well as Safari.
1786      *
1787      * @method disableBrowserConsole
1788      */
1789     YAHOO.widget.Logger.disableBrowserConsole = function() {
1790         YAHOO.log("Logger output to the function console.log() has been disabled.");
1791         this._browserConsoleEnabled = false;
1792     };
1794     /**
1795      * Enables output to the browser's global console.log() function, which is used
1796      * by the Firebug extension to Firefox as well as Safari.
1797      *
1798      * @method enableBrowserConsole
1799      */
1800     YAHOO.widget.Logger.enableBrowserConsole = function() {
1801         this._browserConsoleEnabled = true;
1802         YAHOO.log("Logger output to the function console.log() has been enabled.");
1803     };
1805     /**
1806      * Surpresses native JavaScript errors and outputs to console. By default,
1807      * Logger does not handle JavaScript window error events.
1808      * NB: Not all browsers support the window.onerror event.
1809      *
1810      * @method handleWindowErrors
1811      */
1812     YAHOO.widget.Logger.handleWindowErrors = function() {
1813         if(!YAHOO.widget.Logger._windowErrorsHandled) {
1814             // Save any previously defined handler to call
1815             if(window.error) {
1816                 YAHOO.widget.Logger._origOnWindowError = window.onerror;
1817             }
1818             window.onerror = YAHOO.widget.Logger._onWindowError;
1819             YAHOO.widget.Logger._windowErrorsHandled = true;
1820             YAHOO.log("Logger handling of window.onerror has been enabled.");
1821         }
1822         else {
1823             YAHOO.log("Logger handling of window.onerror had already been enabled.");
1824         }
1825     };
1827     /**
1828      * Unsurpresses native JavaScript errors. By default,
1829      * Logger does not handle JavaScript window error events.
1830      * NB: Not all browsers support the window.onerror event.
1831      *
1832      * @method unhandleWindowErrors
1833      */
1834     YAHOO.widget.Logger.unhandleWindowErrors = function() {
1835         if(YAHOO.widget.Logger._windowErrorsHandled) {
1836             // Revert to any previously defined handler to call
1837             if(YAHOO.widget.Logger._origOnWindowError) {
1838                 window.onerror = YAHOO.widget.Logger._origOnWindowError;
1839                 YAHOO.widget.Logger._origOnWindowError = null;
1840             }
1841             else {
1842                 window.onerror = null;
1843             }
1844             YAHOO.widget.Logger._windowErrorsHandled = false;
1845             YAHOO.log("Logger handling of window.onerror has been disabled.");
1846         }
1847         else {
1848             YAHOO.log("Logger handling of window.onerror had already been disabled.");
1849         }
1850     };
1851     
1852     /////////////////////////////////////////////////////////////////////////////
1853     //
1854     // Public events
1855     //
1856     /////////////////////////////////////////////////////////////////////////////
1858      /**
1859      * Fired when a new category has been created.
1860      *
1861      * @event categoryCreateEvent
1862      * @param sCategory {String} Category name.
1863      */
1864     YAHOO.widget.Logger.categoryCreateEvent =
1865         new YAHOO.util.CustomEvent("categoryCreate", this, true);
1867      /**
1868      * Fired when a new source has been named.
1869      *
1870      * @event sourceCreateEvent
1871      * @param sSource {String} Source name.
1872      */
1873     YAHOO.widget.Logger.sourceCreateEvent =
1874         new YAHOO.util.CustomEvent("sourceCreate", this, true);
1876      /**
1877      * Fired when a new log message has been created.
1878      *
1879      * @event newLogEvent
1880      * @param sMsg {String} Log message.
1881      */
1882     YAHOO.widget.Logger.newLogEvent = new YAHOO.util.CustomEvent("newLog", this, true);
1884     /**
1885      * Fired when the Logger has been reset has been created.
1886      *
1887      * @event logResetEvent
1888      */
1889     YAHOO.widget.Logger.logResetEvent = new YAHOO.util.CustomEvent("logReset", this, true);
1891     /////////////////////////////////////////////////////////////////////////////
1892     //
1893     // Private methods
1894     //
1895     /////////////////////////////////////////////////////////////////////////////
1897     /**
1898      * Creates a new category of log messages and fires categoryCreateEvent.
1899      *
1900      * @method _createNewCategory
1901      * @param sCategory {String} Category name.
1902      * @private
1903      */
1904     YAHOO.widget.Logger._createNewCategory = function(sCategory) {
1905         this.categories.push(sCategory);
1906         this.categoryCreateEvent.fire(sCategory);
1907     };
1909     /**
1910      * Checks to see if a category has already been created.
1911      *
1912      * @method _isNewCategory
1913      * @param sCategory {String} Category name.
1914      * @return {Boolean} Returns true if category is unknown, else returns false.
1915      * @private
1916      */
1917     YAHOO.widget.Logger._isNewCategory = function(sCategory) {
1918         for(var i=0; i < this.categories.length; i++) {
1919             if(sCategory == this.categories[i]) {
1920                 return false;
1921             }
1922         }
1923         return true;
1924     };
1926     /**
1927      * Creates a new source of log messages and fires sourceCreateEvent.
1928      *
1929      * @method _createNewSource
1930      * @param sSource {String} Source name.
1931      * @private
1932      */
1933     YAHOO.widget.Logger._createNewSource = function(sSource) {
1934         this.sources.push(sSource);
1935         this.sourceCreateEvent.fire(sSource);
1936     };
1938     /**
1939      * Checks to see if a source already exists.
1940      *
1941      * @method _isNewSource
1942      * @param sSource {String} Source name.
1943      * @return {Boolean} Returns true if source is unknown, else returns false.
1944      * @private
1945      */
1946     YAHOO.widget.Logger._isNewSource = function(sSource) {
1947         if(sSource) {
1948             for(var i=0; i < this.sources.length; i++) {
1949                 if(sSource == this.sources[i]) {
1950                     return false;
1951                 }
1952             }
1953             return true;
1954         }
1955     };
1957     /**
1958      * Outputs a log message to global console.log() function.
1959      *
1960      * @method _printToBrowserConsole
1961      * @param oEntry {Object} Log entry object.
1962      * @private
1963      */
1964     YAHOO.widget.Logger._printToBrowserConsole = function(oEntry) {
1965         if(window.console && console.log) {
1966             var category = oEntry.category;
1967             var label = oEntry.category.substring(0,4).toUpperCase();
1969             var time = oEntry.time;
1970             var localTime;
1971             if (time.toLocaleTimeString) {
1972                 localTime  = time.toLocaleTimeString();
1973             }
1974             else {
1975                 localTime = time.toString();
1976             }
1978             var msecs = time.getTime();
1979             var elapsedTime = (YAHOO.widget.Logger._lastTime) ?
1980                 (msecs - YAHOO.widget.Logger._lastTime) : 0;
1981             YAHOO.widget.Logger._lastTime = msecs;
1983             var output =
1984                 localTime + " (" +
1985                 elapsedTime + "ms): " +
1986                 oEntry.source + ": ";
1988             console.log(output, oEntry.msg);
1989         }
1990     };
1992     /////////////////////////////////////////////////////////////////////////////
1993     //
1994     // Private event handlers
1995     //
1996     /////////////////////////////////////////////////////////////////////////////
1998     /**
1999      * Handles logging of messages due to window error events.
2000      *
2001      * @method _onWindowError
2002      * @param sMsg {String} The error message.
2003      * @param sUrl {String} URL of the error.
2004      * @param sLine {String} Line number of the error.
2005      * @private
2006      */
2007     YAHOO.widget.Logger._onWindowError = function(sMsg,sUrl,sLine) {
2008         // Logger is not in scope of this event handler
2009         try {
2010             YAHOO.widget.Logger.log(sMsg+' ('+sUrl+', line '+sLine+')', "window");
2011             if(YAHOO.widget.Logger._origOnWindowError) {
2012                 YAHOO.widget.Logger._origOnWindowError();
2013             }
2014         }
2015         catch(e) {
2016             return false;
2017         }
2018     };
2020     /////////////////////////////////////////////////////////////////////////////
2021     //
2022     // First log
2023     //
2024     /////////////////////////////////////////////////////////////////////////////
2026     YAHOO.widget.Logger.log("Logger initialized");
2030 YAHOO.register("logger", YAHOO.widget.Logger, {version: "2.5.2", build: "1076"});