Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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