Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / third_party / tvcm / third_party / Promises / polyfill / src / Promise.js
1 // Copyright (C) 2013:
2 //    Alex Russell <slightlyoff@chromium.org>
3 //    Yehuda Katz
4 //
5 // Use of this source code is governed by
6 //    http://www.apache.org/licenses/LICENSE-2.0
7
8 // FIXME(slightlyoff):
9 //    - Document "npm test"
10 //    - Change global name from "Promise" to something less conflicty
11 (function(global, browserGlobal, underTest) {
12 "use strict";
13
14 // FIXME(slighltyoff):
15 //  * aggregates + tests
16 //  * check on fast-forwarding
17
18 underTest = !!underTest;
19
20 //
21 // Async Utilities
22 //
23
24 // Borrowed from RSVP.js
25 var async;
26
27 var MutationObserver = browserGlobal.MutationObserver ||
28                        browserGlobal.WebKitMutationObserver;
29 var Promise;
30
31 if (typeof process !== 'undefined' &&
32   {}.toString.call(process) === '[object process]') {
33   async = function(callback, binding) {
34     process.nextTick(function() {
35       callback.call(binding);
36     });
37   };
38 } else if (MutationObserver) {
39   var queue = [];
40
41   var observer = new MutationObserver(function() {
42     var toProcess = queue.slice();
43     queue = [];
44     toProcess.forEach(function(tuple) {
45       tuple[0].call(tuple[1]);
46     });
47   });
48
49   var element = document.createElement('div');
50   observer.observe(element, { attributes: true });
51
52   // Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
53   window.addEventListener('unload', function(){
54     observer.disconnect();
55     observer = null;
56   });
57
58   async = function(callback, binding) {
59     queue.push([callback, binding]);
60     element.setAttribute('drainQueue', 'drainQueue');
61   };
62 } else {
63   async = function(callback, binding) {
64     setTimeout(function() {
65       callback.call(binding);
66     }, 1);
67   };
68 }
69
70 //
71 // Object Model Utilities
72 //
73
74 // defineProperties utilities
75 var _readOnlyProperty = function(v) {
76     return {
77       enumerable: true,
78       configurable: false,
79       get: v
80     };
81 };
82
83 var _method = function(v, e, c, w) {
84     return {
85       enumerable:   !!(e || 0),
86       configurable: !!(c || 1),
87       writable:     !!(w || 1),
88       value:           v || function() {}
89     };
90 };
91
92 var _pseudoPrivate = function(v) { return _method(v, 0, 1, 0); };
93 var _public = function(v) { return _method(v, 1); };
94
95 //
96 // Promises Utilities
97 //
98
99 var isThenable = function(any) {
100   if (any === undefined)
101     return false;
102   try {
103     var f = any.then;
104     if (typeof f == "function") {
105       return true;
106     }
107   } catch (e) { /*squelch*/ }
108   return false;
109 };
110
111 var AlreadyResolved = function(name) {
112   Error.call(this, name);
113 };
114 AlreadyResolved.prototype = Object.create(Error.prototype);
115
116 var Backlog = function() {
117   var bl = [];
118   bl.pump = function(value) {
119     async(function() {
120       var l = bl.length;
121       var x = 0;
122       while(x < l) {
123         x++;
124         bl.shift()(value);
125       }
126     });
127   };
128   return bl;
129 };
130
131 //
132 // Resolver Constuctor
133 //
134
135 var Resolver = function(future,
136                         fulfillCallbacks,
137                         rejectCallbacks,
138                         setValue,
139                         setError,
140                         setState) {
141   var isResolved = false;
142
143   var resolver = this;
144   var fulfill = function(value) {
145     // console.log("queueing fulfill with:", value);
146     async(function() {
147       setState("fulfilled");
148       setValue(value);
149       // console.log("fulfilling with:", value);
150       fulfillCallbacks.pump(value);
151     });
152   };
153   var reject = function(reason) {
154     // console.log("queuing reject with:", reason);
155     async(function() {
156       setState("rejected");
157       setError(reason);
158       // console.log("rejecting with:", reason);
159       rejectCallbacks.pump(reason);
160     });
161   };
162   var resolve = function(value) {
163     if (isThenable(value)) {
164       value.then(resolve, reject);
165       return;
166     }
167     fulfill(value);
168   };
169   var ifNotResolved = function(func, name) {
170     return function(value) {
171       if (!isResolved) {
172         isResolved = true;
173         func(value);
174       } else {
175         if (typeof console != "undefined") {
176           console.error("Cannot resolve a Promise multiple times.");
177         }
178       }
179     };
180   };
181
182   // Indirectly resolves the Promise, chaining any passed Promise's resolution
183   this.resolve = ifNotResolved(resolve, "resolve");
184
185   // Directly fulfills the future, no matter what value's type is
186   this.fulfill = ifNotResolved(fulfill, "fulfill");
187
188   // Rejects the future
189   this.reject = ifNotResolved(reject, "reject");
190
191   this.cancel  = function() { resolver.reject(new Error("Cancel")); };
192   this.timeout = function() { resolver.reject(new Error("Timeout")); };
193
194   if (underTest) {
195     Object.defineProperties(this, {
196       _isResolved: _readOnlyProperty(function() { return isResolved; }),
197     });
198   }
199
200   setState("pending");
201 };
202
203 //
204 // Promise Constuctor
205 //
206
207 var Promise = function(init) {
208   var fulfillCallbacks = new Backlog();
209   var rejectCallbacks = new Backlog();
210   var value;
211   var error;
212   var state = "pending";
213
214   if (underTest) {
215     Object.defineProperties(this, {
216       _value: _readOnlyProperty(function() { return value; }),
217       _error: _readOnlyProperty(function() { return error; }),
218       _state: _readOnlyProperty(function() { return state; }),
219     });
220   }
221
222   Object.defineProperties(this, {
223     _addAcceptCallback: _pseudoPrivate(
224       function(cb) {
225         // console.log("adding fulfill callback:", cb);
226         fulfillCallbacks.push(cb);
227         if (state == "fulfilled") {
228           fulfillCallbacks.pump(value);
229         }
230       }
231     ),
232     _addRejectCallback: _pseudoPrivate(
233       function(cb) {
234         // console.log("adding reject callback:", cb);
235         rejectCallbacks.push(cb);
236         if (state == "rejected") {
237           rejectCallbacks.pump(error);
238         }
239       }
240     ),
241   });
242   var r = new Resolver(this,
243                        fulfillCallbacks, rejectCallbacks,
244                        function(v) { value = v; },
245                        function(e) { error = e; },
246                        function(s) { state = s; })
247   try {
248     if (init) { init(r); }
249   } catch(e) {
250     r.reject(e);
251     console.log(e.stack);
252   }
253 };
254
255 //
256 // Consructor
257 //
258
259 var isCallback = function(any) {
260   return (typeof any == "function");
261 };
262
263 // Used in .then()
264 var wrap = function(callback, resolver, disposition) {
265   if (!isCallback(callback)) {
266     // If we don't get a callback, we want to forward whatever resolution we get
267     return resolver[disposition].bind(resolver);
268   }
269
270   return function() {
271     try {
272       var r = callback.apply(null, arguments);
273       resolver.resolve(r);
274     } catch(e) {
275       // Exceptions reject the resolver
276       resolver.reject(e);
277       console.log(e.stack);
278     }
279   };
280 };
281
282 var addCallbacks = function(onfulfill, onreject, scope) {
283   if (isCallback(onfulfill)) {
284     scope._addAcceptCallback(onfulfill);
285   }
286   if (isCallback(onreject)) {
287     scope._addRejectCallback(onreject);
288   }
289   return scope;
290 };
291
292 //
293 // Prototype properties
294 //
295
296 Promise.prototype = Object.create(null, {
297   "then": _public(function(onfulfill, onreject) {
298     // The logic here is:
299     //    We return a new Promise whose resolution merges with the return from
300     //    onfulfill() or onerror(). If onfulfill() returns a Promise, we forward
301     //    the resolution of that future to the resolution of the returned
302     //    Promise.
303     var f = this;
304     return new Promise(function(r) {
305       addCallbacks(wrap(onfulfill, r, "resolve"),
306                    wrap(onreject, r, "reject"), f);
307     });
308   }),
309   "catch": _public(function(onreject) {
310     var f = this;
311     return new Promise(function(r) {
312       addCallbacks(null, wrap(onreject, r, "reject"), f);
313     });
314   }),
315 });
316
317 //
318 // Statics
319 //
320
321 Promise.isThenable = isThenable;
322
323 var toPromiseList = function(list) {
324   return Array.prototype.slice.call(list).map(Promise.resolve);
325 };
326
327 Promise.any = function(/*...futuresOrValues*/) {
328   var futures = toPromiseList(arguments);
329   return new Promise(function(r) {
330     if (!futures.length) {
331       r.reject("No futures passed to Promise.any()");
332     } else {
333       var resolved = false;
334       var firstSuccess = function(value) {
335         if (resolved) { return; }
336         resolved = true;
337         r.resolve(value);
338       };
339       var firstFailure = function(reason) {
340         if (resolved) { return; }
341         resolved = true;
342         r.reject(reason);
343       };
344       futures.forEach(function(f, idx) {
345         f.then(firstSuccess, firstFailure);
346       });
347     }
348   });
349 };
350
351 Promise.every = function(/*...futuresOrValues*/) {
352   var futures = toPromiseList(arguments);
353   return new Promise(function(r) {
354     if (!futures.length) {
355       r.reject("No futures passed to Promise.every()");
356     } else {
357       var values = new Array(futures.length);
358       var count = 0;
359       var accumulate = function(idx, v) {
360         count++;
361         values[idx] = v;
362         if (count == futures.length) {
363           r.resolve(values);
364         }
365       };
366       futures.forEach(function(f, idx) {
367         f.then(accumulate.bind(null, idx), r.reject);
368       });
369     }
370   });
371 };
372
373 Promise.some = function() {
374   var futures = toPromiseList(arguments);
375   return new Promise(function(r) {
376     if (!futures.length) {
377       r.reject("No futures passed to Promise.some()");
378     } else {
379       var count = 0;
380       var accumulateFailures = function(e) {
381         count++;
382         if (count == futures.length) {
383           r.reject();
384         }
385       };
386       futures.forEach(function(f, idx) {
387         f.then(r.resolve, accumulateFailures);
388       });
389     }
390   });
391 };
392
393 Promise.fulfill = function(value) {
394   return new Promise(function(r) {
395     r.fulfill(value);
396   });
397 };
398
399 Promise.resolve = function(value) {
400   return new Promise(function(r) {
401     r.resolve(value);
402   });
403 };
404
405 Promise.reject = function(reason) {
406   return new Promise(function(r) {
407     r.reject(reason);
408   });
409 };
410
411 //
412 // Export
413 //
414
415 global.Promise = Promise;
416
417 })(this,
418   (typeof window !== 'undefined') ? window : {},
419   this.runningUnderTest||false);