Advertisement
Guest User

Untitled

a guest
Oct 3rd, 2012
14
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2. * Arranges loosely coupled interactions between components with a pipeline based on jQuery's promise implementation.
  3. *
  4. * Interactions are set up by building a chain of handling stages that are invoked in turn when the interaction is triggered.
  5. * Just like a promise, an interaction can be in one of three states: pending, resolved (successful) or rejected (failed).
  6. * Unlike promises however, once an interaction is triggered its state can change between resolved and rejected as a result
  7. * of each handling stage; if this happens, later stages will see the new state (possibly until it is again changed, etc).
  8. * All interactions are set up in the context of an object (typically this is a DOM element). Finally, just like promises
  9. * interactions carry a payload which is independent of their state and can also change between stages.
  10. *
  11. * To set up an interaction, call this function with the name of the interaction and one or more handler functions. This will
  12. * add one stage to the interaction each time and can be repeated as many times as desired. The aim of interactions is to
  13. * facilitate loosely coupled workflows, so typically stages are typically added in a bottom-up manner: the first stage is
  14. * added by the most specialized application component and higher-level components progressively build on top of that.
  15. *
  16. * When setting up the signature of this function is similar to <code>$.Deferred.pipe</code>: the first handler is invoked if
  17. * the interaction reaches the current stage in a resolved state, the second if the interaction is in a rejected state, and
  18. * the third receives progress notifications. If a handler is omitted then no processing is done for the current stage and
  19. * the interaction moves to the next stage in the same state as before. To create a stage with only a "rejected" handler
  20. * pass <code>null</code> as the value of the "resolved" handler, again just as when calling <code>$.Deferred.pipe</code>.
  21. *
  22. * All interaction handler functions have the same signature: <code>function handler(payload, isLastStage)</code>. The two
  23. * parameters provide the current payload and a boolean flag that lets each stage know if it is the last one in the chain;
  24. * the current context (<code>this</code>) is set to the object on which the interaction has been triggered; the state of the
  25. * interaction is not provided because it is implicit (a different handler is called for each possible state). Handlers
  26. * influence the interaction based on their return value:
  27. *
  28. * <ul>
  29. *   <li>
  30. *   returning anything that is not a promise results in the state of the interaction remaining unchanged but its payload
  31. *   being modified (for example, a bare <code>return</code> results in the payload being changed to <code>undefined</code>,
  32. *   while <code>return payload</code> results in both state and payload remaining unchanged
  33. *   </li>
  34. *   <li>
  35. *   returning a resolved promise results in potentially both the state and the payload of the interaction being changed
  36. *   before handling reaches the next stage (for example, returning <code>$.Deferred().reject(payload)</code> results in the
  37. *   state being set to "rejected" while the payload remains unchanged)
  38. *   </li>
  39. *   <li>
  40. *   returning an unresolved promise results in handling being "paused" until such time as the promise is fulfilled; when
  41. *   this happens, handling will be resumed from the next stage with interaction state and payload being dictated by the
  42. *   state and payload of the promise
  43. *   </li>
  44. * </ul>
  45. *
  46. * To trigger an interaction, call this function with the name of the interaction and a parameter that determines the initial
  47. * state and payload of the interaction. The value of this parameter affects state and payload in the same manner as the
  48. * return value of handlers as documented above but with two differences:
  49. *
  50. * <ul>
  51. *   <li>
  52. *   if no value is provided then the result is the same as if the value were <code>undefined</code> (see next bullet)
  53. *   </li>
  54. *   <li>
  55. *   a value that is not a promise is treated as a resolved promise with payload equal to the value
  56. *   </li>
  57. *   <li>
  58. *   a value of <code>null</code> cannot be provided directly because this usage is ambiguous with setting up an additional
  59. *   state without a "success" handler; if you want to trigger the interaction in a resolved state and with <code>null</code>
  60. *   payload, pass <code>$.Deferred().resolve(null)</code> as the parameter
  61. *   </li>
  62. * </ul>
  63. *
  64. * @since 1.8.0
  65. */
  66. $.fn.interaction = function(name /*, [payload | doneHandler], [failHandler], [progressHandler] */) {
  67.     var dataKey = "pj.interactionQueue." + name,
  68.         eventName = "event:" + name + ".interaction.pj",
  69.         payload = arguments[1];
  70.  
  71.     if (payload !== null && !$.isFunction(payload)) {
  72.         // Trigger mode: if it's not a function, it's something we 'll use to trigger the interaction
  73.         if (typeof payload === "undefined") {
  74.             // no data provided; create a new Deferred and resolve it on the spot
  75.             payload = $.Deferred().resolve();
  76.         }
  77.         else if (!$.isFunction(payload.promise)) {
  78.             // data provided but it's not a promise; create a fulfilled promise from it
  79.             payload = $.Deferred().resolve(payload);
  80.         }
  81.         // .triggerHandler instead of .trigger because we don't want the event to bubble up -- this
  82.         // would result in interactions with the same name on ancestors being spuriously fired.
  83.         this.each(function() {
  84.             $(this).triggerHandler(eventName, [payload]);
  85.         });
  86.         return this;
  87.     }
  88.  
  89.     // Setup mode: adds handlers to the interaction
  90.     var handlers = Array.prototype.slice.call(arguments, 1);
  91.  
  92.     // Loop over each element in turn so that queues are independent of each other
  93.     return this.each(function() {
  94.         var queue = $(this).data(dataKey);
  95.         if (typeof queue === "undefined") {
  96.             var self = this;
  97.             $(this)
  98.             .data(dataKey, queue = [])
  99.             .bind(eventName, function (ev, deferred) {
  100.                 $.each(queue, function (i) {
  101.                     deferred = deferred.pipe.apply(self, $.map(this,
  102.                     function(handlerToWrap) {
  103.                         return handlerToWrap === null
  104.                             ? [null] // $.map needs special treatment if you want to map to null or undefined!
  105.                             : function(result) {return handlerToWrap.call(self, result, i == queue.length - 1);};
  106.                     }));
  107.                 })
  108.             });
  109.         }
  110.         queue.push(handlers);
  111.     });
  112. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement