Last active
September 25, 2015 07:35
-
-
Save edjafarov/822caaf57c636ec951b8 to your computer and use it in GitHub Desktop.
worker-test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module['exports'] = function(hook){ | |
var Promise = require('promise'); | |
var globalHandler = {}; | |
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | |
module.exports = { | |
ClientToHookStream: function Stream(hookUrl, fetchFn){ | |
var fetchFn = fetchFn || fetch; | |
var StreamHandler=null; | |
return { | |
send: function(message){ | |
console.log(message,JSON.stringify(message)); | |
fetchFn(hookUrl ,{ | |
method: 'POST', | |
headers: { | |
'Accept': '*/*', | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify(message) | |
}).then(function(response){ | |
return response.json(); | |
}).then(function(message){ | |
if(StreamHandler) return StreamHandler(message); | |
return message; | |
}); | |
}, | |
listen: function(handler){ | |
StreamHandler = handler; | |
} | |
} | |
}, | |
//express app | |
//highly experimental | |
HookToClientStream: function ServerClientStream(moduleObj){ | |
var StreamHandler; | |
globalHandler.fn = function(hook){ | |
var message = hook.req.body; | |
message._response = hook.res; | |
StreamHandler(message) | |
}; | |
return { | |
send: function(message){ | |
if(!message._response) throw Error("no response defined"); | |
var res = message._response; | |
message._response = undefined; | |
res.json(message); | |
}, | |
listen: function(handler){ | |
StreamHandler = handler; | |
} | |
} | |
} | |
} | |
},{}],2:[function(require,module,exports){ | |
var logic = require('./logic'); | |
var PromisePipe = logic.PromisePipe; | |
var connectors = require('./HookioConnectorStream.js'); | |
PromisePipe.stream('hook','client').connector(connectors.HookToClientStream()) | |
},{"./HookioConnectorStream.js":1,"./logic":3}],3:[function(require,module,exports){ | |
var PromisePipe = require('../../src/PromisePipe')(); | |
PromisePipe.setMode('DEBUG'); | |
var Promise = require('es6-promise').Promise; | |
var ENV = 'CLIENT'; | |
//set up server | |
if(typeof(window) !== 'object'){ | |
PromisePipe.setEnv('hook'); | |
ENV = 'HOOK'; | |
} | |
module.exports = { | |
PromisePipe: PromisePipe, | |
getSomething: PromisePipe() | |
.then(doInHook(function(){ | |
console.log("doing in hook"); | |
return "MADE IN HOOK" | |
})) | |
.then(function(data){ | |
console.log("AAAA", data); | |
}) | |
} | |
function buildHtml(data){ | |
data = data || []; | |
result = renderTodoApp(renderAppHeader() | |
+ renderAppMain("<ul id='todo-list'>" + | |
data.map(function(item, i){ | |
var result = '<input class="toggle" type="checkbox" ' +(item.done?'checked':'')+ ' onclick="main.doneItem(\''+item._id+'\')"></input>'; | |
result+= "<label>" + item.name + "</label>"; | |
result+='<button class="destroy" onclick="main.removeItem(\''+item._id+'\')"></button>'; | |
result = '<div class="view">'+result+'</div>' | |
result = '<li class="'+(item.done?'completed':'')+'">'+result+'</li>' | |
return result; | |
}).join('') + "</ul>" | |
, data) | |
+ renderAppFooter(data)) + renderAppInfo(); | |
return result; | |
} | |
function renderItems(data){ | |
document.getElementById('todo-app').innerHTML = data; | |
return data; | |
} | |
function renderAppHeader(){ | |
return '<header id="header"><h1>todos</h1><input id="new-todo" placeholder="What needs to be done?" autofocus onkeyup="event.which == 13 && main.addItem(this.value);"></header>'; | |
} | |
function renderAppMain(wrap, items){ | |
var allChecked = items.reduce(function(result, item){ | |
if(!item.done) return false; | |
return result; | |
}, true); | |
return '<section id="main"><input id="toggle-all" '+(allChecked?'checked':'')+' type="checkbox" onclick="main.doneAllItem(this.checked)"><label for="toggle-all">Mark all as complete</label>' + wrap + '</section>'; | |
} | |
function renderAppFooter(data){ | |
return '<footer id="footer"><span id="todo-count">' +(data?data.length:0)+ ' items</span><button id="clear-completed" onclick="main.clearDoneItems()">Clear completed</button></footer>'; | |
} | |
function renderTodoApp(wrap){ | |
return '<section id="todoapp">' + wrap + '</section>'; | |
} | |
function renderAppInfo(){ | |
return '<div id="info"><p>Written by <a href="https://github.com/edjafarov">Eldar Djafarov</a></p><p><a href="https://github.com/edjafarov/PromisePipe/tree/master/example/mongotodo">PromisePipe based MongoDb persistent TodoApp</a> is a part of <a href="http://todomvc.com">TodoMVC</a></p></div>' | |
} | |
function doInHook(fn){ | |
fn._env = 'hook'; | |
return fn | |
} | |
},{"../../src/PromisePipe":9,"es6-promise":5}],4:[function(require,module,exports){ | |
// shim for using process in browser | |
var process = module.exports = {}; | |
var queue = []; | |
var draining = false; | |
function drainQueue() { | |
if (draining) { | |
return; | |
} | |
draining = true; | |
var currentQueue; | |
var len = queue.length; | |
while(len) { | |
currentQueue = queue; | |
queue = []; | |
var i = -1; | |
while (++i < len) { | |
currentQueue[i](); | |
} | |
len = queue.length; | |
} | |
draining = false; | |
} | |
process.nextTick = function (fun) { | |
queue.push(fun); | |
if (!draining) { | |
setTimeout(drainQueue, 0); | |
} | |
}; | |
process.title = 'browser'; | |
process.browser = true; | |
process.env = {}; | |
process.argv = []; | |
process.version = ''; // empty string to avoid regexp issues | |
process.versions = {}; | |
function noop() {} | |
process.on = noop; | |
process.addListener = noop; | |
process.once = noop; | |
process.off = noop; | |
process.removeListener = noop; | |
process.removeAllListeners = noop; | |
process.emit = noop; | |
process.binding = function (name) { | |
throw new Error('process.binding is not supported'); | |
}; | |
// TODO(shtylman) | |
process.cwd = function () { return '/' }; | |
process.chdir = function (dir) { | |
throw new Error('process.chdir is not supported'); | |
}; | |
process.umask = function() { return 0; }; | |
},{}],5:[function(require,module,exports){ | |
(function (process,global){ | |
/*! | |
* @overview es6-promise - a tiny implementation of Promises/A+. | |
* @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) | |
* @license Licensed under MIT license | |
* See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE | |
* @version 2.1.1 | |
*/ | |
(function() { | |
"use strict"; | |
function lib$es6$promise$utils$$objectOrFunction(x) { | |
return typeof x === 'function' || (typeof x === 'object' && x !== null); | |
} | |
function lib$es6$promise$utils$$isFunction(x) { | |
return typeof x === 'function'; | |
} | |
function lib$es6$promise$utils$$isMaybeThenable(x) { | |
return typeof x === 'object' && x !== null; | |
} | |
var lib$es6$promise$utils$$_isArray; | |
if (!Array.isArray) { | |
lib$es6$promise$utils$$_isArray = function (x) { | |
return Object.prototype.toString.call(x) === '[object Array]'; | |
}; | |
} else { | |
lib$es6$promise$utils$$_isArray = Array.isArray; | |
} | |
var lib$es6$promise$utils$$isArray = lib$es6$promise$utils$$_isArray; | |
var lib$es6$promise$asap$$len = 0; | |
var lib$es6$promise$asap$$toString = {}.toString; | |
var lib$es6$promise$asap$$vertxNext; | |
function lib$es6$promise$asap$$asap(callback, arg) { | |
lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len] = callback; | |
lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len + 1] = arg; | |
lib$es6$promise$asap$$len += 2; | |
if (lib$es6$promise$asap$$len === 2) { | |
// If len is 2, that means that we need to schedule an async flush. | |
// If additional callbacks are queued before the queue is flushed, they | |
// will be processed by this flush that we are scheduling. | |
lib$es6$promise$asap$$scheduleFlush(); | |
} | |
} | |
var lib$es6$promise$asap$$default = lib$es6$promise$asap$$asap; | |
var lib$es6$promise$asap$$browserWindow = (typeof window !== 'undefined') ? window : undefined; | |
var lib$es6$promise$asap$$browserGlobal = lib$es6$promise$asap$$browserWindow || {}; | |
var lib$es6$promise$asap$$BrowserMutationObserver = lib$es6$promise$asap$$browserGlobal.MutationObserver || lib$es6$promise$asap$$browserGlobal.WebKitMutationObserver; | |
var lib$es6$promise$asap$$isNode = typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; | |
// test for web worker but not in IE10 | |
var lib$es6$promise$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && | |
typeof importScripts !== 'undefined' && | |
typeof MessageChannel !== 'undefined'; | |
// node | |
function lib$es6$promise$asap$$useNextTick() { | |
var nextTick = process.nextTick; | |
// node version 0.10.x displays a deprecation warning when nextTick is used recursively | |
// setImmediate should be used instead instead | |
var version = process.versions.node.match(/^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$/); | |
if (Array.isArray(version) && version[1] === '0' && version[2] === '10') { | |
nextTick = setImmediate; | |
} | |
return function() { | |
nextTick(lib$es6$promise$asap$$flush); | |
}; | |
} | |
// vertx | |
function lib$es6$promise$asap$$useVertxTimer() { | |
return function() { | |
lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush); | |
}; | |
} | |
function lib$es6$promise$asap$$useMutationObserver() { | |
var iterations = 0; | |
var observer = new lib$es6$promise$asap$$BrowserMutationObserver(lib$es6$promise$asap$$flush); | |
var node = document.createTextNode(''); | |
observer.observe(node, { characterData: true }); | |
return function() { | |
node.data = (iterations = ++iterations % 2); | |
}; | |
} | |
// web worker | |
function lib$es6$promise$asap$$useMessageChannel() { | |
var channel = new MessageChannel(); | |
channel.port1.onmessage = lib$es6$promise$asap$$flush; | |
return function () { | |
channel.port2.postMessage(0); | |
}; | |
} | |
function lib$es6$promise$asap$$useSetTimeout() { | |
return function() { | |
setTimeout(lib$es6$promise$asap$$flush, 1); | |
}; | |
} | |
var lib$es6$promise$asap$$queue = new Array(1000); | |
function lib$es6$promise$asap$$flush() { | |
for (var i = 0; i < lib$es6$promise$asap$$len; i+=2) { | |
var callback = lib$es6$promise$asap$$queue[i]; | |
var arg = lib$es6$promise$asap$$queue[i+1]; | |
callback(arg); | |
lib$es6$promise$asap$$queue[i] = undefined; | |
lib$es6$promise$asap$$queue[i+1] = undefined; | |
} | |
lib$es6$promise$asap$$len = 0; | |
} | |
function lib$es6$promise$asap$$attemptVertex() { | |
try { | |
var r = require; | |
var vertx = r('vertx'); | |
lib$es6$promise$asap$$vertxNext = vertx.runOnLoop || vertx.runOnContext; | |
return lib$es6$promise$asap$$useVertxTimer(); | |
} catch(e) { | |
return lib$es6$promise$asap$$useSetTimeout(); | |
} | |
} | |
var lib$es6$promise$asap$$scheduleFlush; | |
// Decide what async method to use to triggering processing of queued callbacks: | |
if (lib$es6$promise$asap$$isNode) { | |
lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useNextTick(); | |
} else if (lib$es6$promise$asap$$BrowserMutationObserver) { | |
lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMutationObserver(); | |
} else if (lib$es6$promise$asap$$isWorker) { | |
lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMessageChannel(); | |
} else if (lib$es6$promise$asap$$browserWindow === undefined && typeof require === 'function') { | |
lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$attemptVertex(); | |
} else { | |
lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useSetTimeout(); | |
} | |
function lib$es6$promise$$internal$$noop() {} | |
var lib$es6$promise$$internal$$PENDING = void 0; | |
var lib$es6$promise$$internal$$FULFILLED = 1; | |
var lib$es6$promise$$internal$$REJECTED = 2; | |
var lib$es6$promise$$internal$$GET_THEN_ERROR = new lib$es6$promise$$internal$$ErrorObject(); | |
function lib$es6$promise$$internal$$selfFullfillment() { | |
return new TypeError("You cannot resolve a promise with itself"); | |
} | |
function lib$es6$promise$$internal$$cannotReturnOwn() { | |
return new TypeError('A promises callback cannot return that same promise.'); | |
} | |
function lib$es6$promise$$internal$$getThen(promise) { | |
try { | |
return promise.then; | |
} catch(error) { | |
lib$es6$promise$$internal$$GET_THEN_ERROR.error = error; | |
return lib$es6$promise$$internal$$GET_THEN_ERROR; | |
} | |
} | |
function lib$es6$promise$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { | |
try { | |
then.call(value, fulfillmentHandler, rejectionHandler); | |
} catch(e) { | |
return e; | |
} | |
} | |
function lib$es6$promise$$internal$$handleForeignThenable(promise, thenable, then) { | |
lib$es6$promise$asap$$default(function(promise) { | |
var sealed = false; | |
var error = lib$es6$promise$$internal$$tryThen(then, thenable, function(value) { | |
if (sealed) { return; } | |
sealed = true; | |
if (thenable !== value) { | |
lib$es6$promise$$internal$$resolve(promise, value); | |
} else { | |
lib$es6$promise$$internal$$fulfill(promise, value); | |
} | |
}, function(reason) { | |
if (sealed) { return; } | |
sealed = true; | |
lib$es6$promise$$internal$$reject(promise, reason); | |
}, 'Settle: ' + (promise._label || ' unknown promise')); | |
if (!sealed && error) { | |
sealed = true; | |
lib$es6$promise$$internal$$reject(promise, error); | |
} | |
}, promise); | |
} | |
function lib$es6$promise$$internal$$handleOwnThenable(promise, thenable) { | |
if (thenable._state === lib$es6$promise$$internal$$FULFILLED) { | |
lib$es6$promise$$internal$$fulfill(promise, thenable._result); | |
} else if (thenable._state === lib$es6$promise$$internal$$REJECTED) { | |
lib$es6$promise$$internal$$reject(promise, thenable._result); | |
} else { | |
lib$es6$promise$$internal$$subscribe(thenable, undefined, function(value) { | |
lib$es6$promise$$internal$$resolve(promise, value); | |
}, function(reason) { | |
lib$es6$promise$$internal$$reject(promise, reason); | |
}); | |
} | |
} | |
function lib$es6$promise$$internal$$handleMaybeThenable(promise, maybeThenable) { | |
if (maybeThenable.constructor === promise.constructor) { | |
lib$es6$promise$$internal$$handleOwnThenable(promise, maybeThenable); | |
} else { | |
var then = lib$es6$promise$$internal$$getThen(maybeThenable); | |
if (then === lib$es6$promise$$internal$$GET_THEN_ERROR) { | |
lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$GET_THEN_ERROR.error); | |
} else if (then === undefined) { | |
lib$es6$promise$$internal$$fulfill(promise, maybeThenable); | |
} else if (lib$es6$promise$utils$$isFunction(then)) { | |
lib$es6$promise$$internal$$handleForeignThenable(promise, maybeThenable, then); | |
} else { | |
lib$es6$promise$$internal$$fulfill(promise, maybeThenable); | |
} | |
} | |
} | |
function lib$es6$promise$$internal$$resolve(promise, value) { | |
if (promise === value) { | |
lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$selfFullfillment()); | |
} else if (lib$es6$promise$utils$$objectOrFunction(value)) { | |
lib$es6$promise$$internal$$handleMaybeThenable(promise, value); | |
} else { | |
lib$es6$promise$$internal$$fulfill(promise, value); | |
} | |
} | |
function lib$es6$promise$$internal$$publishRejection(promise) { | |
if (promise._onerror) { | |
promise._onerror(promise._result); | |
} | |
lib$es6$promise$$internal$$publish(promise); | |
} | |
function lib$es6$promise$$internal$$fulfill(promise, value) { | |
if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } | |
promise._result = value; | |
promise._state = lib$es6$promise$$internal$$FULFILLED; | |
if (promise._subscribers.length !== 0) { | |
lib$es6$promise$asap$$default(lib$es6$promise$$internal$$publish, promise); | |
} | |
} | |
function lib$es6$promise$$internal$$reject(promise, reason) { | |
if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } | |
promise._state = lib$es6$promise$$internal$$REJECTED; | |
promise._result = reason; | |
lib$es6$promise$asap$$default(lib$es6$promise$$internal$$publishRejection, promise); | |
} | |
function lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection) { | |
var subscribers = parent._subscribers; | |
var length = subscribers.length; | |
parent._onerror = null; | |
subscribers[length] = child; | |
subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment; | |
subscribers[length + lib$es6$promise$$internal$$REJECTED] = onRejection; | |
if (length === 0 && parent._state) { | |
lib$es6$promise$asap$$default(lib$es6$promise$$internal$$publish, parent); | |
} | |
} | |
function lib$es6$promise$$internal$$publish(promise) { | |
var subscribers = promise._subscribers; | |
var settled = promise._state; | |
if (subscribers.length === 0) { return; } | |
var child, callback, detail = promise._result; | |
for (var i = 0; i < subscribers.length; i += 3) { | |
child = subscribers[i]; | |
callback = subscribers[i + settled]; | |
if (child) { | |
lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail); | |
} else { | |
callback(detail); | |
} | |
} | |
promise._subscribers.length = 0; | |
} | |
function lib$es6$promise$$internal$$ErrorObject() { | |
this.error = null; | |
} | |
var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject(); | |
function lib$es6$promise$$internal$$tryCatch(callback, detail) { | |
try { | |
return callback(detail); | |
} catch(e) { | |
lib$es6$promise$$internal$$TRY_CATCH_ERROR.error = e; | |
return lib$es6$promise$$internal$$TRY_CATCH_ERROR; | |
} | |
} | |
function lib$es6$promise$$internal$$invokeCallback(settled, promise, callback, detail) { | |
var hasCallback = lib$es6$promise$utils$$isFunction(callback), | |
value, error, succeeded, failed; | |
if (hasCallback) { | |
value = lib$es6$promise$$internal$$tryCatch(callback, detail); | |
if (value === lib$es6$promise$$internal$$TRY_CATCH_ERROR) { | |
failed = true; | |
error = value.error; | |
value = null; | |
} else { | |
succeeded = true; | |
} | |
if (promise === value) { | |
lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn()); | |
return; | |
} | |
} else { | |
value = detail; | |
succeeded = true; | |
} | |
if (promise._state !== lib$es6$promise$$internal$$PENDING) { | |
// noop | |
} else if (hasCallback && succeeded) { | |
lib$es6$promise$$internal$$resolve(promise, value); | |
} else if (failed) { | |
lib$es6$promise$$internal$$reject(promise, error); | |
} else if (settled === lib$es6$promise$$internal$$FULFILLED) { | |
lib$es6$promise$$internal$$fulfill(promise, value); | |
} else if (settled === lib$es6$promise$$internal$$REJECTED) { | |
lib$es6$promise$$internal$$reject(promise, value); | |
} | |
} | |
function lib$es6$promise$$internal$$initializePromise(promise, resolver) { | |
try { | |
resolver(function resolvePromise(value){ | |
lib$es6$promise$$internal$$resolve(promise, value); | |
}, function rejectPromise(reason) { | |
lib$es6$promise$$internal$$reject(promise, reason); | |
}); | |
} catch(e) { | |
lib$es6$promise$$internal$$reject(promise, e); | |
} | |
} | |
function lib$es6$promise$enumerator$$Enumerator(Constructor, input) { | |
var enumerator = this; | |
enumerator._instanceConstructor = Constructor; | |
enumerator.promise = new Constructor(lib$es6$promise$$internal$$noop); | |
if (enumerator._validateInput(input)) { | |
enumerator._input = input; | |
enumerator.length = input.length; | |
enumerator._remaining = input.length; | |
enumerator._init(); | |
if (enumerator.length === 0) { | |
lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result); | |
} else { | |
enumerator.length = enumerator.length || 0; | |
enumerator._enumerate(); | |
if (enumerator._remaining === 0) { | |
lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result); | |
} | |
} | |
} else { | |
lib$es6$promise$$internal$$reject(enumerator.promise, enumerator._validationError()); | |
} | |
} | |
lib$es6$promise$enumerator$$Enumerator.prototype._validateInput = function(input) { | |
return lib$es6$promise$utils$$isArray(input); | |
}; | |
lib$es6$promise$enumerator$$Enumerator.prototype._validationError = function() { | |
return new Error('Array Methods must be provided an Array'); | |
}; | |
lib$es6$promise$enumerator$$Enumerator.prototype._init = function() { | |
this._result = new Array(this.length); | |
}; | |
var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator; | |
lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() { | |
var enumerator = this; | |
var length = enumerator.length; | |
var promise = enumerator.promise; | |
var input = enumerator._input; | |
for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { | |
enumerator._eachEntry(input[i], i); | |
} | |
}; | |
lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { | |
var enumerator = this; | |
var c = enumerator._instanceConstructor; | |
if (lib$es6$promise$utils$$isMaybeThenable(entry)) { | |
if (entry.constructor === c && entry._state !== lib$es6$promise$$internal$$PENDING) { | |
entry._onerror = null; | |
enumerator._settledAt(entry._state, i, entry._result); | |
} else { | |
enumerator._willSettleAt(c.resolve(entry), i); | |
} | |
} else { | |
enumerator._remaining--; | |
enumerator._result[i] = entry; | |
} | |
}; | |
lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { | |
var enumerator = this; | |
var promise = enumerator.promise; | |
if (promise._state === lib$es6$promise$$internal$$PENDING) { | |
enumerator._remaining--; | |
if (state === lib$es6$promise$$internal$$REJECTED) { | |
lib$es6$promise$$internal$$reject(promise, value); | |
} else { | |
enumerator._result[i] = value; | |
} | |
} | |
if (enumerator._remaining === 0) { | |
lib$es6$promise$$internal$$fulfill(promise, enumerator._result); | |
} | |
}; | |
lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { | |
var enumerator = this; | |
lib$es6$promise$$internal$$subscribe(promise, undefined, function(value) { | |
enumerator._settledAt(lib$es6$promise$$internal$$FULFILLED, i, value); | |
}, function(reason) { | |
enumerator._settledAt(lib$es6$promise$$internal$$REJECTED, i, reason); | |
}); | |
}; | |
function lib$es6$promise$promise$all$$all(entries) { | |
return new lib$es6$promise$enumerator$$default(this, entries).promise; | |
} | |
var lib$es6$promise$promise$all$$default = lib$es6$promise$promise$all$$all; | |
function lib$es6$promise$promise$race$$race(entries) { | |
/*jshint validthis:true */ | |
var Constructor = this; | |
var promise = new Constructor(lib$es6$promise$$internal$$noop); | |
if (!lib$es6$promise$utils$$isArray(entries)) { | |
lib$es6$promise$$internal$$reject(promise, new TypeError('You must pass an array to race.')); | |
return promise; | |
} | |
var length = entries.length; | |
function onFulfillment(value) { | |
lib$es6$promise$$internal$$resolve(promise, value); | |
} | |
function onRejection(reason) { | |
lib$es6$promise$$internal$$reject(promise, reason); | |
} | |
for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { | |
lib$es6$promise$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); | |
} | |
return promise; | |
} | |
var lib$es6$promise$promise$race$$default = lib$es6$promise$promise$race$$race; | |
function lib$es6$promise$promise$resolve$$resolve(object) { | |
/*jshint validthis:true */ | |
var Constructor = this; | |
if (object && typeof object === 'object' && object.constructor === Constructor) { | |
return object; | |
} | |
var promise = new Constructor(lib$es6$promise$$internal$$noop); | |
lib$es6$promise$$internal$$resolve(promise, object); | |
return promise; | |
} | |
var lib$es6$promise$promise$resolve$$default = lib$es6$promise$promise$resolve$$resolve; | |
function lib$es6$promise$promise$reject$$reject(reason) { | |
/*jshint validthis:true */ | |
var Constructor = this; | |
var promise = new Constructor(lib$es6$promise$$internal$$noop); | |
lib$es6$promise$$internal$$reject(promise, reason); | |
return promise; | |
} | |
var lib$es6$promise$promise$reject$$default = lib$es6$promise$promise$reject$$reject; | |
var lib$es6$promise$promise$$counter = 0; | |
function lib$es6$promise$promise$$needsResolver() { | |
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); | |
} | |
function lib$es6$promise$promise$$needsNew() { | |
throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); | |
} | |
var lib$es6$promise$promise$$default = lib$es6$promise$promise$$Promise; | |
/** | |
Promise objects represent the eventual result of an asynchronous operation. The | |
primary way of interacting with a promise is through its `then` method, which | |
registers callbacks to receive either a promise’s eventual value or the reason | |
why the promise cannot be fulfilled. | |
Terminology | |
----------- | |
- `promise` is an object or function with a `then` method whose behavior conforms to this specification. | |
- `thenable` is an object or function that defines a `then` method. | |
- `value` is any legal JavaScript value (including undefined, a thenable, or a promise). | |
- `exception` is a value that is thrown using the throw statement. | |
- `reason` is a value that indicates why a promise was rejected. | |
- `settled` the final resting state of a promise, fulfilled or rejected. | |
A promise can be in one of three states: pending, fulfilled, or rejected. | |
Promises that are fulfilled have a fulfillment value and are in the fulfilled | |
state. Promises that are rejected have a rejection reason and are in the | |
rejected state. A fulfillment value is never a thenable. | |
Promises can also be said to *resolve* a value. If this value is also a | |
promise, then the original promise's settled state will match the value's | |
settled state. So a promise that *resolves* a promise that rejects will | |
itself reject, and a promise that *resolves* a promise that fulfills will | |
itself fulfill. | |
Basic Usage: | |
------------ | |
```js | |
var promise = new Promise(function(resolve, reject) { | |
// on success | |
resolve(value); | |
// on failure | |
reject(reason); | |
}); | |
promise.then(function(value) { | |
// on fulfillment | |
}, function(reason) { | |
// on rejection | |
}); | |
``` | |
Advanced Usage: | |
--------------- | |
Promises shine when abstracting away asynchronous interactions such as | |
`XMLHttpRequest`s. | |
```js | |
function getJSON(url) { | |
return new Promise(function(resolve, reject){ | |
var xhr = new XMLHttpRequest(); | |
xhr.open('GET', url); | |
xhr.onreadystatechange = handler; | |
xhr.responseType = 'json'; | |
xhr.setRequestHeader('Accept', 'application/json'); | |
xhr.send(); | |
function handler() { | |
if (this.readyState === this.DONE) { | |
if (this.status === 200) { | |
resolve(this.response); | |
} else { | |
reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); | |
} | |
} | |
}; | |
}); | |
} | |
getJSON('/posts.json').then(function(json) { | |
// on fulfillment | |
}, function(reason) { | |
// on rejection | |
}); | |
``` | |
Unlike callbacks, promises are great composable primitives. | |
```js | |
Promise.all([ | |
getJSON('/posts'), | |
getJSON('/comments') | |
]).then(function(values){ | |
values[0] // => postsJSON | |
values[1] // => commentsJSON | |
return values; | |
}); | |
``` | |
@class Promise | |
@param {function} resolver | |
Useful for tooling. | |
@constructor | |
*/ | |
function lib$es6$promise$promise$$Promise(resolver) { | |
this._id = lib$es6$promise$promise$$counter++; | |
this._state = undefined; | |
this._result = undefined; | |
this._subscribers = []; | |
if (lib$es6$promise$$internal$$noop !== resolver) { | |
if (!lib$es6$promise$utils$$isFunction(resolver)) { | |
lib$es6$promise$promise$$needsResolver(); | |
} | |
if (!(this instanceof lib$es6$promise$promise$$Promise)) { | |
lib$es6$promise$promise$$needsNew(); | |
} | |
lib$es6$promise$$internal$$initializePromise(this, resolver); | |
} | |
} | |
lib$es6$promise$promise$$Promise.all = lib$es6$promise$promise$all$$default; | |
lib$es6$promise$promise$$Promise.race = lib$es6$promise$promise$race$$default; | |
lib$es6$promise$promise$$Promise.resolve = lib$es6$promise$promise$resolve$$default; | |
lib$es6$promise$promise$$Promise.reject = lib$es6$promise$promise$reject$$default; | |
lib$es6$promise$promise$$Promise.prototype = { | |
constructor: lib$es6$promise$promise$$Promise, | |
/** | |
The primary way of interacting with a promise is through its `then` method, | |
which registers callbacks to receive either a promise's eventual value or the | |
reason why the promise cannot be fulfilled. | |
```js | |
findUser().then(function(user){ | |
// user is available | |
}, function(reason){ | |
// user is unavailable, and you are given the reason why | |
}); | |
``` | |
Chaining | |
-------- | |
The return value of `then` is itself a promise. This second, 'downstream' | |
promise is resolved with the return value of the first promise's fulfillment | |
or rejection handler, or rejected if the handler throws an exception. | |
```js | |
findUser().then(function (user) { | |
return user.name; | |
}, function (reason) { | |
return 'default name'; | |
}).then(function (userName) { | |
// If `findUser` fulfilled, `userName` will be the user's name, otherwise it | |
// will be `'default name'` | |
}); | |
findUser().then(function (user) { | |
throw new Error('Found user, but still unhappy'); | |
}, function (reason) { | |
throw new Error('`findUser` rejected and we're unhappy'); | |
}).then(function (value) { | |
// never reached | |
}, function (reason) { | |
// if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. | |
// If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. | |
}); | |
``` | |
If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. | |
```js | |
findUser().then(function (user) { | |
throw new PedagogicalException('Upstream error'); | |
}).then(function (value) { | |
// never reached | |
}).then(function (value) { | |
// never reached | |
}, function (reason) { | |
// The `PedgagocialException` is propagated all the way down to here | |
}); | |
``` | |
Assimilation | |
------------ | |
Sometimes the value you want to propagate to a downstream promise can only be | |
retrieved asynchronously. This can be achieved by returning a promise in the | |
fulfillment or rejection handler. The downstream promise will then be pending | |
until the returned promise is settled. This is called *assimilation*. | |
```js | |
findUser().then(function (user) { | |
return findCommentsByAuthor(user); | |
}).then(function (comments) { | |
// The user's comments are now available | |
}); | |
``` | |
If the assimliated promise rejects, then the downstream promise will also reject. | |
```js | |
findUser().then(function (user) { | |
return findCommentsByAuthor(user); | |
}).then(function (comments) { | |
// If `findCommentsByAuthor` fulfills, we'll have the value here | |
}, function (reason) { | |
// If `findCommentsByAuthor` rejects, we'll have the reason here | |
}); | |
``` | |
Simple Example | |
-------------- | |
Synchronous Example | |
```javascript | |
var result; | |
try { | |
result = findResult(); | |
// success | |
} catch(reason) { | |
// failure | |
} | |
``` | |
Errback Example | |
```js | |
findResult(function(result, err){ | |
if (err) { | |
// failure | |
} else { | |
// success | |
} | |
}); | |
``` | |
Promise Example; | |
```javascript | |
findResult().then(function(result){ | |
// success | |
}, function(reason){ | |
// failure | |
}); | |
``` | |
Advanced Example | |
-------------- | |
Synchronous Example | |
```javascript | |
var author, books; | |
try { | |
author = findAuthor(); | |
books = findBooksByAuthor(author); | |
// success | |
} catch(reason) { | |
// failure | |
} | |
``` | |
Errback Example | |
```js | |
function foundBooks(books) { | |
} | |
function failure(reason) { | |
} | |
findAuthor(function(author, err){ | |
if (err) { | |
failure(err); | |
// failure | |
} else { | |
try { | |
findBoooksByAuthor(author, function(books, err) { | |
if (err) { | |
failure(err); | |
} else { | |
try { | |
foundBooks(books); | |
} catch(reason) { | |
failure(reason); | |
} | |
} | |
}); | |
} catch(error) { | |
failure(err); | |
} | |
// success | |
} | |
}); | |
``` | |
Promise Example; | |
```javascript | |
findAuthor(). | |
then(findBooksByAuthor). | |
then(function(books){ | |
// found books | |
}).catch(function(reason){ | |
// something went wrong | |
}); | |
``` | |
@method then | |
@param {Function} onFulfilled | |
@param {Function} onRejected | |
Useful for tooling. | |
@return {Promise} | |
*/ | |
then: function(onFulfillment, onRejection) { | |
var parent = this; | |
var state = parent._state; | |
if (state === lib$es6$promise$$internal$$FULFILLED && !onFulfillment || state === lib$es6$promise$$internal$$REJECTED && !onRejection) { | |
return this; | |
} | |
var child = new this.constructor(lib$es6$promise$$internal$$noop); | |
var result = parent._result; | |
if (state) { | |
var callback = arguments[state - 1]; | |
lib$es6$promise$asap$$default(function(){ | |
lib$es6$promise$$internal$$invokeCallback(state, child, callback, result); | |
}); | |
} else { | |
lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection); | |
} | |
return child; | |
}, | |
/** | |
`catch` is simply sugar for `then(undefined, onRejection)` which makes it the same | |
as the catch block of a try/catch statement. | |
```js | |
function findAuthor(){ | |
throw new Error('couldn't find that author'); | |
} | |
// synchronous | |
try { | |
findAuthor(); | |
} catch(reason) { | |
// something went wrong | |
} | |
// async with promises | |
findAuthor().catch(function(reason){ | |
// something went wrong | |
}); | |
``` | |
@method catch | |
@param {Function} onRejection | |
Useful for tooling. | |
@return {Promise} | |
*/ | |
'catch': function(onRejection) { | |
return this.then(null, onRejection); | |
} | |
}; | |
function lib$es6$promise$polyfill$$polyfill() { | |
var local; | |
if (typeof global !== 'undefined') { | |
local = global; | |
} else if (typeof self !== 'undefined') { | |
local = self; | |
} else { | |
try { | |
local = Function('return this')(); | |
} catch (e) { | |
throw new Error('polyfill failed because global object is unavailable in this environment'); | |
} | |
} | |
var P = local.Promise; | |
if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) { | |
return; | |
} | |
local.Promise = lib$es6$promise$promise$$default; | |
} | |
var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill; | |
var lib$es6$promise$umd$$ES6Promise = { | |
'Promise': lib$es6$promise$promise$$default, | |
'polyfill': lib$es6$promise$polyfill$$default | |
}; | |
/* global define:true module:true window: true */ | |
if (typeof define === 'function' && define['amd']) { | |
define(function() { return lib$es6$promise$umd$$ES6Promise; }); | |
} else if (typeof module !== 'undefined' && module['exports']) { | |
module['exports'] = lib$es6$promise$umd$$ES6Promise; | |
} else if (typeof this !== 'undefined') { | |
this['ES6Promise'] = lib$es6$promise$umd$$ES6Promise; | |
} | |
lib$es6$promise$polyfill$$default(); | |
}).call(this); | |
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"_process":4}],6:[function(require,module,exports){ | |
(function (process,global){ | |
/*! | |
* @overview es6-promise - a tiny implementation of Promises/A+. | |
* @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) | |
* @license Licensed under MIT license | |
* See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE | |
* @version 2.0.1 | |
*/ | |
(function() { | |
"use strict"; | |
function $$utils$$objectOrFunction(x) { | |
return typeof x === 'function' || (typeof x === 'object' && x !== null); | |
} | |
function $$utils$$isFunction(x) { | |
return typeof x === 'function'; | |
} | |
function $$utils$$isMaybeThenable(x) { | |
return typeof x === 'object' && x !== null; | |
} | |
var $$utils$$_isArray; | |
if (!Array.isArray) { | |
$$utils$$_isArray = function (x) { | |
return Object.prototype.toString.call(x) === '[object Array]'; | |
}; | |
} else { | |
$$utils$$_isArray = Array.isArray; | |
} | |
var $$utils$$isArray = $$utils$$_isArray; | |
var $$utils$$now = Date.now || function() { return new Date().getTime(); }; | |
function $$utils$$F() { } | |
var $$utils$$o_create = (Object.create || function (o) { | |
if (arguments.length > 1) { | |
throw new Error('Second argument not supported'); | |
} | |
if (typeof o !== 'object') { | |
throw new TypeError('Argument must be an object'); | |
} | |
$$utils$$F.prototype = o; | |
return new $$utils$$F(); | |
}); | |
var $$asap$$len = 0; | |
var $$asap$$default = function asap(callback, arg) { | |
$$asap$$queue[$$asap$$len] = callback; | |
$$asap$$queue[$$asap$$len + 1] = arg; | |
$$asap$$len += 2; | |
if ($$asap$$len === 2) { | |
// If len is 1, that means that we need to schedule an async flush. | |
// If additional callbacks are queued before the queue is flushed, they | |
// will be processed by this flush that we are scheduling. | |
$$asap$$scheduleFlush(); | |
} | |
}; | |
var $$asap$$browserGlobal = (typeof window !== 'undefined') ? window : {}; | |
var $$asap$$BrowserMutationObserver = $$asap$$browserGlobal.MutationObserver || $$asap$$browserGlobal.WebKitMutationObserver; | |
// test for web worker but not in IE10 | |
var $$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && | |
typeof importScripts !== 'undefined' && | |
typeof MessageChannel !== 'undefined'; | |
// node | |
function $$asap$$useNextTick() { | |
return function() { | |
process.nextTick($$asap$$flush); | |
}; | |
} | |
function $$asap$$useMutationObserver() { | |
var iterations = 0; | |
var observer = new $$asap$$BrowserMutationObserver($$asap$$flush); | |
var node = document.createTextNode(''); | |
observer.observe(node, { characterData: true }); | |
return function() { | |
node.data = (iterations = ++iterations % 2); | |
}; | |
} | |
// web worker | |
function $$asap$$useMessageChannel() { | |
var channel = new MessageChannel(); | |
channel.port1.onmessage = $$asap$$flush; | |
return function () { | |
channel.port2.postMessage(0); | |
}; | |
} | |
function $$asap$$useSetTimeout() { | |
return function() { | |
setTimeout($$asap$$flush, 1); | |
}; | |
} | |
var $$asap$$queue = new Array(1000); | |
function $$asap$$flush() { | |
for (var i = 0; i < $$asap$$len; i+=2) { | |
var callback = $$asap$$queue[i]; | |
var arg = $$asap$$queue[i+1]; | |
callback(arg); | |
$$asap$$queue[i] = undefined; | |
$$asap$$queue[i+1] = undefined; | |
} | |
$$asap$$len = 0; | |
} | |
var $$asap$$scheduleFlush; | |
// Decide what async method to use to triggering processing of queued callbacks: | |
if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') { | |
$$asap$$scheduleFlush = $$asap$$useNextTick(); | |
} else if ($$asap$$BrowserMutationObserver) { | |
$$asap$$scheduleFlush = $$asap$$useMutationObserver(); | |
} else if ($$asap$$isWorker) { | |
$$asap$$scheduleFlush = $$asap$$useMessageChannel(); | |
} else { | |
$$asap$$scheduleFlush = $$asap$$useSetTimeout(); | |
} | |
function $$$internal$$noop() {} | |
var $$$internal$$PENDING = void 0; | |
var $$$internal$$FULFILLED = 1; | |
var $$$internal$$REJECTED = 2; | |
var $$$internal$$GET_THEN_ERROR = new $$$internal$$ErrorObject(); | |
function $$$internal$$selfFullfillment() { | |
return new TypeError("You cannot resolve a promise with itself"); | |
} | |
function $$$internal$$cannotReturnOwn() { | |
return new TypeError('A promises callback cannot return that same promise.') | |
} | |
function $$$internal$$getThen(promise) { | |
try { | |
return promise.then; | |
} catch(error) { | |
$$$internal$$GET_THEN_ERROR.error = error; | |
return $$$internal$$GET_THEN_ERROR; | |
} | |
} | |
function $$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { | |
try { | |
then.call(value, fulfillmentHandler, rejectionHandler); | |
} catch(e) { | |
return e; | |
} | |
} | |
function $$$internal$$handleForeignThenable(promise, thenable, then) { | |
$$asap$$default(function(promise) { | |
var sealed = false; | |
var error = $$$internal$$tryThen(then, thenable, function(value) { | |
if (sealed) { return; } | |
sealed = true; | |
if (thenable !== value) { | |
$$$internal$$resolve(promise, value); | |
} else { | |
$$$internal$$fulfill(promise, value); | |
} | |
}, function(reason) { | |
if (sealed) { return; } | |
sealed = true; | |
$$$internal$$reject(promise, reason); | |
}, 'Settle: ' + (promise._label || ' unknown promise')); | |
if (!sealed && error) { | |
sealed = true; | |
$$$internal$$reject(promise, error); | |
} | |
}, promise); | |
} | |
function $$$internal$$handleOwnThenable(promise, thenable) { | |
if (thenable._state === $$$internal$$FULFILLED) { | |
$$$internal$$fulfill(promise, thenable._result); | |
} else if (promise._state === $$$internal$$REJECTED) { | |
$$$internal$$reject(promise, thenable._result); | |
} else { | |
$$$internal$$subscribe(thenable, undefined, function(value) { | |
$$$internal$$resolve(promise, value); | |
}, function(reason) { | |
$$$internal$$reject(promise, reason); | |
}); | |
} | |
} | |
function $$$internal$$handleMaybeThenable(promise, maybeThenable) { | |
if (maybeThenable.constructor === promise.constructor) { | |
$$$internal$$handleOwnThenable(promise, maybeThenable); | |
} else { | |
var then = $$$internal$$getThen(maybeThenable); | |
if (then === $$$internal$$GET_THEN_ERROR) { | |
$$$internal$$reject(promise, $$$internal$$GET_THEN_ERROR.error); | |
} else if (then === undefined) { | |
$$$internal$$fulfill(promise, maybeThenable); | |
} else if ($$utils$$isFunction(then)) { | |
$$$internal$$handleForeignThenable(promise, maybeThenable, then); | |
} else { | |
$$$internal$$fulfill(promise, maybeThenable); | |
} | |
} | |
} | |
function $$$internal$$resolve(promise, value) { | |
if (promise === value) { | |
$$$internal$$reject(promise, $$$internal$$selfFullfillment()); | |
} else if ($$utils$$objectOrFunction(value)) { | |
$$$internal$$handleMaybeThenable(promise, value); | |
} else { | |
$$$internal$$fulfill(promise, value); | |
} | |
} | |
function $$$internal$$publishRejection(promise) { | |
if (promise._onerror) { | |
promise._onerror(promise._result); | |
} | |
$$$internal$$publish(promise); | |
} | |
function $$$internal$$fulfill(promise, value) { | |
if (promise._state !== $$$internal$$PENDING) { return; } | |
promise._result = value; | |
promise._state = $$$internal$$FULFILLED; | |
if (promise._subscribers.length === 0) { | |
} else { | |
$$asap$$default($$$internal$$publish, promise); | |
} | |
} | |
function $$$internal$$reject(promise, reason) { | |
if (promise._state !== $$$internal$$PENDING) { return; } | |
promise._state = $$$internal$$REJECTED; | |
promise._result = reason; | |
$$asap$$default($$$internal$$publishRejection, promise); | |
} | |
function $$$internal$$subscribe(parent, child, onFulfillment, onRejection) { | |
var subscribers = parent._subscribers; | |
var length = subscribers.length; | |
parent._onerror = null; | |
subscribers[length] = child; | |
subscribers[length + $$$internal$$FULFILLED] = onFulfillment; | |
subscribers[length + $$$internal$$REJECTED] = onRejection; | |
if (length === 0 && parent._state) { | |
$$asap$$default($$$internal$$publish, parent); | |
} | |
} | |
function $$$internal$$publish(promise) { | |
var subscribers = promise._subscribers; | |
var settled = promise._state; | |
if (subscribers.length === 0) { return; } | |
var child, callback, detail = promise._result; | |
for (var i = 0; i < subscribers.length; i += 3) { | |
child = subscribers[i]; | |
callback = subscribers[i + settled]; | |
if (child) { | |
$$$internal$$invokeCallback(settled, child, callback, detail); | |
} else { | |
callback(detail); | |
} | |
} | |
promise._subscribers.length = 0; | |
} | |
function $$$internal$$ErrorObject() { | |
this.error = null; | |
} | |
var $$$internal$$TRY_CATCH_ERROR = new $$$internal$$ErrorObject(); | |
function $$$internal$$tryCatch(callback, detail) { | |
try { | |
return callback(detail); | |
} catch(e) { | |
$$$internal$$TRY_CATCH_ERROR.error = e; | |
return $$$internal$$TRY_CATCH_ERROR; | |
} | |
} | |
function $$$internal$$invokeCallback(settled, promise, callback, detail) { | |
var hasCallback = $$utils$$isFunction(callback), | |
value, error, succeeded, failed; | |
if (hasCallback) { | |
value = $$$internal$$tryCatch(callback, detail); | |
if (value === $$$internal$$TRY_CATCH_ERROR) { | |
failed = true; | |
error = value.error; | |
value = null; | |
} else { | |
succeeded = true; | |
} | |
if (promise === value) { | |
$$$internal$$reject(promise, $$$internal$$cannotReturnOwn()); | |
return; | |
} | |
} else { | |
value = detail; | |
succeeded = true; | |
} | |
if (promise._state !== $$$internal$$PENDING) { | |
// noop | |
} else if (hasCallback && succeeded) { | |
$$$internal$$resolve(promise, value); | |
} else if (failed) { | |
$$$internal$$reject(promise, error); | |
} else if (settled === $$$internal$$FULFILLED) { | |
$$$internal$$fulfill(promise, value); | |
} else if (settled === $$$internal$$REJECTED) { | |
$$$internal$$reject(promise, value); | |
} | |
} | |
function $$$internal$$initializePromise(promise, resolver) { | |
try { | |
resolver(function resolvePromise(value){ | |
$$$internal$$resolve(promise, value); | |
}, function rejectPromise(reason) { | |
$$$internal$$reject(promise, reason); | |
}); | |
} catch(e) { | |
$$$internal$$reject(promise, e); | |
} | |
} | |
function $$$enumerator$$makeSettledResult(state, position, value) { | |
if (state === $$$internal$$FULFILLED) { | |
return { | |
state: 'fulfilled', | |
value: value | |
}; | |
} else { | |
return { | |
state: 'rejected', | |
reason: value | |
}; | |
} | |
} | |
function $$$enumerator$$Enumerator(Constructor, input, abortOnReject, label) { | |
this._instanceConstructor = Constructor; | |
this.promise = new Constructor($$$internal$$noop, label); | |
this._abortOnReject = abortOnReject; | |
if (this._validateInput(input)) { | |
this._input = input; | |
this.length = input.length; | |
this._remaining = input.length; | |
this._init(); | |
if (this.length === 0) { | |
$$$internal$$fulfill(this.promise, this._result); | |
} else { | |
this.length = this.length || 0; | |
this._enumerate(); | |
if (this._remaining === 0) { | |
$$$internal$$fulfill(this.promise, this._result); | |
} | |
} | |
} else { | |
$$$internal$$reject(this.promise, this._validationError()); | |
} | |
} | |
$$$enumerator$$Enumerator.prototype._validateInput = function(input) { | |
return $$utils$$isArray(input); | |
}; | |
$$$enumerator$$Enumerator.prototype._validationError = function() { | |
return new Error('Array Methods must be provided an Array'); | |
}; | |
$$$enumerator$$Enumerator.prototype._init = function() { | |
this._result = new Array(this.length); | |
}; | |
var $$$enumerator$$default = $$$enumerator$$Enumerator; | |
$$$enumerator$$Enumerator.prototype._enumerate = function() { | |
var length = this.length; | |
var promise = this.promise; | |
var input = this._input; | |
for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) { | |
this._eachEntry(input[i], i); | |
} | |
}; | |
$$$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { | |
var c = this._instanceConstructor; | |
if ($$utils$$isMaybeThenable(entry)) { | |
if (entry.constructor === c && entry._state !== $$$internal$$PENDING) { | |
entry._onerror = null; | |
this._settledAt(entry._state, i, entry._result); | |
} else { | |
this._willSettleAt(c.resolve(entry), i); | |
} | |
} else { | |
this._remaining--; | |
this._result[i] = this._makeResult($$$internal$$FULFILLED, i, entry); | |
} | |
}; | |
$$$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { | |
var promise = this.promise; | |
if (promise._state === $$$internal$$PENDING) { | |
this._remaining--; | |
if (this._abortOnReject && state === $$$internal$$REJECTED) { | |
$$$internal$$reject(promise, value); | |
} else { | |
this._result[i] = this._makeResult(state, i, value); | |
} | |
} | |
if (this._remaining === 0) { | |
$$$internal$$fulfill(promise, this._result); | |
} | |
}; | |
$$$enumerator$$Enumerator.prototype._makeResult = function(state, i, value) { | |
return value; | |
}; | |
$$$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { | |
var enumerator = this; | |
$$$internal$$subscribe(promise, undefined, function(value) { | |
enumerator._settledAt($$$internal$$FULFILLED, i, value); | |
}, function(reason) { | |
enumerator._settledAt($$$internal$$REJECTED, i, reason); | |
}); | |
}; | |
var $$promise$all$$default = function all(entries, label) { | |
return new $$$enumerator$$default(this, entries, true /* abort on reject */, label).promise; | |
}; | |
var $$promise$race$$default = function race(entries, label) { | |
/*jshint validthis:true */ | |
var Constructor = this; | |
var promise = new Constructor($$$internal$$noop, label); | |
if (!$$utils$$isArray(entries)) { | |
$$$internal$$reject(promise, new TypeError('You must pass an array to race.')); | |
return promise; | |
} | |
var length = entries.length; | |
function onFulfillment(value) { | |
$$$internal$$resolve(promise, value); | |
} | |
function onRejection(reason) { | |
$$$internal$$reject(promise, reason); | |
} | |
for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) { | |
$$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); | |
} | |
return promise; | |
}; | |
var $$promise$resolve$$default = function resolve(object, label) { | |
/*jshint validthis:true */ | |
var Constructor = this; | |
if (object && typeof object === 'object' && object.constructor === Constructor) { | |
return object; | |
} | |
var promise = new Constructor($$$internal$$noop, label); | |
$$$internal$$resolve(promise, object); | |
return promise; | |
}; | |
var $$promise$reject$$default = function reject(reason, label) { | |
/*jshint validthis:true */ | |
var Constructor = this; | |
var promise = new Constructor($$$internal$$noop, label); | |
$$$internal$$reject(promise, reason); | |
return promise; | |
}; | |
var $$es6$promise$promise$$counter = 0; | |
function $$es6$promise$promise$$needsResolver() { | |
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); | |
} | |
function $$es6$promise$promise$$needsNew() { | |
throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); | |
} | |
var $$es6$promise$promise$$default = $$es6$promise$promise$$Promise; | |
/** | |
Promise objects represent the eventual result of an asynchronous operation. The | |
primary way of interacting with a promise is through its `then` method, which | |
registers callbacks to receive either a promise’s eventual value or the reason | |
why the promise cannot be fulfilled. | |
Terminology | |
----------- | |
- `promise` is an object or function with a `then` method whose behavior conforms to this specification. | |
- `thenable` is an object or function that defines a `then` method. | |
- `value` is any legal JavaScript value (including undefined, a thenable, or a promise). | |
- `exception` is a value that is thrown using the throw statement. | |
- `reason` is a value that indicates why a promise was rejected. | |
- `settled` the final resting state of a promise, fulfilled or rejected. | |
A promise can be in one of three states: pending, fulfilled, or rejected. | |
Promises that are fulfilled have a fulfillment value and are in the fulfilled | |
state. Promises that are rejected have a rejection reason and are in the | |
rejected state. A fulfillment value is never a thenable. | |
Promises can also be said to *resolve* a value. If this value is also a | |
promise, then the original promise's settled state will match the value's | |
settled state. So a promise that *resolves* a promise that rejects will | |
itself reject, and a promise that *resolves* a promise that fulfills will | |
itself fulfill. | |
Basic Usage: | |
------------ | |
```js | |
var promise = new Promise(function(resolve, reject) { | |
// on success | |
resolve(value); | |
// on failure | |
reject(reason); | |
}); | |
promise.then(function(value) { | |
// on fulfillment | |
}, function(reason) { | |
// on rejection | |
}); | |
``` | |
Advanced Usage: | |
--------------- | |
Promises shine when abstracting away asynchronous interactions such as | |
`XMLHttpRequest`s. | |
```js | |
function getJSON(url) { | |
return new Promise(function(resolve, reject){ | |
var xhr = new XMLHttpRequest(); | |
xhr.open('GET', url); | |
xhr.onreadystatechange = handler; | |
xhr.responseType = 'json'; | |
xhr.setRequestHeader('Accept', 'application/json'); | |
xhr.send(); | |
function handler() { | |
if (this.readyState === this.DONE) { | |
if (this.status === 200) { | |
resolve(this.response); | |
} else { | |
reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); | |
} | |
} | |
}; | |
}); | |
} | |
getJSON('/posts.json').then(function(json) { | |
// on fulfillment | |
}, function(reason) { | |
// on rejection | |
}); | |
``` | |
Unlike callbacks, promises are great composable primitives. | |
```js | |
Promise.all([ | |
getJSON('/posts'), | |
getJSON('/comments') | |
]).then(function(values){ | |
values[0] // => postsJSON | |
values[1] // => commentsJSON | |
return values; | |
}); | |
``` | |
@class Promise | |
@param {function} resolver | |
Useful for tooling. | |
@constructor | |
*/ | |
function $$es6$promise$promise$$Promise(resolver) { | |
this._id = $$es6$promise$promise$$counter++; | |
this._state = undefined; | |
this._result = undefined; | |
this._subscribers = []; | |
if ($$$internal$$noop !== resolver) { | |
if (!$$utils$$isFunction(resolver)) { | |
$$es6$promise$promise$$needsResolver(); | |
} | |
if (!(this instanceof $$es6$promise$promise$$Promise)) { | |
$$es6$promise$promise$$needsNew(); | |
} | |
$$$internal$$initializePromise(this, resolver); | |
} | |
} | |
$$es6$promise$promise$$Promise.all = $$promise$all$$default; | |
$$es6$promise$promise$$Promise.race = $$promise$race$$default; | |
$$es6$promise$promise$$Promise.resolve = $$promise$resolve$$default; | |
$$es6$promise$promise$$Promise.reject = $$promise$reject$$default; | |
$$es6$promise$promise$$Promise.prototype = { | |
constructor: $$es6$promise$promise$$Promise, | |
/** | |
The primary way of interacting with a promise is through its `then` method, | |
which registers callbacks to receive either a promise's eventual value or the | |
reason why the promise cannot be fulfilled. | |
```js | |
findUser().then(function(user){ | |
// user is available | |
}, function(reason){ | |
// user is unavailable, and you are given the reason why | |
}); | |
``` | |
Chaining | |
-------- | |
The return value of `then` is itself a promise. This second, 'downstream' | |
promise is resolved with the return value of the first promise's fulfillment | |
or rejection handler, or rejected if the handler throws an exception. | |
```js | |
findUser().then(function (user) { | |
return user.name; | |
}, function (reason) { | |
return 'default name'; | |
}).then(function (userName) { | |
// If `findUser` fulfilled, `userName` will be the user's name, otherwise it | |
// will be `'default name'` | |
}); | |
findUser().then(function (user) { | |
throw new Error('Found user, but still unhappy'); | |
}, function (reason) { | |
throw new Error('`findUser` rejected and we're unhappy'); | |
}).then(function (value) { | |
// never reached | |
}, function (reason) { | |
// if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. | |
// If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. | |
}); | |
``` | |
If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. | |
```js | |
findUser().then(function (user) { | |
throw new PedagogicalException('Upstream error'); | |
}).then(function (value) { | |
// never reached | |
}).then(function (value) { | |
// never reached | |
}, function (reason) { | |
// The `PedgagocialException` is propagated all the way down to here | |
}); | |
``` | |
Assimilation | |
------------ | |
Sometimes the value you want to propagate to a downstream promise can only be | |
retrieved asynchronously. This can be achieved by returning a promise in the | |
fulfillment or rejection handler. The downstream promise will then be pending | |
until the returned promise is settled. This is called *assimilation*. | |
```js | |
findUser().then(function (user) { | |
return findCommentsByAuthor(user); | |
}).then(function (comments) { | |
// The user's comments are now available | |
}); | |
``` | |
If the assimliated promise rejects, then the downstream promise will also reject. | |
```js | |
findUser().then(function (user) { | |
return findCommentsByAuthor(user); | |
}).then(function (comments) { | |
// If `findCommentsByAuthor` fulfills, we'll have the value here | |
}, function (reason) { | |
// If `findCommentsByAuthor` rejects, we'll have the reason here | |
}); | |
``` | |
Simple Example | |
-------------- | |
Synchronous Example | |
```javascript | |
var result; | |
try { | |
result = findResult(); | |
// success | |
} catch(reason) { | |
// failure | |
} | |
``` | |
Errback Example | |
```js | |
findResult(function(result, err){ | |
if (err) { | |
// failure | |
} else { | |
// success | |
} | |
}); | |
``` | |
Promise Example; | |
```javascript | |
findResult().then(function(result){ | |
// success | |
}, function(reason){ | |
// failure | |
}); | |
``` | |
Advanced Example | |
-------------- | |
Synchronous Example | |
```javascript | |
var author, books; | |
try { | |
author = findAuthor(); | |
books = findBooksByAuthor(author); | |
// success | |
} catch(reason) { | |
// failure | |
} | |
``` | |
Errback Example | |
```js | |
function foundBooks(books) { | |
} | |
function failure(reason) { | |
} | |
findAuthor(function(author, err){ | |
if (err) { | |
failure(err); | |
// failure | |
} else { | |
try { | |
findBoooksByAuthor(author, function(books, err) { | |
if (err) { | |
failure(err); | |
} else { | |
try { | |
foundBooks(books); | |
} catch(reason) { | |
failure(reason); | |
} | |
} | |
}); | |
} catch(error) { | |
failure(err); | |
} | |
// success | |
} | |
}); | |
``` | |
Promise Example; | |
```javascript | |
findAuthor(). | |
then(findBooksByAuthor). | |
then(function(books){ | |
// found books | |
}).catch(function(reason){ | |
// something went wrong | |
}); | |
``` | |
@method then | |
@param {Function} onFulfilled | |
@param {Function} onRejected | |
Useful for tooling. | |
@return {Promise} | |
*/ | |
then: function(onFulfillment, onRejection) { | |
var parent = this; | |
var state = parent._state; | |
if (state === $$$internal$$FULFILLED && !onFulfillment || state === $$$internal$$REJECTED && !onRejection) { | |
return this; | |
} | |
var child = new this.constructor($$$internal$$noop); | |
var result = parent._result; | |
if (state) { | |
var callback = arguments[state - 1]; | |
$$asap$$default(function(){ | |
$$$internal$$invokeCallback(state, child, callback, result); | |
}); | |
} else { | |
$$$internal$$subscribe(parent, child, onFulfillment, onRejection); | |
} | |
return child; | |
}, | |
/** | |
`catch` is simply sugar for `then(undefined, onRejection)` which makes it the same | |
as the catch block of a try/catch statement. | |
```js | |
function findAuthor(){ | |
throw new Error('couldn't find that author'); | |
} | |
// synchronous | |
try { | |
findAuthor(); | |
} catch(reason) { | |
// something went wrong | |
} | |
// async with promises | |
findAuthor().catch(function(reason){ | |
// something went wrong | |
}); | |
``` | |
@method catch | |
@param {Function} onRejection | |
Useful for tooling. | |
@return {Promise} | |
*/ | |
'catch': function(onRejection) { | |
return this.then(null, onRejection); | |
} | |
}; | |
var $$es6$promise$polyfill$$default = function polyfill() { | |
var local; | |
if (typeof global !== 'undefined') { | |
local = global; | |
} else if (typeof window !== 'undefined' && window.document) { | |
local = window; | |
} else { | |
local = self; | |
} | |
var es6PromiseSupport = | |
"Promise" in local && | |
// Some of these methods are missing from | |
// Firefox/Chrome experimental implementations | |
"resolve" in local.Promise && | |
"reject" in local.Promise && | |
"all" in local.Promise && | |
"race" in local.Promise && | |
// Older version of the spec had a resolver object | |
// as the arg rather than a function | |
(function() { | |
var resolve; | |
new local.Promise(function(r) { resolve = r; }); | |
return $$utils$$isFunction(resolve); | |
}()); | |
if (!es6PromiseSupport) { | |
local.Promise = $$es6$promise$promise$$default; | |
} | |
}; | |
var es6$promise$umd$$ES6Promise = { | |
'Promise': $$es6$promise$promise$$default, | |
'polyfill': $$es6$promise$polyfill$$default | |
}; | |
/* global define:true module:true window: true */ | |
if (typeof define === 'function' && define['amd']) { | |
define(function() { return es6$promise$umd$$ES6Promise; }); | |
} else if (typeof module !== 'undefined' && module['exports']) { | |
module['exports'] = es6$promise$umd$$ES6Promise; | |
} else if (typeof this !== 'undefined') { | |
this['ES6Promise'] = es6$promise$umd$$ES6Promise; | |
} | |
}).call(this); | |
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"_process":4}],7:[function(require,module,exports){ | |
exports = module.exports = stringify | |
exports.getSerialize = serializer | |
function stringify(obj, replacer, spaces, cycleReplacer) { | |
return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces) | |
} | |
function serializer(replacer, cycleReplacer) { | |
var stack = [], keys = [] | |
if (cycleReplacer == null) cycleReplacer = function(key, value) { | |
if (stack[0] === value) return "[Circular ~]" | |
return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]" | |
} | |
return function(key, value) { | |
if (stack.length > 0) { | |
var thisPos = stack.indexOf(this) | |
~thisPos ? stack.splice(thisPos + 1) : stack.push(this) | |
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key) | |
if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value) | |
} | |
else stack.push(value) | |
return replacer == null ? value : replacer.call(this, key, value) | |
} | |
} | |
},{}],8:[function(require,module,exports){ | |
// Domain Public by Eric Wendelin http://www.eriwen.com/ (2008) | |
// Luke Smith http://lucassmith.name/ (2008) | |
// Loic Dachary <[email protected]> (2008) | |
// Johan Euphrosine <[email protected]> (2008) | |
// Oyvind Sean Kinsey http://kinsey.no/blog (2010) | |
// Victor Homyakov <[email protected]> (2010) | |
/*global module, exports, define, ActiveXObject*/ | |
(function(global, factory) { | |
if (typeof exports === 'object') { | |
// Node | |
module.exports = factory(); | |
} else if (typeof define === 'function' && define.amd) { | |
// AMD | |
define(factory); | |
} else { | |
// Browser globals | |
global.printStackTrace = factory(); | |
} | |
}(this, function() { | |
/** | |
* Main function giving a function stack trace with a forced or passed in Error | |
* | |
* @cfg {Error} e The error to create a stacktrace from (optional) | |
* @cfg {Boolean} guess If we should try to resolve the names of anonymous functions | |
* @return {Array} of Strings with functions, lines, files, and arguments where possible | |
*/ | |
function printStackTrace(options) { | |
options = options || {guess: true}; | |
var ex = options.e || null, guess = !!options.guess, mode = options.mode || null; | |
var p = new printStackTrace.implementation(), result = p.run(ex, mode); | |
return (guess) ? p.guessAnonymousFunctions(result) : result; | |
} | |
printStackTrace.implementation = function() { | |
}; | |
printStackTrace.implementation.prototype = { | |
/** | |
* @param {Error} [ex] The error to create a stacktrace from (optional) | |
* @param {String} [mode] Forced mode (optional, mostly for unit tests) | |
*/ | |
run: function(ex, mode) { | |
ex = ex || this.createException(); | |
mode = mode || this.mode(ex); | |
if (mode === 'other') { | |
return this.other(arguments.callee); | |
} else { | |
return this[mode](ex); | |
} | |
}, | |
createException: function() { | |
try { | |
this.undef(); | |
} catch (e) { | |
return e; | |
} | |
}, | |
/** | |
* Mode could differ for different exception, e.g. | |
* exceptions in Chrome may or may not have arguments or stack. | |
* | |
* @return {String} mode of operation for the exception | |
*/ | |
mode: function(e) { | |
if (typeof window !== 'undefined' && window.navigator.userAgent.indexOf('PhantomJS') > -1) { | |
return 'phantomjs'; | |
} | |
if (e['arguments'] && e.stack) { | |
return 'chrome'; | |
} | |
if (e.stack && e.sourceURL) { | |
return 'safari'; | |
} | |
if (e.stack && e.number) { | |
return 'ie'; | |
} | |
if (e.stack && e.fileName) { | |
return 'firefox'; | |
} | |
if (e.message && e['opera#sourceloc']) { | |
// e.message.indexOf("Backtrace:") > -1 -> opera9 | |
// 'opera#sourceloc' in e -> opera9, opera10a | |
// !e.stacktrace -> opera9 | |
if (!e.stacktrace) { | |
return 'opera9'; // use e.message | |
} | |
if (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length) { | |
// e.message may have more stack entries than e.stacktrace | |
return 'opera9'; // use e.message | |
} | |
return 'opera10a'; // use e.stacktrace | |
} | |
if (e.message && e.stack && e.stacktrace) { | |
// e.stacktrace && e.stack -> opera10b | |
if (e.stacktrace.indexOf("called from line") < 0) { | |
return 'opera10b'; // use e.stacktrace, format differs from 'opera10a' | |
} | |
// e.stacktrace && e.stack -> opera11 | |
return 'opera11'; // use e.stacktrace, format differs from 'opera10a', 'opera10b' | |
} | |
if (e.stack && !e.fileName) { | |
// Chrome 27 does not have e.arguments as earlier versions, | |
// but still does not have e.fileName as Firefox | |
return 'chrome'; | |
} | |
return 'other'; | |
}, | |
/** | |
* Given a context, function name, and callback function, overwrite it so that it calls | |
* printStackTrace() first with a callback and then runs the rest of the body. | |
* | |
* @param {Object} context of execution (e.g. window) | |
* @param {String} functionName to instrument | |
* @param {Function} callback function to call with a stack trace on invocation | |
*/ | |
instrumentFunction: function(context, functionName, callback) { | |
context = context || window; | |
var original = context[functionName]; | |
context[functionName] = function instrumented() { | |
callback.call(this, printStackTrace().slice(4)); | |
return context[functionName]._instrumented.apply(this, arguments); | |
}; | |
context[functionName]._instrumented = original; | |
}, | |
/** | |
* Given a context and function name of a function that has been | |
* instrumented, revert the function to it's original (non-instrumented) | |
* state. | |
* | |
* @param {Object} context of execution (e.g. window) | |
* @param {String} functionName to de-instrument | |
*/ | |
deinstrumentFunction: function(context, functionName) { | |
if (context[functionName].constructor === Function && | |
context[functionName]._instrumented && | |
context[functionName]._instrumented.constructor === Function) { | |
context[functionName] = context[functionName]._instrumented; | |
} | |
}, | |
/** | |
* Given an Error object, return a formatted Array based on Chrome's stack string. | |
* | |
* @param e - Error object to inspect | |
* @return Array<String> of function calls, files and line numbers | |
*/ | |
chrome: function(e) { | |
return (e.stack + '\n') | |
.replace(/^[\s\S]+?\s+at\s+/, ' at ') // remove message | |
.replace(/^\s+(at eval )?at\s+/gm, '') // remove 'at' and indentation | |
.replace(/^([^\(]+?)([\n$])/gm, '{anonymous}() ($1)$2') | |
.replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}() ($1)') | |
.replace(/^(.+) \((.+)\)$/gm, '$1@$2') | |
.split('\n') | |
.slice(0, -1); | |
}, | |
/** | |
* Given an Error object, return a formatted Array based on Safari's stack string. | |
* | |
* @param e - Error object to inspect | |
* @return Array<String> of function calls, files and line numbers | |
*/ | |
safari: function(e) { | |
return e.stack.replace(/\[native code\]\n/m, '') | |
.replace(/^(?=\w+Error\:).*$\n/m, '') | |
.replace(/^@/gm, '{anonymous}()@') | |
.split('\n'); | |
}, | |
/** | |
* Given an Error object, return a formatted Array based on IE's stack string. | |
* | |
* @param e - Error object to inspect | |
* @return Array<String> of function calls, files and line numbers | |
*/ | |
ie: function(e) { | |
return e.stack | |
.replace(/^\s*at\s+(.*)$/gm, '$1') | |
.replace(/^Anonymous function\s+/gm, '{anonymous}() ') | |
.replace(/^(.+)\s+\((.+)\)$/gm, '$1@$2') | |
.split('\n') | |
.slice(1); | |
}, | |
/** | |
* Given an Error object, return a formatted Array based on Firefox's stack string. | |
* | |
* @param e - Error object to inspect | |
* @return Array<String> of function calls, files and line numbers | |
*/ | |
firefox: function(e) { | |
return e.stack.replace(/(?:\n@:0)?\s+$/m, '') | |
.replace(/^(?:\((\S*)\))?@/gm, '{anonymous}($1)@') | |
.split('\n'); | |
}, | |
opera11: function(e) { | |
var ANON = '{anonymous}', lineRE = /^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$/; | |
var lines = e.stacktrace.split('\n'), result = []; | |
for (var i = 0, len = lines.length; i < len; i += 2) { | |
var match = lineRE.exec(lines[i]); | |
if (match) { | |
var location = match[4] + ':' + match[1] + ':' + match[2]; | |
var fnName = match[3] || "global code"; | |
fnName = fnName.replace(/<anonymous function: (\S+)>/, "$1").replace(/<anonymous function>/, ANON); | |
result.push(fnName + '@' + location + ' -- ' + lines[i + 1].replace(/^\s+/, '')); | |
} | |
} | |
return result; | |
}, | |
opera10b: function(e) { | |
// "<anonymous function: run>([arguments not available])@file://localhost/G:/js/stacktrace.js:27\n" + | |
// "printStackTrace([arguments not available])@file://localhost/G:/js/stacktrace.js:18\n" + | |
// "@file://localhost/G:/js/test/functional/testcase1.html:15" | |
var lineRE = /^(.*)@(.+):(\d+)$/; | |
var lines = e.stacktrace.split('\n'), result = []; | |
for (var i = 0, len = lines.length; i < len; i++) { | |
var match = lineRE.exec(lines[i]); | |
if (match) { | |
var fnName = match[1] ? (match[1] + '()') : "global code"; | |
result.push(fnName + '@' + match[2] + ':' + match[3]); | |
} | |
} | |
return result; | |
}, | |
/** | |
* Given an Error object, return a formatted Array based on Opera 10's stacktrace string. | |
* | |
* @param e - Error object to inspect | |
* @return Array<String> of function calls, files and line numbers | |
*/ | |
opera10a: function(e) { | |
// " Line 27 of linked script file://localhost/G:/js/stacktrace.js\n" | |
// " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function foo\n" | |
var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i; | |
var lines = e.stacktrace.split('\n'), result = []; | |
for (var i = 0, len = lines.length; i < len; i += 2) { | |
var match = lineRE.exec(lines[i]); | |
if (match) { | |
var fnName = match[3] || ANON; | |
result.push(fnName + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, '')); | |
} | |
} | |
return result; | |
}, | |
// Opera 7.x-9.2x only! | |
opera9: function(e) { | |
// " Line 43 of linked script file://localhost/G:/js/stacktrace.js\n" | |
// " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" | |
var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)/i; | |
var lines = e.message.split('\n'), result = []; | |
for (var i = 2, len = lines.length; i < len; i += 2) { | |
var match = lineRE.exec(lines[i]); | |
if (match) { | |
result.push(ANON + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, '')); | |
} | |
} | |
return result; | |
}, | |
phantomjs: function(e) { | |
var ANON = '{anonymous}', lineRE = /(\S+) \((\S+)\)/i; | |
var lines = e.stack.split('\n'), result = []; | |
for (var i = 1, len = lines.length; i < len; i++) { | |
lines[i] = lines[i].replace(/^\s+at\s+/gm, ''); | |
var match = lineRE.exec(lines[i]); | |
if (match) { | |
result.push(match[1] + '()@' + match[2]); | |
} | |
else { | |
result.push(ANON + '()@' + lines[i]); | |
} | |
} | |
return result; | |
}, | |
// Safari 5-, IE 9-, and others | |
other: function(curr) { | |
var ANON = '{anonymous}', fnRE = /function(?:\s+([\w$]+))?\s*\(/, stack = [], fn, args, maxStackSize = 10; | |
var slice = Array.prototype.slice; | |
while (curr && stack.length < maxStackSize) { | |
fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON; | |
try { | |
args = slice.call(curr['arguments'] || []); | |
} catch (e) { | |
args = ['Cannot access arguments: ' + e]; | |
} | |
stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')'; | |
try { | |
curr = curr.caller; | |
} catch (e) { | |
stack[stack.length] = 'Cannot access caller: ' + e; | |
break; | |
} | |
} | |
return stack; | |
}, | |
/** | |
* Given arguments array as a String, substituting type names for non-string types. | |
* | |
* @param {Arguments,Array} args | |
* @return {String} stringified arguments | |
*/ | |
stringifyArguments: function(args) { | |
var result = []; | |
var slice = Array.prototype.slice; | |
for (var i = 0; i < args.length; ++i) { | |
var arg = args[i]; | |
if (arg === undefined) { | |
result[i] = 'undefined'; | |
} else if (arg === null) { | |
result[i] = 'null'; | |
} else if (arg.constructor) { | |
// TODO constructor comparison does not work for iframes | |
if (arg.constructor === Array) { | |
if (arg.length < 3) { | |
result[i] = '[' + this.stringifyArguments(arg) + ']'; | |
} else { | |
result[i] = '[' + this.stringifyArguments(slice.call(arg, 0, 1)) + '...' + this.stringifyArguments(slice.call(arg, -1)) + ']'; | |
} | |
} else if (arg.constructor === Object) { | |
result[i] = '#object'; | |
} else if (arg.constructor === Function) { | |
result[i] = '#function'; | |
} else if (arg.constructor === String) { | |
result[i] = '"' + arg + '"'; | |
} else if (arg.constructor === Number) { | |
result[i] = arg; | |
} else { | |
result[i] = '?'; | |
} | |
} | |
} | |
return result.join(','); | |
}, | |
sourceCache: {}, | |
/** | |
* @return {String} the text from a given URL | |
*/ | |
ajax: function(url) { | |
var req = this.createXMLHTTPObject(); | |
if (req) { | |
try { | |
req.open('GET', url, false); | |
//req.overrideMimeType('text/plain'); | |
//req.overrideMimeType('text/javascript'); | |
req.send(null); | |
//return req.status == 200 ? req.responseText : ''; | |
return req.responseText; | |
} catch (e) { | |
} | |
} | |
return ''; | |
}, | |
/** | |
* Try XHR methods in order and store XHR factory. | |
* | |
* @return {XMLHttpRequest} XHR function or equivalent | |
*/ | |
createXMLHTTPObject: function() { | |
var xmlhttp, XMLHttpFactories = [ | |
function() { | |
return new XMLHttpRequest(); | |
}, function() { | |
return new ActiveXObject('Msxml2.XMLHTTP'); | |
}, function() { | |
return new ActiveXObject('Msxml3.XMLHTTP'); | |
}, function() { | |
return new ActiveXObject('Microsoft.XMLHTTP'); | |
} | |
]; | |
for (var i = 0; i < XMLHttpFactories.length; i++) { | |
try { | |
xmlhttp = XMLHttpFactories[i](); | |
// Use memoization to cache the factory | |
this.createXMLHTTPObject = XMLHttpFactories[i]; | |
return xmlhttp; | |
} catch (e) { | |
} | |
} | |
}, | |
/** | |
* Given a URL, check if it is in the same domain (so we can get the source | |
* via Ajax). | |
* | |
* @param url {String} source url | |
* @return {Boolean} False if we need a cross-domain request | |
*/ | |
isSameDomain: function(url) { | |
return typeof location !== "undefined" && url.indexOf(location.hostname) !== -1; // location may not be defined, e.g. when running from nodejs. | |
}, | |
/** | |
* Get source code from given URL if in the same domain. | |
* | |
* @param url {String} JS source URL | |
* @return {Array} Array of source code lines | |
*/ | |
getSource: function(url) { | |
// TODO reuse source from script tags? | |
if (!(url in this.sourceCache)) { | |
this.sourceCache[url] = this.ajax(url).split('\n'); | |
} | |
return this.sourceCache[url]; | |
}, | |
guessAnonymousFunctions: function(stack) { | |
for (var i = 0; i < stack.length; ++i) { | |
var reStack = /\{anonymous\}\(.*\)@(.*)/, | |
reRef = /^(.*?)(?::(\d+))(?::(\d+))?(?: -- .+)?$/, | |
frame = stack[i], ref = reStack.exec(frame); | |
if (ref) { | |
var m = reRef.exec(ref[1]); | |
if (m) { // If falsey, we did not get any file/line information | |
var file = m[1], lineno = m[2], charno = m[3] || 0; | |
if (file && this.isSameDomain(file) && lineno) { | |
var functionName = this.guessAnonymousFunction(file, lineno, charno); | |
stack[i] = frame.replace('{anonymous}', functionName); | |
} | |
} | |
} | |
} | |
return stack; | |
}, | |
guessAnonymousFunction: function(url, lineNo, charNo) { | |
var ret; | |
try { | |
ret = this.findFunctionName(this.getSource(url), lineNo); | |
} catch (e) { | |
ret = 'getSource failed with url: ' + url + ', exception: ' + e.toString(); | |
} | |
return ret; | |
}, | |
findFunctionName: function(source, lineNo) { | |
// FIXME findFunctionName fails for compressed source | |
// (more than one function on the same line) | |
// function {name}({args}) m[1]=name m[2]=args | |
var reFunctionDeclaration = /function\s+([^(]*?)\s*\(([^)]*)\)/; | |
// {name} = function ({args}) TODO args capture | |
// /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*function(?:[^(]*)/ | |
var reFunctionExpression = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/; | |
// {name} = eval() | |
var reFunctionEvaluation = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/; | |
// Walk backwards in the source lines until we find | |
// the line which matches one of the patterns above | |
var code = "", line, maxLines = Math.min(lineNo, 20), m, commentPos; | |
for (var i = 0; i < maxLines; ++i) { | |
// lineNo is 1-based, source[] is 0-based | |
line = source[lineNo - i - 1]; | |
commentPos = line.indexOf('//'); | |
if (commentPos >= 0) { | |
line = line.substr(0, commentPos); | |
} | |
// TODO check other types of comments? Commented code may lead to false positive | |
if (line) { | |
code = line + code; | |
m = reFunctionExpression.exec(code); | |
if (m && m[1]) { | |
return m[1]; | |
} | |
m = reFunctionDeclaration.exec(code); | |
if (m && m[1]) { | |
//return m[1] + "(" + (m[2] || "") + ")"; | |
return m[1]; | |
} | |
m = reFunctionEvaluation.exec(code); | |
if (m && m[1]) { | |
return m[1]; | |
} | |
} | |
} | |
return '(?)'; | |
} | |
}; | |
return printStackTrace; | |
})); | |
},{}],9:[function(require,module,exports){ | |
var Promise = Promise || require('es6-promise').Promise; | |
var stackTrace = require('stacktrace-js'); | |
var serialize = require('json-stringify-safe'); | |
var TransactionController = require('./TransactionController'); | |
function augmentContext(context, property, value){ | |
Object.defineProperty(context, property, { | |
value: value, | |
writable: false, | |
enumerable: false, | |
configurable: true | |
}); | |
} | |
function PromisePipeFactory(){ | |
/** | |
* cleanup PromisePipe call ID/and env at the Pipe end | |
*/ | |
function cleanup(data, context){ | |
delete context._pipecallId; | |
delete context._env; | |
return data; | |
} | |
/** | |
* PromisePipe chain constructor | |
* @param {Array} sequence Sequence of chain functions | |
*/ | |
function PromisePipe(options, sequence){ | |
if(Array.isArray(options)){ | |
sequence = options; | |
} | |
sequence || (sequence = []); | |
function result(data, context){ | |
context || (context = {}); | |
// set Random PromisePipe call ID | |
augmentContext(context, '_pipecallId', Math.ceil(Math.random() * Math.pow(10, 16))); | |
// set current PromisePipe env | |
augmentContext(context, '_env', PromisePipe.env); | |
var _trace = {}; | |
_trace[context._pipecallId] = []; | |
augmentContext(context, '_trace', _trace); | |
var toConcat = [sequence]; | |
if(PromisePipe._mode === 'DEBUG') { | |
var debugChain = { | |
func: printDebug, | |
_id: ID(), | |
_env: PromisePipe.env | |
}; | |
toConcat.push(debugChain); | |
} | |
var cleanupChain = { | |
func: cleanup, | |
_id: ID(), | |
_env: PromisePipe.env | |
}; | |
toConcat.push(cleanupChain); | |
var chain = [].concat.apply([], toConcat); | |
chain = chain.map(bindTo(context).bindIt.bind(result)).map(function(fn){ | |
if(!fn._env) { | |
fn._env = PromisePipe.env; | |
} | |
return fn; | |
}); | |
// run the chain | |
return doit(chain, data, result, context); | |
} | |
function printDebug(data, context){ | |
var ln = context._trace[context._pipecallId].length; | |
printDebugChain(context._trace[context._pipecallId].slice(0, ln - 1)); | |
return data; | |
} | |
function printDebugChain(traceLog){ | |
var seqIds = sequence.map(function(fn){ | |
return fn._id; | |
}); | |
function showLevel(i, traceLog){ | |
if(!traceLog[i]) return console.log("Tracelog is empty"); | |
var item = traceLog[i]; | |
var fnId = seqIds.indexOf(item.chainId); | |
var name = ''; | |
if (!!~fnId) { | |
name = sequence[fnId].name || sequence[fnId]._name; | |
} | |
console.group('.then(' + name + ')[' + item.env + ']'); | |
console.log('data', item.data && JSON.parse(item.data)); | |
console.log('context', JSON.parse(item.context)); | |
if(traceLog[i + 1]) { | |
showLevel(i + 1, traceLog); | |
} | |
console.groupEnd('.then(' + name + ')'); | |
} | |
if(console.group){ | |
showLevel(0, traceLog); | |
} else { | |
traceLog.forEach(function(item, i){ | |
var shift = new Array(i * 4 + 1).join(''); | |
var fnId = seqIds.indexOf(item.chainId); | |
var name = ''; | |
if(!!~fnId) { | |
name = sequence[fnId].name; | |
} | |
console.log(shift + ".then(" + name + ")[" + item.env + "]"); | |
console.log(shift + " data : " + JSON.stringify(item.data)); | |
console.log(shift + " context : " + item.context); | |
return result; | |
}); | |
} | |
} | |
//promise pipe ID | |
result._id = ID(); | |
PromisePipe.pipes[result._id] = { | |
id: result._id, | |
seq: sequence, | |
name: options && options.name, | |
description: options && options.description, | |
Pipe: result | |
} | |
// add function to the chain of a pipe | |
result.then = function(fn){ | |
var chain = { | |
func: fn, | |
_id: ID(), | |
name: fn.name, | |
_env: fn._env | |
}; | |
sequence.push(chain); | |
return result; | |
}; | |
// add catch to the chain of a pipe | |
result.catch = function(fn){ | |
var chain = { | |
func: fn, | |
_id: ID(), | |
isCatch: true, | |
name: fn.name, | |
_env: fn._env | |
}; | |
sequence.push(chain); | |
return result; | |
}; | |
// join pipes | |
result.join = function(){ | |
var sequences = [].map.call(arguments, function(pipe){ | |
return pipe._getSequence(); | |
}); | |
var newSequence = sequence.concat.apply(sequence, sequences); | |
return PromisePipe(newSequence); | |
}; | |
// get an array of pipes | |
result._getSequence = function(){ | |
return sequence; | |
}; | |
// add API extensions for the promisepipe | |
result = Object.keys(PromisePipe.transformations).reduce(function(thePipe, name){ | |
var customApi = PromisePipe.transformations[name]; | |
customApi._name = name; | |
if(typeof customApi === 'object'){ | |
thePipe[name] = wrapObjectPromise(customApi, sequence, result); | |
} else { | |
thePipe[name] = wrapPromise(customApi, sequence, result); | |
} | |
return thePipe; | |
}, result); | |
return result; | |
} | |
function wrapObjectPromise(customApi, sequence, result){ | |
return Object.keys(customApi).reduce(function(api, apiname){ | |
if(apiname.charAt(0) === "_") return api; | |
customApi[apiname]._env = customApi._env; | |
customApi[apiname]._name = customApi._name +"."+ apiname; | |
if(typeof customApi[apiname] === 'object') { | |
api[apiname] = wrapObjectPromise(customApi[apiname], sequence, result); | |
} else { | |
api[apiname] = wrapPromise(customApi[apiname], sequence, result); | |
} | |
return api; | |
}, {}); | |
} | |
function wrapPromise(transObject, sequence, result){ | |
return function(){ | |
var args = [].slice.call(arguments); | |
//TODO: try to use .bind here | |
var wrappedFunction = function(data, context){ | |
var argumentsToPassInside = [data, context].concat(args); | |
return transObject.apply(result, argumentsToPassInside); | |
}; | |
var chain = { | |
func: wrappedFunction, | |
_id: ID(), | |
name: transObject._name, | |
_env: transObject._env, | |
isCatch: transObject.isCatch | |
}; | |
sequence.push(chain); | |
return result; | |
}; | |
} | |
// PromisePipe is a singleton | |
// that knows about all pipes and you can get a pipe by ID's | |
PromisePipe.pipes = {}; | |
PromisePipe._mode = 'PROD'; | |
/** | |
* DEBUG/TEST/PROD | |
* | |
*/ | |
PromisePipe.setMode = function(mode){ | |
PromisePipe._mode = mode; | |
}; | |
/* | |
* setting up env for pipe | |
*/ | |
PromisePipe.setEnv = function(env){ | |
PromisePipe.env = env; | |
}; | |
// the ENV is a client by default | |
PromisePipe.setEnv('client'); | |
/* | |
* Is setting up function to be executed inside specific ENV | |
* usage: | |
* var doOnServer = PromisePipe.in('server'); | |
* PromisePipe().then(doOnServer(fn)); | |
* or | |
* PromisePipe().then(PromisePipe.in('worker').do(fn)); | |
*/ | |
PromisePipe.in = function(env){ | |
if(!env) throw new Error('You should explicitly specify env'); | |
var result = function makeEnv(fn){ | |
var ret = fn.bind(null); | |
ret._env = env; | |
return ret; | |
}; | |
result.do = function doIn(fn){ | |
var ret = fn.bind(null); | |
ret._env = env; | |
return ret; | |
}; | |
return result; | |
}; | |
PromisePipe.envTransitions = {}; | |
// Inside transition you describe how to send message from one | |
// env to another within a Pipe call | |
PromisePipe.envTransition = function(from, to, transition){ | |
if(!PromisePipe.envTransitions[from]) { | |
PromisePipe.envTransitions[from] = {}; | |
} | |
PromisePipe.envTransitions[from][to] = transition; | |
}; | |
//env transformations | |
PromisePipe.envContextTransformations = function(from, to, transformation){ | |
if(!PromisePipe.contextTransformations[from]) { | |
PromisePipe.contextTransformations[from] = {}; | |
} | |
PromisePipe.contextTransformations[from][to] = transformation; | |
}; | |
PromisePipe.transformations = {}; | |
// You can extend PromisePipe API with extensions | |
PromisePipe.use = function(name, transformation, options){ | |
options || (options = {}); | |
transformation = transformation || function(){} | |
if(!options._env) { | |
options._env = PromisePipe.env; | |
} | |
PromisePipe.transformations[name] = transformation; | |
Object.keys(options).forEach(function(optname){ | |
PromisePipe.transformations[name][optname] = options[optname]; | |
}); | |
}; | |
// when you pass Message to another env, you have to wait | |
// until it will come back | |
// messageResolvers save the call and resoves it when message came back | |
PromisePipe.messageResolvers = {}; | |
//TODO: cover by tests | |
PromisePipe.stream = function(from, to, processor){ | |
return { | |
connector: function(strm){ | |
//set transition | |
PromisePipe.envTransition(from, to, function(message){ | |
strm.send(message); | |
return PromisePipe.promiseMessage(message); | |
}); | |
strm.listen(function(message){ | |
var context = message.context; | |
var data = message.data; | |
function end(data){ | |
message.context = context; | |
message.data = data; | |
strm.send(message); | |
} | |
if(processor){ | |
function executor(data, context){ | |
message.data = data; | |
message.context = context; | |
return PromisePipe.execTransitionMessage(message); | |
} | |
var localContext = {}; | |
localContext.__proto__= context; | |
return processor(data, localContext, executor, end); | |
} | |
return PromisePipe.execTransitionMessage(message).then(end); | |
}); | |
} | |
}; | |
}; | |
var TransactionHandler = TransactionController(); | |
PromisePipe.promiseMessage = function(message){ | |
return new Promise(function(resolve, reject){ | |
PromisePipe.messageResolvers[message.call] = { | |
resolve: resolve, | |
reject: reject, | |
context: message.context | |
}; | |
}); | |
}; | |
// when you pass a message within a pipe to other env | |
// you should | |
PromisePipe.execTransitionMessage = function execTransitionMessage(message){ | |
if(TransactionHandler.processTransaction(message))return {then: function(){}} | |
var context = message.context; | |
context._env = PromisePipe.env; | |
delete context._passChains; | |
//get back contexts non enumerables | |
augmentContext(context, '_pipecallId', message.call); | |
augmentContext(context, '_trace', message._trace); | |
var sequence = PromisePipe.pipes[message.pipe].seq; | |
var chain = [].concat(sequence); | |
var ids = chain.map(function(el){ | |
return el._id; | |
}); | |
//Check that this is bounded chain nothing is passed through | |
var firstChainIndex = ids.indexOf(message.chains[0]); | |
//someone is trying to hack the Pipe | |
if(firstChainIndex > 0 && sequence[firstChainIndex]._env === sequence[firstChainIndex - 1]._env) { | |
console.error("Non-consistent pipe call, message is trying to omit chains"); | |
return Promise.reject({error: "Non-consistent pipe call, message is trying to omit chains"}).catch(unhandledCatch); | |
} | |
var newChain = chain.slice(firstChainIndex, ids.indexOf(message.chains[1]) + 1); | |
newChain = newChain.map(bindTo(context).bindIt); | |
//catch inside env | |
function unhandledCatch(data){ | |
message.unhandledFail = data; | |
return data; | |
} | |
return doit(newChain, message.data, {_id: message.pipe}, context).catch(unhandledCatch); | |
}; | |
PromisePipe.createTransitionMessage = function createTransitionMessage(data, context, pipeId, chainId, envBackChainId, callId){ | |
var contextToSend = JSON.parse(JSON.stringify(context)); | |
delete contextToSend[context._env] | |
return { | |
data: data, | |
context: contextToSend, | |
pipe: pipeId, | |
chains: [chainId, envBackChainId], | |
call: callId, | |
_trace: context._trace | |
}; | |
}; | |
/* | |
experimental | |
*/ | |
PromisePipe.localContext = function(context){ | |
return { | |
execTransitionMessage: function(message){ | |
var origContext = message.context; | |
context.__proto__ = origContext; | |
message.context = context; | |
return PromisePipe.execTransitionMessage(message).then(function(data){ | |
message.context = origContext; | |
return data; | |
}); | |
}, | |
//TODO:cover with Tests | |
wrap: function(fn){ | |
return function(data, origContext){ | |
context.__proto__ = origContext; | |
return fn(data, context); | |
}; | |
} | |
}; | |
}; | |
//PromisePipe.api = require('./RemoteAPIHandlers')(); | |
// build a chain of promises | |
function doit(sequence, data, pipe, ctx) { | |
return sequence.reduce(function(doWork, funcArr, funcIndex) { | |
var systemEnvs = { | |
both: { | |
predicate: function () { | |
return funcArr._env === 'both'; | |
}, | |
handler: function () { | |
var toNextEnv = getNameNextEnv(PromisePipe.env); | |
if (!toNextEnv) { | |
return function (data) { | |
return funcArr(data); | |
}; | |
} | |
return function () { | |
return doWork.then(funcArr).then(function () { | |
var msg = PromisePipe.createTransitionMessage(data, ctx, pipe._id, funcArr._id, funcArr._id, ctx._pipecallId); | |
var range = rangeChain(funcArr._id, sequence); | |
ctx._passChains = passChains(range[0]-1, range[0]-1); | |
return TransactionHandler.createTransaction(msg) | |
.send(PromisePipe.envTransitions[ctx._env][toNextEnv]) | |
.then(updateContextAfterTransition) | |
.then(handleRejectAfterTransition) | |
}); | |
}; | |
} | |
}, | |
inherit: { | |
predicate: function () { | |
return funcArr._env === 'inherit'; | |
}, | |
handler: function () { | |
funcArr._env = sequence[funcIndex]._env; | |
return funcArr; | |
} | |
} | |
}; | |
function getNameNextEnv(env) { | |
if (!PromisePipe.envTransitions[ctx._env]) { | |
return null; | |
} | |
return Object.keys(PromisePipe.envTransitions[ctx._env]).reduce(function (nextEnv, name) { | |
if (nextEnv) { return nextEnv; } | |
if (name === env) { | |
return nextEnv; | |
} | |
if (name !== env) { | |
return name; | |
} | |
}, null); | |
} | |
/** | |
* Get index of next env appearance | |
* @param {Number} fromIndex | |
* @param {String} env | |
* @return {Number} | |
*/ | |
function getIndexOfNextEnvAppearance(fromIndex, env){ | |
return sequence.map(function(el){ | |
return el._env; | |
}).indexOf(env, fromIndex); | |
} | |
/** | |
* Check env of system behavoir | |
* @param {String} env Env for checking | |
* @return {Boolean} | |
*/ | |
function isSystemTransition (env) { | |
return !!systemEnvs[env]; | |
} | |
/** | |
* Check valid is transition | |
*/ | |
function isValidTransition (funcArr, ctx) { | |
var isValid = true; | |
if (!(PromisePipe.envTransitions[ctx._env] && PromisePipe.envTransitions[ctx._env][funcArr._env])) { | |
if (!isSystemTransition(funcArr._env)) { | |
isValid = false; | |
} | |
} | |
return isValid; | |
} | |
/** | |
* Return filtered list for passing functions | |
* @param {Number} first | |
* @param {Number} last | |
* @return {Array} | |
*/ | |
function passChains (first, last) { | |
return sequence.map(function (el) { | |
return el._id; | |
}).slice(first, last + 1); | |
} | |
/** | |
* Return lastChain index | |
* @param {Number} first | |
* @return {Number} | |
*/ | |
function lastChain (first) { | |
var index = getIndexOfNextEnvAppearance(first, PromisePipe.env, sequence); | |
return index === -1 ? (sequence.length - 1) : (index - 1); | |
} | |
/** | |
* Return tuple of chained indexes | |
* @param {Number} id | |
* @return {Tuple} | |
*/ | |
function rangeChain (id) { | |
var first = getChainIndexById(id, sequence); | |
return [first, lastChain(first, sequence)]; | |
} | |
/** | |
* Get chain by index | |
* @param {String} id | |
* @param {Array} sequence | |
*/ | |
function getChainIndexById(id){ | |
return sequence.map(function(el){ | |
return el._id; | |
}).indexOf(id); | |
} | |
//transition returns context in another object. | |
//we must preserve existing object and make changes accordingly | |
function updateContextAfterTransition(message){ | |
//inherit from coming message context | |
Object.keys(message.context).reduce(function(context, name){ | |
if(name !== '_env') context[name] = message.context[name]; | |
return context; | |
}, ctx); | |
return message; | |
} | |
function handleRejectAfterTransition(message){ | |
return new Promise(function(resolve, reject){ | |
if(message.unhandledFail) return reject(message.data); | |
resolve(message.data); | |
}) | |
} | |
function jump (range) { | |
return function (data) { | |
var msg = PromisePipe.createTransitionMessage(data, ctx, pipe._id, funcArr._id, sequence[range[1]]._id, ctx._pipecallId); | |
var result = TransactionHandler.createTransaction(msg) | |
.send(PromisePipe.envTransitions[ctx._env][funcArr._env]) | |
.then(updateContextAfterTransition) | |
.then(handleRejectAfterTransition); | |
return result; | |
}; | |
} | |
/** | |
* Jump to next env | |
* @return {Function} | |
*/ | |
function toNextEnv () { | |
var range = rangeChain(funcArr._id, sequence); | |
ctx._passChains = passChains(range[0], range[1]); | |
if (!isValidTransition(funcArr, ctx)) { | |
throw Error('there is no transition ' + ctx._env + ' to ' + funcArr._env); | |
} | |
return jump(range); | |
} | |
/** | |
* Will we go to the next env | |
*/ | |
function goToNextEnv () { | |
return ctx._env !== funcArr._env; | |
} | |
//it shows error in console and passes it down | |
function errorEnhancer(data){ | |
//is plain Error and was not yet caught | |
if(data instanceof Error && !data.caughtOnChainId){ | |
data.caughtOnChainId = funcArr._id; | |
var trace = stackTrace({e: data}); | |
if(funcArr._name) { | |
console.log('Failed inside ' + funcArr._name); | |
} | |
console.log(data.toString()); | |
console.log(trace.join('\n')); | |
} | |
return Promise.reject(data); | |
} | |
/** | |
* Skip this chain | |
* @return {Function} | |
*/ | |
function toNextChain () { | |
return function (data) { | |
return data; | |
}; | |
} | |
/** | |
* Check is skip chain | |
* @return {Boolean} | |
*/ | |
function skipChain() { | |
if (!ctx._passChains) { | |
return false; | |
} | |
if (!!~ctx._passChains.indexOf(funcArr._id)) { | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Execute handler on correct env | |
* @return {Function} | |
*/ | |
function doOnPropEnv () { | |
return Object.keys(systemEnvs).reduce(function (chain, name) { | |
if (chain !== funcArr) { | |
// fixed handler for current chain | |
return chain; | |
} | |
if (systemEnvs[name].predicate(sequence, funcArr)) { | |
return systemEnvs[name].handler(sequence, funcArr, funcIndex, ctx); | |
} | |
if (goToNextEnv() && !skipChain()) { | |
return toNextEnv(); | |
} | |
if (goToNextEnv() && skipChain()) { | |
return toNextChain(); | |
} | |
return chain; | |
}, funcArr); | |
} | |
if (funcArr && funcArr.isCatch) { | |
return doWork.catch(funcArr); | |
} | |
return doWork.then(doOnPropEnv()).catch(errorEnhancer); | |
}, Promise.resolve(data)); | |
} | |
function bindTo(that){ | |
return { | |
bindIt: function bindIt(chain){ | |
var handler = chain.func; | |
var newArgFunc = function newArgFunc(data){ | |
// advanced debugging | |
if(PromisePipe._mode === 'DEBUG'){ | |
if(that._pipecallId && that._trace){ | |
var joinedContext = getProtoChain(that) | |
.reverse() | |
.reduce(join, {}); | |
var cleanContext = JSON.parse(serialize(joinedContext)); | |
//should be hidden | |
delete cleanContext._passChains; | |
that._trace[that._pipecallId].push({ | |
chainId: chain._id, | |
data: serialize(data), | |
context: JSON.stringify(cleanContext), | |
timestamp: Date.now(), | |
env: that._env | |
}); | |
} | |
} | |
return handler.call(that, data, that); | |
}; | |
newArgFunc._name = chain.name; | |
Object.keys(chain).reduce(function(funObj, key){ | |
if (key !== 'name') funObj[key] = chain[key]; | |
return funObj; | |
}, newArgFunc); | |
return newArgFunc; | |
} | |
}; | |
} | |
function join(result, obj){ | |
Object.keys(obj).forEach(function(key){ | |
result[key] = obj[key]; | |
}); | |
return result; | |
} | |
function getProtoChain(obj, result){ | |
if(!result) result = []; | |
result.push(obj); | |
if(obj.__proto__) return getProtoChain(obj.__proto__, result); | |
return result; | |
} | |
var counter = 1234567890987; | |
function ID() { | |
counter++; | |
// Math.random should be unique because of its seeding algorithm. | |
// Convert it to base 36 (numbers + letters), and grab the first 9 characters | |
// after the decimal. | |
return counter.toString(36).substr(-8); | |
} | |
PromisePipe.api = require('./RemoteAPIHandlers')(PromisePipe); | |
return PromisePipe; | |
} | |
module.exports = PromisePipeFactory; | |
},{"./RemoteAPIHandlers":10,"./TransactionController":11,"es6-promise":6,"json-stringify-safe":7,"stacktrace-js":8}],10:[function(require,module,exports){ | |
module.exports = function(PromisePipe){ | |
return { | |
provide: function(connector, apiName){ | |
connector.listen(function(message){ | |
function end(data){ | |
message.data = data; | |
connector.send(message); | |
} | |
PromisePipe.pipes[message.id].Pipe(message.data, message.context || {}).catch(unhandledCatch).then(end); | |
//catch inside env | |
function unhandledCatch(data){ | |
message.unhandledFail = data; | |
return data; | |
} | |
}) | |
return generateClientAPI(apiName, PromisePipe); | |
} | |
} | |
} | |
var TransactionController = require('./TransactionController'); | |
function generateClientAPI(apiName, PromisePipe){ | |
var result = [ | |
TransactionController.toString(), | |
"var TransactionHandler = TransactionController();\n", | |
"connector.listen(TransactionHandler.processTransaction);\n", | |
handleRejectAfterTransition.toString(), | |
apiCall.toString() | |
].join("\n") | |
var theApiHash = Object.keys(PromisePipe.pipes).map(function(item){ | |
return PromisePipe.pipes[item]; | |
}).reduce(oneChain, []); | |
result += "\nreturn {"+theApiHash.join(",\n")+"}\n"; | |
return "function " + (apiName||'initApi') + "(connector){\n"+result+"}" | |
} | |
function apiCall(id){ | |
return function(data, context){ | |
var message = { | |
data: data, | |
id: id | |
} | |
return TransactionHandler.createTransaction(message).send(connector.send).then(handleRejectAfterTransition); | |
} | |
} | |
function handleRejectAfterTransition(message){ | |
return new Promise(function(resolve, reject){ | |
if(message.unhandledFail) return reject(message.data); | |
resolve(message.data); | |
}) | |
} | |
function oneChain(result, item){ | |
result.push(item.name + ": apiCall('" + item.id + "')"); | |
return result; | |
} | |
/** | |
//EXPOSING | |
PP({name: 'getItems', description: 'Get Items from collection'}).then().then(); | |
PP({name: 'saveItems', description: 'Save Items from collection'}).then().then(); | |
PP.api.provide(connector); | |
//USAGE | |
<script src="http://api.url.com/v0.1"></script>.... | |
or | |
var mySerivceApi = PP.api.get(apiPath); | |
PP.use('api', mySerivceApi); | |
PP().api.getItems().then(); | |
PP().api.saveItems().then(); | |
*/ | |
},{"./TransactionController":11}],11:[function(require,module,exports){ | |
var Promise = Promise || require('es6-promise').Promise; | |
module.exports = function TransactionController(){ | |
var transactions = {}; | |
return { | |
createTransaction: function createTransaction (message){ | |
message._transactionId = Math.ceil(Math.random() * Math.pow(10, 16)); | |
return { | |
send: function sendTransaction(handler){ | |
return new Promise(function(resolve, reject){ | |
//save transaction Resolvers | |
transactions[message._transactionId] = { | |
resolve: resolve | |
} | |
handler(message); | |
}) | |
} | |
} | |
}, | |
processTransaction: function processTransaction(transactionMessage){ | |
if(transactions[transactionMessage._transactionId]){ | |
var id = transactionMessage._transactionId; | |
delete transactionMessage._transactionId; | |
transactions[id].resolve(transactionMessage); | |
delete transactions[transactionMessage._transactionId] | |
return true; | |
} | |
return false; | |
} | |
} | |
} | |
//createTransaction(message).send(messageSender).then(responseHandler); | |
},{"es6-promise":6}]},{},[2]); | |
globalHandler.fn(hook); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment