Last active
August 17, 2017 13:57
-
-
Save goto-bus-stop/daf8166313c7ec5eae32e44a683c04c8 to your computer and use it in GitHub Desktop.
This file contains 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
(function(){ | |
var _$browser_3 = {}; | |
// shim for using process in browser | |
var process = _$browser_3 = {}; | |
// cached from whatever global is present so that test runners that stub it | |
// don't break things. But we need to wrap it in a try catch in case it is | |
// wrapped in strict mode code which doesn't define any globals. It's inside a | |
// function because try/catches deoptimize in certain engines. | |
var cachedSetTimeout; | |
var cachedClearTimeout; | |
function defaultSetTimout() { | |
throw new Error('setTimeout has not been defined'); | |
} | |
function defaultClearTimeout () { | |
throw new Error('clearTimeout has not been defined'); | |
} | |
(function () { | |
try { | |
if (typeof setTimeout === 'function') { | |
cachedSetTimeout = setTimeout; | |
} else { | |
cachedSetTimeout = defaultSetTimout; | |
} | |
} catch (e) { | |
cachedSetTimeout = defaultSetTimout; | |
} | |
try { | |
if (typeof clearTimeout === 'function') { | |
cachedClearTimeout = clearTimeout; | |
} else { | |
cachedClearTimeout = defaultClearTimeout; | |
} | |
} catch (e) { | |
cachedClearTimeout = defaultClearTimeout; | |
} | |
} ()) | |
function runTimeout(fun) { | |
if (cachedSetTimeout === setTimeout) { | |
//normal enviroments in sane situations | |
return setTimeout(fun, 0); | |
} | |
// if setTimeout wasn't available but was latter defined | |
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { | |
cachedSetTimeout = setTimeout; | |
return setTimeout(fun, 0); | |
} | |
try { | |
// when when somebody has screwed with setTimeout but no I.E. maddness | |
return cachedSetTimeout(fun, 0); | |
} catch(e){ | |
try { | |
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally | |
return cachedSetTimeout.call(null, fun, 0); | |
} catch(e){ | |
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error | |
return cachedSetTimeout.call(this, fun, 0); | |
} | |
} | |
} | |
function runClearTimeout(marker) { | |
if (cachedClearTimeout === clearTimeout) { | |
//normal enviroments in sane situations | |
return clearTimeout(marker); | |
} | |
// if clearTimeout wasn't available but was latter defined | |
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { | |
cachedClearTimeout = clearTimeout; | |
return clearTimeout(marker); | |
} | |
try { | |
// when when somebody has screwed with setTimeout but no I.E. maddness | |
return cachedClearTimeout(marker); | |
} catch (e){ | |
try { | |
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally | |
return cachedClearTimeout.call(null, marker); | |
} catch (e){ | |
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. | |
// Some versions of I.E. have different rules for clearTimeout vs setTimeout | |
return cachedClearTimeout.call(this, marker); | |
} | |
} | |
} | |
var queue = []; | |
var draining = false; | |
var currentQueue; | |
var queueIndex = -1; | |
function cleanUpNextTick() { | |
if (!draining || !currentQueue) { | |
return; | |
} | |
draining = false; | |
if (currentQueue.length) { | |
queue = currentQueue.concat(queue); | |
} else { | |
queueIndex = -1; | |
} | |
if (queue.length) { | |
drainQueue(); | |
} | |
} | |
function drainQueue() { | |
if (draining) { | |
return; | |
} | |
var timeout = runTimeout(cleanUpNextTick); | |
draining = true; | |
var len = queue.length; | |
while(len) { | |
currentQueue = queue; | |
queue = []; | |
while (++queueIndex < len) { | |
if (currentQueue) { | |
currentQueue[queueIndex].run(); | |
} | |
} | |
queueIndex = -1; | |
len = queue.length; | |
} | |
currentQueue = null; | |
draining = false; | |
runClearTimeout(timeout); | |
} | |
process.nextTick = function (fun) { | |
var args = new Array(arguments.length - 1); | |
if (arguments.length > 1) { | |
for (var i = 1; i < arguments.length; i++) { | |
args[i - 1] = arguments[i]; | |
} | |
} | |
queue.push(new Item(fun, args)); | |
if (queue.length === 1 && !draining) { | |
runTimeout(drainQueue); | |
} | |
}; | |
// v8 likes predictible objects | |
function Item(fun, array) { | |
this.fun = fun; | |
this.array = array; | |
} | |
Item.prototype.run = function () { | |
this.fun.apply(null, this.array); | |
}; | |
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.prependListener = noop; | |
process.prependOnceListener = noop; | |
process.listeners = function (name) { return [] } | |
process.binding = function (name) { | |
throw new Error('process.binding is not supported'); | |
}; | |
process.cwd = function () { return '/' }; | |
process.chdir = function (dir) { | |
throw new Error('process.chdir is not supported'); | |
}; | |
process.umask = function() { return 0; }; | |
var _$inherits_browser_4 = {}; | |
if (typeof Object.create === 'function') { | |
// implementation from standard node.js 'util' module | |
_$inherits_browser_4 = function inherits(ctor, superCtor) { | |
ctor.super_ = superCtor | |
ctor.prototype = Object.create(superCtor.prototype, { | |
constructor: { | |
value: ctor, | |
enumerable: false, | |
writable: true, | |
configurable: true | |
} | |
}); | |
}; | |
} else { | |
// old school shim for old browsers | |
_$inherits_browser_4 = function inherits(ctor, superCtor) { | |
ctor.super_ = superCtor | |
var TempCtor = function () {} | |
TempCtor.prototype = superCtor.prototype | |
ctor.prototype = new TempCtor() | |
ctor.prototype.constructor = ctor | |
} | |
} | |
var _$isBuffer_5 = function isBuffer(arg) { | |
return arg && typeof arg === 'object' | |
&& typeof arg.copy === 'function' | |
&& typeof arg.fill === 'function' | |
&& typeof arg.readUInt8 === 'function'; | |
} | |
var _$util_6 = {}; | |
(function (process,global){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
var formatRegExp = /%[sdj%]/g; | |
_$util_6.format = function(f) { | |
if (!isString(f)) { | |
var objects = []; | |
for (var i = 0; i < arguments.length; i++) { | |
objects.push(inspect(arguments[i])); | |
} | |
return objects.join(' '); | |
} | |
var i = 1; | |
var args = arguments; | |
var len = args.length; | |
var str = String(f).replace(formatRegExp, function(x) { | |
if (x === '%%') return '%'; | |
if (i >= len) return x; | |
switch (x) { | |
case '%s': return String(args[i++]); | |
case '%d': return Number(args[i++]); | |
case '%j': | |
try { | |
return JSON.stringify(args[i++]); | |
} catch (_) { | |
return '[Circular]'; | |
} | |
default: | |
return x; | |
} | |
}); | |
for (var x = args[i]; i < len; x = args[++i]) { | |
if (isNull(x) || !isObject(x)) { | |
str += ' ' + x; | |
} else { | |
str += ' ' + inspect(x); | |
} | |
} | |
return str; | |
}; | |
// Mark that a method should not be used. | |
// Returns a modified function which warns once by default. | |
// If --no-deprecation is set, then it is a no-op. | |
_$util_6.deprecate = function(fn, msg) { | |
// Allow for deprecating things in the process of starting up. | |
if (isUndefined(global.process)) { | |
return function() { | |
return _$util_6.deprecate(fn, msg).apply(this, arguments); | |
}; | |
} | |
if (process.noDeprecation === true) { | |
return fn; | |
} | |
var warned = false; | |
function deprecated() { | |
if (!warned) { | |
if (process.throwDeprecation) { | |
throw new Error(msg); | |
} else if (process.traceDeprecation) { | |
console.trace(msg); | |
} else { | |
console.error(msg); | |
} | |
warned = true; | |
} | |
return fn.apply(this, arguments); | |
} | |
return deprecated; | |
}; | |
var debugs = {}; | |
var debugEnviron; | |
/* common-shake removed: exports.debuglog = */ void function(set) { | |
if (isUndefined(debugEnviron)) | |
debugEnviron = process.env.NODE_DEBUG || ''; | |
set = set.toUpperCase(); | |
if (!debugs[set]) { | |
if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { | |
var pid = process.pid; | |
debugs[set] = function() { | |
var msg = _$util_6.format.apply(_$util_6, arguments); | |
console.error('%s %d: %s', set, pid, msg); | |
}; | |
} else { | |
debugs[set] = function() {}; | |
} | |
} | |
return debugs[set]; | |
}; | |
/** | |
* Echos the value of a value. Trys to print the value out | |
* in the best way possible given the different types. | |
* | |
* @param {Object} obj The object to print out. | |
* @param {Object} opts Optional options object that alters the output. | |
*/ | |
/* legacy: obj, showHidden, depth, colors*/ | |
function inspect(obj, opts) { | |
// default options | |
var ctx = { | |
seen: [], | |
stylize: stylizeNoColor | |
}; | |
// legacy... | |
if (arguments.length >= 3) ctx.depth = arguments[2]; | |
if (arguments.length >= 4) ctx.colors = arguments[3]; | |
if (isBoolean(opts)) { | |
// legacy... | |
ctx.showHidden = opts; | |
} else if (opts) { | |
// got an "options" object | |
_$util_6._extend(ctx, opts); | |
} | |
// set default options | |
if (isUndefined(ctx.showHidden)) ctx.showHidden = false; | |
if (isUndefined(ctx.depth)) ctx.depth = 2; | |
if (isUndefined(ctx.colors)) ctx.colors = false; | |
if (isUndefined(ctx.customInspect)) ctx.customInspect = true; | |
if (ctx.colors) ctx.stylize = stylizeWithColor; | |
return formatValue(ctx, obj, ctx.depth); | |
} | |
_$util_6.inspect = inspect; | |
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics | |
inspect.colors = { | |
'bold' : [1, 22], | |
'italic' : [3, 23], | |
'underline' : [4, 24], | |
'inverse' : [7, 27], | |
'white' : [37, 39], | |
'grey' : [90, 39], | |
'black' : [30, 39], | |
'blue' : [34, 39], | |
'cyan' : [36, 39], | |
'green' : [32, 39], | |
'magenta' : [35, 39], | |
'red' : [31, 39], | |
'yellow' : [33, 39] | |
}; | |
// Don't use 'blue' not visible on cmd.exe | |
inspect.styles = { | |
'special': 'cyan', | |
'number': 'yellow', | |
'boolean': 'yellow', | |
'undefined': 'grey', | |
'null': 'bold', | |
'string': 'green', | |
'date': 'magenta', | |
// "name": intentionally not styling | |
'regexp': 'red' | |
}; | |
function stylizeWithColor(str, styleType) { | |
var style = inspect.styles[styleType]; | |
if (style) { | |
return '\u001b[' + inspect.colors[style][0] + 'm' + str + | |
'\u001b[' + inspect.colors[style][1] + 'm'; | |
} else { | |
return str; | |
} | |
} | |
function stylizeNoColor(str, styleType) { | |
return str; | |
} | |
function arrayToHash(array) { | |
var hash = {}; | |
array.forEach(function(val, idx) { | |
hash[val] = true; | |
}); | |
return hash; | |
} | |
function formatValue(ctx, value, recurseTimes) { | |
// Provide a hook for user-specified inspect functions. | |
// Check that value is an object with an inspect function on it | |
if (ctx.customInspect && | |
value && | |
isFunction(value.inspect) && | |
// Filter out the util module, it's inspect function is special | |
value.inspect !== _$util_6.inspect && | |
// Also filter out any prototype objects using the circular check. | |
!(value.constructor && value.constructor.prototype === value)) { | |
var ret = value.inspect(recurseTimes, ctx); | |
if (!isString(ret)) { | |
ret = formatValue(ctx, ret, recurseTimes); | |
} | |
return ret; | |
} | |
// Primitive types cannot have properties | |
var primitive = formatPrimitive(ctx, value); | |
if (primitive) { | |
return primitive; | |
} | |
// Look up the keys of the object. | |
var keys = Object.keys(value); | |
var visibleKeys = arrayToHash(keys); | |
if (ctx.showHidden) { | |
keys = Object.getOwnPropertyNames(value); | |
} | |
// IE doesn't make error fields non-enumerable | |
// http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx | |
if (isError(value) | |
&& (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { | |
return formatError(value); | |
} | |
// Some type of object without properties can be shortcutted. | |
if (keys.length === 0) { | |
if (isFunction(value)) { | |
var name = value.name ? ': ' + value.name : ''; | |
return ctx.stylize('[Function' + name + ']', 'special'); | |
} | |
if (isRegExp(value)) { | |
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); | |
} | |
if (isDate(value)) { | |
return ctx.stylize(Date.prototype.toString.call(value), 'date'); | |
} | |
if (isError(value)) { | |
return formatError(value); | |
} | |
} | |
var base = '', array = false, braces = ['{', '}']; | |
// Make Array say that they are Array | |
if (isArray(value)) { | |
array = true; | |
braces = ['[', ']']; | |
} | |
// Make functions say that they are functions | |
if (isFunction(value)) { | |
var n = value.name ? ': ' + value.name : ''; | |
base = ' [Function' + n + ']'; | |
} | |
// Make RegExps say that they are RegExps | |
if (isRegExp(value)) { | |
base = ' ' + RegExp.prototype.toString.call(value); | |
} | |
// Make dates with properties first say the date | |
if (isDate(value)) { | |
base = ' ' + Date.prototype.toUTCString.call(value); | |
} | |
// Make error with message first say the error | |
if (isError(value)) { | |
base = ' ' + formatError(value); | |
} | |
if (keys.length === 0 && (!array || value.length == 0)) { | |
return braces[0] + base + braces[1]; | |
} | |
if (recurseTimes < 0) { | |
if (isRegExp(value)) { | |
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); | |
} else { | |
return ctx.stylize('[Object]', 'special'); | |
} | |
} | |
ctx.seen.push(value); | |
var output; | |
if (array) { | |
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); | |
} else { | |
output = keys.map(function(key) { | |
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); | |
}); | |
} | |
ctx.seen.pop(); | |
return reduceToSingleString(output, base, braces); | |
} | |
function formatPrimitive(ctx, value) { | |
if (isUndefined(value)) | |
return ctx.stylize('undefined', 'undefined'); | |
if (isString(value)) { | |
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') | |
.replace(/'/g, "\\'") | |
.replace(/\\"/g, '"') + '\''; | |
return ctx.stylize(simple, 'string'); | |
} | |
if (isNumber(value)) | |
return ctx.stylize('' + value, 'number'); | |
if (isBoolean(value)) | |
return ctx.stylize('' + value, 'boolean'); | |
// For some reason typeof null is "object", so special case here. | |
if (isNull(value)) | |
return ctx.stylize('null', 'null'); | |
} | |
function formatError(value) { | |
return '[' + Error.prototype.toString.call(value) + ']'; | |
} | |
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { | |
var output = []; | |
for (var i = 0, l = value.length; i < l; ++i) { | |
if (hasOwnProperty(value, String(i))) { | |
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, | |
String(i), true)); | |
} else { | |
output.push(''); | |
} | |
} | |
keys.forEach(function(key) { | |
if (!key.match(/^\d+$/)) { | |
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, | |
key, true)); | |
} | |
}); | |
return output; | |
} | |
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { | |
var name, str, desc; | |
desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; | |
if (desc.get) { | |
if (desc.set) { | |
str = ctx.stylize('[Getter/Setter]', 'special'); | |
} else { | |
str = ctx.stylize('[Getter]', 'special'); | |
} | |
} else { | |
if (desc.set) { | |
str = ctx.stylize('[Setter]', 'special'); | |
} | |
} | |
if (!hasOwnProperty(visibleKeys, key)) { | |
name = '[' + key + ']'; | |
} | |
if (!str) { | |
if (ctx.seen.indexOf(desc.value) < 0) { | |
if (isNull(recurseTimes)) { | |
str = formatValue(ctx, desc.value, null); | |
} else { | |
str = formatValue(ctx, desc.value, recurseTimes - 1); | |
} | |
if (str.indexOf('\n') > -1) { | |
if (array) { | |
str = str.split('\n').map(function(line) { | |
return ' ' + line; | |
}).join('\n').substr(2); | |
} else { | |
str = '\n' + str.split('\n').map(function(line) { | |
return ' ' + line; | |
}).join('\n'); | |
} | |
} | |
} else { | |
str = ctx.stylize('[Circular]', 'special'); | |
} | |
} | |
if (isUndefined(name)) { | |
if (array && key.match(/^\d+$/)) { | |
return str; | |
} | |
name = JSON.stringify('' + key); | |
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { | |
name = name.substr(1, name.length - 2); | |
name = ctx.stylize(name, 'name'); | |
} else { | |
name = name.replace(/'/g, "\\'") | |
.replace(/\\"/g, '"') | |
.replace(/(^"|"$)/g, "'"); | |
name = ctx.stylize(name, 'string'); | |
} | |
} | |
return name + ': ' + str; | |
} | |
function reduceToSingleString(output, base, braces) { | |
var numLinesEst = 0; | |
var length = output.reduce(function(prev, cur) { | |
numLinesEst++; | |
if (cur.indexOf('\n') >= 0) numLinesEst++; | |
return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; | |
}, 0); | |
if (length > 60) { | |
return braces[0] + | |
(base === '' ? '' : base + '\n ') + | |
' ' + | |
output.join(',\n ') + | |
' ' + | |
braces[1]; | |
} | |
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; | |
} | |
// NOTE: These type checking functions intentionally don't use `instanceof` | |
// because it is fragile and can be easily faked with `Object.create()`. | |
function isArray(ar) { | |
return Array.isArray(ar); | |
} | |
/* common-shake removed: exports.isArray = */ void isArray; | |
function isBoolean(arg) { | |
return typeof arg === 'boolean'; | |
} | |
/* common-shake removed: exports.isBoolean = */ void isBoolean; | |
function isNull(arg) { | |
return arg === null; | |
} | |
/* common-shake removed: exports.isNull = */ void isNull; | |
function isNullOrUndefined(arg) { | |
return arg == null; | |
} | |
/* common-shake removed: exports.isNullOrUndefined = */ void isNullOrUndefined; | |
function isNumber(arg) { | |
return typeof arg === 'number'; | |
} | |
/* common-shake removed: exports.isNumber = */ void isNumber; | |
function isString(arg) { | |
return typeof arg === 'string'; | |
} | |
/* common-shake removed: exports.isString = */ void isString; | |
function isSymbol(arg) { | |
return typeof arg === 'symbol'; | |
} | |
/* common-shake removed: exports.isSymbol = */ void isSymbol; | |
function isUndefined(arg) { | |
return arg === void 0; | |
} | |
/* common-shake removed: exports.isUndefined = */ void isUndefined; | |
function isRegExp(re) { | |
return isObject(re) && objectToString(re) === '[object RegExp]'; | |
} | |
_$util_6.isRegExp = isRegExp; | |
function isObject(arg) { | |
return typeof arg === 'object' && arg !== null; | |
} | |
/* common-shake removed: exports.isObject = */ void isObject; | |
function isDate(d) { | |
return isObject(d) && objectToString(d) === '[object Date]'; | |
} | |
_$util_6.isDate = isDate; | |
function isError(e) { | |
return isObject(e) && | |
(objectToString(e) === '[object Error]' || e instanceof Error); | |
} | |
_$util_6.isError = isError; | |
function isFunction(arg) { | |
return typeof arg === 'function'; | |
} | |
_$util_6.isFunction = isFunction; | |
function isPrimitive(arg) { | |
return arg === null || | |
typeof arg === 'boolean' || | |
typeof arg === 'number' || | |
typeof arg === 'string' || | |
typeof arg === 'symbol' || // ES6 symbol | |
typeof arg === 'undefined'; | |
} | |
_$util_6.isPrimitive = isPrimitive; | |
/* common-shake removed: exports.isBuffer = */ void _$isBuffer_5; | |
function objectToString(o) { | |
return Object.prototype.toString.call(o); | |
} | |
function pad(n) { | |
return n < 10 ? '0' + n.toString(10) : n.toString(10); | |
} | |
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', | |
'Oct', 'Nov', 'Dec']; | |
// 26 Feb 16:19:34 | |
function timestamp() { | |
var d = new Date(); | |
var time = [pad(d.getHours()), | |
pad(d.getMinutes()), | |
pad(d.getSeconds())].join(':'); | |
return [d.getDate(), months[d.getMonth()], time].join(' '); | |
} | |
// log is just a thin wrapper to console.log that prepends a timestamp | |
/* common-shake removed: exports.log = */ void function() { | |
console.log('%s - %s', timestamp(), _$util_6.format.apply(_$util_6, arguments)); | |
}; | |
/** | |
* Inherit the prototype methods from one constructor into another. | |
* | |
* The Function.prototype.inherits from lang.js rewritten as a standalone | |
* function (not on Function.prototype). NOTE: If this file is to be loaded | |
* during bootstrapping this function needs to be rewritten using some native | |
* functions as prototype setup using normal JavaScript does not work as | |
* expected during bootstrapping (see mirror.js in r114903). | |
* | |
* @param {function} ctor Constructor function which needs to inherit the | |
* prototype. | |
* @param {function} superCtor Constructor function to inherit prototype from. | |
*/ | |
_$util_6.inherits = _$inherits_browser_4; | |
_$util_6._extend = function(origin, add) { | |
// Don't do anything if add isn't an object | |
if (!add || !isObject(add)) return origin; | |
var keys = Object.keys(add); | |
var i = keys.length; | |
while (i--) { | |
origin[keys[i]] = add[keys[i]]; | |
} | |
return origin; | |
}; | |
function hasOwnProperty(obj, prop) { | |
return Object.prototype.hasOwnProperty.call(obj, prop); | |
} | |
}).call(this,_$browser_3,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$assert_1 = {}; | |
(function (global){ | |
'use strict'; | |
// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js | |
// original notice: | |
/*! | |
* The buffer module from node.js, for the browser. | |
* | |
* @author Feross Aboukhadijeh <[email protected]> <http://feross.org> | |
* @license MIT | |
*/ | |
function compare(a, b) { | |
if (a === b) { | |
return 0; | |
} | |
var x = a.length; | |
var y = b.length; | |
for (var i = 0, len = Math.min(x, y); i < len; ++i) { | |
if (a[i] !== b[i]) { | |
x = a[i]; | |
y = b[i]; | |
break; | |
} | |
} | |
if (x < y) { | |
return -1; | |
} | |
if (y < x) { | |
return 1; | |
} | |
return 0; | |
} | |
function isBuffer(b) { | |
if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { | |
return global.Buffer.isBuffer(b); | |
} | |
return !!(b != null && b._isBuffer); | |
} | |
// based on node assert, original notice: | |
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 | |
// | |
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! | |
// | |
// Originally from narwhal.js (http://narwhaljs.org) | |
// Copyright (c) 2009 Thomas Robinson <280north.com> | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the 'Software'), to | |
// deal in the Software without restriction, including without limitation the | |
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
// sell copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included in | |
// all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
var util = _$util_6; | |
var hasOwn = Object.prototype.hasOwnProperty; | |
var pSlice = Array.prototype.slice; | |
var functionsHaveNames = (function () { | |
return function foo() {}.name === 'foo'; | |
}()); | |
function pToString (obj) { | |
return Object.prototype.toString.call(obj); | |
} | |
function isView(arrbuf) { | |
if (isBuffer(arrbuf)) { | |
return false; | |
} | |
if (typeof global.ArrayBuffer !== 'function') { | |
return false; | |
} | |
if (typeof ArrayBuffer.isView === 'function') { | |
return ArrayBuffer.isView(arrbuf); | |
} | |
if (!arrbuf) { | |
return false; | |
} | |
if (arrbuf instanceof DataView) { | |
return true; | |
} | |
if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { | |
return true; | |
} | |
return false; | |
} | |
// 1. The assert module provides functions that throw | |
// AssertionError's when particular conditions are not met. The | |
// assert module must conform to the following interface. | |
var assert = _$assert_1 = ok; | |
// 2. The AssertionError is defined in assert. | |
// new assert.AssertionError({ message: message, | |
// actual: actual, | |
// expected: expected }) | |
var regex = /\s*function\s+([^\(\s]*)\s*/; | |
// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js | |
function getName(func) { | |
if (!util.isFunction(func)) { | |
return; | |
} | |
if (functionsHaveNames) { | |
return func.name; | |
} | |
var str = func.toString(); | |
var match = str.match(regex); | |
return match && match[1]; | |
} | |
assert.AssertionError = function AssertionError(options) { | |
this.name = 'AssertionError'; | |
this.actual = options.actual; | |
this.expected = options.expected; | |
this.operator = options.operator; | |
if (options.message) { | |
this.message = options.message; | |
this.generatedMessage = false; | |
} else { | |
this.message = getMessage(this); | |
this.generatedMessage = true; | |
} | |
var stackStartFunction = options.stackStartFunction || fail; | |
if (Error.captureStackTrace) { | |
Error.captureStackTrace(this, stackStartFunction); | |
} else { | |
// non v8 browsers so we can have a stacktrace | |
var err = new Error(); | |
if (err.stack) { | |
var out = err.stack; | |
// try to strip useless frames | |
var fn_name = getName(stackStartFunction); | |
var idx = out.indexOf('\n' + fn_name); | |
if (idx >= 0) { | |
// once we have located the function frame | |
// we need to strip out everything before it (and its line) | |
var next_line = out.indexOf('\n', idx + 1); | |
out = out.substring(next_line + 1); | |
} | |
this.stack = out; | |
} | |
} | |
}; | |
// assert.AssertionError instanceof Error | |
util.inherits(assert.AssertionError, Error); | |
function truncate(s, n) { | |
if (typeof s === 'string') { | |
return s.length < n ? s : s.slice(0, n); | |
} else { | |
return s; | |
} | |
} | |
function inspect(something) { | |
if (functionsHaveNames || !util.isFunction(something)) { | |
return util.inspect(something); | |
} | |
var rawname = getName(something); | |
var name = rawname ? ': ' + rawname : ''; | |
return '[Function' + name + ']'; | |
} | |
function getMessage(self) { | |
return truncate(inspect(self.actual), 128) + ' ' + | |
self.operator + ' ' + | |
truncate(inspect(self.expected), 128); | |
} | |
// At present only the three keys mentioned above are used and | |
// understood by the spec. Implementations or sub modules can pass | |
// other keys to the AssertionError's constructor - they will be | |
// ignored. | |
// 3. All of the following functions must throw an AssertionError | |
// when a corresponding condition is not met, with a message that | |
// may be undefined if not provided. All assertion methods provide | |
// both the actual and expected values to the assertion error for | |
// display purposes. | |
function fail(actual, expected, message, operator, stackStartFunction) { | |
throw new assert.AssertionError({ | |
message: message, | |
actual: actual, | |
expected: expected, | |
operator: operator, | |
stackStartFunction: stackStartFunction | |
}); | |
} | |
// EXTENSION! allows for well behaved errors defined elsewhere. | |
assert.fail = fail; | |
// 4. Pure assertion tests whether a value is truthy, as determined | |
// by !!guard. | |
// assert.ok(guard, message_opt); | |
// This statement is equivalent to assert.equal(true, !!guard, | |
// message_opt);. To test strictly for the value true, use | |
// assert.strictEqual(true, guard, message_opt);. | |
function ok(value, message) { | |
if (!value) fail(value, true, message, '==', assert.ok); | |
} | |
assert.ok = ok; | |
// 5. The equality assertion tests shallow, coercive equality with | |
// ==. | |
// assert.equal(actual, expected, message_opt); | |
assert.equal = function equal(actual, expected, message) { | |
if (actual != expected) fail(actual, expected, message, '==', assert.equal); | |
}; | |
// 6. The non-equality assertion tests for whether two objects are not equal | |
// with != assert.notEqual(actual, expected, message_opt); | |
assert.notEqual = function notEqual(actual, expected, message) { | |
if (actual == expected) { | |
fail(actual, expected, message, '!=', assert.notEqual); | |
} | |
}; | |
// 7. The equivalence assertion tests a deep equality relation. | |
// assert.deepEqual(actual, expected, message_opt); | |
assert.deepEqual = function deepEqual(actual, expected, message) { | |
if (!_deepEqual(actual, expected, false)) { | |
fail(actual, expected, message, 'deepEqual', assert.deepEqual); | |
} | |
}; | |
assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { | |
if (!_deepEqual(actual, expected, true)) { | |
fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); | |
} | |
}; | |
function _deepEqual(actual, expected, strict, memos) { | |
// 7.1. All identical values are equivalent, as determined by ===. | |
if (actual === expected) { | |
return true; | |
} else if (isBuffer(actual) && isBuffer(expected)) { | |
return compare(actual, expected) === 0; | |
// 7.2. If the expected value is a Date object, the actual value is | |
// equivalent if it is also a Date object that refers to the same time. | |
} else if (util.isDate(actual) && util.isDate(expected)) { | |
return actual.getTime() === expected.getTime(); | |
// 7.3 If the expected value is a RegExp object, the actual value is | |
// equivalent if it is also a RegExp object with the same source and | |
// properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). | |
} else if (util.isRegExp(actual) && util.isRegExp(expected)) { | |
return actual.source === expected.source && | |
actual.global === expected.global && | |
actual.multiline === expected.multiline && | |
actual.lastIndex === expected.lastIndex && | |
actual.ignoreCase === expected.ignoreCase; | |
// 7.4. Other pairs that do not both pass typeof value == 'object', | |
// equivalence is determined by ==. | |
} else if ((actual === null || typeof actual !== 'object') && | |
(expected === null || typeof expected !== 'object')) { | |
return strict ? actual === expected : actual == expected; | |
// If both values are instances of typed arrays, wrap their underlying | |
// ArrayBuffers in a Buffer each to increase performance | |
// This optimization requires the arrays to have the same type as checked by | |
// Object.prototype.toString (aka pToString). Never perform binary | |
// comparisons for Float*Arrays, though, since e.g. +0 === -0 but their | |
// bit patterns are not identical. | |
} else if (isView(actual) && isView(expected) && | |
pToString(actual) === pToString(expected) && | |
!(actual instanceof Float32Array || | |
actual instanceof Float64Array)) { | |
return compare(new Uint8Array(actual.buffer), | |
new Uint8Array(expected.buffer)) === 0; | |
// 7.5 For all other Object pairs, including Array objects, equivalence is | |
// determined by having the same number of owned properties (as verified | |
// with Object.prototype.hasOwnProperty.call), the same set of keys | |
// (although not necessarily the same order), equivalent values for every | |
// corresponding key, and an identical 'prototype' property. Note: this | |
// accounts for both named and indexed properties on Arrays. | |
} else if (isBuffer(actual) !== isBuffer(expected)) { | |
return false; | |
} else { | |
memos = memos || {actual: [], expected: []}; | |
var actualIndex = memos.actual.indexOf(actual); | |
if (actualIndex !== -1) { | |
if (actualIndex === memos.expected.indexOf(expected)) { | |
return true; | |
} | |
} | |
memos.actual.push(actual); | |
memos.expected.push(expected); | |
return objEquiv(actual, expected, strict, memos); | |
} | |
} | |
function isArguments(object) { | |
return Object.prototype.toString.call(object) == '[object Arguments]'; | |
} | |
function objEquiv(a, b, strict, actualVisitedObjects) { | |
if (a === null || a === undefined || b === null || b === undefined) | |
return false; | |
// if one is a primitive, the other must be same | |
if (util.isPrimitive(a) || util.isPrimitive(b)) | |
return a === b; | |
if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) | |
return false; | |
var aIsArgs = isArguments(a); | |
var bIsArgs = isArguments(b); | |
if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) | |
return false; | |
if (aIsArgs) { | |
a = pSlice.call(a); | |
b = pSlice.call(b); | |
return _deepEqual(a, b, strict); | |
} | |
var ka = objectKeys(a); | |
var kb = objectKeys(b); | |
var key, i; | |
// having the same number of owned properties (keys incorporates | |
// hasOwnProperty) | |
if (ka.length !== kb.length) | |
return false; | |
//the same set of keys (although not necessarily the same order), | |
ka.sort(); | |
kb.sort(); | |
//~~~cheap key test | |
for (i = ka.length - 1; i >= 0; i--) { | |
if (ka[i] !== kb[i]) | |
return false; | |
} | |
//equivalent values for every corresponding key, and | |
//~~~possibly expensive deep test | |
for (i = ka.length - 1; i >= 0; i--) { | |
key = ka[i]; | |
if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) | |
return false; | |
} | |
return true; | |
} | |
// 8. The non-equivalence assertion tests for any deep inequality. | |
// assert.notDeepEqual(actual, expected, message_opt); | |
assert.notDeepEqual = function notDeepEqual(actual, expected, message) { | |
if (_deepEqual(actual, expected, false)) { | |
fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); | |
} | |
}; | |
assert.notDeepStrictEqual = notDeepStrictEqual; | |
function notDeepStrictEqual(actual, expected, message) { | |
if (_deepEqual(actual, expected, true)) { | |
fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); | |
} | |
} | |
// 9. The strict equality assertion tests strict equality, as determined by ===. | |
// assert.strictEqual(actual, expected, message_opt); | |
assert.strictEqual = function strictEqual(actual, expected, message) { | |
if (actual !== expected) { | |
fail(actual, expected, message, '===', assert.strictEqual); | |
} | |
}; | |
// 10. The strict non-equality assertion tests for strict inequality, as | |
// determined by !==. assert.notStrictEqual(actual, expected, message_opt); | |
assert.notStrictEqual = function notStrictEqual(actual, expected, message) { | |
if (actual === expected) { | |
fail(actual, expected, message, '!==', assert.notStrictEqual); | |
} | |
}; | |
function expectedException(actual, expected) { | |
if (!actual || !expected) { | |
return false; | |
} | |
if (Object.prototype.toString.call(expected) == '[object RegExp]') { | |
return expected.test(actual); | |
} | |
try { | |
if (actual instanceof expected) { | |
return true; | |
} | |
} catch (e) { | |
// Ignore. The instanceof check doesn't work for arrow functions. | |
} | |
if (Error.isPrototypeOf(expected)) { | |
return false; | |
} | |
return expected.call({}, actual) === true; | |
} | |
function _tryBlock(block) { | |
var error; | |
try { | |
block(); | |
} catch (e) { | |
error = e; | |
} | |
return error; | |
} | |
function _throws(shouldThrow, block, expected, message) { | |
var actual; | |
if (typeof block !== 'function') { | |
throw new TypeError('"block" argument must be a function'); | |
} | |
if (typeof expected === 'string') { | |
message = expected; | |
expected = null; | |
} | |
actual = _tryBlock(block); | |
message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + | |
(message ? ' ' + message : '.'); | |
if (shouldThrow && !actual) { | |
fail(actual, expected, 'Missing expected exception' + message); | |
} | |
var userProvidedMessage = typeof message === 'string'; | |
var isUnwantedException = !shouldThrow && util.isError(actual); | |
var isUnexpectedException = !shouldThrow && actual && !expected; | |
if ((isUnwantedException && | |
userProvidedMessage && | |
expectedException(actual, expected)) || | |
isUnexpectedException) { | |
fail(actual, expected, 'Got unwanted exception' + message); | |
} | |
if ((shouldThrow && actual && expected && | |
!expectedException(actual, expected)) || (!shouldThrow && actual)) { | |
throw actual; | |
} | |
} | |
// 11. Expected to throw an error: | |
// assert.throws(block, Error_opt, message_opt); | |
assert.throws = function(block, /*optional*/error, /*optional*/message) { | |
_throws(true, block, error, message); | |
}; | |
// EXTENSION! This is annoying to write outside this module. | |
assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { | |
_throws(false, block, error, message); | |
}; | |
assert.ifError = function(err) { if (err) throw err; }; | |
var objectKeys = Object.keys || function (obj) { | |
var keys = []; | |
for (var key in obj) { | |
if (hasOwn.call(obj, key)) keys.push(key); | |
} | |
return keys; | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$empty_2 = {}; | |
var _$after_7 = after | |
function after(count, callback, err_cb) { | |
var bail = false | |
err_cb = err_cb || __noop_7 | |
proxy.count = count | |
return (count === 0) ? callback() : proxy | |
function proxy(err, result) { | |
if (proxy.count <= 0) { | |
throw new Error('after called too many times') | |
} | |
--proxy.count | |
// after first error, rest are passed to err_cb | |
if (err) { | |
bail = true | |
callback(err) | |
// future error callbacks will go to error handler | |
callback = err_cb | |
} else if (proxy.count === 0 && !bail) { | |
callback(null, result) | |
} | |
} | |
} | |
function __noop_7() {} | |
/** | |
* An abstraction for slicing an arraybuffer even when | |
* ArrayBuffer.prototype.slice is not supported | |
* | |
* @api public | |
*/ | |
var _$arraybufferSlice_8 = function(arraybuffer, start, end) { | |
var bytes = arraybuffer.byteLength; | |
start = start || 0; | |
end = end || bytes; | |
if (arraybuffer.slice) { return arraybuffer.slice(start, end); } | |
if (start < 0) { start += bytes; } | |
if (end < 0) { end += bytes; } | |
if (end > bytes) { end = bytes; } | |
if (start >= bytes || start >= end || bytes === 0) { | |
return new ArrayBuffer(0); | |
} | |
var abv = new Uint8Array(arraybuffer); | |
var result = new Uint8Array(end - start); | |
for (var i = start, ii = 0; i < end; i++, ii++) { | |
result[ii] = abv[i]; | |
} | |
return result.buffer; | |
}; | |
/** | |
* Expose `Backoff`. | |
*/ | |
var _$Backoff_9 = Backoff; | |
/** | |
* Initialize backoff timer with `opts`. | |
* | |
* - `min` initial timeout in milliseconds [100] | |
* - `max` max timeout [10000] | |
* - `jitter` [0] | |
* - `factor` [2] | |
* | |
* @param {Object} opts | |
* @api public | |
*/ | |
function Backoff(opts) { | |
opts = opts || {}; | |
this.ms = opts.min || 100; | |
this.max = opts.max || 10000; | |
this.factor = opts.factor || 2; | |
this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0; | |
this.attempts = 0; | |
} | |
/** | |
* Return the backoff duration. | |
* | |
* @return {Number} | |
* @api public | |
*/ | |
Backoff.prototype.duration = function(){ | |
var ms = this.ms * Math.pow(this.factor, this.attempts++); | |
if (this.jitter) { | |
var rand = Math.random(); | |
var deviation = Math.floor(rand * this.jitter * ms); | |
ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation; | |
} | |
return Math.min(ms, this.max) | 0; | |
}; | |
/** | |
* Reset the number of attempts. | |
* | |
* @api public | |
*/ | |
Backoff.prototype.reset = function(){ | |
this.attempts = 0; | |
}; | |
/** | |
* Set the minimum duration | |
* | |
* @api public | |
*/ | |
Backoff.prototype.setMin = function(min){ | |
this.ms = min; | |
}; | |
/** | |
* Set the maximum duration | |
* | |
* @api public | |
*/ | |
Backoff.prototype.setMax = function(max){ | |
this.max = max; | |
}; | |
/** | |
* Set the jitter | |
* | |
* @api public | |
*/ | |
Backoff.prototype.setJitter = function(jitter){ | |
this.jitter = jitter; | |
}; | |
var _$base64Arraybuffer_10 = {}; | |
/* | |
* base64-arraybuffer | |
* https://github.com/niklasvh/base64-arraybuffer | |
* | |
* Copyright (c) 2012 Niklas von Hertzen | |
* Licensed under the MIT license. | |
*/ | |
(function(){ | |
"use strict"; | |
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
// Use a lookup table to find the index. | |
var lookup = new Uint8Array(256); | |
for (var i = 0; i < chars.length; i++) { | |
lookup[chars.charCodeAt(i)] = i; | |
} | |
_$base64Arraybuffer_10.encode = function(arraybuffer) { | |
var bytes = new Uint8Array(arraybuffer), | |
i, len = bytes.length, base64 = ""; | |
for (i = 0; i < len; i+=3) { | |
base64 += chars[bytes[i] >> 2]; | |
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; | |
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; | |
base64 += chars[bytes[i + 2] & 63]; | |
} | |
if ((len % 3) === 2) { | |
base64 = base64.substring(0, base64.length - 1) + "="; | |
} else if (len % 3 === 1) { | |
base64 = base64.substring(0, base64.length - 2) + "=="; | |
} | |
return base64; | |
}; | |
_$base64Arraybuffer_10.decode = function(base64) { | |
var bufferLength = base64.length * 0.75, | |
len = base64.length, i, p = 0, | |
encoded1, encoded2, encoded3, encoded4; | |
if (base64[base64.length - 1] === "=") { | |
bufferLength--; | |
if (base64[base64.length - 2] === "=") { | |
bufferLength--; | |
} | |
} | |
var arraybuffer = new ArrayBuffer(bufferLength), | |
bytes = new Uint8Array(arraybuffer); | |
for (i = 0; i < len; i+=4) { | |
encoded1 = lookup[base64.charCodeAt(i)]; | |
encoded2 = lookup[base64.charCodeAt(i+1)]; | |
encoded3 = lookup[base64.charCodeAt(i+2)]; | |
encoded4 = lookup[base64.charCodeAt(i+3)]; | |
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); | |
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); | |
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); | |
} | |
return arraybuffer; | |
}; | |
})(); | |
var _$doccy_36 = {}; | |
(function (global){ | |
var topLevel = typeof global !== 'undefined' ? global : | |
typeof window !== 'undefined' ? window : {} | |
var minDoc = _$empty_2; | |
var doccy; | |
if (typeof document !== 'undefined') { | |
doccy = document; | |
} else { | |
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4']; | |
if (!doccy) { | |
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc; | |
} | |
} | |
_$doccy_36 = doccy; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$attributeToProperty_40 = attributeToProperty | |
var transform = { | |
'class': 'className', | |
'for': 'htmlFor', | |
'http-equiv': 'httpEquiv' | |
} | |
function attributeToProperty (h) { | |
return function (tagName, attrs, children) { | |
for (var attr in attrs) { | |
if (attr in transform) { | |
attrs[transform[attr]] = attrs[attr] | |
delete attrs[attr] | |
} | |
} | |
return h(tagName, attrs, children) | |
} | |
} | |
/* removed: var _$attributeToProperty_40 = require('hyperscript-attribute-to-property') */; | |
var VAR = 0, TEXT = 1, OPEN = 2, CLOSE = 3, ATTR = 4 | |
var ATTR_KEY = 5, ATTR_KEY_W = 6 | |
var ATTR_VALUE_W = 7, ATTR_VALUE = 8 | |
var ATTR_VALUE_SQ = 9, ATTR_VALUE_DQ = 10 | |
var ATTR_EQ = 11, ATTR_BREAK = 12 | |
var COMMENT = 13 | |
var _$hyperx_41 = function (h, opts) { | |
if (!opts) opts = {} | |
var concat = opts.concat || function (a, b) { | |
return String(a) + String(b) | |
} | |
if (opts.attrToProp !== false) { | |
h = _$attributeToProperty_40(h) | |
} | |
return function (strings) { | |
var state = TEXT, reg = '' | |
var arglen = arguments.length | |
var parts = [] | |
for (var i = 0; i < strings.length; i++) { | |
if (i < arglen - 1) { | |
var arg = arguments[i+1] | |
var p = parse(strings[i]) | |
var xstate = state | |
if (xstate === ATTR_VALUE_DQ) xstate = ATTR_VALUE | |
if (xstate === ATTR_VALUE_SQ) xstate = ATTR_VALUE | |
if (xstate === ATTR_VALUE_W) xstate = ATTR_VALUE | |
if (xstate === ATTR) xstate = ATTR_KEY | |
p.push([ VAR, xstate, arg ]) | |
parts.push.apply(parts, p) | |
} else parts.push.apply(parts, parse(strings[i])) | |
} | |
var tree = [null,{},[]] | |
var stack = [[tree,-1]] | |
for (var i = 0; i < parts.length; i++) { | |
var cur = stack[stack.length-1][0] | |
var p = parts[i], s = p[0] | |
if (s === OPEN && /^\//.test(p[1])) { | |
var ix = stack[stack.length-1][1] | |
if (stack.length > 1) { | |
stack.pop() | |
stack[stack.length-1][0][2][ix] = h( | |
cur[0], cur[1], cur[2].length ? cur[2] : undefined | |
) | |
} | |
} else if (s === OPEN) { | |
var c = [p[1],{},[]] | |
cur[2].push(c) | |
stack.push([c,cur[2].length-1]) | |
} else if (s === ATTR_KEY || (s === VAR && p[1] === ATTR_KEY)) { | |
var key = '' | |
var copyKey | |
for (; i < parts.length; i++) { | |
if (parts[i][0] === ATTR_KEY) { | |
key = concat(key, parts[i][1]) | |
} else if (parts[i][0] === VAR && parts[i][1] === ATTR_KEY) { | |
if (typeof parts[i][2] === 'object' && !key) { | |
for (copyKey in parts[i][2]) { | |
if (parts[i][2].hasOwnProperty(copyKey) && !cur[1][copyKey]) { | |
cur[1][copyKey] = parts[i][2][copyKey] | |
} | |
} | |
} else { | |
key = concat(key, parts[i][2]) | |
} | |
} else break | |
} | |
if (parts[i][0] === ATTR_EQ) i++ | |
var j = i | |
for (; i < parts.length; i++) { | |
if (parts[i][0] === ATTR_VALUE || parts[i][0] === ATTR_KEY) { | |
if (!cur[1][key]) cur[1][key] = strfn(parts[i][1]) | |
else cur[1][key] = concat(cur[1][key], parts[i][1]) | |
} else if (parts[i][0] === VAR | |
&& (parts[i][1] === ATTR_VALUE || parts[i][1] === ATTR_KEY)) { | |
if (!cur[1][key]) cur[1][key] = strfn(parts[i][2]) | |
else cur[1][key] = concat(cur[1][key], parts[i][2]) | |
} else { | |
if (key.length && !cur[1][key] && i === j | |
&& (parts[i][0] === CLOSE || parts[i][0] === ATTR_BREAK)) { | |
// https://html.spec.whatwg.org/multipage/infrastructure.html#boolean-attributes | |
// empty string is falsy, not well behaved value in browser | |
cur[1][key] = key.toLowerCase() | |
} | |
break | |
} | |
} | |
} else if (s === ATTR_KEY) { | |
cur[1][p[1]] = true | |
} else if (s === VAR && p[1] === ATTR_KEY) { | |
cur[1][p[2]] = true | |
} else if (s === CLOSE) { | |
if (selfClosing(cur[0]) && stack.length) { | |
var ix = stack[stack.length-1][1] | |
stack.pop() | |
stack[stack.length-1][0][2][ix] = h( | |
cur[0], cur[1], cur[2].length ? cur[2] : undefined | |
) | |
} | |
} else if (s === VAR && p[1] === TEXT) { | |
if (p[2] === undefined || p[2] === null) p[2] = '' | |
else if (!p[2]) p[2] = concat('', p[2]) | |
if (Array.isArray(p[2][0])) { | |
cur[2].push.apply(cur[2], p[2]) | |
} else { | |
cur[2].push(p[2]) | |
} | |
} else if (s === TEXT) { | |
cur[2].push(p[1]) | |
} else if (s === ATTR_EQ || s === ATTR_BREAK) { | |
// no-op | |
} else { | |
throw new Error('unhandled: ' + s) | |
} | |
} | |
if (tree[2].length > 1 && /^\s*$/.test(tree[2][0])) { | |
tree[2].shift() | |
} | |
if (tree[2].length > 2 | |
|| (tree[2].length === 2 && /\S/.test(tree[2][1]))) { | |
throw new Error( | |
'multiple root elements must be wrapped in an enclosing tag' | |
) | |
} | |
if (Array.isArray(tree[2][0]) && typeof tree[2][0][0] === 'string' | |
&& Array.isArray(tree[2][0][2])) { | |
tree[2][0] = h(tree[2][0][0], tree[2][0][1], tree[2][0][2]) | |
} | |
return tree[2][0] | |
function parse (str) { | |
var res = [] | |
if (state === ATTR_VALUE_W) state = ATTR | |
for (var i = 0; i < str.length; i++) { | |
var c = str.charAt(i) | |
if (state === TEXT && c === '<') { | |
if (reg.length) res.push([TEXT, reg]) | |
reg = '' | |
state = OPEN | |
} else if (c === '>' && !quot(state) && state !== COMMENT) { | |
if (state === OPEN) { | |
res.push([OPEN,reg]) | |
} else if (state === ATTR_KEY) { | |
res.push([ATTR_KEY,reg]) | |
} else if (state === ATTR_VALUE && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
} | |
res.push([CLOSE]) | |
reg = '' | |
state = TEXT | |
} else if (state === COMMENT && /-$/.test(reg) && c === '-') { | |
if (opts.comments) { | |
res.push([ATTR_VALUE,reg.substr(0, reg.length - 1)],[CLOSE]) | |
} | |
reg = '' | |
state = TEXT | |
} else if (state === OPEN && /^!--$/.test(reg)) { | |
if (opts.comments) { | |
res.push([OPEN, reg],[ATTR_KEY,'comment'],[ATTR_EQ]) | |
} | |
reg = c | |
state = COMMENT | |
} else if (state === TEXT || state === COMMENT) { | |
reg += c | |
} else if (state === OPEN && /\s/.test(c)) { | |
res.push([OPEN, reg]) | |
reg = '' | |
state = ATTR | |
} else if (state === OPEN) { | |
reg += c | |
} else if (state === ATTR && /[^\s"'=/]/.test(c)) { | |
state = ATTR_KEY | |
reg = c | |
} else if (state === ATTR && /\s/.test(c)) { | |
if (reg.length) res.push([ATTR_KEY,reg]) | |
res.push([ATTR_BREAK]) | |
} else if (state === ATTR_KEY && /\s/.test(c)) { | |
res.push([ATTR_KEY,reg]) | |
reg = '' | |
state = ATTR_KEY_W | |
} else if (state === ATTR_KEY && c === '=') { | |
res.push([ATTR_KEY,reg],[ATTR_EQ]) | |
reg = '' | |
state = ATTR_VALUE_W | |
} else if (state === ATTR_KEY) { | |
reg += c | |
} else if ((state === ATTR_KEY_W || state === ATTR) && c === '=') { | |
res.push([ATTR_EQ]) | |
state = ATTR_VALUE_W | |
} else if ((state === ATTR_KEY_W || state === ATTR) && !/\s/.test(c)) { | |
res.push([ATTR_BREAK]) | |
if (/[\w-]/.test(c)) { | |
reg += c | |
state = ATTR_KEY | |
} else state = ATTR | |
} else if (state === ATTR_VALUE_W && c === '"') { | |
state = ATTR_VALUE_DQ | |
} else if (state === ATTR_VALUE_W && c === "'") { | |
state = ATTR_VALUE_SQ | |
} else if (state === ATTR_VALUE_DQ && c === '"') { | |
res.push([ATTR_VALUE,reg],[ATTR_BREAK]) | |
reg = '' | |
state = ATTR | |
} else if (state === ATTR_VALUE_SQ && c === "'") { | |
res.push([ATTR_VALUE,reg],[ATTR_BREAK]) | |
reg = '' | |
state = ATTR | |
} else if (state === ATTR_VALUE_W && !/\s/.test(c)) { | |
state = ATTR_VALUE | |
i-- | |
} else if (state === ATTR_VALUE && /\s/.test(c)) { | |
res.push([ATTR_VALUE,reg],[ATTR_BREAK]) | |
reg = '' | |
state = ATTR | |
} else if (state === ATTR_VALUE || state === ATTR_VALUE_SQ | |
|| state === ATTR_VALUE_DQ) { | |
reg += c | |
} | |
} | |
if (state === TEXT && reg.length) { | |
res.push([TEXT,reg]) | |
reg = '' | |
} else if (state === ATTR_VALUE && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
reg = '' | |
} else if (state === ATTR_VALUE_DQ && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
reg = '' | |
} else if (state === ATTR_VALUE_SQ && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
reg = '' | |
} else if (state === ATTR_KEY) { | |
res.push([ATTR_KEY,reg]) | |
reg = '' | |
} | |
return res | |
} | |
} | |
function strfn (x) { | |
if (typeof x === 'function') return x | |
else if (typeof x === 'string') return x | |
else if (x && typeof x === 'object') return x | |
else return concat('', x) | |
} | |
} | |
function quot (state) { | |
return state === ATTR_VALUE_SQ || state === ATTR_VALUE_DQ | |
} | |
var hasOwn = Object.prototype.hasOwnProperty | |
function has (obj, key) { return hasOwn.call(obj, key) } | |
var closeRE = RegExp('^(' + [ | |
'area', 'base', 'basefont', 'bgsound', 'br', 'col', 'command', 'embed', | |
'frame', 'hr', 'img', 'input', 'isindex', 'keygen', 'link', 'meta', 'param', | |
'source', 'track', 'wbr', '!--', | |
// SVG TAGS | |
'animate', 'animateTransform', 'circle', 'cursor', 'desc', 'ellipse', | |
'feBlend', 'feColorMatrix', 'feComposite', | |
'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', | |
'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', | |
'feGaussianBlur', 'feImage', 'feMergeNode', 'feMorphology', | |
'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', | |
'feTurbulence', 'font-face-format', 'font-face-name', 'font-face-uri', | |
'glyph', 'glyphRef', 'hkern', 'image', 'line', 'missing-glyph', 'mpath', | |
'path', 'polygon', 'polyline', 'rect', 'set', 'stop', 'tref', 'use', 'view', | |
'vkern' | |
].join('|') + ')(?:[\.#][a-zA-Z0-9\u007F-\uFFFF_:-]+)*$') | |
function selfClosing (tag) { return closeRE.test(tag) } | |
var _$win_37 = {}; | |
(function (global){ | |
var win; | |
if (typeof window !== "undefined") { | |
win = window; | |
} else if (typeof global !== "undefined") { | |
win = global; | |
} else if (typeof self !== "undefined"){ | |
win = self; | |
} else { | |
win = {}; | |
} | |
_$win_37 = win; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
/* global MutationObserver */ | |
/* removed: var _$doccy_36 = require('global/document') */; | |
/* removed: var _$win_37 = require('global/window') */; | |
var watch = Object.create(null) | |
var KEY_ID = 'onloadid' + (new Date() % 9e6).toString(36) | |
var KEY_ATTR = 'data-' + KEY_ID | |
var INDEX = 0 | |
if (_$win_37 && _$win_37.MutationObserver) { | |
var observer = new MutationObserver(function (mutations) { | |
if (Object.keys(watch).length < 1) return | |
for (var i = 0; i < mutations.length; i++) { | |
if (mutations[i].attributeName === KEY_ATTR) { | |
eachAttr(mutations[i], turnon, turnoff) | |
continue | |
} | |
eachMutation(mutations[i].removedNodes, turnoff) | |
eachMutation(mutations[i].addedNodes, turnon) | |
} | |
}) | |
observer.observe(_$doccy_36.body, { | |
childList: true, | |
subtree: true, | |
attributes: true, | |
attributeOldValue: true, | |
attributeFilter: [KEY_ATTR] | |
}) | |
} | |
var _$onload_50 = function onload (el, on, off, caller) { | |
on = on || function () {} | |
off = off || function () {} | |
el.setAttribute(KEY_ATTR, 'o' + INDEX) | |
watch['o' + INDEX] = [on, off, 0, caller || onload.caller] | |
INDEX += 1 | |
return el | |
} | |
function turnon (index, el) { | |
if (watch[index][0] && watch[index][2] === 0) { | |
watch[index][0](el) | |
watch[index][2] = 1 | |
} | |
} | |
function turnoff (index, el) { | |
if (watch[index][1] && watch[index][2] === 1) { | |
watch[index][1](el) | |
watch[index][2] = 0 | |
} | |
} | |
function eachAttr (mutation, on, off) { | |
var newValue = mutation.target.getAttribute(KEY_ATTR) | |
if (sameOrigin(mutation.oldValue, newValue)) { | |
watch[newValue] = watch[mutation.oldValue] | |
return | |
} | |
if (watch[mutation.oldValue]) { | |
off(mutation.oldValue, mutation.target) | |
} | |
if (watch[newValue]) { | |
on(newValue, mutation.target) | |
} | |
} | |
function sameOrigin (oldValue, newValue) { | |
if (!oldValue || !newValue) return false | |
return watch[oldValue][3] === watch[newValue][3] | |
} | |
function eachMutation (nodes, fn) { | |
var keys = Object.keys(watch) | |
for (var i = 0; i < nodes.length; i++) { | |
if (nodes[i] && nodes[i].getAttribute && nodes[i].getAttribute(KEY_ATTR)) { | |
var onloadid = nodes[i].getAttribute(KEY_ATTR) | |
keys.forEach(function (k) { | |
if (onloadid === k) { | |
fn(k, nodes[i]) | |
} | |
}) | |
} | |
if (nodes[i].childNodes.length > 0) { | |
eachMutation(nodes[i].childNodes, fn) | |
} | |
} | |
} | |
var _$bel_11 = {}; | |
/* removed: var _$doccy_36 = require('global/document') */; | |
/* removed: var _$hyperx_41 = require('hyperx') */; | |
/* removed: var _$onload_50 = require('on-load') */; | |
var SVGNS = 'http://www.w3.org/2000/svg' | |
var XLINKNS = 'http://www.w3.org/1999/xlink' | |
var BOOL_PROPS = { | |
autofocus: 1, | |
checked: 1, | |
defaultchecked: 1, | |
disabled: 1, | |
formnovalidate: 1, | |
indeterminate: 1, | |
readonly: 1, | |
required: 1, | |
selected: 1, | |
willvalidate: 1 | |
} | |
var COMMENT_TAG = '!--' | |
var SVG_TAGS = [ | |
'svg', | |
'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animate', 'animateColor', | |
'animateMotion', 'animateTransform', 'circle', 'clipPath', 'color-profile', | |
'cursor', 'defs', 'desc', 'ellipse', 'feBlend', 'feColorMatrix', | |
'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', | |
'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', | |
'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', | |
'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', | |
'feSpotLight', 'feTile', 'feTurbulence', 'filter', 'font', 'font-face', | |
'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', | |
'foreignObject', 'g', 'glyph', 'glyphRef', 'hkern', 'image', 'line', | |
'linearGradient', 'marker', 'mask', 'metadata', 'missing-glyph', 'mpath', | |
'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', | |
'set', 'stop', 'switch', 'symbol', 'text', 'textPath', 'title', 'tref', | |
'tspan', 'use', 'view', 'vkern' | |
] | |
function belCreateElement (tag, props, children) { | |
var el | |
// If an svg tag, it needs a namespace | |
if (SVG_TAGS.indexOf(tag) !== -1) { | |
props.namespace = SVGNS | |
} | |
// If we are using a namespace | |
var ns = false | |
if (props.namespace) { | |
ns = props.namespace | |
delete props.namespace | |
} | |
// Create the element | |
if (ns) { | |
el = _$doccy_36.createElementNS(ns, tag) | |
} else if (tag === COMMENT_TAG) { | |
return _$doccy_36.createComment(props.comment) | |
} else { | |
el = _$doccy_36.createElement(tag) | |
} | |
// If adding onload events | |
if (props.onload || props.onunload) { | |
var load = props.onload || function () {} | |
var unload = props.onunload || function () {} | |
_$onload_50(el, function belOnload () { | |
load(el) | |
}, function belOnunload () { | |
unload(el) | |
}, | |
// We have to use non-standard `caller` to find who invokes `belCreateElement` | |
belCreateElement.caller.caller.caller) | |
delete props.onload | |
delete props.onunload | |
} | |
// Create the properties | |
for (var p in props) { | |
if (props.hasOwnProperty(p)) { | |
var key = p.toLowerCase() | |
var val = props[p] | |
// Normalize className | |
if (key === 'classname') { | |
key = 'class' | |
p = 'class' | |
} | |
// The for attribute gets transformed to htmlFor, but we just set as for | |
if (p === 'htmlFor') { | |
p = 'for' | |
} | |
// If a property is boolean, set itself to the key | |
if (BOOL_PROPS[key]) { | |
if (val === 'true') val = key | |
else if (val === 'false') continue | |
} | |
// If a property prefers being set directly vs setAttribute | |
if (key.slice(0, 2) === 'on') { | |
el[p] = val | |
} else { | |
if (ns) { | |
if (p === 'xlink:href') { | |
el.setAttributeNS(XLINKNS, p, val) | |
} else if (/^xmlns($|:)/i.test(p)) { | |
// skip xmlns definitions | |
} else { | |
el.setAttributeNS(null, p, val) | |
} | |
} else { | |
el.setAttribute(p, val) | |
} | |
} | |
} | |
} | |
function appendChild (childs) { | |
if (!Array.isArray(childs)) return | |
for (var i = 0; i < childs.length; i++) { | |
var node = childs[i] | |
if (Array.isArray(node)) { | |
appendChild(node) | |
continue | |
} | |
if (typeof node === 'number' || | |
typeof node === 'boolean' || | |
typeof node === 'function' || | |
node instanceof Date || | |
node instanceof RegExp) { | |
node = node.toString() | |
} | |
if (typeof node === 'string') { | |
if (el.lastChild && el.lastChild.nodeName === '#text') { | |
el.lastChild.nodeValue += node | |
continue | |
} | |
node = _$doccy_36.createTextNode(node) | |
} | |
if (node && node.nodeType) { | |
el.appendChild(node) | |
} | |
} | |
} | |
appendChild(children) | |
return el | |
} | |
_$bel_11 = _$hyperx_41(belCreateElement, {comments: true}) | |
_$bel_11.default = _$bel_11 | |
_$bel_11.createElement = belCreateElement | |
var _$blob_12 = {}; | |
(function (global){ | |
/** | |
* Create a blob builder even when vendor prefixes exist | |
*/ | |
var BlobBuilder = global.BlobBuilder | |
|| global.WebKitBlobBuilder | |
|| global.MSBlobBuilder | |
|| global.MozBlobBuilder; | |
/** | |
* Check if Blob constructor is supported | |
*/ | |
var blobSupported = (function() { | |
try { | |
var a = new Blob(['hi']); | |
return a.size === 2; | |
} catch(e) { | |
return false; | |
} | |
})(); | |
/** | |
* Check if Blob constructor supports ArrayBufferViews | |
* Fails in Safari 6, so we need to map to ArrayBuffers there. | |
*/ | |
var blobSupportsArrayBufferView = blobSupported && (function() { | |
try { | |
var b = new Blob([new Uint8Array([1,2])]); | |
return b.size === 2; | |
} catch(e) { | |
return false; | |
} | |
})(); | |
/** | |
* Check if BlobBuilder is supported | |
*/ | |
var blobBuilderSupported = BlobBuilder | |
&& BlobBuilder.prototype.append | |
&& BlobBuilder.prototype.getBlob; | |
/** | |
* Helper function that maps ArrayBufferViews to ArrayBuffers | |
* Used by BlobBuilder constructor and old browsers that didn't | |
* support it in the Blob constructor. | |
*/ | |
function mapArrayBufferViews(ary) { | |
for (var i = 0; i < ary.length; i++) { | |
var chunk = ary[i]; | |
if (chunk.buffer instanceof ArrayBuffer) { | |
var buf = chunk.buffer; | |
// if this is a subarray, make a copy so we only | |
// include the subarray region from the underlying buffer | |
if (chunk.byteLength !== buf.byteLength) { | |
var copy = new Uint8Array(chunk.byteLength); | |
copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength)); | |
buf = copy.buffer; | |
} | |
ary[i] = buf; | |
} | |
} | |
} | |
function BlobBuilderConstructor(ary, options) { | |
options = options || {}; | |
var bb = new BlobBuilder(); | |
mapArrayBufferViews(ary); | |
for (var i = 0; i < ary.length; i++) { | |
bb.append(ary[i]); | |
} | |
return (options.type) ? bb.getBlob(options.type) : bb.getBlob(); | |
}; | |
function BlobConstructor(ary, options) { | |
mapArrayBufferViews(ary); | |
return new Blob(ary, options || {}); | |
}; | |
_$blob_12 = (function() { | |
if (blobSupported) { | |
return blobSupportsArrayBufferView ? global.Blob : BlobConstructor; | |
} else if (blobBuilderSupported) { | |
return BlobBuilderConstructor; | |
} else { | |
return undefined; | |
} | |
})(); | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
/** | |
* Slice reference. | |
*/ | |
var slice = [].slice; | |
/** | |
* Bind `obj` to `fn`. | |
* | |
* @param {Object} obj | |
* @param {Function|String} fn or string | |
* @return {Function} | |
* @api public | |
*/ | |
var _$componentBind_13 = function(obj, fn){ | |
if ('string' == typeof fn) fn = obj[fn]; | |
if ('function' != typeof fn) throw new Error('bind() requires a function'); | |
var args = slice.call(arguments, 2); | |
return function(){ | |
return fn.apply(obj, args.concat(slice.call(arguments))); | |
} | |
}; | |
var _$componentEmitter_14 = { exports: {} }; | |
/** | |
* Expose `Emitter`. | |
*/ | |
if ("object" !== 'undefined') { | |
_$componentEmitter_14.exports = Emitter; | |
} | |
/** | |
* Initialize a new `Emitter`. | |
* | |
* @api public | |
*/ | |
function Emitter(obj) { | |
if (obj) return mixin(obj); | |
}; | |
/** | |
* Mixin the emitter properties. | |
* | |
* @param {Object} obj | |
* @return {Object} | |
* @api private | |
*/ | |
function mixin(obj) { | |
for (var key in Emitter.prototype) { | |
obj[key] = Emitter.prototype[key]; | |
} | |
return obj; | |
} | |
/** | |
* Listen on the given `event` with `fn`. | |
* | |
* @param {String} event | |
* @param {Function} fn | |
* @return {Emitter} | |
* @api public | |
*/ | |
Emitter.prototype.on = | |
Emitter.prototype.addEventListener = function(event, fn){ | |
this._callbacks = this._callbacks || {}; | |
(this._callbacks['$' + event] = this._callbacks['$' + event] || []) | |
.push(fn); | |
return this; | |
}; | |
/** | |
* Adds an `event` listener that will be invoked a single | |
* time then automatically removed. | |
* | |
* @param {String} event | |
* @param {Function} fn | |
* @return {Emitter} | |
* @api public | |
*/ | |
Emitter.prototype.once = function(event, fn){ | |
function on() { | |
this.off(event, on); | |
fn.apply(this, arguments); | |
} | |
on.fn = fn; | |
this.on(event, on); | |
return this; | |
}; | |
/** | |
* Remove the given callback for `event` or all | |
* registered callbacks. | |
* | |
* @param {String} event | |
* @param {Function} fn | |
* @return {Emitter} | |
* @api public | |
*/ | |
Emitter.prototype.off = | |
Emitter.prototype.removeListener = | |
Emitter.prototype.removeAllListeners = | |
Emitter.prototype.removeEventListener = function(event, fn){ | |
this._callbacks = this._callbacks || {}; | |
// all | |
if (0 == arguments.length) { | |
this._callbacks = {}; | |
return this; | |
} | |
// specific event | |
var callbacks = this._callbacks['$' + event]; | |
if (!callbacks) return this; | |
// remove all handlers | |
if (1 == arguments.length) { | |
delete this._callbacks['$' + event]; | |
return this; | |
} | |
// remove specific handler | |
var cb; | |
for (var i = 0; i < callbacks.length; i++) { | |
cb = callbacks[i]; | |
if (cb === fn || cb.fn === fn) { | |
callbacks.splice(i, 1); | |
break; | |
} | |
} | |
return this; | |
}; | |
/** | |
* Emit `event` with the given args. | |
* | |
* @param {String} event | |
* @param {Mixed} ... | |
* @return {Emitter} | |
*/ | |
Emitter.prototype.emit = function(event){ | |
this._callbacks = this._callbacks || {}; | |
var args = [].slice.call(arguments, 1) | |
, callbacks = this._callbacks['$' + event]; | |
if (callbacks) { | |
callbacks = callbacks.slice(0); | |
for (var i = 0, len = callbacks.length; i < len; ++i) { | |
callbacks[i].apply(this, args); | |
} | |
} | |
return this; | |
}; | |
/** | |
* Return array of callbacks for `event`. | |
* | |
* @param {String} event | |
* @return {Array} | |
* @api public | |
*/ | |
Emitter.prototype.listeners = function(event){ | |
this._callbacks = this._callbacks || {}; | |
return this._callbacks['$' + event] || []; | |
}; | |
/** | |
* Check if this emitter has `event` handlers. | |
* | |
* @param {String} event | |
* @return {Boolean} | |
* @api public | |
*/ | |
Emitter.prototype.hasListeners = function(event){ | |
return !! this.listeners(event).length; | |
}; | |
_$componentEmitter_14 = _$componentEmitter_14.exports | |
var _$componentInherit_15 = function(a, b){ | |
var fn = function(){}; | |
fn.prototype = b.prototype; | |
a.prototype = new fn; | |
a.prototype.constructor = a; | |
}; | |
var _$browserCuid_16 = { exports: {} }; | |
/** | |
* cuid.js | |
* Collision-resistant UID generator for browsers and node. | |
* Sequential for fast db lookups and recency sorting. | |
* Safe for element IDs and server-side lookups. | |
* | |
* Extracted from CLCTR | |
* | |
* Copyright (c) Eric Elliott 2012 | |
* MIT License | |
*/ | |
/*global window, navigator, document, require, process, module */ | |
(function (app) { | |
'use strict'; | |
var namespace = 'cuid', | |
c = 0, | |
blockSize = 4, | |
base = 36, | |
discreteValues = Math.pow(base, blockSize), | |
pad = function pad(num, size) { | |
var s = "000000000" + num; | |
return s.substr(s.length-size); | |
}, | |
randomBlock = function randomBlock() { | |
return pad((Math.random() * | |
discreteValues << 0) | |
.toString(base), blockSize); | |
}, | |
safeCounter = function () { | |
c = (c < discreteValues) ? c : 0; | |
c++; // this is not subliminal | |
return c - 1; | |
}, | |
api = function cuid() { | |
// Starting with a lowercase letter makes | |
// it HTML element ID friendly. | |
var letter = 'c', // hard-coded allows for sequential access | |
// timestamp | |
// warning: this exposes the exact date and time | |
// that the uid was created. | |
timestamp = (new Date().getTime()).toString(base), | |
// Prevent same-machine collisions. | |
counter, | |
// A few chars to generate distinct ids for different | |
// clients (so different computers are far less | |
// likely to generate the same id) | |
fingerprint = api.fingerprint(), | |
// Grab some more chars from Math.random() | |
random = randomBlock() + randomBlock(); | |
counter = pad(safeCounter().toString(base), blockSize); | |
return (letter + timestamp + counter + fingerprint + random); | |
}; | |
api.slug = function slug() { | |
var date = new Date().getTime().toString(36), | |
counter, | |
print = api.fingerprint().slice(0,1) + | |
api.fingerprint().slice(-1), | |
random = randomBlock().slice(-2); | |
counter = safeCounter().toString(36).slice(-4); | |
return date.slice(-2) + | |
counter + print + random; | |
}; | |
api.globalCount = function globalCount() { | |
// We want to cache the results of this | |
var cache = (function calc() { | |
var i, | |
count = 0; | |
for (i in window) { | |
count++; | |
} | |
return count; | |
}()); | |
api.globalCount = function () { return cache; }; | |
return cache; | |
}; | |
api.fingerprint = function browserPrint() { | |
return pad((navigator.mimeTypes.length + | |
navigator.userAgent.length).toString(36) + | |
api.globalCount().toString(36), 4); | |
}; | |
// don't change anything from here down. | |
if (app.register) { | |
app.register(namespace, api); | |
} else if ("object" !== 'undefined') { | |
_$browserCuid_16.exports = api; | |
} else { | |
app[namespace] = api; | |
} | |
}(this.applitude || this)); | |
_$browserCuid_16 = _$browserCuid_16.exports | |
/** | |
* Helpers. | |
*/ | |
var s = 1000 | |
var m = s * 60 | |
var h = m * 60 | |
var d = h * 24 | |
var y = d * 365.25 | |
/** | |
* Parse or format the given `val`. | |
* | |
* Options: | |
* | |
* - `long` verbose formatting [false] | |
* | |
* @param {String|Number} val | |
* @param {Object} [options] | |
* @throws {Error} throw an error if val is not a non-empty string or a number | |
* @return {String|Number} | |
* @api public | |
*/ | |
var _$ms_47 = function (val, options) { | |
options = options || {} | |
var type = typeof val | |
if (type === 'string' && val.length > 0) { | |
return parse(val) | |
} else if (type === 'number' && isNaN(val) === false) { | |
return options.long ? | |
fmtLong(val) : | |
fmtShort(val) | |
} | |
throw new Error('val is not a non-empty string or a valid number. val=' + JSON.stringify(val)) | |
} | |
/** | |
* Parse the given `str` and return milliseconds. | |
* | |
* @param {String} str | |
* @return {Number} | |
* @api private | |
*/ | |
function parse(str) { | |
str = String(str) | |
if (str.length > 10000) { | |
return | |
} | |
var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str) | |
if (!match) { | |
return | |
} | |
var n = parseFloat(match[1]) | |
var type = (match[2] || 'ms').toLowerCase() | |
switch (type) { | |
case 'years': | |
case 'year': | |
case 'yrs': | |
case 'yr': | |
case 'y': | |
return n * y | |
case 'days': | |
case 'day': | |
case 'd': | |
return n * d | |
case 'hours': | |
case 'hour': | |
case 'hrs': | |
case 'hr': | |
case 'h': | |
return n * h | |
case 'minutes': | |
case 'minute': | |
case 'mins': | |
case 'min': | |
case 'm': | |
return n * m | |
case 'seconds': | |
case 'second': | |
case 'secs': | |
case 'sec': | |
case 's': | |
return n * s | |
case 'milliseconds': | |
case 'millisecond': | |
case 'msecs': | |
case 'msec': | |
case 'ms': | |
return n | |
default: | |
return undefined | |
} | |
} | |
/** | |
* Short format for `ms`. | |
* | |
* @param {Number} ms | |
* @return {String} | |
* @api private | |
*/ | |
function fmtShort(ms) { | |
if (ms >= d) { | |
return Math.round(ms / d) + 'd' | |
} | |
if (ms >= h) { | |
return Math.round(ms / h) + 'h' | |
} | |
if (ms >= m) { | |
return Math.round(ms / m) + 'm' | |
} | |
if (ms >= s) { | |
return Math.round(ms / s) + 's' | |
} | |
return ms + 'ms' | |
} | |
/** | |
* Long format for `ms`. | |
* | |
* @param {Number} ms | |
* @return {String} | |
* @api private | |
*/ | |
function fmtLong(ms) { | |
return plural(ms, d, 'day') || | |
plural(ms, h, 'hour') || | |
plural(ms, m, 'minute') || | |
plural(ms, s, 'second') || | |
ms + ' ms' | |
} | |
/** | |
* Pluralization helper. | |
*/ | |
function plural(ms, n, name) { | |
if (ms < n) { | |
return | |
} | |
if (ms < n * 1.5) { | |
return Math.floor(ms / n) + ' ' + name | |
} | |
return Math.ceil(ms / n) + ' ' + name + 's' | |
} | |
var _$debug_18 = {}; | |
/** | |
* This is the common logic for both the Node.js and web browser | |
* implementations of `debug()`. | |
* | |
* Expose `debug()` as the module. | |
*/ | |
_$debug_18 = _$debug_18 = createDebug.debug = createDebug['default'] = createDebug; | |
_$debug_18.coerce = coerce; | |
_$debug_18.disable = disable; | |
_$debug_18.enable = enable; | |
_$debug_18.enabled = enabled; | |
_$debug_18.humanize = _$ms_47; | |
/** | |
* The currently active debug mode names, and names to skip. | |
*/ | |
_$debug_18.names = []; | |
_$debug_18.skips = []; | |
/** | |
* Map of special "%n" handling functions, for the debug "format" argument. | |
* | |
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". | |
*/ | |
_$debug_18.formatters = {}; | |
/** | |
* Previous log timestamp. | |
*/ | |
var prevTime; | |
/** | |
* Select a color. | |
* @param {String} namespace | |
* @return {Number} | |
* @api private | |
*/ | |
function selectColor(namespace) { | |
var hash = 0, i; | |
for (i in namespace) { | |
hash = ((hash << 5) - hash) + namespace.charCodeAt(i); | |
hash |= 0; // Convert to 32bit integer | |
} | |
return _$debug_18.colors[Math.abs(hash) % _$debug_18.colors.length]; | |
} | |
/** | |
* Create a debugger with the given `namespace`. | |
* | |
* @param {String} namespace | |
* @return {Function} | |
* @api public | |
*/ | |
function createDebug(namespace) { | |
function debug() { | |
// disabled? | |
if (!debug.enabled) return; | |
var self = debug; | |
// set `diff` timestamp | |
var curr = +new Date(); | |
var ms = curr - (prevTime || curr); | |
self.diff = ms; | |
self.prev = prevTime; | |
self.curr = curr; | |
prevTime = curr; | |
// turn the `arguments` into a proper Array | |
var args = new Array(arguments.length); | |
for (var i = 0; i < args.length; i++) { | |
args[i] = arguments[i]; | |
} | |
args[0] = _$debug_18.coerce(args[0]); | |
if ('string' !== typeof args[0]) { | |
// anything else let's inspect with %O | |
args.unshift('%O'); | |
} | |
// apply any `formatters` transformations | |
var index = 0; | |
args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { | |
// if we encounter an escaped % then don't increase the array index | |
if (match === '%%') return match; | |
index++; | |
var formatter = _$debug_18.formatters[format]; | |
if ('function' === typeof formatter) { | |
var val = args[index]; | |
match = formatter.call(self, val); | |
// now we need to remove `args[index]` since it's inlined in the `format` | |
args.splice(index, 1); | |
index--; | |
} | |
return match; | |
}); | |
// apply env-specific formatting (colors, etc.) | |
_$debug_18.formatArgs.call(self, args); | |
var logFn = debug.log || _$debug_18.log || console.log.bind(console); | |
logFn.apply(self, args); | |
} | |
debug.namespace = namespace; | |
debug.enabled = _$debug_18.enabled(namespace); | |
debug.useColors = _$debug_18.useColors(); | |
debug.color = selectColor(namespace); | |
// env-specific initialization logic for debug instances | |
if ('function' === typeof _$debug_18.init) { | |
_$debug_18.init(debug); | |
} | |
return debug; | |
} | |
/** | |
* Enables a debug mode by namespaces. This can include modes | |
* separated by a colon and wildcards. | |
* | |
* @param {String} namespaces | |
* @api public | |
*/ | |
function enable(namespaces) { | |
_$debug_18.save(namespaces); | |
_$debug_18.names = []; | |
_$debug_18.skips = []; | |
var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); | |
var len = split.length; | |
for (var i = 0; i < len; i++) { | |
if (!split[i]) continue; // ignore empty strings | |
namespaces = split[i].replace(/\*/g, '.*?'); | |
if (namespaces[0] === '-') { | |
_$debug_18.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); | |
} else { | |
_$debug_18.names.push(new RegExp('^' + namespaces + '$')); | |
} | |
} | |
} | |
/** | |
* Disable debug output. | |
* | |
* @api public | |
*/ | |
function disable() { | |
_$debug_18.enable(''); | |
} | |
/** | |
* Returns true if the given mode name is enabled, false otherwise. | |
* | |
* @param {String} name | |
* @return {Boolean} | |
* @api public | |
*/ | |
function enabled(name) { | |
var i, len; | |
for (i = 0, len = _$debug_18.skips.length; i < len; i++) { | |
if (_$debug_18.skips[i].test(name)) { | |
return false; | |
} | |
} | |
for (i = 0, len = _$debug_18.names.length; i < len; i++) { | |
if (_$debug_18.names[i].test(name)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* Coerce `val`. | |
* | |
* @param {Mixed} val | |
* @return {Mixed} | |
* @api private | |
*/ | |
function coerce(val) { | |
if (val instanceof Error) return val.stack || val.message; | |
return val; | |
} | |
var _$browser_17 = {}; | |
(function (process){ | |
/** | |
* This is the web browser implementation of `debug()`. | |
* | |
* Expose `debug()` as the module. | |
*/ | |
_$browser_17 = _$browser_17 = _$debug_18; | |
_$browser_17.log = log; | |
_$browser_17.formatArgs = formatArgs; | |
_$browser_17.save = save; | |
_$browser_17.load = load; | |
_$browser_17.useColors = useColors; | |
_$browser_17.storage = 'undefined' != typeof chrome | |
&& 'undefined' != typeof chrome.storage | |
? chrome.storage.local | |
: localstorage(); | |
/** | |
* Colors. | |
*/ | |
_$browser_17.colors = [ | |
'lightseagreen', | |
'forestgreen', | |
'goldenrod', | |
'dodgerblue', | |
'darkorchid', | |
'crimson' | |
]; | |
/** | |
* Currently only WebKit-based Web Inspectors, Firefox >= v31, | |
* and the Firebug extension (any Firefox version) are known | |
* to support "%c" CSS customizations. | |
* | |
* TODO: add a `localStorage` variable to explicitly enable/disable colors | |
*/ | |
function useColors() { | |
// NB: In an Electron preload script, document will be defined but not fully | |
// initialized. Since we know we're in Chrome, we'll just detect this case | |
// explicitly | |
if (typeof window !== 'undefined' && window && typeof window.process !== 'undefined' && window.process.type === 'renderer') { | |
return true; | |
} | |
// is webkit? http://stackoverflow.com/a/16459606/376773 | |
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 | |
return (typeof document !== 'undefined' && document && 'WebkitAppearance' in document.documentElement.style) || | |
// is firebug? http://stackoverflow.com/a/398120/376773 | |
(typeof window !== 'undefined' && window && window.console && (console.firebug || (console.exception && console.table))) || | |
// is firefox >= v31? | |
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages | |
(typeof navigator !== 'undefined' && navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || | |
// double check webkit in userAgent just in case we are in a worker | |
(typeof navigator !== 'undefined' && navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); | |
} | |
/** | |
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. | |
*/ | |
_$browser_17.formatters.j = function(v) { | |
try { | |
return JSON.stringify(v); | |
} catch (err) { | |
return '[UnexpectedJSONParseError]: ' + err.message; | |
} | |
}; | |
/** | |
* Colorize log arguments if enabled. | |
* | |
* @api public | |
*/ | |
function formatArgs(args) { | |
var useColors = this.useColors; | |
args[0] = (useColors ? '%c' : '') | |
+ this.namespace | |
+ (useColors ? ' %c' : ' ') | |
+ args[0] | |
+ (useColors ? '%c ' : ' ') | |
+ '+' + _$browser_17.humanize(this.diff); | |
if (!useColors) return; | |
var c = 'color: ' + this.color; | |
args.splice(1, 0, c, 'color: inherit') | |
// the final "%c" is somewhat tricky, because there could be other | |
// arguments passed either before or after the %c, so we need to | |
// figure out the correct index to insert the CSS into | |
var index = 0; | |
var lastC = 0; | |
args[0].replace(/%[a-zA-Z%]/g, function(match) { | |
if ('%%' === match) return; | |
index++; | |
if ('%c' === match) { | |
// we only are interested in the *last* %c | |
// (the user may have provided their own) | |
lastC = index; | |
} | |
}); | |
args.splice(lastC, 0, c); | |
} | |
/** | |
* Invokes `console.log()` when available. | |
* No-op when `console.log` is not a "function". | |
* | |
* @api public | |
*/ | |
function log() { | |
// this hackery is required for IE8/9, where | |
// the `console.log` function doesn't have 'apply' | |
return 'object' === typeof console | |
&& console.log | |
&& Function.prototype.apply.call(console.log, console, arguments); | |
} | |
/** | |
* Save `namespaces`. | |
* | |
* @param {String} namespaces | |
* @api private | |
*/ | |
function save(namespaces) { | |
try { | |
if (null == namespaces) { | |
_$browser_17.storage.removeItem('debug'); | |
} else { | |
_$browser_17.storage.debug = namespaces; | |
} | |
} catch(e) {} | |
} | |
/** | |
* Load `namespaces`. | |
* | |
* @return {String} returns the previously persisted debug modes | |
* @api private | |
*/ | |
function load() { | |
var r; | |
try { | |
r = _$browser_17.storage.debug; | |
} catch(e) {} | |
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG | |
if (!r && typeof process !== 'undefined' && 'env' in process) { | |
r = process.env.DEBUG; | |
} | |
return r; | |
} | |
/** | |
* Enable namespaces listed in `localStorage.debug` initially. | |
*/ | |
_$browser_17.enable(load()); | |
/** | |
* Localstorage attempts to return the localstorage. | |
* | |
* This is necessary because safari throws | |
* when a user disables cookies/localstorage | |
* and you attempt to access it. | |
* | |
* @return {LocalStorage} | |
* @api private | |
*/ | |
function localstorage() { | |
try { | |
return window.localStorage; | |
} catch (e) {} | |
} | |
}).call(this,_$browser_3) | |
var _$flatten_34 = function flatten(list, depth) { | |
depth = (typeof depth == 'number') ? depth : Infinity; | |
if (!depth) { | |
if (Array.isArray(list)) { | |
return list.map(function(i) { return i; }); | |
} | |
return list; | |
} | |
return _flatten(list, 1); | |
function _flatten(list, d) { | |
return list.reduce(function (acc, item) { | |
if (Array.isArray(item) && d < depth) { | |
return acc.concat(_flatten(item, d + 1)); | |
} | |
else { | |
return acc.concat(item); | |
} | |
}, []); | |
} | |
}; | |
var _$runParallel_58 = {}; | |
(function (process){ | |
_$runParallel_58 = function (tasks, cb) { | |
var results, pending, keys | |
var isSync = true | |
if (Array.isArray(tasks)) { | |
results = [] | |
pending = tasks.length | |
} else { | |
keys = Object.keys(tasks) | |
results = {} | |
pending = keys.length | |
} | |
function done (err) { | |
function end () { | |
if (cb) cb(err, results) | |
cb = null | |
} | |
if (isSync) process.nextTick(end) | |
else end() | |
} | |
function each (i, err, result) { | |
results[i] = result | |
if (--pending === 0 || err) { | |
done(err) | |
} | |
} | |
if (!pending) { | |
// empty | |
done(null) | |
} else if (keys) { | |
// object | |
keys.forEach(function (key) { | |
tasks[key](function (err, result) { each(key, err, result) }) | |
}) | |
} else { | |
// array | |
tasks.forEach(function (task, i) { | |
task(function (err, result) { each(i, err, result) }) | |
}) | |
} | |
isSync = false | |
} | |
}).call(this,_$browser_3) | |
var _$dragDrop_19 = dragDrop | |
/* removed: var _$flatten_34 = require('flatten') */; | |
/* removed: var _$runParallel_58 = require('run-parallel') */; | |
function dragDrop (elem, listeners) { | |
if (typeof elem === 'string') { | |
var selector = elem | |
elem = window.document.querySelector(elem) | |
if (!elem) { | |
throw new Error('"' + selector + '" does not match any HTML elements') | |
} | |
} | |
if (!elem) { | |
throw new Error('"' + elem + '" is not a valid HTML element') | |
} | |
if (typeof listeners === 'function') { | |
listeners = { onDrop: listeners } | |
} | |
var timeout | |
elem.addEventListener('dragenter', onDragEnter, false) | |
elem.addEventListener('dragover', onDragOver, false) | |
elem.addEventListener('dragleave', onDragLeave, false) | |
elem.addEventListener('drop', onDrop, false) | |
// Function to remove drag-drop listeners | |
return function remove () { | |
removeDragClass() | |
elem.removeEventListener('dragenter', onDragEnter, false) | |
elem.removeEventListener('dragover', onDragOver, false) | |
elem.removeEventListener('dragleave', onDragLeave, false) | |
elem.removeEventListener('drop', onDrop, false) | |
} | |
function onDragEnter (e) { | |
if (listeners.onDragEnter) { | |
listeners.onDragEnter(e) | |
} | |
// Prevent event | |
e.stopPropagation() | |
e.preventDefault() | |
return false | |
} | |
function onDragOver (e) { | |
e.stopPropagation() | |
e.preventDefault() | |
if (e.dataTransfer.items) { | |
// Only add "drag" class when `items` contains items that are able to be | |
// handled by the registered listeners (files vs. text) | |
var items = toArray(e.dataTransfer.items) | |
var fileItems = items.filter(function (item) { return item.kind === 'file' }) | |
var textItems = items.filter(function (item) { return item.kind === 'string' }) | |
if (fileItems.length === 0 && !listeners.onDropText) return | |
if (textItems.length === 0 && !listeners.onDrop) return | |
if (fileItems.length === 0 && textItems.length === 0) return | |
} | |
elem.classList.add('drag') | |
clearTimeout(timeout) | |
if (listeners.onDragOver) { | |
listeners.onDragOver(e) | |
} | |
e.dataTransfer.dropEffect = 'copy' | |
return false | |
} | |
function onDragLeave (e) { | |
e.stopPropagation() | |
e.preventDefault() | |
if (listeners.onDragLeave) { | |
listeners.onDragLeave(e) | |
} | |
clearTimeout(timeout) | |
timeout = setTimeout(removeDragClass, 50) | |
return false | |
} | |
function onDrop (e) { | |
e.stopPropagation() | |
e.preventDefault() | |
if (listeners.onDragLeave) { | |
listeners.onDragLeave(e) | |
} | |
clearTimeout(timeout) | |
removeDragClass() | |
var pos = { | |
x: e.clientX, | |
y: e.clientY | |
} | |
// text drop support | |
var text = e.dataTransfer.getData('text') | |
if (text && listeners.onDropText) { | |
listeners.onDropText(text, pos) | |
} | |
// file drop support | |
if (e.dataTransfer.items) { | |
// Handle directories in Chrome using the proprietary FileSystem API | |
var items = toArray(e.dataTransfer.items).filter(function (item) { | |
return item.kind === 'file' | |
}) | |
if (items.length === 0) return | |
_$runParallel_58(items.map(function (item) { | |
return function (cb) { | |
processEntry(item.webkitGetAsEntry(), cb) | |
} | |
}), function (err, results) { | |
// This catches permission errors with file:// in Chrome. This should never | |
// throw in production code, so the user does not need to use try-catch. | |
if (err) throw err | |
if (listeners.onDrop) { | |
listeners.onDrop(_$flatten_34(results), pos) | |
} | |
}) | |
} else { | |
var files = toArray(e.dataTransfer.files) | |
if (files.length === 0) return | |
files.forEach(function (file) { | |
file.fullPath = '/' + file.name | |
}) | |
if (listeners.onDrop) { | |
listeners.onDrop(files, pos) | |
} | |
} | |
return false | |
} | |
function removeDragClass () { | |
elem.classList.remove('drag') | |
} | |
} | |
function processEntry (entry, cb) { | |
var entries = [] | |
if (entry.isFile) { | |
entry.file(function (file) { | |
file.fullPath = entry.fullPath // preserve pathing for consumer | |
cb(null, file) | |
}, function (err) { | |
cb(err) | |
}) | |
} else if (entry.isDirectory) { | |
var reader = entry.createReader() | |
readEntries() | |
} | |
function readEntries () { | |
reader.readEntries(function (entries_) { | |
if (entries_.length > 0) { | |
entries = entries.concat(toArray(entries_)) | |
readEntries() // continue reading entries until `readEntries` returns no more | |
} else { | |
doneEntries() | |
} | |
}) | |
} | |
function doneEntries () { | |
_$runParallel_58(entries.map(function (entry) { | |
return function (cb) { | |
processEntry(entry, cb) | |
} | |
}), cb) | |
} | |
} | |
function toArray (list) { | |
return Array.prototype.slice.call(list || [], 0) | |
} | |
/** | |
* Gets the keys for an object. | |
* | |
* @return {Array} keys | |
* @api private | |
*/ | |
var _$keys_31 = Object.keys || function keys (obj){ | |
var arr = []; | |
var has = Object.prototype.hasOwnProperty; | |
for (var i in obj) { | |
if (has.call(obj, i)) { | |
arr.push(i); | |
} | |
} | |
return arr; | |
}; | |
var _$utf8_32 = { exports: {} }; | |
(function (global){ | |
/*! https://mths.be/utf8js v2.1.2 by @mathias */ | |
;(function(root) { | |
// Detect free variables `exports` | |
var freeExports = typeof _$utf8_32.exports == 'object' && _$utf8_32.exports; | |
// Detect free variable `module` | |
var freeModule = "object" == 'object' && _$utf8_32 && | |
_$utf8_32.exports == freeExports && _$utf8_32; | |
// Detect free variable `global`, from Node.js or Browserified code, | |
// and use it as `root` | |
var freeGlobal = typeof global == 'object' && global; | |
if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { | |
root = freeGlobal; | |
} | |
/*--------------------------------------------------------------------------*/ | |
var stringFromCharCode = String.fromCharCode; | |
// Taken from https://mths.be/punycode | |
function ucs2decode(string) { | |
var output = []; | |
var counter = 0; | |
var length = string.length; | |
var value; | |
var extra; | |
while (counter < length) { | |
value = string.charCodeAt(counter++); | |
if (value >= 0xD800 && value <= 0xDBFF && counter < length) { | |
// high surrogate, and there is a next character | |
extra = string.charCodeAt(counter++); | |
if ((extra & 0xFC00) == 0xDC00) { // low surrogate | |
output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); | |
} else { | |
// unmatched surrogate; only append this code unit, in case the next | |
// code unit is the high surrogate of a surrogate pair | |
output.push(value); | |
counter--; | |
} | |
} else { | |
output.push(value); | |
} | |
} | |
return output; | |
} | |
// Taken from https://mths.be/punycode | |
function ucs2encode(array) { | |
var length = array.length; | |
var index = -1; | |
var value; | |
var output = ''; | |
while (++index < length) { | |
value = array[index]; | |
if (value > 0xFFFF) { | |
value -= 0x10000; | |
output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); | |
value = 0xDC00 | value & 0x3FF; | |
} | |
output += stringFromCharCode(value); | |
} | |
return output; | |
} | |
function checkScalarValue(codePoint, strict) { | |
if (codePoint >= 0xD800 && codePoint <= 0xDFFF) { | |
if (strict) { | |
throw Error( | |
'Lone surrogate U+' + codePoint.toString(16).toUpperCase() + | |
' is not a scalar value' | |
); | |
} | |
return false; | |
} | |
return true; | |
} | |
/*--------------------------------------------------------------------------*/ | |
function createByte(codePoint, shift) { | |
return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80); | |
} | |
function encodeCodePoint(codePoint, strict) { | |
if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence | |
return stringFromCharCode(codePoint); | |
} | |
var symbol = ''; | |
if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence | |
symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0); | |
} | |
else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence | |
if (!checkScalarValue(codePoint, strict)) { | |
codePoint = 0xFFFD; | |
} | |
symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0); | |
symbol += createByte(codePoint, 6); | |
} | |
else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence | |
symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0); | |
symbol += createByte(codePoint, 12); | |
symbol += createByte(codePoint, 6); | |
} | |
symbol += stringFromCharCode((codePoint & 0x3F) | 0x80); | |
return symbol; | |
} | |
function utf8encode(string, opts) { | |
opts = opts || {}; | |
var strict = false !== opts.strict; | |
var codePoints = ucs2decode(string); | |
var length = codePoints.length; | |
var index = -1; | |
var codePoint; | |
var byteString = ''; | |
while (++index < length) { | |
codePoint = codePoints[index]; | |
byteString += encodeCodePoint(codePoint, strict); | |
} | |
return byteString; | |
} | |
/*--------------------------------------------------------------------------*/ | |
function readContinuationByte() { | |
if (byteIndex >= byteCount) { | |
throw Error('Invalid byte index'); | |
} | |
var continuationByte = byteArray[byteIndex] & 0xFF; | |
byteIndex++; | |
if ((continuationByte & 0xC0) == 0x80) { | |
return continuationByte & 0x3F; | |
} | |
// If we end up here, it’s not a continuation byte | |
throw Error('Invalid continuation byte'); | |
} | |
function decodeSymbol(strict) { | |
var byte1; | |
var byte2; | |
var byte3; | |
var byte4; | |
var codePoint; | |
if (byteIndex > byteCount) { | |
throw Error('Invalid byte index'); | |
} | |
if (byteIndex == byteCount) { | |
return false; | |
} | |
// Read first byte | |
byte1 = byteArray[byteIndex] & 0xFF; | |
byteIndex++; | |
// 1-byte sequence (no continuation bytes) | |
if ((byte1 & 0x80) == 0) { | |
return byte1; | |
} | |
// 2-byte sequence | |
if ((byte1 & 0xE0) == 0xC0) { | |
byte2 = readContinuationByte(); | |
codePoint = ((byte1 & 0x1F) << 6) | byte2; | |
if (codePoint >= 0x80) { | |
return codePoint; | |
} else { | |
throw Error('Invalid continuation byte'); | |
} | |
} | |
// 3-byte sequence (may include unpaired surrogates) | |
if ((byte1 & 0xF0) == 0xE0) { | |
byte2 = readContinuationByte(); | |
byte3 = readContinuationByte(); | |
codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; | |
if (codePoint >= 0x0800) { | |
return checkScalarValue(codePoint, strict) ? codePoint : 0xFFFD; | |
} else { | |
throw Error('Invalid continuation byte'); | |
} | |
} | |
// 4-byte sequence | |
if ((byte1 & 0xF8) == 0xF0) { | |
byte2 = readContinuationByte(); | |
byte3 = readContinuationByte(); | |
byte4 = readContinuationByte(); | |
codePoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0C) | | |
(byte3 << 0x06) | byte4; | |
if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) { | |
return codePoint; | |
} | |
} | |
throw Error('Invalid UTF-8 detected'); | |
} | |
var byteArray; | |
var byteCount; | |
var byteIndex; | |
function utf8decode(byteString, opts) { | |
opts = opts || {}; | |
var strict = false !== opts.strict; | |
byteArray = ucs2decode(byteString); | |
byteCount = byteArray.length; | |
byteIndex = 0; | |
var codePoints = []; | |
var tmp; | |
while ((tmp = decodeSymbol(strict)) !== false) { | |
codePoints.push(tmp); | |
} | |
return ucs2encode(codePoints); | |
} | |
/*--------------------------------------------------------------------------*/ | |
var utf8 = { | |
'version': '2.1.2', | |
'encode': utf8encode, | |
'decode': utf8decode | |
}; | |
// Some AMD build optimizers, like r.js, check for specific condition patterns | |
// like the following: | |
if ( | |
typeof define == 'function' && | |
typeof define.amd == 'object' && | |
define.amd | |
) { | |
define(function() { | |
return utf8; | |
}); | |
} else if (freeExports && !freeExports.nodeType) { | |
if (freeModule) { // in Node.js or RingoJS v0.8.0+ | |
freeModule.exports = utf8; | |
} else { // in Narwhal or RingoJS v0.7.0- | |
var object = {}; | |
var hasOwnProperty = object.hasOwnProperty; | |
for (var key in utf8) { | |
hasOwnProperty.call(utf8, key) && (freeExports[key] = utf8[key]); | |
} | |
} | |
} else { // in Rhino or a web browser | |
root.utf8 = utf8; | |
} | |
}(this)); | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
_$utf8_32 = _$utf8_32.exports | |
var toString = {}.toString; | |
var _$isarray_43 = Array.isArray || function (arr) { | |
return toString.call(arr) == '[object Array]'; | |
}; | |
var _$hasBinary_38 = {}; | |
(function (global){ | |
/* global Blob File */ | |
/* | |
* Module requirements. | |
*/ | |
var isArray = _$isarray_43; | |
var toString = Object.prototype.toString; | |
var withNativeBlob = typeof global.Blob === 'function' || toString.call(global.Blob) === '[object BlobConstructor]'; | |
var withNativeFile = typeof global.File === 'function' || toString.call(global.File) === '[object FileConstructor]'; | |
/** | |
* Module exports. | |
*/ | |
_$hasBinary_38 = hasBinary; | |
/** | |
* Checks for binary data. | |
* | |
* Supports Buffer, ArrayBuffer, Blob and File. | |
* | |
* @param {Object} anything | |
* @api public | |
*/ | |
function hasBinary (obj) { | |
if (!obj || typeof obj !== 'object') { | |
return false; | |
} | |
if (isArray(obj)) { | |
for (var i = 0, l = obj.length; i < l; i++) { | |
if (hasBinary(obj[i])) { | |
return true; | |
} | |
} | |
return false; | |
} | |
if ((typeof global.Buffer === 'function' && global.Buffer.isBuffer && global.Buffer.isBuffer(obj)) || | |
(typeof global.ArrayBuffer === 'function' && obj instanceof ArrayBuffer) || | |
(withNativeBlob && obj instanceof Blob) || | |
(withNativeFile && obj instanceof File) | |
) { | |
return true; | |
} | |
// see: https://github.com/Automattic/has-binary/pull/4 | |
if (obj.toJSON && typeof obj.toJSON === 'function' && arguments.length === 1) { | |
return hasBinary(obj.toJSON(), true); | |
} | |
for (var key in obj) { | |
if (Object.prototype.hasOwnProperty.call(obj, key) && hasBinary(obj[key])) { | |
return true; | |
} | |
} | |
return false; | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$browser_30 = {}; | |
(function (global){ | |
/** | |
* Module dependencies. | |
*/ | |
var keys = _$keys_31; | |
var hasBinary = _$hasBinary_38; | |
var sliceBuffer = _$arraybufferSlice_8; | |
var after = _$after_7; | |
var utf8 = _$utf8_32; | |
var base64encoder; | |
if (global && global.ArrayBuffer) { | |
base64encoder = _$base64Arraybuffer_10; | |
} | |
/** | |
* Check if we are running an android browser. That requires us to use | |
* ArrayBuffer with polling transports... | |
* | |
* http://ghinda.net/jpeg-blob-ajax-android/ | |
*/ | |
var isAndroid = typeof navigator !== 'undefined' && /Android/i.test(navigator.userAgent); | |
/** | |
* Check if we are running in PhantomJS. | |
* Uploading a Blob with PhantomJS does not work correctly, as reported here: | |
* https://github.com/ariya/phantomjs/issues/11395 | |
* @type boolean | |
*/ | |
var isPhantomJS = typeof navigator !== 'undefined' && /PhantomJS/i.test(navigator.userAgent); | |
/** | |
* When true, avoids using Blobs to encode payloads. | |
* @type boolean | |
*/ | |
var dontSendBlobs = isAndroid || isPhantomJS; | |
/** | |
* Current protocol version. | |
*/ | |
_$browser_30.protocol = 3; | |
/** | |
* Packet types. | |
*/ | |
var packets = _$browser_30.packets = { | |
open: 0 // non-ws | |
, close: 1 // non-ws | |
, ping: 2 | |
, pong: 3 | |
, message: 4 | |
, upgrade: 5 | |
, noop: 6 | |
}; | |
var packetslist = keys(packets); | |
/** | |
* Premade error packet. | |
*/ | |
var err = { type: 'error', data: 'parser error' }; | |
/** | |
* Create a blob api even for blob builder when vendor prefixes exist | |
*/ | |
var Blob = _$blob_12; | |
/** | |
* Encodes a packet. | |
* | |
* <packet type id> [ <data> ] | |
* | |
* Example: | |
* | |
* 5hello world | |
* 3 | |
* 4 | |
* | |
* Binary is encoded in an identical principle | |
* | |
* @api private | |
*/ | |
_$browser_30.encodePacket = function (packet, supportsBinary, utf8encode, callback) { | |
if (typeof supportsBinary === 'function') { | |
callback = supportsBinary; | |
supportsBinary = false; | |
} | |
if (typeof utf8encode === 'function') { | |
callback = utf8encode; | |
utf8encode = null; | |
} | |
var data = (packet.data === undefined) | |
? undefined | |
: packet.data.buffer || packet.data; | |
if (global.ArrayBuffer && data instanceof ArrayBuffer) { | |
return encodeArrayBuffer(packet, supportsBinary, callback); | |
} else if (Blob && data instanceof global.Blob) { | |
return encodeBlob(packet, supportsBinary, callback); | |
} | |
// might be an object with { base64: true, data: dataAsBase64String } | |
if (data && data.base64) { | |
return encodeBase64Object(packet, callback); | |
} | |
// Sending data as a utf-8 string | |
var encoded = packets[packet.type]; | |
// data fragment is optional | |
if (undefined !== packet.data) { | |
encoded += utf8encode ? utf8.encode(String(packet.data), { strict: false }) : String(packet.data); | |
} | |
return callback('' + encoded); | |
}; | |
function encodeBase64Object(packet, callback) { | |
// packet data is an object { base64: true, data: dataAsBase64String } | |
var message = 'b' + _$browser_30.packets[packet.type] + packet.data.data; | |
return callback(message); | |
} | |
/** | |
* Encode packet helpers for binary types | |
*/ | |
function encodeArrayBuffer(packet, supportsBinary, callback) { | |
if (!supportsBinary) { | |
return _$browser_30.encodeBase64Packet(packet, callback); | |
} | |
var data = packet.data; | |
var contentArray = new Uint8Array(data); | |
var resultBuffer = new Uint8Array(1 + data.byteLength); | |
resultBuffer[0] = packets[packet.type]; | |
for (var i = 0; i < contentArray.length; i++) { | |
resultBuffer[i+1] = contentArray[i]; | |
} | |
return callback(resultBuffer.buffer); | |
} | |
function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) { | |
if (!supportsBinary) { | |
return _$browser_30.encodeBase64Packet(packet, callback); | |
} | |
var fr = new FileReader(); | |
fr.onload = function() { | |
packet.data = fr.result; | |
_$browser_30.encodePacket(packet, supportsBinary, true, callback); | |
}; | |
return fr.readAsArrayBuffer(packet.data); | |
} | |
function encodeBlob(packet, supportsBinary, callback) { | |
if (!supportsBinary) { | |
return _$browser_30.encodeBase64Packet(packet, callback); | |
} | |
if (dontSendBlobs) { | |
return encodeBlobAsArrayBuffer(packet, supportsBinary, callback); | |
} | |
var length = new Uint8Array(1); | |
length[0] = packets[packet.type]; | |
var blob = new Blob([length.buffer, packet.data]); | |
return callback(blob); | |
} | |
/** | |
* Encodes a packet with binary data in a base64 string | |
* | |
* @param {Object} packet, has `type` and `data` | |
* @return {String} base64 encoded message | |
*/ | |
_$browser_30.encodeBase64Packet = function(packet, callback) { | |
var message = 'b' + _$browser_30.packets[packet.type]; | |
if (Blob && packet.data instanceof global.Blob) { | |
var fr = new FileReader(); | |
fr.onload = function() { | |
var b64 = fr.result.split(',')[1]; | |
callback(message + b64); | |
}; | |
return fr.readAsDataURL(packet.data); | |
} | |
var b64data; | |
try { | |
b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data)); | |
} catch (e) { | |
// iPhone Safari doesn't let you apply with typed arrays | |
var typed = new Uint8Array(packet.data); | |
var basic = new Array(typed.length); | |
for (var i = 0; i < typed.length; i++) { | |
basic[i] = typed[i]; | |
} | |
b64data = String.fromCharCode.apply(null, basic); | |
} | |
message += global.btoa(b64data); | |
return callback(message); | |
}; | |
/** | |
* Decodes a packet. Changes format to Blob if requested. | |
* | |
* @return {Object} with `type` and `data` (if any) | |
* @api private | |
*/ | |
_$browser_30.decodePacket = function (data, binaryType, utf8decode) { | |
if (data === undefined) { | |
return err; | |
} | |
// String data | |
if (typeof data === 'string') { | |
if (data.charAt(0) === 'b') { | |
return _$browser_30.decodeBase64Packet(data.substr(1), binaryType); | |
} | |
if (utf8decode) { | |
data = tryDecode(data); | |
if (data === false) { | |
return err; | |
} | |
} | |
var type = data.charAt(0); | |
if (Number(type) != type || !packetslist[type]) { | |
return err; | |
} | |
if (data.length > 1) { | |
return { type: packetslist[type], data: data.substring(1) }; | |
} else { | |
return { type: packetslist[type] }; | |
} | |
} | |
var asArray = new Uint8Array(data); | |
var type = asArray[0]; | |
var rest = sliceBuffer(data, 1); | |
if (Blob && binaryType === 'blob') { | |
rest = new Blob([rest]); | |
} | |
return { type: packetslist[type], data: rest }; | |
}; | |
function tryDecode(data) { | |
try { | |
data = utf8.decode(data, { strict: false }); | |
} catch (e) { | |
return false; | |
} | |
return data; | |
} | |
/** | |
* Decodes a packet encoded in a base64 string | |
* | |
* @param {String} base64 encoded message | |
* @return {Object} with `type` and `data` (if any) | |
*/ | |
_$browser_30.decodeBase64Packet = function(msg, binaryType) { | |
var type = packetslist[msg.charAt(0)]; | |
if (!base64encoder) { | |
return { type: type, data: { base64: true, data: msg.substr(1) } }; | |
} | |
var data = base64encoder.decode(msg.substr(1)); | |
if (binaryType === 'blob' && Blob) { | |
data = new Blob([data]); | |
} | |
return { type: type, data: data }; | |
}; | |
/** | |
* Encodes multiple messages (payload). | |
* | |
* <length>:data | |
* | |
* Example: | |
* | |
* 11:hello world2:hi | |
* | |
* If any contents are binary, they will be encoded as base64 strings. Base64 | |
* encoded strings are marked with a b before the length specifier | |
* | |
* @param {Array} packets | |
* @api private | |
*/ | |
_$browser_30.encodePayload = function (packets, supportsBinary, callback) { | |
if (typeof supportsBinary === 'function') { | |
callback = supportsBinary; | |
supportsBinary = null; | |
} | |
var isBinary = hasBinary(packets); | |
if (supportsBinary && isBinary) { | |
if (Blob && !dontSendBlobs) { | |
return _$browser_30.encodePayloadAsBlob(packets, callback); | |
} | |
return _$browser_30.encodePayloadAsArrayBuffer(packets, callback); | |
} | |
if (!packets.length) { | |
return callback('0:'); | |
} | |
function setLengthHeader(message) { | |
return message.length + ':' + message; | |
} | |
function encodeOne(packet, doneCallback) { | |
_$browser_30.encodePacket(packet, !isBinary ? false : supportsBinary, false, function(message) { | |
doneCallback(null, setLengthHeader(message)); | |
}); | |
} | |
map(packets, encodeOne, function(err, results) { | |
return callback(results.join('')); | |
}); | |
}; | |
/** | |
* Async array map using after | |
*/ | |
function map(ary, each, done) { | |
var result = new Array(ary.length); | |
var next = after(ary.length, done); | |
var eachWithIndex = function(i, el, cb) { | |
each(el, function(error, msg) { | |
result[i] = msg; | |
cb(error, result); | |
}); | |
}; | |
for (var i = 0; i < ary.length; i++) { | |
eachWithIndex(i, ary[i], next); | |
} | |
} | |
/* | |
* Decodes data when a payload is maybe expected. Possible binary contents are | |
* decoded from their base64 representation | |
* | |
* @param {String} data, callback method | |
* @api public | |
*/ | |
_$browser_30.decodePayload = function (data, binaryType, callback) { | |
if (typeof data !== 'string') { | |
return _$browser_30.decodePayloadAsBinary(data, binaryType, callback); | |
} | |
if (typeof binaryType === 'function') { | |
callback = binaryType; | |
binaryType = null; | |
} | |
var packet; | |
if (data === '') { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
var length = '', n, msg; | |
for (var i = 0, l = data.length; i < l; i++) { | |
var chr = data.charAt(i); | |
if (chr !== ':') { | |
length += chr; | |
continue; | |
} | |
if (length === '' || (length != (n = Number(length)))) { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
msg = data.substr(i + 1, n); | |
if (length != msg.length) { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
if (msg.length) { | |
packet = _$browser_30.decodePacket(msg, binaryType, false); | |
if (err.type === packet.type && err.data === packet.data) { | |
// parser error in individual packet - ignoring payload | |
return callback(err, 0, 1); | |
} | |
var ret = callback(packet, i + n, l); | |
if (false === ret) return; | |
} | |
// advance cursor | |
i += n; | |
length = ''; | |
} | |
if (length !== '') { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
}; | |
/** | |
* Encodes multiple messages (payload) as binary. | |
* | |
* <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number | |
* 255><data> | |
* | |
* Example: | |
* 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers | |
* | |
* @param {Array} packets | |
* @return {ArrayBuffer} encoded payload | |
* @api private | |
*/ | |
_$browser_30.encodePayloadAsArrayBuffer = function(packets, callback) { | |
if (!packets.length) { | |
return callback(new ArrayBuffer(0)); | |
} | |
function encodeOne(packet, doneCallback) { | |
_$browser_30.encodePacket(packet, true, true, function(data) { | |
return doneCallback(null, data); | |
}); | |
} | |
map(packets, encodeOne, function(err, encodedPackets) { | |
var totalLength = encodedPackets.reduce(function(acc, p) { | |
var len; | |
if (typeof p === 'string'){ | |
len = p.length; | |
} else { | |
len = p.byteLength; | |
} | |
return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2 | |
}, 0); | |
var resultArray = new Uint8Array(totalLength); | |
var bufferIndex = 0; | |
encodedPackets.forEach(function(p) { | |
var isString = typeof p === 'string'; | |
var ab = p; | |
if (isString) { | |
var view = new Uint8Array(p.length); | |
for (var i = 0; i < p.length; i++) { | |
view[i] = p.charCodeAt(i); | |
} | |
ab = view.buffer; | |
} | |
if (isString) { // not true binary | |
resultArray[bufferIndex++] = 0; | |
} else { // true binary | |
resultArray[bufferIndex++] = 1; | |
} | |
var lenStr = ab.byteLength.toString(); | |
for (var i = 0; i < lenStr.length; i++) { | |
resultArray[bufferIndex++] = parseInt(lenStr[i]); | |
} | |
resultArray[bufferIndex++] = 255; | |
var view = new Uint8Array(ab); | |
for (var i = 0; i < view.length; i++) { | |
resultArray[bufferIndex++] = view[i]; | |
} | |
}); | |
return callback(resultArray.buffer); | |
}); | |
}; | |
/** | |
* Encode as Blob | |
*/ | |
_$browser_30.encodePayloadAsBlob = function(packets, callback) { | |
function encodeOne(packet, doneCallback) { | |
_$browser_30.encodePacket(packet, true, true, function(encoded) { | |
var binaryIdentifier = new Uint8Array(1); | |
binaryIdentifier[0] = 1; | |
if (typeof encoded === 'string') { | |
var view = new Uint8Array(encoded.length); | |
for (var i = 0; i < encoded.length; i++) { | |
view[i] = encoded.charCodeAt(i); | |
} | |
encoded = view.buffer; | |
binaryIdentifier[0] = 0; | |
} | |
var len = (encoded instanceof ArrayBuffer) | |
? encoded.byteLength | |
: encoded.size; | |
var lenStr = len.toString(); | |
var lengthAry = new Uint8Array(lenStr.length + 1); | |
for (var i = 0; i < lenStr.length; i++) { | |
lengthAry[i] = parseInt(lenStr[i]); | |
} | |
lengthAry[lenStr.length] = 255; | |
if (Blob) { | |
var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]); | |
doneCallback(null, blob); | |
} | |
}); | |
} | |
map(packets, encodeOne, function(err, results) { | |
return callback(new Blob(results)); | |
}); | |
}; | |
/* | |
* Decodes data when a payload is maybe expected. Strings are decoded by | |
* interpreting each byte as a key code for entries marked to start with 0. See | |
* description of encodePayloadAsBinary | |
* | |
* @param {ArrayBuffer} data, callback method | |
* @api public | |
*/ | |
_$browser_30.decodePayloadAsBinary = function (data, binaryType, callback) { | |
if (typeof binaryType === 'function') { | |
callback = binaryType; | |
binaryType = null; | |
} | |
var bufferTail = data; | |
var buffers = []; | |
while (bufferTail.byteLength > 0) { | |
var tailArray = new Uint8Array(bufferTail); | |
var isString = tailArray[0] === 0; | |
var msgLength = ''; | |
for (var i = 1; ; i++) { | |
if (tailArray[i] === 255) break; | |
// 310 = char length of Number.MAX_VALUE | |
if (msgLength.length > 310) { | |
return callback(err, 0, 1); | |
} | |
msgLength += tailArray[i]; | |
} | |
bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length); | |
msgLength = parseInt(msgLength); | |
var msg = sliceBuffer(bufferTail, 0, msgLength); | |
if (isString) { | |
try { | |
msg = String.fromCharCode.apply(null, new Uint8Array(msg)); | |
} catch (e) { | |
// iPhone Safari doesn't let you apply to typed arrays | |
var typed = new Uint8Array(msg); | |
msg = ''; | |
for (var i = 0; i < typed.length; i++) { | |
msg += String.fromCharCode(typed[i]); | |
} | |
} | |
} | |
buffers.push(msg); | |
bufferTail = sliceBuffer(bufferTail, msgLength); | |
} | |
var total = buffers.length; | |
buffers.forEach(function(buffer, i) { | |
callback(_$browser_30.decodePacket(buffer, binaryType, true), i, total); | |
}); | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
/** | |
* Module dependencies. | |
*/ | |
/* removed: var _$browser_30 = require('engine.io-parser'); */; | |
/* removed: var _$componentEmitter_14 = require('component-emitter'); */; | |
/** | |
* Module exports. | |
*/ | |
var _$Transport_23 = Transport; | |
/** | |
* Transport abstract constructor. | |
* | |
* @param {Object} options. | |
* @api private | |
*/ | |
function Transport (opts) { | |
this.path = opts.path; | |
this.hostname = opts.hostname; | |
this.port = opts.port; | |
this.secure = opts.secure; | |
this.query = opts.query; | |
this.timestampParam = opts.timestampParam; | |
this.timestampRequests = opts.timestampRequests; | |
this.readyState = ''; | |
this.agent = opts.agent || false; | |
this.socket = opts.socket; | |
this.enablesXDR = opts.enablesXDR; | |
// SSL options for Node.js client | |
this.pfx = opts.pfx; | |
this.key = opts.key; | |
this.passphrase = opts.passphrase; | |
this.cert = opts.cert; | |
this.ca = opts.ca; | |
this.ciphers = opts.ciphers; | |
this.rejectUnauthorized = opts.rejectUnauthorized; | |
this.forceNode = opts.forceNode; | |
// other options for Node.js client | |
this.extraHeaders = opts.extraHeaders; | |
this.localAddress = opts.localAddress; | |
} | |
/** | |
* Mix in `Emitter`. | |
*/ | |
_$componentEmitter_14(Transport.prototype); | |
/** | |
* Emits an error. | |
* | |
* @param {String} str | |
* @return {Transport} for chaining | |
* @api public | |
*/ | |
Transport.prototype.onError = function (msg, desc) { | |
var err = new Error(msg); | |
err.type = 'TransportError'; | |
err.description = desc; | |
this.emit('error', err); | |
return this; | |
}; | |
/** | |
* Opens the transport. | |
* | |
* @api public | |
*/ | |
Transport.prototype.open = function () { | |
if ('closed' === this.readyState || '' === this.readyState) { | |
this.readyState = 'opening'; | |
this.doOpen(); | |
} | |
return this; | |
}; | |
/** | |
* Closes the transport. | |
* | |
* @api private | |
*/ | |
Transport.prototype.close = function () { | |
if ('opening' === this.readyState || 'open' === this.readyState) { | |
this.doClose(); | |
this.onClose(); | |
} | |
return this; | |
}; | |
/** | |
* Sends multiple packets. | |
* | |
* @param {Array} packets | |
* @api private | |
*/ | |
Transport.prototype.send = function (packets) { | |
if ('open' === this.readyState) { | |
this.write(packets); | |
} else { | |
throw new Error('Transport not open'); | |
} | |
}; | |
/** | |
* Called upon open | |
* | |
* @api private | |
*/ | |
Transport.prototype.onOpen = function () { | |
this.readyState = 'open'; | |
this.writable = true; | |
this.emit('open'); | |
}; | |
/** | |
* Called with data. | |
* | |
* @param {String} data | |
* @api private | |
*/ | |
Transport.prototype.onData = function (data) { | |
var packet = _$browser_30.decodePacket(data, this.socket.binaryType); | |
this.onPacket(packet); | |
}; | |
/** | |
* Called with a decoded packet. | |
*/ | |
Transport.prototype.onPacket = function (packet) { | |
this.emit('packet', packet); | |
}; | |
/** | |
* Called upon close. | |
* | |
* @api private | |
*/ | |
Transport.prototype.onClose = function () { | |
this.readyState = 'closed'; | |
this.emit('close'); | |
}; | |
var _$hasCors_39 = {}; | |
/** | |
* Module exports. | |
* | |
* Logic borrowed from Modernizr: | |
* | |
* - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js | |
*/ | |
try { | |
_$hasCors_39 = typeof XMLHttpRequest !== 'undefined' && | |
'withCredentials' in new XMLHttpRequest(); | |
} catch (err) { | |
// if XMLHttp support is disabled in IE then it will throw | |
// when trying to create | |
_$hasCors_39 = false; | |
} | |
var _$xmlhttprequest_29 = {}; | |
(function (global){ | |
// browser shim for xmlhttprequest module | |
var hasCORS = _$hasCors_39; | |
_$xmlhttprequest_29 = function (opts) { | |
var xdomain = opts.xdomain; | |
// scheme must be same when usign XDomainRequest | |
// http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx | |
var xscheme = opts.xscheme; | |
// XDomainRequest has a flow of not sending cookie, therefore it should be disabled as a default. | |
// https://github.com/Automattic/engine.io-client/pull/217 | |
var enablesXDR = opts.enablesXDR; | |
// XMLHttpRequest can be disabled on IE | |
try { | |
if ('undefined' !== typeof XMLHttpRequest && (!xdomain || hasCORS)) { | |
return new XMLHttpRequest(); | |
} | |
} catch (e) { } | |
// Use XDomainRequest for IE8 if enablesXDR is true | |
// because loading bar keeps flashing when using jsonp-polling | |
// https://github.com/yujiosaka/socke.io-ie8-loading-example | |
try { | |
if ('undefined' !== typeof XDomainRequest && !xscheme && enablesXDR) { | |
return new XDomainRequest(); | |
} | |
} catch (e) { } | |
if (!xdomain) { | |
try { | |
return new global[['Active'].concat('Object').join('X')]('Microsoft.XMLHTTP'); | |
} catch (e) { } | |
} | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$parseqs_52 = {}; | |
/** | |
* Compiles a querystring | |
* Returns string representation of the object | |
* | |
* @param {Object} | |
* @api private | |
*/ | |
_$parseqs_52.encode = function (obj) { | |
var str = ''; | |
for (var i in obj) { | |
if (obj.hasOwnProperty(i)) { | |
if (str.length) str += '&'; | |
str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]); | |
} | |
} | |
return str; | |
}; | |
/** | |
* Parses a simple querystring into an object | |
* | |
* @param {String} qs | |
* @api private | |
*/ | |
_$parseqs_52.decode = function(qs){ | |
var qry = {}; | |
var pairs = qs.split('&'); | |
for (var i = 0, l = pairs.length; i < l; i++) { | |
var pair = pairs[i].split('='); | |
qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); | |
} | |
return qry; | |
}; | |
'use strict'; | |
var alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split('') | |
, length = 64 | |
, map = {} | |
, seed = 0 | |
, i = 0 | |
, prev; | |
/** | |
* Return a string representing the specified number. | |
* | |
* @param {Number} num The number to convert. | |
* @returns {String} The string representation of the number. | |
* @api public | |
*/ | |
function encode(num) { | |
var encoded = ''; | |
do { | |
encoded = alphabet[num % length] + encoded; | |
num = Math.floor(num / length); | |
} while (num > 0); | |
return encoded; | |
} | |
/** | |
* Return the integer value specified by the given string. | |
* | |
* @param {String} str The string to convert. | |
* @returns {Number} The integer value represented by the string. | |
* @api public | |
*/ | |
function decode(str) { | |
var decoded = 0; | |
for (i = 0; i < str.length; i++) { | |
decoded = decoded * length + map[str.charAt(i)]; | |
} | |
return decoded; | |
} | |
/** | |
* Yeast: A tiny growing id generator. | |
* | |
* @returns {String} A unique id. | |
* @api public | |
*/ | |
function yeast() { | |
var now = encode(+new Date()); | |
if (now !== prev) return seed = 0, prev = now; | |
return now +'.'+ encode(seed++); | |
} | |
// | |
// Map each character to its index. | |
// | |
for (; i < length; i++) map[alphabet[i]] = i; | |
// | |
// Expose the `yeast`, `encode` and `decode` functions. | |
// | |
yeast.encode = encode; | |
yeast.decode = decode; | |
var _$yeast_79 = yeast; | |
/** | |
* Module dependencies. | |
*/ | |
/* removed: var _$Transport_23 = require('../transport'); */; | |
/* removed: var _$parseqs_52 = require('parseqs'); */; | |
/* removed: var _$browser_30 = require('engine.io-parser'); */; | |
/* removed: var _$componentInherit_15 = require('component-inherit'); */; | |
/* removed: var _$yeast_79 = require('yeast'); */; | |
var debug = _$browser_17('engine.io-client:polling'); | |
/** | |
* Module exports. | |
*/ | |
var _$Polling_27 = Polling; | |
/** | |
* Is XHR2 supported? | |
*/ | |
var hasXHR2 = (function () { | |
var XMLHttpRequest = _$xmlhttprequest_29; | |
var xhr = new XMLHttpRequest({ xdomain: false }); | |
return null != xhr.responseType; | |
})(); | |
/** | |
* Polling interface. | |
* | |
* @param {Object} opts | |
* @api private | |
*/ | |
function Polling (opts) { | |
var forceBase64 = (opts && opts.forceBase64); | |
if (!hasXHR2 || forceBase64) { | |
this.supportsBinary = false; | |
} | |
_$Transport_23.call(this, opts); | |
} | |
/** | |
* Inherits from Transport. | |
*/ | |
_$componentInherit_15(Polling, _$Transport_23); | |
/** | |
* Transport name. | |
*/ | |
Polling.prototype.name = 'polling'; | |
/** | |
* Opens the socket (triggers polling). We write a PING message to determine | |
* when the transport is open. | |
* | |
* @api private | |
*/ | |
Polling.prototype.doOpen = function () { | |
this.poll(); | |
}; | |
/** | |
* Pauses polling. | |
* | |
* @param {Function} callback upon buffers are flushed and transport is paused | |
* @api private | |
*/ | |
Polling.prototype.pause = function (onPause) { | |
var self = this; | |
this.readyState = 'pausing'; | |
function pause () { | |
debug('paused'); | |
self.readyState = 'paused'; | |
onPause(); | |
} | |
if (this.polling || !this.writable) { | |
var total = 0; | |
if (this.polling) { | |
debug('we are currently polling - waiting to pause'); | |
total++; | |
this.once('pollComplete', function () { | |
debug('pre-pause polling complete'); | |
--total || pause(); | |
}); | |
} | |
if (!this.writable) { | |
debug('we are currently writing - waiting to pause'); | |
total++; | |
this.once('drain', function () { | |
debug('pre-pause writing complete'); | |
--total || pause(); | |
}); | |
} | |
} else { | |
pause(); | |
} | |
}; | |
/** | |
* Starts polling cycle. | |
* | |
* @api public | |
*/ | |
Polling.prototype.poll = function () { | |
debug('polling'); | |
this.polling = true; | |
this.doPoll(); | |
this.emit('poll'); | |
}; | |
/** | |
* Overloads onData to detect payloads. | |
* | |
* @api private | |
*/ | |
Polling.prototype.onData = function (data) { | |
var self = this; | |
debug('polling got data %s', data); | |
var callback = function (packet, index, total) { | |
// if its the first message we consider the transport open | |
if ('opening' === self.readyState) { | |
self.onOpen(); | |
} | |
// if its a close packet, we close the ongoing requests | |
if ('close' === packet.type) { | |
self.onClose(); | |
return false; | |
} | |
// otherwise bypass onData and handle the message | |
self.onPacket(packet); | |
}; | |
// decode payload | |
_$browser_30.decodePayload(data, this.socket.binaryType, callback); | |
// if an event did not trigger closing | |
if ('closed' !== this.readyState) { | |
// if we got data we're not polling | |
this.polling = false; | |
this.emit('pollComplete'); | |
if ('open' === this.readyState) { | |
this.poll(); | |
} else { | |
debug('ignoring poll - transport state "%s"', this.readyState); | |
} | |
} | |
}; | |
/** | |
* For polling, send a close packet. | |
* | |
* @api private | |
*/ | |
Polling.prototype.doClose = function () { | |
var self = this; | |
function close () { | |
debug('writing close packet'); | |
self.write([{ type: 'close' }]); | |
} | |
if ('open' === this.readyState) { | |
debug('transport open - closing'); | |
close(); | |
} else { | |
// in case we're trying to close while | |
// handshaking is in progress (GH-164) | |
debug('transport not open - deferring close'); | |
this.once('open', close); | |
} | |
}; | |
/** | |
* Writes a packets payload. | |
* | |
* @param {Array} data packets | |
* @param {Function} drain callback | |
* @api private | |
*/ | |
Polling.prototype.write = function (packets) { | |
var self = this; | |
this.writable = false; | |
var callbackfn = function () { | |
self.writable = true; | |
self.emit('drain'); | |
}; | |
_$browser_30.encodePayload(packets, this.supportsBinary, function (data) { | |
self.doWrite(data, callbackfn); | |
}); | |
}; | |
/** | |
* Generates uri for connection. | |
* | |
* @api private | |
*/ | |
Polling.prototype.uri = function () { | |
var query = this.query || {}; | |
var schema = this.secure ? 'https' : 'http'; | |
var port = ''; | |
// cache busting is forced | |
if (false !== this.timestampRequests) { | |
query[this.timestampParam] = _$yeast_79(); | |
} | |
if (!this.supportsBinary && !query.sid) { | |
query.b64 = 1; | |
} | |
query = _$parseqs_52.encode(query); | |
// avoid port if default for schema | |
if (this.port && (('https' === schema && Number(this.port) !== 443) || | |
('http' === schema && Number(this.port) !== 80))) { | |
port = ':' + this.port; | |
} | |
// prepend ? to query | |
if (query.length) { | |
query = '?' + query; | |
} | |
var ipv6 = this.hostname.indexOf(':') !== -1; | |
return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query; | |
}; | |
var _$JSONPPolling_25 = {}; | |
(function (global){ | |
/** | |
* Module requirements. | |
*/ | |
var Polling = _$Polling_27; | |
var inherit = _$componentInherit_15; | |
/** | |
* Module exports. | |
*/ | |
_$JSONPPolling_25 = JSONPPolling; | |
/** | |
* Cached regular expressions. | |
*/ | |
var rNewline = /\n/g; | |
var rEscapedNewline = /\\n/g; | |
/** | |
* Global JSONP callbacks. | |
*/ | |
var callbacks; | |
/** | |
* Noop. | |
*/ | |
function empty () { } | |
/** | |
* JSONP Polling constructor. | |
* | |
* @param {Object} opts. | |
* @api public | |
*/ | |
function JSONPPolling (opts) { | |
Polling.call(this, opts); | |
this.query = this.query || {}; | |
// define global callbacks array if not present | |
// we do this here (lazily) to avoid unneeded global pollution | |
if (!callbacks) { | |
// we need to consider multiple engines in the same page | |
if (!global.___eio) global.___eio = []; | |
callbacks = global.___eio; | |
} | |
// callback identifier | |
this.index = callbacks.length; | |
// add callback to jsonp global | |
var self = this; | |
callbacks.push(function (msg) { | |
self.onData(msg); | |
}); | |
// append to query string | |
this.query.j = this.index; | |
// prevent spurious errors from being emitted when the window is unloaded | |
if (global.document && global.addEventListener) { | |
global.addEventListener('beforeunload', function () { | |
if (self.script) self.script.onerror = empty; | |
}, false); | |
} | |
} | |
/** | |
* Inherits from Polling. | |
*/ | |
inherit(JSONPPolling, Polling); | |
/* | |
* JSONP only supports binary as base64 encoded strings | |
*/ | |
JSONPPolling.prototype.supportsBinary = false; | |
/** | |
* Closes the socket. | |
* | |
* @api private | |
*/ | |
JSONPPolling.prototype.doClose = function () { | |
if (this.script) { | |
this.script.parentNode.removeChild(this.script); | |
this.script = null; | |
} | |
if (this.form) { | |
this.form.parentNode.removeChild(this.form); | |
this.form = null; | |
this.iframe = null; | |
} | |
Polling.prototype.doClose.call(this); | |
}; | |
/** | |
* Starts a poll cycle. | |
* | |
* @api private | |
*/ | |
JSONPPolling.prototype.doPoll = function () { | |
var self = this; | |
var script = document.createElement('script'); | |
if (this.script) { | |
this.script.parentNode.removeChild(this.script); | |
this.script = null; | |
} | |
script.async = true; | |
script.src = this.uri(); | |
script.onerror = function (e) { | |
self.onError('jsonp poll error', e); | |
}; | |
var insertAt = document.getElementsByTagName('script')[0]; | |
if (insertAt) { | |
insertAt.parentNode.insertBefore(script, insertAt); | |
} else { | |
(document.head || document.body).appendChild(script); | |
} | |
this.script = script; | |
var isUAgecko = 'undefined' !== typeof navigator && /gecko/i.test(navigator.userAgent); | |
if (isUAgecko) { | |
setTimeout(function () { | |
var iframe = document.createElement('iframe'); | |
document.body.appendChild(iframe); | |
document.body.removeChild(iframe); | |
}, 100); | |
} | |
}; | |
/** | |
* Writes with a hidden iframe. | |
* | |
* @param {String} data to send | |
* @param {Function} called upon flush. | |
* @api private | |
*/ | |
JSONPPolling.prototype.doWrite = function (data, fn) { | |
var self = this; | |
if (!this.form) { | |
var form = document.createElement('form'); | |
var area = document.createElement('textarea'); | |
var id = this.iframeId = 'eio_iframe_' + this.index; | |
var iframe; | |
form.className = 'socketio'; | |
form.style.position = 'absolute'; | |
form.style.top = '-1000px'; | |
form.style.left = '-1000px'; | |
form.target = id; | |
form.method = 'POST'; | |
form.setAttribute('accept-charset', 'utf-8'); | |
area.name = 'd'; | |
form.appendChild(area); | |
document.body.appendChild(form); | |
this.form = form; | |
this.area = area; | |
} | |
this.form.action = this.uri(); | |
function complete () { | |
initIframe(); | |
fn(); | |
} | |
function initIframe () { | |
if (self.iframe) { | |
try { | |
self.form.removeChild(self.iframe); | |
} catch (e) { | |
self.onError('jsonp polling iframe removal error', e); | |
} | |
} | |
try { | |
// ie6 dynamic iframes with target="" support (thanks Chris Lambacher) | |
var html = '<iframe src="javascript:0" name="' + self.iframeId + '">'; | |
iframe = document.createElement(html); | |
} catch (e) { | |
iframe = document.createElement('iframe'); | |
iframe.name = self.iframeId; | |
iframe.src = 'javascript:0'; | |
} | |
iframe.id = self.iframeId; | |
self.form.appendChild(iframe); | |
self.iframe = iframe; | |
} | |
initIframe(); | |
// escape \n to prevent it from being converted into \r\n by some UAs | |
// double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side | |
data = data.replace(rEscapedNewline, '\\\n'); | |
this.area.value = data.replace(rNewline, '\\n'); | |
try { | |
this.form.submit(); | |
} catch (e) {} | |
if (this.iframe.attachEvent) { | |
this.iframe.onreadystatechange = function () { | |
if (self.iframe.readyState === 'complete') { | |
complete(); | |
} | |
}; | |
} else { | |
this.iframe.onload = complete; | |
} | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$pollingXhr_26 = {}; | |
(function (global){ | |
/** | |
* Module requirements. | |
*/ | |
var XMLHttpRequest = _$xmlhttprequest_29; | |
var Polling = _$Polling_27; | |
var Emitter = _$componentEmitter_14; | |
var inherit = _$componentInherit_15; | |
var debug = _$browser_17('engine.io-client:polling-xhr'); | |
/** | |
* Module exports. | |
*/ | |
_$pollingXhr_26 = XHR; | |
_$pollingXhr_26.Request = Request; | |
/** | |
* Empty function | |
*/ | |
function empty () {} | |
/** | |
* XHR Polling constructor. | |
* | |
* @param {Object} opts | |
* @api public | |
*/ | |
function XHR (opts) { | |
Polling.call(this, opts); | |
this.requestTimeout = opts.requestTimeout; | |
this.extraHeaders = opts.extraHeaders; | |
if (global.location) { | |
var isSSL = 'https:' === location.protocol; | |
var port = location.port; | |
// some user agents have empty `location.port` | |
if (!port) { | |
port = isSSL ? 443 : 80; | |
} | |
this.xd = opts.hostname !== global.location.hostname || | |
port !== opts.port; | |
this.xs = opts.secure !== isSSL; | |
} | |
} | |
/** | |
* Inherits from Polling. | |
*/ | |
inherit(XHR, Polling); | |
/** | |
* XHR supports binary | |
*/ | |
XHR.prototype.supportsBinary = true; | |
/** | |
* Creates a request. | |
* | |
* @param {String} method | |
* @api private | |
*/ | |
XHR.prototype.request = function (opts) { | |
opts = opts || {}; | |
opts.uri = this.uri(); | |
opts.xd = this.xd; | |
opts.xs = this.xs; | |
opts.agent = this.agent || false; | |
opts.supportsBinary = this.supportsBinary; | |
opts.enablesXDR = this.enablesXDR; | |
// SSL options for Node.js client | |
opts.pfx = this.pfx; | |
opts.key = this.key; | |
opts.passphrase = this.passphrase; | |
opts.cert = this.cert; | |
opts.ca = this.ca; | |
opts.ciphers = this.ciphers; | |
opts.rejectUnauthorized = this.rejectUnauthorized; | |
opts.requestTimeout = this.requestTimeout; | |
// other options for Node.js client | |
opts.extraHeaders = this.extraHeaders; | |
return new Request(opts); | |
}; | |
/** | |
* Sends data. | |
* | |
* @param {String} data to send. | |
* @param {Function} called upon flush. | |
* @api private | |
*/ | |
XHR.prototype.doWrite = function (data, fn) { | |
var isBinary = typeof data !== 'string' && data !== undefined; | |
var req = this.request({ method: 'POST', data: data, isBinary: isBinary }); | |
var self = this; | |
req.on('success', fn); | |
req.on('error', function (err) { | |
self.onError('xhr post error', err); | |
}); | |
this.sendXhr = req; | |
}; | |
/** | |
* Starts a poll cycle. | |
* | |
* @api private | |
*/ | |
XHR.prototype.doPoll = function () { | |
debug('xhr poll'); | |
var req = this.request(); | |
var self = this; | |
req.on('data', function (data) { | |
self.onData(data); | |
}); | |
req.on('error', function (err) { | |
self.onError('xhr poll error', err); | |
}); | |
this.pollXhr = req; | |
}; | |
/** | |
* Request constructor | |
* | |
* @param {Object} options | |
* @api public | |
*/ | |
function Request (opts) { | |
this.method = opts.method || 'GET'; | |
this.uri = opts.uri; | |
this.xd = !!opts.xd; | |
this.xs = !!opts.xs; | |
this.async = false !== opts.async; | |
this.data = undefined !== opts.data ? opts.data : null; | |
this.agent = opts.agent; | |
this.isBinary = opts.isBinary; | |
this.supportsBinary = opts.supportsBinary; | |
this.enablesXDR = opts.enablesXDR; | |
this.requestTimeout = opts.requestTimeout; | |
// SSL options for Node.js client | |
this.pfx = opts.pfx; | |
this.key = opts.key; | |
this.passphrase = opts.passphrase; | |
this.cert = opts.cert; | |
this.ca = opts.ca; | |
this.ciphers = opts.ciphers; | |
this.rejectUnauthorized = opts.rejectUnauthorized; | |
// other options for Node.js client | |
this.extraHeaders = opts.extraHeaders; | |
this.create(); | |
} | |
/** | |
* Mix in `Emitter`. | |
*/ | |
Emitter(Request.prototype); | |
/** | |
* Creates the XHR object and sends the request. | |
* | |
* @api private | |
*/ | |
Request.prototype.create = function () { | |
var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR }; | |
// SSL options for Node.js client | |
opts.pfx = this.pfx; | |
opts.key = this.key; | |
opts.passphrase = this.passphrase; | |
opts.cert = this.cert; | |
opts.ca = this.ca; | |
opts.ciphers = this.ciphers; | |
opts.rejectUnauthorized = this.rejectUnauthorized; | |
var xhr = this.xhr = new XMLHttpRequest(opts); | |
var self = this; | |
try { | |
debug('xhr open %s: %s', this.method, this.uri); | |
xhr.open(this.method, this.uri, this.async); | |
try { | |
if (this.extraHeaders) { | |
xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true); | |
for (var i in this.extraHeaders) { | |
if (this.extraHeaders.hasOwnProperty(i)) { | |
xhr.setRequestHeader(i, this.extraHeaders[i]); | |
} | |
} | |
} | |
} catch (e) {} | |
if ('POST' === this.method) { | |
try { | |
if (this.isBinary) { | |
xhr.setRequestHeader('Content-type', 'application/octet-stream'); | |
} else { | |
xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); | |
} | |
} catch (e) {} | |
} | |
try { | |
xhr.setRequestHeader('Accept', '*/*'); | |
} catch (e) {} | |
// ie6 check | |
if ('withCredentials' in xhr) { | |
xhr.withCredentials = true; | |
} | |
if (this.requestTimeout) { | |
xhr.timeout = this.requestTimeout; | |
} | |
if (this.hasXDR()) { | |
xhr.onload = function () { | |
self.onLoad(); | |
}; | |
xhr.onerror = function () { | |
self.onError(xhr.responseText); | |
}; | |
} else { | |
xhr.onreadystatechange = function () { | |
if (xhr.readyState === 2) { | |
var contentType; | |
try { | |
contentType = xhr.getResponseHeader('Content-Type'); | |
} catch (e) {} | |
if (contentType === 'application/octet-stream') { | |
xhr.responseType = 'arraybuffer'; | |
} | |
} | |
if (4 !== xhr.readyState) return; | |
if (200 === xhr.status || 1223 === xhr.status) { | |
self.onLoad(); | |
} else { | |
// make sure the `error` event handler that's user-set | |
// does not throw in the same tick and gets caught here | |
setTimeout(function () { | |
self.onError(xhr.status); | |
}, 0); | |
} | |
}; | |
} | |
debug('xhr data %s', this.data); | |
xhr.send(this.data); | |
} catch (e) { | |
// Need to defer since .create() is called directly fhrom the constructor | |
// and thus the 'error' event can only be only bound *after* this exception | |
// occurs. Therefore, also, we cannot throw here at all. | |
setTimeout(function () { | |
self.onError(e); | |
}, 0); | |
return; | |
} | |
if (global.document) { | |
this.index = Request.requestsCount++; | |
Request.requests[this.index] = this; | |
} | |
}; | |
/** | |
* Called upon successful response. | |
* | |
* @api private | |
*/ | |
Request.prototype.onSuccess = function () { | |
this.emit('success'); | |
this.cleanup(); | |
}; | |
/** | |
* Called if we have data. | |
* | |
* @api private | |
*/ | |
Request.prototype.onData = function (data) { | |
this.emit('data', data); | |
this.onSuccess(); | |
}; | |
/** | |
* Called upon error. | |
* | |
* @api private | |
*/ | |
Request.prototype.onError = function (err) { | |
this.emit('error', err); | |
this.cleanup(true); | |
}; | |
/** | |
* Cleans up house. | |
* | |
* @api private | |
*/ | |
Request.prototype.cleanup = function (fromError) { | |
if ('undefined' === typeof this.xhr || null === this.xhr) { | |
return; | |
} | |
// xmlhttprequest | |
if (this.hasXDR()) { | |
this.xhr.onload = this.xhr.onerror = empty; | |
} else { | |
this.xhr.onreadystatechange = empty; | |
} | |
if (fromError) { | |
try { | |
this.xhr.abort(); | |
} catch (e) {} | |
} | |
if (global.document) { | |
delete Request.requests[this.index]; | |
} | |
this.xhr = null; | |
}; | |
/** | |
* Called upon load. | |
* | |
* @api private | |
*/ | |
Request.prototype.onLoad = function () { | |
var data; | |
try { | |
var contentType; | |
try { | |
contentType = this.xhr.getResponseHeader('Content-Type'); | |
} catch (e) {} | |
if (contentType === 'application/octet-stream') { | |
data = this.xhr.response || this.xhr.responseText; | |
} else { | |
data = this.xhr.responseText; | |
} | |
} catch (e) { | |
this.onError(e); | |
} | |
if (null != data) { | |
this.onData(data); | |
} | |
}; | |
/** | |
* Check if it has XDomainRequest. | |
* | |
* @api private | |
*/ | |
Request.prototype.hasXDR = function () { | |
return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR; | |
}; | |
/** | |
* Aborts the request. | |
* | |
* @api public | |
*/ | |
Request.prototype.abort = function () { | |
this.cleanup(); | |
}; | |
/** | |
* Aborts pending requests when unloading the window. This is needed to prevent | |
* memory leaks (e.g. when using IE) and to ensure that no spurious error is | |
* emitted. | |
*/ | |
Request.requestsCount = 0; | |
Request.requests = {}; | |
if (global.document) { | |
if (global.attachEvent) { | |
global.attachEvent('onunload', unloadHandler); | |
} else if (global.addEventListener) { | |
global.addEventListener('beforeunload', unloadHandler, false); | |
} | |
} | |
function unloadHandler () { | |
for (var i in Request.requests) { | |
if (Request.requests.hasOwnProperty(i)) { | |
Request.requests[i].abort(); | |
} | |
} | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$WS_28 = {}; | |
(function (global){ | |
/** | |
* Module dependencies. | |
*/ | |
var Transport = _$Transport_23; | |
var parser = _$browser_30; | |
var parseqs = _$parseqs_52; | |
var inherit = _$componentInherit_15; | |
var yeast = _$yeast_79; | |
var debug = _$browser_17('engine.io-client:websocket'); | |
var BrowserWebSocket = global.WebSocket || global.MozWebSocket; | |
var NodeWebSocket; | |
if (typeof window === 'undefined') { | |
try { | |
NodeWebSocket = _$empty_2; | |
} catch (e) { } | |
} | |
/** | |
* Get either the `WebSocket` or `MozWebSocket` globals | |
* in the browser or try to resolve WebSocket-compatible | |
* interface exposed by `ws` for Node-like environment. | |
*/ | |
var WebSocket = BrowserWebSocket; | |
if (!WebSocket && typeof window === 'undefined') { | |
WebSocket = NodeWebSocket; | |
} | |
/** | |
* Module exports. | |
*/ | |
_$WS_28 = WS; | |
/** | |
* WebSocket transport constructor. | |
* | |
* @api {Object} connection options | |
* @api public | |
*/ | |
function WS (opts) { | |
var forceBase64 = (opts && opts.forceBase64); | |
if (forceBase64) { | |
this.supportsBinary = false; | |
} | |
this.perMessageDeflate = opts.perMessageDeflate; | |
this.usingBrowserWebSocket = BrowserWebSocket && !opts.forceNode; | |
this.protocols = opts.protocols; | |
if (!this.usingBrowserWebSocket) { | |
WebSocket = NodeWebSocket; | |
} | |
Transport.call(this, opts); | |
} | |
/** | |
* Inherits from Transport. | |
*/ | |
inherit(WS, Transport); | |
/** | |
* Transport name. | |
* | |
* @api public | |
*/ | |
WS.prototype.name = 'websocket'; | |
/* | |
* WebSockets support binary | |
*/ | |
WS.prototype.supportsBinary = true; | |
/** | |
* Opens socket. | |
* | |
* @api private | |
*/ | |
WS.prototype.doOpen = function () { | |
if (!this.check()) { | |
// let probe timeout | |
return; | |
} | |
var uri = this.uri(); | |
var protocols = this.protocols; | |
var opts = { | |
agent: this.agent, | |
perMessageDeflate: this.perMessageDeflate | |
}; | |
// SSL options for Node.js client | |
opts.pfx = this.pfx; | |
opts.key = this.key; | |
opts.passphrase = this.passphrase; | |
opts.cert = this.cert; | |
opts.ca = this.ca; | |
opts.ciphers = this.ciphers; | |
opts.rejectUnauthorized = this.rejectUnauthorized; | |
if (this.extraHeaders) { | |
opts.headers = this.extraHeaders; | |
} | |
if (this.localAddress) { | |
opts.localAddress = this.localAddress; | |
} | |
try { | |
this.ws = this.usingBrowserWebSocket ? (protocols ? new WebSocket(uri, protocols) : new WebSocket(uri)) : new WebSocket(uri, protocols, opts); | |
} catch (err) { | |
return this.emit('error', err); | |
} | |
if (this.ws.binaryType === undefined) { | |
this.supportsBinary = false; | |
} | |
if (this.ws.supports && this.ws.supports.binary) { | |
this.supportsBinary = true; | |
this.ws.binaryType = 'nodebuffer'; | |
} else { | |
this.ws.binaryType = 'arraybuffer'; | |
} | |
this.addEventListeners(); | |
}; | |
/** | |
* Adds event listeners to the socket | |
* | |
* @api private | |
*/ | |
WS.prototype.addEventListeners = function () { | |
var self = this; | |
this.ws.onopen = function () { | |
self.onOpen(); | |
}; | |
this.ws.onclose = function () { | |
self.onClose(); | |
}; | |
this.ws.onmessage = function (ev) { | |
self.onData(ev.data); | |
}; | |
this.ws.onerror = function (e) { | |
self.onError('websocket error', e); | |
}; | |
}; | |
/** | |
* Writes data to socket. | |
* | |
* @param {Array} array of packets. | |
* @api private | |
*/ | |
WS.prototype.write = function (packets) { | |
var self = this; | |
this.writable = false; | |
// encodePacket efficient as it uses WS framing | |
// no need for encodePayload | |
var total = packets.length; | |
for (var i = 0, l = total; i < l; i++) { | |
(function (packet) { | |
parser.encodePacket(packet, self.supportsBinary, function (data) { | |
if (!self.usingBrowserWebSocket) { | |
// always create a new object (GH-437) | |
var opts = {}; | |
if (packet.options) { | |
opts.compress = packet.options.compress; | |
} | |
if (self.perMessageDeflate) { | |
var len = 'string' === typeof data ? global.Buffer.byteLength(data) : data.length; | |
if (len < self.perMessageDeflate.threshold) { | |
opts.compress = false; | |
} | |
} | |
} | |
// Sometimes the websocket has already been closed but the browser didn't | |
// have a chance of informing us about it yet, in that case send will | |
// throw an error | |
try { | |
if (self.usingBrowserWebSocket) { | |
// TypeError is thrown when passing the second argument on Safari | |
self.ws.send(data); | |
} else { | |
self.ws.send(data, opts); | |
} | |
} catch (e) { | |
debug('websocket closed before onclose event'); | |
} | |
--total || done(); | |
}); | |
})(packets[i]); | |
} | |
function done () { | |
self.emit('flush'); | |
// fake drain | |
// defer to next tick to allow Socket to clear writeBuffer | |
setTimeout(function () { | |
self.writable = true; | |
self.emit('drain'); | |
}, 0); | |
} | |
}; | |
/** | |
* Called upon close | |
* | |
* @api private | |
*/ | |
WS.prototype.onClose = function () { | |
Transport.prototype.onClose.call(this); | |
}; | |
/** | |
* Closes socket. | |
* | |
* @api private | |
*/ | |
WS.prototype.doClose = function () { | |
if (typeof this.ws !== 'undefined') { | |
this.ws.close(); | |
} | |
}; | |
/** | |
* Generates uri for connection. | |
* | |
* @api private | |
*/ | |
WS.prototype.uri = function () { | |
var query = this.query || {}; | |
var schema = this.secure ? 'wss' : 'ws'; | |
var port = ''; | |
// avoid port if default for schema | |
if (this.port && (('wss' === schema && Number(this.port) !== 443) || | |
('ws' === schema && Number(this.port) !== 80))) { | |
port = ':' + this.port; | |
} | |
// append timestamp to URI | |
if (this.timestampRequests) { | |
query[this.timestampParam] = yeast(); | |
} | |
// communicate binary support capabilities | |
if (!this.supportsBinary) { | |
query.b64 = 1; | |
} | |
query = parseqs.encode(query); | |
// prepend ? to query | |
if (query.length) { | |
query = '?' + query; | |
} | |
var ipv6 = this.hostname.indexOf(':') !== -1; | |
return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query; | |
}; | |
/** | |
* Feature detection for WebSocket. | |
* | |
* @return {Boolean} whether this transport is available. | |
* @api public | |
*/ | |
WS.prototype.check = function () { | |
return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name); | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$transports_24 = {}; | |
(function (global){ | |
/** | |
* Module dependencies | |
*/ | |
var XMLHttpRequest = _$xmlhttprequest_29; | |
var XHR = _$pollingXhr_26; | |
var JSONP = _$JSONPPolling_25; | |
var websocket = _$WS_28; | |
/** | |
* Export transports. | |
*/ | |
_$transports_24.polling = polling; | |
_$transports_24.websocket = websocket; | |
/** | |
* Polling transport polymorphic constructor. | |
* Decides on xhr vs jsonp based on feature detection. | |
* | |
* @api private | |
*/ | |
function polling (opts) { | |
var xhr; | |
var xd = false; | |
var xs = false; | |
var jsonp = false !== opts.jsonp; | |
if (global.location) { | |
var isSSL = 'https:' === location.protocol; | |
var port = location.port; | |
// some user agents have empty `location.port` | |
if (!port) { | |
port = isSSL ? 443 : 80; | |
} | |
xd = opts.hostname !== location.hostname || port !== opts.port; | |
xs = opts.secure !== isSSL; | |
} | |
opts.xdomain = xd; | |
opts.xscheme = xs; | |
xhr = new XMLHttpRequest(opts); | |
if ('open' in xhr && !opts.forceJSONP) { | |
return new XHR(opts); | |
} else { | |
if (!jsonp) throw new Error('JSONP disabled'); | |
return new JSONP(opts); | |
} | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var indexOf = [].indexOf; | |
var _$indexof_42 = function(arr, obj){ | |
if (indexOf) return arr.indexOf(obj); | |
for (var i = 0; i < arr.length; ++i) { | |
if (arr[i] === obj) return i; | |
} | |
return -1; | |
}; | |
var _$parsejson_51 = {}; | |
(function (global){ | |
/** | |
* JSON parse. | |
* | |
* @see Based on jQuery#parseJSON (MIT) and JSON2 | |
* @api private | |
*/ | |
var rvalidchars = /^[\],:{}\s]*$/; | |
var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; | |
var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; | |
var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g; | |
var rtrimLeft = /^\s+/; | |
var rtrimRight = /\s+$/; | |
_$parsejson_51 = function parsejson(data) { | |
if ('string' != typeof data || !data) { | |
return null; | |
} | |
data = data.replace(rtrimLeft, '').replace(rtrimRight, ''); | |
// Attempt to parse using the native JSON parser first | |
if (global.JSON && JSON.parse) { | |
return JSON.parse(data); | |
} | |
if (rvalidchars.test(data.replace(rvalidescape, '@') | |
.replace(rvalidtokens, ']') | |
.replace(rvalidbraces, ''))) { | |
return (new Function('return ' + data))(); | |
} | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
/** | |
* Parses an URI | |
* | |
* @author Steven Levithan <stevenlevithan.com> (MIT license) | |
* @api private | |
*/ | |
var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; | |
var parts = [ | |
'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor' | |
]; | |
var _$parseuri_53 = function parseuri(str) { | |
var src = str, | |
b = str.indexOf('['), | |
e = str.indexOf(']'); | |
if (b != -1 && e != -1) { | |
str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length); | |
} | |
var m = re.exec(str || ''), | |
uri = {}, | |
i = 14; | |
while (i--) { | |
uri[parts[i]] = m[i] || ''; | |
} | |
if (b != -1 && e != -1) { | |
uri.source = src; | |
uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':'); | |
uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':'); | |
uri.ipv6uri = true; | |
} | |
return uri; | |
}; | |
var _$Socket_22 = {}; | |
(function (global){ | |
/** | |
* Module dependencies. | |
*/ | |
var transports = _$transports_24; | |
var Emitter = _$componentEmitter_14; | |
var debug = _$browser_17('engine.io-client:socket'); | |
var index = _$indexof_42; | |
var parser = _$browser_30; | |
var parseuri = _$parseuri_53; | |
var parsejson = _$parsejson_51; | |
var parseqs = _$parseqs_52; | |
/** | |
* Module exports. | |
*/ | |
_$Socket_22 = Socket; | |
/** | |
* Socket constructor. | |
* | |
* @param {String|Object} uri or options | |
* @param {Object} options | |
* @api public | |
*/ | |
function Socket (uri, opts) { | |
if (!(this instanceof Socket)) return new Socket(uri, opts); | |
opts = opts || {}; | |
if (uri && 'object' === typeof uri) { | |
opts = uri; | |
uri = null; | |
} | |
if (uri) { | |
uri = parseuri(uri); | |
opts.hostname = uri.host; | |
opts.secure = uri.protocol === 'https' || uri.protocol === 'wss'; | |
opts.port = uri.port; | |
if (uri.query) opts.query = uri.query; | |
} else if (opts.host) { | |
opts.hostname = parseuri(opts.host).host; | |
} | |
this.secure = null != opts.secure ? opts.secure | |
: (global.location && 'https:' === location.protocol); | |
if (opts.hostname && !opts.port) { | |
// if no port is specified manually, use the protocol default | |
opts.port = this.secure ? '443' : '80'; | |
} | |
this.agent = opts.agent || false; | |
this.hostname = opts.hostname || | |
(global.location ? location.hostname : 'localhost'); | |
this.port = opts.port || (global.location && location.port | |
? location.port | |
: (this.secure ? 443 : 80)); | |
this.query = opts.query || {}; | |
if ('string' === typeof this.query) this.query = parseqs.decode(this.query); | |
this.upgrade = false !== opts.upgrade; | |
this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; | |
this.forceJSONP = !!opts.forceJSONP; | |
this.jsonp = false !== opts.jsonp; | |
this.forceBase64 = !!opts.forceBase64; | |
this.enablesXDR = !!opts.enablesXDR; | |
this.timestampParam = opts.timestampParam || 't'; | |
this.timestampRequests = opts.timestampRequests; | |
this.transports = opts.transports || ['polling', 'websocket']; | |
this.transportOptions = opts.transportOptions || {}; | |
this.readyState = ''; | |
this.writeBuffer = []; | |
this.prevBufferLen = 0; | |
this.policyPort = opts.policyPort || 843; | |
this.rememberUpgrade = opts.rememberUpgrade || false; | |
this.binaryType = null; | |
this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; | |
this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || {}) : false; | |
if (true === this.perMessageDeflate) this.perMessageDeflate = {}; | |
if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) { | |
this.perMessageDeflate.threshold = 1024; | |
} | |
// SSL options for Node.js client | |
this.pfx = opts.pfx || null; | |
this.key = opts.key || null; | |
this.passphrase = opts.passphrase || null; | |
this.cert = opts.cert || null; | |
this.ca = opts.ca || null; | |
this.ciphers = opts.ciphers || null; | |
this.rejectUnauthorized = opts.rejectUnauthorized === undefined ? true : opts.rejectUnauthorized; | |
this.forceNode = !!opts.forceNode; | |
// other options for Node.js client | |
var freeGlobal = typeof global === 'object' && global; | |
if (freeGlobal.global === freeGlobal) { | |
if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) { | |
this.extraHeaders = opts.extraHeaders; | |
} | |
if (opts.localAddress) { | |
this.localAddress = opts.localAddress; | |
} | |
} | |
// set on handshake | |
this.id = null; | |
this.upgrades = null; | |
this.pingInterval = null; | |
this.pingTimeout = null; | |
// set on heartbeat | |
this.pingIntervalTimer = null; | |
this.pingTimeoutTimer = null; | |
this.open(); | |
} | |
Socket.priorWebsocketSuccess = false; | |
/** | |
* Mix in `Emitter`. | |
*/ | |
Emitter(Socket.prototype); | |
/** | |
* Protocol version. | |
* | |
* @api public | |
*/ | |
Socket.protocol = parser.protocol; // this is an int | |
/** | |
* Expose deps for legacy compatibility | |
* and standalone browser access. | |
*/ | |
Socket.Socket = Socket; | |
Socket.Transport = _$Transport_23; | |
Socket.transports = _$transports_24; | |
Socket.parser = _$browser_30; | |
/** | |
* Creates transport of the given type. | |
* | |
* @param {String} transport name | |
* @return {Transport} | |
* @api private | |
*/ | |
Socket.prototype.createTransport = function (name) { | |
debug('creating transport "%s"', name); | |
var query = clone(this.query); | |
// append engine.io protocol identifier | |
query.EIO = parser.protocol; | |
// transport name | |
query.transport = name; | |
// per-transport options | |
var options = this.transportOptions[name] || {}; | |
// session id if we already have one | |
if (this.id) query.sid = this.id; | |
var transport = new transports[name]({ | |
query: query, | |
socket: this, | |
agent: options.agent || this.agent, | |
hostname: options.hostname || this.hostname, | |
port: options.port || this.port, | |
secure: options.secure || this.secure, | |
path: options.path || this.path, | |
forceJSONP: options.forceJSONP || this.forceJSONP, | |
jsonp: options.jsonp || this.jsonp, | |
forceBase64: options.forceBase64 || this.forceBase64, | |
enablesXDR: options.enablesXDR || this.enablesXDR, | |
timestampRequests: options.timestampRequests || this.timestampRequests, | |
timestampParam: options.timestampParam || this.timestampParam, | |
policyPort: options.policyPort || this.policyPort, | |
pfx: options.pfx || this.pfx, | |
key: options.key || this.key, | |
passphrase: options.passphrase || this.passphrase, | |
cert: options.cert || this.cert, | |
ca: options.ca || this.ca, | |
ciphers: options.ciphers || this.ciphers, | |
rejectUnauthorized: options.rejectUnauthorized || this.rejectUnauthorized, | |
perMessageDeflate: options.perMessageDeflate || this.perMessageDeflate, | |
extraHeaders: options.extraHeaders || this.extraHeaders, | |
forceNode: options.forceNode || this.forceNode, | |
localAddress: options.localAddress || this.localAddress, | |
requestTimeout: options.requestTimeout || this.requestTimeout, | |
protocols: options.protocols || void (0) | |
}); | |
return transport; | |
}; | |
function clone (obj) { | |
var o = {}; | |
for (var i in obj) { | |
if (obj.hasOwnProperty(i)) { | |
o[i] = obj[i]; | |
} | |
} | |
return o; | |
} | |
/** | |
* Initializes transport to use and starts probe. | |
* | |
* @api private | |
*/ | |
Socket.prototype.open = function () { | |
var transport; | |
if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') !== -1) { | |
transport = 'websocket'; | |
} else if (0 === this.transports.length) { | |
// Emit error on next tick so it can be listened to | |
var self = this; | |
setTimeout(function () { | |
self.emit('error', 'No transports available'); | |
}, 0); | |
return; | |
} else { | |
transport = this.transports[0]; | |
} | |
this.readyState = 'opening'; | |
// Retry with the next transport if the transport is disabled (jsonp: false) | |
try { | |
transport = this.createTransport(transport); | |
} catch (e) { | |
this.transports.shift(); | |
this.open(); | |
return; | |
} | |
transport.open(); | |
this.setTransport(transport); | |
}; | |
/** | |
* Sets the current transport. Disables the existing one (if any). | |
* | |
* @api private | |
*/ | |
Socket.prototype.setTransport = function (transport) { | |
debug('setting transport %s', transport.name); | |
var self = this; | |
if (this.transport) { | |
debug('clearing existing transport %s', this.transport.name); | |
this.transport.removeAllListeners(); | |
} | |
// set up transport | |
this.transport = transport; | |
// set up transport listeners | |
transport | |
.on('drain', function () { | |
self.onDrain(); | |
}) | |
.on('packet', function (packet) { | |
self.onPacket(packet); | |
}) | |
.on('error', function (e) { | |
self.onError(e); | |
}) | |
.on('close', function () { | |
self.onClose('transport close'); | |
}); | |
}; | |
/** | |
* Probes a transport. | |
* | |
* @param {String} transport name | |
* @api private | |
*/ | |
Socket.prototype.probe = function (name) { | |
debug('probing transport "%s"', name); | |
var transport = this.createTransport(name, { probe: 1 }); | |
var failed = false; | |
var self = this; | |
Socket.priorWebsocketSuccess = false; | |
function onTransportOpen () { | |
if (self.onlyBinaryUpgrades) { | |
var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; | |
failed = failed || upgradeLosesBinary; | |
} | |
if (failed) return; | |
debug('probe transport "%s" opened', name); | |
transport.send([{ type: 'ping', data: 'probe' }]); | |
transport.once('packet', function (msg) { | |
if (failed) return; | |
if ('pong' === msg.type && 'probe' === msg.data) { | |
debug('probe transport "%s" pong', name); | |
self.upgrading = true; | |
self.emit('upgrading', transport); | |
if (!transport) return; | |
Socket.priorWebsocketSuccess = 'websocket' === transport.name; | |
debug('pausing current transport "%s"', self.transport.name); | |
self.transport.pause(function () { | |
if (failed) return; | |
if ('closed' === self.readyState) return; | |
debug('changing transport and sending upgrade packet'); | |
cleanup(); | |
self.setTransport(transport); | |
transport.send([{ type: 'upgrade' }]); | |
self.emit('upgrade', transport); | |
transport = null; | |
self.upgrading = false; | |
self.flush(); | |
}); | |
} else { | |
debug('probe transport "%s" failed', name); | |
var err = new Error('probe error'); | |
err.transport = transport.name; | |
self.emit('upgradeError', err); | |
} | |
}); | |
} | |
function freezeTransport () { | |
if (failed) return; | |
// Any callback called by transport should be ignored since now | |
failed = true; | |
cleanup(); | |
transport.close(); | |
transport = null; | |
} | |
// Handle any error that happens while probing | |
function onerror (err) { | |
var error = new Error('probe error: ' + err); | |
error.transport = transport.name; | |
freezeTransport(); | |
debug('probe transport "%s" failed because of error: %s', name, err); | |
self.emit('upgradeError', error); | |
} | |
function onTransportClose () { | |
onerror('transport closed'); | |
} | |
// When the socket is closed while we're probing | |
function onclose () { | |
onerror('socket closed'); | |
} | |
// When the socket is upgraded while we're probing | |
function onupgrade (to) { | |
if (transport && to.name !== transport.name) { | |
debug('"%s" works - aborting "%s"', to.name, transport.name); | |
freezeTransport(); | |
} | |
} | |
// Remove all listeners on the transport and on self | |
function cleanup () { | |
transport.removeListener('open', onTransportOpen); | |
transport.removeListener('error', onerror); | |
transport.removeListener('close', onTransportClose); | |
self.removeListener('close', onclose); | |
self.removeListener('upgrading', onupgrade); | |
} | |
transport.once('open', onTransportOpen); | |
transport.once('error', onerror); | |
transport.once('close', onTransportClose); | |
this.once('close', onclose); | |
this.once('upgrading', onupgrade); | |
transport.open(); | |
}; | |
/** | |
* Called when connection is deemed open. | |
* | |
* @api public | |
*/ | |
Socket.prototype.onOpen = function () { | |
debug('socket open'); | |
this.readyState = 'open'; | |
Socket.priorWebsocketSuccess = 'websocket' === this.transport.name; | |
this.emit('open'); | |
this.flush(); | |
// we check for `readyState` in case an `open` | |
// listener already closed the socket | |
if ('open' === this.readyState && this.upgrade && this.transport.pause) { | |
debug('starting upgrade probes'); | |
for (var i = 0, l = this.upgrades.length; i < l; i++) { | |
this.probe(this.upgrades[i]); | |
} | |
} | |
}; | |
/** | |
* Handles a packet. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onPacket = function (packet) { | |
if ('opening' === this.readyState || 'open' === this.readyState || | |
'closing' === this.readyState) { | |
debug('socket receive: type "%s", data "%s"', packet.type, packet.data); | |
this.emit('packet', packet); | |
// Socket is live - any packet counts | |
this.emit('heartbeat'); | |
switch (packet.type) { | |
case 'open': | |
this.onHandshake(parsejson(packet.data)); | |
break; | |
case 'pong': | |
this.setPing(); | |
this.emit('pong'); | |
break; | |
case 'error': | |
var err = new Error('server error'); | |
err.code = packet.data; | |
this.onError(err); | |
break; | |
case 'message': | |
this.emit('data', packet.data); | |
this.emit('message', packet.data); | |
break; | |
} | |
} else { | |
debug('packet received with socket readyState "%s"', this.readyState); | |
} | |
}; | |
/** | |
* Called upon handshake completion. | |
* | |
* @param {Object} handshake obj | |
* @api private | |
*/ | |
Socket.prototype.onHandshake = function (data) { | |
this.emit('handshake', data); | |
this.id = data.sid; | |
this.transport.query.sid = data.sid; | |
this.upgrades = this.filterUpgrades(data.upgrades); | |
this.pingInterval = data.pingInterval; | |
this.pingTimeout = data.pingTimeout; | |
this.onOpen(); | |
// In case open handler closes socket | |
if ('closed' === this.readyState) return; | |
this.setPing(); | |
// Prolong liveness of socket on heartbeat | |
this.removeListener('heartbeat', this.onHeartbeat); | |
this.on('heartbeat', this.onHeartbeat); | |
}; | |
/** | |
* Resets ping timeout. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onHeartbeat = function (timeout) { | |
clearTimeout(this.pingTimeoutTimer); | |
var self = this; | |
self.pingTimeoutTimer = setTimeout(function () { | |
if ('closed' === self.readyState) return; | |
self.onClose('ping timeout'); | |
}, timeout || (self.pingInterval + self.pingTimeout)); | |
}; | |
/** | |
* Pings server every `this.pingInterval` and expects response | |
* within `this.pingTimeout` or closes connection. | |
* | |
* @api private | |
*/ | |
Socket.prototype.setPing = function () { | |
var self = this; | |
clearTimeout(self.pingIntervalTimer); | |
self.pingIntervalTimer = setTimeout(function () { | |
debug('writing ping packet - expecting pong within %sms', self.pingTimeout); | |
self.ping(); | |
self.onHeartbeat(self.pingTimeout); | |
}, self.pingInterval); | |
}; | |
/** | |
* Sends a ping packet. | |
* | |
* @api private | |
*/ | |
Socket.prototype.ping = function () { | |
var self = this; | |
this.sendPacket('ping', function () { | |
self.emit('ping'); | |
}); | |
}; | |
/** | |
* Called on `drain` event | |
* | |
* @api private | |
*/ | |
Socket.prototype.onDrain = function () { | |
this.writeBuffer.splice(0, this.prevBufferLen); | |
// setting prevBufferLen = 0 is very important | |
// for example, when upgrading, upgrade packet is sent over, | |
// and a nonzero prevBufferLen could cause problems on `drain` | |
this.prevBufferLen = 0; | |
if (0 === this.writeBuffer.length) { | |
this.emit('drain'); | |
} else { | |
this.flush(); | |
} | |
}; | |
/** | |
* Flush write buffers. | |
* | |
* @api private | |
*/ | |
Socket.prototype.flush = function () { | |
if ('closed' !== this.readyState && this.transport.writable && | |
!this.upgrading && this.writeBuffer.length) { | |
debug('flushing %d packets in socket', this.writeBuffer.length); | |
this.transport.send(this.writeBuffer); | |
// keep track of current length of writeBuffer | |
// splice writeBuffer and callbackBuffer on `drain` | |
this.prevBufferLen = this.writeBuffer.length; | |
this.emit('flush'); | |
} | |
}; | |
/** | |
* Sends a message. | |
* | |
* @param {String} message. | |
* @param {Function} callback function. | |
* @param {Object} options. | |
* @return {Socket} for chaining. | |
* @api public | |
*/ | |
Socket.prototype.write = | |
Socket.prototype.send = function (msg, options, fn) { | |
this.sendPacket('message', msg, options, fn); | |
return this; | |
}; | |
/** | |
* Sends a packet. | |
* | |
* @param {String} packet type. | |
* @param {String} data. | |
* @param {Object} options. | |
* @param {Function} callback function. | |
* @api private | |
*/ | |
Socket.prototype.sendPacket = function (type, data, options, fn) { | |
if ('function' === typeof data) { | |
fn = data; | |
data = undefined; | |
} | |
if ('function' === typeof options) { | |
fn = options; | |
options = null; | |
} | |
if ('closing' === this.readyState || 'closed' === this.readyState) { | |
return; | |
} | |
options = options || {}; | |
options.compress = false !== options.compress; | |
var packet = { | |
type: type, | |
data: data, | |
options: options | |
}; | |
this.emit('packetCreate', packet); | |
this.writeBuffer.push(packet); | |
if (fn) this.once('flush', fn); | |
this.flush(); | |
}; | |
/** | |
* Closes the connection. | |
* | |
* @api private | |
*/ | |
Socket.prototype.close = function () { | |
if ('opening' === this.readyState || 'open' === this.readyState) { | |
this.readyState = 'closing'; | |
var self = this; | |
if (this.writeBuffer.length) { | |
this.once('drain', function () { | |
if (this.upgrading) { | |
waitForUpgrade(); | |
} else { | |
close(); | |
} | |
}); | |
} else if (this.upgrading) { | |
waitForUpgrade(); | |
} else { | |
close(); | |
} | |
} | |
function close () { | |
self.onClose('forced close'); | |
debug('socket closing - telling transport to close'); | |
self.transport.close(); | |
} | |
function cleanupAndClose () { | |
self.removeListener('upgrade', cleanupAndClose); | |
self.removeListener('upgradeError', cleanupAndClose); | |
close(); | |
} | |
function waitForUpgrade () { | |
// wait for upgrade to finish since we can't send packets while pausing a transport | |
self.once('upgrade', cleanupAndClose); | |
self.once('upgradeError', cleanupAndClose); | |
} | |
return this; | |
}; | |
/** | |
* Called upon transport error | |
* | |
* @api private | |
*/ | |
Socket.prototype.onError = function (err) { | |
debug('socket error %j', err); | |
Socket.priorWebsocketSuccess = false; | |
this.emit('error', err); | |
this.onClose('transport error', err); | |
}; | |
/** | |
* Called upon transport close. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onClose = function (reason, desc) { | |
if ('opening' === this.readyState || 'open' === this.readyState || 'closing' === this.readyState) { | |
debug('socket close with reason: "%s"', reason); | |
var self = this; | |
// clear timers | |
clearTimeout(this.pingIntervalTimer); | |
clearTimeout(this.pingTimeoutTimer); | |
// stop event from firing again for transport | |
this.transport.removeAllListeners('close'); | |
// ensure transport won't stay open | |
this.transport.close(); | |
// ignore further transport communication | |
this.transport.removeAllListeners(); | |
// set ready state | |
this.readyState = 'closed'; | |
// clear session id | |
this.id = null; | |
// emit close event | |
this.emit('close', reason, desc); | |
// clean buffers after, so users can still | |
// grab the buffers on `close` event | |
self.writeBuffer = []; | |
self.prevBufferLen = 0; | |
} | |
}; | |
/** | |
* Filters upgrades, returning only those matching client transports. | |
* | |
* @param {Array} server upgrades | |
* @api private | |
* | |
*/ | |
Socket.prototype.filterUpgrades = function (upgrades) { | |
var filteredUpgrades = []; | |
for (var i = 0, j = upgrades.length; i < j; i++) { | |
if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]); | |
} | |
return filteredUpgrades; | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$lib_21 = {}; | |
_$lib_21 = _$Socket_22; | |
/** | |
* Exports parser | |
* | |
* @api public | |
* | |
*/ | |
_$lib_21.parser = _$browser_30; | |
var _$engineIoClient_20 = _$lib_21; | |
'use strict'; | |
var __hasOwn_33 = Object.prototype.hasOwnProperty; | |
var toStr = Object.prototype.toString; | |
var isArray = function isArray(arr) { | |
if (typeof Array.isArray === 'function') { | |
return Array.isArray(arr); | |
} | |
return toStr.call(arr) === '[object Array]'; | |
}; | |
var isPlainObject = function isPlainObject(obj) { | |
if (!obj || toStr.call(obj) !== '[object Object]') { | |
return false; | |
} | |
var hasOwnConstructor = __hasOwn_33.call(obj, 'constructor'); | |
var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && __hasOwn_33.call(obj.constructor.prototype, 'isPrototypeOf'); | |
// Not own constructor property must be Object | |
if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) { | |
return false; | |
} | |
// Own properties are enumerated firstly, so to speed up, | |
// if last one is own, then all properties are own. | |
var key; | |
for (key in obj) { /**/ } | |
return typeof key === 'undefined' || __hasOwn_33.call(obj, key); | |
}; | |
var _$extend_33 = function extend() { | |
var options, name, src, copy, copyIsArray, clone; | |
var target = arguments[0]; | |
var i = 1; | |
var length = arguments.length; | |
var deep = false; | |
// Handle a deep copy situation | |
if (typeof target === 'boolean') { | |
deep = target; | |
target = arguments[1] || {}; | |
// skip the boolean and the target | |
i = 2; | |
} | |
if (target == null || (typeof target !== 'object' && typeof target !== 'function')) { | |
target = {}; | |
} | |
for (; i < length; ++i) { | |
options = arguments[i]; | |
// Only deal with non-null/undefined values | |
if (options != null) { | |
// Extend the base object | |
for (name in options) { | |
src = target[name]; | |
copy = options[name]; | |
// Prevent never-ending loop | |
if (target !== copy) { | |
// Recurse if we're merging plain objects or arrays | |
if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { | |
if (copyIsArray) { | |
copyIsArray = false; | |
clone = src && isArray(src) ? src : []; | |
} else { | |
clone = src && isPlainObject(src) ? src : {}; | |
} | |
// Never move original objects, clone them | |
target[name] = extend(deep, clone, copy); | |
// Don't bring in undefined values | |
} else if (typeof copy !== 'undefined') { | |
target[name] = copy; | |
} | |
} | |
} | |
} | |
} | |
// Return the modified object | |
return target; | |
}; | |
var _$lib_35 = {}; | |
'use strict'; | |
_$lib_35.__esModule = true; | |
var NODE_LIST_CLASSES = { | |
'[object HTMLCollection]': true, | |
'[object NodeList]': true, | |
'[object RadioNodeList]': true | |
}; | |
// .type values for elements which can appear in .elements and should be ignored | |
var IGNORED_ELEMENT_TYPES = { | |
'button': true, | |
'fieldset': true, | |
// 'keygen': true, | |
// 'output': true, | |
'reset': true, | |
'submit': true | |
}; | |
var CHECKED_INPUT_TYPES = { | |
'checkbox': true, | |
'radio': true | |
}; | |
var TRIM_RE = /^\s+|\s+$/g; | |
var __slice_35 = Array.prototype.slice; | |
var __toString_35 = Object.prototype.toString; | |
/** | |
* @param {HTMLFormElement} form | |
* @param {Object} options | |
* @return {Object.<string,(string|Array.<string>)>} an object containing | |
* submittable value(s) held in the form's .elements collection, with | |
* properties named as per element names or ids. | |
*/ | |
function getFormData(form) { | |
var options = arguments.length <= 1 || arguments[1] === undefined ? { trim: false } : arguments[1]; | |
if (!form) { | |
throw new Error('A form is required by getFormData, was given form=' + form); | |
} | |
var data = {}; | |
var elementName = undefined; | |
var elementNames = []; | |
var elementNameLookup = {}; | |
// Get unique submittable element names for the form | |
for (var i = 0, l = form.elements.length; i < l; i++) { | |
var element = form.elements[i]; | |
if (IGNORED_ELEMENT_TYPES[element.type] || element.disabled) { | |
continue; | |
} | |
elementName = element.name || element.id; | |
if (elementName && !elementNameLookup[elementName]) { | |
elementNames.push(elementName); | |
elementNameLookup[elementName] = true; | |
} | |
} | |
// Extract element data name-by-name for consistent handling of special cases | |
// around elements which contain multiple inputs. | |
for (var i = 0, l = elementNames.length; i < l; i++) { | |
elementName = elementNames[i]; | |
var value = getNamedFormElementData(form, elementName, options); | |
if (value != null) { | |
data[elementName] = value; | |
} | |
} | |
return data; | |
} | |
/** | |
* @param {HTMLFormElement} form | |
* @param {string} elementName | |
* @param {Object} options | |
* @return {(string|Array.<string>)} submittable value(s) in the form for a | |
* named element from its .elements collection, or null if there was no | |
* element with that name or the element had no submittable value(s). | |
*/ | |
function getNamedFormElementData(form, elementName) { | |
var options = arguments.length <= 2 || arguments[2] === undefined ? { trim: false } : arguments[2]; | |
if (!form) { | |
throw new Error('A form is required by getNamedFormElementData, was given form=' + form); | |
} | |
if (!elementName && __toString_35.call(elementName) !== '[object String]') { | |
throw new Error('A form element name is required by getNamedFormElementData, was given elementName=' + elementName); | |
} | |
var element = form.elements[elementName]; | |
if (!element || element.disabled) { | |
return null; | |
} | |
if (!NODE_LIST_CLASSES[__toString_35.call(element)]) { | |
return getFormElementValue(element, options.trim); | |
} | |
// Deal with multiple form controls which have the same name | |
var data = []; | |
var allRadios = true; | |
for (var i = 0, l = element.length; i < l; i++) { | |
if (element[i].disabled) { | |
continue; | |
} | |
if (allRadios && element[i].type !== 'radio') { | |
allRadios = false; | |
} | |
var value = getFormElementValue(element[i], options.trim); | |
if (value != null) { | |
data = data.concat(value); | |
} | |
} | |
// Special case for an element with multiple same-named inputs which were all | |
// radio buttons: if there was a selected value, only return the value. | |
if (allRadios && data.length === 1) { | |
return data[0]; | |
} | |
return data.length > 0 ? data : null; | |
} | |
/** | |
* @param {HTMLElement} element a form element. | |
* @param {booleam} trim should values for text entry inputs be trimmed? | |
* @return {(string|Array.<string>|File|Array.<File>)} the element's submittable | |
* value(s), or null if it had none. | |
*/ | |
function getFormElementValue(element, trim) { | |
var value = null; | |
var type = element.type; | |
if (type === 'select-one') { | |
if (element.options.length) { | |
value = element.options[element.selectedIndex].value; | |
} | |
return value; | |
} | |
if (type === 'select-multiple') { | |
value = []; | |
for (var i = 0, l = element.options.length; i < l; i++) { | |
if (element.options[i].selected) { | |
value.push(element.options[i].value); | |
} | |
} | |
if (value.length === 0) { | |
value = null; | |
} | |
return value; | |
} | |
// If a file input doesn't have a files attribute, fall through to using its | |
// value attribute. | |
if (type === 'file' && 'files' in element) { | |
if (element.multiple) { | |
value = __slice_35.call(element.files); | |
if (value.length === 0) { | |
value = null; | |
} | |
} else { | |
// Should be null if not present, according to the spec | |
value = element.files[0]; | |
} | |
return value; | |
} | |
if (!CHECKED_INPUT_TYPES[type]) { | |
value = trim ? element.value.replace(TRIM_RE, '') : element.value; | |
} else if (element.checked) { | |
value = element.value; | |
} | |
return value; | |
} | |
getFormData.getNamedFormElementData = getNamedFormElementData; | |
_$lib_35['default'] = getFormData; | |
_$lib_35 = _$lib_35['default']; | |
var _$throttle_44 = {}; | |
(function (global){ | |
/** | |
* lodash (Custom Build) <https://lodash.com/> | |
* Build: `lodash modularize exports="npm" -o ./` | |
* Copyright jQuery Foundation and other contributors <https://jquery.org/> | |
* Released under MIT license <https://lodash.com/license> | |
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> | |
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors | |
*/ | |
/** Used as the `TypeError` message for "Functions" methods. */ | |
var FUNC_ERROR_TEXT = 'Expected a function'; | |
/** Used as references for various `Number` constants. */ | |
var NAN = 0 / 0; | |
/** `Object#toString` result references. */ | |
var symbolTag = '[object Symbol]'; | |
/** Used to match leading and trailing whitespace. */ | |
var reTrim = /^\s+|\s+$/g; | |
/** Used to detect bad signed hexadecimal string values. */ | |
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; | |
/** Used to detect binary string values. */ | |
var reIsBinary = /^0b[01]+$/i; | |
/** Used to detect octal string values. */ | |
var reIsOctal = /^0o[0-7]+$/i; | |
/** Built-in method references without a dependency on `root`. */ | |
var freeParseInt = parseInt; | |
/** Detect free variable `global` from Node.js. */ | |
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; | |
/** Detect free variable `self`. */ | |
var freeSelf = typeof self == 'object' && self && self.Object === Object && self; | |
/** Used as a reference to the global object. */ | |
var root = freeGlobal || freeSelf || Function('return this')(); | |
/** Used for built-in method references. */ | |
var objectProto = Object.prototype; | |
/** | |
* Used to resolve the | |
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) | |
* of values. | |
*/ | |
var objectToString = objectProto.toString; | |
/* Built-in method references for those with the same name as other `lodash` methods. */ | |
var nativeMax = Math.max, | |
nativeMin = Math.min; | |
/** | |
* Gets the timestamp of the number of milliseconds that have elapsed since | |
* the Unix epoch (1 January 1970 00:00:00 UTC). | |
* | |
* @static | |
* @memberOf _ | |
* @since 2.4.0 | |
* @category Date | |
* @returns {number} Returns the timestamp. | |
* @example | |
* | |
* _.defer(function(stamp) { | |
* console.log(_.now() - stamp); | |
* }, _.now()); | |
* // => Logs the number of milliseconds it took for the deferred invocation. | |
*/ | |
var now = function() { | |
return root.Date.now(); | |
}; | |
/** | |
* Creates a debounced function that delays invoking `func` until after `wait` | |
* milliseconds have elapsed since the last time the debounced function was | |
* invoked. The debounced function comes with a `cancel` method to cancel | |
* delayed `func` invocations and a `flush` method to immediately invoke them. | |
* Provide `options` to indicate whether `func` should be invoked on the | |
* leading and/or trailing edge of the `wait` timeout. The `func` is invoked | |
* with the last arguments provided to the debounced function. Subsequent | |
* calls to the debounced function return the result of the last `func` | |
* invocation. | |
* | |
* **Note:** If `leading` and `trailing` options are `true`, `func` is | |
* invoked on the trailing edge of the timeout only if the debounced function | |
* is invoked more than once during the `wait` timeout. | |
* | |
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred | |
* until to the next tick, similar to `setTimeout` with a timeout of `0`. | |
* | |
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) | |
* for details over the differences between `_.debounce` and `_.throttle`. | |
* | |
* @static | |
* @memberOf _ | |
* @since 0.1.0 | |
* @category Function | |
* @param {Function} func The function to debounce. | |
* @param {number} [wait=0] The number of milliseconds to delay. | |
* @param {Object} [options={}] The options object. | |
* @param {boolean} [options.leading=false] | |
* Specify invoking on the leading edge of the timeout. | |
* @param {number} [options.maxWait] | |
* The maximum time `func` is allowed to be delayed before it's invoked. | |
* @param {boolean} [options.trailing=true] | |
* Specify invoking on the trailing edge of the timeout. | |
* @returns {Function} Returns the new debounced function. | |
* @example | |
* | |
* // Avoid costly calculations while the window size is in flux. | |
* jQuery(window).on('resize', _.debounce(calculateLayout, 150)); | |
* | |
* // Invoke `sendMail` when clicked, debouncing subsequent calls. | |
* jQuery(element).on('click', _.debounce(sendMail, 300, { | |
* 'leading': true, | |
* 'trailing': false | |
* })); | |
* | |
* // Ensure `batchLog` is invoked once after 1 second of debounced calls. | |
* var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); | |
* var source = new EventSource('/stream'); | |
* jQuery(source).on('message', debounced); | |
* | |
* // Cancel the trailing debounced invocation. | |
* jQuery(window).on('popstate', debounced.cancel); | |
*/ | |
function debounce(func, wait, options) { | |
var lastArgs, | |
lastThis, | |
maxWait, | |
result, | |
timerId, | |
lastCallTime, | |
lastInvokeTime = 0, | |
leading = false, | |
maxing = false, | |
trailing = true; | |
if (typeof func != 'function') { | |
throw new TypeError(FUNC_ERROR_TEXT); | |
} | |
wait = toNumber(wait) || 0; | |
if (isObject(options)) { | |
leading = !!options.leading; | |
maxing = 'maxWait' in options; | |
maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; | |
trailing = 'trailing' in options ? !!options.trailing : trailing; | |
} | |
function invokeFunc(time) { | |
var args = lastArgs, | |
thisArg = lastThis; | |
lastArgs = lastThis = undefined; | |
lastInvokeTime = time; | |
result = func.apply(thisArg, args); | |
return result; | |
} | |
function leadingEdge(time) { | |
// Reset any `maxWait` timer. | |
lastInvokeTime = time; | |
// Start the timer for the trailing edge. | |
timerId = setTimeout(timerExpired, wait); | |
// Invoke the leading edge. | |
return leading ? invokeFunc(time) : result; | |
} | |
function remainingWait(time) { | |
var timeSinceLastCall = time - lastCallTime, | |
timeSinceLastInvoke = time - lastInvokeTime, | |
result = wait - timeSinceLastCall; | |
return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result; | |
} | |
function shouldInvoke(time) { | |
var timeSinceLastCall = time - lastCallTime, | |
timeSinceLastInvoke = time - lastInvokeTime; | |
// Either this is the first call, activity has stopped and we're at the | |
// trailing edge, the system time has gone backwards and we're treating | |
// it as the trailing edge, or we've hit the `maxWait` limit. | |
return (lastCallTime === undefined || (timeSinceLastCall >= wait) || | |
(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); | |
} | |
function timerExpired() { | |
var time = now(); | |
if (shouldInvoke(time)) { | |
return trailingEdge(time); | |
} | |
// Restart the timer. | |
timerId = setTimeout(timerExpired, remainingWait(time)); | |
} | |
function trailingEdge(time) { | |
timerId = undefined; | |
// Only invoke if we have `lastArgs` which means `func` has been | |
// debounced at least once. | |
if (trailing && lastArgs) { | |
return invokeFunc(time); | |
} | |
lastArgs = lastThis = undefined; | |
return result; | |
} | |
function cancel() { | |
if (timerId !== undefined) { | |
clearTimeout(timerId); | |
} | |
lastInvokeTime = 0; | |
lastArgs = lastCallTime = lastThis = timerId = undefined; | |
} | |
function flush() { | |
return timerId === undefined ? result : trailingEdge(now()); | |
} | |
function debounced() { | |
var time = now(), | |
isInvoking = shouldInvoke(time); | |
lastArgs = arguments; | |
lastThis = this; | |
lastCallTime = time; | |
if (isInvoking) { | |
if (timerId === undefined) { | |
return leadingEdge(lastCallTime); | |
} | |
if (maxing) { | |
// Handle invocations in a tight loop. | |
timerId = setTimeout(timerExpired, wait); | |
return invokeFunc(lastCallTime); | |
} | |
} | |
if (timerId === undefined) { | |
timerId = setTimeout(timerExpired, wait); | |
} | |
return result; | |
} | |
debounced.cancel = cancel; | |
debounced.flush = flush; | |
return debounced; | |
} | |
/** | |
* Creates a throttled function that only invokes `func` at most once per | |
* every `wait` milliseconds. The throttled function comes with a `cancel` | |
* method to cancel delayed `func` invocations and a `flush` method to | |
* immediately invoke them. Provide `options` to indicate whether `func` | |
* should be invoked on the leading and/or trailing edge of the `wait` | |
* timeout. The `func` is invoked with the last arguments provided to the | |
* throttled function. Subsequent calls to the throttled function return the | |
* result of the last `func` invocation. | |
* | |
* **Note:** If `leading` and `trailing` options are `true`, `func` is | |
* invoked on the trailing edge of the timeout only if the throttled function | |
* is invoked more than once during the `wait` timeout. | |
* | |
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred | |
* until to the next tick, similar to `setTimeout` with a timeout of `0`. | |
* | |
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) | |
* for details over the differences between `_.throttle` and `_.debounce`. | |
* | |
* @static | |
* @memberOf _ | |
* @since 0.1.0 | |
* @category Function | |
* @param {Function} func The function to throttle. | |
* @param {number} [wait=0] The number of milliseconds to throttle invocations to. | |
* @param {Object} [options={}] The options object. | |
* @param {boolean} [options.leading=true] | |
* Specify invoking on the leading edge of the timeout. | |
* @param {boolean} [options.trailing=true] | |
* Specify invoking on the trailing edge of the timeout. | |
* @returns {Function} Returns the new throttled function. | |
* @example | |
* | |
* // Avoid excessively updating the position while scrolling. | |
* jQuery(window).on('scroll', _.throttle(updatePosition, 100)); | |
* | |
* // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. | |
* var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); | |
* jQuery(element).on('click', throttled); | |
* | |
* // Cancel the trailing throttled invocation. | |
* jQuery(window).on('popstate', throttled.cancel); | |
*/ | |
function throttle(func, wait, options) { | |
var leading = true, | |
trailing = true; | |
if (typeof func != 'function') { | |
throw new TypeError(FUNC_ERROR_TEXT); | |
} | |
if (isObject(options)) { | |
leading = 'leading' in options ? !!options.leading : leading; | |
trailing = 'trailing' in options ? !!options.trailing : trailing; | |
} | |
return debounce(func, wait, { | |
'leading': leading, | |
'maxWait': wait, | |
'trailing': trailing | |
}); | |
} | |
/** | |
* Checks if `value` is the | |
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) | |
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) | |
* | |
* @static | |
* @memberOf _ | |
* @since 0.1.0 | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is an object, else `false`. | |
* @example | |
* | |
* _.isObject({}); | |
* // => true | |
* | |
* _.isObject([1, 2, 3]); | |
* // => true | |
* | |
* _.isObject(_.noop); | |
* // => true | |
* | |
* _.isObject(null); | |
* // => false | |
*/ | |
function isObject(value) { | |
var type = typeof value; | |
return !!value && (type == 'object' || type == 'function'); | |
} | |
/** | |
* Checks if `value` is object-like. A value is object-like if it's not `null` | |
* and has a `typeof` result of "object". | |
* | |
* @static | |
* @memberOf _ | |
* @since 4.0.0 | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is object-like, else `false`. | |
* @example | |
* | |
* _.isObjectLike({}); | |
* // => true | |
* | |
* _.isObjectLike([1, 2, 3]); | |
* // => true | |
* | |
* _.isObjectLike(_.noop); | |
* // => false | |
* | |
* _.isObjectLike(null); | |
* // => false | |
*/ | |
function isObjectLike(value) { | |
return !!value && typeof value == 'object'; | |
} | |
/** | |
* Checks if `value` is classified as a `Symbol` primitive or object. | |
* | |
* @static | |
* @memberOf _ | |
* @since 4.0.0 | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is a symbol, else `false`. | |
* @example | |
* | |
* _.isSymbol(Symbol.iterator); | |
* // => true | |
* | |
* _.isSymbol('abc'); | |
* // => false | |
*/ | |
function isSymbol(value) { | |
return typeof value == 'symbol' || | |
(isObjectLike(value) && objectToString.call(value) == symbolTag); | |
} | |
/** | |
* Converts `value` to a number. | |
* | |
* @static | |
* @memberOf _ | |
* @since 4.0.0 | |
* @category Lang | |
* @param {*} value The value to process. | |
* @returns {number} Returns the number. | |
* @example | |
* | |
* _.toNumber(3.2); | |
* // => 3.2 | |
* | |
* _.toNumber(Number.MIN_VALUE); | |
* // => 5e-324 | |
* | |
* _.toNumber(Infinity); | |
* // => Infinity | |
* | |
* _.toNumber('3.2'); | |
* // => 3.2 | |
*/ | |
function toNumber(value) { | |
if (typeof value == 'number') { | |
return value; | |
} | |
if (isSymbol(value)) { | |
return NAN; | |
} | |
if (isObject(value)) { | |
var other = typeof value.valueOf == 'function' ? value.valueOf() : value; | |
value = isObject(other) ? (other + '') : other; | |
} | |
if (typeof value != 'string') { | |
return value === 0 ? value : +value; | |
} | |
value = value.replace(reTrim, ''); | |
var isBinary = reIsBinary.test(value); | |
return (isBinary || reIsOctal.test(value)) | |
? freeParseInt(value.slice(2), isBinary ? 2 : 8) | |
: (reIsBadHex.test(value) ? NAN : +value); | |
} | |
_$throttle_44 = throttle; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
/* jshint node: true */ | |
'use strict'; | |
/** | |
# wildcard | |
Very simple wildcard matching, which is designed to provide the same | |
functionality that is found in the | |
[eve](https://github.com/adobe-webplatform/eve) eventing library. | |
## Usage | |
It works with strings: | |
<<< examples/strings.js | |
Arrays: | |
<<< examples/arrays.js | |
Objects (matching against keys): | |
<<< examples/objects.js | |
While the library works in Node, if you are are looking for file-based | |
wildcard matching then you should have a look at: | |
<https://github.com/isaacs/node-glob> | |
**/ | |
function WildcardMatcher(text, separator) { | |
this.text = text = text || ''; | |
this.hasWild = ~text.indexOf('*'); | |
this.separator = separator; | |
this.parts = text.split(separator); | |
} | |
WildcardMatcher.prototype.match = function(input) { | |
var matches = true; | |
var parts = this.parts; | |
var ii; | |
var partsCount = parts.length; | |
var testParts; | |
if (typeof input == 'string' || input instanceof String) { | |
if (!this.hasWild && this.text != input) { | |
matches = false; | |
} else { | |
testParts = (input || '').split(this.separator); | |
for (ii = 0; matches && ii < partsCount; ii++) { | |
if (parts[ii] === '*') { | |
continue; | |
} else if (ii < testParts.length) { | |
matches = parts[ii] === testParts[ii]; | |
} else { | |
matches = false; | |
} | |
} | |
// If matches, then return the component parts | |
matches = matches && testParts; | |
} | |
} | |
else if (typeof input.splice == 'function') { | |
matches = []; | |
for (ii = input.length; ii--; ) { | |
if (this.match(input[ii])) { | |
matches[matches.length] = input[ii]; | |
} | |
} | |
} | |
else if (typeof input == 'object') { | |
matches = {}; | |
for (var key in input) { | |
if (this.match(key)) { | |
matches[key] = input[key]; | |
} | |
} | |
} | |
return matches; | |
}; | |
var _$wildcard_78 = function(text, test, separator) { | |
var matcher = new WildcardMatcher(text, separator || /[\/\.]/); | |
if (typeof test != 'undefined') { | |
return matcher.match(test); | |
} | |
return matcher; | |
}; | |
/* removed: var _$wildcard_78 = require('wildcard'); */; | |
var reMimePartSplit = /[\/\+\.]/; | |
/** | |
# mime-match | |
A simple function to checker whether a target mime type matches a mime-type | |
pattern (e.g. image/jpeg matches image/jpeg OR image/*). | |
## Example Usage | |
<<< example.js | |
**/ | |
var _$mimeMatch_45 = function(target, pattern) { | |
function test(pattern) { | |
var result = _$wildcard_78(pattern, target, reMimePartSplit); | |
// ensure that we have a valid mime type (should have two parts) | |
return result && result.length >= 2; | |
} | |
return pattern ? test(pattern.split(';')[0]) : test; | |
}; | |
'use strict'; | |
var range; // Create a range object for efficently rendering strings to elements. | |
var NS_XHTML = 'http://www.w3.org/1999/xhtml'; | |
var doc = typeof document === 'undefined' ? undefined : document; | |
var testEl = doc ? | |
doc.body || doc.createElement('div') : | |
{}; | |
// Fixes <https://github.com/patrick-steele-idem/morphdom/issues/32> | |
// (IE7+ support) <=IE7 does not support el.hasAttribute(name) | |
var actualHasAttributeNS; | |
if (testEl.hasAttributeNS) { | |
actualHasAttributeNS = function(el, namespaceURI, name) { | |
return el.hasAttributeNS(namespaceURI, name); | |
}; | |
} else if (testEl.hasAttribute) { | |
actualHasAttributeNS = function(el, namespaceURI, name) { | |
return el.hasAttribute(name); | |
}; | |
} else { | |
actualHasAttributeNS = function(el, namespaceURI, name) { | |
return el.getAttributeNode(namespaceURI, name) != null; | |
}; | |
} | |
var hasAttributeNS = actualHasAttributeNS; | |
function toElement(str) { | |
if (!range && doc.createRange) { | |
range = doc.createRange(); | |
range.selectNode(doc.body); | |
} | |
var fragment; | |
if (range && range.createContextualFragment) { | |
fragment = range.createContextualFragment(str); | |
} else { | |
fragment = doc.createElement('body'); | |
fragment.innerHTML = str; | |
} | |
return fragment.childNodes[0]; | |
} | |
/** | |
* Returns true if two node's names are the same. | |
* | |
* NOTE: We don't bother checking `namespaceURI` because you will never find two HTML elements with the same | |
* nodeName and different namespace URIs. | |
* | |
* @param {Element} a | |
* @param {Element} b The target element | |
* @return {boolean} | |
*/ | |
function compareNodeNames(fromEl, toEl) { | |
var fromNodeName = fromEl.nodeName; | |
var toNodeName = toEl.nodeName; | |
if (fromNodeName === toNodeName) { | |
return true; | |
} | |
if (toEl.actualize && | |
fromNodeName.charCodeAt(0) < 91 && /* from tag name is upper case */ | |
toNodeName.charCodeAt(0) > 90 /* target tag name is lower case */) { | |
// If the target element is a virtual DOM node then we may need to normalize the tag name | |
// before comparing. Normal HTML elements that are in the "http://www.w3.org/1999/xhtml" | |
// are converted to upper case | |
return fromNodeName === toNodeName.toUpperCase(); | |
} else { | |
return false; | |
} | |
} | |
/** | |
* Create an element, optionally with a known namespace URI. | |
* | |
* @param {string} name the element name, e.g. 'div' or 'svg' | |
* @param {string} [namespaceURI] the element's namespace URI, i.e. the value of | |
* its `xmlns` attribute or its inferred namespace. | |
* | |
* @return {Element} | |
*/ | |
function createElementNS(name, namespaceURI) { | |
return !namespaceURI || namespaceURI === NS_XHTML ? | |
doc.createElement(name) : | |
doc.createElementNS(namespaceURI, name); | |
} | |
/** | |
* Copies the children of one DOM element to another DOM element | |
*/ | |
function moveChildren(fromEl, toEl) { | |
var curChild = fromEl.firstChild; | |
while (curChild) { | |
var nextChild = curChild.nextSibling; | |
toEl.appendChild(curChild); | |
curChild = nextChild; | |
} | |
return toEl; | |
} | |
function morphAttrs(fromNode, toNode) { | |
var attrs = toNode.attributes; | |
var i; | |
var attr; | |
var attrName; | |
var attrNamespaceURI; | |
var attrValue; | |
var fromValue; | |
for (i = attrs.length - 1; i >= 0; --i) { | |
attr = attrs[i]; | |
attrName = attr.name; | |
attrNamespaceURI = attr.namespaceURI; | |
attrValue = attr.value; | |
if (attrNamespaceURI) { | |
attrName = attr.localName || attrName; | |
fromValue = fromNode.getAttributeNS(attrNamespaceURI, attrName); | |
if (fromValue !== attrValue) { | |
fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue); | |
} | |
} else { | |
fromValue = fromNode.getAttribute(attrName); | |
if (fromValue !== attrValue) { | |
fromNode.setAttribute(attrName, attrValue); | |
} | |
} | |
} | |
// Remove any extra attributes found on the original DOM element that | |
// weren't found on the target element. | |
attrs = fromNode.attributes; | |
for (i = attrs.length - 1; i >= 0; --i) { | |
attr = attrs[i]; | |
if (attr.specified !== false) { | |
attrName = attr.name; | |
attrNamespaceURI = attr.namespaceURI; | |
if (attrNamespaceURI) { | |
attrName = attr.localName || attrName; | |
if (!hasAttributeNS(toNode, attrNamespaceURI, attrName)) { | |
fromNode.removeAttributeNS(attrNamespaceURI, attrName); | |
} | |
} else { | |
if (!hasAttributeNS(toNode, null, attrName)) { | |
fromNode.removeAttribute(attrName); | |
} | |
} | |
} | |
} | |
} | |
function syncBooleanAttrProp(fromEl, toEl, name) { | |
if (fromEl[name] !== toEl[name]) { | |
fromEl[name] = toEl[name]; | |
if (fromEl[name]) { | |
fromEl.setAttribute(name, ''); | |
} else { | |
fromEl.removeAttribute(name, ''); | |
} | |
} | |
} | |
var specialElHandlers = { | |
/** | |
* Needed for IE. Apparently IE doesn't think that "selected" is an | |
* attribute when reading over the attributes using selectEl.attributes | |
*/ | |
OPTION: function(fromEl, toEl) { | |
syncBooleanAttrProp(fromEl, toEl, 'selected'); | |
}, | |
/** | |
* The "value" attribute is special for the <input> element since it sets | |
* the initial value. Changing the "value" attribute without changing the | |
* "value" property will have no effect since it is only used to the set the | |
* initial value. Similar for the "checked" attribute, and "disabled". | |
*/ | |
INPUT: function(fromEl, toEl) { | |
syncBooleanAttrProp(fromEl, toEl, 'checked'); | |
syncBooleanAttrProp(fromEl, toEl, 'disabled'); | |
if (fromEl.value !== toEl.value) { | |
fromEl.value = toEl.value; | |
} | |
if (!hasAttributeNS(toEl, null, 'value')) { | |
fromEl.removeAttribute('value'); | |
} | |
}, | |
TEXTAREA: function(fromEl, toEl) { | |
var newValue = toEl.value; | |
if (fromEl.value !== newValue) { | |
fromEl.value = newValue; | |
} | |
var firstChild = fromEl.firstChild; | |
if (firstChild) { | |
// Needed for IE. Apparently IE sets the placeholder as the | |
// node value and vise versa. This ignores an empty update. | |
var oldValue = firstChild.nodeValue; | |
if (oldValue == newValue || (!newValue && oldValue == fromEl.placeholder)) { | |
return; | |
} | |
firstChild.nodeValue = newValue; | |
} | |
}, | |
SELECT: function(fromEl, toEl) { | |
if (!hasAttributeNS(toEl, null, 'multiple')) { | |
var selectedIndex = -1; | |
var i = 0; | |
var curChild = toEl.firstChild; | |
while(curChild) { | |
var nodeName = curChild.nodeName; | |
if (nodeName && nodeName.toUpperCase() === 'OPTION') { | |
if (hasAttributeNS(curChild, null, 'selected')) { | |
selectedIndex = i; | |
break; | |
} | |
i++; | |
} | |
curChild = curChild.nextSibling; | |
} | |
fromEl.selectedIndex = i; | |
} | |
} | |
}; | |
var ELEMENT_NODE = 1; | |
var TEXT_NODE = 3; | |
var COMMENT_NODE = 8; | |
function __noop_46() {} | |
function defaultGetNodeKey(node) { | |
return node.id; | |
} | |
function morphdomFactory(morphAttrs) { | |
return function morphdom(fromNode, toNode, options) { | |
if (!options) { | |
options = {}; | |
} | |
if (typeof toNode === 'string') { | |
if (fromNode.nodeName === '#document' || fromNode.nodeName === 'HTML') { | |
var toNodeHtml = toNode; | |
toNode = doc.createElement('html'); | |
toNode.innerHTML = toNodeHtml; | |
} else { | |
toNode = toElement(toNode); | |
} | |
} | |
var getNodeKey = options.getNodeKey || defaultGetNodeKey; | |
var onBeforeNodeAdded = options.onBeforeNodeAdded || __noop_46; | |
var onNodeAdded = options.onNodeAdded || __noop_46; | |
var onBeforeElUpdated = options.onBeforeElUpdated || __noop_46; | |
var onElUpdated = options.onElUpdated || __noop_46; | |
var onBeforeNodeDiscarded = options.onBeforeNodeDiscarded || __noop_46; | |
var onNodeDiscarded = options.onNodeDiscarded || __noop_46; | |
var onBeforeElChildrenUpdated = options.onBeforeElChildrenUpdated || __noop_46; | |
var childrenOnly = options.childrenOnly === true; | |
// This object is used as a lookup to quickly find all keyed elements in the original DOM tree. | |
var fromNodesLookup = {}; | |
var keyedRemovalList; | |
function addKeyedRemoval(key) { | |
if (keyedRemovalList) { | |
keyedRemovalList.push(key); | |
} else { | |
keyedRemovalList = [key]; | |
} | |
} | |
function walkDiscardedChildNodes(node, skipKeyedNodes) { | |
if (node.nodeType === ELEMENT_NODE) { | |
var curChild = node.firstChild; | |
while (curChild) { | |
var key = undefined; | |
if (skipKeyedNodes && (key = getNodeKey(curChild))) { | |
// If we are skipping keyed nodes then we add the key | |
// to a list so that it can be handled at the very end. | |
addKeyedRemoval(key); | |
} else { | |
// Only report the node as discarded if it is not keyed. We do this because | |
// at the end we loop through all keyed elements that were unmatched | |
// and then discard them in one final pass. | |
onNodeDiscarded(curChild); | |
if (curChild.firstChild) { | |
walkDiscardedChildNodes(curChild, skipKeyedNodes); | |
} | |
} | |
curChild = curChild.nextSibling; | |
} | |
} | |
} | |
/** | |
* Removes a DOM node out of the original DOM | |
* | |
* @param {Node} node The node to remove | |
* @param {Node} parentNode The nodes parent | |
* @param {Boolean} skipKeyedNodes If true then elements with keys will be skipped and not discarded. | |
* @return {undefined} | |
*/ | |
function removeNode(node, parentNode, skipKeyedNodes) { | |
if (onBeforeNodeDiscarded(node) === false) { | |
return; | |
} | |
if (parentNode) { | |
parentNode.removeChild(node); | |
} | |
onNodeDiscarded(node); | |
walkDiscardedChildNodes(node, skipKeyedNodes); | |
} | |
// // TreeWalker implementation is no faster, but keeping this around in case this changes in the future | |
// function indexTree(root) { | |
// var treeWalker = document.createTreeWalker( | |
// root, | |
// NodeFilter.SHOW_ELEMENT); | |
// | |
// var el; | |
// while((el = treeWalker.nextNode())) { | |
// var key = getNodeKey(el); | |
// if (key) { | |
// fromNodesLookup[key] = el; | |
// } | |
// } | |
// } | |
// // NodeIterator implementation is no faster, but keeping this around in case this changes in the future | |
// | |
// function indexTree(node) { | |
// var nodeIterator = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT); | |
// var el; | |
// while((el = nodeIterator.nextNode())) { | |
// var key = getNodeKey(el); | |
// if (key) { | |
// fromNodesLookup[key] = el; | |
// } | |
// } | |
// } | |
function indexTree(node) { | |
if (node.nodeType === ELEMENT_NODE) { | |
var curChild = node.firstChild; | |
while (curChild) { | |
var key = getNodeKey(curChild); | |
if (key) { | |
fromNodesLookup[key] = curChild; | |
} | |
// Walk recursively | |
indexTree(curChild); | |
curChild = curChild.nextSibling; | |
} | |
} | |
} | |
indexTree(fromNode); | |
function handleNodeAdded(el) { | |
onNodeAdded(el); | |
var curChild = el.firstChild; | |
while (curChild) { | |
var nextSibling = curChild.nextSibling; | |
var key = getNodeKey(curChild); | |
if (key) { | |
var unmatchedFromEl = fromNodesLookup[key]; | |
if (unmatchedFromEl && compareNodeNames(curChild, unmatchedFromEl)) { | |
curChild.parentNode.replaceChild(unmatchedFromEl, curChild); | |
morphEl(unmatchedFromEl, curChild); | |
} | |
} | |
handleNodeAdded(curChild); | |
curChild = nextSibling; | |
} | |
} | |
function morphEl(fromEl, toEl, childrenOnly) { | |
var toElKey = getNodeKey(toEl); | |
var curFromNodeKey; | |
if (toElKey) { | |
// If an element with an ID is being morphed then it is will be in the final | |
// DOM so clear it out of the saved elements collection | |
delete fromNodesLookup[toElKey]; | |
} | |
if (toNode.isSameNode && toNode.isSameNode(fromNode)) { | |
return; | |
} | |
if (!childrenOnly) { | |
if (onBeforeElUpdated(fromEl, toEl) === false) { | |
return; | |
} | |
morphAttrs(fromEl, toEl); | |
onElUpdated(fromEl); | |
if (onBeforeElChildrenUpdated(fromEl, toEl) === false) { | |
return; | |
} | |
} | |
if (fromEl.nodeName !== 'TEXTAREA') { | |
var curToNodeChild = toEl.firstChild; | |
var curFromNodeChild = fromEl.firstChild; | |
var curToNodeKey; | |
var fromNextSibling; | |
var toNextSibling; | |
var matchingFromEl; | |
outer: while (curToNodeChild) { | |
toNextSibling = curToNodeChild.nextSibling; | |
curToNodeKey = getNodeKey(curToNodeChild); | |
while (curFromNodeChild) { | |
fromNextSibling = curFromNodeChild.nextSibling; | |
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) { | |
curToNodeChild = toNextSibling; | |
curFromNodeChild = fromNextSibling; | |
continue outer; | |
} | |
curFromNodeKey = getNodeKey(curFromNodeChild); | |
var curFromNodeType = curFromNodeChild.nodeType; | |
var isCompatible = undefined; | |
if (curFromNodeType === curToNodeChild.nodeType) { | |
if (curFromNodeType === ELEMENT_NODE) { | |
// Both nodes being compared are Element nodes | |
if (curToNodeKey) { | |
// The target node has a key so we want to match it up with the correct element | |
// in the original DOM tree | |
if (curToNodeKey !== curFromNodeKey) { | |
// The current element in the original DOM tree does not have a matching key so | |
// let's check our lookup to see if there is a matching element in the original | |
// DOM tree | |
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) { | |
if (curFromNodeChild.nextSibling === matchingFromEl) { | |
// Special case for single element removals. To avoid removing the original | |
// DOM node out of the tree (since that can break CSS transitions, etc.), | |
// we will instead discard the current node and wait until the next | |
// iteration to properly match up the keyed target element with its matching | |
// element in the original tree | |
isCompatible = false; | |
} else { | |
// We found a matching keyed element somewhere in the original DOM tree. | |
// Let's moving the original DOM node into the current position and morph | |
// it. | |
// NOTE: We use insertBefore instead of replaceChild because we want to go through | |
// the `removeNode()` function for the node that is being discarded so that | |
// all lifecycle hooks are correctly invoked | |
fromEl.insertBefore(matchingFromEl, curFromNodeChild); | |
fromNextSibling = curFromNodeChild.nextSibling; | |
if (curFromNodeKey) { | |
// Since the node is keyed it might be matched up later so we defer | |
// the actual removal to later | |
addKeyedRemoval(curFromNodeKey); | |
} else { | |
// NOTE: we skip nested keyed nodes from being removed since there is | |
// still a chance they will be matched up later | |
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */); | |
} | |
curFromNodeChild = matchingFromEl; | |
} | |
} else { | |
// The nodes are not compatible since the "to" node has a key and there | |
// is no matching keyed node in the source tree | |
isCompatible = false; | |
} | |
} | |
} else if (curFromNodeKey) { | |
// The original has a key | |
isCompatible = false; | |
} | |
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild); | |
if (isCompatible) { | |
// We found compatible DOM elements so transform | |
// the current "from" node to match the current | |
// target DOM node. | |
morphEl(curFromNodeChild, curToNodeChild); | |
} | |
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) { | |
// Both nodes being compared are Text or Comment nodes | |
isCompatible = true; | |
// Simply update nodeValue on the original node to | |
// change the text value | |
curFromNodeChild.nodeValue = curToNodeChild.nodeValue; | |
} | |
} | |
if (isCompatible) { | |
// Advance both the "to" child and the "from" child since we found a match | |
curToNodeChild = toNextSibling; | |
curFromNodeChild = fromNextSibling; | |
continue outer; | |
} | |
// No compatible match so remove the old node from the DOM and continue trying to find a | |
// match in the original DOM. However, we only do this if the from node is not keyed | |
// since it is possible that a keyed node might match up with a node somewhere else in the | |
// target tree and we don't want to discard it just yet since it still might find a | |
// home in the final DOM tree. After everything is done we will remove any keyed nodes | |
// that didn't find a home | |
if (curFromNodeKey) { | |
// Since the node is keyed it might be matched up later so we defer | |
// the actual removal to later | |
addKeyedRemoval(curFromNodeKey); | |
} else { | |
// NOTE: we skip nested keyed nodes from being removed since there is | |
// still a chance they will be matched up later | |
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */); | |
} | |
curFromNodeChild = fromNextSibling; | |
} | |
// If we got this far then we did not find a candidate match for | |
// our "to node" and we exhausted all of the children "from" | |
// nodes. Therefore, we will just append the current "to" node | |
// to the end | |
if (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) { | |
fromEl.appendChild(matchingFromEl); | |
morphEl(matchingFromEl, curToNodeChild); | |
} else { | |
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild); | |
if (onBeforeNodeAddedResult !== false) { | |
if (onBeforeNodeAddedResult) { | |
curToNodeChild = onBeforeNodeAddedResult; | |
} | |
if (curToNodeChild.actualize) { | |
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc); | |
} | |
fromEl.appendChild(curToNodeChild); | |
handleNodeAdded(curToNodeChild); | |
} | |
} | |
curToNodeChild = toNextSibling; | |
curFromNodeChild = fromNextSibling; | |
} | |
// We have processed all of the "to nodes". If curFromNodeChild is | |
// non-null then we still have some from nodes left over that need | |
// to be removed | |
while (curFromNodeChild) { | |
fromNextSibling = curFromNodeChild.nextSibling; | |
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) { | |
// Since the node is keyed it might be matched up later so we defer | |
// the actual removal to later | |
addKeyedRemoval(curFromNodeKey); | |
} else { | |
// NOTE: we skip nested keyed nodes from being removed since there is | |
// still a chance they will be matched up later | |
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */); | |
} | |
curFromNodeChild = fromNextSibling; | |
} | |
} | |
var specialElHandler = specialElHandlers[fromEl.nodeName]; | |
if (specialElHandler) { | |
specialElHandler(fromEl, toEl); | |
} | |
} // END: morphEl(...) | |
var morphedNode = fromNode; | |
var morphedNodeType = morphedNode.nodeType; | |
var toNodeType = toNode.nodeType; | |
if (!childrenOnly) { | |
// Handle the case where we are given two DOM nodes that are not | |
// compatible (e.g. <div> --> <span> or <div> --> TEXT) | |
if (morphedNodeType === ELEMENT_NODE) { | |
if (toNodeType === ELEMENT_NODE) { | |
if (!compareNodeNames(fromNode, toNode)) { | |
onNodeDiscarded(fromNode); | |
morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI)); | |
} | |
} else { | |
// Going from an element node to a text node | |
morphedNode = toNode; | |
} | |
} else if (morphedNodeType === TEXT_NODE || morphedNodeType === COMMENT_NODE) { // Text or comment node | |
if (toNodeType === morphedNodeType) { | |
morphedNode.nodeValue = toNode.nodeValue; | |
return morphedNode; | |
} else { | |
// Text node to something else | |
morphedNode = toNode; | |
} | |
} | |
} | |
if (morphedNode === toNode) { | |
// The "to node" was not compatible with the "from node" so we had to | |
// toss out the "from node" and use the "to node" | |
onNodeDiscarded(fromNode); | |
} else { | |
morphEl(morphedNode, toNode, childrenOnly); | |
// We now need to loop over any keyed nodes that might need to be | |
// removed. We only do the removal if we know that the keyed node | |
// never found a match. When a keyed node is matched up we remove | |
// it out of fromNodesLookup and we use fromNodesLookup to determine | |
// if a keyed node has been matched up or not | |
if (keyedRemovalList) { | |
for (var i=0, len=keyedRemovalList.length; i<len; i++) { | |
var elToRemove = fromNodesLookup[keyedRemovalList[i]]; | |
if (elToRemove) { | |
removeNode(elToRemove, elToRemove.parentNode, false); | |
} | |
} | |
} | |
} | |
if (!childrenOnly && morphedNode !== fromNode && fromNode.parentNode) { | |
if (morphedNode.actualize) { | |
morphedNode = morphedNode.actualize(fromNode.ownerDocument || doc); | |
} | |
// If we had to swap out the from node with a new node because the old | |
// node was not compatible with the target node then we need to | |
// replace the old DOM node in the original DOM tree. This is only | |
// possible if the original DOM node was part of a DOM tree which | |
// we know is the case if it has a parent node. | |
fromNode.parentNode.replaceChild(morphedNode, fromNode); | |
} | |
return morphedNode; | |
}; | |
} | |
var morphdom = morphdomFactory(morphAttrs); | |
var _$morphdom_46 = morphdom; | |
/** | |
* Create an event emitter with namespaces | |
* @name createNamespaceEmitter | |
* @example | |
* var emitter = require('./index')() | |
* | |
* emitter.on('*', function () { | |
* console.log('all events emitted', this.event) | |
* }) | |
* | |
* emitter.on('example', function () { | |
* console.log('example event emitted') | |
* }) | |
*/ | |
var _$createNamespaceEmitter_48 = function createNamespaceEmitter () { | |
var emitter = { _fns: {} } | |
/** | |
* Emit an event. Optionally namespace the event. Handlers are fired in the order in which they were added with exact matches taking precedence. Separate the namespace and event with a `:` | |
* @name emit | |
* @param {String} event – the name of the event, with optional namespace | |
* @param {...*} data – data variables that will be passed as arguments to the event listener | |
* @example | |
* emitter.emit('example') | |
* emitter.emit('demo:test') | |
* emitter.emit('data', { example: true}, 'a string', 1) | |
*/ | |
emitter.emit = function emit (event) { | |
var args = [].slice.call(arguments, 1) | |
var namespaced = namespaces(event) | |
if (this._fns[event]) emitAll(event, this._fns[event], args) | |
if (namespaced) emitAll(event, namespaced, args) | |
} | |
/** | |
* Create en event listener. | |
* @name on | |
* @param {String} event | |
* @param {Function} fn | |
* @example | |
* emitter.on('example', function () {}) | |
* emitter.on('demo', function () {}) | |
*/ | |
emitter.on = function on (event, fn) { | |
if (typeof fn !== 'function') { throw new Error('callback required') } | |
(this._fns[event] = this._fns[event] || []).push(fn) | |
} | |
/** | |
* Create en event listener that fires once. | |
* @name once | |
* @param {String} event | |
* @param {Function} fn | |
* @example | |
* emitter.once('example', function () {}) | |
* emitter.once('demo', function () {}) | |
*/ | |
emitter.once = function once (event, fn) { | |
function one () { | |
fn.apply(this, arguments) | |
emitter.off(event, one) | |
} | |
this.on(event, one) | |
} | |
/** | |
* Stop listening to an event. Stop all listeners on an event by only passing the event name. Stop a single listener by passing that event handler as a callback. | |
* You must be explicit about what will be unsubscribed: `emitter.off('demo')` will unsubscribe an `emitter.on('demo')` listener, | |
* `emitter.off('demo:example')` will unsubscribe an `emitter.on('demo:example')` listener | |
* @name off | |
* @param {String} event | |
* @param {Function} [fn] – the specific handler | |
* @example | |
* emitter.off('example') | |
* emitter.off('demo', function () {}) | |
*/ | |
emitter.off = function off (event, fn) { | |
var keep = [] | |
if (event && fn) { | |
var fns = this._fns[event] | |
for (var i = 0; i < fns.length; i++) { | |
if (fns[i] !== fn) { | |
keep.push(fns[i]) | |
} | |
} | |
} | |
keep.length ? this._fns[event] = keep : delete this._fns[event] | |
} | |
function namespaces (e) { | |
var out = [] | |
var args = e.split(':') | |
var fns = emitter._fns | |
Object.keys(fns).forEach(function (key) { | |
if (key === '*') out = out.concat(fns[key]) | |
if (args.length === 2 && args[0] === key) out = out.concat(fns[key]) | |
}) | |
return out | |
} | |
function emitAll (e, fns, args) { | |
for (var i = 0; i < fns.length; i++) { | |
if (!fns[i]) break | |
fns[i].event = e | |
fns[i].apply(fns[i], args) | |
} | |
} | |
return emitter | |
} | |
'use strict' | |
/* removed: var _$assert_1 = require('assert') */; | |
var _$nanoraf_49 = nanoraf | |
// Only call RAF when needed | |
// (fn, fn?) -> fn | |
function nanoraf (render, raf) { | |
_$assert_1.equal(typeof render, 'function', 'nanoraf: render should be a function') | |
_$assert_1.ok(typeof raf === 'function' || typeof raf === 'undefined', 'nanoraf: raf should be a function or undefined') | |
if (!raf) raf = window.requestAnimationFrame | |
var redrawScheduled = false | |
var args = null | |
return function frame () { | |
if (args === null && !redrawScheduled) { | |
redrawScheduled = true | |
raf(function redraw () { | |
redrawScheduled = false | |
var length = args.length | |
var _args = new Array(length) | |
for (var i = 0; i < length; i++) _args[i] = args[i] | |
render.apply(render, _args) | |
args = null | |
}) | |
} | |
args = arguments | |
} | |
} | |
var _$prettierBytes_54 = prettierBytes | |
function prettierBytes (num) { | |
if (typeof num !== 'number' || isNaN(num)) { | |
throw new TypeError('Expected a number, got ' + typeof num) | |
} | |
var neg = num < 0 | |
var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] | |
if (neg) { | |
num = -num | |
} | |
if (num < 1) { | |
return (neg ? '-' : '') + num + ' B' | |
} | |
var exponent = Math.min(Math.floor(Math.log(num) / Math.log(1000)), units.length - 1) | |
num = Number(num / Math.pow(1000, exponent)) | |
var unit = units[exponent] | |
if (num >= 10 || num % 1 === 0) { | |
// Do not show decimals when the number is two-digit, or if the number has no | |
// decimal component. | |
return (neg ? '-' : '') + num.toFixed(0) + ' ' + unit | |
} else { | |
return (neg ? '-' : '') + num.toFixed(1) + ' ' + unit | |
} | |
} | |
var _$querystringify_55 = {}; | |
'use strict'; | |
var __has_55 = Object.prototype.hasOwnProperty; | |
/** | |
* Decode a URI encoded string. | |
* | |
* @param {String} input The URI encoded string. | |
* @returns {String} The decoded string. | |
* @api private | |
*/ | |
function __decode_55(input) { | |
return decodeURIComponent(input.replace(/\+/g, ' ')); | |
} | |
/** | |
* Simple query string parser. | |
* | |
* @param {String} query The query string that needs to be parsed. | |
* @returns {Object} | |
* @api public | |
*/ | |
function querystring(query) { | |
var parser = /([^=?&]+)=?([^&]*)/g | |
, result = {} | |
, part; | |
// | |
// Little nifty parsing hack, leverage the fact that RegExp.exec increments | |
// the lastIndex property so we can continue executing this loop until we've | |
// parsed all results. | |
// | |
for (; | |
part = parser.exec(query); | |
result[__decode_55(part[1])] = __decode_55(part[2]) | |
); | |
return result; | |
} | |
/** | |
* Transform a query string to an object. | |
* | |
* @param {Object} obj Object that should be transformed. | |
* @param {String} prefix Optional prefix. | |
* @returns {String} | |
* @api public | |
*/ | |
function querystringify(obj, prefix) { | |
prefix = prefix || ''; | |
var pairs = []; | |
// | |
// Optionally prefix with a '?' if needed | |
// | |
if ('string' !== typeof prefix) prefix = '?'; | |
for (var key in obj) { | |
if (__has_55.call(obj, key)) { | |
pairs.push(encodeURIComponent(key) +'='+ encodeURIComponent(obj[key])); | |
} | |
} | |
return pairs.length ? prefix + pairs.join('&') : ''; | |
} | |
// | |
// Expose the module. | |
// | |
_$querystringify_55.stringify = querystringify; | |
_$querystringify_55.parse = querystring; | |
'use strict'; | |
/** | |
* Check if we're required to add a port number. | |
* | |
* @see https://url.spec.whatwg.org/#default-port | |
* @param {Number|String} port Port number we need to check | |
* @param {String} protocol Protocol we need to check against. | |
* @returns {Boolean} Is it a default port for the given protocol | |
* @api private | |
*/ | |
var _$required_56 = function required(port, protocol) { | |
protocol = protocol.split(':')[0]; | |
port = +port; | |
if (!port) return false; | |
switch (protocol) { | |
case 'http': | |
case 'ws': | |
return port !== 80; | |
case 'https': | |
case 'wss': | |
return port !== 443; | |
case 'ftp': | |
return port !== 21; | |
case 'gopher': | |
return port !== 70; | |
case 'file': | |
return false; | |
} | |
return port !== 0; | |
}; | |
var _$resolveUrl_57 = {}; | |
// Copyright 2014 Simon Lydell | |
// X11 (“MIT”) Licensed. (See LICENSE.) | |
void (function(root, factory) { | |
if (typeof define === "function" && define.amd) { | |
define(factory) | |
} else if (typeof _$resolveUrl_57 === "object") { | |
_$resolveUrl_57 = factory() | |
} else { | |
root.resolveUrl = factory() | |
} | |
}(this, function() { | |
function resolveUrl(/* ...urls */) { | |
var numUrls = arguments.length | |
if (numUrls === 0) { | |
throw new Error("resolveUrl requires at least one argument; got none.") | |
} | |
var base = document.createElement("base") | |
base.href = arguments[0] | |
if (numUrls === 1) { | |
return base.href | |
} | |
var head = document.getElementsByTagName("head")[0] | |
head.insertBefore(base, head.firstChild) | |
var a = document.createElement("a") | |
var resolved | |
for (var index = 1; index < numUrls; index++) { | |
a.href = arguments[index] | |
resolved = a.href | |
base.href = resolved | |
} | |
head.removeChild(base) | |
return resolved | |
} | |
return resolveUrl | |
})); | |
/** | |
* Module exports. | |
*/ | |
var _$on_61 = on; | |
/** | |
* Helper for subscriptions. | |
* | |
* @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter` | |
* @param {String} event name | |
* @param {Function} callback | |
* @api public | |
*/ | |
function on (obj, ev, fn) { | |
obj.on(ev, fn); | |
return { | |
destroy: function () { | |
obj.removeListener(ev, fn); | |
} | |
}; | |
} | |
var _$isBuf_66 = {}; | |
(function (global){ | |
_$isBuf_66 = isBuf; | |
/** | |
* Returns true if obj is a buffer or an arraybuffer. | |
* | |
* @api private | |
*/ | |
function isBuf(obj) { | |
return (global.Buffer && global.Buffer.isBuffer(obj)) || | |
(global.ArrayBuffer && obj instanceof ArrayBuffer); | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$binary_64 = {}; | |
(function (global){ | |
/*global Blob,File*/ | |
/** | |
* Module requirements | |
*/ | |
var isArray = _$isarray_43; | |
var isBuf = _$isBuf_66; | |
var toString = Object.prototype.toString; | |
var withNativeBlob = typeof global.Blob === 'function' || toString.call(global.Blob) === '[object BlobConstructor]'; | |
var withNativeFile = typeof global.File === 'function' || toString.call(global.File) === '[object FileConstructor]'; | |
/** | |
* Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder. | |
* Anything with blobs or files should be fed through removeBlobs before coming | |
* here. | |
* | |
* @param {Object} packet - socket.io event packet | |
* @return {Object} with deconstructed packet and list of buffers | |
* @api public | |
*/ | |
_$binary_64.deconstructPacket = function(packet) { | |
var buffers = []; | |
var packetData = packet.data; | |
var pack = packet; | |
pack.data = _deconstructPacket(packetData, buffers); | |
pack.attachments = buffers.length; // number of binary 'attachments' | |
return {packet: pack, buffers: buffers}; | |
}; | |
function _deconstructPacket(data, buffers) { | |
if (!data) return data; | |
if (isBuf(data)) { | |
var placeholder = { _placeholder: true, num: buffers.length }; | |
buffers.push(data); | |
return placeholder; | |
} else if (isArray(data)) { | |
var newData = new Array(data.length); | |
for (var i = 0; i < data.length; i++) { | |
newData[i] = _deconstructPacket(data[i], buffers); | |
} | |
return newData; | |
} else if (typeof data === 'object' && !(data instanceof Date)) { | |
var newData = {}; | |
for (var key in data) { | |
newData[key] = _deconstructPacket(data[key], buffers); | |
} | |
return newData; | |
} | |
return data; | |
} | |
/** | |
* Reconstructs a binary packet from its placeholder packet and buffers | |
* | |
* @param {Object} packet - event packet with placeholders | |
* @param {Array} buffers - binary buffers to put in placeholder positions | |
* @return {Object} reconstructed packet | |
* @api public | |
*/ | |
_$binary_64.reconstructPacket = function(packet, buffers) { | |
packet.data = _reconstructPacket(packet.data, buffers); | |
packet.attachments = undefined; // no longer useful | |
return packet; | |
}; | |
function _reconstructPacket(data, buffers) { | |
if (!data) return data; | |
if (data && data._placeholder) { | |
return buffers[data.num]; // appropriate buffer (should be natural order anyway) | |
} else if (isArray(data)) { | |
for (var i = 0; i < data.length; i++) { | |
data[i] = _reconstructPacket(data[i], buffers); | |
} | |
} else if (typeof data === 'object') { | |
for (var key in data) { | |
data[key] = _reconstructPacket(data[key], buffers); | |
} | |
} | |
return data; | |
} | |
/** | |
* Asynchronously removes Blobs or Files from data via | |
* FileReader's readAsArrayBuffer method. Used before encoding | |
* data as msgpack. Calls callback with the blobless data. | |
* | |
* @param {Object} data | |
* @param {Function} callback | |
* @api private | |
*/ | |
_$binary_64.removeBlobs = function(data, callback) { | |
function _removeBlobs(obj, curKey, containingObject) { | |
if (!obj) return obj; | |
// convert any blob | |
if ((withNativeBlob && obj instanceof Blob) || | |
(withNativeFile && obj instanceof File)) { | |
pendingBlobs++; | |
// async filereader | |
var fileReader = new FileReader(); | |
fileReader.onload = function() { // this.result == arraybuffer | |
if (containingObject) { | |
containingObject[curKey] = this.result; | |
} | |
else { | |
bloblessData = this.result; | |
} | |
// if nothing pending its callback time | |
if(! --pendingBlobs) { | |
callback(bloblessData); | |
} | |
}; | |
fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer | |
} else if (isArray(obj)) { // handle array | |
for (var i = 0; i < obj.length; i++) { | |
_removeBlobs(obj[i], i, obj); | |
} | |
} else if (typeof obj === 'object' && !isBuf(obj)) { // and object | |
for (var key in obj) { | |
_removeBlobs(obj[key], key, obj); | |
} | |
} | |
} | |
var pendingBlobs = 0; | |
var bloblessData = data; | |
_removeBlobs(bloblessData); | |
if (!pendingBlobs) { | |
callback(bloblessData); | |
} | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$socketIoParser_65 = {}; | |
/** | |
* Module dependencies. | |
*/ | |
var __debug_65 = _$browser_17('socket.io-parser'); | |
/* removed: var _$componentEmitter_14 = require('component-emitter'); */; | |
/* removed: var _$hasBinary_38 = require('has-binary2'); */; | |
/* removed: var _$binary_64 = require('./binary'); */; | |
/* removed: var _$isBuf_66 = require('./is-buffer'); */; | |
/** | |
* Protocol version. | |
* | |
* @api public | |
*/ | |
_$socketIoParser_65.protocol = 4; | |
/** | |
* Packet types. | |
* | |
* @api public | |
*/ | |
_$socketIoParser_65.types = [ | |
'CONNECT', | |
'DISCONNECT', | |
'EVENT', | |
'ACK', | |
'ERROR', | |
'BINARY_EVENT', | |
'BINARY_ACK' | |
]; | |
/** | |
* Packet type `connect`. | |
* | |
* @api public | |
*/ | |
_$socketIoParser_65.CONNECT = 0; | |
/** | |
* Packet type `disconnect`. | |
* | |
* @api public | |
*/ | |
_$socketIoParser_65.DISCONNECT = 1; | |
/** | |
* Packet type `event`. | |
* | |
* @api public | |
*/ | |
_$socketIoParser_65.EVENT = 2; | |
/** | |
* Packet type `ack`. | |
* | |
* @api public | |
*/ | |
_$socketIoParser_65.ACK = 3; | |
/** | |
* Packet type `error`. | |
* | |
* @api public | |
*/ | |
_$socketIoParser_65.ERROR = 4; | |
/** | |
* Packet type 'binary event' | |
* | |
* @api public | |
*/ | |
_$socketIoParser_65.BINARY_EVENT = 5; | |
/** | |
* Packet type `binary ack`. For acks with binary arguments. | |
* | |
* @api public | |
*/ | |
_$socketIoParser_65.BINARY_ACK = 6; | |
/** | |
* Encoder constructor. | |
* | |
* @api public | |
*/ | |
_$socketIoParser_65.Encoder = Encoder; | |
/** | |
* Decoder constructor. | |
* | |
* @api public | |
*/ | |
_$socketIoParser_65.Decoder = Decoder; | |
/** | |
* A socket.io Encoder instance | |
* | |
* @api public | |
*/ | |
function Encoder() {} | |
/** | |
* Encode a packet as a single string if non-binary, or as a | |
* buffer sequence, depending on packet type. | |
* | |
* @param {Object} obj - packet object | |
* @param {Function} callback - function to handle encodings (likely engine.write) | |
* @return Calls callback with Array of encodings | |
* @api public | |
*/ | |
Encoder.prototype.encode = function(obj, callback){ | |
if ((obj.type === _$socketIoParser_65.EVENT || obj.type === _$socketIoParser_65.ACK) && _$hasBinary_38(obj.data)) { | |
obj.type = obj.type === _$socketIoParser_65.EVENT ? _$socketIoParser_65.BINARY_EVENT : _$socketIoParser_65.BINARY_ACK; | |
} | |
__debug_65('encoding packet %j', obj); | |
if (_$socketIoParser_65.BINARY_EVENT === obj.type || _$socketIoParser_65.BINARY_ACK === obj.type) { | |
encodeAsBinary(obj, callback); | |
} | |
else { | |
var encoding = encodeAsString(obj); | |
callback([encoding]); | |
} | |
}; | |
/** | |
* Encode packet as string. | |
* | |
* @param {Object} packet | |
* @return {String} encoded | |
* @api private | |
*/ | |
function encodeAsString(obj) { | |
// first is type | |
var str = '' + obj.type; | |
// attachments if we have them | |
if (_$socketIoParser_65.BINARY_EVENT === obj.type || _$socketIoParser_65.BINARY_ACK === obj.type) { | |
str += obj.attachments + '-'; | |
} | |
// if we have a namespace other than `/` | |
// we append it followed by a comma `,` | |
if (obj.nsp && '/' !== obj.nsp) { | |
str += obj.nsp + ','; | |
} | |
// immediately followed by the id | |
if (null != obj.id) { | |
str += obj.id; | |
} | |
// json data | |
if (null != obj.data) { | |
str += JSON.stringify(obj.data); | |
} | |
__debug_65('encoded %j as %s', obj, str); | |
return str; | |
} | |
/** | |
* Encode packet as 'buffer sequence' by removing blobs, and | |
* deconstructing packet into object with placeholders and | |
* a list of buffers. | |
* | |
* @param {Object} packet | |
* @return {Buffer} encoded | |
* @api private | |
*/ | |
function encodeAsBinary(obj, callback) { | |
function writeEncoding(bloblessData) { | |
var deconstruction = _$binary_64.deconstructPacket(bloblessData); | |
var pack = encodeAsString(deconstruction.packet); | |
var buffers = deconstruction.buffers; | |
buffers.unshift(pack); // add packet info to beginning of data list | |
callback(buffers); // write all the buffers | |
} | |
_$binary_64.removeBlobs(obj, writeEncoding); | |
} | |
/** | |
* A socket.io Decoder instance | |
* | |
* @return {Object} decoder | |
* @api public | |
*/ | |
function Decoder() { | |
this.reconstructor = null; | |
} | |
/** | |
* Mix in `Emitter` with Decoder. | |
*/ | |
_$componentEmitter_14(Decoder.prototype); | |
/** | |
* Decodes an ecoded packet string into packet JSON. | |
* | |
* @param {String} obj - encoded packet | |
* @return {Object} packet | |
* @api public | |
*/ | |
Decoder.prototype.add = function(obj) { | |
var packet; | |
if (typeof obj === 'string') { | |
packet = decodeString(obj); | |
if (_$socketIoParser_65.BINARY_EVENT === packet.type || _$socketIoParser_65.BINARY_ACK === packet.type) { // binary packet's json | |
this.reconstructor = new BinaryReconstructor(packet); | |
// no attachments, labeled binary but no binary data to follow | |
if (this.reconstructor.reconPack.attachments === 0) { | |
this.emit('decoded', packet); | |
} | |
} else { // non-binary full packet | |
this.emit('decoded', packet); | |
} | |
} | |
else if (_$isBuf_66(obj) || obj.base64) { // raw binary data | |
if (!this.reconstructor) { | |
throw new Error('got binary data when not reconstructing a packet'); | |
} else { | |
packet = this.reconstructor.takeBinaryData(obj); | |
if (packet) { // received final buffer | |
this.reconstructor = null; | |
this.emit('decoded', packet); | |
} | |
} | |
} | |
else { | |
throw new Error('Unknown type: ' + obj); | |
} | |
}; | |
/** | |
* Decode a packet String (JSON data) | |
* | |
* @param {String} str | |
* @return {Object} packet | |
* @api private | |
*/ | |
function decodeString(str) { | |
var i = 0; | |
// look up type | |
var p = { | |
type: Number(str.charAt(0)) | |
}; | |
if (null == _$socketIoParser_65.types[p.type]) return error(); | |
// look up attachments if type binary | |
if (_$socketIoParser_65.BINARY_EVENT === p.type || _$socketIoParser_65.BINARY_ACK === p.type) { | |
var buf = ''; | |
while (str.charAt(++i) !== '-') { | |
buf += str.charAt(i); | |
if (i == str.length) break; | |
} | |
if (buf != Number(buf) || str.charAt(i) !== '-') { | |
throw new Error('Illegal attachments'); | |
} | |
p.attachments = Number(buf); | |
} | |
// look up namespace (if any) | |
if ('/' === str.charAt(i + 1)) { | |
p.nsp = ''; | |
while (++i) { | |
var c = str.charAt(i); | |
if (',' === c) break; | |
p.nsp += c; | |
if (i === str.length) break; | |
} | |
} else { | |
p.nsp = '/'; | |
} | |
// look up id | |
var next = str.charAt(i + 1); | |
if ('' !== next && Number(next) == next) { | |
p.id = ''; | |
while (++i) { | |
var c = str.charAt(i); | |
if (null == c || Number(c) != c) { | |
--i; | |
break; | |
} | |
p.id += str.charAt(i); | |
if (i === str.length) break; | |
} | |
p.id = Number(p.id); | |
} | |
// look up json data | |
if (str.charAt(++i)) { | |
p = tryParse(p, str.substr(i)); | |
} | |
__debug_65('decoded %s as %j', str, p); | |
return p; | |
} | |
function tryParse(p, str) { | |
try { | |
p.data = JSON.parse(str); | |
} catch(e){ | |
return error(); | |
} | |
return p; | |
} | |
/** | |
* Deallocates a parser's resources | |
* | |
* @api public | |
*/ | |
Decoder.prototype.destroy = function() { | |
if (this.reconstructor) { | |
this.reconstructor.finishedReconstruction(); | |
} | |
}; | |
/** | |
* A manager of a binary event's 'buffer sequence'. Should | |
* be constructed whenever a packet of type BINARY_EVENT is | |
* decoded. | |
* | |
* @param {Object} packet | |
* @return {BinaryReconstructor} initialized reconstructor | |
* @api private | |
*/ | |
function BinaryReconstructor(packet) { | |
this.reconPack = packet; | |
this.buffers = []; | |
} | |
/** | |
* Method to be called when binary data received from connection | |
* after a BINARY_EVENT packet. | |
* | |
* @param {Buffer | ArrayBuffer} binData - the raw binary data received | |
* @return {null | Object} returns null if more binary data is expected or | |
* a reconstructed packet object if all buffers have been received. | |
* @api private | |
*/ | |
BinaryReconstructor.prototype.takeBinaryData = function(binData) { | |
this.buffers.push(binData); | |
if (this.buffers.length === this.reconPack.attachments) { // done with buffer list | |
var packet = _$binary_64.reconstructPacket(this.reconPack, this.buffers); | |
this.finishedReconstruction(); | |
return packet; | |
} | |
return null; | |
}; | |
/** | |
* Cleans up binary packet reconstruction variables. | |
* | |
* @api private | |
*/ | |
BinaryReconstructor.prototype.finishedReconstruction = function() { | |
this.reconPack = null; | |
this.buffers = []; | |
}; | |
function error() { | |
return { | |
type: _$socketIoParser_65.ERROR, | |
data: 'parser error' | |
}; | |
} | |
var _$toArray_67 = __toArray_67 | |
function __toArray_67(list, index) { | |
var array = [] | |
index = index || 0 | |
for (var i = index || 0; i < list.length; i++) { | |
array[i - index] = list[i] | |
} | |
return array | |
} | |
var _$socket_62 = {}; | |
/** | |
* Module dependencies. | |
*/ | |
/* removed: var _$socketIoParser_65 = require('socket.io-parser'); */; | |
/* removed: var _$componentEmitter_14 = require('component-emitter'); */; | |
/* removed: var _$toArray_67 = require('to-array'); */; | |
/* removed: var _$on_61 = require('./on'); */; | |
/* removed: var _$componentBind_13 = require('component-bind'); */; | |
var __debug_62 = _$browser_17('socket.io-client:socket'); | |
/** | |
* Module exports. | |
*/ | |
_$socket_62 = _$socket_62 = Socket; | |
/** | |
* Internal events (blacklisted). | |
* These events can't be emitted by the user. | |
* | |
* @api private | |
*/ | |
var events = { | |
connect: 1, | |
connect_error: 1, | |
connect_timeout: 1, | |
connecting: 1, | |
disconnect: 1, | |
error: 1, | |
reconnect: 1, | |
reconnect_attempt: 1, | |
reconnect_failed: 1, | |
reconnect_error: 1, | |
reconnecting: 1, | |
ping: 1, | |
pong: 1 | |
}; | |
/** | |
* Shortcut to `Emitter#emit`. | |
*/ | |
var emit = _$componentEmitter_14.prototype.emit; | |
/** | |
* `Socket` constructor. | |
* | |
* @api public | |
*/ | |
function Socket (io, nsp, opts) { | |
this.io = io; | |
this.nsp = nsp; | |
this.json = this; // compat | |
this.ids = 0; | |
this.acks = {}; | |
this.receiveBuffer = []; | |
this.sendBuffer = []; | |
this.connected = false; | |
this.disconnected = true; | |
if (opts && opts.query) { | |
this.query = opts.query; | |
} | |
if (this.io.autoConnect) this.open(); | |
} | |
/** | |
* Mix in `Emitter`. | |
*/ | |
_$componentEmitter_14(Socket.prototype); | |
/** | |
* Subscribe to open, close and packet events | |
* | |
* @api private | |
*/ | |
Socket.prototype.subEvents = function () { | |
if (this.subs) return; | |
var io = this.io; | |
this.subs = [ | |
_$on_61(io, 'open', _$componentBind_13(this, 'onopen')), | |
_$on_61(io, 'packet', _$componentBind_13(this, 'onpacket')), | |
_$on_61(io, 'close', _$componentBind_13(this, 'onclose')) | |
]; | |
}; | |
/** | |
* "Opens" the socket. | |
* | |
* @api public | |
*/ | |
Socket.prototype.open = | |
Socket.prototype.connect = function () { | |
if (this.connected) return this; | |
this.subEvents(); | |
this.io.open(); // ensure open | |
if ('open' === this.io.readyState) this.onopen(); | |
this.emit('connecting'); | |
return this; | |
}; | |
/** | |
* Sends a `message` event. | |
* | |
* @return {Socket} self | |
* @api public | |
*/ | |
Socket.prototype.send = function () { | |
var args = _$toArray_67(arguments); | |
args.unshift('message'); | |
this.emit.apply(this, args); | |
return this; | |
}; | |
/** | |
* Override `emit`. | |
* If the event is in `events`, it's emitted normally. | |
* | |
* @param {String} event name | |
* @return {Socket} self | |
* @api public | |
*/ | |
Socket.prototype.emit = function (ev) { | |
if (events.hasOwnProperty(ev)) { | |
emit.apply(this, arguments); | |
return this; | |
} | |
var args = _$toArray_67(arguments); | |
var packet = { type: _$socketIoParser_65.EVENT, data: args }; | |
packet.options = {}; | |
packet.options.compress = !this.flags || false !== this.flags.compress; | |
// event ack callback | |
if ('function' === typeof args[args.length - 1]) { | |
__debug_62('emitting packet with ack id %d', this.ids); | |
this.acks[this.ids] = args.pop(); | |
packet.id = this.ids++; | |
} | |
if (this.connected) { | |
this.packet(packet); | |
} else { | |
this.sendBuffer.push(packet); | |
} | |
delete this.flags; | |
return this; | |
}; | |
/** | |
* Sends a packet. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.packet = function (packet) { | |
packet.nsp = this.nsp; | |
this.io.packet(packet); | |
}; | |
/** | |
* Called upon engine `open`. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onopen = function () { | |
__debug_62('transport is open - connecting'); | |
// write connect packet if necessary | |
if ('/' !== this.nsp) { | |
if (this.query) { | |
this.packet({type: _$socketIoParser_65.CONNECT, query: this.query}); | |
} else { | |
this.packet({type: _$socketIoParser_65.CONNECT}); | |
} | |
} | |
}; | |
/** | |
* Called upon engine `close`. | |
* | |
* @param {String} reason | |
* @api private | |
*/ | |
Socket.prototype.onclose = function (reason) { | |
__debug_62('close (%s)', reason); | |
this.connected = false; | |
this.disconnected = true; | |
delete this.id; | |
this.emit('disconnect', reason); | |
}; | |
/** | |
* Called with socket packet. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.onpacket = function (packet) { | |
if (packet.nsp !== this.nsp) return; | |
switch (packet.type) { | |
case _$socketIoParser_65.CONNECT: | |
this.onconnect(); | |
break; | |
case _$socketIoParser_65.EVENT: | |
this.onevent(packet); | |
break; | |
case _$socketIoParser_65.BINARY_EVENT: | |
this.onevent(packet); | |
break; | |
case _$socketIoParser_65.ACK: | |
this.onack(packet); | |
break; | |
case _$socketIoParser_65.BINARY_ACK: | |
this.onack(packet); | |
break; | |
case _$socketIoParser_65.DISCONNECT: | |
this.ondisconnect(); | |
break; | |
case _$socketIoParser_65.ERROR: | |
this.emit('error', packet.data); | |
break; | |
} | |
}; | |
/** | |
* Called upon a server event. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.onevent = function (packet) { | |
var args = packet.data || []; | |
__debug_62('emitting event %j', args); | |
if (null != packet.id) { | |
__debug_62('attaching ack callback to event'); | |
args.push(this.ack(packet.id)); | |
} | |
if (this.connected) { | |
emit.apply(this, args); | |
} else { | |
this.receiveBuffer.push(args); | |
} | |
}; | |
/** | |
* Produces an ack callback to emit with an event. | |
* | |
* @api private | |
*/ | |
Socket.prototype.ack = function (id) { | |
var self = this; | |
var sent = false; | |
return function () { | |
// prevent double callbacks | |
if (sent) return; | |
sent = true; | |
var args = _$toArray_67(arguments); | |
__debug_62('sending ack %j', args); | |
self.packet({ | |
type: _$socketIoParser_65.ACK, | |
id: id, | |
data: args | |
}); | |
}; | |
}; | |
/** | |
* Called upon a server acknowlegement. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.onack = function (packet) { | |
var ack = this.acks[packet.id]; | |
if ('function' === typeof ack) { | |
__debug_62('calling ack %s with %j', packet.id, packet.data); | |
ack.apply(this, packet.data); | |
delete this.acks[packet.id]; | |
} else { | |
__debug_62('bad ack %s', packet.id); | |
} | |
}; | |
/** | |
* Called upon server connect. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onconnect = function () { | |
this.connected = true; | |
this.disconnected = false; | |
this.emit('connect'); | |
this.emitBuffered(); | |
}; | |
/** | |
* Emit buffered events (received and emitted). | |
* | |
* @api private | |
*/ | |
Socket.prototype.emitBuffered = function () { | |
var i; | |
for (i = 0; i < this.receiveBuffer.length; i++) { | |
emit.apply(this, this.receiveBuffer[i]); | |
} | |
this.receiveBuffer = []; | |
for (i = 0; i < this.sendBuffer.length; i++) { | |
this.packet(this.sendBuffer[i]); | |
} | |
this.sendBuffer = []; | |
}; | |
/** | |
* Called upon server disconnect. | |
* | |
* @api private | |
*/ | |
Socket.prototype.ondisconnect = function () { | |
__debug_62('server disconnect (%s)', this.nsp); | |
this.destroy(); | |
this.onclose('io server disconnect'); | |
}; | |
/** | |
* Called upon forced client/server side disconnections, | |
* this method ensures the manager stops tracking us and | |
* that reconnections don't get triggered for this. | |
* | |
* @api private. | |
*/ | |
Socket.prototype.destroy = function () { | |
if (this.subs) { | |
// clean subscriptions to avoid reconnections | |
for (var i = 0; i < this.subs.length; i++) { | |
this.subs[i].destroy(); | |
} | |
this.subs = null; | |
} | |
this.io.destroy(this); | |
}; | |
/** | |
* Disconnects the socket manually. | |
* | |
* @return {Socket} self | |
* @api public | |
*/ | |
Socket.prototype.close = | |
Socket.prototype.disconnect = function () { | |
if (this.connected) { | |
__debug_62('performing disconnect (%s)', this.nsp); | |
this.packet({ type: _$socketIoParser_65.DISCONNECT }); | |
} | |
// remove socket from pool | |
this.destroy(); | |
if (this.connected) { | |
// fire events | |
this.onclose('io client disconnect'); | |
} | |
return this; | |
}; | |
/** | |
* Sets the compress flag. | |
* | |
* @param {Boolean} if `true`, compresses the sending data | |
* @return {Socket} self | |
* @api public | |
*/ | |
Socket.prototype.compress = function (compress) { | |
this.flags = this.flags || {}; | |
this.flags.compress = compress; | |
return this; | |
}; | |
/** | |
* Module dependencies. | |
*/ | |
/* removed: var _$engineIoClient_20 = require('engine.io-client'); */; | |
/* removed: var _$socket_62 = require('./socket'); */; | |
/* removed: var _$componentEmitter_14 = require('component-emitter'); */; | |
/* removed: var _$socketIoParser_65 = require('socket.io-parser'); */; | |
/* removed: var _$on_61 = require('./on'); */; | |
/* removed: var _$componentBind_13 = require('component-bind'); */; | |
var __debug_60 = _$browser_17('socket.io-client:manager'); | |
/* removed: var _$indexof_42 = require('indexof'); */; | |
/* removed: var _$Backoff_9 = require('backo2'); */; | |
/** | |
* IE6+ hasOwnProperty | |
*/ | |
var __has_60 = Object.prototype.hasOwnProperty; | |
/** | |
* Module exports | |
*/ | |
var _$Manager_60 = Manager; | |
/** | |
* `Manager` constructor. | |
* | |
* @param {String} engine instance or engine uri/opts | |
* @param {Object} options | |
* @api public | |
*/ | |
function Manager (uri, opts) { | |
if (!(this instanceof Manager)) return new Manager(uri, opts); | |
if (uri && ('object' === typeof uri)) { | |
opts = uri; | |
uri = undefined; | |
} | |
opts = opts || {}; | |
opts.path = opts.path || '/socket.io'; | |
this.nsps = {}; | |
this.subs = []; | |
this.opts = opts; | |
this.reconnection(opts.reconnection !== false); | |
this.reconnectionAttempts(opts.reconnectionAttempts || Infinity); | |
this.reconnectionDelay(opts.reconnectionDelay || 1000); | |
this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000); | |
this.randomizationFactor(opts.randomizationFactor || 0.5); | |
this.backoff = new _$Backoff_9({ | |
min: this.reconnectionDelay(), | |
max: this.reconnectionDelayMax(), | |
jitter: this.randomizationFactor() | |
}); | |
this.timeout(null == opts.timeout ? 20000 : opts.timeout); | |
this.readyState = 'closed'; | |
this.uri = uri; | |
this.connecting = []; | |
this.lastPing = null; | |
this.encoding = false; | |
this.packetBuffer = []; | |
var _parser = opts.parser || _$socketIoParser_65; | |
this.encoder = new _parser.Encoder(); | |
this.decoder = new _parser.Decoder(); | |
this.autoConnect = opts.autoConnect !== false; | |
if (this.autoConnect) this.open(); | |
} | |
/** | |
* Propagate given event to sockets and emit on `this` | |
* | |
* @api private | |
*/ | |
Manager.prototype.emitAll = function () { | |
this.emit.apply(this, arguments); | |
for (var nsp in this.nsps) { | |
if (__has_60.call(this.nsps, nsp)) { | |
this.nsps[nsp].emit.apply(this.nsps[nsp], arguments); | |
} | |
} | |
}; | |
/** | |
* Update `socket.id` of all sockets | |
* | |
* @api private | |
*/ | |
Manager.prototype.updateSocketIds = function () { | |
for (var nsp in this.nsps) { | |
if (__has_60.call(this.nsps, nsp)) { | |
this.nsps[nsp].id = this.generateId(nsp); | |
} | |
} | |
}; | |
/** | |
* generate `socket.id` for the given `nsp` | |
* | |
* @param {String} nsp | |
* @return {String} | |
* @api private | |
*/ | |
Manager.prototype.generateId = function (nsp) { | |
return (nsp === '/' ? '' : (nsp + '#')) + this.engine.id; | |
}; | |
/** | |
* Mix in `Emitter`. | |
*/ | |
_$componentEmitter_14(Manager.prototype); | |
/** | |
* Sets the `reconnection` config. | |
* | |
* @param {Boolean} true/false if it should automatically reconnect | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnection = function (v) { | |
if (!arguments.length) return this._reconnection; | |
this._reconnection = !!v; | |
return this; | |
}; | |
/** | |
* Sets the reconnection attempts config. | |
* | |
* @param {Number} max reconnection attempts before giving up | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnectionAttempts = function (v) { | |
if (!arguments.length) return this._reconnectionAttempts; | |
this._reconnectionAttempts = v; | |
return this; | |
}; | |
/** | |
* Sets the delay between reconnections. | |
* | |
* @param {Number} delay | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnectionDelay = function (v) { | |
if (!arguments.length) return this._reconnectionDelay; | |
this._reconnectionDelay = v; | |
this.backoff && this.backoff.setMin(v); | |
return this; | |
}; | |
Manager.prototype.randomizationFactor = function (v) { | |
if (!arguments.length) return this._randomizationFactor; | |
this._randomizationFactor = v; | |
this.backoff && this.backoff.setJitter(v); | |
return this; | |
}; | |
/** | |
* Sets the maximum delay between reconnections. | |
* | |
* @param {Number} delay | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnectionDelayMax = function (v) { | |
if (!arguments.length) return this._reconnectionDelayMax; | |
this._reconnectionDelayMax = v; | |
this.backoff && this.backoff.setMax(v); | |
return this; | |
}; | |
/** | |
* Sets the connection timeout. `false` to disable | |
* | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.timeout = function (v) { | |
if (!arguments.length) return this._timeout; | |
this._timeout = v; | |
return this; | |
}; | |
/** | |
* Starts trying to reconnect if reconnection is enabled and we have not | |
* started reconnecting yet | |
* | |
* @api private | |
*/ | |
Manager.prototype.maybeReconnectOnOpen = function () { | |
// Only try to reconnect if it's the first time we're connecting | |
if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) { | |
// keeps reconnection from firing twice for the same reconnection loop | |
this.reconnect(); | |
} | |
}; | |
/** | |
* Sets the current transport `socket`. | |
* | |
* @param {Function} optional, callback | |
* @return {Manager} self | |
* @api public | |
*/ | |
Manager.prototype.open = | |
Manager.prototype.connect = function (fn, opts) { | |
__debug_60('readyState %s', this.readyState); | |
if (~this.readyState.indexOf('open')) return this; | |
__debug_60('opening %s', this.uri); | |
this.engine = _$engineIoClient_20(this.uri, this.opts); | |
var socket = this.engine; | |
var self = this; | |
this.readyState = 'opening'; | |
this.skipReconnect = false; | |
// emit `open` | |
var openSub = _$on_61(socket, 'open', function () { | |
self.onopen(); | |
fn && fn(); | |
}); | |
// emit `connect_error` | |
var errorSub = _$on_61(socket, 'error', function (data) { | |
__debug_60('connect_error'); | |
self.cleanup(); | |
self.readyState = 'closed'; | |
self.emitAll('connect_error', data); | |
if (fn) { | |
var err = new Error('Connection error'); | |
err.data = data; | |
fn(err); | |
} else { | |
// Only do this if there is no fn to handle the error | |
self.maybeReconnectOnOpen(); | |
} | |
}); | |
// emit `connect_timeout` | |
if (false !== this._timeout) { | |
var timeout = this._timeout; | |
__debug_60('connect attempt will timeout after %d', timeout); | |
// set timer | |
var timer = setTimeout(function () { | |
__debug_60('connect attempt timed out after %d', timeout); | |
openSub.destroy(); | |
socket.close(); | |
socket.emit('error', 'timeout'); | |
self.emitAll('connect_timeout', timeout); | |
}, timeout); | |
this.subs.push({ | |
destroy: function () { | |
clearTimeout(timer); | |
} | |
}); | |
} | |
this.subs.push(openSub); | |
this.subs.push(errorSub); | |
return this; | |
}; | |
/** | |
* Called upon transport open. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onopen = function () { | |
__debug_60('open'); | |
// clear old subs | |
this.cleanup(); | |
// mark as open | |
this.readyState = 'open'; | |
this.emit('open'); | |
// add new subs | |
var socket = this.engine; | |
this.subs.push(_$on_61(socket, 'data', _$componentBind_13(this, 'ondata'))); | |
this.subs.push(_$on_61(socket, 'ping', _$componentBind_13(this, 'onping'))); | |
this.subs.push(_$on_61(socket, 'pong', _$componentBind_13(this, 'onpong'))); | |
this.subs.push(_$on_61(socket, 'error', _$componentBind_13(this, 'onerror'))); | |
this.subs.push(_$on_61(socket, 'close', _$componentBind_13(this, 'onclose'))); | |
this.subs.push(_$on_61(this.decoder, 'decoded', _$componentBind_13(this, 'ondecoded'))); | |
}; | |
/** | |
* Called upon a ping. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onping = function () { | |
this.lastPing = new Date(); | |
this.emitAll('ping'); | |
}; | |
/** | |
* Called upon a packet. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onpong = function () { | |
this.emitAll('pong', new Date() - this.lastPing); | |
}; | |
/** | |
* Called with data. | |
* | |
* @api private | |
*/ | |
Manager.prototype.ondata = function (data) { | |
this.decoder.add(data); | |
}; | |
/** | |
* Called when parser fully decodes a packet. | |
* | |
* @api private | |
*/ | |
Manager.prototype.ondecoded = function (packet) { | |
this.emit('packet', packet); | |
}; | |
/** | |
* Called upon socket error. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onerror = function (err) { | |
__debug_60('error', err); | |
this.emitAll('error', err); | |
}; | |
/** | |
* Creates a new socket for the given `nsp`. | |
* | |
* @return {Socket} | |
* @api public | |
*/ | |
Manager.prototype.socket = function (nsp, opts) { | |
var socket = this.nsps[nsp]; | |
if (!socket) { | |
socket = new _$socket_62(this, nsp, opts); | |
this.nsps[nsp] = socket; | |
var self = this; | |
socket.on('connecting', onConnecting); | |
socket.on('connect', function () { | |
socket.id = self.generateId(nsp); | |
}); | |
if (this.autoConnect) { | |
// manually call here since connecting event is fired before listening | |
onConnecting(); | |
} | |
} | |
function onConnecting () { | |
if (!~_$indexof_42(self.connecting, socket)) { | |
self.connecting.push(socket); | |
} | |
} | |
return socket; | |
}; | |
/** | |
* Called upon a socket close. | |
* | |
* @param {Socket} socket | |
*/ | |
Manager.prototype.destroy = function (socket) { | |
var index = _$indexof_42(this.connecting, socket); | |
if (~index) this.connecting.splice(index, 1); | |
if (this.connecting.length) return; | |
this.close(); | |
}; | |
/** | |
* Writes a packet. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Manager.prototype.packet = function (packet) { | |
__debug_60('writing packet %j', packet); | |
var self = this; | |
if (packet.query && packet.type === 0) packet.nsp += '?' + packet.query; | |
if (!self.encoding) { | |
// encode, then write to engine with result | |
self.encoding = true; | |
this.encoder.encode(packet, function (encodedPackets) { | |
for (var i = 0; i < encodedPackets.length; i++) { | |
self.engine.write(encodedPackets[i], packet.options); | |
} | |
self.encoding = false; | |
self.processPacketQueue(); | |
}); | |
} else { // add packet to the queue | |
self.packetBuffer.push(packet); | |
} | |
}; | |
/** | |
* If packet buffer is non-empty, begins encoding the | |
* next packet in line. | |
* | |
* @api private | |
*/ | |
Manager.prototype.processPacketQueue = function () { | |
if (this.packetBuffer.length > 0 && !this.encoding) { | |
var pack = this.packetBuffer.shift(); | |
this.packet(pack); | |
} | |
}; | |
/** | |
* Clean up transport subscriptions and packet buffer. | |
* | |
* @api private | |
*/ | |
Manager.prototype.cleanup = function () { | |
__debug_60('cleanup'); | |
var subsLength = this.subs.length; | |
for (var i = 0; i < subsLength; i++) { | |
var sub = this.subs.shift(); | |
sub.destroy(); | |
} | |
this.packetBuffer = []; | |
this.encoding = false; | |
this.lastPing = null; | |
this.decoder.destroy(); | |
}; | |
/** | |
* Close the current socket. | |
* | |
* @api private | |
*/ | |
Manager.prototype.close = | |
Manager.prototype.disconnect = function () { | |
__debug_60('disconnect'); | |
this.skipReconnect = true; | |
this.reconnecting = false; | |
if ('opening' === this.readyState) { | |
// `onclose` will not fire because | |
// an open event never happened | |
this.cleanup(); | |
} | |
this.backoff.reset(); | |
this.readyState = 'closed'; | |
if (this.engine) this.engine.close(); | |
}; | |
/** | |
* Called upon engine close. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onclose = function (reason) { | |
__debug_60('onclose'); | |
this.cleanup(); | |
this.backoff.reset(); | |
this.readyState = 'closed'; | |
this.emit('close', reason); | |
if (this._reconnection && !this.skipReconnect) { | |
this.reconnect(); | |
} | |
}; | |
/** | |
* Attempt a reconnection. | |
* | |
* @api private | |
*/ | |
Manager.prototype.reconnect = function () { | |
if (this.reconnecting || this.skipReconnect) return this; | |
var self = this; | |
if (this.backoff.attempts >= this._reconnectionAttempts) { | |
__debug_60('reconnect failed'); | |
this.backoff.reset(); | |
this.emitAll('reconnect_failed'); | |
this.reconnecting = false; | |
} else { | |
var delay = this.backoff.duration(); | |
__debug_60('will wait %dms before reconnect attempt', delay); | |
this.reconnecting = true; | |
var timer = setTimeout(function () { | |
if (self.skipReconnect) return; | |
__debug_60('attempting reconnect'); | |
self.emitAll('reconnect_attempt', self.backoff.attempts); | |
self.emitAll('reconnecting', self.backoff.attempts); | |
// check again for the case socket closed in above events | |
if (self.skipReconnect) return; | |
self.open(function (err) { | |
if (err) { | |
__debug_60('reconnect attempt error'); | |
self.reconnecting = false; | |
self.reconnect(); | |
self.emitAll('reconnect_error', err.data); | |
} else { | |
__debug_60('reconnect success'); | |
self.onreconnect(); | |
} | |
}); | |
}, delay); | |
this.subs.push({ | |
destroy: function () { | |
clearTimeout(timer); | |
} | |
}); | |
} | |
}; | |
/** | |
* Called upon successful reconnect. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onreconnect = function () { | |
var attempt = this.backoff.attempts; | |
this.reconnecting = false; | |
this.backoff.reset(); | |
this.updateSocketIds(); | |
this.emitAll('reconnect', attempt); | |
}; | |
var _$url_63 = {}; | |
(function (global){ | |
/** | |
* Module dependencies. | |
*/ | |
var parseuri = _$parseuri_53; | |
var debug = _$browser_17('socket.io-client:url'); | |
/** | |
* Module exports. | |
*/ | |
_$url_63 = url; | |
/** | |
* URL parser. | |
* | |
* @param {String} url | |
* @param {Object} An object meant to mimic window.location. | |
* Defaults to window.location. | |
* @api public | |
*/ | |
function url (uri, loc) { | |
var obj = uri; | |
// default to window.location | |
loc = loc || global.location; | |
if (null == uri) uri = loc.protocol + '//' + loc.host; | |
// relative path support | |
if ('string' === typeof uri) { | |
if ('/' === uri.charAt(0)) { | |
if ('/' === uri.charAt(1)) { | |
uri = loc.protocol + uri; | |
} else { | |
uri = loc.host + uri; | |
} | |
} | |
if (!/^(https?|wss?):\/\//.test(uri)) { | |
debug('protocol-less url %s', uri); | |
if ('undefined' !== typeof loc) { | |
uri = loc.protocol + '//' + uri; | |
} else { | |
uri = 'https://' + uri; | |
} | |
} | |
// parse | |
debug('parse %s', uri); | |
obj = parseuri(uri); | |
} | |
// make sure we treat `localhost:80` and `localhost` equally | |
if (!obj.port) { | |
if (/^(http|ws)$/.test(obj.protocol)) { | |
obj.port = '80'; | |
} else if (/^(http|ws)s$/.test(obj.protocol)) { | |
obj.port = '443'; | |
} | |
} | |
obj.path = obj.path || '/'; | |
var ipv6 = obj.host.indexOf(':') !== -1; | |
var host = ipv6 ? '[' + obj.host + ']' : obj.host; | |
// define unique id | |
obj.id = obj.protocol + '://' + host + ':' + obj.port; | |
// define href | |
obj.href = obj.protocol + '://' + host + (loc && loc.port === obj.port ? '' : (':' + obj.port)); | |
return obj; | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$lib_59 = {}; | |
/** | |
* Module dependencies. | |
*/ | |
/* removed: var _$url_63 = require('./url'); */; | |
/* removed: var _$socketIoParser_65 = require('socket.io-parser'); */; | |
/* removed: var _$Manager_60 = require('./manager'); */; | |
var __debug_59 = _$browser_17('socket.io-client'); | |
/** | |
* Module exports. | |
*/ | |
_$lib_59 = _$lib_59 = lookup; | |
/** | |
* Managers cache. | |
*/ | |
var cache = _$lib_59.managers = {}; | |
/** | |
* Looks up an existing `Manager` for multiplexing. | |
* If the user summons: | |
* | |
* `io('http://localhost/a');` | |
* `io('http://localhost/b');` | |
* | |
* We reuse the existing instance based on same scheme/port/host, | |
* and we initialize sockets for each namespace. | |
* | |
* @api public | |
*/ | |
function lookup (uri, opts) { | |
if (typeof uri === 'object') { | |
opts = uri; | |
uri = undefined; | |
} | |
opts = opts || {}; | |
var parsed = _$url_63(uri); | |
var source = parsed.source; | |
var id = parsed.id; | |
var path = parsed.path; | |
var sameNamespace = cache[id] && path in cache[id].nsps; | |
var newConnection = opts.forceNew || opts['force new connection'] || | |
false === opts.multiplex || sameNamespace; | |
var io; | |
if (newConnection) { | |
__debug_59('ignoring socket cache for %s', source); | |
io = _$Manager_60(source, opts); | |
} else { | |
if (!cache[id]) { | |
__debug_59('new io instance for %s', source); | |
cache[id] = _$Manager_60(source, opts); | |
} | |
io = cache[id]; | |
} | |
if (parsed.query && !opts.query) { | |
opts.query = parsed.query; | |
} else if (opts && 'object' === typeof opts.query) { | |
opts.query = encodeQueryString(opts.query); | |
} | |
return io.socket(parsed.path, opts); | |
} | |
/** | |
* Helper method to parse query objects to string. | |
* @param {object} query | |
* @returns {string} | |
*/ | |
function encodeQueryString (obj) { | |
var str = []; | |
for (var p in obj) { | |
if (obj.hasOwnProperty(p)) { | |
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p])); | |
} | |
} | |
return str.join('&'); | |
} | |
/** | |
* Protocol version. | |
* | |
* @api public | |
*/ | |
_$lib_59.protocol = _$socketIoParser_65.protocol; | |
/** | |
* `connect`. | |
* | |
* @param {String} uri | |
* @api public | |
*/ | |
_$lib_59.connect = lookup; | |
/** | |
* Expose constructors for standalone build. | |
* | |
* @api public | |
*/ | |
_$lib_59.Manager = _$Manager_60; | |
_$lib_59.Socket = _$socket_62; | |
var _$base64_68 = {}; | |
// Generated by Babel | |
"use strict"; | |
Object.defineProperty(_$base64_68, "__esModule", { | |
value: true | |
}); | |
_$base64_68.encode = __encode_68; | |
/* global: window */ | |
var _window = window; | |
var btoa = _window.btoa; | |
function __encode_68(data) { | |
return btoa(unescape(encodeURIComponent(data))); | |
} | |
var isSupported = _$base64_68.isSupported = "btoa" in window; | |
var _$request_69 = {}; | |
// Generated by Babel | |
"use strict"; | |
Object.defineProperty(_$request_69, "__esModule", { | |
value: true | |
}); | |
_$request_69.newRequest = newRequest; | |
_$request_69.resolveUrl = resolveUrl; | |
/* removed: var _$resolveUrl_57 = require("resolve-url"); */; | |
var _resolveUrl2 = _interopRequireDefault(_$resolveUrl_57); | |
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | |
function newRequest() { | |
return new window.XMLHttpRequest(); | |
} /* global window */ | |
function resolveUrl(origin, link) { | |
return (0, _resolveUrl2.default)(origin, link); | |
} | |
var _$source_70 = {}; | |
// Generated by Babel | |
"use strict"; | |
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | |
Object.defineProperty(_$source_70, "__esModule", { | |
value: true | |
}); | |
_$source_70.getSource = getSource; | |
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | |
var FileSource = function () { | |
function FileSource(file) { | |
_classCallCheck(this, FileSource); | |
this._file = file; | |
this.size = file.size; | |
} | |
_createClass(FileSource, [{ | |
key: "slice", | |
value: function slice(start, end) { | |
return this._file.slice(start, end); | |
} | |
}, { | |
key: "close", | |
value: function close() {} | |
}]); | |
return FileSource; | |
}(); | |
function getSource(input) { | |
// Since we emulate the Blob type in our tests (not all target browsers | |
// support it), we cannot use `instanceof` for testing whether the input value | |
// can be handled. Instead, we simply check is the slice() function and the | |
// size property are available. | |
if (typeof input.slice === "function" && typeof input.size !== "undefined") { | |
return new FileSource(input); | |
} | |
throw new Error("source object may only be an instance of File or Blob in this environment"); | |
} | |
var _$storage_71 = {}; | |
// Generated by Babel | |
"use strict"; | |
Object.defineProperty(_$storage_71, "__esModule", { | |
value: true | |
}); | |
_$storage_71.setItem = setItem; | |
_$storage_71.getItem = getItem; | |
_$storage_71.removeItem = removeItem; | |
/* global window, localStorage */ | |
var hasStorage = false; | |
try { | |
hasStorage = "localStorage" in window; | |
// Attempt to store and read entries from the local storage to detect Private | |
// Mode on Safari on iOS (see #49) | |
var key = "tusSupport"; | |
localStorage.setItem(key, localStorage.getItem(key)); | |
} catch (e) { | |
// If we try to access localStorage inside a sandboxed iframe, a SecurityError | |
// is thrown. When in private mode on iOS Safari, a QuotaExceededError is | |
// thrown (see #49) | |
if (e.code === e.SECURITY_ERR || e.code === e.QUOTA_EXCEEDED_ERR) { | |
hasStorage = false; | |
} else { | |
throw e; | |
} | |
} | |
var canStoreURLs = _$storage_71.canStoreURLs = hasStorage; | |
function setItem(key, value) { | |
if (!hasStorage) return; | |
return localStorage.setItem(key, value); | |
} | |
function getItem(key) { | |
if (!hasStorage) return; | |
return localStorage.getItem(key); | |
} | |
function removeItem(key) { | |
if (!hasStorage) return; | |
return localStorage.removeItem(key); | |
} | |
var _$error_72 = {}; | |
// Generated by Babel | |
"use strict"; | |
Object.defineProperty(_$error_72, "__esModule", { | |
value: true | |
}); | |
function ___classCallCheck_72(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | |
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | |
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } | |
var DetailedError = function (_Error) { | |
_inherits(DetailedError, _Error); | |
function DetailedError(error) { | |
var causingErr = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; | |
var xhr = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2]; | |
___classCallCheck_72(this, DetailedError); | |
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(DetailedError).call(this, error.message)); | |
_this.originalRequest = xhr; | |
_this.causingError = causingErr; | |
var message = error.message; | |
if (causingErr != null) { | |
message += ", caused by " + causingErr.toString(); | |
} | |
if (xhr != null) { | |
message += ", originated from request (response code: " + xhr.status + ", response text: " + xhr.responseText + ")"; | |
} | |
_this.message = message; | |
return _this; | |
} | |
return DetailedError; | |
}(Error); | |
_$error_72.default = DetailedError; | |
var _$fingerprint_73 = {}; | |
// Generated by Babel | |
"use strict"; | |
Object.defineProperty(_$fingerprint_73, "__esModule", { | |
value: true | |
}); | |
_$fingerprint_73.default = fingerprint; | |
/** | |
* Generate a fingerprint for a file which will be used the store the endpoint | |
* | |
* @param {File} file | |
* @return {String} | |
*/ | |
function fingerprint(file) { | |
return ["tus", file.name, file.type, file.size, file.lastModified].join("-"); | |
} | |
var _$upload_75 = {}; | |
// Generated by Babel | |
"use strict"; | |
var ___createClass_75 = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* global window */ | |
// We import the files used inside the Node environment which are rewritten | |
// for browsers using the rules defined in the package.json | |
Object.defineProperty(_$upload_75, "__esModule", { | |
value: true | |
}); | |
/* removed: var _$fingerprint_73 = require("./fingerprint"); */; | |
var _fingerprint2 = ___interopRequireDefault_75(_$fingerprint_73); | |
/* removed: var _$error_72 = require("./error"); */; | |
var _error2 = ___interopRequireDefault_75(_$error_72); | |
/* removed: var _$extend_33 = require("extend"); */; | |
var _extend2 = ___interopRequireDefault_75(_$extend_33); | |
/* removed: var _$request_69 = require("./node/request"); */; | |
/* removed: var _$source_70 = require("./node/source"); */; | |
/* removed: var _$base64_68 = require("./node/base64"); */; | |
var Base64 = _interopRequireWildcard(_$base64_68); | |
/* removed: var _$storage_71 = require("./node/storage"); */; | |
var Storage = _interopRequireWildcard(_$storage_71); | |
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | |
function ___interopRequireDefault_75(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | |
function ___classCallCheck_75(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | |
var defaultOptions = { | |
endpoint: "", | |
fingerprint: _fingerprint2.default, | |
resume: true, | |
onProgress: null, | |
onChunkComplete: null, | |
onSuccess: null, | |
onError: null, | |
headers: {}, | |
chunkSize: Infinity, | |
withCredentials: false, | |
uploadUrl: null, | |
uploadSize: null, | |
overridePatchMethod: false, | |
retryDelays: null | |
}; | |
var Upload = function () { | |
function Upload(file, options) { | |
___classCallCheck_75(this, Upload); | |
this.options = (0, _extend2.default)(true, {}, defaultOptions, options); | |
// The underlying File/Blob object | |
this.file = file; | |
// The URL against which the file will be uploaded | |
this.url = null; | |
// The underlying XHR object for the current PATCH request | |
this._xhr = null; | |
// The fingerpinrt for the current file (set after start()) | |
this._fingerprint = null; | |
// The offset used in the current PATCH request | |
this._offset = null; | |
// True if the current PATCH request has been aborted | |
this._aborted = false; | |
// The file's size in bytes | |
this._size = null; | |
// The Source object which will wrap around the given file and provides us | |
// with a unified interface for getting its size and slice chunks from its | |
// content allowing us to easily handle Files, Blobs, Buffers and Streams. | |
this._source = null; | |
// The current count of attempts which have been made. Null indicates none. | |
this._retryAttempt = 0; | |
// The timeout's ID which is used to delay the next retry | |
this._retryTimeout = null; | |
// The offset of the remote upload before the latest attempt was started. | |
this._offsetBeforeRetry = 0; | |
} | |
___createClass_75(Upload, [{ | |
key: "start", | |
value: function start() { | |
var _this = this; | |
var file = this.file; | |
if (!file) { | |
this._emitError(new Error("tus: no file or stream to upload provided")); | |
return; | |
} | |
if (!this.options.endpoint) { | |
this._emitError(new Error("tus: no endpoint provided")); | |
return; | |
} | |
var source = this._source = (0, _$source_70.getSource)(file, this.options.chunkSize); | |
// Firstly, check if the caller has supplied a manual upload size or else | |
// we will use the calculated size by the source object. | |
if (this.options.uploadSize != null) { | |
var size = +this.options.uploadSize; | |
if (isNaN(size)) { | |
throw new Error("tus: cannot convert `uploadSize` option into a number"); | |
} | |
this._size = size; | |
} else { | |
var size = source.size; | |
// The size property will be null if we cannot calculate the file's size, | |
// for example if you handle a stream. | |
if (size == null) { | |
throw new Error("tus: cannot automatically derive upload's size from input and must be specified manually using the `uploadSize` option"); | |
} | |
this._size = size; | |
} | |
var retryDelays = this.options.retryDelays; | |
if (retryDelays != null) { | |
if (Object.prototype.toString.call(retryDelays) !== "[object Array]") { | |
throw new Error("tus: the `retryDelays` option must either be an array or null"); | |
} else { | |
(function () { | |
var errorCallback = _this.options.onError; | |
_this.options.onError = function (err) { | |
// Restore the original error callback which may have been set. | |
_this.options.onError = errorCallback; | |
// We will reset the attempt counter if | |
// - we were already able to connect to the server (offset != null) and | |
// - we were able to upload a small chunk of data to the server | |
var shouldResetDelays = _this._offset != null && _this._offset > _this._offsetBeforeRetry; | |
if (shouldResetDelays) { | |
_this._retryAttempt = 0; | |
} | |
var isOnline = true; | |
if (typeof window !== "undefined" && "navigator" in window && window.navigator.onLine === false) { | |
isOnline = false; | |
} | |
// We only attempt a retry if | |
// - we didn't exceed the maxium number of retries, yet, and | |
// - this error was caused by a request or it's response and | |
// - the browser does not indicate that we are offline | |
var shouldRetry = _this._retryAttempt < retryDelays.length && err.originalRequest != null && isOnline; | |
if (!shouldRetry) { | |
_this._emitError(err); | |
return; | |
} | |
var delay = retryDelays[_this._retryAttempt++]; | |
_this._offsetBeforeRetry = _this._offset; | |
_this.options.uploadUrl = _this.url; | |
_this._retryTimeout = setTimeout(function () { | |
_this.start(); | |
}, delay); | |
}; | |
})(); | |
} | |
} | |
// Reset the aborted flag when the upload is started or else the | |
// _startUpload will stop before sending a request if the upload has been | |
// aborted previously. | |
this._aborted = false; | |
// A URL has manually been specified, so we try to resume | |
if (this.options.uploadUrl != null) { | |
this.url = this.options.uploadUrl; | |
this._resumeUpload(); | |
return; | |
} | |
// Try to find the endpoint for the file in the storage | |
if (this.options.resume) { | |
this._fingerprint = this.options.fingerprint(file); | |
var resumedUrl = Storage.getItem(this._fingerprint); | |
if (resumedUrl != null) { | |
this.url = resumedUrl; | |
this._resumeUpload(); | |
return; | |
} | |
} | |
// An upload has not started for the file yet, so we start a new one | |
this._createUpload(); | |
} | |
}, { | |
key: "abort", | |
value: function abort() { | |
if (this._xhr !== null) { | |
this._xhr.abort(); | |
this._source.close(); | |
this._aborted = true; | |
} | |
if (this._retryTimeout != null) { | |
clearTimeout(this._retryTimeout); | |
this._retryTimeout = null; | |
} | |
} | |
}, { | |
key: "_emitXhrError", | |
value: function _emitXhrError(xhr, err, causingErr) { | |
this._emitError(new _error2.default(err, causingErr, xhr)); | |
} | |
}, { | |
key: "_emitError", | |
value: function _emitError(err) { | |
if (typeof this.options.onError === "function") { | |
this.options.onError(err); | |
} else { | |
throw err; | |
} | |
} | |
}, { | |
key: "_emitSuccess", | |
value: function _emitSuccess() { | |
if (typeof this.options.onSuccess === "function") { | |
this.options.onSuccess(); | |
} | |
} | |
/** | |
* Publishes notification when data has been sent to the server. This | |
* data may not have been accepted by the server yet. | |
* @param {number} bytesSent Number of bytes sent to the server. | |
* @param {number} bytesTotal Total number of bytes to be sent to the server. | |
*/ | |
}, { | |
key: "_emitProgress", | |
value: function _emitProgress(bytesSent, bytesTotal) { | |
if (typeof this.options.onProgress === "function") { | |
this.options.onProgress(bytesSent, bytesTotal); | |
} | |
} | |
/** | |
* Publishes notification when a chunk of data has been sent to the server | |
* and accepted by the server. | |
* @param {number} chunkSize Size of the chunk that was accepted by the | |
* server. | |
* @param {number} bytesAccepted Total number of bytes that have been | |
* accepted by the server. | |
* @param {number} bytesTotal Total number of bytes to be sent to the server. | |
*/ | |
}, { | |
key: "_emitChunkComplete", | |
value: function _emitChunkComplete(chunkSize, bytesAccepted, bytesTotal) { | |
if (typeof this.options.onChunkComplete === "function") { | |
this.options.onChunkComplete(chunkSize, bytesAccepted, bytesTotal); | |
} | |
} | |
/** | |
* Set the headers used in the request and the withCredentials property | |
* as defined in the options | |
* | |
* @param {XMLHttpRequest} xhr | |
*/ | |
}, { | |
key: "_setupXHR", | |
value: function _setupXHR(xhr) { | |
xhr.setRequestHeader("Tus-Resumable", "1.0.0"); | |
var headers = this.options.headers; | |
for (var name in headers) { | |
xhr.setRequestHeader(name, headers[name]); | |
} | |
xhr.withCredentials = this.options.withCredentials; | |
} | |
/** | |
* Create a new upload using the creation extension by sending a POST | |
* request to the endpoint. After successful creation the file will be | |
* uploaded | |
* | |
* @api private | |
*/ | |
}, { | |
key: "_createUpload", | |
value: function _createUpload() { | |
var _this2 = this; | |
var xhr = (0, _$request_69.newRequest)(); | |
xhr.open("POST", this.options.endpoint, true); | |
xhr.onload = function () { | |
if (!(xhr.status >= 200 && xhr.status < 300)) { | |
_this2._emitXhrError(xhr, new Error("tus: unexpected response while creating upload")); | |
return; | |
} | |
_this2.url = (0, _$request_69.resolveUrl)(_this2.options.endpoint, xhr.getResponseHeader("Location")); | |
if (_this2.options.resume) { | |
Storage.setItem(_this2._fingerprint, _this2.url); | |
} | |
_this2._offset = 0; | |
_this2._startUpload(); | |
}; | |
xhr.onerror = function (err) { | |
_this2._emitXhrError(xhr, new Error("tus: failed to create upload"), err); | |
}; | |
this._setupXHR(xhr); | |
xhr.setRequestHeader("Upload-Length", this._size); | |
// Add metadata if values have been added | |
var metadata = encodeMetadata(this.options.metadata); | |
if (metadata !== "") { | |
xhr.setRequestHeader("Upload-Metadata", metadata); | |
} | |
xhr.send(null); | |
} | |
/* | |
* Try to resume an existing upload. First a HEAD request will be sent | |
* to retrieve the offset. If the request fails a new upload will be | |
* created. In the case of a successful response the file will be uploaded. | |
* | |
* @api private | |
*/ | |
}, { | |
key: "_resumeUpload", | |
value: function _resumeUpload() { | |
var _this3 = this; | |
var xhr = (0, _$request_69.newRequest)(); | |
xhr.open("HEAD", this.url, true); | |
xhr.onload = function () { | |
if (!(xhr.status >= 200 && xhr.status < 300)) { | |
if (_this3.options.resume) { | |
// Remove stored fingerprint and corresponding endpoint, | |
// since the file can not be found | |
Storage.removeItem(_this3._fingerprint); | |
} | |
// If the upload is locked (indicated by the 423 Locked status code), we | |
// emit an error instead of directly starting a new upload. This way the | |
// retry logic can catch the error and will retry the upload. An upload | |
// is usually locked for a short period of time and will be available | |
// afterwards. | |
if (xhr.status === 423) { | |
_this3._emitXhrError(xhr, new Error("tus: upload is currently locked; retry later")); | |
return; | |
} | |
// Try to create a new upload | |
_this3.url = null; | |
_this3._createUpload(); | |
return; | |
} | |
var offset = parseInt(xhr.getResponseHeader("Upload-Offset"), 10); | |
if (isNaN(offset)) { | |
_this3._emitXhrError(xhr, new Error("tus: invalid or missing offset value")); | |
return; | |
} | |
var length = parseInt(xhr.getResponseHeader("Upload-Length"), 10); | |
if (isNaN(length)) { | |
_this3._emitXhrError(xhr, new Error("tus: invalid or missing length value")); | |
return; | |
} | |
// Upload has already been completed and we do not need to send additional | |
// data to the server | |
if (offset === length) { | |
_this3._emitProgress(length, length); | |
_this3._emitSuccess(); | |
return; | |
} | |
_this3._offset = offset; | |
_this3._startUpload(); | |
}; | |
xhr.onerror = function (err) { | |
_this3._emitXhrError(xhr, new Error("tus: failed to resume upload"), err); | |
}; | |
this._setupXHR(xhr); | |
xhr.send(null); | |
} | |
/** | |
* Start uploading the file using PATCH requests. The file will be divided | |
* into chunks as specified in the chunkSize option. During the upload | |
* the onProgress event handler may be invoked multiple times. | |
* | |
* @api private | |
*/ | |
}, { | |
key: "_startUpload", | |
value: function _startUpload() { | |
var _this4 = this; | |
// If the upload has been aborted, we will not send the next PATCH request. | |
// This is important if the abort method was called during a callback, such | |
// as onChunkComplete or onProgress. | |
if (this._aborted) { | |
return; | |
} | |
var xhr = this._xhr = (0, _$request_69.newRequest)(); | |
// Some browser and servers may not support the PATCH method. For those | |
// cases, you can tell tus-js-client to use a POST request with the | |
// X-HTTP-Method-Override header for simulating a PATCH request. | |
if (this.options.overridePatchMethod) { | |
xhr.open("POST", this.url, true); | |
xhr.setRequestHeader("X-HTTP-Method-Override", "PATCH"); | |
} else { | |
xhr.open("PATCH", this.url, true); | |
} | |
xhr.onload = function () { | |
if (!(xhr.status >= 200 && xhr.status < 300)) { | |
_this4._emitXhrError(xhr, new Error("tus: unexpected response while uploading chunk")); | |
return; | |
} | |
var offset = parseInt(xhr.getResponseHeader("Upload-Offset"), 10); | |
if (isNaN(offset)) { | |
_this4._emitXhrError(xhr, new Error("tus: invalid or missing offset value")); | |
return; | |
} | |
_this4._emitProgress(offset, _this4._size); | |
_this4._emitChunkComplete(offset - _this4._offset, offset, _this4._size); | |
_this4._offset = offset; | |
if (offset == _this4._size) { | |
// Yay, finally done :) | |
_this4._emitSuccess(); | |
_this4._source.close(); | |
return; | |
} | |
_this4._startUpload(); | |
}; | |
xhr.onerror = function (err) { | |
// Don't emit an error if the upload was aborted manually | |
if (_this4._aborted) { | |
return; | |
} | |
_this4._emitXhrError(xhr, new Error("tus: failed to upload chunk at offset " + _this4._offset), err); | |
}; | |
// Test support for progress events before attaching an event listener | |
if ("upload" in xhr) { | |
xhr.upload.onprogress = function (e) { | |
if (!e.lengthComputable) { | |
return; | |
} | |
_this4._emitProgress(start + e.loaded, _this4._size); | |
}; | |
} | |
this._setupXHR(xhr); | |
xhr.setRequestHeader("Upload-Offset", this._offset); | |
xhr.setRequestHeader("Content-Type", "application/offset+octet-stream"); | |
var start = this._offset; | |
var end = this._offset + this.options.chunkSize; | |
// The specified chunkSize may be Infinity or the calcluated end position | |
// may exceed the file's size. In both cases, we limit the end position to | |
// the input's total size for simpler calculations and correctness. | |
if (end === Infinity || end > this._size) { | |
end = this._size; | |
} | |
xhr.send(this._source.slice(start, end)); | |
} | |
}]); | |
return Upload; | |
}(); | |
function encodeMetadata(metadata) { | |
if (!Base64.isSupported) { | |
return ""; | |
} | |
var encoded = []; | |
for (var key in metadata) { | |
encoded.push(key + " " + Base64.encode(metadata[key])); | |
} | |
return encoded.join(","); | |
} | |
Upload.defaultOptions = defaultOptions; | |
_$upload_75.default = Upload; | |
// Generated by Babel | |
"use strict"; | |
/* removed: var _$upload_75 = require("./upload"); */; | |
var _upload2 = ___interopRequireDefault_74(_$upload_75); | |
/* removed: var _$storage_71 = require("./node/storage"); */; | |
function ___interopRequireDefault_74(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | |
/* global window */ | |
var __defaultOptions_74 = _upload2.default.defaultOptions; | |
if (typeof window !== "undefined") { | |
// Browser environment using XMLHttpRequest | |
var ___window_74 = window; | |
var XMLHttpRequest = ___window_74.XMLHttpRequest; | |
var Blob = ___window_74.Blob; | |
var __isSupported_74 = XMLHttpRequest && Blob && typeof Blob.prototype.slice === "function"; | |
} else { | |
// Node.js environment using http module | |
var __isSupported_74 = true; | |
} | |
// The usage of the commonjs exporting syntax instead of the new ECMAScript | |
// one is actually inteded and prevents weird behaviour if we are trying to | |
// import this module in another module using Babel. | |
var _$libEs5_74 = { | |
Upload: _upload2.default, | |
/* common-shake removed: isSupported: isSupported */ | |
/* common-shake removed: canStoreURLs: _storage.canStoreURLs */ | |
/* common-shake removed: defaultOptions: defaultOptions */ | |
}; | |
var _$URL_76 = {}; | |
(function (global){ | |
'use strict'; | |
var required = _$required_56 | |
, qs = _$querystringify_55 | |
, protocolre = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\S\s]*)/i | |
, slashes = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//; | |
/** | |
* These are the parse rules for the URL parser, it informs the parser | |
* about: | |
* | |
* 0. The char it Needs to parse, if it's a string it should be done using | |
* indexOf, RegExp using exec and NaN means set as current value. | |
* 1. The property we should set when parsing this value. | |
* 2. Indication if it's backwards or forward parsing, when set as number it's | |
* the value of extra chars that should be split off. | |
* 3. Inherit from location if non existing in the parser. | |
* 4. `toLowerCase` the resulting value. | |
*/ | |
var rules = [ | |
['#', 'hash'], // Extract from the back. | |
['?', 'query'], // Extract from the back. | |
['/', 'pathname'], // Extract from the back. | |
['@', 'auth', 1], // Extract from the front. | |
[NaN, 'host', undefined, 1, 1], // Set left over value. | |
[/:(\d+)$/, 'port', undefined, 1], // RegExp the back. | |
[NaN, 'hostname', undefined, 1, 1] // Set left over. | |
]; | |
/** | |
* These properties should not be copied or inherited from. This is only needed | |
* for all non blob URL's as a blob URL does not include a hash, only the | |
* origin. | |
* | |
* @type {Object} | |
* @private | |
*/ | |
var ignore = { hash: 1, query: 1 }; | |
/** | |
* The location object differs when your code is loaded through a normal page, | |
* Worker or through a worker using a blob. And with the blobble begins the | |
* trouble as the location object will contain the URL of the blob, not the | |
* location of the page where our code is loaded in. The actual origin is | |
* encoded in the `pathname` so we can thankfully generate a good "default" | |
* location from it so we can generate proper relative URL's again. | |
* | |
* @param {Object|String} loc Optional default location object. | |
* @returns {Object} lolcation object. | |
* @api public | |
*/ | |
function lolcation(loc) { | |
loc = loc || global.location || {}; | |
var finaldestination = {} | |
, type = typeof loc | |
, key; | |
if ('blob:' === loc.protocol) { | |
finaldestination = new URL(unescape(loc.pathname), {}); | |
} else if ('string' === type) { | |
finaldestination = new URL(loc, {}); | |
for (key in ignore) delete finaldestination[key]; | |
} else if ('object' === type) { | |
for (key in loc) { | |
if (key in ignore) continue; | |
finaldestination[key] = loc[key]; | |
} | |
if (finaldestination.slashes === undefined) { | |
finaldestination.slashes = slashes.test(loc.href); | |
} | |
} | |
return finaldestination; | |
} | |
/** | |
* @typedef ProtocolExtract | |
* @type Object | |
* @property {String} protocol Protocol matched in the URL, in lowercase. | |
* @property {Boolean} slashes `true` if protocol is followed by "//", else `false`. | |
* @property {String} rest Rest of the URL that is not part of the protocol. | |
*/ | |
/** | |
* Extract protocol information from a URL with/without double slash ("//"). | |
* | |
* @param {String} address URL we want to extract from. | |
* @return {ProtocolExtract} Extracted information. | |
* @api private | |
*/ | |
function extractProtocol(address) { | |
var match = protocolre.exec(address); | |
return { | |
protocol: match[1] ? match[1].toLowerCase() : '', | |
slashes: !!match[2], | |
rest: match[3] | |
}; | |
} | |
/** | |
* Resolve a relative URL pathname against a base URL pathname. | |
* | |
* @param {String} relative Pathname of the relative URL. | |
* @param {String} base Pathname of the base URL. | |
* @return {String} Resolved pathname. | |
* @api private | |
*/ | |
function resolve(relative, base) { | |
var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/')) | |
, i = path.length | |
, last = path[i - 1] | |
, unshift = false | |
, up = 0; | |
while (i--) { | |
if (path[i] === '.') { | |
path.splice(i, 1); | |
} else if (path[i] === '..') { | |
path.splice(i, 1); | |
up++; | |
} else if (up) { | |
if (i === 0) unshift = true; | |
path.splice(i, 1); | |
up--; | |
} | |
} | |
if (unshift) path.unshift(''); | |
if (last === '.' || last === '..') path.push(''); | |
return path.join('/'); | |
} | |
/** | |
* The actual URL instance. Instead of returning an object we've opted-in to | |
* create an actual constructor as it's much more memory efficient and | |
* faster and it pleases my OCD. | |
* | |
* @constructor | |
* @param {String} address URL we want to parse. | |
* @param {Object|String} location Location defaults for relative paths. | |
* @param {Boolean|Function} parser Parser for the query string. | |
* @api public | |
*/ | |
function URL(address, location, parser) { | |
if (!(this instanceof URL)) { | |
return new URL(address, location, parser); | |
} | |
var relative, extracted, parse, instruction, index, key | |
, instructions = rules.slice() | |
, type = typeof location | |
, url = this | |
, i = 0; | |
// | |
// The following if statements allows this module two have compatibility with | |
// 2 different API: | |
// | |
// 1. Node.js's `url.parse` api which accepts a URL, boolean as arguments | |
// where the boolean indicates that the query string should also be parsed. | |
// | |
// 2. The `URL` interface of the browser which accepts a URL, object as | |
// arguments. The supplied object will be used as default values / fall-back | |
// for relative paths. | |
// | |
if ('object' !== type && 'string' !== type) { | |
parser = location; | |
location = null; | |
} | |
if (parser && 'function' !== typeof parser) parser = qs.parse; | |
location = lolcation(location); | |
// | |
// Extract protocol information before running the instructions. | |
// | |
extracted = extractProtocol(address || ''); | |
relative = !extracted.protocol && !extracted.slashes; | |
url.slashes = extracted.slashes || relative && location.slashes; | |
url.protocol = extracted.protocol || location.protocol || ''; | |
address = extracted.rest; | |
// | |
// When the authority component is absent the URL starts with a path | |
// component. | |
// | |
if (!extracted.slashes) instructions[2] = [/(.*)/, 'pathname']; | |
for (; i < instructions.length; i++) { | |
instruction = instructions[i]; | |
parse = instruction[0]; | |
key = instruction[1]; | |
if (parse !== parse) { | |
url[key] = address; | |
} else if ('string' === typeof parse) { | |
if (~(index = address.indexOf(parse))) { | |
if ('number' === typeof instruction[2]) { | |
url[key] = address.slice(0, index); | |
address = address.slice(index + instruction[2]); | |
} else { | |
url[key] = address.slice(index); | |
address = address.slice(0, index); | |
} | |
} | |
} else if ((index = parse.exec(address))) { | |
url[key] = index[1]; | |
address = address.slice(0, index.index); | |
} | |
url[key] = url[key] || ( | |
relative && instruction[3] ? location[key] || '' : '' | |
); | |
// | |
// Hostname, host and protocol should be lowercased so they can be used to | |
// create a proper `origin`. | |
// | |
if (instruction[4]) url[key] = url[key].toLowerCase(); | |
} | |
// | |
// Also parse the supplied query string in to an object. If we're supplied | |
// with a custom parser as function use that instead of the default build-in | |
// parser. | |
// | |
if (parser) url.query = parser(url.query); | |
// | |
// If the URL is relative, resolve the pathname against the base URL. | |
// | |
if ( | |
relative | |
&& location.slashes | |
&& url.pathname.charAt(0) !== '/' | |
&& (url.pathname !== '' || location.pathname !== '') | |
) { | |
url.pathname = resolve(url.pathname, location.pathname); | |
} | |
// | |
// We should not add port numbers if they are already the default port number | |
// for a given protocol. As the host also contains the port number we're going | |
// override it with the hostname which contains no port number. | |
// | |
if (!required(url.port, url.protocol)) { | |
url.host = url.hostname; | |
url.port = ''; | |
} | |
// | |
// Parse down the `auth` for the username and password. | |
// | |
url.username = url.password = ''; | |
if (url.auth) { | |
instruction = url.auth.split(':'); | |
url.username = instruction[0] || ''; | |
url.password = instruction[1] || ''; | |
} | |
url.origin = url.protocol && url.host && url.protocol !== 'file:' | |
? url.protocol +'//'+ url.host | |
: 'null'; | |
// | |
// The href is just the compiled result. | |
// | |
url.href = url.toString(); | |
} | |
/** | |
* This is convenience method for changing properties in the URL instance to | |
* insure that they all propagate correctly. | |
* | |
* @param {String} part Property we need to adjust. | |
* @param {Mixed} value The newly assigned value. | |
* @param {Boolean|Function} fn When setting the query, it will be the function | |
* used to parse the query. | |
* When setting the protocol, double slash will be | |
* removed from the final url if it is true. | |
* @returns {URL} | |
* @api public | |
*/ | |
function set(part, value, fn) { | |
var url = this; | |
switch (part) { | |
case 'query': | |
if ('string' === typeof value && value.length) { | |
value = (fn || qs.parse)(value); | |
} | |
url[part] = value; | |
break; | |
case 'port': | |
url[part] = value; | |
if (!required(value, url.protocol)) { | |
url.host = url.hostname; | |
url[part] = ''; | |
} else if (value) { | |
url.host = url.hostname +':'+ value; | |
} | |
break; | |
case 'hostname': | |
url[part] = value; | |
if (url.port) value += ':'+ url.port; | |
url.host = value; | |
break; | |
case 'host': | |
url[part] = value; | |
if (/:\d+$/.test(value)) { | |
value = value.split(':'); | |
url.port = value.pop(); | |
url.hostname = value.join(':'); | |
} else { | |
url.hostname = value; | |
url.port = ''; | |
} | |
break; | |
case 'protocol': | |
url.protocol = value.toLowerCase(); | |
url.slashes = !fn; | |
break; | |
case 'pathname': | |
url.pathname = value.length && value.charAt(0) !== '/' ? '/' + value : value; | |
break; | |
default: | |
url[part] = value; | |
} | |
for (var i = 0; i < rules.length; i++) { | |
var ins = rules[i]; | |
if (ins[4]) url[ins[1]] = url[ins[1]].toLowerCase(); | |
} | |
url.origin = url.protocol && url.host && url.protocol !== 'file:' | |
? url.protocol +'//'+ url.host | |
: 'null'; | |
url.href = url.toString(); | |
return url; | |
} | |
/** | |
* Transform the properties back in to a valid and full URL string. | |
* | |
* @param {Function} stringify Optional query stringify function. | |
* @returns {String} | |
* @api public | |
*/ | |
function toString(stringify) { | |
if (!stringify || 'function' !== typeof stringify) stringify = qs.stringify; | |
var query | |
, url = this | |
, protocol = url.protocol; | |
if (protocol && protocol.charAt(protocol.length - 1) !== ':') protocol += ':'; | |
var result = protocol + (url.slashes ? '//' : ''); | |
if (url.username) { | |
result += url.username; | |
if (url.password) result += ':'+ url.password; | |
result += '@'; | |
} | |
result += url.host + url.pathname; | |
query = 'object' === typeof url.query ? stringify(url.query) : url.query; | |
if (query) result += '?' !== query.charAt(0) ? '?'+ query : query; | |
if (url.hash) result += url.hash; | |
return result; | |
} | |
URL.prototype = { set: set, toString: toString }; | |
// | |
// Expose the URL parser and some additional properties that might be useful for | |
// others or testing. | |
// | |
URL.extractProtocol = extractProtocol; | |
URL.location = lolcation; | |
URL.qs = qs; | |
_$URL_76 = URL; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
var _$fetch_77 = {}; | |
(function(self) { | |
'use strict'; | |
if (self.fetch) { | |
return | |
} | |
var support = { | |
searchParams: 'URLSearchParams' in self, | |
iterable: 'Symbol' in self && 'iterator' in Symbol, | |
blob: 'FileReader' in self && 'Blob' in self && (function() { | |
try { | |
new Blob() | |
return true | |
} catch(e) { | |
return false | |
} | |
})(), | |
formData: 'FormData' in self, | |
arrayBuffer: 'ArrayBuffer' in self | |
} | |
if (support.arrayBuffer) { | |
var viewClasses = [ | |
'[object Int8Array]', | |
'[object Uint8Array]', | |
'[object Uint8ClampedArray]', | |
'[object Int16Array]', | |
'[object Uint16Array]', | |
'[object Int32Array]', | |
'[object Uint32Array]', | |
'[object Float32Array]', | |
'[object Float64Array]' | |
] | |
var isDataView = function(obj) { | |
return obj && DataView.prototype.isPrototypeOf(obj) | |
} | |
var isArrayBufferView = ArrayBuffer.isView || function(obj) { | |
return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 | |
} | |
} | |
function normalizeName(name) { | |
if (typeof name !== 'string') { | |
name = String(name) | |
} | |
if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { | |
throw new TypeError('Invalid character in header field name') | |
} | |
return name.toLowerCase() | |
} | |
function normalizeValue(value) { | |
if (typeof value !== 'string') { | |
value = String(value) | |
} | |
return value | |
} | |
// Build a destructive iterator for the value list | |
function iteratorFor(items) { | |
var iterator = { | |
next: function() { | |
var value = items.shift() | |
return {done: value === undefined, value: value} | |
} | |
} | |
if (support.iterable) { | |
iterator[Symbol.iterator] = function() { | |
return iterator | |
} | |
} | |
return iterator | |
} | |
function Headers(headers) { | |
this.map = {} | |
if (headers instanceof Headers) { | |
headers.forEach(function(value, name) { | |
this.append(name, value) | |
}, this) | |
} else if (Array.isArray(headers)) { | |
headers.forEach(function(header) { | |
this.append(header[0], header[1]) | |
}, this) | |
} else if (headers) { | |
Object.getOwnPropertyNames(headers).forEach(function(name) { | |
this.append(name, headers[name]) | |
}, this) | |
} | |
} | |
Headers.prototype.append = function(name, value) { | |
name = normalizeName(name) | |
value = normalizeValue(value) | |
var oldValue = this.map[name] | |
this.map[name] = oldValue ? oldValue+','+value : value | |
} | |
Headers.prototype['delete'] = function(name) { | |
delete this.map[normalizeName(name)] | |
} | |
Headers.prototype.get = function(name) { | |
name = normalizeName(name) | |
return this.has(name) ? this.map[name] : null | |
} | |
Headers.prototype.has = function(name) { | |
return this.map.hasOwnProperty(normalizeName(name)) | |
} | |
Headers.prototype.set = function(name, value) { | |
this.map[normalizeName(name)] = normalizeValue(value) | |
} | |
Headers.prototype.forEach = function(callback, thisArg) { | |
for (var name in this.map) { | |
if (this.map.hasOwnProperty(name)) { | |
callback.call(thisArg, this.map[name], name, this) | |
} | |
} | |
} | |
Headers.prototype.keys = function() { | |
var items = [] | |
this.forEach(function(value, name) { items.push(name) }) | |
return iteratorFor(items) | |
} | |
Headers.prototype.values = function() { | |
var items = [] | |
this.forEach(function(value) { items.push(value) }) | |
return iteratorFor(items) | |
} | |
Headers.prototype.entries = function() { | |
var items = [] | |
this.forEach(function(value, name) { items.push([name, value]) }) | |
return iteratorFor(items) | |
} | |
if (support.iterable) { | |
Headers.prototype[Symbol.iterator] = Headers.prototype.entries | |
} | |
function consumed(body) { | |
if (body.bodyUsed) { | |
return Promise.reject(new TypeError('Already read')) | |
} | |
body.bodyUsed = true | |
} | |
function fileReaderReady(reader) { | |
return new Promise(function(resolve, reject) { | |
reader.onload = function() { | |
resolve(reader.result) | |
} | |
reader.onerror = function() { | |
reject(reader.error) | |
} | |
}) | |
} | |
function readBlobAsArrayBuffer(blob) { | |
var reader = new FileReader() | |
var promise = fileReaderReady(reader) | |
reader.readAsArrayBuffer(blob) | |
return promise | |
} | |
function readBlobAsText(blob) { | |
var reader = new FileReader() | |
var promise = fileReaderReady(reader) | |
reader.readAsText(blob) | |
return promise | |
} | |
function readArrayBufferAsText(buf) { | |
var view = new Uint8Array(buf) | |
var chars = new Array(view.length) | |
for (var i = 0; i < view.length; i++) { | |
chars[i] = String.fromCharCode(view[i]) | |
} | |
return chars.join('') | |
} | |
function bufferClone(buf) { | |
if (buf.slice) { | |
return buf.slice(0) | |
} else { | |
var view = new Uint8Array(buf.byteLength) | |
view.set(new Uint8Array(buf)) | |
return view.buffer | |
} | |
} | |
function Body() { | |
this.bodyUsed = false | |
this._initBody = function(body) { | |
this._bodyInit = body | |
if (!body) { | |
this._bodyText = '' | |
} else if (typeof body === 'string') { | |
this._bodyText = body | |
} else if (support.blob && Blob.prototype.isPrototypeOf(body)) { | |
this._bodyBlob = body | |
} else if (support.formData && FormData.prototype.isPrototypeOf(body)) { | |
this._bodyFormData = body | |
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { | |
this._bodyText = body.toString() | |
} else if (support.arrayBuffer && support.blob && isDataView(body)) { | |
this._bodyArrayBuffer = bufferClone(body.buffer) | |
// IE 10-11 can't handle a DataView body. | |
this._bodyInit = new Blob([this._bodyArrayBuffer]) | |
} else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { | |
this._bodyArrayBuffer = bufferClone(body) | |
} else { | |
throw new Error('unsupported BodyInit type') | |
} | |
if (!this.headers.get('content-type')) { | |
if (typeof body === 'string') { | |
this.headers.set('content-type', 'text/plain;charset=UTF-8') | |
} else if (this._bodyBlob && this._bodyBlob.type) { | |
this.headers.set('content-type', this._bodyBlob.type) | |
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { | |
this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') | |
} | |
} | |
} | |
if (support.blob) { | |
this.blob = function() { | |
var rejected = consumed(this) | |
if (rejected) { | |
return rejected | |
} | |
if (this._bodyBlob) { | |
return Promise.resolve(this._bodyBlob) | |
} else if (this._bodyArrayBuffer) { | |
return Promise.resolve(new Blob([this._bodyArrayBuffer])) | |
} else if (this._bodyFormData) { | |
throw new Error('could not read FormData body as blob') | |
} else { | |
return Promise.resolve(new Blob([this._bodyText])) | |
} | |
} | |
this.arrayBuffer = function() { | |
if (this._bodyArrayBuffer) { | |
return consumed(this) || Promise.resolve(this._bodyArrayBuffer) | |
} else { | |
return this.blob().then(readBlobAsArrayBuffer) | |
} | |
} | |
} | |
this.text = function() { | |
var rejected = consumed(this) | |
if (rejected) { | |
return rejected | |
} | |
if (this._bodyBlob) { | |
return readBlobAsText(this._bodyBlob) | |
} else if (this._bodyArrayBuffer) { | |
return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) | |
} else if (this._bodyFormData) { | |
throw new Error('could not read FormData body as text') | |
} else { | |
return Promise.resolve(this._bodyText) | |
} | |
} | |
if (support.formData) { | |
this.formData = function() { | |
return this.text().then(decode) | |
} | |
} | |
this.json = function() { | |
return this.text().then(JSON.parse) | |
} | |
return this | |
} | |
// HTTP methods whose capitalization should be normalized | |
var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] | |
function normalizeMethod(method) { | |
var upcased = method.toUpperCase() | |
return (methods.indexOf(upcased) > -1) ? upcased : method | |
} | |
function Request(input, options) { | |
options = options || {} | |
var body = options.body | |
if (input instanceof Request) { | |
if (input.bodyUsed) { | |
throw new TypeError('Already read') | |
} | |
this.url = input.url | |
this.credentials = input.credentials | |
if (!options.headers) { | |
this.headers = new Headers(input.headers) | |
} | |
this.method = input.method | |
this.mode = input.mode | |
if (!body && input._bodyInit != null) { | |
body = input._bodyInit | |
input.bodyUsed = true | |
} | |
} else { | |
this.url = String(input) | |
} | |
this.credentials = options.credentials || this.credentials || 'omit' | |
if (options.headers || !this.headers) { | |
this.headers = new Headers(options.headers) | |
} | |
this.method = normalizeMethod(options.method || this.method || 'GET') | |
this.mode = options.mode || this.mode || null | |
this.referrer = null | |
if ((this.method === 'GET' || this.method === 'HEAD') && body) { | |
throw new TypeError('Body not allowed for GET or HEAD requests') | |
} | |
this._initBody(body) | |
} | |
Request.prototype.clone = function() { | |
return new Request(this, { body: this._bodyInit }) | |
} | |
function decode(body) { | |
var form = new FormData() | |
body.trim().split('&').forEach(function(bytes) { | |
if (bytes) { | |
var split = bytes.split('=') | |
var name = split.shift().replace(/\+/g, ' ') | |
var value = split.join('=').replace(/\+/g, ' ') | |
form.append(decodeURIComponent(name), decodeURIComponent(value)) | |
} | |
}) | |
return form | |
} | |
function parseHeaders(rawHeaders) { | |
var headers = new Headers() | |
rawHeaders.split(/\r?\n/).forEach(function(line) { | |
var parts = line.split(':') | |
var key = parts.shift().trim() | |
if (key) { | |
var value = parts.join(':').trim() | |
headers.append(key, value) | |
} | |
}) | |
return headers | |
} | |
Body.call(Request.prototype) | |
function Response(bodyInit, options) { | |
if (!options) { | |
options = {} | |
} | |
this.type = 'default' | |
this.status = 'status' in options ? options.status : 200 | |
this.ok = this.status >= 200 && this.status < 300 | |
this.statusText = 'statusText' in options ? options.statusText : 'OK' | |
this.headers = new Headers(options.headers) | |
this.url = options.url || '' | |
this._initBody(bodyInit) | |
} | |
Body.call(Response.prototype) | |
Response.prototype.clone = function() { | |
return new Response(this._bodyInit, { | |
status: this.status, | |
statusText: this.statusText, | |
headers: new Headers(this.headers), | |
url: this.url | |
}) | |
} | |
Response.error = function() { | |
var response = new Response(null, {status: 0, statusText: ''}) | |
response.type = 'error' | |
return response | |
} | |
var redirectStatuses = [301, 302, 303, 307, 308] | |
Response.redirect = function(url, status) { | |
if (redirectStatuses.indexOf(status) === -1) { | |
throw new RangeError('Invalid status code') | |
} | |
return new Response(null, {status: status, headers: {location: url}}) | |
} | |
self.Headers = Headers | |
self.Request = Request | |
self.Response = Response | |
self.fetch = function(input, init) { | |
return new Promise(function(resolve, reject) { | |
var request = new Request(input, init) | |
var xhr = new XMLHttpRequest() | |
xhr.onload = function() { | |
var options = { | |
status: xhr.status, | |
statusText: xhr.statusText, | |
headers: parseHeaders(xhr.getAllResponseHeaders() || '') | |
} | |
options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL') | |
var body = 'response' in xhr ? xhr.response : xhr.responseText | |
resolve(new Response(body, options)) | |
} | |
xhr.onerror = function() { | |
reject(new TypeError('Network request failed')) | |
} | |
xhr.ontimeout = function() { | |
reject(new TypeError('Network request failed')) | |
} | |
xhr.open(request.method, request.url, true) | |
if (request.credentials === 'include') { | |
xhr.withCredentials = true | |
} | |
if ('responseType' in xhr && support.blob) { | |
xhr.responseType = 'blob' | |
} | |
request.headers.forEach(function(value, name) { | |
xhr.setRequestHeader(name, value) | |
}) | |
xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) | |
}) | |
} | |
self.fetch.polyfill = true | |
})(typeof self !== 'undefined' ? self : this); | |
var _$updateEvents_81 = [ | |
// attribute events (can be set with attributes) | |
'onclick', | |
'ondblclick', | |
'onmousedown', | |
'onmouseup', | |
'onmouseover', | |
'onmousemove', | |
'onmouseout', | |
'ondragstart', | |
'ondrag', | |
'ondragenter', | |
'ondragleave', | |
'ondragover', | |
'ondrop', | |
'ondragend', | |
'onkeydown', | |
'onkeypress', | |
'onkeyup', | |
'onunload', | |
'onabort', | |
'onerror', | |
'onresize', | |
'onscroll', | |
'onselect', | |
'onchange', | |
'onsubmit', | |
'onreset', | |
'onfocus', | |
'onblur', | |
'oninput', | |
// other common events | |
'oncontextmenu', | |
'onfocusin', | |
'onfocusout' | |
] | |
var _$yoYo_80 = {}; | |
/* removed: var _$bel_11 = require('bel') */; // turns template tag into DOM elements | |
/* removed: var _$morphdom_46 = require('morphdom') */; // efficiently diffs + morphs two DOM elements | |
/* removed: var _$updateEvents_81 = require('./update-events.js') */; // default events to be copied when dom elements update | |
_$yoYo_80 = _$bel_11 | |
// TODO move this + defaultEvents to a new module once we receive more feedback | |
_$yoYo_80.update = function (fromNode, toNode, opts) { | |
if (!opts) opts = {} | |
if (opts.events !== false) { | |
if (!opts.onBeforeElUpdated) opts.onBeforeElUpdated = copier | |
} | |
return _$morphdom_46(fromNode, toNode, opts) | |
// morphdom only copies attributes. we decided we also wanted to copy events | |
// that can be set via attributes | |
function copier (f, t) { | |
// copy events: | |
var events = opts.events || _$updateEvents_81 | |
for (var i = 0; i < events.length; i++) { | |
var ev = events[i] | |
if (t[ev]) { // if new element has a whitelisted attribute | |
f[ev] = t[ev] // update existing element | |
} else if (f[ev]) { // if existing element has it and new one doesnt | |
f[ev] = undefined // remove it from existing element | |
} | |
} | |
var oldValue = f.value | |
var newValue = t.value | |
// copy values for form elements | |
if ((f.nodeName === 'INPUT' && f.type !== 'file') || f.nodeName === 'SELECT') { | |
if (!newValue) { | |
t.value = f.value | |
} else if (newValue !== oldValue) { | |
f.value = newValue | |
} | |
} else if (f.nodeName === 'TEXTAREA') { | |
if (t.getAttribute('value') === null) f.value = t.value | |
} | |
} | |
} | |
/** | |
* Translates strings with interpolation & pluralization support. | |
* Extensible with custom dictionaries and pluralization functions. | |
* | |
* Borrows heavily from and inspired by Polyglot https://github.com/airbnb/polyglot.js, | |
* basically a stripped-down version of it. Differences: pluralization functions are not hardcoded | |
* and can be easily added among with dictionaries, nested objects are used for pluralization | |
* as opposed to `||||` delimeter | |
* | |
* Usage example: `translator.translate('files_chosen', {smart_count: 3})` | |
* | |
* @param {object} opts | |
*/ | |
var _$Translator_83 = class Translator { | |
constructor (opts) { | |
const defaultOptions = { | |
locale: { | |
strings: {}, | |
pluralize: function (n) { | |
if (n === 1) { | |
return 0 | |
} | |
return 1 | |
} | |
} | |
} | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.locale = Object.assign({}, defaultOptions.locale, opts.locale) | |
// console.log(this.opts.locale) | |
// this.locale.pluralize = this.locale ? this.locale.pluralize : defaultPluralize | |
// this.locale.strings = Object.assign({}, en_US.strings, this.opts.locale.strings) | |
} | |
/** | |
* Takes a string with placeholder variables like `%{smart_count} file selected` | |
* and replaces it with values from options `{smart_count: 5}` | |
* | |
* @license https://github.com/airbnb/polyglot.js/blob/master/LICENSE | |
* taken from https://github.com/airbnb/polyglot.js/blob/master/lib/polyglot.js#L299 | |
* | |
* @param {string} phrase that needs interpolation, with placeholders | |
* @param {object} options with values that will be used to replace placeholders | |
* @return {string} interpolated | |
*/ | |
interpolate (phrase, options) { | |
const replace = String.prototype.replace | |
const dollarRegex = /\$/g | |
const dollarBillsYall = '$$$$' | |
for (let arg in options) { | |
if (arg !== '_' && options.hasOwnProperty(arg)) { | |
// Ensure replacement value is escaped to prevent special $-prefixed | |
// regex replace tokens. the "$$$$" is needed because each "$" needs to | |
// be escaped with "$" itself, and we need two in the resulting output. | |
var replacement = options[arg] | |
if (typeof replacement === 'string') { | |
replacement = replace.call(options[arg], dollarRegex, dollarBillsYall) | |
} | |
// We create a new `RegExp` each time instead of using a more-efficient | |
// string replace so that the same argument can be replaced multiple times | |
// in the same phrase. | |
phrase = replace.call(phrase, new RegExp('%\\{' + arg + '\\}', 'g'), replacement) | |
} | |
} | |
return phrase | |
} | |
/** | |
* Public translate method | |
* | |
* @param {string} key | |
* @param {object} options with values that will be used later to replace placeholders in string | |
* @return {string} translated (and interpolated) | |
*/ | |
translate (key, options) { | |
if (options && options.smart_count) { | |
var plural = this.locale.pluralize(options.smart_count) | |
return this.interpolate(this.opts.locale.strings[key][plural], options) | |
} | |
return this.interpolate(this.opts.locale.strings[key], options) | |
} | |
} | |
/* removed: const _$createNamespaceEmitter_48 = require('namespace-emitter') */; | |
var _$UppySocket_84 = class UppySocket { | |
constructor (opts) { | |
this.queued = [] | |
this.isOpen = false | |
this.socket = new WebSocket(opts.target) | |
this.emitter = _$createNamespaceEmitter_48() | |
this.socket.onopen = (e) => { | |
this.isOpen = true | |
while (this.queued.length > 0 && this.isOpen) { | |
const first = this.queued[0] | |
this.send(first.action, first.payload) | |
this.queued = this.queued.slice(1) | |
} | |
} | |
this.socket.onclose = (e) => { | |
this.isOpen = false | |
} | |
this._handleMessage = this._handleMessage.bind(this) | |
this.socket.onmessage = this._handleMessage | |
this.close = this.close.bind(this) | |
this.emit = this.emit.bind(this) | |
this.on = this.on.bind(this) | |
this.once = this.once.bind(this) | |
this.send = this.send.bind(this) | |
} | |
close () { | |
return this.socket.close() | |
} | |
send (action, payload) { | |
// attach uuid | |
if (!this.isOpen) { | |
this.queued.push({action, payload}) | |
return | |
} | |
this.socket.send(JSON.stringify({ | |
action, | |
payload | |
})) | |
} | |
on (action, handler) { | |
console.log(action) | |
this.emitter.on(action, handler) | |
} | |
emit (action, payload) { | |
console.log(action) | |
this.emitter.emit(action, payload) | |
} | |
once (action, handler) { | |
this.emitter.once(action, handler) | |
} | |
_handleMessage (e) { | |
try { | |
const message = JSON.parse(e.data) | |
console.log(message) | |
this.emit(message.action, message.payload) | |
} catch (err) { | |
console.log(err) | |
} | |
} | |
} | |
'use strict'; | |
var _$fileType_138 = input => { | |
const buf = new Uint8Array(input); | |
if (!(buf && buf.length > 1)) { | |
return null; | |
} | |
const check = (header, opts) => { | |
opts = Object.assign({ | |
offset: 0 | |
}, opts); | |
for (let i = 0; i < header.length; i++) { | |
if (header[i] !== buf[i + opts.offset]) { | |
return false; | |
} | |
} | |
return true; | |
}; | |
if (check([0xFF, 0xD8, 0xFF])) { | |
return { | |
ext: 'jpg', | |
mime: 'image/jpeg' | |
}; | |
} | |
if (check([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])) { | |
return { | |
ext: 'png', | |
mime: 'image/png' | |
}; | |
} | |
if (check([0x47, 0x49, 0x46])) { | |
return { | |
ext: 'gif', | |
mime: 'image/gif' | |
}; | |
} | |
if (check([0x57, 0x45, 0x42, 0x50], {offset: 8})) { | |
return { | |
ext: 'webp', | |
mime: 'image/webp' | |
}; | |
} | |
if (check([0x46, 0x4C, 0x49, 0x46])) { | |
return { | |
ext: 'flif', | |
mime: 'image/flif' | |
}; | |
} | |
// Needs to be before `tif` check | |
if ( | |
(check([0x49, 0x49, 0x2A, 0x0]) || check([0x4D, 0x4D, 0x0, 0x2A])) && | |
check([0x43, 0x52], {offset: 8}) | |
) { | |
return { | |
ext: 'cr2', | |
mime: 'image/x-canon-cr2' | |
}; | |
} | |
if ( | |
check([0x49, 0x49, 0x2A, 0x0]) || | |
check([0x4D, 0x4D, 0x0, 0x2A]) | |
) { | |
return { | |
ext: 'tif', | |
mime: 'image/tiff' | |
}; | |
} | |
if (check([0x42, 0x4D])) { | |
return { | |
ext: 'bmp', | |
mime: 'image/bmp' | |
}; | |
} | |
if (check([0x49, 0x49, 0xBC])) { | |
return { | |
ext: 'jxr', | |
mime: 'image/vnd.ms-photo' | |
}; | |
} | |
if (check([0x38, 0x42, 0x50, 0x53])) { | |
return { | |
ext: 'psd', | |
mime: 'image/vnd.adobe.photoshop' | |
}; | |
} | |
// Needs to be before the `zip` check | |
if ( | |
check([0x50, 0x4B, 0x3, 0x4]) && | |
check([0x6D, 0x69, 0x6D, 0x65, 0x74, 0x79, 0x70, 0x65, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x65, 0x70, 0x75, 0x62, 0x2B, 0x7A, 0x69, 0x70], {offset: 30}) | |
) { | |
return { | |
ext: 'epub', | |
mime: 'application/epub+zip' | |
}; | |
} | |
// Needs to be before `zip` check | |
// Assumes signed `.xpi` from addons.mozilla.org | |
if ( | |
check([0x50, 0x4B, 0x3, 0x4]) && | |
check([0x4D, 0x45, 0x54, 0x41, 0x2D, 0x49, 0x4E, 0x46, 0x2F, 0x6D, 0x6F, 0x7A, 0x69, 0x6C, 0x6C, 0x61, 0x2E, 0x72, 0x73, 0x61], {offset: 30}) | |
) { | |
return { | |
ext: 'xpi', | |
mime: 'application/x-xpinstall' | |
}; | |
} | |
if ( | |
check([0x50, 0x4B]) && | |
(buf[2] === 0x3 || buf[2] === 0x5 || buf[2] === 0x7) && | |
(buf[3] === 0x4 || buf[3] === 0x6 || buf[3] === 0x8) | |
) { | |
return { | |
ext: 'zip', | |
mime: 'application/zip' | |
}; | |
} | |
if (check([0x75, 0x73, 0x74, 0x61, 0x72], {offset: 257})) { | |
return { | |
ext: 'tar', | |
mime: 'application/x-tar' | |
}; | |
} | |
if ( | |
check([0x52, 0x61, 0x72, 0x21, 0x1A, 0x7]) && | |
(buf[6] === 0x0 || buf[6] === 0x1) | |
) { | |
return { | |
ext: 'rar', | |
mime: 'application/x-rar-compressed' | |
}; | |
} | |
if (check([0x1F, 0x8B, 0x8])) { | |
return { | |
ext: 'gz', | |
mime: 'application/gzip' | |
}; | |
} | |
if (check([0x42, 0x5A, 0x68])) { | |
return { | |
ext: 'bz2', | |
mime: 'application/x-bzip2' | |
}; | |
} | |
if (check([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C])) { | |
return { | |
ext: '7z', | |
mime: 'application/x-7z-compressed' | |
}; | |
} | |
if (check([0x78, 0x01])) { | |
return { | |
ext: 'dmg', | |
mime: 'application/x-apple-diskimage' | |
}; | |
} | |
if ( | |
( | |
check([0x0, 0x0, 0x0]) && | |
(buf[3] === 0x18 || buf[3] === 0x20) && | |
check([0x66, 0x74, 0x79, 0x70], {offset: 4}) | |
) || | |
check([0x33, 0x67, 0x70, 0x35]) || | |
( | |
check([0x0, 0x0, 0x0, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34, 0x32]) && | |
check([0x6D, 0x70, 0x34, 0x31, 0x6D, 0x70, 0x34, 0x32, 0x69, 0x73, 0x6F, 0x6D], {offset: 16}) | |
) || | |
check([0x0, 0x0, 0x0, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6F, 0x6D]) || | |
check([0x0, 0x0, 0x0, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34, 0x32, 0x0, 0x0, 0x0, 0x0]) | |
) { | |
return { | |
ext: 'mp4', | |
mime: 'video/mp4' | |
}; | |
} | |
if (check([0x0, 0x0, 0x0, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x4D, 0x34, 0x56])) { | |
return { | |
ext: 'm4v', | |
mime: 'video/x-m4v' | |
}; | |
} | |
if (check([0x4D, 0x54, 0x68, 0x64])) { | |
return { | |
ext: 'mid', | |
mime: 'audio/midi' | |
}; | |
} | |
// https://github.com/threatstack/libmagic/blob/master/magic/Magdir/matroska | |
if (check([0x1A, 0x45, 0xDF, 0xA3])) { | |
const sliced = buf.subarray(4, 4 + 4096); | |
const idPos = sliced.findIndex((el, i, arr) => arr[i] === 0x42 && arr[i + 1] === 0x82); | |
if (idPos >= 0) { | |
const docTypePos = idPos + 3; | |
const findDocType = type => Array.from(type).every((c, i) => sliced[docTypePos + i] === c.charCodeAt(0)); | |
if (findDocType('matroska')) { | |
return { | |
ext: 'mkv', | |
mime: 'video/x-matroska' | |
}; | |
} | |
if (findDocType('webm')) { | |
return { | |
ext: 'webm', | |
mime: 'video/webm' | |
}; | |
} | |
} | |
} | |
if (check([0x0, 0x0, 0x0, 0x14, 0x66, 0x74, 0x79, 0x70, 0x71, 0x74, 0x20, 0x20]) || | |
check([0x66, 0x72, 0x65, 0x65], {offset: 4}) || | |
check([0x66, 0x74, 0x79, 0x70, 0x71, 0x74, 0x20, 0x20], {offset: 4}) || | |
check([0x6D, 0x64, 0x61, 0x74], {offset: 4}) || // MJPEG | |
check([0x77, 0x69, 0x64, 0x65], {offset: 4})) { | |
return { | |
ext: 'mov', | |
mime: 'video/quicktime' | |
}; | |
} | |
if ( | |
check([0x52, 0x49, 0x46, 0x46]) && | |
check([0x41, 0x56, 0x49], {offset: 8}) | |
) { | |
return { | |
ext: 'avi', | |
mime: 'video/x-msvideo' | |
}; | |
} | |
if (check([0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9])) { | |
return { | |
ext: 'wmv', | |
mime: 'video/x-ms-wmv' | |
}; | |
} | |
if (check([0x0, 0x0, 0x1, 0xBA])) { | |
return { | |
ext: 'mpg', | |
mime: 'video/mpeg' | |
}; | |
} | |
if ( | |
check([0x49, 0x44, 0x33]) || | |
check([0xFF, 0xFB]) | |
) { | |
return { | |
ext: 'mp3', | |
mime: 'audio/mpeg' | |
}; | |
} | |
if ( | |
check([0x66, 0x74, 0x79, 0x70, 0x4D, 0x34, 0x41], {offset: 4}) || | |
check([0x4D, 0x34, 0x41, 0x20]) | |
) { | |
return { | |
ext: 'm4a', | |
mime: 'audio/m4a' | |
}; | |
} | |
// Needs to be before `ogg` check | |
if (check([0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64], {offset: 28})) { | |
return { | |
ext: 'opus', | |
mime: 'audio/opus' | |
}; | |
} | |
if (check([0x4F, 0x67, 0x67, 0x53])) { | |
return { | |
ext: 'ogg', | |
mime: 'audio/ogg' | |
}; | |
} | |
if (check([0x66, 0x4C, 0x61, 0x43])) { | |
return { | |
ext: 'flac', | |
mime: 'audio/x-flac' | |
}; | |
} | |
if ( | |
check([0x52, 0x49, 0x46, 0x46]) && | |
check([0x57, 0x41, 0x56, 0x45], {offset: 8}) | |
) { | |
return { | |
ext: 'wav', | |
mime: 'audio/x-wav' | |
}; | |
} | |
if (check([0x23, 0x21, 0x41, 0x4D, 0x52, 0x0A])) { | |
return { | |
ext: 'amr', | |
mime: 'audio/amr' | |
}; | |
} | |
if (check([0x25, 0x50, 0x44, 0x46])) { | |
return { | |
ext: 'pdf', | |
mime: 'application/pdf' | |
}; | |
} | |
if (check([0x4D, 0x5A])) { | |
return { | |
ext: 'exe', | |
mime: 'application/x-msdownload' | |
}; | |
} | |
if ( | |
(buf[0] === 0x43 || buf[0] === 0x46) && | |
check([0x57, 0x53], {offset: 1}) | |
) { | |
return { | |
ext: 'swf', | |
mime: 'application/x-shockwave-flash' | |
}; | |
} | |
if (check([0x7B, 0x5C, 0x72, 0x74, 0x66])) { | |
return { | |
ext: 'rtf', | |
mime: 'application/rtf' | |
}; | |
} | |
if (check([0x00, 0x61, 0x73, 0x6D])) { | |
return { | |
ext: 'wasm', | |
mime: 'application/wasm' | |
}; | |
} | |
if ( | |
check([0x77, 0x4F, 0x46, 0x46]) && | |
( | |
check([0x00, 0x01, 0x00, 0x00], {offset: 4}) || | |
check([0x4F, 0x54, 0x54, 0x4F], {offset: 4}) | |
) | |
) { | |
return { | |
ext: 'woff', | |
mime: 'font/woff' | |
}; | |
} | |
if ( | |
check([0x77, 0x4F, 0x46, 0x32]) && | |
( | |
check([0x00, 0x01, 0x00, 0x00], {offset: 4}) || | |
check([0x4F, 0x54, 0x54, 0x4F], {offset: 4}) | |
) | |
) { | |
return { | |
ext: 'woff2', | |
mime: 'font/woff2' | |
}; | |
} | |
if ( | |
check([0x4C, 0x50], {offset: 34}) && | |
( | |
check([0x00, 0x00, 0x01], {offset: 8}) || | |
check([0x01, 0x00, 0x02], {offset: 8}) || | |
check([0x02, 0x00, 0x02], {offset: 8}) | |
) | |
) { | |
return { | |
ext: 'eot', | |
mime: 'application/octet-stream' | |
}; | |
} | |
if (check([0x00, 0x01, 0x00, 0x00, 0x00])) { | |
return { | |
ext: 'ttf', | |
mime: 'font/ttf' | |
}; | |
} | |
if (check([0x4F, 0x54, 0x54, 0x4F, 0x00])) { | |
return { | |
ext: 'otf', | |
mime: 'font/otf' | |
}; | |
} | |
if (check([0x00, 0x00, 0x01, 0x00])) { | |
return { | |
ext: 'ico', | |
mime: 'image/x-icon' | |
}; | |
} | |
if (check([0x46, 0x4C, 0x56, 0x01])) { | |
return { | |
ext: 'flv', | |
mime: 'video/x-flv' | |
}; | |
} | |
if (check([0x25, 0x21])) { | |
return { | |
ext: 'ps', | |
mime: 'application/postscript' | |
}; | |
} | |
if (check([0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00])) { | |
return { | |
ext: 'xz', | |
mime: 'application/x-xz' | |
}; | |
} | |
if (check([0x53, 0x51, 0x4C, 0x69])) { | |
return { | |
ext: 'sqlite', | |
mime: 'application/x-sqlite3' | |
}; | |
} | |
if (check([0x4E, 0x45, 0x53, 0x1A])) { | |
return { | |
ext: 'nes', | |
mime: 'application/x-nintendo-nes-rom' | |
}; | |
} | |
if (check([0x43, 0x72, 0x32, 0x34])) { | |
return { | |
ext: 'crx', | |
mime: 'application/x-google-chrome-extension' | |
}; | |
} | |
if ( | |
check([0x4D, 0x53, 0x43, 0x46]) || | |
check([0x49, 0x53, 0x63, 0x28]) | |
) { | |
return { | |
ext: 'cab', | |
mime: 'application/vnd.ms-cab-compressed' | |
}; | |
} | |
// Needs to be before `ar` check | |
if (check([0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A, 0x64, 0x65, 0x62, 0x69, 0x61, 0x6E, 0x2D, 0x62, 0x69, 0x6E, 0x61, 0x72, 0x79])) { | |
return { | |
ext: 'deb', | |
mime: 'application/x-deb' | |
}; | |
} | |
if (check([0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E])) { | |
return { | |
ext: 'ar', | |
mime: 'application/x-unix-archive' | |
}; | |
} | |
if (check([0xED, 0xAB, 0xEE, 0xDB])) { | |
return { | |
ext: 'rpm', | |
mime: 'application/x-rpm' | |
}; | |
} | |
if ( | |
check([0x1F, 0xA0]) || | |
check([0x1F, 0x9D]) | |
) { | |
return { | |
ext: 'Z', | |
mime: 'application/x-compress' | |
}; | |
} | |
if (check([0x4C, 0x5A, 0x49, 0x50])) { | |
return { | |
ext: 'lz', | |
mime: 'application/x-lzip' | |
}; | |
} | |
if (check([0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1])) { | |
return { | |
ext: 'msi', | |
mime: 'application/x-msi' | |
}; | |
} | |
if (check([0x06, 0x0E, 0x2B, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0D, 0x01, 0x02, 0x01, 0x01, 0x02])) { | |
return { | |
ext: 'mxf', | |
mime: 'application/mxf' | |
}; | |
} | |
if (check([0x47], {offset: 4}) && (check([0x47], {offset: 192}) || check([0x47], {offset: 196}))) { | |
return { | |
ext: 'mts', | |
mime: 'video/mp2t' | |
}; | |
} | |
if (check([0x42, 0x4C, 0x45, 0x4E, 0x44, 0x45, 0x52])) { | |
return { | |
ext: 'blend', | |
mime: 'application/x-blender' | |
}; | |
} | |
if (check([0x42, 0x50, 0x47, 0xFB])) { | |
return { | |
ext: 'bpg', | |
mime: 'image/bpg' | |
}; | |
} | |
return null; | |
}; | |
/* removed: const _$throttle_44 = require('lodash.throttle') */; | |
// we inline file-type module, as opposed to using the NPM version, | |
// because of this https://github.com/sindresorhus/file-type/issues/78 | |
// and https://github.com/sindresorhus/copy-text-to-clipboard/issues/5 | |
/* removed: const _$fileType_138 = require('../vendor/file-type') */; | |
/** | |
* A collection of small utility functions that help with dom manipulation, adding listeners, | |
* promises and other good things. | |
* | |
* @module Utils | |
*/ | |
/** | |
* Shallow flatten nested arrays. | |
*/ | |
function __flatten_85 (arr) { | |
return [].concat.apply([], arr) | |
} | |
function isTouchDevice () { | |
return 'ontouchstart' in window || // works on most browsers | |
navigator.maxTouchPoints // works on IE10/11 and Surface | |
} | |
// /** | |
// * Shorter and fast way to select a single node in the DOM | |
// * @param { String } selector - unique dom selector | |
// * @param { Object } ctx - DOM node where the target of our search will is located | |
// * @returns { Object } dom node found | |
// */ | |
// function $ (selector, ctx) { | |
// return (ctx || document).querySelector(selector) | |
// } | |
// /** | |
// * Shorter and fast way to select multiple nodes in the DOM | |
// * @param { String|Array } selector - DOM selector or nodes list | |
// * @param { Object } ctx - DOM node where the targets of our search will is located | |
// * @returns { Object } dom nodes found | |
// */ | |
// function $$ (selector, ctx) { | |
// var els | |
// if (typeof selector === 'string') { | |
// els = (ctx || document).querySelectorAll(selector) | |
// } else { | |
// els = selector | |
// return Array.prototype.slice.call(els) | |
// } | |
// } | |
function truncateString (str, length) { | |
if (str.length > length) { | |
return str.substr(0, length / 2) + '...' + str.substr(str.length - length / 4, str.length) | |
} | |
return str | |
// more precise version if needed | |
// http://stackoverflow.com/a/831583 | |
} | |
function secondsToTime (rawSeconds) { | |
const hours = Math.floor(rawSeconds / 3600) % 24 | |
const minutes = Math.floor(rawSeconds / 60) % 60 | |
const seconds = Math.floor(rawSeconds % 60) | |
return { hours, minutes, seconds } | |
} | |
/** | |
* Partition array by a grouping function. | |
* @param {[type]} array Input array | |
* @param {[type]} groupingFn Grouping function | |
* @return {[type]} Array of arrays | |
*/ | |
function groupBy (array, groupingFn) { | |
return array.reduce((result, item) => { | |
let key = groupingFn(item) | |
let xs = result.get(key) || [] | |
xs.push(item) | |
result.set(key, xs) | |
return result | |
}, new Map()) | |
} | |
/** | |
* Tests if every array element passes predicate | |
* @param {Array} array Input array | |
* @param {Object} predicateFn Predicate | |
* @return {bool} Every element pass | |
*/ | |
function every (array, predicateFn) { | |
return array.reduce((result, item) => { | |
if (!result) { | |
return false | |
} | |
return predicateFn(item) | |
}, true) | |
} | |
/** | |
* Converts list into array | |
*/ | |
function __toArray_85 (list) { | |
return Array.prototype.slice.call(list || [], 0) | |
} | |
/** | |
* Takes a fileName and turns it into fileID, by converting to lowercase, | |
* removing extra characters and adding unix timestamp | |
* | |
* @param {String} fileName | |
* | |
*/ | |
function generateFileID (file) { | |
let fileID = file.name.toLowerCase() | |
fileID = fileID.replace(/[^A-Z0-9]/ig, '') | |
fileID = fileID + file.data.lastModified | |
return fileID | |
} | |
function extend (...objs) { | |
return Object.assign.apply(this, [{}].concat(objs)) | |
} | |
/** | |
* Runs an array of promise-returning functions in sequence. | |
*/ | |
function runPromiseSequence (functions, ...args) { | |
let promise = Promise.resolve() | |
functions.forEach((func) => { | |
promise = promise.then(() => func(...args)) | |
}) | |
return promise | |
} | |
/** | |
* Takes function or class, returns its name. | |
* Because IE doesn’t support `constructor.name`. | |
* https://gist.github.com/dfkaye/6384439, http://stackoverflow.com/a/15714445 | |
* | |
* @param {Object} fn — function | |
* | |
*/ | |
// function getFnName (fn) { | |
// var f = typeof fn === 'function' | |
// var s = f && ((fn.name && ['', fn.name]) || fn.toString().match(/function ([^\(]+)/)) | |
// return (!f && 'not a function') || (s && s[1] || 'anonymous') | |
// } | |
function isPreviewSupported (fileTypeSpecific) { | |
// list of images that browsers can preview | |
if (/^(jpeg|gif|png|svg|svg\+xml|bmp)$/.test(fileTypeSpecific)) { | |
return true | |
} | |
return false | |
} | |
function getArrayBuffer (chunk) { | |
return new Promise(function (resolve, reject) { | |
var reader = new FileReader() | |
reader.addEventListener('load', function (e) { | |
// e.target.result is an ArrayBuffer | |
resolve(e.target.result) | |
}) | |
reader.addEventListener('error', function (err) { | |
console.error('FileReader error' + err) | |
reject(err) | |
}) | |
// file-type only needs the first 4100 bytes | |
reader.readAsArrayBuffer(chunk) | |
}) | |
} | |
function getFileType (file) { | |
const emptyFileType = ['', ''] | |
const extensionsToMime = { | |
'md': 'text/markdown', | |
'markdown': 'text/markdown', | |
'mp4': 'video/mp4', | |
'mp3': 'audio/mp3' | |
} | |
// no smart detection for remote files, just trust the provider | |
if (file.isRemote) { | |
return Promise.resolve(file.type.split('/')) | |
} | |
const fileExtension = getFileNameAndExtension(file.name)[1] | |
// 1. try to determine file type from magic bytes with file-type module | |
// this should be the most trustworthy way | |
const chunk = file.data.slice(0, 4100) | |
return getArrayBuffer(chunk) | |
.then((buffer) => { | |
const type = _$fileType_138(buffer) | |
if (type && type.mime) { | |
return type.mime.split('/') | |
} | |
// 2. if that’s no good, check if mime type is set in the file object | |
if (file.type) { | |
return file.type.split('/') | |
} | |
// 3. if that’s no good, see if we can map extension to a mime type | |
if (extensionsToMime[fileExtension]) { | |
return extensionsToMime[fileExtension].split('/') | |
} | |
// if all fails, well, return empty | |
return emptyFileType | |
}) | |
.catch(() => { | |
return emptyFileType | |
}) | |
// if (file.type) { | |
// return Promise.resolve(file.type.split('/')) | |
// } | |
// return mime.lookup(file.name) | |
// return file.type ? file.type.split('/') : ['', ''] | |
} | |
// TODO Check which types are actually supported in browsers. Chrome likes webm | |
// from my testing, but we may need more. | |
// We could use a library but they tend to contain dozens of KBs of mappings, | |
// most of which will go unused, so not sure if that's worth it. | |
const mimeToExtensions = { | |
'video/ogg': 'ogv', | |
'audio/ogg': 'ogg', | |
'video/webm': 'webm', | |
'audio/webm': 'webm', | |
'video/mp4': 'mp4', | |
'audio/mp3': 'mp3' | |
} | |
function getFileTypeExtension (mimeType) { | |
return mimeToExtensions[mimeType] || null | |
} | |
// returns [fileName, fileExt] | |
function getFileNameAndExtension (fullFileName) { | |
var re = /(?:\.([^.]+))?$/ | |
var fileExt = re.exec(fullFileName)[1] | |
var fileName = fullFileName.replace('.' + fileExt, '') | |
return [fileName, fileExt] | |
} | |
function supportsMediaRecorder () { | |
return typeof MediaRecorder === 'function' && !!MediaRecorder.prototype && | |
typeof MediaRecorder.prototype.start === 'function' | |
} | |
/** | |
* Check if a URL string is an object URL from `URL.createObjectURL`. | |
* | |
* @param {string} url | |
* @return {boolean} | |
*/ | |
function isObjectURL (url) { | |
return url.indexOf('blob:') === 0 | |
} | |
function getProportionalHeight (img, width) { | |
const aspect = img.width / img.height | |
return Math.round(width / aspect) | |
} | |
/** | |
* Create a thumbnail for the given Uppy file object. | |
* | |
* @param {{data: Blob}} file | |
* @param {number} width | |
* @return {Promise} | |
*/ | |
function createThumbnail (file, targetWidth) { | |
const originalUrl = URL.createObjectURL(file.data) | |
const onload = new Promise((resolve, reject) => { | |
const image = new Image() | |
image.src = originalUrl | |
image.onload = () => { | |
URL.revokeObjectURL(originalUrl) | |
resolve(image) | |
} | |
image.onerror = () => { | |
// The onerror event is totally useless unfortunately, as far as I know | |
URL.revokeObjectURL(originalUrl) | |
reject(new Error('Could not create thumbnail')) | |
} | |
}) | |
return onload.then((image) => { | |
const targetHeight = getProportionalHeight(image, targetWidth) | |
let sourceWidth = image.width | |
let sourceHeight = image.height | |
if (targetHeight < image.height / 2) { | |
const steps = Math.floor(Math.log(image.width / targetWidth) / Math.log(2)) | |
;({ | |
image, | |
sourceWidth, | |
sourceHeight | |
} = downScaleInSteps(image, steps)) | |
} | |
const canvas = document.createElement('canvas') | |
canvas.width = targetWidth | |
canvas.height = targetHeight | |
const context = canvas.getContext('2d') | |
context.drawImage(image, | |
0, 0, sourceWidth, sourceHeight, | |
0, 0, targetWidth, targetHeight) | |
return canvasToBlob(canvas) | |
}).then((blob) => { | |
return URL.createObjectURL(blob) | |
}) | |
} | |
/** | |
* Downscale an image by 50% `steps` times. | |
*/ | |
function downScaleInSteps (image, steps) { | |
let source = image | |
let currentWidth = source.width | |
let currentHeight = source.height | |
const canvas = document.createElement('canvas') | |
const context = canvas.getContext('2d') | |
canvas.width = currentWidth / 2 | |
canvas.height = currentHeight / 2 | |
for (let i = 0; i < steps; i += 1) { | |
context.drawImage(source, | |
// The entire source image. We pass width and height here, | |
// because we reuse this canvas, and should only scale down | |
// the part of the canvas that contains the previous scale step. | |
0, 0, currentWidth, currentHeight, | |
// Draw to 50% size | |
0, 0, currentWidth / 2, currentHeight / 2) | |
currentWidth /= 2 | |
currentHeight /= 2 | |
source = canvas | |
} | |
return { | |
image: canvas, | |
sourceWidth: currentWidth, | |
sourceHeight: currentHeight | |
} | |
} | |
/** | |
* Save a <canvas> element's content to a Blob object. | |
* | |
* @param {HTMLCanvasElement} canvas | |
* @return {Promise} | |
*/ | |
function canvasToBlob (canvas) { | |
if (canvas.toBlob) { | |
return new Promise((resolve) => { | |
canvas.toBlob(resolve) | |
}) | |
} | |
return Promise.resolve().then(() => { | |
return dataURItoBlob(canvas.toDataURL(), {}) | |
}) | |
} | |
function dataURItoBlob (dataURI, opts, toFile) { | |
// get the base64 data | |
var data = dataURI.split(',')[1] | |
// user may provide mime type, if not get it from data URI | |
var mimeType = opts.mimeType || dataURI.split(',')[0].split(':')[1].split(';')[0] | |
// default to plain/text if data URI has no mimeType | |
if (mimeType == null) { | |
mimeType = 'plain/text' | |
} | |
var binary = atob(data) | |
var array = [] | |
for (var i = 0; i < binary.length; i++) { | |
array.push(binary.charCodeAt(i)) | |
} | |
// Convert to a File? | |
if (toFile) { | |
return new File([new Uint8Array(array)], opts.name || '', {type: mimeType}) | |
} | |
return new Blob([new Uint8Array(array)], {type: mimeType}) | |
} | |
function dataURItoFile (dataURI, opts) { | |
return dataURItoBlob(dataURI, opts, true) | |
} | |
/** | |
* Copies text to clipboard by creating an almost invisible textarea, | |
* adding text there, then running execCommand('copy'). | |
* Falls back to prompt() when the easy way fails (hello, Safari!) | |
* From http://stackoverflow.com/a/30810322 | |
* | |
* @param {String} textToCopy | |
* @param {String} fallbackString | |
* @return {Promise} | |
*/ | |
function copyToClipboard (textToCopy, fallbackString) { | |
fallbackString = fallbackString || 'Copy the URL below' | |
return new Promise((resolve, reject) => { | |
const textArea = document.createElement('textarea') | |
textArea.setAttribute('style', { | |
position: 'fixed', | |
top: 0, | |
left: 0, | |
width: '2em', | |
height: '2em', | |
padding: 0, | |
border: 'none', | |
outline: 'none', | |
boxShadow: 'none', | |
background: 'transparent' | |
}) | |
textArea.value = textToCopy | |
document.body.appendChild(textArea) | |
textArea.select() | |
const magicCopyFailed = (err) => { | |
document.body.removeChild(textArea) | |
window.prompt(fallbackString, textToCopy) | |
return reject('Oops, unable to copy displayed fallback prompt: ' + err) | |
} | |
try { | |
const successful = document.execCommand('copy') | |
if (!successful) { | |
return magicCopyFailed('copy command unavailable') | |
} | |
document.body.removeChild(textArea) | |
return resolve() | |
} catch (err) { | |
document.body.removeChild(textArea) | |
return magicCopyFailed(err) | |
} | |
}) | |
} | |
function getSpeed (fileProgress) { | |
if (!fileProgress.bytesUploaded) return 0 | |
const timeElapsed = (new Date()) - fileProgress.uploadStarted | |
const uploadSpeed = fileProgress.bytesUploaded / (timeElapsed / 1000) | |
return uploadSpeed | |
} | |
function getBytesRemaining (fileProgress) { | |
return fileProgress.bytesTotal - fileProgress.bytesUploaded | |
} | |
function getETA (fileProgress) { | |
if (!fileProgress.bytesUploaded) return 0 | |
const uploadSpeed = getSpeed(fileProgress) | |
const bytesRemaining = getBytesRemaining(fileProgress) | |
const secondsRemaining = Math.round(bytesRemaining / uploadSpeed * 10) / 10 | |
return secondsRemaining | |
} | |
function prettyETA (seconds) { | |
const time = secondsToTime(seconds) | |
// Only display hours and minutes if they are greater than 0 but always | |
// display minutes if hours is being displayed | |
// Display a leading zero if the there is a preceding unit: 1m 05s, but 5s | |
const hoursStr = time.hours ? time.hours + 'h ' : '' | |
const minutesVal = time.hours ? ('0' + time.minutes).substr(-2) : time.minutes | |
const minutesStr = minutesVal ? minutesVal + 'm ' : '' | |
const secondsVal = minutesVal ? ('0' + time.seconds).substr(-2) : time.seconds | |
const secondsStr = secondsVal + 's' | |
return `${hoursStr}${minutesStr}${secondsStr}` | |
} | |
/** | |
* Check if an object is a DOM element. Duck-typing based on `nodeType`. | |
* | |
* @param {*} obj | |
*/ | |
function isDOMElement (obj) { | |
return obj && typeof obj === 'object' && obj.nodeType === Node.ELEMENT_NODE | |
} | |
/** | |
* Find a DOM element. | |
* | |
* @param {Node|string} element | |
* @return {Node|null} | |
*/ | |
function findDOMElement (element) { | |
if (typeof element === 'string') { | |
return document.querySelector(element) | |
} | |
if (typeof element === 'object' && isDOMElement(element)) { | |
return element | |
} | |
} | |
function getSocketHost (url) { | |
// get the host domain | |
var regex = /^(?:https?:\/\/|\/\/)?(?:[^@\n]+@)?(?:www\.)?([^\n]+)/ | |
var host = regex.exec(url)[1] | |
var socketProtocol = location.protocol === 'https:' ? 'wss' : 'ws' | |
return `${socketProtocol}://${host}` | |
} | |
function _emitSocketProgress (uploader, progressData, file) { | |
const {progress, bytesUploaded, bytesTotal} = progressData | |
if (progress) { | |
uploader.core.log(`Upload progress: ${progress}`) | |
uploader.core.emitter.emit('core:upload-progress', { | |
uploader, | |
id: file.id, | |
bytesUploaded: bytesUploaded, | |
bytesTotal: bytesTotal | |
}) | |
} | |
} | |
const emitSocketProgress = _$throttle_44(_emitSocketProgress, 300, {leading: true, trailing: true}) | |
var _$Utils_85 = { | |
generateFileID, | |
toArray: __toArray_85, | |
/* common-shake removed: every */ | |
/* common-shake removed: flatten */ | |
/* common-shake removed: groupBy */ | |
extend, | |
/* common-shake removed: runPromiseSequence */ | |
supportsMediaRecorder, | |
isTouchDevice, | |
getFileNameAndExtension, | |
truncateString, | |
getFileTypeExtension, | |
getFileType, | |
/* common-shake removed: getArrayBuffer */ | |
isPreviewSupported, | |
isObjectURL, | |
createThumbnail, | |
/* common-shake removed: secondsToTime */ | |
/* common-shake removed: dataURItoBlob */ | |
/* common-shake removed: dataURItoFile */ | |
getSpeed, | |
getBytesRemaining, | |
getETA, | |
copyToClipboard, | |
prettyETA, | |
findDOMElement, | |
getSocketHost, | |
emitSocketProgress | |
} | |
var _$Core_82 = {}; | |
(function (global){ | |
/* removed: const _$Utils_85 = require('../core/Utils') */; | |
/* removed: const _$Translator_83 = require('../core/Translator') */; | |
/* removed: const _$UppySocket_84 = require('./UppySocket') */; | |
/* removed: const _$createNamespaceEmitter_48 = require('namespace-emitter') */; | |
/* removed: const _$browserCuid_16 = require('cuid') */; | |
/* removed: const _$throttle_44 = require('lodash.throttle') */; | |
/* removed: const _$prettierBytes_54 = require('prettier-bytes') */; | |
/* removed: const _$mimeMatch_45 = require('mime-match') */; | |
// const en_US = require('../locales/en_US') | |
// const deepFreeze = require('deep-freeze-strict') | |
/** | |
* Main Uppy core | |
* | |
* @param {object} opts general options, like locales, to show modal or not to show | |
*/ | |
class Uppy { | |
constructor (opts) { | |
const defaultLocale = { | |
strings: { | |
youCanOnlyUploadX: { | |
0: 'You can only upload %{smart_count} file', | |
1: 'You can only upload %{smart_count} files' | |
}, | |
youHaveToAtLeastSelectX: { | |
0: 'You have to select at least %{smart_count} file', | |
1: 'You have to select at least %{smart_count} files' | |
}, | |
exceedsSize: 'This file exceeds maximum allowed size of', | |
youCanOnlyUploadFileTypes: 'You can only upload:', | |
uppyServerError: 'Connection with Uppy server failed' | |
} | |
} | |
// set default options | |
const defaultOptions = { | |
autoProceed: true, | |
debug: false, | |
restrictions: { | |
maxFileSize: false, | |
maxNumberOfFiles: false, | |
minNumberOfFiles: false, | |
allowedFileTypes: false | |
}, | |
onBeforeFileAdded: (currentFile, files) => Promise.resolve(), | |
onBeforeUpload: (files, done) => Promise.resolve(), | |
locale: defaultLocale | |
} | |
// Merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
// // Dictates in what order different plugin types are ran: | |
// this.types = [ 'presetter', 'orchestrator', 'progressindicator', | |
// 'acquirer', 'modifier', 'uploader', 'presenter', 'debugger'] | |
this.locale = Object.assign({}, defaultLocale, this.opts.locale) | |
this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings) | |
// i18n | |
this.translator = new _$Translator_83({locale: this.locale}) | |
this.i18n = this.translator.translate.bind(this.translator) | |
// Container for different types of plugins | |
this.plugins = {} | |
// @TODO maybe bindall | |
this.translator = new _$Translator_83({locale: this.opts.locale}) | |
this.i18n = this.translator.translate.bind(this.translator) | |
this.getState = this.getState.bind(this) | |
this.updateMeta = this.updateMeta.bind(this) | |
this.initSocket = this.initSocket.bind(this) | |
this.log = this.log.bind(this) | |
this.addFile = this.addFile.bind(this) | |
this.calculateProgress = this.calculateProgress.bind(this) | |
this.resetProgress = this.resetProgress.bind(this) | |
// this.bus = this.emitter = ee() | |
this.emitter = _$createNamespaceEmitter_48() | |
this.on = this.emitter.on.bind(this.emitter) | |
this.off = this.emitter.off.bind(this.emitter) | |
this.once = this.emitter.once.bind(this.emitter) | |
this.emit = this.emitter.emit.bind(this.emitter) | |
this.preProcessors = [] | |
this.uploaders = [] | |
this.postProcessors = [] | |
this.state = { | |
files: {}, | |
capabilities: { | |
resumableUploads: false | |
}, | |
totalProgress: 0, | |
meta: Object.assign({}, this.opts.meta), | |
info: { | |
isHidden: true, | |
type: '', | |
msg: '' | |
} | |
} | |
// for debugging and testing | |
this.updateNum = 0 | |
if (this.opts.debug) { | |
global.UppyState = this.state | |
global.uppyLog = '' | |
// global.UppyAddFile = this.addFile.bind(this) | |
global._uppy = this | |
} | |
} | |
/** | |
* Iterate on all plugins and run `update` on them. Called each time state changes | |
* | |
*/ | |
updateAll (state) { | |
Object.keys(this.plugins).forEach((pluginType) => { | |
this.plugins[pluginType].forEach((plugin) => { | |
plugin.update(state) | |
}) | |
}) | |
} | |
/** | |
* Updates state | |
* | |
* @param {newState} object | |
*/ | |
setState (stateUpdate) { | |
const newState = Object.assign({}, this.state, stateUpdate) | |
this.emit('core:state-update', this.state, newState, stateUpdate) | |
this.state = newState | |
this.updateAll(this.state) | |
} | |
/** | |
* Returns current state | |
* | |
*/ | |
getState () { | |
// use deepFreeze for debugging | |
// return deepFreeze(this.state) | |
return this.state | |
} | |
reset () { | |
this.emit('core:pause-all') | |
this.emit('core:cancel-all') | |
this.setState({ | |
totalProgress: 0 | |
}) | |
} | |
resetProgress () { | |
const defaultProgress = { | |
percentage: 0, | |
bytesUploaded: 0, | |
uploadComplete: false, | |
uploadStarted: false | |
} | |
const files = Object.assign({}, this.state.files) | |
const updatedFiles = {} | |
Object.keys(files).forEach(fileID => { | |
const updatedFile = Object.assign({}, files[fileID]) | |
updatedFile.progress = Object.assign({}, updatedFile.progress, defaultProgress) | |
updatedFiles[fileID] = updatedFile | |
}) | |
console.log(updatedFiles) | |
this.setState({ | |
files: updatedFiles, | |
totalProgress: 0 | |
}) | |
} | |
addPreProcessor (fn) { | |
this.preProcessors.push(fn) | |
} | |
removePreProcessor (fn) { | |
const i = this.preProcessors.indexOf(fn) | |
if (i !== -1) { | |
this.preProcessors.splice(i, 1) | |
} | |
} | |
addPostProcessor (fn) { | |
this.postProcessors.push(fn) | |
} | |
removePostProcessor (fn) { | |
const i = this.postProcessors.indexOf(fn) | |
if (i !== -1) { | |
this.postProcessors.splice(i, 1) | |
} | |
} | |
addUploader (fn) { | |
this.uploaders.push(fn) | |
} | |
removeUploader (fn) { | |
const i = this.uploaders.indexOf(fn) | |
if (i !== -1) { | |
this.uploaders.splice(i, 1) | |
} | |
} | |
setMeta (data) { | |
const newMeta = Object.assign({}, this.getState().meta, data) | |
this.log('Adding metadata:') | |
this.log(data) | |
this.setState({meta: newMeta}) | |
} | |
updateMeta (data, fileID) { | |
const updatedFiles = Object.assign({}, this.getState().files) | |
const newMeta = Object.assign({}, updatedFiles[fileID].meta, data) | |
updatedFiles[fileID] = Object.assign({}, updatedFiles[fileID], { | |
meta: newMeta | |
}) | |
this.setState({files: updatedFiles}) | |
} | |
checkRestrictions (checkMinNumberOfFiles, file, fileType) { | |
const {maxFileSize, maxNumberOfFiles, minNumberOfFiles, allowedFileTypes} = this.opts.restrictions | |
if (checkMinNumberOfFiles && minNumberOfFiles) { | |
if (Object.keys(this.state.files).length < minNumberOfFiles) { | |
this.info(`${this.i18n('youHaveToAtLeastSelectX', {smart_count: minNumberOfFiles})}`, 'error', 5000) | |
return false | |
} | |
return true | |
} | |
if (maxNumberOfFiles) { | |
if (Object.keys(this.state.files).length + 1 > maxNumberOfFiles) { | |
this.info(`${this.i18n('youCanOnlyUploadX', {smart_count: maxNumberOfFiles})}`, 'error', 5000) | |
return false | |
} | |
} | |
if (allowedFileTypes) { | |
const isCorrectFileType = allowedFileTypes.filter(_$mimeMatch_45(fileType.join('/'))).length > 0 | |
if (!isCorrectFileType) { | |
const allowedFileTypesString = allowedFileTypes.join(', ') | |
this.info(`${this.i18n('youCanOnlyUploadFileTypes')} ${allowedFileTypesString}`, 'error', 5000) | |
return false | |
} | |
} | |
if (maxFileSize) { | |
if (file.data.size > maxFileSize) { | |
this.info(`${this.i18n('exceedsSize')} ${_$prettierBytes_54(maxFileSize)}`, 'error', 5000) | |
return false | |
} | |
} | |
return true | |
} | |
addFile (file) { | |
return this.opts.onBeforeFileAdded(file, this.getState().files).catch((err) => { | |
this.info(err, 'error', 5000) | |
return Promise.reject(`onBeforeFileAdded: ${err}`) | |
}).then(() => { | |
return _$Utils_85.getFileType(file).then((fileType) => { | |
const updatedFiles = Object.assign({}, this.state.files) | |
const fileName = file.name || 'noname' | |
const fileExtension = _$Utils_85.getFileNameAndExtension(fileName)[1] | |
const isRemote = file.isRemote || false | |
const fileID = _$Utils_85.generateFileID(file) | |
const fileTypeGeneral = fileType[0] | |
const fileTypeSpecific = fileType[1] | |
const newFile = { | |
source: file.source || '', | |
id: fileID, | |
name: fileName, | |
extension: fileExtension || '', | |
meta: Object.assign({}, { name: fileName }, this.getState().meta), | |
type: { | |
general: fileTypeGeneral, | |
specific: fileTypeSpecific | |
}, | |
data: file.data, | |
progress: { | |
percentage: 0, | |
bytesUploaded: 0, | |
bytesTotal: file.data.size || 0, | |
uploadComplete: false, | |
uploadStarted: false | |
}, | |
size: file.data.size || 'N/A', | |
isRemote: isRemote, | |
remote: file.remote || '', | |
preview: file.preview | |
} | |
if (_$Utils_85.isPreviewSupported(fileTypeSpecific) && !isRemote) { | |
console.time(`createThumbnail ${file.id}`) | |
_$Utils_85.createThumbnail(file, 200).then((thumbnail) => { | |
console.timeEnd(`createThumbnail ${file.id}`) | |
this.setPreviewURL(fileID, thumbnail) | |
}).catch((err) => { | |
console.warn(err.stack || err.message) | |
}) | |
} | |
const isFileAllowed = this.checkRestrictions(false, newFile, fileType) | |
if (!isFileAllowed) return Promise.reject('File not allowed') | |
updatedFiles[fileID] = newFile | |
this.setState({files: updatedFiles}) | |
this.emit('core:file-added', newFile) | |
this.log(`Added file: ${fileName}, ${fileID}, mime type: ${fileType}`) | |
if (this.opts.autoProceed && !this.scheduledAutoProceed) { | |
this.scheduledAutoProceed = setTimeout(() => { | |
this.scheduledAutoProceed = null | |
this.upload().catch((err) => { | |
console.error(err.stack || err.message || err) | |
}) | |
}, 4) | |
} | |
}) | |
}) | |
} | |
/** | |
* Get a file object. | |
* | |
* @param {string} fileID The ID of the file object to return. | |
*/ | |
getFile (fileID) { | |
return this.getState().files[fileID] | |
} | |
/** | |
* Set the preview URL for a file. | |
*/ | |
setPreviewURL (fileID, preview) { | |
const { files } = this.state | |
this.setState({ | |
files: Object.assign({}, files, { | |
[fileID]: Object.assign({}, files[fileID], { | |
preview: preview | |
}) | |
}) | |
}) | |
} | |
removeFile (fileID) { | |
const updatedFiles = Object.assign({}, this.getState().files) | |
const removedFile = updatedFiles[fileID] | |
delete updatedFiles[fileID] | |
this.setState({files: updatedFiles}) | |
this.calculateTotalProgress() | |
this.emit('core:file-removed', fileID) | |
// Clean up object URLs. | |
if (removedFile.preview && _$Utils_85.isObjectURL(removedFile.preview)) { | |
URL.revokeObjectURL(removedFile.preview) | |
} | |
this.log(`Removed file: ${fileID}`) | |
} | |
calculateProgress (data) { | |
const fileID = data.id | |
const updatedFiles = Object.assign({}, this.getState().files) | |
// skip progress event for a file that’s been removed | |
if (!updatedFiles[fileID]) { | |
this.log('Trying to set progress for a file that’s not with us anymore: ', fileID) | |
return | |
} | |
const updatedFile = Object.assign({}, updatedFiles[fileID], | |
Object.assign({}, { | |
progress: Object.assign({}, updatedFiles[fileID].progress, { | |
bytesUploaded: data.bytesUploaded, | |
bytesTotal: data.bytesTotal, | |
percentage: Math.floor((data.bytesUploaded / data.bytesTotal * 100).toFixed(2)) | |
}) | |
} | |
)) | |
updatedFiles[data.id] = updatedFile | |
this.setState({ | |
files: updatedFiles | |
}) | |
this.calculateTotalProgress() | |
} | |
calculateTotalProgress () { | |
// calculate total progress, using the number of files currently uploading, | |
// multiplied by 100 and the summ of individual progress of each file | |
const files = Object.assign({}, this.getState().files) | |
const inProgress = Object.keys(files).filter((file) => { | |
return files[file].progress.uploadStarted | |
}) | |
const progressMax = inProgress.length * 100 | |
let progressAll = 0 | |
inProgress.forEach((file) => { | |
progressAll = progressAll + files[file].progress.percentage | |
}) | |
const totalProgress = Math.floor((progressAll * 100 / progressMax).toFixed(2)) | |
this.setState({ | |
totalProgress: totalProgress | |
}) | |
} | |
/** | |
* Registers listeners for all global actions, like: | |
* `file-add`, `file-remove`, `upload-progress`, `reset` | |
* | |
*/ | |
actions () { | |
// this.bus.on('*', (payload) => { | |
// console.log('emitted: ', this.event) | |
// console.log('with payload: ', payload) | |
// }) | |
// stress-test re-rendering | |
// setInterval(() => { | |
// this.setState({bla: 'bla'}) | |
// }, 20) | |
this.on('core:error', (error) => { | |
this.setState({ error }) | |
}) | |
this.on('core:upload', () => { | |
this.setState({ error: null }) | |
}) | |
this.on('core:file-add', (data) => { | |
this.addFile(data) | |
}) | |
// `remove-file` removes a file from `state.files`, for example when | |
// a user decides not to upload particular file and clicks a button to remove it | |
this.on('core:file-remove', (fileID) => { | |
this.removeFile(fileID) | |
}) | |
this.on('core:cancel-all', () => { | |
// let updatedFiles = this.getState().files | |
// updatedFiles = {} | |
this.setState({files: {}}) | |
}) | |
this.on('core:upload-started', (fileID, upload) => { | |
const updatedFiles = Object.assign({}, this.getState().files) | |
const updatedFile = Object.assign({}, updatedFiles[fileID], | |
Object.assign({}, { | |
progress: Object.assign({}, updatedFiles[fileID].progress, { | |
uploadStarted: Date.now() | |
}) | |
} | |
)) | |
updatedFiles[fileID] = updatedFile | |
this.setState({files: updatedFiles}) | |
}) | |
// upload progress events can occur frequently, especially when you have a good | |
// connection to the remote server. Therefore, we are throtteling them to | |
// prevent accessive function calls. | |
// see also: https://github.com/tus/tus-js-client/commit/9940f27b2361fd7e10ba58b09b60d82422183bbb | |
const throttledCalculateProgress = _$throttle_44(this.calculateProgress, 100, {leading: true, trailing: false}) | |
this.on('core:upload-progress', (data) => { | |
// this.calculateProgress(data) | |
throttledCalculateProgress(data) | |
}) | |
this.on('core:upload-success', (fileID, uploadResp, uploadURL) => { | |
const updatedFiles = Object.assign({}, this.getState().files) | |
const updatedFile = Object.assign({}, updatedFiles[fileID], { | |
progress: Object.assign({}, updatedFiles[fileID].progress, { | |
uploadComplete: true, | |
// good or bad idea? setting the percentage to 100 if upload is successful, | |
// so that if we lost some progress events on the way, its still marked “compete”? | |
percentage: 100 | |
}), | |
uploadURL: uploadURL | |
}) | |
updatedFiles[fileID] = updatedFile | |
this.setState({ | |
files: updatedFiles | |
}) | |
this.calculateTotalProgress() | |
if (this.getState().totalProgress === 100) { | |
const completeFiles = Object.keys(updatedFiles).filter((file) => { | |
return updatedFiles[file].progress.uploadComplete | |
}) | |
this.emit('core:upload-complete', completeFiles.length) | |
} | |
}) | |
this.on('core:update-meta', (data, fileID) => { | |
this.updateMeta(data, fileID) | |
}) | |
this.on('core:preprocess-progress', (fileID, progress) => { | |
const files = Object.assign({}, this.getState().files) | |
files[fileID] = Object.assign({}, files[fileID], { | |
progress: Object.assign({}, files[fileID].progress, { | |
preprocess: progress | |
}) | |
}) | |
this.setState({ files: files }) | |
}) | |
this.on('core:preprocess-complete', (fileID) => { | |
const files = Object.assign({}, this.getState().files) | |
files[fileID] = Object.assign({}, files[fileID], { | |
progress: Object.assign({}, files[fileID].progress) | |
}) | |
delete files[fileID].progress.preprocess | |
this.setState({ files: files }) | |
}) | |
this.on('core:postprocess-progress', (fileID, progress) => { | |
const files = Object.assign({}, this.getState().files) | |
files[fileID] = Object.assign({}, files[fileID], { | |
progress: Object.assign({}, files[fileID].progress, { | |
postprocess: progress | |
}) | |
}) | |
this.setState({ files: files }) | |
}) | |
this.on('core:postprocess-complete', (fileID) => { | |
const files = Object.assign({}, this.getState().files) | |
files[fileID] = Object.assign({}, files[fileID], { | |
progress: Object.assign({}, files[fileID].progress) | |
}) | |
delete files[fileID].progress.postprocess | |
// TODO should we set some kind of `fullyComplete` property on the file object | |
// so it's easier to see that the file is upload…fully complete…rather than | |
// what we have to do now (`uploadComplete && !postprocess`) | |
this.setState({ files: files }) | |
}) | |
// show informer if offline | |
if (typeof window !== 'undefined') { | |
window.addEventListener('online', () => this.isOnline(true)) | |
window.addEventListener('offline', () => this.isOnline(false)) | |
setTimeout(() => this.isOnline(), 3000) | |
} | |
} | |
isOnline (status) { | |
const online = status || window.navigator.onLine | |
if (!online) { | |
this.emit('is-offline') | |
this.info('No internet connection', 'error', 0) | |
this.wasOffline = true | |
} else { | |
this.emit('is-online') | |
if (this.wasOffline) { | |
this.emit('back-online') | |
this.info('Connected!', 'success', 3000) | |
this.wasOffline = false | |
} | |
} | |
} | |
/** | |
* Registers a plugin with Core | |
* | |
* @param {Class} Plugin object | |
* @param {Object} options object that will be passed to Plugin later | |
* @return {Object} self for chaining | |
*/ | |
use (Plugin, opts) { | |
// Instantiate | |
const plugin = new Plugin(this, opts) | |
const pluginName = plugin.id | |
this.plugins[plugin.type] = this.plugins[plugin.type] || [] | |
if (!pluginName) { | |
throw new Error('Your plugin must have a name') | |
} | |
if (!plugin.type) { | |
throw new Error('Your plugin must have a type') | |
} | |
let existsPluginAlready = this.getPlugin(pluginName) | |
if (existsPluginAlready) { | |
let msg = `Already found a plugin named '${existsPluginAlready.name}'. | |
Tried to use: '${pluginName}'. | |
Uppy is currently limited to running one of every plugin. | |
Share your use case with us over at | |
https://github.com/transloadit/uppy/issues/ | |
if you want us to reconsider.` | |
throw new Error(msg) | |
} | |
this.plugins[plugin.type].push(plugin) | |
plugin.install() | |
return this | |
} | |
/** | |
* Find one Plugin by name | |
* | |
* @param string name description | |
*/ | |
getPlugin (name) { | |
let foundPlugin = false | |
this.iteratePlugins((plugin) => { | |
const pluginName = plugin.id | |
if (pluginName === name) { | |
foundPlugin = plugin | |
return false | |
} | |
}) | |
return foundPlugin | |
} | |
/** | |
* Iterate through all `use`d plugins | |
* | |
* @param function method description | |
*/ | |
iteratePlugins (method) { | |
Object.keys(this.plugins).forEach((pluginType) => { | |
this.plugins[pluginType].forEach(method) | |
}) | |
} | |
/** | |
* Uninstall and remove a plugin. | |
* | |
* @param {Plugin} instance The plugin instance to remove. | |
*/ | |
removePlugin (instance) { | |
const list = this.plugins[instance.type] | |
if (instance.uninstall) { | |
instance.uninstall() | |
} | |
const index = list.indexOf(instance) | |
if (index !== -1) { | |
list.splice(index, 1) | |
} | |
} | |
/** | |
* Uninstall all plugins and close down this Uppy instance. | |
*/ | |
close () { | |
this.reset() | |
this.iteratePlugins((plugin) => { | |
plugin.uninstall() | |
}) | |
if (this.socket) { | |
this.socket.close() | |
} | |
} | |
/** | |
* Set info message in `state.info`, so that UI plugins like `Informer` | |
* can display the message | |
* | |
* @param {string} msg Message to be displayed by the informer | |
*/ | |
info (msg, type, duration) { | |
this.setState({ | |
info: { | |
isHidden: false, | |
type: type, | |
msg: msg | |
} | |
}) | |
this.emit('core:info-visible') | |
window.clearTimeout(this.infoTimeoutID) | |
if (duration === 0) { | |
this.infoTimeoutID = undefined | |
return | |
} | |
// hide the informer after `duration` milliseconds | |
this.infoTimeoutID = setTimeout(() => { | |
const newInformer = Object.assign({}, this.state.info, { | |
isHidden: true | |
}) | |
this.setState({ | |
info: newInformer | |
}) | |
this.emit('core:info-hidden') | |
}, duration) | |
} | |
hideInfo () { | |
const newInfo = Object.assign({}, this.core.state.info, { | |
isHidden: true | |
}) | |
this.setState({ | |
info: newInfo | |
}) | |
this.emit('core:info-hidden') | |
} | |
/** | |
* Logs stuff to console, only if `debug` is set to true. Silent in production. | |
* | |
* @return {String|Object} to log | |
*/ | |
log (msg, type) { | |
if (!this.opts.debug) { | |
return | |
} | |
if (type === 'error') { | |
console.error(`LOG: ${msg}`) | |
return | |
} | |
if (msg === `${msg}`) { | |
console.log(`LOG: ${msg}`) | |
} else { | |
console.dir(msg) | |
} | |
global.uppyLog = global.uppyLog + '\n' + 'DEBUG LOG: ' + msg | |
} | |
initSocket (opts) { | |
if (!this.socket) { | |
this.socket = new _$UppySocket_84(opts) | |
} | |
return this.socket | |
} | |
/** | |
* Initializes actions, installs all plugins (by iterating on them and calling `install`), sets options | |
* | |
*/ | |
run () { | |
this.log('Core is run, initializing actions...') | |
this.actions() | |
// Forse set `autoProceed` option to false if there are multiple selector Plugins active | |
// if (this.plugins.acquirer && this.plugins.acquirer.length > 1) { | |
// this.opts.autoProceed = false | |
// } | |
// Install all plugins | |
// this.installAll() | |
return this | |
} | |
/** | |
* Restore an upload by its ID. | |
*/ | |
restore (uploadID) { | |
this.log(`Core: attempting to restore upload "${uploadID}"`) | |
if (!this.state.currentUploads[uploadID]) { | |
this.removeUpload(uploadID) | |
return Promise.reject(new Error('Nonexistent upload')) | |
} | |
return this.runUpload(uploadID) | |
} | |
/** | |
* Create an upload for a bunch of files. | |
* | |
* @param {Array<string>} fileIDs File IDs to include in this upload. | |
* @return {string} ID of this upload. | |
*/ | |
createUpload (fileIDs) { | |
const uploadID = _$browserCuid_16() | |
this.emit('core:upload', { | |
id: uploadID, | |
fileIDs: fileIDs | |
}) | |
this.setState({ | |
currentUploads: Object.assign({}, this.state.currentUploads, { | |
[uploadID]: { | |
fileIDs: fileIDs, | |
step: 0 | |
} | |
}) | |
}) | |
return uploadID | |
} | |
/** | |
* Remove an upload, eg. if it has been canceled or completed. | |
* | |
* @param {string} uploadID The ID of the upload. | |
*/ | |
removeUpload (uploadID) { | |
const currentUploads = Object.assign({}, this.state.currentUploads) | |
delete currentUploads[uploadID] | |
this.setState({ | |
currentUploads: currentUploads | |
}) | |
} | |
/** | |
* Run an upload. This picks up where it left off in case the upload is being restored. | |
* | |
* @private | |
*/ | |
runUpload (uploadID) { | |
const uploadData = this.state.currentUploads[uploadID] | |
const fileIDs = uploadData.fileIDs | |
const restoreStep = uploadData.step | |
const steps = [ | |
...this.preProcessors, | |
...this.uploaders, | |
...this.postProcessors | |
] | |
let lastStep = Promise.resolve() | |
steps.forEach((fn, step) => { | |
// Skip this step if we are restoring and have already completed this step before. | |
if (step < restoreStep) { | |
return | |
} | |
lastStep = lastStep.then(() => { | |
const currentUpload = Object.assign({}, this.state.currentUploads[uploadID], { | |
step: step | |
}) | |
this.setState({ | |
currentUploads: Object.assign({}, this.state.currentUploads, { | |
[uploadID]: currentUpload | |
}) | |
}) | |
return fn(fileIDs) | |
}) | |
}) | |
// Not returning the `catch`ed promise, because we still want to return a rejected | |
// promise from this method if the upload failed. | |
lastStep.catch((err) => { | |
this.emit('core:error', err) | |
this.removeUpload(uploadID) | |
}) | |
return lastStep.then(() => { | |
// return number of uploaded files | |
this.emit('core:success', fileIDs) | |
this.removeUpload(uploadID) | |
}) | |
} | |
/** | |
* Start an upload for all the files that are not currently being uploaded. | |
* | |
* @return {Promise} | |
*/ | |
upload (forceUpload) { | |
const isMinNumberOfFilesReached = this.checkRestrictions(true) | |
if (!isMinNumberOfFilesReached) { | |
return Promise.reject('Minimum number of files has not been reached') | |
} | |
return this.opts.onBeforeUpload(this.state.files).catch((err) => { | |
this.info(err, 'error', 5000) | |
return Promise.reject(`onBeforeUpload: ${err}`) | |
}).then(() => { | |
const waitingFileIDs = [] | |
Object.keys(this.state.files).forEach((fileID) => { | |
const file = this.getFile(fileID) | |
// TODO: replace files[file].isRemote with some logic | |
// | |
// filter files that are now yet being uploaded / haven’t been uploaded | |
// and remote too | |
if (forceUpload) { | |
this.resetProgress() | |
waitingFileIDs.push(file.id) | |
} else if (!file.progress.uploadStarted || file.isRemote) { | |
waitingFileIDs.push(file.id) | |
} | |
}) | |
const uploadID = this.createUpload(waitingFileIDs) | |
return this.runUpload(uploadID) | |
}) | |
} | |
} | |
_$Core_82 = function (opts) { | |
if (!(this instanceof Uppy)) { | |
return new Uppy(opts) | |
} | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
/* removed: const _$Core_82 = require('./Core') */; | |
var _$Core_86 = _$Core_82 | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
var _$Loader_91 = (props) => { | |
return _$yoYo_80` | |
<div class="UppyProvider-loading"> | |
<span>Loading...</span> | |
</div> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$onload_50 = require('on-load') */; | |
/* removed: const _$Loader_91 = require('./Loader') */; | |
var _$AuthView_87 = (props) => { | |
const demoLink = props.demo ? _$yoYo_80`<button class="UppyProvider-authBtnDemo" onclick=${props.handleDemoAuth}>Proceed with Demo Account</button>` : null | |
const AuthBlock = () => _$yoYo_80` | |
<div class="UppyProvider-auth"> | |
<h1 class="UppyProvider-authTitle">Please authenticate with <span class="UppyProvider-authTitleName">${props.pluginName}</span><br> to select files</h1> | |
<button type="button" class="UppyProvider-authBtn" onclick=${props.handleAuth}>Authenticate</button> | |
${demoLink} | |
</div> | |
` | |
return _$onload_50(_$yoYo_80` | |
<div style="height: 100%;"> | |
${props.checkAuthInProgress | |
? _$Loader_91() | |
: AuthBlock() | |
} | |
</div>`, props.checkAuth, null, `auth${props.pluginName}`) | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
var _$Breadcrumb_88 = (props) => { | |
return _$yoYo_80` | |
<li> | |
<button type="button" onclick=${props.getFolder}>${props.title}</button> | |
</li> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$Breadcrumb_88 = require('./Breadcrumb') */; | |
var _$Breadcrumbs_89 = (props) => { | |
return _$yoYo_80` | |
<ul class="UppyProvider-breadcrumbs"> | |
${ | |
props.directories.map((directory, i) => { | |
return _$Breadcrumb_88({ | |
getFolder: () => props.getFolder(directory.id), | |
title: i === 0 ? props.title : directory.title | |
}) | |
}) | |
} | |
</ul> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
var _$TableColumn_93 = (props) => { | |
return _$yoYo_80` | |
<td class="BrowserTable-rowColumn BrowserTable-column"> | |
${props.getItemIcon()} ${props.value} | |
</td> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$TableColumn_93 = require('./TableColumn') */; | |
var _$TableRow_94 = (props) => { | |
const classes = props.active ? 'BrowserTable-row is-active' : 'BrowserTable-row' | |
return _$yoYo_80` | |
<tr onclick=${props.handleClick} class=${classes}> | |
${_$TableColumn_93({ | |
getItemIcon: props.getItemIcon, | |
value: props.title | |
})} | |
</tr> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$TableRow_94 = require('./TableRow') */; | |
var _$Table_92 = (props) => { | |
// const headers = props.columns.map((column) => { | |
// return html` | |
// <th class="BrowserTable-headerColumn BrowserTable-column" onclick=${props.sortByTitle}> | |
// ${column.name} | |
// </th> | |
// ` | |
// }) | |
// <thead class="BrowserTable-header"> | |
// <tr>${headers}</tr> | |
// </thead> | |
return _$yoYo_80` | |
<table class="BrowserTable" onscroll=${props.handleScroll}> | |
<tbody> | |
${props.folders.map((folder) => { | |
return _$TableRow_94({ | |
title: props.getItemName(folder), | |
active: props.activeRow(folder), | |
getItemIcon: () => props.getItemIcon(folder), | |
handleClick: () => props.handleFolderClick(folder), | |
columns: props.columns | |
}) | |
})} | |
${props.files.map((file) => { | |
return _$TableRow_94({ | |
title: props.getItemName(file), | |
active: props.activeRow(file), | |
getItemIcon: () => props.getItemIcon(file), | |
handleClick: () => props.handleFileClick(file), | |
columns: props.columns | |
}) | |
})} | |
</tbody> | |
</table> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$Breadcrumbs_89 = require('./Breadcrumbs') */; | |
/* removed: const _$Table_92 = require('./Table') */; | |
var _$Browser_90 = (props) => { | |
let filteredFolders = props.folders | |
let filteredFiles = props.files | |
if (props.filterInput !== '') { | |
filteredFolders = props.filterItems(props.folders) | |
filteredFiles = props.filterItems(props.files) | |
} | |
return _$yoYo_80` | |
<div class="Browser Browser-viewType--${props.viewType}"> | |
<header class="Browser-header"> | |
<div class="Browser-search" aria-hidden="${!props.isSearchVisible}"> | |
<input type="text" class="Browser-searchInput" placeholder="Search" | |
onkeyup=${props.filterQuery} value="${props.filterInput}"/> | |
<button type="button" class="Browser-searchClose" | |
onclick=${props.toggleSearch}> | |
<svg class="UppyIcon" viewBox="0 0 19 19"> | |
<path d="M17.318 17.232L9.94 9.854 9.586 9.5l-.354.354-7.378 7.378h.707l-.62-.62v.706L9.318 9.94l.354-.354-.354-.354L1.94 1.854v.707l.62-.62h-.706l7.378 7.378.354.354.354-.354 7.378-7.378h-.707l.622.62v-.706L9.854 9.232l-.354.354.354.354 7.378 7.378.708-.707-7.38-7.378v.708l7.38-7.38.353-.353-.353-.353-.622-.622-.353-.353-.354.352-7.378 7.38h.708L2.56 1.23 2.208.88l-.353.353-.622.62-.353.355.352.353 7.38 7.38v-.708l-7.38 7.38-.353.353.352.353.622.622.353.353.354-.353 7.38-7.38h-.708l7.38 7.38z"/> | |
</svg> | |
</button> | |
</div> | |
<div class="Browser-headerBar"> | |
<button type="button" class="Browser-searchToggle" | |
onclick=${props.toggleSearch}> | |
<svg class="UppyIcon" viewBox="0 0 100 100"> | |
<path d="M87.533 80.03L62.942 55.439c3.324-4.587 5.312-10.207 5.312-16.295 0-.312-.043-.611-.092-.908.05-.301.093-.605.093-.922 0-15.36-12.497-27.857-27.857-27.857-.273 0-.536.043-.799.08-.265-.037-.526-.08-.799-.08-15.361 0-27.858 12.497-27.858 27.857 0 .312.042.611.092.909a5.466 5.466 0 0 0-.093.921c0 15.36 12.496 27.858 27.857 27.858.273 0 .535-.043.8-.081.263.038.524.081.798.081 5.208 0 10.071-1.464 14.245-3.963L79.582 87.98a5.603 5.603 0 0 0 3.976 1.647 5.621 5.621 0 0 0 3.975-9.597zM39.598 55.838c-.265-.038-.526-.081-.8-.081-9.16 0-16.612-7.452-16.612-16.612 0-.312-.042-.611-.092-.908.051-.301.093-.605.093-.922 0-9.16 7.453-16.612 16.613-16.612.272 0 .534-.042.799-.079.263.037.525.079.799.079 9.16 0 16.612 7.452 16.612 16.612 0 .312.043.611.092.909-.05.301-.094.604-.094.921 0 9.16-7.452 16.612-16.612 16.612-.274 0-.536.043-.798.081z"/> | |
</svg> | |
</button> | |
${_$Breadcrumbs_89({ | |
getFolder: props.getFolder, | |
directories: props.directories, | |
title: props.title | |
})} | |
<button type="button" onclick=${props.logout} class="Browser-userLogout">Log out</button> | |
</div> | |
</header> | |
<div class="Browser-body"> | |
${_$Table_92({ | |
columns: [{ | |
name: 'Name', | |
key: 'title' | |
}], | |
folders: filteredFolders, | |
files: filteredFiles, | |
activeRow: props.isActiveRow, | |
sortByTitle: props.sortByTitle, | |
sortByDate: props.sortByDate, | |
handleFileClick: props.addFile, | |
handleFolderClick: props.getNextFolder, | |
getItemName: props.getItemName, | |
getItemIcon: props.getItemIcon, | |
handleScroll: props.handleScroll | |
})} | |
</div> | |
</div> | |
` | |
} | |
/* removed: const _$AuthView_87 = require('./AuthView') */; | |
/* removed: const _$Browser_90 = require('./Browser') */; | |
/* removed: const _$Loader_91 = require('./Loader') */; | |
/* removed: const _$Utils_85 = require('../core/Utils') */; | |
/** | |
* Class to easily generate generic views for plugins | |
* | |
* This class expects the plugin using to have the following attributes | |
* | |
* stateId {String} object key of which the plugin state is stored | |
* | |
* This class also expects the plugin instance using it to have the following | |
* accessor methods. | |
* Each method takes the item whose property is to be accessed | |
* as a param | |
* | |
* isFolder | |
* @return {Boolean} for if the item is a folder or not | |
* getItemData | |
* @return {Object} that is format ready for uppy upload/download | |
* getItemIcon | |
* @return {Object} html instance of the item's icon | |
* getItemSubList | |
* @return {Array} sub-items in the item. e.g a folder may contain sub-items | |
* getItemName | |
* @return {String} display friendly name of the item | |
* getMimeType | |
* @return {String} mime type of the item | |
* getItemId | |
* @return {String} unique id of the item | |
* getItemRequestPath | |
* @return {String} unique request path of the item when making calls to uppy server | |
* getItemModifiedDate | |
* @return {object} or {String} date of when last the item was modified | |
* getItemThumbnailUrl | |
* @return {String} | |
*/ | |
var _$View_95 = class View { | |
/** | |
* @param {object} instance of the plugin | |
*/ | |
constructor (plugin, opts) { | |
this.plugin = plugin | |
this.Provider = plugin[plugin.id] | |
// set default options | |
const defaultOptions = { | |
viewType: 'list' | |
} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
// Logic | |
this.addFile = this.addFile.bind(this) | |
this.filterItems = this.filterItems.bind(this) | |
this.filterQuery = this.filterQuery.bind(this) | |
this.toggleSearch = this.toggleSearch.bind(this) | |
this.getFolder = this.getFolder.bind(this) | |
this.getNextFolder = this.getNextFolder.bind(this) | |
this.logout = this.logout.bind(this) | |
this.checkAuth = this.checkAuth.bind(this) | |
this.handleAuth = this.handleAuth.bind(this) | |
this.handleDemoAuth = this.handleDemoAuth.bind(this) | |
this.sortByTitle = this.sortByTitle.bind(this) | |
this.sortByDate = this.sortByDate.bind(this) | |
this.isActiveRow = this.isActiveRow.bind(this) | |
this.handleError = this.handleError.bind(this) | |
this.handleScroll = this.handleScroll.bind(this) | |
// Visual | |
this.render = this.render.bind(this) | |
} | |
/** | |
* Little shorthand to update the state with the plugin's state | |
*/ | |
updateState (newState) { | |
let stateId = this.plugin.stateId | |
const {state} = this.plugin.core | |
this.plugin.core.setState({[stateId]: Object.assign({}, state[stateId], newState)}) | |
} | |
_updateFilesAndFolders (res, files, folders) { | |
this.plugin.getItemSubList(res).forEach((item) => { | |
if (this.plugin.isFolder(item)) { | |
folders.push(item) | |
} else { | |
files.push(item) | |
} | |
}) | |
this.updateState({ folders, files }) | |
} | |
checkAuth () { | |
this.updateState({ checkAuthInProgress: true }) | |
this.Provider.checkAuth() | |
.then((authenticated) => { | |
this.updateState({ checkAuthInProgress: false }) | |
this.plugin.onAuth(authenticated) | |
}) | |
.catch((err) => { | |
this.updateState({ checkAuthInProgress: false }) | |
this.handleError(err) | |
}) | |
} | |
/** | |
* Based on folder ID, fetch a new folder and update it to state | |
* @param {String} id Folder id | |
* @return {Promise} Folders/files in folder | |
*/ | |
getFolder (id, name) { | |
return this._loaderWrapper( | |
this.Provider.list(id), | |
(res) => { | |
let folders = [] | |
let files = [] | |
let updatedDirectories | |
const state = this.plugin.core.getState()[this.plugin.stateId] | |
const index = state.directories.findIndex((dir) => id === dir.id) | |
if (index !== -1) { | |
updatedDirectories = state.directories.slice(0, index + 1) | |
} else { | |
updatedDirectories = state.directories.concat([{id, title: name || this.plugin.getItemName(res)}]) | |
} | |
this._updateFilesAndFolders(res, files, folders) | |
this.updateState({ directories: updatedDirectories }) | |
}, | |
this.handleError) | |
} | |
/** | |
* Fetches new folder | |
* @param {Object} Folder | |
* @param {String} title Folder title | |
*/ | |
getNextFolder (folder) { | |
let id = this.plugin.getItemRequestPath(folder) | |
this.getFolder(id, this.plugin.getItemName(folder)) | |
} | |
addFile (file) { | |
const tagFile = { | |
source: this.plugin.id, | |
data: this.plugin.getItemData(file), | |
name: this.plugin.getItemName(file) || this.plugin.getItemId(file), | |
type: this.plugin.getMimeType(file), | |
isRemote: true, | |
body: { | |
fileId: this.plugin.getItemId(file) | |
}, | |
remote: { | |
host: this.plugin.opts.host, | |
url: `${this.Provider.fileUrl(this.plugin.getItemRequestPath(file))}`, | |
body: { | |
fileId: this.plugin.getItemId(file) | |
} | |
} | |
} | |
_$Utils_85.getFileType(tagFile).then(fileType => { | |
if (_$Utils_85.isPreviewSupported(fileType[1])) { | |
tagFile.preview = this.plugin.getItemThumbnailUrl(file) | |
} | |
this.plugin.core.log('Adding remote file') | |
this.plugin.core.addFile(tagFile) | |
}) | |
} | |
/** | |
* Removes session token on client side. | |
*/ | |
logout () { | |
this.Provider.logout(location.href) | |
.then((res) => res.json()) | |
.then((res) => { | |
if (res.ok) { | |
const newState = { | |
authenticated: false, | |
files: [], | |
folders: [], | |
directories: [] | |
} | |
this.updateState(newState) | |
} | |
}).catch(this.handleError) | |
} | |
filterQuery (e) { | |
const state = this.plugin.core.getState()[this.plugin.stateId] | |
this.updateState(Object.assign({}, state, { | |
filterInput: e.target.value | |
})) | |
} | |
toggleSearch () { | |
const state = this.plugin.core.getState()[this.plugin.stateId] | |
const searchInputEl = document.querySelector('.Browser-searchInput') | |
this.updateState(Object.assign({}, state, { | |
isSearchVisible: !state.isSearchVisible, | |
filterInput: '' | |
})) | |
searchInputEl.value = '' | |
if (!state.isSearchVisible) { | |
searchInputEl.focus() | |
} | |
} | |
filterItems (items) { | |
const state = this.plugin.core.getState()[this.plugin.stateId] | |
return items.filter((folder) => { | |
return this.plugin.getItemName(folder).toLowerCase().indexOf(state.filterInput.toLowerCase()) !== -1 | |
}) | |
} | |
sortByTitle () { | |
const state = Object.assign({}, this.plugin.core.getState()[this.plugin.stateId]) | |
const {files, folders, sorting} = state | |
let sortedFiles = files.sort((fileA, fileB) => { | |
if (sorting === 'titleDescending') { | |
return this.plugin.getItemName(fileB).localeCompare(this.plugin.getItemName(fileA)) | |
} | |
return this.plugin.getItemName(fileA).localeCompare(this.plugin.getItemName(fileB)) | |
}) | |
let sortedFolders = folders.sort((folderA, folderB) => { | |
if (sorting === 'titleDescending') { | |
return this.plugin.getItemName(folderB).localeCompare(this.plugin.getItemName(folderA)) | |
} | |
return this.plugin.getItemName(folderA).localeCompare(this.plugin.getItemName(folderB)) | |
}) | |
this.updateState(Object.assign({}, state, { | |
files: sortedFiles, | |
folders: sortedFolders, | |
sorting: (sorting === 'titleDescending') ? 'titleAscending' : 'titleDescending' | |
})) | |
} | |
sortByDate () { | |
const state = Object.assign({}, this.plugin.core.getState()[this.plugin.stateId]) | |
const {files, folders, sorting} = state | |
let sortedFiles = files.sort((fileA, fileB) => { | |
let a = new Date(this.plugin.getItemModifiedDate(fileA)) | |
let b = new Date(this.plugin.getItemModifiedDate(fileB)) | |
if (sorting === 'dateDescending') { | |
return a > b ? -1 : a < b ? 1 : 0 | |
} | |
return a > b ? 1 : a < b ? -1 : 0 | |
}) | |
let sortedFolders = folders.sort((folderA, folderB) => { | |
let a = new Date(this.plugin.getItemModifiedDate(folderA)) | |
let b = new Date(this.plugin.getItemModifiedDate(folderB)) | |
if (sorting === 'dateDescending') { | |
return a > b ? -1 : a < b ? 1 : 0 | |
} | |
return a > b ? 1 : a < b ? -1 : 0 | |
}) | |
this.updateState(Object.assign({}, state, { | |
files: sortedFiles, | |
folders: sortedFolders, | |
sorting: (sorting === 'dateDescending') ? 'dateAscending' : 'dateDescending' | |
})) | |
} | |
sortBySize () { | |
const state = Object.assign({}, this.plugin.core.getState()[this.plugin.stateId]) | |
const {files, sorting} = state | |
// check that plugin supports file sizes | |
if (!files.length || !this.plugin.getItemData(files[0]).size) { | |
return | |
} | |
let sortedFiles = files.sort((fileA, fileB) => { | |
let a = this.plugin.getItemData(fileA).size | |
let b = this.plugin.getItemData(fileB).size | |
if (sorting === 'sizeDescending') { | |
return a > b ? -1 : a < b ? 1 : 0 | |
} | |
return a > b ? 1 : a < b ? -1 : 0 | |
}) | |
this.updateState(Object.assign({}, state, { | |
files: sortedFiles, | |
sorting: (sorting === 'sizeDescending') ? 'sizeAscending' : 'sizeDescending' | |
})) | |
} | |
isActiveRow (file) { | |
return this.plugin.core.getState()[this.plugin.stateId].activeRow === this.plugin.getItemId(file) | |
} | |
handleDemoAuth () { | |
const state = this.plugin.core.getState()[this.plugin.stateId] | |
this.updateState({}, state, { | |
authenticated: true | |
}) | |
} | |
handleAuth () { | |
const urlId = Math.floor(Math.random() * 999999) + 1 | |
const redirect = `${location.href}${location.search ? '&' : '?'}id=${urlId}` | |
const authState = btoa(JSON.stringify({ redirect })) | |
const link = `${this.Provider.authUrl()}?state=${authState}` | |
const authWindow = window.open(link, '_blank') | |
const checkAuth = () => { | |
let authWindowUrl | |
try { | |
authWindowUrl = authWindow.location.href | |
} catch (e) { | |
if (e instanceof DOMException || e instanceof TypeError) { | |
return setTimeout(checkAuth, 100) | |
} else throw e | |
} | |
// split url because chrome adds '#' to redirects | |
if (authWindowUrl && authWindowUrl.split('#')[0] === redirect) { | |
authWindow.close() | |
this._loaderWrapper(this.Provider.checkAuth(), this.plugin.onAuth, this.handleError) | |
} else { | |
setTimeout(checkAuth, 100) | |
} | |
} | |
checkAuth() | |
} | |
handleError (error) { | |
const core = this.plugin.core | |
const message = core.i18n('uppyServerError') | |
core.log(error.toString()) | |
core.emit('informer', message, 'error', 5000) | |
} | |
handleScroll (e) { | |
const scrollPos = e.target.scrollHeight - (e.target.scrollTop + e.target.offsetHeight) | |
const path = this.plugin.getNextPagePath ? this.plugin.getNextPagePath() : null | |
if (scrollPos < 50 && path && !this._isHandlingScroll) { | |
this.Provider.list(path) | |
.then((res) => { | |
const { files, folders } = this.plugin.core.getState()[this.plugin.stateId] | |
this._updateFilesAndFolders(res, files, folders) | |
}).catch(this.handleError) | |
.then(() => { this._isHandlingScroll = false }) // always called | |
this._isHandlingScroll = true | |
} | |
} | |
// displays loader view while asynchronous request is being made. | |
_loaderWrapper (promise, then, catch_) { | |
promise | |
.then(then).catch(catch_) | |
.then(() => this.updateState({ loading: false })) // always called. | |
this.updateState({ loading: true }) | |
} | |
render (state) { | |
const { authenticated, checkAuthInProgress, loading } = state[this.plugin.stateId] | |
if (loading) { | |
return _$Loader_91() | |
} | |
if (!authenticated) { | |
return _$AuthView_87({ | |
pluginName: this.plugin.title, | |
demo: this.plugin.opts.demo, | |
checkAuth: this.checkAuth, | |
handleAuth: this.handleAuth, | |
handleDemoAuth: this.handleDemoAuth, | |
checkAuthInProgress: checkAuthInProgress | |
}) | |
} | |
const browserProps = Object.assign({}, state[this.plugin.stateId], { | |
getNextFolder: this.getNextFolder, | |
getFolder: this.getFolder, | |
addFile: this.addFile, | |
filterItems: this.filterItems, | |
filterQuery: this.filterQuery, | |
toggleSearch: this.toggleSearch, | |
sortByTitle: this.sortByTitle, | |
sortByDate: this.sortByDate, | |
logout: this.logout, | |
demo: this.plugin.opts.demo, | |
isActiveRow: this.isActiveRow, | |
getItemName: this.plugin.getItemName, | |
getItemIcon: this.plugin.getItemIcon, | |
handleScroll: this.handleScroll, | |
title: this.plugin.title, | |
viewType: this.opts.viewType | |
}) | |
return _$Browser_90(browserProps) | |
} | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
// https://css-tricks.com/creating-svg-icon-system-react/ | |
function defaultTabIcon () { | |
return _$yoYo_80`<svg class="UppyIcon" width="30" height="30" viewBox="0 0 30 30"> | |
<path d="M15 30c8.284 0 15-6.716 15-15 0-8.284-6.716-15-15-15C6.716 0 0 6.716 0 15c0 8.284 6.716 15 15 15zm4.258-12.676v6.846h-8.426v-6.846H5.204l9.82-12.364 9.82 12.364H19.26z" /> | |
</svg>` | |
} | |
function iconCopy () { | |
return _$yoYo_80`<svg class="UppyIcon" width="51" height="51" viewBox="0 0 51 51"> | |
<path d="M17.21 45.765a5.394 5.394 0 0 1-7.62 0l-4.12-4.122a5.393 5.393 0 0 1 0-7.618l6.774-6.775-2.404-2.404-6.775 6.776c-3.424 3.427-3.424 9 0 12.426l4.12 4.123a8.766 8.766 0 0 0 6.216 2.57c2.25 0 4.5-.858 6.214-2.57l13.55-13.552a8.72 8.72 0 0 0 2.575-6.213 8.73 8.73 0 0 0-2.575-6.213l-4.123-4.12-2.404 2.404 4.123 4.12a5.352 5.352 0 0 1 1.58 3.81c0 1.438-.562 2.79-1.58 3.808l-13.55 13.55z"/> | |
<path d="M44.256 2.858A8.728 8.728 0 0 0 38.043.283h-.002a8.73 8.73 0 0 0-6.212 2.574l-13.55 13.55a8.725 8.725 0 0 0-2.575 6.214 8.73 8.73 0 0 0 2.574 6.216l4.12 4.12 2.405-2.403-4.12-4.12a5.357 5.357 0 0 1-1.58-3.812c0-1.437.562-2.79 1.58-3.808l13.55-13.55a5.348 5.348 0 0 1 3.81-1.58c1.44 0 2.792.562 3.81 1.58l4.12 4.12c2.1 2.1 2.1 5.518 0 7.617L39.2 23.775l2.404 2.404 6.775-6.777c3.426-3.427 3.426-9 0-12.426l-4.12-4.12z"/> | |
</svg>` | |
} | |
function iconResume () { | |
return _$yoYo_80`<svg class="UppyIcon" width="25" height="25" viewBox="0 0 44 44"> | |
<polygon class="play" transform="translate(6, 5.5)" points="13 21.6666667 13 11 21 16.3333333" /> | |
</svg>` | |
} | |
function iconPause () { | |
return _$yoYo_80`<svg class="UppyIcon" width="25px" height="25px" viewBox="0 0 44 44"> | |
<g transform="translate(18, 17)" class="pause"> | |
<rect x="0" y="0" width="2" height="10" rx="0" /> | |
<rect x="6" y="0" width="2" height="10" rx="0" /> | |
</g> | |
</svg>` | |
} | |
function iconEdit () { | |
return _$yoYo_80`<svg class="UppyIcon" width="28" height="28" viewBox="0 0 28 28"> | |
<path d="M25.436 2.566a7.98 7.98 0 0 0-2.078-1.51C22.638.703 21.906.5 21.198.5a3 3 0 0 0-1.023.17 2.436 2.436 0 0 0-.893.562L2.292 18.217.5 27.5l9.28-1.796 16.99-16.99c.255-.254.444-.56.562-.888a3 3 0 0 0 .17-1.023c0-.708-.205-1.44-.555-2.16a8 8 0 0 0-1.51-2.077zM9.01 24.252l-4.313.834c0-.03.008-.06.012-.09.007-.944-.74-1.715-1.67-1.723-.04 0-.078.007-.118.01l.83-4.29L17.72 5.024l5.264 5.264L9.01 24.252zm16.84-16.96a.818.818 0 0 1-.194.31l-1.57 1.57-5.26-5.26 1.57-1.57a.82.82 0 0 1 .31-.194 1.45 1.45 0 0 1 .492-.074c.397 0 .917.126 1.468.397.55.27 1.13.678 1.656 1.21.53.53.94 1.11 1.208 1.655.272.55.397 1.07.393 1.468.004.193-.027.358-.074.488z" /> | |
</svg>` | |
} | |
function localIcon () { | |
return _$yoYo_80`<svg class="UppyIcon" width="27" height="25" viewBox="0 0 27 25"> | |
<path d="M5.586 9.288a.313.313 0 0 0 .282.176h4.84v3.922c0 1.514 1.25 2.24 2.792 2.24 1.54 0 2.79-.726 2.79-2.24V9.464h4.84c.122 0 .23-.068.284-.176a.304.304 0 0 0-.046-.324L13.735.106a.316.316 0 0 0-.472 0l-7.63 8.857a.302.302 0 0 0-.047.325z"/> | |
<path d="M24.3 5.093c-.218-.76-.54-1.187-1.208-1.187h-4.856l1.018 1.18h3.948l2.043 11.038h-7.193v2.728H9.114v-2.725h-7.36l2.66-11.04h3.33l1.018-1.18H3.907c-.668 0-1.06.46-1.21 1.186L0 16.456v7.062C0 24.338.676 25 1.51 25h23.98c.833 0 1.51-.663 1.51-1.482v-7.062L24.3 5.093z"/> | |
</svg>` | |
} | |
function closeIcon () { | |
return _$yoYo_80`<svg class="UppyIcon" width="14px" height="14px" viewBox="0 0 19 19"> | |
<path d="M17.318 17.232L9.94 9.854 9.586 9.5l-.354.354-7.378 7.378h.707l-.62-.62v.706L9.318 9.94l.354-.354-.354-.354L1.94 1.854v.707l.62-.62h-.706l7.378 7.378.354.354.354-.354 7.378-7.378h-.707l.622.62v-.706L9.854 9.232l-.354.354.354.354 7.378 7.378.708-.707-7.38-7.378v.708l7.38-7.38.353-.353-.353-.353-.622-.622-.353-.353-.354.352-7.378 7.38h.708L2.56 1.23 2.208.88l-.353.353-.622.62-.353.355.352.353 7.38 7.38v-.708l-7.38 7.38-.353.353.352.353.622.622.353.353.354-.353 7.38-7.38h-.708l7.38 7.38z"/> | |
</svg>` | |
} | |
function pluginIcon () { | |
return _$yoYo_80`<svg class="UppyIcon" width="16px" height="16px" viewBox="0 0 32 30"> | |
<path d="M6.6209894,11.1451162 C6.6823051,11.2751669 6.81374248,11.3572188 6.95463813,11.3572188 L12.6925482,11.3572188 L12.6925482,16.0630427 C12.6925482,17.880509 14.1726048,18.75 16.0000083,18.75 C17.8261072,18.75 19.3074684,17.8801847 19.3074684,16.0630427 L19.3074684,11.3572188 L25.0437478,11.3572188 C25.1875787,11.3572188 25.3164069,11.2751669 25.3790272,11.1451162 C25.4370814,11.0173358 25.4171865,10.8642587 25.3252129,10.7562615 L16.278212,0.127131837 C16.2093949,0.0463771751 16.1069846,0 15.9996822,0 C15.8910751,0 15.7886648,0.0463771751 15.718217,0.127131837 L6.6761083,10.7559371 C6.58250402,10.8642587 6.56293518,11.0173358 6.6209894,11.1451162 L6.6209894,11.1451162 Z"/> | |
<path d="M28.8008722,6.11142645 C28.5417891,5.19831555 28.1583331,4.6875 27.3684848,4.6875 L21.6124454,4.6875 L22.8190234,6.10307874 L27.4986725,6.10307874 L29.9195817,19.3486449 L21.3943891,19.3502502 L21.3943891,22.622552 L10.8023461,22.622552 L10.8023461,19.3524977 L2.07815702,19.3534609 L5.22979699,6.10307874 L9.17871529,6.10307874 L10.3840011,4.6875 L4.6308691,4.6875 C3.83940559,4.6875 3.37421888,5.2390909 3.19815864,6.11142645 L0,19.7470874 L0,28.2212959 C0,29.2043992 0.801477937,30 1.78870751,30 L30.2096773,30 C31.198199,30 32,29.2043992 32,28.2212959 L32,19.7470874 L28.8008722,6.11142645 L28.8008722,6.11142645 Z"/> | |
</svg>` | |
} | |
function checkIcon () { | |
return _$yoYo_80`<svg class="UppyIcon UppyIcon-check" width="13px" height="9px" viewBox="0 0 13 9"> | |
<polygon points="5 7.293 1.354 3.647 0.646 4.354 5 8.707 12.354 1.354 11.646 0.647"></polygon> | |
</svg>` | |
} | |
function iconAudio () { | |
return _$yoYo_80`<svg class="UppyIcon" viewBox="0 0 55 55"> | |
<path d="M52.66.25c-.216-.19-.5-.276-.79-.242l-31 4.01a1 1 0 0 0-.87.992V40.622C18.174 38.428 15.273 37 12 37c-5.514 0-10 4.037-10 9s4.486 9 10 9 10-4.037 10-9c0-.232-.02-.46-.04-.687.014-.065.04-.124.04-.192V16.12l29-3.753v18.257C49.174 28.428 46.273 27 43 27c-5.514 0-10 4.037-10 9s4.486 9 10 9c5.464 0 9.913-3.966 9.993-8.867 0-.013.007-.024.007-.037V1a.998.998 0 0 0-.34-.75zM12 53c-4.41 0-8-3.14-8-7s3.59-7 8-7 8 3.14 8 7-3.59 7-8 7zm31-10c-4.41 0-8-3.14-8-7s3.59-7 8-7 8 3.14 8 7-3.59 7-8 7zM22 14.1V5.89l29-3.753v8.21l-29 3.754z"/> | |
</svg>` | |
} | |
function iconVideo () { | |
return _$yoYo_80`<svg class="UppyIcon" viewBox="0 0 58 58"> | |
<path d="M36.537 28.156l-11-7a1.005 1.005 0 0 0-1.02-.033C24.2 21.3 24 21.635 24 22v14a1 1 0 0 0 1.537.844l11-7a1.002 1.002 0 0 0 0-1.688zM26 34.18V23.82L34.137 29 26 34.18z"/><path d="M57 6H1a1 1 0 0 0-1 1v44a1 1 0 0 0 1 1h56a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1zM10 28H2v-9h8v9zm-8 2h8v9H2v-9zm10 10V8h34v42H12V40zm44-12h-8v-9h8v9zm-8 2h8v9h-8v-9zm8-22v9h-8V8h8zM2 8h8v9H2V8zm0 42v-9h8v9H2zm54 0h-8v-9h8v9z"/> | |
</svg>` | |
} | |
function iconPDF () { | |
return _$yoYo_80`<svg class="UppyIcon" viewBox="0 0 342 335"> | |
<path d="M329.337 227.84c-2.1 1.3-8.1 2.1-11.9 2.1-12.4 0-27.6-5.7-49.1-14.9 8.3-.6 15.8-.9 22.6-.9 12.4 0 16 0 28.2 3.1 12.1 3 12.2 9.3 10.2 10.6zm-215.1 1.9c4.8-8.4 9.7-17.3 14.7-26.8 12.2-23.1 20-41.3 25.7-56.2 11.5 20.9 25.8 38.6 42.5 52.8 2.1 1.8 4.3 3.5 6.7 5.3-34.1 6.8-63.6 15-89.6 24.9zm39.8-218.9c6.8 0 10.7 17.06 11 33.16.3 16-3.4 27.2-8.1 35.6-3.9-12.4-5.7-31.8-5.7-44.5 0 0-.3-24.26 2.8-24.26zm-133.4 307.2c3.9-10.5 19.1-31.3 41.6-49.8 1.4-1.1 4.9-4.4 8.1-7.4-23.5 37.6-39.3 52.5-49.7 57.2zm315.2-112.3c-6.8-6.7-22-10.2-45-10.5-15.6-.2-34.3 1.2-54.1 3.9-8.8-5.1-17.9-10.6-25.1-17.3-19.2-18-35.2-42.9-45.2-70.3.6-2.6 1.2-4.8 1.7-7.1 0 0 10.8-61.5 7.9-82.3-.4-2.9-.6-3.7-1.4-5.9l-.9-2.5c-2.9-6.76-8.7-13.96-17.8-13.57l-5.3-.17h-.1c-10.1 0-18.4 5.17-20.5 12.84-6.6 24.3.2 60.5 12.5 107.4l-3.2 7.7c-8.8 21.4-19.8 43-29.5 62l-1.3 2.5c-10.2 20-19.5 37-27.9 51.4l-8.7 4.6c-.6.4-15.5 8.2-19 10.3-29.6 17.7-49.28 37.8-52.54 53.8-1.04 5-.26 11.5 5.01 14.6l8.4 4.2c3.63 1.8 7.53 2.7 11.43 2.7 21.1 0 45.6-26.2 79.3-85.1 39-12.7 83.4-23.3 122.3-29.1 29.6 16.7 66 28.3 89 28.3 4.1 0 7.6-.4 10.5-1.2 4.4-1.1 8.1-3.6 10.4-7.1 4.4-6.7 5.4-15.9 4.1-25.4-.3-2.8-2.6-6.3-5-8.7z" /> | |
</svg>` | |
} | |
function iconFile () { | |
return _$yoYo_80`<svg class="UppyIcon" width="44" height="58" viewBox="0 0 44 58"> | |
<path d="M27.437.517a1 1 0 0 0-.094.03H4.25C2.037.548.217 2.368.217 4.58v48.405c0 2.212 1.82 4.03 4.03 4.03H39.03c2.21 0 4.03-1.818 4.03-4.03V15.61a1 1 0 0 0-.03-.28 1 1 0 0 0 0-.093 1 1 0 0 0-.03-.032 1 1 0 0 0 0-.03 1 1 0 0 0-.032-.063 1 1 0 0 0-.03-.063 1 1 0 0 0-.032 0 1 1 0 0 0-.03-.063 1 1 0 0 0-.032-.03 1 1 0 0 0-.03-.063 1 1 0 0 0-.063-.062l-14.593-14a1 1 0 0 0-.062-.062A1 1 0 0 0 28 .708a1 1 0 0 0-.374-.157 1 1 0 0 0-.156 0 1 1 0 0 0-.03-.03l-.003-.003zM4.25 2.547h22.218v9.97c0 2.21 1.82 4.03 4.03 4.03h10.564v36.438a2.02 2.02 0 0 1-2.032 2.032H4.25c-1.13 0-2.032-.9-2.032-2.032V4.58c0-1.13.902-2.032 2.03-2.032zm24.218 1.345l10.375 9.937.75.718H30.5c-1.13 0-2.032-.9-2.032-2.03V3.89z" /> | |
</svg>` | |
} | |
function iconText () { | |
return _$yoYo_80`<svg class="UppyIcon" viewBox="0 0 64 64"> | |
<path d="M8 64h48V0H22.586L8 14.586V64zm46-2H10V16h14V2h30v60zM11.414 14L22 3.414V14H11.414z"/> | |
<path d="M32 13h14v2H32zM18 23h28v2H18zM18 33h28v2H18zM18 43h28v2H18zM18 53h28v2H18z"/> | |
</svg>` | |
} | |
function uploadIcon () { | |
return _$yoYo_80`<svg class="UppyIcon" width="37" height="33" viewBox="0 0 37 33"> | |
<path d="M29.107 24.5c4.07 0 7.393-3.355 7.393-7.442 0-3.994-3.105-7.307-7.012-7.502l.468.415C29.02 4.52 24.34.5 18.886.5c-4.348 0-8.27 2.522-10.138 6.506l.446-.288C4.394 6.782.5 10.758.5 15.608c0 4.924 3.906 8.892 8.76 8.892h4.872c.635 0 1.095-.467 1.095-1.104 0-.636-.46-1.103-1.095-1.103H9.26c-3.644 0-6.63-3.035-6.63-6.744 0-3.71 2.926-6.685 6.57-6.685h.964l.14-.28.177-.362c1.477-3.4 4.744-5.576 8.347-5.576 4.58 0 8.45 3.452 9.01 8.072l.06.536.05.446h1.101c2.87 0 5.204 2.37 5.204 5.295s-2.333 5.296-5.204 5.296h-6.062c-.634 0-1.094.467-1.094 1.103 0 .637.46 1.104 1.094 1.104h6.12z"/> | |
<path d="M23.196 18.92l-4.828-5.258-.366-.4-.368.398-4.828 5.196a1.13 1.13 0 0 0 0 1.546c.428.46 1.11.46 1.537 0l3.45-3.71-.868-.34v15.03c0 .64.445 1.118 1.075 1.118.63 0 1.075-.48 1.075-1.12V16.35l-.867.34 3.45 3.712a1 1 0 0 0 .767.345 1 1 0 0 0 .77-.345c.416-.33.416-1.036 0-1.485v.003z"/> | |
</svg>` | |
} | |
function dashboardBgIcon () { | |
return _$yoYo_80`<svg class="UppyIcon" width="48" height="69" viewBox="0 0 48 69"> | |
<path d="M.5 1.5h5zM10.5 1.5h5zM20.5 1.5h5zM30.504 1.5h5zM45.5 11.5v5zM45.5 21.5v5zM45.5 31.5v5zM45.5 41.502v5zM45.5 51.502v5zM45.5 61.5v5zM45.5 66.502h-4.998zM35.503 66.502h-5zM25.5 66.502h-5zM15.5 66.502h-5zM5.5 66.502h-5zM.5 66.502v-5zM.5 56.502v-5zM.5 46.503V41.5zM.5 36.5v-5zM.5 26.5v-5zM.5 16.5v-5zM.5 6.5V1.498zM44.807 11H36V2.195z"/> | |
</svg>` | |
} | |
var _$icons_106 = { | |
defaultTabIcon, | |
iconCopy, | |
/* common-shake removed: iconResume */ | |
/* common-shake removed: iconPause */ | |
iconEdit, | |
localIcon, | |
closeIcon, | |
/* common-shake removed: pluginIcon */ | |
checkIcon, | |
iconAudio, | |
iconVideo, | |
iconPDF, | |
/* common-shake removed: iconFile */ | |
iconText, | |
uploadIcon, | |
dashboardBgIcon | |
} | |
const { iconText: __iconText_105, iconAudio: __iconAudio_105, iconVideo: __iconVideo_105, iconPDF: __iconPDF_105 } = _$icons_106 | |
var _$getIconByMime_105 = function getIconByMime (fileTypeGeneral, fileTypeSpecific) { | |
if (fileTypeGeneral === 'text') { | |
return { | |
color: '#000', | |
icon: __iconText_105() | |
} | |
} | |
if (fileTypeGeneral === 'audio') { | |
return { | |
color: '#1abc9c', | |
icon: __iconAudio_105() | |
} | |
} | |
if (fileTypeGeneral === 'video') { | |
return { | |
color: '#2980b9', | |
icon: __iconVideo_105() | |
} | |
} | |
if (fileTypeGeneral === 'application' && fileTypeSpecific === 'pdf') { | |
return { | |
color: '#e74c3c', | |
icon: __iconPDF_105() | |
} | |
} | |
if (fileTypeGeneral === 'image') { | |
return { | |
color: '#f2f2f2', | |
icon: '' | |
} | |
} | |
return { | |
color: '#cbcbcb', | |
icon: '' | |
} | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$getIconByMime_105 = require('./getFileTypeIcon') */; | |
const { checkIcon: __checkIcon_99 } = _$icons_106 | |
var _$fileCard_99 = function fileCard (props) { | |
const file = props.fileCardFor ? props.files[props.fileCardFor] : false | |
const meta = {} | |
function tempStoreMeta (ev) { | |
const value = ev.target.value | |
const name = ev.target.attributes.name.value | |
meta[name] = value | |
} | |
function renderMetaFields (file) { | |
const metaFields = props.metaFields || [] | |
return metaFields.map((field) => { | |
return _$yoYo_80`<fieldset class="UppyDashboardFileCard-fieldset"> | |
<label class="UppyDashboardFileCard-label">${field.name}</label> | |
<input class="UppyDashboardFileCard-input" | |
name="${field.id}" | |
type="text" | |
value="${file.meta[field.id]}" | |
placeholder="${field.placeholder || ''}" | |
onkeyup=${tempStoreMeta} /></fieldset>` | |
}) | |
} | |
return _$yoYo_80`<div class="UppyDashboardFileCard" aria-hidden="${!props.fileCardFor}"> | |
<div class="UppyDashboardContent-bar"> | |
<h2 class="UppyDashboardContent-title">Editing <span class="UppyDashboardContent-titleFile">${file.meta ? file.meta.name : file.name}</span></h2> | |
<button class="UppyDashboardContent-back" type="button" title="Finish editing file" | |
onclick=${() => props.done(meta, file.id)}>Done</button> | |
</div> | |
${props.fileCardFor | |
? _$yoYo_80`<div class="UppyDashboardFileCard-inner"> | |
<div class="UppyDashboardFileCard-preview" style="background-color: ${_$getIconByMime_105(file.type.general, file.type.specific).color}"> | |
${file.preview | |
? _$yoYo_80`<img alt="${file.name}" src="${file.preview}">` | |
: _$yoYo_80`<div class="UppyDashboardItem-previewIconWrap"> | |
<span class="UppyDashboardItem-previewIcon" style="color: ${_$getIconByMime_105(file.type.general, file.type.specific).color}">${_$getIconByMime_105(file.type.general, file.type.specific).icon}</span> | |
<svg class="UppyDashboardItem-previewIconBg" width="72" height="93" viewBox="0 0 72 93"><g><path d="M24.08 5h38.922A2.997 2.997 0 0 1 66 8.003v74.994A2.997 2.997 0 0 1 63.004 86H8.996A2.998 2.998 0 0 1 6 83.01V22.234L24.08 5z" fill="#FFF"/><path d="M24 5L6 22.248h15.007A2.995 2.995 0 0 0 24 19.244V5z" fill="#E4E4E4"/></g></svg> | |
</div>` | |
} | |
</div> | |
<div class="UppyDashboardFileCard-info"> | |
<fieldset class="UppyDashboardFileCard-fieldset"> | |
<label class="UppyDashboardFileCard-label">Name</label> | |
<input class="UppyDashboardFileCard-input" name="name" type="text" value="${file.meta.name}" | |
onkeyup=${tempStoreMeta} /> | |
</fieldset> | |
${renderMetaFields(file)} | |
</div> | |
</div>` | |
: null | |
} | |
<div class="UppyDashboard-actions"> | |
<button class="UppyButton--circular UppyButton--blue UppyDashboardFileCard-done" | |
type="button" | |
title="Finish editing file" | |
onclick=${() => props.done(meta, file.id)}>${__checkIcon_99()}</button> | |
</div> | |
</div>` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
var _$ActionBrowseTagline_97 = (props) => { | |
const input = _$yoYo_80` | |
<input class="UppyDashboard-input" type="file" name="files[]" multiple="true" | |
onchange=${props.handleInputChange} /> | |
` | |
return _$yoYo_80` | |
<span> | |
${props.acquirers.length === 0 | |
? props.i18n('dropPaste') | |
: props.i18n('dropPasteImport') | |
} | |
<button type="button" | |
class="UppyDashboard-browse" | |
onclick=${(ev) => { | |
input.click() | |
}}>${props.i18n('browse')}</button> | |
${input} | |
</span> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
// http://codepen.io/Harkko/pen/rVxvNM | |
// https://css-tricks.com/svg-line-animation-works/ | |
// https://gist.github.com/eswak/ad4ea57bcd5ff7aa5d42 | |
// circle length equals 2 * PI * R | |
const circleLength = 2 * Math.PI * 15 | |
// stroke-dashoffset is a percentage of the progress from circleLength, | |
// substracted from circleLength, because its an offset | |
var _$FileItemProgress_101 = (props) => { | |
return _$yoYo_80` | |
<svg width="70" height="70" viewBox="0 0 36 36" class="UppyIcon UppyIcon-progressCircle"> | |
<g class="progress-group"> | |
<circle r="15" cx="18" cy="18" stroke-width="2" fill="none" class="bg"/> | |
<circle r="15" cx="18" cy="18" transform="rotate(-90, 18, 18)" stroke-width="2" fill="none" class="progress" | |
stroke-dasharray=${circleLength} | |
stroke-dashoffset=${circleLength - (circleLength / 100 * props.progress)} | |
/> | |
</g> | |
<polygon transform="translate(3, 3)" points="12 20 12 10 20 15" class="play"/> | |
<g transform="translate(14.5, 13)" class="pause"> | |
<rect x="0" y="0" width="2" height="10" rx="0" /> | |
<rect x="5" y="0" width="2" height="10" rx="0" /> | |
</g> | |
<polygon transform="translate(2, 3)" points="14 22.5 7 15.2457065 8.99985857 13.1732815 14 18.3547104 22.9729883 9 25 11.1005634" class="check"/> | |
<polygon class="cancel" transform="translate(2, 2)" points="19.8856516 11.0625 16 14.9481516 12.1019737 11.0625 11.0625 12.1143484 14.9481516 16 11.0625 19.8980263 12.1019737 20.9375 16 17.0518484 19.8856516 20.9375 20.9375 19.8980263 17.0518484 16 20.9375 12"></polygon> | |
</svg>` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
const { getETA: __getETA_100, | |
getSpeed: __getSpeed_100, | |
prettyETA: __prettyETA_100, | |
getFileNameAndExtension: __getFileNameAndExtension_100, | |
truncateString: __truncateString_100, | |
copyToClipboard: __copyToClipboard_100 } = _$Utils_85 | |
/* removed: const _$prettierBytes_54 = require('prettier-bytes') */; | |
/* removed: const _$FileItemProgress_101 = require('./FileItemProgress') */; | |
/* removed: const _$getIconByMime_105 = require('./getFileTypeIcon') */; | |
const { iconEdit: __iconEdit_100, iconCopy: __iconCopy_100 } = _$icons_106 | |
var _$fileItem_100 = function fileItem (props) { | |
const file = props.file | |
const acquirers = props.acquirers | |
const isUploaded = file.progress.uploadComplete | |
const uploadInProgressOrComplete = file.progress.uploadStarted | |
const uploadInProgress = file.progress.uploadStarted && !file.progress.uploadComplete | |
const isPaused = file.isPaused || false | |
const fileName = __getFileNameAndExtension_100(file.meta.name)[0] | |
const truncatedFileName = props.isWide ? __truncateString_100(fileName, 15) : fileName | |
return _$yoYo_80`<li class="UppyDashboardItem | |
${uploadInProgress ? 'is-inprogress' : ''} | |
${isUploaded ? 'is-complete' : ''} | |
${isPaused ? 'is-paused' : ''} | |
${props.resumableUploads ? 'is-resumable' : ''}" | |
id="uppy_${file.id}" | |
title="${file.meta.name}"> | |
<div class="UppyDashboardItem-preview"> | |
<div class="UppyDashboardItem-previewInnerWrap" style="background-color: ${_$getIconByMime_105(file.type.general, file.type.specific).color}"> | |
${file.preview | |
? _$yoYo_80`<img alt="${file.name}" src="${file.preview}">` | |
: _$yoYo_80`<div class="UppyDashboardItem-previewIconWrap"> | |
<span class="UppyDashboardItem-previewIcon" style="color: ${_$getIconByMime_105(file.type.general, file.type.specific).color}">${_$getIconByMime_105(file.type.general, file.type.specific).icon}</span> | |
<svg class="UppyDashboardItem-previewIconBg" width="72" height="93" viewBox="0 0 72 93"><g><path d="M24.08 5h38.922A2.997 2.997 0 0 1 66 8.003v74.994A2.997 2.997 0 0 1 63.004 86H8.996A2.998 2.998 0 0 1 6 83.01V22.234L24.08 5z" fill="#FFF"/><path d="M24 5L6 22.248h15.007A2.995 2.995 0 0 0 24 19.244V5z" fill="#E4E4E4"/></g></svg> | |
</div>` | |
} | |
</div> | |
<div class="UppyDashboardItem-progress"> | |
<button class="UppyDashboardItem-progressBtn" | |
type="button" | |
title="${isUploaded | |
? 'upload complete' | |
: props.resumableUploads | |
? file.isPaused | |
? 'resume upload' | |
: 'pause upload' | |
: 'cancel upload' | |
}" | |
onclick=${(ev) => { | |
if (isUploaded) return | |
if (props.resumableUploads) { | |
props.pauseUpload(file.id) | |
} else { | |
props.cancelUpload(file.id) | |
} | |
}}> | |
${_$FileItemProgress_101({ | |
progress: file.progress.percentage, | |
fileID: file.id | |
})} | |
</button> | |
${props.showProgressDetails | |
? _$yoYo_80`<div class="UppyDashboardItem-progressInfo" | |
title="${props.i18n('fileProgress')}" | |
aria-label="${props.i18n('fileProgress')}"> | |
${!file.isPaused && !isUploaded | |
? _$yoYo_80`<span>${__prettyETA_100(__getETA_100(file.progress))} ・ ↑ ${_$prettierBytes_54(__getSpeed_100(file.progress))}/s</span>` | |
: null | |
} | |
</div>` | |
: null | |
} | |
</div> | |
</div> | |
<div class="UppyDashboardItem-info"> | |
<h4 class="UppyDashboardItem-name" title="${fileName}"> | |
${file.uploadURL | |
? _$yoYo_80`<a href="${file.uploadURL}" target="_blank"> | |
${file.extension ? truncatedFileName + '.' + file.extension : truncatedFileName} | |
</a>` | |
: file.extension ? truncatedFileName + '.' + file.extension : truncatedFileName | |
} | |
</h4> | |
<div class="UppyDashboardItem-status"> | |
<div class="UppyDashboardItem-statusSize">${file.data.size ? _$prettierBytes_54(file.data.size) : ''}</div> | |
${file.source | |
? _$yoYo_80`<div class="UppyDashboardItem-sourceIcon"> | |
${acquirers.map(acquirer => { | |
if (acquirer.id === file.source) return _$yoYo_80`<span title="${acquirer.name}">${acquirer.icon()}</span>` | |
})} | |
</div>` | |
: '' | |
} | |
</div> | |
${!uploadInProgressOrComplete | |
? _$yoYo_80`<button class="UppyDashboardItem-edit" | |
type="button" | |
aria-label="Edit file" | |
title="Edit file" | |
onclick=${(e) => props.showFileCard(file.id)}> | |
${__iconEdit_100()}</button>` | |
: null | |
} | |
${file.uploadURL | |
? _$yoYo_80`<button class="UppyDashboardItem-copyLink" | |
type="button" | |
aria-label="Copy link" | |
title="Copy link" | |
onclick=${() => { | |
__copyToClipboard_100(file.uploadURL, props.i18n('copyLinkToClipboardFallback')) | |
.then(() => { | |
props.log('Link copied to clipboard.') | |
props.info(props.i18n('copyLinkToClipboardSuccess'), 'info', 3000) | |
}) | |
.catch(props.log) | |
}}>${__iconCopy_100()}</button>` | |
: null | |
} | |
</div> | |
<div class="UppyDashboardItem-action"> | |
${!isUploaded | |
? _$yoYo_80`<button class="UppyDashboardItem-remove" | |
type="button" | |
aria-label="Remove file" | |
title="Remove file" | |
onclick=${() => props.removeFile(file.id)}> | |
<svg class="UppyIcon" width="22" height="21" viewBox="0 0 18 17"> | |
<ellipse cx="8.62" cy="8.383" rx="8.62" ry="8.383"/> | |
<path stroke="#FFF" fill="#FFF" d="M11 6.147L10.85 6 8.5 8.284 6.15 6 6 6.147 8.35 8.43 6 10.717l.15.146L8.5 8.578l2.35 2.284.15-.146L8.65 8.43z"/> | |
</svg> | |
</button>` | |
: null | |
} | |
</div> | |
</li>` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$fileItem_100 = require('./FileItem') */; | |
/* removed: const _$ActionBrowseTagline_97 = require('./ActionBrowseTagline') */; | |
const { dashboardBgIcon: __dashboardBgIcon_102 } = _$icons_106 | |
var _$FileList_102 = (props) => { | |
return _$yoYo_80`<ul class="UppyDashboard-files | |
${props.totalFileCount === 0 ? 'UppyDashboard-files--noFiles' : ''}"> | |
${props.totalFileCount === 0 | |
? _$yoYo_80`<div class="UppyDashboard-bgIcon"> | |
${__dashboardBgIcon_102()} | |
<h3 class="UppyDashboard-dropFilesTitle"> | |
${_$ActionBrowseTagline_97({ | |
acquirers: props.acquirers, | |
handleInputChange: props.handleInputChange, | |
i18n: props.i18n | |
})} | |
</h3> | |
${props.note | |
? _$yoYo_80`<p class="UppyDashboard-note">${props.note}</p>` | |
: '' | |
} | |
<input class="UppyDashboard-input" type="file" name="files[]" multiple="true" | |
onchange=${props.handleInputChange} /> | |
</div>` | |
: null | |
} | |
${Object.keys(props.files).map((fileID) => { | |
return _$fileItem_100({ | |
acquirers: props.acquirers, | |
file: props.files[fileID], | |
showFileCard: props.showFileCard, | |
showProgressDetails: props.showProgressDetails, | |
info: props.info, | |
log: props.log, | |
i18n: props.i18n, | |
removeFile: props.removeFile, | |
pauseUpload: props.pauseUpload, | |
cancelUpload: props.cancelUpload, | |
resumableUploads: props.resumableUploads, | |
isWide: props.isWide | |
}) | |
})} | |
</ul>` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$ActionBrowseTagline_97 = require('./ActionBrowseTagline') */; | |
const { localIcon: __localIcon_103 } = _$icons_106 | |
var _$Tabs_103 = (props) => { | |
const isHidden = Object.keys(props.files).length === 0 | |
if (props.acquirers.length === 0) { | |
return _$yoYo_80` | |
<div class="UppyDashboardTabs" aria-hidden="${isHidden}"> | |
<h3 class="UppyDashboardTabs-title"> | |
${_$ActionBrowseTagline_97({ | |
acquirers: props.acquirers, | |
handleInputChange: props.handleInputChange, | |
i18n: props.i18n | |
})} | |
</h3> | |
</div> | |
` | |
} | |
const input = _$yoYo_80` | |
<input class="UppyDashboard-input" type="file" name="files[]" multiple="true" | |
onchange=${props.handleInputChange} /> | |
` | |
return _$yoYo_80`<div class="UppyDashboardTabs"> | |
<nav> | |
<ul class="UppyDashboardTabs-list" role="tablist"> | |
<li class="UppyDashboardTab"> | |
<button type="button" class="UppyDashboardTab-btn UppyDashboard-focus" | |
role="tab" | |
tabindex="0" | |
onclick=${(ev) => { | |
input.click() | |
}}> | |
${__localIcon_103()} | |
<h5 class="UppyDashboardTab-name">${props.i18n('myDevice')}</h5> | |
</button> | |
${input} | |
</li> | |
${props.acquirers.map((target) => { | |
return _$yoYo_80`<li class="UppyDashboardTab"> | |
<button class="UppyDashboardTab-btn" | |
type="button" | |
role="tab" | |
tabindex="0" | |
aria-controls="UppyDashboardContent-panel--${target.id}" | |
aria-selected="${target.isHidden ? 'false' : 'true'}" | |
onclick=${() => props.showPanel(target.id)}> | |
${target.icon()} | |
<h5 class="UppyDashboardTab-name">${target.name}</h5> | |
</button> | |
</li>` | |
})} | |
</ul> | |
</nav> | |
</div>` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
const { uploadIcon: __uploadIcon_104 } = _$icons_106 | |
var _$UploadBtn_104 = (props) => { | |
props = props || {} | |
return _$yoYo_80`<button class="UppyButton--circular | |
UppyButton--blue | |
UppyDashboard-upload" | |
type="button" | |
title="${props.i18n('uploadAllNewFiles')}" | |
aria-label="${props.i18n('uploadAllNewFiles')}" | |
onclick=${props.startUpload}> | |
${__uploadIcon_104()} | |
<sup class="UppyDashboard-uploadCount" | |
title="${props.i18n('numberOfSelectedFiles')}" | |
aria-label="${props.i18n('numberOfSelectedFiles')}"> | |
${props.newFileCount}</sup> | |
</button> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$FileList_102 = require('./FileList') */; | |
/* removed: const _$Tabs_103 = require('./Tabs') */; | |
/* removed: const _$fileCard_99 = require('./FileCard') */; | |
/* removed: const _$UploadBtn_104 = require('./UploadBtn') */; | |
const { isTouchDevice: __isTouchDevice_98, toArray: __toArray_98 } = _$Utils_85 | |
const { closeIcon: __closeIcon_98 } = _$icons_106 | |
// http://dev.edenspiekermann.com/2016/02/11/introducing-accessible-modal-dialog | |
var _$Dashboard_98 = function Dashboard (props) { | |
function handleInputChange (ev) { | |
ev.preventDefault() | |
const files = __toArray_98(ev.target.files) | |
files.forEach((file) => { | |
props.addFile({ | |
source: props.id, | |
name: file.name, | |
type: file.type, | |
data: file | |
}) | |
}) | |
} | |
// @TODO Exprimental, work in progress | |
// no names, weird API, Chrome-only http://stackoverflow.com/a/22940020 | |
function handlePaste (ev) { | |
ev.preventDefault() | |
const files = __toArray_98(ev.clipboardData.items) | |
files.forEach((file) => { | |
if (file.kind !== 'file') return | |
const blob = file.getAsFile() | |
props.log('File pasted') | |
props.addFile({ | |
source: props.id, | |
name: file.name, | |
type: file.type, | |
data: blob | |
}) | |
}) | |
} | |
return _$yoYo_80` | |
<div class="Uppy UppyTheme--default UppyDashboard | |
${__isTouchDevice_98() ? 'Uppy--isTouchDevice' : ''} | |
${props.semiTransparent ? 'UppyDashboard--semiTransparent' : ''} | |
${!props.inline ? 'UppyDashboard--modal' : ''} | |
${props.isWide ? 'UppyDashboard--wide' : ''}" | |
aria-hidden="${props.inline ? 'false' : props.modal.isHidden}" | |
aria-label="${!props.inline | |
? props.i18n('dashboardWindowTitle') | |
: props.i18n('dashboardTitle')}" | |
role="dialog" | |
onpaste=${handlePaste} | |
onload=${() => props.updateDashboardElWidth()}> | |
<div class="UppyDashboard-overlay" onclick=${() => { | |
// props.hideModal | |
}}></div> | |
<div class="UppyDashboard-inner" | |
tabindex="0" | |
style=" | |
${props.inline && props.maxWidth ? `max-width: ${props.maxWidth}px;` : ''} | |
${props.inline && props.maxHeight ? `max-height: ${props.maxHeight}px;` : ''} | |
"> | |
<button class="UppyDashboard-close" | |
type="button" | |
aria-label="${props.i18n('closeModal')}" | |
title="${props.i18n('closeModal')}" | |
onclick=${props.hideModal}>${__closeIcon_98()}</button> | |
<div class="UppyDashboard-innerWrap"> | |
${_$Tabs_103({ | |
files: props.files, | |
handleInputChange: handleInputChange, | |
acquirers: props.acquirers, | |
panelSelectorPrefix: props.panelSelectorPrefix, | |
showPanel: props.showPanel, | |
i18n: props.i18n | |
})} | |
${_$fileCard_99({ | |
files: props.files, | |
fileCardFor: props.fileCardFor, | |
done: props.fileCardDone, | |
metaFields: props.metaFields, | |
log: props.log, | |
i18n: props.i18n | |
})} | |
<div class="UppyDashboard-filesContainer"> | |
${_$FileList_102({ | |
acquirers: props.acquirers, | |
files: props.files, | |
handleInputChange: handleInputChange, | |
showFileCard: props.showFileCard, | |
showProgressDetails: props.showProgressDetails, | |
totalProgress: props.totalProgress, | |
totalFileCount: props.totalFileCount, | |
info: props.info, | |
note: props.note, | |
i18n: props.i18n, | |
log: props.log, | |
removeFile: props.removeFile, | |
pauseAll: props.pauseAll, | |
resumeAll: props.resumeAll, | |
pauseUpload: props.pauseUpload, | |
startUpload: props.startUpload, | |
cancelUpload: props.cancelUpload, | |
resumableUploads: props.resumableUploads, | |
isWide: props.isWide | |
})} | |
<div class="UppyDashboard-actions"> | |
${!props.hideUploadButton && !props.autoProceed && props.newFiles.length > 0 | |
? _$UploadBtn_104({ | |
i18n: props.i18n, | |
startUpload: props.startUpload, | |
newFileCount: props.newFiles.length | |
}) | |
: null | |
} | |
</div> | |
</div> | |
<div class="UppyDashboardContent-panel" | |
role="tabpanel" | |
aria-hidden="${props.activePanel ? 'false' : 'true'}"> | |
<div class="UppyDashboardContent-bar"> | |
<h2 class="UppyDashboardContent-title"> | |
${props.i18n('importFrom')} ${props.activePanel ? props.activePanel.name : null} | |
</h2> | |
<button class="UppyDashboardContent-back" | |
type="button" | |
onclick=${props.hideAllPanels}>${props.i18n('done')}</button> | |
</div> | |
${props.activePanel ? props.activePanel.render(props.state) : ''} | |
</div> | |
<div class="UppyDashboard-progressindicators"> | |
${props.progressindicators.map((target) => { | |
return target.render(props.state) | |
})} | |
</div> | |
</div> | |
</div> | |
</div> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$nanoraf_49 = require('nanoraf') */; | |
const { findDOMElement: __findDOMElement_117 } = _$Utils_85 | |
/* removed: const _$lib_35 = require('get-form-data') */; | |
/** | |
* Boilerplate that all Plugins share - and should not be used | |
* directly. It also shows which methods final plugins should implement/override, | |
* this deciding on structure. | |
* | |
* @param {object} main Uppy core object | |
* @param {object} object with plugin options | |
* @return {array | string} files or success/fail message | |
*/ | |
var _$Plugin_117 = class Plugin { | |
constructor (core, opts) { | |
this.core = core | |
this.opts = opts || {} | |
this.type = 'none' | |
// clear everything inside the target selector | |
this.opts.replaceTargetContent === this.opts.replaceTargetContent || true | |
this.update = this.update.bind(this) | |
this.mount = this.mount.bind(this) | |
this.install = this.install.bind(this) | |
this.uninstall = this.uninstall.bind(this) | |
} | |
update (state) { | |
if (typeof this.el === 'undefined') { | |
return | |
} | |
if (this.updateUI) { | |
this.updateUI(state) | |
} | |
} | |
/** | |
* Check if supplied `target` is a DOM element or an `object`. | |
* If it’s an object — target is a plugin, and we search `plugins` | |
* for a plugin with same name and return its target. | |
* | |
* @param {String|Object} target | |
* | |
*/ | |
mount (target, plugin) { | |
const callerPluginName = plugin.id | |
const targetElement = __findDOMElement_117(target) | |
// Set up nanoraf. | |
this.updateUI = _$nanoraf_49((state) => { | |
this.el = _$yoYo_80.update(this.el, this.render(state)) | |
}) | |
if (targetElement) { | |
this.core.log(`Installing ${callerPluginName} to a DOM element`) | |
// attempt to extract meta from form element | |
if (this.opts.getMetaFromForm && targetElement.nodeName === 'FORM') { | |
const formMeta = _$lib_35(targetElement) | |
this.core.setMeta(formMeta) | |
} | |
// clear everything inside the target container | |
if (this.opts.replaceTargetContent) { | |
targetElement.innerHTML = '' | |
} | |
this.el = plugin.render(this.core.state) | |
targetElement.appendChild(this.el) | |
return targetElement | |
} else { | |
// TODO: is instantiating the plugin really the way to roll | |
// just to get the plugin name? | |
const Target = target | |
const targetPluginName = new Target().id | |
this.core.log(`Installing ${callerPluginName} to ${targetPluginName}`) | |
const targetPlugin = this.core.getPlugin(targetPluginName) | |
const selectorTarget = targetPlugin.addTarget(plugin) | |
return selectorTarget | |
} | |
} | |
unmount () { | |
if (this.el && this.el.parentNode) { | |
this.el.parentNode.removeChild(this.el) | |
} | |
} | |
install () { | |
return | |
} | |
uninstall () { | |
this.unmount() | |
} | |
} | |
/* removed: const _$Plugin_117 = require('./Plugin') */; | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/** | |
* Informer | |
* Shows rad message bubbles | |
* used like this: `core.emit('informer', 'hello world', 'info', 5000)` | |
* or for errors: `core.emit('informer', 'Error uploading img.jpg', 'error', 5000)` | |
* | |
*/ | |
var _$Informer_114 = class Informer extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.type = 'progressindicator' | |
this.id = 'Informer' | |
this.title = 'Informer' | |
// this.timeoutID = undefined | |
// set default options | |
const defaultOptions = { | |
typeColors: { | |
info: { | |
text: '#fff', | |
bg: '#000' | |
}, | |
warning: { | |
text: '#fff', | |
bg: '#F6A623' | |
}, | |
error: { | |
text: '#fff', | |
bg: '#e74c3c' | |
}, | |
success: { | |
text: '#fff', | |
bg: '#7ac824' | |
} | |
} | |
} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.render = this.render.bind(this) | |
} | |
render (state) { | |
const isHidden = state.info.isHidden | |
const msg = state.info.msg | |
const type = state.info.type || 'info' | |
const style = `background-color: ${this.opts.typeColors[type].bg}; color: ${this.opts.typeColors[type].text};` | |
// @TODO add aria-live for screen-readers | |
return _$yoYo_80`<div class="Uppy UppyTheme--default UppyInformer" style="${style}" aria-hidden="${isHidden}"> | |
<p>${msg}</p> | |
</div>` | |
} | |
install () { | |
const target = this.opts.target | |
const plugin = this | |
this.target = this.mount(target, plugin) | |
} | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$throttle_44 = require('lodash.throttle') */; | |
function progressDetails (props) { | |
return _$yoYo_80`<span>${props.totalProgress || 0}%・${props.complete} / ${props.inProgress}・${props.totalUploadedSize} / ${props.totalSize}・↑ ${props.totalSpeed}/s・${props.totalETA}</span>` | |
} | |
const throttledProgressDetails = _$throttle_44(progressDetails, 1000, {leading: true, trailing: true}) | |
const STATE_ERROR = 'error' | |
const STATE_WAITING = 'waiting' | |
const STATE_PREPROCESSING = 'preprocessing' | |
const STATE_UPLOADING = 'uploading' | |
const STATE_POSTPROCESSING = 'postprocessing' | |
const STATE_COMPLETE = 'complete' | |
function getUploadingState (props, files) { | |
if (props.error) { | |
return STATE_ERROR | |
} | |
// If ALL files have been completed, show the completed state. | |
if (props.isAllComplete) { | |
return STATE_COMPLETE | |
} | |
let state = STATE_WAITING | |
const fileIDs = Object.keys(files) | |
for (let i = 0; i < fileIDs.length; i++) { | |
const progress = files[fileIDs[i]].progress | |
// If ANY files are being uploaded right now, show the uploading state. | |
if (progress.uploadStarted && !progress.uploadComplete) { | |
return STATE_UPLOADING | |
} | |
// If files are being preprocessed AND postprocessed at this time, we show the | |
// preprocess state. If any files are being uploaded we show uploading. | |
if (progress.preprocess && state !== STATE_UPLOADING) { | |
state = STATE_PREPROCESSING | |
} | |
// If NO files are being preprocessed or uploaded right now, but some files are | |
// being postprocessed, show the postprocess state. | |
if (progress.postprocess && state !== STATE_UPLOADING && state !== STATE_PREPROCESSING) { | |
state = STATE_POSTPROCESSING | |
} | |
} | |
return state | |
} | |
function calculateProcessingProgress (files) { | |
// Collect pre or postprocessing progress states. | |
const progresses = [] | |
Object.keys(files).forEach((fileID) => { | |
const { progress } = files[fileID] | |
if (progress.preprocess) { | |
progresses.push(progress.preprocess) | |
} | |
if (progress.postprocess) { | |
progresses.push(progress.postprocess) | |
} | |
}) | |
// In the future we should probably do this differently. For now we'll take the | |
// mode and message from the first file… | |
const { mode, message } = progresses[0] | |
const value = progresses.filter(isDeterminate).reduce((total, progress, index, all) => { | |
return total + progress.value / all.length | |
}, 0) | |
function isDeterminate (progress) { | |
return progress.mode === 'determinate' | |
} | |
return { | |
mode, | |
message, | |
value | |
} | |
} | |
var _$StatusBar_119 = (props) => { | |
props = props || {} | |
const uploadState = getUploadingState(props, props.files || {}) | |
let progressValue = props.totalProgress | |
let progressMode | |
let progressBarContent | |
if (uploadState === STATE_PREPROCESSING || uploadState === STATE_POSTPROCESSING) { | |
const progress = calculateProcessingProgress(props.files) | |
progressMode = progress.mode | |
if (progressMode === 'determinate') { | |
progressValue = progress.value * 100 | |
} | |
progressBarContent = ProgressBarProcessing(progress) | |
} else if (uploadState === STATE_COMPLETE) { | |
progressBarContent = ProgressBarComplete(props) | |
} else if (uploadState === STATE_UPLOADING) { | |
progressBarContent = ProgressBarUploading(props) | |
} else if (uploadState === STATE_ERROR) { | |
progressValue = undefined | |
progressBarContent = ProgressBarError(props) | |
} | |
const width = typeof progressValue === 'number' ? progressValue : 100 | |
return _$yoYo_80` | |
<div class="UppyStatusBar is-${uploadState}" | |
aria-hidden="${uploadState === STATE_WAITING}" | |
title=""> | |
<progress style="display: none;" min="0" max="100" value=${progressValue}></progress> | |
<div class="UppyStatusBar-progress ${progressMode ? `is-${progressMode}` : ''}" | |
style="width: ${width}%"></div> | |
${progressBarContent} | |
</div> | |
` | |
} | |
const ProgressBarProcessing = (props) => { | |
return _$yoYo_80` | |
<div class="UppyStatusBar-content"> | |
${props.mode === 'determinate' ? `${Math.round(props.value * 100)}%・` : ''} | |
${props.message} | |
</div> | |
` | |
} | |
const ProgressBarUploading = (props) => { | |
return _$yoYo_80` | |
<div class="UppyStatusBar-content"> | |
${props.isUploadStarted && !props.isAllComplete | |
? !props.isAllPaused | |
? _$yoYo_80`<span title="Uploading">${pauseResumeButtons(props)} Uploading... ${throttledProgressDetails(props)}</span>` | |
: _$yoYo_80`<span title="Paused">${pauseResumeButtons(props)} Paused・${props.totalProgress}%</span>` | |
: null | |
} | |
</div> | |
` | |
} | |
const ProgressBarComplete = ({ totalProgress }) => { | |
return _$yoYo_80` | |
<div class="UppyStatusBar-content"> | |
<span title="Complete"> | |
<svg class="UppyStatusBar-action UppyIcon" width="18" height="17" viewBox="0 0 23 17"> | |
<path d="M8.944 17L0 7.865l2.555-2.61 6.39 6.525L20.41 0 23 2.645z" /> | |
</svg> | |
Upload complete・${totalProgress}% | |
</span> | |
</div> | |
` | |
} | |
const ProgressBarError = ({ error }) => { | |
return _$yoYo_80` | |
<div class="UppyStatusBar-content"> | |
<span> | |
${error.message} | |
</span> | |
</div> | |
` | |
} | |
const pauseResumeButtons = (props) => { | |
const title = props.resumableUploads | |
? props.isAllPaused | |
? 'resume upload' | |
: 'pause upload' | |
: 'cancel upload' | |
return _$yoYo_80`<button title="${title}" class="UppyStatusBar-action" type="button" onclick=${() => togglePauseResume(props)}> | |
${props.resumableUploads | |
? props.isAllPaused | |
? _$yoYo_80`<svg class="UppyIcon" width="15" height="17" viewBox="0 0 11 13"> | |
<path d="M1.26 12.534a.67.67 0 0 1-.674.012.67.67 0 0 1-.336-.583v-11C.25.724.38.5.586.382a.658.658 0 0 1 .673.012l9.165 5.5a.66.66 0 0 1 .325.57.66.66 0 0 1-.325.573l-9.166 5.5z" /> | |
</svg>` | |
: _$yoYo_80`<svg class="UppyIcon" width="16" height="17" viewBox="0 0 12 13"> | |
<path d="M4.888.81v11.38c0 .446-.324.81-.722.81H2.722C2.324 13 2 12.636 2 12.19V.81c0-.446.324-.81.722-.81h1.444c.398 0 .722.364.722.81zM9.888.81v11.38c0 .446-.324.81-.722.81H7.722C7.324 13 7 12.636 7 12.19V.81c0-.446.324-.81.722-.81h1.444c.398 0 .722.364.722.81z"/> | |
</svg>` | |
: _$yoYo_80`<svg class="UppyIcon" width="16px" height="16px" viewBox="0 0 19 19"> | |
<path d="M17.318 17.232L9.94 9.854 9.586 9.5l-.354.354-7.378 7.378h.707l-.62-.62v.706L9.318 9.94l.354-.354-.354-.354L1.94 1.854v.707l.62-.62h-.706l7.378 7.378.354.354.354-.354 7.378-7.378h-.707l.622.62v-.706L9.854 9.232l-.354.354.354.354 7.378 7.378.708-.707-7.38-7.378v.708l7.38-7.38.353-.353-.353-.353-.622-.622-.353-.353-.354.352-7.378 7.38h.708L2.56 1.23 2.208.88l-.353.353-.622.62-.353.355.352.353 7.38 7.38v-.708l-7.38 7.38-.353.353.352.353.622.622.353.353.354-.353 7.38-7.38h-.708l7.38 7.38z"/> | |
</svg>` | |
} | |
</button>` | |
} | |
const togglePauseResume = (props) => { | |
if (props.isAllComplete) return | |
if (!props.resumableUploads) { | |
return props.cancelAll() | |
} | |
if (props.isAllPaused) { | |
return props.resumeAll() | |
} | |
return props.pauseAll() | |
} | |
/* removed: const _$Plugin_117 = require('../Plugin') */; | |
/* removed: const _$StatusBar_119 = require('./StatusBar') */; | |
const { getSpeed: __getSpeed_120 } = _$Utils_85 | |
const { getBytesRemaining: __getBytesRemaining_120 } = _$Utils_85 | |
const { prettyETA: __prettyETA_120 } = _$Utils_85 | |
/* removed: const _$prettierBytes_54 = require('prettier-bytes') */; | |
/** | |
* A status bar. | |
*/ | |
var _$StatusBarUI_120 = class StatusBarUI extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.id = 'StatusBarUI' | |
this.title = 'StatusBar UI' | |
this.type = 'progressindicator' | |
// set default options | |
const defaultOptions = { | |
target: 'body', | |
showProgressDetails: false | |
} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.pauseAll = this.pauseAll.bind(this) | |
this.resumeAll = this.resumeAll.bind(this) | |
this.cancelAll = this.cancelAll.bind(this) | |
this.render = this.render.bind(this) | |
this.install = this.install.bind(this) | |
} | |
cancelAll () { | |
this.core.emit('core:cancel-all') | |
} | |
pauseAll () { | |
this.core.emit('core:pause-all') | |
} | |
resumeAll () { | |
this.core.emit('core:resume-all') | |
} | |
getTotalSpeed (files) { | |
let totalSpeed = 0 | |
files.forEach((file) => { | |
totalSpeed = totalSpeed + __getSpeed_120(file.progress) | |
}) | |
return totalSpeed | |
} | |
getTotalETA (files) { | |
const totalSpeed = this.getTotalSpeed(files) | |
if (totalSpeed === 0) { | |
return 0 | |
} | |
const totalBytesRemaining = files.reduce((total, file) => { | |
return total + __getBytesRemaining_120(file.progress) | |
}, 0) | |
return Math.round(totalBytesRemaining / totalSpeed * 10) / 10 | |
} | |
render (state) { | |
const files = state.files | |
const uploadStartedFiles = Object.keys(files).filter((file) => { | |
return files[file].progress.uploadStarted | |
}) | |
const completeFiles = Object.keys(files).filter((file) => { | |
return files[file].progress.uploadComplete | |
}) | |
const inProgressFiles = Object.keys(files).filter((file) => { | |
return !files[file].progress.uploadComplete && | |
files[file].progress.uploadStarted && | |
!files[file].isPaused | |
}) | |
const processingFiles = Object.keys(files).filter((file) => { | |
return files[file].progress.preprocess || files[file].progress.postprocess | |
}) | |
let inProgressFilesArray = [] | |
inProgressFiles.forEach((file) => { | |
inProgressFilesArray.push(files[file]) | |
}) | |
const totalSpeed = _$prettierBytes_54(this.getTotalSpeed(inProgressFilesArray)) | |
const totalETA = __prettyETA_120(this.getTotalETA(inProgressFilesArray)) | |
// total size and uploaded size | |
let totalSize = 0 | |
let totalUploadedSize = 0 | |
inProgressFilesArray.forEach((file) => { | |
totalSize = totalSize + (file.progress.bytesTotal || 0) | |
totalUploadedSize = totalUploadedSize + (file.progress.bytesUploaded || 0) | |
}) | |
totalSize = _$prettierBytes_54(totalSize) | |
totalUploadedSize = _$prettierBytes_54(totalUploadedSize) | |
const isAllComplete = state.totalProgress === 100 && | |
completeFiles.length === Object.keys(files).length && | |
processingFiles.length === 0 | |
const isAllPaused = inProgressFiles.length === 0 && !isAllComplete && uploadStartedFiles.length > 0 | |
const isUploadStarted = uploadStartedFiles.length > 0 | |
const resumableUploads = this.core.getState().capabilities.resumableUploads || false | |
return _$StatusBar_119({ | |
error: state.error, | |
totalProgress: state.totalProgress, | |
totalSize: totalSize, | |
totalUploadedSize: totalUploadedSize, | |
uploadStartedFiles: uploadStartedFiles, | |
isAllComplete: isAllComplete, | |
isAllPaused: isAllPaused, | |
isUploadStarted: isUploadStarted, | |
pauseAll: this.pauseAll, | |
resumeAll: this.resumeAll, | |
cancelAll: this.cancelAll, | |
complete: completeFiles.length, | |
inProgress: uploadStartedFiles.length, | |
totalSpeed: totalSpeed, | |
totalETA: totalETA, | |
files: state.files, | |
resumableUploads: resumableUploads | |
}) | |
} | |
install () { | |
const target = this.opts.target | |
const plugin = this | |
this.target = this.mount(target, plugin) | |
} | |
uninstall () { | |
this.unmount() | |
} | |
} | |
/* removed: const _$Plugin_117 = require('../Plugin') */; | |
/* removed: const _$Translator_83 = require('../../core/Translator') */; | |
/* removed: const _$dragDrop_19 = require('drag-drop') */; | |
/* removed: const _$Dashboard_98 = require('./Dashboard') */; | |
/* removed: const _$StatusBarUI_120 = require('../StatusBar') */; | |
/* removed: const _$Informer_114 = require('../Informer') */; | |
const { findDOMElement: __findDOMElement_107 } = _$Utils_85 | |
/* removed: const _$prettierBytes_54 = require('prettier-bytes') */; | |
const { defaultTabIcon: __defaultTabIcon_107 } = _$icons_106 | |
/** | |
* Modal Dialog & Dashboard | |
*/ | |
var _$DashboardUI_107 = class DashboardUI extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.id = 'DashboardUI' | |
this.title = 'Dashboard UI' | |
this.type = 'orchestrator' | |
const defaultLocale = { | |
strings: { | |
selectToUpload: 'Select files to upload', | |
closeModal: 'Close Modal', | |
upload: 'Upload', | |
importFrom: 'Import files from', | |
dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)', | |
dashboardTitle: 'Uppy Dashboard', | |
copyLinkToClipboardSuccess: 'Link copied to clipboard.', | |
copyLinkToClipboardFallback: 'Copy the URL below', | |
done: 'Done', | |
localDisk: 'Local Disk', | |
myDevice: 'My Device', | |
dropPasteImport: 'Drop files here, paste, import from one of the locations above or', | |
dropPaste: 'Drop files here, paste or', | |
browse: 'browse', | |
fileProgress: 'File progress: upload speed and ETA', | |
numberOfSelectedFiles: 'Number of selected files', | |
uploadAllNewFiles: 'Upload all new files' | |
} | |
} | |
// set default options | |
const defaultOptions = { | |
target: 'body', | |
getMetaFromForm: true, | |
trigger: '#uppy-select-files', | |
inline: false, | |
width: 750, | |
height: 550, | |
semiTransparent: false, | |
defaultTabIcon: __defaultTabIcon_107(), | |
showProgressDetails: false, | |
hideUploadButton: false, | |
note: false, | |
locale: defaultLocale | |
} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.locale = Object.assign({}, defaultLocale, this.opts.locale) | |
this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings) | |
this.translator = new _$Translator_83({locale: this.locale}) | |
this.containerWidth = this.translator.translate.bind(this.translator) | |
this.hideModal = this.hideModal.bind(this) | |
this.showModal = this.showModal.bind(this) | |
this.addTarget = this.addTarget.bind(this) | |
this.actions = this.actions.bind(this) | |
this.hideAllPanels = this.hideAllPanels.bind(this) | |
this.showPanel = this.showPanel.bind(this) | |
this.initEvents = this.initEvents.bind(this) | |
this.handleEscapeKeyPress = this.handleEscapeKeyPress.bind(this) | |
this.handleFileCard = this.handleFileCard.bind(this) | |
this.handleDrop = this.handleDrop.bind(this) | |
this.pauseAll = this.pauseAll.bind(this) | |
this.resumeAll = this.resumeAll.bind(this) | |
this.cancelAll = this.cancelAll.bind(this) | |
this.updateDashboardElWidth = this.updateDashboardElWidth.bind(this) | |
this.render = this.render.bind(this) | |
this.install = this.install.bind(this) | |
} | |
addTarget (plugin) { | |
const callerPluginId = plugin.id || plugin.constructor.name | |
const callerPluginName = plugin.title || callerPluginId | |
const callerPluginIcon = plugin.icon || this.opts.defaultTabIcon | |
const callerPluginType = plugin.type | |
if (callerPluginType !== 'acquirer' && | |
callerPluginType !== 'progressindicator' && | |
callerPluginType !== 'presenter') { | |
let msg = 'Dashboard: Modal can only be used by plugins of types: acquirer, progressindicator, presenter' | |
this.core.log(msg) | |
return | |
} | |
const target = { | |
id: callerPluginId, | |
name: callerPluginName, | |
icon: callerPluginIcon, | |
type: callerPluginType, | |
focus: plugin.focus, | |
render: plugin.render, | |
isHidden: true | |
} | |
const modal = this.core.getState().modal | |
const newTargets = modal.targets.slice() | |
newTargets.push(target) | |
this.core.setState({ | |
modal: Object.assign({}, modal, { | |
targets: newTargets | |
}) | |
}) | |
return this.target | |
} | |
hideAllPanels () { | |
const modal = this.core.getState().modal | |
this.core.setState({modal: Object.assign({}, modal, { | |
activePanel: false | |
})}) | |
} | |
showPanel (id) { | |
const modal = this.core.getState().modal | |
const activePanel = modal.targets.filter((target) => { | |
return target.type === 'acquirer' && target.id === id | |
})[0] | |
this.core.setState({modal: Object.assign({}, modal, { | |
activePanel: activePanel | |
})}) | |
} | |
hideModal () { | |
const modal = this.core.getState().modal | |
this.core.setState({ | |
modal: Object.assign({}, modal, { | |
isHidden: true | |
}) | |
}) | |
document.body.classList.remove('is-UppyDashboard-open') | |
window.scrollTo(0, this.savedDocumentScrollPosition) | |
} | |
showModal () { | |
const modal = this.core.getState().modal | |
this.core.setState({ | |
modal: Object.assign({}, modal, { | |
isHidden: false | |
}) | |
}) | |
// save scroll position | |
this.savedDocumentScrollPosition = window.scrollY | |
// add class to body that sets position fixed, move everything back | |
// to scroll position | |
document.body.classList.add('is-UppyDashboard-open') | |
document.body.style.top = `-${this.savedDocumentScrollPosition}px` | |
// focus on modal inner block | |
this.target.querySelector('.UppyDashboard-inner').focus() | |
// this.updateDashboardElWidth() | |
// to be sure, sometimes when the function runs, container size is still 0 | |
setTimeout(this.updateDashboardElWidth, 500) | |
} | |
// Close the Modal on esc key press | |
handleEscapeKeyPress (event) { | |
if (event.keyCode === 27) { | |
this.hideModal() | |
} | |
} | |
initEvents () { | |
// Modal open button | |
const showModalTrigger = __findDOMElement_107(this.opts.trigger) | |
if (!this.opts.inline && showModalTrigger) { | |
showModalTrigger.addEventListener('click', this.showModal) | |
} | |
if (!this.opts.inline && !showModalTrigger) { | |
this.core.log('Dashboard modal trigger not found, you won’t be able to select files. Make sure `trigger` is set correctly in Dashboard options', 'error') | |
} | |
document.body.addEventListener('keyup', this.handleEscapeKeyPress) | |
// Drag Drop | |
this.removeDragDropListener = _$dragDrop_19(this.el, (files) => { | |
this.handleDrop(files) | |
}) | |
} | |
removeEvents () { | |
const showModalTrigger = __findDOMElement_107(this.opts.trigger) | |
if (!this.opts.inline && showModalTrigger) { | |
showModalTrigger.removeEventListener('click', this.showModal) | |
} | |
this.removeDragDropListener() | |
document.body.removeEventListener('keyup', this.handleEscapeKeyPress) | |
} | |
actions () { | |
this.core.on('core:file-added', this.hideAllPanels) | |
this.core.on('dashboard:file-card', this.handleFileCard) | |
window.addEventListener('resize', this.updateDashboardElWidth) | |
} | |
removeActions () { | |
window.removeEventListener('resize', this.updateDashboardElWidth) | |
this.core.off('core:file-added', this.hideAllPanels) | |
this.core.off('dashboard:file-card', this.handleFileCard) | |
} | |
updateDashboardElWidth () { | |
const dashboardEl = this.target.querySelector('.UppyDashboard-inner') | |
this.core.log(`Dashboard width: ${dashboardEl.offsetWidth}`) | |
const modal = this.core.getState().modal | |
this.core.setState({ | |
modal: Object.assign({}, modal, { | |
containerWidth: dashboardEl.offsetWidth | |
}) | |
}) | |
} | |
handleFileCard (fileId) { | |
const modal = this.core.getState().modal | |
this.core.setState({ | |
modal: Object.assign({}, modal, { | |
fileCardFor: fileId || false | |
}) | |
}) | |
} | |
handleDrop (files) { | |
this.core.log('All right, someone dropped something...') | |
files.forEach((file) => { | |
this.core.addFile({ | |
source: this.id, | |
name: file.name, | |
type: file.type, | |
data: file | |
}) | |
}) | |
} | |
cancelAll () { | |
this.core.emit('core:cancel-all') | |
} | |
pauseAll () { | |
this.core.emit('core:pause-all') | |
} | |
resumeAll () { | |
this.core.emit('core:resume-all') | |
} | |
render (state) { | |
const files = state.files | |
const newFiles = Object.keys(files).filter((file) => { | |
return !files[file].progress.uploadStarted | |
}) | |
const inProgressFiles = Object.keys(files).filter((file) => { | |
return !files[file].progress.uploadComplete && | |
files[file].progress.uploadStarted && | |
!files[file].isPaused | |
}) | |
let inProgressFilesArray = [] | |
inProgressFiles.forEach((file) => { | |
inProgressFilesArray.push(files[file]) | |
}) | |
// total size and uploaded size | |
let totalSize = 0 | |
let totalUploadedSize = 0 | |
inProgressFilesArray.forEach((file) => { | |
totalSize = totalSize + (file.progress.bytesTotal || 0) | |
totalUploadedSize = totalUploadedSize + (file.progress.bytesUploaded || 0) | |
}) | |
totalSize = _$prettierBytes_54(totalSize) | |
totalUploadedSize = _$prettierBytes_54(totalUploadedSize) | |
const acquirers = state.modal.targets.filter((target) => { | |
return target.type === 'acquirer' | |
}) | |
const progressindicators = state.modal.targets.filter((target) => { | |
return target.type === 'progressindicator' | |
}) | |
// const addFile = (file) => { | |
// this.core.emitter.emit('core:file-add', file) | |
// } | |
const removeFile = (fileID) => { | |
this.core.emitter.emit('core:file-remove', fileID) | |
} | |
const startUpload = (ev) => { | |
this.core.upload().catch((err) => { | |
// Log error. | |
console.error(err.stack || err.message || err) | |
}) | |
} | |
const pauseUpload = (fileID) => { | |
this.core.emitter.emit('core:upload-pause', fileID) | |
} | |
const cancelUpload = (fileID) => { | |
this.core.emitter.emit('core:upload-cancel', fileID) | |
this.core.emitter.emit('core:file-remove', fileID) | |
} | |
const showFileCard = (fileID) => { | |
this.core.emitter.emit('dashboard:file-card', fileID) | |
} | |
const fileCardDone = (meta, fileID) => { | |
this.core.emitter.emit('core:update-meta', meta, fileID) | |
this.core.emitter.emit('dashboard:file-card') | |
} | |
const info = (text, type, duration) => { | |
this.core.info(text, type, duration) | |
} | |
const resumableUploads = this.core.getState().capabilities.resumableUploads || false | |
return _$Dashboard_98({ | |
state: state, | |
modal: state.modal, | |
newFiles: newFiles, | |
files: files, | |
totalFileCount: Object.keys(files).length, | |
totalProgress: state.totalProgress, | |
acquirers: acquirers, | |
activePanel: state.modal.activePanel, | |
progressindicators: progressindicators, | |
autoProceed: this.core.opts.autoProceed, | |
hideUploadButton: this.opts.hideUploadButton, | |
id: this.id, | |
hideModal: this.hideModal, | |
showProgressDetails: this.opts.showProgressDetails, | |
inline: this.opts.inline, | |
semiTransparent: this.opts.semiTransparent, | |
showPanel: this.showPanel, | |
hideAllPanels: this.hideAllPanels, | |
log: this.core.log, | |
i18n: this.containerWidth, | |
pauseAll: this.pauseAll, | |
resumeAll: this.resumeAll, | |
addFile: this.core.addFile, | |
removeFile: removeFile, | |
info: info, | |
note: this.opts.note, | |
metaFields: state.metaFields, | |
resumableUploads: resumableUploads, | |
startUpload: startUpload, | |
pauseUpload: pauseUpload, | |
cancelUpload: cancelUpload, | |
fileCardFor: state.modal.fileCardFor, | |
showFileCard: showFileCard, | |
fileCardDone: fileCardDone, | |
updateDashboardElWidth: this.updateDashboardElWidth, | |
maxWidth: this.opts.maxWidth, | |
maxHeight: this.opts.maxHeight, | |
currentWidth: state.modal.containerWidth, | |
isWide: state.modal.containerWidth > 400 | |
}) | |
} | |
install () { | |
// Set default state for Modal | |
this.core.setState({modal: { | |
isHidden: true, | |
showFileCard: false, | |
activePanel: false, | |
targets: [] | |
}}) | |
const target = this.opts.target | |
const plugin = this | |
this.target = this.mount(target, plugin) | |
if (!this.opts.disableStatusBar) { | |
this.core.use(_$StatusBarUI_120, { | |
target: DashboardUI | |
}) | |
} | |
if (!this.opts.disableInformer) { | |
this.core.use(_$Informer_114, { | |
target: DashboardUI | |
}) | |
} | |
this.initEvents() | |
this.actions() | |
} | |
uninstall () { | |
this.unmount() | |
this.removeActions() | |
this.removeEvents() | |
} | |
} | |
/* removed: const _$Plugin_117 = require('./../Plugin') */; | |
/* removed: const _$Translator_83 = require('../../core/Translator') */; | |
const { toArray: __toArray_108 } = _$Utils_85 | |
/* removed: const _$dragDrop_19 = require('drag-drop') */; | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/** | |
* Drag & Drop plugin | |
* | |
*/ | |
var _$DragDrop_108 = class DragDrop extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.type = 'acquirer' | |
this.id = 'DragDrop' | |
this.title = 'Drag & Drop' | |
this.icon = _$yoYo_80` | |
<svg class="UppyIcon" width="28" height="28" viewBox="0 0 16 16"> | |
<path d="M15.982 2.97c0-.02 0-.02-.018-.037 0-.017-.017-.035-.035-.053 0 0 0-.018-.02-.018-.017-.018-.034-.053-.052-.07L13.19.123c-.017-.017-.034-.035-.07-.053h-.018c-.018-.017-.035-.017-.053-.034h-.02c-.017 0-.034-.018-.052-.018h-6.31a.415.415 0 0 0-.446.426V11.11c0 .25.196.446.445.446h8.89A.44.44 0 0 0 16 11.11V3.023c-.018-.018-.018-.035-.018-.053zm-2.65-1.46l1.157 1.157h-1.157V1.51zm1.78 9.157h-8V.89h5.332v2.22c0 .25.196.446.445.446h2.22v7.11z"/> | |
<path d="M9.778 12.89H4V2.666a.44.44 0 0 0-.444-.445.44.44 0 0 0-.445.445v10.666c0 .25.197.445.446.445h6.222a.44.44 0 0 0 .444-.445.44.44 0 0 0-.444-.444z"/> | |
<path d="M.444 16h6.223a.44.44 0 0 0 .444-.444.44.44 0 0 0-.443-.445H.89V4.89a.44.44 0 0 0-.446-.446A.44.44 0 0 0 0 4.89v10.666c0 .248.196.444.444.444z"/> | |
</svg> | |
` | |
const defaultLocale = { | |
strings: { | |
chooseFile: 'Choose a file', | |
orDragDrop: 'or drop it here', | |
upload: 'Upload', | |
selectedFiles: { | |
0: '%{smart_count} file selected', | |
1: '%{smart_count} files selected' | |
} | |
} | |
} | |
// Default options | |
const defaultOpts = { | |
target: '.UppyDragDrop', | |
getMetaFromForm: true, | |
locale: defaultLocale | |
} | |
// Merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOpts, opts) | |
// Check for browser dragDrop support | |
this.isDragDropSupported = this.checkDragDropSupport() | |
this.locale = Object.assign({}, defaultLocale, this.opts.locale) | |
this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings) | |
// i18n | |
this.translator = new _$Translator_83({locale: this.locale}) | |
this.i18n = this.translator.translate.bind(this.translator) | |
// Bind `this` to class methods | |
this.handleDrop = this.handleDrop.bind(this) | |
this.checkDragDropSupport = this.checkDragDropSupport.bind(this) | |
this.handleInputChange = this.handleInputChange.bind(this) | |
this.render = this.render.bind(this) | |
} | |
/** | |
* Checks if the browser supports Drag & Drop (not supported on mobile devices, for example). | |
* @return {Boolean} true if supported, false otherwise | |
*/ | |
checkDragDropSupport () { | |
const div = document.createElement('div') | |
if (!('draggable' in div) || !('ondragstart' in div && 'ondrop' in div)) { | |
return false | |
} | |
if (!('FormData' in window)) { | |
return false | |
} | |
if (!('FileReader' in window)) { | |
return false | |
} | |
return true | |
} | |
handleDrop (files) { | |
this.core.log('All right, someone dropped something...') | |
files.forEach((file) => { | |
this.core.addFile({ | |
source: this.id, | |
name: file.name, | |
type: file.type, | |
data: file | |
}) | |
}) | |
} | |
handleInputChange (ev) { | |
this.core.log('All right, something selected through input...') | |
const files = __toArray_108(ev.target.files) | |
files.forEach((file) => { | |
this.core.addFile({ | |
source: this.id, | |
name: file.name, | |
type: file.type, | |
data: file | |
}) | |
}) | |
} | |
render (state) { | |
const onSelect = (ev) => { | |
const input = this.target.querySelector('.UppyDragDrop-input') | |
input.click() | |
} | |
const selectedFilesCount = Object.keys(state.files).length | |
return _$yoYo_80` | |
<div class="Uppy UppyTheme--default UppyDragDrop-container ${this.isDragDropSupported ? 'is-dragdrop-supported' : ''}"> | |
<form class="UppyDragDrop-inner" | |
onsubmit=${(ev) => ev.preventDefault()}> | |
<input class="UppyDragDrop-input UppyDragDrop-focus" | |
type="file" | |
name="files[]" | |
multiple="true" | |
value="" | |
onchange=${this.handleInputChange.bind(this)} /> | |
<label class="UppyDragDrop-label" onclick=${onSelect}> | |
<strong>${this.i18n('chooseFile')}</strong> | |
<span class="UppyDragDrop-dragText">${this.i18n('orDragDrop')}</span> | |
</label> | |
${selectedFilesCount > 0 | |
? _$yoYo_80`<div class="UppyDragDrop-selectedCount"> | |
${this.i18n('selectedFiles', {'smart_count': selectedFilesCount})} | |
</div>` | |
: ''} | |
</form> | |
</div> | |
` | |
} | |
install () { | |
const target = this.opts.target | |
const plugin = this | |
this.target = this.mount(target, plugin) | |
const dndContainer = this.target.querySelector('.UppyDragDrop-container') | |
this.removeDragDropListener = _$dragDrop_19(dndContainer, (files) => { | |
this.handleDrop(files) | |
this.core.log(files) | |
}) | |
} | |
uninstall () { | |
this.removeDragDropListener() | |
this.unmount() | |
} | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
var _$icons_109 = { | |
folder: () => | |
_$yoYo_80`<svg class="UppyIcon" style="width:16px;margin-right:3px" viewBox="0 0 276.157 276.157"> | |
<path d="M273.08 101.378c-3.3-4.65-8.86-7.32-15.254-7.32h-24.34V67.59c0-10.2-8.3-18.5-18.5-18.5h-85.322c-3.63 0-9.295-2.875-11.436-5.805l-6.386-8.735c-4.982-6.814-15.104-11.954-23.546-11.954H58.73c-9.292 0-18.638 6.608-21.737 15.372l-2.033 5.752c-.958 2.71-4.72 5.37-7.596 5.37H18.5C8.3 49.09 0 57.39 0 67.59v167.07c0 .886.16 1.73.443 2.52.152 3.306 1.18 6.424 3.053 9.064 3.3 4.652 8.86 7.32 15.255 7.32h188.487c11.395 0 23.27-8.425 27.035-19.18l40.677-116.188c2.11-6.035 1.43-12.164-1.87-16.816zM18.5 64.088h8.864c9.295 0 18.64-6.607 21.738-15.37l2.032-5.75c.96-2.712 4.722-5.373 7.597-5.373h29.565c3.63 0 9.295 2.876 11.437 5.806l6.386 8.735c4.982 6.815 15.104 11.954 23.546 11.954h85.322c1.898 0 3.5 1.602 3.5 3.5v26.47H69.34c-11.395 0-23.27 8.423-27.035 19.178L15 191.23V67.59c0-1.898 1.603-3.5 3.5-3.5zm242.29 49.15l-40.676 116.188c-1.674 4.78-7.812 9.135-12.877 9.135H18.75c-1.447 0-2.576-.372-3.02-.997-.442-.625-.422-1.814.057-3.18l40.677-116.19c1.674-4.78 7.812-9.134 12.877-9.134h188.487c1.448 0 2.577.372 3.02.997.443.625.423 1.814-.056 3.18z"/> | |
</svg>`, | |
music: () => | |
_$yoYo_80`<svg class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 48.000000" | |
preserveAspectRatio="xMidYMid meet"> | |
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)" | |
fill="#525050" stroke="none"> | |
<path d="M209 473 c0 -5 0 -52 1 -106 1 -54 -2 -118 -6 -143 l-7 -46 -44 5 | |
c-73 8 -133 -46 -133 -120 0 -17 -5 -35 -10 -38 -18 -11 0 -25 33 -24 30 1 30 | |
1 7 8 -15 4 -20 10 -13 14 6 4 9 16 6 27 -9 34 7 70 40 90 17 11 39 20 47 20 | |
8 0 -3 -9 -26 -19 -42 -19 -54 -36 -54 -75 0 -36 30 -56 84 -56 41 0 53 5 82 | |
34 19 19 34 31 34 27 0 -4 -5 -12 -12 -19 -9 -9 -1 -12 39 -12 106 0 183 -21 | |
121 -33 -17 -3 -14 -5 10 -6 25 -1 32 3 32 17 0 26 -20 42 -51 42 -39 0 -43 | |
13 -10 38 56 41 76 124 45 185 -25 48 -72 105 -103 123 -15 9 -36 29 -47 45 | |
-17 26 -63 41 -65 22z m56 -48 c16 -24 31 -42 34 -39 9 9 79 -69 74 -83 -3 -7 | |
-2 -13 3 -12 18 3 25 -1 19 -12 -5 -7 -16 -2 -33 13 l-26 23 16 -25 c17 -27 | |
29 -92 16 -84 -4 3 -8 -8 -8 -25 0 -16 4 -33 10 -36 5 -3 7 0 4 9 -3 9 3 20 | |
15 28 13 8 21 24 22 43 1 18 3 23 6 12 3 -10 2 -29 -1 -43 -7 -26 -62 -94 -77 | |
-94 -13 0 -11 17 4 32 21 19 4 88 -28 115 -14 13 -22 23 -16 23 5 0 21 -14 35 | |
-31 14 -17 26 -25 26 -19 0 21 -60 72 -79 67 -16 -4 -17 -1 -8 34 6 24 14 36 | |
21 32 6 -3 1 5 -11 18 -12 13 -22 29 -23 34 -1 6 -6 17 -12 25 -6 10 -7 -39 | |
-4 -142 l6 -158 -26 10 c-33 13 -44 12 -21 -1 17 -10 24 -44 10 -52 -5 -3 -39 | |
-8 -76 -12 -68 -7 -69 -7 -65 17 4 28 64 60 117 62 l36 1 0 157 c0 87 2 158 5 | |
158 3 0 18 -20 35 -45z m15 -159 c0 -2 -7 -7 -16 -10 -8 -3 -12 -2 -9 4 6 10 | |
25 14 25 6z m50 -92 c0 -13 -4 -26 -10 -29 -14 -9 -13 -48 2 -63 9 -9 6 -12 | |
-15 -12 -22 0 -27 5 -27 24 0 14 -4 28 -10 31 -15 9 -13 102 3 108 18 7 57 | |
-33 57 -59z m-139 -135 c-32 -26 -121 -25 -121 2 0 6 8 5 19 -1 26 -14 64 -13 | |
55 1 -4 8 1 9 16 4 13 -4 20 -3 17 2 -3 5 4 10 16 10 22 2 22 2 -2 -18z"/> | |
<path d="M330 345 c19 -19 36 -35 39 -35 3 0 -10 16 -29 35 -19 19 -36 35 -39 | |
35 -3 0 10 -16 29 -35z"/> | |
<path d="M349 123 c-13 -16 -12 -17 4 -4 16 13 21 21 13 21 -2 0 -10 -8 -17 | |
-17z"/> | |
<path d="M243 13 c15 -2 39 -2 55 0 15 2 2 4 -28 4 -30 0 -43 -2 -27 -4z"/> | |
</g> | |
</svg>`, | |
page_white_picture: () => | |
_$yoYo_80` | |
<svg class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 36.000000" | |
preserveAspectRatio="xMidYMid meet"> | |
<g transform="translate(0.000000,36.000000) scale(0.100000,-0.100000)" | |
fill="#565555" stroke="none"> | |
<path d="M0 180 l0 -180 240 0 240 0 0 180 0 180 -240 0 -240 0 0 -180z m470 | |
0 l0 -170 -230 0 -230 0 0 170 0 170 230 0 230 0 0 -170z"/> | |
<path d="M40 185 l0 -135 200 0 200 0 0 135 0 135 -200 0 -200 0 0 -135z m390 | |
59 l0 -65 -29 20 c-37 27 -45 26 -65 -4 -9 -14 -22 -25 -28 -25 -7 0 -24 -12 | |
-39 -26 -26 -25 -28 -25 -53 -9 -17 11 -26 13 -26 6 0 -7 -4 -9 -10 -6 -5 3 | |
-22 -2 -37 -12 l-28 -18 20 27 c11 15 26 25 33 23 6 -2 12 -1 12 4 0 10 -37 | |
21 -65 20 -14 -1 -12 -3 7 -8 l28 -6 -50 -55 -49 -55 0 126 1 126 189 1 189 2 | |
0 -66z m-16 -73 c11 -12 14 -21 8 -21 -6 0 -13 4 -17 10 -3 5 -12 7 -19 4 -8 | |
-3 -16 2 -19 13 -3 11 -4 7 -4 -9 1 -19 6 -25 18 -23 19 4 46 -21 35 -32 -4 | |
-4 -11 -1 -16 7 -6 8 -10 10 -10 4 0 -6 7 -17 15 -24 24 -20 11 -24 -76 -27 | |
-69 -1 -83 1 -97 18 -9 10 -20 19 -25 19 -5 0 -4 -6 2 -14 14 -17 -5 -26 -55 | |
-26 -36 0 -46 16 -17 27 10 4 22 13 27 22 8 13 10 12 17 -4 7 -17 8 -18 8 -2 | |
1 23 11 22 55 -8 33 -22 35 -23 26 -5 -9 16 -8 20 5 20 8 0 15 5 15 11 0 5 -4 | |
7 -10 4 -5 -3 -10 -4 -10 -1 0 4 59 36 67 36 2 0 1 -10 -2 -21 -5 -15 -4 -19 | |
5 -14 6 4 9 17 6 28 -12 49 27 53 68 8z"/> | |
<path d="M100 296 c0 -2 7 -7 16 -10 8 -3 12 -2 9 4 -6 10 -25 14 -25 6z"/> | |
<path d="M243 293 c9 -2 23 -2 30 0 6 3 -1 5 -18 5 -16 0 -22 -2 -12 -5z"/> | |
<path d="M65 280 c-3 -5 -2 -10 4 -10 5 0 13 5 16 10 3 6 2 10 -4 10 -5 0 -13 | |
-4 -16 -10z"/> | |
<path d="M155 270 c-3 -6 1 -7 9 -4 18 7 21 14 7 14 -6 0 -13 -4 -16 -10z"/> | |
<path d="M233 252 c-13 -2 -23 -8 -23 -13 0 -7 -12 -8 -30 -4 -22 5 -30 3 -30 | |
-7 0 -10 -2 -10 -9 1 -5 8 -19 12 -35 9 -14 -3 -27 -1 -30 4 -2 5 -4 4 -3 -3 | |
2 -6 6 -10 10 -10 3 0 20 -4 37 -9 18 -5 32 -5 36 1 3 6 13 8 21 5 13 -5 113 | |
21 113 30 0 3 -19 2 -57 -4z"/> | |
<path d="M275 220 c-13 -6 -15 -9 -5 -9 8 0 22 4 30 9 18 12 2 12 -25 0z"/> | |
<path d="M132 23 c59 -2 158 -2 220 0 62 1 14 3 -107 3 -121 0 -172 -2 -113 | |
-3z"/> | |
</g> | |
</svg>`, | |
word: () => | |
_$yoYo_80`<svg class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 48.000000" | |
preserveAspectRatio="xMidYMid meet"> | |
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)" | |
fill="#423d3d" stroke="none"> | |
<path d="M0 466 c0 -15 87 -26 213 -26 l77 0 0 -140 0 -140 -77 0 c-105 0 | |
-213 -11 -213 -21 0 -5 15 -9 34 -9 25 0 33 -4 33 -17 0 -74 4 -113 13 -113 6 | |
0 10 32 10 75 l0 75 105 0 105 0 0 150 0 150 -105 0 c-87 0 -105 3 -105 15 0 | |
11 -12 15 -45 15 -31 0 -45 -4 -45 -14z"/> | |
<path d="M123 468 c-2 -5 50 -8 116 -8 l121 0 0 -50 c0 -46 -2 -50 -23 -50 | |
-14 0 -24 -6 -24 -15 0 -8 4 -15 9 -15 4 0 8 -20 8 -45 0 -25 -4 -45 -8 -45 | |
-5 0 -9 -7 -9 -15 0 -9 10 -15 24 -15 22 0 23 3 23 75 l0 75 50 0 50 0 0 -170 | |
0 -170 -175 0 -175 0 -2 63 c-2 59 -2 60 -5 13 -3 -27 -2 -60 2 -73 l5 -23 | |
183 2 182 3 2 216 c3 275 19 254 -194 254 -85 0 -157 -3 -160 -7z m337 -85 c0 | |
-2 -18 -3 -39 -3 -39 0 -39 0 -43 45 l-3 44 42 -41 c24 -23 43 -43 43 -45z | |
m-19 50 c19 -22 23 -29 9 -18 -36 30 -50 43 -50 49 0 11 6 6 41 -31z"/> | |
<path d="M4 300 c0 -74 1 -105 3 -67 2 37 2 97 0 135 -2 37 -3 6 -3 -68z"/> | |
<path d="M20 300 l0 -131 128 3 127 3 3 128 3 127 -131 0 -130 0 0 -130z m250 | |
100 c0 -16 -7 -20 -33 -20 -31 0 -34 -2 -34 -31 0 -28 2 -30 13 -14 8 10 11 | |
22 8 26 -3 5 1 9 9 9 11 0 9 -12 -12 -50 -14 -27 -32 -50 -39 -50 -15 0 -31 | |
38 -26 63 2 10 -1 15 -8 11 -6 -4 -9 -1 -6 6 2 8 10 16 16 18 8 2 12 -10 12 | |
-38 0 -38 2 -41 16 -29 9 7 12 15 7 16 -5 2 -7 17 -5 33 4 26 1 30 -20 30 -17 | |
0 -29 -9 -39 -27 -20 -41 -22 -50 -6 -30 14 17 15 16 20 -5 4 -13 2 -40 -2 | |
-60 -9 -37 -8 -38 20 -38 26 0 33 8 64 70 19 39 37 70 40 70 3 0 5 -40 5 -90 | |
l0 -90 -120 0 -120 0 0 120 0 120 120 0 c113 0 120 -1 120 -20z"/> | |
<path d="M40 371 c0 -6 5 -13 10 -16 6 -3 10 -35 10 -71 0 -57 2 -64 20 -64 | |
13 0 27 14 40 40 25 49 25 63 0 30 -19 -25 -39 -23 -24 2 5 7 7 23 6 35 -2 11 | |
2 24 7 28 23 13 9 25 -29 25 -22 0 -40 -4 -40 -9z m53 -9 c-6 -4 -13 -28 -15 | |
-52 l-3 -45 -5 53 c-5 47 -3 52 15 52 13 0 16 -3 8 -8z"/> | |
<path d="M313 165 c0 -9 10 -15 24 -15 14 0 23 6 23 15 0 9 -9 15 -23 15 -14 | |
0 -24 -6 -24 -15z"/> | |
<path d="M180 105 c0 -12 17 -15 90 -15 73 0 90 3 90 15 0 12 -17 15 -90 15 | |
-73 0 -90 -3 -90 -15z"/> | |
</g> | |
</svg>`, | |
powerpoint: () => | |
_$yoYo_80`<svg class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000" | |
preserveAspectRatio="xMidYMid meet"> | |
<g transform="translate(0.000000,144.000000) scale(0.100000,-0.100000)" | |
fill="#494747" stroke="none"> | |
<path d="M0 1390 l0 -50 93 0 c50 0 109 -3 130 -6 l37 -7 0 57 0 56 -130 0 | |
-130 0 0 -50z"/> | |
<path d="M870 1425 c0 -8 -12 -18 -27 -22 l-28 -6 30 -9 c17 -5 75 -10 130 | |
-12 86 -2 100 -5 99 -19 0 -10 -1 -80 -2 -157 l-2 -140 -65 0 c-60 0 -80 -9 | |
-55 -25 8 -5 7 -11 -1 -21 -17 -20 2 -25 112 -27 l94 -2 0 40 0 40 100 5 c55 | |
3 104 3 108 -1 8 -6 11 -1008 4 -1016 -2 -2 -236 -4 -520 -6 -283 -1 -519 -5 | |
-523 -9 -4 -4 -1 -14 6 -23 11 -13 82 -15 561 -15 l549 0 0 570 c0 543 -1 570 | |
-18 570 -10 0 -56 39 -103 86 -46 47 -93 90 -104 95 -11 6 22 -31 73 -82 50 | |
-50 92 -95 92 -99 0 -14 -23 -16 -136 -12 l-111 4 -6 124 c-6 119 -7 126 -32 | |
145 -14 12 -23 25 -20 30 4 5 -38 9 -99 9 -87 0 -106 -3 -106 -15z"/> | |
<path d="M1190 1429 c0 -14 225 -239 239 -239 7 0 11 30 11 85 0 77 -2 85 -19 | |
85 -21 0 -61 44 -61 66 0 11 -20 14 -85 14 -55 0 -85 -4 -85 -11z"/> | |
<path d="M281 1331 c-24 -16 7 -23 127 -31 100 -6 107 -7 47 -9 -38 -1 -142 | |
-8 -229 -14 l-160 -12 -7 -28 c-10 -37 -16 -683 -6 -693 4 -4 10 -4 15 0 4 4 | |
8 166 9 359 l2 352 358 -3 358 -2 5 -353 c3 -193 2 -356 -2 -361 -3 -4 -136 | |
-8 -295 -7 -290 2 -423 -4 -423 -20 0 -5 33 -9 73 -9 39 0 90 -3 111 -7 l39 | |
-6 -45 -18 c-26 -10 -90 -20 -151 -25 l-107 -7 0 -38 c0 -35 3 -39 24 -39 36 | |
0 126 -48 128 -68 1 -9 2 -40 3 -69 2 -29 6 -91 10 -138 l7 -85 44 0 44 0 0 | |
219 0 220 311 1 c172 0 314 2 318 4 5 4 6 301 2 759 l-1 137 -297 0 c-164 0 | |
-304 -4 -312 -9z"/> | |
<path d="M2 880 c-1 -276 2 -378 10 -360 12 30 11 657 -2 710 -5 21 -8 -121 | |
-8 -350z"/> | |
<path d="M145 1178 c-3 -8 -4 -141 -3 -298 l3 -285 295 0 295 0 0 295 0 295 | |
-293 3 c-230 2 -294 0 -297 -10z m553 -27 c11 -6 13 -60 11 -260 -1 -139 -6 | |
-254 -9 -256 -4 -3 -124 -6 -266 -7 l-259 -3 -3 255 c-1 140 0 260 3 267 3 10 | |
62 13 257 13 139 0 259 -4 266 -9z"/> | |
<path d="M445 1090 l-210 -5 -3 -37 -3 -38 225 0 226 0 0 34 c0 18 -6 37 -12 | |
42 -7 5 -107 7 -223 4z"/> | |
<path d="M295 940 c-3 -6 1 -12 9 -15 9 -3 23 -7 31 -10 10 -3 15 -18 15 -49 | |
0 -25 3 -47 8 -49 15 -9 47 11 52 33 9 38 28 34 41 -8 10 -35 9 -43 -7 -66 | |
-23 -31 -51 -34 -56 -4 -4 31 -26 34 -38 4 -5 -14 -12 -26 -16 -26 -4 0 -22 | |
16 -41 36 -33 35 -34 40 -28 86 7 48 6 50 -16 46 -18 -2 -23 -9 -21 -23 2 -11 | |
3 -49 3 -85 0 -72 6 -83 60 -111 57 -29 95 -25 144 15 37 31 46 34 83 29 40 | |
-5 42 -5 42 21 0 24 -3 27 -27 24 -24 -3 -28 1 -31 25 -3 24 0 28 20 25 13 -2 | |
23 2 23 7 0 6 -9 9 -20 8 -13 -2 -28 9 -44 32 -13 19 -31 35 -41 35 -10 0 -23 | |
7 -30 15 -14 17 -105 21 -115 5z"/> | |
<path d="M522 919 c-28 -11 -20 -29 14 -29 14 0 24 6 24 14 0 21 -11 25 -38 | |
15z"/> | |
<path d="M623 922 c-53 -5 -43 -32 12 -32 32 0 45 4 45 14 0 17 -16 22 -57 18z"/> | |
<path d="M597 854 c-13 -14 6 -24 44 -24 28 0 39 4 39 15 0 11 -11 15 -38 15 | |
-21 0 -42 -3 -45 -6z"/> | |
<path d="M597 794 c-4 -4 -7 -18 -7 -31 0 -21 4 -23 46 -23 44 0 45 1 42 28 | |
-3 23 -8 27 -38 30 -20 2 -39 0 -43 -4z"/> | |
<path d="M989 883 c-34 -4 -37 -6 -37 -37 0 -32 2 -34 45 -40 25 -3 72 -6 104 | |
-6 l59 0 0 45 0 45 -67 -2 c-38 -1 -84 -3 -104 -5z"/> | |
<path d="M993 703 c-42 -4 -54 -15 -33 -28 8 -5 8 -11 0 -20 -16 -20 -3 -24 | |
104 -31 l96 -7 0 47 0 46 -62 -2 c-35 -1 -82 -3 -105 -5z"/> | |
<path d="M1005 523 c-50 -6 -59 -12 -46 -26 8 -10 7 -17 -1 -25 -6 -6 -9 -14 | |
-6 -17 3 -3 51 -8 107 -12 l101 -6 0 46 0 47 -62 -1 c-35 -1 -76 -4 -93 -6z"/> | |
<path d="M537 344 c-4 -4 -7 -25 -7 -46 l0 -38 46 0 45 0 -3 43 c-3 40 -4 42 | |
-38 45 -20 2 -39 0 -43 -4z"/> | |
<path d="M714 341 c-2 -2 -4 -22 -4 -43 l0 -38 225 0 225 0 0 45 0 46 -221 -3 | |
c-121 -2 -222 -5 -225 -7z"/> | |
<path d="M304 205 c0 -66 1 -92 3 -57 2 34 2 88 0 120 -2 31 -3 3 -3 -63z"/> | |
</g> | |
</svg>`, | |
page_white: () => | |
_$yoYo_80`<svg class="UppyIcon" width="16.000000pt" height="16.000000pt" viewBox="0 0 48.000000 48.000000" | |
preserveAspectRatio="xMidYMid meet"> | |
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)" | |
fill="#000000" stroke="none"> | |
<path d="M20 240 c1 -202 3 -240 16 -240 12 0 14 38 14 240 0 208 -2 240 -15 | |
240 -13 0 -15 -31 -15 -240z"/> | |
<path d="M75 471 c-4 -8 32 -11 119 -11 l126 0 0 -50 0 -50 50 0 c28 0 50 5 | |
50 10 0 6 -18 10 -40 10 l-40 0 0 42 0 42 43 -39 42 -40 -43 45 -42 45 -129 3 | |
c-85 2 -131 0 -136 -7z"/> | |
<path d="M398 437 l42 -43 0 -197 c0 -168 2 -197 15 -197 13 0 15 29 15 198 | |
l0 198 -36 42 c-21 25 -44 42 -57 42 -18 0 -16 -6 21 -43z"/> | |
<path d="M92 353 l2 -88 3 78 4 77 89 0 89 0 8 -42 c8 -43 9 -43 55 -46 44 -3 | |
47 -5 51 -35 4 -31 4 -31 5 6 l2 37 -50 0 -50 0 0 50 0 50 -105 0 -105 0 2 | |
-87z"/> | |
<path d="M75 10 c8 -13 332 -13 340 0 4 7 -55 10 -170 10 -115 0 -174 -3 -170 | |
-10z"/> | |
</g> | |
</svg>` | |
} | |
'use strict' | |
_$fetch_77 | |
const _getName = (id) => { | |
return id.split('-').map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(' ') | |
} | |
var _$Provider_135 = class Provider { | |
constructor (core, opts) { | |
this.core = core | |
this.opts = opts | |
this.provider = opts.provider | |
this.id = this.provider | |
this.authProvider = opts.authProvider || this.provider | |
this.name = this.opts.name || _getName(this.id) | |
this.onReceiveResponse = this.onReceiveResponse.bind(this) | |
} | |
get hostname () { | |
const uppyServer = this.core.state.uppyServer || {} | |
const host = this.opts.host | |
return uppyServer[host] || host | |
} | |
onReceiveResponse (response) { | |
const uppyServer = this.core.state.uppyServer || {} | |
const host = this.opts.host | |
const headers = response.headers | |
// Store the self-identified domain name for the uppy-server we just hit. | |
if (headers.has('i-am') && headers.get('i-am') !== uppyServer[host]) { | |
this.core.setState({ | |
uppyServer: Object.assign({}, uppyServer, { | |
[host]: headers.get('i-am') | |
}) | |
}) | |
} | |
return response | |
} | |
checkAuth () { | |
return fetch(`${this.hostname}/${this.id}/authorized`, { | |
method: 'get', | |
credentials: 'include', | |
headers: { | |
'Accept': 'application/json', | |
'Content-Type': 'application/json' | |
} | |
}) | |
.then(this.onReceiveResponse) | |
.then((res) => { | |
return res.json() | |
.then((payload) => { | |
return payload.authenticated | |
}) | |
}) | |
} | |
authUrl () { | |
return `${this.opts.host}/${this.id}/connect` | |
} | |
fileUrl (id) { | |
return `${this.opts.host}/${this.id}/get/${id}` | |
} | |
list (directory) { | |
return fetch(`${this.hostname}/${this.id}/list/${directory || ''}`, { | |
method: 'get', | |
credentials: 'include', | |
headers: { | |
'Accept': 'application/json', | |
'Content-Type': 'application/json' | |
} | |
}) | |
.then(this.onReceiveResponse) | |
.then((res) => res.json()) | |
} | |
logout (redirect = location.href) { | |
return fetch(`${this.hostname}/${this.id}/logout?redirect=${redirect}`, { | |
method: 'get', | |
credentials: 'include', | |
headers: { | |
'Accept': 'application/json', | |
'Content-Type': 'application/json' | |
} | |
}) | |
} | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$Plugin_117 = require('../Plugin') */; | |
/* removed: const _$Provider_135 = require('../../uppy-base/src/plugins/Provider') */; | |
/* removed: const _$View_95 = require('../../generic-provider-views/index') */; | |
/* removed: const _$icons_109 = require('./icons') */; | |
var _$Dropbox_110 = class Dropbox extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.type = 'acquirer' | |
this.id = 'Dropbox' | |
this.title = 'Dropbox' | |
this.stateId = 'dropbox' | |
this.icon = () => _$yoYo_80` | |
<svg class="UppyIcon" width="128" height="118" viewBox="0 0 128 118"> | |
<path d="M38.145.777L1.108 24.96l25.608 20.507 37.344-23.06z"/> | |
<path d="M1.108 65.975l37.037 24.183L64.06 68.525l-37.343-23.06zM64.06 68.525l25.917 21.633 37.036-24.183-25.61-20.51z"/> | |
<path d="M127.014 24.96L89.977.776 64.06 22.407l37.345 23.06zM64.136 73.18l-25.99 21.567-11.122-7.262v8.142l37.112 22.256 37.114-22.256v-8.142l-11.12 7.262z"/> | |
</svg> | |
` | |
// writing out the key explicitly for readability the key used to store | |
// the provider instance must be equal to this.id. | |
this.Dropbox = new _$Provider_135(core, { | |
host: this.opts.host, | |
provider: 'dropbox' | |
}) | |
this.files = [] | |
this.onAuth = this.onAuth.bind(this) | |
// Visual | |
this.render = this.render.bind(this) | |
// set default options | |
const defaultOptions = {} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
} | |
install () { | |
this.view = new _$View_95(this) | |
// Set default state | |
this.core.setState({ | |
// writing out the key explicitly for readability the key used to store | |
// the plugin state must be equal to this.stateId. | |
dropbox: { | |
authenticated: false, | |
files: [], | |
folders: [], | |
directories: [], | |
activeRow: -1, | |
filterInput: '', | |
isSearchVisible: false | |
} | |
}) | |
const target = this.opts.target | |
const plugin = this | |
this.target = this.mount(target, plugin) | |
} | |
uninstall () { | |
this.unmount() | |
} | |
onAuth (authenticated) { | |
this.view.updateState({authenticated}) | |
if (authenticated) { | |
this.view.getFolder() | |
} | |
} | |
isFolder (item) { | |
return item.is_dir | |
} | |
getItemData (item) { | |
return Object.assign({}, item, {size: item.bytes}) | |
} | |
getItemIcon (item) { | |
var icon = _$icons_109[item.icon] | |
if (!icon) { | |
if (item.icon.startsWith('folder')) { | |
icon = _$icons_109['folder'] | |
} else { | |
icon = _$icons_109['page_white'] | |
} | |
} | |
return icon() | |
} | |
getItemSubList (item) { | |
return item.contents | |
} | |
getItemName (item) { | |
return item.path.length > 1 ? item.path.substring(1) : item.path | |
} | |
getMimeType (item) { | |
return item.mime_type | |
} | |
getItemId (item) { | |
return item.rev | |
} | |
getItemRequestPath (item) { | |
return encodeURIComponent(this.getItemName(item)) | |
} | |
getItemModifiedDate (item) { | |
return item.modified | |
} | |
getItemThumbnailUrl (item) { | |
return `${this.opts.host}/${this.Dropbox.id}/thumbnail/${this.getItemRequestPath(item)}` | |
} | |
render (state) { | |
return this.view.render(state) | |
} | |
} | |
/* removed: const _$Plugin_117 = require('./Plugin') */; | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
// const yo = require('yo-yo') | |
/** | |
* Dummy | |
* A test plugin, does nothing useful | |
*/ | |
var _$Dummy_111 = class Dummy extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.type = 'acquirer' | |
this.id = 'Dummy' | |
this.title = 'Mr. Plugin' | |
// set default options | |
const defaultOptions = {} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.strange = _$yoYo_80`<h1>this is strange 1</h1>` | |
this.render = this.render.bind(this) | |
this.install = this.install.bind(this) | |
} | |
addFakeFileJustToTest () { | |
const blob = new Blob( | |
['data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTIwIDEyMCI+CiAgPGNpcmNsZSBjeD0iNjAiIGN5PSI2MCIgcj0iNTAiLz4KPC9zdmc+Cg=='], | |
{type: 'image/svg+xml'} | |
) | |
const file = { | |
source: 'acceptance-test', | |
name: 'test-file', | |
type: 'image/svg+xml', | |
data: blob | |
} | |
this.props.log('Adding fake file blob') | |
this.props.addFile(file) | |
} | |
render (state) { | |
const bla = _$yoYo_80`<h2>this is strange 2</h2>` | |
return _$yoYo_80` | |
<div class="wow-this-works"> | |
<input class="UppyDummy-firstInput" type="text" value="hello" onload=${(el) => { | |
el.focus() | |
}} /> | |
${this.strange} | |
${bla} | |
${state.dummy.text} | |
</div> | |
` | |
} | |
install () { | |
this.core.setState({dummy: {text: '123'}}) | |
const target = this.opts.target | |
const plugin = this | |
this.target = this.mount(target, plugin) | |
setTimeout(() => { | |
this.core.setState({dummy: {text: '!!!'}}) | |
}, 2000) | |
} | |
} | |
// module.exports = function (core, opts) { | |
// if (!(this instanceof Dummy)) { | |
// return new Dummy(core, opts) | |
// } | |
// } | |
/* removed: const _$Plugin_117 = require('./Plugin') */; | |
const { toArray: __toArray_112 } = _$Utils_85 | |
/* removed: const _$Translator_83 = require('../core/Translator') */; | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
var _$FileInput_112 = class FileInput extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.id = 'FileInput' | |
this.title = 'File Input' | |
this.type = 'acquirer' | |
const defaultLocale = { | |
strings: { | |
selectToUpload: 'Select to upload' | |
} | |
} | |
// Default options | |
const defaultOptions = { | |
target: '.UppyForm', | |
getMetaFromForm: true, | |
replaceTargetContent: true, | |
multipleFiles: true, | |
pretty: true, | |
locale: defaultLocale, | |
inputName: 'files[]' | |
} | |
// Merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.locale = Object.assign({}, defaultLocale, this.opts.locale) | |
this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings) | |
// i18n | |
this.translator = new _$Translator_83({locale: this.locale}) | |
this.i18n = this.translator.translate.bind(this.translator) | |
this.render = this.render.bind(this) | |
} | |
handleInputChange (ev) { | |
this.core.log('All right, something selected through input...') | |
const files = __toArray_112(ev.target.files) | |
files.forEach((file) => { | |
this.core.addFile({ | |
source: this.id, | |
name: file.name, | |
type: file.type, | |
data: file | |
}) | |
}) | |
} | |
render (state) { | |
const hiddenInputStyle = 'width: 0.1px; height: 0.1px; opacity: 0; overflow: hidden; position: absolute; z-index: -1;' | |
const input = _$yoYo_80`<input class="uppy-FileInput-input" | |
style="${this.opts.pretty ? hiddenInputStyle : ''}" | |
type="file" | |
name=${this.opts.inputName} | |
onchange=${this.handleInputChange.bind(this)} | |
multiple="${this.opts.multipleFiles ? 'true' : 'false'}" | |
value="">` | |
return _$yoYo_80`<form class="Uppy uppy-FileInput-form"> | |
${input} | |
${this.opts.pretty | |
? _$yoYo_80`<button class="uppy-FileInput-btn" type="button" onclick=${() => input.click()}> | |
${this.i18n('selectToUpload')} | |
</button>` | |
: null | |
} | |
</form>` | |
} | |
install () { | |
const target = this.opts.target | |
const plugin = this | |
this.target = this.mount(target, plugin) | |
} | |
uninstall () { | |
this.unmount() | |
} | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$Plugin_117 = require('../Plugin') */; | |
/* removed: const _$Provider_135 = require('../../uppy-base/src/plugins/Provider') */; | |
/* removed: const _$View_95 = require('../../generic-provider-views/index') */; | |
var _$Google_113 = class Google extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.type = 'acquirer' | |
this.id = 'GoogleDrive' | |
this.title = 'Google Drive' | |
this.stateId = 'googleDrive' | |
this.icon = () => _$yoYo_80` | |
<svg class="UppyIcon UppyModalTab-icon" width="28" height="28" viewBox="0 0 16 16"> | |
<path d="M2.955 14.93l2.667-4.62H16l-2.667 4.62H2.955zm2.378-4.62l-2.666 4.62L0 10.31l5.19-8.99 2.666 4.62-2.523 4.37zm10.523-.25h-5.333l-5.19-8.99h5.334l5.19 8.99z"/> | |
</svg> | |
` | |
// writing out the key explicitly for readability the key used to store | |
// the provider instance must be equal to this.id. | |
this.GoogleDrive = new _$Provider_135(core, { | |
host: this.opts.host, | |
provider: 'drive', | |
authProvider: 'google' | |
}) | |
this.files = [] | |
this.onAuth = this.onAuth.bind(this) | |
// Visual | |
this.render = this.render.bind(this) | |
// set default options | |
const defaultOptions = {} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
} | |
install () { | |
this.view = new _$View_95(this) | |
// Set default state for Google Drive | |
this.core.setState({ | |
// writing out the key explicitly for readability the key used to store | |
// the plugin state must be equal to this.stateId. | |
googleDrive: { | |
authenticated: false, | |
files: [], | |
folders: [], | |
directories: [], | |
activeRow: -1, | |
filterInput: '', | |
isSearchVisible: false | |
} | |
}) | |
const target = this.opts.target | |
const plugin = this | |
this.target = this.mount(target, plugin) | |
} | |
uninstall () { | |
this.unmount() | |
} | |
onAuth (authenticated) { | |
this.view.updateState({authenticated}) | |
if (authenticated) { | |
this.view.getFolder('root') | |
} | |
} | |
isFolder (item) { | |
return item.mimeType === 'application/vnd.google-apps.folder' | |
} | |
getItemData (item) { | |
return Object.assign({}, item, {size: parseFloat(item.fileSize)}) | |
} | |
getItemIcon (item) { | |
return _$yoYo_80`<img src=${item.iconLink}/>` | |
} | |
getItemSubList (item) { | |
return item.items.filter((i) => { | |
return this.isFolder(i) || !i.mimeType.startsWith('application/vnd.google') | |
}) | |
} | |
getItemName (item) { | |
return item.title ? item.title : '/' | |
} | |
getMimeType (item) { | |
return item.mimeType | |
} | |
getItemId (item) { | |
return item.id | |
} | |
getItemRequestPath (item) { | |
return this.getItemId(item) | |
} | |
getItemModifiedDate (item) { | |
return item.modifiedByMeDate | |
} | |
getItemThumbnailUrl (item) { | |
return `${this.opts.host}/${this.GoogleDrive.id}/thumbnail/${this.getItemRequestPath(item)}` | |
} | |
render (state) { | |
return this.view.render(state) | |
} | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$Plugin_117 = require('../Plugin') */; | |
/* removed: const _$Provider_135 = require('../../uppy-base/src/plugins/Provider') */; | |
/* removed: const _$View_95 = require('../../generic-provider-views/index') */; | |
var _$Instagram_115 = class Instagram extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.type = 'acquirer' | |
this.id = 'Instagram' | |
this.title = 'Instagram' | |
this.stateId = 'instagram' | |
this.icon = () => _$yoYo_80` | |
<svg class="UppyIcon UppyModalTab-icon" width="28" height="28" viewBox="0 0 512 512"> | |
<path | |
d="M256,49.471c67.266,0,75.233.257,101.8,1.469,24.562,1.121,37.9,5.224,46.778,8.674a78.052,78.052,0,0,1,28.966,18.845,78.052,78.052,0,0,1,18.845,28.966c3.45,8.877,7.554,22.216,8.674,46.778,1.212,26.565,1.469,34.532,1.469,101.8s-0.257,75.233-1.469,101.8c-1.121,24.562-5.225,37.9-8.674,46.778a83.427,83.427,0,0,1-47.811,47.811c-8.877,3.45-22.216,7.554-46.778,8.674-26.56,1.212-34.527,1.469-101.8,1.469s-75.237-.257-101.8-1.469c-24.562-1.121-37.9-5.225-46.778-8.674a78.051,78.051,0,0,1-28.966-18.845,78.053,78.053,0,0,1-18.845-28.966c-3.45-8.877-7.554-22.216-8.674-46.778-1.212-26.564-1.469-34.532-1.469-101.8s0.257-75.233,1.469-101.8c1.121-24.562,5.224-37.9,8.674-46.778A78.052,78.052,0,0,1,78.458,78.458a78.053,78.053,0,0,1,28.966-18.845c8.877-3.45,22.216-7.554,46.778-8.674,26.565-1.212,34.532-1.469,101.8-1.469m0-45.391c-68.418,0-77,.29-103.866,1.516-26.815,1.224-45.127,5.482-61.151,11.71a123.488,123.488,0,0,0-44.62,29.057A123.488,123.488,0,0,0,17.3,90.982C11.077,107.007,6.819,125.319,5.6,152.134,4.369,179,4.079,187.582,4.079,256S4.369,333,5.6,359.866c1.224,26.815,5.482,45.127,11.71,61.151a123.489,123.489,0,0,0,29.057,44.62,123.486,123.486,0,0,0,44.62,29.057c16.025,6.228,34.337,10.486,61.151,11.71,26.87,1.226,35.449,1.516,103.866,1.516s77-.29,103.866-1.516c26.815-1.224,45.127-5.482,61.151-11.71a128.817,128.817,0,0,0,73.677-73.677c6.228-16.025,10.486-34.337,11.71-61.151,1.226-26.87,1.516-35.449,1.516-103.866s-0.29-77-1.516-103.866c-1.224-26.815-5.482-45.127-11.71-61.151a123.486,123.486,0,0,0-29.057-44.62A123.487,123.487,0,0,0,421.018,17.3C404.993,11.077,386.681,6.819,359.866,5.6,333,4.369,324.418,4.079,256,4.079h0Z"/> | |
<path | |
d="M256,126.635A129.365,129.365,0,1,0,385.365,256,129.365,129.365,0,0,0,256,126.635Zm0,213.338A83.973,83.973,0,1,1,339.974,256,83.974,83.974,0,0,1,256,339.973Z"/> | |
<circle cx="390.476" cy="121.524" r="30.23"/> | |
</svg> | |
` | |
// writing out the key explicitly for readability the key used to store | |
// the provider instance must be equal to this.id. | |
this.Instagram = new _$Provider_135(core, { | |
host: this.opts.host, | |
provider: 'instagram', | |
authProvider: 'instagram' | |
}) | |
this.files = [] | |
this.onAuth = this.onAuth.bind(this) | |
// Visual | |
this.render = this.render.bind(this) | |
// set default options | |
const defaultOptions = {} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
} | |
install () { | |
this.view = new _$View_95(this, { | |
viewType: 'grid' | |
}) | |
// Set default state for Google Drive | |
this.core.setState({ | |
// writing out the key explicitly for readability the key used to store | |
// the plugin state must be equal to this.stateId. | |
instagram: { | |
authenticated: false, | |
files: [], | |
folders: [], | |
directories: [], | |
activeRow: -1, | |
filterInput: '', | |
isSearchVisible: false | |
} | |
}) | |
const target = this.opts.target | |
const plugin = this | |
this.target = this.mount(target, plugin) | |
} | |
uninstall () { | |
this.unmount() | |
} | |
onAuth (authenticated) { | |
this.view.updateState({authenticated}) | |
if (authenticated) { | |
this.view.getFolder('recent') | |
} | |
} | |
isFolder (item) { | |
return false | |
} | |
getItemData (item) { | |
return item | |
} | |
getItemIcon (item) { | |
return _$yoYo_80`<img width="100px" src=${item.images.thumbnail.url}/>` | |
} | |
getItemSubList (item) { | |
const subItems = [] | |
item.data.forEach((subItem) => { | |
if (subItem.carousel_media) { | |
subItem.carousel_media.forEach((i, index) => { | |
const { id, created_time } = subItem | |
const newSubItem = Object.assign({}, i, { id, created_time }) | |
newSubItem.carousel_id = index | |
subItems.push(newSubItem) | |
}) | |
} else { | |
subItems.push(subItem) | |
} | |
}) | |
return subItems | |
} | |
getItemName (item) { | |
return '' | |
} | |
getMimeType (item) { | |
return item.type === 'video' ? 'video/mp4' : 'image/jpeg' | |
} | |
getItemId (item) { | |
return `${item.id}${item.carousel_id || ''}` | |
} | |
getItemRequestPath (item) { | |
const suffix = isNaN(item.carousel_id) ? '' : `?carousel_id=${item.carousel_id}` | |
return `${item.id}${suffix}` | |
} | |
getItemModifiedDate (item) { | |
return item.created_time | |
} | |
getItemThumbnailUrl (item) { | |
return item.images.thumbnail.url | |
} | |
getNextPagePath () { | |
const { files } = this.core.getState()[this.stateId] | |
return `recent?max_id=${this.getItemId(files[files.length - 1])}` | |
} | |
render (state) { | |
return this.view.render(state) | |
} | |
} | |
/* removed: const _$Plugin_117 = require('./Plugin') */; | |
/** | |
* Meta Data | |
* Adds metadata fields to Uppy | |
* | |
*/ | |
var _$MetaData_116 = class MetaData extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.type = 'modifier' | |
this.id = 'MetaData' | |
this.title = 'Meta Data' | |
// set default options | |
const defaultOptions = {} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.handleFileAdded = this.handleFileAdded.bind(this) | |
} | |
handleFileAdded (file) { | |
const metaFields = this.opts.fields | |
metaFields.forEach((item) => { | |
const obj = {} | |
obj[item.id] = item.value | |
this.core.updateMeta(obj, file.id) | |
}) | |
} | |
addInitialMeta () { | |
const metaFields = this.opts.fields | |
this.core.setState({ | |
metaFields: metaFields | |
}) | |
this.core.on('core:file-added', this.handleFileAdded) | |
} | |
install () { | |
this.addInitialMeta() | |
} | |
uninstall () { | |
this.core.off('core:file-added', this.handleFileAdded) | |
} | |
} | |
/* removed: const _$Plugin_117 = require('./Plugin') */; | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/** | |
* Progress bar | |
* | |
*/ | |
var _$ProgressBar_118 = class ProgressBar extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.id = 'ProgressBar' | |
this.title = 'Progress Bar' | |
this.type = 'progressindicator' | |
// set default options | |
const defaultOptions = { | |
replaceTargetContent: false, | |
fixed: false | |
} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.render = this.render.bind(this) | |
} | |
render (state) { | |
const progress = state.totalProgress || 0 | |
return _$yoYo_80`<div class="UppyProgressBar" style="${this.opts.fixed ? 'position: fixed' : 'null'}"> | |
<div class="UppyProgressBar-inner" style="width: ${progress}%"></div> | |
<div class="UppyProgressBar-percentage">${progress}</div> | |
</div>` | |
} | |
install () { | |
const target = this.opts.target | |
const plugin = this | |
this.target = this.mount(target, plugin) | |
} | |
uninstall () { | |
this.unmount() | |
} | |
} | |
/** | |
* A Barebones HTTP API client for Transloadit. | |
*/ | |
var _$Client_121 = class Client { | |
constructor (opts = {}) { | |
this.apiUrl = 'https://api2.transloadit.com' | |
this.opts = opts | |
} | |
/** | |
* Create a new assembly. | |
* | |
* @param {object} options | |
*/ | |
createAssembly ({ | |
templateId, | |
params, | |
fields, | |
signature, | |
expectedFiles | |
}) { | |
const data = new FormData() | |
data.append('params', typeof params === 'string' | |
? params | |
: JSON.stringify(params)) | |
if (signature) { | |
data.append('signature', signature) | |
} | |
Object.keys(fields).forEach((key) => { | |
data.append(key, fields[key]) | |
}) | |
data.append('tus_num_expected_upload_files', expectedFiles) | |
return fetch(`${this.apiUrl}/assemblies`, { | |
method: 'post', | |
body: data | |
}).then((response) => response.json()).then((assembly) => { | |
if (assembly.error) { | |
const error = new Error(assembly.message) | |
error.code = assembly.error | |
error.status = assembly | |
throw error | |
} | |
return assembly | |
}) | |
} | |
/** | |
* Get the current status for an assembly. | |
* | |
* @param {string} url The status endpoint of the assembly. | |
*/ | |
getAssemblyStatus (url) { | |
return fetch(url) | |
.then((response) => response.json()) | |
} | |
} | |
/* removed: const _$lib_59 = require('socket.io-client') */; | |
/* removed: const _$createNamespaceEmitter_48 = require('namespace-emitter') */; | |
/* removed: const _$URL_76 = require('url-parse') */; | |
/** | |
* WebSocket status API client for Transloadit. | |
*/ | |
var _$TransloaditSocket_122 = class TransloaditSocket { | |
constructor (url, assembly) { | |
const emitter = _$createNamespaceEmitter_48() | |
this.on = emitter.on.bind(emitter) | |
this.off = emitter.off.bind(emitter) | |
this.emit = emitter.emit.bind(emitter) | |
const parsed = _$URL_76(url) | |
this.assembly = assembly | |
this.socket = _$lib_59.connect(parsed.origin, { | |
path: parsed.pathname | |
}) | |
this.attachDefaultHandlers() | |
} | |
attachDefaultHandlers () { | |
this.socket.on('connect', () => { | |
this.socket.emit('assembly_connect', { | |
id: this.assembly.assembly_id | |
}) | |
this.emit('connect') | |
}) | |
this.socket.on('assembly_finished', () => { | |
this.emit('finished') | |
this.close() | |
}) | |
this.socket.on('assembly_upload_finished', (file) => { | |
this.emit('upload', file) | |
}) | |
this.socket.on('assembly_upload_meta_data_extracted', () => { | |
this.emit('metadata') | |
}) | |
this.socket.on('assembly_result_finished', (stepName, result) => { | |
this.emit('result', stepName, result) | |
}) | |
this.socket.on('assembly_error', (err) => { | |
this.emit('error', Object.assign(new Error(err.message), err)) | |
}) | |
} | |
close () { | |
this.socket.disconnect() | |
} | |
} | |
/* removed: const _$Plugin_117 = require('../Plugin') */; | |
/* removed: const _$Client_121 = require('./Client') */; | |
/* removed: const _$TransloaditSocket_122 = require('./Socket') */; | |
/** | |
* Upload files to Transloadit using Tus. | |
*/ | |
var _$Transloadit_123 = class Transloadit extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.type = 'uploader' | |
this.id = 'Transloadit' | |
this.title = 'Transloadit' | |
const defaultLocale = { | |
strings: { | |
creatingAssembly: 'Preparing upload...', | |
creatingAssemblyFailed: 'Transloadit: Could not create assembly', | |
encoding: 'Encoding...' | |
} | |
} | |
const defaultOptions = { | |
waitForEncoding: false, | |
waitForMetadata: false, | |
signature: null, | |
params: null, | |
fields: {}, | |
getAssemblyOptions (file, options) { | |
return { | |
params: options.params, | |
signature: options.signature, | |
fields: options.fields | |
} | |
}, | |
locale: defaultLocale | |
} | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.locale = Object.assign({}, defaultLocale, this.opts.locale) | |
this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings) | |
this.prepareUpload = this.prepareUpload.bind(this) | |
this.afterUpload = this.afterUpload.bind(this) | |
if (this.opts.params) { | |
this.validateParams(this.opts.params) | |
} | |
this.client = new _$Client_121() | |
this.sockets = {} | |
} | |
validateParams (params) { | |
if (!params) { | |
throw new Error('Transloadit: The `params` option is required.') | |
} | |
if (typeof params === 'string') { | |
try { | |
params = JSON.parse(params) | |
} catch (err) { | |
// Tell the user that this is not an Uppy bug! | |
err.message = 'Transloadit: The `params` option is a malformed JSON string: ' + | |
err.message | |
throw err | |
} | |
} | |
if (!params.auth || !params.auth.key) { | |
throw new Error('Transloadit: The `params.auth.key` option is required. ' + | |
'You can find your Transloadit API key at https://transloadit.com/accounts/credentials.') | |
} | |
} | |
getAssemblyOptions (fileIDs) { | |
const options = this.opts | |
return Promise.all( | |
fileIDs.map((fileID) => { | |
const file = this.core.getFile(fileID) | |
const promise = Promise.resolve(options.getAssemblyOptions(file, options)) | |
return promise.then((assemblyOptions) => { | |
this.validateParams(assemblyOptions.params) | |
return { | |
fileIDs: [fileID], | |
options: assemblyOptions | |
} | |
}) | |
}) | |
) | |
} | |
dedupeAssemblyOptions (list) { | |
const dedupeMap = Object.create(null) | |
list.forEach(({ fileIDs, options }) => { | |
const id = JSON.stringify(options) | |
if (dedupeMap[id]) { | |
dedupeMap[id].fileIDs.push(...fileIDs) | |
} else { | |
dedupeMap[id] = { | |
options, | |
fileIDs: [...fileIDs] | |
} | |
} | |
}) | |
return Object.keys(dedupeMap).map((id) => dedupeMap[id]) | |
} | |
createAssembly (fileIDs, options) { | |
this.core.log('Transloadit: create assembly') | |
return this.client.createAssembly({ | |
params: options.params, | |
fields: options.fields, | |
expectedFiles: fileIDs.length, | |
signature: options.signature | |
}).then((assembly) => { | |
this.updateState({ | |
assemblies: Object.assign(this.state.assemblies, { | |
[assembly.assembly_id]: assembly | |
}) | |
}) | |
function attachAssemblyMetadata (file, assembly) { | |
// Attach meta parameters for the Tus plugin. See: | |
// https://github.com/tus/tusd/wiki/Uploading-to-Transloadit-using-tus#uploading-using-tus | |
// TODO Should this `meta` be moved to a `tus.meta` property instead? | |
// If the MetaData plugin can add eg. resize parameters, it doesn't | |
// make much sense to set those as upload-metadata for tus. | |
const meta = Object.assign({}, file.meta, { | |
assembly_url: assembly.assembly_url, | |
filename: file.name, | |
fieldname: 'file' | |
}) | |
// Add assembly-specific Tus endpoint. | |
const tus = Object.assign({}, file.tus, { | |
endpoint: assembly.tus_url | |
}) | |
const transloadit = { | |
assembly: assembly.assembly_id | |
} | |
return Object.assign( | |
{}, | |
file, | |
{ meta, tus, transloadit } | |
) | |
} | |
const files = Object.assign({}, this.core.state.files) | |
fileIDs.forEach((id) => { | |
files[id] = attachAssemblyMetadata(files[id], assembly) | |
}) | |
this.core.setState({ files }) | |
this.core.emit('transloadit:assembly-created', assembly, fileIDs) | |
return this.connectSocket(assembly) | |
}).then(() => { | |
this.core.log('Transloadit: Created assembly') | |
}).catch((err) => { | |
this.core.emit('informer', this.opts.locale.strings.creatingAssemblyFailed, 'error', 0) | |
// Reject the promise. | |
throw err | |
}) | |
} | |
shouldWait () { | |
return this.opts.waitForEncoding || this.opts.waitForMetadata | |
} | |
findFile (uploadedFile) { | |
const files = this.core.state.files | |
for (const id in files) { | |
if (!files.hasOwnProperty(id)) { | |
continue | |
} | |
if (files[id].uploadURL === uploadedFile.tus_upload_url) { | |
return files[id] | |
} | |
} | |
} | |
onFileUploadComplete (assemblyId, uploadedFile) { | |
const file = this.findFile(uploadedFile) | |
this.updateState({ | |
files: Object.assign({}, this.state.files, { | |
[uploadedFile.id]: { | |
id: file.id, | |
uploadedFile | |
} | |
}) | |
}) | |
this.core.bus.emit('transloadit:upload', uploadedFile, this.getAssembly(assemblyId)) | |
} | |
onResult (assemblyId, stepName, result) { | |
const file = this.state.files[result.original_id] | |
// The `file` may not exist if an import robot was used instead of a file upload. | |
result.localId = file ? file.id : null | |
this.updateState({ | |
results: this.state.results.concat(result) | |
}) | |
this.core.bus.emit('transloadit:result', stepName, result, this.getAssembly(assemblyId)) | |
} | |
onAssemblyFinished (url) { | |
this.client.getAssemblyStatus(url).then((assembly) => { | |
this.updateState({ | |
assemblies: Object.assign({}, this.state.assemblies, { | |
[assembly.assembly_id]: assembly | |
}) | |
}) | |
this.core.emit('transloadit:complete', assembly) | |
}) | |
} | |
connectSocket (assembly) { | |
const socket = new _$TransloaditSocket_122( | |
assembly.websocket_url, | |
assembly | |
) | |
this.sockets[assembly.assembly_id] = socket | |
socket.on('upload', this.onFileUploadComplete.bind(this, assembly.assembly_id)) | |
socket.on('error', (error) => { | |
this.core.emit('transloadit:assembly-error', assembly, error) | |
}) | |
if (this.opts.waitForEncoding) { | |
socket.on('result', this.onResult.bind(this, assembly.assembly_id)) | |
} | |
if (this.opts.waitForEncoding) { | |
socket.on('finished', () => { | |
this.onAssemblyFinished(assembly.assembly_ssl_url) | |
}) | |
} else if (this.opts.waitForMetadata) { | |
socket.on('metadata', () => { | |
this.onAssemblyFinished(assembly.assembly_ssl_url) | |
this.core.emit('transloadit:complete', assembly) | |
}) | |
} | |
return new Promise((resolve, reject) => { | |
socket.on('connect', resolve) | |
socket.on('error', reject) | |
}).then(() => { | |
this.core.log('Transloadit: Socket is ready') | |
}) | |
} | |
prepareUpload (fileIDs) { | |
fileIDs.forEach((fileID) => { | |
this.core.emit('core:preprocess-progress', fileID, { | |
mode: 'indeterminate', | |
message: this.opts.locale.strings.creatingAssembly | |
}) | |
}) | |
const createAssembly = ({ fileIDs, options }) => { | |
return this.createAssembly(fileIDs, options).then(() => { | |
fileIDs.forEach((fileID) => { | |
this.core.emit('core:preprocess-complete', fileID) | |
}) | |
}) | |
} | |
return this.getAssemblyOptions(fileIDs) | |
.then((allOptions) => this.dedupeAssemblyOptions(allOptions)) | |
.then((assemblies) => Promise.all( | |
assemblies.map(createAssembly) | |
)) | |
} | |
afterUpload (fileIDs) { | |
// A file ID that is part of this assembly... | |
const fileID = fileIDs[0] | |
// If we don't have to wait for encoding metadata or results, we can close | |
// the socket immediately and finish the upload. | |
if (!this.shouldWait()) { | |
const file = this.core.getFile(fileID) | |
const socket = this.sockets[file.transloadit.assembly] | |
socket.close() | |
return Promise.resolve() | |
} | |
return new Promise((resolve, reject) => { | |
fileIDs.forEach((fileID) => { | |
this.core.emit('core:postprocess-progress', fileID, { | |
mode: 'indeterminate', | |
message: this.opts.locale.strings.encoding | |
}) | |
}) | |
const onAssemblyFinished = (assembly) => { | |
const file = this.core.getFile(fileID) | |
// An assembly for a different upload just finished. We can ignore it. | |
if (assembly.assembly_id !== file.transloadit.assembly) { | |
return | |
} | |
// Remove this handler once we find the assembly we needed. | |
this.core.emitter.off('transloadit:complete', onAssemblyFinished) | |
// TODO set the `file.uploadURL` to a result? | |
// We will probably need an option here so the plugin user can tell us | |
// which result to pick…? | |
fileIDs.forEach((fileID) => { | |
this.core.emit('core:postprocess-complete', fileID) | |
}) | |
resolve() | |
} | |
const onAssemblyError = (assembly, error) => { | |
const file = this.core.getFile(fileID) | |
// An assembly for a different upload just errored. We can ignore it. | |
if (assembly.assembly_id !== file.transloadit.assembly) { | |
return | |
} | |
// Remove this handler once we find the assembly we needed. | |
this.core.emitter.off('transloadit:assembly-error', onAssemblyError) | |
// Clear postprocessing state for all our files. | |
fileIDs.forEach((fileID) => { | |
this.core.emit('core:postprocess-complete', fileID) | |
}) | |
// Reject the `afterUpload()` promise. | |
reject(error) | |
} | |
this.core.on('transloadit:complete', onAssemblyFinished) | |
this.core.on('transloadit:assembly-error', onAssemblyError) | |
}) | |
} | |
install () { | |
this.core.addPreProcessor(this.prepareUpload) | |
this.core.addPostProcessor(this.afterUpload) | |
this.updateState({ | |
assemblies: {}, | |
files: {}, | |
results: [] | |
}) | |
} | |
uninstall () { | |
this.core.removePreProcessor(this.prepareUpload) | |
this.core.removePostProcessor(this.afterUpload) | |
} | |
getAssembly (id) { | |
return this.state.assemblies[id] | |
} | |
get state () { | |
return this.core.state.transloadit || {} | |
} | |
updateState (newState) { | |
const transloadit = Object.assign({}, this.state, newState) | |
this.core.setState({ transloadit }) | |
} | |
} | |
/* removed: const _$Plugin_117 = require('./Plugin') */; | |
/* removed: const _$libEs5_74 = require('tus-js-client') */; | |
/* removed: const _$UppySocket_84 = require('../core/UppySocket') */; | |
/* removed: const _$Utils_85 = require('../core/Utils') */; | |
_$fetch_77 | |
// Extracted from https://github.com/tus/tus-js-client/blob/master/lib/upload.js#L13 | |
// excepted we removed 'fingerprint' key to avoid adding more dependencies | |
const tusDefaultOptions = { | |
endpoint: '', | |
resume: true, | |
onProgress: null, | |
onChunkComplete: null, | |
onSuccess: null, | |
onError: null, | |
headers: {}, | |
chunkSize: Infinity, | |
withCredentials: false, | |
uploadUrl: null, | |
uploadSize: null, | |
overridePatchMethod: false, | |
retryDelays: null | |
} | |
/** | |
* Tus resumable file uploader | |
* | |
*/ | |
var _$Tus10_124 = class Tus10 extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.type = 'uploader' | |
this.id = 'Tus' | |
this.title = 'Tus' | |
// set default options | |
const defaultOptions = { | |
resume: true, | |
allowPause: true, | |
autoRetry: true, | |
retryDelays: [0, 1000, 3000, 5000] | |
} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.handlePauseAll = this.handlePauseAll.bind(this) | |
this.handleResumeAll = this.handleResumeAll.bind(this) | |
this.handleUpload = this.handleUpload.bind(this) | |
} | |
pauseResume (action, fileID) { | |
const updatedFiles = Object.assign({}, this.core.getState().files) | |
const inProgressUpdatedFiles = Object.keys(updatedFiles).filter((file) => { | |
return !updatedFiles[file].progress.uploadComplete && | |
updatedFiles[file].progress.uploadStarted | |
}) | |
switch (action) { | |
case 'toggle': | |
if (updatedFiles[fileID].uploadComplete) return | |
const wasPaused = updatedFiles[fileID].isPaused || false | |
const isPaused = !wasPaused | |
let updatedFile | |
if (wasPaused) { | |
updatedFile = Object.assign({}, updatedFiles[fileID], { | |
isPaused: false | |
}) | |
} else { | |
updatedFile = Object.assign({}, updatedFiles[fileID], { | |
isPaused: true | |
}) | |
} | |
updatedFiles[fileID] = updatedFile | |
this.core.setState({files: updatedFiles}) | |
return isPaused | |
case 'pauseAll': | |
inProgressUpdatedFiles.forEach((file) => { | |
const updatedFile = Object.assign({}, updatedFiles[file], { | |
isPaused: true | |
}) | |
updatedFiles[file] = updatedFile | |
}) | |
this.core.setState({files: updatedFiles}) | |
return | |
case 'resumeAll': | |
inProgressUpdatedFiles.forEach((file) => { | |
const updatedFile = Object.assign({}, updatedFiles[file], { | |
isPaused: false | |
}) | |
updatedFiles[file] = updatedFile | |
}) | |
this.core.setState({files: updatedFiles}) | |
return | |
} | |
} | |
handlePauseAll () { | |
this.pauseResume('pauseAll') | |
} | |
handleResumeAll () { | |
this.pauseResume('resumeAll') | |
} | |
/** | |
* Create a new Tus upload | |
* | |
* @param {object} file for use with upload | |
* @param {integer} current file in a queue | |
* @param {integer} total number of files in a queue | |
* @returns {Promise} | |
*/ | |
upload (file, current, total) { | |
this.core.log(`uploading ${current} of ${total}`) | |
// Create a new tus upload | |
return new Promise((resolve, reject) => { | |
const optsTus = Object.assign( | |
{}, | |
tusDefaultOptions, | |
this.opts, | |
// Install file-specific upload overrides. | |
file.tus || {} | |
) | |
optsTus.onError = (err) => { | |
this.core.log(err) | |
this.core.emit('core:upload-error', file.id, err) | |
reject('Failed because: ' + err) | |
} | |
optsTus.onProgress = (bytesUploaded, bytesTotal) => { | |
this.onReceiveUploadUrl(file, upload.url) | |
this.core.emit('core:upload-progress', { | |
uploader: this, | |
id: file.id, | |
bytesUploaded: bytesUploaded, | |
bytesTotal: bytesTotal | |
}) | |
} | |
optsTus.onSuccess = () => { | |
this.core.emit('core:upload-success', file.id, upload, upload.url) | |
if (upload.url) { | |
this.core.log('Download ' + upload.file.name + ' from ' + upload.url) | |
} | |
resolve(upload) | |
} | |
optsTus.metadata = file.meta | |
const upload = new _$libEs5_74.Upload(file.data, optsTus) | |
this.onFileRemove(file.id, (targetFileID) => { | |
// this.core.log(`removing file: ${targetFileID}`) | |
upload.abort() | |
resolve(`upload ${targetFileID} was removed`) | |
}) | |
this.onPause(file.id, (isPaused) => { | |
isPaused ? upload.abort() : upload.start() | |
}) | |
this.onPauseAll(file.id, () => { | |
upload.abort() | |
}) | |
this.onResumeAll(file.id, () => { | |
upload.start() | |
}) | |
this.core.on('core:retry-started', () => { | |
const files = this.core.getState().files | |
if (files[file.id].progress.uploadComplete || | |
!files[file.id].progress.uploadStarted || | |
files[file.id].isPaused | |
) { | |
return | |
} | |
upload.start() | |
}) | |
upload.start() | |
this.core.emit('core:upload-started', file.id, upload) | |
}) | |
} | |
uploadRemote (file, current, total) { | |
return new Promise((resolve, reject) => { | |
this.core.log(file.remote.url) | |
if (file.serverToken) { | |
this.connectToServerSocket(file) | |
} else { | |
let endpoint = this.opts.endpoint | |
if (file.tus && file.tus.endpoint) { | |
endpoint = file.tus.endpoint | |
} | |
this.core.emitter.emit('core:upload-started', file.id) | |
fetch(file.remote.url, { | |
method: 'post', | |
credentials: 'include', | |
headers: { | |
'Accept': 'application/json', | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify(Object.assign({}, file.remote.body, { | |
endpoint, | |
protocol: 'tus', | |
size: file.data.size, | |
metadata: file.meta | |
})) | |
}) | |
.then((res) => { | |
if (res.status < 200 && res.status > 300) { | |
return reject(res.statusText) | |
} | |
res.json().then((data) => { | |
const token = data.token | |
file = this.getFile(file.id) | |
file.serverToken = token | |
this.updateFile(file) | |
this.connectToServerSocket(file) | |
resolve() | |
}) | |
}) | |
} | |
}) | |
} | |
connectToServerSocket (file) { | |
const token = file.serverToken | |
const host = _$Utils_85.getSocketHost(file.remote.host) | |
const socket = new _$UppySocket_84({ target: `${host}/api/${token}` }) | |
this.onFileRemove(file.id, () => socket.send('pause', {})) | |
this.onPause(file.id, (isPaused) => { | |
isPaused ? socket.send('pause', {}) : socket.send('resume', {}) | |
}) | |
this.onPauseAll(file.id, () => socket.send('pause', {})) | |
this.onResumeAll(file.id, () => socket.send('resume', {})) | |
socket.on('progress', (progressData) => _$Utils_85.emitSocketProgress(this, progressData, file)) | |
socket.on('success', (data) => { | |
this.core.emitter.emit('core:upload-success', file.id, data, data.url) | |
socket.close() | |
}) | |
} | |
getFile (fileID) { | |
return this.core.state.files[fileID] | |
} | |
updateFile (file) { | |
const files = Object.assign({}, this.core.state.files, { | |
[file.id]: file | |
}) | |
this.core.setState({ files }) | |
} | |
onReceiveUploadUrl (file, uploadURL) { | |
const currentFile = this.getFile(file.id) | |
if (!currentFile) return | |
// Only do the update if we didn't have an upload URL yet. | |
if (!currentFile.tus || currentFile.tus.uploadUrl !== uploadURL) { | |
const newFile = Object.assign({}, currentFile, { | |
tus: Object.assign({}, currentFile.tus, { | |
uploadUrl: uploadURL | |
}) | |
}) | |
this.updateFile(newFile) | |
} | |
} | |
onFileRemove (fileID, cb) { | |
this.core.on('core:file-removed', (targetFileID) => { | |
if (fileID === targetFileID) cb(targetFileID) | |
}) | |
} | |
onPause (fileID, cb) { | |
this.core.on('core:upload-pause', (targetFileID) => { | |
if (fileID === targetFileID) { | |
const isPaused = this.pauseResume('toggle', fileID) | |
cb(isPaused) | |
} | |
}) | |
} | |
onPauseAll (fileID, cb) { | |
this.core.on('core:pause-all', () => { | |
if (!this.core.getFile(fileID)) return | |
cb() | |
}) | |
} | |
onResumeAll (fileID, cb) { | |
this.core.on('core:resume-all', () => { | |
if (!this.core.getFile(fileID)) return | |
cb() | |
}) | |
} | |
uploadFiles (files) { | |
files.forEach((file, index) => { | |
const current = parseInt(index, 10) + 1 | |
const total = files.length | |
if (!file.isRemote) { | |
this.upload(file, current, total) | |
} else { | |
this.uploadRemote(file, current, total) | |
} | |
}) | |
} | |
handleUpload (fileIDs) { | |
if (fileIDs.length === 0) { | |
this.core.log('Tus: no files to upload!') | |
return Promise.resolve() | |
} | |
this.core.log('Tus is uploading...') | |
const filesToUpload = fileIDs.map((fileID) => this.core.getFile(fileID)) | |
this.uploadFiles(filesToUpload) | |
return new Promise((resolve) => { | |
this.core.once('core:upload-complete', resolve) | |
}) | |
} | |
actions () { | |
this.core.on('core:pause-all', this.handlePauseAll) | |
this.core.on('core:resume-all', this.handleResumeAll) | |
if (this.opts.autoRetry) { | |
this.core.on('back-online', () => { | |
this.core.emit('core:retry-started') | |
}) | |
} | |
} | |
addResumableUploadsCapabilityFlag () { | |
const newCapabilities = Object.assign({}, this.core.getState().capabilities) | |
newCapabilities.resumableUploads = true | |
this.core.setState({ | |
capabilities: newCapabilities | |
}) | |
} | |
install () { | |
this.addResumableUploadsCapabilityFlag() | |
this.core.addUploader(this.handleUpload) | |
this.actions() | |
} | |
uninstall () { | |
this.core.removeUploader(this.handleUpload) | |
this.core.off('core:pause-all', this.handlePauseAll) | |
this.core.off('core:resume-all', this.handleResumeAll) | |
} | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
var _$RecordStartIcon_129 = (props) => { | |
return _$yoYo_80`<svg class="UppyIcon" width="100" height="100" viewBox="0 0 100 100"> | |
<circle cx="50" cy="50" r="40" /> | |
</svg>` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
var _$RecordStopIcon_130 = (props) => { | |
return _$yoYo_80`<svg class="UppyIcon" width="100" height="100" viewBox="0 0 100 100"> | |
<rect x="15" y="15" width="70" height="70" /> | |
</svg>` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$RecordStartIcon_129 = require('./RecordStartIcon') */; | |
/* removed: const _$RecordStopIcon_130 = require('./RecordStopIcon') */; | |
var _$RecordButton_128 = function RecordButton ({ recording, onStartRecording, onStopRecording }) { | |
if (recording) { | |
return _$yoYo_80` | |
<button class="UppyButton--circular UppyButton--red UppyButton--sizeM UppyWebcam-recordButton" | |
type="button" | |
title="Stop Recording" | |
aria-label="Stop Recording" | |
onclick=${onStopRecording}> | |
${_$RecordStopIcon_130()} | |
</button> | |
` | |
} | |
return _$yoYo_80` | |
<button class="UppyButton--circular UppyButton--red UppyButton--sizeM UppyWebcam-recordButton" | |
type="button" | |
title="Begin Recording" | |
aria-label="Begin Recording" | |
onclick=${onStartRecording}> | |
${_$RecordStartIcon_129()} | |
</button> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
var _$CameraIcon_125 = (props) => { | |
return _$yoYo_80`<svg class="UppyIcon" width="100" height="77" viewBox="0 0 100 77"> | |
<path d="M50 32c-7.168 0-13 5.832-13 13s5.832 13 13 13 13-5.832 13-13-5.832-13-13-13z"/> | |
<path d="M87 13H72c0-7.18-5.82-13-13-13H41c-7.18 0-13 5.82-13 13H13C5.82 13 0 18.82 0 26v38c0 7.18 5.82 13 13 13h74c7.18 0 13-5.82 13-13V26c0-7.18-5.82-13-13-13zM50 68c-12.683 0-23-10.318-23-23s10.317-23 23-23 23 10.318 23 23-10.317 23-23 23z"/> | |
</svg>` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$CameraIcon_125 = require('./CameraIcon') */; | |
var _$SnapshotButton_131 = function SnapshotButton ({ onSnapshot }) { | |
return _$yoYo_80` | |
<button class="UppyButton--circular UppyButton--red UppyButton--sizeM UppyWebcam-recordButton" | |
type="button" | |
title="Take a snapshot" | |
aria-label="Take a snapshot" | |
onclick=${onSnapshot}> | |
${_$CameraIcon_125()} | |
</button> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
/* removed: const _$SnapshotButton_131 = require('./SnapshotButton') */; | |
/* removed: const _$RecordButton_128 = require('./RecordButton') */; | |
function isModeAvailable (modes, mode) { | |
return modes.indexOf(mode) !== -1 | |
} | |
var _$CameraScreen_126 = (props) => { | |
const src = props.src || '' | |
let video | |
if (props.useTheFlash) { | |
video = props.getSWFHTML() | |
} else { | |
video = _$yoYo_80`<video class="UppyWebcam-video" autoplay muted src="${src}"></video>` | |
} | |
const shouldShowRecordButton = props.supportsRecording && ( | |
isModeAvailable(props.modes, 'video-only') || | |
isModeAvailable(props.modes, 'audio-only') || | |
isModeAvailable(props.modes, 'video-audio') | |
) | |
const shouldShowSnapshotButton = isModeAvailable(props.modes, 'picture') | |
return _$yoYo_80` | |
<div class="UppyWebcam-container" onload=${(el) => { | |
props.onFocus() | |
const recordButton = el.querySelector('.UppyWebcam-recordButton') | |
if (recordButton) recordButton.focus() | |
}} onunload=${(el) => { | |
props.onStop() | |
}}> | |
<div class='UppyWebcam-videoContainer'> | |
${video} | |
</div> | |
<div class='UppyWebcam-buttonContainer'> | |
${shouldShowRecordButton ? _$RecordButton_128(props) : null} | |
${shouldShowSnapshotButton ? _$SnapshotButton_131(props) : null} | |
</div> | |
<canvas class="UppyWebcam-canvas" style="display: none;"></canvas> | |
</div> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
var _$PermissionsScreen_127 = (props) => { | |
return _$yoYo_80` | |
<div class="uppy-Webcam-permissons"> | |
<h1>Please allow access to your camera</h1> | |
<p>You have been prompted to allow camera access from this site.<br> | |
In order to take pictures with your camera you must approve this request.</p> | |
</div> | |
` | |
} | |
/* removed: const _$yoYo_80 = require('yo-yo') */; | |
var _$WebcamIcon_132 = (props) => { | |
return _$yoYo_80` | |
<svg class="UppyIcon" width="18" height="21" viewBox="0 0 18 21"> | |
<path d="M14.8 16.9c1.9-1.7 3.2-4.1 3.2-6.9 0-5-4-9-9-9s-9 4-9 9c0 2.8 1.2 5.2 3.2 6.9C1.9 17.9.5 19.4 0 21h3c1-1.9 11-1.9 12 0h3c-.5-1.6-1.9-3.1-3.2-4.1zM9 4c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6 2.7-6 6-6z"/> | |
<path d="M9 14c2.2 0 4-1.8 4-4s-1.8-4-4-4-4 1.8-4 4 1.8 4 4 4zM8 8c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1c0-.5.4-1 1-1z"/> | |
</svg> | |
` | |
} | |
function __dataURItoBlob_137 (dataURI, opts, toFile) { | |
// get the base64 data | |
var data = dataURI.split(',')[1] | |
// user may provide mime type, if not get it from data URI | |
var mimeType = opts.mimeType || dataURI.split(',')[0].split(':')[1].split(';')[0] | |
// default to plain/text if data URI has no mimeType | |
if (mimeType == null) { | |
mimeType = 'plain/text' | |
} | |
var binary = atob(data) | |
var array = [] | |
for (var i = 0; i < binary.length; i++) { | |
array.push(binary.charCodeAt(i)) | |
} | |
// Convert to a File? | |
if (toFile) { | |
return new File([new Uint8Array(array)], opts.name || '', {type: mimeType}) | |
} | |
return new Blob([new Uint8Array(array)], {type: mimeType}) | |
} | |
var _$dataURItoFile_137 = function (dataURI, opts) { | |
return __dataURItoBlob_137(dataURI, opts, true) | |
} | |
'use strict' | |
/* removed: const _$dataURItoFile_137 = require('../utils/dataURItoFile') */; | |
/** | |
* Webcam Plugin | |
*/ | |
var _$Webcam_136 = class Webcam { | |
constructor (opts = {}, params = {}) { | |
this._userMedia | |
this.userMedia = true | |
this.protocol = location.protocol.match(/https/i) ? 'https' : 'http' | |
// set default options | |
const defaultOptions = { | |
enableFlash: true, | |
modes: [] | |
} | |
const defaultParams = { | |
swfURL: 'webcam.swf', | |
width: 400, | |
height: 300, | |
dest_width: 800, // size of captured image | |
dest_height: 600, // these default to width/height | |
image_format: 'jpeg', // image format (may be jpeg or png) | |
jpeg_quality: 90, // jpeg image quality from 0 (worst) to 100 (best) | |
enable_flash: true, // enable flash fallback, | |
force_flash: false, // force flash mode, | |
flip_horiz: false, // flip image horiz (mirror mode) | |
fps: 30, // camera frames per second | |
upload_name: 'webcam', // name of file in upload post data | |
constraints: null, // custom user media constraints, | |
flashNotDetectedText: 'ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).', | |
noInterfaceFoundText: 'No supported webcam interface found.', | |
unfreeze_snap: true // Whether to unfreeze the camera after snap (defaults to true) | |
} | |
this.params = Object.assign({}, defaultParams, params) | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
// Camera controls | |
this.start = this.start.bind(this) | |
this.init = this.init.bind(this) | |
this.stop = this.stop.bind(this) | |
// this.startRecording = this.startRecording.bind(this) | |
// this.stopRecording = this.stopRecording.bind(this) | |
this.takeSnapshot = this.takeSnapshot.bind(this) | |
this.getImage = this.getImage.bind(this) | |
this.getSWFHTML = this.getSWFHTML.bind(this) | |
this.detectFlash = this.detectFlash.bind(this) | |
this.getUserMedia = this.getUserMedia.bind(this) | |
this.getMediaDevices = this.getMediaDevices.bind(this) | |
} | |
/** | |
* Checks for getUserMedia support | |
*/ | |
init () { | |
// initialize, check for getUserMedia support | |
this.mediaDevices = this.getMediaDevices() | |
this.userMedia = this.getUserMedia(this.mediaDevices) | |
// Make sure media stream is closed when navigating away from page | |
if (this.userMedia) { | |
window.addEventListener('beforeunload', (event) => { | |
this.reset() | |
}) | |
} | |
return { | |
mediaDevices: this.mediaDevices, | |
userMedia: this.userMedia | |
} | |
} | |
// Setup getUserMedia, with polyfill for older browsers | |
// Adapted from: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia | |
getMediaDevices () { | |
return (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) | |
? navigator.mediaDevices : ((navigator.mozGetUserMedia || navigator.webkitGetUserMedia) ? { | |
getUserMedia: function (opts) { | |
return new Promise(function (resolve, reject) { | |
(navigator.mozGetUserMedia || | |
navigator.webkitGetUserMedia).call(navigator, opts, resolve, reject) | |
}) | |
} | |
} : null) | |
} | |
getUserMedia (mediaDevices) { | |
const userMedia = true | |
// Older versions of firefox (< 21) apparently claim support but user media does not actually work | |
if (navigator.userAgent.match(/Firefox\D+(\d+)/)) { | |
if (parseInt(RegExp.$1, 10) < 21) { | |
return null | |
} | |
} | |
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL | |
return userMedia && !!mediaDevices && !!window.URL | |
} | |
start () { | |
this.userMedia = this._userMedia === undefined ? this.userMedia : this._userMedia | |
return new Promise((resolve, reject) => { | |
if (this.userMedia) { | |
const acceptsAudio = this.opts.modes.indexOf('video-audio') !== -1 || | |
this.opts.modes.indexOf('audio-only') !== -1 | |
const acceptsVideo = this.opts.modes.indexOf('video-audio') !== -1 || | |
this.opts.modes.indexOf('video-only') !== -1 || | |
this.opts.modes.indexOf('picture') !== -1 | |
// ask user for access to their camera | |
this.mediaDevices.getUserMedia({ | |
audio: acceptsAudio, | |
video: acceptsVideo | |
}) | |
.then((stream) => { | |
return resolve(stream) | |
}) | |
.catch((err) => { | |
return reject(err) | |
}) | |
} | |
}) | |
} | |
/** | |
* Detects if browser supports flash | |
* Code snippet borrowed from: https://github.com/swfobject/swfobject | |
* | |
* @return {bool} flash supported | |
*/ | |
detectFlash () { | |
const SHOCKWAVE_FLASH = 'Shockwave Flash' | |
const SHOCKWAVE_FLASH_AX = 'ShockwaveFlash.ShockwaveFlash' | |
const FLASH_MIME_TYPE = 'application/x-shockwave-flash' | |
const win = window | |
const nav = navigator | |
let hasFlash = false | |
if (typeof nav.plugins !== 'undefined' && typeof nav.plugins[SHOCKWAVE_FLASH] === 'object') { | |
var desc = nav.plugins[SHOCKWAVE_FLASH].description | |
if (desc && (typeof nav.mimeTypes !== 'undefined' && nav.mimeTypes[FLASH_MIME_TYPE] && nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { | |
hasFlash = true | |
} | |
} else if (typeof win.ActiveXObject !== 'undefined') { | |
try { | |
var ax = new win.ActiveXObject(SHOCKWAVE_FLASH_AX) | |
if (ax) { | |
var ver = ax.GetVariable('$version') | |
if (ver) hasFlash = true | |
} | |
} catch (e) {} | |
} | |
return hasFlash | |
} | |
reset () { | |
// shutdown camera, reset to potentially attach again | |
if (this.preview_active) this.unfreeze() | |
if (this.userMedia) { | |
if (this.stream) { | |
if (this.stream.getVideoTracks) { | |
// get video track to call stop on it | |
var tracks = this.stream.getVideoTracks() | |
if (tracks && tracks[0] && tracks[0].stop) tracks[0].stop() | |
} else if (this.stream.stop) { | |
// deprecated, may be removed in future | |
this.stream.stop() | |
} | |
} | |
delete this.stream | |
} | |
if (this.userMedia !== true) { | |
// call for turn off camera in flash | |
this.getMovie()._releaseCamera() | |
} | |
} | |
getSWFHTML () { | |
// Return HTML for embedding flash based webcam capture movie | |
var swfURL = this.params.swfURL | |
// make sure we aren't running locally (flash doesn't work) | |
if (location.protocol.match(/file/)) { | |
return '<h3 style="color:red">ERROR: the Webcam.js Flash fallback does not work from local disk. Please run it from a web server.</h3>' | |
} | |
// make sure we have flash | |
if (!this.detectFlash()) { | |
return '<h3 style="color:red">No flash</h3>' | |
} | |
// set default swfURL if not explicitly set | |
if (!swfURL) { | |
// find our script tag, and use that base URL | |
var baseUrl = '' | |
var scpts = document.getElementsByTagName('script') | |
for (var idx = 0, len = scpts.length; idx < len; idx++) { | |
var src = scpts[idx].getAttribute('src') | |
if (src && src.match(/\/webcam(\.min)?\.js/)) { | |
baseUrl = src.replace(/\/webcam(\.min)?\.js.*$/, '') | |
idx = len | |
} | |
} | |
if (baseUrl) swfURL = baseUrl + '/webcam.swf' | |
else swfURL = 'webcam.swf' | |
} | |
// // if this is the user's first visit, set flashvar so flash privacy settings panel is shown first | |
// if (window.localStorage && !localStorage.getItem('visited')) { | |
// // this.params.new_user = 1 | |
// localStorage.setItem('visited', 1) | |
// } | |
// this.params.new_user = 1 | |
// construct flashvars string | |
var flashvars = '' | |
for (var key in this.params) { | |
if (flashvars) flashvars += '&' | |
flashvars += key + '=' + escape(this.params[key]) | |
} | |
// construct object/embed tag | |
return `<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" type="application/x-shockwave-flash" codebase="${this.protocol}://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="${this.params.width}" height="${this.params.height}" id="webcam_movie_obj" align="middle"><param name="wmode" value="opaque" /><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="${swfURL}" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="${flashvars}"/><embed id="webcam_movie_embed" src="${swfURL}" wmode="opaque" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="${this.params.width}" height="${this.params.height}" name="webcam_movie_embed" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="${flashvars}"></embed></object>` | |
} | |
getMovie () { | |
// get reference to movie object/embed in DOM | |
var movie = document.getElementById('webcam_movie_obj') | |
if (!movie || !movie._snap) movie = document.getElementById('webcam_movie_embed') | |
if (!movie) console.log('getMovie error') | |
return movie | |
} | |
/** | |
* Stops the webcam capture and video playback. | |
*/ | |
stop () { | |
let { videoStream } = this | |
this.updateState({ | |
cameraReady: false | |
}) | |
if (videoStream) { | |
if (videoStream.stop) { | |
videoStream.stop() | |
} else if (videoStream.msStop) { | |
videoStream.msStop() | |
} | |
videoStream.onended = null | |
videoStream = null | |
} | |
} | |
flashNotify (type, msg) { | |
// receive notification from flash about event | |
switch (type) { | |
case 'flashLoadComplete': | |
// movie loaded successfully | |
break | |
case 'cameraLive': | |
// camera is live and ready to snap | |
this.live = true | |
break | |
case 'error': | |
// Flash error | |
console.log('There was a flash error', msg) | |
break | |
default: | |
// catch-all event, just in case | |
console.log('webcam flash_notify: ' + type + ': ' + msg) | |
break | |
} | |
} | |
configure (panel) { | |
// open flash configuration panel -- specify tab name: | |
// 'camera', 'privacy', 'default', 'localStorage', 'microphone', 'settingsManager' | |
if (!panel) panel = 'camera' | |
this.getMovie()._configure(panel) | |
} | |
/** | |
* Takes a snapshot and displays it in a canvas. | |
*/ | |
getImage (video, opts) { | |
var canvas = document.createElement('canvas') | |
canvas.width = video.videoWidth | |
canvas.height = video.videoHeight | |
canvas.getContext('2d').drawImage(video, 0, 0) | |
var dataUrl = canvas.toDataURL(opts.mimeType) | |
var file = _$dataURItoFile_137(dataUrl, { | |
name: opts.name | |
}) | |
return { | |
dataUrl: dataUrl, | |
data: file, | |
type: opts.mimeType | |
} | |
} | |
takeSnapshot (video, canvas) { | |
const opts = { | |
name: `webcam-${Date.now()}.jpg`, | |
mimeType: 'image/jpeg' | |
} | |
const image = this.getImage(video, canvas, opts) | |
const tagFile = { | |
source: this.id, | |
name: opts.name, | |
data: image.data, | |
type: opts.type | |
} | |
return tagFile | |
} | |
} | |
/* removed: const _$Plugin_117 = require('../Plugin') */; | |
/* removed: const _$Webcam_136 = require('../../uppy-base/src/plugins/Webcam') */; | |
/* removed: const _$Translator_83 = require('../../core/Translator') */; | |
const { extend: __extend_133, | |
getFileTypeExtension: __getFileTypeExtension_133, | |
supportsMediaRecorder: __supportsMediaRecorder_133 } = _$Utils_85 | |
/* removed: const _$WebcamIcon_132 = require('./WebcamIcon') */; | |
/* removed: const _$CameraScreen_126 = require('./CameraScreen') */; | |
/* removed: const _$PermissionsScreen_127 = require('./PermissionsScreen') */; | |
/** | |
* Webcam | |
*/ | |
var _$Webcam_133 = class Webcam extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.userMedia = true | |
this.protocol = location.protocol.match(/https/i) ? 'https' : 'http' | |
this.type = 'acquirer' | |
this.id = 'Webcam' | |
this.title = 'Webcam' | |
this.icon = _$WebcamIcon_132 | |
this.focus = this.focus.bind(this) | |
const defaultLocale = { | |
strings: { | |
smile: 'Smile!' | |
} | |
} | |
// set default options | |
const defaultOptions = { | |
enableFlash: true, | |
onBeforeSnapshot: () => Promise.resolve(), | |
countdown: false, | |
locale: defaultLocale, | |
modes: [ | |
'video-audio', | |
'video-only', | |
'audio-only', | |
'picture' | |
] | |
} | |
this.params = { | |
swfURL: 'webcam.swf', | |
width: 400, | |
height: 300, | |
dest_width: 800, // size of captured image | |
dest_height: 600, // these default to width/height | |
image_format: 'jpeg', // image format (may be jpeg or png) | |
jpeg_quality: 90, // jpeg image quality from 0 (worst) to 100 (best) | |
enable_flash: true, // enable flash fallback, | |
force_flash: false, // force flash mode, | |
flip_horiz: false, // flip image horiz (mirror mode) | |
fps: 30, // camera frames per second | |
upload_name: 'webcam', // name of file in upload post data | |
constraints: null, // custom user media constraints, | |
flashNotDetectedText: 'ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).', | |
noInterfaceFoundText: 'No supported webcam interface found.', | |
unfreeze_snap: true // Whether to unfreeze the camera after snap (defaults to true) | |
} | |
// merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.locale = Object.assign({}, defaultLocale, this.opts.locale) | |
this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings) | |
// i18n | |
this.translator = new _$Translator_83({locale: this.locale}) | |
this.i18n = this.translator.translate.bind(this.translator) | |
this.install = this.install.bind(this) | |
this.updateState = this.updateState.bind(this) | |
this.render = this.render.bind(this) | |
// Camera controls | |
this.start = this.start.bind(this) | |
this.stop = this.stop.bind(this) | |
this.takeSnapshot = this.takeSnapshot.bind(this) | |
this.startRecording = this.startRecording.bind(this) | |
this.stopRecording = this.stopRecording.bind(this) | |
this.oneTwoThreeSmile = this.oneTwoThreeSmile.bind(this) | |
// this.justSmile = this.justSmile.bind(this) | |
this.webcam = new _$Webcam_136(this.opts, this.params) | |
this.webcamActive = false | |
if (this.opts.countdown) { | |
this.opts.onBeforeSnapshot = this.oneTwoThreeSmile | |
} | |
// if (typeof opts.onBeforeSnapshot === 'undefined' || !this.opts.onBeforeSnapshot) { | |
// if (this.opts.countdown) { | |
// this.opts.onBeforeSnapshot = this.oneTwoThreeSmile | |
// } else { | |
// this.opts.onBeforeSnapshot = this.justSmile | |
// } | |
// } | |
} | |
/** | |
* Little shorthand to update the state with my new state | |
*/ | |
updateState (newState) { | |
const {state} = this.core | |
const webcam = Object.assign({}, state.webcam, newState) | |
this.core.setState({webcam}) | |
} | |
start () { | |
this.webcamActive = true | |
this.webcam.start() | |
.then((stream) => { | |
this.stream = stream | |
this.updateState({ | |
// videoStream: stream, | |
cameraReady: true | |
}) | |
}) | |
.catch((err) => { | |
this.updateState({ | |
cameraError: err | |
}) | |
}) | |
} | |
startRecording () { | |
// TODO We can check here if any of the mime types listed in the | |
// mimeToExtensions map in Utils.js are supported, and prefer to use one of | |
// those. | |
// Right now we let the browser pick a type that it deems appropriate. | |
this.recorder = new MediaRecorder(this.stream) | |
this.recordingChunks = [] | |
this.recorder.addEventListener('dataavailable', (event) => { | |
this.recordingChunks.push(event.data) | |
}) | |
this.recorder.start() | |
this.updateState({ | |
isRecording: true | |
}) | |
} | |
stopRecording () { | |
return new Promise((resolve, reject) => { | |
this.recorder.addEventListener('stop', () => { | |
this.updateState({ | |
isRecording: false | |
}) | |
const mimeType = this.recordingChunks[0].type | |
const fileExtension = __getFileTypeExtension_133(mimeType) | |
if (!fileExtension) { | |
reject(new Error(`Could not upload file: Unsupported media type "${mimeType}"`)) | |
return | |
} | |
const file = { | |
source: this.id, | |
name: `webcam-${Date.now()}.${fileExtension}`, | |
type: mimeType, | |
data: new Blob(this.recordingChunks, { type: mimeType }) | |
} | |
this.core.addfile(file) | |
this.recordingChunks = null | |
this.recorder = null | |
resolve() | |
}) | |
this.recorder.stop() | |
}) | |
} | |
stop () { | |
this.stream.getAudioTracks().forEach((track) => { | |
track.stop() | |
}) | |
this.stream.getVideoTracks().forEach((track) => { | |
track.stop() | |
}) | |
this.webcamActive = false | |
this.stream = null | |
this.streamSrc = null | |
} | |
oneTwoThreeSmile () { | |
return new Promise((resolve, reject) => { | |
let count = this.opts.countdown | |
let countDown = setInterval(() => { | |
if (!this.webcamActive) { | |
clearInterval(countDown) | |
this.captureInProgress = false | |
return reject('Webcam is not active') | |
} | |
if (count > 0) { | |
this.core.info(`${count}...`, 'warning', 800) | |
count-- | |
} else { | |
clearInterval(countDown) | |
this.core.info(this.i18n('smile'), 'success', 1500) | |
setTimeout(() => resolve(), 1500) | |
} | |
}, 1000) | |
}) | |
} | |
// justSmile () { | |
// return new Promise((resolve, reject) => { | |
// setTimeout(() => this.core.info(this.i18n('smile'), 'success', 1000), 1500) | |
// setTimeout(() => resolve(), 2000) | |
// }) | |
// } | |
takeSnapshot () { | |
const opts = { | |
name: `webcam-${Date.now()}.jpg`, | |
mimeType: 'image/jpeg' | |
} | |
this.videoEl = this.target.querySelector('.UppyWebcam-video') | |
if (this.captureInProgress) return | |
this.captureInProgress = true | |
this.opts.onBeforeSnapshot().catch((err) => { | |
this.core.info(err, 'error', 5000) | |
return Promise.reject(`onBeforeSnapshot: ${err}`) | |
}).then(() => { | |
const video = this.target.querySelector('.UppyWebcam-video') | |
if (!video) { | |
this.captureInProgress = false | |
return Promise.reject('No video element found, likely due to the Webcam tab being closed.') | |
} | |
const image = this.webcam.getImage(video, opts) | |
const tagFile = { | |
source: this.id, | |
name: opts.name, | |
data: image.data, | |
type: opts.mimeType | |
} | |
this.captureInProgress = false | |
this.core.addFile(tagFile) | |
}) | |
} | |
focus () { | |
if (this.opts.countdown) return | |
setTimeout(() => { | |
this.core.info(this.i18n('smile'), 'success', 1500) | |
}, 1000) | |
} | |
render (state) { | |
if (!this.webcamActive) { | |
this.start() | |
} | |
if (!state.webcam.cameraReady && !state.webcam.useTheFlash) { | |
return _$PermissionsScreen_127(state.webcam) | |
} | |
if (!this.streamSrc) { | |
this.streamSrc = this.stream ? URL.createObjectURL(this.stream) : null | |
} | |
return _$CameraScreen_126(__extend_133(state.webcam, { | |
onSnapshot: this.takeSnapshot, | |
onStartRecording: this.startRecording, | |
onStopRecording: this.stopRecording, | |
onFocus: this.focus, | |
onStop: this.stop, | |
modes: this.opts.modes, | |
supportsRecording: __supportsMediaRecorder_133(), | |
recording: state.webcam.isRecording, | |
getSWFHTML: this.webcam.getSWFHTML, | |
src: this.streamSrc | |
})) | |
} | |
install () { | |
this.webcam.init() | |
this.core.setState({ | |
webcam: { | |
cameraReady: false | |
} | |
}) | |
const target = this.opts.target | |
const plugin = this | |
this.target = this.mount(target, plugin) | |
} | |
uninstall () { | |
this.webcam.reset() | |
this.unmount() | |
} | |
} | |
/* removed: const _$Plugin_117 = require('./Plugin') */; | |
/* removed: const _$UppySocket_84 = require('../core/UppySocket') */; | |
/* removed: const _$Utils_85 = require('../core/Utils') */; | |
var _$XHRUpload_134 = class XHRUpload extends _$Plugin_117 { | |
constructor (core, opts) { | |
super(core, opts) | |
this.type = 'uploader' | |
this.id = 'XHRUpload' | |
this.title = 'XHRUpload' | |
// Default options | |
const defaultOptions = { | |
formData: true, | |
fieldName: 'files[]', | |
method: 'post', | |
metaFields: null, | |
responseUrlFieldName: 'url', | |
bundle: true, | |
headers: {}, | |
getResponseData (xhr) { | |
return JSON.parse(xhr.response) | |
} | |
} | |
// Merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.handleUpload = this.handleUpload.bind(this) | |
} | |
createFormDataUpload (file, opts) { | |
const formPost = new FormData() | |
const metaFields = Array.isArray(opts.metaFields) | |
? opts.metaFields | |
// Send along all fields by default. | |
: Object.keys(file.meta) | |
metaFields.forEach((item) => { | |
formPost.append(item, file.meta[item]) | |
}) | |
formPost.append(opts.fieldName, file.data) | |
return formPost | |
} | |
createBareUpload (file, opts) { | |
return file.data | |
} | |
upload (file, current, total) { | |
const opts = Object.assign({}, | |
this.opts, | |
this.core.state.xhrUpload || {}, | |
file.xhrUpload || {} | |
) | |
this.core.log(`uploading ${current} of ${total}`) | |
return new Promise((resolve, reject) => { | |
const data = opts.formData | |
? this.createFormDataUpload(file, opts) | |
: this.createBareUpload(file, opts) | |
const xhr = new XMLHttpRequest() | |
xhr.upload.addEventListener('progress', (ev) => { | |
if (ev.lengthComputable) { | |
this.core.emit('core:upload-progress', { | |
uploader: this, | |
id: file.id, | |
bytesUploaded: ev.loaded, | |
bytesTotal: ev.total | |
}) | |
} | |
}) | |
xhr.addEventListener('load', (ev) => { | |
if (ev.target.status >= 200 && ev.target.status < 300) { | |
const resp = opts.getResponseData(xhr) | |
const uploadURL = resp[opts.responseUrlFieldName] | |
this.core.emit('core:upload-success', file.id, resp, uploadURL) | |
if (uploadURL) { | |
this.core.log(`Download ${file.name} from ${file.uploadURL}`) | |
} | |
return resolve(file) | |
} else { | |
this.core.emit('core:upload-error', file.id, xhr) | |
return reject('Upload error') | |
} | |
// var upload = {} | |
// | |
// if (opts.bundle) { | |
// upload = {files: files} | |
// } else { | |
// upload = {file: files[current]} | |
// } | |
}) | |
xhr.addEventListener('error', (ev) => { | |
this.core.emit('core:upload-error', file.id) | |
return reject('Upload error') | |
}) | |
xhr.open(opts.method.toUpperCase(), opts.endpoint, true) | |
Object.keys(opts.headers).forEach((header) => { | |
xhr.setRequestHeader(header, opts.headers[header]) | |
}) | |
xhr.send(data) | |
this.core.on('core:upload-cancel', (fileID) => { | |
if (fileID === file.id) { | |
xhr.abort() | |
} | |
}) | |
this.core.on('core:cancel-all', () => { | |
// const files = this.core.getState().files | |
// if (!files[file.id]) return | |
xhr.abort() | |
}) | |
this.core.emit('core:upload-started', file.id) | |
}) | |
} | |
uploadRemote (file, current, total) { | |
const opts = Object.assign({}, this.opts, file.xhrUpload || {}) | |
return new Promise((resolve, reject) => { | |
this.core.emit('core:upload-started', file.id) | |
fetch(file.remote.url, { | |
method: 'post', | |
credentials: 'include', | |
headers: { | |
'Accept': 'application/json', | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify(Object.assign({}, file.remote.body, { | |
endpoint: opts.endpoint, | |
size: file.data.size, | |
fieldname: opts.fieldName | |
})) | |
}) | |
.then((res) => { | |
if (res.status < 200 && res.status > 300) { | |
return reject(res.statusText) | |
} | |
res.json().then((data) => { | |
const token = data.token | |
const host = _$Utils_85.getSocketHost(file.remote.host) | |
const socket = new _$UppySocket_84({ target: `${host}/api/${token}` }) | |
socket.on('progress', (progressData) => _$Utils_85.emitSocketProgress(this, progressData, file)) | |
socket.on('success', (data) => { | |
this.core.emit('core:upload-success', file.id, data, data.url) | |
socket.close() | |
return resolve() | |
}) | |
}) | |
}) | |
}) | |
} | |
selectForUpload (files) { | |
files.forEach((file, i) => { | |
const current = parseInt(i, 10) + 1 | |
const total = files.length | |
if (file.isRemote) { | |
this.uploadRemote(file, current, total) | |
} else { | |
this.upload(file, current, total) | |
} | |
}) | |
// if (this.opts.bundle) { | |
// uploaders.push(this.upload(files, 0, files.length)) | |
// } else { | |
// for (let i in files) { | |
// uploaders.push(this.upload(files, i, files.length)) | |
// } | |
// } | |
} | |
handleUpload (fileIDs) { | |
if (fileIDs.length === 0) { | |
this.core.log('XHRUpload: no files to upload!') | |
return Promise.resolve() | |
} | |
this.core.log('XHRUpload is uploading...') | |
const files = fileIDs.map(getFile, this) | |
function getFile (fileID) { | |
return this.core.state.files[fileID] | |
} | |
this.selectForUpload(files) | |
return new Promise((resolve) => { | |
this.core.once('core:upload-complete', resolve) | |
}) | |
} | |
install () { | |
this.core.addUploader(this.handleUpload) | |
} | |
uninstall () { | |
this.core.removeUploader(this.handleUpload) | |
} | |
} | |
/* removed: const _$Core_86 = require('./core/index.js') */; | |
// Parent | |
/* removed: const _$Plugin_117 = require('./plugins/Plugin') */; | |
// Orchestrators | |
/* removed: const _$DashboardUI_107 = require('./plugins/Dashboard/index.js') */; | |
// Acquirers | |
/* removed: const _$Dummy_111 = require('./plugins/Dummy') */; | |
/* removed: const _$DragDrop_108 = require('./plugins/DragDrop/index.js') */; | |
/* removed: const _$FileInput_112 = require('./plugins/FileInput.js') */; | |
/* removed: const _$Google_113 = require('./plugins/GoogleDrive/index.js') */; | |
/* removed: const _$Dropbox_110 = require('./plugins/Dropbox/index.js') */; | |
/* removed: const _$Instagram_115 = require('./plugins/Instagram/index.js') */; | |
/* removed: const _$Webcam_133 = require('./plugins/Webcam/index.js') */; | |
// Progressindicators | |
/* removed: const _$ProgressBar_118 = require('./plugins/ProgressBar.js') */; | |
/* removed: const _$Informer_114 = require('./plugins/Informer.js') */; | |
// Modifiers | |
/* removed: const _$MetaData_116 = require('./plugins/MetaData.js') */; | |
// Uploaders | |
/* removed: const _$Tus10_124 = require('./plugins/Tus10') */; | |
/* removed: const _$XHRUpload_134 = require('./plugins/XHRUpload') */; | |
/* removed: const _$Transloadit_123 = require('./plugins/Transloadit') */; | |
var _$src_96 = { | |
Core: _$Core_86, | |
Plugin: _$Plugin_117, | |
Dummy: _$Dummy_111, | |
ProgressBar: _$ProgressBar_118, | |
Informer: _$Informer_114, | |
DragDrop: _$DragDrop_108, | |
GoogleDrive: _$Google_113, | |
Dropbox: _$Dropbox_110, | |
Instagram: _$Instagram_115, | |
FileInput: _$FileInput_112, | |
Tus10: _$Tus10_124, | |
XHRUpload: _$XHRUpload_134, | |
Transloadit: _$Transloadit_123, | |
Dashboard: _$DashboardUI_107, | |
MetaData: _$MetaData_116, | |
Webcam: _$Webcam_133 | |
} | |
module.exports = _$src_96; | |
}()); |
This file has been truncated, but you can view the full file.
This file contains 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
(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){ | |
(function (global){ | |
'use strict'; | |
// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js | |
// original notice: | |
/*! | |
* The buffer module from node.js, for the browser. | |
* | |
* @author Feross Aboukhadijeh <[email protected]> <http://feross.org> | |
* @license MIT | |
*/ | |
function compare(a, b) { | |
if (a === b) { | |
return 0; | |
} | |
var x = a.length; | |
var y = b.length; | |
for (var i = 0, len = Math.min(x, y); i < len; ++i) { | |
if (a[i] !== b[i]) { | |
x = a[i]; | |
y = b[i]; | |
break; | |
} | |
} | |
if (x < y) { | |
return -1; | |
} | |
if (y < x) { | |
return 1; | |
} | |
return 0; | |
} | |
function isBuffer(b) { | |
if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { | |
return global.Buffer.isBuffer(b); | |
} | |
return !!(b != null && b._isBuffer); | |
} | |
// based on node assert, original notice: | |
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 | |
// | |
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! | |
// | |
// Originally from narwhal.js (http://narwhaljs.org) | |
// Copyright (c) 2009 Thomas Robinson <280north.com> | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the 'Software'), to | |
// deal in the Software without restriction, including without limitation the | |
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
// sell copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included in | |
// all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
var util = require('util/'); | |
var hasOwn = Object.prototype.hasOwnProperty; | |
var pSlice = Array.prototype.slice; | |
var functionsHaveNames = (function () { | |
return function foo() {}.name === 'foo'; | |
}()); | |
function pToString (obj) { | |
return Object.prototype.toString.call(obj); | |
} | |
function isView(arrbuf) { | |
if (isBuffer(arrbuf)) { | |
return false; | |
} | |
if (typeof global.ArrayBuffer !== 'function') { | |
return false; | |
} | |
if (typeof ArrayBuffer.isView === 'function') { | |
return ArrayBuffer.isView(arrbuf); | |
} | |
if (!arrbuf) { | |
return false; | |
} | |
if (arrbuf instanceof DataView) { | |
return true; | |
} | |
if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { | |
return true; | |
} | |
return false; | |
} | |
// 1. The assert module provides functions that throw | |
// AssertionError's when particular conditions are not met. The | |
// assert module must conform to the following interface. | |
var assert = module.exports = ok; | |
// 2. The AssertionError is defined in assert. | |
// new assert.AssertionError({ message: message, | |
// actual: actual, | |
// expected: expected }) | |
var regex = /\s*function\s+([^\(\s]*)\s*/; | |
// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js | |
function getName(func) { | |
if (!util.isFunction(func)) { | |
return; | |
} | |
if (functionsHaveNames) { | |
return func.name; | |
} | |
var str = func.toString(); | |
var match = str.match(regex); | |
return match && match[1]; | |
} | |
assert.AssertionError = function AssertionError(options) { | |
this.name = 'AssertionError'; | |
this.actual = options.actual; | |
this.expected = options.expected; | |
this.operator = options.operator; | |
if (options.message) { | |
this.message = options.message; | |
this.generatedMessage = false; | |
} else { | |
this.message = getMessage(this); | |
this.generatedMessage = true; | |
} | |
var stackStartFunction = options.stackStartFunction || fail; | |
if (Error.captureStackTrace) { | |
Error.captureStackTrace(this, stackStartFunction); | |
} else { | |
// non v8 browsers so we can have a stacktrace | |
var err = new Error(); | |
if (err.stack) { | |
var out = err.stack; | |
// try to strip useless frames | |
var fn_name = getName(stackStartFunction); | |
var idx = out.indexOf('\n' + fn_name); | |
if (idx >= 0) { | |
// once we have located the function frame | |
// we need to strip out everything before it (and its line) | |
var next_line = out.indexOf('\n', idx + 1); | |
out = out.substring(next_line + 1); | |
} | |
this.stack = out; | |
} | |
} | |
}; | |
// assert.AssertionError instanceof Error | |
util.inherits(assert.AssertionError, Error); | |
function truncate(s, n) { | |
if (typeof s === 'string') { | |
return s.length < n ? s : s.slice(0, n); | |
} else { | |
return s; | |
} | |
} | |
function inspect(something) { | |
if (functionsHaveNames || !util.isFunction(something)) { | |
return util.inspect(something); | |
} | |
var rawname = getName(something); | |
var name = rawname ? ': ' + rawname : ''; | |
return '[Function' + name + ']'; | |
} | |
function getMessage(self) { | |
return truncate(inspect(self.actual), 128) + ' ' + | |
self.operator + ' ' + | |
truncate(inspect(self.expected), 128); | |
} | |
// At present only the three keys mentioned above are used and | |
// understood by the spec. Implementations or sub modules can pass | |
// other keys to the AssertionError's constructor - they will be | |
// ignored. | |
// 3. All of the following functions must throw an AssertionError | |
// when a corresponding condition is not met, with a message that | |
// may be undefined if not provided. All assertion methods provide | |
// both the actual and expected values to the assertion error for | |
// display purposes. | |
function fail(actual, expected, message, operator, stackStartFunction) { | |
throw new assert.AssertionError({ | |
message: message, | |
actual: actual, | |
expected: expected, | |
operator: operator, | |
stackStartFunction: stackStartFunction | |
}); | |
} | |
// EXTENSION! allows for well behaved errors defined elsewhere. | |
assert.fail = fail; | |
// 4. Pure assertion tests whether a value is truthy, as determined | |
// by !!guard. | |
// assert.ok(guard, message_opt); | |
// This statement is equivalent to assert.equal(true, !!guard, | |
// message_opt);. To test strictly for the value true, use | |
// assert.strictEqual(true, guard, message_opt);. | |
function ok(value, message) { | |
if (!value) fail(value, true, message, '==', assert.ok); | |
} | |
assert.ok = ok; | |
// 5. The equality assertion tests shallow, coercive equality with | |
// ==. | |
// assert.equal(actual, expected, message_opt); | |
assert.equal = function equal(actual, expected, message) { | |
if (actual != expected) fail(actual, expected, message, '==', assert.equal); | |
}; | |
// 6. The non-equality assertion tests for whether two objects are not equal | |
// with != assert.notEqual(actual, expected, message_opt); | |
assert.notEqual = function notEqual(actual, expected, message) { | |
if (actual == expected) { | |
fail(actual, expected, message, '!=', assert.notEqual); | |
} | |
}; | |
// 7. The equivalence assertion tests a deep equality relation. | |
// assert.deepEqual(actual, expected, message_opt); | |
assert.deepEqual = function deepEqual(actual, expected, message) { | |
if (!_deepEqual(actual, expected, false)) { | |
fail(actual, expected, message, 'deepEqual', assert.deepEqual); | |
} | |
}; | |
assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { | |
if (!_deepEqual(actual, expected, true)) { | |
fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); | |
} | |
}; | |
function _deepEqual(actual, expected, strict, memos) { | |
// 7.1. All identical values are equivalent, as determined by ===. | |
if (actual === expected) { | |
return true; | |
} else if (isBuffer(actual) && isBuffer(expected)) { | |
return compare(actual, expected) === 0; | |
// 7.2. If the expected value is a Date object, the actual value is | |
// equivalent if it is also a Date object that refers to the same time. | |
} else if (util.isDate(actual) && util.isDate(expected)) { | |
return actual.getTime() === expected.getTime(); | |
// 7.3 If the expected value is a RegExp object, the actual value is | |
// equivalent if it is also a RegExp object with the same source and | |
// properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). | |
} else if (util.isRegExp(actual) && util.isRegExp(expected)) { | |
return actual.source === expected.source && | |
actual.global === expected.global && | |
actual.multiline === expected.multiline && | |
actual.lastIndex === expected.lastIndex && | |
actual.ignoreCase === expected.ignoreCase; | |
// 7.4. Other pairs that do not both pass typeof value == 'object', | |
// equivalence is determined by ==. | |
} else if ((actual === null || typeof actual !== 'object') && | |
(expected === null || typeof expected !== 'object')) { | |
return strict ? actual === expected : actual == expected; | |
// If both values are instances of typed arrays, wrap their underlying | |
// ArrayBuffers in a Buffer each to increase performance | |
// This optimization requires the arrays to have the same type as checked by | |
// Object.prototype.toString (aka pToString). Never perform binary | |
// comparisons for Float*Arrays, though, since e.g. +0 === -0 but their | |
// bit patterns are not identical. | |
} else if (isView(actual) && isView(expected) && | |
pToString(actual) === pToString(expected) && | |
!(actual instanceof Float32Array || | |
actual instanceof Float64Array)) { | |
return compare(new Uint8Array(actual.buffer), | |
new Uint8Array(expected.buffer)) === 0; | |
// 7.5 For all other Object pairs, including Array objects, equivalence is | |
// determined by having the same number of owned properties (as verified | |
// with Object.prototype.hasOwnProperty.call), the same set of keys | |
// (although not necessarily the same order), equivalent values for every | |
// corresponding key, and an identical 'prototype' property. Note: this | |
// accounts for both named and indexed properties on Arrays. | |
} else if (isBuffer(actual) !== isBuffer(expected)) { | |
return false; | |
} else { | |
memos = memos || {actual: [], expected: []}; | |
var actualIndex = memos.actual.indexOf(actual); | |
if (actualIndex !== -1) { | |
if (actualIndex === memos.expected.indexOf(expected)) { | |
return true; | |
} | |
} | |
memos.actual.push(actual); | |
memos.expected.push(expected); | |
return objEquiv(actual, expected, strict, memos); | |
} | |
} | |
function isArguments(object) { | |
return Object.prototype.toString.call(object) == '[object Arguments]'; | |
} | |
function objEquiv(a, b, strict, actualVisitedObjects) { | |
if (a === null || a === undefined || b === null || b === undefined) | |
return false; | |
// if one is a primitive, the other must be same | |
if (util.isPrimitive(a) || util.isPrimitive(b)) | |
return a === b; | |
if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) | |
return false; | |
var aIsArgs = isArguments(a); | |
var bIsArgs = isArguments(b); | |
if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) | |
return false; | |
if (aIsArgs) { | |
a = pSlice.call(a); | |
b = pSlice.call(b); | |
return _deepEqual(a, b, strict); | |
} | |
var ka = objectKeys(a); | |
var kb = objectKeys(b); | |
var key, i; | |
// having the same number of owned properties (keys incorporates | |
// hasOwnProperty) | |
if (ka.length !== kb.length) | |
return false; | |
//the same set of keys (although not necessarily the same order), | |
ka.sort(); | |
kb.sort(); | |
//~~~cheap key test | |
for (i = ka.length - 1; i >= 0; i--) { | |
if (ka[i] !== kb[i]) | |
return false; | |
} | |
//equivalent values for every corresponding key, and | |
//~~~possibly expensive deep test | |
for (i = ka.length - 1; i >= 0; i--) { | |
key = ka[i]; | |
if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) | |
return false; | |
} | |
return true; | |
} | |
// 8. The non-equivalence assertion tests for any deep inequality. | |
// assert.notDeepEqual(actual, expected, message_opt); | |
assert.notDeepEqual = function notDeepEqual(actual, expected, message) { | |
if (_deepEqual(actual, expected, false)) { | |
fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); | |
} | |
}; | |
assert.notDeepStrictEqual = notDeepStrictEqual; | |
function notDeepStrictEqual(actual, expected, message) { | |
if (_deepEqual(actual, expected, true)) { | |
fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); | |
} | |
} | |
// 9. The strict equality assertion tests strict equality, as determined by ===. | |
// assert.strictEqual(actual, expected, message_opt); | |
assert.strictEqual = function strictEqual(actual, expected, message) { | |
if (actual !== expected) { | |
fail(actual, expected, message, '===', assert.strictEqual); | |
} | |
}; | |
// 10. The strict non-equality assertion tests for strict inequality, as | |
// determined by !==. assert.notStrictEqual(actual, expected, message_opt); | |
assert.notStrictEqual = function notStrictEqual(actual, expected, message) { | |
if (actual === expected) { | |
fail(actual, expected, message, '!==', assert.notStrictEqual); | |
} | |
}; | |
function expectedException(actual, expected) { | |
if (!actual || !expected) { | |
return false; | |
} | |
if (Object.prototype.toString.call(expected) == '[object RegExp]') { | |
return expected.test(actual); | |
} | |
try { | |
if (actual instanceof expected) { | |
return true; | |
} | |
} catch (e) { | |
// Ignore. The instanceof check doesn't work for arrow functions. | |
} | |
if (Error.isPrototypeOf(expected)) { | |
return false; | |
} | |
return expected.call({}, actual) === true; | |
} | |
function _tryBlock(block) { | |
var error; | |
try { | |
block(); | |
} catch (e) { | |
error = e; | |
} | |
return error; | |
} | |
function _throws(shouldThrow, block, expected, message) { | |
var actual; | |
if (typeof block !== 'function') { | |
throw new TypeError('"block" argument must be a function'); | |
} | |
if (typeof expected === 'string') { | |
message = expected; | |
expected = null; | |
} | |
actual = _tryBlock(block); | |
message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + | |
(message ? ' ' + message : '.'); | |
if (shouldThrow && !actual) { | |
fail(actual, expected, 'Missing expected exception' + message); | |
} | |
var userProvidedMessage = typeof message === 'string'; | |
var isUnwantedException = !shouldThrow && util.isError(actual); | |
var isUnexpectedException = !shouldThrow && actual && !expected; | |
if ((isUnwantedException && | |
userProvidedMessage && | |
expectedException(actual, expected)) || | |
isUnexpectedException) { | |
fail(actual, expected, 'Got unwanted exception' + message); | |
} | |
if ((shouldThrow && actual && expected && | |
!expectedException(actual, expected)) || (!shouldThrow && actual)) { | |
throw actual; | |
} | |
} | |
// 11. Expected to throw an error: | |
// assert.throws(block, Error_opt, message_opt); | |
assert.throws = function(block, /*optional*/error, /*optional*/message) { | |
_throws(true, block, error, message); | |
}; | |
// EXTENSION! This is annoying to write outside this module. | |
assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { | |
_throws(false, block, error, message); | |
}; | |
assert.ifError = function(err) { if (err) throw err; }; | |
var objectKeys = Object.keys || function (obj) { | |
var keys = []; | |
for (var key in obj) { | |
if (hasOwn.call(obj, key)) keys.push(key); | |
} | |
return keys; | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"util/":6}],2:[function(require,module,exports){ | |
},{}],3:[function(require,module,exports){ | |
// shim for using process in browser | |
var process = module.exports = {}; | |
// cached from whatever global is present so that test runners that stub it | |
// don't break things. But we need to wrap it in a try catch in case it is | |
// wrapped in strict mode code which doesn't define any globals. It's inside a | |
// function because try/catches deoptimize in certain engines. | |
var cachedSetTimeout; | |
var cachedClearTimeout; | |
function defaultSetTimout() { | |
throw new Error('setTimeout has not been defined'); | |
} | |
function defaultClearTimeout () { | |
throw new Error('clearTimeout has not been defined'); | |
} | |
(function () { | |
try { | |
if (typeof setTimeout === 'function') { | |
cachedSetTimeout = setTimeout; | |
} else { | |
cachedSetTimeout = defaultSetTimout; | |
} | |
} catch (e) { | |
cachedSetTimeout = defaultSetTimout; | |
} | |
try { | |
if (typeof clearTimeout === 'function') { | |
cachedClearTimeout = clearTimeout; | |
} else { | |
cachedClearTimeout = defaultClearTimeout; | |
} | |
} catch (e) { | |
cachedClearTimeout = defaultClearTimeout; | |
} | |
} ()) | |
function runTimeout(fun) { | |
if (cachedSetTimeout === setTimeout) { | |
//normal enviroments in sane situations | |
return setTimeout(fun, 0); | |
} | |
// if setTimeout wasn't available but was latter defined | |
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { | |
cachedSetTimeout = setTimeout; | |
return setTimeout(fun, 0); | |
} | |
try { | |
// when when somebody has screwed with setTimeout but no I.E. maddness | |
return cachedSetTimeout(fun, 0); | |
} catch(e){ | |
try { | |
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally | |
return cachedSetTimeout.call(null, fun, 0); | |
} catch(e){ | |
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error | |
return cachedSetTimeout.call(this, fun, 0); | |
} | |
} | |
} | |
function runClearTimeout(marker) { | |
if (cachedClearTimeout === clearTimeout) { | |
//normal enviroments in sane situations | |
return clearTimeout(marker); | |
} | |
// if clearTimeout wasn't available but was latter defined | |
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { | |
cachedClearTimeout = clearTimeout; | |
return clearTimeout(marker); | |
} | |
try { | |
// when when somebody has screwed with setTimeout but no I.E. maddness | |
return cachedClearTimeout(marker); | |
} catch (e){ | |
try { | |
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally | |
return cachedClearTimeout.call(null, marker); | |
} catch (e){ | |
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. | |
// Some versions of I.E. have different rules for clearTimeout vs setTimeout | |
return cachedClearTimeout.call(this, marker); | |
} | |
} | |
} | |
var queue = []; | |
var draining = false; | |
var currentQueue; | |
var queueIndex = -1; | |
function cleanUpNextTick() { | |
if (!draining || !currentQueue) { | |
return; | |
} | |
draining = false; | |
if (currentQueue.length) { | |
queue = currentQueue.concat(queue); | |
} else { | |
queueIndex = -1; | |
} | |
if (queue.length) { | |
drainQueue(); | |
} | |
} | |
function drainQueue() { | |
if (draining) { | |
return; | |
} | |
var timeout = runTimeout(cleanUpNextTick); | |
draining = true; | |
var len = queue.length; | |
while(len) { | |
currentQueue = queue; | |
queue = []; | |
while (++queueIndex < len) { | |
if (currentQueue) { | |
currentQueue[queueIndex].run(); | |
} | |
} | |
queueIndex = -1; | |
len = queue.length; | |
} | |
currentQueue = null; | |
draining = false; | |
runClearTimeout(timeout); | |
} | |
process.nextTick = function (fun) { | |
var args = new Array(arguments.length - 1); | |
if (arguments.length > 1) { | |
for (var i = 1; i < arguments.length; i++) { | |
args[i - 1] = arguments[i]; | |
} | |
} | |
queue.push(new Item(fun, args)); | |
if (queue.length === 1 && !draining) { | |
runTimeout(drainQueue); | |
} | |
}; | |
// v8 likes predictible objects | |
function Item(fun, array) { | |
this.fun = fun; | |
this.array = array; | |
} | |
Item.prototype.run = function () { | |
this.fun.apply(null, this.array); | |
}; | |
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.prependListener = noop; | |
process.prependOnceListener = noop; | |
process.listeners = function (name) { return [] } | |
process.binding = function (name) { | |
throw new Error('process.binding is not supported'); | |
}; | |
process.cwd = function () { return '/' }; | |
process.chdir = function (dir) { | |
throw new Error('process.chdir is not supported'); | |
}; | |
process.umask = function() { return 0; }; | |
},{}],4:[function(require,module,exports){ | |
if (typeof Object.create === 'function') { | |
// implementation from standard node.js 'util' module | |
module.exports = function inherits(ctor, superCtor) { | |
ctor.super_ = superCtor | |
ctor.prototype = Object.create(superCtor.prototype, { | |
constructor: { | |
value: ctor, | |
enumerable: false, | |
writable: true, | |
configurable: true | |
} | |
}); | |
}; | |
} else { | |
// old school shim for old browsers | |
module.exports = function inherits(ctor, superCtor) { | |
ctor.super_ = superCtor | |
var TempCtor = function () {} | |
TempCtor.prototype = superCtor.prototype | |
ctor.prototype = new TempCtor() | |
ctor.prototype.constructor = ctor | |
} | |
} | |
},{}],5:[function(require,module,exports){ | |
module.exports = function isBuffer(arg) { | |
return arg && typeof arg === 'object' | |
&& typeof arg.copy === 'function' | |
&& typeof arg.fill === 'function' | |
&& typeof arg.readUInt8 === 'function'; | |
} | |
},{}],6:[function(require,module,exports){ | |
(function (process,global){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
var formatRegExp = /%[sdj%]/g; | |
exports.format = function(f) { | |
if (!isString(f)) { | |
var objects = []; | |
for (var i = 0; i < arguments.length; i++) { | |
objects.push(inspect(arguments[i])); | |
} | |
return objects.join(' '); | |
} | |
var i = 1; | |
var args = arguments; | |
var len = args.length; | |
var str = String(f).replace(formatRegExp, function(x) { | |
if (x === '%%') return '%'; | |
if (i >= len) return x; | |
switch (x) { | |
case '%s': return String(args[i++]); | |
case '%d': return Number(args[i++]); | |
case '%j': | |
try { | |
return JSON.stringify(args[i++]); | |
} catch (_) { | |
return '[Circular]'; | |
} | |
default: | |
return x; | |
} | |
}); | |
for (var x = args[i]; i < len; x = args[++i]) { | |
if (isNull(x) || !isObject(x)) { | |
str += ' ' + x; | |
} else { | |
str += ' ' + inspect(x); | |
} | |
} | |
return str; | |
}; | |
// Mark that a method should not be used. | |
// Returns a modified function which warns once by default. | |
// If --no-deprecation is set, then it is a no-op. | |
exports.deprecate = function(fn, msg) { | |
// Allow for deprecating things in the process of starting up. | |
if (isUndefined(global.process)) { | |
return function() { | |
return exports.deprecate(fn, msg).apply(this, arguments); | |
}; | |
} | |
if (process.noDeprecation === true) { | |
return fn; | |
} | |
var warned = false; | |
function deprecated() { | |
if (!warned) { | |
if (process.throwDeprecation) { | |
throw new Error(msg); | |
} else if (process.traceDeprecation) { | |
console.trace(msg); | |
} else { | |
console.error(msg); | |
} | |
warned = true; | |
} | |
return fn.apply(this, arguments); | |
} | |
return deprecated; | |
}; | |
var debugs = {}; | |
var debugEnviron; | |
exports.debuglog = function(set) { | |
if (isUndefined(debugEnviron)) | |
debugEnviron = process.env.NODE_DEBUG || ''; | |
set = set.toUpperCase(); | |
if (!debugs[set]) { | |
if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { | |
var pid = process.pid; | |
debugs[set] = function() { | |
var msg = exports.format.apply(exports, arguments); | |
console.error('%s %d: %s', set, pid, msg); | |
}; | |
} else { | |
debugs[set] = function() {}; | |
} | |
} | |
return debugs[set]; | |
}; | |
/** | |
* Echos the value of a value. Trys to print the value out | |
* in the best way possible given the different types. | |
* | |
* @param {Object} obj The object to print out. | |
* @param {Object} opts Optional options object that alters the output. | |
*/ | |
/* legacy: obj, showHidden, depth, colors*/ | |
function inspect(obj, opts) { | |
// default options | |
var ctx = { | |
seen: [], | |
stylize: stylizeNoColor | |
}; | |
// legacy... | |
if (arguments.length >= 3) ctx.depth = arguments[2]; | |
if (arguments.length >= 4) ctx.colors = arguments[3]; | |
if (isBoolean(opts)) { | |
// legacy... | |
ctx.showHidden = opts; | |
} else if (opts) { | |
// got an "options" object | |
exports._extend(ctx, opts); | |
} | |
// set default options | |
if (isUndefined(ctx.showHidden)) ctx.showHidden = false; | |
if (isUndefined(ctx.depth)) ctx.depth = 2; | |
if (isUndefined(ctx.colors)) ctx.colors = false; | |
if (isUndefined(ctx.customInspect)) ctx.customInspect = true; | |
if (ctx.colors) ctx.stylize = stylizeWithColor; | |
return formatValue(ctx, obj, ctx.depth); | |
} | |
exports.inspect = inspect; | |
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics | |
inspect.colors = { | |
'bold' : [1, 22], | |
'italic' : [3, 23], | |
'underline' : [4, 24], | |
'inverse' : [7, 27], | |
'white' : [37, 39], | |
'grey' : [90, 39], | |
'black' : [30, 39], | |
'blue' : [34, 39], | |
'cyan' : [36, 39], | |
'green' : [32, 39], | |
'magenta' : [35, 39], | |
'red' : [31, 39], | |
'yellow' : [33, 39] | |
}; | |
// Don't use 'blue' not visible on cmd.exe | |
inspect.styles = { | |
'special': 'cyan', | |
'number': 'yellow', | |
'boolean': 'yellow', | |
'undefined': 'grey', | |
'null': 'bold', | |
'string': 'green', | |
'date': 'magenta', | |
// "name": intentionally not styling | |
'regexp': 'red' | |
}; | |
function stylizeWithColor(str, styleType) { | |
var style = inspect.styles[styleType]; | |
if (style) { | |
return '\u001b[' + inspect.colors[style][0] + 'm' + str + | |
'\u001b[' + inspect.colors[style][1] + 'm'; | |
} else { | |
return str; | |
} | |
} | |
function stylizeNoColor(str, styleType) { | |
return str; | |
} | |
function arrayToHash(array) { | |
var hash = {}; | |
array.forEach(function(val, idx) { | |
hash[val] = true; | |
}); | |
return hash; | |
} | |
function formatValue(ctx, value, recurseTimes) { | |
// Provide a hook for user-specified inspect functions. | |
// Check that value is an object with an inspect function on it | |
if (ctx.customInspect && | |
value && | |
isFunction(value.inspect) && | |
// Filter out the util module, it's inspect function is special | |
value.inspect !== exports.inspect && | |
// Also filter out any prototype objects using the circular check. | |
!(value.constructor && value.constructor.prototype === value)) { | |
var ret = value.inspect(recurseTimes, ctx); | |
if (!isString(ret)) { | |
ret = formatValue(ctx, ret, recurseTimes); | |
} | |
return ret; | |
} | |
// Primitive types cannot have properties | |
var primitive = formatPrimitive(ctx, value); | |
if (primitive) { | |
return primitive; | |
} | |
// Look up the keys of the object. | |
var keys = Object.keys(value); | |
var visibleKeys = arrayToHash(keys); | |
if (ctx.showHidden) { | |
keys = Object.getOwnPropertyNames(value); | |
} | |
// IE doesn't make error fields non-enumerable | |
// http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx | |
if (isError(value) | |
&& (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { | |
return formatError(value); | |
} | |
// Some type of object without properties can be shortcutted. | |
if (keys.length === 0) { | |
if (isFunction(value)) { | |
var name = value.name ? ': ' + value.name : ''; | |
return ctx.stylize('[Function' + name + ']', 'special'); | |
} | |
if (isRegExp(value)) { | |
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); | |
} | |
if (isDate(value)) { | |
return ctx.stylize(Date.prototype.toString.call(value), 'date'); | |
} | |
if (isError(value)) { | |
return formatError(value); | |
} | |
} | |
var base = '', array = false, braces = ['{', '}']; | |
// Make Array say that they are Array | |
if (isArray(value)) { | |
array = true; | |
braces = ['[', ']']; | |
} | |
// Make functions say that they are functions | |
if (isFunction(value)) { | |
var n = value.name ? ': ' + value.name : ''; | |
base = ' [Function' + n + ']'; | |
} | |
// Make RegExps say that they are RegExps | |
if (isRegExp(value)) { | |
base = ' ' + RegExp.prototype.toString.call(value); | |
} | |
// Make dates with properties first say the date | |
if (isDate(value)) { | |
base = ' ' + Date.prototype.toUTCString.call(value); | |
} | |
// Make error with message first say the error | |
if (isError(value)) { | |
base = ' ' + formatError(value); | |
} | |
if (keys.length === 0 && (!array || value.length == 0)) { | |
return braces[0] + base + braces[1]; | |
} | |
if (recurseTimes < 0) { | |
if (isRegExp(value)) { | |
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); | |
} else { | |
return ctx.stylize('[Object]', 'special'); | |
} | |
} | |
ctx.seen.push(value); | |
var output; | |
if (array) { | |
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); | |
} else { | |
output = keys.map(function(key) { | |
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); | |
}); | |
} | |
ctx.seen.pop(); | |
return reduceToSingleString(output, base, braces); | |
} | |
function formatPrimitive(ctx, value) { | |
if (isUndefined(value)) | |
return ctx.stylize('undefined', 'undefined'); | |
if (isString(value)) { | |
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') | |
.replace(/'/g, "\\'") | |
.replace(/\\"/g, '"') + '\''; | |
return ctx.stylize(simple, 'string'); | |
} | |
if (isNumber(value)) | |
return ctx.stylize('' + value, 'number'); | |
if (isBoolean(value)) | |
return ctx.stylize('' + value, 'boolean'); | |
// For some reason typeof null is "object", so special case here. | |
if (isNull(value)) | |
return ctx.stylize('null', 'null'); | |
} | |
function formatError(value) { | |
return '[' + Error.prototype.toString.call(value) + ']'; | |
} | |
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { | |
var output = []; | |
for (var i = 0, l = value.length; i < l; ++i) { | |
if (hasOwnProperty(value, String(i))) { | |
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, | |
String(i), true)); | |
} else { | |
output.push(''); | |
} | |
} | |
keys.forEach(function(key) { | |
if (!key.match(/^\d+$/)) { | |
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, | |
key, true)); | |
} | |
}); | |
return output; | |
} | |
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { | |
var name, str, desc; | |
desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; | |
if (desc.get) { | |
if (desc.set) { | |
str = ctx.stylize('[Getter/Setter]', 'special'); | |
} else { | |
str = ctx.stylize('[Getter]', 'special'); | |
} | |
} else { | |
if (desc.set) { | |
str = ctx.stylize('[Setter]', 'special'); | |
} | |
} | |
if (!hasOwnProperty(visibleKeys, key)) { | |
name = '[' + key + ']'; | |
} | |
if (!str) { | |
if (ctx.seen.indexOf(desc.value) < 0) { | |
if (isNull(recurseTimes)) { | |
str = formatValue(ctx, desc.value, null); | |
} else { | |
str = formatValue(ctx, desc.value, recurseTimes - 1); | |
} | |
if (str.indexOf('\n') > -1) { | |
if (array) { | |
str = str.split('\n').map(function(line) { | |
return ' ' + line; | |
}).join('\n').substr(2); | |
} else { | |
str = '\n' + str.split('\n').map(function(line) { | |
return ' ' + line; | |
}).join('\n'); | |
} | |
} | |
} else { | |
str = ctx.stylize('[Circular]', 'special'); | |
} | |
} | |
if (isUndefined(name)) { | |
if (array && key.match(/^\d+$/)) { | |
return str; | |
} | |
name = JSON.stringify('' + key); | |
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { | |
name = name.substr(1, name.length - 2); | |
name = ctx.stylize(name, 'name'); | |
} else { | |
name = name.replace(/'/g, "\\'") | |
.replace(/\\"/g, '"') | |
.replace(/(^"|"$)/g, "'"); | |
name = ctx.stylize(name, 'string'); | |
} | |
} | |
return name + ': ' + str; | |
} | |
function reduceToSingleString(output, base, braces) { | |
var numLinesEst = 0; | |
var length = output.reduce(function(prev, cur) { | |
numLinesEst++; | |
if (cur.indexOf('\n') >= 0) numLinesEst++; | |
return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; | |
}, 0); | |
if (length > 60) { | |
return braces[0] + | |
(base === '' ? '' : base + '\n ') + | |
' ' + | |
output.join(',\n ') + | |
' ' + | |
braces[1]; | |
} | |
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; | |
} | |
// NOTE: These type checking functions intentionally don't use `instanceof` | |
// because it is fragile and can be easily faked with `Object.create()`. | |
function isArray(ar) { | |
return Array.isArray(ar); | |
} | |
exports.isArray = isArray; | |
function isBoolean(arg) { | |
return typeof arg === 'boolean'; | |
} | |
exports.isBoolean = isBoolean; | |
function isNull(arg) { | |
return arg === null; | |
} | |
exports.isNull = isNull; | |
function isNullOrUndefined(arg) { | |
return arg == null; | |
} | |
exports.isNullOrUndefined = isNullOrUndefined; | |
function isNumber(arg) { | |
return typeof arg === 'number'; | |
} | |
exports.isNumber = isNumber; | |
function isString(arg) { | |
return typeof arg === 'string'; | |
} | |
exports.isString = isString; | |
function isSymbol(arg) { | |
return typeof arg === 'symbol'; | |
} | |
exports.isSymbol = isSymbol; | |
function isUndefined(arg) { | |
return arg === void 0; | |
} | |
exports.isUndefined = isUndefined; | |
function isRegExp(re) { | |
return isObject(re) && objectToString(re) === '[object RegExp]'; | |
} | |
exports.isRegExp = isRegExp; | |
function isObject(arg) { | |
return typeof arg === 'object' && arg !== null; | |
} | |
exports.isObject = isObject; | |
function isDate(d) { | |
return isObject(d) && objectToString(d) === '[object Date]'; | |
} | |
exports.isDate = isDate; | |
function isError(e) { | |
return isObject(e) && | |
(objectToString(e) === '[object Error]' || e instanceof Error); | |
} | |
exports.isError = isError; | |
function isFunction(arg) { | |
return typeof arg === 'function'; | |
} | |
exports.isFunction = isFunction; | |
function isPrimitive(arg) { | |
return arg === null || | |
typeof arg === 'boolean' || | |
typeof arg === 'number' || | |
typeof arg === 'string' || | |
typeof arg === 'symbol' || // ES6 symbol | |
typeof arg === 'undefined'; | |
} | |
exports.isPrimitive = isPrimitive; | |
exports.isBuffer = require('./support/isBuffer'); | |
function objectToString(o) { | |
return Object.prototype.toString.call(o); | |
} | |
function pad(n) { | |
return n < 10 ? '0' + n.toString(10) : n.toString(10); | |
} | |
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', | |
'Oct', 'Nov', 'Dec']; | |
// 26 Feb 16:19:34 | |
function timestamp() { | |
var d = new Date(); | |
var time = [pad(d.getHours()), | |
pad(d.getMinutes()), | |
pad(d.getSeconds())].join(':'); | |
return [d.getDate(), months[d.getMonth()], time].join(' '); | |
} | |
// log is just a thin wrapper to console.log that prepends a timestamp | |
exports.log = function() { | |
console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); | |
}; | |
/** | |
* Inherit the prototype methods from one constructor into another. | |
* | |
* The Function.prototype.inherits from lang.js rewritten as a standalone | |
* function (not on Function.prototype). NOTE: If this file is to be loaded | |
* during bootstrapping this function needs to be rewritten using some native | |
* functions as prototype setup using normal JavaScript does not work as | |
* expected during bootstrapping (see mirror.js in r114903). | |
* | |
* @param {function} ctor Constructor function which needs to inherit the | |
* prototype. | |
* @param {function} superCtor Constructor function to inherit prototype from. | |
*/ | |
exports.inherits = require('inherits'); | |
exports._extend = function(origin, add) { | |
// Don't do anything if add isn't an object | |
if (!add || !isObject(add)) return origin; | |
var keys = Object.keys(add); | |
var i = keys.length; | |
while (i--) { | |
origin[keys[i]] = add[keys[i]]; | |
} | |
return origin; | |
}; | |
function hasOwnProperty(obj, prop) { | |
return Object.prototype.hasOwnProperty.call(obj, prop); | |
} | |
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./support/isBuffer":5,"_process":3,"inherits":4}],7:[function(require,module,exports){ | |
module.exports = after | |
function after(count, callback, err_cb) { | |
var bail = false | |
err_cb = err_cb || noop | |
proxy.count = count | |
return (count === 0) ? callback() : proxy | |
function proxy(err, result) { | |
if (proxy.count <= 0) { | |
throw new Error('after called too many times') | |
} | |
--proxy.count | |
// after first error, rest are passed to err_cb | |
if (err) { | |
bail = true | |
callback(err) | |
// future error callbacks will go to error handler | |
callback = err_cb | |
} else if (proxy.count === 0 && !bail) { | |
callback(null, result) | |
} | |
} | |
} | |
function noop() {} | |
},{}],8:[function(require,module,exports){ | |
/** | |
* An abstraction for slicing an arraybuffer even when | |
* ArrayBuffer.prototype.slice is not supported | |
* | |
* @api public | |
*/ | |
module.exports = function(arraybuffer, start, end) { | |
var bytes = arraybuffer.byteLength; | |
start = start || 0; | |
end = end || bytes; | |
if (arraybuffer.slice) { return arraybuffer.slice(start, end); } | |
if (start < 0) { start += bytes; } | |
if (end < 0) { end += bytes; } | |
if (end > bytes) { end = bytes; } | |
if (start >= bytes || start >= end || bytes === 0) { | |
return new ArrayBuffer(0); | |
} | |
var abv = new Uint8Array(arraybuffer); | |
var result = new Uint8Array(end - start); | |
for (var i = start, ii = 0; i < end; i++, ii++) { | |
result[ii] = abv[i]; | |
} | |
return result.buffer; | |
}; | |
},{}],9:[function(require,module,exports){ | |
/** | |
* Expose `Backoff`. | |
*/ | |
module.exports = Backoff; | |
/** | |
* Initialize backoff timer with `opts`. | |
* | |
* - `min` initial timeout in milliseconds [100] | |
* - `max` max timeout [10000] | |
* - `jitter` [0] | |
* - `factor` [2] | |
* | |
* @param {Object} opts | |
* @api public | |
*/ | |
function Backoff(opts) { | |
opts = opts || {}; | |
this.ms = opts.min || 100; | |
this.max = opts.max || 10000; | |
this.factor = opts.factor || 2; | |
this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0; | |
this.attempts = 0; | |
} | |
/** | |
* Return the backoff duration. | |
* | |
* @return {Number} | |
* @api public | |
*/ | |
Backoff.prototype.duration = function(){ | |
var ms = this.ms * Math.pow(this.factor, this.attempts++); | |
if (this.jitter) { | |
var rand = Math.random(); | |
var deviation = Math.floor(rand * this.jitter * ms); | |
ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation; | |
} | |
return Math.min(ms, this.max) | 0; | |
}; | |
/** | |
* Reset the number of attempts. | |
* | |
* @api public | |
*/ | |
Backoff.prototype.reset = function(){ | |
this.attempts = 0; | |
}; | |
/** | |
* Set the minimum duration | |
* | |
* @api public | |
*/ | |
Backoff.prototype.setMin = function(min){ | |
this.ms = min; | |
}; | |
/** | |
* Set the maximum duration | |
* | |
* @api public | |
*/ | |
Backoff.prototype.setMax = function(max){ | |
this.max = max; | |
}; | |
/** | |
* Set the jitter | |
* | |
* @api public | |
*/ | |
Backoff.prototype.setJitter = function(jitter){ | |
this.jitter = jitter; | |
}; | |
},{}],10:[function(require,module,exports){ | |
/* | |
* base64-arraybuffer | |
* https://github.com/niklasvh/base64-arraybuffer | |
* | |
* Copyright (c) 2012 Niklas von Hertzen | |
* Licensed under the MIT license. | |
*/ | |
(function(){ | |
"use strict"; | |
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
// Use a lookup table to find the index. | |
var lookup = new Uint8Array(256); | |
for (var i = 0; i < chars.length; i++) { | |
lookup[chars.charCodeAt(i)] = i; | |
} | |
exports.encode = function(arraybuffer) { | |
var bytes = new Uint8Array(arraybuffer), | |
i, len = bytes.length, base64 = ""; | |
for (i = 0; i < len; i+=3) { | |
base64 += chars[bytes[i] >> 2]; | |
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; | |
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; | |
base64 += chars[bytes[i + 2] & 63]; | |
} | |
if ((len % 3) === 2) { | |
base64 = base64.substring(0, base64.length - 1) + "="; | |
} else if (len % 3 === 1) { | |
base64 = base64.substring(0, base64.length - 2) + "=="; | |
} | |
return base64; | |
}; | |
exports.decode = function(base64) { | |
var bufferLength = base64.length * 0.75, | |
len = base64.length, i, p = 0, | |
encoded1, encoded2, encoded3, encoded4; | |
if (base64[base64.length - 1] === "=") { | |
bufferLength--; | |
if (base64[base64.length - 2] === "=") { | |
bufferLength--; | |
} | |
} | |
var arraybuffer = new ArrayBuffer(bufferLength), | |
bytes = new Uint8Array(arraybuffer); | |
for (i = 0; i < len; i+=4) { | |
encoded1 = lookup[base64.charCodeAt(i)]; | |
encoded2 = lookup[base64.charCodeAt(i+1)]; | |
encoded3 = lookup[base64.charCodeAt(i+2)]; | |
encoded4 = lookup[base64.charCodeAt(i+3)]; | |
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); | |
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); | |
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); | |
} | |
return arraybuffer; | |
}; | |
})(); | |
},{}],11:[function(require,module,exports){ | |
var document = require('global/document') | |
var hyperx = require('hyperx') | |
var onload = require('on-load') | |
var SVGNS = 'http://www.w3.org/2000/svg' | |
var XLINKNS = 'http://www.w3.org/1999/xlink' | |
var BOOL_PROPS = { | |
autofocus: 1, | |
checked: 1, | |
defaultchecked: 1, | |
disabled: 1, | |
formnovalidate: 1, | |
indeterminate: 1, | |
readonly: 1, | |
required: 1, | |
selected: 1, | |
willvalidate: 1 | |
} | |
var COMMENT_TAG = '!--' | |
var SVG_TAGS = [ | |
'svg', | |
'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animate', 'animateColor', | |
'animateMotion', 'animateTransform', 'circle', 'clipPath', 'color-profile', | |
'cursor', 'defs', 'desc', 'ellipse', 'feBlend', 'feColorMatrix', | |
'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', | |
'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', | |
'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', | |
'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', | |
'feSpotLight', 'feTile', 'feTurbulence', 'filter', 'font', 'font-face', | |
'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', | |
'foreignObject', 'g', 'glyph', 'glyphRef', 'hkern', 'image', 'line', | |
'linearGradient', 'marker', 'mask', 'metadata', 'missing-glyph', 'mpath', | |
'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', | |
'set', 'stop', 'switch', 'symbol', 'text', 'textPath', 'title', 'tref', | |
'tspan', 'use', 'view', 'vkern' | |
] | |
function belCreateElement (tag, props, children) { | |
var el | |
// If an svg tag, it needs a namespace | |
if (SVG_TAGS.indexOf(tag) !== -1) { | |
props.namespace = SVGNS | |
} | |
// If we are using a namespace | |
var ns = false | |
if (props.namespace) { | |
ns = props.namespace | |
delete props.namespace | |
} | |
// Create the element | |
if (ns) { | |
el = document.createElementNS(ns, tag) | |
} else if (tag === COMMENT_TAG) { | |
return document.createComment(props.comment) | |
} else { | |
el = document.createElement(tag) | |
} | |
// If adding onload events | |
if (props.onload || props.onunload) { | |
var load = props.onload || function () {} | |
var unload = props.onunload || function () {} | |
onload(el, function belOnload () { | |
load(el) | |
}, function belOnunload () { | |
unload(el) | |
}, | |
// We have to use non-standard `caller` to find who invokes `belCreateElement` | |
belCreateElement.caller.caller.caller) | |
delete props.onload | |
delete props.onunload | |
} | |
// Create the properties | |
for (var p in props) { | |
if (props.hasOwnProperty(p)) { | |
var key = p.toLowerCase() | |
var val = props[p] | |
// Normalize className | |
if (key === 'classname') { | |
key = 'class' | |
p = 'class' | |
} | |
// The for attribute gets transformed to htmlFor, but we just set as for | |
if (p === 'htmlFor') { | |
p = 'for' | |
} | |
// If a property is boolean, set itself to the key | |
if (BOOL_PROPS[key]) { | |
if (val === 'true') val = key | |
else if (val === 'false') continue | |
} | |
// If a property prefers being set directly vs setAttribute | |
if (key.slice(0, 2) === 'on') { | |
el[p] = val | |
} else { | |
if (ns) { | |
if (p === 'xlink:href') { | |
el.setAttributeNS(XLINKNS, p, val) | |
} else if (/^xmlns($|:)/i.test(p)) { | |
// skip xmlns definitions | |
} else { | |
el.setAttributeNS(null, p, val) | |
} | |
} else { | |
el.setAttribute(p, val) | |
} | |
} | |
} | |
} | |
function appendChild (childs) { | |
if (!Array.isArray(childs)) return | |
for (var i = 0; i < childs.length; i++) { | |
var node = childs[i] | |
if (Array.isArray(node)) { | |
appendChild(node) | |
continue | |
} | |
if (typeof node === 'number' || | |
typeof node === 'boolean' || | |
typeof node === 'function' || | |
node instanceof Date || | |
node instanceof RegExp) { | |
node = node.toString() | |
} | |
if (typeof node === 'string') { | |
if (el.lastChild && el.lastChild.nodeName === '#text') { | |
el.lastChild.nodeValue += node | |
continue | |
} | |
node = document.createTextNode(node) | |
} | |
if (node && node.nodeType) { | |
el.appendChild(node) | |
} | |
} | |
} | |
appendChild(children) | |
return el | |
} | |
module.exports = hyperx(belCreateElement, {comments: true}) | |
module.exports.default = module.exports | |
module.exports.createElement = belCreateElement | |
},{"global/document":36,"hyperx":41,"on-load":50}],12:[function(require,module,exports){ | |
(function (global){ | |
/** | |
* Create a blob builder even when vendor prefixes exist | |
*/ | |
var BlobBuilder = global.BlobBuilder | |
|| global.WebKitBlobBuilder | |
|| global.MSBlobBuilder | |
|| global.MozBlobBuilder; | |
/** | |
* Check if Blob constructor is supported | |
*/ | |
var blobSupported = (function() { | |
try { | |
var a = new Blob(['hi']); | |
return a.size === 2; | |
} catch(e) { | |
return false; | |
} | |
})(); | |
/** | |
* Check if Blob constructor supports ArrayBufferViews | |
* Fails in Safari 6, so we need to map to ArrayBuffers there. | |
*/ | |
var blobSupportsArrayBufferView = blobSupported && (function() { | |
try { | |
var b = new Blob([new Uint8Array([1,2])]); | |
return b.size === 2; | |
} catch(e) { | |
return false; | |
} | |
})(); | |
/** | |
* Check if BlobBuilder is supported | |
*/ | |
var blobBuilderSupported = BlobBuilder | |
&& BlobBuilder.prototype.append | |
&& BlobBuilder.prototype.getBlob; | |
/** | |
* Helper function that maps ArrayBufferViews to ArrayBuffers | |
* Used by BlobBuilder constructor and old browsers that didn't | |
* support it in the Blob constructor. | |
*/ | |
function mapArrayBufferViews(ary) { | |
for (var i = 0; i < ary.length; i++) { | |
var chunk = ary[i]; | |
if (chunk.buffer instanceof ArrayBuffer) { | |
var buf = chunk.buffer; | |
// if this is a subarray, make a copy so we only | |
// include the subarray region from the underlying buffer | |
if (chunk.byteLength !== buf.byteLength) { | |
var copy = new Uint8Array(chunk.byteLength); | |
copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength)); | |
buf = copy.buffer; | |
} | |
ary[i] = buf; | |
} | |
} | |
} | |
function BlobBuilderConstructor(ary, options) { | |
options = options || {}; | |
var bb = new BlobBuilder(); | |
mapArrayBufferViews(ary); | |
for (var i = 0; i < ary.length; i++) { | |
bb.append(ary[i]); | |
} | |
return (options.type) ? bb.getBlob(options.type) : bb.getBlob(); | |
}; | |
function BlobConstructor(ary, options) { | |
mapArrayBufferViews(ary); | |
return new Blob(ary, options || {}); | |
}; | |
module.exports = (function() { | |
if (blobSupported) { | |
return blobSupportsArrayBufferView ? global.Blob : BlobConstructor; | |
} else if (blobBuilderSupported) { | |
return BlobBuilderConstructor; | |
} else { | |
return undefined; | |
} | |
})(); | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],13:[function(require,module,exports){ | |
/** | |
* Slice reference. | |
*/ | |
var slice = [].slice; | |
/** | |
* Bind `obj` to `fn`. | |
* | |
* @param {Object} obj | |
* @param {Function|String} fn or string | |
* @return {Function} | |
* @api public | |
*/ | |
module.exports = function(obj, fn){ | |
if ('string' == typeof fn) fn = obj[fn]; | |
if ('function' != typeof fn) throw new Error('bind() requires a function'); | |
var args = slice.call(arguments, 2); | |
return function(){ | |
return fn.apply(obj, args.concat(slice.call(arguments))); | |
} | |
}; | |
},{}],14:[function(require,module,exports){ | |
/** | |
* Expose `Emitter`. | |
*/ | |
if (typeof module !== 'undefined') { | |
module.exports = Emitter; | |
} | |
/** | |
* Initialize a new `Emitter`. | |
* | |
* @api public | |
*/ | |
function Emitter(obj) { | |
if (obj) return mixin(obj); | |
}; | |
/** | |
* Mixin the emitter properties. | |
* | |
* @param {Object} obj | |
* @return {Object} | |
* @api private | |
*/ | |
function mixin(obj) { | |
for (var key in Emitter.prototype) { | |
obj[key] = Emitter.prototype[key]; | |
} | |
return obj; | |
} | |
/** | |
* Listen on the given `event` with `fn`. | |
* | |
* @param {String} event | |
* @param {Function} fn | |
* @return {Emitter} | |
* @api public | |
*/ | |
Emitter.prototype.on = | |
Emitter.prototype.addEventListener = function(event, fn){ | |
this._callbacks = this._callbacks || {}; | |
(this._callbacks['$' + event] = this._callbacks['$' + event] || []) | |
.push(fn); | |
return this; | |
}; | |
/** | |
* Adds an `event` listener that will be invoked a single | |
* time then automatically removed. | |
* | |
* @param {String} event | |
* @param {Function} fn | |
* @return {Emitter} | |
* @api public | |
*/ | |
Emitter.prototype.once = function(event, fn){ | |
function on() { | |
this.off(event, on); | |
fn.apply(this, arguments); | |
} | |
on.fn = fn; | |
this.on(event, on); | |
return this; | |
}; | |
/** | |
* Remove the given callback for `event` or all | |
* registered callbacks. | |
* | |
* @param {String} event | |
* @param {Function} fn | |
* @return {Emitter} | |
* @api public | |
*/ | |
Emitter.prototype.off = | |
Emitter.prototype.removeListener = | |
Emitter.prototype.removeAllListeners = | |
Emitter.prototype.removeEventListener = function(event, fn){ | |
this._callbacks = this._callbacks || {}; | |
// all | |
if (0 == arguments.length) { | |
this._callbacks = {}; | |
return this; | |
} | |
// specific event | |
var callbacks = this._callbacks['$' + event]; | |
if (!callbacks) return this; | |
// remove all handlers | |
if (1 == arguments.length) { | |
delete this._callbacks['$' + event]; | |
return this; | |
} | |
// remove specific handler | |
var cb; | |
for (var i = 0; i < callbacks.length; i++) { | |
cb = callbacks[i]; | |
if (cb === fn || cb.fn === fn) { | |
callbacks.splice(i, 1); | |
break; | |
} | |
} | |
return this; | |
}; | |
/** | |
* Emit `event` with the given args. | |
* | |
* @param {String} event | |
* @param {Mixed} ... | |
* @return {Emitter} | |
*/ | |
Emitter.prototype.emit = function(event){ | |
this._callbacks = this._callbacks || {}; | |
var args = [].slice.call(arguments, 1) | |
, callbacks = this._callbacks['$' + event]; | |
if (callbacks) { | |
callbacks = callbacks.slice(0); | |
for (var i = 0, len = callbacks.length; i < len; ++i) { | |
callbacks[i].apply(this, args); | |
} | |
} | |
return this; | |
}; | |
/** | |
* Return array of callbacks for `event`. | |
* | |
* @param {String} event | |
* @return {Array} | |
* @api public | |
*/ | |
Emitter.prototype.listeners = function(event){ | |
this._callbacks = this._callbacks || {}; | |
return this._callbacks['$' + event] || []; | |
}; | |
/** | |
* Check if this emitter has `event` handlers. | |
* | |
* @param {String} event | |
* @return {Boolean} | |
* @api public | |
*/ | |
Emitter.prototype.hasListeners = function(event){ | |
return !! this.listeners(event).length; | |
}; | |
},{}],15:[function(require,module,exports){ | |
module.exports = function(a, b){ | |
var fn = function(){}; | |
fn.prototype = b.prototype; | |
a.prototype = new fn; | |
a.prototype.constructor = a; | |
}; | |
},{}],16:[function(require,module,exports){ | |
/** | |
* cuid.js | |
* Collision-resistant UID generator for browsers and node. | |
* Sequential for fast db lookups and recency sorting. | |
* Safe for element IDs and server-side lookups. | |
* | |
* Extracted from CLCTR | |
* | |
* Copyright (c) Eric Elliott 2012 | |
* MIT License | |
*/ | |
/*global window, navigator, document, require, process, module */ | |
(function (app) { | |
'use strict'; | |
var namespace = 'cuid', | |
c = 0, | |
blockSize = 4, | |
base = 36, | |
discreteValues = Math.pow(base, blockSize), | |
pad = function pad(num, size) { | |
var s = "000000000" + num; | |
return s.substr(s.length-size); | |
}, | |
randomBlock = function randomBlock() { | |
return pad((Math.random() * | |
discreteValues << 0) | |
.toString(base), blockSize); | |
}, | |
safeCounter = function () { | |
c = (c < discreteValues) ? c : 0; | |
c++; // this is not subliminal | |
return c - 1; | |
}, | |
api = function cuid() { | |
// Starting with a lowercase letter makes | |
// it HTML element ID friendly. | |
var letter = 'c', // hard-coded allows for sequential access | |
// timestamp | |
// warning: this exposes the exact date and time | |
// that the uid was created. | |
timestamp = (new Date().getTime()).toString(base), | |
// Prevent same-machine collisions. | |
counter, | |
// A few chars to generate distinct ids for different | |
// clients (so different computers are far less | |
// likely to generate the same id) | |
fingerprint = api.fingerprint(), | |
// Grab some more chars from Math.random() | |
random = randomBlock() + randomBlock(); | |
counter = pad(safeCounter().toString(base), blockSize); | |
return (letter + timestamp + counter + fingerprint + random); | |
}; | |
api.slug = function slug() { | |
var date = new Date().getTime().toString(36), | |
counter, | |
print = api.fingerprint().slice(0,1) + | |
api.fingerprint().slice(-1), | |
random = randomBlock().slice(-2); | |
counter = safeCounter().toString(36).slice(-4); | |
return date.slice(-2) + | |
counter + print + random; | |
}; | |
api.globalCount = function globalCount() { | |
// We want to cache the results of this | |
var cache = (function calc() { | |
var i, | |
count = 0; | |
for (i in window) { | |
count++; | |
} | |
return count; | |
}()); | |
api.globalCount = function () { return cache; }; | |
return cache; | |
}; | |
api.fingerprint = function browserPrint() { | |
return pad((navigator.mimeTypes.length + | |
navigator.userAgent.length).toString(36) + | |
api.globalCount().toString(36), 4); | |
}; | |
// don't change anything from here down. | |
if (app.register) { | |
app.register(namespace, api); | |
} else if (typeof module !== 'undefined') { | |
module.exports = api; | |
} else { | |
app[namespace] = api; | |
} | |
}(this.applitude || this)); | |
},{}],17:[function(require,module,exports){ | |
(function (process){ | |
/** | |
* This is the web browser implementation of `debug()`. | |
* | |
* Expose `debug()` as the module. | |
*/ | |
exports = module.exports = require('./debug'); | |
exports.log = log; | |
exports.formatArgs = formatArgs; | |
exports.save = save; | |
exports.load = load; | |
exports.useColors = useColors; | |
exports.storage = 'undefined' != typeof chrome | |
&& 'undefined' != typeof chrome.storage | |
? chrome.storage.local | |
: localstorage(); | |
/** | |
* Colors. | |
*/ | |
exports.colors = [ | |
'lightseagreen', | |
'forestgreen', | |
'goldenrod', | |
'dodgerblue', | |
'darkorchid', | |
'crimson' | |
]; | |
/** | |
* Currently only WebKit-based Web Inspectors, Firefox >= v31, | |
* and the Firebug extension (any Firefox version) are known | |
* to support "%c" CSS customizations. | |
* | |
* TODO: add a `localStorage` variable to explicitly enable/disable colors | |
*/ | |
function useColors() { | |
// NB: In an Electron preload script, document will be defined but not fully | |
// initialized. Since we know we're in Chrome, we'll just detect this case | |
// explicitly | |
if (typeof window !== 'undefined' && window && typeof window.process !== 'undefined' && window.process.type === 'renderer') { | |
return true; | |
} | |
// is webkit? http://stackoverflow.com/a/16459606/376773 | |
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 | |
return (typeof document !== 'undefined' && document && 'WebkitAppearance' in document.documentElement.style) || | |
// is firebug? http://stackoverflow.com/a/398120/376773 | |
(typeof window !== 'undefined' && window && window.console && (console.firebug || (console.exception && console.table))) || | |
// is firefox >= v31? | |
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages | |
(typeof navigator !== 'undefined' && navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || | |
// double check webkit in userAgent just in case we are in a worker | |
(typeof navigator !== 'undefined' && navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); | |
} | |
/** | |
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. | |
*/ | |
exports.formatters.j = function(v) { | |
try { | |
return JSON.stringify(v); | |
} catch (err) { | |
return '[UnexpectedJSONParseError]: ' + err.message; | |
} | |
}; | |
/** | |
* Colorize log arguments if enabled. | |
* | |
* @api public | |
*/ | |
function formatArgs(args) { | |
var useColors = this.useColors; | |
args[0] = (useColors ? '%c' : '') | |
+ this.namespace | |
+ (useColors ? ' %c' : ' ') | |
+ args[0] | |
+ (useColors ? '%c ' : ' ') | |
+ '+' + exports.humanize(this.diff); | |
if (!useColors) return; | |
var c = 'color: ' + this.color; | |
args.splice(1, 0, c, 'color: inherit') | |
// the final "%c" is somewhat tricky, because there could be other | |
// arguments passed either before or after the %c, so we need to | |
// figure out the correct index to insert the CSS into | |
var index = 0; | |
var lastC = 0; | |
args[0].replace(/%[a-zA-Z%]/g, function(match) { | |
if ('%%' === match) return; | |
index++; | |
if ('%c' === match) { | |
// we only are interested in the *last* %c | |
// (the user may have provided their own) | |
lastC = index; | |
} | |
}); | |
args.splice(lastC, 0, c); | |
} | |
/** | |
* Invokes `console.log()` when available. | |
* No-op when `console.log` is not a "function". | |
* | |
* @api public | |
*/ | |
function log() { | |
// this hackery is required for IE8/9, where | |
// the `console.log` function doesn't have 'apply' | |
return 'object' === typeof console | |
&& console.log | |
&& Function.prototype.apply.call(console.log, console, arguments); | |
} | |
/** | |
* Save `namespaces`. | |
* | |
* @param {String} namespaces | |
* @api private | |
*/ | |
function save(namespaces) { | |
try { | |
if (null == namespaces) { | |
exports.storage.removeItem('debug'); | |
} else { | |
exports.storage.debug = namespaces; | |
} | |
} catch(e) {} | |
} | |
/** | |
* Load `namespaces`. | |
* | |
* @return {String} returns the previously persisted debug modes | |
* @api private | |
*/ | |
function load() { | |
var r; | |
try { | |
r = exports.storage.debug; | |
} catch(e) {} | |
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG | |
if (!r && typeof process !== 'undefined' && 'env' in process) { | |
r = process.env.DEBUG; | |
} | |
return r; | |
} | |
/** | |
* Enable namespaces listed in `localStorage.debug` initially. | |
*/ | |
exports.enable(load()); | |
/** | |
* Localstorage attempts to return the localstorage. | |
* | |
* This is necessary because safari throws | |
* when a user disables cookies/localstorage | |
* and you attempt to access it. | |
* | |
* @return {LocalStorage} | |
* @api private | |
*/ | |
function localstorage() { | |
try { | |
return window.localStorage; | |
} catch (e) {} | |
} | |
}).call(this,require('_process')) | |
},{"./debug":18,"_process":3}],18:[function(require,module,exports){ | |
/** | |
* This is the common logic for both the Node.js and web browser | |
* implementations of `debug()`. | |
* | |
* Expose `debug()` as the module. | |
*/ | |
exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; | |
exports.coerce = coerce; | |
exports.disable = disable; | |
exports.enable = enable; | |
exports.enabled = enabled; | |
exports.humanize = require('ms'); | |
/** | |
* The currently active debug mode names, and names to skip. | |
*/ | |
exports.names = []; | |
exports.skips = []; | |
/** | |
* Map of special "%n" handling functions, for the debug "format" argument. | |
* | |
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". | |
*/ | |
exports.formatters = {}; | |
/** | |
* Previous log timestamp. | |
*/ | |
var prevTime; | |
/** | |
* Select a color. | |
* @param {String} namespace | |
* @return {Number} | |
* @api private | |
*/ | |
function selectColor(namespace) { | |
var hash = 0, i; | |
for (i in namespace) { | |
hash = ((hash << 5) - hash) + namespace.charCodeAt(i); | |
hash |= 0; // Convert to 32bit integer | |
} | |
return exports.colors[Math.abs(hash) % exports.colors.length]; | |
} | |
/** | |
* Create a debugger with the given `namespace`. | |
* | |
* @param {String} namespace | |
* @return {Function} | |
* @api public | |
*/ | |
function createDebug(namespace) { | |
function debug() { | |
// disabled? | |
if (!debug.enabled) return; | |
var self = debug; | |
// set `diff` timestamp | |
var curr = +new Date(); | |
var ms = curr - (prevTime || curr); | |
self.diff = ms; | |
self.prev = prevTime; | |
self.curr = curr; | |
prevTime = curr; | |
// turn the `arguments` into a proper Array | |
var args = new Array(arguments.length); | |
for (var i = 0; i < args.length; i++) { | |
args[i] = arguments[i]; | |
} | |
args[0] = exports.coerce(args[0]); | |
if ('string' !== typeof args[0]) { | |
// anything else let's inspect with %O | |
args.unshift('%O'); | |
} | |
// apply any `formatters` transformations | |
var index = 0; | |
args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { | |
// if we encounter an escaped % then don't increase the array index | |
if (match === '%%') return match; | |
index++; | |
var formatter = exports.formatters[format]; | |
if ('function' === typeof formatter) { | |
var val = args[index]; | |
match = formatter.call(self, val); | |
// now we need to remove `args[index]` since it's inlined in the `format` | |
args.splice(index, 1); | |
index--; | |
} | |
return match; | |
}); | |
// apply env-specific formatting (colors, etc.) | |
exports.formatArgs.call(self, args); | |
var logFn = debug.log || exports.log || console.log.bind(console); | |
logFn.apply(self, args); | |
} | |
debug.namespace = namespace; | |
debug.enabled = exports.enabled(namespace); | |
debug.useColors = exports.useColors(); | |
debug.color = selectColor(namespace); | |
// env-specific initialization logic for debug instances | |
if ('function' === typeof exports.init) { | |
exports.init(debug); | |
} | |
return debug; | |
} | |
/** | |
* Enables a debug mode by namespaces. This can include modes | |
* separated by a colon and wildcards. | |
* | |
* @param {String} namespaces | |
* @api public | |
*/ | |
function enable(namespaces) { | |
exports.save(namespaces); | |
exports.names = []; | |
exports.skips = []; | |
var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); | |
var len = split.length; | |
for (var i = 0; i < len; i++) { | |
if (!split[i]) continue; // ignore empty strings | |
namespaces = split[i].replace(/\*/g, '.*?'); | |
if (namespaces[0] === '-') { | |
exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); | |
} else { | |
exports.names.push(new RegExp('^' + namespaces + '$')); | |
} | |
} | |
} | |
/** | |
* Disable debug output. | |
* | |
* @api public | |
*/ | |
function disable() { | |
exports.enable(''); | |
} | |
/** | |
* Returns true if the given mode name is enabled, false otherwise. | |
* | |
* @param {String} name | |
* @return {Boolean} | |
* @api public | |
*/ | |
function enabled(name) { | |
var i, len; | |
for (i = 0, len = exports.skips.length; i < len; i++) { | |
if (exports.skips[i].test(name)) { | |
return false; | |
} | |
} | |
for (i = 0, len = exports.names.length; i < len; i++) { | |
if (exports.names[i].test(name)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* Coerce `val`. | |
* | |
* @param {Mixed} val | |
* @return {Mixed} | |
* @api private | |
*/ | |
function coerce(val) { | |
if (val instanceof Error) return val.stack || val.message; | |
return val; | |
} | |
},{"ms":47}],19:[function(require,module,exports){ | |
module.exports = dragDrop | |
var flatten = require('flatten') | |
var parallel = require('run-parallel') | |
function dragDrop (elem, listeners) { | |
if (typeof elem === 'string') { | |
var selector = elem | |
elem = window.document.querySelector(elem) | |
if (!elem) { | |
throw new Error('"' + selector + '" does not match any HTML elements') | |
} | |
} | |
if (!elem) { | |
throw new Error('"' + elem + '" is not a valid HTML element') | |
} | |
if (typeof listeners === 'function') { | |
listeners = { onDrop: listeners } | |
} | |
var timeout | |
elem.addEventListener('dragenter', onDragEnter, false) | |
elem.addEventListener('dragover', onDragOver, false) | |
elem.addEventListener('dragleave', onDragLeave, false) | |
elem.addEventListener('drop', onDrop, false) | |
// Function to remove drag-drop listeners | |
return function remove () { | |
removeDragClass() | |
elem.removeEventListener('dragenter', onDragEnter, false) | |
elem.removeEventListener('dragover', onDragOver, false) | |
elem.removeEventListener('dragleave', onDragLeave, false) | |
elem.removeEventListener('drop', onDrop, false) | |
} | |
function onDragEnter (e) { | |
if (listeners.onDragEnter) { | |
listeners.onDragEnter(e) | |
} | |
// Prevent event | |
e.stopPropagation() | |
e.preventDefault() | |
return false | |
} | |
function onDragOver (e) { | |
e.stopPropagation() | |
e.preventDefault() | |
if (e.dataTransfer.items) { | |
// Only add "drag" class when `items` contains items that are able to be | |
// handled by the registered listeners (files vs. text) | |
var items = toArray(e.dataTransfer.items) | |
var fileItems = items.filter(function (item) { return item.kind === 'file' }) | |
var textItems = items.filter(function (item) { return item.kind === 'string' }) | |
if (fileItems.length === 0 && !listeners.onDropText) return | |
if (textItems.length === 0 && !listeners.onDrop) return | |
if (fileItems.length === 0 && textItems.length === 0) return | |
} | |
elem.classList.add('drag') | |
clearTimeout(timeout) | |
if (listeners.onDragOver) { | |
listeners.onDragOver(e) | |
} | |
e.dataTransfer.dropEffect = 'copy' | |
return false | |
} | |
function onDragLeave (e) { | |
e.stopPropagation() | |
e.preventDefault() | |
if (listeners.onDragLeave) { | |
listeners.onDragLeave(e) | |
} | |
clearTimeout(timeout) | |
timeout = setTimeout(removeDragClass, 50) | |
return false | |
} | |
function onDrop (e) { | |
e.stopPropagation() | |
e.preventDefault() | |
if (listeners.onDragLeave) { | |
listeners.onDragLeave(e) | |
} | |
clearTimeout(timeout) | |
removeDragClass() | |
var pos = { | |
x: e.clientX, | |
y: e.clientY | |
} | |
// text drop support | |
var text = e.dataTransfer.getData('text') | |
if (text && listeners.onDropText) { | |
listeners.onDropText(text, pos) | |
} | |
// file drop support | |
if (e.dataTransfer.items) { | |
// Handle directories in Chrome using the proprietary FileSystem API | |
var items = toArray(e.dataTransfer.items).filter(function (item) { | |
return item.kind === 'file' | |
}) | |
if (items.length === 0) return | |
parallel(items.map(function (item) { | |
return function (cb) { | |
processEntry(item.webkitGetAsEntry(), cb) | |
} | |
}), function (err, results) { | |
// This catches permission errors with file:// in Chrome. This should never | |
// throw in production code, so the user does not need to use try-catch. | |
if (err) throw err | |
if (listeners.onDrop) { | |
listeners.onDrop(flatten(results), pos) | |
} | |
}) | |
} else { | |
var files = toArray(e.dataTransfer.files) | |
if (files.length === 0) return | |
files.forEach(function (file) { | |
file.fullPath = '/' + file.name | |
}) | |
if (listeners.onDrop) { | |
listeners.onDrop(files, pos) | |
} | |
} | |
return false | |
} | |
function removeDragClass () { | |
elem.classList.remove('drag') | |
} | |
} | |
function processEntry (entry, cb) { | |
var entries = [] | |
if (entry.isFile) { | |
entry.file(function (file) { | |
file.fullPath = entry.fullPath // preserve pathing for consumer | |
cb(null, file) | |
}, function (err) { | |
cb(err) | |
}) | |
} else if (entry.isDirectory) { | |
var reader = entry.createReader() | |
readEntries() | |
} | |
function readEntries () { | |
reader.readEntries(function (entries_) { | |
if (entries_.length > 0) { | |
entries = entries.concat(toArray(entries_)) | |
readEntries() // continue reading entries until `readEntries` returns no more | |
} else { | |
doneEntries() | |
} | |
}) | |
} | |
function doneEntries () { | |
parallel(entries.map(function (entry) { | |
return function (cb) { | |
processEntry(entry, cb) | |
} | |
}), cb) | |
} | |
} | |
function toArray (list) { | |
return Array.prototype.slice.call(list || [], 0) | |
} | |
},{"flatten":34,"run-parallel":58}],20:[function(require,module,exports){ | |
module.exports = require('./lib/index'); | |
},{"./lib/index":21}],21:[function(require,module,exports){ | |
module.exports = require('./socket'); | |
/** | |
* Exports parser | |
* | |
* @api public | |
* | |
*/ | |
module.exports.parser = require('engine.io-parser'); | |
},{"./socket":22,"engine.io-parser":30}],22:[function(require,module,exports){ | |
(function (global){ | |
/** | |
* Module dependencies. | |
*/ | |
var transports = require('./transports/index'); | |
var Emitter = require('component-emitter'); | |
var debug = require('debug')('engine.io-client:socket'); | |
var index = require('indexof'); | |
var parser = require('engine.io-parser'); | |
var parseuri = require('parseuri'); | |
var parsejson = require('parsejson'); | |
var parseqs = require('parseqs'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = Socket; | |
/** | |
* Socket constructor. | |
* | |
* @param {String|Object} uri or options | |
* @param {Object} options | |
* @api public | |
*/ | |
function Socket (uri, opts) { | |
if (!(this instanceof Socket)) return new Socket(uri, opts); | |
opts = opts || {}; | |
if (uri && 'object' === typeof uri) { | |
opts = uri; | |
uri = null; | |
} | |
if (uri) { | |
uri = parseuri(uri); | |
opts.hostname = uri.host; | |
opts.secure = uri.protocol === 'https' || uri.protocol === 'wss'; | |
opts.port = uri.port; | |
if (uri.query) opts.query = uri.query; | |
} else if (opts.host) { | |
opts.hostname = parseuri(opts.host).host; | |
} | |
this.secure = null != opts.secure ? opts.secure | |
: (global.location && 'https:' === location.protocol); | |
if (opts.hostname && !opts.port) { | |
// if no port is specified manually, use the protocol default | |
opts.port = this.secure ? '443' : '80'; | |
} | |
this.agent = opts.agent || false; | |
this.hostname = opts.hostname || | |
(global.location ? location.hostname : 'localhost'); | |
this.port = opts.port || (global.location && location.port | |
? location.port | |
: (this.secure ? 443 : 80)); | |
this.query = opts.query || {}; | |
if ('string' === typeof this.query) this.query = parseqs.decode(this.query); | |
this.upgrade = false !== opts.upgrade; | |
this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; | |
this.forceJSONP = !!opts.forceJSONP; | |
this.jsonp = false !== opts.jsonp; | |
this.forceBase64 = !!opts.forceBase64; | |
this.enablesXDR = !!opts.enablesXDR; | |
this.timestampParam = opts.timestampParam || 't'; | |
this.timestampRequests = opts.timestampRequests; | |
this.transports = opts.transports || ['polling', 'websocket']; | |
this.transportOptions = opts.transportOptions || {}; | |
this.readyState = ''; | |
this.writeBuffer = []; | |
this.prevBufferLen = 0; | |
this.policyPort = opts.policyPort || 843; | |
this.rememberUpgrade = opts.rememberUpgrade || false; | |
this.binaryType = null; | |
this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; | |
this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || {}) : false; | |
if (true === this.perMessageDeflate) this.perMessageDeflate = {}; | |
if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) { | |
this.perMessageDeflate.threshold = 1024; | |
} | |
// SSL options for Node.js client | |
this.pfx = opts.pfx || null; | |
this.key = opts.key || null; | |
this.passphrase = opts.passphrase || null; | |
this.cert = opts.cert || null; | |
this.ca = opts.ca || null; | |
this.ciphers = opts.ciphers || null; | |
this.rejectUnauthorized = opts.rejectUnauthorized === undefined ? true : opts.rejectUnauthorized; | |
this.forceNode = !!opts.forceNode; | |
// other options for Node.js client | |
var freeGlobal = typeof global === 'object' && global; | |
if (freeGlobal.global === freeGlobal) { | |
if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) { | |
this.extraHeaders = opts.extraHeaders; | |
} | |
if (opts.localAddress) { | |
this.localAddress = opts.localAddress; | |
} | |
} | |
// set on handshake | |
this.id = null; | |
this.upgrades = null; | |
this.pingInterval = null; | |
this.pingTimeout = null; | |
// set on heartbeat | |
this.pingIntervalTimer = null; | |
this.pingTimeoutTimer = null; | |
this.open(); | |
} | |
Socket.priorWebsocketSuccess = false; | |
/** | |
* Mix in `Emitter`. | |
*/ | |
Emitter(Socket.prototype); | |
/** | |
* Protocol version. | |
* | |
* @api public | |
*/ | |
Socket.protocol = parser.protocol; // this is an int | |
/** | |
* Expose deps for legacy compatibility | |
* and standalone browser access. | |
*/ | |
Socket.Socket = Socket; | |
Socket.Transport = require('./transport'); | |
Socket.transports = require('./transports/index'); | |
Socket.parser = require('engine.io-parser'); | |
/** | |
* Creates transport of the given type. | |
* | |
* @param {String} transport name | |
* @return {Transport} | |
* @api private | |
*/ | |
Socket.prototype.createTransport = function (name) { | |
debug('creating transport "%s"', name); | |
var query = clone(this.query); | |
// append engine.io protocol identifier | |
query.EIO = parser.protocol; | |
// transport name | |
query.transport = name; | |
// per-transport options | |
var options = this.transportOptions[name] || {}; | |
// session id if we already have one | |
if (this.id) query.sid = this.id; | |
var transport = new transports[name]({ | |
query: query, | |
socket: this, | |
agent: options.agent || this.agent, | |
hostname: options.hostname || this.hostname, | |
port: options.port || this.port, | |
secure: options.secure || this.secure, | |
path: options.path || this.path, | |
forceJSONP: options.forceJSONP || this.forceJSONP, | |
jsonp: options.jsonp || this.jsonp, | |
forceBase64: options.forceBase64 || this.forceBase64, | |
enablesXDR: options.enablesXDR || this.enablesXDR, | |
timestampRequests: options.timestampRequests || this.timestampRequests, | |
timestampParam: options.timestampParam || this.timestampParam, | |
policyPort: options.policyPort || this.policyPort, | |
pfx: options.pfx || this.pfx, | |
key: options.key || this.key, | |
passphrase: options.passphrase || this.passphrase, | |
cert: options.cert || this.cert, | |
ca: options.ca || this.ca, | |
ciphers: options.ciphers || this.ciphers, | |
rejectUnauthorized: options.rejectUnauthorized || this.rejectUnauthorized, | |
perMessageDeflate: options.perMessageDeflate || this.perMessageDeflate, | |
extraHeaders: options.extraHeaders || this.extraHeaders, | |
forceNode: options.forceNode || this.forceNode, | |
localAddress: options.localAddress || this.localAddress, | |
requestTimeout: options.requestTimeout || this.requestTimeout, | |
protocols: options.protocols || void (0) | |
}); | |
return transport; | |
}; | |
function clone (obj) { | |
var o = {}; | |
for (var i in obj) { | |
if (obj.hasOwnProperty(i)) { | |
o[i] = obj[i]; | |
} | |
} | |
return o; | |
} | |
/** | |
* Initializes transport to use and starts probe. | |
* | |
* @api private | |
*/ | |
Socket.prototype.open = function () { | |
var transport; | |
if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') !== -1) { | |
transport = 'websocket'; | |
} else if (0 === this.transports.length) { | |
// Emit error on next tick so it can be listened to | |
var self = this; | |
setTimeout(function () { | |
self.emit('error', 'No transports available'); | |
}, 0); | |
return; | |
} else { | |
transport = this.transports[0]; | |
} | |
this.readyState = 'opening'; | |
// Retry with the next transport if the transport is disabled (jsonp: false) | |
try { | |
transport = this.createTransport(transport); | |
} catch (e) { | |
this.transports.shift(); | |
this.open(); | |
return; | |
} | |
transport.open(); | |
this.setTransport(transport); | |
}; | |
/** | |
* Sets the current transport. Disables the existing one (if any). | |
* | |
* @api private | |
*/ | |
Socket.prototype.setTransport = function (transport) { | |
debug('setting transport %s', transport.name); | |
var self = this; | |
if (this.transport) { | |
debug('clearing existing transport %s', this.transport.name); | |
this.transport.removeAllListeners(); | |
} | |
// set up transport | |
this.transport = transport; | |
// set up transport listeners | |
transport | |
.on('drain', function () { | |
self.onDrain(); | |
}) | |
.on('packet', function (packet) { | |
self.onPacket(packet); | |
}) | |
.on('error', function (e) { | |
self.onError(e); | |
}) | |
.on('close', function () { | |
self.onClose('transport close'); | |
}); | |
}; | |
/** | |
* Probes a transport. | |
* | |
* @param {String} transport name | |
* @api private | |
*/ | |
Socket.prototype.probe = function (name) { | |
debug('probing transport "%s"', name); | |
var transport = this.createTransport(name, { probe: 1 }); | |
var failed = false; | |
var self = this; | |
Socket.priorWebsocketSuccess = false; | |
function onTransportOpen () { | |
if (self.onlyBinaryUpgrades) { | |
var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; | |
failed = failed || upgradeLosesBinary; | |
} | |
if (failed) return; | |
debug('probe transport "%s" opened', name); | |
transport.send([{ type: 'ping', data: 'probe' }]); | |
transport.once('packet', function (msg) { | |
if (failed) return; | |
if ('pong' === msg.type && 'probe' === msg.data) { | |
debug('probe transport "%s" pong', name); | |
self.upgrading = true; | |
self.emit('upgrading', transport); | |
if (!transport) return; | |
Socket.priorWebsocketSuccess = 'websocket' === transport.name; | |
debug('pausing current transport "%s"', self.transport.name); | |
self.transport.pause(function () { | |
if (failed) return; | |
if ('closed' === self.readyState) return; | |
debug('changing transport and sending upgrade packet'); | |
cleanup(); | |
self.setTransport(transport); | |
transport.send([{ type: 'upgrade' }]); | |
self.emit('upgrade', transport); | |
transport = null; | |
self.upgrading = false; | |
self.flush(); | |
}); | |
} else { | |
debug('probe transport "%s" failed', name); | |
var err = new Error('probe error'); | |
err.transport = transport.name; | |
self.emit('upgradeError', err); | |
} | |
}); | |
} | |
function freezeTransport () { | |
if (failed) return; | |
// Any callback called by transport should be ignored since now | |
failed = true; | |
cleanup(); | |
transport.close(); | |
transport = null; | |
} | |
// Handle any error that happens while probing | |
function onerror (err) { | |
var error = new Error('probe error: ' + err); | |
error.transport = transport.name; | |
freezeTransport(); | |
debug('probe transport "%s" failed because of error: %s', name, err); | |
self.emit('upgradeError', error); | |
} | |
function onTransportClose () { | |
onerror('transport closed'); | |
} | |
// When the socket is closed while we're probing | |
function onclose () { | |
onerror('socket closed'); | |
} | |
// When the socket is upgraded while we're probing | |
function onupgrade (to) { | |
if (transport && to.name !== transport.name) { | |
debug('"%s" works - aborting "%s"', to.name, transport.name); | |
freezeTransport(); | |
} | |
} | |
// Remove all listeners on the transport and on self | |
function cleanup () { | |
transport.removeListener('open', onTransportOpen); | |
transport.removeListener('error', onerror); | |
transport.removeListener('close', onTransportClose); | |
self.removeListener('close', onclose); | |
self.removeListener('upgrading', onupgrade); | |
} | |
transport.once('open', onTransportOpen); | |
transport.once('error', onerror); | |
transport.once('close', onTransportClose); | |
this.once('close', onclose); | |
this.once('upgrading', onupgrade); | |
transport.open(); | |
}; | |
/** | |
* Called when connection is deemed open. | |
* | |
* @api public | |
*/ | |
Socket.prototype.onOpen = function () { | |
debug('socket open'); | |
this.readyState = 'open'; | |
Socket.priorWebsocketSuccess = 'websocket' === this.transport.name; | |
this.emit('open'); | |
this.flush(); | |
// we check for `readyState` in case an `open` | |
// listener already closed the socket | |
if ('open' === this.readyState && this.upgrade && this.transport.pause) { | |
debug('starting upgrade probes'); | |
for (var i = 0, l = this.upgrades.length; i < l; i++) { | |
this.probe(this.upgrades[i]); | |
} | |
} | |
}; | |
/** | |
* Handles a packet. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onPacket = function (packet) { | |
if ('opening' === this.readyState || 'open' === this.readyState || | |
'closing' === this.readyState) { | |
debug('socket receive: type "%s", data "%s"', packet.type, packet.data); | |
this.emit('packet', packet); | |
// Socket is live - any packet counts | |
this.emit('heartbeat'); | |
switch (packet.type) { | |
case 'open': | |
this.onHandshake(parsejson(packet.data)); | |
break; | |
case 'pong': | |
this.setPing(); | |
this.emit('pong'); | |
break; | |
case 'error': | |
var err = new Error('server error'); | |
err.code = packet.data; | |
this.onError(err); | |
break; | |
case 'message': | |
this.emit('data', packet.data); | |
this.emit('message', packet.data); | |
break; | |
} | |
} else { | |
debug('packet received with socket readyState "%s"', this.readyState); | |
} | |
}; | |
/** | |
* Called upon handshake completion. | |
* | |
* @param {Object} handshake obj | |
* @api private | |
*/ | |
Socket.prototype.onHandshake = function (data) { | |
this.emit('handshake', data); | |
this.id = data.sid; | |
this.transport.query.sid = data.sid; | |
this.upgrades = this.filterUpgrades(data.upgrades); | |
this.pingInterval = data.pingInterval; | |
this.pingTimeout = data.pingTimeout; | |
this.onOpen(); | |
// In case open handler closes socket | |
if ('closed' === this.readyState) return; | |
this.setPing(); | |
// Prolong liveness of socket on heartbeat | |
this.removeListener('heartbeat', this.onHeartbeat); | |
this.on('heartbeat', this.onHeartbeat); | |
}; | |
/** | |
* Resets ping timeout. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onHeartbeat = function (timeout) { | |
clearTimeout(this.pingTimeoutTimer); | |
var self = this; | |
self.pingTimeoutTimer = setTimeout(function () { | |
if ('closed' === self.readyState) return; | |
self.onClose('ping timeout'); | |
}, timeout || (self.pingInterval + self.pingTimeout)); | |
}; | |
/** | |
* Pings server every `this.pingInterval` and expects response | |
* within `this.pingTimeout` or closes connection. | |
* | |
* @api private | |
*/ | |
Socket.prototype.setPing = function () { | |
var self = this; | |
clearTimeout(self.pingIntervalTimer); | |
self.pingIntervalTimer = setTimeout(function () { | |
debug('writing ping packet - expecting pong within %sms', self.pingTimeout); | |
self.ping(); | |
self.onHeartbeat(self.pingTimeout); | |
}, self.pingInterval); | |
}; | |
/** | |
* Sends a ping packet. | |
* | |
* @api private | |
*/ | |
Socket.prototype.ping = function () { | |
var self = this; | |
this.sendPacket('ping', function () { | |
self.emit('ping'); | |
}); | |
}; | |
/** | |
* Called on `drain` event | |
* | |
* @api private | |
*/ | |
Socket.prototype.onDrain = function () { | |
this.writeBuffer.splice(0, this.prevBufferLen); | |
// setting prevBufferLen = 0 is very important | |
// for example, when upgrading, upgrade packet is sent over, | |
// and a nonzero prevBufferLen could cause problems on `drain` | |
this.prevBufferLen = 0; | |
if (0 === this.writeBuffer.length) { | |
this.emit('drain'); | |
} else { | |
this.flush(); | |
} | |
}; | |
/** | |
* Flush write buffers. | |
* | |
* @api private | |
*/ | |
Socket.prototype.flush = function () { | |
if ('closed' !== this.readyState && this.transport.writable && | |
!this.upgrading && this.writeBuffer.length) { | |
debug('flushing %d packets in socket', this.writeBuffer.length); | |
this.transport.send(this.writeBuffer); | |
// keep track of current length of writeBuffer | |
// splice writeBuffer and callbackBuffer on `drain` | |
this.prevBufferLen = this.writeBuffer.length; | |
this.emit('flush'); | |
} | |
}; | |
/** | |
* Sends a message. | |
* | |
* @param {String} message. | |
* @param {Function} callback function. | |
* @param {Object} options. | |
* @return {Socket} for chaining. | |
* @api public | |
*/ | |
Socket.prototype.write = | |
Socket.prototype.send = function (msg, options, fn) { | |
this.sendPacket('message', msg, options, fn); | |
return this; | |
}; | |
/** | |
* Sends a packet. | |
* | |
* @param {String} packet type. | |
* @param {String} data. | |
* @param {Object} options. | |
* @param {Function} callback function. | |
* @api private | |
*/ | |
Socket.prototype.sendPacket = function (type, data, options, fn) { | |
if ('function' === typeof data) { | |
fn = data; | |
data = undefined; | |
} | |
if ('function' === typeof options) { | |
fn = options; | |
options = null; | |
} | |
if ('closing' === this.readyState || 'closed' === this.readyState) { | |
return; | |
} | |
options = options || {}; | |
options.compress = false !== options.compress; | |
var packet = { | |
type: type, | |
data: data, | |
options: options | |
}; | |
this.emit('packetCreate', packet); | |
this.writeBuffer.push(packet); | |
if (fn) this.once('flush', fn); | |
this.flush(); | |
}; | |
/** | |
* Closes the connection. | |
* | |
* @api private | |
*/ | |
Socket.prototype.close = function () { | |
if ('opening' === this.readyState || 'open' === this.readyState) { | |
this.readyState = 'closing'; | |
var self = this; | |
if (this.writeBuffer.length) { | |
this.once('drain', function () { | |
if (this.upgrading) { | |
waitForUpgrade(); | |
} else { | |
close(); | |
} | |
}); | |
} else if (this.upgrading) { | |
waitForUpgrade(); | |
} else { | |
close(); | |
} | |
} | |
function close () { | |
self.onClose('forced close'); | |
debug('socket closing - telling transport to close'); | |
self.transport.close(); | |
} | |
function cleanupAndClose () { | |
self.removeListener('upgrade', cleanupAndClose); | |
self.removeListener('upgradeError', cleanupAndClose); | |
close(); | |
} | |
function waitForUpgrade () { | |
// wait for upgrade to finish since we can't send packets while pausing a transport | |
self.once('upgrade', cleanupAndClose); | |
self.once('upgradeError', cleanupAndClose); | |
} | |
return this; | |
}; | |
/** | |
* Called upon transport error | |
* | |
* @api private | |
*/ | |
Socket.prototype.onError = function (err) { | |
debug('socket error %j', err); | |
Socket.priorWebsocketSuccess = false; | |
this.emit('error', err); | |
this.onClose('transport error', err); | |
}; | |
/** | |
* Called upon transport close. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onClose = function (reason, desc) { | |
if ('opening' === this.readyState || 'open' === this.readyState || 'closing' === this.readyState) { | |
debug('socket close with reason: "%s"', reason); | |
var self = this; | |
// clear timers | |
clearTimeout(this.pingIntervalTimer); | |
clearTimeout(this.pingTimeoutTimer); | |
// stop event from firing again for transport | |
this.transport.removeAllListeners('close'); | |
// ensure transport won't stay open | |
this.transport.close(); | |
// ignore further transport communication | |
this.transport.removeAllListeners(); | |
// set ready state | |
this.readyState = 'closed'; | |
// clear session id | |
this.id = null; | |
// emit close event | |
this.emit('close', reason, desc); | |
// clean buffers after, so users can still | |
// grab the buffers on `close` event | |
self.writeBuffer = []; | |
self.prevBufferLen = 0; | |
} | |
}; | |
/** | |
* Filters upgrades, returning only those matching client transports. | |
* | |
* @param {Array} server upgrades | |
* @api private | |
* | |
*/ | |
Socket.prototype.filterUpgrades = function (upgrades) { | |
var filteredUpgrades = []; | |
for (var i = 0, j = upgrades.length; i < j; i++) { | |
if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]); | |
} | |
return filteredUpgrades; | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./transport":23,"./transports/index":24,"component-emitter":14,"debug":17,"engine.io-parser":30,"indexof":42,"parsejson":51,"parseqs":52,"parseuri":53}],23:[function(require,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var parser = require('engine.io-parser'); | |
var Emitter = require('component-emitter'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = Transport; | |
/** | |
* Transport abstract constructor. | |
* | |
* @param {Object} options. | |
* @api private | |
*/ | |
function Transport (opts) { | |
this.path = opts.path; | |
this.hostname = opts.hostname; | |
this.port = opts.port; | |
this.secure = opts.secure; | |
this.query = opts.query; | |
this.timestampParam = opts.timestampParam; | |
this.timestampRequests = opts.timestampRequests; | |
this.readyState = ''; | |
this.agent = opts.agent || false; | |
this.socket = opts.socket; | |
this.enablesXDR = opts.enablesXDR; | |
// SSL options for Node.js client | |
this.pfx = opts.pfx; | |
this.key = opts.key; | |
this.passphrase = opts.passphrase; | |
this.cert = opts.cert; | |
this.ca = opts.ca; | |
this.ciphers = opts.ciphers; | |
this.rejectUnauthorized = opts.rejectUnauthorized; | |
this.forceNode = opts.forceNode; | |
// other options for Node.js client | |
this.extraHeaders = opts.extraHeaders; | |
this.localAddress = opts.localAddress; | |
} | |
/** | |
* Mix in `Emitter`. | |
*/ | |
Emitter(Transport.prototype); | |
/** | |
* Emits an error. | |
* | |
* @param {String} str | |
* @return {Transport} for chaining | |
* @api public | |
*/ | |
Transport.prototype.onError = function (msg, desc) { | |
var err = new Error(msg); | |
err.type = 'TransportError'; | |
err.description = desc; | |
this.emit('error', err); | |
return this; | |
}; | |
/** | |
* Opens the transport. | |
* | |
* @api public | |
*/ | |
Transport.prototype.open = function () { | |
if ('closed' === this.readyState || '' === this.readyState) { | |
this.readyState = 'opening'; | |
this.doOpen(); | |
} | |
return this; | |
}; | |
/** | |
* Closes the transport. | |
* | |
* @api private | |
*/ | |
Transport.prototype.close = function () { | |
if ('opening' === this.readyState || 'open' === this.readyState) { | |
this.doClose(); | |
this.onClose(); | |
} | |
return this; | |
}; | |
/** | |
* Sends multiple packets. | |
* | |
* @param {Array} packets | |
* @api private | |
*/ | |
Transport.prototype.send = function (packets) { | |
if ('open' === this.readyState) { | |
this.write(packets); | |
} else { | |
throw new Error('Transport not open'); | |
} | |
}; | |
/** | |
* Called upon open | |
* | |
* @api private | |
*/ | |
Transport.prototype.onOpen = function () { | |
this.readyState = 'open'; | |
this.writable = true; | |
this.emit('open'); | |
}; | |
/** | |
* Called with data. | |
* | |
* @param {String} data | |
* @api private | |
*/ | |
Transport.prototype.onData = function (data) { | |
var packet = parser.decodePacket(data, this.socket.binaryType); | |
this.onPacket(packet); | |
}; | |
/** | |
* Called with a decoded packet. | |
*/ | |
Transport.prototype.onPacket = function (packet) { | |
this.emit('packet', packet); | |
}; | |
/** | |
* Called upon close. | |
* | |
* @api private | |
*/ | |
Transport.prototype.onClose = function () { | |
this.readyState = 'closed'; | |
this.emit('close'); | |
}; | |
},{"component-emitter":14,"engine.io-parser":30}],24:[function(require,module,exports){ | |
(function (global){ | |
/** | |
* Module dependencies | |
*/ | |
var XMLHttpRequest = require('xmlhttprequest-ssl'); | |
var XHR = require('./polling-xhr'); | |
var JSONP = require('./polling-jsonp'); | |
var websocket = require('./websocket'); | |
/** | |
* Export transports. | |
*/ | |
exports.polling = polling; | |
exports.websocket = websocket; | |
/** | |
* Polling transport polymorphic constructor. | |
* Decides on xhr vs jsonp based on feature detection. | |
* | |
* @api private | |
*/ | |
function polling (opts) { | |
var xhr; | |
var xd = false; | |
var xs = false; | |
var jsonp = false !== opts.jsonp; | |
if (global.location) { | |
var isSSL = 'https:' === location.protocol; | |
var port = location.port; | |
// some user agents have empty `location.port` | |
if (!port) { | |
port = isSSL ? 443 : 80; | |
} | |
xd = opts.hostname !== location.hostname || port !== opts.port; | |
xs = opts.secure !== isSSL; | |
} | |
opts.xdomain = xd; | |
opts.xscheme = xs; | |
xhr = new XMLHttpRequest(opts); | |
if ('open' in xhr && !opts.forceJSONP) { | |
return new XHR(opts); | |
} else { | |
if (!jsonp) throw new Error('JSONP disabled'); | |
return new JSONP(opts); | |
} | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./polling-jsonp":25,"./polling-xhr":26,"./websocket":28,"xmlhttprequest-ssl":29}],25:[function(require,module,exports){ | |
(function (global){ | |
/** | |
* Module requirements. | |
*/ | |
var Polling = require('./polling'); | |
var inherit = require('component-inherit'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = JSONPPolling; | |
/** | |
* Cached regular expressions. | |
*/ | |
var rNewline = /\n/g; | |
var rEscapedNewline = /\\n/g; | |
/** | |
* Global JSONP callbacks. | |
*/ | |
var callbacks; | |
/** | |
* Noop. | |
*/ | |
function empty () { } | |
/** | |
* JSONP Polling constructor. | |
* | |
* @param {Object} opts. | |
* @api public | |
*/ | |
function JSONPPolling (opts) { | |
Polling.call(this, opts); | |
this.query = this.query || {}; | |
// define global callbacks array if not present | |
// we do this here (lazily) to avoid unneeded global pollution | |
if (!callbacks) { | |
// we need to consider multiple engines in the same page | |
if (!global.___eio) global.___eio = []; | |
callbacks = global.___eio; | |
} | |
// callback identifier | |
this.index = callbacks.length; | |
// add callback to jsonp global | |
var self = this; | |
callbacks.push(function (msg) { | |
self.onData(msg); | |
}); | |
// append to query string | |
this.query.j = this.index; | |
// prevent spurious errors from being emitted when the window is unloaded | |
if (global.document && global.addEventListener) { | |
global.addEventListener('beforeunload', function () { | |
if (self.script) self.script.onerror = empty; | |
}, false); | |
} | |
} | |
/** | |
* Inherits from Polling. | |
*/ | |
inherit(JSONPPolling, Polling); | |
/* | |
* JSONP only supports binary as base64 encoded strings | |
*/ | |
JSONPPolling.prototype.supportsBinary = false; | |
/** | |
* Closes the socket. | |
* | |
* @api private | |
*/ | |
JSONPPolling.prototype.doClose = function () { | |
if (this.script) { | |
this.script.parentNode.removeChild(this.script); | |
this.script = null; | |
} | |
if (this.form) { | |
this.form.parentNode.removeChild(this.form); | |
this.form = null; | |
this.iframe = null; | |
} | |
Polling.prototype.doClose.call(this); | |
}; | |
/** | |
* Starts a poll cycle. | |
* | |
* @api private | |
*/ | |
JSONPPolling.prototype.doPoll = function () { | |
var self = this; | |
var script = document.createElement('script'); | |
if (this.script) { | |
this.script.parentNode.removeChild(this.script); | |
this.script = null; | |
} | |
script.async = true; | |
script.src = this.uri(); | |
script.onerror = function (e) { | |
self.onError('jsonp poll error', e); | |
}; | |
var insertAt = document.getElementsByTagName('script')[0]; | |
if (insertAt) { | |
insertAt.parentNode.insertBefore(script, insertAt); | |
} else { | |
(document.head || document.body).appendChild(script); | |
} | |
this.script = script; | |
var isUAgecko = 'undefined' !== typeof navigator && /gecko/i.test(navigator.userAgent); | |
if (isUAgecko) { | |
setTimeout(function () { | |
var iframe = document.createElement('iframe'); | |
document.body.appendChild(iframe); | |
document.body.removeChild(iframe); | |
}, 100); | |
} | |
}; | |
/** | |
* Writes with a hidden iframe. | |
* | |
* @param {String} data to send | |
* @param {Function} called upon flush. | |
* @api private | |
*/ | |
JSONPPolling.prototype.doWrite = function (data, fn) { | |
var self = this; | |
if (!this.form) { | |
var form = document.createElement('form'); | |
var area = document.createElement('textarea'); | |
var id = this.iframeId = 'eio_iframe_' + this.index; | |
var iframe; | |
form.className = 'socketio'; | |
form.style.position = 'absolute'; | |
form.style.top = '-1000px'; | |
form.style.left = '-1000px'; | |
form.target = id; | |
form.method = 'POST'; | |
form.setAttribute('accept-charset', 'utf-8'); | |
area.name = 'd'; | |
form.appendChild(area); | |
document.body.appendChild(form); | |
this.form = form; | |
this.area = area; | |
} | |
this.form.action = this.uri(); | |
function complete () { | |
initIframe(); | |
fn(); | |
} | |
function initIframe () { | |
if (self.iframe) { | |
try { | |
self.form.removeChild(self.iframe); | |
} catch (e) { | |
self.onError('jsonp polling iframe removal error', e); | |
} | |
} | |
try { | |
// ie6 dynamic iframes with target="" support (thanks Chris Lambacher) | |
var html = '<iframe src="javascript:0" name="' + self.iframeId + '">'; | |
iframe = document.createElement(html); | |
} catch (e) { | |
iframe = document.createElement('iframe'); | |
iframe.name = self.iframeId; | |
iframe.src = 'javascript:0'; | |
} | |
iframe.id = self.iframeId; | |
self.form.appendChild(iframe); | |
self.iframe = iframe; | |
} | |
initIframe(); | |
// escape \n to prevent it from being converted into \r\n by some UAs | |
// double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side | |
data = data.replace(rEscapedNewline, '\\\n'); | |
this.area.value = data.replace(rNewline, '\\n'); | |
try { | |
this.form.submit(); | |
} catch (e) {} | |
if (this.iframe.attachEvent) { | |
this.iframe.onreadystatechange = function () { | |
if (self.iframe.readyState === 'complete') { | |
complete(); | |
} | |
}; | |
} else { | |
this.iframe.onload = complete; | |
} | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./polling":27,"component-inherit":15}],26:[function(require,module,exports){ | |
(function (global){ | |
/** | |
* Module requirements. | |
*/ | |
var XMLHttpRequest = require('xmlhttprequest-ssl'); | |
var Polling = require('./polling'); | |
var Emitter = require('component-emitter'); | |
var inherit = require('component-inherit'); | |
var debug = require('debug')('engine.io-client:polling-xhr'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = XHR; | |
module.exports.Request = Request; | |
/** | |
* Empty function | |
*/ | |
function empty () {} | |
/** | |
* XHR Polling constructor. | |
* | |
* @param {Object} opts | |
* @api public | |
*/ | |
function XHR (opts) { | |
Polling.call(this, opts); | |
this.requestTimeout = opts.requestTimeout; | |
this.extraHeaders = opts.extraHeaders; | |
if (global.location) { | |
var isSSL = 'https:' === location.protocol; | |
var port = location.port; | |
// some user agents have empty `location.port` | |
if (!port) { | |
port = isSSL ? 443 : 80; | |
} | |
this.xd = opts.hostname !== global.location.hostname || | |
port !== opts.port; | |
this.xs = opts.secure !== isSSL; | |
} | |
} | |
/** | |
* Inherits from Polling. | |
*/ | |
inherit(XHR, Polling); | |
/** | |
* XHR supports binary | |
*/ | |
XHR.prototype.supportsBinary = true; | |
/** | |
* Creates a request. | |
* | |
* @param {String} method | |
* @api private | |
*/ | |
XHR.prototype.request = function (opts) { | |
opts = opts || {}; | |
opts.uri = this.uri(); | |
opts.xd = this.xd; | |
opts.xs = this.xs; | |
opts.agent = this.agent || false; | |
opts.supportsBinary = this.supportsBinary; | |
opts.enablesXDR = this.enablesXDR; | |
// SSL options for Node.js client | |
opts.pfx = this.pfx; | |
opts.key = this.key; | |
opts.passphrase = this.passphrase; | |
opts.cert = this.cert; | |
opts.ca = this.ca; | |
opts.ciphers = this.ciphers; | |
opts.rejectUnauthorized = this.rejectUnauthorized; | |
opts.requestTimeout = this.requestTimeout; | |
// other options for Node.js client | |
opts.extraHeaders = this.extraHeaders; | |
return new Request(opts); | |
}; | |
/** | |
* Sends data. | |
* | |
* @param {String} data to send. | |
* @param {Function} called upon flush. | |
* @api private | |
*/ | |
XHR.prototype.doWrite = function (data, fn) { | |
var isBinary = typeof data !== 'string' && data !== undefined; | |
var req = this.request({ method: 'POST', data: data, isBinary: isBinary }); | |
var self = this; | |
req.on('success', fn); | |
req.on('error', function (err) { | |
self.onError('xhr post error', err); | |
}); | |
this.sendXhr = req; | |
}; | |
/** | |
* Starts a poll cycle. | |
* | |
* @api private | |
*/ | |
XHR.prototype.doPoll = function () { | |
debug('xhr poll'); | |
var req = this.request(); | |
var self = this; | |
req.on('data', function (data) { | |
self.onData(data); | |
}); | |
req.on('error', function (err) { | |
self.onError('xhr poll error', err); | |
}); | |
this.pollXhr = req; | |
}; | |
/** | |
* Request constructor | |
* | |
* @param {Object} options | |
* @api public | |
*/ | |
function Request (opts) { | |
this.method = opts.method || 'GET'; | |
this.uri = opts.uri; | |
this.xd = !!opts.xd; | |
this.xs = !!opts.xs; | |
this.async = false !== opts.async; | |
this.data = undefined !== opts.data ? opts.data : null; | |
this.agent = opts.agent; | |
this.isBinary = opts.isBinary; | |
this.supportsBinary = opts.supportsBinary; | |
this.enablesXDR = opts.enablesXDR; | |
this.requestTimeout = opts.requestTimeout; | |
// SSL options for Node.js client | |
this.pfx = opts.pfx; | |
this.key = opts.key; | |
this.passphrase = opts.passphrase; | |
this.cert = opts.cert; | |
this.ca = opts.ca; | |
this.ciphers = opts.ciphers; | |
this.rejectUnauthorized = opts.rejectUnauthorized; | |
// other options for Node.js client | |
this.extraHeaders = opts.extraHeaders; | |
this.create(); | |
} | |
/** | |
* Mix in `Emitter`. | |
*/ | |
Emitter(Request.prototype); | |
/** | |
* Creates the XHR object and sends the request. | |
* | |
* @api private | |
*/ | |
Request.prototype.create = function () { | |
var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR }; | |
// SSL options for Node.js client | |
opts.pfx = this.pfx; | |
opts.key = this.key; | |
opts.passphrase = this.passphrase; | |
opts.cert = this.cert; | |
opts.ca = this.ca; | |
opts.ciphers = this.ciphers; | |
opts.rejectUnauthorized = this.rejectUnauthorized; | |
var xhr = this.xhr = new XMLHttpRequest(opts); | |
var self = this; | |
try { | |
debug('xhr open %s: %s', this.method, this.uri); | |
xhr.open(this.method, this.uri, this.async); | |
try { | |
if (this.extraHeaders) { | |
xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true); | |
for (var i in this.extraHeaders) { | |
if (this.extraHeaders.hasOwnProperty(i)) { | |
xhr.setRequestHeader(i, this.extraHeaders[i]); | |
} | |
} | |
} | |
} catch (e) {} | |
if ('POST' === this.method) { | |
try { | |
if (this.isBinary) { | |
xhr.setRequestHeader('Content-type', 'application/octet-stream'); | |
} else { | |
xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); | |
} | |
} catch (e) {} | |
} | |
try { | |
xhr.setRequestHeader('Accept', '*/*'); | |
} catch (e) {} | |
// ie6 check | |
if ('withCredentials' in xhr) { | |
xhr.withCredentials = true; | |
} | |
if (this.requestTimeout) { | |
xhr.timeout = this.requestTimeout; | |
} | |
if (this.hasXDR()) { | |
xhr.onload = function () { | |
self.onLoad(); | |
}; | |
xhr.onerror = function () { | |
self.onError(xhr.responseText); | |
}; | |
} else { | |
xhr.onreadystatechange = function () { | |
if (xhr.readyState === 2) { | |
var contentType; | |
try { | |
contentType = xhr.getResponseHeader('Content-Type'); | |
} catch (e) {} | |
if (contentType === 'application/octet-stream') { | |
xhr.responseType = 'arraybuffer'; | |
} | |
} | |
if (4 !== xhr.readyState) return; | |
if (200 === xhr.status || 1223 === xhr.status) { | |
self.onLoad(); | |
} else { | |
// make sure the `error` event handler that's user-set | |
// does not throw in the same tick and gets caught here | |
setTimeout(function () { | |
self.onError(xhr.status); | |
}, 0); | |
} | |
}; | |
} | |
debug('xhr data %s', this.data); | |
xhr.send(this.data); | |
} catch (e) { | |
// Need to defer since .create() is called directly fhrom the constructor | |
// and thus the 'error' event can only be only bound *after* this exception | |
// occurs. Therefore, also, we cannot throw here at all. | |
setTimeout(function () { | |
self.onError(e); | |
}, 0); | |
return; | |
} | |
if (global.document) { | |
this.index = Request.requestsCount++; | |
Request.requests[this.index] = this; | |
} | |
}; | |
/** | |
* Called upon successful response. | |
* | |
* @api private | |
*/ | |
Request.prototype.onSuccess = function () { | |
this.emit('success'); | |
this.cleanup(); | |
}; | |
/** | |
* Called if we have data. | |
* | |
* @api private | |
*/ | |
Request.prototype.onData = function (data) { | |
this.emit('data', data); | |
this.onSuccess(); | |
}; | |
/** | |
* Called upon error. | |
* | |
* @api private | |
*/ | |
Request.prototype.onError = function (err) { | |
this.emit('error', err); | |
this.cleanup(true); | |
}; | |
/** | |
* Cleans up house. | |
* | |
* @api private | |
*/ | |
Request.prototype.cleanup = function (fromError) { | |
if ('undefined' === typeof this.xhr || null === this.xhr) { | |
return; | |
} | |
// xmlhttprequest | |
if (this.hasXDR()) { | |
this.xhr.onload = this.xhr.onerror = empty; | |
} else { | |
this.xhr.onreadystatechange = empty; | |
} | |
if (fromError) { | |
try { | |
this.xhr.abort(); | |
} catch (e) {} | |
} | |
if (global.document) { | |
delete Request.requests[this.index]; | |
} | |
this.xhr = null; | |
}; | |
/** | |
* Called upon load. | |
* | |
* @api private | |
*/ | |
Request.prototype.onLoad = function () { | |
var data; | |
try { | |
var contentType; | |
try { | |
contentType = this.xhr.getResponseHeader('Content-Type'); | |
} catch (e) {} | |
if (contentType === 'application/octet-stream') { | |
data = this.xhr.response || this.xhr.responseText; | |
} else { | |
data = this.xhr.responseText; | |
} | |
} catch (e) { | |
this.onError(e); | |
} | |
if (null != data) { | |
this.onData(data); | |
} | |
}; | |
/** | |
* Check if it has XDomainRequest. | |
* | |
* @api private | |
*/ | |
Request.prototype.hasXDR = function () { | |
return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR; | |
}; | |
/** | |
* Aborts the request. | |
* | |
* @api public | |
*/ | |
Request.prototype.abort = function () { | |
this.cleanup(); | |
}; | |
/** | |
* Aborts pending requests when unloading the window. This is needed to prevent | |
* memory leaks (e.g. when using IE) and to ensure that no spurious error is | |
* emitted. | |
*/ | |
Request.requestsCount = 0; | |
Request.requests = {}; | |
if (global.document) { | |
if (global.attachEvent) { | |
global.attachEvent('onunload', unloadHandler); | |
} else if (global.addEventListener) { | |
global.addEventListener('beforeunload', unloadHandler, false); | |
} | |
} | |
function unloadHandler () { | |
for (var i in Request.requests) { | |
if (Request.requests.hasOwnProperty(i)) { | |
Request.requests[i].abort(); | |
} | |
} | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./polling":27,"component-emitter":14,"component-inherit":15,"debug":17,"xmlhttprequest-ssl":29}],27:[function(require,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var Transport = require('../transport'); | |
var parseqs = require('parseqs'); | |
var parser = require('engine.io-parser'); | |
var inherit = require('component-inherit'); | |
var yeast = require('yeast'); | |
var debug = require('debug')('engine.io-client:polling'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = Polling; | |
/** | |
* Is XHR2 supported? | |
*/ | |
var hasXHR2 = (function () { | |
var XMLHttpRequest = require('xmlhttprequest-ssl'); | |
var xhr = new XMLHttpRequest({ xdomain: false }); | |
return null != xhr.responseType; | |
})(); | |
/** | |
* Polling interface. | |
* | |
* @param {Object} opts | |
* @api private | |
*/ | |
function Polling (opts) { | |
var forceBase64 = (opts && opts.forceBase64); | |
if (!hasXHR2 || forceBase64) { | |
this.supportsBinary = false; | |
} | |
Transport.call(this, opts); | |
} | |
/** | |
* Inherits from Transport. | |
*/ | |
inherit(Polling, Transport); | |
/** | |
* Transport name. | |
*/ | |
Polling.prototype.name = 'polling'; | |
/** | |
* Opens the socket (triggers polling). We write a PING message to determine | |
* when the transport is open. | |
* | |
* @api private | |
*/ | |
Polling.prototype.doOpen = function () { | |
this.poll(); | |
}; | |
/** | |
* Pauses polling. | |
* | |
* @param {Function} callback upon buffers are flushed and transport is paused | |
* @api private | |
*/ | |
Polling.prototype.pause = function (onPause) { | |
var self = this; | |
this.readyState = 'pausing'; | |
function pause () { | |
debug('paused'); | |
self.readyState = 'paused'; | |
onPause(); | |
} | |
if (this.polling || !this.writable) { | |
var total = 0; | |
if (this.polling) { | |
debug('we are currently polling - waiting to pause'); | |
total++; | |
this.once('pollComplete', function () { | |
debug('pre-pause polling complete'); | |
--total || pause(); | |
}); | |
} | |
if (!this.writable) { | |
debug('we are currently writing - waiting to pause'); | |
total++; | |
this.once('drain', function () { | |
debug('pre-pause writing complete'); | |
--total || pause(); | |
}); | |
} | |
} else { | |
pause(); | |
} | |
}; | |
/** | |
* Starts polling cycle. | |
* | |
* @api public | |
*/ | |
Polling.prototype.poll = function () { | |
debug('polling'); | |
this.polling = true; | |
this.doPoll(); | |
this.emit('poll'); | |
}; | |
/** | |
* Overloads onData to detect payloads. | |
* | |
* @api private | |
*/ | |
Polling.prototype.onData = function (data) { | |
var self = this; | |
debug('polling got data %s', data); | |
var callback = function (packet, index, total) { | |
// if its the first message we consider the transport open | |
if ('opening' === self.readyState) { | |
self.onOpen(); | |
} | |
// if its a close packet, we close the ongoing requests | |
if ('close' === packet.type) { | |
self.onClose(); | |
return false; | |
} | |
// otherwise bypass onData and handle the message | |
self.onPacket(packet); | |
}; | |
// decode payload | |
parser.decodePayload(data, this.socket.binaryType, callback); | |
// if an event did not trigger closing | |
if ('closed' !== this.readyState) { | |
// if we got data we're not polling | |
this.polling = false; | |
this.emit('pollComplete'); | |
if ('open' === this.readyState) { | |
this.poll(); | |
} else { | |
debug('ignoring poll - transport state "%s"', this.readyState); | |
} | |
} | |
}; | |
/** | |
* For polling, send a close packet. | |
* | |
* @api private | |
*/ | |
Polling.prototype.doClose = function () { | |
var self = this; | |
function close () { | |
debug('writing close packet'); | |
self.write([{ type: 'close' }]); | |
} | |
if ('open' === this.readyState) { | |
debug('transport open - closing'); | |
close(); | |
} else { | |
// in case we're trying to close while | |
// handshaking is in progress (GH-164) | |
debug('transport not open - deferring close'); | |
this.once('open', close); | |
} | |
}; | |
/** | |
* Writes a packets payload. | |
* | |
* @param {Array} data packets | |
* @param {Function} drain callback | |
* @api private | |
*/ | |
Polling.prototype.write = function (packets) { | |
var self = this; | |
this.writable = false; | |
var callbackfn = function () { | |
self.writable = true; | |
self.emit('drain'); | |
}; | |
parser.encodePayload(packets, this.supportsBinary, function (data) { | |
self.doWrite(data, callbackfn); | |
}); | |
}; | |
/** | |
* Generates uri for connection. | |
* | |
* @api private | |
*/ | |
Polling.prototype.uri = function () { | |
var query = this.query || {}; | |
var schema = this.secure ? 'https' : 'http'; | |
var port = ''; | |
// cache busting is forced | |
if (false !== this.timestampRequests) { | |
query[this.timestampParam] = yeast(); | |
} | |
if (!this.supportsBinary && !query.sid) { | |
query.b64 = 1; | |
} | |
query = parseqs.encode(query); | |
// avoid port if default for schema | |
if (this.port && (('https' === schema && Number(this.port) !== 443) || | |
('http' === schema && Number(this.port) !== 80))) { | |
port = ':' + this.port; | |
} | |
// prepend ? to query | |
if (query.length) { | |
query = '?' + query; | |
} | |
var ipv6 = this.hostname.indexOf(':') !== -1; | |
return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query; | |
}; | |
},{"../transport":23,"component-inherit":15,"debug":17,"engine.io-parser":30,"parseqs":52,"xmlhttprequest-ssl":29,"yeast":79}],28:[function(require,module,exports){ | |
(function (global){ | |
/** | |
* Module dependencies. | |
*/ | |
var Transport = require('../transport'); | |
var parser = require('engine.io-parser'); | |
var parseqs = require('parseqs'); | |
var inherit = require('component-inherit'); | |
var yeast = require('yeast'); | |
var debug = require('debug')('engine.io-client:websocket'); | |
var BrowserWebSocket = global.WebSocket || global.MozWebSocket; | |
var NodeWebSocket; | |
if (typeof window === 'undefined') { | |
try { | |
NodeWebSocket = require('ws'); | |
} catch (e) { } | |
} | |
/** | |
* Get either the `WebSocket` or `MozWebSocket` globals | |
* in the browser or try to resolve WebSocket-compatible | |
* interface exposed by `ws` for Node-like environment. | |
*/ | |
var WebSocket = BrowserWebSocket; | |
if (!WebSocket && typeof window === 'undefined') { | |
WebSocket = NodeWebSocket; | |
} | |
/** | |
* Module exports. | |
*/ | |
module.exports = WS; | |
/** | |
* WebSocket transport constructor. | |
* | |
* @api {Object} connection options | |
* @api public | |
*/ | |
function WS (opts) { | |
var forceBase64 = (opts && opts.forceBase64); | |
if (forceBase64) { | |
this.supportsBinary = false; | |
} | |
this.perMessageDeflate = opts.perMessageDeflate; | |
this.usingBrowserWebSocket = BrowserWebSocket && !opts.forceNode; | |
this.protocols = opts.protocols; | |
if (!this.usingBrowserWebSocket) { | |
WebSocket = NodeWebSocket; | |
} | |
Transport.call(this, opts); | |
} | |
/** | |
* Inherits from Transport. | |
*/ | |
inherit(WS, Transport); | |
/** | |
* Transport name. | |
* | |
* @api public | |
*/ | |
WS.prototype.name = 'websocket'; | |
/* | |
* WebSockets support binary | |
*/ | |
WS.prototype.supportsBinary = true; | |
/** | |
* Opens socket. | |
* | |
* @api private | |
*/ | |
WS.prototype.doOpen = function () { | |
if (!this.check()) { | |
// let probe timeout | |
return; | |
} | |
var uri = this.uri(); | |
var protocols = this.protocols; | |
var opts = { | |
agent: this.agent, | |
perMessageDeflate: this.perMessageDeflate | |
}; | |
// SSL options for Node.js client | |
opts.pfx = this.pfx; | |
opts.key = this.key; | |
opts.passphrase = this.passphrase; | |
opts.cert = this.cert; | |
opts.ca = this.ca; | |
opts.ciphers = this.ciphers; | |
opts.rejectUnauthorized = this.rejectUnauthorized; | |
if (this.extraHeaders) { | |
opts.headers = this.extraHeaders; | |
} | |
if (this.localAddress) { | |
opts.localAddress = this.localAddress; | |
} | |
try { | |
this.ws = this.usingBrowserWebSocket ? (protocols ? new WebSocket(uri, protocols) : new WebSocket(uri)) : new WebSocket(uri, protocols, opts); | |
} catch (err) { | |
return this.emit('error', err); | |
} | |
if (this.ws.binaryType === undefined) { | |
this.supportsBinary = false; | |
} | |
if (this.ws.supports && this.ws.supports.binary) { | |
this.supportsBinary = true; | |
this.ws.binaryType = 'nodebuffer'; | |
} else { | |
this.ws.binaryType = 'arraybuffer'; | |
} | |
this.addEventListeners(); | |
}; | |
/** | |
* Adds event listeners to the socket | |
* | |
* @api private | |
*/ | |
WS.prototype.addEventListeners = function () { | |
var self = this; | |
this.ws.onopen = function () { | |
self.onOpen(); | |
}; | |
this.ws.onclose = function () { | |
self.onClose(); | |
}; | |
this.ws.onmessage = function (ev) { | |
self.onData(ev.data); | |
}; | |
this.ws.onerror = function (e) { | |
self.onError('websocket error', e); | |
}; | |
}; | |
/** | |
* Writes data to socket. | |
* | |
* @param {Array} array of packets. | |
* @api private | |
*/ | |
WS.prototype.write = function (packets) { | |
var self = this; | |
this.writable = false; | |
// encodePacket efficient as it uses WS framing | |
// no need for encodePayload | |
var total = packets.length; | |
for (var i = 0, l = total; i < l; i++) { | |
(function (packet) { | |
parser.encodePacket(packet, self.supportsBinary, function (data) { | |
if (!self.usingBrowserWebSocket) { | |
// always create a new object (GH-437) | |
var opts = {}; | |
if (packet.options) { | |
opts.compress = packet.options.compress; | |
} | |
if (self.perMessageDeflate) { | |
var len = 'string' === typeof data ? global.Buffer.byteLength(data) : data.length; | |
if (len < self.perMessageDeflate.threshold) { | |
opts.compress = false; | |
} | |
} | |
} | |
// Sometimes the websocket has already been closed but the browser didn't | |
// have a chance of informing us about it yet, in that case send will | |
// throw an error | |
try { | |
if (self.usingBrowserWebSocket) { | |
// TypeError is thrown when passing the second argument on Safari | |
self.ws.send(data); | |
} else { | |
self.ws.send(data, opts); | |
} | |
} catch (e) { | |
debug('websocket closed before onclose event'); | |
} | |
--total || done(); | |
}); | |
})(packets[i]); | |
} | |
function done () { | |
self.emit('flush'); | |
// fake drain | |
// defer to next tick to allow Socket to clear writeBuffer | |
setTimeout(function () { | |
self.writable = true; | |
self.emit('drain'); | |
}, 0); | |
} | |
}; | |
/** | |
* Called upon close | |
* | |
* @api private | |
*/ | |
WS.prototype.onClose = function () { | |
Transport.prototype.onClose.call(this); | |
}; | |
/** | |
* Closes socket. | |
* | |
* @api private | |
*/ | |
WS.prototype.doClose = function () { | |
if (typeof this.ws !== 'undefined') { | |
this.ws.close(); | |
} | |
}; | |
/** | |
* Generates uri for connection. | |
* | |
* @api private | |
*/ | |
WS.prototype.uri = function () { | |
var query = this.query || {}; | |
var schema = this.secure ? 'wss' : 'ws'; | |
var port = ''; | |
// avoid port if default for schema | |
if (this.port && (('wss' === schema && Number(this.port) !== 443) || | |
('ws' === schema && Number(this.port) !== 80))) { | |
port = ':' + this.port; | |
} | |
// append timestamp to URI | |
if (this.timestampRequests) { | |
query[this.timestampParam] = yeast(); | |
} | |
// communicate binary support capabilities | |
if (!this.supportsBinary) { | |
query.b64 = 1; | |
} | |
query = parseqs.encode(query); | |
// prepend ? to query | |
if (query.length) { | |
query = '?' + query; | |
} | |
var ipv6 = this.hostname.indexOf(':') !== -1; | |
return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query; | |
}; | |
/** | |
* Feature detection for WebSocket. | |
* | |
* @return {Boolean} whether this transport is available. | |
* @api public | |
*/ | |
WS.prototype.check = function () { | |
return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name); | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"../transport":23,"component-inherit":15,"debug":17,"engine.io-parser":30,"parseqs":52,"ws":2,"yeast":79}],29:[function(require,module,exports){ | |
(function (global){ | |
// browser shim for xmlhttprequest module | |
var hasCORS = require('has-cors'); | |
module.exports = function (opts) { | |
var xdomain = opts.xdomain; | |
// scheme must be same when usign XDomainRequest | |
// http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx | |
var xscheme = opts.xscheme; | |
// XDomainRequest has a flow of not sending cookie, therefore it should be disabled as a default. | |
// https://github.com/Automattic/engine.io-client/pull/217 | |
var enablesXDR = opts.enablesXDR; | |
// XMLHttpRequest can be disabled on IE | |
try { | |
if ('undefined' !== typeof XMLHttpRequest && (!xdomain || hasCORS)) { | |
return new XMLHttpRequest(); | |
} | |
} catch (e) { } | |
// Use XDomainRequest for IE8 if enablesXDR is true | |
// because loading bar keeps flashing when using jsonp-polling | |
// https://github.com/yujiosaka/socke.io-ie8-loading-example | |
try { | |
if ('undefined' !== typeof XDomainRequest && !xscheme && enablesXDR) { | |
return new XDomainRequest(); | |
} | |
} catch (e) { } | |
if (!xdomain) { | |
try { | |
return new global[['Active'].concat('Object').join('X')]('Microsoft.XMLHTTP'); | |
} catch (e) { } | |
} | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"has-cors":39}],30:[function(require,module,exports){ | |
(function (global){ | |
/** | |
* Module dependencies. | |
*/ | |
var keys = require('./keys'); | |
var hasBinary = require('has-binary2'); | |
var sliceBuffer = require('arraybuffer.slice'); | |
var after = require('after'); | |
var utf8 = require('./utf8'); | |
var base64encoder; | |
if (global && global.ArrayBuffer) { | |
base64encoder = require('base64-arraybuffer'); | |
} | |
/** | |
* Check if we are running an android browser. That requires us to use | |
* ArrayBuffer with polling transports... | |
* | |
* http://ghinda.net/jpeg-blob-ajax-android/ | |
*/ | |
var isAndroid = typeof navigator !== 'undefined' && /Android/i.test(navigator.userAgent); | |
/** | |
* Check if we are running in PhantomJS. | |
* Uploading a Blob with PhantomJS does not work correctly, as reported here: | |
* https://github.com/ariya/phantomjs/issues/11395 | |
* @type boolean | |
*/ | |
var isPhantomJS = typeof navigator !== 'undefined' && /PhantomJS/i.test(navigator.userAgent); | |
/** | |
* When true, avoids using Blobs to encode payloads. | |
* @type boolean | |
*/ | |
var dontSendBlobs = isAndroid || isPhantomJS; | |
/** | |
* Current protocol version. | |
*/ | |
exports.protocol = 3; | |
/** | |
* Packet types. | |
*/ | |
var packets = exports.packets = { | |
open: 0 // non-ws | |
, close: 1 // non-ws | |
, ping: 2 | |
, pong: 3 | |
, message: 4 | |
, upgrade: 5 | |
, noop: 6 | |
}; | |
var packetslist = keys(packets); | |
/** | |
* Premade error packet. | |
*/ | |
var err = { type: 'error', data: 'parser error' }; | |
/** | |
* Create a blob api even for blob builder when vendor prefixes exist | |
*/ | |
var Blob = require('blob'); | |
/** | |
* Encodes a packet. | |
* | |
* <packet type id> [ <data> ] | |
* | |
* Example: | |
* | |
* 5hello world | |
* 3 | |
* 4 | |
* | |
* Binary is encoded in an identical principle | |
* | |
* @api private | |
*/ | |
exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) { | |
if (typeof supportsBinary === 'function') { | |
callback = supportsBinary; | |
supportsBinary = false; | |
} | |
if (typeof utf8encode === 'function') { | |
callback = utf8encode; | |
utf8encode = null; | |
} | |
var data = (packet.data === undefined) | |
? undefined | |
: packet.data.buffer || packet.data; | |
if (global.ArrayBuffer && data instanceof ArrayBuffer) { | |
return encodeArrayBuffer(packet, supportsBinary, callback); | |
} else if (Blob && data instanceof global.Blob) { | |
return encodeBlob(packet, supportsBinary, callback); | |
} | |
// might be an object with { base64: true, data: dataAsBase64String } | |
if (data && data.base64) { | |
return encodeBase64Object(packet, callback); | |
} | |
// Sending data as a utf-8 string | |
var encoded = packets[packet.type]; | |
// data fragment is optional | |
if (undefined !== packet.data) { | |
encoded += utf8encode ? utf8.encode(String(packet.data), { strict: false }) : String(packet.data); | |
} | |
return callback('' + encoded); | |
}; | |
function encodeBase64Object(packet, callback) { | |
// packet data is an object { base64: true, data: dataAsBase64String } | |
var message = 'b' + exports.packets[packet.type] + packet.data.data; | |
return callback(message); | |
} | |
/** | |
* Encode packet helpers for binary types | |
*/ | |
function encodeArrayBuffer(packet, supportsBinary, callback) { | |
if (!supportsBinary) { | |
return exports.encodeBase64Packet(packet, callback); | |
} | |
var data = packet.data; | |
var contentArray = new Uint8Array(data); | |
var resultBuffer = new Uint8Array(1 + data.byteLength); | |
resultBuffer[0] = packets[packet.type]; | |
for (var i = 0; i < contentArray.length; i++) { | |
resultBuffer[i+1] = contentArray[i]; | |
} | |
return callback(resultBuffer.buffer); | |
} | |
function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) { | |
if (!supportsBinary) { | |
return exports.encodeBase64Packet(packet, callback); | |
} | |
var fr = new FileReader(); | |
fr.onload = function() { | |
packet.data = fr.result; | |
exports.encodePacket(packet, supportsBinary, true, callback); | |
}; | |
return fr.readAsArrayBuffer(packet.data); | |
} | |
function encodeBlob(packet, supportsBinary, callback) { | |
if (!supportsBinary) { | |
return exports.encodeBase64Packet(packet, callback); | |
} | |
if (dontSendBlobs) { | |
return encodeBlobAsArrayBuffer(packet, supportsBinary, callback); | |
} | |
var length = new Uint8Array(1); | |
length[0] = packets[packet.type]; | |
var blob = new Blob([length.buffer, packet.data]); | |
return callback(blob); | |
} | |
/** | |
* Encodes a packet with binary data in a base64 string | |
* | |
* @param {Object} packet, has `type` and `data` | |
* @return {String} base64 encoded message | |
*/ | |
exports.encodeBase64Packet = function(packet, callback) { | |
var message = 'b' + exports.packets[packet.type]; | |
if (Blob && packet.data instanceof global.Blob) { | |
var fr = new FileReader(); | |
fr.onload = function() { | |
var b64 = fr.result.split(',')[1]; | |
callback(message + b64); | |
}; | |
return fr.readAsDataURL(packet.data); | |
} | |
var b64data; | |
try { | |
b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data)); | |
} catch (e) { | |
// iPhone Safari doesn't let you apply with typed arrays | |
var typed = new Uint8Array(packet.data); | |
var basic = new Array(typed.length); | |
for (var i = 0; i < typed.length; i++) { | |
basic[i] = typed[i]; | |
} | |
b64data = String.fromCharCode.apply(null, basic); | |
} | |
message += global.btoa(b64data); | |
return callback(message); | |
}; | |
/** | |
* Decodes a packet. Changes format to Blob if requested. | |
* | |
* @return {Object} with `type` and `data` (if any) | |
* @api private | |
*/ | |
exports.decodePacket = function (data, binaryType, utf8decode) { | |
if (data === undefined) { | |
return err; | |
} | |
// String data | |
if (typeof data === 'string') { | |
if (data.charAt(0) === 'b') { | |
return exports.decodeBase64Packet(data.substr(1), binaryType); | |
} | |
if (utf8decode) { | |
data = tryDecode(data); | |
if (data === false) { | |
return err; | |
} | |
} | |
var type = data.charAt(0); | |
if (Number(type) != type || !packetslist[type]) { | |
return err; | |
} | |
if (data.length > 1) { | |
return { type: packetslist[type], data: data.substring(1) }; | |
} else { | |
return { type: packetslist[type] }; | |
} | |
} | |
var asArray = new Uint8Array(data); | |
var type = asArray[0]; | |
var rest = sliceBuffer(data, 1); | |
if (Blob && binaryType === 'blob') { | |
rest = new Blob([rest]); | |
} | |
return { type: packetslist[type], data: rest }; | |
}; | |
function tryDecode(data) { | |
try { | |
data = utf8.decode(data, { strict: false }); | |
} catch (e) { | |
return false; | |
} | |
return data; | |
} | |
/** | |
* Decodes a packet encoded in a base64 string | |
* | |
* @param {String} base64 encoded message | |
* @return {Object} with `type` and `data` (if any) | |
*/ | |
exports.decodeBase64Packet = function(msg, binaryType) { | |
var type = packetslist[msg.charAt(0)]; | |
if (!base64encoder) { | |
return { type: type, data: { base64: true, data: msg.substr(1) } }; | |
} | |
var data = base64encoder.decode(msg.substr(1)); | |
if (binaryType === 'blob' && Blob) { | |
data = new Blob([data]); | |
} | |
return { type: type, data: data }; | |
}; | |
/** | |
* Encodes multiple messages (payload). | |
* | |
* <length>:data | |
* | |
* Example: | |
* | |
* 11:hello world2:hi | |
* | |
* If any contents are binary, they will be encoded as base64 strings. Base64 | |
* encoded strings are marked with a b before the length specifier | |
* | |
* @param {Array} packets | |
* @api private | |
*/ | |
exports.encodePayload = function (packets, supportsBinary, callback) { | |
if (typeof supportsBinary === 'function') { | |
callback = supportsBinary; | |
supportsBinary = null; | |
} | |
var isBinary = hasBinary(packets); | |
if (supportsBinary && isBinary) { | |
if (Blob && !dontSendBlobs) { | |
return exports.encodePayloadAsBlob(packets, callback); | |
} | |
return exports.encodePayloadAsArrayBuffer(packets, callback); | |
} | |
if (!packets.length) { | |
return callback('0:'); | |
} | |
function setLengthHeader(message) { | |
return message.length + ':' + message; | |
} | |
function encodeOne(packet, doneCallback) { | |
exports.encodePacket(packet, !isBinary ? false : supportsBinary, false, function(message) { | |
doneCallback(null, setLengthHeader(message)); | |
}); | |
} | |
map(packets, encodeOne, function(err, results) { | |
return callback(results.join('')); | |
}); | |
}; | |
/** | |
* Async array map using after | |
*/ | |
function map(ary, each, done) { | |
var result = new Array(ary.length); | |
var next = after(ary.length, done); | |
var eachWithIndex = function(i, el, cb) { | |
each(el, function(error, msg) { | |
result[i] = msg; | |
cb(error, result); | |
}); | |
}; | |
for (var i = 0; i < ary.length; i++) { | |
eachWithIndex(i, ary[i], next); | |
} | |
} | |
/* | |
* Decodes data when a payload is maybe expected. Possible binary contents are | |
* decoded from their base64 representation | |
* | |
* @param {String} data, callback method | |
* @api public | |
*/ | |
exports.decodePayload = function (data, binaryType, callback) { | |
if (typeof data !== 'string') { | |
return exports.decodePayloadAsBinary(data, binaryType, callback); | |
} | |
if (typeof binaryType === 'function') { | |
callback = binaryType; | |
binaryType = null; | |
} | |
var packet; | |
if (data === '') { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
var length = '', n, msg; | |
for (var i = 0, l = data.length; i < l; i++) { | |
var chr = data.charAt(i); | |
if (chr !== ':') { | |
length += chr; | |
continue; | |
} | |
if (length === '' || (length != (n = Number(length)))) { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
msg = data.substr(i + 1, n); | |
if (length != msg.length) { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
if (msg.length) { | |
packet = exports.decodePacket(msg, binaryType, false); | |
if (err.type === packet.type && err.data === packet.data) { | |
// parser error in individual packet - ignoring payload | |
return callback(err, 0, 1); | |
} | |
var ret = callback(packet, i + n, l); | |
if (false === ret) return; | |
} | |
// advance cursor | |
i += n; | |
length = ''; | |
} | |
if (length !== '') { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
}; | |
/** | |
* Encodes multiple messages (payload) as binary. | |
* | |
* <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number | |
* 255><data> | |
* | |
* Example: | |
* 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers | |
* | |
* @param {Array} packets | |
* @return {ArrayBuffer} encoded payload | |
* @api private | |
*/ | |
exports.encodePayloadAsArrayBuffer = function(packets, callback) { | |
if (!packets.length) { | |
return callback(new ArrayBuffer(0)); | |
} | |
function encodeOne(packet, doneCallback) { | |
exports.encodePacket(packet, true, true, function(data) { | |
return doneCallback(null, data); | |
}); | |
} | |
map(packets, encodeOne, function(err, encodedPackets) { | |
var totalLength = encodedPackets.reduce(function(acc, p) { | |
var len; | |
if (typeof p === 'string'){ | |
len = p.length; | |
} else { | |
len = p.byteLength; | |
} | |
return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2 | |
}, 0); | |
var resultArray = new Uint8Array(totalLength); | |
var bufferIndex = 0; | |
encodedPackets.forEach(function(p) { | |
var isString = typeof p === 'string'; | |
var ab = p; | |
if (isString) { | |
var view = new Uint8Array(p.length); | |
for (var i = 0; i < p.length; i++) { | |
view[i] = p.charCodeAt(i); | |
} | |
ab = view.buffer; | |
} | |
if (isString) { // not true binary | |
resultArray[bufferIndex++] = 0; | |
} else { // true binary | |
resultArray[bufferIndex++] = 1; | |
} | |
var lenStr = ab.byteLength.toString(); | |
for (var i = 0; i < lenStr.length; i++) { | |
resultArray[bufferIndex++] = parseInt(lenStr[i]); | |
} | |
resultArray[bufferIndex++] = 255; | |
var view = new Uint8Array(ab); | |
for (var i = 0; i < view.length; i++) { | |
resultArray[bufferIndex++] = view[i]; | |
} | |
}); | |
return callback(resultArray.buffer); | |
}); | |
}; | |
/** | |
* Encode as Blob | |
*/ | |
exports.encodePayloadAsBlob = function(packets, callback) { | |
function encodeOne(packet, doneCallback) { | |
exports.encodePacket(packet, true, true, function(encoded) { | |
var binaryIdentifier = new Uint8Array(1); | |
binaryIdentifier[0] = 1; | |
if (typeof encoded === 'string') { | |
var view = new Uint8Array(encoded.length); | |
for (var i = 0; i < encoded.length; i++) { | |
view[i] = encoded.charCodeAt(i); | |
} | |
encoded = view.buffer; | |
binaryIdentifier[0] = 0; | |
} | |
var len = (encoded instanceof ArrayBuffer) | |
? encoded.byteLength | |
: encoded.size; | |
var lenStr = len.toString(); | |
var lengthAry = new Uint8Array(lenStr.length + 1); | |
for (var i = 0; i < lenStr.length; i++) { | |
lengthAry[i] = parseInt(lenStr[i]); | |
} | |
lengthAry[lenStr.length] = 255; | |
if (Blob) { | |
var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]); | |
doneCallback(null, blob); | |
} | |
}); | |
} | |
map(packets, encodeOne, function(err, results) { | |
return callback(new Blob(results)); | |
}); | |
}; | |
/* | |
* Decodes data when a payload is maybe expected. Strings are decoded by | |
* interpreting each byte as a key code for entries marked to start with 0. See | |
* description of encodePayloadAsBinary | |
* | |
* @param {ArrayBuffer} data, callback method | |
* @api public | |
*/ | |
exports.decodePayloadAsBinary = function (data, binaryType, callback) { | |
if (typeof binaryType === 'function') { | |
callback = binaryType; | |
binaryType = null; | |
} | |
var bufferTail = data; | |
var buffers = []; | |
while (bufferTail.byteLength > 0) { | |
var tailArray = new Uint8Array(bufferTail); | |
var isString = tailArray[0] === 0; | |
var msgLength = ''; | |
for (var i = 1; ; i++) { | |
if (tailArray[i] === 255) break; | |
// 310 = char length of Number.MAX_VALUE | |
if (msgLength.length > 310) { | |
return callback(err, 0, 1); | |
} | |
msgLength += tailArray[i]; | |
} | |
bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length); | |
msgLength = parseInt(msgLength); | |
var msg = sliceBuffer(bufferTail, 0, msgLength); | |
if (isString) { | |
try { | |
msg = String.fromCharCode.apply(null, new Uint8Array(msg)); | |
} catch (e) { | |
// iPhone Safari doesn't let you apply to typed arrays | |
var typed = new Uint8Array(msg); | |
msg = ''; | |
for (var i = 0; i < typed.length; i++) { | |
msg += String.fromCharCode(typed[i]); | |
} | |
} | |
} | |
buffers.push(msg); | |
bufferTail = sliceBuffer(bufferTail, msgLength); | |
} | |
var total = buffers.length; | |
buffers.forEach(function(buffer, i) { | |
callback(exports.decodePacket(buffer, binaryType, true), i, total); | |
}); | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./keys":31,"./utf8":32,"after":7,"arraybuffer.slice":8,"base64-arraybuffer":10,"blob":12,"has-binary2":38}],31:[function(require,module,exports){ | |
/** | |
* Gets the keys for an object. | |
* | |
* @return {Array} keys | |
* @api private | |
*/ | |
module.exports = Object.keys || function keys (obj){ | |
var arr = []; | |
var has = Object.prototype.hasOwnProperty; | |
for (var i in obj) { | |
if (has.call(obj, i)) { | |
arr.push(i); | |
} | |
} | |
return arr; | |
}; | |
},{}],32:[function(require,module,exports){ | |
(function (global){ | |
/*! https://mths.be/utf8js v2.1.2 by @mathias */ | |
;(function(root) { | |
// Detect free variables `exports` | |
var freeExports = typeof exports == 'object' && exports; | |
// Detect free variable `module` | |
var freeModule = typeof module == 'object' && module && | |
module.exports == freeExports && module; | |
// Detect free variable `global`, from Node.js or Browserified code, | |
// and use it as `root` | |
var freeGlobal = typeof global == 'object' && global; | |
if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { | |
root = freeGlobal; | |
} | |
/*--------------------------------------------------------------------------*/ | |
var stringFromCharCode = String.fromCharCode; | |
// Taken from https://mths.be/punycode | |
function ucs2decode(string) { | |
var output = []; | |
var counter = 0; | |
var length = string.length; | |
var value; | |
var extra; | |
while (counter < length) { | |
value = string.charCodeAt(counter++); | |
if (value >= 0xD800 && value <= 0xDBFF && counter < length) { | |
// high surrogate, and there is a next character | |
extra = string.charCodeAt(counter++); | |
if ((extra & 0xFC00) == 0xDC00) { // low surrogate | |
output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); | |
} else { | |
// unmatched surrogate; only append this code unit, in case the next | |
// code unit is the high surrogate of a surrogate pair | |
output.push(value); | |
counter--; | |
} | |
} else { | |
output.push(value); | |
} | |
} | |
return output; | |
} | |
// Taken from https://mths.be/punycode | |
function ucs2encode(array) { | |
var length = array.length; | |
var index = -1; | |
var value; | |
var output = ''; | |
while (++index < length) { | |
value = array[index]; | |
if (value > 0xFFFF) { | |
value -= 0x10000; | |
output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); | |
value = 0xDC00 | value & 0x3FF; | |
} | |
output += stringFromCharCode(value); | |
} | |
return output; | |
} | |
function checkScalarValue(codePoint, strict) { | |
if (codePoint >= 0xD800 && codePoint <= 0xDFFF) { | |
if (strict) { | |
throw Error( | |
'Lone surrogate U+' + codePoint.toString(16).toUpperCase() + | |
' is not a scalar value' | |
); | |
} | |
return false; | |
} | |
return true; | |
} | |
/*--------------------------------------------------------------------------*/ | |
function createByte(codePoint, shift) { | |
return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80); | |
} | |
function encodeCodePoint(codePoint, strict) { | |
if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence | |
return stringFromCharCode(codePoint); | |
} | |
var symbol = ''; | |
if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence | |
symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0); | |
} | |
else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence | |
if (!checkScalarValue(codePoint, strict)) { | |
codePoint = 0xFFFD; | |
} | |
symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0); | |
symbol += createByte(codePoint, 6); | |
} | |
else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence | |
symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0); | |
symbol += createByte(codePoint, 12); | |
symbol += createByte(codePoint, 6); | |
} | |
symbol += stringFromCharCode((codePoint & 0x3F) | 0x80); | |
return symbol; | |
} | |
function utf8encode(string, opts) { | |
opts = opts || {}; | |
var strict = false !== opts.strict; | |
var codePoints = ucs2decode(string); | |
var length = codePoints.length; | |
var index = -1; | |
var codePoint; | |
var byteString = ''; | |
while (++index < length) { | |
codePoint = codePoints[index]; | |
byteString += encodeCodePoint(codePoint, strict); | |
} | |
return byteString; | |
} | |
/*--------------------------------------------------------------------------*/ | |
function readContinuationByte() { | |
if (byteIndex >= byteCount) { | |
throw Error('Invalid byte index'); | |
} | |
var continuationByte = byteArray[byteIndex] & 0xFF; | |
byteIndex++; | |
if ((continuationByte & 0xC0) == 0x80) { | |
return continuationByte & 0x3F; | |
} | |
// If we end up here, it’s not a continuation byte | |
throw Error('Invalid continuation byte'); | |
} | |
function decodeSymbol(strict) { | |
var byte1; | |
var byte2; | |
var byte3; | |
var byte4; | |
var codePoint; | |
if (byteIndex > byteCount) { | |
throw Error('Invalid byte index'); | |
} | |
if (byteIndex == byteCount) { | |
return false; | |
} | |
// Read first byte | |
byte1 = byteArray[byteIndex] & 0xFF; | |
byteIndex++; | |
// 1-byte sequence (no continuation bytes) | |
if ((byte1 & 0x80) == 0) { | |
return byte1; | |
} | |
// 2-byte sequence | |
if ((byte1 & 0xE0) == 0xC0) { | |
byte2 = readContinuationByte(); | |
codePoint = ((byte1 & 0x1F) << 6) | byte2; | |
if (codePoint >= 0x80) { | |
return codePoint; | |
} else { | |
throw Error('Invalid continuation byte'); | |
} | |
} | |
// 3-byte sequence (may include unpaired surrogates) | |
if ((byte1 & 0xF0) == 0xE0) { | |
byte2 = readContinuationByte(); | |
byte3 = readContinuationByte(); | |
codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; | |
if (codePoint >= 0x0800) { | |
return checkScalarValue(codePoint, strict) ? codePoint : 0xFFFD; | |
} else { | |
throw Error('Invalid continuation byte'); | |
} | |
} | |
// 4-byte sequence | |
if ((byte1 & 0xF8) == 0xF0) { | |
byte2 = readContinuationByte(); | |
byte3 = readContinuationByte(); | |
byte4 = readContinuationByte(); | |
codePoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0C) | | |
(byte3 << 0x06) | byte4; | |
if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) { | |
return codePoint; | |
} | |
} | |
throw Error('Invalid UTF-8 detected'); | |
} | |
var byteArray; | |
var byteCount; | |
var byteIndex; | |
function utf8decode(byteString, opts) { | |
opts = opts || {}; | |
var strict = false !== opts.strict; | |
byteArray = ucs2decode(byteString); | |
byteCount = byteArray.length; | |
byteIndex = 0; | |
var codePoints = []; | |
var tmp; | |
while ((tmp = decodeSymbol(strict)) !== false) { | |
codePoints.push(tmp); | |
} | |
return ucs2encode(codePoints); | |
} | |
/*--------------------------------------------------------------------------*/ | |
var utf8 = { | |
'version': '2.1.2', | |
'encode': utf8encode, | |
'decode': utf8decode | |
}; | |
// Some AMD build optimizers, like r.js, check for specific condition patterns | |
// like the following: | |
if ( | |
typeof define == 'function' && | |
typeof define.amd == 'object' && | |
define.amd | |
) { | |
define(function() { | |
return utf8; | |
}); | |
} else if (freeExports && !freeExports.nodeType) { | |
if (freeModule) { // in Node.js or RingoJS v0.8.0+ | |
freeModule.exports = utf8; | |
} else { // in Narwhal or RingoJS v0.7.0- | |
var object = {}; | |
var hasOwnProperty = object.hasOwnProperty; | |
for (var key in utf8) { | |
hasOwnProperty.call(utf8, key) && (freeExports[key] = utf8[key]); | |
} | |
} | |
} else { // in Rhino or a web browser | |
root.utf8 = utf8; | |
} | |
}(this)); | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],33:[function(require,module,exports){ | |
'use strict'; | |
var hasOwn = Object.prototype.hasOwnProperty; | |
var toStr = Object.prototype.toString; | |
var isArray = function isArray(arr) { | |
if (typeof Array.isArray === 'function') { | |
return Array.isArray(arr); | |
} | |
return toStr.call(arr) === '[object Array]'; | |
}; | |
var isPlainObject = function isPlainObject(obj) { | |
if (!obj || toStr.call(obj) !== '[object Object]') { | |
return false; | |
} | |
var hasOwnConstructor = hasOwn.call(obj, 'constructor'); | |
var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf'); | |
// Not own constructor property must be Object | |
if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) { | |
return false; | |
} | |
// Own properties are enumerated firstly, so to speed up, | |
// if last one is own, then all properties are own. | |
var key; | |
for (key in obj) { /**/ } | |
return typeof key === 'undefined' || hasOwn.call(obj, key); | |
}; | |
module.exports = function extend() { | |
var options, name, src, copy, copyIsArray, clone; | |
var target = arguments[0]; | |
var i = 1; | |
var length = arguments.length; | |
var deep = false; | |
// Handle a deep copy situation | |
if (typeof target === 'boolean') { | |
deep = target; | |
target = arguments[1] || {}; | |
// skip the boolean and the target | |
i = 2; | |
} | |
if (target == null || (typeof target !== 'object' && typeof target !== 'function')) { | |
target = {}; | |
} | |
for (; i < length; ++i) { | |
options = arguments[i]; | |
// Only deal with non-null/undefined values | |
if (options != null) { | |
// Extend the base object | |
for (name in options) { | |
src = target[name]; | |
copy = options[name]; | |
// Prevent never-ending loop | |
if (target !== copy) { | |
// Recurse if we're merging plain objects or arrays | |
if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { | |
if (copyIsArray) { | |
copyIsArray = false; | |
clone = src && isArray(src) ? src : []; | |
} else { | |
clone = src && isPlainObject(src) ? src : {}; | |
} | |
// Never move original objects, clone them | |
target[name] = extend(deep, clone, copy); | |
// Don't bring in undefined values | |
} else if (typeof copy !== 'undefined') { | |
target[name] = copy; | |
} | |
} | |
} | |
} | |
} | |
// Return the modified object | |
return target; | |
}; | |
},{}],34:[function(require,module,exports){ | |
module.exports = function flatten(list, depth) { | |
depth = (typeof depth == 'number') ? depth : Infinity; | |
if (!depth) { | |
if (Array.isArray(list)) { | |
return list.map(function(i) { return i; }); | |
} | |
return list; | |
} | |
return _flatten(list, 1); | |
function _flatten(list, d) { | |
return list.reduce(function (acc, item) { | |
if (Array.isArray(item) && d < depth) { | |
return acc.concat(_flatten(item, d + 1)); | |
} | |
else { | |
return acc.concat(item); | |
} | |
}, []); | |
} | |
}; | |
},{}],35:[function(require,module,exports){ | |
'use strict'; | |
exports.__esModule = true; | |
var NODE_LIST_CLASSES = { | |
'[object HTMLCollection]': true, | |
'[object NodeList]': true, | |
'[object RadioNodeList]': true | |
}; | |
// .type values for elements which can appear in .elements and should be ignored | |
var IGNORED_ELEMENT_TYPES = { | |
'button': true, | |
'fieldset': true, | |
// 'keygen': true, | |
// 'output': true, | |
'reset': true, | |
'submit': true | |
}; | |
var CHECKED_INPUT_TYPES = { | |
'checkbox': true, | |
'radio': true | |
}; | |
var TRIM_RE = /^\s+|\s+$/g; | |
var slice = Array.prototype.slice; | |
var toString = Object.prototype.toString; | |
/** | |
* @param {HTMLFormElement} form | |
* @param {Object} options | |
* @return {Object.<string,(string|Array.<string>)>} an object containing | |
* submittable value(s) held in the form's .elements collection, with | |
* properties named as per element names or ids. | |
*/ | |
function getFormData(form) { | |
var options = arguments.length <= 1 || arguments[1] === undefined ? { trim: false } : arguments[1]; | |
if (!form) { | |
throw new Error('A form is required by getFormData, was given form=' + form); | |
} | |
var data = {}; | |
var elementName = undefined; | |
var elementNames = []; | |
var elementNameLookup = {}; | |
// Get unique submittable element names for the form | |
for (var i = 0, l = form.elements.length; i < l; i++) { | |
var element = form.elements[i]; | |
if (IGNORED_ELEMENT_TYPES[element.type] || element.disabled) { | |
continue; | |
} | |
elementName = element.name || element.id; | |
if (elementName && !elementNameLookup[elementName]) { | |
elementNames.push(elementName); | |
elementNameLookup[elementName] = true; | |
} | |
} | |
// Extract element data name-by-name for consistent handling of special cases | |
// around elements which contain multiple inputs. | |
for (var i = 0, l = elementNames.length; i < l; i++) { | |
elementName = elementNames[i]; | |
var value = getNamedFormElementData(form, elementName, options); | |
if (value != null) { | |
data[elementName] = value; | |
} | |
} | |
return data; | |
} | |
/** | |
* @param {HTMLFormElement} form | |
* @param {string} elementName | |
* @param {Object} options | |
* @return {(string|Array.<string>)} submittable value(s) in the form for a | |
* named element from its .elements collection, or null if there was no | |
* element with that name or the element had no submittable value(s). | |
*/ | |
function getNamedFormElementData(form, elementName) { | |
var options = arguments.length <= 2 || arguments[2] === undefined ? { trim: false } : arguments[2]; | |
if (!form) { | |
throw new Error('A form is required by getNamedFormElementData, was given form=' + form); | |
} | |
if (!elementName && toString.call(elementName) !== '[object String]') { | |
throw new Error('A form element name is required by getNamedFormElementData, was given elementName=' + elementName); | |
} | |
var element = form.elements[elementName]; | |
if (!element || element.disabled) { | |
return null; | |
} | |
if (!NODE_LIST_CLASSES[toString.call(element)]) { | |
return getFormElementValue(element, options.trim); | |
} | |
// Deal with multiple form controls which have the same name | |
var data = []; | |
var allRadios = true; | |
for (var i = 0, l = element.length; i < l; i++) { | |
if (element[i].disabled) { | |
continue; | |
} | |
if (allRadios && element[i].type !== 'radio') { | |
allRadios = false; | |
} | |
var value = getFormElementValue(element[i], options.trim); | |
if (value != null) { | |
data = data.concat(value); | |
} | |
} | |
// Special case for an element with multiple same-named inputs which were all | |
// radio buttons: if there was a selected value, only return the value. | |
if (allRadios && data.length === 1) { | |
return data[0]; | |
} | |
return data.length > 0 ? data : null; | |
} | |
/** | |
* @param {HTMLElement} element a form element. | |
* @param {booleam} trim should values for text entry inputs be trimmed? | |
* @return {(string|Array.<string>|File|Array.<File>)} the element's submittable | |
* value(s), or null if it had none. | |
*/ | |
function getFormElementValue(element, trim) { | |
var value = null; | |
var type = element.type; | |
if (type === 'select-one') { | |
if (element.options.length) { | |
value = element.options[element.selectedIndex].value; | |
} | |
return value; | |
} | |
if (type === 'select-multiple') { | |
value = []; | |
for (var i = 0, l = element.options.length; i < l; i++) { | |
if (element.options[i].selected) { | |
value.push(element.options[i].value); | |
} | |
} | |
if (value.length === 0) { | |
value = null; | |
} | |
return value; | |
} | |
// If a file input doesn't have a files attribute, fall through to using its | |
// value attribute. | |
if (type === 'file' && 'files' in element) { | |
if (element.multiple) { | |
value = slice.call(element.files); | |
if (value.length === 0) { | |
value = null; | |
} | |
} else { | |
// Should be null if not present, according to the spec | |
value = element.files[0]; | |
} | |
return value; | |
} | |
if (!CHECKED_INPUT_TYPES[type]) { | |
value = trim ? element.value.replace(TRIM_RE, '') : element.value; | |
} else if (element.checked) { | |
value = element.value; | |
} | |
return value; | |
} | |
getFormData.getNamedFormElementData = getNamedFormElementData; | |
exports['default'] = getFormData; | |
module.exports = exports['default']; | |
},{}],36:[function(require,module,exports){ | |
(function (global){ | |
var topLevel = typeof global !== 'undefined' ? global : | |
typeof window !== 'undefined' ? window : {} | |
var minDoc = require('min-document'); | |
var doccy; | |
if (typeof document !== 'undefined') { | |
doccy = document; | |
} else { | |
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4']; | |
if (!doccy) { | |
doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc; | |
} | |
} | |
module.exports = doccy; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"min-document":2}],37:[function(require,module,exports){ | |
(function (global){ | |
var win; | |
if (typeof window !== "undefined") { | |
win = window; | |
} else if (typeof global !== "undefined") { | |
win = global; | |
} else if (typeof self !== "undefined"){ | |
win = self; | |
} else { | |
win = {}; | |
} | |
module.exports = win; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],38:[function(require,module,exports){ | |
(function (global){ | |
/* global Blob File */ | |
/* | |
* Module requirements. | |
*/ | |
var isArray = require('isarray'); | |
var toString = Object.prototype.toString; | |
var withNativeBlob = typeof global.Blob === 'function' || toString.call(global.Blob) === '[object BlobConstructor]'; | |
var withNativeFile = typeof global.File === 'function' || toString.call(global.File) === '[object FileConstructor]'; | |
/** | |
* Module exports. | |
*/ | |
module.exports = hasBinary; | |
/** | |
* Checks for binary data. | |
* | |
* Supports Buffer, ArrayBuffer, Blob and File. | |
* | |
* @param {Object} anything | |
* @api public | |
*/ | |
function hasBinary (obj) { | |
if (!obj || typeof obj !== 'object') { | |
return false; | |
} | |
if (isArray(obj)) { | |
for (var i = 0, l = obj.length; i < l; i++) { | |
if (hasBinary(obj[i])) { | |
return true; | |
} | |
} | |
return false; | |
} | |
if ((typeof global.Buffer === 'function' && global.Buffer.isBuffer && global.Buffer.isBuffer(obj)) || | |
(typeof global.ArrayBuffer === 'function' && obj instanceof ArrayBuffer) || | |
(withNativeBlob && obj instanceof Blob) || | |
(withNativeFile && obj instanceof File) | |
) { | |
return true; | |
} | |
// see: https://github.com/Automattic/has-binary/pull/4 | |
if (obj.toJSON && typeof obj.toJSON === 'function' && arguments.length === 1) { | |
return hasBinary(obj.toJSON(), true); | |
} | |
for (var key in obj) { | |
if (Object.prototype.hasOwnProperty.call(obj, key) && hasBinary(obj[key])) { | |
return true; | |
} | |
} | |
return false; | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"isarray":43}],39:[function(require,module,exports){ | |
/** | |
* Module exports. | |
* | |
* Logic borrowed from Modernizr: | |
* | |
* - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js | |
*/ | |
try { | |
module.exports = typeof XMLHttpRequest !== 'undefined' && | |
'withCredentials' in new XMLHttpRequest(); | |
} catch (err) { | |
// if XMLHttp support is disabled in IE then it will throw | |
// when trying to create | |
module.exports = false; | |
} | |
},{}],40:[function(require,module,exports){ | |
module.exports = attributeToProperty | |
var transform = { | |
'class': 'className', | |
'for': 'htmlFor', | |
'http-equiv': 'httpEquiv' | |
} | |
function attributeToProperty (h) { | |
return function (tagName, attrs, children) { | |
for (var attr in attrs) { | |
if (attr in transform) { | |
attrs[transform[attr]] = attrs[attr] | |
delete attrs[attr] | |
} | |
} | |
return h(tagName, attrs, children) | |
} | |
} | |
},{}],41:[function(require,module,exports){ | |
var attrToProp = require('hyperscript-attribute-to-property') | |
var VAR = 0, TEXT = 1, OPEN = 2, CLOSE = 3, ATTR = 4 | |
var ATTR_KEY = 5, ATTR_KEY_W = 6 | |
var ATTR_VALUE_W = 7, ATTR_VALUE = 8 | |
var ATTR_VALUE_SQ = 9, ATTR_VALUE_DQ = 10 | |
var ATTR_EQ = 11, ATTR_BREAK = 12 | |
var COMMENT = 13 | |
module.exports = function (h, opts) { | |
if (!opts) opts = {} | |
var concat = opts.concat || function (a, b) { | |
return String(a) + String(b) | |
} | |
if (opts.attrToProp !== false) { | |
h = attrToProp(h) | |
} | |
return function (strings) { | |
var state = TEXT, reg = '' | |
var arglen = arguments.length | |
var parts = [] | |
for (var i = 0; i < strings.length; i++) { | |
if (i < arglen - 1) { | |
var arg = arguments[i+1] | |
var p = parse(strings[i]) | |
var xstate = state | |
if (xstate === ATTR_VALUE_DQ) xstate = ATTR_VALUE | |
if (xstate === ATTR_VALUE_SQ) xstate = ATTR_VALUE | |
if (xstate === ATTR_VALUE_W) xstate = ATTR_VALUE | |
if (xstate === ATTR) xstate = ATTR_KEY | |
p.push([ VAR, xstate, arg ]) | |
parts.push.apply(parts, p) | |
} else parts.push.apply(parts, parse(strings[i])) | |
} | |
var tree = [null,{},[]] | |
var stack = [[tree,-1]] | |
for (var i = 0; i < parts.length; i++) { | |
var cur = stack[stack.length-1][0] | |
var p = parts[i], s = p[0] | |
if (s === OPEN && /^\//.test(p[1])) { | |
var ix = stack[stack.length-1][1] | |
if (stack.length > 1) { | |
stack.pop() | |
stack[stack.length-1][0][2][ix] = h( | |
cur[0], cur[1], cur[2].length ? cur[2] : undefined | |
) | |
} | |
} else if (s === OPEN) { | |
var c = [p[1],{},[]] | |
cur[2].push(c) | |
stack.push([c,cur[2].length-1]) | |
} else if (s === ATTR_KEY || (s === VAR && p[1] === ATTR_KEY)) { | |
var key = '' | |
var copyKey | |
for (; i < parts.length; i++) { | |
if (parts[i][0] === ATTR_KEY) { | |
key = concat(key, parts[i][1]) | |
} else if (parts[i][0] === VAR && parts[i][1] === ATTR_KEY) { | |
if (typeof parts[i][2] === 'object' && !key) { | |
for (copyKey in parts[i][2]) { | |
if (parts[i][2].hasOwnProperty(copyKey) && !cur[1][copyKey]) { | |
cur[1][copyKey] = parts[i][2][copyKey] | |
} | |
} | |
} else { | |
key = concat(key, parts[i][2]) | |
} | |
} else break | |
} | |
if (parts[i][0] === ATTR_EQ) i++ | |
var j = i | |
for (; i < parts.length; i++) { | |
if (parts[i][0] === ATTR_VALUE || parts[i][0] === ATTR_KEY) { | |
if (!cur[1][key]) cur[1][key] = strfn(parts[i][1]) | |
else cur[1][key] = concat(cur[1][key], parts[i][1]) | |
} else if (parts[i][0] === VAR | |
&& (parts[i][1] === ATTR_VALUE || parts[i][1] === ATTR_KEY)) { | |
if (!cur[1][key]) cur[1][key] = strfn(parts[i][2]) | |
else cur[1][key] = concat(cur[1][key], parts[i][2]) | |
} else { | |
if (key.length && !cur[1][key] && i === j | |
&& (parts[i][0] === CLOSE || parts[i][0] === ATTR_BREAK)) { | |
// https://html.spec.whatwg.org/multipage/infrastructure.html#boolean-attributes | |
// empty string is falsy, not well behaved value in browser | |
cur[1][key] = key.toLowerCase() | |
} | |
break | |
} | |
} | |
} else if (s === ATTR_KEY) { | |
cur[1][p[1]] = true | |
} else if (s === VAR && p[1] === ATTR_KEY) { | |
cur[1][p[2]] = true | |
} else if (s === CLOSE) { | |
if (selfClosing(cur[0]) && stack.length) { | |
var ix = stack[stack.length-1][1] | |
stack.pop() | |
stack[stack.length-1][0][2][ix] = h( | |
cur[0], cur[1], cur[2].length ? cur[2] : undefined | |
) | |
} | |
} else if (s === VAR && p[1] === TEXT) { | |
if (p[2] === undefined || p[2] === null) p[2] = '' | |
else if (!p[2]) p[2] = concat('', p[2]) | |
if (Array.isArray(p[2][0])) { | |
cur[2].push.apply(cur[2], p[2]) | |
} else { | |
cur[2].push(p[2]) | |
} | |
} else if (s === TEXT) { | |
cur[2].push(p[1]) | |
} else if (s === ATTR_EQ || s === ATTR_BREAK) { | |
// no-op | |
} else { | |
throw new Error('unhandled: ' + s) | |
} | |
} | |
if (tree[2].length > 1 && /^\s*$/.test(tree[2][0])) { | |
tree[2].shift() | |
} | |
if (tree[2].length > 2 | |
|| (tree[2].length === 2 && /\S/.test(tree[2][1]))) { | |
throw new Error( | |
'multiple root elements must be wrapped in an enclosing tag' | |
) | |
} | |
if (Array.isArray(tree[2][0]) && typeof tree[2][0][0] === 'string' | |
&& Array.isArray(tree[2][0][2])) { | |
tree[2][0] = h(tree[2][0][0], tree[2][0][1], tree[2][0][2]) | |
} | |
return tree[2][0] | |
function parse (str) { | |
var res = [] | |
if (state === ATTR_VALUE_W) state = ATTR | |
for (var i = 0; i < str.length; i++) { | |
var c = str.charAt(i) | |
if (state === TEXT && c === '<') { | |
if (reg.length) res.push([TEXT, reg]) | |
reg = '' | |
state = OPEN | |
} else if (c === '>' && !quot(state) && state !== COMMENT) { | |
if (state === OPEN) { | |
res.push([OPEN,reg]) | |
} else if (state === ATTR_KEY) { | |
res.push([ATTR_KEY,reg]) | |
} else if (state === ATTR_VALUE && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
} | |
res.push([CLOSE]) | |
reg = '' | |
state = TEXT | |
} else if (state === COMMENT && /-$/.test(reg) && c === '-') { | |
if (opts.comments) { | |
res.push([ATTR_VALUE,reg.substr(0, reg.length - 1)],[CLOSE]) | |
} | |
reg = '' | |
state = TEXT | |
} else if (state === OPEN && /^!--$/.test(reg)) { | |
if (opts.comments) { | |
res.push([OPEN, reg],[ATTR_KEY,'comment'],[ATTR_EQ]) | |
} | |
reg = c | |
state = COMMENT | |
} else if (state === TEXT || state === COMMENT) { | |
reg += c | |
} else if (state === OPEN && /\s/.test(c)) { | |
res.push([OPEN, reg]) | |
reg = '' | |
state = ATTR | |
} else if (state === OPEN) { | |
reg += c | |
} else if (state === ATTR && /[^\s"'=/]/.test(c)) { | |
state = ATTR_KEY | |
reg = c | |
} else if (state === ATTR && /\s/.test(c)) { | |
if (reg.length) res.push([ATTR_KEY,reg]) | |
res.push([ATTR_BREAK]) | |
} else if (state === ATTR_KEY && /\s/.test(c)) { | |
res.push([ATTR_KEY,reg]) | |
reg = '' | |
state = ATTR_KEY_W | |
} else if (state === ATTR_KEY && c === '=') { | |
res.push([ATTR_KEY,reg],[ATTR_EQ]) | |
reg = '' | |
state = ATTR_VALUE_W | |
} else if (state === ATTR_KEY) { | |
reg += c | |
} else if ((state === ATTR_KEY_W || state === ATTR) && c === '=') { | |
res.push([ATTR_EQ]) | |
state = ATTR_VALUE_W | |
} else if ((state === ATTR_KEY_W || state === ATTR) && !/\s/.test(c)) { | |
res.push([ATTR_BREAK]) | |
if (/[\w-]/.test(c)) { | |
reg += c | |
state = ATTR_KEY | |
} else state = ATTR | |
} else if (state === ATTR_VALUE_W && c === '"') { | |
state = ATTR_VALUE_DQ | |
} else if (state === ATTR_VALUE_W && c === "'") { | |
state = ATTR_VALUE_SQ | |
} else if (state === ATTR_VALUE_DQ && c === '"') { | |
res.push([ATTR_VALUE,reg],[ATTR_BREAK]) | |
reg = '' | |
state = ATTR | |
} else if (state === ATTR_VALUE_SQ && c === "'") { | |
res.push([ATTR_VALUE,reg],[ATTR_BREAK]) | |
reg = '' | |
state = ATTR | |
} else if (state === ATTR_VALUE_W && !/\s/.test(c)) { | |
state = ATTR_VALUE | |
i-- | |
} else if (state === ATTR_VALUE && /\s/.test(c)) { | |
res.push([ATTR_VALUE,reg],[ATTR_BREAK]) | |
reg = '' | |
state = ATTR | |
} else if (state === ATTR_VALUE || state === ATTR_VALUE_SQ | |
|| state === ATTR_VALUE_DQ) { | |
reg += c | |
} | |
} | |
if (state === TEXT && reg.length) { | |
res.push([TEXT,reg]) | |
reg = '' | |
} else if (state === ATTR_VALUE && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
reg = '' | |
} else if (state === ATTR_VALUE_DQ && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
reg = '' | |
} else if (state === ATTR_VALUE_SQ && reg.length) { | |
res.push([ATTR_VALUE,reg]) | |
reg = '' | |
} else if (state === ATTR_KEY) { | |
res.push([ATTR_KEY,reg]) | |
reg = '' | |
} | |
return res | |
} | |
} | |
function strfn (x) { | |
if (typeof x === 'function') return x | |
else if (typeof x === 'string') return x | |
else if (x && typeof x === 'object') return x | |
else return concat('', x) | |
} | |
} | |
function quot (state) { | |
return state === ATTR_VALUE_SQ || state === ATTR_VALUE_DQ | |
} | |
var hasOwn = Object.prototype.hasOwnProperty | |
function has (obj, key) { return hasOwn.call(obj, key) } | |
var closeRE = RegExp('^(' + [ | |
'area', 'base', 'basefont', 'bgsound', 'br', 'col', 'command', 'embed', | |
'frame', 'hr', 'img', 'input', 'isindex', 'keygen', 'link', 'meta', 'param', | |
'source', 'track', 'wbr', '!--', | |
// SVG TAGS | |
'animate', 'animateTransform', 'circle', 'cursor', 'desc', 'ellipse', | |
'feBlend', 'feColorMatrix', 'feComposite', | |
'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', | |
'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', | |
'feGaussianBlur', 'feImage', 'feMergeNode', 'feMorphology', | |
'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', | |
'feTurbulence', 'font-face-format', 'font-face-name', 'font-face-uri', | |
'glyph', 'glyphRef', 'hkern', 'image', 'line', 'missing-glyph', 'mpath', | |
'path', 'polygon', 'polyline', 'rect', 'set', 'stop', 'tref', 'use', 'view', | |
'vkern' | |
].join('|') + ')(?:[\.#][a-zA-Z0-9\u007F-\uFFFF_:-]+)*$') | |
function selfClosing (tag) { return closeRE.test(tag) } | |
},{"hyperscript-attribute-to-property":40}],42:[function(require,module,exports){ | |
var indexOf = [].indexOf; | |
module.exports = function(arr, obj){ | |
if (indexOf) return arr.indexOf(obj); | |
for (var i = 0; i < arr.length; ++i) { | |
if (arr[i] === obj) return i; | |
} | |
return -1; | |
}; | |
},{}],43:[function(require,module,exports){ | |
var toString = {}.toString; | |
module.exports = Array.isArray || function (arr) { | |
return toString.call(arr) == '[object Array]'; | |
}; | |
},{}],44:[function(require,module,exports){ | |
(function (global){ | |
/** | |
* lodash (Custom Build) <https://lodash.com/> | |
* Build: `lodash modularize exports="npm" -o ./` | |
* Copyright jQuery Foundation and other contributors <https://jquery.org/> | |
* Released under MIT license <https://lodash.com/license> | |
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> | |
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors | |
*/ | |
/** Used as the `TypeError` message for "Functions" methods. */ | |
var FUNC_ERROR_TEXT = 'Expected a function'; | |
/** Used as references for various `Number` constants. */ | |
var NAN = 0 / 0; | |
/** `Object#toString` result references. */ | |
var symbolTag = '[object Symbol]'; | |
/** Used to match leading and trailing whitespace. */ | |
var reTrim = /^\s+|\s+$/g; | |
/** Used to detect bad signed hexadecimal string values. */ | |
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; | |
/** Used to detect binary string values. */ | |
var reIsBinary = /^0b[01]+$/i; | |
/** Used to detect octal string values. */ | |
var reIsOctal = /^0o[0-7]+$/i; | |
/** Built-in method references without a dependency on `root`. */ | |
var freeParseInt = parseInt; | |
/** Detect free variable `global` from Node.js. */ | |
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; | |
/** Detect free variable `self`. */ | |
var freeSelf = typeof self == 'object' && self && self.Object === Object && self; | |
/** Used as a reference to the global object. */ | |
var root = freeGlobal || freeSelf || Function('return this')(); | |
/** Used for built-in method references. */ | |
var objectProto = Object.prototype; | |
/** | |
* Used to resolve the | |
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) | |
* of values. | |
*/ | |
var objectToString = objectProto.toString; | |
/* Built-in method references for those with the same name as other `lodash` methods. */ | |
var nativeMax = Math.max, | |
nativeMin = Math.min; | |
/** | |
* Gets the timestamp of the number of milliseconds that have elapsed since | |
* the Unix epoch (1 January 1970 00:00:00 UTC). | |
* | |
* @static | |
* @memberOf _ | |
* @since 2.4.0 | |
* @category Date | |
* @returns {number} Returns the timestamp. | |
* @example | |
* | |
* _.defer(function(stamp) { | |
* console.log(_.now() - stamp); | |
* }, _.now()); | |
* // => Logs the number of milliseconds it took for the deferred invocation. | |
*/ | |
var now = function() { | |
return root.Date.now(); | |
}; | |
/** | |
* Creates a debounced function that delays invoking `func` until after `wait` | |
* milliseconds have elapsed since the last time the debounced function was | |
* invoked. The debounced function comes with a `cancel` method to cancel | |
* delayed `func` invocations and a `flush` method to immediately invoke them. | |
* Provide `options` to indicate whether `func` should be invoked on the | |
* leading and/or trailing edge of the `wait` timeout. The `func` is invoked | |
* with the last arguments provided to the debounced function. Subsequent | |
* calls to the debounced function return the result of the last `func` | |
* invocation. | |
* | |
* **Note:** If `leading` and `trailing` options are `true`, `func` is | |
* invoked on the trailing edge of the timeout only if the debounced function | |
* is invoked more than once during the `wait` timeout. | |
* | |
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred | |
* until to the next tick, similar to `setTimeout` with a timeout of `0`. | |
* | |
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) | |
* for details over the differences between `_.debounce` and `_.throttle`. | |
* | |
* @static | |
* @memberOf _ | |
* @since 0.1.0 | |
* @category Function | |
* @param {Function} func The function to debounce. | |
* @param {number} [wait=0] The number of milliseconds to delay. | |
* @param {Object} [options={}] The options object. | |
* @param {boolean} [options.leading=false] | |
* Specify invoking on the leading edge of the timeout. | |
* @param {number} [options.maxWait] | |
* The maximum time `func` is allowed to be delayed before it's invoked. | |
* @param {boolean} [options.trailing=true] | |
* Specify invoking on the trailing edge of the timeout. | |
* @returns {Function} Returns the new debounced function. | |
* @example | |
* | |
* // Avoid costly calculations while the window size is in flux. | |
* jQuery(window).on('resize', _.debounce(calculateLayout, 150)); | |
* | |
* // Invoke `sendMail` when clicked, debouncing subsequent calls. | |
* jQuery(element).on('click', _.debounce(sendMail, 300, { | |
* 'leading': true, | |
* 'trailing': false | |
* })); | |
* | |
* // Ensure `batchLog` is invoked once after 1 second of debounced calls. | |
* var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); | |
* var source = new EventSource('/stream'); | |
* jQuery(source).on('message', debounced); | |
* | |
* // Cancel the trailing debounced invocation. | |
* jQuery(window).on('popstate', debounced.cancel); | |
*/ | |
function debounce(func, wait, options) { | |
var lastArgs, | |
lastThis, | |
maxWait, | |
result, | |
timerId, | |
lastCallTime, | |
lastInvokeTime = 0, | |
leading = false, | |
maxing = false, | |
trailing = true; | |
if (typeof func != 'function') { | |
throw new TypeError(FUNC_ERROR_TEXT); | |
} | |
wait = toNumber(wait) || 0; | |
if (isObject(options)) { | |
leading = !!options.leading; | |
maxing = 'maxWait' in options; | |
maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; | |
trailing = 'trailing' in options ? !!options.trailing : trailing; | |
} | |
function invokeFunc(time) { | |
var args = lastArgs, | |
thisArg = lastThis; | |
lastArgs = lastThis = undefined; | |
lastInvokeTime = time; | |
result = func.apply(thisArg, args); | |
return result; | |
} | |
function leadingEdge(time) { | |
// Reset any `maxWait` timer. | |
lastInvokeTime = time; | |
// Start the timer for the trailing edge. | |
timerId = setTimeout(timerExpired, wait); | |
// Invoke the leading edge. | |
return leading ? invokeFunc(time) : result; | |
} | |
function remainingWait(time) { | |
var timeSinceLastCall = time - lastCallTime, | |
timeSinceLastInvoke = time - lastInvokeTime, | |
result = wait - timeSinceLastCall; | |
return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result; | |
} | |
function shouldInvoke(time) { | |
var timeSinceLastCall = time - lastCallTime, | |
timeSinceLastInvoke = time - lastInvokeTime; | |
// Either this is the first call, activity has stopped and we're at the | |
// trailing edge, the system time has gone backwards and we're treating | |
// it as the trailing edge, or we've hit the `maxWait` limit. | |
return (lastCallTime === undefined || (timeSinceLastCall >= wait) || | |
(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); | |
} | |
function timerExpired() { | |
var time = now(); | |
if (shouldInvoke(time)) { | |
return trailingEdge(time); | |
} | |
// Restart the timer. | |
timerId = setTimeout(timerExpired, remainingWait(time)); | |
} | |
function trailingEdge(time) { | |
timerId = undefined; | |
// Only invoke if we have `lastArgs` which means `func` has been | |
// debounced at least once. | |
if (trailing && lastArgs) { | |
return invokeFunc(time); | |
} | |
lastArgs = lastThis = undefined; | |
return result; | |
} | |
function cancel() { | |
if (timerId !== undefined) { | |
clearTimeout(timerId); | |
} | |
lastInvokeTime = 0; | |
lastArgs = lastCallTime = lastThis = timerId = undefined; | |
} | |
function flush() { | |
return timerId === undefined ? result : trailingEdge(now()); | |
} | |
function debounced() { | |
var time = now(), | |
isInvoking = shouldInvoke(time); | |
lastArgs = arguments; | |
lastThis = this; | |
lastCallTime = time; | |
if (isInvoking) { | |
if (timerId === undefined) { | |
return leadingEdge(lastCallTime); | |
} | |
if (maxing) { | |
// Handle invocations in a tight loop. | |
timerId = setTimeout(timerExpired, wait); | |
return invokeFunc(lastCallTime); | |
} | |
} | |
if (timerId === undefined) { | |
timerId = setTimeout(timerExpired, wait); | |
} | |
return result; | |
} | |
debounced.cancel = cancel; | |
debounced.flush = flush; | |
return debounced; | |
} | |
/** | |
* Creates a throttled function that only invokes `func` at most once per | |
* every `wait` milliseconds. The throttled function comes with a `cancel` | |
* method to cancel delayed `func` invocations and a `flush` method to | |
* immediately invoke them. Provide `options` to indicate whether `func` | |
* should be invoked on the leading and/or trailing edge of the `wait` | |
* timeout. The `func` is invoked with the last arguments provided to the | |
* throttled function. Subsequent calls to the throttled function return the | |
* result of the last `func` invocation. | |
* | |
* **Note:** If `leading` and `trailing` options are `true`, `func` is | |
* invoked on the trailing edge of the timeout only if the throttled function | |
* is invoked more than once during the `wait` timeout. | |
* | |
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred | |
* until to the next tick, similar to `setTimeout` with a timeout of `0`. | |
* | |
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) | |
* for details over the differences between `_.throttle` and `_.debounce`. | |
* | |
* @static | |
* @memberOf _ | |
* @since 0.1.0 | |
* @category Function | |
* @param {Function} func The function to throttle. | |
* @param {number} [wait=0] The number of milliseconds to throttle invocations to. | |
* @param {Object} [options={}] The options object. | |
* @param {boolean} [options.leading=true] | |
* Specify invoking on the leading edge of the timeout. | |
* @param {boolean} [options.trailing=true] | |
* Specify invoking on the trailing edge of the timeout. | |
* @returns {Function} Returns the new throttled function. | |
* @example | |
* | |
* // Avoid excessively updating the position while scrolling. | |
* jQuery(window).on('scroll', _.throttle(updatePosition, 100)); | |
* | |
* // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. | |
* var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); | |
* jQuery(element).on('click', throttled); | |
* | |
* // Cancel the trailing throttled invocation. | |
* jQuery(window).on('popstate', throttled.cancel); | |
*/ | |
function throttle(func, wait, options) { | |
var leading = true, | |
trailing = true; | |
if (typeof func != 'function') { | |
throw new TypeError(FUNC_ERROR_TEXT); | |
} | |
if (isObject(options)) { | |
leading = 'leading' in options ? !!options.leading : leading; | |
trailing = 'trailing' in options ? !!options.trailing : trailing; | |
} | |
return debounce(func, wait, { | |
'leading': leading, | |
'maxWait': wait, | |
'trailing': trailing | |
}); | |
} | |
/** | |
* Checks if `value` is the | |
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) | |
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) | |
* | |
* @static | |
* @memberOf _ | |
* @since 0.1.0 | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is an object, else `false`. | |
* @example | |
* | |
* _.isObject({}); | |
* // => true | |
* | |
* _.isObject([1, 2, 3]); | |
* // => true | |
* | |
* _.isObject(_.noop); | |
* // => true | |
* | |
* _.isObject(null); | |
* // => false | |
*/ | |
function isObject(value) { | |
var type = typeof value; | |
return !!value && (type == 'object' || type == 'function'); | |
} | |
/** | |
* Checks if `value` is object-like. A value is object-like if it's not `null` | |
* and has a `typeof` result of "object". | |
* | |
* @static | |
* @memberOf _ | |
* @since 4.0.0 | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is object-like, else `false`. | |
* @example | |
* | |
* _.isObjectLike({}); | |
* // => true | |
* | |
* _.isObjectLike([1, 2, 3]); | |
* // => true | |
* | |
* _.isObjectLike(_.noop); | |
* // => false | |
* | |
* _.isObjectLike(null); | |
* // => false | |
*/ | |
function isObjectLike(value) { | |
return !!value && typeof value == 'object'; | |
} | |
/** | |
* Checks if `value` is classified as a `Symbol` primitive or object. | |
* | |
* @static | |
* @memberOf _ | |
* @since 4.0.0 | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is a symbol, else `false`. | |
* @example | |
* | |
* _.isSymbol(Symbol.iterator); | |
* // => true | |
* | |
* _.isSymbol('abc'); | |
* // => false | |
*/ | |
function isSymbol(value) { | |
return typeof value == 'symbol' || | |
(isObjectLike(value) && objectToString.call(value) == symbolTag); | |
} | |
/** | |
* Converts `value` to a number. | |
* | |
* @static | |
* @memberOf _ | |
* @since 4.0.0 | |
* @category Lang | |
* @param {*} value The value to process. | |
* @returns {number} Returns the number. | |
* @example | |
* | |
* _.toNumber(3.2); | |
* // => 3.2 | |
* | |
* _.toNumber(Number.MIN_VALUE); | |
* // => 5e-324 | |
* | |
* _.toNumber(Infinity); | |
* // => Infinity | |
* | |
* _.toNumber('3.2'); | |
* // => 3.2 | |
*/ | |
function toNumber(value) { | |
if (typeof value == 'number') { | |
return value; | |
} | |
if (isSymbol(value)) { | |
return NAN; | |
} | |
if (isObject(value)) { | |
var other = typeof value.valueOf == 'function' ? value.valueOf() : value; | |
value = isObject(other) ? (other + '') : other; | |
} | |
if (typeof value != 'string') { | |
return value === 0 ? value : +value; | |
} | |
value = value.replace(reTrim, ''); | |
var isBinary = reIsBinary.test(value); | |
return (isBinary || reIsOctal.test(value)) | |
? freeParseInt(value.slice(2), isBinary ? 2 : 8) | |
: (reIsBadHex.test(value) ? NAN : +value); | |
} | |
module.exports = throttle; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],45:[function(require,module,exports){ | |
var wildcard = require('wildcard'); | |
var reMimePartSplit = /[\/\+\.]/; | |
/** | |
# mime-match | |
A simple function to checker whether a target mime type matches a mime-type | |
pattern (e.g. image/jpeg matches image/jpeg OR image/*). | |
## Example Usage | |
<<< example.js | |
**/ | |
module.exports = function(target, pattern) { | |
function test(pattern) { | |
var result = wildcard(pattern, target, reMimePartSplit); | |
// ensure that we have a valid mime type (should have two parts) | |
return result && result.length >= 2; | |
} | |
return pattern ? test(pattern.split(';')[0]) : test; | |
}; | |
},{"wildcard":78}],46:[function(require,module,exports){ | |
'use strict'; | |
var range; // Create a range object for efficently rendering strings to elements. | |
var NS_XHTML = 'http://www.w3.org/1999/xhtml'; | |
var doc = typeof document === 'undefined' ? undefined : document; | |
var testEl = doc ? | |
doc.body || doc.createElement('div') : | |
{}; | |
// Fixes <https://github.com/patrick-steele-idem/morphdom/issues/32> | |
// (IE7+ support) <=IE7 does not support el.hasAttribute(name) | |
var actualHasAttributeNS; | |
if (testEl.hasAttributeNS) { | |
actualHasAttributeNS = function(el, namespaceURI, name) { | |
return el.hasAttributeNS(namespaceURI, name); | |
}; | |
} else if (testEl.hasAttribute) { | |
actualHasAttributeNS = function(el, namespaceURI, name) { | |
return el.hasAttribute(name); | |
}; | |
} else { | |
actualHasAttributeNS = function(el, namespaceURI, name) { | |
return el.getAttributeNode(namespaceURI, name) != null; | |
}; | |
} | |
var hasAttributeNS = actualHasAttributeNS; | |
function toElement(str) { | |
if (!range && doc.createRange) { | |
range = doc.createRange(); | |
range.selectNode(doc.body); | |
} | |
var fragment; | |
if (range && range.createContextualFragment) { | |
fragment = range.createContextualFragment(str); | |
} else { | |
fragment = doc.createElement('body'); | |
fragment.innerHTML = str; | |
} | |
return fragment.childNodes[0]; | |
} | |
/** | |
* Returns true if two node's names are the same. | |
* | |
* NOTE: We don't bother checking `namespaceURI` because you will never find two HTML elements with the same | |
* nodeName and different namespace URIs. | |
* | |
* @param {Element} a | |
* @param {Element} b The target element | |
* @return {boolean} | |
*/ | |
function compareNodeNames(fromEl, toEl) { | |
var fromNodeName = fromEl.nodeName; | |
var toNodeName = toEl.nodeName; | |
if (fromNodeName === toNodeName) { | |
return true; | |
} | |
if (toEl.actualize && | |
fromNodeName.charCodeAt(0) < 91 && /* from tag name is upper case */ | |
toNodeName.charCodeAt(0) > 90 /* target tag name is lower case */) { | |
// If the target element is a virtual DOM node then we may need to normalize the tag name | |
// before comparing. Normal HTML elements that are in the "http://www.w3.org/1999/xhtml" | |
// are converted to upper case | |
return fromNodeName === toNodeName.toUpperCase(); | |
} else { | |
return false; | |
} | |
} | |
/** | |
* Create an element, optionally with a known namespace URI. | |
* | |
* @param {string} name the element name, e.g. 'div' or 'svg' | |
* @param {string} [namespaceURI] the element's namespace URI, i.e. the value of | |
* its `xmlns` attribute or its inferred namespace. | |
* | |
* @return {Element} | |
*/ | |
function createElementNS(name, namespaceURI) { | |
return !namespaceURI || namespaceURI === NS_XHTML ? | |
doc.createElement(name) : | |
doc.createElementNS(namespaceURI, name); | |
} | |
/** | |
* Copies the children of one DOM element to another DOM element | |
*/ | |
function moveChildren(fromEl, toEl) { | |
var curChild = fromEl.firstChild; | |
while (curChild) { | |
var nextChild = curChild.nextSibling; | |
toEl.appendChild(curChild); | |
curChild = nextChild; | |
} | |
return toEl; | |
} | |
function morphAttrs(fromNode, toNode) { | |
var attrs = toNode.attributes; | |
var i; | |
var attr; | |
var attrName; | |
var attrNamespaceURI; | |
var attrValue; | |
var fromValue; | |
for (i = attrs.length - 1; i >= 0; --i) { | |
attr = attrs[i]; | |
attrName = attr.name; | |
attrNamespaceURI = attr.namespaceURI; | |
attrValue = attr.value; | |
if (attrNamespaceURI) { | |
attrName = attr.localName || attrName; | |
fromValue = fromNode.getAttributeNS(attrNamespaceURI, attrName); | |
if (fromValue !== attrValue) { | |
fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue); | |
} | |
} else { | |
fromValue = fromNode.getAttribute(attrName); | |
if (fromValue !== attrValue) { | |
fromNode.setAttribute(attrName, attrValue); | |
} | |
} | |
} | |
// Remove any extra attributes found on the original DOM element that | |
// weren't found on the target element. | |
attrs = fromNode.attributes; | |
for (i = attrs.length - 1; i >= 0; --i) { | |
attr = attrs[i]; | |
if (attr.specified !== false) { | |
attrName = attr.name; | |
attrNamespaceURI = attr.namespaceURI; | |
if (attrNamespaceURI) { | |
attrName = attr.localName || attrName; | |
if (!hasAttributeNS(toNode, attrNamespaceURI, attrName)) { | |
fromNode.removeAttributeNS(attrNamespaceURI, attrName); | |
} | |
} else { | |
if (!hasAttributeNS(toNode, null, attrName)) { | |
fromNode.removeAttribute(attrName); | |
} | |
} | |
} | |
} | |
} | |
function syncBooleanAttrProp(fromEl, toEl, name) { | |
if (fromEl[name] !== toEl[name]) { | |
fromEl[name] = toEl[name]; | |
if (fromEl[name]) { | |
fromEl.setAttribute(name, ''); | |
} else { | |
fromEl.removeAttribute(name, ''); | |
} | |
} | |
} | |
var specialElHandlers = { | |
/** | |
* Needed for IE. Apparently IE doesn't think that "selected" is an | |
* attribute when reading over the attributes using selectEl.attributes | |
*/ | |
OPTION: function(fromEl, toEl) { | |
syncBooleanAttrProp(fromEl, toEl, 'selected'); | |
}, | |
/** | |
* The "value" attribute is special for the <input> element since it sets | |
* the initial value. Changing the "value" attribute without changing the | |
* "value" property will have no effect since it is only used to the set the | |
* initial value. Similar for the "checked" attribute, and "disabled". | |
*/ | |
INPUT: function(fromEl, toEl) { | |
syncBooleanAttrProp(fromEl, toEl, 'checked'); | |
syncBooleanAttrProp(fromEl, toEl, 'disabled'); | |
if (fromEl.value !== toEl.value) { | |
fromEl.value = toEl.value; | |
} | |
if (!hasAttributeNS(toEl, null, 'value')) { | |
fromEl.removeAttribute('value'); | |
} | |
}, | |
TEXTAREA: function(fromEl, toEl) { | |
var newValue = toEl.value; | |
if (fromEl.value !== newValue) { | |
fromEl.value = newValue; | |
} | |
var firstChild = fromEl.firstChild; | |
if (firstChild) { | |
// Needed for IE. Apparently IE sets the placeholder as the | |
// node value and vise versa. This ignores an empty update. | |
var oldValue = firstChild.nodeValue; | |
if (oldValue == newValue || (!newValue && oldValue == fromEl.placeholder)) { | |
return; | |
} | |
firstChild.nodeValue = newValue; | |
} | |
}, | |
SELECT: function(fromEl, toEl) { | |
if (!hasAttributeNS(toEl, null, 'multiple')) { | |
var selectedIndex = -1; | |
var i = 0; | |
var curChild = toEl.firstChild; | |
while(curChild) { | |
var nodeName = curChild.nodeName; | |
if (nodeName && nodeName.toUpperCase() === 'OPTION') { | |
if (hasAttributeNS(curChild, null, 'selected')) { | |
selectedIndex = i; | |
break; | |
} | |
i++; | |
} | |
curChild = curChild.nextSibling; | |
} | |
fromEl.selectedIndex = i; | |
} | |
} | |
}; | |
var ELEMENT_NODE = 1; | |
var TEXT_NODE = 3; | |
var COMMENT_NODE = 8; | |
function noop() {} | |
function defaultGetNodeKey(node) { | |
return node.id; | |
} | |
function morphdomFactory(morphAttrs) { | |
return function morphdom(fromNode, toNode, options) { | |
if (!options) { | |
options = {}; | |
} | |
if (typeof toNode === 'string') { | |
if (fromNode.nodeName === '#document' || fromNode.nodeName === 'HTML') { | |
var toNodeHtml = toNode; | |
toNode = doc.createElement('html'); | |
toNode.innerHTML = toNodeHtml; | |
} else { | |
toNode = toElement(toNode); | |
} | |
} | |
var getNodeKey = options.getNodeKey || defaultGetNodeKey; | |
var onBeforeNodeAdded = options.onBeforeNodeAdded || noop; | |
var onNodeAdded = options.onNodeAdded || noop; | |
var onBeforeElUpdated = options.onBeforeElUpdated || noop; | |
var onElUpdated = options.onElUpdated || noop; | |
var onBeforeNodeDiscarded = options.onBeforeNodeDiscarded || noop; | |
var onNodeDiscarded = options.onNodeDiscarded || noop; | |
var onBeforeElChildrenUpdated = options.onBeforeElChildrenUpdated || noop; | |
var childrenOnly = options.childrenOnly === true; | |
// This object is used as a lookup to quickly find all keyed elements in the original DOM tree. | |
var fromNodesLookup = {}; | |
var keyedRemovalList; | |
function addKeyedRemoval(key) { | |
if (keyedRemovalList) { | |
keyedRemovalList.push(key); | |
} else { | |
keyedRemovalList = [key]; | |
} | |
} | |
function walkDiscardedChildNodes(node, skipKeyedNodes) { | |
if (node.nodeType === ELEMENT_NODE) { | |
var curChild = node.firstChild; | |
while (curChild) { | |
var key = undefined; | |
if (skipKeyedNodes && (key = getNodeKey(curChild))) { | |
// If we are skipping keyed nodes then we add the key | |
// to a list so that it can be handled at the very end. | |
addKeyedRemoval(key); | |
} else { | |
// Only report the node as discarded if it is not keyed. We do this because | |
// at the end we loop through all keyed elements that were unmatched | |
// and then discard them in one final pass. | |
onNodeDiscarded(curChild); | |
if (curChild.firstChild) { | |
walkDiscardedChildNodes(curChild, skipKeyedNodes); | |
} | |
} | |
curChild = curChild.nextSibling; | |
} | |
} | |
} | |
/** | |
* Removes a DOM node out of the original DOM | |
* | |
* @param {Node} node The node to remove | |
* @param {Node} parentNode The nodes parent | |
* @param {Boolean} skipKeyedNodes If true then elements with keys will be skipped and not discarded. | |
* @return {undefined} | |
*/ | |
function removeNode(node, parentNode, skipKeyedNodes) { | |
if (onBeforeNodeDiscarded(node) === false) { | |
return; | |
} | |
if (parentNode) { | |
parentNode.removeChild(node); | |
} | |
onNodeDiscarded(node); | |
walkDiscardedChildNodes(node, skipKeyedNodes); | |
} | |
// // TreeWalker implementation is no faster, but keeping this around in case this changes in the future | |
// function indexTree(root) { | |
// var treeWalker = document.createTreeWalker( | |
// root, | |
// NodeFilter.SHOW_ELEMENT); | |
// | |
// var el; | |
// while((el = treeWalker.nextNode())) { | |
// var key = getNodeKey(el); | |
// if (key) { | |
// fromNodesLookup[key] = el; | |
// } | |
// } | |
// } | |
// // NodeIterator implementation is no faster, but keeping this around in case this changes in the future | |
// | |
// function indexTree(node) { | |
// var nodeIterator = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT); | |
// var el; | |
// while((el = nodeIterator.nextNode())) { | |
// var key = getNodeKey(el); | |
// if (key) { | |
// fromNodesLookup[key] = el; | |
// } | |
// } | |
// } | |
function indexTree(node) { | |
if (node.nodeType === ELEMENT_NODE) { | |
var curChild = node.firstChild; | |
while (curChild) { | |
var key = getNodeKey(curChild); | |
if (key) { | |
fromNodesLookup[key] = curChild; | |
} | |
// Walk recursively | |
indexTree(curChild); | |
curChild = curChild.nextSibling; | |
} | |
} | |
} | |
indexTree(fromNode); | |
function handleNodeAdded(el) { | |
onNodeAdded(el); | |
var curChild = el.firstChild; | |
while (curChild) { | |
var nextSibling = curChild.nextSibling; | |
var key = getNodeKey(curChild); | |
if (key) { | |
var unmatchedFromEl = fromNodesLookup[key]; | |
if (unmatchedFromEl && compareNodeNames(curChild, unmatchedFromEl)) { | |
curChild.parentNode.replaceChild(unmatchedFromEl, curChild); | |
morphEl(unmatchedFromEl, curChild); | |
} | |
} | |
handleNodeAdded(curChild); | |
curChild = nextSibling; | |
} | |
} | |
function morphEl(fromEl, toEl, childrenOnly) { | |
var toElKey = getNodeKey(toEl); | |
var curFromNodeKey; | |
if (toElKey) { | |
// If an element with an ID is being morphed then it is will be in the final | |
// DOM so clear it out of the saved elements collection | |
delete fromNodesLookup[toElKey]; | |
} | |
if (toNode.isSameNode && toNode.isSameNode(fromNode)) { | |
return; | |
} | |
if (!childrenOnly) { | |
if (onBeforeElUpdated(fromEl, toEl) === false) { | |
return; | |
} | |
morphAttrs(fromEl, toEl); | |
onElUpdated(fromEl); | |
if (onBeforeElChildrenUpdated(fromEl, toEl) === false) { | |
return; | |
} | |
} | |
if (fromEl.nodeName !== 'TEXTAREA') { | |
var curToNodeChild = toEl.firstChild; | |
var curFromNodeChild = fromEl.firstChild; | |
var curToNodeKey; | |
var fromNextSibling; | |
var toNextSibling; | |
var matchingFromEl; | |
outer: while (curToNodeChild) { | |
toNextSibling = curToNodeChild.nextSibling; | |
curToNodeKey = getNodeKey(curToNodeChild); | |
while (curFromNodeChild) { | |
fromNextSibling = curFromNodeChild.nextSibling; | |
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) { | |
curToNodeChild = toNextSibling; | |
curFromNodeChild = fromNextSibling; | |
continue outer; | |
} | |
curFromNodeKey = getNodeKey(curFromNodeChild); | |
var curFromNodeType = curFromNodeChild.nodeType; | |
var isCompatible = undefined; | |
if (curFromNodeType === curToNodeChild.nodeType) { | |
if (curFromNodeType === ELEMENT_NODE) { | |
// Both nodes being compared are Element nodes | |
if (curToNodeKey) { | |
// The target node has a key so we want to match it up with the correct element | |
// in the original DOM tree | |
if (curToNodeKey !== curFromNodeKey) { | |
// The current element in the original DOM tree does not have a matching key so | |
// let's check our lookup to see if there is a matching element in the original | |
// DOM tree | |
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) { | |
if (curFromNodeChild.nextSibling === matchingFromEl) { | |
// Special case for single element removals. To avoid removing the original | |
// DOM node out of the tree (since that can break CSS transitions, etc.), | |
// we will instead discard the current node and wait until the next | |
// iteration to properly match up the keyed target element with its matching | |
// element in the original tree | |
isCompatible = false; | |
} else { | |
// We found a matching keyed element somewhere in the original DOM tree. | |
// Let's moving the original DOM node into the current position and morph | |
// it. | |
// NOTE: We use insertBefore instead of replaceChild because we want to go through | |
// the `removeNode()` function for the node that is being discarded so that | |
// all lifecycle hooks are correctly invoked | |
fromEl.insertBefore(matchingFromEl, curFromNodeChild); | |
fromNextSibling = curFromNodeChild.nextSibling; | |
if (curFromNodeKey) { | |
// Since the node is keyed it might be matched up later so we defer | |
// the actual removal to later | |
addKeyedRemoval(curFromNodeKey); | |
} else { | |
// NOTE: we skip nested keyed nodes from being removed since there is | |
// still a chance they will be matched up later | |
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */); | |
} | |
curFromNodeChild = matchingFromEl; | |
} | |
} else { | |
// The nodes are not compatible since the "to" node has a key and there | |
// is no matching keyed node in the source tree | |
isCompatible = false; | |
} | |
} | |
} else if (curFromNodeKey) { | |
// The original has a key | |
isCompatible = false; | |
} | |
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild); | |
if (isCompatible) { | |
// We found compatible DOM elements so transform | |
// the current "from" node to match the current | |
// target DOM node. | |
morphEl(curFromNodeChild, curToNodeChild); | |
} | |
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) { | |
// Both nodes being compared are Text or Comment nodes | |
isCompatible = true; | |
// Simply update nodeValue on the original node to | |
// change the text value | |
curFromNodeChild.nodeValue = curToNodeChild.nodeValue; | |
} | |
} | |
if (isCompatible) { | |
// Advance both the "to" child and the "from" child since we found a match | |
curToNodeChild = toNextSibling; | |
curFromNodeChild = fromNextSibling; | |
continue outer; | |
} | |
// No compatible match so remove the old node from the DOM and continue trying to find a | |
// match in the original DOM. However, we only do this if the from node is not keyed | |
// since it is possible that a keyed node might match up with a node somewhere else in the | |
// target tree and we don't want to discard it just yet since it still might find a | |
// home in the final DOM tree. After everything is done we will remove any keyed nodes | |
// that didn't find a home | |
if (curFromNodeKey) { | |
// Since the node is keyed it might be matched up later so we defer | |
// the actual removal to later | |
addKeyedRemoval(curFromNodeKey); | |
} else { | |
// NOTE: we skip nested keyed nodes from being removed since there is | |
// still a chance they will be matched up later | |
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */); | |
} | |
curFromNodeChild = fromNextSibling; | |
} | |
// If we got this far then we did not find a candidate match for | |
// our "to node" and we exhausted all of the children "from" | |
// nodes. Therefore, we will just append the current "to" node | |
// to the end | |
if (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) { | |
fromEl.appendChild(matchingFromEl); | |
morphEl(matchingFromEl, curToNodeChild); | |
} else { | |
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild); | |
if (onBeforeNodeAddedResult !== false) { | |
if (onBeforeNodeAddedResult) { | |
curToNodeChild = onBeforeNodeAddedResult; | |
} | |
if (curToNodeChild.actualize) { | |
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc); | |
} | |
fromEl.appendChild(curToNodeChild); | |
handleNodeAdded(curToNodeChild); | |
} | |
} | |
curToNodeChild = toNextSibling; | |
curFromNodeChild = fromNextSibling; | |
} | |
// We have processed all of the "to nodes". If curFromNodeChild is | |
// non-null then we still have some from nodes left over that need | |
// to be removed | |
while (curFromNodeChild) { | |
fromNextSibling = curFromNodeChild.nextSibling; | |
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) { | |
// Since the node is keyed it might be matched up later so we defer | |
// the actual removal to later | |
addKeyedRemoval(curFromNodeKey); | |
} else { | |
// NOTE: we skip nested keyed nodes from being removed since there is | |
// still a chance they will be matched up later | |
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */); | |
} | |
curFromNodeChild = fromNextSibling; | |
} | |
} | |
var specialElHandler = specialElHandlers[fromEl.nodeName]; | |
if (specialElHandler) { | |
specialElHandler(fromEl, toEl); | |
} | |
} // END: morphEl(...) | |
var morphedNode = fromNode; | |
var morphedNodeType = morphedNode.nodeType; | |
var toNodeType = toNode.nodeType; | |
if (!childrenOnly) { | |
// Handle the case where we are given two DOM nodes that are not | |
// compatible (e.g. <div> --> <span> or <div> --> TEXT) | |
if (morphedNodeType === ELEMENT_NODE) { | |
if (toNodeType === ELEMENT_NODE) { | |
if (!compareNodeNames(fromNode, toNode)) { | |
onNodeDiscarded(fromNode); | |
morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI)); | |
} | |
} else { | |
// Going from an element node to a text node | |
morphedNode = toNode; | |
} | |
} else if (morphedNodeType === TEXT_NODE || morphedNodeType === COMMENT_NODE) { // Text or comment node | |
if (toNodeType === morphedNodeType) { | |
morphedNode.nodeValue = toNode.nodeValue; | |
return morphedNode; | |
} else { | |
// Text node to something else | |
morphedNode = toNode; | |
} | |
} | |
} | |
if (morphedNode === toNode) { | |
// The "to node" was not compatible with the "from node" so we had to | |
// toss out the "from node" and use the "to node" | |
onNodeDiscarded(fromNode); | |
} else { | |
morphEl(morphedNode, toNode, childrenOnly); | |
// We now need to loop over any keyed nodes that might need to be | |
// removed. We only do the removal if we know that the keyed node | |
// never found a match. When a keyed node is matched up we remove | |
// it out of fromNodesLookup and we use fromNodesLookup to determine | |
// if a keyed node has been matched up or not | |
if (keyedRemovalList) { | |
for (var i=0, len=keyedRemovalList.length; i<len; i++) { | |
var elToRemove = fromNodesLookup[keyedRemovalList[i]]; | |
if (elToRemove) { | |
removeNode(elToRemove, elToRemove.parentNode, false); | |
} | |
} | |
} | |
} | |
if (!childrenOnly && morphedNode !== fromNode && fromNode.parentNode) { | |
if (morphedNode.actualize) { | |
morphedNode = morphedNode.actualize(fromNode.ownerDocument || doc); | |
} | |
// If we had to swap out the from node with a new node because the old | |
// node was not compatible with the target node then we need to | |
// replace the old DOM node in the original DOM tree. This is only | |
// possible if the original DOM node was part of a DOM tree which | |
// we know is the case if it has a parent node. | |
fromNode.parentNode.replaceChild(morphedNode, fromNode); | |
} | |
return morphedNode; | |
}; | |
} | |
var morphdom = morphdomFactory(morphAttrs); | |
module.exports = morphdom; | |
},{}],47:[function(require,module,exports){ | |
/** | |
* Helpers. | |
*/ | |
var s = 1000 | |
var m = s * 60 | |
var h = m * 60 | |
var d = h * 24 | |
var y = d * 365.25 | |
/** | |
* Parse or format the given `val`. | |
* | |
* Options: | |
* | |
* - `long` verbose formatting [false] | |
* | |
* @param {String|Number} val | |
* @param {Object} [options] | |
* @throws {Error} throw an error if val is not a non-empty string or a number | |
* @return {String|Number} | |
* @api public | |
*/ | |
module.exports = function (val, options) { | |
options = options || {} | |
var type = typeof val | |
if (type === 'string' && val.length > 0) { | |
return parse(val) | |
} else if (type === 'number' && isNaN(val) === false) { | |
return options.long ? | |
fmtLong(val) : | |
fmtShort(val) | |
} | |
throw new Error('val is not a non-empty string or a valid number. val=' + JSON.stringify(val)) | |
} | |
/** | |
* Parse the given `str` and return milliseconds. | |
* | |
* @param {String} str | |
* @return {Number} | |
* @api private | |
*/ | |
function parse(str) { | |
str = String(str) | |
if (str.length > 10000) { | |
return | |
} | |
var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str) | |
if (!match) { | |
return | |
} | |
var n = parseFloat(match[1]) | |
var type = (match[2] || 'ms').toLowerCase() | |
switch (type) { | |
case 'years': | |
case 'year': | |
case 'yrs': | |
case 'yr': | |
case 'y': | |
return n * y | |
case 'days': | |
case 'day': | |
case 'd': | |
return n * d | |
case 'hours': | |
case 'hour': | |
case 'hrs': | |
case 'hr': | |
case 'h': | |
return n * h | |
case 'minutes': | |
case 'minute': | |
case 'mins': | |
case 'min': | |
case 'm': | |
return n * m | |
case 'seconds': | |
case 'second': | |
case 'secs': | |
case 'sec': | |
case 's': | |
return n * s | |
case 'milliseconds': | |
case 'millisecond': | |
case 'msecs': | |
case 'msec': | |
case 'ms': | |
return n | |
default: | |
return undefined | |
} | |
} | |
/** | |
* Short format for `ms`. | |
* | |
* @param {Number} ms | |
* @return {String} | |
* @api private | |
*/ | |
function fmtShort(ms) { | |
if (ms >= d) { | |
return Math.round(ms / d) + 'd' | |
} | |
if (ms >= h) { | |
return Math.round(ms / h) + 'h' | |
} | |
if (ms >= m) { | |
return Math.round(ms / m) + 'm' | |
} | |
if (ms >= s) { | |
return Math.round(ms / s) + 's' | |
} | |
return ms + 'ms' | |
} | |
/** | |
* Long format for `ms`. | |
* | |
* @param {Number} ms | |
* @return {String} | |
* @api private | |
*/ | |
function fmtLong(ms) { | |
return plural(ms, d, 'day') || | |
plural(ms, h, 'hour') || | |
plural(ms, m, 'minute') || | |
plural(ms, s, 'second') || | |
ms + ' ms' | |
} | |
/** | |
* Pluralization helper. | |
*/ | |
function plural(ms, n, name) { | |
if (ms < n) { | |
return | |
} | |
if (ms < n * 1.5) { | |
return Math.floor(ms / n) + ' ' + name | |
} | |
return Math.ceil(ms / n) + ' ' + name + 's' | |
} | |
},{}],48:[function(require,module,exports){ | |
/** | |
* Create an event emitter with namespaces | |
* @name createNamespaceEmitter | |
* @example | |
* var emitter = require('./index')() | |
* | |
* emitter.on('*', function () { | |
* console.log('all events emitted', this.event) | |
* }) | |
* | |
* emitter.on('example', function () { | |
* console.log('example event emitted') | |
* }) | |
*/ | |
module.exports = function createNamespaceEmitter () { | |
var emitter = { _fns: {} } | |
/** | |
* Emit an event. Optionally namespace the event. Handlers are fired in the order in which they were added with exact matches taking precedence. Separate the namespace and event with a `:` | |
* @name emit | |
* @param {String} event – the name of the event, with optional namespace | |
* @param {...*} data – data variables that will be passed as arguments to the event listener | |
* @example | |
* emitter.emit('example') | |
* emitter.emit('demo:test') | |
* emitter.emit('data', { example: true}, 'a string', 1) | |
*/ | |
emitter.emit = function emit (event) { | |
var args = [].slice.call(arguments, 1) | |
var namespaced = namespaces(event) | |
if (this._fns[event]) emitAll(event, this._fns[event], args) | |
if (namespaced) emitAll(event, namespaced, args) | |
} | |
/** | |
* Create en event listener. | |
* @name on | |
* @param {String} event | |
* @param {Function} fn | |
* @example | |
* emitter.on('example', function () {}) | |
* emitter.on('demo', function () {}) | |
*/ | |
emitter.on = function on (event, fn) { | |
if (typeof fn !== 'function') { throw new Error('callback required') } | |
(this._fns[event] = this._fns[event] || []).push(fn) | |
} | |
/** | |
* Create en event listener that fires once. | |
* @name once | |
* @param {String} event | |
* @param {Function} fn | |
* @example | |
* emitter.once('example', function () {}) | |
* emitter.once('demo', function () {}) | |
*/ | |
emitter.once = function once (event, fn) { | |
function one () { | |
fn.apply(this, arguments) | |
emitter.off(event, one) | |
} | |
this.on(event, one) | |
} | |
/** | |
* Stop listening to an event. Stop all listeners on an event by only passing the event name. Stop a single listener by passing that event handler as a callback. | |
* You must be explicit about what will be unsubscribed: `emitter.off('demo')` will unsubscribe an `emitter.on('demo')` listener, | |
* `emitter.off('demo:example')` will unsubscribe an `emitter.on('demo:example')` listener | |
* @name off | |
* @param {String} event | |
* @param {Function} [fn] – the specific handler | |
* @example | |
* emitter.off('example') | |
* emitter.off('demo', function () {}) | |
*/ | |
emitter.off = function off (event, fn) { | |
var keep = [] | |
if (event && fn) { | |
var fns = this._fns[event] | |
for (var i = 0; i < fns.length; i++) { | |
if (fns[i] !== fn) { | |
keep.push(fns[i]) | |
} | |
} | |
} | |
keep.length ? this._fns[event] = keep : delete this._fns[event] | |
} | |
function namespaces (e) { | |
var out = [] | |
var args = e.split(':') | |
var fns = emitter._fns | |
Object.keys(fns).forEach(function (key) { | |
if (key === '*') out = out.concat(fns[key]) | |
if (args.length === 2 && args[0] === key) out = out.concat(fns[key]) | |
}) | |
return out | |
} | |
function emitAll (e, fns, args) { | |
for (var i = 0; i < fns.length; i++) { | |
if (!fns[i]) break | |
fns[i].event = e | |
fns[i].apply(fns[i], args) | |
} | |
} | |
return emitter | |
} | |
},{}],49:[function(require,module,exports){ | |
'use strict' | |
var assert = require('assert') | |
module.exports = nanoraf | |
// Only call RAF when needed | |
// (fn, fn?) -> fn | |
function nanoraf (render, raf) { | |
assert.equal(typeof render, 'function', 'nanoraf: render should be a function') | |
assert.ok(typeof raf === 'function' || typeof raf === 'undefined', 'nanoraf: raf should be a function or undefined') | |
if (!raf) raf = window.requestAnimationFrame | |
var redrawScheduled = false | |
var args = null | |
return function frame () { | |
if (args === null && !redrawScheduled) { | |
redrawScheduled = true | |
raf(function redraw () { | |
redrawScheduled = false | |
var length = args.length | |
var _args = new Array(length) | |
for (var i = 0; i < length; i++) _args[i] = args[i] | |
render.apply(render, _args) | |
args = null | |
}) | |
} | |
args = arguments | |
} | |
} | |
},{"assert":1}],50:[function(require,module,exports){ | |
/* global MutationObserver */ | |
var document = require('global/document') | |
var window = require('global/window') | |
var watch = Object.create(null) | |
var KEY_ID = 'onloadid' + (new Date() % 9e6).toString(36) | |
var KEY_ATTR = 'data-' + KEY_ID | |
var INDEX = 0 | |
if (window && window.MutationObserver) { | |
var observer = new MutationObserver(function (mutations) { | |
if (Object.keys(watch).length < 1) return | |
for (var i = 0; i < mutations.length; i++) { | |
if (mutations[i].attributeName === KEY_ATTR) { | |
eachAttr(mutations[i], turnon, turnoff) | |
continue | |
} | |
eachMutation(mutations[i].removedNodes, turnoff) | |
eachMutation(mutations[i].addedNodes, turnon) | |
} | |
}) | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true, | |
attributes: true, | |
attributeOldValue: true, | |
attributeFilter: [KEY_ATTR] | |
}) | |
} | |
module.exports = function onload (el, on, off, caller) { | |
on = on || function () {} | |
off = off || function () {} | |
el.setAttribute(KEY_ATTR, 'o' + INDEX) | |
watch['o' + INDEX] = [on, off, 0, caller || onload.caller] | |
INDEX += 1 | |
return el | |
} | |
function turnon (index, el) { | |
if (watch[index][0] && watch[index][2] === 0) { | |
watch[index][0](el) | |
watch[index][2] = 1 | |
} | |
} | |
function turnoff (index, el) { | |
if (watch[index][1] && watch[index][2] === 1) { | |
watch[index][1](el) | |
watch[index][2] = 0 | |
} | |
} | |
function eachAttr (mutation, on, off) { | |
var newValue = mutation.target.getAttribute(KEY_ATTR) | |
if (sameOrigin(mutation.oldValue, newValue)) { | |
watch[newValue] = watch[mutation.oldValue] | |
return | |
} | |
if (watch[mutation.oldValue]) { | |
off(mutation.oldValue, mutation.target) | |
} | |
if (watch[newValue]) { | |
on(newValue, mutation.target) | |
} | |
} | |
function sameOrigin (oldValue, newValue) { | |
if (!oldValue || !newValue) return false | |
return watch[oldValue][3] === watch[newValue][3] | |
} | |
function eachMutation (nodes, fn) { | |
var keys = Object.keys(watch) | |
for (var i = 0; i < nodes.length; i++) { | |
if (nodes[i] && nodes[i].getAttribute && nodes[i].getAttribute(KEY_ATTR)) { | |
var onloadid = nodes[i].getAttribute(KEY_ATTR) | |
keys.forEach(function (k) { | |
if (onloadid === k) { | |
fn(k, nodes[i]) | |
} | |
}) | |
} | |
if (nodes[i].childNodes.length > 0) { | |
eachMutation(nodes[i].childNodes, fn) | |
} | |
} | |
} | |
},{"global/document":36,"global/window":37}],51:[function(require,module,exports){ | |
(function (global){ | |
/** | |
* JSON parse. | |
* | |
* @see Based on jQuery#parseJSON (MIT) and JSON2 | |
* @api private | |
*/ | |
var rvalidchars = /^[\],:{}\s]*$/; | |
var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; | |
var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; | |
var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g; | |
var rtrimLeft = /^\s+/; | |
var rtrimRight = /\s+$/; | |
module.exports = function parsejson(data) { | |
if ('string' != typeof data || !data) { | |
return null; | |
} | |
data = data.replace(rtrimLeft, '').replace(rtrimRight, ''); | |
// Attempt to parse using the native JSON parser first | |
if (global.JSON && JSON.parse) { | |
return JSON.parse(data); | |
} | |
if (rvalidchars.test(data.replace(rvalidescape, '@') | |
.replace(rvalidtokens, ']') | |
.replace(rvalidbraces, ''))) { | |
return (new Function('return ' + data))(); | |
} | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],52:[function(require,module,exports){ | |
/** | |
* Compiles a querystring | |
* Returns string representation of the object | |
* | |
* @param {Object} | |
* @api private | |
*/ | |
exports.encode = function (obj) { | |
var str = ''; | |
for (var i in obj) { | |
if (obj.hasOwnProperty(i)) { | |
if (str.length) str += '&'; | |
str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]); | |
} | |
} | |
return str; | |
}; | |
/** | |
* Parses a simple querystring into an object | |
* | |
* @param {String} qs | |
* @api private | |
*/ | |
exports.decode = function(qs){ | |
var qry = {}; | |
var pairs = qs.split('&'); | |
for (var i = 0, l = pairs.length; i < l; i++) { | |
var pair = pairs[i].split('='); | |
qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); | |
} | |
return qry; | |
}; | |
},{}],53:[function(require,module,exports){ | |
/** | |
* Parses an URI | |
* | |
* @author Steven Levithan <stevenlevithan.com> (MIT license) | |
* @api private | |
*/ | |
var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; | |
var parts = [ | |
'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor' | |
]; | |
module.exports = function parseuri(str) { | |
var src = str, | |
b = str.indexOf('['), | |
e = str.indexOf(']'); | |
if (b != -1 && e != -1) { | |
str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length); | |
} | |
var m = re.exec(str || ''), | |
uri = {}, | |
i = 14; | |
while (i--) { | |
uri[parts[i]] = m[i] || ''; | |
} | |
if (b != -1 && e != -1) { | |
uri.source = src; | |
uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':'); | |
uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':'); | |
uri.ipv6uri = true; | |
} | |
return uri; | |
}; | |
},{}],54:[function(require,module,exports){ | |
module.exports = prettierBytes | |
function prettierBytes (num) { | |
if (typeof num !== 'number' || isNaN(num)) { | |
throw new TypeError('Expected a number, got ' + typeof num) | |
} | |
var neg = num < 0 | |
var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] | |
if (neg) { | |
num = -num | |
} | |
if (num < 1) { | |
return (neg ? '-' : '') + num + ' B' | |
} | |
var exponent = Math.min(Math.floor(Math.log(num) / Math.log(1000)), units.length - 1) | |
num = Number(num / Math.pow(1000, exponent)) | |
var unit = units[exponent] | |
if (num >= 10 || num % 1 === 0) { | |
// Do not show decimals when the number is two-digit, or if the number has no | |
// decimal component. | |
return (neg ? '-' : '') + num.toFixed(0) + ' ' + unit | |
} else { | |
return (neg ? '-' : '') + num.toFixed(1) + ' ' + unit | |
} | |
} | |
},{}],55:[function(require,module,exports){ | |
'use strict'; | |
var has = Object.prototype.hasOwnProperty; | |
/** | |
* Decode a URI encoded string. | |
* | |
* @param {String} input The URI encoded string. | |
* @returns {String} The decoded string. | |
* @api private | |
*/ | |
function decode(input) { | |
return decodeURIComponent(input.replace(/\+/g, ' ')); | |
} | |
/** | |
* Simple query string parser. | |
* | |
* @param {String} query The query string that needs to be parsed. | |
* @returns {Object} | |
* @api public | |
*/ | |
function querystring(query) { | |
var parser = /([^=?&]+)=?([^&]*)/g | |
, result = {} | |
, part; | |
// | |
// Little nifty parsing hack, leverage the fact that RegExp.exec increments | |
// the lastIndex property so we can continue executing this loop until we've | |
// parsed all results. | |
// | |
for (; | |
part = parser.exec(query); | |
result[decode(part[1])] = decode(part[2]) | |
); | |
return result; | |
} | |
/** | |
* Transform a query string to an object. | |
* | |
* @param {Object} obj Object that should be transformed. | |
* @param {String} prefix Optional prefix. | |
* @returns {String} | |
* @api public | |
*/ | |
function querystringify(obj, prefix) { | |
prefix = prefix || ''; | |
var pairs = []; | |
// | |
// Optionally prefix with a '?' if needed | |
// | |
if ('string' !== typeof prefix) prefix = '?'; | |
for (var key in obj) { | |
if (has.call(obj, key)) { | |
pairs.push(encodeURIComponent(key) +'='+ encodeURIComponent(obj[key])); | |
} | |
} | |
return pairs.length ? prefix + pairs.join('&') : ''; | |
} | |
// | |
// Expose the module. | |
// | |
exports.stringify = querystringify; | |
exports.parse = querystring; | |
},{}],56:[function(require,module,exports){ | |
'use strict'; | |
/** | |
* Check if we're required to add a port number. | |
* | |
* @see https://url.spec.whatwg.org/#default-port | |
* @param {Number|String} port Port number we need to check | |
* @param {String} protocol Protocol we need to check against. | |
* @returns {Boolean} Is it a default port for the given protocol | |
* @api private | |
*/ | |
module.exports = function required(port, protocol) { | |
protocol = protocol.split(':')[0]; | |
port = +port; | |
if (!port) return false; | |
switch (protocol) { | |
case 'http': | |
case 'ws': | |
return port !== 80; | |
case 'https': | |
case 'wss': | |
return port !== 443; | |
case 'ftp': | |
return port !== 21; | |
case 'gopher': | |
return port !== 70; | |
case 'file': | |
return false; | |
} | |
return port !== 0; | |
}; | |
},{}],57:[function(require,module,exports){ | |
// Copyright 2014 Simon Lydell | |
// X11 (“MIT”) Licensed. (See LICENSE.) | |
void (function(root, factory) { | |
if (typeof define === "function" && define.amd) { | |
define(factory) | |
} else if (typeof exports === "object") { | |
module.exports = factory() | |
} else { | |
root.resolveUrl = factory() | |
} | |
}(this, function() { | |
function resolveUrl(/* ...urls */) { | |
var numUrls = arguments.length | |
if (numUrls === 0) { | |
throw new Error("resolveUrl requires at least one argument; got none.") | |
} | |
var base = document.createElement("base") | |
base.href = arguments[0] | |
if (numUrls === 1) { | |
return base.href | |
} | |
var head = document.getElementsByTagName("head")[0] | |
head.insertBefore(base, head.firstChild) | |
var a = document.createElement("a") | |
var resolved | |
for (var index = 1; index < numUrls; index++) { | |
a.href = arguments[index] | |
resolved = a.href | |
base.href = resolved | |
} | |
head.removeChild(base) | |
return resolved | |
} | |
return resolveUrl | |
})); | |
},{}],58:[function(require,module,exports){ | |
(function (process){ | |
module.exports = function (tasks, cb) { | |
var results, pending, keys | |
var isSync = true | |
if (Array.isArray(tasks)) { | |
results = [] | |
pending = tasks.length | |
} else { | |
keys = Object.keys(tasks) | |
results = {} | |
pending = keys.length | |
} | |
function done (err) { | |
function end () { | |
if (cb) cb(err, results) | |
cb = null | |
} | |
if (isSync) process.nextTick(end) | |
else end() | |
} | |
function each (i, err, result) { | |
results[i] = result | |
if (--pending === 0 || err) { | |
done(err) | |
} | |
} | |
if (!pending) { | |
// empty | |
done(null) | |
} else if (keys) { | |
// object | |
keys.forEach(function (key) { | |
tasks[key](function (err, result) { each(key, err, result) }) | |
}) | |
} else { | |
// array | |
tasks.forEach(function (task, i) { | |
task(function (err, result) { each(i, err, result) }) | |
}) | |
} | |
isSync = false | |
} | |
}).call(this,require('_process')) | |
},{"_process":3}],59:[function(require,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var url = require('./url'); | |
var parser = require('socket.io-parser'); | |
var Manager = require('./manager'); | |
var debug = require('debug')('socket.io-client'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = exports = lookup; | |
/** | |
* Managers cache. | |
*/ | |
var cache = exports.managers = {}; | |
/** | |
* Looks up an existing `Manager` for multiplexing. | |
* If the user summons: | |
* | |
* `io('http://localhost/a');` | |
* `io('http://localhost/b');` | |
* | |
* We reuse the existing instance based on same scheme/port/host, | |
* and we initialize sockets for each namespace. | |
* | |
* @api public | |
*/ | |
function lookup (uri, opts) { | |
if (typeof uri === 'object') { | |
opts = uri; | |
uri = undefined; | |
} | |
opts = opts || {}; | |
var parsed = url(uri); | |
var source = parsed.source; | |
var id = parsed.id; | |
var path = parsed.path; | |
var sameNamespace = cache[id] && path in cache[id].nsps; | |
var newConnection = opts.forceNew || opts['force new connection'] || | |
false === opts.multiplex || sameNamespace; | |
var io; | |
if (newConnection) { | |
debug('ignoring socket cache for %s', source); | |
io = Manager(source, opts); | |
} else { | |
if (!cache[id]) { | |
debug('new io instance for %s', source); | |
cache[id] = Manager(source, opts); | |
} | |
io = cache[id]; | |
} | |
if (parsed.query && !opts.query) { | |
opts.query = parsed.query; | |
} else if (opts && 'object' === typeof opts.query) { | |
opts.query = encodeQueryString(opts.query); | |
} | |
return io.socket(parsed.path, opts); | |
} | |
/** | |
* Helper method to parse query objects to string. | |
* @param {object} query | |
* @returns {string} | |
*/ | |
function encodeQueryString (obj) { | |
var str = []; | |
for (var p in obj) { | |
if (obj.hasOwnProperty(p)) { | |
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p])); | |
} | |
} | |
return str.join('&'); | |
} | |
/** | |
* Protocol version. | |
* | |
* @api public | |
*/ | |
exports.protocol = parser.protocol; | |
/** | |
* `connect`. | |
* | |
* @param {String} uri | |
* @api public | |
*/ | |
exports.connect = lookup; | |
/** | |
* Expose constructors for standalone build. | |
* | |
* @api public | |
*/ | |
exports.Manager = require('./manager'); | |
exports.Socket = require('./socket'); | |
},{"./manager":60,"./socket":62,"./url":63,"debug":17,"socket.io-parser":65}],60:[function(require,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var eio = require('engine.io-client'); | |
var Socket = require('./socket'); | |
var Emitter = require('component-emitter'); | |
var parser = require('socket.io-parser'); | |
var on = require('./on'); | |
var bind = require('component-bind'); | |
var debug = require('debug')('socket.io-client:manager'); | |
var indexOf = require('indexof'); | |
var Backoff = require('backo2'); | |
/** | |
* IE6+ hasOwnProperty | |
*/ | |
var has = Object.prototype.hasOwnProperty; | |
/** | |
* Module exports | |
*/ | |
module.exports = Manager; | |
/** | |
* `Manager` constructor. | |
* | |
* @param {String} engine instance or engine uri/opts | |
* @param {Object} options | |
* @api public | |
*/ | |
function Manager (uri, opts) { | |
if (!(this instanceof Manager)) return new Manager(uri, opts); | |
if (uri && ('object' === typeof uri)) { | |
opts = uri; | |
uri = undefined; | |
} | |
opts = opts || {}; | |
opts.path = opts.path || '/socket.io'; | |
this.nsps = {}; | |
this.subs = []; | |
this.opts = opts; | |
this.reconnection(opts.reconnection !== false); | |
this.reconnectionAttempts(opts.reconnectionAttempts || Infinity); | |
this.reconnectionDelay(opts.reconnectionDelay || 1000); | |
this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000); | |
this.randomizationFactor(opts.randomizationFactor || 0.5); | |
this.backoff = new Backoff({ | |
min: this.reconnectionDelay(), | |
max: this.reconnectionDelayMax(), | |
jitter: this.randomizationFactor() | |
}); | |
this.timeout(null == opts.timeout ? 20000 : opts.timeout); | |
this.readyState = 'closed'; | |
this.uri = uri; | |
this.connecting = []; | |
this.lastPing = null; | |
this.encoding = false; | |
this.packetBuffer = []; | |
var _parser = opts.parser || parser; | |
this.encoder = new _parser.Encoder(); | |
this.decoder = new _parser.Decoder(); | |
this.autoConnect = opts.autoConnect !== false; | |
if (this.autoConnect) this.open(); | |
} | |
/** | |
* Propagate given event to sockets and emit on `this` | |
* | |
* @api private | |
*/ | |
Manager.prototype.emitAll = function () { | |
this.emit.apply(this, arguments); | |
for (var nsp in this.nsps) { | |
if (has.call(this.nsps, nsp)) { | |
this.nsps[nsp].emit.apply(this.nsps[nsp], arguments); | |
} | |
} | |
}; | |
/** | |
* Update `socket.id` of all sockets | |
* | |
* @api private | |
*/ | |
Manager.prototype.updateSocketIds = function () { | |
for (var nsp in this.nsps) { | |
if (has.call(this.nsps, nsp)) { | |
this.nsps[nsp].id = this.generateId(nsp); | |
} | |
} | |
}; | |
/** | |
* generate `socket.id` for the given `nsp` | |
* | |
* @param {String} nsp | |
* @return {String} | |
* @api private | |
*/ | |
Manager.prototype.generateId = function (nsp) { | |
return (nsp === '/' ? '' : (nsp + '#')) + this.engine.id; | |
}; | |
/** | |
* Mix in `Emitter`. | |
*/ | |
Emitter(Manager.prototype); | |
/** | |
* Sets the `reconnection` config. | |
* | |
* @param {Boolean} true/false if it should automatically reconnect | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnection = function (v) { | |
if (!arguments.length) return this._reconnection; | |
this._reconnection = !!v; | |
return this; | |
}; | |
/** | |
* Sets the reconnection attempts config. | |
* | |
* @param {Number} max reconnection attempts before giving up | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnectionAttempts = function (v) { | |
if (!arguments.length) return this._reconnectionAttempts; | |
this._reconnectionAttempts = v; | |
return this; | |
}; | |
/** | |
* Sets the delay between reconnections. | |
* | |
* @param {Number} delay | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnectionDelay = function (v) { | |
if (!arguments.length) return this._reconnectionDelay; | |
this._reconnectionDelay = v; | |
this.backoff && this.backoff.setMin(v); | |
return this; | |
}; | |
Manager.prototype.randomizationFactor = function (v) { | |
if (!arguments.length) return this._randomizationFactor; | |
this._randomizationFactor = v; | |
this.backoff && this.backoff.setJitter(v); | |
return this; | |
}; | |
/** | |
* Sets the maximum delay between reconnections. | |
* | |
* @param {Number} delay | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnectionDelayMax = function (v) { | |
if (!arguments.length) return this._reconnectionDelayMax; | |
this._reconnectionDelayMax = v; | |
this.backoff && this.backoff.setMax(v); | |
return this; | |
}; | |
/** | |
* Sets the connection timeout. `false` to disable | |
* | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.timeout = function (v) { | |
if (!arguments.length) return this._timeout; | |
this._timeout = v; | |
return this; | |
}; | |
/** | |
* Starts trying to reconnect if reconnection is enabled and we have not | |
* started reconnecting yet | |
* | |
* @api private | |
*/ | |
Manager.prototype.maybeReconnectOnOpen = function () { | |
// Only try to reconnect if it's the first time we're connecting | |
if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) { | |
// keeps reconnection from firing twice for the same reconnection loop | |
this.reconnect(); | |
} | |
}; | |
/** | |
* Sets the current transport `socket`. | |
* | |
* @param {Function} optional, callback | |
* @return {Manager} self | |
* @api public | |
*/ | |
Manager.prototype.open = | |
Manager.prototype.connect = function (fn, opts) { | |
debug('readyState %s', this.readyState); | |
if (~this.readyState.indexOf('open')) return this; | |
debug('opening %s', this.uri); | |
this.engine = eio(this.uri, this.opts); | |
var socket = this.engine; | |
var self = this; | |
this.readyState = 'opening'; | |
this.skipReconnect = false; | |
// emit `open` | |
var openSub = on(socket, 'open', function () { | |
self.onopen(); | |
fn && fn(); | |
}); | |
// emit `connect_error` | |
var errorSub = on(socket, 'error', function (data) { | |
debug('connect_error'); | |
self.cleanup(); | |
self.readyState = 'closed'; | |
self.emitAll('connect_error', data); | |
if (fn) { | |
var err = new Error('Connection error'); | |
err.data = data; | |
fn(err); | |
} else { | |
// Only do this if there is no fn to handle the error | |
self.maybeReconnectOnOpen(); | |
} | |
}); | |
// emit `connect_timeout` | |
if (false !== this._timeout) { | |
var timeout = this._timeout; | |
debug('connect attempt will timeout after %d', timeout); | |
// set timer | |
var timer = setTimeout(function () { | |
debug('connect attempt timed out after %d', timeout); | |
openSub.destroy(); | |
socket.close(); | |
socket.emit('error', 'timeout'); | |
self.emitAll('connect_timeout', timeout); | |
}, timeout); | |
this.subs.push({ | |
destroy: function () { | |
clearTimeout(timer); | |
} | |
}); | |
} | |
this.subs.push(openSub); | |
this.subs.push(errorSub); | |
return this; | |
}; | |
/** | |
* Called upon transport open. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onopen = function () { | |
debug('open'); | |
// clear old subs | |
this.cleanup(); | |
// mark as open | |
this.readyState = 'open'; | |
this.emit('open'); | |
// add new subs | |
var socket = this.engine; | |
this.subs.push(on(socket, 'data', bind(this, 'ondata'))); | |
this.subs.push(on(socket, 'ping', bind(this, 'onping'))); | |
this.subs.push(on(socket, 'pong', bind(this, 'onpong'))); | |
this.subs.push(on(socket, 'error', bind(this, 'onerror'))); | |
this.subs.push(on(socket, 'close', bind(this, 'onclose'))); | |
this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded'))); | |
}; | |
/** | |
* Called upon a ping. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onping = function () { | |
this.lastPing = new Date(); | |
this.emitAll('ping'); | |
}; | |
/** | |
* Called upon a packet. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onpong = function () { | |
this.emitAll('pong', new Date() - this.lastPing); | |
}; | |
/** | |
* Called with data. | |
* | |
* @api private | |
*/ | |
Manager.prototype.ondata = function (data) { | |
this.decoder.add(data); | |
}; | |
/** | |
* Called when parser fully decodes a packet. | |
* | |
* @api private | |
*/ | |
Manager.prototype.ondecoded = function (packet) { | |
this.emit('packet', packet); | |
}; | |
/** | |
* Called upon socket error. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onerror = function (err) { | |
debug('error', err); | |
this.emitAll('error', err); | |
}; | |
/** | |
* Creates a new socket for the given `nsp`. | |
* | |
* @return {Socket} | |
* @api public | |
*/ | |
Manager.prototype.socket = function (nsp, opts) { | |
var socket = this.nsps[nsp]; | |
if (!socket) { | |
socket = new Socket(this, nsp, opts); | |
this.nsps[nsp] = socket; | |
var self = this; | |
socket.on('connecting', onConnecting); | |
socket.on('connect', function () { | |
socket.id = self.generateId(nsp); | |
}); | |
if (this.autoConnect) { | |
// manually call here since connecting event is fired before listening | |
onConnecting(); | |
} | |
} | |
function onConnecting () { | |
if (!~indexOf(self.connecting, socket)) { | |
self.connecting.push(socket); | |
} | |
} | |
return socket; | |
}; | |
/** | |
* Called upon a socket close. | |
* | |
* @param {Socket} socket | |
*/ | |
Manager.prototype.destroy = function (socket) { | |
var index = indexOf(this.connecting, socket); | |
if (~index) this.connecting.splice(index, 1); | |
if (this.connecting.length) return; | |
this.close(); | |
}; | |
/** | |
* Writes a packet. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Manager.prototype.packet = function (packet) { | |
debug('writing packet %j', packet); | |
var self = this; | |
if (packet.query && packet.type === 0) packet.nsp += '?' + packet.query; | |
if (!self.encoding) { | |
// encode, then write to engine with result | |
self.encoding = true; | |
this.encoder.encode(packet, function (encodedPackets) { | |
for (var i = 0; i < encodedPackets.length; i++) { | |
self.engine.write(encodedPackets[i], packet.options); | |
} | |
self.encoding = false; | |
self.processPacketQueue(); | |
}); | |
} else { // add packet to the queue | |
self.packetBuffer.push(packet); | |
} | |
}; | |
/** | |
* If packet buffer is non-empty, begins encoding the | |
* next packet in line. | |
* | |
* @api private | |
*/ | |
Manager.prototype.processPacketQueue = function () { | |
if (this.packetBuffer.length > 0 && !this.encoding) { | |
var pack = this.packetBuffer.shift(); | |
this.packet(pack); | |
} | |
}; | |
/** | |
* Clean up transport subscriptions and packet buffer. | |
* | |
* @api private | |
*/ | |
Manager.prototype.cleanup = function () { | |
debug('cleanup'); | |
var subsLength = this.subs.length; | |
for (var i = 0; i < subsLength; i++) { | |
var sub = this.subs.shift(); | |
sub.destroy(); | |
} | |
this.packetBuffer = []; | |
this.encoding = false; | |
this.lastPing = null; | |
this.decoder.destroy(); | |
}; | |
/** | |
* Close the current socket. | |
* | |
* @api private | |
*/ | |
Manager.prototype.close = | |
Manager.prototype.disconnect = function () { | |
debug('disconnect'); | |
this.skipReconnect = true; | |
this.reconnecting = false; | |
if ('opening' === this.readyState) { | |
// `onclose` will not fire because | |
// an open event never happened | |
this.cleanup(); | |
} | |
this.backoff.reset(); | |
this.readyState = 'closed'; | |
if (this.engine) this.engine.close(); | |
}; | |
/** | |
* Called upon engine close. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onclose = function (reason) { | |
debug('onclose'); | |
this.cleanup(); | |
this.backoff.reset(); | |
this.readyState = 'closed'; | |
this.emit('close', reason); | |
if (this._reconnection && !this.skipReconnect) { | |
this.reconnect(); | |
} | |
}; | |
/** | |
* Attempt a reconnection. | |
* | |
* @api private | |
*/ | |
Manager.prototype.reconnect = function () { | |
if (this.reconnecting || this.skipReconnect) return this; | |
var self = this; | |
if (this.backoff.attempts >= this._reconnectionAttempts) { | |
debug('reconnect failed'); | |
this.backoff.reset(); | |
this.emitAll('reconnect_failed'); | |
this.reconnecting = false; | |
} else { | |
var delay = this.backoff.duration(); | |
debug('will wait %dms before reconnect attempt', delay); | |
this.reconnecting = true; | |
var timer = setTimeout(function () { | |
if (self.skipReconnect) return; | |
debug('attempting reconnect'); | |
self.emitAll('reconnect_attempt', self.backoff.attempts); | |
self.emitAll('reconnecting', self.backoff.attempts); | |
// check again for the case socket closed in above events | |
if (self.skipReconnect) return; | |
self.open(function (err) { | |
if (err) { | |
debug('reconnect attempt error'); | |
self.reconnecting = false; | |
self.reconnect(); | |
self.emitAll('reconnect_error', err.data); | |
} else { | |
debug('reconnect success'); | |
self.onreconnect(); | |
} | |
}); | |
}, delay); | |
this.subs.push({ | |
destroy: function () { | |
clearTimeout(timer); | |
} | |
}); | |
} | |
}; | |
/** | |
* Called upon successful reconnect. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onreconnect = function () { | |
var attempt = this.backoff.attempts; | |
this.reconnecting = false; | |
this.backoff.reset(); | |
this.updateSocketIds(); | |
this.emitAll('reconnect', attempt); | |
}; | |
},{"./on":61,"./socket":62,"backo2":9,"component-bind":13,"component-emitter":14,"debug":17,"engine.io-client":20,"indexof":42,"socket.io-parser":65}],61:[function(require,module,exports){ | |
/** | |
* Module exports. | |
*/ | |
module.exports = on; | |
/** | |
* Helper for subscriptions. | |
* | |
* @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter` | |
* @param {String} event name | |
* @param {Function} callback | |
* @api public | |
*/ | |
function on (obj, ev, fn) { | |
obj.on(ev, fn); | |
return { | |
destroy: function () { | |
obj.removeListener(ev, fn); | |
} | |
}; | |
} | |
},{}],62:[function(require,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var parser = require('socket.io-parser'); | |
var Emitter = require('component-emitter'); | |
var toArray = require('to-array'); | |
var on = require('./on'); | |
var bind = require('component-bind'); | |
var debug = require('debug')('socket.io-client:socket'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = exports = Socket; | |
/** | |
* Internal events (blacklisted). | |
* These events can't be emitted by the user. | |
* | |
* @api private | |
*/ | |
var events = { | |
connect: 1, | |
connect_error: 1, | |
connect_timeout: 1, | |
connecting: 1, | |
disconnect: 1, | |
error: 1, | |
reconnect: 1, | |
reconnect_attempt: 1, | |
reconnect_failed: 1, | |
reconnect_error: 1, | |
reconnecting: 1, | |
ping: 1, | |
pong: 1 | |
}; | |
/** | |
* Shortcut to `Emitter#emit`. | |
*/ | |
var emit = Emitter.prototype.emit; | |
/** | |
* `Socket` constructor. | |
* | |
* @api public | |
*/ | |
function Socket (io, nsp, opts) { | |
this.io = io; | |
this.nsp = nsp; | |
this.json = this; // compat | |
this.ids = 0; | |
this.acks = {}; | |
this.receiveBuffer = []; | |
this.sendBuffer = []; | |
this.connected = false; | |
this.disconnected = true; | |
if (opts && opts.query) { | |
this.query = opts.query; | |
} | |
if (this.io.autoConnect) this.open(); | |
} | |
/** | |
* Mix in `Emitter`. | |
*/ | |
Emitter(Socket.prototype); | |
/** | |
* Subscribe to open, close and packet events | |
* | |
* @api private | |
*/ | |
Socket.prototype.subEvents = function () { | |
if (this.subs) return; | |
var io = this.io; | |
this.subs = [ | |
on(io, 'open', bind(this, 'onopen')), | |
on(io, 'packet', bind(this, 'onpacket')), | |
on(io, 'close', bind(this, 'onclose')) | |
]; | |
}; | |
/** | |
* "Opens" the socket. | |
* | |
* @api public | |
*/ | |
Socket.prototype.open = | |
Socket.prototype.connect = function () { | |
if (this.connected) return this; | |
this.subEvents(); | |
this.io.open(); // ensure open | |
if ('open' === this.io.readyState) this.onopen(); | |
this.emit('connecting'); | |
return this; | |
}; | |
/** | |
* Sends a `message` event. | |
* | |
* @return {Socket} self | |
* @api public | |
*/ | |
Socket.prototype.send = function () { | |
var args = toArray(arguments); | |
args.unshift('message'); | |
this.emit.apply(this, args); | |
return this; | |
}; | |
/** | |
* Override `emit`. | |
* If the event is in `events`, it's emitted normally. | |
* | |
* @param {String} event name | |
* @return {Socket} self | |
* @api public | |
*/ | |
Socket.prototype.emit = function (ev) { | |
if (events.hasOwnProperty(ev)) { | |
emit.apply(this, arguments); | |
return this; | |
} | |
var args = toArray(arguments); | |
var packet = { type: parser.EVENT, data: args }; | |
packet.options = {}; | |
packet.options.compress = !this.flags || false !== this.flags.compress; | |
// event ack callback | |
if ('function' === typeof args[args.length - 1]) { | |
debug('emitting packet with ack id %d', this.ids); | |
this.acks[this.ids] = args.pop(); | |
packet.id = this.ids++; | |
} | |
if (this.connected) { | |
this.packet(packet); | |
} else { | |
this.sendBuffer.push(packet); | |
} | |
delete this.flags; | |
return this; | |
}; | |
/** | |
* Sends a packet. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.packet = function (packet) { | |
packet.nsp = this.nsp; | |
this.io.packet(packet); | |
}; | |
/** | |
* Called upon engine `open`. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onopen = function () { | |
debug('transport is open - connecting'); | |
// write connect packet if necessary | |
if ('/' !== this.nsp) { | |
if (this.query) { | |
this.packet({type: parser.CONNECT, query: this.query}); | |
} else { | |
this.packet({type: parser.CONNECT}); | |
} | |
} | |
}; | |
/** | |
* Called upon engine `close`. | |
* | |
* @param {String} reason | |
* @api private | |
*/ | |
Socket.prototype.onclose = function (reason) { | |
debug('close (%s)', reason); | |
this.connected = false; | |
this.disconnected = true; | |
delete this.id; | |
this.emit('disconnect', reason); | |
}; | |
/** | |
* Called with socket packet. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.onpacket = function (packet) { | |
if (packet.nsp !== this.nsp) return; | |
switch (packet.type) { | |
case parser.CONNECT: | |
this.onconnect(); | |
break; | |
case parser.EVENT: | |
this.onevent(packet); | |
break; | |
case parser.BINARY_EVENT: | |
this.onevent(packet); | |
break; | |
case parser.ACK: | |
this.onack(packet); | |
break; | |
case parser.BINARY_ACK: | |
this.onack(packet); | |
break; | |
case parser.DISCONNECT: | |
this.ondisconnect(); | |
break; | |
case parser.ERROR: | |
this.emit('error', packet.data); | |
break; | |
} | |
}; | |
/** | |
* Called upon a server event. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.onevent = function (packet) { | |
var args = packet.data || []; | |
debug('emitting event %j', args); | |
if (null != packet.id) { | |
debug('attaching ack callback to event'); | |
args.push(this.ack(packet.id)); | |
} | |
if (this.connected) { | |
emit.apply(this, args); | |
} else { | |
this.receiveBuffer.push(args); | |
} | |
}; | |
/** | |
* Produces an ack callback to emit with an event. | |
* | |
* @api private | |
*/ | |
Socket.prototype.ack = function (id) { | |
var self = this; | |
var sent = false; | |
return function () { | |
// prevent double callbacks | |
if (sent) return; | |
sent = true; | |
var args = toArray(arguments); | |
debug('sending ack %j', args); | |
self.packet({ | |
type: parser.ACK, | |
id: id, | |
data: args | |
}); | |
}; | |
}; | |
/** | |
* Called upon a server acknowlegement. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.onack = function (packet) { | |
var ack = this.acks[packet.id]; | |
if ('function' === typeof ack) { | |
debug('calling ack %s with %j', packet.id, packet.data); | |
ack.apply(this, packet.data); | |
delete this.acks[packet.id]; | |
} else { | |
debug('bad ack %s', packet.id); | |
} | |
}; | |
/** | |
* Called upon server connect. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onconnect = function () { | |
this.connected = true; | |
this.disconnected = false; | |
this.emit('connect'); | |
this.emitBuffered(); | |
}; | |
/** | |
* Emit buffered events (received and emitted). | |
* | |
* @api private | |
*/ | |
Socket.prototype.emitBuffered = function () { | |
var i; | |
for (i = 0; i < this.receiveBuffer.length; i++) { | |
emit.apply(this, this.receiveBuffer[i]); | |
} | |
this.receiveBuffer = []; | |
for (i = 0; i < this.sendBuffer.length; i++) { | |
this.packet(this.sendBuffer[i]); | |
} | |
this.sendBuffer = []; | |
}; | |
/** | |
* Called upon server disconnect. | |
* | |
* @api private | |
*/ | |
Socket.prototype.ondisconnect = function () { | |
debug('server disconnect (%s)', this.nsp); | |
this.destroy(); | |
this.onclose('io server disconnect'); | |
}; | |
/** | |
* Called upon forced client/server side disconnections, | |
* this method ensures the manager stops tracking us and | |
* that reconnections don't get triggered for this. | |
* | |
* @api private. | |
*/ | |
Socket.prototype.destroy = function () { | |
if (this.subs) { | |
// clean subscriptions to avoid reconnections | |
for (var i = 0; i < this.subs.length; i++) { | |
this.subs[i].destroy(); | |
} | |
this.subs = null; | |
} | |
this.io.destroy(this); | |
}; | |
/** | |
* Disconnects the socket manually. | |
* | |
* @return {Socket} self | |
* @api public | |
*/ | |
Socket.prototype.close = | |
Socket.prototype.disconnect = function () { | |
if (this.connected) { | |
debug('performing disconnect (%s)', this.nsp); | |
this.packet({ type: parser.DISCONNECT }); | |
} | |
// remove socket from pool | |
this.destroy(); | |
if (this.connected) { | |
// fire events | |
this.onclose('io client disconnect'); | |
} | |
return this; | |
}; | |
/** | |
* Sets the compress flag. | |
* | |
* @param {Boolean} if `true`, compresses the sending data | |
* @return {Socket} self | |
* @api public | |
*/ | |
Socket.prototype.compress = function (compress) { | |
this.flags = this.flags || {}; | |
this.flags.compress = compress; | |
return this; | |
}; | |
},{"./on":61,"component-bind":13,"component-emitter":14,"debug":17,"socket.io-parser":65,"to-array":67}],63:[function(require,module,exports){ | |
(function (global){ | |
/** | |
* Module dependencies. | |
*/ | |
var parseuri = require('parseuri'); | |
var debug = require('debug')('socket.io-client:url'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = url; | |
/** | |
* URL parser. | |
* | |
* @param {String} url | |
* @param {Object} An object meant to mimic window.location. | |
* Defaults to window.location. | |
* @api public | |
*/ | |
function url (uri, loc) { | |
var obj = uri; | |
// default to window.location | |
loc = loc || global.location; | |
if (null == uri) uri = loc.protocol + '//' + loc.host; | |
// relative path support | |
if ('string' === typeof uri) { | |
if ('/' === uri.charAt(0)) { | |
if ('/' === uri.charAt(1)) { | |
uri = loc.protocol + uri; | |
} else { | |
uri = loc.host + uri; | |
} | |
} | |
if (!/^(https?|wss?):\/\//.test(uri)) { | |
debug('protocol-less url %s', uri); | |
if ('undefined' !== typeof loc) { | |
uri = loc.protocol + '//' + uri; | |
} else { | |
uri = 'https://' + uri; | |
} | |
} | |
// parse | |
debug('parse %s', uri); | |
obj = parseuri(uri); | |
} | |
// make sure we treat `localhost:80` and `localhost` equally | |
if (!obj.port) { | |
if (/^(http|ws)$/.test(obj.protocol)) { | |
obj.port = '80'; | |
} else if (/^(http|ws)s$/.test(obj.protocol)) { | |
obj.port = '443'; | |
} | |
} | |
obj.path = obj.path || '/'; | |
var ipv6 = obj.host.indexOf(':') !== -1; | |
var host = ipv6 ? '[' + obj.host + ']' : obj.host; | |
// define unique id | |
obj.id = obj.protocol + '://' + host + ':' + obj.port; | |
// define href | |
obj.href = obj.protocol + '://' + host + (loc && loc.port === obj.port ? '' : (':' + obj.port)); | |
return obj; | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"debug":17,"parseuri":53}],64:[function(require,module,exports){ | |
(function (global){ | |
/*global Blob,File*/ | |
/** | |
* Module requirements | |
*/ | |
var isArray = require('isarray'); | |
var isBuf = require('./is-buffer'); | |
var toString = Object.prototype.toString; | |
var withNativeBlob = typeof global.Blob === 'function' || toString.call(global.Blob) === '[object BlobConstructor]'; | |
var withNativeFile = typeof global.File === 'function' || toString.call(global.File) === '[object FileConstructor]'; | |
/** | |
* Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder. | |
* Anything with blobs or files should be fed through removeBlobs before coming | |
* here. | |
* | |
* @param {Object} packet - socket.io event packet | |
* @return {Object} with deconstructed packet and list of buffers | |
* @api public | |
*/ | |
exports.deconstructPacket = function(packet) { | |
var buffers = []; | |
var packetData = packet.data; | |
var pack = packet; | |
pack.data = _deconstructPacket(packetData, buffers); | |
pack.attachments = buffers.length; // number of binary 'attachments' | |
return {packet: pack, buffers: buffers}; | |
}; | |
function _deconstructPacket(data, buffers) { | |
if (!data) return data; | |
if (isBuf(data)) { | |
var placeholder = { _placeholder: true, num: buffers.length }; | |
buffers.push(data); | |
return placeholder; | |
} else if (isArray(data)) { | |
var newData = new Array(data.length); | |
for (var i = 0; i < data.length; i++) { | |
newData[i] = _deconstructPacket(data[i], buffers); | |
} | |
return newData; | |
} else if (typeof data === 'object' && !(data instanceof Date)) { | |
var newData = {}; | |
for (var key in data) { | |
newData[key] = _deconstructPacket(data[key], buffers); | |
} | |
return newData; | |
} | |
return data; | |
} | |
/** | |
* Reconstructs a binary packet from its placeholder packet and buffers | |
* | |
* @param {Object} packet - event packet with placeholders | |
* @param {Array} buffers - binary buffers to put in placeholder positions | |
* @return {Object} reconstructed packet | |
* @api public | |
*/ | |
exports.reconstructPacket = function(packet, buffers) { | |
packet.data = _reconstructPacket(packet.data, buffers); | |
packet.attachments = undefined; // no longer useful | |
return packet; | |
}; | |
function _reconstructPacket(data, buffers) { | |
if (!data) return data; | |
if (data && data._placeholder) { | |
return buffers[data.num]; // appropriate buffer (should be natural order anyway) | |
} else if (isArray(data)) { | |
for (var i = 0; i < data.length; i++) { | |
data[i] = _reconstructPacket(data[i], buffers); | |
} | |
} else if (typeof data === 'object') { | |
for (var key in data) { | |
data[key] = _reconstructPacket(data[key], buffers); | |
} | |
} | |
return data; | |
} | |
/** | |
* Asynchronously removes Blobs or Files from data via | |
* FileReader's readAsArrayBuffer method. Used before encoding | |
* data as msgpack. Calls callback with the blobless data. | |
* | |
* @param {Object} data | |
* @param {Function} callback | |
* @api private | |
*/ | |
exports.removeBlobs = function(data, callback) { | |
function _removeBlobs(obj, curKey, containingObject) { | |
if (!obj) return obj; | |
// convert any blob | |
if ((withNativeBlob && obj instanceof Blob) || | |
(withNativeFile && obj instanceof File)) { | |
pendingBlobs++; | |
// async filereader | |
var fileReader = new FileReader(); | |
fileReader.onload = function() { // this.result == arraybuffer | |
if (containingObject) { | |
containingObject[curKey] = this.result; | |
} | |
else { | |
bloblessData = this.result; | |
} | |
// if nothing pending its callback time | |
if(! --pendingBlobs) { | |
callback(bloblessData); | |
} | |
}; | |
fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer | |
} else if (isArray(obj)) { // handle array | |
for (var i = 0; i < obj.length; i++) { | |
_removeBlobs(obj[i], i, obj); | |
} | |
} else if (typeof obj === 'object' && !isBuf(obj)) { // and object | |
for (var key in obj) { | |
_removeBlobs(obj[key], key, obj); | |
} | |
} | |
} | |
var pendingBlobs = 0; | |
var bloblessData = data; | |
_removeBlobs(bloblessData); | |
if (!pendingBlobs) { | |
callback(bloblessData); | |
} | |
}; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./is-buffer":66,"isarray":43}],65:[function(require,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var debug = require('debug')('socket.io-parser'); | |
var Emitter = require('component-emitter'); | |
var hasBin = require('has-binary2'); | |
var binary = require('./binary'); | |
var isBuf = require('./is-buffer'); | |
/** | |
* Protocol version. | |
* | |
* @api public | |
*/ | |
exports.protocol = 4; | |
/** | |
* Packet types. | |
* | |
* @api public | |
*/ | |
exports.types = [ | |
'CONNECT', | |
'DISCONNECT', | |
'EVENT', | |
'ACK', | |
'ERROR', | |
'BINARY_EVENT', | |
'BINARY_ACK' | |
]; | |
/** | |
* Packet type `connect`. | |
* | |
* @api public | |
*/ | |
exports.CONNECT = 0; | |
/** | |
* Packet type `disconnect`. | |
* | |
* @api public | |
*/ | |
exports.DISCONNECT = 1; | |
/** | |
* Packet type `event`. | |
* | |
* @api public | |
*/ | |
exports.EVENT = 2; | |
/** | |
* Packet type `ack`. | |
* | |
* @api public | |
*/ | |
exports.ACK = 3; | |
/** | |
* Packet type `error`. | |
* | |
* @api public | |
*/ | |
exports.ERROR = 4; | |
/** | |
* Packet type 'binary event' | |
* | |
* @api public | |
*/ | |
exports.BINARY_EVENT = 5; | |
/** | |
* Packet type `binary ack`. For acks with binary arguments. | |
* | |
* @api public | |
*/ | |
exports.BINARY_ACK = 6; | |
/** | |
* Encoder constructor. | |
* | |
* @api public | |
*/ | |
exports.Encoder = Encoder; | |
/** | |
* Decoder constructor. | |
* | |
* @api public | |
*/ | |
exports.Decoder = Decoder; | |
/** | |
* A socket.io Encoder instance | |
* | |
* @api public | |
*/ | |
function Encoder() {} | |
/** | |
* Encode a packet as a single string if non-binary, or as a | |
* buffer sequence, depending on packet type. | |
* | |
* @param {Object} obj - packet object | |
* @param {Function} callback - function to handle encodings (likely engine.write) | |
* @return Calls callback with Array of encodings | |
* @api public | |
*/ | |
Encoder.prototype.encode = function(obj, callback){ | |
if ((obj.type === exports.EVENT || obj.type === exports.ACK) && hasBin(obj.data)) { | |
obj.type = obj.type === exports.EVENT ? exports.BINARY_EVENT : exports.BINARY_ACK; | |
} | |
debug('encoding packet %j', obj); | |
if (exports.BINARY_EVENT === obj.type || exports.BINARY_ACK === obj.type) { | |
encodeAsBinary(obj, callback); | |
} | |
else { | |
var encoding = encodeAsString(obj); | |
callback([encoding]); | |
} | |
}; | |
/** | |
* Encode packet as string. | |
* | |
* @param {Object} packet | |
* @return {String} encoded | |
* @api private | |
*/ | |
function encodeAsString(obj) { | |
// first is type | |
var str = '' + obj.type; | |
// attachments if we have them | |
if (exports.BINARY_EVENT === obj.type || exports.BINARY_ACK === obj.type) { | |
str += obj.attachments + '-'; | |
} | |
// if we have a namespace other than `/` | |
// we append it followed by a comma `,` | |
if (obj.nsp && '/' !== obj.nsp) { | |
str += obj.nsp + ','; | |
} | |
// immediately followed by the id | |
if (null != obj.id) { | |
str += obj.id; | |
} | |
// json data | |
if (null != obj.data) { | |
str += JSON.stringify(obj.data); | |
} | |
debug('encoded %j as %s', obj, str); | |
return str; | |
} | |
/** | |
* Encode packet as 'buffer sequence' by removing blobs, and | |
* deconstructing packet into object with placeholders and | |
* a list of buffers. | |
* | |
* @param {Object} packet | |
* @return {Buffer} encoded | |
* @api private | |
*/ | |
function encodeAsBinary(obj, callback) { | |
function writeEncoding(bloblessData) { | |
var deconstruction = binary.deconstructPacket(bloblessData); | |
var pack = encodeAsString(deconstruction.packet); | |
var buffers = deconstruction.buffers; | |
buffers.unshift(pack); // add packet info to beginning of data list | |
callback(buffers); // write all the buffers | |
} | |
binary.removeBlobs(obj, writeEncoding); | |
} | |
/** | |
* A socket.io Decoder instance | |
* | |
* @return {Object} decoder | |
* @api public | |
*/ | |
function Decoder() { | |
this.reconstructor = null; | |
} | |
/** | |
* Mix in `Emitter` with Decoder. | |
*/ | |
Emitter(Decoder.prototype); | |
/** | |
* Decodes an ecoded packet string into packet JSON. | |
* | |
* @param {String} obj - encoded packet | |
* @return {Object} packet | |
* @api public | |
*/ | |
Decoder.prototype.add = function(obj) { | |
var packet; | |
if (typeof obj === 'string') { | |
packet = decodeString(obj); | |
if (exports.BINARY_EVENT === packet.type || exports.BINARY_ACK === packet.type) { // binary packet's json | |
this.reconstructor = new BinaryReconstructor(packet); | |
// no attachments, labeled binary but no binary data to follow | |
if (this.reconstructor.reconPack.attachments === 0) { | |
this.emit('decoded', packet); | |
} | |
} else { // non-binary full packet | |
this.emit('decoded', packet); | |
} | |
} | |
else if (isBuf(obj) || obj.base64) { // raw binary data | |
if (!this.reconstructor) { | |
throw new Error('got binary data when not reconstructing a packet'); | |
} else { | |
packet = this.reconstructor.takeBinaryData(obj); | |
if (packet) { // received final buffer | |
this.reconstructor = null; | |
this.emit('decoded', packet); | |
} | |
} | |
} | |
else { | |
throw new Error('Unknown type: ' + obj); | |
} | |
}; | |
/** | |
* Decode a packet String (JSON data) | |
* | |
* @param {String} str | |
* @return {Object} packet | |
* @api private | |
*/ | |
function decodeString(str) { | |
var i = 0; | |
// look up type | |
var p = { | |
type: Number(str.charAt(0)) | |
}; | |
if (null == exports.types[p.type]) return error(); | |
// look up attachments if type binary | |
if (exports.BINARY_EVENT === p.type || exports.BINARY_ACK === p.type) { | |
var buf = ''; | |
while (str.charAt(++i) !== '-') { | |
buf += str.charAt(i); | |
if (i == str.length) break; | |
} | |
if (buf != Number(buf) || str.charAt(i) !== '-') { | |
throw new Error('Illegal attachments'); | |
} | |
p.attachments = Number(buf); | |
} | |
// look up namespace (if any) | |
if ('/' === str.charAt(i + 1)) { | |
p.nsp = ''; | |
while (++i) { | |
var c = str.charAt(i); | |
if (',' === c) break; | |
p.nsp += c; | |
if (i === str.length) break; | |
} | |
} else { | |
p.nsp = '/'; | |
} | |
// look up id | |
var next = str.charAt(i + 1); | |
if ('' !== next && Number(next) == next) { | |
p.id = ''; | |
while (++i) { | |
var c = str.charAt(i); | |
if (null == c || Number(c) != c) { | |
--i; | |
break; | |
} | |
p.id += str.charAt(i); | |
if (i === str.length) break; | |
} | |
p.id = Number(p.id); | |
} | |
// look up json data | |
if (str.charAt(++i)) { | |
p = tryParse(p, str.substr(i)); | |
} | |
debug('decoded %s as %j', str, p); | |
return p; | |
} | |
function tryParse(p, str) { | |
try { | |
p.data = JSON.parse(str); | |
} catch(e){ | |
return error(); | |
} | |
return p; | |
} | |
/** | |
* Deallocates a parser's resources | |
* | |
* @api public | |
*/ | |
Decoder.prototype.destroy = function() { | |
if (this.reconstructor) { | |
this.reconstructor.finishedReconstruction(); | |
} | |
}; | |
/** | |
* A manager of a binary event's 'buffer sequence'. Should | |
* be constructed whenever a packet of type BINARY_EVENT is | |
* decoded. | |
* | |
* @param {Object} packet | |
* @return {BinaryReconstructor} initialized reconstructor | |
* @api private | |
*/ | |
function BinaryReconstructor(packet) { | |
this.reconPack = packet; | |
this.buffers = []; | |
} | |
/** | |
* Method to be called when binary data received from connection | |
* after a BINARY_EVENT packet. | |
* | |
* @param {Buffer | ArrayBuffer} binData - the raw binary data received | |
* @return {null | Object} returns null if more binary data is expected or | |
* a reconstructed packet object if all buffers have been received. | |
* @api private | |
*/ | |
BinaryReconstructor.prototype.takeBinaryData = function(binData) { | |
this.buffers.push(binData); | |
if (this.buffers.length === this.reconPack.attachments) { // done with buffer list | |
var packet = binary.reconstructPacket(this.reconPack, this.buffers); | |
this.finishedReconstruction(); | |
return packet; | |
} | |
return null; | |
}; | |
/** | |
* Cleans up binary packet reconstruction variables. | |
* | |
* @api private | |
*/ | |
BinaryReconstructor.prototype.finishedReconstruction = function() { | |
this.reconPack = null; | |
this.buffers = []; | |
}; | |
function error() { | |
return { | |
type: exports.ERROR, | |
data: 'parser error' | |
}; | |
} | |
},{"./binary":64,"./is-buffer":66,"component-emitter":14,"debug":17,"has-binary2":38}],66:[function(require,module,exports){ | |
(function (global){ | |
module.exports = isBuf; | |
/** | |
* Returns true if obj is a buffer or an arraybuffer. | |
* | |
* @api private | |
*/ | |
function isBuf(obj) { | |
return (global.Buffer && global.Buffer.isBuffer(obj)) || | |
(global.ArrayBuffer && obj instanceof ArrayBuffer); | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],67:[function(require,module,exports){ | |
module.exports = toArray | |
function toArray(list, index) { | |
var array = [] | |
index = index || 0 | |
for (var i = index || 0; i < list.length; i++) { | |
array[i - index] = list[i] | |
} | |
return array | |
} | |
},{}],68:[function(require,module,exports){ | |
// Generated by Babel | |
"use strict"; | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
exports.encode = encode; | |
/* global: window */ | |
var _window = window; | |
var btoa = _window.btoa; | |
function encode(data) { | |
return btoa(unescape(encodeURIComponent(data))); | |
} | |
var isSupported = exports.isSupported = "btoa" in window; | |
},{}],69:[function(require,module,exports){ | |
// Generated by Babel | |
"use strict"; | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
exports.newRequest = newRequest; | |
exports.resolveUrl = resolveUrl; | |
var _resolveUrl = require("resolve-url"); | |
var _resolveUrl2 = _interopRequireDefault(_resolveUrl); | |
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | |
function newRequest() { | |
return new window.XMLHttpRequest(); | |
} /* global window */ | |
function resolveUrl(origin, link) { | |
return (0, _resolveUrl2.default)(origin, link); | |
} | |
},{"resolve-url":57}],70:[function(require,module,exports){ | |
// Generated by Babel | |
"use strict"; | |
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
exports.getSource = getSource; | |
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | |
var FileSource = function () { | |
function FileSource(file) { | |
_classCallCheck(this, FileSource); | |
this._file = file; | |
this.size = file.size; | |
} | |
_createClass(FileSource, [{ | |
key: "slice", | |
value: function slice(start, end) { | |
return this._file.slice(start, end); | |
} | |
}, { | |
key: "close", | |
value: function close() {} | |
}]); | |
return FileSource; | |
}(); | |
function getSource(input) { | |
// Since we emulate the Blob type in our tests (not all target browsers | |
// support it), we cannot use `instanceof` for testing whether the input value | |
// can be handled. Instead, we simply check is the slice() function and the | |
// size property are available. | |
if (typeof input.slice === "function" && typeof input.size !== "undefined") { | |
return new FileSource(input); | |
} | |
throw new Error("source object may only be an instance of File or Blob in this environment"); | |
} | |
},{}],71:[function(require,module,exports){ | |
// Generated by Babel | |
"use strict"; | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
exports.setItem = setItem; | |
exports.getItem = getItem; | |
exports.removeItem = removeItem; | |
/* global window, localStorage */ | |
var hasStorage = false; | |
try { | |
hasStorage = "localStorage" in window; | |
// Attempt to store and read entries from the local storage to detect Private | |
// Mode on Safari on iOS (see #49) | |
var key = "tusSupport"; | |
localStorage.setItem(key, localStorage.getItem(key)); | |
} catch (e) { | |
// If we try to access localStorage inside a sandboxed iframe, a SecurityError | |
// is thrown. When in private mode on iOS Safari, a QuotaExceededError is | |
// thrown (see #49) | |
if (e.code === e.SECURITY_ERR || e.code === e.QUOTA_EXCEEDED_ERR) { | |
hasStorage = false; | |
} else { | |
throw e; | |
} | |
} | |
var canStoreURLs = exports.canStoreURLs = hasStorage; | |
function setItem(key, value) { | |
if (!hasStorage) return; | |
return localStorage.setItem(key, value); | |
} | |
function getItem(key) { | |
if (!hasStorage) return; | |
return localStorage.getItem(key); | |
} | |
function removeItem(key) { | |
if (!hasStorage) return; | |
return localStorage.removeItem(key); | |
} | |
},{}],72:[function(require,module,exports){ | |
// Generated by Babel | |
"use strict"; | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | |
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | |
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } | |
var DetailedError = function (_Error) { | |
_inherits(DetailedError, _Error); | |
function DetailedError(error) { | |
var causingErr = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; | |
var xhr = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2]; | |
_classCallCheck(this, DetailedError); | |
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(DetailedError).call(this, error.message)); | |
_this.originalRequest = xhr; | |
_this.causingError = causingErr; | |
var message = error.message; | |
if (causingErr != null) { | |
message += ", caused by " + causingErr.toString(); | |
} | |
if (xhr != null) { | |
message += ", originated from request (response code: " + xhr.status + ", response text: " + xhr.responseText + ")"; | |
} | |
_this.message = message; | |
return _this; | |
} | |
return DetailedError; | |
}(Error); | |
exports.default = DetailedError; | |
},{}],73:[function(require,module,exports){ | |
// Generated by Babel | |
"use strict"; | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
exports.default = fingerprint; | |
/** | |
* Generate a fingerprint for a file which will be used the store the endpoint | |
* | |
* @param {File} file | |
* @return {String} | |
*/ | |
function fingerprint(file) { | |
return ["tus", file.name, file.type, file.size, file.lastModified].join("-"); | |
} | |
},{}],74:[function(require,module,exports){ | |
// Generated by Babel | |
"use strict"; | |
var _upload = require("./upload"); | |
var _upload2 = _interopRequireDefault(_upload); | |
var _storage = require("./node/storage"); | |
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | |
/* global window */ | |
var defaultOptions = _upload2.default.defaultOptions; | |
if (typeof window !== "undefined") { | |
// Browser environment using XMLHttpRequest | |
var _window = window; | |
var XMLHttpRequest = _window.XMLHttpRequest; | |
var Blob = _window.Blob; | |
var isSupported = XMLHttpRequest && Blob && typeof Blob.prototype.slice === "function"; | |
} else { | |
// Node.js environment using http module | |
var isSupported = true; | |
} | |
// The usage of the commonjs exporting syntax instead of the new ECMAScript | |
// one is actually inteded and prevents weird behaviour if we are trying to | |
// import this module in another module using Babel. | |
module.exports = { | |
Upload: _upload2.default, | |
isSupported: isSupported, | |
canStoreURLs: _storage.canStoreURLs, | |
defaultOptions: defaultOptions | |
}; | |
},{"./node/storage":71,"./upload":75}],75:[function(require,module,exports){ | |
// Generated by Babel | |
"use strict"; | |
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* global window */ | |
// We import the files used inside the Node environment which are rewritten | |
// for browsers using the rules defined in the package.json | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
var _fingerprint = require("./fingerprint"); | |
var _fingerprint2 = _interopRequireDefault(_fingerprint); | |
var _error = require("./error"); | |
var _error2 = _interopRequireDefault(_error); | |
var _extend = require("extend"); | |
var _extend2 = _interopRequireDefault(_extend); | |
var _request = require("./node/request"); | |
var _source = require("./node/source"); | |
var _base = require("./node/base64"); | |
var Base64 = _interopRequireWildcard(_base); | |
var _storage = require("./node/storage"); | |
var Storage = _interopRequireWildcard(_storage); | |
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | |
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | |
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | |
var defaultOptions = { | |
endpoint: "", | |
fingerprint: _fingerprint2.default, | |
resume: true, | |
onProgress: null, | |
onChunkComplete: null, | |
onSuccess: null, | |
onError: null, | |
headers: {}, | |
chunkSize: Infinity, | |
withCredentials: false, | |
uploadUrl: null, | |
uploadSize: null, | |
overridePatchMethod: false, | |
retryDelays: null | |
}; | |
var Upload = function () { | |
function Upload(file, options) { | |
_classCallCheck(this, Upload); | |
this.options = (0, _extend2.default)(true, {}, defaultOptions, options); | |
// The underlying File/Blob object | |
this.file = file; | |
// The URL against which the file will be uploaded | |
this.url = null; | |
// The underlying XHR object for the current PATCH request | |
this._xhr = null; | |
// The fingerpinrt for the current file (set after start()) | |
this._fingerprint = null; | |
// The offset used in the current PATCH request | |
this._offset = null; | |
// True if the current PATCH request has been aborted | |
this._aborted = false; | |
// The file's size in bytes | |
this._size = null; | |
// The Source object which will wrap around the given file and provides us | |
// with a unified interface for getting its size and slice chunks from its | |
// content allowing us to easily handle Files, Blobs, Buffers and Streams. | |
this._source = null; | |
// The current count of attempts which have been made. Null indicates none. | |
this._retryAttempt = 0; | |
// The timeout's ID which is used to delay the next retry | |
this._retryTimeout = null; | |
// The offset of the remote upload before the latest attempt was started. | |
this._offsetBeforeRetry = 0; | |
} | |
_createClass(Upload, [{ | |
key: "start", | |
value: function start() { | |
var _this = this; | |
var file = this.file; | |
if (!file) { | |
this._emitError(new Error("tus: no file or stream to upload provided")); | |
return; | |
} | |
if (!this.options.endpoint) { | |
this._emitError(new Error("tus: no endpoint provided")); | |
return; | |
} | |
var source = this._source = (0, _source.getSource)(file, this.options.chunkSize); | |
// Firstly, check if the caller has supplied a manual upload size or else | |
// we will use the calculated size by the source object. | |
if (this.options.uploadSize != null) { | |
var size = +this.options.uploadSize; | |
if (isNaN(size)) { | |
throw new Error("tus: cannot convert `uploadSize` option into a number"); | |
} | |
this._size = size; | |
} else { | |
var size = source.size; | |
// The size property will be null if we cannot calculate the file's size, | |
// for example if you handle a stream. | |
if (size == null) { | |
throw new Error("tus: cannot automatically derive upload's size from input and must be specified manually using the `uploadSize` option"); | |
} | |
this._size = size; | |
} | |
var retryDelays = this.options.retryDelays; | |
if (retryDelays != null) { | |
if (Object.prototype.toString.call(retryDelays) !== "[object Array]") { | |
throw new Error("tus: the `retryDelays` option must either be an array or null"); | |
} else { | |
(function () { | |
var errorCallback = _this.options.onError; | |
_this.options.onError = function (err) { | |
// Restore the original error callback which may have been set. | |
_this.options.onError = errorCallback; | |
// We will reset the attempt counter if | |
// - we were already able to connect to the server (offset != null) and | |
// - we were able to upload a small chunk of data to the server | |
var shouldResetDelays = _this._offset != null && _this._offset > _this._offsetBeforeRetry; | |
if (shouldResetDelays) { | |
_this._retryAttempt = 0; | |
} | |
var isOnline = true; | |
if (typeof window !== "undefined" && "navigator" in window && window.navigator.onLine === false) { | |
isOnline = false; | |
} | |
// We only attempt a retry if | |
// - we didn't exceed the maxium number of retries, yet, and | |
// - this error was caused by a request or it's response and | |
// - the browser does not indicate that we are offline | |
var shouldRetry = _this._retryAttempt < retryDelays.length && err.originalRequest != null && isOnline; | |
if (!shouldRetry) { | |
_this._emitError(err); | |
return; | |
} | |
var delay = retryDelays[_this._retryAttempt++]; | |
_this._offsetBeforeRetry = _this._offset; | |
_this.options.uploadUrl = _this.url; | |
_this._retryTimeout = setTimeout(function () { | |
_this.start(); | |
}, delay); | |
}; | |
})(); | |
} | |
} | |
// Reset the aborted flag when the upload is started or else the | |
// _startUpload will stop before sending a request if the upload has been | |
// aborted previously. | |
this._aborted = false; | |
// A URL has manually been specified, so we try to resume | |
if (this.options.uploadUrl != null) { | |
this.url = this.options.uploadUrl; | |
this._resumeUpload(); | |
return; | |
} | |
// Try to find the endpoint for the file in the storage | |
if (this.options.resume) { | |
this._fingerprint = this.options.fingerprint(file); | |
var resumedUrl = Storage.getItem(this._fingerprint); | |
if (resumedUrl != null) { | |
this.url = resumedUrl; | |
this._resumeUpload(); | |
return; | |
} | |
} | |
// An upload has not started for the file yet, so we start a new one | |
this._createUpload(); | |
} | |
}, { | |
key: "abort", | |
value: function abort() { | |
if (this._xhr !== null) { | |
this._xhr.abort(); | |
this._source.close(); | |
this._aborted = true; | |
} | |
if (this._retryTimeout != null) { | |
clearTimeout(this._retryTimeout); | |
this._retryTimeout = null; | |
} | |
} | |
}, { | |
key: "_emitXhrError", | |
value: function _emitXhrError(xhr, err, causingErr) { | |
this._emitError(new _error2.default(err, causingErr, xhr)); | |
} | |
}, { | |
key: "_emitError", | |
value: function _emitError(err) { | |
if (typeof this.options.onError === "function") { | |
this.options.onError(err); | |
} else { | |
throw err; | |
} | |
} | |
}, { | |
key: "_emitSuccess", | |
value: function _emitSuccess() { | |
if (typeof this.options.onSuccess === "function") { | |
this.options.onSuccess(); | |
} | |
} | |
/** | |
* Publishes notification when data has been sent to the server. This | |
* data may not have been accepted by the server yet. | |
* @param {number} bytesSent Number of bytes sent to the server. | |
* @param {number} bytesTotal Total number of bytes to be sent to the server. | |
*/ | |
}, { | |
key: "_emitProgress", | |
value: function _emitProgress(bytesSent, bytesTotal) { | |
if (typeof this.options.onProgress === "function") { | |
this.options.onProgress(bytesSent, bytesTotal); | |
} | |
} | |
/** | |
* Publishes notification when a chunk of data has been sent to the server | |
* and accepted by the server. | |
* @param {number} chunkSize Size of the chunk that was accepted by the | |
* server. | |
* @param {number} bytesAccepted Total number of bytes that have been | |
* accepted by the server. | |
* @param {number} bytesTotal Total number of bytes to be sent to the server. | |
*/ | |
}, { | |
key: "_emitChunkComplete", | |
value: function _emitChunkComplete(chunkSize, bytesAccepted, bytesTotal) { | |
if (typeof this.options.onChunkComplete === "function") { | |
this.options.onChunkComplete(chunkSize, bytesAccepted, bytesTotal); | |
} | |
} | |
/** | |
* Set the headers used in the request and the withCredentials property | |
* as defined in the options | |
* | |
* @param {XMLHttpRequest} xhr | |
*/ | |
}, { | |
key: "_setupXHR", | |
value: function _setupXHR(xhr) { | |
xhr.setRequestHeader("Tus-Resumable", "1.0.0"); | |
var headers = this.options.headers; | |
for (var name in headers) { | |
xhr.setRequestHeader(name, headers[name]); | |
} | |
xhr.withCredentials = this.options.withCredentials; | |
} | |
/** | |
* Create a new upload using the creation extension by sending a POST | |
* request to the endpoint. After successful creation the file will be | |
* uploaded | |
* | |
* @api private | |
*/ | |
}, { | |
key: "_createUpload", | |
value: function _createUpload() { | |
var _this2 = this; | |
var xhr = (0, _request.newRequest)(); | |
xhr.open("POST", this.options.endpoint, true); | |
xhr.onload = function () { | |
if (!(xhr.status >= 200 && xhr.status < 300)) { | |
_this2._emitXhrError(xhr, new Error("tus: unexpected response while creating upload")); | |
return; | |
} | |
_this2.url = (0, _request.resolveUrl)(_this2.options.endpoint, xhr.getResponseHeader("Location")); | |
if (_this2.options.resume) { | |
Storage.setItem(_this2._fingerprint, _this2.url); | |
} | |
_this2._offset = 0; | |
_this2._startUpload(); | |
}; | |
xhr.onerror = function (err) { | |
_this2._emitXhrError(xhr, new Error("tus: failed to create upload"), err); | |
}; | |
this._setupXHR(xhr); | |
xhr.setRequestHeader("Upload-Length", this._size); | |
// Add metadata if values have been added | |
var metadata = encodeMetadata(this.options.metadata); | |
if (metadata !== "") { | |
xhr.setRequestHeader("Upload-Metadata", metadata); | |
} | |
xhr.send(null); | |
} | |
/* | |
* Try to resume an existing upload. First a HEAD request will be sent | |
* to retrieve the offset. If the request fails a new upload will be | |
* created. In the case of a successful response the file will be uploaded. | |
* | |
* @api private | |
*/ | |
}, { | |
key: "_resumeUpload", | |
value: function _resumeUpload() { | |
var _this3 = this; | |
var xhr = (0, _request.newRequest)(); | |
xhr.open("HEAD", this.url, true); | |
xhr.onload = function () { | |
if (!(xhr.status >= 200 && xhr.status < 300)) { | |
if (_this3.options.resume) { | |
// Remove stored fingerprint and corresponding endpoint, | |
// since the file can not be found | |
Storage.removeItem(_this3._fingerprint); | |
} | |
// If the upload is locked (indicated by the 423 Locked status code), we | |
// emit an error instead of directly starting a new upload. This way the | |
// retry logic can catch the error and will retry the upload. An upload | |
// is usually locked for a short period of time and will be available | |
// afterwards. | |
if (xhr.status === 423) { | |
_this3._emitXhrError(xhr, new Error("tus: upload is currently locked; retry later")); | |
return; | |
} | |
// Try to create a new upload | |
_this3.url = null; | |
_this3._createUpload(); | |
return; | |
} | |
var offset = parseInt(xhr.getResponseHeader("Upload-Offset"), 10); | |
if (isNaN(offset)) { | |
_this3._emitXhrError(xhr, new Error("tus: invalid or missing offset value")); | |
return; | |
} | |
var length = parseInt(xhr.getResponseHeader("Upload-Length"), 10); | |
if (isNaN(length)) { | |
_this3._emitXhrError(xhr, new Error("tus: invalid or missing length value")); | |
return; | |
} | |
// Upload has already been completed and we do not need to send additional | |
// data to the server | |
if (offset === length) { | |
_this3._emitProgress(length, length); | |
_this3._emitSuccess(); | |
return; | |
} | |
_this3._offset = offset; | |
_this3._startUpload(); | |
}; | |
xhr.onerror = function (err) { | |
_this3._emitXhrError(xhr, new Error("tus: failed to resume upload"), err); | |
}; | |
this._setupXHR(xhr); | |
xhr.send(null); | |
} | |
/** | |
* Start uploading the file using PATCH requests. The file will be divided | |
* into chunks as specified in the chunkSize option. During the upload | |
* the onProgress event handler may be invoked multiple times. | |
* | |
* @api private | |
*/ | |
}, { | |
key: "_startUpload", | |
value: function _startUpload() { | |
var _this4 = this; | |
// If the upload has been aborted, we will not send the next PATCH request. | |
// This is important if the abort method was called during a callback, such | |
// as onChunkComplete or onProgress. | |
if (this._aborted) { | |
return; | |
} | |
var xhr = this._xhr = (0, _request.newRequest)(); | |
// Some browser and servers may not support the PATCH method. For those | |
// cases, you can tell tus-js-client to use a POST request with the | |
// X-HTTP-Method-Override header for simulating a PATCH request. | |
if (this.options.overridePatchMethod) { | |
xhr.open("POST", this.url, true); | |
xhr.setRequestHeader("X-HTTP-Method-Override", "PATCH"); | |
} else { | |
xhr.open("PATCH", this.url, true); | |
} | |
xhr.onload = function () { | |
if (!(xhr.status >= 200 && xhr.status < 300)) { | |
_this4._emitXhrError(xhr, new Error("tus: unexpected response while uploading chunk")); | |
return; | |
} | |
var offset = parseInt(xhr.getResponseHeader("Upload-Offset"), 10); | |
if (isNaN(offset)) { | |
_this4._emitXhrError(xhr, new Error("tus: invalid or missing offset value")); | |
return; | |
} | |
_this4._emitProgress(offset, _this4._size); | |
_this4._emitChunkComplete(offset - _this4._offset, offset, _this4._size); | |
_this4._offset = offset; | |
if (offset == _this4._size) { | |
// Yay, finally done :) | |
_this4._emitSuccess(); | |
_this4._source.close(); | |
return; | |
} | |
_this4._startUpload(); | |
}; | |
xhr.onerror = function (err) { | |
// Don't emit an error if the upload was aborted manually | |
if (_this4._aborted) { | |
return; | |
} | |
_this4._emitXhrError(xhr, new Error("tus: failed to upload chunk at offset " + _this4._offset), err); | |
}; | |
// Test support for progress events before attaching an event listener | |
if ("upload" in xhr) { | |
xhr.upload.onprogress = function (e) { | |
if (!e.lengthComputable) { | |
return; | |
} | |
_this4._emitProgress(start + e.loaded, _this4._size); | |
}; | |
} | |
this._setupXHR(xhr); | |
xhr.setRequestHeader("Upload-Offset", this._offset); | |
xhr.setRequestHeader("Content-Type", "application/offset+octet-stream"); | |
var start = this._offset; | |
var end = this._offset + this.options.chunkSize; | |
// The specified chunkSize may be Infinity or the calcluated end position | |
// may exceed the file's size. In both cases, we limit the end position to | |
// the input's total size for simpler calculations and correctness. | |
if (end === Infinity || end > this._size) { | |
end = this._size; | |
} | |
xhr.send(this._source.slice(start, end)); | |
} | |
}]); | |
return Upload; | |
}(); | |
function encodeMetadata(metadata) { | |
if (!Base64.isSupported) { | |
return ""; | |
} | |
var encoded = []; | |
for (var key in metadata) { | |
encoded.push(key + " " + Base64.encode(metadata[key])); | |
} | |
return encoded.join(","); | |
} | |
Upload.defaultOptions = defaultOptions; | |
exports.default = Upload; | |
},{"./error":72,"./fingerprint":73,"./node/base64":68,"./node/request":69,"./node/source":70,"./node/storage":71,"extend":33}],76:[function(require,module,exports){ | |
(function (global){ | |
'use strict'; | |
var required = require('requires-port') | |
, qs = require('querystringify') | |
, protocolre = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\S\s]*)/i | |
, slashes = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//; | |
/** | |
* These are the parse rules for the URL parser, it informs the parser | |
* about: | |
* | |
* 0. The char it Needs to parse, if it's a string it should be done using | |
* indexOf, RegExp using exec and NaN means set as current value. | |
* 1. The property we should set when parsing this value. | |
* 2. Indication if it's backwards or forward parsing, when set as number it's | |
* the value of extra chars that should be split off. | |
* 3. Inherit from location if non existing in the parser. | |
* 4. `toLowerCase` the resulting value. | |
*/ | |
var rules = [ | |
['#', 'hash'], // Extract from the back. | |
['?', 'query'], // Extract from the back. | |
['/', 'pathname'], // Extract from the back. | |
['@', 'auth', 1], // Extract from the front. | |
[NaN, 'host', undefined, 1, 1], // Set left over value. | |
[/:(\d+)$/, 'port', undefined, 1], // RegExp the back. | |
[NaN, 'hostname', undefined, 1, 1] // Set left over. | |
]; | |
/** | |
* These properties should not be copied or inherited from. This is only needed | |
* for all non blob URL's as a blob URL does not include a hash, only the | |
* origin. | |
* | |
* @type {Object} | |
* @private | |
*/ | |
var ignore = { hash: 1, query: 1 }; | |
/** | |
* The location object differs when your code is loaded through a normal page, | |
* Worker or through a worker using a blob. And with the blobble begins the | |
* trouble as the location object will contain the URL of the blob, not the | |
* location of the page where our code is loaded in. The actual origin is | |
* encoded in the `pathname` so we can thankfully generate a good "default" | |
* location from it so we can generate proper relative URL's again. | |
* | |
* @param {Object|String} loc Optional default location object. | |
* @returns {Object} lolcation object. | |
* @api public | |
*/ | |
function lolcation(loc) { | |
loc = loc || global.location || {}; | |
var finaldestination = {} | |
, type = typeof loc | |
, key; | |
if ('blob:' === loc.protocol) { | |
finaldestination = new URL(unescape(loc.pathname), {}); | |
} else if ('string' === type) { | |
finaldestination = new URL(loc, {}); | |
for (key in ignore) delete finaldestination[key]; | |
} else if ('object' === type) { | |
for (key in loc) { | |
if (key in ignore) continue; | |
finaldestination[key] = loc[key]; | |
} | |
if (finaldestination.slashes === undefined) { | |
finaldestination.slashes = slashes.test(loc.href); | |
} | |
} | |
return finaldestination; | |
} | |
/** | |
* @typedef ProtocolExtract | |
* @type Object | |
* @property {String} protocol Protocol matched in the URL, in lowercase. | |
* @property {Boolean} slashes `true` if protocol is followed by "//", else `false`. | |
* @property {String} rest Rest of the URL that is not part of the protocol. | |
*/ | |
/** | |
* Extract protocol information from a URL with/without double slash ("//"). | |
* | |
* @param {String} address URL we want to extract from. | |
* @return {ProtocolExtract} Extracted information. | |
* @api private | |
*/ | |
function extractProtocol(address) { | |
var match = protocolre.exec(address); | |
return { | |
protocol: match[1] ? match[1].toLowerCase() : '', | |
slashes: !!match[2], | |
rest: match[3] | |
}; | |
} | |
/** | |
* Resolve a relative URL pathname against a base URL pathname. | |
* | |
* @param {String} relative Pathname of the relative URL. | |
* @param {String} base Pathname of the base URL. | |
* @return {String} Resolved pathname. | |
* @api private | |
*/ | |
function resolve(relative, base) { | |
var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/')) | |
, i = path.length | |
, last = path[i - 1] | |
, unshift = false | |
, up = 0; | |
while (i--) { | |
if (path[i] === '.') { | |
path.splice(i, 1); | |
} else if (path[i] === '..') { | |
path.splice(i, 1); | |
up++; | |
} else if (up) { | |
if (i === 0) unshift = true; | |
path.splice(i, 1); | |
up--; | |
} | |
} | |
if (unshift) path.unshift(''); | |
if (last === '.' || last === '..') path.push(''); | |
return path.join('/'); | |
} | |
/** | |
* The actual URL instance. Instead of returning an object we've opted-in to | |
* create an actual constructor as it's much more memory efficient and | |
* faster and it pleases my OCD. | |
* | |
* @constructor | |
* @param {String} address URL we want to parse. | |
* @param {Object|String} location Location defaults for relative paths. | |
* @param {Boolean|Function} parser Parser for the query string. | |
* @api public | |
*/ | |
function URL(address, location, parser) { | |
if (!(this instanceof URL)) { | |
return new URL(address, location, parser); | |
} | |
var relative, extracted, parse, instruction, index, key | |
, instructions = rules.slice() | |
, type = typeof location | |
, url = this | |
, i = 0; | |
// | |
// The following if statements allows this module two have compatibility with | |
// 2 different API: | |
// | |
// 1. Node.js's `url.parse` api which accepts a URL, boolean as arguments | |
// where the boolean indicates that the query string should also be parsed. | |
// | |
// 2. The `URL` interface of the browser which accepts a URL, object as | |
// arguments. The supplied object will be used as default values / fall-back | |
// for relative paths. | |
// | |
if ('object' !== type && 'string' !== type) { | |
parser = location; | |
location = null; | |
} | |
if (parser && 'function' !== typeof parser) parser = qs.parse; | |
location = lolcation(location); | |
// | |
// Extract protocol information before running the instructions. | |
// | |
extracted = extractProtocol(address || ''); | |
relative = !extracted.protocol && !extracted.slashes; | |
url.slashes = extracted.slashes || relative && location.slashes; | |
url.protocol = extracted.protocol || location.protocol || ''; | |
address = extracted.rest; | |
// | |
// When the authority component is absent the URL starts with a path | |
// component. | |
// | |
if (!extracted.slashes) instructions[2] = [/(.*)/, 'pathname']; | |
for (; i < instructions.length; i++) { | |
instruction = instructions[i]; | |
parse = instruction[0]; | |
key = instruction[1]; | |
if (parse !== parse) { | |
url[key] = address; | |
} else if ('string' === typeof parse) { | |
if (~(index = address.indexOf(parse))) { | |
if ('number' === typeof instruction[2]) { | |
url[key] = address.slice(0, index); | |
address = address.slice(index + instruction[2]); | |
} else { | |
url[key] = address.slice(index); | |
address = address.slice(0, index); | |
} | |
} | |
} else if ((index = parse.exec(address))) { | |
url[key] = index[1]; | |
address = address.slice(0, index.index); | |
} | |
url[key] = url[key] || ( | |
relative && instruction[3] ? location[key] || '' : '' | |
); | |
// | |
// Hostname, host and protocol should be lowercased so they can be used to | |
// create a proper `origin`. | |
// | |
if (instruction[4]) url[key] = url[key].toLowerCase(); | |
} | |
// | |
// Also parse the supplied query string in to an object. If we're supplied | |
// with a custom parser as function use that instead of the default build-in | |
// parser. | |
// | |
if (parser) url.query = parser(url.query); | |
// | |
// If the URL is relative, resolve the pathname against the base URL. | |
// | |
if ( | |
relative | |
&& location.slashes | |
&& url.pathname.charAt(0) !== '/' | |
&& (url.pathname !== '' || location.pathname !== '') | |
) { | |
url.pathname = resolve(url.pathname, location.pathname); | |
} | |
// | |
// We should not add port numbers if they are already the default port number | |
// for a given protocol. As the host also contains the port number we're going | |
// override it with the hostname which contains no port number. | |
// | |
if (!required(url.port, url.protocol)) { | |
url.host = url.hostname; | |
url.port = ''; | |
} | |
// | |
// Parse down the `auth` for the username and password. | |
// | |
url.username = url.password = ''; | |
if (url.auth) { | |
instruction = url.auth.split(':'); | |
url.username = instruction[0] || ''; | |
url.password = instruction[1] || ''; | |
} | |
url.origin = url.protocol && url.host && url.protocol !== 'file:' | |
? url.protocol +'//'+ url.host | |
: 'null'; | |
// | |
// The href is just the compiled result. | |
// | |
url.href = url.toString(); | |
} | |
/** | |
* This is convenience method for changing properties in the URL instance to | |
* insure that they all propagate correctly. | |
* | |
* @param {String} part Property we need to adjust. | |
* @param {Mixed} value The newly assigned value. | |
* @param {Boolean|Function} fn When setting the query, it will be the function | |
* used to parse the query. | |
* When setting the protocol, double slash will be | |
* removed from the final url if it is true. | |
* @returns {URL} | |
* @api public | |
*/ | |
function set(part, value, fn) { | |
var url = this; | |
switch (part) { | |
case 'query': | |
if ('string' === typeof value && value.length) { | |
value = (fn || qs.parse)(value); | |
} | |
url[part] = value; | |
break; | |
case 'port': | |
url[part] = value; | |
if (!required(value, url.protocol)) { | |
url.host = url.hostname; | |
url[part] = ''; | |
} else if (value) { | |
url.host = url.hostname +':'+ value; | |
} | |
break; | |
case 'hostname': | |
url[part] = value; | |
if (url.port) value += ':'+ url.port; | |
url.host = value; | |
break; | |
case 'host': | |
url[part] = value; | |
if (/:\d+$/.test(value)) { | |
value = value.split(':'); | |
url.port = value.pop(); | |
url.hostname = value.join(':'); | |
} else { | |
url.hostname = value; | |
url.port = ''; | |
} | |
break; | |
case 'protocol': | |
url.protocol = value.toLowerCase(); | |
url.slashes = !fn; | |
break; | |
case 'pathname': | |
url.pathname = value.length && value.charAt(0) !== '/' ? '/' + value : value; | |
break; | |
default: | |
url[part] = value; | |
} | |
for (var i = 0; i < rules.length; i++) { | |
var ins = rules[i]; | |
if (ins[4]) url[ins[1]] = url[ins[1]].toLowerCase(); | |
} | |
url.origin = url.protocol && url.host && url.protocol !== 'file:' | |
? url.protocol +'//'+ url.host | |
: 'null'; | |
url.href = url.toString(); | |
return url; | |
} | |
/** | |
* Transform the properties back in to a valid and full URL string. | |
* | |
* @param {Function} stringify Optional query stringify function. | |
* @returns {String} | |
* @api public | |
*/ | |
function toString(stringify) { | |
if (!stringify || 'function' !== typeof stringify) stringify = qs.stringify; | |
var query | |
, url = this | |
, protocol = url.protocol; | |
if (protocol && protocol.charAt(protocol.length - 1) !== ':') protocol += ':'; | |
var result = protocol + (url.slashes ? '//' : ''); | |
if (url.username) { | |
result += url.username; | |
if (url.password) result += ':'+ url.password; | |
result += '@'; | |
} | |
result += url.host + url.pathname; | |
query = 'object' === typeof url.query ? stringify(url.query) : url.query; | |
if (query) result += '?' !== query.charAt(0) ? '?'+ query : query; | |
if (url.hash) result += url.hash; | |
return result; | |
} | |
URL.prototype = { set: set, toString: toString }; | |
// | |
// Expose the URL parser and some additional properties that might be useful for | |
// others or testing. | |
// | |
URL.extractProtocol = extractProtocol; | |
URL.location = lolcation; | |
URL.qs = qs; | |
module.exports = URL; | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"querystringify":55,"requires-port":56}],77:[function(require,module,exports){ | |
(function(self) { | |
'use strict'; | |
if (self.fetch) { | |
return | |
} | |
var support = { | |
searchParams: 'URLSearchParams' in self, | |
iterable: 'Symbol' in self && 'iterator' in Symbol, | |
blob: 'FileReader' in self && 'Blob' in self && (function() { | |
try { | |
new Blob() | |
return true | |
} catch(e) { | |
return false | |
} | |
})(), | |
formData: 'FormData' in self, | |
arrayBuffer: 'ArrayBuffer' in self | |
} | |
if (support.arrayBuffer) { | |
var viewClasses = [ | |
'[object Int8Array]', | |
'[object Uint8Array]', | |
'[object Uint8ClampedArray]', | |
'[object Int16Array]', | |
'[object Uint16Array]', | |
'[object Int32Array]', | |
'[object Uint32Array]', | |
'[object Float32Array]', | |
'[object Float64Array]' | |
] | |
var isDataView = function(obj) { | |
return obj && DataView.prototype.isPrototypeOf(obj) | |
} | |
var isArrayBufferView = ArrayBuffer.isView || function(obj) { | |
return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 | |
} | |
} | |
function normalizeName(name) { | |
if (typeof name !== 'string') { | |
name = String(name) | |
} | |
if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { | |
throw new TypeError('Invalid character in header field name') | |
} | |
return name.toLowerCase() | |
} | |
function normalizeValue(value) { | |
if (typeof value !== 'string') { | |
value = String(value) | |
} | |
return value | |
} | |
// Build a destructive iterator for the value list | |
function iteratorFor(items) { | |
var iterator = { | |
next: function() { | |
var value = items.shift() | |
return {done: value === undefined, value: value} | |
} | |
} | |
if (support.iterable) { | |
iterator[Symbol.iterator] = function() { | |
return iterator | |
} | |
} | |
return iterator | |
} | |
function Headers(headers) { | |
this.map = {} | |
if (headers instanceof Headers) { | |
headers.forEach(function(value, name) { | |
this.append(name, value) | |
}, this) | |
} else if (Array.isArray(headers)) { | |
headers.forEach(function(header) { | |
this.append(header[0], header[1]) | |
}, this) | |
} else if (headers) { | |
Object.getOwnPropertyNames(headers).forEach(function(name) { | |
this.append(name, headers[name]) | |
}, this) | |
} | |
} | |
Headers.prototype.append = function(name, value) { | |
name = normalizeName(name) | |
value = normalizeValue(value) | |
var oldValue = this.map[name] | |
this.map[name] = oldValue ? oldValue+','+value : value | |
} | |
Headers.prototype['delete'] = function(name) { | |
delete this.map[normalizeName(name)] | |
} | |
Headers.prototype.get = function(name) { | |
name = normalizeName(name) | |
return this.has(name) ? this.map[name] : null | |
} | |
Headers.prototype.has = function(name) { | |
return this.map.hasOwnProperty(normalizeName(name)) | |
} | |
Headers.prototype.set = function(name, value) { | |
this.map[normalizeName(name)] = normalizeValue(value) | |
} | |
Headers.prototype.forEach = function(callback, thisArg) { | |
for (var name in this.map) { | |
if (this.map.hasOwnProperty(name)) { | |
callback.call(thisArg, this.map[name], name, this) | |
} | |
} | |
} | |
Headers.prototype.keys = function() { | |
var items = [] | |
this.forEach(function(value, name) { items.push(name) }) | |
return iteratorFor(items) | |
} | |
Headers.prototype.values = function() { | |
var items = [] | |
this.forEach(function(value) { items.push(value) }) | |
return iteratorFor(items) | |
} | |
Headers.prototype.entries = function() { | |
var items = [] | |
this.forEach(function(value, name) { items.push([name, value]) }) | |
return iteratorFor(items) | |
} | |
if (support.iterable) { | |
Headers.prototype[Symbol.iterator] = Headers.prototype.entries | |
} | |
function consumed(body) { | |
if (body.bodyUsed) { | |
return Promise.reject(new TypeError('Already read')) | |
} | |
body.bodyUsed = true | |
} | |
function fileReaderReady(reader) { | |
return new Promise(function(resolve, reject) { | |
reader.onload = function() { | |
resolve(reader.result) | |
} | |
reader.onerror = function() { | |
reject(reader.error) | |
} | |
}) | |
} | |
function readBlobAsArrayBuffer(blob) { | |
var reader = new FileReader() | |
var promise = fileReaderReady(reader) | |
reader.readAsArrayBuffer(blob) | |
return promise | |
} | |
function readBlobAsText(blob) { | |
var reader = new FileReader() | |
var promise = fileReaderReady(reader) | |
reader.readAsText(blob) | |
return promise | |
} | |
function readArrayBufferAsText(buf) { | |
var view = new Uint8Array(buf) | |
var chars = new Array(view.length) | |
for (var i = 0; i < view.length; i++) { | |
chars[i] = String.fromCharCode(view[i]) | |
} | |
return chars.join('') | |
} | |
function bufferClone(buf) { | |
if (buf.slice) { | |
return buf.slice(0) | |
} else { | |
var view = new Uint8Array(buf.byteLength) | |
view.set(new Uint8Array(buf)) | |
return view.buffer | |
} | |
} | |
function Body() { | |
this.bodyUsed = false | |
this._initBody = function(body) { | |
this._bodyInit = body | |
if (!body) { | |
this._bodyText = '' | |
} else if (typeof body === 'string') { | |
this._bodyText = body | |
} else if (support.blob && Blob.prototype.isPrototypeOf(body)) { | |
this._bodyBlob = body | |
} else if (support.formData && FormData.prototype.isPrototypeOf(body)) { | |
this._bodyFormData = body | |
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { | |
this._bodyText = body.toString() | |
} else if (support.arrayBuffer && support.blob && isDataView(body)) { | |
this._bodyArrayBuffer = bufferClone(body.buffer) | |
// IE 10-11 can't handle a DataView body. | |
this._bodyInit = new Blob([this._bodyArrayBuffer]) | |
} else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { | |
this._bodyArrayBuffer = bufferClone(body) | |
} else { | |
throw new Error('unsupported BodyInit type') | |
} | |
if (!this.headers.get('content-type')) { | |
if (typeof body === 'string') { | |
this.headers.set('content-type', 'text/plain;charset=UTF-8') | |
} else if (this._bodyBlob && this._bodyBlob.type) { | |
this.headers.set('content-type', this._bodyBlob.type) | |
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { | |
this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') | |
} | |
} | |
} | |
if (support.blob) { | |
this.blob = function() { | |
var rejected = consumed(this) | |
if (rejected) { | |
return rejected | |
} | |
if (this._bodyBlob) { | |
return Promise.resolve(this._bodyBlob) | |
} else if (this._bodyArrayBuffer) { | |
return Promise.resolve(new Blob([this._bodyArrayBuffer])) | |
} else if (this._bodyFormData) { | |
throw new Error('could not read FormData body as blob') | |
} else { | |
return Promise.resolve(new Blob([this._bodyText])) | |
} | |
} | |
this.arrayBuffer = function() { | |
if (this._bodyArrayBuffer) { | |
return consumed(this) || Promise.resolve(this._bodyArrayBuffer) | |
} else { | |
return this.blob().then(readBlobAsArrayBuffer) | |
} | |
} | |
} | |
this.text = function() { | |
var rejected = consumed(this) | |
if (rejected) { | |
return rejected | |
} | |
if (this._bodyBlob) { | |
return readBlobAsText(this._bodyBlob) | |
} else if (this._bodyArrayBuffer) { | |
return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) | |
} else if (this._bodyFormData) { | |
throw new Error('could not read FormData body as text') | |
} else { | |
return Promise.resolve(this._bodyText) | |
} | |
} | |
if (support.formData) { | |
this.formData = function() { | |
return this.text().then(decode) | |
} | |
} | |
this.json = function() { | |
return this.text().then(JSON.parse) | |
} | |
return this | |
} | |
// HTTP methods whose capitalization should be normalized | |
var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] | |
function normalizeMethod(method) { | |
var upcased = method.toUpperCase() | |
return (methods.indexOf(upcased) > -1) ? upcased : method | |
} | |
function Request(input, options) { | |
options = options || {} | |
var body = options.body | |
if (input instanceof Request) { | |
if (input.bodyUsed) { | |
throw new TypeError('Already read') | |
} | |
this.url = input.url | |
this.credentials = input.credentials | |
if (!options.headers) { | |
this.headers = new Headers(input.headers) | |
} | |
this.method = input.method | |
this.mode = input.mode | |
if (!body && input._bodyInit != null) { | |
body = input._bodyInit | |
input.bodyUsed = true | |
} | |
} else { | |
this.url = String(input) | |
} | |
this.credentials = options.credentials || this.credentials || 'omit' | |
if (options.headers || !this.headers) { | |
this.headers = new Headers(options.headers) | |
} | |
this.method = normalizeMethod(options.method || this.method || 'GET') | |
this.mode = options.mode || this.mode || null | |
this.referrer = null | |
if ((this.method === 'GET' || this.method === 'HEAD') && body) { | |
throw new TypeError('Body not allowed for GET or HEAD requests') | |
} | |
this._initBody(body) | |
} | |
Request.prototype.clone = function() { | |
return new Request(this, { body: this._bodyInit }) | |
} | |
function decode(body) { | |
var form = new FormData() | |
body.trim().split('&').forEach(function(bytes) { | |
if (bytes) { | |
var split = bytes.split('=') | |
var name = split.shift().replace(/\+/g, ' ') | |
var value = split.join('=').replace(/\+/g, ' ') | |
form.append(decodeURIComponent(name), decodeURIComponent(value)) | |
} | |
}) | |
return form | |
} | |
function parseHeaders(rawHeaders) { | |
var headers = new Headers() | |
rawHeaders.split(/\r?\n/).forEach(function(line) { | |
var parts = line.split(':') | |
var key = parts.shift().trim() | |
if (key) { | |
var value = parts.join(':').trim() | |
headers.append(key, value) | |
} | |
}) | |
return headers | |
} | |
Body.call(Request.prototype) | |
function Response(bodyInit, options) { | |
if (!options) { | |
options = {} | |
} | |
this.type = 'default' | |
this.status = 'status' in options ? options.status : 200 | |
this.ok = this.status >= 200 && this.status < 300 | |
this.statusText = 'statusText' in options ? options.statusText : 'OK' | |
this.headers = new Headers(options.headers) | |
this.url = options.url || '' | |
this._initBody(bodyInit) | |
} | |
Body.call(Response.prototype) | |
Response.prototype.clone = function() { | |
return new Response(this._bodyInit, { | |
status: this.status, | |
statusText: this.statusText, | |
headers: new Headers(this.headers), | |
url: this.url | |
}) | |
} | |
Response.error = function() { | |
var response = new Response(null, {status: 0, statusText: ''}) | |
response.type = 'error' | |
return response | |
} | |
var redirectStatuses = [301, 302, 303, 307, 308] | |
Response.redirect = function(url, status) { | |
if (redirectStatuses.indexOf(status) === -1) { | |
throw new RangeError('Invalid status code') | |
} | |
return new Response(null, {status: status, headers: {location: url}}) | |
} | |
self.Headers = Headers | |
self.Request = Request | |
self.Response = Response | |
self.fetch = function(input, init) { | |
return new Promise(function(resolve, reject) { | |
var request = new Request(input, init) | |
var xhr = new XMLHttpRequest() | |
xhr.onload = function() { | |
var options = { | |
status: xhr.status, | |
statusText: xhr.statusText, | |
headers: parseHeaders(xhr.getAllResponseHeaders() || '') | |
} | |
options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL') | |
var body = 'response' in xhr ? xhr.response : xhr.responseText | |
resolve(new Response(body, options)) | |
} | |
xhr.onerror = function() { | |
reject(new TypeError('Network request failed')) | |
} | |
xhr.ontimeout = function() { | |
reject(new TypeError('Network request failed')) | |
} | |
xhr.open(request.method, request.url, true) | |
if (request.credentials === 'include') { | |
xhr.withCredentials = true | |
} | |
if ('responseType' in xhr && support.blob) { | |
xhr.responseType = 'blob' | |
} | |
request.headers.forEach(function(value, name) { | |
xhr.setRequestHeader(name, value) | |
}) | |
xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) | |
}) | |
} | |
self.fetch.polyfill = true | |
})(typeof self !== 'undefined' ? self : this); | |
},{}],78:[function(require,module,exports){ | |
/* jshint node: true */ | |
'use strict'; | |
/** | |
# wildcard | |
Very simple wildcard matching, which is designed to provide the same | |
functionality that is found in the | |
[eve](https://github.com/adobe-webplatform/eve) eventing library. | |
## Usage | |
It works with strings: | |
<<< examples/strings.js | |
Arrays: | |
<<< examples/arrays.js | |
Objects (matching against keys): | |
<<< examples/objects.js | |
While the library works in Node, if you are are looking for file-based | |
wildcard matching then you should have a look at: | |
<https://github.com/isaacs/node-glob> | |
**/ | |
function WildcardMatcher(text, separator) { | |
this.text = text = text || ''; | |
this.hasWild = ~text.indexOf('*'); | |
this.separator = separator; | |
this.parts = text.split(separator); | |
} | |
WildcardMatcher.prototype.match = function(input) { | |
var matches = true; | |
var parts = this.parts; | |
var ii; | |
var partsCount = parts.length; | |
var testParts; | |
if (typeof input == 'string' || input instanceof String) { | |
if (!this.hasWild && this.text != input) { | |
matches = false; | |
} else { | |
testParts = (input || '').split(this.separator); | |
for (ii = 0; matches && ii < partsCount; ii++) { | |
if (parts[ii] === '*') { | |
continue; | |
} else if (ii < testParts.length) { | |
matches = parts[ii] === testParts[ii]; | |
} else { | |
matches = false; | |
} | |
} | |
// If matches, then return the component parts | |
matches = matches && testParts; | |
} | |
} | |
else if (typeof input.splice == 'function') { | |
matches = []; | |
for (ii = input.length; ii--; ) { | |
if (this.match(input[ii])) { | |
matches[matches.length] = input[ii]; | |
} | |
} | |
} | |
else if (typeof input == 'object') { | |
matches = {}; | |
for (var key in input) { | |
if (this.match(key)) { | |
matches[key] = input[key]; | |
} | |
} | |
} | |
return matches; | |
}; | |
module.exports = function(text, test, separator) { | |
var matcher = new WildcardMatcher(text, separator || /[\/\.]/); | |
if (typeof test != 'undefined') { | |
return matcher.match(test); | |
} | |
return matcher; | |
}; | |
},{}],79:[function(require,module,exports){ | |
'use strict'; | |
var alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split('') | |
, length = 64 | |
, map = {} | |
, seed = 0 | |
, i = 0 | |
, prev; | |
/** | |
* Return a string representing the specified number. | |
* | |
* @param {Number} num The number to convert. | |
* @returns {String} The string representation of the number. | |
* @api public | |
*/ | |
function encode(num) { | |
var encoded = ''; | |
do { | |
encoded = alphabet[num % length] + encoded; | |
num = Math.floor(num / length); | |
} while (num > 0); | |
return encoded; | |
} | |
/** | |
* Return the integer value specified by the given string. | |
* | |
* @param {String} str The string to convert. | |
* @returns {Number} The integer value represented by the string. | |
* @api public | |
*/ | |
function decode(str) { | |
var decoded = 0; | |
for (i = 0; i < str.length; i++) { | |
decoded = decoded * length + map[str.charAt(i)]; | |
} | |
return decoded; | |
} | |
/** | |
* Yeast: A tiny growing id generator. | |
* | |
* @returns {String} A unique id. | |
* @api public | |
*/ | |
function yeast() { | |
var now = encode(+new Date()); | |
if (now !== prev) return seed = 0, prev = now; | |
return now +'.'+ encode(seed++); | |
} | |
// | |
// Map each character to its index. | |
// | |
for (; i < length; i++) map[alphabet[i]] = i; | |
// | |
// Expose the `yeast`, `encode` and `decode` functions. | |
// | |
yeast.encode = encode; | |
yeast.decode = decode; | |
module.exports = yeast; | |
},{}],80:[function(require,module,exports){ | |
var bel = require('bel') // turns template tag into DOM elements | |
var morphdom = require('morphdom') // efficiently diffs + morphs two DOM elements | |
var defaultEvents = require('./update-events.js') // default events to be copied when dom elements update | |
module.exports = bel | |
// TODO move this + defaultEvents to a new module once we receive more feedback | |
module.exports.update = function (fromNode, toNode, opts) { | |
if (!opts) opts = {} | |
if (opts.events !== false) { | |
if (!opts.onBeforeElUpdated) opts.onBeforeElUpdated = copier | |
} | |
return morphdom(fromNode, toNode, opts) | |
// morphdom only copies attributes. we decided we also wanted to copy events | |
// that can be set via attributes | |
function copier (f, t) { | |
// copy events: | |
var events = opts.events || defaultEvents | |
for (var i = 0; i < events.length; i++) { | |
var ev = events[i] | |
if (t[ev]) { // if new element has a whitelisted attribute | |
f[ev] = t[ev] // update existing element | |
} else if (f[ev]) { // if existing element has it and new one doesnt | |
f[ev] = undefined // remove it from existing element | |
} | |
} | |
var oldValue = f.value | |
var newValue = t.value | |
// copy values for form elements | |
if ((f.nodeName === 'INPUT' && f.type !== 'file') || f.nodeName === 'SELECT') { | |
if (!newValue) { | |
t.value = f.value | |
} else if (newValue !== oldValue) { | |
f.value = newValue | |
} | |
} else if (f.nodeName === 'TEXTAREA') { | |
if (t.getAttribute('value') === null) f.value = t.value | |
} | |
} | |
} | |
},{"./update-events.js":81,"bel":11,"morphdom":46}],81:[function(require,module,exports){ | |
module.exports = [ | |
// attribute events (can be set with attributes) | |
'onclick', | |
'ondblclick', | |
'onmousedown', | |
'onmouseup', | |
'onmouseover', | |
'onmousemove', | |
'onmouseout', | |
'ondragstart', | |
'ondrag', | |
'ondragenter', | |
'ondragleave', | |
'ondragover', | |
'ondrop', | |
'ondragend', | |
'onkeydown', | |
'onkeypress', | |
'onkeyup', | |
'onunload', | |
'onabort', | |
'onerror', | |
'onresize', | |
'onscroll', | |
'onselect', | |
'onchange', | |
'onsubmit', | |
'onreset', | |
'onfocus', | |
'onblur', | |
'oninput', | |
// other common events | |
'oncontextmenu', | |
'onfocusin', | |
'onfocusout' | |
] | |
},{}],82:[function(require,module,exports){ | |
(function (global){ | |
const Utils = require('../core/Utils') | |
const Translator = require('../core/Translator') | |
const UppySocket = require('./UppySocket') | |
const ee = require('namespace-emitter') | |
const cuid = require('cuid') | |
const throttle = require('lodash.throttle') | |
const prettyBytes = require('prettier-bytes') | |
const match = require('mime-match') | |
// const en_US = require('../locales/en_US') | |
// const deepFreeze = require('deep-freeze-strict') | |
/** | |
* Main Uppy core | |
* | |
* @param {object} opts general options, like locales, to show modal or not to show | |
*/ | |
class Uppy { | |
constructor (opts) { | |
const defaultLocale = { | |
strings: { | |
youCanOnlyUploadX: { | |
0: 'You can only upload %{smart_count} file', | |
1: 'You can only upload %{smart_count} files' | |
}, | |
youHaveToAtLeastSelectX: { | |
0: 'You have to select at least %{smart_count} file', | |
1: 'You have to select at least %{smart_count} files' | |
}, | |
exceedsSize: 'This file exceeds maximum allowed size of', | |
youCanOnlyUploadFileTypes: 'You can only upload:', | |
uppyServerError: 'Connection with Uppy server failed' | |
} | |
} | |
// set default options | |
const defaultOptions = { | |
autoProceed: true, | |
debug: false, | |
restrictions: { | |
maxFileSize: false, | |
maxNumberOfFiles: false, | |
minNumberOfFiles: false, | |
allowedFileTypes: false | |
}, | |
onBeforeFileAdded: (currentFile, files) => Promise.resolve(), | |
onBeforeUpload: (files, done) => Promise.resolve(), | |
locale: defaultLocale | |
} | |
// Merge default options with the ones set by user | |
this.opts = Object.assign({}, defaultOptions, opts) | |
// // Dictates in what order different plugin types are ran: | |
// this.types = [ 'presetter', 'orchestrator', 'progressindicator', | |
// 'acquirer', 'modifier', 'uploader', 'presenter', 'debugger'] | |
this.locale = Object.assign({}, defaultLocale, this.opts.locale) | |
this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings) | |
// i18n | |
this.translator = new Translator({locale: this.locale}) | |
this.i18n = this.translator.translate.bind(this.translator) | |
// Container for different types of plugins | |
this.plugins = {} | |
// @TODO maybe bindall | |
this.translator = new Translator({locale: this.opts.locale}) | |
this.i18n = this.translator.translate.bind(this.translator) | |
this.getState = this.getState.bind(this) | |
this.updateMeta = this.updateMeta.bind(this) | |
this.initSocket = this.initSocket.bind(this) | |
this.log = this.log.bind(this) | |
this.addFile = this.addFile.bind(this) | |
this.calculateProgress = this.calculateProgress.bind(this) | |
this.resetProgress = this.resetProgress.bind(this) | |
// this.bus = this.emitter = ee() | |
this.emitter = ee() | |
this.on = this.emitter.on.bind(this.emitter) | |
this.off = this.emitter.off.bind(this.emitter) | |
this.once = this.emitter.once.bind(this.emitter) | |
this.emit = this.emitter.emit.bind(this.emitter) | |
this.preProcessors = [] | |
this.uploaders = [] | |
this.postProcessors = [] | |
this.state = { | |
files: {}, | |
capabilities: { | |
resumableUploads: false | |
}, | |
totalProgress: 0, | |
meta: Object.assign({}, this.opts.meta), | |
info: { | |
isHidden: true, | |
type: '', | |
msg: '' | |
} | |
} | |
// for debugging and testing | |
this.updateNum = 0 | |
if (this.opts.debug) { | |
global.UppyState = this.state | |
global.uppyLog = '' | |
// global.UppyAddFile = this.addFile.bind(this) | |
global._uppy = this | |
} | |
} | |
/** | |
* Iterate on all plugins and run `update` on them. Called each time state changes | |
* | |
*/ | |
updateAll (state) { | |
Object.keys(this.plugins).forEach((pluginType) => { | |
this.plugins[pluginType].forEach((plugin) => { | |
plugin.update(state) | |
}) | |
}) | |
} | |
/** | |
* Updates state | |
* | |
* @param {newState} object | |
*/ | |
setState (stateUpdate) { | |
const newState = Object.assign({}, this.state, stateUpdate) | |
this.emit('core:state-update', this.state, newState, stateUpdate) | |
this.state = newState | |
this.updateAll(this.state) | |
} | |
/** | |
* Returns current state | |
* | |
*/ | |
getState () { | |
// use deepFreeze for debugging | |
// return deepFreeze(this.state) | |
return this.state | |
} | |
reset () { | |
this.emit('core:pause-all') | |
this.emit('core:cancel-all') | |
this.setState({ | |
totalProgress: 0 | |
}) | |
} | |
resetProgress () { | |
const defaultProgress = { | |
percentage: 0, | |
bytesUploaded: 0, | |
uploadComplete: false, | |
uploadStarted: false | |
} | |
const files = Object.assign({}, this.state.files) | |
const updatedFiles = {} | |
Object.keys(files).forEach(fileID => { | |
const updatedFile = Object.assign({}, files[fileID]) | |
updatedFile.progress = Object.assign({}, updatedFile.progress, defaultProgress) | |
updatedFiles[fileID] = updatedFile | |
}) | |
console.log(updatedFiles) | |
this.setState({ | |
files: updatedFiles, | |
totalProgress: 0 | |
}) | |
} | |
addPreProcessor (fn) { | |
this.preProcessors.push(fn) | |
} | |
removePreProcessor (fn) { | |
const i = this.preProcessors.indexOf(fn) | |
if (i !== -1) { | |
this.preProcessors.splice(i, 1) | |
} | |
} | |
addPostProcessor (fn) { | |
this.postProcessors.push(fn) | |
} | |
removePostProcessor (fn) { | |
const i = this.postProcessors.indexOf(fn) | |
if (i !== -1) { | |
this.postProcessors.splice(i, 1) | |
} | |
} | |
addUploader (fn) { | |
this.uploaders.push(fn) | |
} | |
removeUploader (fn) { | |
const i = this.uploaders.indexOf(fn) | |
if (i !== -1) { | |
this.uploaders.splice(i, 1) | |
} | |
} | |
setMeta (data) { | |
const newMeta = Object.assign({}, this.getState().meta, data) | |
this.log('Adding metadata:') | |
this.log(data) | |
this.setState({meta: newMeta}) | |
} | |
updateMeta (data, fileID) { | |
const updatedFiles = Object.assign({}, this.getState().files) | |
const newMeta = Object.assign({}, updatedFiles[fileID].meta, data) | |
updatedFiles[fileID] = Object.assign({}, updatedFiles[fileID], { | |
meta: newMeta | |
}) | |
this.setState({files: updatedFiles}) | |
} | |
checkRestrictions (checkMinNumberOfFiles, file, fileType) { | |
const {maxFileSize, maxNumberOfFiles, minNumberOfFiles, allowedFileTypes} = this.opts.restrictions | |
if (checkMinNumberOfFiles && minNumberOfFiles) { | |
if (Object.keys(this.state.files).length < minNumberOfFiles) { | |
this.info(`${this.i18n('youHaveToAtLeastSelectX', {smart_count: minNumberOfFiles})}`, 'error', 5000) | |
return false | |
} | |
return true | |
} | |
if (maxNumberOfFiles) { | |
if (Object.keys(this.state.files).length + 1 > maxNumberOfFiles) { | |
this.info(`${this.i18n('youCanOnlyUploadX', {smart_count: maxNumberOfFiles})}`, 'error', 5000) | |
return false | |
} | |
} | |
if (allowedFileTypes) { | |
const isCorrectFileType = allowedFileTypes.filter(match(fileType.join('/'))).length > 0 | |
if (!isCorrectFileType) { | |
const allowedFileTypesString = allowedFileTypes.join(', ') | |
this.info(`${this.i18n('youCanOnlyUploadFileTypes')} ${allowedFileTypesString}`, 'error', 5000) | |
return false | |
} | |
} | |
if (maxFileSize) { | |
if (file.data.size > maxFileSize) { | |
this.info(`${this.i18n('exceedsSize')} ${prettyBytes(maxFileSize)}`, 'error', 5000) | |
return false | |
} | |
} | |
return true | |
} | |
addFile (file) { | |
return this.opts.onBeforeFileAdded(file, this.getState().files).catch((err) => { | |
this.info(err, 'error', 5000) | |
return Promise.reject(`onBeforeFileAdded: ${err}`) | |
}).then(() => { | |
return Utils.getFileType(file).then((fileType) => { | |
const updatedFiles = Object.assign({}, this.state.files) | |
const fileName = file.name || 'noname' | |
const fileExtension = Utils.getFileNameAndExtension(fileName)[1] | |
const isRemote = file.isRemote || false | |
const fileID = Utils.generateFileID(file) | |
const fileTypeGeneral = fileType[0] | |
const fileTypeSpecific = fileType[1] | |
const newFile = { | |
source: file.source || '', | |
id: fileID, | |
name: fileName, | |
extension: fileExtension || '', | |
meta: Object.assign({}, { name: fileName }, this.getState().meta), | |
type: { | |
general: fileTypeGeneral, | |
specific: fileTypeSpecific | |
}, | |
data: file.data, | |
progress: { | |
percentage: 0, | |
bytesUploaded: 0, | |
bytesTotal: file.data.size || 0, | |
uploadComplete: false, | |
uploadStarted: false | |
}, | |
size: file.data.size || 'N/A', | |
isRemote: isRemote, | |
remote: file.remote || '', | |
preview: file.preview | |
} | |
if (Utils.isPreviewSupported(fileTypeSpecific) && !isRemote) { | |
console.time(`createThumbnail ${file.id}`) | |
Utils.createThumbnail(file, 200).then((thumbnail) => { | |
console.timeEnd(`createThumbnail ${file.id}`) | |
this.setPreviewURL(fileID, thumbnail) | |
}).catch((err) => { | |
console.warn(err.stack || err.message) | |
}) | |
} | |
const isFileAllowed = this.checkRestrictions(false, newFile, fileType) | |
if (!isFileAllowed) return Promise.reject('File not allowed') | |
updatedFiles[fileID] = newFile | |
this.setState({files: updatedFiles}) | |
this.emit('core:file-added', newFile) | |
this.log(`Added file: ${fileName}, ${fileID}, mime type: ${fileType}`) | |
if (this.opts.autoProceed && !this.scheduledAutoProceed) { | |
this.scheduledAutoProceed = setTimeout(() => { | |
this.scheduledAutoProceed = null | |
this.upload().catch((err) => { | |
console.error(err.stack || err.message || err) | |
}) | |
}, 4) | |
} | |
}) | |
}) | |
} | |
/** | |
* Get a file object. | |
* | |
* @param {string} fileID The ID of the file object to return. | |
*/ | |
getFile (fileID) { | |
return this.getState().files[fileID] | |
} | |
/** | |
* Set the preview URL for a file. | |
*/ | |
setPreviewURL (fileID, preview) { | |
const { files } = this.state | |
this.setState({ | |
files: Object.assign({}, files, { | |
[fileID]: Object.assign({}, files[fileID], { | |
preview: preview | |
}) | |
}) | |
}) | |
} | |
removeFile (fileID) { | |
const updatedFiles = Object.assign({}, this.getState().files) | |
const removedFile = updatedFiles[fileID] | |
delete updatedFiles[fileID] | |
this.setState({files: updatedFiles}) | |
this.calculateTotalProgress() | |
this.emit('core:file-removed', fileID) | |
// Clean up object URLs. | |
if (removedFile.preview && Utils.isObjectURL(removedFile.preview)) { | |
URL.revokeObjectURL(removedFile.preview) | |
} | |
this.log(`Removed file: ${fileID}`) | |
} | |
calculateProgress (data) { | |
const fileID = data.id | |
const updatedFiles = Object.assign({}, this.getState().files) | |
// skip progress event for a file that’s been removed | |
if (!updatedFiles[fileID]) { | |
this.log('Trying to set progress for a file that’s not with us anymore: ', fileID) | |
return | |
} | |
const updatedFile = Object.assign({}, updatedFiles[fileID], | |
Object.assign({}, { | |
progress: Object.assign({}, updatedFiles[fileID].progress, { | |
bytesUploaded: data.bytesUploaded, | |
bytesTotal: data.bytesTotal, | |
percentage: Math.floor((data.bytesUploaded / data.bytesTotal * 100).toFixed(2)) | |
}) | |
} | |
)) | |
updatedFiles[data.id] = updatedFile | |
this.setState({ | |
files: updatedFiles | |
}) | |
this.calculateTotalProgress() | |
} | |
calculateTotalProgress () { | |
// calculate total progress, using the number of files currently uploading, | |
// multiplied by 100 and the summ of individual progress of each file | |
const files = Object.assign({}, this.getState().files) | |
const inProgress = Object.keys(files).filter((file) => { | |
return files[file].progress.uploadStarted | |
}) | |
const progressMax = inProgress.length * 100 | |
let progressAll = 0 | |
inProgress.forEach((file) => { | |
progressAll = progressAll + files[file].progress.percentage | |
}) | |
const totalProgress = Math.floor((progressAll * 100 / progressMax).toFixed(2)) | |
this.setState({ | |
totalProgress: totalProgress | |
}) | |
} | |
/** | |
* Registers listeners for all global actions, like: | |
* `file-add`, `file-remove`, `upload-progress`, `reset` | |
* | |
*/ | |
actions () { | |
// this.bus.on('*', (payload) => { | |
// console.log('emitted: ', this.event) | |
// console.log('with payload: ', payload) | |
// }) | |
// stress-test re-rendering | |
// setInterval(() => { | |
// this.setState({bla: 'bla'}) | |
// }, 20) | |
this.on('core:error', (error) => { | |
this.setState({ error }) | |
}) | |
this.on('core:upload', () => { | |
this.setState({ error: null }) | |
}) | |
this.on('core:file-add', (data) => { | |
this.addFile(data) | |
}) | |
// `remove-file` removes a file from `state.files`, for example when | |
// a user decides not to upload particular file and clicks a button to remove it | |
this.on('core:file-remove', (fileID) => { | |
this.removeFile(fileID) | |
}) | |
this.on('core:cancel-all', () => { | |
// let updatedFiles = this.getState().files | |
// updatedFiles = {} | |
this.setState({files: {}}) | |
}) | |
this.on('core:upload-started', (fileID, upload) => { | |
const updatedFiles = Object.assign({}, this.getState().files) | |
const updatedFile = Object.assign({}, updatedFiles[fileID], | |
Object.assign({}, { | |
progress: Object.assign({}, updatedFiles[fileID].progress, { | |
uploadStarted: Date.now() | |
}) | |
} | |
)) | |
updatedFiles[fileID] = updatedFile | |
this.setState({files: updatedFiles}) | |
}) | |
// upload progress events can occur frequently, especially when you have a good | |
// connection to the remote server. Therefore, we are throtteling them to | |
// prevent accessive function calls. | |
// see also: https://github.com/tus/tus-js-client/commit/9940f27b2361fd7e10ba58b09b60d82422183bbb | |
const throttledCalculateProgress = throttle(this.calculateProgress, 100, {leading: true, trailing: false}) | |
this.on('core:upload-progress', (data) => { | |
// this.calculateProgress(data) | |
throttledCalculateProgress(data) | |
}) | |
this.on('core:upload-success', (fileID, uploadResp, uploadURL) => { | |
const updatedFiles = Object.assign({}, this.getState().files) | |
const updatedFile = Object.assign({}, updatedFiles[fileID], { | |
progress: Object.assign({}, updatedFiles[fileID].progress, { | |
uploadComplete: true, | |
// good or bad idea? setting the percentage to 100 if upload is successful, | |
// so that if we lost some progress events on the way, its still marked “compete”? | |
percentage: 100 | |
}), | |
uploadURL: uploadURL | |
}) | |
updatedFiles[fileID] = updatedFile | |
this.setState({ | |
files: updatedFiles | |
}) | |
this.calculateTotalProgress() | |
if (this.getState().totalProgress === 100) { | |
const completeFiles = Object.keys(updatedFiles).filter((file) => { | |
return updatedFiles[file].progress.uploadComplete | |
}) | |
this.emit('core:upload-complete', completeFiles.length) | |
} | |
}) | |
this.on('core:update-meta', (data, fileID) => { | |
this.updateMeta(data, fileID) | |
}) | |
this.on('core:preprocess-progress', (fileID, progress) => { | |
const files = Object.assign({}, this.getState().files) | |
files[fileID] = Object.assign({}, files[fileID], { | |
progress: Object.assign({}, files[fileID].progress, { | |
preprocess: progress | |
}) | |
}) | |
this.setState({ files: files }) | |
}) | |
this.on('core:preprocess-complete', (fileID) => { | |
const files = Object.assign({}, this.getState().files) | |
files[fileID] = Object.assign({}, files[fileID], { | |
progress: Object.assign({}, files[fileID].progress) | |
}) | |
delete files[fileID].progress.preprocess | |
this.setState({ files: files }) | |
}) | |
this.on('core:postprocess-progress', (fileID, progress) => { | |
const files = Object.assign({}, this.getState().files) | |
files[fileID] = Object.assign({}, files[fileID], { | |
progress: Object.assign({}, files[fileID].progress, { | |
postprocess: progress | |
}) | |
}) | |
this.setState({ files: files }) | |
}) | |
this.on('core:postprocess-complete', (fileID) => { | |
const files = Object.assign({}, this.getState().files) | |
files[fileID] = Object.assign({}, files[fileID], { | |
progress: Object.assign({}, files[fileID].progress) | |
}) | |
delete files[fileID].progress.postprocess | |
// TODO should we set some kind of `fullyComplete` property on the file object | |
// so it's easier to see that the file is upload…fully complete…rather than | |
// what we have to do now (`uploadComplete && !postprocess`) | |
this.setState({ files: files }) | |
}) | |
// show informer if offline | |
if (typeof window !== 'undefined') { | |
window.addEventListener('online', () => this.isOnline(true)) | |
window.addEventListener('offline', () => this.isOnline(false)) | |
setTimeout(() => this.isOnline(), 3000) | |
} | |
} | |
isOnline (status) { | |
const online = status || window.navigator.onLine | |
if (!online) { | |
this.emit('is-offline') | |
this.info('No internet connection', 'error', 0) | |
this.wasOffline = true | |
} else { | |
this.emit('is-online') | |
if (this.wasOffline) { | |
this.emit('back-online') | |
this.info('Connected!', 'success', 3000) | |
this.wasOffline = false | |
} | |
} | |
} | |
/** | |
* Registers a plugin with Core | |
* | |
* @param {Class} Plugin object | |
* @param {Object} options object that will be passed to Plugin later | |
* @return {Object} self for chaining | |
*/ | |
use (Plugin, opts) { | |
// Instantiate | |
const plugin = new Plugin(this, opts) | |
const pluginName = plugin.id | |
this.plugins[plugin.type] = this.plugins[plugin.type] || [] | |
if (!pluginName) { | |
throw new Error('Your plugin must have a name') | |
} | |
if (!plugin.type) { | |
throw new Error('Your plugin must have a type') | |
} | |
let existsPluginAlready = this.getPlugin(pluginName) | |
if (existsPluginAlready) { | |
let msg = `Already found a plugin named '${existsPluginAlready.name}'. | |
Tried to use: '${pluginName}'. | |
Uppy is currently limited to running one of every plugin. | |
Share your use case with us over at | |
https://github.com/transloadit/uppy/issues/ | |
if you want us to reconsider.` | |
throw new Error(msg) | |
} | |
this.plugins[plugin.type].push(plugin) | |
plugin.install() | |
return this | |
} | |
/** | |
* Find one Plugin by name | |
* | |
* @param string name description | |
*/ | |
getPlugin (name) { | |
let foundPlugin = false | |
this.iteratePlugins((plugin) => { | |
const pluginName = plugin.id | |
if (pluginName === name) { | |
foundPlugin = plugin | |
return false | |
} | |
}) | |
return foundPlugin | |
} | |
/** | |
* Iterate through all `use`d plugins | |
* | |
* @param function method description | |
*/ | |
iteratePlugins (method) { | |
Object.keys(this.plugins).forEach((pluginType) => { | |
this.plugins[pluginType].forEach(method) | |
}) | |
} | |
/** | |
* Uninstall and remove a plugin. | |
* | |
* @param {Plugin} instance The plugin instance to remove. | |
*/ | |
removePlugin (instance) { | |
const list = this.plugins[instance.type] | |
if (instance.uninstall) { | |
instance.uninstall() | |
} | |
const index = list.indexOf(instance) | |
if (index !== -1) { | |
list.splice(index, 1) | |
} | |
} | |
/** | |
* Uninstall all plugins and close down this Uppy instance. | |
*/ | |
close () { | |
this.reset() | |
this.iteratePlugins((plugin) => { | |
plugin.uninstall() | |
}) | |
if (this.socket) { | |
this.socket.close() | |
} | |
} | |
/** | |
* Set info message in `state.info`, so that UI plugins like `Informer` | |
* can display the message | |
* | |
* @param {string} msg Message to be displayed by the informer | |
*/ | |
info (msg, type, duration) { | |
this.setState({ | |
info: { | |
isHidden: false, | |
type: type, | |
msg: msg | |
} | |
}) | |
this.emit('core:info-visible') | |
window.clearTimeout(this.infoTimeoutID) | |
if (duration === 0) { | |
this.infoTimeoutID = undefined | |
return | |
} | |
// hide the informer after `duration` milliseconds | |
this.infoTimeoutID = setTimeout(() => { | |
const newInformer = Object.assign({}, this.state.info, { | |
isHidden: true | |
}) | |
this.setState({ | |
info: newInformer | |
}) | |
this.emit('core:info-hidden') | |
}, duration) | |
} | |
hideInfo () { | |
const newInfo = Object.assign({}, this.core.state.info, { | |
isHidden: true | |
}) | |
this.setState({ | |
info: newInfo | |
}) | |
this.emit('core:info-hidden') | |
} | |
/** | |
* Logs stuff to console, only if `debug` is set to true. Silent in production. | |
* | |
* @return {String|Object} to log | |
*/ | |
log (msg, type) { | |
if (!this.opts.debug) { | |
return | |
} | |
if (type === 'error') { | |
console.error(`LOG: ${msg}`) | |
return | |
} | |
if (msg === `${msg}`) { | |
console.log(`LOG: ${msg}`) | |
} else { | |
console.dir(msg) | |
} | |
global.uppyLog = global.uppyLog + '\n' + 'DEBUG LOG: ' + msg | |
} | |
initSocket (opts) { | |
if (!this.socket) { | |
this.socket = new UppySocket(opts) | |
} | |
return this.socket | |
} | |
/** | |
* Initializes actions, installs all plugins (by iterating on them and calling `install`), sets options | |
* | |
*/ | |
run () { | |
this.log('Core is run, initializing actions...') | |
this.actions() | |
// Forse set `autoProceed` option to false if there are multiple selector Plugins active | |
// if (this.plugins.acquirer && this.plugins.acquirer.length > 1) { | |
// this.opts.autoProceed = false | |
// } | |
// Install all plugins | |
// this.installAll() | |
return this | |
} | |
/** | |
* Restore an upload by its ID. | |
*/ | |
restore (uploadID) { | |
this.log(`Core: attempting to restore upload "${uploadID}"`) | |
if (!this.state.currentUploads[uploadID]) { | |
this.removeUpload(uploadID) | |
return Promise.reject(new Error('Nonexistent upload')) | |
} | |
return this.runUpload(uploadID) | |
} | |
/** | |
* Create an upload for a bunch of files. | |
* | |
* @param {Array<string>} fileIDs File IDs to include in this upload. | |
* @return {string} ID of this upload. | |
*/ | |
createUpload (fileIDs) { | |
const uploadID = cuid() | |
this.emit('core:upload', { | |
id: uploadID, | |
fileIDs: fileIDs | |
}) | |
this.setState({ | |
currentUploads: Object.assign({}, this.state.currentUploads, { | |
[uploadID]: { | |
fileIDs: fileIDs, | |
step: 0 | |
} | |
}) | |
}) | |
return uploadID | |
} | |
/** | |
* Remove an upload, eg. if it has been canceled or completed. | |
* | |
* @param {string} uploadID The ID of the upload. | |
*/ | |
removeUpload (uploadID) { | |
const currentUploads = Object.assign({}, this.state.currentUploads) | |
delete currentUploads[uploadID] | |
this.setState({ | |
currentUploads: currentUploads | |
}) | |
} | |
/** | |
* Run an upload. This picks up where it left off in case the upload is being restored. | |
* | |
* @private | |
*/ | |
runUpload (uploadID) { | |
const uploadData = this.state.currentUploads[uploadID] | |
const fileIDs = uploadData.fileIDs | |
const restoreStep = uploadData.step | |
const steps = [ | |
...this.preProcessors, | |
...this.uploaders, | |
...this.postProcessors | |
] | |
let lastStep = Promise.resolve() | |
steps.forEach((fn, step) => { | |
// Skip this step if we are restoring and have already completed this step before. | |
if (step < restoreStep) { | |
return | |
} | |
lastStep = lastStep.then(() => { | |
const currentUpload = Object.assign({}, this.state.currentUploads[uploadID], { | |
step: step | |
}) | |
this.setState({ | |
currentUploads: Object.assign({}, this.state.currentUploads, { | |
[uploadID]: currentUpload | |
}) | |
}) | |
return fn(fileIDs) | |
}) | |
}) | |
// Not returning the `catch`ed promise, because we still want to return a rejected | |
// promise from this method if the upload failed. | |
lastStep.catch((err) => { | |
this.emit('core:error', err) | |
this.removeUpload(uploadID) | |
}) | |
return lastStep.then(() => { | |
// return number of uploaded files | |
this.emit('core:success', fileIDs) | |
this.removeUpload(uploadID) | |
}) | |
} | |
/** | |
* Start an upload for all the files that are not currently being uploaded. | |
* | |
* @return {Promise} | |
*/ | |
upload (forceUpload) { | |
const isMinNumberOfFilesReached = this.checkRestrictions(true) | |
if (!isMinNumberOfFilesReached) { | |
return Promise.reject('Minimum number of files has not been reached') | |
} | |
return this.opts.onBeforeUpload(this.state.files).catch((err) => { | |
this.info(err, 'error', 5000) | |
return Promise.reject(`onBeforeUpload: ${err}`) | |
}).then(() => { | |
const waitingFileIDs = [] | |
Object.keys(this.state.files).forEach((fileID) => { | |
const file = this.getFile(fileID) | |
// TODO: replace files[file].isRemote with some logic | |
// | |
// filter files that are now yet being uploaded / haven’t been uploaded | |
// and remote too | |
if (forceUpload) { | |
this.resetProgress() | |
waitingFileIDs.push(file.id) | |
} else if (!file.progress.uploadStarted || file.isRemote) { | |
waitingFileIDs.push(file.id) | |
} | |
}) | |
const uploadID = this.createUpload(waitingFileIDs) | |
return this.runUpload(uploadID) | |
}) | |
} | |
} | |
module.exports = function (opts) { | |
if (!(this instanceof Uppy)) { | |
return new Uppy(opts) | |
} | |
} | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"../core/Translator":83,"../core/Utils":85,"./UppySocket":84,"cuid":16,"lodash.throttle":44,"mime-match":45,"namespace-emitter":48,"prettier-bytes":54}],83:[function(require,module,exports){ | |
/** | |
* Translates strings with interpolation & pluralization support. | |
* Extensible with custom dictionaries and pluralization functions. | |
* | |
* Borrows heavily from and inspired by Polyglot https://github.com/airbnb/polyglot.js, | |
* basically a stripped-down version of it. Differences: pluralization functions are not hardcoded | |
* and can be easily added among with dictionaries, nested objects are used for pluralization | |
* as opposed to `||||` delimeter | |
* | |
* Usage example: `translator.translate('files_chosen', {smart_count: 3})` | |
* | |
* @param {object} opts | |
*/ | |
module.exports = class Translator { | |
constructor (opts) { | |
const defaultOptions = { | |
locale: { | |
strings: {}, | |
pluralize: function (n) { | |
if (n === 1) { | |
return 0 | |
} | |
return 1 | |
} | |
} | |
} | |
this.opts = Object.assign({}, defaultOptions, opts) | |
this.locale = Object.assign({}, defaultOptions.locale, opts.locale) | |
// console.log(this.opts.locale) | |
// this.locale.pluralize = this.locale ? this.locale.pluralize : defaultPluralize | |
// this.locale.strings = Object.assign({}, en_US.strings, this.opts.locale.strings) | |
} | |
/** | |
* Takes a string with placeholder variables like `%{smart_count} file selected` | |
* and replaces it with values from options `{smart_count: 5}` | |
* | |
* @license https://github.com/airbnb/polyglot.js/blob/master/LICENSE | |
* taken from https://github.com/airbnb/polyglot.js/blob/master/lib/polyglot.js#L299 | |
* | |
* @param {string} phrase that needs interpolation, with placeholders | |
* @param {object} options with values that will be used to replace placeholders | |
* @return {string} interpolated | |
*/ | |
interpolate (phrase, options) { | |
const replace = String.prototype.replace | |
const dollarRegex = /\$/g | |
const dollarBillsYall = '$$$$' | |
for (let arg in options) { | |
if (arg !== '_' && options.hasOwnProperty(arg)) { | |
// Ensure replacement value is escaped to prevent special $-prefixed | |
// regex replace tokens. the "$$$$" is needed because each "$" needs to | |
// be escaped with "$" itself, and we need two in the resulting output. | |
var replacement = options[arg] | |
if (typeof replacement === 'string') { | |
replacement = replace.call(options[arg], dollarRegex, dollarBillsYall) | |
} | |
// We create a new `RegExp` each time instead of using a more-efficient | |
// string replace so that the same argument can be replaced multiple times | |
// in the same phrase. | |
phrase = replace.call(phrase, new RegExp('%\\{' + arg + '\\}', 'g'), replacement) | |
} | |
} | |
return phrase | |
} | |
/** | |
* Public translate method | |
* | |
* @param {string} key | |
* @param {object} options with values that will be used later to replace placeholders in string | |
* @return {string} translated (and interpolated) | |
*/ | |
translate (key, options) { | |
if (options && options.smart_count) { | |
var plural = this.locale.pluralize(options.smart_count) | |
return this.interpolate(this.opts.locale.strings[key][plural], options) | |
} | |
return this.interpolate(this.opts.locale.strings[key], options) | |
} | |
} | |
},{}],84:[function(require,module,exports){ | |
const ee = require('namespace-emitter') | |
module.exports = class UppySocket { | |
constructor (opts) { | |
this.queued = [] | |
this.isOpen = false | |
this.socket = new WebSocket(opts.target) | |
this.emitter = ee() | |
this.socket.onopen = (e) => { | |
this.isOpen = true | |
while (this.queued.length > 0 && this.isOpen) { | |
const first = this.queued[0] | |
this.send(first.action, first.payload) | |
this.queued = this.queued.slice(1) | |
} | |
} | |
this.socket.onclose = (e) => { | |
this.isOpen = false | |
} | |
this._handleMessage = this._handleMessage.bind(this) | |
this.socket.onmessage = this._handleMessage | |
this.close = this.close.bind(this) | |
this.emit = this.emit.bind(this) | |
this.on = this.on.bind(this) | |
this.once = this.once.bind(this) | |
this.send = this.send.bind(this) | |
} | |
close () { | |
return this.socket.close() | |
} | |
send (action, payload) { | |
// attach uuid | |
if (!this.isOpen) { | |
this.queued.push({action, payload}) | |
return | |
} | |
this.socket.send(JSON.stringify({ | |
action, | |
payload | |
})) | |
} | |
on (action, handler) { | |
console.log(action) | |
this.emitter.on(action, handler) | |
} | |
emit (action, payload) { | |
console.log(action) | |
this.emitter.emit(action, payload) | |
} | |
once (action, handler) { | |
this.emitter.once(action, handler) | |
} | |
_handleMessage (e) { | |
try { | |
const message = JSON.parse(e.data) | |
console.log(message) | |
this.emit(message.action, message.payload) | |
} catch (err) { | |
console.log(err) | |
} | |
} | |
} | |
},{"namespace-emitter":48}],85:[function(require,module,exports){ | |
const throttle = require('lodash.throttle') | |
// we inline file-type module, as opposed to using the NPM version, | |
// because of this https://github.com/sindresorhus/file-type/issues/78 | |
// and https://github.com/sindresorhus/copy-text-to-clipboard/issues/5 | |
const fileType = require('../vendor/file-type') | |
/** | |
* A collection of small utility functions that help with dom manipulation, adding listeners, | |
* promises and other good things. | |
* | |
* @module Utils | |
*/ | |
/** | |
* Shallow flatten nested arrays. | |
*/ | |
function flatten (arr) { | |
return [].concat.apply([], arr) | |
} | |
function isTouchDevice () { | |
return 'ontouchstart' in window || // works on most browsers | |
navigator.maxTouchPoints // works on IE10/11 and Surface | |
} | |
// /** | |
// * Shorter and fast way to select a single node in the DOM | |
// * @param { String } selector - unique dom selector | |
// * @param { Object } ctx - DOM node where the target of our search will is located | |
// * @returns { Object } dom node found | |
// */ | |
// function $ (selector, ctx) { | |
// return (ctx || document).querySelector(selector) | |
// } | |
// /** | |
// * Shorter and fast way to select multiple nodes in the DOM | |
// * @param { String|Array } selector - DOM selector or nodes list | |
// * @param { Object } ctx - DOM node where the targets of our search will is located | |
// * @returns { Object } dom nodes found | |
// */ | |
// function $$ (selector, ctx) { | |
// var els | |
// if (typeof selector === 'string') { | |
// els = (ctx || document).querySelectorAll(selector) | |
// } else { | |
// els = selector | |
// return Array.prototype.slice.call(els) | |
// } | |
// } | |
function truncateString (str, length) { | |
if (str.length > length) { | |
return str.substr(0, length / 2) + '...' + str.substr(str.length - length / 4, str.length) | |
} | |
return str | |
// more precise version if needed | |
// http://stackoverflow.com/a/831583 | |
} | |
function secondsToTime (rawSeconds) { | |
const hours = Math.floor(rawSeconds / 3600) % 24 | |
const minutes = Math.floor(rawSeconds / 60) % 60 | |
const seconds = Math.floor(rawSeconds % 60) | |
return { hours, minutes, seconds } | |
} | |
/** | |
* Partition array by a grouping function. | |
* @param {[type]} array Input array | |
* @param {[type]} groupingFn Grouping function | |
* @return {[type]} Array of arrays | |
*/ | |
function groupBy (array, groupingFn) { | |
return array.reduce((result, item) => { | |
let key = groupingFn(item) | |
let xs = result.get(key) || [] | |
xs.push(item) | |
result.set(key, xs) | |
return result | |
}, new Map()) | |
} | |
/** | |
* Tests if every array element passes predicate | |
* @param {Array} array Input array | |
* @param {Object} predicateFn Predicate | |
* @return {bool} Every element pass | |
*/ | |
function every (array, predicateFn) { | |
return array.reduce((result, item) => { | |
if (!result) { | |
return false | |
} | |
return predicateFn(item) | |
}, true) | |
} | |
/** | |
* Converts list into array | |
*/ | |
function toArray (list) { | |
return Array.prototype.slice.call(list || [], 0) | |
} | |
/** | |
* Takes a fileName and turns it into fileID, by converting to lowercase, | |
* removing extra characters and adding unix timestamp | |
* | |
* @param {String} fileName | |
* | |
*/ | |
function generateFileID (file) { | |
let fileID = file.name.toLowerCase() | |
fileID = fileID.replace(/[^A-Z0-9]/ig, '') | |
fileID = fileID + file.data.lastModified | |
return fileID | |
} | |
function extend (...objs) { | |
return Object.assign.apply(this, [{}].concat(objs)) | |
} | |
/** | |
* Runs an array of promise-returning functions in sequence. | |
*/ | |
function runPromiseSequence (functions, ...args) { | |
let promise = Promise.resolve() | |
functions.forEach((func) => { | |
promise = promise.then(() => func(...args)) | |
}) | |
return promise | |
} | |
/** | |
* Takes function or class, returns its name. | |
* Because IE doesn’t support `constructor.name`. | |
* https://gist.github.com/dfkaye/6384439, http://stackoverflow.com/a/15714445 | |
* | |
* @param {Object} fn — function | |
* | |
*/ | |
// function getFnName (fn) { | |
// var f = typeof fn === 'function' | |
// var s = f && ((fn.name && ['', fn.name]) || fn.toString().match(/function ([^\(]+)/)) | |
// return (!f && 'not a function') || (s && s[1] || 'anonymous') | |
// } | |
function isPreviewSupported (fileTypeSpecific) { | |
// list of images that browsers can preview | |
if (/^(jpeg|gif|png|svg|svg\+xml|bmp)$/.test(fileTypeSpecific)) { | |
return true | |
} | |
return false | |
} | |
function getArrayBuffer (chunk) { | |
return new Promise(function (resolve, reject) { | |
var reader = new FileReader() | |
reader.addEventListener('load', function (e) { | |
// e.target.result is an ArrayBuffer | |
resolve(e.target.result) | |
}) | |
reader.addEventListener('error', function (err) { | |
console.error('FileReader error' + err) | |
reject(err) | |
}) | |
// file-type only needs the first 4100 bytes | |
reader.readAsArrayBuffer(chunk) | |
}) | |
} | |
function getFileType (file) { | |
const emptyFileType = ['', ''] | |
const extensionsToMime = { | |
'md': 'text/markdown', | |
'markdown': 'text/markdown', | |
'mp4': 'video/mp4', | |
'mp3': 'audio/mp3' | |
} | |
// no smart detection for remote files, just trust the provider | |
if (file.isRemote) { | |
return Promise.resolve(file.type.split('/')) | |
} | |
const fileExtension = getFileNameAndExtension(file.name)[1] | |
// 1. try to determine file type from magic bytes with file-type module | |
// this should be the most trustworthy way | |
const chunk = file.data.slice(0, 4100) | |
return getArrayBuffer(chunk) | |
.then((buffer) => { | |
const type = fileType(buffer) | |
if (type && type.mime) { | |
return type.mime.split('/') | |
} | |
// 2. if that’s no good, check if mime type is set in the file object | |
if (file.type) { | |
return file.type.split('/') | |
} | |
// 3. if that’s no good, see if we can map extension to a mime type | |
if (extensionsToMime[fileExtension]) { | |
return extensionsToMime[fileExtension].split('/') | |
} | |
// if all fails, well, return empty | |
return emptyFileType | |
}) | |
.catch(() => { | |
return emptyFileType | |
}) | |
// if (file.type) { | |
// return Promise.resolve(file.type.split('/')) | |
// } | |
// return mime.lookup(file.name) | |
// return file.type ? file.type.split('/') : ['', ''] | |
} | |
// TODO Check which types are actually supported in browsers. Chrome likes webm | |
// from my testing, but we may need more. | |
// We could use a library but they tend to contain dozens of KBs of mappings, | |
// most of which will go unused, so not sure if that's worth it. | |
const mimeToExtensions = { | |
'video/ogg': 'ogv', | |
'audio/ogg': 'ogg', | |
'video/webm': 'webm', | |
'audio/webm': 'webm', | |
'video/mp4': 'mp4', | |
'audio/mp3': 'mp3' | |
} | |
function getFileTypeExtension (mimeType) { | |
return mimeToExtensions[mimeType] || null | |
} | |
// returns [fileName, fileExt] | |
function getFileNameAndExtension (fullFileName) { | |
var re = /(?:\.([^.]+))?$/ | |
var fileExt = re.exec(fullFileName)[1] | |
var fileName = fullFileName.replace('.' + fileExt, '') | |
return [fileName, fileExt] | |
} | |
function supportsMediaRecorder () { | |
return typeof MediaRecorder === 'function' && !!MediaRecorder.prototype && | |
typeof MediaRecorder.prototype.start === 'function' | |
} | |
/** | |
* Check if a URL string is an object URL from `URL.createObjectURL`. | |
* | |
* @param {string} url | |
* @return {boolean} | |
*/ | |
function isObjectURL (url) { | |
return url.indexOf('blob:') === 0 | |
} | |
function getProportionalHeight (img, width) { | |
const aspect = img.width / img.height | |
return Math.round(width / aspect) | |
} | |
/** | |
* Create a thumbnail for the given Uppy file object. | |
* | |
* @param {{data: Blob}} file | |
* @param {number} width | |
* @return {Promise} | |
*/ | |
function createThumbnail (file, targetWidth) { | |
const originalUrl = URL.createObjectURL(file.data) | |
const onload = new Promise((resolve, reject) => { | |
const image = new Image() | |
image.src = originalUrl | |
image.onload = () => { | |
URL.revokeObjectURL(originalUrl) | |
resolve(image) | |
} | |
image.onerror = () => { | |
// The onerror event is totally useless unfortunately, as far as I know | |
URL.revokeObjectURL(originalUrl) | |
reject(new Error('Could not create thumbnail')) | |
} | |
}) | |
return onload.then((image) => { | |
const targetHeight = getProportionalHeight(image, targetWidth) | |
let sourceWidth = image.width | |
let sourceHeight = image.height | |
if (targetHeight < image.height / 2) { | |
const steps = Math.floor(Math.log(image.width / targetWidth) / Math.log(2)) | |
;({ | |
image, | |
sourceWidth, | |
sourceHeight | |
} = downScaleInSteps(image, steps)) | |
} | |
const canvas = document.createElement('canvas') | |
canvas.width = targetWidth | |
canvas.height = targetHeight | |
const context = canvas.getContext('2d') | |
context.drawImage(image, | |
0, 0, sourceWidth, sourceHeight, | |
0, 0, targetWidth, targetHeight) | |
return canvasToBlob(canvas) | |
}).then((blob) => { | |
return URL.createObjectURL(blob) | |
}) | |
} | |
/** | |
* Downscale an image by 50% `steps` times. | |
*/ | |
function downScaleInSteps (image, steps) { | |
let source = image | |
let currentWidth = source.width | |
let currentHeight = source.height | |
const canvas = document.createElement('canvas') | |
const context = canvas.getContext('2d') | |
canvas.width = currentWidth / 2 | |
canvas.height = currentHeight / 2 | |
for (let i = 0; i < steps; i += 1) { | |
context.drawImage(source, | |
// The entire source image. We pass width and height here, | |
// because we reuse this canvas, and should only scale down | |
// the part of the canvas that contains the previous scale step. | |
0, 0, currentWidth, currentHeight, | |
// Draw to 50% size | |
0, 0, currentWidth / 2, currentHeight / 2) | |
currentWidth /= 2 | |
currentHeight /= 2 | |
source = canvas | |
} | |
return { | |
image: canvas, | |
sourceWidth: currentWidth, | |
sourceHeight: currentHeight | |
} | |
} | |
/** | |
* Save a <canvas> element's content to a Blob object. | |
* | |
* @param {HTMLCanvasElement} canvas | |
* @return {Promise} | |
*/ | |
function canvasToBlob (canvas) { | |
if (canvas.toBlob) { | |
return new Promise((resolve) => { | |
canvas.toBlob(resolve) | |
}) | |
} | |
return Promise.resolve().then(() => { | |
return dataURItoBlob(canvas.toDataURL(), {}) | |
}) | |
} | |
function dataURItoBlob (dataURI, opts, toFile) { | |
// get the base64 data | |
var data = dataURI.split(',')[1] | |
// user may provide mime type, if not get it from data URI | |
var mimeType = opts.mimeType || dataURI.split(',')[0].split(':')[1].split(';')[0] | |
// default to plain/text if data URI has no mimeType | |
if (mimeType == null) { | |
mimeType = 'plain/text' | |
} | |
var binary = atob(data) | |
var array = [] | |
for (var i = 0; i < binary.length; i++) { | |
array.push(binary.charCodeAt(i)) | |
} | |
// Convert to a File? | |
if (toFile) { | |
return new File([new Uint8Array(array)], opts.name || '', {type: mimeType}) | |
} | |
return new Blob([new Uint8Array(array)], {type: mimeType}) | |
} | |
function dataURItoFile (dataURI, opts) { | |
return dataURItoBlob(dataURI, opts, true) | |
} | |
/** | |
* Copies text to clipboard by creating an almost invisible textarea, | |
* adding text there, then running execCommand('copy'). | |
* Falls back to prompt() when the easy way fails (hello, Safari!) | |
* From http://stackoverflow.com/a/30810322 | |
* | |
* @param {String} textToCopy | |
* @param {String} fallbackString | |
* @return {Promise} | |
*/ | |
function copyToClipboard (textToCopy, fallbackString) { | |
fallbackString = fallbackString || 'Copy the URL below' | |
return new Promise((resolve, reject) => { | |
const textArea = document.createElement('textarea') | |
textArea.setAttribute('style', { | |
position: 'fixed', | |
top: 0, | |
left: 0, | |
width: '2em', | |
height: '2em', | |
padding: 0, | |
border: 'none', | |
outline: 'none', | |
boxShadow: 'none', | |
background: 'transparent' | |
}) | |
textArea.value = textToCopy | |
document.body.appendChild(textArea) | |
textArea.select() | |
const magicCopyFailed = (err) => { | |
document.body.removeChild(textArea) | |
window.prompt(fallbackString, textToCopy) | |
return reject('Oops, unable to copy displayed fallback prompt: ' + err) | |
} | |
try { | |
const successful = document.execCommand('copy') | |
if (!successful) { | |
return magicCopyFailed('copy command unavailable') | |
} | |
document.body.removeChild(textArea) | |
return resolve() | |
} catch (err) { | |
document.body.removeChild(textArea) | |
return magicCopyFailed(err) | |
} | |
}) | |
} | |
function getSpeed (fileProgress) { | |
if (!fileProgress.bytesUploaded) return 0 | |
const timeElapsed = (new Date()) - fileProgress.uploadStarted | |
const uploadSpeed = fileProgress.bytesUploaded / (timeElapsed / 1000) | |
return uploadSpeed | |
} | |
function getBytesRemaining (fileProgress) { | |
return fileProgress.bytesTotal - fileProgress.bytesUploaded | |
} | |
function getETA (fileProgress) { | |
if (!fileProgress.bytesUploaded) return 0 | |
const uploadSpeed = getSpeed(fileProgress) | |
const bytesRemaining = getBytesRemaining(fileProgress) | |
const secondsRemaining = Math.round(bytesRemaining / uploadSpeed * 10) / 10 | |
return secondsRemaining | |
} | |
function prettyETA (seconds) { | |
const time = secondsToTime(seconds) | |
// Only display hours and minutes if they are greater than 0 but always | |
// display minutes if hours is being displayed | |
// Display a leading zero if the there is a preceding unit: 1m 05s, but 5s | |
const hoursStr = time.hours ? time.hours + 'h ' : '' | |
const minutesVal = time.hours ? ('0' + time.minutes).substr(-2) : time.minutes | |
const minutesStr = minutesVal ? minutesVal + 'm ' : '' | |
const secondsVal = minutesVal ? ('0' + time.seconds).substr(-2) : time.seconds | |
const secondsStr = secondsVal + 's' | |
return `${hoursStr}${minutesStr}${secondsStr}` | |
} | |
/** | |
* Check if an object is a DOM element. Duck-typing based on `nodeType`. | |
* | |
* @param {*} obj | |
*/ | |
function isDOMElement (obj) { | |
return obj && typeof obj === 'object' && obj.nodeType === Node.ELEMENT_NODE | |
} | |
/** | |
* Find a DOM element. | |
* | |
* @param {Node|string} element | |
* @return {Node|null} | |
*/ | |
function findDOMElement (element) { | |
if (typeof element === 'string') { | |
return document.querySelector(element) | |
} | |
if (typeof element === 'object' && isDOMElement(element)) { | |
return element | |
} | |
} | |
function getSocketHost (url) { | |
// get the host domain | |
var regex = /^(?:https?:\/\/|\/\/)?(?:[^@\n]+@)?(?:www\.)?([^\n]+)/ | |
var host = regex.exec(url)[1] | |
var socketProtocol = location.protocol === 'https:' ? 'wss' : 'ws' | |
return `${socketProtocol}://${host}` | |
} | |
function _emitSocketProgress (uploader, progressData, file) { | |
const {progress, bytesUploaded, bytesTotal} = progressData | |
if (progress) { | |
uploader.core.log(`Upload progress: ${progress}`) | |
uploader.core.emitter.emit('core:upload-progress', { | |
uploader, | |
id: file.id, | |
bytesUploaded: bytesUploaded, | |
bytesTotal: bytesTotal | |
}) | |
} | |
} | |
const emitSocketProgress = throttle(_emitSocketProgress, 300, {leading: true, trailing: true}) | |
module.exports = { | |
generateFileID, | |
toArray, | |
every, | |
flatten, | |
groupBy, | |
extend, | |
runPromiseSequence, | |
supportsMediaRecorder, | |
isTouchDevice, | |
getFileNameAndExtension, | |
truncateString, | |
getFileTypeExtension, | |
getFileType, | |
getArrayBuffer, | |
isPreviewSupported, | |
isObjectURL, | |
createThumbnail, | |
secondsToTime, | |
dataURItoBlob, | |
dataURItoFile, | |
getSpeed, | |
getBytesRemaining, | |
getETA, | |
copyToClipboard, | |
prettyETA, | |
findDOMElement, | |
getSocketHost, | |
emitSocketProgress | |
} | |
},{"../vendor/file-type":138,"lodash.throttle":44}],86:[function(require,module,exports){ | |
const Core = require('./Core') | |
module.exports = Core | |
},{"./Core":82}],87:[function(require,module,exports){ | |
const html = require('yo-yo') | |
const onload = require('on-load') | |
const LoaderView = require('./Loader') | |
module.exports = (props) => { | |
const demoLink = props.demo ? html`<button class="UppyProvider-authBtnDemo" onclick=${props.handleDemoAuth}>Proceed with Demo Account</button>` : null | |
const AuthBlock = () => html` | |
<div class="UppyProvider-auth"> | |
<h1 class="UppyProvider-authTitle">Please authenticate with <span class="UppyProvider-authTitleName">${props.pluginName}</span><br> to select files</h1> | |
<button type="button" class="UppyProvider-authBtn" onclick=${props.handleAuth}>Authenticate</button> | |
${demoLink} | |
</div> | |
` | |
return onload(html` | |
<div style="height: 100%;"> | |
${props.checkAuthInProgress | |
? LoaderView() | |
: AuthBlock() | |
} | |
</div>`, props.checkAuth, null, `auth${props.pluginName}`) | |
} | |
},{"./Loader":91,"on-load":50,"yo-yo":80}],88:[function(require,module,exports){ | |
const html = require('yo-yo') | |
module.exports = (props) => { | |
return html` | |
<li> | |
<button type="button" onclick=${props.getFolder}>${props.title}</button> | |
</li> | |
` | |
} | |
},{"yo-yo":80}],89:[function(require,module,exports){ | |
const html = require('yo-yo') | |
const Breadcrumb = require('./Breadcrumb') | |
module.exports = (props) => { | |
return html` | |
<ul class="UppyProvider-breadcrumbs"> | |
${ | |
props.directories.map((directory, i) => { | |
return Breadcrumb({ | |
getFolder: () => props.getFolder(directory.id), | |
title: i === 0 ? props.title : directory.title | |
}) | |
}) | |
} | |
</ul> | |
` | |
} | |
},{"./Breadcrumb":88,"yo-yo":80}],90:[function(require,module,exports){ | |
const html = require('yo-yo') | |
const Breadcrumbs = require('./Breadcrumbs') | |
const Table = require('./Table') | |
module.exports = (props) => { | |
let filteredFolders = props.folders | |
let filteredFiles = props.files | |
if (props.filterInput !== '') { | |
filteredFolders = props.filterItems(props.folders) | |
filteredFiles = props.filterItems(props.files) | |
} | |
return html` | |
<div class="Browser Browser-viewType--${props.viewType}"> | |
<header class="Browser-header"> | |
<div class="Browser-search" aria-hidden="${!props.isSearchVisible}"> | |
<input type="text" class="Browser-searchInput" placeholder="Search" | |
onkeyup=${props.filterQuery} value="${props.filterInput}"/> | |
<button type="button" class="Browser-searchClose" | |
onclick=${props.toggleSearch}> | |
<svg class="UppyIcon" viewBox="0 0 19 19"> | |
<path d="M17.318 17.232L9.94 9.854 9.586 9.5l-.354.354-7.378 7.378h.707l-.62-.62v.706L9.318 9.94l.354-.354-.354-.354L1.94 1.854v.707l.62-.62h-.706l7.378 7.378.354.354.354-.354 7.378-7.378h-.707l.622.62v-.706L9.854 9.232l-.354.354.354.354 7.378 7.378.708-.707-7.38-7.378v.708l7.38-7.38.353-.353-.353-.353-.622-.622-.353-.353-.354.352-7.378 7.38h.708L2.56 1.23 2.208.88l-.353.353-.622.62-.353.355.352.353 7.38 7.38v-.708l-7.38 7.38-.353.353.352.353.622.622.353.353.354-.353 7.38-7.38h-.708l7.38 7.38z"/> | |
</svg> | |
</button> | |
</div> | |
<div class="Browser-headerBar"> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment