2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
7 /****************************************************************************/
8 /****************************************************************************/
9 /****************************************************************************/
12 * The LogMsg class defines a single log message.
16 * @param oConfigs {Object} Object literal of configuration params.
18 YAHOO.widget.LogMsg = function(oConfigs) {
44 * Log source. The first word passed in as the source argument.
52 * Log source detail. The remainder of the string passed in as the source argument, not
53 * including the first word (if any).
55 * @property sourceDetail
58 this.sourceDetail = null;
60 if (oConfigs && (oConfigs.constructor == Object)) {
61 for(var param in oConfigs) {
62 if (oConfigs.hasOwnProperty(param)) {
63 this[param] = oConfigs[param];
69 /****************************************************************************/
70 /****************************************************************************/
71 /****************************************************************************/
74 * The LogWriter class provides a mechanism to log messages through
75 * YAHOO.widget.Logger from a named source.
79 * @param sSource {String} Source of LogWriter instance.
81 YAHOO.widget.LogWriter = function(sSource) {
83 YAHOO.log("Could not instantiate LogWriter due to invalid source.",
84 "error", "LogWriter");
87 this._source = sSource;
90 /////////////////////////////////////////////////////////////////////////////
94 /////////////////////////////////////////////////////////////////////////////
97 * Public accessor to the unique name of the LogWriter instance.
100 * @return {String} Unique name of the LogWriter instance.
102 YAHOO.widget.LogWriter.prototype.toString = function() {
103 return "LogWriter " + this._sSource;
107 * Logs a message attached to the source of the LogWriter.
110 * @param sMsg {String} The log message.
111 * @param sCategory {String} Category name.
113 YAHOO.widget.LogWriter.prototype.log = function(sMsg, sCategory) {
114 YAHOO.widget.Logger.log(sMsg, sCategory, this._source);
118 * Public accessor to get the source name.
121 * @return {String} The LogWriter source.
123 YAHOO.widget.LogWriter.prototype.getSource = function() {
128 * Public accessor to set the source name.
131 * @param sSource {String} Source of LogWriter instance.
133 YAHOO.widget.LogWriter.prototype.setSource = function(sSource) {
135 YAHOO.log("Could not set source due to invalid source.", "error", this.toString());
139 this._source = sSource;
143 /////////////////////////////////////////////////////////////////////////////
145 // Private member variables
147 /////////////////////////////////////////////////////////////////////////////
150 * Source of the LogWriter instance.
156 YAHOO.widget.LogWriter.prototype._source = null;
161 /****************************************************************************/
162 /****************************************************************************/
163 /****************************************************************************/
166 * The LogReader class provides UI to read messages logged to YAHOO.widget.Logger.
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.
174 YAHOO.widget.LogReader = function(elContainer, oConfigs) {
175 this._sName = YAHOO.widget.LogReader._index;
176 YAHOO.widget.LogReader._index++;
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
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];
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());
199 this._initHeaderEl();
200 this._initConsoleEl();
201 this._initFooterEl();
203 this._initDragDrop();
205 this._initCategories();
208 // Subscribe to Logger custom events
209 YAHOO.widget.Logger.newLogEvent.subscribe(this._onNewLog, this);
210 YAHOO.widget.Logger.logResetEvent.subscribe(this._onReset, this);
212 YAHOO.widget.Logger.categoryCreateEvent.subscribe(this._onCategoryCreate, this);
213 YAHOO.widget.Logger.sourceCreateEvent.subscribe(this._onSourceCreate, this);
216 YAHOO.log("LogReader initialized", null, this.toString());
219 /////////////////////////////////////////////////////////////////////////////
221 // Static member variables
223 /////////////////////////////////////////////////////////////////////////////
224 YAHOO.lang.augmentObject(YAHOO.widget.LogReader, {
226 * Internal class member to index multiple LogReader instances.
228 * @property _memberName
237 * Node template for the log entries
238 * @property ENTRY_TEMPLATE
240 * @type {HTMLElement}
241 * @default PRE.yui-log-entry element
243 ENTRY_TEMPLATE : (function () {
244 var t = document.createElement('pre');
245 YAHOO.util.Dom.addClass(t,'yui-log-entry');
250 * Template used for innerHTML of verbose entry output.
251 * @property VERBOSE_TEMPLATE
253 * @default "<span class='{category}'>{label}</span>{totalTime}ms (+{elapsedTime}) {localTime}:</p><p>{sourceAndDetail}</p><p>{message}</p>"
255 VERBOSE_TEMPLATE : "<p><span class='{category}'>{label}</span> {totalTime}ms (+{elapsedTime}) {localTime}:</p><p>{sourceAndDetail}</p><p>{message}</p>",
258 * Template used for innerHTML of compact entry output.
259 * @property BASIC_TEMPLATE
261 * @default "<p><span class='{category}'>{label}</span>{totalTime}ms (+{elapsedTime}) {localTime}: {sourceAndDetail}: {message}</p>"
263 BASIC_TEMPLATE : "<p><span class='{category}'>{label}</span> {totalTime}ms (+{elapsedTime}) {localTime}: {sourceAndDetail}: {message}</p>"
266 /////////////////////////////////////////////////////////////////////////////
268 // Public member variables
270 /////////////////////////////////////////////////////////////////////////////
272 YAHOO.widget.LogReader.prototype = {
274 * Whether or not LogReader is enabled to output log messages.
276 * @property logReaderEnabled
280 logReaderEnabled : true,
283 * Public member to access CSS width of the LogReader container.
291 * Public member to access CSS height of the LogReader container.
299 * Public member to access CSS top position of the LogReader container.
307 * Public member to access CSS left position of the LogReader container.
315 * Public member to access CSS right position of the LogReader container.
323 * Public member to access CSS bottom position of the LogReader container.
331 * Public member to access CSS font size of the LogReader container.
339 * Whether or not the footer UI is enabled for the LogReader.
341 * @property footerEnabled
345 footerEnabled : true,
348 * Whether or not output is verbose (more readable). Setting to true will make
349 * output more compact (less readable).
351 * @property verboseOutput
355 verboseOutput : true,
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:
365 * <li>sourceAndDetail</li>
368 * <li>elapsedTime</li>
372 * @property entryFormat
379 * Whether or not newest message is printed on top.
381 * @property newestOnTop
387 * Output timeout buffer in milliseconds.
389 * @property outputBuffer
396 * Maximum number of messages a LogReader console will display.
398 * @property thresholdMax
405 * When a LogReader console reaches its thresholdMax, it will clear out messages
406 * and print out the latest thresholdMin number of messages.
408 * @property thresholdMin
415 * True when LogReader is in a collapsed state, false otherwise.
417 * @property isCollapsed
424 * True when LogReader is in a paused state, false otherwise.
433 * Enables draggable LogReader if DragDrop Utility is present.
435 * @property draggable
441 /////////////////////////////////////////////////////////////////////////////
445 /////////////////////////////////////////////////////////////////////////////
448 * Public accessor to the unique name of the LogReader instance.
451 * @return {String} Unique name of the LogReader instance.
453 toString : function() {
454 return "LogReader instance" + this._sName;
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.
463 this.isPaused = true;
464 this._timeout = null;
465 this.logReaderEnabled = false;
466 if (this._btnPause) {
467 this._btnPause.value = "Resume";
472 * Resumes output of log messages, including outputting any log messages that
473 * have been saved to buffer while paused.
477 resume : function() {
478 this.isPaused = false;
479 this.logReaderEnabled = true;
481 if (this._btnPause) {
482 this._btnPause.value = "Pause";
487 * Hides UI of LogReader. Logging functionality is not disrupted.
492 this._elContainer.style.display = "none";
496 * Shows UI of LogReader. Logging functionality is not disrupted.
501 this._elContainer.style.display = "block";
505 * Collapses UI of LogReader. Logging functionality is not disrupted.
509 collapse : function() {
510 this._elConsole.style.display = "none";
512 this._elFt.style.display = "none";
514 this._btnCollapse.value = "Expand";
515 this.isCollapsed = true;
519 * Expands UI of LogReader. Logging functionality is not disrupted.
523 expand : function() {
524 this._elConsole.style.display = "block";
526 this._elFt.style.display = "block";
528 this._btnCollapse.value = "Collapse";
529 this.isCollapsed = false;
533 * Returns related checkbox element for given filter (i.e., category or source).
535 * @method getCheckbox
536 * @param {String} Category or source name.
537 * @return {Array} Array of all filter checkboxes.
539 getCheckbox : function(filter) {
540 return this._filterCheckboxes[filter];
544 * Returns array of enabled categories.
546 * @method getCategories
547 * @return {String[]} Array of enabled categories.
549 getCategories : function() {
550 return this._categoryFilters;
554 * Shows log messages associated with given category.
556 * @method showCategory
557 * @param {String} Category name.
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) {
568 // ...or do it the old-fashioned way
570 for(var i=0; i<filtersArray.length; i++) {
571 if(filtersArray[i] === sCategory){
577 this._categoryFilters.push(sCategory);
579 var elCheckbox = this.getCheckbox(sCategory);
581 elCheckbox.checked = true;
586 * Hides log messages associated with given category.
588 * @method hideCategory
589 * @param {String} Category name.
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);
600 var elCheckbox = this.getCheckbox(sCategory);
602 elCheckbox.checked = false;
607 * Returns array of enabled sources.
610 * @return {Array} Array of enabled sources.
612 getSources : function() {
613 return this._sourceFilters;
617 * Shows log messages associated with given source.
620 * @param {String} Source name.
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) {
631 // ...or do it the old-fashioned way
633 for(var i=0; i<filtersArray.length; i++) {
634 if(sSource == filtersArray[i]){
639 filtersArray.push(sSource);
641 var elCheckbox = this.getCheckbox(sSource);
643 elCheckbox.checked = true;
648 * Hides log messages associated with given source.
651 * @param {String} Source name.
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);
662 var elCheckbox = this.getCheckbox(sSource);
664 elCheckbox.checked = false;
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.
674 * @method clearConsole
676 clearConsole : function() {
677 // Clear the buffer of any pending messages
678 this._timeout = null;
680 this._consoleMsgCount = 0;
682 var elConsole = this._elConsole;
683 elConsole.innerHTML = '';
687 * Updates title to given string.
690 * @param sTitle {String} New title.
692 setTitle : function(sTitle) {
693 this._title.innerHTML = this.html2Text(sTitle);
697 * Gets timestamp of the last log.
699 * @method getLastTime
700 * @return {Date} Timestamp of the last log.
702 getLastTime : function() {
703 return this._lastTime;
706 formatMsg : function (entry) {
707 var Static = YAHOO.widget.LogReader,
708 entryFormat = this.entryFormat || (this.verboseOutput ?
709 Static.VERBOSE_TEMPLATE : Static.BASIC_TEMPLATE),
711 category : entry.category,
713 // Label for color-coded display
714 label : entry.category.substring(0,4).toUpperCase(),
716 sourceAndDetail : entry.sourceDetail ?
717 entry.source + " " + entry.sourceDetail :
720 // Escape HTML entities in the log message itself for output
722 message : this.html2Text(entry.msg || entry.message || '')
726 if (entry.time && entry.time.getTime) {
727 info.localTime = entry.time.toLocaleTimeString ?
728 entry.time.toLocaleTimeString() :
729 entry.time.toString();
731 // Calculate the elapsed time to be from the last item that
732 // passed through the filter, not the absolute previous item
734 info.elapsedTime = entry.time.getTime() - this.getLastTime();
736 info.totalTime = entry.time.getTime() -
737 YAHOO.widget.Logger.getStartTime();
740 var msg = Static.ENTRY_TEMPLATE.cloneNode(true);
741 if (this.verboseOutput) {
742 msg.className += ' yui-log-verbose';
745 // Bug 2061169: Workaround for YAHOO.lang.substitute()
746 msg.innerHTML = entryFormat.replace(/\{(\w+)\}/g, function (x, placeholder) { return (placeholder in info) ? info[placeholder] : ''; });
752 * Converts input chars "<", ">", and "&" to HTML entities.
755 * @param sHtml {String} String to convert.
758 html2Text : function(sHtml) {
761 return sHtml.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
766 /////////////////////////////////////////////////////////////////////////////
768 // Private member variables
770 /////////////////////////////////////////////////////////////////////////////
773 * Name of LogReader instance.
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.
787 * @property _elDefaultContainer
791 //YAHOO.widget.LogReader._elDefaultContainer = null;
794 * Buffer of log message objects for batch output.
803 * Number of log messages output to console.
805 * @property _consoleMsgCount
810 _consoleMsgCount : 0,
813 * Date of last output log message.
815 * @property _lastTime
822 * Batched output timeout ID.
831 * Hash of filters and their related checkbox elements.
833 * @property _filterCheckboxes
837 _filterCheckboxes : null,
840 * Array of filters for log message categories.
842 * @property _categoryFilters
846 _categoryFilters : null,
849 * Array of filters for log message sources.
851 * @property _sourceFilters
855 _sourceFilters : null,
858 * LogReader container element.
860 * @property _elContainer
867 * LogReader header element.
876 * LogReader collapse element.
878 * @property _elCollapse
885 * LogReader collapse button element.
887 * @property _btnCollapse
894 * LogReader title header element.
903 * LogReader console element.
905 * @property _elConsole
912 * LogReader footer element.
921 * LogReader buttons container element.
930 * Container element for LogReader category filter checkboxes.
932 * @property _elCategoryFilters
936 _elCategoryFilters : null,
939 * Container element for LogReader source filter checkboxes.
941 * @property _elSourceFilters
945 _elSourceFilters : null,
948 * LogReader pause button element.
950 * @property _btnPause
957 * Clear button element.
959 * @property _btnClear
965 /////////////////////////////////////////////////////////////////////////////
969 /////////////////////////////////////////////////////////////////////////////
972 * Initializes the primary container element.
974 * @method _initContainerEl
975 * @param elContainer {HTMLElement} Container element by reference or string ID.
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");
986 // ...or create container from scratch
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");
993 //YAHOO.widget.LogReader._elDefaultContainer = this._elContainer;
995 // If implementer has provided container values, trust and set those
996 var containerStyle = this._elContainer.style;
998 containerStyle.width = this.width;
1001 containerStyle.right = this.right;
1004 containerStyle.top = this.top;
1007 containerStyle.left = this.left;
1008 containerStyle.right = "auto";
1011 containerStyle.bottom = this.bottom;
1012 containerStyle.top = "auto";
1015 containerStyle.fontSize = this.fontSize;
1018 if(navigator.userAgent.toLowerCase().indexOf("opera") != -1) {
1019 document.body.style += '';
1025 * Initializes the header element.
1027 * @method _initHeaderEl
1030 _initHeaderEl : function() {
1035 // Unhook DOM events
1036 YAHOO.util.Event.purgeElement(this._elHd, true);
1038 // Remove DOM elements
1039 this._elHd.innerHTML = "";
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";
1047 this._elCollapse = this._elHd.appendChild(document.createElement("div"));
1048 this._elCollapse.className = "yui-log-btns";
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);
1060 this._title = this._elHd.appendChild(document.createElement("h4"));
1061 this._title.innerHTML = "Logger Console";
1065 * Initializes the console element.
1067 * @method _initConsoleEl
1070 _initConsoleEl : function() {
1072 if(this._elConsole) {
1073 // Unhook DOM events
1074 YAHOO.util.Event.purgeElement(this._elConsole, true);
1076 // Remove DOM elements
1077 this._elConsole.innerHTML = "";
1081 this._elConsole = this._elContainer.appendChild(document.createElement("div"));
1082 this._elConsole.className = "yui-log-bd";
1084 // If implementer has provided console, trust and set those
1086 this._elConsole.style.height = this.height;
1091 * Initializes the footer element.
1093 * @method _initFooterEl
1096 _initFooterEl : function() {
1099 // Don't create footer elements if footer is disabled
1100 if(this.footerEnabled) {
1103 // Unhook DOM events
1104 YAHOO.util.Event.purgeElement(this._elFt, true);
1106 // Remove DOM elements
1107 this._elFt.innerHTML = "";
1110 this._elFt = this._elContainer.appendChild(document.createElement("div"));
1111 this._elFt.className = "yui-log-ft";
1113 this._elBtns = this._elFt.appendChild(document.createElement("div"));
1114 this._elBtns.className = "yui-log-btns";
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);
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);
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";
1144 * Initializes Drag and Drop on the header element.
1146 * @method _initDragDrop
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";
1162 * Initializes category filters.
1164 * @method _initCategories
1167 _initCategories : function() {
1168 // Initialize category filters
1169 this._categoryFilters = [];
1170 var aInitialCategories = YAHOO.widget.Logger.categories;
1172 for(var j=0; j < aInitialCategories.length; j++) {
1173 var sCategory = aInitialCategories[j];
1175 // Add category to the internal array of filters
1176 this._categoryFilters.push(sCategory);
1178 // Add checkbox element if UI is enabled
1179 if(this._elCategoryFilters) {
1180 this._createCategoryCheckbox(sCategory);
1186 * Initializes source filters.
1188 * @method _initSources
1191 _initSources : function() {
1192 // Initialize source filters
1193 this._sourceFilters = [];
1194 var aInitialSources = YAHOO.widget.Logger.sources;
1196 for(var j=0; j < aInitialSources.length; j++) {
1197 var sSource = aInitialSources[j];
1199 // Add source to the internal array of filters
1200 this._sourceFilters.push(sSource);
1202 // Add checkbox element if UI is enabled
1203 if(this._elSourceFilters) {
1204 this._createSourceCheckbox(sSource);
1210 * Creates the UI for a category filter in the LogReader footer element.
1212 * @method _createCategoryCheckbox
1213 * @param sCategory {String} Category name.
1216 _createCategoryCheckbox : function(sCategory) {
1220 var elParent = this._elCategoryFilters;
1221 var elFilter = elParent.appendChild(document.createElement("span"));
1222 elFilter.className = "yui-log-filtergrp";
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;
1234 // Subscribe to the click event
1235 YAHOO.util.Event.addListener(chkCategory,'click',oSelf._onCheckCategory,oSelf);
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;
1243 this._filterCheckboxes[sCategory] = chkCategory;
1248 * Creates a checkbox in the LogReader footer element to filter by source.
1250 * @method _createSourceCheckbox
1251 * @param sSource {String} Source name.
1254 _createSourceCheckbox : function(sSource) {
1258 var elParent = this._elSourceFilters;
1259 var elFilter = elParent.appendChild(document.createElement("span"));
1260 elFilter.className = "yui-log-filtergrp";
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;
1272 // Subscribe to the click event
1273 YAHOO.util.Event.addListener(chkSource,'click',oSelf._onCheckSource,oSelf);
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;
1281 this._filterCheckboxes[sSource] = chkSource;
1286 * Reprints all log messages in the stack through filters.
1288 * @method _filterLogs
1291 _filterLogs : function() {
1292 // Reprint stack with new filters
1293 if (this._elConsole !== null) {
1294 this.clearConsole();
1295 this._printToConsole(YAHOO.widget.Logger.getStack());
1300 * Sends buffer of log messages to output and clears buffer.
1302 * @method _printBuffer
1305 _printBuffer : function() {
1306 this._timeout = null;
1308 if(this._elConsole !== null) {
1309 var thresholdMax = this.thresholdMax;
1310 thresholdMax = (thresholdMax && !isNaN(thresholdMax)) ? thresholdMax : 500;
1311 if(this._consoleMsgCount < thresholdMax) {
1313 for (var i=0; i<this._buffer.length; i++) {
1314 entries[i] = this._buffer[i];
1317 this._printToConsole(entries);
1323 if(!this.newestOnTop) {
1324 this._elConsole.scrollTop = this._elConsole.scrollHeight;
1330 * Cycles through an array of log messages, and outputs each one to the console
1331 * if its category has not been filtered out.
1333 * @method _printToConsole
1334 * @param aEntries {Object[]} Array of LogMsg objects to output to console.
1337 _printToConsole : function(aEntries) {
1338 // Manage the number of messages displayed in the console
1339 var entriesLen = aEntries.length,
1340 df = document.createDocumentFragment(),
1342 thresholdMin = this.thresholdMin,
1343 sourceFiltersLen = this._sourceFilters.length,
1344 categoryFiltersLen = this._categoryFilters.length,
1348 if(isNaN(thresholdMin) || (thresholdMin > this.thresholdMax)) {
1351 entriesStartIndex = (entriesLen > thresholdMin) ? (entriesLen - thresholdMin) : 0;
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;
1359 // Get log message details
1360 var entry = aEntries[i];
1361 var source = entry.source;
1362 var category = entry.category;
1364 for(j=0; j<sourceFiltersLen; j++) {
1365 if(source == this._sourceFilters[j]) {
1366 okToFilterCats = true;
1370 if(okToFilterCats) {
1371 for(j=0; j<categoryFiltersLen; j++) {
1372 if(category == this._categoryFilters[j]) {
1379 msg = this.formatMsg(entry);
1380 if (typeof msg === 'string') {
1381 msgHTML[msgHTML.length] = msg;
1383 df.insertBefore(msg, this.newestOnTop ?
1384 df.firstChild || null : null);
1386 this._consoleMsgCount++;
1387 this._lastTime = entry.time.getTime();
1391 if (msgHTML.length) {
1392 msgHTML.splice(0,0,this._elConsole.innerHTML);
1393 this._elConsole.innerHTML = this.newestOnTop ?
1394 msgHTML.reverse().join('') :
1396 } else if (df.firstChild) {
1397 this._elConsole.insertBefore(df, this.newestOnTop ?
1398 this._elConsole.firstChild || null : null);
1402 /////////////////////////////////////////////////////////////////////////////
1404 // Private event handlers
1406 /////////////////////////////////////////////////////////////////////////////
1409 * Handles Logger's categoryCreateEvent.
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.
1417 _onCategoryCreate : function(sType, aArgs, oSelf) {
1418 var category = aArgs[0];
1420 // Add category to the internal array of filters
1421 oSelf._categoryFilters.push(category);
1424 oSelf._createCategoryCheckbox(category);
1429 * Handles Logger's sourceCreateEvent.
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.
1437 _onSourceCreate : function(sType, aArgs, oSelf) {
1438 var source = aArgs[0];
1440 // Add source to the internal array of filters
1441 oSelf._sourceFilters.push(source);
1444 oSelf._createSourceCheckbox(source);
1449 * Handles check events on the category filter checkboxes.
1451 * @method _onCheckCategory
1452 * @param v {HTMLEvent} The click event.
1453 * @param oSelf {Object} The LogReader instance.
1456 _onCheckCategory : function(v, oSelf) {
1457 var category = this.category;
1459 oSelf.hideCategory(category);
1462 oSelf.showCategory(category);
1467 * Handles check events on the category filter checkboxes.
1469 * @method _onCheckSource
1470 * @param v {HTMLEvent} The click event.
1471 * @param oSelf {Object} The LogReader instance.
1474 _onCheckSource : function(v, oSelf) {
1475 var source = this.source;
1477 oSelf.hideSource(source);
1480 oSelf.showSource(source);
1485 * Handles click events on the collapse button.
1487 * @method _onClickCollapseBtn
1488 * @param v {HTMLEvent} The click event.
1489 * @param oSelf {Object} The LogReader instance
1492 _onClickCollapseBtn : function(v, oSelf) {
1493 if(!oSelf.isCollapsed) {
1502 * Handles click events on the pause button.
1504 * @method _onClickPauseBtn
1505 * @param v {HTMLEvent} The click event.
1506 * @param oSelf {Object} The LogReader instance.
1509 _onClickPauseBtn : function(v, oSelf) {
1510 if(!oSelf.isPaused) {
1519 * Handles click events on the clear button.
1521 * @method _onClickClearBtn
1522 * @param v {HTMLEvent} The click event.
1523 * @param oSelf {Object} The LogReader instance.
1526 _onClickClearBtn : function(v, oSelf) {
1527 oSelf.clearConsole();
1531 * Handles Logger's newLogEvent.
1534 * @param sType {String} The event.
1535 * @param aArgs {Object[]} Data passed from event firer.
1536 * @param oSelf {Object} The LogReader instance.
1539 _onNewLog : function(sType, aArgs, oSelf) {
1540 var logEntry = aArgs[0];
1541 oSelf._buffer.push(logEntry);
1543 if (oSelf.logReaderEnabled === true && oSelf._timeout === null) {
1544 oSelf._timeout = setTimeout(function(){oSelf._printBuffer();}, oSelf.outputBuffer);
1549 * Handles Logger's resetEvent.
1552 * @param sType {String} The event.
1553 * @param aArgs {Object[]} Data passed from event firer.
1554 * @param oSelf {Object} The LogReader instance.
1557 _onReset : function(sType, aArgs, oSelf) {
1558 oSelf._filterLogs();
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
1570 * @requires yahoo, event, dom
1571 * @optional dragdrop
1572 * @namespace YAHOO.widget
1573 * @title Logger Widget
1576 /****************************************************************************/
1577 /****************************************************************************/
1578 /****************************************************************************/
1581 if(!YAHOO.widget.Logger) {
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.
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
1606 /////////////////////////////////////////////////////////////////////////////
1608 // Public properties
1610 /////////////////////////////////////////////////////////////////////////////
1612 * True if Logger is enabled, false otherwise.
1614 * @property loggerEnabled
1621 * Array of categories.
1623 * @property categories
1626 * @default ["info","warn","error","time","window"]
1635 * @default ["global"]
1639 * Upper limit on size of internal stack.
1641 * @property maxStackEntries
1647 /////////////////////////////////////////////////////////////////////////////
1649 // Private properties
1651 /////////////////////////////////////////////////////////////////////////////
1653 * Internal property to track whether output to browser console is enabled.
1655 * @property _browserConsoleEnabled
1663 * Array to hold all log messages.
1671 * Static timestamp of Logger initialization.
1673 * @property _startTime
1679 * Timestamp of last logged message.
1681 * @property _lastTime
1686 /////////////////////////////////////////////////////////////////////////////
1690 /////////////////////////////////////////////////////////////////////////////
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.
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.
1702 YAHOO.widget.Logger.log = function(sMsg, sCategory, sSource) {
1703 if(this.loggerEnabled) {
1705 sCategory = "info"; // default category
1708 sCategory = sCategory.toLocaleLowerCase();
1709 if(this._isNewCategory(sCategory)) {
1710 this._createNewCategory(sCategory);
1713 var sClass = "global"; // default source
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);
1726 if(this._isNewSource(sClass)) {
1727 this._createNewSource(sClass);
1731 var timestamp = new Date();
1732 var logEntry = new YAHOO.widget.LogMsg({
1735 category: sCategory,
1737 sourceDetail: sDetail
1740 var stack = this._stack;
1741 var maxStackEntries = this.maxStackEntries;
1742 if(maxStackEntries && !isNaN(maxStackEntries) &&
1743 (stack.length >= maxStackEntries)) {
1746 stack.push(logEntry);
1747 this.newLogEvent.fire(logEntry);
1749 if(this._browserConsoleEnabled) {
1750 this._printToBrowserConsole(logEntry);
1760 * Resets internal stack and startTime, enables Logger, and fires logResetEvent.
1764 YAHOO.widget.Logger.reset = function() {
1766 this._startTime = new Date().getTime();
1767 this.loggerEnabled = true;
1768 this.log("Logger reset");
1769 this.logResetEvent.fire();
1773 * Public accessor to internal stack of log message objects.
1776 * @return {Object[]} Array of log message objects.
1778 YAHOO.widget.Logger.getStack = function() {
1783 * Public accessor to internal start time.
1785 * @method getStartTime
1786 * @return {Date} Internal date of when Logger singleton was initialized.
1788 YAHOO.widget.Logger.getStartTime = function() {
1789 return this._startTime;
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.
1796 * @method disableBrowserConsole
1798 YAHOO.widget.Logger.disableBrowserConsole = function() {
1799 YAHOO.log("Logger output to the function console.log() has been disabled.");
1800 this._browserConsoleEnabled = false;
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.
1807 * @method enableBrowserConsole
1809 YAHOO.widget.Logger.enableBrowserConsole = function() {
1810 this._browserConsoleEnabled = true;
1811 YAHOO.log("Logger output to the function console.log() has been enabled.");
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.
1819 * @method handleWindowErrors
1821 YAHOO.widget.Logger.handleWindowErrors = function() {
1822 if(!YAHOO.widget.Logger._windowErrorsHandled) {
1823 // Save any previously defined handler to call
1825 YAHOO.widget.Logger._origOnWindowError = window.onerror;
1827 window.onerror = YAHOO.widget.Logger._onWindowError;
1828 YAHOO.widget.Logger._windowErrorsHandled = true;
1829 YAHOO.log("Logger handling of window.onerror has been enabled.");
1832 YAHOO.log("Logger handling of window.onerror had already been enabled.");
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.
1841 * @method unhandleWindowErrors
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;
1851 window.onerror = null;
1853 YAHOO.widget.Logger._windowErrorsHandled = false;
1854 YAHOO.log("Logger handling of window.onerror has been disabled.");
1857 YAHOO.log("Logger handling of window.onerror had already been disabled.");
1861 /////////////////////////////////////////////////////////////////////////////
1865 /////////////////////////////////////////////////////////////////////////////
1868 * Fired when a new category has been created.
1870 * @event categoryCreateEvent
1871 * @param sCategory {String} Category name.
1873 YAHOO.widget.Logger.categoryCreateEvent =
1874 new YAHOO.util.CustomEvent("categoryCreate", this, true);
1877 * Fired when a new source has been named.
1879 * @event sourceCreateEvent
1880 * @param sSource {String} Source name.
1882 YAHOO.widget.Logger.sourceCreateEvent =
1883 new YAHOO.util.CustomEvent("sourceCreate", this, true);
1886 * Fired when a new log message has been created.
1888 * @event newLogEvent
1889 * @param sMsg {String} Log message.
1891 YAHOO.widget.Logger.newLogEvent = new YAHOO.util.CustomEvent("newLog", this, true);
1894 * Fired when the Logger has been reset has been created.
1896 * @event logResetEvent
1898 YAHOO.widget.Logger.logResetEvent = new YAHOO.util.CustomEvent("logReset", this, true);
1900 /////////////////////////////////////////////////////////////////////////////
1904 /////////////////////////////////////////////////////////////////////////////
1907 * Creates a new category of log messages and fires categoryCreateEvent.
1909 * @method _createNewCategory
1910 * @param sCategory {String} Category name.
1913 YAHOO.widget.Logger._createNewCategory = function(sCategory) {
1914 this.categories.push(sCategory);
1915 this.categoryCreateEvent.fire(sCategory);
1919 * Checks to see if a category has already been created.
1921 * @method _isNewCategory
1922 * @param sCategory {String} Category name.
1923 * @return {Boolean} Returns true if category is unknown, else returns false.
1926 YAHOO.widget.Logger._isNewCategory = function(sCategory) {
1927 for(var i=0; i < this.categories.length; i++) {
1928 if(sCategory == this.categories[i]) {
1936 * Creates a new source of log messages and fires sourceCreateEvent.
1938 * @method _createNewSource
1939 * @param sSource {String} Source name.
1942 YAHOO.widget.Logger._createNewSource = function(sSource) {
1943 this.sources.push(sSource);
1944 this.sourceCreateEvent.fire(sSource);
1948 * Checks to see if a source already exists.
1950 * @method _isNewSource
1951 * @param sSource {String} Source name.
1952 * @return {Boolean} Returns true if source is unknown, else returns false.
1955 YAHOO.widget.Logger._isNewSource = function(sSource) {
1957 for(var i=0; i < this.sources.length; i++) {
1958 if(sSource == this.sources[i]) {
1967 * Outputs a log message to global console.log() function.
1969 * @method _printToBrowserConsole
1970 * @param oEntry {Object} Log entry object.
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();
1978 var time = oEntry.time;
1980 if (time.toLocaleTimeString) {
1981 localTime = time.toLocaleTimeString();
1984 localTime = time.toString();
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;
1994 elapsedTime + "ms): " +
1995 oEntry.source + ": ";
1998 if (YAHOO.env.ua.webkit) {
1999 output += oEntry.msg;
2002 console.log(output, oEntry.msg);
2006 /////////////////////////////////////////////////////////////////////////////
2008 // Private event handlers
2010 /////////////////////////////////////////////////////////////////////////////
2013 * Handles logging of messages due to window error events.
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.
2021 YAHOO.widget.Logger._onWindowError = function(sMsg,sUrl,sLine) {
2022 // Logger is not in scope of this event handler
2024 YAHOO.widget.Logger.log(sMsg+' ('+sUrl+', line '+sLine+')', "window");
2025 if(YAHOO.widget.Logger._origOnWindowError) {
2026 YAHOO.widget.Logger._origOnWindowError();
2034 /////////////////////////////////////////////////////////////////////////////
2038 /////////////////////////////////////////////////////////////////////////////
2040 YAHOO.widget.Logger.log("Logger initialized");
2044 YAHOO.register("logger", YAHOO.widget.Logger, {version: "2.7.0", build: "1799"});