diff --git a/Makefile b/Makefile index c0b404c91..2c94d0fa9 100644 --- a/Makefile +++ b/Makefile @@ -149,6 +149,8 @@ watch: stamp-bundler BUILDS = dist/converse.js \ dist/converse.min.js \ + dist/inverse.js \ + dist/inverse.min.js \ dist/converse-mobile.js \ dist/converse-mobile.min.js \ dist/converse-muc-embedded.js \ @@ -162,6 +164,10 @@ dist/converse.min.js: src locale node_modules *.js $(RJS) -o src/build.js include=converse out=dist/converse.min.js dist/converse.js: src locale node_modules *.js $(RJS) -o src/build.js include=converse out=dist/converse.js optimize=none +dist/inverse.js: src locale node_modules *.js + $(RJS) -o src/build-inverse.js include=inverse out=dist/inverse.js optimize=none +dist/inverse.min.js: src locale node_modules *.js + $(RJS) -o src/build-inverse.js include=inverse out=dist/inverse.min.js dist/converse-no-jquery.min.js: src locale node_modules *.js $(RJS) -o src/build.js include=converse wrap.endFile=end-no-jquery.frag exclude=jquery exclude=jquery.noconflict out=dist/converse-no-jquery.min.js dist/converse-no-jquery.js: src locale node_modules *.js diff --git a/dist/converse.js b/dist/converse.js index ddd5172d9..58e4bdef1 100644 --- a/dist/converse.js +++ b/dist/converse.js @@ -29536,9 +29536,1062 @@ define('jquery.noconflict',['jquery'], function (jq) { /*global define */ define('lodash.noconflict',['lodash'], function (_) { + if (!_.isUndefined(require) && !_.isUndefined(require.s)) { + /* XXX: This is a hack to make sure that the compiled templates have + * access to the _ object. + * + * Otherwise we sometimes get errors like this: + * + * TypeError: Cannot read property 'escape' of undefined + * at eval (./src/templates/chatroom_sidebar.html:6) + */ + var lodashLoader = require.s.contexts._.config.lodashLoader; + lodashLoader.templateSettings.imports = { '_': _ }; + require.config({'lodashLoader': lodashLoader}); + } return _.noConflict(); }); +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define('lodash.converter',[], factory); + else if(typeof exports === 'object') + exports["fp"] = factory(); + else + root["fp"] = factory(); +})(this, 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') { + _ = 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) { + return function() { + var value = arguments[0]; + return isArray(value) + ? castArray(cloneArray(value)) + : castArray.apply(undefined, arguments); + }; + }, + 'iteratee': function(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) { + 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) { + return function(n) { + var arity = n < 0 ? 1 : (toInteger(n) + 1); + return curry(nthArg(n), arity); + }; + }, + 'rearg': function(rearg) { + return function(func, indexes) { + var arity = indexes ? indexes.length : 0; + return curry(rearg(func, indexes), arity); + }; + }, + 'runInContext': function(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 = {}; + + +/***/ } +/******/ ]) +}); +; if (!String.prototype.endsWith) { String.prototype.endsWith = function (searchString, position) { var subjectString = this.toString(); @@ -31246,7 +32299,7 @@ define('text!zh',[],function () { return '{\n "domain": "converse",\n "local */ /*global define */ (function (root, factory) { - define("locales", ['jed', + define('locales',['jed', 'text!af', 'text!ca', 'text!de', @@ -38048,6 +39101,18 @@ return __p return utils.detectLocale(isSupportedByLibrary) || 'en'; }; + utils.isOfType = function (type, item) { + return item.get('type') == type; + } + + utils.isInstance = function (type, item) { + return item instanceof type; + }; + + utils.getAttribute = function (key, item) { + return item.get(key); + }; + utils.contains.not = function (attr, query) { return function (item) { return !(utils.contains(attr, query)(item)); @@ -44456,19 +45521,20 @@ require(["strophe-polyfill"]); // overriding method is called. This is done to enable // chaining of plugin methods, all the way up to the // original method. - wrappedOverride: function wrappedOverride(key, value, super_method) { + wrappedOverride: function wrappedOverride(key, value, super_method, default_super) { if (typeof super_method === "function") { if (typeof this.__super__ === "undefined") { /* We're not on the context of the plugged object. - * This can happen when the overridden method is called via - * an event handler. In this case, we simply tack on the - * __super__ obj. - */ - this.__super__ = {}; + * This can happen when the overridden method is called via + * an event handler or when it's a constructor. + * + * In this case, we simply tack on the __super__ obj. + */ + this.__super__ = default_super; } this.__super__[key] = super_method.bind(this); } - return value.apply(this, _.drop(arguments, 3)); + return value.apply(this, _.drop(arguments, 4)); }, // `_overrideAttribute` overrides an attribute on the original object @@ -44489,7 +45555,10 @@ require(["strophe-polyfill"]); _overrideAttribute: function _overrideAttribute(key, plugin) { var value = plugin.overrides[key]; if (typeof value === "function") { - var wrapped_function = _.partial(this.wrappedOverride, key, value, this.plugged[key]); + var default_super = {}; + default_super[this.name] = this.plugged; + + var wrapped_function = _.partial(this.wrappedOverride, key, value, this.plugged[key], default_super); this.plugged[key] = wrapped_function; } else { this.plugged[key] = value; @@ -44511,7 +45580,10 @@ require(["strophe-polyfill"]); // overriding method is called. This is done to enable // chaining of plugin methods, all the way up to the // original method. - var wrapped_function = _.partial(that.wrappedOverride, key, value, obj.prototype[key]); + var default_super = {}; + default_super[that.name] = that.plugged; + + var wrapped_function = _.partial(that.wrappedOverride, key, value, obj.prototype[key], default_super); obj.prototype[key] = wrapped_function; } else { obj.prototype[key] = value; @@ -47224,6 +48296,7 @@ return Backbone.BrowserStorage; define('converse-core',["sizzle", "jquery.noconflict", "lodash.noconflict", + "lodash.converter", "polyfill", "utils", "moment_with_locales", @@ -47234,12 +48307,18 @@ return Backbone.BrowserStorage; "backbone.browserStorage", "backbone.overview", ], factory); -}(this, function (sizzle, $, _, polyfill, utils, moment, Strophe, pluggable, Backbone) { +}(this, function ( + sizzle, $, _, lodashConverter, polyfill, + utils, moment, Strophe, pluggable, Backbone) { + /* Cannot use this due to Safari bug. * See https://github.com/jcbrand/converse.js/issues/196 */ // "use strict"; + // Create the FP (functional programming) version of lodash + var fp = lodashConverter(_.runInContext()); + // Strophe globals var $build = Strophe.$build; var $iq = Strophe.$iq; @@ -47253,8 +48332,9 @@ return Backbone.BrowserStorage; * config of requirejs-tpl in main.js). This one is for normal inline templates. */ _.templateSettings = { - evaluate : /\{\[([\s\S]+?)\]\}/g, - interpolate : /\{\{([\s\S]+?)\}\}/g + 'escape': /\{\{\{([\s\S]+?)\}\}\}/g, + 'evaluate': /\{\[([\s\S]+?)\]\}/g, + 'interpolate': /\{\{([\s\S]+?)\}\}/g }; var _converse = {}; @@ -47293,6 +48373,7 @@ return Backbone.BrowserStorage; 'converse-otr', 'converse-ping', 'converse-register', + 'converse-roomslist', 'converse-rosterview', 'converse-vcard' ]; @@ -47398,9 +48479,11 @@ return Backbone.BrowserStorage; Strophe.addNamespace('CSI', 'urn:xmpp:csi:0'); Strophe.addNamespace('DELAY', 'urn:xmpp:delay'); Strophe.addNamespace('HINTS', 'urn:xmpp:hints'); + Strophe.addNamespace('MAM', 'urn:xmpp:mam:0'); Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick'); Strophe.addNamespace('PUBSUB', 'http://jabber.org/protocol/pubsub'); Strophe.addNamespace('ROSTERX', 'http://jabber.org/protocol/rosterx'); + Strophe.addNamespace('RSM', 'http://jabber.org/protocol/rsm'); Strophe.addNamespace('XFORM', 'jabber:x:data'); // Instance level constants @@ -47732,26 +48815,21 @@ return Backbone.BrowserStorage; } }; - this.updateMsgCounter = function () { - if (this.msg_counter > 0) { - if (document.title.search(/^Messages \(\d+\) /) === -1) { - document.title = "Messages (" + this.msg_counter + ") " + document.title; - } else { - document.title = document.title.replace(/^Messages \(\d+\) /, "Messages (" + this.msg_counter + ") "); - } - } else if (document.title.search(/^Messages \(\d+\) /) !== -1) { - document.title = document.title.replace(/^Messages \(\d+\) /, ""); - } - }; - this.incrementMsgCounter = function () { this.msg_counter += 1; - this.updateMsgCounter(); + var unreadMsgCount = this.msg_counter; + if (document.title.search(/^Messages \(\d+\) /) === -1) { + document.title = "Messages (" + unreadMsgCount + ") " + document.title; + } else { + document.title = document.title.replace(/^Messages \(\d+\) /, "Messages (" + unreadMsgCount + ") "); + } }; this.clearMsgCounter = function () { this.msg_counter = 0; - this.updateMsgCounter(); + if (document.title.search(/^Messages \(\d+\) /) !== -1) { + document.title = document.title.replace(/^Messages \(\d+\) /, ""); + } }; this.initStatus = function () { @@ -47818,6 +48896,7 @@ return Backbone.BrowserStorage; _converse.clearMsgCounter(); } _converse.windowState = state; + _converse.emit('windowStateChanged', {state: state}) }; this.registerGlobalEventHandlers = function () { @@ -47986,6 +49065,17 @@ return Backbone.BrowserStorage; this.RosterContact = Backbone.Model.extend({ + defaults: { + 'bookmarked': false, + 'chat_state': undefined, + 'chat_status': 'offline', + 'groups': [], + 'image': DEFAULT_IMAGE, + 'image_type': DEFAULT_IMAGE_TYPE, + 'num_unread': 0, + 'status': '', + }, + initialize: function (attributes) { var jid = attributes.jid; var bare_jid = Strophe.getBareJidFromJid(jid).toLowerCase(); @@ -47995,13 +49085,8 @@ return Backbone.BrowserStorage; 'id': bare_jid, 'jid': bare_jid, 'fullname': bare_jid, - 'chat_status': 'offline', 'user_id': Strophe.getNodeFromJid(jid), - 'resources': resource ? {'resource':0} : {}, - 'groups': [], - 'image_type': DEFAULT_IMAGE_TYPE, - 'image': DEFAULT_IMAGE, - 'status': '' + 'resources': resource ? {resource :0} : {}, }, attributes)); this.on('destroy', function () { this.removeFromRoster(); }.bind(this)); @@ -48561,6 +49646,14 @@ return Backbone.BrowserStorage; this.ChatBox = Backbone.Model.extend({ + defaults: { + 'type': 'chatbox', + 'bookmarked': false, + 'chat_state': undefined, + 'num_unread': 0, + 'url': '' + }, + initialize: function () { this.messages = new _converse.Messages(); this.messages.browserStorage = new Backbone.BrowserStorage[_converse.message_storage]( @@ -48569,10 +49662,7 @@ return Backbone.BrowserStorage; // The chat_state will be set to ACTIVE once the chat box is opened // and we listen for change:chat_state, so shouldn't set it to ACTIVE here. 'box_id' : b64_sha1(this.get('jid')), - 'chat_state': undefined, - 'num_unread': this.get('num_unread') || 0, 'time_opened': this.get('time_opened') || moment().valueOf(), - 'url': '', 'user_id' : Strophe.getNodeFromJid(this.get('jid')) }); }, @@ -48627,13 +49717,54 @@ return Backbone.BrowserStorage; createMessage: function (message, delay, original_stanza) { return this.messages.create(this.getMessageAttributes.apply(this, arguments)); + }, + + isNewMessage: function (stanza) { + /* Given a message stanza, determine whether it's a new + * message, i.e. not an archived one. + */ + return !(sizzle('result[xmlns="'+Strophe.NS.MAM+'"]', stanza).length); + }, + + newMessageWillBeHidden: function () { + /* Returns a boolean to indicate whether a newly received + * message will be visible to the user or not. + */ + return this.get('hidden') || + this.get('minimized') || + this.isScrolledUp() || + _converse.windowState === 'hidden'; + }, + + incrementUnreadMsgCounter: function (stanza) { + /* Given a newly received message, update the unread counter if + * necessary. + */ + if (_.isNull(stanza.querySelector('body'))) { + return; // The message has no text + } + if (this.isNewMessage(stanza) && this.newMessageWillBeHidden()) { + this.save({'num_unread': this.get('num_unread') + 1}); + _converse.incrementMsgCounter(); + } + }, + + clearUnreadMsgCounter: function() { + this.save({'num_unread': 0}); + }, + + isScrolledUp: function () { + return this.get('scrolled', true); } }); this.ChatBoxes = Backbone.Collection.extend({ - model: _converse.ChatBox, comparator: 'time_opened', + model: function (attrs, options) { + return new _converse.ChatBox(attrs, options); + }, + registerMessageHandler: function () { _converse.connection.addHandler(this.onMessage.bind(this), null, 'message', 'chat'); _converse.connection.addHandler(this.onErrorMessage.bind(this), null, 'message', 'error'); @@ -48690,8 +49821,8 @@ return Backbone.BrowserStorage; * stanzas. */ var original_stanza = message, - contact_jid, forwarded, delay, from_bare_jid, - from_resource, is_me, msgid, + contact_jid, delay, from_bare_jid, + from_resource, is_me, msgid, messages, chatbox, resource, from_jid = message.getAttribute('from'), to_jid = message.getAttribute('to'), @@ -48714,7 +49845,7 @@ return Backbone.BrowserStorage; ); return true; } - forwarded = message.querySelector('forwarded'); + var forwarded = message.querySelector('forwarded'); if (!_.isNull(forwarded)) { var forwarded_message = forwarded.querySelector('message'); var forwarded_from = forwarded_message.getAttribute('from'); @@ -48732,7 +49863,6 @@ return Backbone.BrowserStorage; from_bare_jid = Strophe.getBareJidFromJid(from_jid); from_resource = Strophe.getResourceFromJid(from_jid); is_me = from_bare_jid === _converse.bare_jid; - msgid = message.getAttribute('id'); if (is_me) { // I am the sender, so this must be a forwarded message... contact_jid = Strophe.getBareJidFromJid(to_jid); @@ -48741,19 +49871,55 @@ return Backbone.BrowserStorage; contact_jid = from_bare_jid; resource = from_resource; } - _converse.emit('message', original_stanza); // Get chat box, but only create a new one when the message has a body. chatbox = this.getChatBox(contact_jid, !_.isNull(message.querySelector('body'))); - if (!chatbox) { - return true; + msgid = message.getAttribute('id'); + if (chatbox) { + messages = msgid && chatbox.messages.findWhere({msgid: msgid}) || []; + if (_.isEmpty(messages)) { + // Only create the message when we're sure it's not a + // duplicate + chatbox.incrementUnreadMsgCounter(original_stanza); + chatbox.createMessage(message, delay, original_stanza); + } } - if (msgid && chatbox.messages.findWhere({msgid: msgid})) { - return true; // We already have this message stored. - } - chatbox.createMessage(message, delay, original_stanza); + _converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox}); return true; }, + createChatBox: function (jid, attrs) { + /* Creates a chat box + * + * Parameters: + * (String) jid - The JID of the user for whom a chat box + * gets created. + * (Object) attrs - Optional chat box atributes. + */ + var bare_jid = Strophe.getBareJidFromJid(jid); + var roster_info = {}; + var roster_item = _converse.roster.get(bare_jid); + if (! _.isUndefined(roster_item)) { + roster_info = { + 'fullname': _.isEmpty(roster_item.get('fullname'))? jid: roster_item.get('fullname'), + 'image_type': roster_item.get('image_type'), + 'image': roster_item.get('image'), + 'url': roster_item.get('url'), + }; + } else if (!_converse.allow_non_roster_messaging) { + _converse.log('Could not get roster item for JID '+bare_jid+ + ' and allow_non_roster_messaging is set to false', 'error'); + return; + } + return this.create(_.assignIn({ + 'id': bare_jid, + 'jid': bare_jid, + 'fullname': jid, + 'image_type': DEFAULT_IMAGE_TYPE, + 'image': DEFAULT_IMAGE, + 'url': '', + }, roster_info, attrs || {})); + }, + getChatBox: function (jid, create, attrs) { /* Returns a chat box or optionally return a newly * created one if one doesn't exist. @@ -48764,31 +49930,9 @@ return Backbone.BrowserStorage; * (Object) attrs - Optional chat box atributes. */ jid = jid.toLowerCase(); - var bare_jid = Strophe.getBareJidFromJid(jid); - var chatbox = this.get(bare_jid); + var chatbox = this.get(Strophe.getBareJidFromJid(jid)); if (!chatbox && create) { - var roster_info = {}; - var roster_item = _converse.roster.get(bare_jid); - if (! _.isUndefined(roster_item)) { - roster_info = { - 'fullname': _.isEmpty(roster_item.get('fullname'))? jid: roster_item.get('fullname'), - 'image_type': roster_item.get('image_type'), - 'image': roster_item.get('image'), - 'url': roster_item.get('url'), - }; - } else if (!_converse.allow_non_roster_messaging) { - _converse.log('Could not get roster item for JID '+bare_jid+ - ' and allow_non_roster_messaging is set to false', 'error'); - return; - } - chatbox = this.create(_.assignIn({ - 'id': bare_jid, - 'jid': bare_jid, - 'fullname': jid, - 'image_type': DEFAULT_IMAGE_TYPE, - 'image': DEFAULT_IMAGE, - 'url': '', - }, roster_info, attrs || {})); + chatbox = this.createChatBox(jid, attrs); } return chatbox; } @@ -49080,39 +50224,92 @@ return Backbone.BrowserStorage; xhr.send(); }; - this.attemptPreboundSession = function (reconnecting) { - /* Handle session resumption or initialization when prebind is being used. - */ - if (!reconnecting && this.keepalive) { - if (!this.jid) { - throw new Error("attemptPreboundSession: when using 'keepalive' with 'prebind, "+ - "you must supply the JID of the current user."); - } - try { - return this.connection.restore(this.jid, this.onConnectStatusChanged); - } catch (e) { - this.log("Could not restore session for jid: "+this.jid+" Error message: "+e.message); - this.clearSession(); // If there's a roster, we want to clear it (see #555) + this.restoreBOSHSession = function (jid_is_required) { + /* Tries to restore a cached BOSH session. */ + if (!this.jid) { + var msg = "restoreBOSHSession: tried to restore a \"keepalive\" session "+ + "but we don't have the JID for the user!"; + if (jid_is_required) { + throw new Error(msg); + } else { + _converse.log(msg); } } + try { + this.connection.restore(this.jid, this.onConnectStatusChanged); + return true; + } catch (e) { + this.log( + "Could not restore session for jid: "+ + this.jid+" Error message: "+e.message); + this.clearSession(); // If there's a roster, we want to clear it (see #555) + return false; + } + }; - // No keepalive, or session resumption has failed. - if (!reconnecting && this.jid && this.sid && this.rid) { - return this.connection.attach(this.jid, this.sid, this.rid, this.onConnectStatusChanged); - } else if (this.prebind_url) { + this.attemptPreboundSession = function (reconnecting) { + /* Handle session resumption or initialization when prebind is + * being used. + */ + if (!reconnecting) { + if (this.keepalive && this.restoreBOSHSession(true)) { + return; + } + // No keepalive, or session resumption has failed. + if (this.jid && this.sid && this.rid) { + return this.connection.attach( + this.jid, this.sid, this.rid, + this.onConnectStatusChanged + ); + } + } + if (this.prebind_url) { return this.startNewBOSHSession(); } else { - throw new Error("attemptPreboundSession: If you use prebind and not keepalive, "+ + throw new Error( + "attemptPreboundSession: If you use prebind and not keepalive, "+ "then you MUST supply JID, RID and SID values or a prebind_url."); } }; + this.attemptNonPreboundSession = function (credentials, reconnecting) { + /* Handle session resumption or initialization when prebind is not being used. + * + * Two potential options exist and are handled in this method: + * 1. keepalive + * 2. auto_login + */ + if (!reconnecting && this.keepalive && this.restoreBOSHSession()) { + return; + } + if (this.auto_login) { + if (credentials) { + // When credentials are passed in, they override prebinding + // or credentials fetching via HTTP + this.autoLogin(credentials); + } else if (this.credentials_url) { + this.fetchLoginCredentials().done(this.autoLogin.bind(this)); + } else if (!this.jid) { + throw new Error( + "attemptNonPreboundSession: If you use auto_login, "+ + "you also need to give either a jid value (and if "+ + "applicable a password) or you need to pass in a URL "+ + "from where the username and password can be fetched "+ + "(via credentials_url)." + ); + } else { + this.autoLogin(); // Probably ANONYMOUS login + } + } else if (reconnecting) { + this.autoLogin(); + } + }; + this.autoLogin = function (credentials) { if (credentials) { - // If passed in, then they come from credentials_url, so we - // set them on the _converse object. + // If passed in, the credentials come from credentials_url, + // so we set them on the converse object. this.jid = credentials.jid; - this.password = credentials.password; } if (this.authentication === _converse.ANONYMOUS) { if (!this.jid) { @@ -49124,9 +50321,9 @@ return Backbone.BrowserStorage; this.connection.reset(); this.connection.connect(this.jid.toLowerCase(), null, this.onConnectStatusChanged); } else if (this.authentication === _converse.LOGIN) { - var password = _converse.connection.pass || this.password; + var password = _.isNil(credentials) ? (_converse.connection.pass || this.password) : credentials.password; if (!password) { - if (this.auto_login && !this.password) { + if (this.auto_login) { throw new Error("initConnection: If you use auto_login and "+ "authentication='login' then you also need to provide a password."); } @@ -49145,44 +50342,6 @@ return Backbone.BrowserStorage; } }; - this.attemptNonPreboundSession = function (credentials, reconnecting) { - /* Handle session resumption or initialization when prebind is not being used. - * - * Two potential options exist and are handled in this method: - * 1. keepalive - * 2. auto_login - */ - if (this.keepalive && !reconnecting) { - try { - return this.connection.restore(this.jid, this.onConnectStatusChanged); - } catch (e) { - this.log("Could not restore session. Error message: "+e.message); - this.clearSession(); // If there's a roster, we want to clear it (see #555) - } - } - if (this.auto_login) { - if (credentials) { - // When credentials are passed in, they override prebinding - // or credentials fetching via HTTP - this.autoLogin(credentials); - } else if (this.credentials_url) { - this.fetchLoginCredentials().done(this.autoLogin.bind(this)); - } else if (!this.jid) { - throw new Error( - "initConnection: If you use auto_login, you also need"+ - "to give either a jid value (and if applicable a "+ - "password) or you need to pass in a URL from where the "+ - "username and password can be fetched (via credentials_url)." - ); - } else { - // Probably ANONYMOUS login - this.autoLogin(); - } - } else if (reconnecting) { - this.autoLogin(); - } - }; - this.logIn = function (credentials, reconnecting) { // We now try to resume or automatically set up a new session. // Otherwise the user will be shown a login form. @@ -49487,6 +50646,7 @@ return Backbone.BrowserStorage; 'Backbone': Backbone, 'Strophe': Strophe, '_': _, + 'fp': fp, 'b64_sha1': b64_sha1, 'jQuery': $, 'moment': moment, @@ -49502,7 +50662,7 @@ obj || (obj = {}); var __t, __p = '', __e = _.escape, __j = Array.prototype.join; function print() { __p += __j.call(arguments, '') } with (obj) { -__p += '