Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * Arranges loosely coupled interactions between components with a pipeline based on jQuery's promise implementation.
- *
- * Interactions are set up by building a chain of handling stages that are invoked in turn when the interaction is triggered.
- * Just like a promise, an interaction can be in one of three states: pending, resolved (successful) or rejected (failed).
- * Unlike promises however, once an interaction is triggered its state can change between resolved and rejected as a result
- * of each handling stage; if this happens, later stages will see the new state (possibly until it is again changed, etc).
- * All interactions are set up in the context of an object (typically this is a DOM element). Finally, just like promises
- * interactions carry a payload which is independent of their state and can also change between stages.
- *
- * To set up an interaction, call this function with the name of the interaction and one or more handler functions. This will
- * add one stage to the interaction each time and can be repeated as many times as desired. The aim of interactions is to
- * facilitate loosely coupled workflows, so typically stages are typically added in a bottom-up manner: the first stage is
- * added by the most specialized application component and higher-level components progressively build on top of that.
- *
- * When setting up the signature of this function is similar to <code>$.Deferred.pipe</code>: the first handler is invoked if
- * the interaction reaches the current stage in a resolved state, the second if the interaction is in a rejected state, and
- * the third receives progress notifications. If a handler is omitted then no processing is done for the current stage and
- * the interaction moves to the next stage in the same state as before. To create a stage with only a "rejected" handler
- * pass <code>null</code> as the value of the "resolved" handler, again just as when calling <code>$.Deferred.pipe</code>.
- *
- * All interaction handler functions have the same signature: <code>function handler(payload, isLastStage)</code>. The two
- * parameters provide the current payload and a boolean flag that lets each stage know if it is the last one in the chain;
- * the current context (<code>this</code>) is set to the object on which the interaction has been triggered; the state of the
- * interaction is not provided because it is implicit (a different handler is called for each possible state). Handlers
- * influence the interaction based on their return value:
- *
- * <ul>
- * <li>
- * returning anything that is not a promise results in the state of the interaction remaining unchanged but its payload
- * being modified (for example, a bare <code>return</code> results in the payload being changed to <code>undefined</code>,
- * while <code>return payload</code> results in both state and payload remaining unchanged
- * </li>
- * <li>
- * returning a resolved promise results in potentially both the state and the payload of the interaction being changed
- * before handling reaches the next stage (for example, returning <code>$.Deferred().reject(payload)</code> results in the
- * state being set to "rejected" while the payload remains unchanged)
- * </li>
- * <li>
- * returning an unresolved promise results in handling being "paused" until such time as the promise is fulfilled; when
- * this happens, handling will be resumed from the next stage with interaction state and payload being dictated by the
- * state and payload of the promise
- * </li>
- * </ul>
- *
- * To trigger an interaction, call this function with the name of the interaction and a parameter that determines the initial
- * state and payload of the interaction. The value of this parameter affects state and payload in the same manner as the
- * return value of handlers as documented above but with two differences:
- *
- * <ul>
- * <li>
- * if no value is provided then the result is the same as if the value were <code>undefined</code> (see next bullet)
- * </li>
- * <li>
- * a value that is not a promise is treated as a resolved promise with payload equal to the value
- * </li>
- * <li>
- * a value of <code>null</code> cannot be provided directly because this usage is ambiguous with setting up an additional
- * state without a "success" handler; if you want to trigger the interaction in a resolved state and with <code>null</code>
- * payload, pass <code>$.Deferred().resolve(null)</code> as the parameter
- * </li>
- * </ul>
- *
- * @since 1.8.0
- */
- $.fn.interaction = function(name /*, [payload | doneHandler], [failHandler], [progressHandler] */) {
- var dataKey = "pj.interactionQueue." + name,
- eventName = "event:" + name + ".interaction.pj",
- payload = arguments[1];
- if (payload !== null && !$.isFunction(payload)) {
- // Trigger mode: if it's not a function, it's something we 'll use to trigger the interaction
- if (typeof payload === "undefined") {
- // no data provided; create a new Deferred and resolve it on the spot
- payload = $.Deferred().resolve();
- }
- else if (!$.isFunction(payload.promise)) {
- // data provided but it's not a promise; create a fulfilled promise from it
- payload = $.Deferred().resolve(payload);
- }
- // .triggerHandler instead of .trigger because we don't want the event to bubble up -- this
- // would result in interactions with the same name on ancestors being spuriously fired.
- this.each(function() {
- $(this).triggerHandler(eventName, [payload]);
- });
- return this;
- }
- // Setup mode: adds handlers to the interaction
- var handlers = Array.prototype.slice.call(arguments, 1);
- // Loop over each element in turn so that queues are independent of each other
- return this.each(function() {
- var queue = $(this).data(dataKey);
- if (typeof queue === "undefined") {
- var self = this;
- $(this)
- .data(dataKey, queue = [])
- .bind(eventName, function (ev, deferred) {
- $.each(queue, function (i) {
- deferred = deferred.pipe.apply(self, $.map(this,
- function(handlerToWrap) {
- return handlerToWrap === null
- ? [null] // $.map needs special treatment if you want to map to null or undefined!
- : function(result) {return handlerToWrap.call(self, result, i == queue.length - 1);};
- }));
- })
- });
- }
- queue.push(handlers);
- });
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement