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