Y.later
for the print
* loop interval.
*
* @property _printLoop
* @type Object
* @default null
* @protected
*/
_printLoop : null,
/**
* Array of normalized message objects awaiting printing.
*
* @property buffer
* @type Array
* @default null
* @protected
*/
buffer : null,
/**
* Wrapper for Y.log
.
*
* @method log
* @param arg* {MIXED} (all arguments passed through to Y.log
)
* @chainable
*/
log : function () {
Y.log.apply(Y,arguments);
return this;
},
/**
* Clear the console of messages and flush the buffer of pending messages.
*
* @method clearConsole
* @chainable
*/
clearConsole : function () {
// TODO: clear event listeners from console contents
this._body.empty();
this._cancelPrintLoop();
this.buffer = [];
return this;
},
/**
* Clears the console and resets internal timers.
*
* @method reset
* @chainable
*/
reset : function () {
this.fire(RESET);
return this;
},
/**
* Collapses the body and footer.
*
* @method collapse
* @chainable
*/
collapse : function () {
this.set(COLLAPSED, true);
return this;
},
/**
* Expands the body and footer if collapsed.
*
* @method expand
* @chainable
*/
expand : function () {
this.set(COLLAPSED, false);
return this;
},
/**
* Outputs buffered messages to the console UI. This is typically called
* from a scheduled interval until the buffer is empty (referred to as the
* print loop). The number of buffered messages output to the Console is
* limited to the number provided as an argument. If no limit is passed,
* all buffered messages are rendered.
*
* @method printBuffer
* @param limit {Number} (optional) max number of buffered entries to write
* @chainable
*/
printBuffer: function (limit) {
var messages = this.buffer,
debug = Y.config.debug,
entries = [],
consoleLimit= this.get('consoleLimit'),
newestOnTop = this.get('newestOnTop'),
anchor = newestOnTop ? this._body.get('firstChild') : null,
i;
if (messages.length > consoleLimit) {
messages.splice(0, messages.length - consoleLimit);
}
limit = Math.min(messages.length, (limit || messages.length));
// turn off logging system
Y.config.debug = false;
if (!this.get(PAUSED) && this.get('rendered')) {
for (i = 0; i < limit && messages.length; ++i) {
entries[i] = this._createEntryHTML(messages.shift());
}
if (!messages.length) {
this._cancelPrintLoop();
}
if (entries.length) {
if (newestOnTop) {
entries.reverse();
}
this._body.insertBefore(create(entries.join('')), anchor);
if (this.get('scrollIntoView')) {
this.scrollToLatest();
}
this._trimOldEntries();
}
}
// restore logging system
Y.config.debug = debug;
return this;
},
/**
* Constructor code. Set up the buffer and entry template, publish
* internal events, and subscribe to the configured logEvent.
*
* @method initializer
* @protected
*/
initializer : function () {
this._evtCat = Y.stamp(this) + '|';
this.buffer = [];
this.get('logSource').on(this._evtCat +
this.get('logEvent'),Y.bind("_onLogEvent",this));
/**
* Transfers a received message to the print loop buffer. Default
* behavior defined in _defEntryFn.
*
* @event entry
* @param event {EventFacade} An Event Facade object with the following attribute specific properties added:
* this.clearConsole()
.
*
* @method _onClearClick
* @param e {Event} DOM event facade for the click event
* @protected
*/
_onClearClick : function (e) {
this.clearConsole();
},
/**
* Event handler for clicking on the Collapse/Expand button. Sets the
* "collapsed" attribute accordingly.
*
* @method _onCollapseClick
* @param e {Event} DOM event facade for the click event
* @protected
*/
_onCollapseClick : function (e) {
this.set(COLLAPSED, !this.get(COLLAPSED));
},
/**
* Validator for logSource attribute.
*
* @method _validateLogSource
* @param v {Object} the desired logSource
* @return {Boolean} true if the input is an object with an on
* method
* @protected
*/
_validateLogSource: function (v) {
return v && Y.Lang.isFunction(v.on);
},
/**
* Setter method for logLevel attribute. Acceptable values are
* "error", "warn", and "info" (case
* insensitive). Other values are treated as "info".
*
* @method _setLogLevel
* @param v {String} the desired log level
* @return String One of Console.LOG_LEVEL_INFO, _WARN, or _ERROR
* @protected
*/
_setLogLevel : function (v) {
if (isString(v)) {
v = v.toLowerCase();
}
return (v === WARN || v === ERROR) ? v : INFO;
},
/**
* Getter method for useBrowserConsole attribute. Just a pass through to
* the YUI instance configuration setting.
*
* @method _getUseBrowserConsole
* @return {Boolean} or null if logSource is not a YUI instance
* @protected
*/
_getUseBrowserConsole: function () {
var logSource = this.get('logSource');
return logSource instanceof YUI ?
logSource.config.useBrowserConsole : null;
},
/**
* Setter method for useBrowserConsole attributes. Only functional if the
* logSource attribute points to a YUI instance. Passes the value down to
* the YUI instance. NOTE: multiple Console instances cannot maintain
* independent useBrowserConsole values, since it is just a pass through to
* the YUI instance configuration.
*
* @method _setUseBrowserConsole
* @param v {Boolean} false to disable browser console printing (default)
* @return {Boolean} true|false if logSource is a YUI instance
* @protected
*/
_setUseBrowserConsole: function (v) {
var logSource = this.get('logSource');
if (logSource instanceof YUI) {
v = !!v;
logSource.config.useBrowserConsole = v;
return v;
} else {
return Y.Attribute.INVALID_VALUE;
}
},
/**
* 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.
* Overrides Widget.prototype._uiSetHeight.
*
* @method _uiSetHeight
* @param v {String|Number} the new height
* @protected
*/
_uiSetHeight : function (v) {
Console.superclass._uiSetHeight.apply(this,arguments);
if (this._head && this._foot) {
var h = this.get('boundingBox').get('offsetHeight') -
this._head.get('offsetHeight') -
this._foot.get('offsetHeight');
this._body.setStyle(HEIGHT,h+'px');
}
},
/**
* Over-ride default content box sizing to do nothing, since we're sizing
* the body section to fill out height ourselves.
*
* @method _uiSizeCB
* @protected
*/
_uiSizeCB : function() {
// Do Nothing. Ideally want to move to Widget-StdMod, which accounts for
// _uiSizeCB
},
/**
* Updates the UI if changes are made to any of the strings in the strings
* attribute.
*
* @method _afterStringsChange
* @param e {Event} Custom event for the attribute change
* @protected
*/
_afterStringsChange : function (e) {
var prop = e.subAttrName ? e.subAttrName.split(DOT)[1] : null,
cb = this.get(CONTENT_BOX),
before = e.prevVal,
after = e.newVal;
if ((!prop || prop === TITLE) && before.title !== after.title) {
cb.all(DOT+C_CONSOLE_TITLE).setHTML(after.title);
}
if ((!prop || prop === PAUSE) && before.pause !== after.pause) {
cb.all(DOT+C_PAUSE_LABEL).setHTML(after.pause);
}
if ((!prop || prop === CLEAR) && before.clear !== after.clear) {
cb.all(DOT+C_CLEAR).set('value',after.clear);
}
},
/**
* Updates the UI and schedules or cancels the print loop.
*
* @method _afterPausedChange
* @param e {Event} Custom event for the attribute change
* @protected
*/
_afterPausedChange : function (e) {
var paused = e.newVal;
if (e.src !== Y.Widget.SRC_UI) {
this._uiUpdatePaused(paused);
}
if (!paused) {
this._schedulePrint();
} else if (this._printLoop) {
this._cancelPrintLoop();
}
},
/**
* Checks or unchecks the paused checkbox
*
* @method _uiUpdatePaused
* @param on {Boolean} the new checked state
* @protected
*/
_uiUpdatePaused : function (on) {
var node = this._foot.all('input[type=checkbox].'+C_PAUSE);
if (node) {
node.set(CHECKED,on);
}
},
/**
* Calls this._trimOldEntries() in response to changes in the configured
* consoleLimit attribute.
*
* @method _afterConsoleLimitChange
* @param e {Event} Custom event for the attribute change
* @protected
*/
_afterConsoleLimitChange : function () {
this._trimOldEntries();
},
/**
* Updates the className of the contentBox, which should trigger CSS to
* hide or show the body and footer sections depending on the new value.
*
* @method _afterCollapsedChange
* @param e {Event} Custom event for the attribute change
* @protected
*/
_afterCollapsedChange : function (e) {
this._uiUpdateCollapsed(e.newVal);
},
/**
* Updates the UI to reflect the new Collapsed state
*
* @method _uiUpdateCollapsed
* @param v {Boolean} true for collapsed, false for expanded
* @protected
*/
_uiUpdateCollapsed : function (v) {
var bb = this.get('boundingBox'),
button = bb.all('button.'+C_COLLAPSE),
method = v ? 'addClass' : 'removeClass',
str = this.get('strings.'+(v ? 'expand' : 'collapse'));
bb[method](C_COLLAPSED);
if (button) {
button.setHTML(str);
}
this._uiSetHeight(v ? this._head.get('offsetHeight'): this.get(HEIGHT));
},
/**
* Makes adjustments to the UI if needed when the Console is hidden or shown
*
* @method _afterVisibleChange
* @param e {Event} the visibleChange event
* @protected
*/
_afterVisibleChange : function (e) {
Console.superclass._afterVisibleChange.apply(this,arguments);
this._uiUpdateFromHideShow(e.newVal);
},
/**
* Recalculates dimensions and updates appropriately when shown
*
* @method _uiUpdateFromHideShow
* @param v {Boolean} true for visible, false for hidden
* @protected
*/
_uiUpdateFromHideShow : function (v) {
if (v) {
this._uiSetHeight(this.get(HEIGHT));
}
},
/**
* Responds to log events by normalizing qualifying messages and passing
* them along through the entry event for buffering etc.
*
* @method _onLogEvent
* @param msg {String} the log message
* @param cat {String} OPTIONAL the category or logLevel of the message
* @param src {String} OPTIONAL the source of the message (e.g. widget name)
* @protected
*/
_onLogEvent : function (e) {
if (!this.get(DISABLED) && this._isInLogLevel(e)) {
var debug = Y.config.debug;
/* TODO: needed? */
Y.config.debug = false;
this.fire(ENTRY, {
message : this._normalizeMessage(e)
});
Y.config.debug = debug;
}
},
/**
* Clears the console, resets the startTime attribute, enables and
* unpauses the widget.
*
* @method _defResetFn
* @protected
*/
_defResetFn : function () {
this.clearConsole();
this.set(START_TIME,new Date());
this.set(DISABLED,false);
this.set(PAUSED,false);
},
/**
* Buffers incoming message objects and schedules the printing.
*
* @method _defEntryFn
* @param e {Event} The Custom event carrying the message in its payload
* @protected
*/
_defEntryFn : function (e) {
if (e.message) {
this.buffer.push(e.message);
this._schedulePrint();
}
}
},
// Y.Console static properties
{
/**
* The identity of the widget.
*
* @property NAME
* @type String
* @static
*/
NAME : CONSOLE,
/**
* Static identifier for logLevel configuration setting to allow all
* incoming messages to generate Console entries.
*
* @property LOG_LEVEL_INFO
* @type String
* @static
*/
LOG_LEVEL_INFO : INFO,
/**
* Static identifier for logLevel configuration setting to allow only
* incoming messages of logLevel "warn" or "error"
* to generate Console entries.
*
* @property LOG_LEVEL_WARN
* @type String
* @static
*/
LOG_LEVEL_WARN : WARN,
/**
* Static identifier for logLevel configuration setting to allow only
* incoming messages of logLevel "error" to generate
* Console entries.
*
* @property LOG_LEVEL_ERROR
* @type String
* @static
*/
LOG_LEVEL_ERROR : ERROR,
/**
* Map (object) of classNames used to populate the placeholders in the
* Console.ENTRY_TEMPLATE markup when rendering a new Console entry.
*
* By default, the keys contained in the object are:
*By default, the keys contained in the object are:
*use
d to the
* moment each new entry is logged (not rendered).
*
* This value is reset by the instance method myConsole.reset().
*
* @attribute startTime
* @type Date
* @default The moment the console module is use
d
*/
startTime : {
value : new Date()
},
/**
* The precise time the last entry was logged. Used to measure elapsed
* time between log messages.
*
* @attribute lastTime
* @type Date
* @default The moment the console module is use
d
*/
lastTime : {
value : new Date(),
readOnly: true
},
/**
* Controls the collapsed state of the Console
*
* @attribute collapsed
* @type Boolean
* @default false
*/
collapsed : {
value : false
},
/**
* String with units, or number, representing the height of the Console,
* inclusive of header and footer. If a number is provided, the default
* unit, defined by Widget's DEF_UNIT, property is used.
*
* @attribute height
* @default "300px"
* @type {String | Number}
*/
height: {
value: "300px"
},
/**
* String with units, or number, representing the width of the Console.
* If a number is provided, the default unit, defined by Widget's
* DEF_UNIT, property is used.
*
* @attribute width
* @default "300px"
* @type {String | Number}
*/
width: {
value: "300px"
},
/**
* Pass through to the YUI instance useBrowserConsole configuration.
* By default this is set to false, which will disable logging to the
* browser console when a Console instance is created. If the
* logSource is not a YUI instance, this has no effect.
*
* @attribute useBrowserConsole
* @type {Boolean}
* @default false
*/
useBrowserConsole : {
lazyAdd: false,
value: false,
getter : function () {
return this._getUseBrowserConsole();
},
setter : function (v) {
return this._setUseBrowserConsole(v);
}
},
/**
* Allows the Console to flow in the document. Available values are
* 'inline', 'block', and 'separate' (the default).
*
* @attribute style
* @type {String}
* @default 'separate'
*/
style : {
value : 'separate',
writeOnce : true,
validator : function (v) {
return this._validateStyle(v);
}
}
}
});
}, '3.17.2', {"requires": ["yui-log", "widget"], "skinnable": true, "lang": ["en", "es", "hu", "it", "ja"]});