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