Skip to content

Instantly share code, notes, and snippets.

@bwg
Created January 24, 2011 16:02
Show Gist options
  • Save bwg/793426 to your computer and use it in GitHub Desktop.
Save bwg/793426 to your computer and use it in GitHub Desktop.
plugin for widgetparent that uses an async-queue to render children
YUI.add('sm-widget-parent-renderqueue', function(Y) {
/**
* Plugin for WidgetParent that uses an async-queue to render children
*
* @module widget-parent-render-queue
*/
var NAME = 'renderqueue',
HOST = 'host',
BOUNDING_BOX = 'boundingBox',
PARENT = 'parent',
RENDERED = 'rendered',
TIMEOUT = 'timeout',
WIDGET_RENDER_FN = Y.Widget.prototype._defRenderFn;
/**
* Plugin for WidgetParent providing async-queue functionality for
* rendering child widgets
*
* @class WidgetParentRenderQueue
* @constructor
* @extends Plugin.Base
* @param config {Object} User configuration object
*/
function RenderQueue(config) {
RenderQueue.superclass.constructor.apply(this, arguments);
}
/**
* Static property provides a string to identify the class.
*
* @property WidgetParentRenderQueue.NAME
* @type String
* @static
*/
RenderQueue.NAME = NAME;
/**
* Static property provides a string to identify the namespace.
*
* @property WidgetParentRenderQueue.NS
* @type String
* @static
*/
RenderQueue.NS = NAME;
/**
* Static property used to define the default attribute
* configuration for the WidgetParentRenderQueue.
*
* @property WidgetParentRenderQueue.ATTRS
* @type Object
* @static
*/
RenderQueue.ATTRS = {
/**
* Queue execution timer
*
* @attribute timeout
* @default Y.AsyncQueue.defaults.timeout (currently 10ms)
*/
timeout: {
value: Y.AsyncQueue.defaults.timeout
}
};
Y.extend(RenderQueue, Y.Plugin.Base, {
/**
* the async queue instance
*
* @property _queue
* @private
*/
_queue: new Y.AsyncQueue(),
/**
* Initializer lifecycle method for the plugin.
*
* @method initializer
* @protected
*/
initializer: function(cfg) {
var host = this.get(HOST),
q = this._queue;
// set the defaults for the queue
q.defaults.context = host;
q.defaults.timeout = this.get(TIMEOUT);
// add the plugin as a bubble target for queue events
q.addTarget(this);
this.on(TIMEOUT + 'Change', function(e) {
q.defaults.timeout = e.newVal;
});
//this.beforeHostMethod('_uiAddChild', this._uiAddChild, host);
this.onHostEvent('addChild', this._onAddChild);
},
/**
* Destructor lifecycle implementation for the plugin. Resets all
* child widgets to their default state
*
* @method destructor
* @protected
*/
destructor: function() {
var host = this.get(HOST),
q = this._queue;
// HostMethod/Event listeners are automatically
// detached by plugin base destructor
// return the default renderFn for children
// back to its...um...default
host.each(function(child) {
child._defRenderFn = WIDGET_RENDER_FN;
});
//clean up all events on the queue
q.detachAll();
},
/**
* This is the same _uiAddChild method copied from Widget
* YUI v3.3.0
*
* Modifications:
* Added check for nextSibling.get(RENDERED) and
* prevSibling.get(RENDERED) because with queued rendering
* a sibling may exist that is not yet rendered into the DOM.
* Return Y.Do.Prevent to prevent original method from executing
*
* Note: I will be filing a bug about this, as assuming the
* sibling is rendered is kinda reckless anyways. When that
* behavior changes in YUI core, this method can be removed
*
* @method _uiAddChild
* @protected
* @param child {Widget} The child Widget instance to render.
* @param parentNode {Object} The Node under which the
* child Widget is to be rendered.
*/
_uiAddChild: function(child, parentNode) {
child.render(parentNode);
// TODO: Ideally this should be in Child's render UI.
var childBB = child.get(BOUNDING_BOX),
siblingBB,
nextSibling = child.next(false),
prevSibling;
// Insert or Append to last child.
// Avoiding index, and using the current sibling
// state (which should be accurate), means we don't have
// to worry about decorator elements which may be added
// to the _childContainer node.
if (nextSibling && nextSibling.get(RENDERED)) {
siblingBB = nextSibling.get(BOUNDING_BOX);
siblingBB.insert(childBB, "before");
} else {
prevSibling = child.previous(false);
if (prevSibling && prevSibling.get(RENDERED)) {
siblingBB = prevSibling.get(BOUNDING_BOX);
siblingBB.insert(childBB, "after");
}
}
return new Y.Do.Prevent();
},
/**
* Resets the childs render event defaultFn
*
* @method _onAddChild
* @see _onChildRender
* @private
* @param event {EventFacade} The Event object
*/
_onAddChild: function(event) {
var child = event.child;
/**
* Reassign the _defRenderFn to the plugin renderFn
* Because a plugin could be added at any point in
* an objects lifecycle, doing a reassignment instead
* of using an event listener or AOP is the most reliable.
* We dont have to worry about any other listeners that
* may have been already added or where the new listener
* would be in the event bubble chain. We also wont be
* creating a new event for every child
*/
child._defRenderFn = this._childRender;
},
/**
* This is the new default handler for child render events
* It adds the old default child renderFn (_defRenderFn)
* to the plugin render queue.
*
* @method _childRender
* @private
* @param event {EventFacade} The Event object
*/
_childRender: function(event) {
var child = event.target,
q = child.get(PARENT)[NAME]._queue,
// get the defaultFn so we can add it to the queue
fn = WIDGET_RENDER_FN,
// arguments we want to pass on to the default fn
args = event.details;
// add the default fn to our render queue
q.add({
fn: fn,
args: args,
context: child
});
q.run();
}
});
Y.namespace('SM.Plugin').WidgetParentRenderQueue = RenderQueue;
}, '110122183848' ,{'requires': ['plugin', 'async-queue']});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment