/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./src/converse.js"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./3rdparty/lodash.fp.js": /*!*******************************!*\ !*** ./3rdparty/lodash.fp.js ***! \*******************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; (function webpackUniversalModuleDefinition(root, factory) { if (true) module.exports = factory();else {} })(void 0, function () { return ( /******/ function (modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if (installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }( /************************************************************************/ /******/ [ /* 0 */ /***/ function (module, exports, __webpack_require__) { var baseConvert = __webpack_require__(1); /** * Converts `lodash` to an immutable auto-curried iteratee-first data-last * version with conversion `options` applied. * * @param {Function} lodash The lodash function to convert. * @param {Object} [options] The options object. See `baseConvert` for more details. * @returns {Function} Returns the converted `lodash`. */ function browserConvert(lodash, options) { return baseConvert(lodash, lodash, options); } if (typeof _ == 'function' && typeof _.runInContext == 'function') { // XXX: Customization in order to be able to run both _ and fp in the // non-AMD usecase. fp = browserConvert(_.runInContext()); } module.exports = browserConvert; /***/ }, /* 1 */ /***/ function (module, exports, __webpack_require__) { var mapping = __webpack_require__(2), fallbackHolder = __webpack_require__(3); /** Built-in value reference. */ var push = Array.prototype.push; /** * Creates a function, with an arity of `n`, that invokes `func` with the * arguments it receives. * * @private * @param {Function} func The function to wrap. * @param {number} n The arity of the new function. * @returns {Function} Returns the new function. */ function baseArity(func, n) { return n == 2 ? function (a, b) { return func.apply(undefined, arguments); } : function (a) { return func.apply(undefined, arguments); }; } /** * Creates a function that invokes `func`, with up to `n` arguments, ignoring * any additional arguments. * * @private * @param {Function} func The function to cap arguments for. * @param {number} n The arity cap. * @returns {Function} Returns the new function. */ function baseAry(func, n) { return n == 2 ? function (a, b) { return func(a, b); } : function (a) { return func(a); }; } /** * Creates a clone of `array`. * * @private * @param {Array} array The array to clone. * @returns {Array} Returns the cloned array. */ function cloneArray(array) { var length = array ? array.length : 0, result = Array(length); while (length--) { result[length] = array[length]; } return result; } /** * Creates a function that clones a given object using the assignment `func`. * * @private * @param {Function} func The assignment function. * @returns {Function} Returns the new cloner function. */ function createCloner(func) { return function (object) { return func({}, object); }; } /** * A specialized version of `_.spread` which flattens the spread array into * the arguments of the invoked `func`. * * @private * @param {Function} func The function to spread arguments over. * @param {number} start The start position of the spread. * @returns {Function} Returns the new function. */ function flatSpread(func, start) { return function () { var length = arguments.length, lastIndex = length - 1, args = Array(length); while (length--) { args[length] = arguments[length]; } var array = args[start], otherArgs = args.slice(0, start); if (array) { push.apply(otherArgs, array); } if (start != lastIndex) { push.apply(otherArgs, args.slice(start + 1)); } return func.apply(this, otherArgs); }; } /** * Creates a function that wraps `func` and uses `cloner` to clone the first * argument it receives. * * @private * @param {Function} func The function to wrap. * @param {Function} cloner The function to clone arguments. * @returns {Function} Returns the new immutable function. */ function wrapImmutable(func, cloner) { return function () { var length = arguments.length; if (!length) { return; } var args = Array(length); while (length--) { args[length] = arguments[length]; } var result = args[0] = cloner.apply(undefined, args); func.apply(undefined, args); return result; }; } /** * The base implementation of `convert` which accepts a `util` object of methods * required to perform conversions. * * @param {Object} util The util object. * @param {string} name The name of the function to convert. * @param {Function} func The function to convert. * @param {Object} [options] The options object. * @param {boolean} [options.cap=true] Specify capping iteratee arguments. * @param {boolean} [options.curry=true] Specify currying. * @param {boolean} [options.fixed=true] Specify fixed arity. * @param {boolean} [options.immutable=true] Specify immutable operations. * @param {boolean} [options.rearg=true] Specify rearranging arguments. * @returns {Function|Object} Returns the converted function or object. */ function baseConvert(util, name, func, options) { var setPlaceholder, isLib = typeof name == 'function', isObj = name === Object(name); if (isObj) { options = func; func = name; name = undefined; } if (func == null) { throw new TypeError(); } options || (options = {}); var config = { 'cap': 'cap' in options ? options.cap : true, 'curry': 'curry' in options ? options.curry : true, 'fixed': 'fixed' in options ? options.fixed : true, 'immutable': 'immutable' in options ? options.immutable : true, 'rearg': 'rearg' in options ? options.rearg : true }; var forceCurry = 'curry' in options && options.curry, forceFixed = 'fixed' in options && options.fixed, forceRearg = 'rearg' in options && options.rearg, placeholder = isLib ? func : fallbackHolder, pristine = isLib ? func.runInContext() : undefined; var helpers = isLib ? func : { 'ary': util.ary, 'assign': util.assign, 'clone': util.clone, 'curry': util.curry, 'forEach': util.forEach, 'isArray': util.isArray, 'isFunction': util.isFunction, 'iteratee': util.iteratee, 'keys': util.keys, 'rearg': util.rearg, 'toInteger': util.toInteger, 'toPath': util.toPath }; var ary = helpers.ary, assign = helpers.assign, clone = helpers.clone, curry = helpers.curry, each = helpers.forEach, isArray = helpers.isArray, isFunction = helpers.isFunction, keys = helpers.keys, rearg = helpers.rearg, toInteger = helpers.toInteger, toPath = helpers.toPath; var aryMethodKeys = keys(mapping.aryMethod); var wrappers = { 'castArray': function castArray(_castArray) { return function () { var value = arguments[0]; return isArray(value) ? _castArray(cloneArray(value)) : _castArray.apply(undefined, arguments); }; }, 'iteratee': function iteratee(_iteratee) { return function () { var func = arguments[0], arity = arguments[1], result = _iteratee(func, arity), length = result.length; if (config.cap && typeof arity == 'number') { arity = arity > 2 ? arity - 2 : 1; return length && length <= arity ? result : baseAry(result, arity); } return result; }; }, 'mixin': function mixin(_mixin) { return function (source) { var func = this; if (!isFunction(func)) { return _mixin(func, Object(source)); } var pairs = []; each(keys(source), function (key) { if (isFunction(source[key])) { pairs.push([key, func.prototype[key]]); } }); _mixin(func, Object(source)); each(pairs, function (pair) { var value = pair[1]; if (isFunction(value)) { func.prototype[pair[0]] = value; } else { delete func.prototype[pair[0]]; } }); return func; }; }, 'nthArg': function nthArg(_nthArg) { return function (n) { var arity = n < 0 ? 1 : toInteger(n) + 1; return curry(_nthArg(n), arity); }; }, 'rearg': function rearg(_rearg) { return function (func, indexes) { var arity = indexes ? indexes.length : 0; return curry(_rearg(func, indexes), arity); }; }, 'runInContext': function runInContext(_runInContext) { return function (context) { return baseConvert(util, _runInContext(context), options); }; } }; /*--------------------------------------------------------------------------*/ /** * Casts `func` to a function with an arity capped iteratee if needed. * * @private * @param {string} name The name of the function to inspect. * @param {Function} func The function to inspect. * @returns {Function} Returns the cast function. */ function castCap(name, func) { if (config.cap) { var indexes = mapping.iterateeRearg[name]; if (indexes) { return iterateeRearg(func, indexes); } var n = !isLib && mapping.iterateeAry[name]; if (n) { return iterateeAry(func, n); } } return func; } /** * Casts `func` to a curried function if needed. * * @private * @param {string} name The name of the function to inspect. * @param {Function} func The function to inspect. * @param {number} n The arity of `func`. * @returns {Function} Returns the cast function. */ function castCurry(name, func, n) { return forceCurry || config.curry && n > 1 ? curry(func, n) : func; } /** * Casts `func` to a fixed arity function if needed. * * @private * @param {string} name The name of the function to inspect. * @param {Function} func The function to inspect. * @param {number} n The arity cap. * @returns {Function} Returns the cast function. */ function castFixed(name, func, n) { if (config.fixed && (forceFixed || !mapping.skipFixed[name])) { var data = mapping.methodSpread[name], start = data && data.start; return start === undefined ? ary(func, n) : flatSpread(func, start); } return func; } /** * Casts `func` to an rearged function if needed. * * @private * @param {string} name The name of the function to inspect. * @param {Function} func The function to inspect. * @param {number} n The arity of `func`. * @returns {Function} Returns the cast function. */ function castRearg(name, func, n) { return config.rearg && n > 1 && (forceRearg || !mapping.skipRearg[name]) ? rearg(func, mapping.methodRearg[name] || mapping.aryRearg[n]) : func; } /** * Creates a clone of `object` by `path`. * * @private * @param {Object} object The object to clone. * @param {Array|string} path The path to clone by. * @returns {Object} Returns the cloned object. */ function cloneByPath(object, path) { path = toPath(path); var index = -1, length = path.length, lastIndex = length - 1, result = clone(Object(object)), nested = result; while (nested != null && ++index < length) { var key = path[index], value = nested[key]; if (value != null) { nested[path[index]] = clone(index == lastIndex ? value : Object(value)); } nested = nested[key]; } return result; } /** * Converts `lodash` to an immutable auto-curried iteratee-first data-last * version with conversion `options` applied. * * @param {Object} [options] The options object. See `baseConvert` for more details. * @returns {Function} Returns the converted `lodash`. */ function convertLib(options) { return _.runInContext.convert(options)(undefined); } /** * Create a converter function for `func` of `name`. * * @param {string} name The name of the function to convert. * @param {Function} func The function to convert. * @returns {Function} Returns the new converter function. */ function createConverter(name, func) { var realName = mapping.aliasToReal[name] || name, methodName = mapping.remap[realName] || realName, oldOptions = options; return function (options) { var newUtil = isLib ? pristine : helpers, newFunc = isLib ? pristine[methodName] : func, newOptions = assign(assign({}, oldOptions), options); return baseConvert(newUtil, realName, newFunc, newOptions); }; } /** * Creates a function that wraps `func` to invoke its iteratee, with up to `n` * arguments, ignoring any additional arguments. * * @private * @param {Function} func The function to cap iteratee arguments for. * @param {number} n The arity cap. * @returns {Function} Returns the new function. */ function iterateeAry(func, n) { return overArg(func, function (func) { return typeof func == 'function' ? baseAry(func, n) : func; }); } /** * Creates a function that wraps `func` to invoke its iteratee with arguments * arranged according to the specified `indexes` where the argument value at * the first index is provided as the first argument, the argument value at * the second index is provided as the second argument, and so on. * * @private * @param {Function} func The function to rearrange iteratee arguments for. * @param {number[]} indexes The arranged argument indexes. * @returns {Function} Returns the new function. */ function iterateeRearg(func, indexes) { return overArg(func, function (func) { var n = indexes.length; return baseArity(rearg(baseAry(func, n), indexes), n); }); } /** * Creates a function that invokes `func` with its first argument transformed. * * @private * @param {Function} func The function to wrap. * @param {Function} transform The argument transform. * @returns {Function} Returns the new function. */ function overArg(func, transform) { return function () { var length = arguments.length; if (!length) { return func(); } var args = Array(length); while (length--) { args[length] = arguments[length]; } var index = config.rearg ? 0 : length - 1; args[index] = transform(args[index]); return func.apply(undefined, args); }; } /** * Creates a function that wraps `func` and applys the conversions * rules by `name`. * * @private * @param {string} name The name of the function to wrap. * @param {Function} func The function to wrap. * @returns {Function} Returns the converted function. */ function wrap(name, func) { var result, realName = mapping.aliasToReal[name] || name, wrapped = func, wrapper = wrappers[realName]; if (wrapper) { wrapped = wrapper(func); } else if (config.immutable) { if (mapping.mutate.array[realName]) { wrapped = wrapImmutable(func, cloneArray); } else if (mapping.mutate.object[realName]) { wrapped = wrapImmutable(func, createCloner(func)); } else if (mapping.mutate.set[realName]) { wrapped = wrapImmutable(func, cloneByPath); } } each(aryMethodKeys, function (aryKey) { each(mapping.aryMethod[aryKey], function (otherName) { if (realName == otherName) { var data = mapping.methodSpread[realName], afterRearg = data && data.afterRearg; result = afterRearg ? castFixed(realName, castRearg(realName, wrapped, aryKey), aryKey) : castRearg(realName, castFixed(realName, wrapped, aryKey), aryKey); result = castCap(realName, result); result = castCurry(realName, result, aryKey); return false; } }); return !result; }); result || (result = wrapped); if (result == func) { result = forceCurry ? curry(result, 1) : function () { return func.apply(this, arguments); }; } result.convert = createConverter(realName, func); if (mapping.placeholder[realName]) { setPlaceholder = true; result.placeholder = func.placeholder = placeholder; } return result; } /*--------------------------------------------------------------------------*/ if (!isObj) { return wrap(name, func); } var _ = func; // Convert methods by ary cap. var pairs = []; each(aryMethodKeys, function (aryKey) { each(mapping.aryMethod[aryKey], function (key) { var func = _[mapping.remap[key] || key]; if (func) { pairs.push([key, wrap(key, func)]); } }); }); // Convert remaining methods. each(keys(_), function (key) { var func = _[key]; if (typeof func == 'function') { var length = pairs.length; while (length--) { if (pairs[length][0] == key) { return; } } func.convert = createConverter(key, func); pairs.push([key, func]); } }); // Assign to `_` leaving `_.prototype` unchanged to allow chaining. each(pairs, function (pair) { _[pair[0]] = pair[1]; }); _.convert = convertLib; if (setPlaceholder) { _.placeholder = placeholder; } // Assign aliases. each(keys(_), function (key) { each(mapping.realToAlias[key] || [], function (alias) { _[alias] = _[key]; }); }); return _; } module.exports = baseConvert; /***/ }, /* 2 */ /***/ function (module, exports) { /** Used to map aliases to their real names. */ exports.aliasToReal = { // Lodash aliases. 'each': 'forEach', 'eachRight': 'forEachRight', 'entries': 'toPairs', 'entriesIn': 'toPairsIn', 'extend': 'assignIn', 'extendAll': 'assignInAll', 'extendAllWith': 'assignInAllWith', 'extendWith': 'assignInWith', 'first': 'head', // Methods that are curried variants of others. 'conforms': 'conformsTo', 'matches': 'isMatch', 'property': 'get', // Ramda aliases. '__': 'placeholder', 'F': 'stubFalse', 'T': 'stubTrue', 'all': 'every', 'allPass': 'overEvery', 'always': 'constant', 'any': 'some', 'anyPass': 'overSome', 'apply': 'spread', 'assoc': 'set', 'assocPath': 'set', 'complement': 'negate', 'compose': 'flowRight', 'contains': 'includes', 'dissoc': 'unset', 'dissocPath': 'unset', 'dropLast': 'dropRight', 'dropLastWhile': 'dropRightWhile', 'equals': 'isEqual', 'identical': 'eq', 'indexBy': 'keyBy', 'init': 'initial', 'invertObj': 'invert', 'juxt': 'over', 'omitAll': 'omit', 'nAry': 'ary', 'path': 'get', 'pathEq': 'matchesProperty', 'pathOr': 'getOr', 'paths': 'at', 'pickAll': 'pick', 'pipe': 'flow', 'pluck': 'map', 'prop': 'get', 'propEq': 'matchesProperty', 'propOr': 'getOr', 'props': 'at', 'symmetricDifference': 'xor', 'symmetricDifferenceBy': 'xorBy', 'symmetricDifferenceWith': 'xorWith', 'takeLast': 'takeRight', 'takeLastWhile': 'takeRightWhile', 'unapply': 'rest', 'unnest': 'flatten', 'useWith': 'overArgs', 'where': 'conformsTo', 'whereEq': 'isMatch', 'zipObj': 'zipObject' }; /** Used to map ary to method names. */ exports.aryMethod = { '1': ['assignAll', 'assignInAll', 'attempt', 'castArray', 'ceil', 'create', 'curry', 'curryRight', 'defaultsAll', 'defaultsDeepAll', 'floor', 'flow', 'flowRight', 'fromPairs', 'invert', 'iteratee', 'memoize', 'method', 'mergeAll', 'methodOf', 'mixin', 'nthArg', 'over', 'overEvery', 'overSome', 'rest', 'reverse', 'round', 'runInContext', 'spread', 'template', 'trim', 'trimEnd', 'trimStart', 'uniqueId', 'words', 'zipAll'], '2': ['add', 'after', 'ary', 'assign', 'assignAllWith', 'assignIn', 'assignInAllWith', 'at', 'before', 'bind', 'bindAll', 'bindKey', 'chunk', 'cloneDeepWith', 'cloneWith', 'concat', 'conformsTo', 'countBy', 'curryN', 'curryRightN', 'debounce', 'defaults', 'defaultsDeep', 'defaultTo', 'delay', 'difference', 'divide', 'drop', 'dropRight', 'dropRightWhile', 'dropWhile', 'endsWith', 'eq', 'every', 'filter', 'find', 'findIndex', 'findKey', 'findLast', 'findLastIndex', 'findLastKey', 'flatMap', 'flatMapDeep', 'flattenDepth', 'forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight', 'get', 'groupBy', 'gt', 'gte', 'has', 'hasIn', 'includes', 'indexOf', 'intersection', 'invertBy', 'invoke', 'invokeMap', 'isEqual', 'isMatch', 'join', 'keyBy', 'lastIndexOf', 'lt', 'lte', 'map', 'mapKeys', 'mapValues', 'matchesProperty', 'maxBy', 'meanBy', 'merge', 'mergeAllWith', 'minBy', 'multiply', 'nth', 'omit', 'omitBy', 'overArgs', 'pad', 'padEnd', 'padStart', 'parseInt', 'partial', 'partialRight', 'partition', 'pick', 'pickBy', 'propertyOf', 'pull', 'pullAll', 'pullAt', 'random', 'range', 'rangeRight', 'rearg', 'reject', 'remove', 'repeat', 'restFrom', 'result', 'sampleSize', 'some', 'sortBy', 'sortedIndex', 'sortedIndexOf', 'sortedLastIndex', 'sortedLastIndexOf', 'sortedUniqBy', 'split', 'spreadFrom', 'startsWith', 'subtract', 'sumBy', 'take', 'takeRight', 'takeRightWhile', 'takeWhile', 'tap', 'throttle', 'thru', 'times', 'trimChars', 'trimCharsEnd', 'trimCharsStart', 'truncate', 'union', 'uniqBy', 'uniqWith', 'unset', 'unzipWith', 'without', 'wrap', 'xor', 'zip', 'zipObject', 'zipObjectDeep'], '3': ['assignInWith', 'assignWith', 'clamp', 'differenceBy', 'differenceWith', 'findFrom', 'findIndexFrom', 'findLastFrom', 'findLastIndexFrom', 'getOr', 'includesFrom', 'indexOfFrom', 'inRange', 'intersectionBy', 'intersectionWith', 'invokeArgs', 'invokeArgsMap', 'isEqualWith', 'isMatchWith', 'flatMapDepth', 'lastIndexOfFrom', 'mergeWith', 'orderBy', 'padChars', 'padCharsEnd', 'padCharsStart', 'pullAllBy', 'pullAllWith', 'rangeStep', 'rangeStepRight', 'reduce', 'reduceRight', 'replace', 'set', 'slice', 'sortedIndexBy', 'sortedLastIndexBy', 'transform', 'unionBy', 'unionWith', 'update', 'xorBy', 'xorWith', 'zipWith'], '4': ['fill', 'setWith', 'updateWith'] }; /** Used to map ary to rearg configs. */ exports.aryRearg = { '2': [1, 0], '3': [2, 0, 1], '4': [3, 2, 0, 1] }; /** Used to map method names to their iteratee ary. */ exports.iterateeAry = { 'dropRightWhile': 1, 'dropWhile': 1, 'every': 1, 'filter': 1, 'find': 1, 'findFrom': 1, 'findIndex': 1, 'findIndexFrom': 1, 'findKey': 1, 'findLast': 1, 'findLastFrom': 1, 'findLastIndex': 1, 'findLastIndexFrom': 1, 'findLastKey': 1, 'flatMap': 1, 'flatMapDeep': 1, 'flatMapDepth': 1, 'forEach': 1, 'forEachRight': 1, 'forIn': 1, 'forInRight': 1, 'forOwn': 1, 'forOwnRight': 1, 'map': 1, 'mapKeys': 1, 'mapValues': 1, 'partition': 1, 'reduce': 2, 'reduceRight': 2, 'reject': 1, 'remove': 1, 'some': 1, 'takeRightWhile': 1, 'takeWhile': 1, 'times': 1, 'transform': 2 }; /** Used to map method names to iteratee rearg configs. */ exports.iterateeRearg = { 'mapKeys': [1], 'reduceRight': [1, 0] }; /** Used to map method names to rearg configs. */ exports.methodRearg = { 'assignInAllWith': [1, 0], 'assignInWith': [1, 2, 0], 'assignAllWith': [1, 0], 'assignWith': [1, 2, 0], 'differenceBy': [1, 2, 0], 'differenceWith': [1, 2, 0], 'getOr': [2, 1, 0], 'intersectionBy': [1, 2, 0], 'intersectionWith': [1, 2, 0], 'isEqualWith': [1, 2, 0], 'isMatchWith': [2, 1, 0], 'mergeAllWith': [1, 0], 'mergeWith': [1, 2, 0], 'padChars': [2, 1, 0], 'padCharsEnd': [2, 1, 0], 'padCharsStart': [2, 1, 0], 'pullAllBy': [2, 1, 0], 'pullAllWith': [2, 1, 0], 'rangeStep': [1, 2, 0], 'rangeStepRight': [1, 2, 0], 'setWith': [3, 1, 2, 0], 'sortedIndexBy': [2, 1, 0], 'sortedLastIndexBy': [2, 1, 0], 'unionBy': [1, 2, 0], 'unionWith': [1, 2, 0], 'updateWith': [3, 1, 2, 0], 'xorBy': [1, 2, 0], 'xorWith': [1, 2, 0], 'zipWith': [1, 2, 0] }; /** Used to map method names to spread configs. */ exports.methodSpread = { 'assignAll': { 'start': 0 }, 'assignAllWith': { 'start': 0 }, 'assignInAll': { 'start': 0 }, 'assignInAllWith': { 'start': 0 }, 'defaultsAll': { 'start': 0 }, 'defaultsDeepAll': { 'start': 0 }, 'invokeArgs': { 'start': 2 }, 'invokeArgsMap': { 'start': 2 }, 'mergeAll': { 'start': 0 }, 'mergeAllWith': { 'start': 0 }, 'partial': { 'start': 1 }, 'partialRight': { 'start': 1 }, 'without': { 'start': 1 }, 'zipAll': { 'start': 0 } }; /** Used to identify methods which mutate arrays or objects. */ exports.mutate = { 'array': { 'fill': true, 'pull': true, 'pullAll': true, 'pullAllBy': true, 'pullAllWith': true, 'pullAt': true, 'remove': true, 'reverse': true }, 'object': { 'assign': true, 'assignAll': true, 'assignAllWith': true, 'assignIn': true, 'assignInAll': true, 'assignInAllWith': true, 'assignInWith': true, 'assignWith': true, 'defaults': true, 'defaultsAll': true, 'defaultsDeep': true, 'defaultsDeepAll': true, 'merge': true, 'mergeAll': true, 'mergeAllWith': true, 'mergeWith': true }, 'set': { 'set': true, 'setWith': true, 'unset': true, 'update': true, 'updateWith': true } }; /** Used to track methods with placeholder support */ exports.placeholder = { 'bind': true, 'bindKey': true, 'curry': true, 'curryRight': true, 'partial': true, 'partialRight': true }; /** Used to map real names to their aliases. */ exports.realToAlias = function () { var hasOwnProperty = Object.prototype.hasOwnProperty, object = exports.aliasToReal, result = {}; for (var key in object) { var value = object[key]; if (hasOwnProperty.call(result, value)) { result[value].push(key); } else { result[value] = [key]; } } return result; }(); /** Used to map method names to other names. */ exports.remap = { 'assignAll': 'assign', 'assignAllWith': 'assignWith', 'assignInAll': 'assignIn', 'assignInAllWith': 'assignInWith', 'curryN': 'curry', 'curryRightN': 'curryRight', 'defaultsAll': 'defaults', 'defaultsDeepAll': 'defaultsDeep', 'findFrom': 'find', 'findIndexFrom': 'findIndex', 'findLastFrom': 'findLast', 'findLastIndexFrom': 'findLastIndex', 'getOr': 'get', 'includesFrom': 'includes', 'indexOfFrom': 'indexOf', 'invokeArgs': 'invoke', 'invokeArgsMap': 'invokeMap', 'lastIndexOfFrom': 'lastIndexOf', 'mergeAll': 'merge', 'mergeAllWith': 'mergeWith', 'padChars': 'pad', 'padCharsEnd': 'padEnd', 'padCharsStart': 'padStart', 'propertyOf': 'get', 'rangeStep': 'range', 'rangeStepRight': 'rangeRight', 'restFrom': 'rest', 'spreadFrom': 'spread', 'trimChars': 'trim', 'trimCharsEnd': 'trimEnd', 'trimCharsStart': 'trimStart', 'zipAll': 'zip' }; /** Used to track methods that skip fixing their arity. */ exports.skipFixed = { 'castArray': true, 'flow': true, 'flowRight': true, 'iteratee': true, 'mixin': true, 'rearg': true, 'runInContext': true }; /** Used to track methods that skip rearranging arguments. */ exports.skipRearg = { 'add': true, 'assign': true, 'assignIn': true, 'bind': true, 'bindKey': true, 'concat': true, 'difference': true, 'divide': true, 'eq': true, 'gt': true, 'gte': true, 'isEqual': true, 'lt': true, 'lte': true, 'matchesProperty': true, 'merge': true, 'multiply': true, 'overArgs': true, 'partial': true, 'partialRight': true, 'propertyOf': true, 'random': true, 'range': true, 'rangeRight': true, 'subtract': true, 'zip': true, 'zipObject': true, 'zipObjectDeep': true }; /***/ }, /* 3 */ /***/ function (module, exports) { /** * The default argument placeholder value for methods. * * @type {Object} */ module.exports = {}; /***/ } /******/ ]) ); }); ; /***/ }), /***/ "./node_modules/awesomplete-avoid-xss/awesomplete.js": /*!***********************************************************!*\ !*** ./node_modules/awesomplete-avoid-xss/awesomplete.js ***! \***********************************************************/ /*! no static exports found */ /***/ (function(module, exports) { /** * Simple, lightweight, usable local autocomplete library for modern browsers * Because there weren’t enough autocomplete scripts in the world? Because I’m completely insane and have NIH syndrome? Probably both. :P * @author Lea Verou http://leaverou.github.io/awesomplete * MIT license */ (function () { var _ = function (input, o) { var me = this; // Setup this.isOpened = false; this.input = $(input); this.input.setAttribute("autocomplete", "off"); this.input.setAttribute("aria-autocomplete", "list"); o = o || {}; configure(this, { minChars: 2, maxItems: 10, autoFirst: false, data: _.DATA, filter: _.FILTER_CONTAINS, sort: o.sort === false ? false : _.SORT_BYLENGTH, item: _.ITEM, replace: _.REPLACE }, o); this.index = -1; // Create necessary elements this.container = $.create("div", { className: "awesomplete", around: input }); this.ul = $.create("ul", { hidden: "hidden", inside: this.container }); this.status = $.create("span", { className: "visually-hidden", role: "status", "aria-live": "assertive", "aria-relevant": "additions", inside: this.container }); // Bind events this._events = { input: { "input": this.evaluate.bind(this), "blur": this.close.bind(this, { reason: "blur" }), "keydown": function(evt) { var c = evt.keyCode; // If the dropdown `ul` is in view, then act on keydown for the following keys: // Enter / Esc / Up / Down if(me.opened) { if (c === 13 && me.selected) { // Enter evt.preventDefault(); me.select(); } else if (c === 27) { // Esc me.close({ reason: "esc" }); } else if (c === 38 || c === 40) { // Down/Up arrow evt.preventDefault(); me[c === 38? "previous" : "next"](); } } } }, form: { "submit": this.close.bind(this, { reason: "submit" }) }, ul: { "mousedown": function(evt) { var li = evt.target; if (li !== this) { while (li && !/li/i.test(li.nodeName)) { li = li.parentNode; } if (li && evt.button === 0) { // Only select on left click evt.preventDefault(); me.select(li, evt.target); } } } } }; $.bind(this.input, this._events.input); $.bind(this.input.form, this._events.form); $.bind(this.ul, this._events.ul); if (this.input.hasAttribute("list")) { this.list = "#" + this.input.getAttribute("list"); this.input.removeAttribute("list"); } else { this.list = this.input.getAttribute("data-list") || o.list || []; } _.all.push(this); }; _.prototype = { set list(list) { if (Array.isArray(list)) { this._list = list; } else if (typeof list === "string" && list.indexOf(",") > -1) { this._list = list.split(/\s*,\s*/); } else { // Element or CSS selector list = $(list); if (list && list.children) { var items = []; slice.apply(list.children).forEach(function (el) { if (!el.disabled) { var text = el.textContent.trim(); var value = el.value || text; var label = el.label || text; if (value !== "") { items.push({ label: label, value: value }); } } }); this._list = items; } } if (document.activeElement === this.input) { this.evaluate(); } }, get selected() { return this.index > -1; }, get opened() { return this.isOpened; }, close: function (o) { if (!this.opened) { return; } this.ul.setAttribute("hidden", ""); this.isOpened = false; this.index = -1; $.fire(this.input, "awesomplete-close", o || {}); }, open: function () { this.ul.removeAttribute("hidden"); this.isOpened = true; if (this.autoFirst && this.index === -1) { this.goto(0); } $.fire(this.input, "awesomplete-open"); }, destroy: function() { //remove events from the input and its form $.unbind(this.input, this._events.input); $.unbind(this.input.form, this._events.form); //move the input out of the awesomplete container and remove the container and its children var parentNode = this.container.parentNode; parentNode.insertBefore(this.input, this.container); parentNode.removeChild(this.container); //remove autocomplete and aria-autocomplete attributes this.input.removeAttribute("autocomplete"); this.input.removeAttribute("aria-autocomplete"); //remove this awesomeplete instance from the global array of instances var indexOfAwesomplete = _.all.indexOf(this); if (indexOfAwesomplete !== -1) { _.all.splice(indexOfAwesomplete, 1); } }, next: function () { var count = this.ul.children.length; this.goto(this.index < count - 1 ? this.index + 1 : (count ? 0 : -1) ); }, previous: function () { var count = this.ul.children.length; var pos = this.index - 1; this.goto(this.selected && pos !== -1 ? pos : count - 1); }, // Should not be used, highlights specific item without any checks! goto: function (i) { var lis = this.ul.children; if (this.selected) { lis[this.index].setAttribute("aria-selected", "false"); } this.index = i; if (i > -1 && lis.length > 0) { lis[i].setAttribute("aria-selected", "true"); this.status.textContent = lis[i].textContent; // scroll to highlighted element in case parent's height is fixed this.ul.scrollTop = lis[i].offsetTop - this.ul.clientHeight + lis[i].clientHeight; $.fire(this.input, "awesomplete-highlight", { text: this.suggestions[this.index] }); } }, select: function (selected, origin) { if (selected) { this.index = $.siblingIndex(selected); } else { selected = this.ul.children[this.index]; } if (selected) { var suggestion = this.suggestions[this.index]; var allowed = $.fire(this.input, "awesomplete-select", { text: suggestion, origin: origin || selected }); if (allowed) { this.replace(suggestion); this.close({ reason: "select" }); $.fire(this.input, "awesomplete-selectcomplete", { text: suggestion }); } } }, evaluate: function() { var me = this; var value = this.input.value; if (value.length >= this.minChars && this._list.length > 0) { this.index = -1; // Populate list with options that match this.ul.innerHTML = ""; this.suggestions = this._list .map(function(item) { return new Suggestion(me.data(item, value)); }) .filter(function(item) { return me.filter(item, value); }); if (this.sort !== false) { this.suggestions = this.suggestions.sort(this.sort); } this.suggestions = this.suggestions.slice(0, this.maxItems); this.suggestions.forEach(function(text) { me.ul.appendChild(me.item(text, value)); }); if (this.ul.children.length === 0) { this.close({ reason: "nomatches" }); } else { this.open(); } } else { this.close({ reason: "nomatches" }); } } }; // Static methods/properties _.all = []; _.FILTER_CONTAINS = function (text, input) { return RegExp($.regExpEscape(input.trim()), "i").test(text); }; _.FILTER_STARTSWITH = function (text, input) { return RegExp("^" + $.regExpEscape(input.trim()), "i").test(text); }; _.SORT_BYLENGTH = function (a, b) { if (a.length !== b.length) { return a.length - b.length; } return a < b? -1 : 1; }; _.ITEM = function (text, input) { input = input.trim(); var element = document.createElement("li"); element.setAttribute("aria-selected", "false"); var regex = new RegExp("("+input+")", "ig"); var parts = input ? text.split(regex) : [text]; parts.forEach(function (txt) { if (input && txt.match(regex)) { var match = document.createElement("mark"); match.textContent = txt; element.appendChild(match); } else { element.appendChild(document.createTextNode(txt)); } }); return element; }; _.REPLACE = function (text) { this.input.value = text.value; }; _.DATA = function (item/*, input*/) { return item; }; // Private functions function Suggestion(data) { var o = Array.isArray(data) ? { label: data[0], value: data[1] } : typeof data === "object" && "label" in data && "value" in data ? data : { label: data, value: data }; this.label = o.label || o.value; this.value = o.value; } Object.defineProperty(Suggestion.prototype = Object.create(String.prototype), "length", { get: function() { return this.label.length; } }); Suggestion.prototype.toString = Suggestion.prototype.valueOf = function () { return "" + this.label; }; function configure(instance, properties, o) { for (var i in properties) { var initial = properties[i], attrValue = instance.input.getAttribute("data-" + i.toLowerCase()); if (typeof initial === "number") { instance[i] = parseInt(attrValue); } else if (initial === false) { // Boolean options must be false by default anyway instance[i] = attrValue !== null; } else if (initial instanceof Function) { instance[i] = null; } else { instance[i] = attrValue; } if (!instance[i] && instance[i] !== 0) { instance[i] = (i in o)? o[i] : initial; } } } // Helpers var slice = Array.prototype.slice; function $(expr, con) { return typeof expr === "string"? (con || document).querySelector(expr) : expr || null; } function $$(expr, con) { return slice.call((con || document).querySelectorAll(expr)); } $.create = function(tag, o) { var element = document.createElement(tag); for (var i in o) { var val = o[i]; if (i === "inside") { $(val).appendChild(element); } else if (i === "around") { var ref = $(val); ref.parentNode.insertBefore(element, ref); element.appendChild(ref); } else if (i in element) { element[i] = val; } else { element.setAttribute(i, val); } } return element; }; $.bind = function(element, o) { if (element) { for (var event in o) { var callback = o[event]; event.split(/\s+/).forEach(function (event) { element.addEventListener(event, callback); }); } } }; $.unbind = function(element, o) { if (element) { for (var event in o) { var callback = o[event]; event.split(/\s+/).forEach(function(event) { element.removeEventListener(event, callback); }); } } }; $.fire = function(target, type, properties) { var evt = document.createEvent("HTMLEvents"); evt.initEvent(type, true, true ); for (var j in properties) { evt[j] = properties[j]; } return target.dispatchEvent(evt); }; $.regExpEscape = function (s) { return s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); }; $.siblingIndex = function (el) { /* eslint-disable no-cond-assign */ for (var i = 0; el = el.previousElementSibling; i++); return i; }; // Initialization function init() { $$("input.awesomplete").forEach(function (input) { new _(input); }); } // Are we in a browser? Check for Document constructor if (typeof Document !== "undefined") { // DOM already loaded? if (document.readyState !== "loading") { init(); } else { // Wait for it document.addEventListener("DOMContentLoaded", init); } } _.$ = $; _.$$ = $$; // Make sure to export Awesomplete on self when in a browser if (typeof self !== "undefined") { self.Awesomplete = _; } // Expose Awesomplete as a CJS module if (typeof module === "object" && module.exports) { module.exports = _; } return _; }()); /*** EXPORTS FROM exports-loader ***/ module.exports = Awesomplete; /***/ }), /***/ "./node_modules/backbone.browserStorage/backbone.browserStorage.js": /*!*************************************************************************!*\ !*** ./node_modules/backbone.browserStorage/backbone.browserStorage.js ***! \*************************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { /** * Backbone localStorage and sessionStorage Adapter * Version 0.0.3 * * https://github.com/jcbrand/Backbone.browserStorage */ (function (root, factory) { if (true) { module.exports = factory(__webpack_require__(/*! backbone */ "./node_modules/backbone/backbone.js"), __webpack_require__(/*! underscore */ "./src/underscore-shim.js")); } else {} }(this, function(Backbone, _) { // A simple module to replace `Backbone.sync` with *browser storage*-based // persistence. Models are given GUIDS, and saved into a JSON object. Simple // as that. // Hold reference to Underscore.js and Backbone.js in the closure in order // to make things work even if they are removed from the global namespace // Generate four random hex digits. function S4() { return (((1+Math.random())*0x10000)|0).toString(16).substring(1); } // Generate a pseudo-GUID by concatenating random hexadecimal. function guid() { return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); } function contains(array, item) { var i = array.length; while (i--) if (array[i] === item) return true; return false; } function extend(obj, props) { for (var key in props) { obj[key] = props[key]; } return obj; } function _browserStorage (name, serializer, type) { var _store; if (type === 'local' && !window.localStorage ) { throw "Backbone.browserStorage: Environment does not support localStorage."; } else if (type === 'session' && !window.sessionStorage ) { throw "Backbone.browserStorage: Environment does not support sessionStorage."; } this.name = name; this.serializer = serializer || { serialize: function(item) { return _.isObject(item) ? JSON.stringify(item) : item; }, // fix for "illegal access" error on Android when JSON.parse is passed null deserialize: function (data) { return data && JSON.parse(data); } }; if (type === 'session') { this.store = window.sessionStorage; } else if (type === 'local') { this.store = window.localStorage; } else { throw "Backbone.browserStorage: No storage type was specified"; } _store = this.store.getItem(this.name); this.records = (_store && _store.split(",")) || []; } // Our Store is represented by a single JS object in *localStorage* or *sessionStorage*. // Create it with a meaningful name, like the name you'd give a table. Backbone.BrowserStorage = { local: function (name, serializer) { return _browserStorage.bind(this, name, serializer, 'local')(); }, session: function (name, serializer) { return _browserStorage.bind(this, name, serializer, 'session')(); } }; // The browser's local and session stores will be extended with this obj. var _extension = { // Save the current state of the **Store** save: function() { this.store.setItem(this.name, this.records.join(",")); }, // Add a model, giving it a (hopefully)-unique GUID, if it doesn't already // have an id of it's own. create: function(model) { if (!model.id) { model.id = guid(); model.set(model.idAttribute, model.id); } this.store.setItem(this._itemName(model.id), this.serializer.serialize(model)); this.records.push(model.id.toString()); this.save(); return this.find(model) !== false; }, // Update a model by replacing its copy in `this.data`. update: function(model) { this.store.setItem(this._itemName(model.id), this.serializer.serialize(model)); var modelId = model.id.toString(); if (!contains(this.records, modelId)) { this.records.push(modelId); this.save(); } return this.find(model) !== false; }, // Retrieve a model from `this.data` by id. find: function(model) { return this.serializer.deserialize(this.store.getItem(this._itemName(model.id))); }, // Return the array of all models currently in storage. findAll: function() { var result = []; for (var i = 0, id, data; i < this.records.length; i++) { id = this.records[i]; data = this.serializer.deserialize(this.store.getItem(this._itemName(id))); if (data !== null) result.push(data); } return result; }, // Delete a model from `this.data`, returning it. destroy: function(model) { this.store.removeItem(this._itemName(model.id)); var modelId = model.id.toString(); for (var i = 0, id; i < this.records.length; i++) { if (this.records[i] === modelId) { this.records.splice(i, 1); } } this.save(); return model; }, browserStorage: function() { return { session: sessionStorage, local: localStorage }; }, // Clear browserStorage for specific collection. _clear: function() { var local = this.store, itemRe; // Escape special regex characters in id. itemRe = new RegExp("^" + this.name.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") + "-"); // Remove id-tracking item (e.g., "foo"). local.removeItem(this.name); // Match all data items (e.g., "foo-ID") and remove. for (var k in local) { if (itemRe.test(k)) { local.removeItem(k); } } this.records.length = 0; }, // Size of browserStorage. _storageSize: function() { return this.store.length; }, _itemName: function(id) { return this.name+"-"+id; } }; extend(Backbone.BrowserStorage.session.prototype, _extension); extend(Backbone.BrowserStorage.local.prototype, _extension); // localSync delegate to the model or collection's // *browserStorage* property, which should be an instance of `Store`. // window.Store.sync and Backbone.localSync is deprecated, use Backbone.BrowserStorage.sync instead Backbone.BrowserStorage.sync = Backbone.localSync = function(method, model, options) { var store = model.browserStorage || model.collection.browserStorage; var resp, errorMessage; //If $ is having Deferred - use it. var syncDfd = Backbone.$ ? (Backbone.$.Deferred && Backbone.$.Deferred()) : (Backbone.Deferred && Backbone.Deferred()); try { switch (method) { case "read": resp = model.id !== undefined ? store.find(model) : store.findAll(); break; case "create": resp = store.create(model); break; case "update": resp = store.update(model); break; case "delete": resp = store.destroy(model); break; } } catch(error) { if (error.code === 22 && store._storageSize() === 0) errorMessage = "Private browsing is unsupported"; else errorMessage = error.message; } if (resp) { if (options && options.success) { if (Backbone.VERSION === "0.9.10") { options.success(model, resp, options); } else { options.success(resp); } } if (syncDfd) { syncDfd.resolve(resp); } } else { errorMessage = errorMessage ? errorMessage : "Record Not Found"; if (options && options.error) if (Backbone.VERSION === "0.9.10") { options.error(model, errorMessage, options); } else { options.error(errorMessage); } if (syncDfd) syncDfd.reject(errorMessage); } // add compatibility with $.ajax // always execute callback for success and error if (options && options.complete) options.complete(resp); return syncDfd && syncDfd.promise(); }; Backbone.ajaxSync = Backbone.sync; Backbone.getSyncMethod = function(model) { if(model.browserStorage || (model.collection && model.collection.browserStorage)) { return Backbone.localSync; } return Backbone.ajaxSync; }; // Override 'Backbone.sync' to default to localSync, // the original 'Backbone.sync' is still available in 'Backbone.ajaxSync' Backbone.sync = function(method, model, options) { return Backbone.getSyncMethod(model).apply(this, [method, model, options]); }; return Backbone.BrowserStorage; })); /***/ }), /***/ "./node_modules/backbone.nativeview/backbone.nativeview.js": /*!*****************************************************************!*\ !*** ./node_modules/backbone.nativeview/backbone.nativeview.js ***! \*****************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Backbone.NativeView.js 0.3.3 // --------------- // (c) 2015 Adam Krebs, Jimmy Yuen Ho Wong // Backbone.NativeView may be freely distributed under the MIT license. // For all details and documentation: // https://github.com/akre54/Backbone.NativeView (function (factory) { if (true) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(/*! backbone */ "./node_modules/backbone/backbone.js")], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } else {} }(function (Backbone) { // Cached regex to match an opening '<' of an HTML tag, possibly left-padded // with whitespace. var paddedLt = /^\s*=9 and modern browsers. var matchesSelector = ElementProto.matches || ElementProto.webkitMatchesSelector || ElementProto.mozMatchesSelector || ElementProto.msMatchesSelector || ElementProto.oMatchesSelector || // Make our own `Element#matches` for IE8 function(selector) { // Use querySelectorAll to find all elements matching the selector, // then check if the given element is included in that list. // Executing the query on the parentNode reduces the resulting nodeList, // (document doesn't have a parentNode). var nodeList = (this.parentNode || document).querySelectorAll(selector) || []; return ~indexOf(nodeList, this); }; // Cache Backbone.View for later access in constructor var BBView = Backbone.View; // To extend an existing view to use native methods, extend the View prototype // with the mixin: _.extend(MyView.prototype, Backbone.NativeViewMixin); Backbone.NativeViewMixin = { _domEvents: null, constructor: function() { this._domEvents = []; return BBView.apply(this, arguments); }, $: function(selector) { return this.el.querySelectorAll(selector); }, _removeElement: function() { this.undelegateEvents(); if (this.el.parentNode) this.el.parentNode.removeChild(this.el); }, // Apply the `element` to the view. `element` can be a CSS selector, // a string of HTML, or an Element node. _setElement: function(element) { if (typeof element == 'string') { if (paddedLt.test(element)) { var el = document.createElement('div'); el.innerHTML = element; this.el = el.firstChild; } else { this.el = document.querySelector(element); } } else { this.el = element; } }, // Set a hash of attributes to the view's `el`. We use the "prop" version // if available, falling back to `setAttribute` for the catch-all. _setAttributes: function(attrs) { for (var attr in attrs) { attr in this.el ? this.el[attr] = attrs[attr] : this.el.setAttribute(attr, attrs[attr]); } }, // Make a event delegation handler for the given `eventName` and `selector` // and attach it to `this.el`. // If selector is empty, the listener will be bound to `this.el`. If not, a // new handler that will recursively traverse up the event target's DOM // hierarchy looking for a node that matches the selector. If one is found, // the event's `delegateTarget` property is set to it and the return the // result of calling bound `listener` with the parameters given to the // handler. delegate: function(eventName, selector, listener) { if (typeof selector === 'function') { listener = selector; selector = null; } var root = this.el; var handler = selector ? function (e) { var node = e.target || e.srcElement; for (; node && node != root; node = node.parentNode) { if (matchesSelector.call(node, selector)) { e.delegateTarget = node; listener(e); } } } : listener; elementAddEventListener.call(this.el, eventName, handler, false); this._domEvents.push({eventName: eventName, handler: handler, listener: listener, selector: selector}); return handler; }, // Remove a single delegated event. Either `eventName` or `selector` must // be included, `selector` and `listener` are optional. undelegate: function(eventName, selector, listener) { if (typeof selector === 'function') { listener = selector; selector = null; } if (this.el) { var handlers = this._domEvents.slice(); for (var i = 0, len = handlers.length; i < len; i++) { var item = handlers[i]; var match = item.eventName === eventName && (listener ? item.listener === listener : true) && (selector ? item.selector === selector : true); if (!match) continue; elementRemoveEventListener.call(this.el, item.eventName, item.handler, false); this._domEvents.splice(indexOf(handlers, item), 1); } } return this; }, // Remove all events created with `delegate` from `el` undelegateEvents: function() { if (this.el) { for (var i = 0, len = this._domEvents.length; i < len; i++) { var item = this._domEvents[i]; elementRemoveEventListener.call(this.el, item.eventName, item.handler, false); }; this._domEvents.length = 0; } return this; } }; Backbone.NativeView = Backbone.View.extend(Backbone.NativeViewMixin); return Backbone.NativeView; })); /***/ }), /***/ "./node_modules/backbone.overview/backbone.orderedlistview.js": /*!********************************************************************!*\ !*** ./node_modules/backbone.overview/backbone.orderedlistview.js ***! \********************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*** IMPORTS FROM imports-loader ***/ var backbone = (backbone || {}); backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_modules/backbone.nativeview/backbone.nativeview.js"); /*! * Backbone.OrderedListView * * Copyright (c) 2017, JC Brand * Licensed under the Mozilla Public License (MPL) */ (function (root, factory) { if (true) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(/*! underscore */ "./src/underscore-shim.js"), __webpack_require__(/*! backbone */ "./node_modules/backbone/backbone.js"), __webpack_require__(/*! backbone.overview */ "./node_modules/backbone.overview/backbone.overview.js")], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } else {} }(this, function (_, Backbone) { "use strict"; Backbone.OrderedListView = Backbone.Overview.extend({ /* An OrderedListView is a special type of Overview which adds some * methods and conventions for rendering an ordered list of elements. */ // The `listItems` attribute denotes the path (from this View) to the // list of items. listItems: 'model', // The `sortEvent` attribute specifies the event which should cause the // ordered list to be sorted. sortEvent: 'change', // The `listSelector` is the selector used to query for the DOM list // element which contains the ordered items. listSelector: '.ordered-items', // The `itemView` is constructor which should be called to create a // View for a new item. ItemView: undefined, // The `subviewIndex` is the attribute of the list element model which // acts as the index of the subview in the overview. // An overview is a "Collection" of views, and they can be retrieved // via an index. By default this is the 'id' attribute, but it could be // set to something else. subviewIndex: 'id', initialize () { this.sortEventually = _.debounce( this.sortAndPositionAllItems.bind(this), 500); this.items = _.get(this, this.listItems); this.items.on('add', this.sortAndPositionAllItems, this); this.items.on('remove', this.removeView, this); if (!_.isNil(this.sortEvent)) { this.items.on(this.sortEvent, this.sortEventually, this); } }, createItemView (item) { let item_view = this.get(item.get(this.subviewIndex)); if (!item_view) { item_view = new this.ItemView({model: item}); this.add(item.get(this.subviewIndex), item_view); } else { item_view.model = item; item_view.initialize(); } item_view.render(); return item_view; }, removeView (item) { this.remove(item.get(this.subviewIndex)); }, sortAndPositionAllItems () { if (!this.items.length) { return; } this.items.sort(); const list_el = this.el.querySelector(this.listSelector); const div = document.createElement('div'); list_el.parentNode.replaceChild(div, list_el); this.items.each((item) => { let view = this.get(item.get(this.subviewIndex)); if (_.isUndefined(view)) { view = this.createItemView(item) } list_el.insertAdjacentElement('beforeend', view.el); }); div.parentNode.replaceChild(list_el, div); } }); return Backbone.OrderedListView; })); /***/ }), /***/ "./node_modules/backbone.overview/backbone.overview.js": /*!*************************************************************!*\ !*** ./node_modules/backbone.overview/backbone.overview.js ***! \*************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*** IMPORTS FROM imports-loader ***/ var backbone = (backbone || {}); backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_modules/backbone.nativeview/backbone.nativeview.js"); /*! * Backbone.Overview * * Copyright (c) 2018, JC Brand * Licensed under the Mozilla Public License (MPL) */ (function (root, factory) { if (true) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(/*! underscore */ "./src/underscore-shim.js"), __webpack_require__(/*! backbone */ "./node_modules/backbone/backbone.js")], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } else {} }(this, function (_, Backbone) { "use strict"; const View = _.isUndefined(Backbone.NativeView) ? Backbone.View : Backbone.NativeView; const Overview = Backbone.Overview = function (options) { /* An Overview is a View that contains and keeps track of sub-views. * Kind of like what a Collection is to a Model. */ var that = this; this.views = {}; this.keys = _.partial(_.keys, this.views); this.getAll = _.partial(_.identity, this.views); this.get = function (id) { return that.views[id]; }; this.xget = function (id) { /* Exclusive get. Returns all instances except the given id. */ return _.filter(that.views, function (view, vid) { return vid !== id; }); }; this.add = function (id, view) { that.views[id] = view; return view; }; this.remove = function (id) { if (typeof id === "undefined") { new View().remove.apply(that); } var view = that.views[id]; if (view) { delete that.views[id]; view.remove(); return view; } }; this.removeAll = function () { _.each(_.keys(that.views), that.remove); return that; }; View.apply(this, Array.prototype.slice.apply(arguments)); }; var methods = [ 'all', 'any', 'chain', 'collect', 'contains', 'detect', 'difference', 'drop', 'each', 'every', 'filter', 'find', 'first', 'foldl', 'foldr', 'forEach', 'head', 'include', 'indexOf', 'initial', 'inject', 'invoke', 'isEmpty', 'last', 'lastIndexOf', 'map', 'max', 'min', 'reduce', 'reduceRight', 'reject', 'rest', 'sample', 'select', 'shuffle', 'size', 'some', 'sortBy', 'tail', 'take', 'toArray', 'without', ]; // Mix in each Underscore method as a proxy to `Overview#view`. _.each(methods, function(method) { Overview.prototype[method] = function() { var args = Array.prototype.slice.call(arguments); args.unshift(this.views); return _[method].apply(_, args); }; }); _.extend(Overview.prototype, View.prototype); Overview.extend = View.extend; Backbone.OrderedListView = Backbone.Overview.extend({ // The `listItems` attribute denotes the path (from this View) to the // list of items. listItems: 'model', // The `sortEvent` attribute specifies the event which should cause the // ordered list to be sorted. sortEvent: 'change', // The `listSelector` is the selector used to query for the DOM list // element which contains the ordered items. listSelector: '.ordered-items', // The `itemView` is constructor which should be called to create a // View for a new item. ItemView: undefined, initialize () { this.sortEventually = _.debounce(this.sortAndPositionAllItems.bind(this), 500); this.items = _.get(this, this.listItems); this.items.on('add', this.createItemView, this); this.items.on('add', this.sortEventually, this); this.items.on(this.sortEvent, this.sortEventually, this); }, createItemView (item) { let item_view = this.get(item.get('id')); if (!item_view) { item_view = new this.ItemView({model: item}); this.add(item.get('id'), item_view); } else { item_view.model = item; item_view.initialize(); } item_view.render(); return item_view; }, sortAndPositionAllItems () { this.items.sort(); this.items.each((item) => { if (_.isUndefined(this.get(item.get('id')))) { this.createItemView(item) } this.positionItem(item, this.el.querySelector(this.listSelector)); }); }, positionItem (item, list_el) { /* Place the View's DOM element in the correct alphabetical * position in the list. * * IMPORTANT: there's an important implicit assumption being * made here. And that is that initially this method gets called * for each item in the right positional order. * * In other words, it gets called for the 0th, then the * 1st, then the 2nd, 3rd and so on. * * That's why we call it in the "success" handler after * fetching the items, so that we know we have ALL of * them and that they're sorted. */ const view = this.get(item.get('id')), index = this.items.indexOf(item); if (index === 0) { list_el.insertAdjacentElement('afterbegin', view.el); } else if (index === (this.items.length-1)) { list_el.insertAdjacentElement('beforeend', view.el); } else { const neighbour_el = list_el.querySelector('li:nth-child('+index+')'); neighbour_el.insertAdjacentElement('afterend', view.el); } return view; } }); return Backbone.Overview; })); /***/ }), /***/ "./node_modules/backbone.vdomview/backbone.vdomview.js": /*!*************************************************************!*\ !*** ./node_modules/backbone.vdomview/backbone.vdomview.js ***! \*************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*** IMPORTS FROM imports-loader ***/ var backbone = (backbone || {}); backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_modules/backbone.nativeview/backbone.nativeview.js"); /*! * Backbone.VDOMView * * MIT Licensed. Copyright (c) 2017, JC Brand */ (function (root, factory) { if (true) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(/*! snabbdom */ "./node_modules/snabbdom/dist/snabbdom.js"), __webpack_require__(/*! snabbdom-attributes */ "./node_modules/snabbdom/dist/snabbdom-attributes.js"), __webpack_require__(/*! snabbdom-class */ "./node_modules/snabbdom/dist/snabbdom-class.js"), __webpack_require__(/*! snabbdom-dataset */ "./node_modules/snabbdom/dist/snabbdom-dataset.js"), __webpack_require__(/*! snabbdom-props */ "./node_modules/snabbdom/dist/snabbdom-props.js"), __webpack_require__(/*! snabbdom-style */ "./node_modules/snabbdom/dist/snabbdom-style.js"), __webpack_require__(/*! tovnode */ "./node_modules/snabbdom/dist/tovnode.js"), __webpack_require__(/*! underscore */ "./src/underscore-shim.js"), __webpack_require__(/*! backbone */ "./node_modules/backbone/backbone.js") ], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } else {} }(this, function ( snabbdom, snabbdom_attributes, snabbdom_class, snabbdom_dataset, snabbdom_props, snabbdom_style, tovnode, _, Backbone) { "use strict"; let domParser = new DOMParser(); const patch = snabbdom.init([ snabbdom_attributes.default, snabbdom_class.default, snabbdom_dataset.default, snabbdom_props.default, snabbdom_style.default ]); const View = _.isUndefined(Backbone.NativeView) ? Backbone.View : Backbone.NativeView; function parseHTMLToDOM (html_str) { /* Parses a string with HTML and returns a DOM element. * * Forked from vdom_parser: * https://github.com/bitinn/vdom-parser */ if (typeof html_str !== 'string') { throw new Error('Invalid parameter type in parseHTMLToDOM'); } if ( !('DOMParser' in window) ) { throw new Error( 'DOMParser is not available, '+ 'so parsing string to DOM node is not possible.'); } if (!html_str) { return document.createTextNode(''); } domParser = domParser || new DOMParser(); const doc = domParser.parseFromString(html_str, 'text/html'); // most tags default to body if (doc.body.firstChild) { return doc.getElementsByTagName('body')[0].firstChild; // some tags, like script and style, default to head } else if (doc.head.firstChild && (doc.head.firstChild.tagName !== 'TITLE' || doc.title)) { return doc.head.firstChild; // special case for html comment, cdata, doctype } else if (doc.firstChild && doc.firstChild.tagName !== 'HTML') { return doc.firstChild; // other element, such as whitespace, or html/body/head tag, fallback to empty text node } else { return document.createTextNode(''); } } Backbone.VDOMView = View.extend({ updateEventListeners (old_vnode, new_vnode) { this.setElement(new_vnode.elm); }, render () { if (_.isFunction(this.beforeRender)) { this.beforeRender(); } let new_vnode; if (!_.isNil(this.toHTML)) { new_vnode = tovnode.toVNode(parseHTMLToDOM(this.toHTML())); } else { new_vnode = tovnode.toVNode(this.toDOM()); } new_vnode.data.hook = _.extend({ create: this.updateEventListeners.bind(this), update: this.updateEventListeners.bind(this) }); const el = this.vnode ? this.vnode.elm : this.el; if (el.outerHTML !== new_vnode.elm.outerHTML) { this.vnode = patch(this.vnode || this.el, new_vnode); } if (_.isFunction(this.afterRender)) { this.afterRender(); } return this; } }); return Backbone.VDOMView; })); /***/ }), /***/ "./node_modules/backbone/backbone.js": /*!*******************************************!*\ !*** ./node_modules/backbone/backbone.js ***! \*******************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Backbone.js 1.3.3 // (c) 2010-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Backbone may be freely distributed under the MIT license. // For all details and documentation: // http://backbonejs.org (function(factory) { // Establish the root object, `window` (`self`) in the browser, or `global` on the server. // We use `self` instead of `window` for `WebWorker` support. var root = (typeof self == 'object' && self.self === self && self) || (typeof global == 'object' && global.global === global && global); // Set up Backbone appropriately for the environment. Start with AMD. if (true) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(/*! underscore */ "./src/underscore-shim.js"), __webpack_require__(/*! jquery */ "./src/jquery-stub.js"), exports], __WEBPACK_AMD_DEFINE_RESULT__ = (function(_, $, exports) { // Export global even in AMD case in case this script is loaded with // others that may still expect a global Backbone. root.Backbone = factory(root, exports, _, $); }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // Next for Node.js or CommonJS. jQuery may not be needed as a module. } else { var _, $; } })(function(root, Backbone, _, $) { // Initial Setup // ------------- // Save the previous value of the `Backbone` variable, so that it can be // restored later on, if `noConflict` is used. var previousBackbone = root.Backbone; // Create a local reference to a common array method we'll want to use later. var slice = Array.prototype.slice; // Current version of the library. Keep in sync with `package.json`. Backbone.VERSION = '1.3.3'; // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns // the `$` variable. Backbone.$ = $; // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable // to its previous owner. Returns a reference to this Backbone object. Backbone.noConflict = function() { root.Backbone = previousBackbone; return this; }; // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and // set a `X-Http-Method-Override` header. Backbone.emulateHTTP = false; // Turn on `emulateJSON` to support legacy servers that can't deal with direct // `application/json` requests ... this will encode the body as // `application/x-www-form-urlencoded` instead and will send the model in a // form param named `model`. Backbone.emulateJSON = false; // Proxy Backbone class methods to Underscore functions, wrapping the model's // `attributes` object or collection's `models` array behind the scenes. // // collection.filter(function(model) { return model.get('age') > 10 }); // collection.each(this.addView); // // `Function#apply` can be slow so we use the method's arg count, if we know it. var addMethod = function(length, method, attribute) { switch (length) { case 1: return function() { return _[method](this[attribute]); }; case 2: return function(value) { return _[method](this[attribute], value); }; case 3: return function(iteratee, context) { return _[method](this[attribute], cb(iteratee, this), context); }; case 4: return function(iteratee, defaultVal, context) { return _[method](this[attribute], cb(iteratee, this), defaultVal, context); }; default: return function() { var args = slice.call(arguments); args.unshift(this[attribute]); return _[method].apply(_, args); }; } }; var addUnderscoreMethods = function(Class, methods, attribute) { _.each(methods, function(length, method) { if (_[method]) Class.prototype[method] = addMethod(length, method, attribute); }); }; // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`. var cb = function(iteratee, instance) { if (_.isFunction(iteratee)) return iteratee; if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee); if (_.isString(iteratee)) return function(model) { return model.get(iteratee); }; return iteratee; }; var modelMatcher = function(attrs) { var matcher = _.matches(attrs); return function(model) { return matcher(model.attributes); }; }; // Backbone.Events // --------------- // A module that can be mixed in to *any object* in order to provide it with // a custom event channel. You may bind a callback to an event with `on` or // remove with `off`; `trigger`-ing an event fires all callbacks in // succession. // // var object = {}; // _.extend(object, Backbone.Events); // object.on('expand', function(){ alert('expanded'); }); // object.trigger('expand'); // var Events = Backbone.Events = {}; // Regular expression used to split event strings. var eventSplitter = /\s+/; // Iterates over the standard `event, callback` (as well as the fancy multiple // space-separated events `"change blur", callback` and jQuery-style event // maps `{event: callback}`). var eventsApi = function(iteratee, events, name, callback, opts) { var i = 0, names; if (name && typeof name === 'object') { // Handle event maps. if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback; for (names = _.keys(name); i < names.length ; i++) { events = eventsApi(iteratee, events, names[i], name[names[i]], opts); } } else if (name && eventSplitter.test(name)) { // Handle space-separated event names by delegating them individually. for (names = name.split(eventSplitter); i < names.length; i++) { events = iteratee(events, names[i], callback, opts); } } else { // Finally, standard events. events = iteratee(events, name, callback, opts); } return events; }; // Bind an event to a `callback` function. Passing `"all"` will bind // the callback to all events fired. Events.on = function(name, callback, context) { return internalOn(this, name, callback, context); }; // Guard the `listening` argument from the public API. var internalOn = function(obj, name, callback, context, listening) { obj._events = eventsApi(onApi, obj._events || {}, name, callback, { context: context, ctx: obj, listening: listening }); if (listening) { var listeners = obj._listeners || (obj._listeners = {}); listeners[listening.id] = listening; } return obj; }; // Inversion-of-control versions of `on`. Tell *this* object to listen to // an event in another object... keeping track of what it's listening to // for easier unbinding later. Events.listenTo = function(obj, name, callback) { if (!obj) return this; var id = obj._listenId || (obj._listenId = _.uniqueId('l')); var listeningTo = this._listeningTo || (this._listeningTo = {}); var listening = listeningTo[id]; // This object is not listening to any other events on `obj` yet. // Setup the necessary references to track the listening callbacks. if (!listening) { var thisId = this._listenId || (this._listenId = _.uniqueId('l')); listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0}; } // Bind callbacks on obj, and keep track of them on listening. internalOn(obj, name, callback, this, listening); return this; }; // The reducing API that adds a callback to the `events` object. var onApi = function(events, name, callback, options) { if (callback) { var handlers = events[name] || (events[name] = []); var context = options.context, ctx = options.ctx, listening = options.listening; if (listening) listening.count++; handlers.push({callback: callback, context: context, ctx: context || ctx, listening: listening}); } return events; }; // Remove one or many callbacks. If `context` is null, removes all // callbacks with that function. If `callback` is null, removes all // callbacks for the event. If `name` is null, removes all bound // callbacks for all events. Events.off = function(name, callback, context) { if (!this._events) return this; this._events = eventsApi(offApi, this._events, name, callback, { context: context, listeners: this._listeners }); return this; }; // Tell this object to stop listening to either specific events ... or // to every object it's currently listening to. Events.stopListening = function(obj, name, callback) { var listeningTo = this._listeningTo; if (!listeningTo) return this; var ids = obj ? [obj._listenId] : _.keys(listeningTo); for (var i = 0; i < ids.length; i++) { var listening = listeningTo[ids[i]]; // If listening doesn't exist, this object is not currently // listening to obj. Break out early. if (!listening) break; listening.obj.off(name, callback, this); } return this; }; // The reducing API that removes a callback from the `events` object. var offApi = function(events, name, callback, options) { if (!events) return; var i = 0, listening; var context = options.context, listeners = options.listeners; // Delete all events listeners and "drop" events. if (!name && !callback && !context) { var ids = _.keys(listeners); for (; i < ids.length; i++) { listening = listeners[ids[i]]; delete listeners[listening.id]; delete listening.listeningTo[listening.objId]; } return; } var names = name ? [name] : _.keys(events); for (; i < names.length; i++) { name = names[i]; var handlers = events[name]; // Bail out if there are no events stored. if (!handlers) break; // Replace events if there are any remaining. Otherwise, clean up. var remaining = []; for (var j = 0; j < handlers.length; j++) { var handler = handlers[j]; if ( callback && callback !== handler.callback && callback !== handler.callback._callback || context && context !== handler.context ) { remaining.push(handler); } else { listening = handler.listening; if (listening && --listening.count === 0) { delete listeners[listening.id]; delete listening.listeningTo[listening.objId]; } } } // Update tail event if the list has any events. Otherwise, clean up. if (remaining.length) { events[name] = remaining; } else { delete events[name]; } } return events; }; // Bind an event to only be triggered a single time. After the first time // the callback is invoked, its listener will be removed. If multiple events // are passed in using the space-separated syntax, the handler will fire // once for each event, not once for a combination of all events. Events.once = function(name, callback, context) { // Map the event into a `{event: once}` object. var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this)); if (typeof name === 'string' && context == null) callback = void 0; return this.on(events, callback, context); }; // Inversion-of-control versions of `once`. Events.listenToOnce = function(obj, name, callback) { // Map the event into a `{event: once}` object. var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj)); return this.listenTo(obj, events); }; // Reduces the event callbacks into a map of `{event: onceWrapper}`. // `offer` unbinds the `onceWrapper` after it has been called. var onceMap = function(map, name, callback, offer) { if (callback) { var once = map[name] = _.once(function() { offer(name, once); callback.apply(this, arguments); }); once._callback = callback; } return map; }; // Trigger one or many events, firing all bound callbacks. Callbacks are // passed the same arguments as `trigger` is, apart from the event name // (unless you're listening on `"all"`, which will cause your callback to // receive the true name of the event as the first argument). Events.trigger = function(name) { if (!this._events) return this; var length = Math.max(0, arguments.length - 1); var args = Array(length); for (var i = 0; i < length; i++) args[i] = arguments[i + 1]; eventsApi(triggerApi, this._events, name, void 0, args); return this; }; // Handles triggering the appropriate event callbacks. var triggerApi = function(objEvents, name, callback, args) { if (objEvents) { var events = objEvents[name]; var allEvents = objEvents.all; if (events && allEvents) allEvents = allEvents.slice(); if (events) triggerEvents(events, args); if (allEvents) triggerEvents(allEvents, [name].concat(args)); } return objEvents; }; // A difficult-to-believe, but optimized internal dispatch function for // triggering events. Tries to keep the usual cases speedy (most internal // Backbone events have 3 arguments). var triggerEvents = function(events, args) { var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; switch (args.length) { case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; } }; // Aliases for backwards compatibility. Events.bind = Events.on; Events.unbind = Events.off; // Allow the `Backbone` object to serve as a global event bus, for folks who // want global "pubsub" in a convenient place. _.extend(Backbone, Events); // Backbone.Model // -------------- // Backbone **Models** are the basic data object in the framework -- // frequently representing a row in a table in a database on your server. // A discrete chunk of data and a bunch of useful, related methods for // performing computations and transformations on that data. // Create a new model with the specified attributes. A client id (`cid`) // is automatically generated and assigned for you. var Model = Backbone.Model = function(attributes, options) { var attrs = attributes || {}; options || (options = {}); this.cid = _.uniqueId(this.cidPrefix); this.attributes = {}; if (options.collection) this.collection = options.collection; if (options.parse) attrs = this.parse(attrs, options) || {}; var defaults = _.result(this, 'defaults'); attrs = _.defaults(_.extend({}, defaults, attrs), defaults); this.set(attrs, options); this.changed = {}; this.initialize.apply(this, arguments); }; // Attach all inheritable methods to the Model prototype. _.extend(Model.prototype, Events, { // A hash of attributes whose current and previous value differ. changed: null, // The value returned during the last failed validation. validationError: null, // The default name for the JSON `id` attribute is `"id"`. MongoDB and // CouchDB users may want to set this to `"_id"`. idAttribute: 'id', // The prefix is used to create the client id which is used to identify models locally. // You may want to override this if you're experiencing name clashes with model ids. cidPrefix: 'c', // Initialize is an empty function by default. Override it with your own // initialization logic. initialize: function(){}, // Return a copy of the model's `attributes` object. toJSON: function(options) { return _.clone(this.attributes); }, // Proxy `Backbone.sync` by default -- but override this if you need // custom syncing semantics for *this* particular model. sync: function() { return Backbone.sync.apply(this, arguments); }, // Get the value of an attribute. get: function(attr) { return this.attributes[attr]; }, // Get the HTML-escaped value of an attribute. escape: function(attr) { return _.escape(this.get(attr)); }, // Returns `true` if the attribute contains a value that is not null // or undefined. has: function(attr) { return this.get(attr) != null; }, // Special-cased proxy to underscore's `_.matches` method. matches: function(attrs) { return !!_.iteratee(attrs, this)(this.attributes); }, // Set a hash of model attributes on the object, firing `"change"`. This is // the core primitive operation of a model, updating the data and notifying // anyone who needs to know about the change in state. The heart of the beast. set: function(key, val, options) { if (key == null) return this; // Handle both `"key", value` and `{key: value}` -style arguments. var attrs; if (typeof key === 'object') { attrs = key; options = val; } else { (attrs = {})[key] = val; } options || (options = {}); // Run validation. if (!this._validate(attrs, options)) return false; // Extract attributes and options. var unset = options.unset; var silent = options.silent; var changes = []; var changing = this._changing; this._changing = true; if (!changing) { this._previousAttributes = _.clone(this.attributes); this.changed = {}; } var current = this.attributes; var changed = this.changed; var prev = this._previousAttributes; // For each `set` attribute, update or delete the current value. for (var attr in attrs) { val = attrs[attr]; if (!_.isEqual(current[attr], val)) changes.push(attr); if (!_.isEqual(prev[attr], val)) { changed[attr] = val; } else { delete changed[attr]; } unset ? delete current[attr] : current[attr] = val; } // Update the `id`. if (this.idAttribute in attrs) this.id = this.get(this.idAttribute); // Trigger all relevant attribute changes. if (!silent) { if (changes.length) this._pending = options; for (var i = 0; i < changes.length; i++) { this.trigger('change:' + changes[i], this, current[changes[i]], options); } } // You might be wondering why there's a `while` loop here. Changes can // be recursively nested within `"change"` events. if (changing) return this; if (!silent) { while (this._pending) { options = this._pending; this._pending = false; this.trigger('change', this, options); } } this._pending = false; this._changing = false; return this; }, // Remove an attribute from the model, firing `"change"`. `unset` is a noop // if the attribute doesn't exist. unset: function(attr, options) { return this.set(attr, void 0, _.extend({}, options, {unset: true})); }, // Clear all attributes on the model, firing `"change"`. clear: function(options) { var attrs = {}; for (var key in this.attributes) attrs[key] = void 0; return this.set(attrs, _.extend({}, options, {unset: true})); }, // Determine if the model has changed since the last `"change"` event. // If you specify an attribute name, determine if that attribute has changed. hasChanged: function(attr) { if (attr == null) return !_.isEmpty(this.changed); return _.has(this.changed, attr); }, // Return an object containing all the attributes that have changed, or // false if there are no changed attributes. Useful for determining what // parts of a view need to be updated and/or what attributes need to be // persisted to the server. Unset attributes will be set to undefined. // You can also pass an attributes object to diff against the model, // determining if there *would be* a change. changedAttributes: function(diff) { if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; var old = this._changing ? this._previousAttributes : this.attributes; var changed = {}; for (var attr in diff) { var val = diff[attr]; if (_.isEqual(old[attr], val)) continue; changed[attr] = val; } return _.size(changed) ? changed : false; }, // Get the previous value of an attribute, recorded at the time the last // `"change"` event was fired. previous: function(attr) { if (attr == null || !this._previousAttributes) return null; return this._previousAttributes[attr]; }, // Get all of the attributes of the model at the time of the previous // `"change"` event. previousAttributes: function() { return _.clone(this._previousAttributes); }, // Fetch the model from the server, merging the response with the model's // local attributes. Any changed attributes will trigger a "change" event. fetch: function(options) { options = _.extend({parse: true}, options); var model = this; var success = options.success; options.success = function(resp) { var serverAttrs = options.parse ? model.parse(resp, options) : resp; if (!model.set(serverAttrs, options)) return false; if (success) success.call(options.context, model, resp, options); model.trigger('sync', model, resp, options); }; wrapError(this, options); return this.sync('read', this, options); }, // Set a hash of model attributes, and sync the model to the server. // If the server returns an attributes hash that differs, the model's // state will be `set` again. save: function(key, val, options) { // Handle both `"key", value` and `{key: value}` -style arguments. var attrs; if (key == null || typeof key === 'object') { attrs = key; options = val; } else { (attrs = {})[key] = val; } options = _.extend({validate: true, parse: true}, options); var wait = options.wait; // If we're not waiting and attributes exist, save acts as // `set(attr).save(null, opts)` with validation. Otherwise, check if // the model will be valid when the attributes, if any, are set. if (attrs && !wait) { if (!this.set(attrs, options)) return false; } else if (!this._validate(attrs, options)) { return false; } // After a successful server-side save, the client is (optionally) // updated with the server-side state. var model = this; var success = options.success; var attributes = this.attributes; options.success = function(resp) { // Ensure attributes are restored during synchronous saves. model.attributes = attributes; var serverAttrs = options.parse ? model.parse(resp, options) : resp; if (wait) serverAttrs = _.extend({}, attrs, serverAttrs); if (serverAttrs && !model.set(serverAttrs, options)) return false; if (success) success.call(options.context, model, resp, options); model.trigger('sync', model, resp, options); }; wrapError(this, options); // Set temporary attributes if `{wait: true}` to properly find new ids. if (attrs && wait) this.attributes = _.extend({}, attributes, attrs); var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); if (method === 'patch' && !options.attrs) options.attrs = attrs; var xhr = this.sync(method, this, options); // Restore attributes. this.attributes = attributes; return xhr; }, // Destroy this model on the server if it was already persisted. // Optimistically removes the model from its collection, if it has one. // If `wait: true` is passed, waits for the server to respond before removal. destroy: function(options) { options = options ? _.clone(options) : {}; var model = this; var success = options.success; var wait = options.wait; var destroy = function() { model.stopListening(); model.trigger('destroy', model, model.collection, options); }; options.success = function(resp) { if (wait) destroy(); if (success) success.call(options.context, model, resp, options); if (!model.isNew()) model.trigger('sync', model, resp, options); }; var xhr = false; if (this.isNew()) { _.defer(options.success); } else { wrapError(this, options); xhr = this.sync('delete', this, options); } if (!wait) destroy(); return xhr; }, // Default URL for the model's representation on the server -- if you're // using Backbone's restful methods, override this to change the endpoint // that will be called. url: function() { var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError(); if (this.isNew()) return base; var id = this.get(this.idAttribute); return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id); }, // **parse** converts a response into the hash of attributes to be `set` on // the model. The default implementation is just to pass the response along. parse: function(resp, options) { return resp; }, // Create a new model with identical attributes to this one. clone: function() { return new this.constructor(this.attributes); }, // A model is new if it has never been saved to the server, and lacks an id. isNew: function() { return !this.has(this.idAttribute); }, // Check if the model is currently in a valid state. isValid: function(options) { return this._validate({}, _.extend({}, options, {validate: true})); }, // Run validation against the next complete set of model attributes, // returning `true` if all is well. Otherwise, fire an `"invalid"` event. _validate: function(attrs, options) { if (!options.validate || !this.validate) return true; attrs = _.extend({}, this.attributes, attrs); var error = this.validationError = this.validate(attrs, options) || null; if (!error) return true; this.trigger('invalid', this, error, _.extend(options, {validationError: error})); return false; } }); // Underscore methods that we want to implement on the Model, mapped to the // number of arguments they take. var modelMethods = {keys: 1, values: 1, pairs: 1, invert: 1, pick: 0, omit: 0, chain: 1, isEmpty: 1}; // Mix in each Underscore method as a proxy to `Model#attributes`. addUnderscoreMethods(Model, modelMethods, 'attributes'); // Backbone.Collection // ------------------- // If models tend to represent a single row of data, a Backbone Collection is // more analogous to a table full of data ... or a small slice or page of that // table, or a collection of rows that belong together for a particular reason // -- all of the messages in this particular folder, all of the documents // belonging to this particular author, and so on. Collections maintain // indexes of their models, both in order, and for lookup by `id`. // Create a new **Collection**, perhaps to contain a specific type of `model`. // If a `comparator` is specified, the Collection will maintain // its models in sort order, as they're added and removed. var Collection = Backbone.Collection = function(models, options) { options || (options = {}); if (options.model) this.model = options.model; if (options.comparator !== void 0) this.comparator = options.comparator; this._reset(); this.initialize.apply(this, arguments); if (models) this.reset(models, _.extend({silent: true}, options)); }; // Default options for `Collection#set`. var setOptions = {add: true, remove: true, merge: true}; var addOptions = {add: true, remove: false}; // Splices `insert` into `array` at index `at`. var splice = function(array, insert, at) { at = Math.min(Math.max(at, 0), array.length); var tail = Array(array.length - at); var length = insert.length; var i; for (i = 0; i < tail.length; i++) tail[i] = array[i + at]; for (i = 0; i < length; i++) array[i + at] = insert[i]; for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i]; }; // Define the Collection's inheritable methods. _.extend(Collection.prototype, Events, { // The default model for a collection is just a **Backbone.Model**. // This should be overridden in most cases. model: Model, // Initialize is an empty function by default. Override it with your own // initialization logic. initialize: function(){}, // The JSON representation of a Collection is an array of the // models' attributes. toJSON: function(options) { return this.map(function(model) { return model.toJSON(options); }); }, // Proxy `Backbone.sync` by default. sync: function() { return Backbone.sync.apply(this, arguments); }, // Add a model, or list of models to the set. `models` may be Backbone // Models or raw JavaScript objects to be converted to Models, or any // combination of the two. add: function(models, options) { return this.set(models, _.extend({merge: false}, options, addOptions)); }, // Remove a model, or a list of models from the set. remove: function(models, options) { options = _.extend({}, options); var singular = !_.isArray(models); models = singular ? [models] : models.slice(); var removed = this._removeModels(models, options); if (!options.silent && removed.length) { options.changes = {added: [], merged: [], removed: removed}; this.trigger('update', this, options); } return singular ? removed[0] : removed; }, // Update a collection by `set`-ing a new list of models, adding new ones, // removing models that are no longer present, and merging models that // already exist in the collection, as necessary. Similar to **Model#set**, // the core operation for updating the data contained by the collection. set: function(models, options) { if (models == null) return; options = _.extend({}, setOptions, options); if (options.parse && !this._isModel(models)) { models = this.parse(models, options) || []; } var singular = !_.isArray(models); models = singular ? [models] : models.slice(); var at = options.at; if (at != null) at = +at; if (at > this.length) at = this.length; if (at < 0) at += this.length + 1; var set = []; var toAdd = []; var toMerge = []; var toRemove = []; var modelMap = {}; var add = options.add; var merge = options.merge; var remove = options.remove; var sort = false; var sortable = this.comparator && at == null && options.sort !== false; var sortAttr = _.isString(this.comparator) ? this.comparator : null; // Turn bare objects into model references, and prevent invalid models // from being added. var model, i; for (i = 0; i < models.length; i++) { model = models[i]; // If a duplicate is found, prevent it from being added and // optionally merge it into the existing model. var existing = this.get(model); if (existing) { if (merge && model !== existing) { var attrs = this._isModel(model) ? model.attributes : model; if (options.parse) attrs = existing.parse(attrs, options); existing.set(attrs, options); toMerge.push(existing); if (sortable && !sort) sort = existing.hasChanged(sortAttr); } if (!modelMap[existing.cid]) { modelMap[existing.cid] = true; set.push(existing); } models[i] = existing; // If this is a new, valid model, push it to the `toAdd` list. } else if (add) { model = models[i] = this._prepareModel(model, options); if (model) { toAdd.push(model); this._addReference(model, options); modelMap[model.cid] = true; set.push(model); } } } // Remove stale models. if (remove) { for (i = 0; i < this.length; i++) { model = this.models[i]; if (!modelMap[model.cid]) toRemove.push(model); } if (toRemove.length) this._removeModels(toRemove, options); } // See if sorting is needed, update `length` and splice in new models. var orderChanged = false; var replace = !sortable && add && remove; if (set.length && replace) { orderChanged = this.length !== set.length || _.some(this.models, function(m, index) { return m !== set[index]; }); this.models.length = 0; splice(this.models, set, 0); this.length = this.models.length; } else if (toAdd.length) { if (sortable) sort = true; splice(this.models, toAdd, at == null ? this.length : at); this.length = this.models.length; } // Silently sort the collection if appropriate. if (sort) this.sort({silent: true}); // Unless silenced, it's time to fire all appropriate add/sort/update events. if (!options.silent) { for (i = 0; i < toAdd.length; i++) { if (at != null) options.index = at + i; model = toAdd[i]; model.trigger('add', model, this, options); } if (sort || orderChanged) this.trigger('sort', this, options); if (toAdd.length || toRemove.length || toMerge.length) { options.changes = { added: toAdd, removed: toRemove, merged: toMerge }; this.trigger('update', this, options); } } // Return the added (or merged) model (or models). return singular ? models[0] : models; }, // When you have more items than you want to add or remove individually, // you can reset the entire set with a new list of models, without firing // any granular `add` or `remove` events. Fires `reset` when finished. // Useful for bulk operations and optimizations. reset: function(models, options) { options = options ? _.clone(options) : {}; for (var i = 0; i < this.models.length; i++) { this._removeReference(this.models[i], options); } options.previousModels = this.models; this._reset(); models = this.add(models, _.extend({silent: true}, options)); if (!options.silent) this.trigger('reset', this, options); return models; }, // Add a model to the end of the collection. push: function(model, options) { return this.add(model, _.extend({at: this.length}, options)); }, // Remove a model from the end of the collection. pop: function(options) { var model = this.at(this.length - 1); return this.remove(model, options); }, // Add a model to the beginning of the collection. unshift: function(model, options) { return this.add(model, _.extend({at: 0}, options)); }, // Remove a model from the beginning of the collection. shift: function(options) { var model = this.at(0); return this.remove(model, options); }, // Slice out a sub-array of models from the collection. slice: function() { return slice.apply(this.models, arguments); }, // Get a model from the set by id, cid, model object with id or cid // properties, or an attributes object that is transformed through modelId. get: function(obj) { if (obj == null) return void 0; return this._byId[obj] || this._byId[this.modelId(obj.attributes || obj)] || obj.cid && this._byId[obj.cid]; }, // Returns `true` if the model is in the collection. has: function(obj) { return this.get(obj) != null; }, // Get the model at the given index. at: function(index) { if (index < 0) index += this.length; return this.models[index]; }, // Return models with matching attributes. Useful for simple cases of // `filter`. where: function(attrs, first) { return this[first ? 'find' : 'filter'](attrs); }, // Return the first model with matching attributes. Useful for simple cases // of `find`. findWhere: function(attrs) { return this.where(attrs, true); }, // Force the collection to re-sort itself. You don't need to call this under // normal circumstances, as the set will maintain sort order as each item // is added. sort: function(options) { var comparator = this.comparator; if (!comparator) throw new Error('Cannot sort a set without a comparator'); options || (options = {}); var length = comparator.length; if (_.isFunction(comparator)) comparator = _.bind(comparator, this); // Run sort based on type of `comparator`. if (length === 1 || _.isString(comparator)) { this.models = this.sortBy(comparator); } else { this.models.sort(comparator); } if (!options.silent) this.trigger('sort', this, options); return this; }, // Pluck an attribute from each model in the collection. pluck: function(attr) { return this.map(attr + ''); }, // Fetch the default set of models for this collection, resetting the // collection when they arrive. If `reset: true` is passed, the response // data will be passed through the `reset` method instead of `set`. fetch: function(options) { options = _.extend({parse: true}, options); var success = options.success; var collection = this; options.success = function(resp) { var method = options.reset ? 'reset' : 'set'; collection[method](resp, options); if (success) success.call(options.context, collection, resp, options); collection.trigger('sync', collection, resp, options); }; wrapError(this, options); return this.sync('read', this, options); }, // Create a new instance of a model in this collection. Add the model to the // collection immediately, unless `wait: true` is passed, in which case we // wait for the server to agree. create: function(model, options) { options = options ? _.clone(options) : {}; var wait = options.wait; model = this._prepareModel(model, options); if (!model) return false; if (!wait) this.add(model, options); var collection = this; var success = options.success; options.success = function(m, resp, callbackOpts) { if (wait) collection.add(m, callbackOpts); if (success) success.call(callbackOpts.context, m, resp, callbackOpts); }; model.save(null, options); return model; }, // **parse** converts a response into a list of models to be added to the // collection. The default implementation is just to pass it through. parse: function(resp, options) { return resp; }, // Create a new collection with an identical list of models as this one. clone: function() { return new this.constructor(this.models, { model: this.model, comparator: this.comparator }); }, // Define how to uniquely identify models in the collection. modelId: function(attrs) { return attrs[this.model.prototype.idAttribute || 'id']; }, // Private method to reset all internal state. Called when the collection // is first initialized or reset. _reset: function() { this.length = 0; this.models = []; this._byId = {}; }, // Prepare a hash of attributes (or other model) to be added to this // collection. _prepareModel: function(attrs, options) { if (this._isModel(attrs)) { if (!attrs.collection) attrs.collection = this; return attrs; } options = options ? _.clone(options) : {}; options.collection = this; var model = new this.model(attrs, options); if (!model.validationError) return model; this.trigger('invalid', this, model.validationError, options); return false; }, // Internal method called by both remove and set. _removeModels: function(models, options) { var removed = []; for (var i = 0; i < models.length; i++) { var model = this.get(models[i]); if (!model) continue; var index = this.indexOf(model); this.models.splice(index, 1); this.length--; // Remove references before triggering 'remove' event to prevent an // infinite loop. #3693 delete this._byId[model.cid]; var id = this.modelId(model.attributes); if (id != null) delete this._byId[id]; if (!options.silent) { options.index = index; model.trigger('remove', model, this, options); } removed.push(model); this._removeReference(model, options); } return removed; }, // Method for checking whether an object should be considered a model for // the purposes of adding to the collection. _isModel: function(model) { return model instanceof Model; }, // Internal method to create a model's ties to a collection. _addReference: function(model, options) { this._byId[model.cid] = model; var id = this.modelId(model.attributes); if (id != null) this._byId[id] = model; model.on('all', this._onModelEvent, this); }, // Internal method to sever a model's ties to a collection. _removeReference: function(model, options) { delete this._byId[model.cid]; var id = this.modelId(model.attributes); if (id != null) delete this._byId[id]; if (this === model.collection) delete model.collection; model.off('all', this._onModelEvent, this); }, // Internal method called every time a model in the set fires an event. // Sets need to update their indexes when models change ids. All other // events simply proxy through. "add" and "remove" events that originate // in other collections are ignored. _onModelEvent: function(event, model, collection, options) { if (model) { if ((event === 'add' || event === 'remove') && collection !== this) return; if (event === 'destroy') this.remove(model, options); if (event === 'change') { var prevId = this.modelId(model.previousAttributes()); var id = this.modelId(model.attributes); if (prevId !== id) { if (prevId != null) delete this._byId[prevId]; if (id != null) this._byId[id] = model; } } } this.trigger.apply(this, arguments); } }); // Underscore methods that we want to implement on the Collection. // 90% of the core usefulness of Backbone Collections is actually implemented // right here: var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0, foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3, select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3, contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3, head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3, without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3, isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3, sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3}; // Mix in each Underscore method as a proxy to `Collection#models`. addUnderscoreMethods(Collection, collectionMethods, 'models'); // Backbone.View // ------------- // Backbone Views are almost more convention than they are actual code. A View // is simply a JavaScript object that represents a logical chunk of UI in the // DOM. This might be a single item, an entire list, a sidebar or panel, or // even the surrounding frame which wraps your whole app. Defining a chunk of // UI as a **View** allows you to define your DOM events declaratively, without // having to worry about render order ... and makes it easy for the view to // react to specific changes in the state of your models. // Creating a Backbone.View creates its initial element outside of the DOM, // if an existing element is not provided... var View = Backbone.View = function(options) { this.cid = _.uniqueId('view'); _.extend(this, _.pick(options, viewOptions)); this._ensureElement(); this.initialize.apply(this, arguments); }; // Cached regex to split keys for `delegate`. var delegateEventSplitter = /^(\S+)\s*(.*)$/; // List of view options to be set as properties. var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; // Set up all inheritable **Backbone.View** properties and methods. _.extend(View.prototype, Events, { // The default `tagName` of a View's element is `"div"`. tagName: 'div', // jQuery delegate for element lookup, scoped to DOM elements within the // current view. This should be preferred to global lookups where possible. $: function(selector) { return this.$el.find(selector); }, // Initialize is an empty function by default. Override it with your own // initialization logic. initialize: function(){}, // **render** is the core function that your view should override, in order // to populate its element (`this.el`), with the appropriate HTML. The // convention is for **render** to always return `this`. render: function() { return this; }, // Remove this view by taking the element out of the DOM, and removing any // applicable Backbone.Events listeners. remove: function() { this._removeElement(); this.stopListening(); return this; }, // Remove this view's element from the document and all event listeners // attached to it. Exposed for subclasses using an alternative DOM // manipulation API. _removeElement: function() { this.$el.remove(); }, // Change the view's element (`this.el` property) and re-delegate the // view's events on the new element. setElement: function(element) { this.undelegateEvents(); this._setElement(element); this.delegateEvents(); return this; }, // Creates the `this.el` and `this.$el` references for this view using the // given `el`. `el` can be a CSS selector or an HTML string, a jQuery // context or an element. Subclasses can override this to utilize an // alternative DOM manipulation API and are only required to set the // `this.el` property. _setElement: function(el) { this.$el = el instanceof Backbone.$ ? el : Backbone.$(el); this.el = this.$el[0]; }, // Set callbacks, where `this.events` is a hash of // // *{"event selector": "callback"}* // // { // 'mousedown .title': 'edit', // 'click .button': 'save', // 'click .open': function(e) { ... } // } // // pairs. Callbacks will be bound to the view, with `this` set properly. // Uses event delegation for efficiency. // Omitting the selector binds the event to `this.el`. delegateEvents: function(events) { events || (events = _.result(this, 'events')); if (!events) return this; this.undelegateEvents(); for (var key in events) { var method = events[key]; if (!_.isFunction(method)) method = this[method]; if (!method) continue; var match = key.match(delegateEventSplitter); this.delegate(match[1], match[2], _.bind(method, this)); } return this; }, // Add a single event listener to the view's element (or a child element // using `selector`). This only works for delegate-able events: not `focus`, // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer. delegate: function(eventName, selector, listener) { this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener); return this; }, // Clears all callbacks previously bound to the view by `delegateEvents`. // You usually don't need to use this, but may wish to if you have multiple // Backbone views attached to the same DOM element. undelegateEvents: function() { if (this.$el) this.$el.off('.delegateEvents' + this.cid); return this; }, // A finer-grained `undelegateEvents` for removing a single delegated event. // `selector` and `listener` are both optional. undelegate: function(eventName, selector, listener) { this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener); return this; }, // Produces a DOM element to be assigned to your view. Exposed for // subclasses using an alternative DOM manipulation API. _createElement: function(tagName) { return document.createElement(tagName); }, // Ensure that the View has a DOM element to render into. // If `this.el` is a string, pass it through `$()`, take the first // matching element, and re-assign it to `el`. Otherwise, create // an element from the `id`, `className` and `tagName` properties. _ensureElement: function() { if (!this.el) { var attrs = _.extend({}, _.result(this, 'attributes')); if (this.id) attrs.id = _.result(this, 'id'); if (this.className) attrs['class'] = _.result(this, 'className'); this.setElement(this._createElement(_.result(this, 'tagName'))); this._setAttributes(attrs); } else { this.setElement(_.result(this, 'el')); } }, // Set attributes from a hash on this view's element. Exposed for // subclasses using an alternative DOM manipulation API. _setAttributes: function(attributes) { this.$el.attr(attributes); } }); // Backbone.sync // ------------- // Override this function to change the manner in which Backbone persists // models to the server. You will be passed the type of request, and the // model in question. By default, makes a RESTful Ajax request // to the model's `url()`. Some possible customizations could be: // // * Use `setTimeout` to batch rapid-fire updates into a single request. // * Send up the models as XML instead of JSON. // * Persist models via WebSockets instead of Ajax. // // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests // as `POST`, with a `_method` parameter containing the true HTTP method, // as well as all requests with the body as `application/x-www-form-urlencoded` // instead of `application/json` with the model in a param named `model`. // Useful when interfacing with server-side languages like **PHP** that make // it difficult to read the body of `PUT` requests. Backbone.sync = function(method, model, options) { var type = methodMap[method]; // Default options, unless specified. _.defaults(options || (options = {}), { emulateHTTP: Backbone.emulateHTTP, emulateJSON: Backbone.emulateJSON }); // Default JSON-request options. var params = {type: type, dataType: 'json'}; // Ensure that we have a URL. if (!options.url) { params.url = _.result(model, 'url') || urlError(); } // Ensure that we have the appropriate request data. if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { params.contentType = 'application/json'; params.data = JSON.stringify(options.attrs || model.toJSON(options)); } // For older servers, emulate JSON by encoding the request into an HTML-form. if (options.emulateJSON) { params.contentType = 'application/x-www-form-urlencoded'; params.data = params.data ? {model: params.data} : {}; } // For older servers, emulate HTTP by mimicking the HTTP method with `_method` // And an `X-HTTP-Method-Override` header. if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { params.type = 'POST'; if (options.emulateJSON) params.data._method = type; var beforeSend = options.beforeSend; options.beforeSend = function(xhr) { xhr.setRequestHeader('X-HTTP-Method-Override', type); if (beforeSend) return beforeSend.apply(this, arguments); }; } // Don't process data on a non-GET request. if (params.type !== 'GET' && !options.emulateJSON) { params.processData = false; } // Pass along `textStatus` and `errorThrown` from jQuery. var error = options.error; options.error = function(xhr, textStatus, errorThrown) { options.textStatus = textStatus; options.errorThrown = errorThrown; if (error) error.call(options.context, xhr, textStatus, errorThrown); }; // Make the request, allowing the user to override any Ajax options. var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); model.trigger('request', model, xhr, options); return xhr; }; // Map from CRUD to HTTP for our default `Backbone.sync` implementation. var methodMap = { 'create': 'POST', 'update': 'PUT', 'patch': 'PATCH', 'delete': 'DELETE', 'read': 'GET' }; // Set the default implementation of `Backbone.ajax` to proxy through to `$`. // Override this if you'd like to use a different library. Backbone.ajax = function() { return Backbone.$.ajax.apply(Backbone.$, arguments); }; // Backbone.Router // --------------- // Routers map faux-URLs to actions, and fire events when routes are // matched. Creating a new one sets its `routes` hash, if not set statically. var Router = Backbone.Router = function(options) { options || (options = {}); if (options.routes) this.routes = options.routes; this._bindRoutes(); this.initialize.apply(this, arguments); }; // Cached regular expressions for matching named param parts and splatted // parts of route strings. var optionalParam = /\((.*?)\)/g; var namedParam = /(\(\?)?:\w+/g; var splatParam = /\*\w+/g; var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; // Set up all inheritable **Backbone.Router** properties and methods. _.extend(Router.prototype, Events, { // Initialize is an empty function by default. Override it with your own // initialization logic. initialize: function(){}, // Manually bind a single named route to a callback. For example: // // this.route('search/:query/p:num', 'search', function(query, num) { // ... // }); // route: function(route, name, callback) { if (!_.isRegExp(route)) route = this._routeToRegExp(route); if (_.isFunction(name)) { callback = name; name = ''; } if (!callback) callback = this[name]; var router = this; Backbone.history.route(route, function(fragment) { var args = router._extractParameters(route, fragment); if (router.execute(callback, args, name) !== false) { router.trigger.apply(router, ['route:' + name].concat(args)); router.trigger('route', name, args); Backbone.history.trigger('route', router, name, args); } }); return this; }, // Execute a route handler with the provided parameters. This is an // excellent place to do pre-route setup or post-route cleanup. execute: function(callback, args, name) { if (callback) callback.apply(this, args); }, // Simple proxy to `Backbone.history` to save a fragment into the history. navigate: function(fragment, options) { Backbone.history.navigate(fragment, options); return this; }, // Bind all defined routes to `Backbone.history`. We have to reverse the // order of the routes here to support behavior where the most general // routes can be defined at the bottom of the route map. _bindRoutes: function() { if (!this.routes) return; this.routes = _.result(this, 'routes'); var route, routes = _.keys(this.routes); while ((route = routes.pop()) != null) { this.route(route, this.routes[route]); } }, // Convert a route string into a regular expression, suitable for matching // against the current location hash. _routeToRegExp: function(route) { route = route.replace(escapeRegExp, '\\$&') .replace(optionalParam, '(?:$1)?') .replace(namedParam, function(match, optional) { return optional ? match : '([^/?]+)'; }) .replace(splatParam, '([^?]*?)'); return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); }, // Given a route, and a URL fragment that it matches, return the array of // extracted decoded parameters. Empty or unmatched parameters will be // treated as `null` to normalize cross-browser behavior. _extractParameters: function(route, fragment) { var params = route.exec(fragment).slice(1); return _.map(params, function(param, i) { // Don't decode the search params. if (i === params.length - 1) return param || null; return param ? decodeURIComponent(param) : null; }); } }); // Backbone.History // ---------------- // Handles cross-browser history management, based on either // [pushState](http://diveintohtml5.info/history.html) and real URLs, or // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) // and URL fragments. If the browser supports neither (old IE, natch), // falls back to polling. var History = Backbone.History = function() { this.handlers = []; this.checkUrl = _.bind(this.checkUrl, this); // Ensure that `History` can be used outside of the browser. if (typeof window !== 'undefined') { this.location = window.location; this.history = window.history; } }; // Cached regex for stripping a leading hash/slash and trailing space. var routeStripper = /^[#\/]|\s+$/g; // Cached regex for stripping leading and trailing slashes. var rootStripper = /^\/+|\/+$/g; // Cached regex for stripping urls of hash. var pathStripper = /#.*$/; // Has the history handling already been started? History.started = false; // Set up all inheritable **Backbone.History** properties and methods. _.extend(History.prototype, Events, { // The default interval to poll for hash changes, if necessary, is // twenty times a second. interval: 50, // Are we at the app root? atRoot: function() { var path = this.location.pathname.replace(/[^\/]$/, '$&/'); return path === this.root && !this.getSearch(); }, // Does the pathname match the root? matchRoot: function() { var path = this.decodeFragment(this.location.pathname); var rootPath = path.slice(0, this.root.length - 1) + '/'; return rootPath === this.root; }, // Unicode characters in `location.pathname` are percent encoded so they're // decoded for comparison. `%25` should not be decoded since it may be part // of an encoded parameter. decodeFragment: function(fragment) { return decodeURI(fragment.replace(/%25/g, '%2525')); }, // In IE6, the hash fragment and search params are incorrect if the // fragment contains `?`. getSearch: function() { var match = this.location.href.replace(/#.*/, '').match(/\?.+/); return match ? match[0] : ''; }, // Gets the true hash value. Cannot use location.hash directly due to bug // in Firefox where location.hash will always be decoded. getHash: function(window) { var match = (window || this).location.href.match(/#(.*)$/); return match ? match[1] : ''; }, // Get the pathname and search params, without the root. getPath: function() { var path = this.decodeFragment( this.location.pathname + this.getSearch() ).slice(this.root.length - 1); return path.charAt(0) === '/' ? path.slice(1) : path; }, // Get the cross-browser normalized URL fragment from the path or hash. getFragment: function(fragment) { if (fragment == null) { if (this._usePushState || !this._wantsHashChange) { fragment = this.getPath(); } else { fragment = this.getHash(); } } return fragment.replace(routeStripper, ''); }, // Start the hash change handling, returning `true` if the current URL matches // an existing route, and `false` otherwise. start: function(options) { if (History.started) throw new Error('Backbone.history has already been started'); History.started = true; // Figure out the initial configuration. Do we need an iframe? // Is pushState desired ... is it available? this.options = _.extend({root: '/'}, this.options, options); this.root = this.options.root; this._wantsHashChange = this.options.hashChange !== false; this._hasHashChange = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7); this._useHashChange = this._wantsHashChange && this._hasHashChange; this._wantsPushState = !!this.options.pushState; this._hasPushState = !!(this.history && this.history.pushState); this._usePushState = this._wantsPushState && this._hasPushState; this.fragment = this.getFragment(); // Normalize root to always include a leading and trailing slash. this.root = ('/' + this.root + '/').replace(rootStripper, '/'); // Transition from hashChange to pushState or vice versa if both are // requested. if (this._wantsHashChange && this._wantsPushState) { // If we've started off with a route from a `pushState`-enabled // browser, but we're currently in a browser that doesn't support it... if (!this._hasPushState && !this.atRoot()) { var rootPath = this.root.slice(0, -1) || '/'; this.location.replace(rootPath + '#' + this.getPath()); // Return immediately as browser will do redirect to new url return true; // Or if we've started out with a hash-based route, but we're currently // in a browser where it could be `pushState`-based instead... } else if (this._hasPushState && this.atRoot()) { this.navigate(this.getHash(), {replace: true}); } } // Proxy an iframe to handle location events if the browser doesn't // support the `hashchange` event, HTML5 history, or the user wants // `hashChange` but not `pushState`. if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) { this.iframe = document.createElement('iframe'); this.iframe.src = 'javascript:0'; this.iframe.style.display = 'none'; this.iframe.tabIndex = -1; var body = document.body; // Using `appendChild` will throw on IE < 9 if the document is not ready. var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow; iWindow.document.open(); iWindow.document.close(); iWindow.location.hash = '#' + this.fragment; } // Add a cross-platform `addEventListener` shim for older browsers. var addEventListener = window.addEventListener || function(eventName, listener) { return attachEvent('on' + eventName, listener); }; // Depending on whether we're using pushState or hashes, and whether // 'onhashchange' is supported, determine how we check the URL state. if (this._usePushState) { addEventListener('popstate', this.checkUrl, false); } else if (this._useHashChange && !this.iframe) { addEventListener('hashchange', this.checkUrl, false); } else if (this._wantsHashChange) { this._checkUrlInterval = setInterval(this.checkUrl, this.interval); } if (!this.options.silent) return this.loadUrl(); }, // Disable Backbone.history, perhaps temporarily. Not useful in a real app, // but possibly useful for unit testing Routers. stop: function() { // Add a cross-platform `removeEventListener` shim for older browsers. var removeEventListener = window.removeEventListener || function(eventName, listener) { return detachEvent('on' + eventName, listener); }; // Remove window listeners. if (this._usePushState) { removeEventListener('popstate', this.checkUrl, false); } else if (this._useHashChange && !this.iframe) { removeEventListener('hashchange', this.checkUrl, false); } // Clean up the iframe if necessary. if (this.iframe) { document.body.removeChild(this.iframe); this.iframe = null; } // Some environments will throw when clearing an undefined interval. if (this._checkUrlInterval) clearInterval(this._checkUrlInterval); History.started = false; }, // Add a route to be tested when the fragment changes. Routes added later // may override previous routes. route: function(route, callback) { this.handlers.unshift({route: route, callback: callback}); }, // Checks the current URL to see if it has changed, and if it has, // calls `loadUrl`, normalizing across the hidden iframe. checkUrl: function(e) { var current = this.getFragment(); // If the user pressed the back button, the iframe's hash will have // changed and we should use that for comparison. if (current === this.fragment && this.iframe) { current = this.getHash(this.iframe.contentWindow); } if (current === this.fragment) return false; if (this.iframe) this.navigate(current); this.loadUrl(); }, // Attempt to load the current URL fragment. If a route succeeds with a // match, returns `true`. If no defined routes matches the fragment, // returns `false`. loadUrl: function(fragment) { // If the root doesn't match, no routes can match either. if (!this.matchRoot()) return false; fragment = this.fragment = this.getFragment(fragment); return _.some(this.handlers, function(handler) { if (handler.route.test(fragment)) { handler.callback(fragment); return true; } }); }, // Save a fragment into the hash history, or replace the URL state if the // 'replace' option is passed. You are responsible for properly URL-encoding // the fragment in advance. // // The options object can contain `trigger: true` if you wish to have the // route callback be fired (not usually desirable), or `replace: true`, if // you wish to modify the current URL without adding an entry to the history. navigate: function(fragment, options) { if (!History.started) return false; if (!options || options === true) options = {trigger: !!options}; // Normalize the fragment. fragment = this.getFragment(fragment || ''); // Don't include a trailing slash on the root. var rootPath = this.root; if (fragment === '' || fragment.charAt(0) === '?') { rootPath = rootPath.slice(0, -1) || '/'; } var url = rootPath + fragment; // Strip the hash and decode for matching. fragment = this.decodeFragment(fragment.replace(pathStripper, '')); if (this.fragment === fragment) return; this.fragment = fragment; // If pushState is available, we use it to set the fragment as a real URL. if (this._usePushState) { this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url); // If hash changes haven't been explicitly disabled, update the hash // fragment to store history. } else if (this._wantsHashChange) { this._updateHash(this.location, fragment, options.replace); if (this.iframe && fragment !== this.getHash(this.iframe.contentWindow)) { var iWindow = this.iframe.contentWindow; // Opening and closing the iframe tricks IE7 and earlier to push a // history entry on hash-tag change. When replace is true, we don't // want this. if (!options.replace) { iWindow.document.open(); iWindow.document.close(); } this._updateHash(iWindow.location, fragment, options.replace); } // If you've told us that you explicitly don't want fallback hashchange- // based history, then `navigate` becomes a page refresh. } else { return this.location.assign(url); } if (options.trigger) return this.loadUrl(fragment); }, // Update the hash location, either replacing the current entry, or adding // a new one to the browser history. _updateHash: function(location, fragment, replace) { if (replace) { var href = location.href.replace(/(javascript:|#).*$/, ''); location.replace(href + '#' + fragment); } else { // Some browsers require that `hash` contains a leading #. location.hash = '#' + fragment; } } }); // Create the default Backbone.history. Backbone.history = new History; // Helpers // ------- // Helper function to correctly set up the prototype chain for subclasses. // Similar to `goog.inherits`, but uses a hash of prototype properties and // class properties to be extended. var extend = function(protoProps, staticProps) { var parent = this; var child; // The constructor function for the new subclass is either defined by you // (the "constructor" property in your `extend` definition), or defaulted // by us to simply call the parent constructor. if (protoProps && _.has(protoProps, 'constructor')) { child = protoProps.constructor; } else { child = function(){ return parent.apply(this, arguments); }; } // Add static properties to the constructor function, if supplied. _.extend(child, parent, staticProps); // Set the prototype chain to inherit from `parent`, without calling // `parent`'s constructor function and add the prototype properties. child.prototype = _.create(parent.prototype, protoProps); child.prototype.constructor = child; // Set a convenience property in case the parent's prototype is needed // later. child.__super__ = parent.prototype; return child; }; // Set up inheritance for the model, collection, router, view and history. Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend; // Throw an error when a URL is needed, and none is supplied. var urlError = function() { throw new Error('A "url" property or function must be specified'); }; // Wrap an optional error callback with a fallback error event. var wrapError = function(model, options) { var error = options.error; options.error = function(resp) { if (error) error.call(options.context, model, resp, options); model.trigger('error', model, resp, options); }; }; return Backbone; }); /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) /***/ }), /***/ "./node_modules/bootstrap.native/dist/bootstrap-native-v4.js": /*!*******************************************************************!*\ !*** ./node_modules/bootstrap.native/dist/bootstrap-native-v4.js ***! \*******************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Native Javascript for Bootstrap 4 v2.0.22 | © dnp_theme | MIT-License (function (root, factory) { if (true) { // AMD support: !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } else { var bsn; } }(this, function () { /* Native Javascript for Bootstrap 4 | Internal Utility Functions ----------------------------------------------------------------*/ "use strict"; // globals var globalObject = typeof global !== 'undefined' ? global : this||window, DOC = document, HTML = DOC.documentElement, body = 'body', // allow the library to be used in // Native Javascript for Bootstrap Global Object BSN = globalObject.BSN = {}, supports = BSN.supports = [], // function toggle attributes dataToggle = 'data-toggle', dataDismiss = 'data-dismiss', dataSpy = 'data-spy', dataRide = 'data-ride', // components stringAlert = 'Alert', stringButton = 'Button', stringCarousel = 'Carousel', stringCollapse = 'Collapse', stringDropdown = 'Dropdown', stringModal = 'Modal', stringPopover = 'Popover', stringScrollSpy = 'ScrollSpy', stringTab = 'Tab', stringTooltip = 'Tooltip', // options DATA API databackdrop = 'data-backdrop', dataKeyboard = 'data-keyboard', dataTarget = 'data-target', dataInterval = 'data-interval', dataHeight = 'data-height', dataPause = 'data-pause', dataTitle = 'data-title', dataOriginalTitle = 'data-original-title', dataOriginalText = 'data-original-text', dataDismissible = 'data-dismissible', dataTrigger = 'data-trigger', dataAnimation = 'data-animation', dataContainer = 'data-container', dataPlacement = 'data-placement', dataDelay = 'data-delay', dataOffsetTop = 'data-offset-top', dataOffsetBottom = 'data-offset-bottom', // option keys backdrop = 'backdrop', keyboard = 'keyboard', delay = 'delay', content = 'content', target = 'target', interval = 'interval', pause = 'pause', animation = 'animation', placement = 'placement', container = 'container', // box model offsetTop = 'offsetTop', offsetBottom = 'offsetBottom', offsetLeft = 'offsetLeft', scrollTop = 'scrollTop', scrollLeft = 'scrollLeft', clientWidth = 'clientWidth', clientHeight = 'clientHeight', offsetWidth = 'offsetWidth', offsetHeight = 'offsetHeight', innerWidth = 'innerWidth', innerHeight = 'innerHeight', scrollHeight = 'scrollHeight', height = 'height', // aria ariaExpanded = 'aria-expanded', ariaHidden = 'aria-hidden', // event names clickEvent = 'click', hoverEvent = 'hover', keydownEvent = 'keydown', keyupEvent = 'keyup', resizeEvent = 'resize', scrollEvent = 'scroll', // originalEvents showEvent = 'show', shownEvent = 'shown', hideEvent = 'hide', hiddenEvent = 'hidden', closeEvent = 'close', closedEvent = 'closed', slidEvent = 'slid', slideEvent = 'slide', changeEvent = 'change', // other getAttribute = 'getAttribute', setAttribute = 'setAttribute', hasAttribute = 'hasAttribute', createElement = 'createElement', appendChild = 'appendChild', innerHTML = 'innerHTML', getElementsByTagName = 'getElementsByTagName', preventDefault = 'preventDefault', getBoundingClientRect = 'getBoundingClientRect', querySelectorAll = 'querySelectorAll', getElementsByCLASSNAME = 'getElementsByClassName', indexOf = 'indexOf', parentNode = 'parentNode', length = 'length', toLowerCase = 'toLowerCase', Transition = 'Transition', Webkit = 'Webkit', style = 'style', push = 'push', tabindex = 'tabindex', contains = 'contains', active = 'active', showClass = 'show', collapsing = 'collapsing', disabled = 'disabled', loading = 'loading', left = 'left', right = 'right', top = 'top', bottom = 'bottom', // tooltip / popover mouseHover = ('onmouseleave' in DOC) ? [ 'mouseenter', 'mouseleave'] : [ 'mouseover', 'mouseout' ], tipPositions = /\b(top|bottom|left|right)+/, // modal modalOverlay = 0, fixedTop = 'fixed-top', fixedBottom = 'fixed-bottom', // transitionEnd since 2.0.4 supportTransitions = Webkit+Transition in HTML[style] || Transition[toLowerCase]() in HTML[style], transitionEndEvent = Webkit+Transition in HTML[style] ? Webkit[toLowerCase]()+Transition+'End' : Transition[toLowerCase]()+'end', // set new focus element since 2.0.3 setFocus = function(element){ element.focus ? element.focus() : element.setActive(); }, // class manipulation, since 2.0.0 requires polyfill.js addClass = function(element,classNAME) { element.classList.add(classNAME); }, removeClass = function(element,classNAME) { element.classList.remove(classNAME); }, hasClass = function(element,classNAME){ // since 2.0.0 return element.classList[contains](classNAME); }, // selection methods getElementsByClassName = function(element,classNAME) { // returns Array return [].slice.call(element[getElementsByCLASSNAME]( classNAME )); }, queryElement = function (selector, parent) { var lookUp = parent ? parent : DOC; return typeof selector === 'object' ? selector : lookUp.querySelector(selector); }, getClosest = function (element, selector) { //element is the element and selector is for the closest parent element to find // source http://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/ var firstChar = selector.charAt(0), selectorSubstring = selector.substr(1); if ( firstChar === '.' ) {// If selector is a class for ( ; element && element !== DOC; element = element[parentNode] ) { // Get closest match if ( queryElement(selector,element[parentNode]) !== null && hasClass(element,selectorSubstring) ) { return element; } } } else if ( firstChar === '#' ) { // If selector is an ID for ( ; element && element !== DOC; element = element[parentNode] ) { // Get closest match if ( element.id === selectorSubstring ) { return element; } } } return false; }, // event attach jQuery style / trigger since 1.2.0 on = function (element, event, handler) { element.addEventListener(event, handler, false); }, off = function(element, event, handler) { element.removeEventListener(event, handler, false); }, one = function (element, event, handler) { // one since 2.0.4 on(element, event, function handlerWrapper(e){ handler(e); off(element, event, handlerWrapper); }); }, emulateTransitionEnd = function(element,handler){ // emulateTransitionEnd since 2.0.4 if (supportTransitions) { one(element, transitionEndEvent, function(e){ handler(e); }); } else { handler(); } }, bootstrapCustomEvent = function (eventName, componentName, related) { var OriginalCustomEvent = new CustomEvent( eventName + '.bs.' + componentName); OriginalCustomEvent.relatedTarget = related; this.dispatchEvent(OriginalCustomEvent); }, // tooltip / popover stuff getScroll = function() { // also Affix and ScrollSpy uses it return { y : globalObject.pageYOffset || HTML[scrollTop], x : globalObject.pageXOffset || HTML[scrollLeft] } }, styleTip = function(link,element,position,parent) { // both popovers and tooltips (target,tooltip,placement,elementToAppendTo) var elementDimensions = { w : element[offsetWidth], h: element[offsetHeight] }, windowWidth = (HTML[clientWidth] || DOC[body][clientWidth]), windowHeight = (HTML[clientHeight] || DOC[body][clientHeight]), rect = link[getBoundingClientRect](), scroll = parent === DOC[body] ? getScroll() : { x: parent[offsetLeft] + parent[scrollLeft], y: parent[offsetTop] + parent[scrollTop] }, linkDimensions = { w: rect[right] - rect[left], h: rect[bottom] - rect[top] }, isPopover = hasClass(element,'popover'), topPosition, leftPosition, arrow = queryElement('.arrow',element), arrowTop, arrowLeft, arrowWidth, arrowHeight, halfTopExceed = rect[top] + linkDimensions.h/2 - elementDimensions.h/2 < 0, halfLeftExceed = rect[left] + linkDimensions.w/2 - elementDimensions.w/2 < 0, halfRightExceed = rect[left] + elementDimensions.w/2 + linkDimensions.w/2 >= windowWidth, halfBottomExceed = rect[top] + elementDimensions.h/2 + linkDimensions.h/2 >= windowHeight, topExceed = rect[top] - elementDimensions.h < 0, leftExceed = rect[left] - elementDimensions.w < 0, bottomExceed = rect[top] + elementDimensions.h + linkDimensions.h >= windowHeight, rightExceed = rect[left] + elementDimensions.w + linkDimensions.w >= windowWidth; // recompute position position = (position === left || position === right) && leftExceed && rightExceed ? top : position; // first, when both left and right limits are exceeded, we fall back to top|bottom position = position === top && topExceed ? bottom : position; position = position === bottom && bottomExceed ? top : position; position = position === left && leftExceed ? right : position; position = position === right && rightExceed ? left : position; // update tooltip/popover class element.className[indexOf](position) === -1 && (element.className = element.className.replace(tipPositions,position)); // we check the computed width & height and update here arrowWidth = arrow[offsetWidth]; arrowHeight = arrow[offsetHeight]; // apply styling to tooltip or popover if ( position === left || position === right ) { // secondary|side positions if ( position === left ) { // LEFT leftPosition = rect[left] + scroll.x - elementDimensions.w - ( isPopover ? arrowWidth : 0 ); } else { // RIGHT leftPosition = rect[left] + scroll.x + linkDimensions.w; } // adjust top and arrow if (halfTopExceed) { topPosition = rect[top] + scroll.y; arrowTop = linkDimensions.h/2 - arrowWidth; } else if (halfBottomExceed) { topPosition = rect[top] + scroll.y - elementDimensions.h + linkDimensions.h; arrowTop = elementDimensions.h - linkDimensions.h/2 - arrowWidth; } else { topPosition = rect[top] + scroll.y - elementDimensions.h/2 + linkDimensions.h/2; arrowTop = elementDimensions.h/2 - (isPopover ? arrowHeight*0.9 : arrowHeight/2); } } else if ( position === top || position === bottom ) { // primary|vertical positions if ( position === top) { // TOP topPosition = rect[top] + scroll.y - elementDimensions.h - ( isPopover ? arrowHeight : 0 ); } else { // BOTTOM topPosition = rect[top] + scroll.y + linkDimensions.h; } // adjust left | right and also the arrow if (halfLeftExceed) { leftPosition = 0; arrowLeft = rect[left] + linkDimensions.w/2 - arrowWidth; } else if (halfRightExceed) { leftPosition = windowWidth - elementDimensions.w*1.01; arrowLeft = elementDimensions.w - ( windowWidth - rect[left] ) + linkDimensions.w/2 - arrowWidth/2; } else { leftPosition = rect[left] + scroll.x - elementDimensions.w/2 + linkDimensions.w/2; arrowLeft = elementDimensions.w/2 - arrowWidth/2; } } // apply style to tooltip/popover and its arrow element[style][top] = topPosition + 'px'; element[style][left] = leftPosition + 'px'; arrowTop && (arrow[style][top] = arrowTop + 'px'); arrowLeft && (arrow[style][left] = arrowLeft + 'px'); }; BSN.version = '2.0.22'; /* Native Javascript for Bootstrap 4 | Alert -------------------------------------------*/ // ALERT DEFINITION // ================ var Alert = function( element ) { // initialization element element = queryElement(element); // bind, target alert, duration and stuff var self = this, component = 'alert', alert = getClosest(element,'.'+component), triggerHandler = function(){ hasClass(alert,'fade') ? emulateTransitionEnd(alert,transitionEndHandler) : transitionEndHandler(); }, // handlers clickHandler = function(e){ alert = getClosest(e[target],'.'+component); element = queryElement('['+dataDismiss+'="'+component+'"]',alert); element && alert && (element === e[target] || element[contains](e[target])) && self.close(); }, transitionEndHandler = function(){ bootstrapCustomEvent.call(alert, closedEvent, component); off(element, clickEvent, clickHandler); // detach it's listener alert[parentNode].removeChild(alert); }; // public method this.close = function() { if ( alert && element && hasClass(alert,showClass) ) { bootstrapCustomEvent.call(alert, closeEvent, component); removeClass(alert,showClass); alert && triggerHandler(); } }; // init if ( !(stringAlert in element ) ) { // prevent adding event handlers twice on(element, clickEvent, clickHandler); } element[stringAlert] = self; }; // ALERT DATA API // ============== supports[push]([stringAlert, Alert, '['+dataDismiss+'="alert"]']); /* Native Javascript for Bootstrap 4 | Button ---------------------------------------------*/ // BUTTON DEFINITION // =================== var Button = function( element ) { // initialization element element = queryElement(element); // constant var toggled = false, // toggled makes sure to prevent triggering twice the change.bs.button events // strings component = 'button', checked = 'checked', reset = 'reset', LABEL = 'LABEL', INPUT = 'INPUT', // private methods keyHandler = function(e){ var key = e.which || e.keyCode; key === 32 && e[target] === DOC.activeElement && toggle(e); }, preventScroll = function(e){ var key = e.which || e.keyCode; key === 32 && e[preventDefault](); }, toggle = function(e) { var label = e[target].tagName === LABEL ? e[target] : e[target][parentNode].tagName === LABEL ? e[target][parentNode] : null; // the .btn label if ( !label ) return; //react if a label or its immediate child is clicked var eventTarget = e[target], // the button itself, the target of the handler function labels = getElementsByClassName(eventTarget[parentNode],'btn'), // all the button group buttons input = label[getElementsByTagName](INPUT)[0]; if ( !input ) return; //return if no input found // manage the dom manipulation if ( input.type === 'checkbox' ) { //checkboxes if ( !input[checked] ) { addClass(label,active); input[getAttribute](checked); input[setAttribute](checked,checked); input[checked] = true; } else { removeClass(label,active); input[getAttribute](checked); input.removeAttribute(checked); input[checked] = false; } if (!toggled) { // prevent triggering the event twice toggled = true; bootstrapCustomEvent.call(input, changeEvent, component); //trigger the change for the input bootstrapCustomEvent.call(element, changeEvent, component); //trigger the change for the btn-group } } if ( input.type === 'radio' && !toggled ) { // radio buttons if ( !input[checked] ) { // don't trigger if already active addClass(label,active); input[setAttribute](checked,checked); input[checked] = true; bootstrapCustomEvent.call(input, changeEvent, component); //trigger the change for the input bootstrapCustomEvent.call(element, changeEvent, component); //trigger the change for the btn-group toggled = true; for (var i = 0, ll = labels[length]; i= 0; // bottom && top }, setActivePage = function( pageIndex ) { //indicators for ( var i = 0, icl = indicators[length]; i < icl; i++ ) { removeClass(indicators[i],active); } if (indicators[pageIndex]) addClass(indicators[pageIndex], active); }; // public methods this.cycle = function() { timer = setInterval(function() { isElementInScrollRange() && (index++, self.slideTo( index ) ); }, this[interval]); }; this.slideTo = function( next ) { if (isSliding) return; // when controled via methods, make sure to check again var activeItem = this.getActiveIndex(), // the current active orientation; // determine slideDirection first if ( (activeItem < next ) || (activeItem === 0 && next === total -1 ) ) { slideDirection = self[direction] = left; // next } else if ( (activeItem > next) || (activeItem === total - 1 && next === 0 ) ) { slideDirection = self[direction] = right; // prev } // find the right next index if ( next < 0 ) { next = total - 1; } else if ( next === total ){ next = 0; } // update index index = next; orientation = slideDirection === left ? 'next' : 'prev'; //determine type bootstrapCustomEvent.call(element, slideEvent, component, slides[next]); // here we go with the slide isSliding = true; clearInterval(timer); setActivePage( next ); if ( supportTransitions && hasClass(element,'slide') ) { addClass(slides[next],carouselItem +'-'+ orientation); slides[next][offsetWidth]; addClass(slides[next],carouselItem +'-'+ slideDirection); addClass(slides[activeItem],carouselItem +'-'+ slideDirection); one(slides[activeItem], transitionEndEvent, function(e) { var timeout = e[target] !== slides[activeItem] ? e.elapsedTime*1000 : 0; setTimeout(function(){ isSliding = false; addClass(slides[next],active); removeClass(slides[activeItem],active); removeClass(slides[next],carouselItem +'-'+ orientation); removeClass(slides[next],carouselItem +'-'+ slideDirection); removeClass(slides[activeItem],carouselItem +'-'+ slideDirection); bootstrapCustomEvent.call(element, slidEvent, component, slides[next]); if ( !DOC.hidden && self[interval] && !hasClass(element,paused) ) { self.cycle(); } },timeout+100); }); } else { addClass(slides[next],active); slides[next][offsetWidth]; removeClass(slides[activeItem],active); setTimeout(function() { isSliding = false; if ( self[interval] && !hasClass(element,paused) ) { self.cycle(); } bootstrapCustomEvent.call(element, slidEvent, component, slides[next]); }, 100 ); } }; this.getActiveIndex = function () { return slides[indexOf](getElementsByClassName(element,carouselItem+' active')[0]) || 0; }; // init if ( !(stringCarousel in element ) ) { // prevent adding event handlers twice if ( self[pause] && self[interval] ) { on( element, mouseHover[0], pauseHandler ); on( element, mouseHover[1], resumeHandler ); on( element, 'touchstart', pauseHandler ); on( element, 'touchend', resumeHandler ); } rightArrow && on( rightArrow, clickEvent, controlsHandler ); leftArrow && on( leftArrow, clickEvent, controlsHandler ); indicator && on( indicator, clickEvent, indicatorHandler ); self[keyboard] === true && on( globalObject, keydownEvent, keyHandler ); } if (self.getActiveIndex()<0) { slides[length] && addClass(slides[0],active); indicators[length] && setActivePage(0); } if ( self[interval] ){ self.cycle(); } element[stringCarousel] = self; }; // CAROUSEL DATA API // ================= supports[push]( [ stringCarousel, Carousel, '['+dataRide+'="carousel"]' ] ); /* Native Javascript for Bootstrap 4 | Collapse -----------------------------------------------*/ // COLLAPSE DEFINITION // =================== var Collapse = function( element, options ) { // initialization element element = queryElement(element); // set options options = options || {}; // event targets and constants var accordion = null, collapse = null, self = this, isAnimating = false, // when true it will prevent click handlers accordionData = element[getAttribute]('data-parent'), // component strings component = 'collapse', collapsed = 'collapsed', // private methods openAction = function(collapseElement,toggle) { bootstrapCustomEvent.call(collapseElement, showEvent, component); isAnimating = true; addClass(collapseElement,collapsing); removeClass(collapseElement,component); collapseElement[style][height] = collapseElement[scrollHeight] + 'px'; emulateTransitionEnd(collapseElement, function() { isAnimating = false; collapseElement[setAttribute](ariaExpanded,'true'); toggle[setAttribute](ariaExpanded,'true'); removeClass(collapseElement,collapsing); addClass(collapseElement, component); addClass(collapseElement,showClass); collapseElement[style][height] = ''; bootstrapCustomEvent.call(collapseElement, shownEvent, component); }); }, closeAction = function(collapseElement,toggle) { bootstrapCustomEvent.call(collapseElement, hideEvent, component); isAnimating = true; collapseElement[style][height] = collapseElement[scrollHeight] + 'px'; // set height first removeClass(collapseElement,component); removeClass(collapseElement,showClass); addClass(collapseElement,collapsing); collapseElement[offsetWidth]; // force reflow to enable transition collapseElement[style][height] = '0px'; emulateTransitionEnd(collapseElement, function() { isAnimating = false; collapseElement[setAttribute](ariaExpanded,'false'); toggle[setAttribute](ariaExpanded,'false'); removeClass(collapseElement,collapsing); addClass(collapseElement,component); collapseElement[style][height] = ''; bootstrapCustomEvent.call(collapseElement, hiddenEvent, component); }); }, getTarget = function() { var href = element.href && element[getAttribute]('href'), parent = element[getAttribute](dataTarget), id = href || ( parent && parent.charAt(0) === '#' ) && parent; return id && queryElement(id); }; // public methods this.toggle = function(e) { e[preventDefault](); if (isAnimating) return; if (!hasClass(collapse,showClass)) { self.show(); } else { self.hide(); } }; this.hide = function() { closeAction(collapse,element); addClass(element,collapsed); }; this.show = function() { if ( accordion ) { var activeCollapse = queryElement('.'+component+'.'+showClass,accordion), toggle = activeCollapse && (queryElement('['+dataToggle+'="'+component+'"]['+dataTarget+'="#'+activeCollapse.id+'"]',accordion) || queryElement('['+dataToggle+'="'+component+'"][href="#'+activeCollapse.id+'"]',accordion) ), correspondingCollapse = toggle && (toggle[getAttribute](dataTarget) || toggle.href); if ( activeCollapse && toggle && activeCollapse !== collapse ) { closeAction(activeCollapse,toggle); if ( correspondingCollapse.split('#')[1] !== collapse.id ) { addClass(toggle,collapsed); } else { removeClass(toggle,collapsed); } } } openAction(collapse,element); removeClass(element,collapsed); }; // init if ( !(stringCollapse in element ) ) { // prevent adding event handlers twice on(element, clickEvent, self.toggle); } collapse = getTarget(); accordion = queryElement(options.parent) || accordionData && getClosest(element, accordionData); element[stringCollapse] = self; }; // COLLAPSE DATA API // ================= supports[push]( [ stringCollapse, Collapse, '['+dataToggle+'="collapse"]' ] ); /* Native Javascript for Bootstrap 4 | Dropdown ----------------------------------------------*/ // DROPDOWN DEFINITION // =================== var Dropdown = function( element, option ) { // initialization element element = queryElement(element); // set option this.persist = option === true || element[getAttribute]('data-persist') === 'true' || false; // constants, event targets, strings var self = this, children = 'children', parent = element[parentNode], component = 'dropdown', open = 'open', relatedTarget = null, menu = queryElement('.dropdown-menu', parent), menuItems = (function(){ var set = menu[children], newSet = []; for ( var i=0; i1?idx-1:0) : key === 40 ? (idx HTML[clientHeight]; scrollbarWidth = measureScrollbar(); }, adjustDialog = function () { modal[style][paddingLeft] = !bodyIsOverflowing && modalIsOverflowing ? scrollbarWidth + 'px' : ''; modal[style][paddingRight] = bodyIsOverflowing && !modalIsOverflowing ? scrollbarWidth + 'px' : ''; }, resetAdjustments = function () { modal[style][paddingLeft] = ''; modal[style][paddingRight] = ''; }, createOverlay = function() { modalOverlay = 1; var newOverlay = DOC[createElement]('div'); overlay = queryElement('.'+modalBackdropString); if ( overlay === null ) { newOverlay[setAttribute]('class',modalBackdropString+' fade'); overlay = newOverlay; DOC[body][appendChild](overlay); } }, removeOverlay = function() { overlay = queryElement('.'+modalBackdropString); if ( overlay && overlay !== null && typeof overlay === 'object' ) { modalOverlay = 0; DOC[body].removeChild(overlay); overlay = null; } bootstrapCustomEvent.call(modal, hiddenEvent, component); }, keydownHandlerToggle = function() { if (hasClass(modal,showClass)) { on(DOC, keydownEvent, keyHandler); } else { off(DOC, keydownEvent, keyHandler); } }, resizeHandlerToggle = function() { if (hasClass(modal,showClass)) { on(globalObject, resizeEvent, self.update); } else { off(globalObject, resizeEvent, self.update); } }, dismissHandlerToggle = function() { if (hasClass(modal,showClass)) { on(modal, clickEvent, dismissHandler); } else { off(modal, clickEvent, dismissHandler); } }, // triggers triggerShow = function() { setFocus(modal); bootstrapCustomEvent.call(modal, shownEvent, component, relatedTarget); }, triggerHide = function() { modal[style].display = ''; element && (setFocus(element)); (function(){ if (!getElementsByClassName(DOC,component+' '+showClass)[0]) { resetAdjustments(); resetScrollbar(); removeClass(DOC[body],component+'-open'); overlay && hasClass(overlay,'fade') ? (removeClass(overlay,showClass), emulateTransitionEnd(overlay,removeOverlay)) : removeOverlay(); resizeHandlerToggle(); dismissHandlerToggle(); keydownHandlerToggle(); } }()); }, // handlers clickHandler = function(e) { var clickTarget = e[target]; clickTarget = clickTarget[hasAttribute](dataTarget) || clickTarget[hasAttribute]('href') ? clickTarget : clickTarget[parentNode]; if ( clickTarget === element && !hasClass(modal,showClass) ) { modal.modalTrigger = element; relatedTarget = element; self.show(); e[preventDefault](); } }, keyHandler = function(e) { if (self[keyboard] && e.which == 27 && hasClass(modal,showClass)) { self.hide(); } }, dismissHandler = function(e) { var clickTarget = e[target]; if ( hasClass(modal,showClass) && (clickTarget[parentNode][getAttribute](dataDismiss) === component || clickTarget[getAttribute](dataDismiss) === component || (clickTarget === modal && self[backdrop] !== staticString) ) ) { self.hide(); relatedTarget = null; e[preventDefault](); } }; // public methods this.toggle = function() { if ( hasClass(modal,showClass) ) {this.hide();} else {this.show();} }; this.show = function() { bootstrapCustomEvent.call(modal, showEvent, component, relatedTarget); // we elegantly hide any opened modal var currentOpen = getElementsByClassName(DOC,component+' '+showClass)[0]; currentOpen && currentOpen !== modal && currentOpen.modalTrigger[stringModal].hide(); if ( this[backdrop] ) { !modalOverlay && createOverlay(); } if ( overlay && modalOverlay && !hasClass(overlay,showClass)) { overlay[offsetWidth]; // force reflow to enable trasition addClass(overlay, showClass); } setTimeout( function() { modal[style].display = 'block'; checkScrollbar(); setScrollbar(); adjustDialog(); addClass(DOC[body],component+'-open'); addClass(modal,showClass); modal[setAttribute](ariaHidden, false); resizeHandlerToggle(); dismissHandlerToggle(); keydownHandlerToggle(); hasClass(modal,'fade') ? emulateTransitionEnd(modal, triggerShow) : triggerShow(); }, supportTransitions ? 150 : 0); }; this.hide = function() { bootstrapCustomEvent.call(modal, hideEvent, component); overlay = queryElement('.'+modalBackdropString); removeClass(modal,showClass); modal[setAttribute](ariaHidden, true); (function(){ hasClass(modal,'fade') ? emulateTransitionEnd(modal, triggerHide) : triggerHide(); }()); }; this.setContent = function( content ) { queryElement('.'+component+'-content',modal)[innerHTML] = content; }; this.update = function() { if (hasClass(modal,showClass)) { checkScrollbar(); setScrollbar(); adjustDialog(); } }; // init // prevent adding event handlers over and over // modal is independent of a triggering element if ( !!element && !(stringModal in element) ) { on(element, clickEvent, clickHandler); } if ( !!self[content] ) { self.setContent( self[content] ); } !!element && (element[stringModal] = self); }; // DATA API supports[push]( [ stringModal, Modal, '['+dataToggle+'="modal"]' ] ); /* Native Javascript for Bootstrap 4 | Popover ----------------------------------------------*/ // POPOVER DEFINITION // ================== var Popover = function( element, options ) { // initialization element element = queryElement(element); // set options options = options || {}; // DATA API var triggerData = element[getAttribute](dataTrigger), // click / hover / focus animationData = element[getAttribute](dataAnimation), // true / false placementData = element[getAttribute](dataPlacement), dismissibleData = element[getAttribute](dataDismissible), delayData = element[getAttribute](dataDelay), containerData = element[getAttribute](dataContainer), // internal strings component = 'popover', template = 'template', trigger = 'trigger', classString = 'class', div = 'div', fade = 'fade', content = 'content', dataContent = 'data-content', dismissible = 'dismissible', closeBtn = '', // check container containerElement = queryElement(options[container]), containerDataElement = queryElement(containerData), // maybe the element is inside a modal modal = getClosest(element,'.modal'), // maybe the element is inside a fixed navbar navbarFixedTop = getClosest(element,'.'+fixedTop), navbarFixedBottom = getClosest(element,'.'+fixedBottom); // set instance options this[template] = options[template] ? options[template] : null; // JavaScript only this[trigger] = options[trigger] ? options[trigger] : triggerData || hoverEvent; this[animation] = options[animation] && options[animation] !== fade ? options[animation] : animationData || fade; this[placement] = options[placement] ? options[placement] : placementData || top; this[delay] = parseInt(options[delay] || delayData) || 200; this[dismissible] = options[dismissible] || dismissibleData === 'true' ? true : false; this[container] = containerElement ? containerElement : containerDataElement ? containerDataElement : navbarFixedTop ? navbarFixedTop : navbarFixedBottom ? navbarFixedBottom : modal ? modal : DOC[body]; // bind, content var self = this, titleString = element[getAttribute](dataTitle) || null, contentString = element[getAttribute](dataContent) || null; if ( !contentString && !this[template] ) return; // invalidate // constants, vars var popover = null, timer = 0, placementSetting = this[placement], // handlers dismissibleHandler = function(e) { if (popover !== null && e[target] === queryElement('.close',popover)) { self.hide(); } }, // private methods removePopover = function() { self[container].removeChild(popover); timer = null; popover = null; }, createPopover = function() { titleString = element[getAttribute](dataTitle); // check content again contentString = element[getAttribute](dataContent); popover = DOC[createElement](div); // popover arrow var popoverArrow = DOC[createElement](div); popoverArrow[setAttribute](classString,'arrow'); popover[appendChild](popoverArrow); if ( contentString !== null && self[template] === null ) { //create the popover from data attributes popover[setAttribute]('role','tooltip'); if (titleString !== null) { var popoverTitle = DOC[createElement]('h3'); popoverTitle[setAttribute](classString,component+'-header'); popoverTitle[innerHTML] = self[dismissible] ? titleString + closeBtn : titleString; popover[appendChild](popoverTitle); } //set popover content var popoverContent = DOC[createElement](div); popoverContent[setAttribute](classString,component+'-body'); popoverContent[innerHTML] = self[dismissible] && titleString === null ? contentString + closeBtn : contentString; popover[appendChild](popoverContent); } else { // or create the popover from template var popoverTemplate = DOC[createElement](div); popoverTemplate[innerHTML] = self[template]; popover[innerHTML] = popoverTemplate.firstChild[innerHTML]; } //append to the container self[container][appendChild](popover); popover[style].display = 'block'; popover[setAttribute](classString, component+ ' bs-' + component+'-'+placementSetting + ' ' + self[animation]); }, showPopover = function () { !hasClass(popover,showClass) && ( addClass(popover,showClass) ); }, updatePopover = function() { styleTip(element,popover,placementSetting,self[container]); }, // event toggle dismissHandlerToggle = function(type){ if (clickEvent == self[trigger] || 'focus' == self[trigger]) { !self[dismissible] && type( element, 'blur', self.hide ); } self[dismissible] && type( DOC, clickEvent, dismissibleHandler ); type( globalObject, resizeEvent, self.hide ); }, // triggers showTrigger = function() { dismissHandlerToggle(on); bootstrapCustomEvent.call(element, shownEvent, component); }, hideTrigger = function() { dismissHandlerToggle(off); removePopover(); bootstrapCustomEvent.call(element, hiddenEvent, component); }; // public methods / handlers this.toggle = function() { if (popover === null) { self.show(); } else { self.hide(); } }; this.show = function() { clearTimeout(timer); timer = setTimeout( function() { if (popover === null) { placementSetting = self[placement]; // we reset placement in all cases createPopover(); updatePopover(); showPopover(); bootstrapCustomEvent.call(element, showEvent, component); !!self[animation] ? emulateTransitionEnd(popover, showTrigger) : showTrigger(); } }, 20 ); }; this.hide = function() { clearTimeout(timer); timer = setTimeout( function() { if (popover && popover !== null && hasClass(popover,showClass)) { bootstrapCustomEvent.call(element, hideEvent, component); removeClass(popover,showClass); !!self[animation] ? emulateTransitionEnd(popover, hideTrigger) : hideTrigger(); } }, self[delay] ); }; // init if ( !(stringPopover in element) ) { // prevent adding event handlers twice if (self[trigger] === hoverEvent) { on( element, mouseHover[0], self.show ); if (!self[dismissible]) { on( element, mouseHover[1], self.hide ); } } else if (clickEvent == self[trigger] || 'focus' == self[trigger]) { on( element, self[trigger], self.toggle ); } } element[stringPopover] = self; }; // POPOVER DATA API // ================ supports[push]( [ stringPopover, Popover, '['+dataToggle+'="popover"]' ] ); /* Native Javascript for Bootstrap 4 | ScrollSpy -----------------------------------------------*/ // SCROLLSPY DEFINITION // ==================== var ScrollSpy = function(element, options) { // initialization element, the element we spy on element = queryElement(element); // DATA API var targetData = queryElement(element[getAttribute](dataTarget)), offsetData = element[getAttribute]('data-offset'); // set options options = options || {}; if ( !options[target] && !targetData ) { return; } // invalidate // event targets, constants var self = this, spyTarget = options[target] && queryElement(options[target]) || targetData, links = spyTarget && spyTarget[getElementsByTagName]('A'), offset = parseInt(offsetData || options['offset']) || 10, items = [], targetItems = [], scrollOffset, scrollTarget = element[offsetHeight] < element[scrollHeight] ? element : globalObject, // determine which is the real scrollTarget isWindow = scrollTarget === globalObject; // populate items and targets for (var i=0, il=links[length]; i= topEdge && bottomEdge > scrollOffset; if ( !isActive && inside ) { if ( !hasClass(item,active) ) { addClass(item,active); if (dropdownLink && !hasClass(dropdownLink,active) ) { addClass(dropdownLink,active); } bootstrapCustomEvent.call(element, 'activate', 'scrollspy', items[index]); } } else if ( !inside ) { if ( hasClass(item,active) ) { removeClass(item,active); if (dropdownLink && hasClass(dropdownLink,active) && !getElementsByClassName(item[parentNode],active).length ) { removeClass(dropdownLink,active); } } } else if ( !inside && !isActive || isActive && inside ) { return; } }, updateItems = function(){ scrollOffset = isWindow ? getScroll().y : element[scrollTop]; for (var index=0, itl=items[length]; index 1 ) { activeTab = activeTabs[activeTabs[length]-1]; } return activeTab; }, getActiveContent = function() { return queryElement(getActiveTab()[getAttribute]('href')); }, // handler clickHandler = function(e) { var href = e[target][getAttribute]('href'); e[preventDefault](); next = e[target][getAttribute](dataToggle) === component || (href && href.charAt(0) === '#') ? e[target] : e[target][parentNode]; // allow for child elements like icons to use the handler !tabs[isAnimating] && !hasClass(next,active) && self.show(); }; // public method this.show = function() { // the tab we clicked is now the next tab next = next || element; nextContent = queryElement(next[getAttribute]('href')); //this is the actual object, the next tab content to activate activeTab = getActiveTab(); activeContent = getActiveContent(); tabs[isAnimating] = true; removeClass(activeTab,active); addClass(next,active); if ( dropdown ) { if ( !hasClass(element[parentNode],'dropdown-menu') ) { if (hasClass(dropdown,active)) removeClass(dropdown,active); } else { if (!hasClass(dropdown,active)) addClass(dropdown,active); } } bootstrapCustomEvent.call(activeTab, hideEvent, component, next); if (hasClass(activeContent, 'fade')) { removeClass(activeContent,showClass); emulateTransitionEnd(activeContent, triggerHide); } else { triggerHide(); } }; // init if ( !(stringTab in element) ) { // prevent adding event handlers twice on(element, clickEvent, clickHandler); } if (self[height]) { tabsContentContainer = getActiveContent()[parentNode]; } element[stringTab] = self; }; // TAB DATA API // ============ supports[push]( [ stringTab, Tab, '['+dataToggle+'="tab"]' ] ); /* Native Javascript for Bootstrap 4 | Tooltip ---------------------------------------------*/ // TOOLTIP DEFINITION // ================== var Tooltip = function( element,options ) { // initialization element element = queryElement(element); // set options options = options || {}; // DATA API var animationData = element[getAttribute](dataAnimation), placementData = element[getAttribute](dataPlacement), delayData = element[getAttribute](dataDelay), containerData = element[getAttribute](dataContainer), // strings component = 'tooltip', classString = 'class', title = 'title', fade = 'fade', div = 'div', // check container containerElement = queryElement(options[container]), containerDataElement = queryElement(containerData), // maybe the element is inside a modal modal = getClosest(element,'.modal'), // maybe the element is inside a fixed navbar navbarFixedTop = getClosest(element,'.'+fixedTop), navbarFixedBottom = getClosest(element,'.'+fixedBottom); // set instance options this[animation] = options[animation] && options[animation] !== fade ? options[animation] : animationData || fade; this[placement] = options[placement] ? options[placement] : placementData || top; this[delay] = parseInt(options[delay] || delayData) || 200; this[container] = containerElement ? containerElement : containerDataElement ? containerDataElement : navbarFixedTop ? navbarFixedTop : navbarFixedBottom ? navbarFixedBottom : modal ? modal : DOC[body]; // bind, event targets, title and constants var self = this, timer = 0, placementSetting = this[placement], tooltip = null, titleString = element[getAttribute](title) || element[getAttribute](dataTitle) || element[getAttribute](dataOriginalTitle); if ( !titleString || titleString == "" ) return; // invalidate // private methods var removeToolTip = function() { self[container].removeChild(tooltip); tooltip = null; timer = null; }, createToolTip = function() { titleString = element[getAttribute](title) || element[getAttribute](dataTitle) || element[getAttribute](dataOriginalTitle); // read the title again if ( !titleString || titleString == "" ) return false; // invalidate tooltip = DOC[createElement](div); tooltip[setAttribute]('role',component); // tooltip arrow var tooltipArrow = DOC[createElement](div); tooltipArrow[setAttribute](classString,'arrow'); tooltip[appendChild](tooltipArrow); var tooltipInner = DOC[createElement](div); tooltipInner[setAttribute](classString,component+'-inner'); tooltip[appendChild](tooltipInner); tooltipInner[innerHTML] = titleString; self[container][appendChild](tooltip); tooltip[setAttribute](classString, component + ' bs-' + component+'-'+placementSetting + ' ' + self[animation]); }, updateTooltip = function () { styleTip(element,tooltip,placementSetting,self[container]); }, showTooltip = function () { !hasClass(tooltip,showClass) && ( addClass(tooltip,showClass) ); }, // triggers showTrigger = function() { on( globalObject, resizeEvent, self.hide ); bootstrapCustomEvent.call(element, shownEvent, component); }, hideTrigger = function() { off( globalObject, resizeEvent, self.hide ); removeToolTip(); bootstrapCustomEvent.call(element, hiddenEvent, component); }; // public methods this.show = function() { clearTimeout(timer); timer = setTimeout( function() { if (tooltip === null) { placementSetting = self[placement]; // we reset placement in all cases if(createToolTip() == false) return; updateTooltip(); showTooltip(); bootstrapCustomEvent.call(element, showEvent, component); !!self[animation] ? emulateTransitionEnd(tooltip, showTrigger) : showTrigger(); } }, 20 ); }; this.hide = function() { clearTimeout(timer); timer = setTimeout( function() { if (tooltip && hasClass(tooltip,showClass)) { bootstrapCustomEvent.call(element, hideEvent, component); removeClass(tooltip,showClass); !!self[animation] ? emulateTransitionEnd(tooltip, hideTrigger) : hideTrigger(); } }, self[delay]); }; this.toggle = function() { if (!tooltip) { self.show(); } else { self.hide(); } }; // init if ( !(stringTooltip in element) ) { // prevent adding event handlers twice element[setAttribute](dataOriginalTitle,titleString); element.removeAttribute(title); on(element, mouseHover[0], self.show); on(element, mouseHover[1], self.hide); } element[stringTooltip] = self; }; // TOOLTIP DATA API // ================= supports[push]( [ stringTooltip, Tooltip, '['+dataToggle+'="tooltip"]' ] ); /* Native Javascript for Bootstrap 4 | Initialize Data API --------------------------------------------------------*/ var initializeDataAPI = function( constructor, collection ){ for (var i=0, l=collection[length]; i:-)':'1f606', '\':-(':'1f613', '>:-(':'1f620', ':\'-(':'1f622', 'O:-)':'1f607', '0:-3':'1f607', '0:-)':'1f607', '0;^)':'1f607', 'O;-)':'1f607', '0;-)':'1f607', 'O:-3':'1f607', '-__-':'1f611', ':-Þ':'1f61b', ':)':'1f606', '>;)':'1f606', '>=)':'1f606', ';-)':'1f609', '*-)':'1f609', ';-]':'1f609', ';^)':'1f609', '\':(':'1f613', '\'=(':'1f613', ':-*':'1f618', ':^*':'1f618', '>:P':'1f61c', 'X-P':'1f61c', '>:[':'1f61e', ':-(':'1f61e', ':-[':'1f61e', '>:(':'1f620', ':\'(':'1f622', ';-(':'1f622', '>.<':'1f623', '#-)':'1f635', '%-)':'1f635', 'X-)':'1f635', '\\0/':'1f646', '\\O/':'1f646', '0:3':'1f607', '0:)':'1f607', 'O:)':'1f607', 'O=)':'1f607', 'O:3':'1f607', 'B-)':'1f60e', '8-)':'1f60e', 'B-D':'1f60e', '8-D':'1f60e', '-_-':'1f611', '>:\\':'1f615', '>:/':'1f615', ':-/':'1f615', ':-.':'1f615', ':-P':'1f61b', ':Þ':'1f61b', ':-b':'1f61b', ':-O':'1f62e', 'O_O':'1f62e', '>:O':'1f62e', ':-X':'1f636', ':-#':'1f636', ':-)':'1f642', '(y)':'1f44d', '<3':'2764', ':D':'1f603', '=D':'1f603', ';)':'1f609', '*)':'1f609', ';]':'1f609', ';D':'1f609', ':*':'1f618', '=*':'1f618', ':(':'1f61e', ':[':'1f61e', '=(':'1f61e', ':@':'1f620', ';(':'1f622', 'D:':'1f628', ':$':'1f633', '=$':'1f633', '#)':'1f635', '%)':'1f635', 'X)':'1f635', 'B)':'1f60e', '8)':'1f60e', ':/':'1f615', ':\\':'1f615', '=/':'1f615', '=\\':'1f615', ':L':'1f615', '=L':'1f615', ':P':'1f61b', '=P':'1f61b', ':b':'1f61b', ':O':'1f62e', ':X':'1f636', ':#':'1f636', '=X':'1f636', '=#':'1f636', ':)':'1f642', '=]':'1f642', '=)':'1f642', ':]':'1f642' }; ns.asciiRegexp = '(\\*\\\\0\\/\\*|\\*\\\\O\\/\\*|\\-___\\-|\\:\'\\-\\)|\'\\:\\-\\)|\'\\:\\-D|\\>\\:\\-\\)|>\\:\\-\\)|\'\\:\\-\\(|\\>\\:\\-\\(|>\\:\\-\\(|\\:\'\\-\\(|O\\:\\-\\)|0\\:\\-3|0\\:\\-\\)|0;\\^\\)|O;\\-\\)|0;\\-\\)|O\\:\\-3|\\-__\\-|\\:\\-Þ|\\:\\-Þ|\\<\\/3|<\\/3|\\:\'\\)|\\:\\-D|\'\\:\\)|\'\\=\\)|\'\\:D|\'\\=D|\\>\\:\\)|>\\:\\)|\\>;\\)|>;\\)|\\>\\=\\)|>\\=\\)|;\\-\\)|\\*\\-\\)|;\\-\\]|;\\^\\)|\'\\:\\(|\'\\=\\(|\\:\\-\\*|\\:\\^\\*|\\>\\:P|>\\:P|X\\-P|\\>\\:\\[|>\\:\\[|\\:\\-\\(|\\:\\-\\[|\\>\\:\\(|>\\:\\(|\\:\'\\(|;\\-\\(|\\>\\.\\<|>\\.<|#\\-\\)|%\\-\\)|X\\-\\)|\\\\0\\/|\\\\O\\/|0\\:3|0\\:\\)|O\\:\\)|O\\=\\)|O\\:3|B\\-\\)|8\\-\\)|B\\-D|8\\-D|\\-_\\-|\\>\\:\\\\|>\\:\\\\|\\>\\:\\/|>\\:\\/|\\:\\-\\/|\\:\\-\\.|\\:\\-P|\\:Þ|\\:Þ|\\:\\-b|\\:\\-O|O_O|\\>\\:O|>\\:O|\\:\\-X|\\:\\-#|\\:\\-\\)|\\(y\\)|\\<3|<3|\\:D|\\=D|;\\)|\\*\\)|;\\]|;D|\\:\\*|\\=\\*|\\:\\(|\\:\\[|\\=\\(|\\:@|;\\(|D\\:|\\:\\$|\\=\\$|#\\)|%\\)|X\\)|B\\)|8\\)|\\:\\/|\\:\\\\|\\=\\/|\\=\\\\|\\:L|\\=L|\\:P|\\=P|\\:b|\\:O|\\:X|\\:#|\\=X|\\=#|\\:\\)|\\=\\]|\\=\\)|\\:\\])'; ns.emojiVersion = '3.1'; // you can [optionally] modify this to load alternate emoji versions. see readme for backwards compatibility and version options ns.emojiSize = '32'; ns.greedyMatch = false; // set to true for greedy unicode matching ns.imagePathPNG = 'https://cdn.jsdelivr.net/emojione/assets/' + ns.emojiVersion + '/png/'; ns.defaultPathPNG = ns.imagePathPNG; ns.imageTitleTag = true; // set to false to remove title attribute from img tag ns.sprites = false; // if this is true then sprite markup will be used ns.spriteSize = '32'; ns.unicodeAlt = true; // use the unicode char as the alt attribute (makes copy and pasting the resulting text better) ns.ascii = false; // change to true to convert ascii smileys ns.riskyMatchAscii = false; // set true to match ascii without leading/trailing space char ns.regShortNames = new RegExp("]*>.*?<\/object>|]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|("+ns.shortnames+")", "gi"); ns.regAscii = new RegExp("]*>.*?<\/object>|]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|((\\s|^)"+ns.asciiRegexp+"(?=\\s|$|[!,.?]))", "gi"); ns.regAsciiRisky = new RegExp("]*>.*?<\/object>|]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|(()"+ns.asciiRegexp+"())", "gi"); ns.regUnicode = new RegExp("]*>.*?<\/object>|]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|(?:\uD83C\uDFF3)\uFE0F?\u200D?(?:\uD83C\uDF08)|(?:\uD83D\uDC41)\uFE0F?\u200D?(?:\uD83D\uDDE8)\uFE0F?|[#-9]\uFE0F?\u20E3|(?:(?:\uD83C\uDFF4)(?:\uDB40[\uDC60-\uDCFF]){1,6})|(?:\uD83C[\uDDE0-\uDDFF]){2}|(?:(?:\uD83D[\uDC68\uDC69]))\uFE0F?(?:\uD83C[\uDFFA-\uDFFF])?\u200D?(?:[\u2695\u2696\u2708]|\uD83C[\uDF3E-\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92])|(?:\uD83D[\uDC68\uDC69]|\uD83E[\uDDD0-\uDDDF])(?:\uD83C[\uDFFA-\uDFFF])?\u200D?[\u2640\u2642\u2695\u2696\u2708]?\uFE0F?|(?:(?:\u2764|\uD83D[\uDC66-\uDC69\uDC8B])[\u200D\uFE0F]{0,2}){1,3}(?:\u2764|\uD83D[\uDC66-\uDC69\uDC8B])|(?:(?:\u2764|\uD83D[\uDC66-\uDC69\uDC8B])\uFE0F?){2,4}|(?:\uD83D[\uDC68\uDC69\uDC6E\uDC71-\uDC87\uDD75\uDE45-\uDE4E]|\uD83E[\uDD26\uDD37]|\uD83C[\uDFC3-\uDFCC]|\uD83E[\uDD38-\uDD3E]|\uD83D[\uDEA3-\uDEB6]|\u26f9|\uD83D\uDC6F)\uFE0F?(?:\uD83C[\uDFFB-\uDFFF])?\u200D?[\u2640\u2642]?\uFE0F?|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85-\uDFCC]|\uD83D[\uDC42-\uDCAA\uDD74-\uDD96\uDE45-\uDE4F\uDEA3-\uDECC]|\uD83E[\uDD18-\uDD3E])\uFE0F?(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u2194-\u2199\u21a9-\u21aa]\uFE0F?|[\u0023\u002a]|[\u3030\u303d]\uFE0F?|(?:\ud83c[\udd70-\udd71]|\ud83c\udd8e|\ud83c[\udd91-\udd9a])\uFE0F?|\u24c2\uFE0F?|[\u3297\u3299]\uFE0F?|(?:\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|\ud83c[\ude32-\ude3a]|\ud83c[\ude50-\ude51])\uFE0F?|[\u203c\u2049]\uFE0F?|[\u25aa-\u25ab\u25b6\u25c0\u25fb-\u25fe]\uFE0F?|[\u00a9\u00ae]\uFE0F?|[\u2122\u2139]\uFE0F?|\ud83c\udc04\uFE0F?|[\u2b05-\u2b07\u2b1b-\u2b1c\u2b50\u2b55]\uFE0F?|[\u231a-\u231b\u2328\u23cf\u23e9-\u23f3\u23f8-\u23fa]\uFE0F?|\ud83c\udccf|[\u2934\u2935]\uFE0F?)|[\u2700-\u27bf]\uFE0F?|[\ud800-\udbff][\udc00-\udfff]\uFE0F?|[\u2600-\u26FF]\uFE0F?|[\u0030-\u0039]\uFE0F", "g"); ns.toImage = function(str) { str = ns.unicodeToImage(str); str = ns.shortnameToImage(str); return str; }; // Uses toShort to transform all unicode into a standard shortname // then transforms the shortname into unicode // This is done for standardization when converting several unicode types ns.unifyUnicode = function(str) { str = ns.toShort(str); str = ns.shortnameToUnicode(str); return str; }; // Replace shortnames (:wink:) with Ascii equivalents ( ;^) ) // Useful for systems that dont support unicode nor images ns.shortnameToAscii = function(str) { var unicode, // something to keep in mind here is that array flip will destroy // half of the ascii text "emojis" because the unicode numbers are duplicated // this is ok for what it's being used for unicodeToAscii = ns.objectFlip(ns.asciiList); str = str.replace(ns.regShortNames, function(shortname) { if( (typeof shortname === 'undefined') || (shortname === '') || (!(shortname in ns.emojioneList)) ) { // if the shortname doesnt exist just return the entire match return shortname; } else { unicode = ns.emojioneList[shortname].uc_output; if(typeof unicodeToAscii[unicode] !== 'undefined') { return unicodeToAscii[unicode]; } else { return shortname; } } }); return str; }; // will output unicode from shortname // useful for sending emojis back to mobile devices ns.shortnameToUnicode = function(str) { // replace regular shortnames first var unicode,fname; str = str.replace(ns.regShortNames, function(shortname) { if( (typeof shortname === 'undefined') || (shortname === '') || (!(shortname in ns.emojioneList)) ) { // if the shortname doesnt exist just return the entire matchhju return shortname; } unicode = ns.emojioneList[shortname].uc_output.toUpperCase(); fname = ns.emojioneList[shortname].uc_base; return ns.convert(unicode); }); // if ascii smileys are turned on, then we'll replace them! if (ns.ascii) { var asciiRX = ns.riskyMatchAscii ? ns.regAsciiRisky : ns.regAscii; str = str.replace(asciiRX, function(entire, m1, m2, m3) { if( (typeof m3 === 'undefined') || (m3 === '') || (!(ns.unescapeHTML(m3) in ns.asciiList)) ) { // if the ascii doesnt exist just return the entire match return entire; } m3 = ns.unescapeHTML(m3); unicode = ns.asciiList[m3].toUpperCase(); return m2+ns.convert(unicode); }); } return str; }; ns.shortnameToImage = function(str) { // replace regular shortnames first var replaceWith,shortname,unicode,fname,alt,category,title,size,ePath; var mappedUnicode = ns.mapUnicodeToShort(); str = str.replace(ns.regShortNames, function(shortname) { if( (typeof shortname === 'undefined') || (shortname === '') || (ns.shortnames.indexOf(shortname) === -1) ) { // if the shortname doesnt exist just return the entire match return shortname; } else { // map shortname to parent if (!ns.emojioneList[shortname]) { for ( var emoji in ns.emojioneList ) { if (!ns.emojioneList.hasOwnProperty(emoji) || (emoji === '')) continue; if (ns.emojioneList[emoji].shortnames.indexOf(shortname) === -1) continue; shortname = emoji; break; } } unicode = ns.emojioneList[shortname].uc_output; fname = ns.emojioneList[shortname].uc_base; category = (fname.includes("-1f3f")) ? 'diversity' : ns.emojioneList[shortname].category; title = ns.imageTitleTag ? 'title="' + shortname + '"' : ''; size = (ns.spriteSize == '32' || ns.spriteSize == '64') ? ns.spriteSize : '32'; //if the image path has not changed, we'll assume the default cdn path, otherwise we'll assume the provided path ePath = (ns.imagePathPNG != ns.defaultPathPNG) ? ns.imagePathPNG : ns.defaultPathPNG + ns.emojiSize + '/'; // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname alt = (ns.unicodeAlt) ? ns.convert(unicode.toUpperCase()) : shortname; if(ns.sprites) { replaceWith = '' + alt + ''; } else { replaceWith = '' + alt + ''; } return replaceWith; } }); // if ascii smileys are turned on, then we'll replace them! if (ns.ascii) { var asciiRX = ns.riskyMatchAscii ? ns.regAsciiRisky : ns.regAscii; str = str.replace(asciiRX, function(entire, m1, m2, m3) { if( (typeof m3 === 'undefined') || (m3 === '') || (!(ns.unescapeHTML(m3) in ns.asciiList)) ) { // if the ascii doesnt exist just return the entire match return entire; } m3 = ns.unescapeHTML(m3); unicode = ns.asciiList[m3]; shortname = mappedUnicode[unicode]; category = (unicode.includes("-1f3f")) ? 'diversity' : ns.emojioneList[shortname].category; title = ns.imageTitleTag ? 'title="' + ns.escapeHTML(m3) + '"' : ''; size = (ns.spriteSize == '32' || ns.spriteSize == '64') ? ns.spriteSize : '32'; //if the image path has not changed, we'll assume the default cdn path, otherwise we'll assume the provided path ePath = (ns.imagePathPNG != ns.defaultPathPNG) ? ns.imagePathPNG : ns.defaultPathPNG + ns.emojiSize + '/'; // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname alt = (ns.unicodeAlt) ? ns.convert(unicode.toUpperCase()) : ns.escapeHTML(m3); if(ns.sprites) { replaceWith = m2+'' + alt + ''; } else { replaceWith = m2+''+alt+''; } return replaceWith; }); } return str; }; ns.unicodeToImage = function(str) { var replaceWith,unicode,short,fname,alt,category,title,size,ePath; var mappedUnicode = ns.mapUnicodeToShort(); var eList = ns.emojioneList; str = str.replace(ns.regUnicode, function(unicodeChar) { if( (typeof unicodeChar === 'undefined') || (unicodeChar === '') ) { return unicodeChar; } else if ( unicodeChar in ns.jsEscapeMap ) { fname = ns.jsEscapeMap[unicodeChar]; } else if ( ns.greedyMatch && unicodeChar in ns.jsEscapeMapGreedy ) { fname = ns.jsEscapeMapGreedy[unicodeChar]; } else { return unicodeChar; } // then map to shortname and locate the filename short = mappedUnicode[fname]; // then pull the unicode output from emojioneList fname = eList[short].uc_base; unicode = eList[short].uc_output; category = (fname.includes("-1f3f")) ? 'diversity' : eList[short].category; size = (ns.spriteSize == '32' || ns.spriteSize == '64') ? ns.spriteSize : '32'; //if the image path has not changed, we'll assume the default cdn path, otherwise we'll assume the provided path ePath = (ns.imagePathPNG != ns.defaultPathPNG) ? ns.imagePathPNG : ns.defaultPathPNG + ns.emojiSize + '/'; // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname alt = (ns.unicodeAlt) ? ns.convert(unicode.toUpperCase()) : short; title = ns.imageTitleTag ? 'title="' + short + '"' : ''; if(ns.sprites) { replaceWith = '' + alt + ''; } else { replaceWith = '' + alt + ''; } return replaceWith; }); // if ascii smileys are turned on, then we'll replace them! if (ns.ascii) { var asciiRX = ns.riskyMatchAscii ? ns.regAsciiRisky : ns.regAscii; str = str.replace(asciiRX, function(entire, m1, m2, m3) { if( (typeof m3 === 'undefined') || (m3 === '') || (!(ns.unescapeHTML(m3) in ns.asciiList)) ) { // if the ascii doesnt exist just return the entire match return entire; } m3 = ns.unescapeHTML(m3); unicode = ns.asciiList[m3]; shortname = mappedUnicode[unicode]; category = (unicode.includes("-1f3f")) ? 'diversity' : ns.emojioneList[shortname].category; title = ns.imageTitleTag ? 'title="' + ns.escapeHTML(m3) + '"' : ''; size = (ns.spriteSize == '32' || ns.spriteSize == '64') ? ns.spriteSize : '32'; //if the image path has not changed, we'll assume the default cdn path, otherwise we'll assume the provided path ePath = (ns.imagePathPNG != ns.defaultPathPNG) ? ns.imagePathPNG : ns.defaultPathPNG + ns.emojiSize + '/'; // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname alt = (ns.unicodeAlt) ? ns.convert(unicode.toUpperCase()) : ns.escapeHTML(m3); if(ns.sprites) { replaceWith = m2+'' + alt + ''; } else { replaceWith = m2+''+alt+''; } return replaceWith; }); } return str; }; // this is really just unicodeToShortname() but I opted for the shorthand name to match toImage() ns.toShort = function(str) { var find = ns.unicodeCharRegex(); return ns.replaceAll(str, find); }; // for converting unicode code points and code pairs to their respective characters ns.convert = function(unicode) { if(unicode.indexOf("-") > -1) { var parts = []; var s = unicode.split('-'); for(var i = 0; i < s.length; i++) { var part = parseInt(s[i], 16); if (part >= 0x10000 && part <= 0x10FFFF) { var hi = Math.floor((part - 0x10000) / 0x400) + 0xD800; var lo = ((part - 0x10000) % 0x400) + 0xDC00; part = (String.fromCharCode(hi) + String.fromCharCode(lo)); } else { part = String.fromCharCode(part); } parts.push(part); } return parts.join(''); } else { var s = parseInt(unicode, 16); if (s >= 0x10000 && s <= 0x10FFFF) { var hi = Math.floor((s - 0x10000) / 0x400) + 0xD800; var lo = ((s - 0x10000) % 0x400) + 0xDC00; return (String.fromCharCode(hi) + String.fromCharCode(lo)); } else { return String.fromCharCode(s); } } }; ns.escapeHTML = function (string) { var escaped = { '&' : '&', '<' : '<', '>' : '>', '"' : '"', '\'': ''' }; return string.replace(/[&<>"']/g, function (match) { return escaped[match]; }); }; ns.unescapeHTML = function (string) { var unescaped = { '&' : '&', '&' : '&', '&' : '&', '<' : '<', '<' : '<', '<' : '<', '>' : '>', '>' : '>', '>' : '>', '"' : '"', '"' : '"', '"' : '"', ''' : '\'', ''' : '\'', ''' : '\'' }; return string.replace(/&(?:amp|#38|#x26|lt|#60|#x3C|gt|#62|#x3E|apos|#39|#x27|quot|#34|#x22);/ig, function (match) { return unescaped[match]; }); }; ns.shortnameConversionMap = function() { var map = [], emoji; for (emoji in ns.emojioneList) { if (!ns.emojioneList.hasOwnProperty(emoji) || (emoji === '')) continue; map[ns.convert(ns.emojioneList[emoji].uc_output)] = emoji; } return map; }; ns.unicodeCharRegex = function() { var map = []; for (emoji in ns.emojioneList) { if (!ns.emojioneList.hasOwnProperty(emoji) || (emoji === '')) continue; map.push(ns.convert(ns.emojioneList[emoji].uc_output)); } return map.join('|'); }; ns.mapEmojioneList = function (addToMapStorage) { for (var shortname in ns.emojioneList) { if (!ns.emojioneList.hasOwnProperty(shortname)) { continue; } var unicode = ns.emojioneList[shortname].uc_base; addToMapStorage(unicode, shortname); } }; ns.mapUnicodeToShort = function() { if (!ns.memMapShortToUnicode) { ns.memMapShortToUnicode = {}; ns.mapEmojioneList(function (unicode, shortname) { ns.memMapShortToUnicode[unicode] = shortname; }); } return ns.memMapShortToUnicode; }; ns.memorizeReplacement = function() { if (!ns.unicodeReplacementRegEx || !ns.memMapShortToUnicodeCharacters) { var unicodeList = []; ns.memMapShortToUnicodeCharacters = {}; ns.mapEmojioneList(function (unicode, shortname) { var emojiCharacter = ns.convert(unicode); ns.memMapShortToUnicodeCharacters[emojiCharacter] = shortname; unicodeList.push(emojiCharacter); }); ns.unicodeReplacementRegEx = unicodeList.join('|'); } }; ns.mapUnicodeCharactersToShort = function() { ns.memorizeReplacement(); return ns.memMapShortToUnicodeCharacters; }; //reverse an object ns.objectFlip = function (obj) { var key, tmp_obj = {}; for (key in obj) { if (obj.hasOwnProperty(key)) { tmp_obj[obj[key]] = key; } } return tmp_obj; }; ns.escapeRegExp = function(string) { return string.replace(/[-[\]{}()*+?.,;:&\\^$#\s]/g, "\\$&"); }; ns.replaceAll = function(string, find) { var escapedFind = ns.escapeRegExp(find); //sorted largest output to smallest output var search = new RegExp("]*>.*?<\/object>|]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|("+escapedFind+")", "gi"); // callback prevents replacing anything inside of these common html tags as well as between an tag var replace = function(entire, m1) { return ((typeof m1 === 'undefined') || (m1 === '')) ? entire : ns.shortnameConversionMap()[m1]; }; return string.replace(search,replace); }; }(this.emojione = this.emojione || {})); if(true) module.exports = this.emojione; /***/ }), /***/ "./node_modules/es6-promise/dist/es6-promise.auto.js": /*!***********************************************************!*\ !*** ./node_modules/es6-promise/dist/es6-promise.auto.js ***! \***********************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(process, global) {/*! * @overview es6-promise - a tiny implementation of Promises/A+. * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) * @license Licensed under MIT license * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE * @version v4.2.4+314e4831 */ (function (global, factory) { true ? module.exports = factory() : undefined; }(this, (function () { 'use strict'; function objectOrFunction(x) { var type = typeof x; return x !== null && (type === 'object' || type === 'function'); } function isFunction(x) { return typeof x === 'function'; } var _isArray = void 0; if (Array.isArray) { _isArray = Array.isArray; } else { _isArray = function (x) { return Object.prototype.toString.call(x) === '[object Array]'; }; } var isArray = _isArray; var len = 0; var vertxNext = void 0; var customSchedulerFn = void 0; var asap = function asap(callback, arg) { queue[len] = callback; queue[len + 1] = arg; len += 2; if (len === 2) { // If len is 2, that means that we need to schedule an async flush. // If additional callbacks are queued before the queue is flushed, they // will be processed by this flush that we are scheduling. if (customSchedulerFn) { customSchedulerFn(flush); } else { scheduleFlush(); } } }; function setScheduler(scheduleFn) { customSchedulerFn = scheduleFn; } function setAsap(asapFn) { asap = asapFn; } var browserWindow = typeof window !== 'undefined' ? window : undefined; var browserGlobal = browserWindow || {}; var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; // test for web worker but not in IE10 var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; // node function useNextTick() { // node version 0.10.x displays a deprecation warning when nextTick is used recursively // see https://github.com/cujojs/when/issues/410 for details return function () { return process.nextTick(flush); }; } // vertx function useVertxTimer() { if (typeof vertxNext !== 'undefined') { return function () { vertxNext(flush); }; } return useSetTimeout(); } function useMutationObserver() { var iterations = 0; var observer = new BrowserMutationObserver(flush); var node = document.createTextNode(''); observer.observe(node, { characterData: true }); return function () { node.data = iterations = ++iterations % 2; }; } // web worker function useMessageChannel() { var channel = new MessageChannel(); channel.port1.onmessage = flush; return function () { return channel.port2.postMessage(0); }; } function useSetTimeout() { // Store setTimeout reference so es6-promise will be unaffected by // other code modifying setTimeout (like sinon.useFakeTimers()) var globalSetTimeout = setTimeout; return function () { return globalSetTimeout(flush, 1); }; } var queue = new Array(1000); function flush() { for (var i = 0; i < len; i += 2) { var callback = queue[i]; var arg = queue[i + 1]; callback(arg); queue[i] = undefined; queue[i + 1] = undefined; } len = 0; } function attemptVertx() { try { var vertx = Function('return this')().require('vertx'); vertxNext = vertx.runOnLoop || vertx.runOnContext; return useVertxTimer(); } catch (e) { return useSetTimeout(); } } var scheduleFlush = void 0; // Decide what async method to use to triggering processing of queued callbacks: if (isNode) { scheduleFlush = useNextTick(); } else if (BrowserMutationObserver) { scheduleFlush = useMutationObserver(); } else if (isWorker) { scheduleFlush = useMessageChannel(); } else if (browserWindow === undefined && "function" === 'function') { scheduleFlush = attemptVertx(); } else { scheduleFlush = useSetTimeout(); } function then(onFulfillment, onRejection) { var parent = this; var child = new this.constructor(noop); if (child[PROMISE_ID] === undefined) { makePromise(child); } var _state = parent._state; if (_state) { var callback = arguments[_state - 1]; asap(function () { return invokeCallback(_state, child, callback, parent._result); }); } else { subscribe(parent, child, onFulfillment, onRejection); } return child; } /** `Promise.resolve` returns a promise that will become resolved with the passed `value`. It is shorthand for the following: ```javascript let promise = new Promise(function(resolve, reject){ resolve(1); }); promise.then(function(value){ // value === 1 }); ``` Instead of writing the above, your code now simply becomes the following: ```javascript let promise = Promise.resolve(1); promise.then(function(value){ // value === 1 }); ``` @method resolve @static @param {Any} value value that the returned promise will be resolved with Useful for tooling. @return {Promise} a promise that will become fulfilled with the given `value` */ function resolve$1(object) { /*jshint validthis:true */ var Constructor = this; if (object && typeof object === 'object' && object.constructor === Constructor) { return object; } var promise = new Constructor(noop); resolve(promise, object); return promise; } var PROMISE_ID = Math.random().toString(36).substring(2); function noop() {} var PENDING = void 0; var FULFILLED = 1; var REJECTED = 2; var TRY_CATCH_ERROR = { error: null }; function selfFulfillment() { return new TypeError("You cannot resolve a promise with itself"); } function cannotReturnOwn() { return new TypeError('A promises callback cannot return that same promise.'); } function getThen(promise) { try { return promise.then; } catch (error) { TRY_CATCH_ERROR.error = error; return TRY_CATCH_ERROR; } } function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) { try { then$$1.call(value, fulfillmentHandler, rejectionHandler); } catch (e) { return e; } } function handleForeignThenable(promise, thenable, then$$1) { asap(function (promise) { var sealed = false; var error = tryThen(then$$1, thenable, function (value) { if (sealed) { return; } sealed = true; if (thenable !== value) { resolve(promise, value); } else { fulfill(promise, value); } }, function (reason) { if (sealed) { return; } sealed = true; reject(promise, reason); }, 'Settle: ' + (promise._label || ' unknown promise')); if (!sealed && error) { sealed = true; reject(promise, error); } }, promise); } function handleOwnThenable(promise, thenable) { if (thenable._state === FULFILLED) { fulfill(promise, thenable._result); } else if (thenable._state === REJECTED) { reject(promise, thenable._result); } else { subscribe(thenable, undefined, function (value) { return resolve(promise, value); }, function (reason) { return reject(promise, reason); }); } } function handleMaybeThenable(promise, maybeThenable, then$$1) { if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) { handleOwnThenable(promise, maybeThenable); } else { if (then$$1 === TRY_CATCH_ERROR) { reject(promise, TRY_CATCH_ERROR.error); TRY_CATCH_ERROR.error = null; } else if (then$$1 === undefined) { fulfill(promise, maybeThenable); } else if (isFunction(then$$1)) { handleForeignThenable(promise, maybeThenable, then$$1); } else { fulfill(promise, maybeThenable); } } } function resolve(promise, value) { if (promise === value) { reject(promise, selfFulfillment()); } else if (objectOrFunction(value)) { handleMaybeThenable(promise, value, getThen(value)); } else { fulfill(promise, value); } } function publishRejection(promise) { if (promise._onerror) { promise._onerror(promise._result); } publish(promise); } function fulfill(promise, value) { if (promise._state !== PENDING) { return; } promise._result = value; promise._state = FULFILLED; if (promise._subscribers.length !== 0) { asap(publish, promise); } } function reject(promise, reason) { if (promise._state !== PENDING) { return; } promise._state = REJECTED; promise._result = reason; asap(publishRejection, promise); } function subscribe(parent, child, onFulfillment, onRejection) { var _subscribers = parent._subscribers; var length = _subscribers.length; parent._onerror = null; _subscribers[length] = child; _subscribers[length + FULFILLED] = onFulfillment; _subscribers[length + REJECTED] = onRejection; if (length === 0 && parent._state) { asap(publish, parent); } } function publish(promise) { var subscribers = promise._subscribers; var settled = promise._state; if (subscribers.length === 0) { return; } var child = void 0, callback = void 0, detail = promise._result; for (var i = 0; i < subscribers.length; i += 3) { child = subscribers[i]; callback = subscribers[i + settled]; if (child) { invokeCallback(settled, child, callback, detail); } else { callback(detail); } } promise._subscribers.length = 0; } function tryCatch(callback, detail) { try { return callback(detail); } catch (e) { TRY_CATCH_ERROR.error = e; return TRY_CATCH_ERROR; } } function invokeCallback(settled, promise, callback, detail) { var hasCallback = isFunction(callback), value = void 0, error = void 0, succeeded = void 0, failed = void 0; if (hasCallback) { value = tryCatch(callback, detail); if (value === TRY_CATCH_ERROR) { failed = true; error = value.error; value.error = null; } else { succeeded = true; } if (promise === value) { reject(promise, cannotReturnOwn()); return; } } else { value = detail; succeeded = true; } if (promise._state !== PENDING) { // noop } else if (hasCallback && succeeded) { resolve(promise, value); } else if (failed) { reject(promise, error); } else if (settled === FULFILLED) { fulfill(promise, value); } else if (settled === REJECTED) { reject(promise, value); } } function initializePromise(promise, resolver) { try { resolver(function resolvePromise(value) { resolve(promise, value); }, function rejectPromise(reason) { reject(promise, reason); }); } catch (e) { reject(promise, e); } } var id = 0; function nextId() { return id++; } function makePromise(promise) { promise[PROMISE_ID] = id++; promise._state = undefined; promise._result = undefined; promise._subscribers = []; } function validationError() { return new Error('Array Methods must be provided an Array'); } var Enumerator = function () { function Enumerator(Constructor, input) { this._instanceConstructor = Constructor; this.promise = new Constructor(noop); if (!this.promise[PROMISE_ID]) { makePromise(this.promise); } if (isArray(input)) { this.length = input.length; this._remaining = input.length; this._result = new Array(this.length); if (this.length === 0) { fulfill(this.promise, this._result); } else { this.length = this.length || 0; this._enumerate(input); if (this._remaining === 0) { fulfill(this.promise, this._result); } } } else { reject(this.promise, validationError()); } } Enumerator.prototype._enumerate = function _enumerate(input) { for (var i = 0; this._state === PENDING && i < input.length; i++) { this._eachEntry(input[i], i); } }; Enumerator.prototype._eachEntry = function _eachEntry(entry, i) { var c = this._instanceConstructor; var resolve$$1 = c.resolve; if (resolve$$1 === resolve$1) { var _then = getThen(entry); if (_then === then && entry._state !== PENDING) { this._settledAt(entry._state, i, entry._result); } else if (typeof _then !== 'function') { this._remaining--; this._result[i] = entry; } else if (c === Promise$2) { var promise = new c(noop); handleMaybeThenable(promise, entry, _then); this._willSettleAt(promise, i); } else { this._willSettleAt(new c(function (resolve$$1) { return resolve$$1(entry); }), i); } } else { this._willSettleAt(resolve$$1(entry), i); } }; Enumerator.prototype._settledAt = function _settledAt(state, i, value) { var promise = this.promise; if (promise._state === PENDING) { this._remaining--; if (state === REJECTED) { reject(promise, value); } else { this._result[i] = value; } } if (this._remaining === 0) { fulfill(promise, this._result); } }; Enumerator.prototype._willSettleAt = function _willSettleAt(promise, i) { var enumerator = this; subscribe(promise, undefined, function (value) { return enumerator._settledAt(FULFILLED, i, value); }, function (reason) { return enumerator._settledAt(REJECTED, i, reason); }); }; return Enumerator; }(); /** `Promise.all` accepts an array of promises, and returns a new promise which is fulfilled with an array of fulfillment values for the passed promises, or rejected with the reason of the first passed promise to be rejected. It casts all elements of the passed iterable to promises as it runs this algorithm. Example: ```javascript let promise1 = resolve(1); let promise2 = resolve(2); let promise3 = resolve(3); let promises = [ promise1, promise2, promise3 ]; Promise.all(promises).then(function(array){ // The array here would be [ 1, 2, 3 ]; }); ``` If any of the `promises` given to `all` are rejected, the first promise that is rejected will be given as an argument to the returned promises's rejection handler. For example: Example: ```javascript let promise1 = resolve(1); let promise2 = reject(new Error("2")); let promise3 = reject(new Error("3")); let promises = [ promise1, promise2, promise3 ]; Promise.all(promises).then(function(array){ // Code here never runs because there are rejected promises! }, function(error) { // error.message === "2" }); ``` @method all @static @param {Array} entries array of promises @param {String} label optional string for labeling the promise. Useful for tooling. @return {Promise} promise that is fulfilled when all `promises` have been fulfilled, or rejected if any of them become rejected. @static */ function all(entries) { return new Enumerator(this, entries).promise; } /** `Promise.race` returns a new promise which is settled in the same way as the first passed promise to settle. Example: ```javascript let promise1 = new Promise(function(resolve, reject){ setTimeout(function(){ resolve('promise 1'); }, 200); }); let promise2 = new Promise(function(resolve, reject){ setTimeout(function(){ resolve('promise 2'); }, 100); }); Promise.race([promise1, promise2]).then(function(result){ // result === 'promise 2' because it was resolved before promise1 // was resolved. }); ``` `Promise.race` is deterministic in that only the state of the first settled promise matters. For example, even if other promises given to the `promises` array argument are resolved, but the first settled promise has become rejected before the other promises became fulfilled, the returned promise will become rejected: ```javascript let promise1 = new Promise(function(resolve, reject){ setTimeout(function(){ resolve('promise 1'); }, 200); }); let promise2 = new Promise(function(resolve, reject){ setTimeout(function(){ reject(new Error('promise 2')); }, 100); }); Promise.race([promise1, promise2]).then(function(result){ // Code here never runs }, function(reason){ // reason.message === 'promise 2' because promise 2 became rejected before // promise 1 became fulfilled }); ``` An example real-world use case is implementing timeouts: ```javascript Promise.race([ajax('foo.json'), timeout(5000)]) ``` @method race @static @param {Array} promises array of promises to observe Useful for tooling. @return {Promise} a promise which settles in the same way as the first passed promise to settle. */ function race(entries) { /*jshint validthis:true */ var Constructor = this; if (!isArray(entries)) { return new Constructor(function (_, reject) { return reject(new TypeError('You must pass an array to race.')); }); } else { return new Constructor(function (resolve, reject) { var length = entries.length; for (var i = 0; i < length; i++) { Constructor.resolve(entries[i]).then(resolve, reject); } }); } } /** `Promise.reject` returns a promise rejected with the passed `reason`. It is shorthand for the following: ```javascript let promise = new Promise(function(resolve, reject){ reject(new Error('WHOOPS')); }); promise.then(function(value){ // Code here doesn't run because the promise is rejected! }, function(reason){ // reason.message === 'WHOOPS' }); ``` Instead of writing the above, your code now simply becomes the following: ```javascript let promise = Promise.reject(new Error('WHOOPS')); promise.then(function(value){ // Code here doesn't run because the promise is rejected! }, function(reason){ // reason.message === 'WHOOPS' }); ``` @method reject @static @param {Any} reason value that the returned promise will be rejected with. Useful for tooling. @return {Promise} a promise rejected with the given `reason`. */ function reject$1(reason) { /*jshint validthis:true */ var Constructor = this; var promise = new Constructor(noop); reject(promise, reason); return promise; } function needsResolver() { throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); } function needsNew() { throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); } /** Promise objects represent the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its `then` method, which registers callbacks to receive either a promise's eventual value or the reason why the promise cannot be fulfilled. Terminology ----------- - `promise` is an object or function with a `then` method whose behavior conforms to this specification. - `thenable` is an object or function that defines a `then` method. - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). - `exception` is a value that is thrown using the throw statement. - `reason` is a value that indicates why a promise was rejected. - `settled` the final resting state of a promise, fulfilled or rejected. A promise can be in one of three states: pending, fulfilled, or rejected. Promises that are fulfilled have a fulfillment value and are in the fulfilled state. Promises that are rejected have a rejection reason and are in the rejected state. A fulfillment value is never a thenable. Promises can also be said to *resolve* a value. If this value is also a promise, then the original promise's settled state will match the value's settled state. So a promise that *resolves* a promise that rejects will itself reject, and a promise that *resolves* a promise that fulfills will itself fulfill. Basic Usage: ------------ ```js let promise = new Promise(function(resolve, reject) { // on success resolve(value); // on failure reject(reason); }); promise.then(function(value) { // on fulfillment }, function(reason) { // on rejection }); ``` Advanced Usage: --------------- Promises shine when abstracting away asynchronous interactions such as `XMLHttpRequest`s. ```js function getJSON(url) { return new Promise(function(resolve, reject){ let xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onreadystatechange = handler; xhr.responseType = 'json'; xhr.setRequestHeader('Accept', 'application/json'); xhr.send(); function handler() { if (this.readyState === this.DONE) { if (this.status === 200) { resolve(this.response); } else { reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); } } }; }); } getJSON('/posts.json').then(function(json) { // on fulfillment }, function(reason) { // on rejection }); ``` Unlike callbacks, promises are great composable primitives. ```js Promise.all([ getJSON('/posts'), getJSON('/comments') ]).then(function(values){ values[0] // => postsJSON values[1] // => commentsJSON return values; }); ``` @class Promise @param {Function} resolver Useful for tooling. @constructor */ var Promise$2 = function () { function Promise(resolver) { this[PROMISE_ID] = nextId(); this._result = this._state = undefined; this._subscribers = []; if (noop !== resolver) { typeof resolver !== 'function' && needsResolver(); this instanceof Promise ? initializePromise(this, resolver) : needsNew(); } } /** The primary way of interacting with a promise is through its `then` method, which registers callbacks to receive either a promise's eventual value or the reason why the promise cannot be fulfilled. ```js findUser().then(function(user){ // user is available }, function(reason){ // user is unavailable, and you are given the reason why }); ``` Chaining -------- The return value of `then` is itself a promise. This second, 'downstream' promise is resolved with the return value of the first promise's fulfillment or rejection handler, or rejected if the handler throws an exception. ```js findUser().then(function (user) { return user.name; }, function (reason) { return 'default name'; }).then(function (userName) { // If `findUser` fulfilled, `userName` will be the user's name, otherwise it // will be `'default name'` }); findUser().then(function (user) { throw new Error('Found user, but still unhappy'); }, function (reason) { throw new Error('`findUser` rejected and we're unhappy'); }).then(function (value) { // never reached }, function (reason) { // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. }); ``` If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. ```js findUser().then(function (user) { throw new PedagogicalException('Upstream error'); }).then(function (value) { // never reached }).then(function (value) { // never reached }, function (reason) { // The `PedgagocialException` is propagated all the way down to here }); ``` Assimilation ------------ Sometimes the value you want to propagate to a downstream promise can only be retrieved asynchronously. This can be achieved by returning a promise in the fulfillment or rejection handler. The downstream promise will then be pending until the returned promise is settled. This is called *assimilation*. ```js findUser().then(function (user) { return findCommentsByAuthor(user); }).then(function (comments) { // The user's comments are now available }); ``` If the assimliated promise rejects, then the downstream promise will also reject. ```js findUser().then(function (user) { return findCommentsByAuthor(user); }).then(function (comments) { // If `findCommentsByAuthor` fulfills, we'll have the value here }, function (reason) { // If `findCommentsByAuthor` rejects, we'll have the reason here }); ``` Simple Example -------------- Synchronous Example ```javascript let result; try { result = findResult(); // success } catch(reason) { // failure } ``` Errback Example ```js findResult(function(result, err){ if (err) { // failure } else { // success } }); ``` Promise Example; ```javascript findResult().then(function(result){ // success }, function(reason){ // failure }); ``` Advanced Example -------------- Synchronous Example ```javascript let author, books; try { author = findAuthor(); books = findBooksByAuthor(author); // success } catch(reason) { // failure } ``` Errback Example ```js function foundBooks(books) { } function failure(reason) { } findAuthor(function(author, err){ if (err) { failure(err); // failure } else { try { findBoooksByAuthor(author, function(books, err) { if (err) { failure(err); } else { try { foundBooks(books); } catch(reason) { failure(reason); } } }); } catch(error) { failure(err); } // success } }); ``` Promise Example; ```javascript findAuthor(). then(findBooksByAuthor). then(function(books){ // found books }).catch(function(reason){ // something went wrong }); ``` @method then @param {Function} onFulfilled @param {Function} onRejected Useful for tooling. @return {Promise} */ /** `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same as the catch block of a try/catch statement. ```js function findAuthor(){ throw new Error('couldn't find that author'); } // synchronous try { findAuthor(); } catch(reason) { // something went wrong } // async with promises findAuthor().catch(function(reason){ // something went wrong }); ``` @method catch @param {Function} onRejection Useful for tooling. @return {Promise} */ Promise.prototype.catch = function _catch(onRejection) { return this.then(null, onRejection); }; /** `finally` will be invoked regardless of the promise's fate just as native try/catch/finally behaves Synchronous example: ```js findAuthor() { if (Math.random() > 0.5) { throw new Error(); } return new Author(); } try { return findAuthor(); // succeed or fail } catch(error) { return findOtherAuther(); } finally { // always runs // doesn't affect the return value } ``` Asynchronous example: ```js findAuthor().catch(function(reason){ return findOtherAuther(); }).finally(function(){ // author was either found, or not }); ``` @method finally @param {Function} callback @return {Promise} */ Promise.prototype.finally = function _finally(callback) { var promise = this; var constructor = promise.constructor; return promise.then(function (value) { return constructor.resolve(callback()).then(function () { return value; }); }, function (reason) { return constructor.resolve(callback()).then(function () { throw reason; }); }); }; return Promise; }(); Promise$2.prototype.then = then; Promise$2.all = all; Promise$2.race = race; Promise$2.resolve = resolve$1; Promise$2.reject = reject$1; Promise$2._setScheduler = setScheduler; Promise$2._setAsap = setAsap; Promise$2._asap = asap; /*global self*/ function polyfill() { var local = void 0; if (typeof global !== 'undefined') { local = global; } else if (typeof self !== 'undefined') { local = self; } else { try { local = Function('return this')(); } catch (e) { throw new Error('polyfill failed because global object is unavailable in this environment'); } } var P = local.Promise; if (P) { var promiseToString = null; try { promiseToString = Object.prototype.toString.call(P.resolve()); } catch (e) { // silently ignored } if (promiseToString === '[object Promise]' && !P.cast) { return; } } local.Promise = Promise$2; } // Strange compat.. Promise$2.polyfill = polyfill; Promise$2.Promise = Promise$2; Promise$2.polyfill(); return Promise$2; }))); //# sourceMappingURL=es6-promise.auto.map /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../process/browser.js */ "./node_modules/process/browser.js"), __webpack_require__(/*! ./../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) /***/ }), /***/ "./node_modules/filesize/lib/filesize.js": /*!***********************************************!*\ !*** ./node_modules/filesize/lib/filesize.js ***! \***********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(global) { /** * filesize * * @copyright 2018 Jason Mulligan * @license BSD-3-Clause * @version 3.6.1 */ (function (global) { var b = /^(b|B)$/, symbol = { iec: { bits: ["b", "Kib", "Mib", "Gib", "Tib", "Pib", "Eib", "Zib", "Yib"], bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] }, jedec: { bits: ["b", "Kb", "Mb", "Gb", "Tb", "Pb", "Eb", "Zb", "Yb"], bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] } }, fullform = { iec: ["", "kibi", "mebi", "gibi", "tebi", "pebi", "exbi", "zebi", "yobi"], jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"] }; /** * filesize * * @method filesize * @param {Mixed} arg String, Int or Float to transform * @param {Object} descriptor [Optional] Flags * @return {String} Readable file size String */ function filesize(arg) { var descriptor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var result = [], val = 0, e = void 0, base = void 0, bits = void 0, ceil = void 0, full = void 0, fullforms = void 0, neg = void 0, num = void 0, output = void 0, round = void 0, unix = void 0, separator = void 0, spacer = void 0, standard = void 0, symbols = void 0; if (isNaN(arg)) { throw new Error("Invalid arguments"); } bits = descriptor.bits === true; unix = descriptor.unix === true; base = descriptor.base || 2; round = descriptor.round !== void 0 ? descriptor.round : unix ? 1 : 2; separator = descriptor.separator !== void 0 ? descriptor.separator || "" : ""; spacer = descriptor.spacer !== void 0 ? descriptor.spacer : unix ? "" : " "; symbols = descriptor.symbols || descriptor.suffixes || {}; standard = base === 2 ? descriptor.standard || "jedec" : "jedec"; output = descriptor.output || "string"; full = descriptor.fullform === true; fullforms = descriptor.fullforms instanceof Array ? descriptor.fullforms : []; e = descriptor.exponent !== void 0 ? descriptor.exponent : -1; num = Number(arg); neg = num < 0; ceil = base > 2 ? 1000 : 1024; // Flipping a negative number to determine the size if (neg) { num = -num; } // Determining the exponent if (e === -1 || isNaN(e)) { e = Math.floor(Math.log(num) / Math.log(ceil)); if (e < 0) { e = 0; } } // Exceeding supported length, time to reduce & multiply if (e > 8) { e = 8; } // Zero is now a special case because bytes divide by 1 if (num === 0) { result[0] = 0; result[1] = unix ? "" : symbol[standard][bits ? "bits" : "bytes"][e]; } else { val = num / (base === 2 ? Math.pow(2, e * 10) : Math.pow(1000, e)); if (bits) { val = val * 8; if (val >= ceil && e < 8) { val = val / ceil; e++; } } result[0] = Number(val.toFixed(e > 0 ? round : 0)); result[1] = base === 10 && e === 1 ? bits ? "kb" : "kB" : symbol[standard][bits ? "bits" : "bytes"][e]; if (unix) { result[1] = standard === "jedec" ? result[1].charAt(0) : e > 0 ? result[1].replace(/B$/, "") : result[1]; if (b.test(result[1])) { result[0] = Math.floor(result[0]); result[1] = ""; } } } // Decorating a 'diff' if (neg) { result[0] = -result[0]; } // Applying custom symbol result[1] = symbols[result[1]] || result[1]; // Returning Array, Object, or String (default) if (output === "array") { return result; } if (output === "exponent") { return e; } if (output === "object") { return { value: result[0], suffix: result[1], symbol: result[1] }; } if (full) { result[1] = fullforms[e] ? fullforms[e] : fullform[standard][e] + (bits ? "bit" : "byte") + (result[0] === 1 ? "" : "s"); } if (separator.length > 0) { result[0] = result[0].toString().replace(".", separator); } return result.join(spacer); } // Partial application for functional programming filesize.partial = function (opt) { return function (arg) { return filesize(arg, opt); }; }; // CommonJS, AMD, script tag if (true) { module.exports = filesize; } else {} })(typeof window !== "undefined" ? window : global); /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) /***/ }), /***/ "./node_modules/hellojs/dist/hello.all.js": /*!************************************************!*\ !*** ./node_modules/hellojs/dist/hello.all.js ***! \************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(process, setImmediate) {var __WEBPACK_AMD_DEFINE_RESULT__;/*! hellojs v1.16.1 | (c) 2012-2017 Andrew Dodson | MIT https://adodson.com/hello.js/LICENSE */ // ES5 Object.create if (!Object.create) { // Shim, Object create // A shim for Object.create(), it adds a prototype to a new object Object.create = (function() { function F() {} return function(o) { if (arguments.length != 1) { throw new Error('Object.create implementation only accepts one parameter.'); } F.prototype = o; return new F(); }; })(); } // ES5 Object.keys if (!Object.keys) { Object.keys = function(o, k, r) { r = []; for (k in o) { if (r.hasOwnProperty.call(o, k)) r.push(k); } return r; }; } // ES5 [].indexOf if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(s) { for (var j = 0; j < this.length; j++) { if (this[j] === s) { return j; } } return -1; }; } // ES5 [].forEach if (!Array.prototype.forEach) { Array.prototype.forEach = function(fun/*, thisArg*/) { if (this === void 0 || this === null) { throw new TypeError(); } var t = Object(this); var len = t.length >>> 0; if (typeof fun !== 'function') { throw new TypeError(); } var thisArg = arguments.length >= 2 ? arguments[1] : void 0; for (var i = 0; i < len; i++) { if (i in t) { fun.call(thisArg, t[i], i, t); } } return this; }; } // ES5 [].filter if (!Array.prototype.filter) { Array.prototype.filter = function(fun, thisArg) { var a = []; this.forEach(function(val, i, t) { if (fun.call(thisArg || void 0, val, i, t)) { a.push(val); } }); return a; }; } // Production steps of ECMA-262, Edition 5, 15.4.4.19 // Reference: http://es5.github.io/#x15.4.4.19 if (!Array.prototype.map) { Array.prototype.map = function(fun, thisArg) { var a = []; this.forEach(function(val, i, t) { a.push(fun.call(thisArg || void 0, val, i, t)); }); return a; }; } // ES5 isArray if (!Array.isArray) { // Function Array.isArray Array.isArray = function(o) { return Object.prototype.toString.call(o) === '[object Array]'; }; } // Test for location.assign if (typeof window === 'object' && typeof window.location === 'object' && !window.location.assign) { window.location.assign = function(url) { window.location = url; }; } // Test for Function.bind if (!Function.prototype.bind) { // MDN // Polyfill IE8, does not support native Function.bind Function.prototype.bind = function(b) { if (typeof this !== 'function') { throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } function C() {} var a = [].slice; var f = a.call(arguments, 1); var _this = this; var D = function() { return _this.apply(this instanceof C ? this : b || window, f.concat(a.call(arguments))); }; C.prototype = this.prototype; D.prototype = new C(); return D; }; } /** * @hello.js * * HelloJS is a client side Javascript SDK for making OAuth2 logins and subsequent REST calls. * * @author Andrew Dodson * @website https://adodson.com/hello.js/ * * @copyright Andrew Dodson, 2012 - 2015 * @license MIT: You are free to use and modify this code for any use, on the condition that this copyright notice remains. */ var hello = function(name) { return hello.use(name); }; hello.utils = { // Extend the first object with the properties and methods of the second extend: function(r /*, a[, b[, ...]] */) { // Get the arguments as an array but ommit the initial item Array.prototype.slice.call(arguments, 1).forEach(function(a) { if (Array.isArray(r) && Array.isArray(a)) { Array.prototype.push.apply(r, a); } else if (r && (r instanceof Object || typeof r === 'object') && a && (a instanceof Object || typeof a === 'object') && r !== a) { for (var x in a) { r[x] = hello.utils.extend(r[x], a[x]); } } else { if (Array.isArray(a)) { // Clone it a = a.slice(0); } r = a; } }); return r; } }; // Core library hello.utils.extend(hello, { settings: { // OAuth2 authentication defaults redirect_uri: window.location.href.split('#')[0], response_type: 'token', display: 'popup', state: '', // OAuth1 shim // The path to the OAuth1 server for signing user requests // Want to recreate your own? Checkout https://github.com/MrSwitch/node-oauth-shim oauth_proxy: 'https://auth-server.herokuapp.com/proxy', // API timeout in milliseconds timeout: 20000, // Popup Options popup: { resizable: 1, scrollbars: 1, width: 500, height: 550 }, // Default scope // Many services require atleast a profile scope, // HelloJS automatially includes the value of provider.scope_map.basic // If that's not required it can be removed via hello.settings.scope.length = 0; scope: ['basic'], // Scope Maps // This is the default module scope, these are the defaults which each service is mapped too. // By including them here it prevents the scope from being applied accidentally scope_map: { basic: '' }, // Default service / network default_service: null, // Force authentication // When hello.login is fired. // (null): ignore current session expiry and continue with login // (true): ignore current session expiry and continue with login, ask for user to reauthenticate // (false): if the current session looks good for the request scopes return the current session. force: null, // Page URL // When 'display=page' this property defines where the users page should end up after redirect_uri // Ths could be problematic if the redirect_uri is indeed the final place, // Typically this circumvents the problem of the redirect_url being a dumb relay page. page_uri: window.location.href }, // Service configuration objects services: {}, // Use // Define a new instance of the HelloJS library with a default service use: function(service) { // Create self, which inherits from its parent var self = Object.create(this); // Inherit the prototype from its parent self.settings = Object.create(this.settings); // Define the default service if (service) { self.settings.default_service = service; } // Create an instance of Events self.utils.Event.call(self); return self; }, // Initialize // Define the client_ids for the endpoint services // @param object o, contains a key value pair, service => clientId // @param object opts, contains a key value pair of options used for defining the authentication defaults // @param number timeout, timeout in seconds init: function(services, options) { var utils = this.utils; if (!services) { return this.services; } // Define provider credentials // Reformat the ID field for (var x in services) {if (services.hasOwnProperty(x)) { if (typeof (services[x]) !== 'object') { services[x] = {id: services[x]}; } }} // Merge services if there already exists some utils.extend(this.services, services); // Update the default settings with this one. if (options) { utils.extend(this.settings, options); // Do this immediatly incase the browser changes the current path. if ('redirect_uri' in options) { this.settings.redirect_uri = utils.url(options.redirect_uri).href; } } return this; }, // Login // Using the endpoint // @param network stringify name to connect to // @param options object (optional) {display mode, is either none|popup(default)|page, scope: email,birthday,publish, .. } // @param callback function (optional) fired on signin login: function() { // Create an object which inherits its parent as the prototype and constructs a new event chain. var _this = this; var utils = _this.utils; var error = utils.error; var promise = utils.Promise(); // Get parameters var p = utils.args({network: 's', options: 'o', callback: 'f'}, arguments); // Local vars var url; // Get all the custom options and store to be appended to the querystring var qs = utils.diffKey(p.options, _this.settings); // Merge/override options with app defaults var opts = p.options = utils.merge(_this.settings, p.options || {}); // Merge/override options with app defaults opts.popup = utils.merge(_this.settings.popup, p.options.popup || {}); // Network p.network = p.network || _this.settings.default_service; // Bind callback to both reject and fulfill states promise.proxy.then(p.callback, p.callback); // Trigger an event on the global listener function emit(s, value) { hello.emit(s, value); } promise.proxy.then(emit.bind(this, 'auth.login auth'), emit.bind(this, 'auth.failed auth')); // Is our service valid? if (typeof (p.network) !== 'string' || !(p.network in _this.services)) { // Trigger the default login. // Ahh we dont have one. return promise.reject(error('invalid_network', 'The provided network was not recognized')); } var provider = _this.services[p.network]; // Create a global listener to capture events triggered out of scope var callbackId = utils.globalEvent(function(str) { // The responseHandler returns a string, lets save this locally var obj; if (str) { obj = JSON.parse(str); } else { obj = error('cancelled', 'The authentication was not completed'); } // Handle these response using the local // Trigger on the parent if (!obj.error) { // Save on the parent window the new credentials // This fixes an IE10 bug i think... atleast it does for me. utils.store(obj.network, obj); // Fulfill a successful login promise.fulfill({ network: obj.network, authResponse: obj }); } else { // Reject a successful login promise.reject(obj); } }); var redirectUri = utils.url(opts.redirect_uri).href; // May be a space-delimited list of multiple, complementary types var responseType = provider.oauth.response_type || opts.response_type; // Fallback to token if the module hasn't defined a grant url if (/\bcode\b/.test(responseType) && !provider.oauth.grant) { responseType = responseType.replace(/\bcode\b/, 'token'); } // Query string parameters, we may pass our own arguments to form the querystring p.qs = utils.merge(qs, { client_id: encodeURIComponent(provider.id), response_type: encodeURIComponent(responseType), redirect_uri: encodeURIComponent(redirectUri), state: { client_id: provider.id, network: p.network, display: opts.display, callback: callbackId, state: opts.state, redirect_uri: redirectUri } }); // Get current session for merging scopes, and for quick auth response var session = utils.store(p.network); // Scopes (authentication permisions) // Ensure this is a string - IE has a problem moving Arrays between windows // Append the setup scope var SCOPE_SPLIT = /[,\s]+/; // Include default scope settings (cloned). var scope = _this.settings.scope ? [_this.settings.scope.toString()] : []; // Extend the providers scope list with the default var scopeMap = utils.merge(_this.settings.scope_map, provider.scope || {}); // Add user defined scopes... if (opts.scope) { scope.push(opts.scope.toString()); } // Append scopes from a previous session. // This helps keep app credentials constant, // Avoiding having to keep tabs on what scopes are authorized if (session && 'scope' in session && session.scope instanceof String) { scope.push(session.scope); } // Join and Split again scope = scope.join(',').split(SCOPE_SPLIT); // Format remove duplicates and empty values scope = utils.unique(scope).filter(filterEmpty); // Save the the scopes to the state with the names that they were requested with. p.qs.state.scope = scope.join(','); // Map scopes to the providers naming convention scope = scope.map(function(item) { // Does this have a mapping? return (item in scopeMap) ? scopeMap[item] : item; }); // Stringify and Arrayify so that double mapped scopes are given the chance to be formatted scope = scope.join(',').split(SCOPE_SPLIT); // Again... // Format remove duplicates and empty values scope = utils.unique(scope).filter(filterEmpty); // Join with the expected scope delimiter into a string p.qs.scope = scope.join(provider.scope_delim || ','); // Is the user already signed in with the appropriate scopes, valid access_token? if (opts.force === false) { if (session && 'access_token' in session && session.access_token && 'expires' in session && session.expires > ((new Date()).getTime() / 1e3)) { // What is different about the scopes in the session vs the scopes in the new login? var diff = utils.diff((session.scope || '').split(SCOPE_SPLIT), (p.qs.state.scope || '').split(SCOPE_SPLIT)); if (diff.length === 0) { // OK trigger the callback promise.fulfill({ unchanged: true, network: p.network, authResponse: session }); // Nothing has changed return promise; } } } // Page URL if (opts.display === 'page' && opts.page_uri) { // Add a page location, place to endup after session has authenticated p.qs.state.page_uri = utils.url(opts.page_uri).href; } // Bespoke // Override login querystrings from auth_options if ('login' in provider && typeof (provider.login) === 'function') { // Format the paramaters according to the providers formatting function provider.login(p); } // Add OAuth to state // Where the service is going to take advantage of the oauth_proxy if (!/\btoken\b/.test(responseType) || parseInt(provider.oauth.version, 10) < 2 || (opts.display === 'none' && provider.oauth.grant && session && session.refresh_token)) { // Add the oauth endpoints p.qs.state.oauth = provider.oauth; // Add the proxy url p.qs.state.oauth_proxy = opts.oauth_proxy; } // Convert state to a string p.qs.state = encodeURIComponent(JSON.stringify(p.qs.state)); // URL if (parseInt(provider.oauth.version, 10) === 1) { // Turn the request to the OAuth Proxy for 3-legged auth url = utils.qs(opts.oauth_proxy, p.qs, encodeFunction); } // Refresh token else if (opts.display === 'none' && provider.oauth.grant && session && session.refresh_token) { // Add the refresh_token to the request p.qs.refresh_token = session.refresh_token; // Define the request path url = utils.qs(opts.oauth_proxy, p.qs, encodeFunction); } else { url = utils.qs(provider.oauth.auth, p.qs, encodeFunction); } // Broadcast this event as an auth:init emit('auth.init', p); // Execute // Trigger how we want self displayed if (opts.display === 'none') { // Sign-in in the background, iframe utils.iframe(url, redirectUri); } // Triggering popup? else if (opts.display === 'popup') { var popup = utils.popup(url, redirectUri, opts.popup); var timer = setInterval(function() { if (!popup || popup.closed) { clearInterval(timer); if (!promise.state) { var response = error('cancelled', 'Login has been cancelled'); if (!popup) { response = error('blocked', 'Popup was blocked'); } response.network = p.network; promise.reject(response); } } }, 100); } else { window.location = url; } return promise.proxy; function encodeFunction(s) {return s;} function filterEmpty(s) {return !!s;} }, // Remove any data associated with a given service // @param string name of the service // @param function callback logout: function() { var _this = this; var utils = _this.utils; var error = utils.error; // Create a new promise var promise = utils.Promise(); var p = utils.args({name:'s', options: 'o', callback: 'f'}, arguments); p.options = p.options || {}; // Add callback to events promise.proxy.then(p.callback, p.callback); // Trigger an event on the global listener function emit(s, value) { hello.emit(s, value); } promise.proxy.then(emit.bind(this, 'auth.logout auth'), emit.bind(this, 'error')); // Network p.name = p.name || this.settings.default_service; p.authResponse = utils.store(p.name); if (p.name && !(p.name in _this.services)) { promise.reject(error('invalid_network', 'The network was unrecognized')); } else if (p.name && p.authResponse) { // Define the callback var callback = function(opts) { // Remove from the store utils.store(p.name, null); // Emit events by default promise.fulfill(hello.utils.merge({network:p.name}, opts || {})); }; // Run an async operation to remove the users session var _opts = {}; if (p.options.force) { var logout = _this.services[p.name].logout; if (logout) { // Convert logout to URL string, // If no string is returned, then this function will handle the logout async style if (typeof (logout) === 'function') { logout = logout(callback, p); } // If logout is a string then assume URL and open in iframe. if (typeof (logout) === 'string') { utils.iframe(logout); _opts.force = null; _opts.message = 'Logout success on providers site was indeterminate'; } else if (logout === undefined) { // The callback function will handle the response. return promise.proxy; } } } // Remove local credentials callback(_opts); } else { promise.reject(error('invalid_session', 'There was no session to remove')); } return promise.proxy; }, // Returns all the sessions that are subscribed too // @param string optional, name of the service to get information about. getAuthResponse: function(service) { // If the service doesn't exist service = service || this.settings.default_service; if (!service || !(service in this.services)) { return null; } return this.utils.store(service) || null; }, // Events: placeholder for the events events: {} }); // Core utilities hello.utils.extend(hello.utils, { // Error error: function(code, message) { return { error: { code: code, message: message } }; }, // Append the querystring to a url // @param string url // @param object parameters qs: function(url, params, formatFunction) { if (params) { // Set default formatting function formatFunction = formatFunction || encodeURIComponent; // Override the items in the URL which already exist for (var x in params) { var str = '([\\?\\&])' + x + '=[^\\&]*'; var reg = new RegExp(str); if (url.match(reg)) { url = url.replace(reg, '$1' + x + '=' + formatFunction(params[x])); delete params[x]; } } } if (!this.isEmpty(params)) { return url + (url.indexOf('?') > -1 ? '&' : '?') + this.param(params, formatFunction); } return url; }, // Param // Explode/encode the parameters of an URL string/object // @param string s, string to decode param: function(s, formatFunction) { var b; var a = {}; var m; if (typeof (s) === 'string') { formatFunction = formatFunction || decodeURIComponent; m = s.replace(/^[\#\?]/, '').match(/([^=\/\&]+)=([^\&]+)/g); if (m) { for (var i = 0; i < m.length; i++) { b = m[i].match(/([^=]+)=(.*)/); a[b[1]] = formatFunction(b[2]); } } return a; } else { formatFunction = formatFunction || encodeURIComponent; var o = s; a = []; for (var x in o) {if (o.hasOwnProperty(x)) { if (o.hasOwnProperty(x)) { a.push([x, o[x] === '?' ? '?' : formatFunction(o[x])].join('=')); } }} return a.join('&'); } }, // Local storage facade store: (function() { var a = ['localStorage', 'sessionStorage']; var i = -1; var prefix = 'test'; // Set LocalStorage var localStorage; while (a[++i]) { try { // In Chrome with cookies blocked, calling localStorage throws an error localStorage = window[a[i]]; localStorage.setItem(prefix + i, i); localStorage.removeItem(prefix + i); break; } catch (e) { localStorage = null; } } if (!localStorage) { var cache = null; localStorage = { getItem: function(prop) { prop = prop + '='; var m = document.cookie.split(';'); for (var i = 0; i < m.length; i++) { var _m = m[i].replace(/(^\s+|\s+$)/, ''); if (_m && _m.indexOf(prop) === 0) { return _m.substr(prop.length); } } return cache; }, setItem: function(prop, value) { cache = value; document.cookie = prop + '=' + value; } }; // Fill the cache up cache = localStorage.getItem('hello'); } function get() { var json = {}; try { json = JSON.parse(localStorage.getItem('hello')) || {}; } catch (e) {} return json; } function set(json) { localStorage.setItem('hello', JSON.stringify(json)); } // Check if the browser support local storage return function(name, value, days) { // Local storage var json = get(); if (name && value === undefined) { return json[name] || null; } else if (name && value === null) { try { delete json[name]; } catch (e) { json[name] = null; } } else if (name) { json[name] = value; } else { return json; } set(json); return json || null; }; })(), // Create and Append new DOM elements // @param node string // @param attr object literal // @param dom/string append: function(node, attr, target) { var n = typeof (node) === 'string' ? document.createElement(node) : node; if (typeof (attr) === 'object') { if ('tagName' in attr) { target = attr; } else { for (var x in attr) {if (attr.hasOwnProperty(x)) { if (typeof (attr[x]) === 'object') { for (var y in attr[x]) {if (attr[x].hasOwnProperty(y)) { n[x][y] = attr[x][y]; }} } else if (x === 'html') { n.innerHTML = attr[x]; } // IE doesn't like us setting methods with setAttribute else if (!/^on/.test(x)) { n.setAttribute(x, attr[x]); } else { n[x] = attr[x]; } }} } } if (target === 'body') { (function self() { if (document.body) { document.body.appendChild(n); } else { setTimeout(self, 16); } })(); } else if (typeof (target) === 'object') { target.appendChild(n); } else if (typeof (target) === 'string') { document.getElementsByTagName(target)[0].appendChild(n); } return n; }, // An easy way to create a hidden iframe // @param string src iframe: function(src) { this.append('iframe', {src: src, style: {position:'absolute', left: '-1000px', bottom: 0, height: '1px', width: '1px'}}, 'body'); }, // Recursive merge two objects into one, second parameter overides the first // @param a array merge: function(/* Args: a, b, c, .. n */) { var args = Array.prototype.slice.call(arguments); args.unshift({}); return this.extend.apply(null, args); }, // Makes it easier to assign parameters, where some are optional // @param o object // @param a arguments args: function(o, args) { var p = {}; var i = 0; var t = null; var x = null; // 'x' is the first key in the list of object parameters for (x in o) {if (o.hasOwnProperty(x)) { break; }} // Passing in hash object of arguments? // Where the first argument can't be an object if ((args.length === 1) && (typeof (args[0]) === 'object') && o[x] != 'o!') { // Could this object still belong to a property? // Check the object keys if they match any of the property keys for (x in args[0]) {if (o.hasOwnProperty(x)) { // Does this key exist in the property list? if (x in o) { // Yes this key does exist so its most likely this function has been invoked with an object parameter // Return first argument as the hash of all arguments return args[0]; } }} } // Else loop through and account for the missing ones. for (x in o) {if (o.hasOwnProperty(x)) { t = typeof (args[i]); if ((typeof (o[x]) === 'function' && o[x].test(args[i])) || (typeof (o[x]) === 'string' && ( (o[x].indexOf('s') > -1 && t === 'string') || (o[x].indexOf('o') > -1 && t === 'object') || (o[x].indexOf('i') > -1 && t === 'number') || (o[x].indexOf('a') > -1 && t === 'object') || (o[x].indexOf('f') > -1 && t === 'function') )) ) { p[x] = args[i++]; } else if (typeof (o[x]) === 'string' && o[x].indexOf('!') > -1) { return false; } }} return p; }, // Returns a URL instance url: function(path) { // If the path is empty if (!path) { return window.location; } // Chrome and FireFox support new URL() to extract URL objects else if (window.URL && URL instanceof Function && URL.length !== 0) { return new URL(path, window.location); } // Ugly shim, it works! else { var a = document.createElement('a'); a.href = path; return a.cloneNode(false); } }, diff: function(a, b) { return b.filter(function(item) { return a.indexOf(item) === -1; }); }, // Get the different hash of properties unique to `a`, and not in `b` diffKey: function(a, b) { if (a || !b) { var r = {}; for (var x in a) { // Does the property not exist? if (!(x in b)) { r[x] = a[x]; } } return r; } return a; }, // Unique // Remove duplicate and null values from an array // @param a array unique: function(a) { if (!Array.isArray(a)) { return []; } return a.filter(function(item, index) { // Is this the first location of item return a.indexOf(item) === index; }); }, isEmpty: function(obj) { // Scalar if (!obj) return true; // Array if (Array.isArray(obj)) { return !obj.length; } else if (typeof (obj) === 'object') { // Object for (var key in obj) { if (obj.hasOwnProperty(key)) { return false; } } } return true; }, //jscs:disable /*! ** Thenable -- Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable ** Copyright (c) 2013-2014 Ralf S. Engelschall ** Licensed under The MIT License ** Source-Code distributed on */ Promise: (function(){ /* promise states [Promises/A+ 2.1] */ var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */ var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */ var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */ /* promise object constructor */ var api = function (executor) { /* optionally support non-constructor/plain-function call */ if (!(this instanceof api)) return new api(executor); /* initialize object */ this.id = "Thenable/1.0.6"; this.state = STATE_PENDING; /* initial state */ this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */ this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */ this.onFulfilled = []; /* initial handlers */ this.onRejected = []; /* initial handlers */ /* provide optional information-hiding proxy */ this.proxy = { then: this.then.bind(this) }; /* support optional executor function */ if (typeof executor === "function") executor.call(this, this.fulfill.bind(this), this.reject.bind(this)); }; /* promise API methods */ api.prototype = { /* promise resolving methods */ fulfill: function (value) { return deliver(this, STATE_FULFILLED, "fulfillValue", value); }, reject: function (value) { return deliver(this, STATE_REJECTED, "rejectReason", value); }, /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */ then: function (onFulfilled, onRejected) { var curr = this; var next = new api(); /* [Promises/A+ 2.2.7] */ curr.onFulfilled.push( resolver(onFulfilled, next, "fulfill")); /* [Promises/A+ 2.2.2/2.2.6] */ curr.onRejected.push( resolver(onRejected, next, "reject" )); /* [Promises/A+ 2.2.3/2.2.6] */ execute(curr); return next.proxy; /* [Promises/A+ 2.2.7, 3.3] */ } }; /* deliver an action */ var deliver = function (curr, state, name, value) { if (curr.state === STATE_PENDING) { curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */ curr[name] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */ execute(curr); } return curr; }; /* execute all handlers */ var execute = function (curr) { if (curr.state === STATE_FULFILLED) execute_handlers(curr, "onFulfilled", curr.fulfillValue); else if (curr.state === STATE_REJECTED) execute_handlers(curr, "onRejected", curr.rejectReason); }; /* execute particular set of handlers */ var execute_handlers = function (curr, name, value) { /* global process: true */ /* global setImmediate: true */ /* global setTimeout: true */ /* short-circuit processing */ if (curr[name].length === 0) return; /* iterate over all handlers, exactly once */ var handlers = curr[name]; curr[name] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */ var func = function () { for (var i = 0; i < handlers.length; i++) handlers[i](value); /* [Promises/A+ 2.2.5] */ }; /* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */ if (typeof process === "object" && typeof process.nextTick === "function") process.nextTick(func); else if (typeof setImmediate === "function") setImmediate(func); else setTimeout(func, 0); }; /* generate a resolver function */ var resolver = function (cb, next, method) { return function (value) { if (typeof cb !== "function") /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */ next[method].call(next, value); /* [Promises/A+ 2.2.7.3, 2.2.7.4] */ else { var result; try { result = cb(value); } /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */ catch (e) { next.reject(e); /* [Promises/A+ 2.2.7.2] */ return; } resolve(next, result); /* [Promises/A+ 2.2.7.1] */ } }; }; /* "Promise Resolution Procedure" */ /* [Promises/A+ 2.3] */ var resolve = function (promise, x) { /* sanity check arguments */ /* [Promises/A+ 2.3.1] */ if (promise === x || promise.proxy === x) { promise.reject(new TypeError("cannot resolve promise with itself")); return; } /* surgically check for a "then" method (mainly to just call the "getter" of "then" only once) */ var then; if ((typeof x === "object" && x !== null) || typeof x === "function") { try { then = x.then; } /* [Promises/A+ 2.3.3.1, 3.5] */ catch (e) { promise.reject(e); /* [Promises/A+ 2.3.3.2] */ return; } } /* handle own Thenables [Promises/A+ 2.3.2] and similar "thenables" [Promises/A+ 2.3.3] */ if (typeof then === "function") { var resolved = false; try { /* call retrieved "then" method */ /* [Promises/A+ 2.3.3.3] */ then.call(x, /* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */ function (y) { if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */ if (y === x) /* [Promises/A+ 3.6] */ promise.reject(new TypeError("circular thenable chain")); else resolve(promise, y); }, /* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */ function (r) { if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */ promise.reject(r); } ); } catch (e) { if (!resolved) /* [Promises/A+ 2.3.3.3.3] */ promise.reject(e); /* [Promises/A+ 2.3.3.3.4] */ } return; } /* handle other values */ promise.fulfill(x); /* [Promises/A+ 2.3.4, 2.3.3.4] */ }; /* export API */ return api; })(), //jscs:enable // Event // A contructor superclass for adding event menthods, on, off, emit. Event: function() { var separator = /[\s\,]+/; // If this doesn't support getPrototype then we can't get prototype.events of the parent // So lets get the current instance events, and add those to a parent property this.parent = { events: this.events, findEvents: this.findEvents, parent: this.parent, utils: this.utils }; this.events = {}; // On, subscribe to events // @param evt string // @param callback function this.on = function(evt, callback) { if (callback && typeof (callback) === 'function') { var a = evt.split(separator); for (var i = 0; i < a.length; i++) { // Has this event already been fired on this instance? this.events[a[i]] = [callback].concat(this.events[a[i]] || []); } } return this; }; // Off, unsubscribe to events // @param evt string // @param callback function this.off = function(evt, callback) { this.findEvents(evt, function(name, index) { if (!callback || this.events[name][index] === callback) { this.events[name][index] = null; } }); return this; }; // Emit // Triggers any subscribed events this.emit = function(evt /*, data, ... */) { // Get arguments as an Array, knock off the first one var args = Array.prototype.slice.call(arguments, 1); args.push(evt); // Handler var handler = function(name, index) { // Replace the last property with the event name args[args.length - 1] = (name === '*' ? evt : name); // Trigger this.events[name][index].apply(this, args); }; // Find the callbacks which match the condition and call var _this = this; while (_this && _this.findEvents) { // Find events which match _this.findEvents(evt + ',*', handler); _this = _this.parent; } return this; }; // // Easy functions this.emitAfter = function() { var _this = this; var args = arguments; setTimeout(function() { _this.emit.apply(_this, args); }, 0); return this; }; this.findEvents = function(evt, callback) { var a = evt.split(separator); for (var name in this.events) {if (this.events.hasOwnProperty(name)) { if (a.indexOf(name) > -1) { for (var i = 0; i < this.events[name].length; i++) { // Does the event handler exist? if (this.events[name][i]) { // Emit on the local instance of this callback.call(this, name, i); } } } }} }; return this; }, // Global Events // Attach the callback to the window object // Return its unique reference globalEvent: function(callback, guid) { // If the guid has not been supplied then create a new one. guid = guid || '_hellojs_' + parseInt(Math.random() * 1e12, 10).toString(36); // Define the callback function window[guid] = function() { // Trigger the callback try { if (callback.apply(this, arguments)) { delete window[guid]; } } catch (e) { console.error(e); } }; return guid; }, // Trigger a clientside popup // This has been augmented to support PhoneGap popup: function(url, redirectUri, options) { var documentElement = document.documentElement; // Multi Screen Popup Positioning (http://stackoverflow.com/a/16861050) // Credit: http://www.xtf.dk/2011/08/center-new-popup-window-even-on.html // Fixes dual-screen position Most browsers Firefox if (options.height) { var dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top; var height = screen.height || window.innerHeight || documentElement.clientHeight; options.top = (options.top) ? options.top : parseInt((height - options.height) / 2, 10) + dualScreenTop; } if (options.width) { var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left; var width = screen.width || window.innerWidth || documentElement.clientWidth; options.left = (options.left) ? options.left : parseInt((width - options.width) / 2, 10) + dualScreenLeft; } // Convert options into an array var optionsArray = []; Object.keys(options).forEach(function(name) { var value = options[name]; optionsArray.push(name + (value !== null ? '=' + value : '')); }); // Call the open() function with the initial path // // OAuth redirect, fixes URI fragments from being lost in Safari // (URI Fragments within 302 Location URI are lost over HTTPS) // Loading the redirect.html before triggering the OAuth Flow seems to fix it. // // Firefox decodes URL fragments when calling location.hash. // - This is bad if the value contains break points which are escaped // - Hence the url must be encoded twice as it contains breakpoints. if (navigator.userAgent.indexOf('Safari') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) { url = redirectUri + '#oauth_redirect=' + encodeURIComponent(encodeURIComponent(url)); } var popup = window.open( url, '_blank', optionsArray.join(',') ); if (popup && popup.focus) { popup.focus(); } return popup; }, // OAuth and API response handler responseHandler: function(window, parent) { var _this = this; var p; var location = window.location; // Is this an auth relay message which needs to call the proxy? p = _this.param(location.search); // OAuth2 or OAuth1 server response? if (p && p.state && (p.code || p.oauth_token)) { var state = JSON.parse(p.state); // Add this path as the redirect_uri p.redirect_uri = state.redirect_uri || location.href.replace(/[\?\#].*$/, ''); // Redirect to the host var path = _this.qs(state.oauth_proxy, p); location.assign(path); return; } // Save session, from redirected authentication // #access_token has come in? // // FACEBOOK is returning auth errors within as a query_string... thats a stickler for consistency. // SoundCloud is the state in the querystring and the token in the hashtag, so we'll mix the two together p = _this.merge(_this.param(location.search || ''), _this.param(location.hash || '')); // If p.state if (p && 'state' in p) { // Remove any addition information // E.g. p.state = 'facebook.page'; try { var a = JSON.parse(p.state); _this.extend(p, a); } catch (e) { var stateDecoded = decodeURIComponent(p.state); try { var b = JSON.parse(stateDecoded); _this.extend(p, b); } catch (e) { console.error('Could not decode state parameter'); } } // Access_token? if (('access_token' in p && p.access_token) && p.network) { if (!p.expires_in || parseInt(p.expires_in, 10) === 0) { // If p.expires_in is unset, set to 0 p.expires_in = 0; } p.expires_in = parseInt(p.expires_in, 10); p.expires = ((new Date()).getTime() / 1e3) + (p.expires_in || (60 * 60 * 24 * 365)); // Lets use the "state" to assign it to one of our networks authCallback(p, window, parent); } // Error=? // &error_description=? // &state=? else if (('error' in p && p.error) && p.network) { p.error = { code: p.error, message: p.error_message || p.error_description }; // Let the state handler handle it authCallback(p, window, parent); } // API call, or a cancelled login // Result is serialized JSON string else if (p.callback && p.callback in parent) { // Trigger a function in the parent var res = 'result' in p && p.result ? JSON.parse(p.result) : false; // Trigger the callback on the parent callback(parent, p.callback)(res); closeWindow(); } // If this page is still open if (p.page_uri) { location.assign(p.page_uri); } } // OAuth redirect, fixes URI fragments from being lost in Safari // (URI Fragments within 302 Location URI are lost over HTTPS) // Loading the redirect.html before triggering the OAuth Flow seems to fix it. else if ('oauth_redirect' in p) { location.assign(decodeURIComponent(p.oauth_redirect)); return; } // Trigger a callback to authenticate function authCallback(obj, window, parent) { var cb = obj.callback; var network = obj.network; // Trigger the callback on the parent _this.store(network, obj); // If this is a page request it has no parent or opener window to handle callbacks if (('display' in obj) && obj.display === 'page') { return; } // Remove from session object if (parent && cb && cb in parent) { try { delete obj.callback; } catch (e) {} // Update store _this.store(network, obj); // Call the globalEvent function on the parent // It's safer to pass back a string to the parent, // Rather than an object/array (better for IE8) var str = JSON.stringify(obj); try { callback(parent, cb)(str); } catch (e) { // Error thrown whilst executing parent callback } } closeWindow(); } function callback(parent, callbackID) { if (callbackID.indexOf('_hellojs_') !== 0) { return function() { throw 'Could not execute callback ' + callbackID; }; } return parent[callbackID]; } function closeWindow() { if (window.frameElement) { // Inside an iframe, remove from parent parent.document.body.removeChild(window.frameElement); } else { // Close this current window try { window.close(); } catch (e) {} // IOS bug wont let us close a popup if still loading if (window.addEventListener) { window.addEventListener('load', function() { window.close(); }); } } } } }); // Events // Extend the hello object with its own event instance hello.utils.Event.call(hello); /////////////////////////////////// // Monitoring session state // Check for session changes /////////////////////////////////// (function(hello) { // Monitor for a change in state and fire var oldSessions = {}; // Hash of expired tokens var expired = {}; // Listen to other triggers to Auth events, use these to update this hello.on('auth.login, auth.logout', function(auth) { if (auth && typeof (auth) === 'object' && auth.network) { oldSessions[auth.network] = hello.utils.store(auth.network) || {}; } }); (function self() { var CURRENT_TIME = ((new Date()).getTime() / 1e3); var emit = function(eventName) { hello.emit('auth.' + eventName, { network: name, authResponse: session }); }; // Loop through the services for (var name in hello.services) {if (hello.services.hasOwnProperty(name)) { if (!hello.services[name].id) { // We haven't attached an ID so dont listen. continue; } // Get session var session = hello.utils.store(name) || {}; var provider = hello.services[name]; var oldSess = oldSessions[name] || {}; // Listen for globalEvents that did not get triggered from the child if (session && 'callback' in session) { // To do remove from session object... var cb = session.callback; try { delete session.callback; } catch (e) {} // Update store // Removing the callback hello.utils.store(name, session); // Emit global events try { window[cb](session); } catch (e) {} } // Refresh token if (session && ('expires' in session) && session.expires < CURRENT_TIME) { // If auto refresh is possible // Either the browser supports var refresh = provider.refresh || session.refresh_token; // Has the refresh been run recently? if (refresh && (!(name in expired) || expired[name] < CURRENT_TIME)) { // Try to resignin hello.emit('notice', name + ' has expired trying to resignin'); hello.login(name, {display: 'none', force: false}); // Update expired, every 10 minutes expired[name] = CURRENT_TIME + 600; } // Does this provider not support refresh else if (!refresh && !(name in expired)) { // Label the event emit('expired'); expired[name] = true; } // If session has expired then we dont want to store its value until it can be established that its been updated continue; } // Has session changed? else if (oldSess.access_token === session.access_token && oldSess.expires === session.expires) { continue; } // Access_token has been removed else if (!session.access_token && oldSess.access_token) { emit('logout'); } // Access_token has been created else if (session.access_token && !oldSess.access_token) { emit('login'); } // Access_token has been updated else if (session.expires !== oldSess.expires) { emit('update'); } // Updated stored session oldSessions[name] = session; // Remove the expired flags if (name in expired) { delete expired[name]; } }} // Check error events setTimeout(self, 1000); })(); })(hello); // EOF CORE lib ////////////////////////////////// ///////////////////////////////////////// // API // @param path string // @param query object (optional) // @param method string (optional) // @param data object (optional) // @param timeout integer (optional) // @param callback function (optional) hello.api = function() { // Shorthand var _this = this; var utils = _this.utils; var error = utils.error; // Construct a new Promise object var promise = utils.Promise(); // Arguments var p = utils.args({path: 's!', query: 'o', method: 's', data: 'o', timeout: 'i', callback: 'f'}, arguments); // Method p.method = (p.method || 'get').toLowerCase(); // Headers p.headers = p.headers || {}; // Query p.query = p.query || {}; // If get, put all parameters into query if (p.method === 'get' || p.method === 'delete') { utils.extend(p.query, p.data); p.data = {}; } var data = p.data = p.data || {}; // Completed event callback promise.then(p.callback, p.callback); // Remove the network from path, e.g. facebook:/me/friends // Results in { network : facebook, path : me/friends } if (!p.path) { return promise.reject(error('invalid_path', 'Missing the path parameter from the request')); } p.path = p.path.replace(/^\/+/, ''); var a = (p.path.split(/[\/\:]/, 2) || [])[0].toLowerCase(); if (a in _this.services) { p.network = a; var reg = new RegExp('^' + a + ':?\/?'); p.path = p.path.replace(reg, ''); } // Network & Provider // Define the network that this request is made for p.network = _this.settings.default_service = p.network || _this.settings.default_service; var o = _this.services[p.network]; // INVALID // Is there no service by the given network name? if (!o) { return promise.reject(error('invalid_network', 'Could not match the service requested: ' + p.network)); } // PATH // As long as the path isn't flagged as unavaiable, e.g. path == false if (!(!(p.method in o) || !(p.path in o[p.method]) || o[p.method][p.path] !== false)) { return promise.reject(error('invalid_path', 'The provided path is not available on the selected network')); } // PROXY // OAuth1 calls always need a proxy if (!p.oauth_proxy) { p.oauth_proxy = _this.settings.oauth_proxy; } if (!('proxy' in p)) { p.proxy = p.oauth_proxy && o.oauth && parseInt(o.oauth.version, 10) === 1; } // TIMEOUT // Adopt timeout from global settings by default if (!('timeout' in p)) { p.timeout = _this.settings.timeout; } // Format response // Whether to run the raw response through post processing. if (!('formatResponse' in p)) { p.formatResponse = true; } // Get the current session // Append the access_token to the query p.authResponse = _this.getAuthResponse(p.network); if (p.authResponse && p.authResponse.access_token) { p.query.access_token = p.authResponse.access_token; } var url = p.path; var m; // Store the query as options // This is used to populate the request object before the data is augmented by the prewrap handlers. p.options = utils.clone(p.query); // Clone the data object // Prevent this script overwriting the data of the incoming object. // Ensure that everytime we run an iteration the callbacks haven't removed some data p.data = utils.clone(data); // URL Mapping // Is there a map for the given URL? var actions = o[{'delete': 'del'}[p.method] || p.method] || {}; // Extrapolate the QueryString // Provide a clean path // Move the querystring into the data if (p.method === 'get') { var query = url.split(/[\?#]/)[1]; if (query) { utils.extend(p.query, utils.param(query)); // Remove the query part from the URL url = url.replace(/\?.*?(#|$)/, '$1'); } } // Is the hash fragment defined if ((m = url.match(/#(.+)/, ''))) { url = url.split('#')[0]; p.path = m[1]; } else if (url in actions) { p.path = url; url = actions[url]; } else if ('default' in actions) { url = actions['default']; } // Redirect Handler // This defines for the Form+Iframe+Hash hack where to return the results too. p.redirect_uri = _this.settings.redirect_uri; // Define FormatHandler // The request can be procesed in a multitude of ways // Here's the options - depending on the browser and endpoint p.xhr = o.xhr; p.jsonp = o.jsonp; p.form = o.form; // Make request if (typeof (url) === 'function') { // Does self have its own callback? url(p, getPath); } else { // Else the URL is a string getPath(url); } return promise.proxy; // If url needs a base // Wrap everything in function getPath(url) { // Format the string if it needs it url = url.replace(/\@\{([a-z\_\-]+)(\|.*?)?\}/gi, function(m, key, defaults) { var val = defaults ? defaults.replace(/^\|/, '') : ''; if (key in p.query) { val = p.query[key]; delete p.query[key]; } else if (p.data && key in p.data) { val = p.data[key]; delete p.data[key]; } else if (!defaults) { promise.reject(error('missing_attribute', 'The attribute ' + key + ' is missing from the request')); } return val; }); // Add base if (!url.match(/^https?:\/\//)) { url = o.base + url; } // Define the request URL p.url = url; // Make the HTTP request with the curated request object // CALLBACK HANDLER // @ response object // @ statusCode integer if available utils.request(p, function(r, headers) { // Is this a raw response? if (!p.formatResponse) { // Bad request? error statusCode or otherwise contains an error response vis JSONP? if (typeof headers === 'object' ? (headers.statusCode >= 400) : (typeof r === 'object' && 'error' in r)) { promise.reject(r); } else { promise.fulfill(r); } return; } // Should this be an object if (r === true) { r = {success:true}; } else if (!r) { r = {}; } // The delete callback needs a better response if (p.method === 'delete') { r = (!r || utils.isEmpty(r)) ? {success:true} : r; } // FORMAT RESPONSE? // Does self request have a corresponding formatter if (o.wrap && ((p.path in o.wrap) || ('default' in o.wrap))) { var wrap = (p.path in o.wrap ? p.path : 'default'); var time = (new Date()).getTime(); // FORMAT RESPONSE var b = o.wrap[wrap](r, headers, p); // Has the response been utterly overwritten? // Typically self augments the existing object.. but for those rare occassions if (b) { r = b; } } // Is there a next_page defined in the response? if (r && 'paging' in r && r.paging.next) { // Add the relative path if it is missing from the paging/next path if (r.paging.next[0] === '?') { r.paging.next = p.path + r.paging.next; } // The relative path has been defined, lets markup the handler in the HashFragment else { r.paging.next += '#' + p.path; } } // Dispatch to listeners // Emit events which pertain to the formatted response if (!r || 'error' in r) { promise.reject(r); } else { promise.fulfill(r); } }); } }; // API utilities hello.utils.extend(hello.utils, { // Make an HTTP request request: function(p, callback) { var _this = this; var error = _this.error; // This has to go through a POST request if (!_this.isEmpty(p.data) && !('FileList' in window) && _this.hasBinary(p.data)) { // Disable XHR and JSONP p.xhr = false; p.jsonp = false; } // Check if the browser and service support CORS var cors = this.request_cors(function() { // If it does then run this... return ((p.xhr === undefined) || (p.xhr && (typeof (p.xhr) !== 'function' || p.xhr(p, p.query)))); }); if (cors) { formatUrl(p, function(url) { var x = _this.xhr(p.method, url, p.headers, p.data, callback); x.onprogress = p.onprogress || null; // Windows Phone does not support xhr.upload, see #74 // Feature detect if (x.upload && p.onuploadprogress) { x.upload.onprogress = p.onuploadprogress; } }); return; } // Clone the query object // Each request modifies the query object and needs to be tared after each one. var _query = p.query; p.query = _this.clone(p.query); // Assign a new callbackID p.callbackID = _this.globalEvent(); // JSONP if (p.jsonp !== false) { // Clone the query object p.query.callback = p.callbackID; // If the JSONP is a function then run it if (typeof (p.jsonp) === 'function') { p.jsonp(p, p.query); } // Lets use JSONP if the method is 'get' if (p.method === 'get') { formatUrl(p, function(url) { _this.jsonp(url, callback, p.callbackID, p.timeout); }); return; } else { // It's not compatible reset query p.query = _query; } } // Otherwise we're on to the old school, iframe hacks and JSONP if (p.form !== false) { // Add some additional query parameters to the URL // We're pretty stuffed if the endpoint doesn't like these p.query.redirect_uri = p.redirect_uri; p.query.state = JSON.stringify({callback:p.callbackID}); var opts; if (typeof (p.form) === 'function') { // Format the request opts = p.form(p, p.query); } if (p.method === 'post' && opts !== false) { formatUrl(p, function(url) { _this.post(url, p.data, opts, callback, p.callbackID, p.timeout); }); return; } } // None of the methods were successful throw an error callback(error('invalid_request', 'There was no mechanism for handling this request')); return; // Format URL // Constructs the request URL, optionally wraps the URL through a call to a proxy server // Returns the formatted URL function formatUrl(p, callback) { // Are we signing the request? var sign; // OAuth1 // Remove the token from the query before signing if (p.authResponse && p.authResponse.oauth && parseInt(p.authResponse.oauth.version, 10) === 1) { // OAUTH SIGNING PROXY sign = p.query.access_token; // Remove the access_token delete p.query.access_token; // Enfore use of Proxy p.proxy = true; } // POST body to querystring if (p.data && (p.method === 'get' || p.method === 'delete')) { // Attach the p.data to the querystring. _this.extend(p.query, p.data); p.data = null; } // Construct the path var path = _this.qs(p.url, p.query); // Proxy the request through a server // Used for signing OAuth1 // And circumventing services without Access-Control Headers if (p.proxy) { // Use the proxy as a path path = _this.qs(p.oauth_proxy, { path: path, access_token: sign || '', // This will prompt the request to be signed as though it is OAuth1 then: p.proxy_response_type || (p.method.toLowerCase() === 'get' ? 'redirect' : 'proxy'), method: p.method.toLowerCase(), suppress_response_codes: true }); } callback(path); } }, // Test whether the browser supports the CORS response request_cors: function(callback) { return 'withCredentials' in new XMLHttpRequest() && callback(); }, // Return the type of DOM object domInstance: function(type, data) { var test = 'HTML' + (type || '').replace( /^[a-z]/, function(m) { return m.toUpperCase(); } ) + 'Element'; if (!data) { return false; } if (window[test]) { return data instanceof window[test]; } else if (window.Element) { return data instanceof window.Element && (!type || (data.tagName && data.tagName.toLowerCase() === type)); } else { return (!(data instanceof Object || data instanceof Array || data instanceof String || data instanceof Number) && data.tagName && data.tagName.toLowerCase() === type); } }, // Create a clone of an object clone: function(obj) { // Does not clone DOM elements, nor Binary data, e.g. Blobs, Filelists if (obj === null || typeof (obj) !== 'object' || obj instanceof Date || 'nodeName' in obj || this.isBinary(obj) || (typeof FormData === 'function' && obj instanceof FormData)) { return obj; } if (Array.isArray(obj)) { // Clone each item in the array return obj.map(this.clone.bind(this)); } // But does clone everything else. var clone = {}; for (var x in obj) { clone[x] = this.clone(obj[x]); } return clone; }, // XHR: uses CORS to make requests xhr: function(method, url, headers, data, callback) { var r = new XMLHttpRequest(); var error = this.error; // Binary? var binary = false; if (method === 'blob') { binary = method; method = 'GET'; } method = method.toUpperCase(); // Xhr.responseType 'json' is not supported in any of the vendors yet. r.onload = function(e) { var json = r.response; try { json = JSON.parse(r.responseText); } catch (_e) { if (r.status === 401) { json = error('access_denied', r.statusText); } } var headers = headersToJSON(r.getAllResponseHeaders()); headers.statusCode = r.status; callback(json || (method === 'GET' ? error('empty_response', 'Could not get resource') : {}), headers); }; r.onerror = function(e) { var json = r.responseText; try { json = JSON.parse(r.responseText); } catch (_e) {} callback(json || error('access_denied', 'Could not get resource')); }; var x; // Should we add the query to the URL? if (method === 'GET' || method === 'DELETE') { data = null; } else if (data && typeof (data) !== 'string' && !(data instanceof FormData) && !(data instanceof File) && !(data instanceof Blob)) { // Loop through and add formData var f = new FormData(); for (x in data) if (data.hasOwnProperty(x)) { if (data[x] instanceof HTMLInputElement) { if ('files' in data[x] && data[x].files.length > 0) { f.append(x, data[x].files[0]); } } else if (data[x] instanceof Blob) { f.append(x, data[x], data.name); } else { f.append(x, data[x]); } } data = f; } // Open the path, async r.open(method, url, true); if (binary) { if ('responseType' in r) { r.responseType = binary; } else { r.overrideMimeType('text/plain; charset=x-user-defined'); } } // Set any bespoke headers if (headers) { for (x in headers) { r.setRequestHeader(x, headers[x]); } } r.send(data); return r; // Headers are returned as a string function headersToJSON(s) { var r = {}; var reg = /([a-z\-]+):\s?(.*);?/gi; var m; while ((m = reg.exec(s))) { r[m[1]] = m[2]; } return r; } }, // JSONP // Injects a script tag into the DOM to be executed and appends a callback function to the window object // @param string/function pathFunc either a string of the URL or a callback function pathFunc(querystringhash, continueFunc); // @param function callback a function to call on completion; jsonp: function(url, callback, callbackID, timeout) { var _this = this; var error = _this.error; // Change the name of the callback var bool = 0; var head = document.getElementsByTagName('head')[0]; var operaFix; var result = error('server_error', 'server_error'); var cb = function() { if (!(bool++)) { window.setTimeout(function() { callback(result); head.removeChild(script); }, 0); } }; // Add callback to the window object callbackID = _this.globalEvent(function(json) { result = json; return true; // Mark callback as done }, callbackID); // The URL is a function for some cases and as such // Determine its value with a callback containing the new parameters of this function. url = url.replace(new RegExp('=\\?(&|$)'), '=' + callbackID + '$1'); // Build script tag var script = _this.append('script', { id: callbackID, name: callbackID, src: url, async: true, onload: cb, onerror: cb, onreadystatechange: function() { if (/loaded|complete/i.test(this.readyState)) { cb(); } } }); // Opera fix error // Problem: If an error occurs with script loading Opera fails to trigger the script.onerror handler we specified // // Fix: // By setting the request to synchronous we can trigger the error handler when all else fails. // This action will be ignored if we've already called the callback handler "cb" with a successful onload event if (window.navigator.userAgent.toLowerCase().indexOf('opera') > -1) { operaFix = _this.append('script', { text: 'document.getElementById(\'' + callbackID + '\').onerror();' }); script.async = false; } // Add timeout if (timeout) { window.setTimeout(function() { result = error('timeout', 'timeout'); cb(); }, timeout); } // TODO: add fix for IE, // However: unable recreate the bug of firing off the onreadystatechange before the script content has been executed and the value of "result" has been defined. // Inject script tag into the head element head.appendChild(script); // Append Opera Fix to run after our script if (operaFix) { head.appendChild(operaFix); } }, // Post // Send information to a remote location using the post mechanism // @param string uri path // @param object data, key value data to send // @param function callback, function to execute in response post: function(url, data, options, callback, callbackID, timeout) { var _this = this; var error = _this.error; var doc = document; // This hack needs a form var form = null; var reenableAfterSubmit = []; var newform; var i = 0; var x = null; var bool = 0; var cb = function(r) { if (!(bool++)) { callback(r); } }; // What is the name of the callback to contain // We'll also use this to name the iframe _this.globalEvent(cb, callbackID); // Build the iframe window var win; try { // IE7 hack, only lets us define the name here, not later. win = doc.createElement('