View difference between Paste ID: Q29TRSfH and MsknaFGZ
SHOW: | | - or go back to the newest paste.
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 = "inode.interactionQueue." + name,
67+
    var dataKey = "pj.interactionQueue." + name,
68-
        eventName = "event:" + name + ".interaction.inode",
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
};