PageRenderTime 59ms CodeModel.GetById 13ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 0ms

/hudson-war/src/main/webapp/scripts/yui/logger/logger-debug.js

http://github.com/hudson/hudson
JavaScript | 2030 lines | 778 code | 212 blank | 1040 comment | 138 complexity | 99ff4fd16c1ebf5bc7f0e463f3fcaf53 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file