MDL-32843 import YUI 3.5.1
[moodle.git] / lib / yui / 3.5.1 / build / console / console-debug.js
blobe282bac555757f0487ae5c2f72b8f0d3abbe3ade
1 /*
2 YUI 3.5.1 (build 22)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('console', function(Y) {
9 /**
10  * Console creates a visualization for messages logged through calls to a YUI
11  * instance's <code>Y.log( message, category, source )</code> method.  The
12  * debug versions of YUI modules will include logging statements to offer some
13  * insight into the steps executed during that module's operation.  Including
14  * log statements in your code will cause those messages to also appear in the
15  * Console.  Use Console to aid in developing your page or application.
16  *
17  * Entry categories &quot;info&quot;, &quot;warn&quot;, and &quot;error&quot;
18  * are also referred to as the log level, and entries are filtered against the
19  * configured logLevel.
20  *
21  * @module console
22  * @class Console
23  * @extends Widget
24  * @param conf {Object} Configuration object (see Configuration attributes)
25  * @constructor
26  */
27 var getCN = Y.ClassNameManager.getClassName,
28     CHECKED        = 'checked',
29     CLEAR          = 'clear',
30     CLICK          = 'click',
31     COLLAPSED      = 'collapsed',
32     CONSOLE        = 'console',
33     CONTENT_BOX    = 'contentBox',
34     DISABLED       = 'disabled',
35     ENTRY          = 'entry',
36     ERROR          = 'error',
37     HEIGHT         = 'height',
38     INFO           = 'info',
39     LAST_TIME      = 'lastTime',
40     PAUSE          = 'pause',
41     PAUSED         = 'paused',
42     RESET          = 'reset',
43     START_TIME     = 'startTime',
44     TITLE          = 'title',
45     WARN           = 'warn',
47     DOT = '.',
49     C_BUTTON           = getCN(CONSOLE,'button'),
50     C_CHECKBOX         = getCN(CONSOLE,'checkbox'),
51     C_CLEAR            = getCN(CONSOLE,CLEAR),
52     C_COLLAPSE         = getCN(CONSOLE,'collapse'),
53     C_COLLAPSED        = getCN(CONSOLE,COLLAPSED),
54     C_CONSOLE_CONTROLS = getCN(CONSOLE,'controls'),
55     C_CONSOLE_HD       = getCN(CONSOLE,'hd'),
56     C_CONSOLE_BD       = getCN(CONSOLE,'bd'),
57     C_CONSOLE_FT       = getCN(CONSOLE,'ft'),
58     C_CONSOLE_TITLE    = getCN(CONSOLE,TITLE),
59     C_ENTRY            = getCN(CONSOLE,ENTRY),
60     C_ENTRY_CAT        = getCN(CONSOLE,ENTRY,'cat'),
61     C_ENTRY_CONTENT    = getCN(CONSOLE,ENTRY,'content'),
62     C_ENTRY_META       = getCN(CONSOLE,ENTRY,'meta'),
63     C_ENTRY_SRC        = getCN(CONSOLE,ENTRY,'src'),
64     C_ENTRY_TIME       = getCN(CONSOLE,ENTRY,'time'),
65     C_PAUSE            = getCN(CONSOLE,PAUSE),
66     C_PAUSE_LABEL      = getCN(CONSOLE,PAUSE,'label'),
68     RE_INLINE_SOURCE = /^(\S+)\s/,
69     RE_AMP = /&(?!#?[a-z0-9]+;)/g,
70     RE_GT  = />/g,
71     RE_LT  = /</g,
73     ESC_AMP = '&#38;',
74     ESC_GT  = '&#62;',
75     ESC_LT  = '&#60;',
76     
77     ENTRY_TEMPLATE_STR =
78         '<div class="{entry_class} {cat_class} {src_class}">'+
79             '<p class="{entry_meta_class}">'+
80                 '<span class="{entry_src_class}">'+
81                     '{sourceAndDetail}'+
82                 '</span>'+
83                 '<span class="{entry_cat_class}">'+
84                     '{category}</span>'+
85                 '<span class="{entry_time_class}">'+
86                     ' {totalTime}ms (+{elapsedTime}) {localTime}'+
87                 '</span>'+
88             '</p>'+
89             '<pre class="{entry_content_class}">{message}</pre>'+
90         '</div>',
92     L = Y.Lang,
93     create     = Y.Node.create,
94     isNumber   = L.isNumber,
95     isString   = L.isString,
96     merge      = Y.merge,
97     substitute = Y.substitute;
98     
100 function Console() {
101     Console.superclass.constructor.apply(this,arguments);
104 Y.Console = Y.extend(Console, Y.Widget,
106 // Y.Console prototype
108     /**
109      * Category to prefix all event subscriptions to allow for ease of detach
110      * during destroy.
111      *
112      * @property _evtCat
113      * @type string
114      * @protected
115      */
116     _evtCat : null,
118     /**
119      * Reference to the Node instance containing the header contents.
120      *
121      * @property _head
122      * @type Node
123      * @default null
124      * @protected
125      */
126     _head    : null,
128     /**
129      * Reference to the Node instance that will house the console messages.
130      *
131      * @property _body
132      * @type Node
133      * @default null
134      * @protected
135      */
136     _body    : null,
138     /**
139      * Reference to the Node instance containing the footer contents.
140      *
141      * @property _foot
142      * @type Node
143      * @default null
144      * @protected
145      */
146     _foot    : null,
148     /**
149      * Holds the object API returned from <code>Y.later</code> for the print
150      * loop interval.
151      *
152      * @property _printLoop
153      * @type Object
154      * @default null
155      * @protected
156      */
157     _printLoop : null,
159     /**
160      * Array of normalized message objects awaiting printing.
161      *
162      * @property buffer
163      * @type Array
164      * @default null
165      * @protected
166      */
167     buffer   : null,
169     /**
170      * Wrapper for <code>Y.log</code>.
171      *
172      * @method log
173      * @param arg* {MIXED} (all arguments passed through to <code>Y.log</code>)
174      * @chainable
175      */
176     log : function () {
177         Y.log.apply(Y,arguments);
179         return this;
180     },
182     /**
183      * Clear the console of messages and flush the buffer of pending messages.
184      *
185      * @method clearConsole
186      * @chainable
187      */
188     clearConsole : function () {
189         // TODO: clear event listeners from console contents
190         this._body.empty();
192         this._cancelPrintLoop();
194         this.buffer = [];
196         return this;
197     },
199     /**
200      * Clears the console and resets internal timers.
201      *
202      * @method reset
203      * @chainable
204      */
205     reset : function () {
206         this.fire(RESET);
207         
208         return this;
209     },
211     /**
212      * Collapses the body and footer.
213      *
214      * @method collapse
215      * @chainable
216      */
217     collapse : function () {
218         this.set(COLLAPSED, true);
220         return this;
221     },
223     /**
224      * Expands the body and footer if collapsed.
225      *
226      * @method expand
227      * @chainable
228      */
229     expand : function () {
230         this.set(COLLAPSED, false);
232         return this;
233     },
235     /**
236      * Outputs buffered messages to the console UI.  This is typically called
237      * from a scheduled interval until the buffer is empty (referred to as the
238      * print loop).  The number of buffered messages output to the Console is
239      * limited to the number provided as an argument.  If no limit is passed,
240      * all buffered messages are rendered.
241      * 
242      * @method printBuffer
243      * @param limit {Number} (optional) max number of buffered entries to write
244      * @chainable
245      */
246     printBuffer: function (limit) {
247         var messages    = this.buffer,
248             debug       = Y.config.debug,
249             entries     = [],
250             consoleLimit= this.get('consoleLimit'),
251             newestOnTop = this.get('newestOnTop'),
252             anchor      = newestOnTop ? this._body.get('firstChild') : null,
253             i;
255         if (messages.length > consoleLimit) {
256             messages.splice(0, messages.length - consoleLimit);
257         }
259         limit = Math.min(messages.length, (limit || messages.length));
260         
261         // turn off logging system
262         Y.config.debug = false;
264         if (!this.get(PAUSED) && this.get('rendered')) {
266             for (i = 0; i < limit && messages.length; ++i) {
267                 entries[i] = this._createEntryHTML(messages.shift());
268             }
270             if (!messages.length) {
271                 this._cancelPrintLoop();
272             }
274             if (entries.length) {
275                 if (newestOnTop) {
276                     entries.reverse();
277                 }
279                 this._body.insertBefore(create(entries.join('')), anchor);
281                 if (this.get('scrollIntoView')) {
282                     this.scrollToLatest();
283                 }
285                 this._trimOldEntries();
286             }
287         }
289         // restore logging system
290         Y.config.debug = debug;
292         return this;
293     },
295     
296     /**
297      * Constructor code.  Set up the buffer and entry template, publish
298      * internal events, and subscribe to the configured logEvent.
299      * 
300      * @method initializer
301      * @protected
302      */
303     initializer : function () {
304         this._evtCat = Y.stamp(this) + '|';
306         this.buffer = [];
308         this.get('logSource').on(this._evtCat +
309             this.get('logEvent'),Y.bind("_onLogEvent",this));
311         /**
312          * Transfers a received message to the print loop buffer.  Default
313          * behavior defined in _defEntryFn.
314          *
315          * @event entry
316          * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
317          *  <dl>
318          *      <dt>message</dt>
319          *          <dd>The message data normalized into an object literal (see _normalizeMessage)</dd>
320          *  </dl>
321          * @preventable _defEntryFn
322          */
323         this.publish(ENTRY, { defaultFn: this._defEntryFn });
325         /**
326          * Triggers the reset behavior via the default logic in _defResetFn.
327          *
328          * @event reset
329          * @param event {Event.Facade} Event Facade object
330          * @preventable _defResetFn
331          */
332         this.publish(RESET, { defaultFn: this._defResetFn });
334         this.after('rendered', this._schedulePrint);
335     },
337     /**
338      * Tears down the instance, flushing event subscriptions and purging the UI.
339      *
340      * @method destructor
341      * @protected
342      */
343     destructor : function () {
344         var bb = this.get('boundingBox');
346         this._cancelPrintLoop();
348         this.get('logSource').detach(this._evtCat + '*');
349         
350         bb.purge(true);
351     },
353     /**
354      * Generate the Console UI.
355      *
356      * @method renderUI
357      * @protected
358      */
359     renderUI : function () {
360         this._initHead();
361         this._initBody();
362         this._initFoot();
364         // Apply positioning to the bounding box if appropriate
365         var style = this.get('style');
366         if (style !== 'block') {
367             this.get('boundingBox').addClass(this.getClassName(style));
368         }
369     },
371     /**
372      * Sync the UI state to the current attribute state.
373      *
374      * @method syncUI
375      */
376     syncUI : function () {
377         this._uiUpdatePaused(this.get(PAUSED));
378         this._uiUpdateCollapsed(this.get(COLLAPSED));
379         this._uiSetHeight(this.get(HEIGHT));
380     },
382     /**
383      * Set up event listeners to wire up the UI to the internal state.
384      *
385      * @method bindUI
386      * @protected
387      */
388     bindUI : function () {
389         this.get(CONTENT_BOX).one('button.'+C_COLLAPSE).
390             on(CLICK,this._onCollapseClick,this);
392         this.get(CONTENT_BOX).one('input[type=checkbox].'+C_PAUSE).
393             on(CLICK,this._onPauseClick,this);
395         this.get(CONTENT_BOX).one('button.'+C_CLEAR).
396             on(CLICK,this._onClearClick,this);
398         // Attribute changes
399         this.after(this._evtCat + 'stringsChange',
400             this._afterStringsChange);
401         this.after(this._evtCat + 'pausedChange',
402             this._afterPausedChange);
403         this.after(this._evtCat + 'consoleLimitChange',
404             this._afterConsoleLimitChange);
405         this.after(this._evtCat + 'collapsedChange',
406             this._afterCollapsedChange);
407     },
409     
410     /**
411      * Create the DOM structure for the header elements.
412      *
413      * @method _initHead
414      * @protected
415      */
416     _initHead : function () {
417         var cb   = this.get(CONTENT_BOX),
418             info = merge(Console.CHROME_CLASSES, {
419                         str_collapse : this.get('strings.collapse'),
420                         str_title : this.get('strings.title')
421                     });
423         this._head = create(substitute(Console.HEADER_TEMPLATE,info));
425         cb.insertBefore(this._head,cb.get('firstChild'));
426     },
428     /**
429      * Create the DOM structure for the console body&#8212;where messages are
430      * rendered.
431      *
432      * @method _initBody
433      * @protected
434      */
435     _initBody : function () {
436         this._body = create(substitute(
437                             Console.BODY_TEMPLATE,
438                             Console.CHROME_CLASSES));
440         this.get(CONTENT_BOX).appendChild(this._body);
441     },
443     /**
444      * Create the DOM structure for the footer elements.
445      *
446      * @method _initFoot
447      * @protected
448      */
449     _initFoot : function () {
450         var info = merge(Console.CHROME_CLASSES, {
451                 id_guid   : Y.guid(),
452                 str_pause : this.get('strings.pause'),
453                 str_clear : this.get('strings.clear')
454             });
456         this._foot = create(substitute(Console.FOOTER_TEMPLATE,info));
458         this.get(CONTENT_BOX).appendChild(this._foot);
459     },
461     /**
462      * Determine if incoming log messages are within the configured logLevel
463      * to be buffered for printing.
464      *
465      * @method _isInLogLevel
466      * @protected
467      */
468     _isInLogLevel : function (e) {
469         var cat = e.cat, lvl = this.get('logLevel');
471         if (lvl !== INFO) {
472             cat = cat || INFO;
474             if (isString(cat)) {
475                 cat = cat.toLowerCase();
476             }
478             if ((cat === WARN && lvl === ERROR) ||
479                 (cat === INFO && lvl !== INFO)) {
480                 return false;
481             }
482         }
484         return true;
485     },
487     /**
488      * Create a log entry message from the inputs including the following keys:
489      * <ul>
490      *     <li>time - this moment</li>
491      *     <li>message - leg message</li>
492      *     <li>category - logLevel or custom category for the message</li>
493      *     <li>source - when provided, the widget or util calling Y.log</li>
494      *     <li>sourceAndDetail - same as source but can include instance info</li>
495      *     <li>localTime - readable version of time</li>
496      *     <li>elapsedTime - ms since last entry</li>
497      *     <li>totalTime - ms since Console was instantiated or reset</li>
498      * </ul>
499      *
500      * @method _normalizeMessage
501      * @param e {Event} custom event containing the log message
502      * @return Object the message object
503      * @protected
504      */
505     _normalizeMessage : function (e) {
507         var msg = e.msg,
508             cat = e.cat,
509             src = e.src,
511             m = {
512                 time            : new Date(),
513                 message         : msg,
514                 category        : cat || this.get('defaultCategory'),
515                 sourceAndDetail : src || this.get('defaultSource'),
516                 source          : null,
517                 localTime       : null,
518                 elapsedTime     : null,
519                 totalTime       : null
520             };
522         // Extract m.source "Foo" from m.sourceAndDetail "Foo bar baz"
523         m.source          = RE_INLINE_SOURCE.test(m.sourceAndDetail) ?
524                                 RegExp.$1 : m.sourceAndDetail;
525         m.localTime       = m.time.toLocaleTimeString ? 
526                             m.time.toLocaleTimeString() : (m.time + '');
527         m.elapsedTime     = m.time - this.get(LAST_TIME);
528         m.totalTime       = m.time - this.get(START_TIME);
530         this._set(LAST_TIME,m.time);
532         return m;
533     },
535     /**
536      * Sets an interval for buffered messages to be output to the console.
537      *
538      * @method _schedulePrint
539      * @protected
540      */
541     _schedulePrint : function () {
542         if (!this._printLoop && !this.get(PAUSED) && this.get('rendered')) {
543             this._printLoop = Y.later(
544                                 this.get('printTimeout'),
545                                 this, this.printBuffer,
546                                 this.get('printLimit'), true);
547         }
548     },
550     /**
551      * Translates message meta into the markup for a console entry.
552      *
553      * @method _createEntryHTML
554      * @param m {Object} object literal containing normalized message metadata
555      * @return String
556      * @protected
557      */
558     _createEntryHTML : function (m) {
559         m = merge(
560                 this._htmlEscapeMessage(m),
561                 Console.ENTRY_CLASSES,
562                 {
563                     cat_class : this.getClassName(ENTRY,m.category),
564                     src_class : this.getClassName(ENTRY,m.source)
565                 });
567         return this.get('entryTemplate').replace(/\{(\w+)\}/g,
568             function (_,token) {
569                 return token in m ? m[token] : '';
570             });
571     },
573     /**
574      * Scrolls to the most recent entry
575      *
576      * @method scrollToLatest
577      * @chainable
578      */
579     scrollToLatest : function () {
580         var scrollTop = this.get('newestOnTop') ?
581                             0 :
582                             this._body.get('scrollHeight');
584         this._body.set('scrollTop', scrollTop);
585     },
587     /**
588      * Performs HTML escaping on strings in the message object.
589      *
590      * @method _htmlEscapeMessage
591      * @param m {Object} the normalized message object
592      * @return Object the message object with proper escapement
593      * @protected
594      */
595     _htmlEscapeMessage : function (m) {
596         m.message         = this._encodeHTML(m.message);
597         m.source          = this._encodeHTML(m.source);
598         m.sourceAndDetail = this._encodeHTML(m.sourceAndDetail);
599         m.category        = this._encodeHTML(m.category);
601         return m;
602     },
604     /**
605      * Removes the oldest message entries from the UI to maintain the limit
606      * specified in the consoleLimit configuration.
607      *
608      * @method _trimOldEntries
609      * @protected
610      */
611     _trimOldEntries : function () {
612         // Turn off the logging system for the duration of this operation
613         // to prevent an infinite loop
614         Y.config.debug = false;
616         var bd = this._body,
617             limit = this.get('consoleLimit'),
618             debug = Y.config.debug,
619             entries,e,i,l;
621         if (bd) {
622             entries = bd.all(DOT+C_ENTRY);
623             l = entries.size() - limit;
625             if (l > 0) {
626                 if (this.get('newestOnTop')) {
627                     i = limit;
628                     l = entries.size();
629                 } else {
630                     i = 0;
631                 }
633                 this._body.setStyle('display','none');
635                 for (;i < l; ++i) {
636                     e = entries.item(i);
637                     if (e) {
638                         e.remove();
639                     }
640                 }
642                 this._body.setStyle('display','');
643             }
645         }
647         Y.config.debug = debug;
648     },
650     /**
651      * Returns the input string with ampersands (&amp;), &lt, and &gt; encoded
652      * as HTML entities.
653      *
654      * @method _encodeHTML
655      * @param s {String} the raw string
656      * @return String the encoded string
657      * @protected
658      */
659     _encodeHTML : function (s) {
660         return isString(s) ?
661             s.replace(RE_AMP,ESC_AMP).
662               replace(RE_LT, ESC_LT).
663               replace(RE_GT, ESC_GT) :
664             s;
665     },
667     /**
668      * Clears the timeout for printing buffered messages.
669      *
670      * @method _cancelPrintLoop
671      * @protected
672      */
673     _cancelPrintLoop : function () {
674         if (this._printLoop) {
675             this._printLoop.cancel();
676             this._printLoop = null;
677         }
678     },
680     /**
681      * Validates input value for style attribute.  Accepts only values 'inline',
682      * 'block', and 'separate'.
683      *
684      * @method _validateStyle
685      * @param style {String} the proposed value
686      * @return {Boolean} pass/fail
687      * @protected
688      */
689     _validateStyle : function (style) {
690         return style === 'inline' || style === 'block' || style === 'separate';
691     },
693     /**
694      * Event handler for clicking on the Pause checkbox to update the paused
695      * attribute.
696      *
697      * @method _onPauseClick
698      * @param e {Event} DOM event facade for the click event
699      * @protected
700      */
701     _onPauseClick : function (e) {
702         this.set(PAUSED,e.target.get(CHECKED));
703     },
705     /**
706      * Event handler for clicking on the Clear button.  Pass-through to
707      * <code>this.clearConsole()</code>.
708      *
709      * @method _onClearClick
710      * @param e {Event} DOM event facade for the click event
711      * @protected
712      */
713     _onClearClick : function (e) {
714         this.clearConsole();
715     },
717     /**
718      * Event handler for clicking on the Collapse/Expand button. Sets the
719      * &quot;collapsed&quot; attribute accordingly.
720      *
721      * @method _onCollapseClick
722      * @param e {Event} DOM event facade for the click event
723      * @protected
724      */
725     _onCollapseClick : function (e) {
726         this.set(COLLAPSED, !this.get(COLLAPSED));
727     },
730     /**
731      * Validator for logSource attribute.
732      *
733      * @method _validateLogSource
734      * @param v {Object} the desired logSource
735      * @return {Boolean} true if the input is an object with an <code>on</code>
736      *                   method
737      * @protected
738      */
739     _validateLogSource: function (v) {
740         return v && Y.Lang.isFunction(v.on);
741     },
743     /**
744      * Setter method for logLevel attribute.  Acceptable values are
745      * &quot;error&quot, &quot;warn&quot, and &quot;info&quot (case
746      * insensitive).  Other values are treated as &quot;info&quot;.
747      *
748      * @method _setLogLevel
749      * @param v {String} the desired log level
750      * @return String One of Console.LOG_LEVEL_INFO, _WARN, or _ERROR
751      * @protected
752      */
753     _setLogLevel : function (v) {
754         if (isString(v)) {
755             v = v.toLowerCase();
756         }
757         
758         return (v === WARN || v === ERROR) ? v : INFO;
759     },
761     /**
762      * Getter method for useBrowserConsole attribute.  Just a pass through to
763      * the YUI instance configuration setting.
764      *
765      * @method _getUseBrowserConsole
766      * @return {Boolean} or null if logSource is not a YUI instance
767      * @protected
768      */
769     _getUseBrowserConsole: function () {
770         var logSource = this.get('logSource');
771         return logSource instanceof YUI ?
772             logSource.config.useBrowserConsole : null;
773     },
775     /**
776      * Setter method for useBrowserConsole attributes.  Only functional if the
777      * logSource attribute points to a YUI instance.  Passes the value down to
778      * the YUI instance.  NOTE: multiple Console instances cannot maintain
779      * independent useBrowserConsole values, since it is just a pass through to
780      * the YUI instance configuration.
781      *
782      * @method _setUseBrowserConsole
783      * @param v {Boolean} false to disable browser console printing (default)
784      * @return {Boolean} true|false if logSource is a YUI instance
785      * @protected
786      */
787     _setUseBrowserConsole: function (v) {
788         var logSource = this.get('logSource');
789         if (logSource instanceof YUI) {
790             v = !!v;
791             logSource.config.useBrowserConsole = v;
792             return v;
793         } else {
794             return Y.Attribute.INVALID_VALUE;
795         }
796     },
798     /**
799      * Set the height of the Console container.  Set the body height to the
800      * difference between the configured height and the calculated heights of
801      * the header and footer.
802      * Overrides Widget.prototype._uiSetHeight.
803      *
804      * @method _uiSetHeight
805      * @param v {String|Number} the new height
806      * @protected
807      */
808     _uiSetHeight : function (v) {
809         Console.superclass._uiSetHeight.apply(this,arguments);
811         if (this._head && this._foot) {
812             var h = this.get('boundingBox').get('offsetHeight') -
813                     this._head.get('offsetHeight') -
814                     this._foot.get('offsetHeight');
816             this._body.setStyle(HEIGHT,h+'px');
817         }
818     },
820     /**
821      * Over-ride default content box sizing to do nothing, since we're sizing
822      * the body section to fill out height ourselves.
823      * 
824      * @method _uiSizeCB
825      * @protected
826      */
827     _uiSizeCB : function() {
828         // Do Nothing. Ideally want to move to Widget-StdMod, which accounts for
829         // _uiSizeCB        
830     },
832     /**
833      * Updates the UI if changes are made to any of the strings in the strings
834      * attribute.
835      *
836      * @method _afterStringsChange
837      * @param e {Event} Custom event for the attribute change
838      * @protected
839      */
840     _afterStringsChange : function (e) {
841         var prop   = e.subAttrName ? e.subAttrName.split(DOT)[1] : null,
842             cb     = this.get(CONTENT_BOX),
843             before = e.prevVal,
844             after  = e.newVal;
846         if ((!prop || prop === TITLE) && before.title !== after.title) {
847             cb.all(DOT+C_CONSOLE_TITLE).setContent(after.title);
848         }
850         if ((!prop || prop === PAUSE) && before.pause !== after.pause) {
851             cb.all(DOT+C_PAUSE_LABEL).setContent(after.pause);
852         }
854         if ((!prop || prop === CLEAR) && before.clear !== after.clear) {
855             cb.all(DOT+C_CLEAR).set('value',after.clear);
856         }
857     },
859     /**
860      * Updates the UI and schedules or cancels the print loop.
861      *
862      * @method _afterPausedChange
863      * @param e {Event} Custom event for the attribute change
864      * @protected
865      */
866     _afterPausedChange : function (e) {
867         var paused = e.newVal;
869         if (e.src !== Y.Widget.SRC_UI) {
870             this._uiUpdatePaused(paused);
871         }
873         if (!paused) {
874             this._schedulePrint();
875         } else if (this._printLoop) {
876             this._cancelPrintLoop();
877         }
878     },
880     /**
881      * Checks or unchecks the paused checkbox
882      *
883      * @method _uiUpdatePaused
884      * @param on {Boolean} the new checked state
885      * @protected
886      */
887     _uiUpdatePaused : function (on) {
888         var node = this._foot.all('input[type=checkbox].'+C_PAUSE);
890         if (node) {
891             node.set(CHECKED,on);
892         }
893     },
895     /**
896      * Calls this._trimOldEntries() in response to changes in the configured
897      * consoleLimit attribute.
898      * 
899      * @method _afterConsoleLimitChange
900      * @param e {Event} Custom event for the attribute change
901      * @protected
902      */
903     _afterConsoleLimitChange : function () {
904         this._trimOldEntries();
905     },
908     /**
909      * Updates the className of the contentBox, which should trigger CSS to
910      * hide or show the body and footer sections depending on the new value.
911      *
912      * @method _afterCollapsedChange
913      * @param e {Event} Custom event for the attribute change
914      * @protected
915      */
916     _afterCollapsedChange : function (e) {
917         this._uiUpdateCollapsed(e.newVal);
918     },
920     /**
921      * Updates the UI to reflect the new Collapsed state
922      *
923      * @method _uiUpdateCollapsed
924      * @param v {Boolean} true for collapsed, false for expanded
925      * @protected
926      */
927     _uiUpdateCollapsed : function (v) {
928         var bb     = this.get('boundingBox'),
929             button = bb.all('button.'+C_COLLAPSE),
930             method = v ? 'addClass' : 'removeClass',
931             str    = this.get('strings.'+(v ? 'expand' : 'collapse'));
933         bb[method](C_COLLAPSED);
935         if (button) {
936             button.setContent(str);
937         }
939         this._uiSetHeight(v ? this._head.get('offsetHeight'): this.get(HEIGHT));
940     },
942     /**
943      * Makes adjustments to the UI if needed when the Console is hidden or shown
944      *
945      * @method _afterVisibleChange
946      * @param e {Event} the visibleChange event
947      * @protected
948      */
949     _afterVisibleChange : function (e) {
950         Console.superclass._afterVisibleChange.apply(this,arguments);
952         this._uiUpdateFromHideShow(e.newVal);
953     },
955     /**
956      * Recalculates dimensions and updates appropriately when shown
957      *
958      * @method _uiUpdateFromHideShow
959      * @param v {Boolean} true for visible, false for hidden
960      * @protected
961      */
962     _uiUpdateFromHideShow : function (v) {
963         if (v) {
964             this._uiSetHeight(this.get(HEIGHT));
965         }
966     },
968     /**
969      * Responds to log events by normalizing qualifying messages and passing
970      * them along through the entry event for buffering etc.
971      * 
972      * @method _onLogEvent
973      * @param msg {String} the log message
974      * @param cat {String} OPTIONAL the category or logLevel of the message
975      * @param src {String} OPTIONAL the source of the message (e.g. widget name)
976      * @protected
977      */
978     _onLogEvent : function (e) {
980         if (!this.get(DISABLED) && this._isInLogLevel(e)) {
982             var debug = Y.config.debug;
984             /* TODO: needed? */
985             Y.config.debug = false;
987             this.fire(ENTRY, {
988                 message : this._normalizeMessage(e)
989             });
991             Y.config.debug = debug;
992         }
993     },
995     /**
996      * Clears the console, resets the startTime attribute, enables and
997      * unpauses the widget.
998      *
999      * @method _defResetFn
1000      * @protected
1001      */
1002     _defResetFn : function () {
1003         this.clearConsole();
1004         this.set(START_TIME,new Date());
1005         this.set(DISABLED,false);
1006         this.set(PAUSED,false);
1007     },
1009     /**
1010      * Buffers incoming message objects and schedules the printing.
1011      *
1012      * @method _defEntryFn
1013      * @param e {Event} The Custom event carrying the message in its payload
1014      * @protected
1015      */
1016     _defEntryFn : function (e) {
1017         if (e.message) {
1018             this.buffer.push(e.message);
1019             this._schedulePrint();
1020         }
1021     }
1025 // Y.Console static properties
1027     /**
1028      * The identity of the widget.
1029      *
1030      * @property NAME
1031      * @type String
1032      * @static
1033      */
1034     NAME : CONSOLE,
1036     /**
1037      * Static identifier for logLevel configuration setting to allow all
1038      * incoming messages to generate Console entries.
1039      *
1040      * @property LOG_LEVEL_INFO
1041      * @type String
1042      * @static
1043      */
1044     LOG_LEVEL_INFO  : INFO,
1046     /**
1047      * Static identifier for logLevel configuration setting to allow only
1048      * incoming messages of logLevel &quot;warn&quot; or &quot;error&quot;
1049      * to generate Console entries.
1050      *
1051      * @property LOG_LEVEL_WARN
1052      * @type String
1053      * @static
1054      */
1055     LOG_LEVEL_WARN  : WARN,
1057     /**
1058      * Static identifier for logLevel configuration setting to allow only
1059      * incoming messages of logLevel &quot;error&quot; to generate
1060      * Console entries.
1061      *
1062      * @property LOG_LEVEL_ERROR
1063      * @type String
1064      * @static
1065      */
1066     LOG_LEVEL_ERROR : ERROR,
1068     /**
1069      * Map (object) of classNames used to populate the placeholders in the
1070      * Console.ENTRY_TEMPLATE markup when rendering a new Console entry.
1071      *
1072      * <p>By default, the keys contained in the object are:</p>
1073      * <ul>
1074      *    <li>entry_class</li>
1075      *    <li>entry_meta_class</li>
1076      *    <li>entry_cat_class</li>
1077      *    <li>entry_src_class</li>
1078      *    <li>entry_time_class</li>
1079      *    <li>entry_content_class</li>
1080      * </ul>
1081      *
1082      * @property ENTRY_CLASSES
1083      * @type Object
1084      * @static
1085      */
1086     ENTRY_CLASSES   : {
1087         entry_class         : C_ENTRY,
1088         entry_meta_class    : C_ENTRY_META,
1089         entry_cat_class     : C_ENTRY_CAT,
1090         entry_src_class     : C_ENTRY_SRC,
1091         entry_time_class    : C_ENTRY_TIME,
1092         entry_content_class : C_ENTRY_CONTENT
1093     },
1095     /**
1096      * Map (object) of classNames used to populate the placeholders in the
1097      * Console.HEADER_TEMPLATE, Console.BODY_TEMPLATE, and
1098      * Console.FOOTER_TEMPLATE markup when rendering the Console UI.
1099      *
1100      * <p>By default, the keys contained in the object are:</p>
1101      * <ul>
1102      *   <li>console_hd_class</li>
1103      *   <li>console_bd_class</li>
1104      *   <li>console_ft_class</li>
1105      *   <li>console_controls_class</li>
1106      *   <li>console_checkbox_class</li>
1107      *   <li>console_pause_class</li>
1108      *   <li>console_pause_label_class</li>
1109      *   <li>console_button_class</li>
1110      *   <li>console_clear_class</li>
1111      *   <li>console_collapse_class</li>
1112      *   <li>console_title_class</li>
1113      * </ul>
1114      *
1115      * @property CHROME_CLASSES
1116      * @type Object
1117      * @static
1118      */
1119     CHROME_CLASSES  : {
1120         console_hd_class       : C_CONSOLE_HD,
1121         console_bd_class       : C_CONSOLE_BD,
1122         console_ft_class       : C_CONSOLE_FT,
1123         console_controls_class : C_CONSOLE_CONTROLS,
1124         console_checkbox_class : C_CHECKBOX,
1125         console_pause_class    : C_PAUSE,
1126         console_pause_label_class : C_PAUSE_LABEL,
1127         console_button_class   : C_BUTTON,
1128         console_clear_class    : C_CLEAR,
1129         console_collapse_class : C_COLLAPSE,
1130         console_title_class    : C_CONSOLE_TITLE
1131     },
1133     /**
1134      * Markup template used to generate the DOM structure for the header
1135      * section of the Console when it is rendered.  The template includes
1136      * these {placeholder}s:
1137      *
1138      * <ul>
1139      *   <li>console_button_class - contributed by Console.CHROME_CLASSES</li>
1140      *   <li>console_collapse_class - contributed by Console.CHROME_CLASSES</li>
1141      *   <li>console_hd_class - contributed by Console.CHROME_CLASSES</li>
1142      *   <li>console_title_class - contributed by Console.CHROME_CLASSES</li>
1143      *   <li>str_collapse - pulled from attribute strings.collapse</li>
1144      *   <li>str_title - pulled from attribute strings.title</li>
1145      * </ul>
1146      *
1147      * @property HEADER_TEMPLATE
1148      * @type String
1149      * @static
1150      */
1151     HEADER_TEMPLATE :
1152         '<div class="{console_hd_class}">'+
1153             '<h4 class="{console_title_class}">{str_title}</h4>'+
1154             '<button type="button" class="'+
1155                 '{console_button_class} {console_collapse_class}">{str_collapse}'+
1156             '</button>'+
1157         '</div>',
1159     /**
1160      * Markup template used to generate the DOM structure for the Console body
1161      * (where the messages are inserted) when it is rendered.  The template
1162      * includes only the {placeholder} &quot;console_bd_class&quot;, which is
1163      * constributed by Console.CHROME_CLASSES.
1164      *
1165      * @property BODY_TEMPLATE
1166      * @type String
1167      * @static
1168      */
1169     BODY_TEMPLATE : '<div class="{console_bd_class}"></div>',
1171     /**
1172      * Markup template used to generate the DOM structure for the footer
1173      * section of the Console when it is rendered.  The template includes
1174      * many of the {placeholder}s from Console.CHROME_CLASSES as well as:
1175      *
1176      * <ul>
1177      *   <li>id_guid - generated unique id, relates the label and checkbox</li>
1178      *   <li>str_pause - pulled from attribute strings.pause</li>
1179      *   <li>str_clear - pulled from attribute strings.clear</li>
1180      * </ul>
1181      *
1182      * @property FOOTER_TEMPLATE
1183      * @type String
1184      * @static
1185      */
1186     FOOTER_TEMPLATE :
1187         '<div class="{console_ft_class}">'+
1188             '<div class="{console_controls_class}">'+
1189                 '<label for="{id_guid}" class="{console_pause_label_class}">'+
1190                     '<input type="checkbox" class="{console_checkbox_class} '+
1191                         '{console_pause_class}" value="1" id="{id_guid}"> '+
1192                     '{str_pause}</label>' +
1193                 '<button type="button" class="'+
1194                     '{console_button_class} {console_clear_class}">{str_clear}'+
1195                 '</button>'+
1196             '</div>'+
1197         '</div>',
1199     /**
1200      * Default markup template used to create the DOM structure for Console
1201      * entries. The markup contains {placeholder}s for content and classes
1202      * that are replaced via Y.substitute.  The default template contains
1203      * the {placeholder}s identified in Console.ENTRY_CLASSES as well as the
1204      * following placeholders that will be populated by the log entry data:
1205      *
1206      * <ul>
1207      *   <li>cat_class</li>
1208      *   <li>src_class</li>
1209      *   <li>totalTime</li>
1210      *   <li>elapsedTime</li>
1211      *   <li>localTime</li>
1212      *   <li>sourceAndDetail</li>
1213      *   <li>message</li>
1214      * </ul>
1215      *
1216      * @property ENTRY_TEMPLATE
1217      * @type String
1218      * @static
1219      */
1220     ENTRY_TEMPLATE : ENTRY_TEMPLATE_STR,
1222     /**
1223      * Static property used to define the default attribute configuration of
1224      * the Widget.
1225      *
1226      * @property ATTRS
1227      * @Type Object
1228      * @static
1229      */
1230     ATTRS : {
1232         /**
1233          * Name of the custom event that will communicate log messages.
1234          *
1235          * @attribute logEvent
1236          * @type String
1237          * @default "yui:log"
1238          */
1239         logEvent : {
1240             value : 'yui:log',
1241             writeOnce : true,
1242             validator : isString
1243         },
1245         /**
1246          * Object that will emit the log events.  By default the YUI instance.
1247          * To have a single Console capture events from all YUI instances, set
1248          * this to the Y.Global object.
1249          *
1250          * @attribute logSource
1251          * @type EventTarget
1252          * @default Y
1253          */
1254         logSource : {
1255             value : Y,
1256             writeOnce : true,
1257             validator : function (v) {
1258                 return this._validateLogSource(v);
1259             }
1260         },
1262         /**
1263          * Collection of strings used to label elements in the Console UI.
1264          * Default collection contains the following name:value pairs:
1265          *
1266          * <ul>
1267          *   <li>title : &quot;Log Console&quot;</li>
1268          *   <li>pause : &quot;Pause&quot;</li>
1269          *   <li>clear : &quot;Clear&quot;</li>
1270          *   <li>collapse : &quot;Collapse&quot;</li>
1271          *   <li>expand : &quot;Expand&quot;</li>
1272          * </ul>
1273          *
1274          * @attribute strings
1275          * @type Object
1276          */
1277         strings : {
1278             valueFn: function() { return Y.Intl.get("console"); }
1279         },
1281         /**
1282          * Boolean to pause the outputting of new messages to the console.
1283          * When paused, messages will accumulate in the buffer.
1284          *
1285          * @attribute paused
1286          * @type boolean
1287          * @default false
1288          */
1289         paused : {
1290             value : false,
1291             validator : L.isBoolean
1292         },
1294         /**
1295          * If a category is not specified in the Y.log(..) statement, this
1296          * category will be used. Categories &quot;info&quot;,
1297          * &quot;warn&quot;, and &quot;error&quot; are also called log level.
1298          *
1299          * @attribute defaultCategory
1300          * @type String
1301          * @default "info"
1302          */
1303         defaultCategory : {
1304             value : INFO,
1305             validator : isString
1306         },
1308         /**
1309          * If a source is not specified in the Y.log(..) statement, this
1310          * source will be used.
1311          *
1312          * @attribute defaultSource
1313          * @type String
1314          * @default "global"
1315          */
1316         defaultSource   : {
1317             value : 'global',
1318             validator : isString
1319         },
1321         /**
1322          * Markup template used to create the DOM structure for Console entries.
1323          *
1324          * @attribute entryTemplate
1325          * @type String
1326          * @default Console.ENTRY_TEMPLATE
1327          */
1328         entryTemplate : {
1329             value : ENTRY_TEMPLATE_STR,
1330             validator : isString
1331         },
1333         /**
1334          * Minimum entry log level to render into the Console.  The initial
1335          * logLevel value for all Console instances defaults from the
1336          * Y.config.logLevel YUI configuration, or Console.LOG_LEVEL_INFO if
1337          * that configuration is not set.
1338          *
1339          * Possible values are &quot;info&quot;, &quot;warn&quot;,
1340          * &quot;error&quot; (case insensitive), or their corresponding statics
1341          * Console.LOG_LEVEL_INFO and so on.
1342          *
1343          * @attribute logLevel
1344          * @type String
1345          * @default Y.config.logLevel or Console.LOG_LEVEL_INFO
1346          */
1347         logLevel : {
1348             value : Y.config.logLevel || INFO,
1349             setter : function (v) {
1350                 return this._setLogLevel(v);
1351             }
1352         },
1354         /**
1355          * Millisecond timeout between iterations of the print loop, moving
1356          * entries from the buffer to the UI.
1357          *
1358          * @attribute printTimeout
1359          * @type Number
1360          * @default 100
1361          */
1362         printTimeout : {
1363             value : 100,
1364             validator : isNumber
1365         },
1367         /**
1368          * Maximum number of entries printed in each iteration of the print
1369          * loop. This is used to prevent excessive logging locking the page UI.
1370          *
1371          * @attribute printLimit
1372          * @type Number
1373          * @default 50
1374          */
1375         printLimit : {
1376             value : 50,
1377             validator : isNumber
1378         },
1380         /**
1381          * Maximum number of Console entries allowed in the Console body at one
1382          * time.  This is used to keep acquired messages from exploding the
1383          * DOM tree and impacting page performance.
1384          *
1385          * @attribute consoleLimit
1386          * @type Number
1387          * @default 300
1388          */
1389         consoleLimit : {
1390             value : 300,
1391             validator : isNumber
1392         },
1394         /**
1395          * New entries should display at the top of the Console or the bottom?
1396          *
1397          * @attribute newestOnTop
1398          * @type Boolean
1399          * @default true
1400          */
1401         newestOnTop : {
1402             value : true
1403         },
1405         /**
1406          * When new entries are added to the Console UI, should they be
1407          * scrolled into view?
1408          *
1409          * @attribute scrollIntoView
1410          * @type Boolean
1411          * @default true
1412          */
1413         scrollIntoView : {
1414             value : true
1415         },
1417         /**
1418          * The baseline time for this Console instance, used to measure elapsed
1419          * time from the moment the console module is <code>use</code>d to the
1420          * moment each new entry is logged (not rendered).
1421          *
1422          * This value is reset by the instance method myConsole.reset().
1423          *
1424          * @attribute startTime
1425          * @type Date
1426          * @default The moment the console module is <code>use</code>d
1427          */
1428         startTime : {
1429             value : new Date()
1430         },
1432         /**
1433          * The precise time the last entry was logged.  Used to measure elapsed
1434          * time between log messages.
1435          *
1436          * @attribute lastTime
1437          * @type Date
1438          * @default The moment the console module is <code>use</code>d
1439          */
1440         lastTime : {
1441             value : new Date(),
1442             readOnly: true
1443         },
1445         /**
1446          * Controls the collapsed state of the Console
1447          *
1448          * @attribute collapsed
1449          * @type Boolean
1450          * @default false
1451          */
1452         collapsed : {
1453             value : false
1454         },
1456         /**
1457         * String with units, or number, representing the height of the Console,
1458         * inclusive of header and footer. If a number is provided, the default
1459         * unit, defined by Widget's DEF_UNIT, property is used.
1460         *
1461         * @attribute height
1462         * @default "300px"
1463         * @type {String | Number}
1464         */
1465         height: {
1466             value: "300px"
1467         },
1469         /**
1470         * String with units, or number, representing the width of the Console.
1471         * If a number is provided, the default unit, defined by Widget's
1472         * DEF_UNIT, property is used.
1473         *
1474         * @attribute width
1475         * @default "300px"
1476         * @type {String | Number}
1477         */
1478         width: {
1479             value: "300px"
1480         },
1482         /**
1483          * Pass through to the YUI instance useBrowserConsole configuration.
1484          * By default this is set to false, which will disable logging to the
1485          * browser console when a Console instance is created.  If the
1486          * logSource is not a YUI instance, this has no effect.
1487          * 
1488          * @attribute useBrowserConsole
1489          * @type {Boolean}
1490          * @default false
1491          */
1492          useBrowserConsole : {
1493             lazyAdd: false,
1494             value: false,
1495             getter : function () {
1496                 return this._getUseBrowserConsole();
1497             },
1498             setter : function (v) {
1499                 return this._setUseBrowserConsole(v);
1500             }
1501          },
1503          /**
1504           * Allows the Console to flow in the document.  Available values are
1505           * 'inline', 'block', and 'separate' (the default).  
1506           *
1507           * @attribute style
1508           * @type {String}
1509           * @default 'separate'
1510           */
1511          style : {
1512             value : 'separate',
1513             writeOnce : true,
1514             validator : function (v) {
1515                 return this._validateStyle(v);
1516             }
1517          }
1518     }
1523 }, '3.5.1' ,{lang:['en', 'es', 'ja'], requires:['substitute','widget','yui-log']});