Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion gulpGlobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ var globs = {
js: [
'web/js/analytics.js',
'web/js/gfyObject.js',
'web/js/gfyCollection.js'
'web/js/gfyCollection.js',
'web/js/promise.polyfill.js'
]
};

Expand Down
322 changes: 322 additions & 0 deletions web/js/promise.polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,322 @@
/**
* https://github.com/lahmatiy/es6-promise-polyfill
* Strictly a polyfill for ES6 Promise specification and nothing more
*/

(function(global){

//
// Check for prescence of native Promise and ensure it has the correct interface
//

var NativePromise = global.Promise;
var nativePromiseSupported =
NativePromise &&
// Some of these methods are missing from
// Firefox/Chrome experimental implementations
'resolve' in NativePromise &&
'reject' in NativePromise &&
'all' in NativePromise &&
'race' in NativePromise &&
// Older versions of the spec had a resolver object
// as the arg rather than a function
(function(){
var resolve;
new NativePromise(function(r){ resolve = r; });
return typeof resolve === 'function';
})();

//
// Polyfill
//

var PENDING = 'pending';
var SEALED = 'sealed';
var FULFILLED = 'fulfilled';
var REJECTED = 'rejected';
var NOOP = function(){};

function isArray(value) {
return Object.prototype.toString.call(value) === '[object Array]';
}

// async calls
var asyncSetTimer = typeof setImmediate !== 'undefined' ? setImmediate : setTimeout;
var asyncQueue = [];
var asyncTimer;

function asyncFlush(){
// run promise callbacks
for (var i = 0; i < asyncQueue.length; i++)
asyncQueue[i][0](asyncQueue[i][1]);

// reset async asyncQueue
asyncQueue = [];
asyncTimer = false;
}

function asyncCall(callback, arg){
asyncQueue.push([callback, arg]);

if (!asyncTimer)
{
asyncTimer = true;
asyncSetTimer(asyncFlush, 0);
}
}


function invokeResolver(resolver, promise) {
function resolvePromise(value) {
resolve(promise, value);
}

function rejectPromise(reason) {
reject(promise, reason);
}

try {
resolver(resolvePromise, rejectPromise);
} catch(e) {
rejectPromise(e);
}
}

function invokeCallback(subscriber){
var owner = subscriber.owner;
var settled = owner.state_;
var value = owner.data_;
var callback = subscriber[settled];
var promise = subscriber.then;

if (typeof callback === 'function')
{
settled = FULFILLED;
try {
value = callback(value);
} catch(e) {
reject(promise, e);
}
}

if (!handleThenable(promise, value))
{
if (settled === FULFILLED)
resolve(promise, value);

if (settled === REJECTED)
reject(promise, value);
}
}

function handleThenable(promise, value) {
var resolved;

try {
if (promise === value)
throw new TypeError('A promises callback cannot return that same promise.');

if (value && (typeof value === 'function' || typeof value === 'object'))
{
var then = value.then; // then should be retrived only once

if (typeof then === 'function')
{
then.call(value, function(val){
if (!resolved)
{
resolved = true;

if (value !== val)
resolve(promise, val);
else
fulfill(promise, val);
}
}, function(reason){
if (!resolved)
{
resolved = true;

reject(promise, reason);
}
});

return true;
}
}
} catch (e) {
if (!resolved)
reject(promise, e);

return true;
}

return false;
}

function resolve(promise, value){
if (promise === value || !handleThenable(promise, value))
fulfill(promise, value);
}

function fulfill(promise, value){
if (promise.state_ === PENDING)
{
promise.state_ = SEALED;
promise.data_ = value;

asyncCall(publishFulfillment, promise);
}
}

function reject(promise, reason){
if (promise.state_ === PENDING)
{
promise.state_ = SEALED;
promise.data_ = reason;

asyncCall(publishRejection, promise);
}
}

function publish(promise) {
var callbacks = promise.then_;
promise.then_ = undefined;

for (var i = 0; i < callbacks.length; i++) {
invokeCallback(callbacks[i]);
}
}

function publishFulfillment(promise){
promise.state_ = FULFILLED;
publish(promise);
}

function publishRejection(promise){
promise.state_ = REJECTED;
publish(promise);
}

/**
* @class
*/
function Promise(resolver){
if (typeof resolver !== 'function')
throw new TypeError('Promise constructor takes a function argument');

if (this instanceof Promise === false)
throw new TypeError('Failed to construct \'Promise\': Please use the \'new\' operator, this object constructor cannot be called as a function.');

this.then_ = [];

invokeResolver(resolver, this);
}

Promise.prototype = {
constructor: Promise,

state_: PENDING,
then_: null,
data_: undefined,

then: function(onFulfillment, onRejection){
var subscriber = {
owner: this,
then: new this.constructor(NOOP),
fulfilled: onFulfillment,
rejected: onRejection
};

if (this.state_ === FULFILLED || this.state_ === REJECTED)
{
// already resolved, call callback async
asyncCall(invokeCallback, subscriber);
}
else
{
// subscribe
this.then_.push(subscriber);
}

return subscriber.then;
},

'catch': function(onRejection) {
return this.then(null, onRejection);
}
};

Promise.all = function(promises){
var Class = this;

if (!isArray(promises))
throw new TypeError('You must pass an array to Promise.all().');

return new Class(function(resolve, reject){
var results = [];
var remaining = 0;

function resolver(index){
remaining++;
return function(value){
results[index] = value;
if (!--remaining)
resolve(results);
};
}

for (var i = 0, promise; i < promises.length; i++)
{
promise = promises[i];

if (promise && typeof promise.then === 'function')
promise.then(resolver(i), reject);
else
results[i] = promise;
}

if (!remaining)
resolve(results);
});
};

Promise.race = function(promises){
var Class = this;

if (!isArray(promises))
throw new TypeError('You must pass an array to Promise.race().');

return new Class(function(resolve, reject) {
for (var i = 0, promise; i < promises.length; i++)
{
promise = promises[i];

if (promise && typeof promise.then === 'function')
promise.then(resolve, reject);
else
resolve(promise);
}
});
};

Promise.resolve = function(value){
var Class = this;

if (value && typeof value === 'object' && value.constructor === Class)
return value;

return new Class(function(resolve){
resolve(value);
});
};

Promise.reject = function(reason){
var Class = this;

return new Class(function(resolve, reject){
reject(reason);
});
};

})(typeof window != 'undefined' ? window : typeof global != 'undefined' ? global : typeof self != 'undefined' ? self : this);