1(function (global) {
2
3    //
4    // Check for native Promise and it has correct interface
5    //
6
7    var NativePromise = global['Promise'];
8    var nativePromiseSupported =
9        NativePromise &&
10        // Some of these methods are missing from
11        // Firefox/Chrome experimental implementations
12        'resolve' in NativePromise &&
13        'reject' in NativePromise &&
14        'all' in NativePromise &&
15        'race' in NativePromise &&
16        // Older version of the spec had a resolver object
17        // as the arg rather than a function
18        (function () {
19            var resolve;
20            new NativePromise(function (r) { resolve = r; });
21            return typeof resolve === 'function';
22        })();
23
24
25    //
26    // export if necessary
27    //
28
29    if (typeof exports !== 'undefined' && exports) {
30        // node.js
31        exports.Promise = nativePromiseSupported ? NativePromise : Promise;
32        exports.Polyfill = Promise;
33    }
34    else {
35        // AMD
36        if (typeof define == 'function' && define.amd) {
37            define(function () {
38                return nativePromiseSupported ? NativePromise : Promise;
39            });
40        }
41        else {
42            // in browser add to global
43            if (!nativePromiseSupported)
44                global['Promise'] = Promise;
45        }
46    }
47
48
49    //
50    // Polyfill
51    //
52
53    var PENDING = 'pending';
54    var SEALED = 'sealed';
55    var FULFILLED = 'fulfilled';
56    var REJECTED = 'rejected';
57    var NOOP = function () { };
58
59    function isArray(value) {
60        return Object.prototype.toString.call(value) === '[object Array]';
61    }
62
63    // async calls
64    var asyncSetTimer = typeof setImmediate !== 'undefined' ? setImmediate : setTimeout;
65    var asyncQueue = [];
66    var asyncTimer;
67
68    function asyncFlush() {
69        // run promise callbacks
70        for (var i = 0; i < asyncQueue.length; i++)
71            asyncQueue[i][0](asyncQueue[i][1]);
72
73        // reset async asyncQueue
74        asyncQueue = [];
75        asyncTimer = false;
76        // print("asyncFlush");
77    }
78
79    function asyncCall(callback, arg) {
80        asyncQueue.push([callback, arg]);
81
82        if (!asyncTimer) {
83            asyncTimer = true;
84            asyncSetTimer(asyncFlush, 1);
85        }
86    }
87
88
89    function invokeResolver(resolver, promise) {
90        function resolvePromise(value) {
91            resolve(promise, value);
92        }
93
94        function rejectPromise(reason) {
95            reject(promise, reason);
96        }
97
98        try {
99            resolver(resolvePromise, rejectPromise);
100        } catch (e) {
101            rejectPromise(e);
102        }
103    }
104
105    function invokeCallback(subscriber) {
106        var owner = subscriber.owner;
107        var settled = owner.state_;
108        var value = owner.data_;
109        var callback = subscriber[settled];
110        var promise = subscriber.then;
111
112        if (typeof callback === 'function') {
113            settled = FULFILLED;
114            try {
115                value = callback(value);
116            } catch (e) {
117                reject(promise, e);
118            }
119        }
120
121        if (!handleThenable(promise, value)) {
122            if (settled === FULFILLED)
123                resolve(promise, value);
124
125            if (settled === REJECTED)
126                reject(promise, value);
127        }
128    }
129
130    function handleThenable(promise, value) {
131        var resolved;
132
133        try {
134            if (promise === value)
135                throw new TypeError('A promises callback cannot return that same promise.');
136
137            if (value && (typeof value === 'function' || typeof value === 'object')) {
138                var then = value.then;  // then should be retrived only once
139
140                if (typeof then === 'function') {
141                    then.call(value, function (val) {
142                        if (!resolved) {
143                            resolved = true;
144
145                            if (value !== val)
146                                resolve(promise, val);
147                            else
148                                fulfill(promise, val);
149                        }
150                    }, function (reason) {
151                        if (!resolved) {
152                            resolved = true;
153
154                            reject(promise, reason);
155                        }
156                    });
157
158                    return true;
159                }
160            }
161        } catch (e) {
162            if (!resolved)
163                reject(promise, e);
164
165            return true;
166        }
167
168        return false;
169    }
170
171    function resolve(promise, value) {
172        if (promise === value || !handleThenable(promise, value))
173            fulfill(promise, value);
174    }
175
176    function fulfill(promise, value) {
177        if (promise.state_ === PENDING) {
178            promise.state_ = SEALED;
179            promise.data_ = value;
180
181            asyncCall(publishFulfillment, promise);
182        }
183    }
184
185    function reject(promise, reason) {
186        if (promise.state_ === PENDING) {
187            promise.state_ = SEALED;
188            promise.data_ = reason;
189
190            asyncCall(publishRejection, promise);
191        }
192    }
193
194    function publish(promise) {
195        var callbacks = promise.then_;
196        promise.then_ = undefined;
197
198        for (var i = 0; i < callbacks.length; i++) {
199            invokeCallback(callbacks[i]);
200        }
201    }
202
203    function publishFulfillment(promise) {
204        promise.state_ = FULFILLED;
205        publish(promise);
206    }
207
208    function publishRejection(promise) {
209        promise.state_ = REJECTED;
210        publish(promise);
211    }
212
213    /**
214    * @class
215    */
216    function Promise(resolver) {
217        if (typeof resolver !== 'function')
218            throw new TypeError('Promise constructor takes a function argument');
219
220        if (this instanceof Promise === false)
221            throw new TypeError('Failed to construct \'Promise\': Please use the \'new\' operator, this object constructor cannot be called as a function.');
222
223        this.then_ = [];
224
225        invokeResolver(resolver, this);
226    }
227
228    Promise.prototype = {
229        constructor: Promise,
230
231        state_: PENDING,
232        then_: null,
233        data_: undefined,
234
235        then: function (onFulfillment, onRejection) {
236            var subscriber = {
237                owner: this,
238                then: new this.constructor(NOOP),
239                fulfilled: onFulfillment,
240                rejected: onRejection
241            };
242
243            if (this.state_ === FULFILLED || this.state_ === REJECTED) {
244                // already resolved, call callback async
245                asyncCall(invokeCallback, subscriber);
246            }
247            else {
248                // subscribe
249                this.then_.push(subscriber);
250            }
251
252            return subscriber.then;
253        },
254
255        'catch': function (onRejection) {
256            return this.then(null, onRejection);
257        }
258    };
259
260    Promise.all = function (promises) {
261        var Class = this;
262
263        if (!isArray(promises))
264            throw new TypeError('You must pass an array to Promise.all().');
265
266        return new Class(function (resolve, reject) {
267            var results = [];
268            var remaining = 0;
269
270            function resolver(index) {
271                remaining++;
272                return function (value) {
273                    results[index] = value;
274                    if (!--remaining)
275                        resolve(results);
276                };
277            }
278
279            for (var i = 0, promise; i < promises.length; i++) {
280                promise = promises[i];
281
282                if (promise && typeof promise.then === 'function')
283                    promise.then(resolver(i), reject);
284                else
285                    results[i] = promise;
286            }
287
288            if (!remaining)
289                resolve(results);
290        });
291    };
292
293    Promise.race = function (promises) {
294        var Class = this;
295
296        if (!isArray(promises))
297            throw new TypeError('You must pass an array to Promise.race().');
298
299        return new Class(function (resolve, reject) {
300            for (var i = 0, promise; i < promises.length; i++) {
301                promise = promises[i];
302
303                if (promise && typeof promise.then === 'function')
304                    promise.then(resolve, reject);
305                else
306                    resolve(promise);
307            }
308        });
309    };
310
311    Promise.resolve = function (value) {
312        var Class = this;
313
314        if (value && typeof value === 'object' && value.constructor === Class)
315            return value;
316
317        return new Class(function (resolve) {
318            resolve(value);
319        });
320    };
321
322    Promise.reject = function (reason) {
323        var Class = this;
324
325        return new Class(function (resolve, reject) {
326            reject(reason);
327        });
328    };
329
330})(typeof window != 'undefined' ? window : typeof global != 'undefined' ? global : typeof self != 'undefined' ? self : this);