338 lines
6.7 KiB
JavaScript
338 lines
6.7 KiB
JavaScript
/*
|
|
|
|
Copyright 2008-2015 Clipperz Srl
|
|
|
|
This file is part of Clipperz, the online password manager.
|
|
For further information about its features and functionalities please
|
|
refer to http://www.clipperz.com.
|
|
|
|
* Clipperz is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU Affero General Public License as published
|
|
by the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
* Clipperz is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
See the GNU Affero General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU Affero General Public
|
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
|
|
|
*/
|
|
|
|
(function () {
|
|
global = this
|
|
|
|
var queueId = 1
|
|
var queue = {}
|
|
var isRunningTask = false
|
|
|
|
if (!global.setImmediate)
|
|
global.addEventListener('message', function (e) {
|
|
if (e.source == global){
|
|
if (isRunningTask)
|
|
nextTick(queue[e.data])
|
|
else {
|
|
isRunningTask = true
|
|
try {
|
|
queue[e.data]()
|
|
} catch (e) {}
|
|
|
|
delete queue[e.data]
|
|
isRunningTask = false
|
|
}
|
|
}
|
|
})
|
|
|
|
function nextTick(fn) {
|
|
if (global.setImmediate) setImmediate(fn)
|
|
// if inside of web worker
|
|
else if (global.importScripts) setTimeout(fn)
|
|
else {
|
|
queueId++
|
|
queue[queueId] = fn
|
|
global.postMessage(queueId, '*')
|
|
}
|
|
}
|
|
|
|
Deferred.resolve = function (value) {
|
|
if (!(this._d == 1))
|
|
throw TypeError()
|
|
|
|
if (value instanceof Deferred)
|
|
return value
|
|
|
|
return new Deferred(function (resolve) {
|
|
resolve(value)
|
|
})
|
|
}
|
|
|
|
Deferred.reject = function (value) {
|
|
if (!(this._d == 1))
|
|
throw TypeError()
|
|
|
|
return new Deferred(function (resolve, reject) {
|
|
reject(value)
|
|
})
|
|
}
|
|
|
|
Deferred.all = function (arr) {
|
|
if (!(this._d == 1))
|
|
throw TypeError()
|
|
|
|
if (!(arr instanceof Array))
|
|
return Deferred.reject(TypeError())
|
|
|
|
var d = new Deferred()
|
|
|
|
function done(e, v) {
|
|
if (v)
|
|
return d.resolve(v)
|
|
|
|
if (e)
|
|
return d.reject(e)
|
|
|
|
var unresolved = arr.reduce(function (cnt, v) {
|
|
if (v && v.then)
|
|
return cnt + 1
|
|
return cnt
|
|
}, 0)
|
|
|
|
if(unresolved == 0)
|
|
d.resolve(arr)
|
|
|
|
arr.map(function (v, i) {
|
|
if (v && v.then)
|
|
v.then(function (r) {
|
|
arr[i] = r
|
|
done()
|
|
return r
|
|
}, done)
|
|
})
|
|
}
|
|
|
|
done()
|
|
|
|
return d
|
|
}
|
|
|
|
Deferred.race = function (arr) {
|
|
if (!(this._d == 1))
|
|
throw TypeError()
|
|
|
|
if (!(arr instanceof Array))
|
|
return Deferred.reject(TypeError())
|
|
|
|
if (arr.length == 0)
|
|
return new Deferred()
|
|
|
|
var d = new Deferred()
|
|
|
|
function done(e, v) {
|
|
if (v)
|
|
return d.resolve(v)
|
|
|
|
if (e)
|
|
return d.reject(e)
|
|
|
|
var unresolved = arr.reduce(function (cnt, v) {
|
|
if (v && v.then)
|
|
return cnt + 1
|
|
return cnt
|
|
}, 0)
|
|
|
|
if(unresolved == 0)
|
|
d.resolve(arr)
|
|
|
|
arr.map(function (v, i) {
|
|
if (v && v.then)
|
|
v.then(function (r) {
|
|
done(null, r)
|
|
}, done)
|
|
})
|
|
}
|
|
|
|
done()
|
|
|
|
return d
|
|
}
|
|
|
|
Deferred._d = 1
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
*/
|
|
function Deferred(resolver) {
|
|
'use strict'
|
|
if (typeof resolver != 'function' && resolver != undefined)
|
|
throw TypeError()
|
|
|
|
if (typeof this != 'object' || (this && this.then))
|
|
throw TypeError()
|
|
|
|
// states
|
|
// 0: pending
|
|
// 1: resolving
|
|
// 2: rejecting
|
|
// 3: resolved
|
|
// 4: rejected
|
|
var self = this,
|
|
state = 0,
|
|
val = 0,
|
|
next = [],
|
|
fn, er;
|
|
|
|
self['promise'] = self
|
|
|
|
self['resolve'] = function (v) {
|
|
fn = self.fn
|
|
er = self.er
|
|
if (!state) {
|
|
val = v
|
|
state = 1
|
|
|
|
nextTick(fire)
|
|
}
|
|
return self
|
|
}
|
|
|
|
self['reject'] = function (v) {
|
|
fn = self.fn
|
|
er = self.er
|
|
if (!state) {
|
|
val = v
|
|
state = 2
|
|
|
|
nextTick(fire)
|
|
|
|
}
|
|
return self
|
|
}
|
|
|
|
self['_d'] = 1
|
|
|
|
self['then'] = function (_fn, _er) {
|
|
if (!(this._d == 1))
|
|
throw TypeError()
|
|
|
|
var d = new Deferred()
|
|
|
|
d.fn = _fn
|
|
d.er = _er
|
|
if (state == 3) {
|
|
d.resolve(val)
|
|
}
|
|
else if (state == 4) {
|
|
d.reject(val)
|
|
}
|
|
else {
|
|
next.push(d)
|
|
}
|
|
|
|
return d
|
|
}
|
|
|
|
self['catch'] = function (_er) {
|
|
return self['then'](null, _er)
|
|
}
|
|
|
|
var finish = function (type) {
|
|
state = type || 4
|
|
next.map(function (p) {
|
|
state == 3 && p.resolve(val) || p.reject(val)
|
|
})
|
|
}
|
|
|
|
try {
|
|
if (typeof resolver == 'function')
|
|
resolver(self['resolve'], self['reject'])
|
|
} catch (e) {
|
|
self['reject'](e)
|
|
}
|
|
|
|
return self
|
|
|
|
// ref : reference to 'then' function
|
|
// cb, ec, cn : successCallback, failureCallback, notThennableCallback
|
|
function thennable (ref, cb, ec, cn) {
|
|
if ((typeof val == 'object' || typeof val == 'function') && typeof ref == 'function') {
|
|
try {
|
|
|
|
// cnt protects against abuse calls from spec checker
|
|
var cnt = 0
|
|
ref.call(val, function (v) {
|
|
if (cnt++) return
|
|
val = v
|
|
cb()
|
|
}, function (v) {
|
|
if (cnt++) return
|
|
val = v
|
|
ec()
|
|
})
|
|
} catch (e) {
|
|
val = e
|
|
ec()
|
|
}
|
|
} else {
|
|
cn()
|
|
}
|
|
};
|
|
|
|
function fire() {
|
|
|
|
// check if it's a thenable
|
|
var ref;
|
|
try {
|
|
ref = val && val.then
|
|
} catch (e) {
|
|
val = e
|
|
state = 2
|
|
return fire()
|
|
}
|
|
|
|
thennable(ref, function () {
|
|
state = 1
|
|
fire()
|
|
}, function () {
|
|
state = 2
|
|
fire()
|
|
}, function () {
|
|
try {
|
|
if (state == 1 && typeof fn == 'function') {
|
|
val = fn(val)
|
|
}
|
|
|
|
else if (state == 2 && typeof er == 'function') {
|
|
val = er(val)
|
|
state = 1
|
|
}
|
|
} catch (e) {
|
|
val = e
|
|
return finish()
|
|
}
|
|
|
|
if (val == self) {
|
|
val = TypeError()
|
|
finish()
|
|
} else thennable(ref, function () {
|
|
finish(3)
|
|
}, finish, function () {
|
|
finish(state == 1 && 3)
|
|
})
|
|
|
|
})
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// Export our library object, either for node.js or as a globally scoped variable
|
|
if (typeof module != 'undefined') {
|
|
module['exports'] = Deferred
|
|
} else {
|
|
global['Promise'] = global['Promise'] || Deferred
|
|
}
|
|
})()
|