Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/jappix/jappix.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorValérian Saliou <valerian@valeriansaliou.name>2014-10-13 12:48:06 +0400
committerValérian Saliou <valerian@valeriansaliou.name>2014-10-13 12:48:06 +0400
commitad0c36703b8c4880789920bab4d1477c82b3a8b5 (patch)
tree13e3e49d389b2938238db9dcbaf47364b868ad4a /app
parentc21724b3480320c6eb546ee48465f384ff38c3f8 (diff)
fixes #507
Signed-off-by: Valérian Saliou <valerian@valeriansaliou.name>
Diffstat (limited to 'app')
-rw-r--r--app/javascripts/jsjac.jingle.js2405
1 files changed, 1815 insertions, 590 deletions
diff --git a/app/javascripts/jsjac.jingle.js b/app/javascripts/jsjac.jingle.js
index 62e2835e..cb63d617 100644
--- a/app/javascripts/jsjac.jingle.js
+++ b/app/javascripts/jsjac.jingle.js
@@ -2,8 +2,8 @@
* jsjac-jingle [uncompressed]
* @fileoverview JSJaC Jingle library, implementation of XEP-0166.
*
- * @version 0.7.0
- * @date 2014-06-24
+ * @version 0.7.6
+ * @date 2014-10-13
* @author Valérian Saliou https://valeriansaliou.name/
* @license MPL 2.0
*
@@ -12,7 +12,7 @@
* @depends https://github.com/sstrigler/JSJaC
*/
-// Underscore.js 1.6.0
+// Underscore.js 1.7.0
// http://underscorejs.org
// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
@@ -28,9 +28,6 @@
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
- // Establish the object that gets returned to break out of a loop iteration.
- var breaker = {};
-
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
@@ -45,15 +42,6 @@
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
- nativeForEach = ArrayProto.forEach,
- nativeMap = ArrayProto.map,
- nativeReduce = ArrayProto.reduce,
- nativeReduceRight = ArrayProto.reduceRight,
- nativeFilter = ArrayProto.filter,
- nativeEvery = ArrayProto.every,
- nativeSome = ArrayProto.some,
- nativeIndexOf = ArrayProto.indexOf,
- nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
@@ -67,8 +55,7 @@
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
- // the browser, add `_` as a global object via a string identifier,
- // for Closure Compiler "advanced" mode.
+ // the browser, add `_` as a global object.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
@@ -79,98 +66,125 @@
}
// Current version.
- _.VERSION = '1.6.0';
+ _.VERSION = '1.7.0';
+
+ // Internal function that returns an efficient (for current engines) version
+ // of the passed-in callback, to be repeatedly applied in other Underscore
+ // functions.
+ var createCallback = function(func, context, argCount) {
+ if (context === void 0) return func;
+ switch (argCount == null ? 3 : argCount) {
+ case 1: return function(value) {
+ return func.call(context, value);
+ };
+ case 2: return function(value, other) {
+ return func.call(context, value, other);
+ };
+ case 3: return function(value, index, collection) {
+ return func.call(context, value, index, collection);
+ };
+ case 4: return function(accumulator, value, index, collection) {
+ return func.call(context, accumulator, value, index, collection);
+ };
+ }
+ return function() {
+ return func.apply(context, arguments);
+ };
+ };
+
+ // A mostly-internal function to generate callbacks that can be applied
+ // to each element in a collection, returning the desired result — either
+ // identity, an arbitrary callback, a property matcher, or a property accessor.
+ _.iteratee = function(value, context, argCount) {
+ if (value == null) return _.identity;
+ if (_.isFunction(value)) return createCallback(value, context, argCount);
+ if (_.isObject(value)) return _.matches(value);
+ return _.property(value);
+ };
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
- // Handles objects with the built-in `forEach`, arrays, and raw objects.
- // Delegates to **ECMAScript 5**'s native `forEach` if available.
- var each = _.each = _.forEach = function(obj, iterator, context) {
+ // Handles raw objects in addition to array-likes. Treats all
+ // sparse array-likes as if they were dense.
+ _.each = _.forEach = function(obj, iteratee, context) {
if (obj == null) return obj;
- if (nativeForEach && obj.forEach === nativeForEach) {
- obj.forEach(iterator, context);
- } else if (obj.length === +obj.length) {
- for (var i = 0, length = obj.length; i < length; i++) {
- if (iterator.call(context, obj[i], i, obj) === breaker) return;
+ iteratee = createCallback(iteratee, context);
+ var i, length = obj.length;
+ if (length === +length) {
+ for (i = 0; i < length; i++) {
+ iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
- for (var i = 0, length = keys.length; i < length; i++) {
- if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
+ for (i = 0, length = keys.length; i < length; i++) {
+ iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};
- // Return the results of applying the iterator to each element.
- // Delegates to **ECMAScript 5**'s native `map` if available.
- _.map = _.collect = function(obj, iterator, context) {
- var results = [];
- if (obj == null) return results;
- if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
- each(obj, function(value, index, list) {
- results.push(iterator.call(context, value, index, list));
- });
+ // Return the results of applying the iteratee to each element.
+ _.map = _.collect = function(obj, iteratee, context) {
+ if (obj == null) return [];
+ iteratee = _.iteratee(iteratee, context);
+ var keys = obj.length !== +obj.length && _.keys(obj),
+ length = (keys || obj).length,
+ results = Array(length),
+ currentKey;
+ for (var index = 0; index < length; index++) {
+ currentKey = keys ? keys[index] : index;
+ results[index] = iteratee(obj[currentKey], currentKey, obj);
+ }
return results;
};
var reduceError = 'Reduce of empty array with no initial value';
// **Reduce** builds up a single result from a list of values, aka `inject`,
- // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
- _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
- var initial = arguments.length > 2;
+ // or `foldl`.
+ _.reduce = _.foldl = _.inject = function(obj, iteratee, memo, context) {
if (obj == null) obj = [];
- if (nativeReduce && obj.reduce === nativeReduce) {
- if (context) iterator = _.bind(iterator, context);
- return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
+ iteratee = createCallback(iteratee, context, 4);
+ var keys = obj.length !== +obj.length && _.keys(obj),
+ length = (keys || obj).length,
+ index = 0, currentKey;
+ if (arguments.length < 3) {
+ if (!length) throw new TypeError(reduceError);
+ memo = obj[keys ? keys[index++] : index++];
+ }
+ for (; index < length; index++) {
+ currentKey = keys ? keys[index] : index;
+ memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
- each(obj, function(value, index, list) {
- if (!initial) {
- memo = value;
- initial = true;
- } else {
- memo = iterator.call(context, memo, value, index, list);
- }
- });
- if (!initial) throw new TypeError(reduceError);
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
- // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
- _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
- var initial = arguments.length > 2;
+ _.reduceRight = _.foldr = function(obj, iteratee, memo, context) {
if (obj == null) obj = [];
- if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
- if (context) iterator = _.bind(iterator, context);
- return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+ iteratee = createCallback(iteratee, context, 4);
+ var keys = obj.length !== + obj.length && _.keys(obj),
+ index = (keys || obj).length,
+ currentKey;
+ if (arguments.length < 3) {
+ if (!index) throw new TypeError(reduceError);
+ memo = obj[keys ? keys[--index] : --index];
}
- var length = obj.length;
- if (length !== +length) {
- var keys = _.keys(obj);
- length = keys.length;
+ while (index--) {
+ currentKey = keys ? keys[index] : index;
+ memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
- each(obj, function(value, index, list) {
- index = keys ? keys[--length] : --length;
- if (!initial) {
- memo = obj[index];
- initial = true;
- } else {
- memo = iterator.call(context, memo, obj[index], index, list);
- }
- });
- if (!initial) throw new TypeError(reduceError);
return memo;
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
var result;
- any(obj, function(value, index, list) {
- if (predicate.call(context, value, index, list)) {
+ predicate = _.iteratee(predicate, context);
+ _.some(obj, function(value, index, list) {
+ if (predicate(value, index, list)) {
result = value;
return true;
}
@@ -179,61 +193,58 @@
};
// Return all the elements that pass a truth test.
- // Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) {
var results = [];
if (obj == null) return results;
- if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context);
- each(obj, function(value, index, list) {
- if (predicate.call(context, value, index, list)) results.push(value);
+ predicate = _.iteratee(predicate, context);
+ _.each(obj, function(value, index, list) {
+ if (predicate(value, index, list)) results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, predicate, context) {
- return _.filter(obj, function(value, index, list) {
- return !predicate.call(context, value, index, list);
- }, context);
+ return _.filter(obj, _.negate(_.iteratee(predicate)), context);
};
// Determine whether all of the elements match a truth test.
- // Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, predicate, context) {
- predicate || (predicate = _.identity);
- var result = true;
- if (obj == null) return result;
- if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context);
- each(obj, function(value, index, list) {
- if (!(result = result && predicate.call(context, value, index, list))) return breaker;
- });
- return !!result;
+ if (obj == null) return true;
+ predicate = _.iteratee(predicate, context);
+ var keys = obj.length !== +obj.length && _.keys(obj),
+ length = (keys || obj).length,
+ index, currentKey;
+ for (index = 0; index < length; index++) {
+ currentKey = keys ? keys[index] : index;
+ if (!predicate(obj[currentKey], currentKey, obj)) return false;
+ }
+ return true;
};
// Determine if at least one element in the object matches a truth test.
- // Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
- var any = _.some = _.any = function(obj, predicate, context) {
- predicate || (predicate = _.identity);
- var result = false;
- if (obj == null) return result;
- if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context);
- each(obj, function(value, index, list) {
- if (result || (result = predicate.call(context, value, index, list))) return breaker;
- });
- return !!result;
+ _.some = _.any = function(obj, predicate, context) {
+ if (obj == null) return false;
+ predicate = _.iteratee(predicate, context);
+ var keys = obj.length !== +obj.length && _.keys(obj),
+ length = (keys || obj).length,
+ index, currentKey;
+ for (index = 0; index < length; index++) {
+ currentKey = keys ? keys[index] : index;
+ if (predicate(obj[currentKey], currentKey, obj)) return true;
+ }
+ return false;
};
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
if (obj == null) return false;
- if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
- return any(obj, function(value) {
- return value === target;
- });
+ if (obj.length !== +obj.length) obj = _.values(obj);
+ return _.indexOf(obj, target) >= 0;
};
// Invoke a method (with arguments) on every item in a collection.
@@ -262,51 +273,67 @@
return _.find(obj, _.matches(attrs));
};
- // Return the maximum element or (element-based computation).
- // Can't optimize arrays of integers longer than 65,535 elements.
- // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
- _.max = function(obj, iterator, context) {
- if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
- return Math.max.apply(Math, obj);
- }
- var result = -Infinity, lastComputed = -Infinity;
- each(obj, function(value, index, list) {
- var computed = iterator ? iterator.call(context, value, index, list) : value;
- if (computed > lastComputed) {
- result = value;
- lastComputed = computed;
+ // Return the maximum element (or element-based computation).
+ _.max = function(obj, iteratee, context) {
+ var result = -Infinity, lastComputed = -Infinity,
+ value, computed;
+ if (iteratee == null && obj != null) {
+ obj = obj.length === +obj.length ? obj : _.values(obj);
+ for (var i = 0, length = obj.length; i < length; i++) {
+ value = obj[i];
+ if (value > result) {
+ result = value;
+ }
}
- });
+ } else {
+ iteratee = _.iteratee(iteratee, context);
+ _.each(obj, function(value, index, list) {
+ computed = iteratee(value, index, list);
+ if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
+ result = value;
+ lastComputed = computed;
+ }
+ });
+ }
return result;
};
// Return the minimum element (or element-based computation).
- _.min = function(obj, iterator, context) {
- if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
- return Math.min.apply(Math, obj);
- }
- var result = Infinity, lastComputed = Infinity;
- each(obj, function(value, index, list) {
- var computed = iterator ? iterator.call(context, value, index, list) : value;
- if (computed < lastComputed) {
- result = value;
- lastComputed = computed;
+ _.min = function(obj, iteratee, context) {
+ var result = Infinity, lastComputed = Infinity,
+ value, computed;
+ if (iteratee == null && obj != null) {
+ obj = obj.length === +obj.length ? obj : _.values(obj);
+ for (var i = 0, length = obj.length; i < length; i++) {
+ value = obj[i];
+ if (value < result) {
+ result = value;
+ }
}
- });
+ } else {
+ iteratee = _.iteratee(iteratee, context);
+ _.each(obj, function(value, index, list) {
+ computed = iteratee(value, index, list);
+ if (computed < lastComputed || computed === Infinity && result === Infinity) {
+ result = value;
+ lastComputed = computed;
+ }
+ });
+ }
return result;
};
- // Shuffle an array, using the modern version of the
+ // Shuffle a collection, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_.shuffle = function(obj) {
- var rand;
- var index = 0;
- var shuffled = [];
- each(obj, function(value) {
- rand = _.random(index++);
- shuffled[index - 1] = shuffled[rand];
- shuffled[rand] = value;
- });
+ var set = obj && obj.length === +obj.length ? obj : _.values(obj);
+ var length = set.length;
+ var shuffled = Array(length);
+ for (var index = 0, rand; index < length; index++) {
+ rand = _.random(0, index);
+ if (rand !== index) shuffled[index] = shuffled[rand];
+ shuffled[rand] = set[index];
+ }
return shuffled;
};
@@ -321,21 +348,14 @@
return _.shuffle(obj).slice(0, Math.max(0, n));
};
- // An internal function to generate lookup iterators.
- var lookupIterator = function(value) {
- if (value == null) return _.identity;
- if (_.isFunction(value)) return value;
- return _.property(value);
- };
-
- // Sort the object's values by a criterion produced by an iterator.
- _.sortBy = function(obj, iterator, context) {
- iterator = lookupIterator(iterator);
+ // Sort the object's values by a criterion produced by an iteratee.
+ _.sortBy = function(obj, iteratee, context) {
+ iteratee = _.iteratee(iteratee, context);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value: value,
index: index,
- criteria: iterator.call(context, value, index, list)
+ criteria: iteratee(value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
@@ -350,12 +370,12 @@
// An internal function used for aggregate "group by" operations.
var group = function(behavior) {
- return function(obj, iterator, context) {
+ return function(obj, iteratee, context) {
var result = {};
- iterator = lookupIterator(iterator);
- each(obj, function(value, index) {
- var key = iterator.call(context, value, index, obj);
- behavior(result, key, value);
+ iteratee = _.iteratee(iteratee, context);
+ _.each(obj, function(value, index) {
+ var key = iteratee(value, index, obj);
+ behavior(result, value, key);
});
return result;
};
@@ -363,32 +383,32 @@
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
- _.groupBy = group(function(result, key, value) {
- _.has(result, key) ? result[key].push(value) : result[key] = [value];
+ _.groupBy = group(function(result, value, key) {
+ if (_.has(result, key)) result[key].push(value); else result[key] = [value];
});
// Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique.
- _.indexBy = group(function(result, key, value) {
+ _.indexBy = group(function(result, value, key) {
result[key] = value;
});
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
- _.countBy = group(function(result, key) {
- _.has(result, key) ? result[key]++ : result[key] = 1;
+ _.countBy = group(function(result, value, key) {
+ if (_.has(result, key)) result[key]++; else result[key] = 1;
});
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
- _.sortedIndex = function(array, obj, iterator, context) {
- iterator = lookupIterator(iterator);
- var value = iterator.call(context, obj);
+ _.sortedIndex = function(array, obj, iteratee, context) {
+ iteratee = _.iteratee(iteratee, context, 1);
+ var value = iteratee(obj);
var low = 0, high = array.length;
while (low < high) {
- var mid = (low + high) >>> 1;
- iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
+ var mid = low + high >>> 1;
+ if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
}
return low;
};
@@ -404,7 +424,18 @@
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
- return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
+ return obj.length === +obj.length ? obj.length : _.keys(obj).length;
+ };
+
+ // Split a collection into two arrays: one whose elements all satisfy the given
+ // predicate, and one whose elements all do not satisfy the predicate.
+ _.partition = function(obj, predicate, context) {
+ predicate = _.iteratee(predicate, context);
+ var pass = [], fail = [];
+ _.each(obj, function(value, key, obj) {
+ (predicate(value, key, obj) ? pass : fail).push(value);
+ });
+ return [pass, fail];
};
// Array Functions
@@ -415,7 +446,7 @@
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
- if ((n == null) || guard) return array[0];
+ if (n == null || guard) return array[0];
if (n < 0) return [];
return slice.call(array, 0, n);
};
@@ -425,14 +456,14 @@
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_.initial = function(array, n, guard) {
- return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
+ return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
if (array == null) return void 0;
- if ((n == null) || guard) return array[array.length - 1];
+ if (n == null || guard) return array[array.length - 1];
return slice.call(array, Math.max(array.length - n, 0));
};
@@ -441,7 +472,7 @@
// the rest N values in the array. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = _.drop = function(array, n, guard) {
- return slice.call(array, (n == null) || guard ? 1 : n);
+ return slice.call(array, n == null || guard ? 1 : n);
};
// Trim out all falsy values from an array.
@@ -450,23 +481,26 @@
};
// Internal implementation of a recursive `flatten` function.
- var flatten = function(input, shallow, output) {
+ var flatten = function(input, shallow, strict, output) {
if (shallow && _.every(input, _.isArray)) {
return concat.apply(output, input);
}
- each(input, function(value) {
- if (_.isArray(value) || _.isArguments(value)) {
- shallow ? push.apply(output, value) : flatten(value, shallow, output);
+ for (var i = 0, length = input.length; i < length; i++) {
+ var value = input[i];
+ if (!_.isArray(value) && !_.isArguments(value)) {
+ if (!strict) output.push(value);
+ } else if (shallow) {
+ push.apply(output, value);
} else {
- output.push(value);
+ flatten(value, shallow, strict, output);
}
- });
+ }
return output;
};
// Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) {
- return flatten(array, shallow, []);
+ return flatten(array, shallow, false, []);
};
// Return a version of the array that does not contain the specified value(s).
@@ -474,68 +508,77 @@
return _.difference(array, slice.call(arguments, 1));
};
- // Split an array into two arrays: one whose elements all satisfy the given
- // predicate, and one whose elements all do not satisfy the predicate.
- _.partition = function(array, predicate) {
- var pass = [], fail = [];
- each(array, function(elem) {
- (predicate(elem) ? pass : fail).push(elem);
- });
- return [pass, fail];
- };
-
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
- _.uniq = _.unique = function(array, isSorted, iterator, context) {
- if (_.isFunction(isSorted)) {
- context = iterator;
- iterator = isSorted;
+ _.uniq = _.unique = function(array, isSorted, iteratee, context) {
+ if (array == null) return [];
+ if (!_.isBoolean(isSorted)) {
+ context = iteratee;
+ iteratee = isSorted;
isSorted = false;
}
- var initial = iterator ? _.map(array, iterator, context) : array;
- var results = [];
+ if (iteratee != null) iteratee = _.iteratee(iteratee, context);
+ var result = [];
var seen = [];
- each(initial, function(value, index) {
- if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
- seen.push(value);
- results.push(array[index]);
+ for (var i = 0, length = array.length; i < length; i++) {
+ var value = array[i];
+ if (isSorted) {
+ if (!i || seen !== value) result.push(value);
+ seen = value;
+ } else if (iteratee) {
+ var computed = iteratee(value, i, array);
+ if (_.indexOf(seen, computed) < 0) {
+ seen.push(computed);
+ result.push(value);
+ }
+ } else if (_.indexOf(result, value) < 0) {
+ result.push(value);
}
- });
- return results;
+ }
+ return result;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
- return _.uniq(_.flatten(arguments, true));
+ return _.uniq(flatten(arguments, true, true, []));
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
- var rest = slice.call(arguments, 1);
- return _.filter(_.uniq(array), function(item) {
- return _.every(rest, function(other) {
- return _.contains(other, item);
- });
- });
+ if (array == null) return [];
+ var result = [];
+ var argsLength = arguments.length;
+ for (var i = 0, length = array.length; i < length; i++) {
+ var item = array[i];
+ if (_.contains(result, item)) continue;
+ for (var j = 1; j < argsLength; j++) {
+ if (!_.contains(arguments[j], item)) break;
+ }
+ if (j === argsLength) result.push(item);
+ }
+ return result;
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
- var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
- return _.filter(array, function(value){ return !_.contains(rest, value); });
+ var rest = flatten(slice.call(arguments, 1), true, true, []);
+ return _.filter(array, function(value){
+ return !_.contains(rest, value);
+ });
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
- _.zip = function() {
- var length = _.max(_.pluck(arguments, 'length').concat(0));
- var results = new Array(length);
+ _.zip = function(array) {
+ if (array == null) return [];
+ var length = _.max(arguments, 'length').length;
+ var results = Array(length);
for (var i = 0; i < length; i++) {
- results[i] = _.pluck(arguments, '' + i);
+ results[i] = _.pluck(arguments, i);
}
return results;
};
@@ -556,10 +599,8 @@
return result;
};
- // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
- // we need this function. Return the position of the first occurrence of an
- // item in an array, or -1 if the item is not included in the array.
- // Delegates to **ECMAScript 5**'s native `indexOf` if available.
+ // Return the position of the first occurrence of an item in an array,
+ // or -1 if the item is not included in the array.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
@@ -567,26 +608,23 @@
var i = 0, length = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
- i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
+ i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted;
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
- if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < length; i++) if (array[i] === item) return i;
return -1;
};
- // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item, from) {
if (array == null) return -1;
- var hasIndex = from != null;
- if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
- return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
+ var idx = array.length;
+ if (typeof from == 'number') {
+ idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1);
}
- var i = (hasIndex ? from : array.length);
- while (i--) if (array[i] === item) return i;
+ while (--idx >= 0) if (array[idx] === item) return idx;
return -1;
};
@@ -598,15 +636,13 @@
stop = start || 0;
start = 0;
}
- step = arguments[2] || 1;
+ step = step || 1;
var length = Math.max(Math.ceil((stop - start) / step), 0);
- var idx = 0;
- var range = new Array(length);
+ var range = Array(length);
- while(idx < length) {
- range[idx++] = start;
- start += step;
+ for (var idx = 0; idx < length; idx++, start += step) {
+ range[idx] = start;
}
return range;
@@ -616,7 +652,7 @@
// ------------------
// Reusable constructor function for prototype setting.
- var ctor = function(){};
+ var Ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
@@ -624,17 +660,18 @@
_.bind = function(func, context) {
var args, bound;
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
- if (!_.isFunction(func)) throw new TypeError;
+ if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
args = slice.call(arguments, 2);
- return bound = function() {
+ bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
- ctor.prototype = func.prototype;
- var self = new ctor;
- ctor.prototype = null;
+ Ctor.prototype = func.prototype;
+ var self = new Ctor;
+ Ctor.prototype = null;
var result = func.apply(self, args.concat(slice.call(arguments)));
- if (Object(result) === result) return result;
+ if (_.isObject(result)) return result;
return self;
};
+ return bound;
};
// Partially apply a function by creating a version that has had some of its
@@ -657,27 +694,34 @@
// are the method names to be bound. Useful for ensuring that all callbacks
// defined on an object belong to it.
_.bindAll = function(obj) {
- var funcs = slice.call(arguments, 1);
- if (funcs.length === 0) throw new Error('bindAll must be passed function names');
- each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
+ var i, length = arguments.length, key;
+ if (length <= 1) throw new Error('bindAll must be passed function names');
+ for (i = 1; i < length; i++) {
+ key = arguments[i];
+ obj[key] = _.bind(obj[key], obj);
+ }
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
- var memo = {};
- hasher || (hasher = _.identity);
- return function() {
- var key = hasher.apply(this, arguments);
- return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
+ var memoize = function(key) {
+ var cache = memoize.cache;
+ var address = hasher ? hasher.apply(this, arguments) : key;
+ if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
+ return cache[address];
};
+ memoize.cache = {};
+ return memoize;
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
- return setTimeout(function(){ return func.apply(null, args); }, wait);
+ return setTimeout(function(){
+ return func.apply(null, args);
+ }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
@@ -695,12 +739,12 @@
var context, args, result;
var timeout = null;
var previous = 0;
- options || (options = {});
+ if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
- context = args = null;
+ if (!timeout) context = args = null;
};
return function() {
var now = _.now();
@@ -708,12 +752,12 @@
var remaining = wait - (now - previous);
context = this;
args = arguments;
- if (remaining <= 0) {
+ if (remaining <= 0 || remaining > wait) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
- context = args = null;
+ if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
@@ -730,13 +774,14 @@
var later = function() {
var last = _.now() - timestamp;
- if (last < wait) {
+
+ if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
- context = args = null;
+ if (!timeout) context = args = null;
}
}
};
@@ -746,9 +791,7 @@
args = arguments;
timestamp = _.now();
var callNow = immediate && !timeout;
- if (!timeout) {
- timeout = setTimeout(later, wait);
- }
+ if (!timeout) timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
context = args = null;
@@ -758,19 +801,6 @@
};
};
- // Returns a function that will be executed at most one time, no matter how
- // often you call it. Useful for lazy initialization.
- _.once = function(func) {
- var ran = false, memo;
- return function() {
- if (ran) return memo;
- ran = true;
- memo = func.apply(this, arguments);
- func = null;
- return memo;
- };
- };
-
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
@@ -778,16 +808,23 @@
return _.partial(wrapper, func);
};
+ // Returns a negated version of the passed-in predicate.
+ _.negate = function(predicate) {
+ return function() {
+ return !predicate.apply(this, arguments);
+ };
+ };
+
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
- var funcs = arguments;
+ var args = arguments;
+ var start = args.length - 1;
return function() {
- var args = arguments;
- for (var i = funcs.length - 1; i >= 0; i--) {
- args = [funcs[i].apply(this, args)];
- }
- return args[0];
+ var i = start;
+ var result = args[start].apply(this, arguments);
+ while (i--) result = args[i].call(this, result);
+ return result;
};
};
@@ -800,6 +837,23 @@
};
};
+ // Returns a function that will only be executed before being called N times.
+ _.before = function(times, func) {
+ var memo;
+ return function() {
+ if (--times > 0) {
+ memo = func.apply(this, arguments);
+ } else {
+ func = null;
+ }
+ return memo;
+ };
+ };
+
+ // Returns a function that will be executed at most one time, no matter how
+ // often you call it. Useful for lazy initialization.
+ _.once = _.partial(_.before, 2);
+
// Object Functions
// ----------------
@@ -817,7 +871,7 @@
_.values = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
- var values = new Array(length);
+ var values = Array(length);
for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]];
}
@@ -828,7 +882,7 @@
_.pairs = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
- var pairs = new Array(length);
+ var pairs = Array(length);
for (var i = 0; i < length; i++) {
pairs[i] = [keys[i], obj[keys[i]]];
}
@@ -857,45 +911,62 @@
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
- each(slice.call(arguments, 1), function(source) {
- if (source) {
- for (var prop in source) {
- obj[prop] = source[prop];
+ if (!_.isObject(obj)) return obj;
+ var source, prop;
+ for (var i = 1, length = arguments.length; i < length; i++) {
+ source = arguments[i];
+ for (prop in source) {
+ if (hasOwnProperty.call(source, prop)) {
+ obj[prop] = source[prop];
}
}
- });
+ }
return obj;
};
// Return a copy of the object only containing the whitelisted properties.
- _.pick = function(obj) {
- var copy = {};
- var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
- each(keys, function(key) {
- if (key in obj) copy[key] = obj[key];
- });
- return copy;
+ _.pick = function(obj, iteratee, context) {
+ var result = {}, key;
+ if (obj == null) return result;
+ if (_.isFunction(iteratee)) {
+ iteratee = createCallback(iteratee, context);
+ for (key in obj) {
+ var value = obj[key];
+ if (iteratee(value, key, obj)) result[key] = value;
+ }
+ } else {
+ var keys = concat.apply([], slice.call(arguments, 1));
+ obj = new Object(obj);
+ for (var i = 0, length = keys.length; i < length; i++) {
+ key = keys[i];
+ if (key in obj) result[key] = obj[key];
+ }
+ }
+ return result;
};
// Return a copy of the object without the blacklisted properties.
- _.omit = function(obj) {
- var copy = {};
- var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
- for (var key in obj) {
- if (!_.contains(keys, key)) copy[key] = obj[key];
+ _.omit = function(obj, iteratee, context) {
+ if (_.isFunction(iteratee)) {
+ iteratee = _.negate(iteratee);
+ } else {
+ var keys = _.map(concat.apply([], slice.call(arguments, 1)), String);
+ iteratee = function(value, key) {
+ return !_.contains(keys, key);
+ };
}
- return copy;
+ return _.pick(obj, iteratee, context);
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
- each(slice.call(arguments, 1), function(source) {
- if (source) {
- for (var prop in source) {
- if (obj[prop] === void 0) obj[prop] = source[prop];
- }
+ if (!_.isObject(obj)) return obj;
+ for (var i = 1, length = arguments.length; i < length; i++) {
+ var source = arguments[i];
+ for (var prop in source) {
+ if (obj[prop] === void 0) obj[prop] = source[prop];
}
- });
+ }
return obj;
};
@@ -917,7 +988,7 @@
var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
- if (a === b) return a !== 0 || 1 / a == 1 / b;
+ if (a === b) return a !== 0 || 1 / a === 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
@@ -925,29 +996,27 @@
if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
- if (className != toString.call(b)) return false;
+ if (className !== toString.call(b)) return false;
switch (className) {
- // Strings, numbers, dates, and booleans are compared by value.
+ // Strings, numbers, regular expressions, dates, and booleans are compared by value.
+ case '[object RegExp]':
+ // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
- return a == String(b);
+ return '' + a === '' + b;
case '[object Number]':
- // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
- // other numeric values.
- return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
+ // `NaN`s are equivalent, but non-reflexive.
+ // Object(NaN) is equivalent to NaN
+ if (+a !== +a) return +b !== +b;
+ // An `egal` comparison is performed for other numeric values.
+ return +a === 0 ? 1 / +a === 1 / b : +a === +b;
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
- return +a == +b;
- // RegExps are compared by their source patterns and flags.
- case '[object RegExp]':
- return a.source == b.source &&
- a.global == b.global &&
- a.multiline == b.multiline &&
- a.ignoreCase == b.ignoreCase;
+ return +a === +b;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
@@ -956,25 +1025,29 @@
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
- if (aStack[length] == a) return bStack[length] == b;
+ if (aStack[length] === a) return bStack[length] === b;
}
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
- if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
- _.isFunction(bCtor) && (bCtor instanceof bCtor))
- && ('constructor' in a && 'constructor' in b)) {
+ if (
+ aCtor !== bCtor &&
+ // Handle Object.create(x) cases
+ 'constructor' in a && 'constructor' in b &&
+ !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
+ _.isFunction(bCtor) && bCtor instanceof bCtor)
+ ) {
return false;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
- var size = 0, result = true;
+ var size, result;
// Recursively compare objects and arrays.
- if (className == '[object Array]') {
+ if (className === '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
- result = size == b.length;
+ result = size === b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
@@ -983,20 +1056,16 @@
}
} else {
// Deep compare objects.
- for (var key in a) {
- if (_.has(a, key)) {
- // Count the expected number of properties.
- size++;
- // Deep compare each member.
- if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
- }
- }
- // Ensure that both objects contain the same number of properties.
+ var keys = _.keys(a), key;
+ size = keys.length;
+ // Ensure that both objects contain the same number of properties before comparing deep equality.
+ result = _.keys(b).length === size;
if (result) {
- for (key in b) {
- if (_.has(b, key) && !(size--)) break;
+ while (size--) {
+ // Deep compare each member
+ key = keys[size];
+ if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
}
- result = !size;
}
}
// Remove the first object from the stack of traversed objects.
@@ -1014,7 +1083,7 @@
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (obj == null) return true;
- if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
+ if (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
@@ -1027,18 +1096,19 @@
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
- return toString.call(obj) == '[object Array]';
+ return toString.call(obj) === '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
- return obj === Object(obj);
+ var type = typeof obj;
+ return type === 'function' || type === 'object' && !!obj;
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
- each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
+ _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_['is' + name] = function(obj) {
- return toString.call(obj) == '[object ' + name + ']';
+ return toString.call(obj) === '[object ' + name + ']';
};
});
@@ -1046,14 +1116,14 @@
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
- return !!(obj && _.has(obj, 'callee'));
+ return _.has(obj, 'callee');
};
}
- // Optimize `isFunction` if appropriate.
- if (typeof (/./) !== 'function') {
+ // Optimize `isFunction` if appropriate. Work around an IE 11 bug.
+ if (typeof /./ !== 'function') {
_.isFunction = function(obj) {
- return typeof obj === 'function';
+ return typeof obj == 'function' || false;
};
}
@@ -1064,12 +1134,12 @@
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) {
- return _.isNumber(obj) && obj != +obj;
+ return _.isNumber(obj) && obj !== +obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
- return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
+ return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
};
// Is a given value equal to null?
@@ -1085,7 +1155,7 @@
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
- return hasOwnProperty.call(obj, key);
+ return obj != null && hasOwnProperty.call(obj, key);
};
// Utility Functions
@@ -1098,17 +1168,19 @@
return this;
};
- // Keep the identity function around for default iterators.
+ // Keep the identity function around for default iteratees.
_.identity = function(value) {
return value;
};
_.constant = function(value) {
- return function () {
+ return function() {
return value;
};
};
+ _.noop = function(){};
+
_.property = function(key) {
return function(obj) {
return obj[key];
@@ -1117,20 +1189,23 @@
// Returns a predicate for checking whether an object has a given set of `key:value` pairs.
_.matches = function(attrs) {
+ var pairs = _.pairs(attrs), length = pairs.length;
return function(obj) {
- if (obj === attrs) return true; //avoid comparing an object to itself.
- for (var key in attrs) {
- if (attrs[key] !== obj[key])
- return false;
+ if (obj == null) return !length;
+ obj = new Object(obj);
+ for (var i = 0; i < length; i++) {
+ var pair = pairs[i], key = pair[0];
+ if (pair[1] !== obj[key] || !(key in obj)) return false;
}
return true;
- }
+ };
};
// Run a function **n** times.
- _.times = function(n, iterator, context) {
+ _.times = function(n, iteratee, context) {
var accum = Array(Math.max(0, n));
- for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
+ iteratee = createCallback(iteratee, context, 1);
+ for (var i = 0; i < n; i++) accum[i] = iteratee(i);
return accum;
};
@@ -1144,54 +1219,44 @@
};
// A (possibly faster) way to get the current timestamp as an integer.
- _.now = Date.now || function() { return new Date().getTime(); };
-
- // List of HTML entities for escaping.
- var entityMap = {
- escape: {
- '&': '&amp;',
- '<': '&lt;',
- '>': '&gt;',
- '"': '&quot;',
- "'": '&#x27;'
- }
+ _.now = Date.now || function() {
+ return new Date().getTime();
};
- entityMap.unescape = _.invert(entityMap.escape);
- // Regexes containing the keys and values listed immediately above.
- var entityRegexes = {
- escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
- unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
+ // List of HTML entities for escaping.
+ var escapeMap = {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '"': '&quot;',
+ "'": '&#x27;',
+ '`': '&#x60;'
};
+ var unescapeMap = _.invert(escapeMap);
// Functions for escaping and unescaping strings to/from HTML interpolation.
- _.each(['escape', 'unescape'], function(method) {
- _[method] = function(string) {
- if (string == null) return '';
- return ('' + string).replace(entityRegexes[method], function(match) {
- return entityMap[method][match];
- });
+ var createEscaper = function(map) {
+ var escaper = function(match) {
+ return map[match];
};
- });
+ // Regexes for identifying a key that needs to be escaped
+ var source = '(?:' + _.keys(map).join('|') + ')';
+ var testRegexp = RegExp(source);
+ var replaceRegexp = RegExp(source, 'g');
+ return function(string) {
+ string = string == null ? '' : '' + string;
+ return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
+ };
+ };
+ _.escape = createEscaper(escapeMap);
+ _.unescape = createEscaper(unescapeMap);
// If the value of the named `property` is a function then invoke it with the
// `object` as context; otherwise, return it.
_.result = function(object, property) {
if (object == null) return void 0;
var value = object[property];
- return _.isFunction(value) ? value.call(object) : value;
- };
-
- // Add your own custom functions to the Underscore object.
- _.mixin = function(obj) {
- each(_.functions(obj), function(name) {
- var func = _[name] = obj[name];
- _.prototype[name] = function() {
- var args = [this._wrapped];
- push.apply(args, arguments);
- return result.call(this, func.apply(_, args));
- };
- });
+ return _.isFunction(value) ? object[property]() : value;
};
// Generate a unique integer id (unique within the entire client session).
@@ -1222,22 +1287,26 @@
'\\': '\\',
'\r': 'r',
'\n': 'n',
- '\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
- var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
+ var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
+
+ var escapeChar = function(match) {
+ return '\\' + escapes[match];
+ };
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
- _.template = function(text, data, settings) {
- var render;
+ // NB: `oldSettings` only exists for backwards compatibility.
+ _.template = function(text, settings, oldSettings) {
+ if (!settings && oldSettings) settings = oldSettings;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
- var matcher = new RegExp([
+ var matcher = RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
@@ -1247,19 +1316,18 @@
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
- source += text.slice(index, offset)
- .replace(escaper, function(match) { return '\\' + escapes[match]; });
+ source += text.slice(index, offset).replace(escaper, escapeChar);
+ index = offset + match.length;
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
- }
- if (interpolate) {
+ } else if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
- }
- if (evaluate) {
+ } else if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
- index = offset + match.length;
+
+ // Adobe VMs need the match returned to produce the correct offest.
return match;
});
source += "';\n";
@@ -1269,29 +1337,31 @@
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
- source + "return __p;\n";
+ source + 'return __p;\n';
try {
- render = new Function(settings.variable || 'obj', '_', source);
+ var render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
- if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
- // Provide the compiled function source as a convenience for precompilation.
- template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
+ // Provide the compiled source as a convenience for precompilation.
+ var argument = settings.variable || 'obj';
+ template.source = 'function(' + argument + '){\n' + source + '}';
return template;
};
- // Add a "chain" function, which will delegate to the wrapper.
+ // Add a "chain" function. Start chaining a wrapped Underscore object.
_.chain = function(obj) {
- return _(obj).chain();
+ var instance = _(obj);
+ instance._chain = true;
+ return instance;
};
// OOP
@@ -1305,42 +1375,44 @@
return this._chain ? _(obj).chain() : obj;
};
+ // Add your own custom functions to the Underscore object.
+ _.mixin = function(obj) {
+ _.each(_.functions(obj), function(name) {
+ var func = _[name] = obj[name];
+ _.prototype[name] = function() {
+ var args = [this._wrapped];
+ push.apply(args, arguments);
+ return result.call(this, func.apply(_, args));
+ };
+ });
+ };
+
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
- each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+ _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
var obj = this._wrapped;
method.apply(obj, arguments);
- if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
+ if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
return result.call(this, obj);
};
});
// Add all accessor Array functions to the wrapper.
- each(['concat', 'join', 'slice'], function(name) {
+ _.each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return result.call(this, method.apply(this._wrapped, arguments));
};
});
- _.extend(_.prototype, {
-
- // Start chaining a wrapped Underscore object.
- chain: function() {
- this._chain = true;
- return this;
- },
-
- // Extracts the result from a wrapped and chained object.
- value: function() {
- return this._wrapped;
- }
-
- });
+ // Extracts the result from a wrapped and chained object.
+ _.prototype.value = function() {
+ return this._wrapped;
+ };
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
@@ -1354,7 +1426,7 @@
return _;
});
}
-}).call(this);
+}.call(this));
/*
Ring.js
@@ -1485,7 +1557,7 @@ function declare(_) {
_.extend(current, claz.__properties__);
_.each(_.keys(current), function(key) {
var p = current[key];
- if (typeof p !== "function" || ! fnTest.test(p) ||
+ if (typeof p !== "function" || ! fnTest.test(p) || p.__classId__ ||
(key !== "__ringConstructor__" && claz.__ringConvertedObject__))
return;
current[key] = (function(name, fct, supProto) {
@@ -1946,7 +2018,27 @@ var WEBRTC_SDP_TYPE_ANSWER = 'answer';
* @default
* @public
*/
-var R_WEBRTC_SDP_CANDIDATE = /^a=candidate:(\w{1,32}) (\d{1,5}) (udp|tcp) (\d{1,10}) ([a-zA-Z0-9:\.]{1,45}) (\d{1,5}) (typ) (host|srflx|prflx|relay)( (raddr) ([a-zA-Z0-9:\.]{1,45}) (rport) (\d{1,5}))?( (generation) (\d))?/i;
+var R_WEBRTC_SHORT_CANDIDATE = 'candidate:(\\w{1,32}) (\\d{1,5}) (udp|tcp) (\\d{1,10}) ([a-zA-Z0-9:\\.]{1,45}) (\\d{1,5}) (typ) (host|srflx|prflx|relay)( (raddr) ([a-zA-Z0-9:\\.]{1,45}) (rport) (\\d{1,5}))?( (generation) (\\d))?';
+
+/**
+ * @constant
+ * @global
+ * @type {RegExp}
+ * @readonly
+ * @default
+ * @public
+ */
+var R_WEBRTC_DATA_CANDIDATE = new RegExp('^(?:a=)?' + R_WEBRTC_SHORT_CANDIDATE, 'i');
+
+/**
+ * @constant
+ * @global
+ * @type {RegExp}
+ * @readonly
+ * @default
+ * @public
+ */
+var R_WEBRTC_SDP_CANDIDATE = new RegExp('^a=' + R_WEBRTC_SHORT_CANDIDATE, 'i');
/**
* @constant
@@ -2203,6 +2295,16 @@ var NS_JINGLE_SECURITY_STUB = 'urn:xmpp:jingle:security:
* @default
* @public
*/
+var NS_JINGLE_MESSAGE = 'urn:xmpp:jingle-message:0';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
var NS_JABBER_JINGLENODES = 'http://jabber.org/protocol/jinglenodes';
/**
@@ -2406,6 +2508,9 @@ var MAP_DISCO_JINGLE = [
/* http://xmpp.org/extensions/xep-0262.html */
NS_JINGLE_APPS_RTP_ZRTP,
+ /* http://xmpp.org/extensions/xep-0353.html */
+ NS_JINGLE_MESSAGE,
+
/* http://xmpp.org/extensions/xep-0278.html */
NS_JABBER_JINGLENODES,
@@ -2552,6 +2657,16 @@ var JSJAC_JINGLE_STANZA_TIMEOUT = 10;
/**
* @constant
* @global
+ * @type {Number}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_BROADCAST_TIMEOUT = 30;
+
+/**
+ * @constant
+ * @global
* @type {String}
* @readonly
* @default
@@ -2932,6 +3047,56 @@ var JSJAC_JINGLE_ACTION_TRANSPORT_REPLACE = 'transport-replace';
/**
* @constant
* @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_MESSAGE_ACTION_PROPOSE = 'propose';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_MESSAGE_ACTION_RETRACT = 'retract';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_MESSAGE_ACTION_ACCEPT = 'accept';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_MESSAGE_ACTION_PROCEED = 'proceed';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_MESSAGE_ACTION_REJECT = 'reject';
+
+/**
+ * @constant
+ * @global
* @type {Object}
* @readonly
* @default
@@ -3538,6 +3703,76 @@ var JSJAC_JINGLE_IQ_TYPE_ERROR = 'error';
* @default
* @public
*/
+var JSJAC_JINGLE_ICE_CONNECTION_STATE_NEW = 'new';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_ICE_CONNECTION_STATE_CHECKING = 'checking';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_ICE_CONNECTION_STATE_CONNECTED = 'connected';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_ICE_CONNECTION_STATE_COMPLETED = 'completed';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_ICE_CONNECTION_STATE_FAILED = 'failed';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_ICE_CONNECTION_STATE_DISCONNECTED = 'disconnected';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_ICE_CONNECTION_STATE_CLOSED = 'closed';
+
+/**
+ * @constant
+ * @global
+ * @type {String}
+ * @readonly
+ * @default
+ * @public
+ */
var JSJAC_JINGLE_SDP_CANDIDATE_TYPE_HOST = 'host';
/**
@@ -4000,11 +4235,14 @@ var JSJAC_JINGLE_MUJI_MUC_CONFIG_SECRET = 'muc#roomconfig_roomsecre
* @default
* @public
*/
-var JSJAC_JINGLE_SDP_CANDIDATE_TYPES = {};
-JSJAC_JINGLE_SDP_CANDIDATE_TYPES[JSJAC_JINGLE_SDP_CANDIDATE_TYPE_HOST] = JSJAC_JINGLE_SDP_CANDIDATE_METHOD_ICE;
-JSJAC_JINGLE_SDP_CANDIDATE_TYPES[JSJAC_JINGLE_SDP_CANDIDATE_TYPE_SRFLX] = JSJAC_JINGLE_SDP_CANDIDATE_METHOD_ICE;
-JSJAC_JINGLE_SDP_CANDIDATE_TYPES[JSJAC_JINGLE_SDP_CANDIDATE_TYPE_PRFLX] = JSJAC_JINGLE_SDP_CANDIDATE_METHOD_ICE;
-JSJAC_JINGLE_SDP_CANDIDATE_TYPES[JSJAC_JINGLE_SDP_CANDIDATE_TYPE_RELAY] = JSJAC_JINGLE_SDP_CANDIDATE_METHOD_RAW;
+var JSJAC_JINGLE_ICE_CONNECTION_STATES = {};
+JSJAC_JINGLE_ICE_CONNECTION_STATES[JSJAC_JINGLE_ICE_CONNECTION_STATE_NEW] = 1;
+JSJAC_JINGLE_ICE_CONNECTION_STATES[JSJAC_JINGLE_ICE_CONNECTION_STATE_CHECKING] = 1;
+JSJAC_JINGLE_ICE_CONNECTION_STATES[JSJAC_JINGLE_ICE_CONNECTION_STATE_CONNECTED] = 1;
+JSJAC_JINGLE_ICE_CONNECTION_STATES[JSJAC_JINGLE_ICE_CONNECTION_STATE_COMPLETED] = 1;
+JSJAC_JINGLE_ICE_CONNECTION_STATES[JSJAC_JINGLE_ICE_CONNECTION_STATE_FAILED] = 1;
+JSJAC_JINGLE_ICE_CONNECTION_STATES[JSJAC_JINGLE_ICE_CONNECTION_STATE_DISCONNECTED] = 1;
+JSJAC_JINGLE_ICE_CONNECTION_STATES[JSJAC_JINGLE_ICE_CONNECTION_STATE_CLOSED] = 1;
/**
* @constant
@@ -4014,12 +4252,11 @@ JSJAC_JINGLE_SDP_CANDIDATE_TYPES[JSJAC_JINGLE_SDP_CANDIDATE_TYPE_RELAY] = JSJAC
* @default
* @public
*/
-var JSJAC_JINGLE_BROWSERS = {};
-JSJAC_JINGLE_BROWSERS[JSJAC_JINGLE_BROWSER_FIREFOX] = 1;
-JSJAC_JINGLE_BROWSERS[JSJAC_JINGLE_BROWSER_CHROME] = 1;
-JSJAC_JINGLE_BROWSERS[JSJAC_JINGLE_BROWSER_SAFARI] = 1;
-JSJAC_JINGLE_BROWSERS[JSJAC_JINGLE_BROWSER_OPERA] = 1;
-JSJAC_JINGLE_BROWSERS[JSJAC_JINGLE_BROWSER_IE] = 1;
+var JSJAC_JINGLE_SDP_CANDIDATE_TYPES = {};
+JSJAC_JINGLE_SDP_CANDIDATE_TYPES[JSJAC_JINGLE_SDP_CANDIDATE_TYPE_HOST] = JSJAC_JINGLE_SDP_CANDIDATE_METHOD_ICE;
+JSJAC_JINGLE_SDP_CANDIDATE_TYPES[JSJAC_JINGLE_SDP_CANDIDATE_TYPE_SRFLX] = JSJAC_JINGLE_SDP_CANDIDATE_METHOD_ICE;
+JSJAC_JINGLE_SDP_CANDIDATE_TYPES[JSJAC_JINGLE_SDP_CANDIDATE_TYPE_PRFLX] = JSJAC_JINGLE_SDP_CANDIDATE_METHOD_ICE;
+JSJAC_JINGLE_SDP_CANDIDATE_TYPES[JSJAC_JINGLE_SDP_CANDIDATE_TYPE_RELAY] = JSJAC_JINGLE_SDP_CANDIDATE_METHOD_RAW;
/**
* @constant
@@ -4029,11 +4266,12 @@ JSJAC_JINGLE_BROWSERS[JSJAC_JINGLE_BROWSER_IE] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_SENDERS = {};
-JSJAC_JINGLE_SENDERS[JSJAC_JINGLE_SENDERS_BOTH.jingle] = JSJAC_JINGLE_SENDERS_BOTH.sdp;
-JSJAC_JINGLE_SENDERS[JSJAC_JINGLE_SENDERS_INITIATOR.jingle] = JSJAC_JINGLE_SENDERS_INITIATOR.sdp;
-JSJAC_JINGLE_SENDERS[JSJAC_JINGLE_SENDERS_NONE.jingle] = JSJAC_JINGLE_SENDERS_NONE.sdp;
-JSJAC_JINGLE_SENDERS[JSJAC_JINGLE_SENDERS_RESPONDER.jingle] = JSJAC_JINGLE_SENDERS_RESPONDER.sdp;
+var JSJAC_JINGLE_BROWSERS = {};
+JSJAC_JINGLE_BROWSERS[JSJAC_JINGLE_BROWSER_FIREFOX] = 1;
+JSJAC_JINGLE_BROWSERS[JSJAC_JINGLE_BROWSER_CHROME] = 1;
+JSJAC_JINGLE_BROWSERS[JSJAC_JINGLE_BROWSER_SAFARI] = 1;
+JSJAC_JINGLE_BROWSERS[JSJAC_JINGLE_BROWSER_OPERA] = 1;
+JSJAC_JINGLE_BROWSERS[JSJAC_JINGLE_BROWSER_IE] = 1;
/**
* @constant
@@ -4043,9 +4281,11 @@ JSJAC_JINGLE_SENDERS[JSJAC_JINGLE_SENDERS_RESPONDER.jingle] = JSJAC
* @default
* @public
*/
-var JSJAC_JINGLE_CREATORS = {};
-JSJAC_JINGLE_CREATORS[JSJAC_JINGLE_CREATOR_INITIATOR] = 1;
-JSJAC_JINGLE_CREATORS[JSJAC_JINGLE_CREATOR_RESPONDER] = 1;
+var JSJAC_JINGLE_SENDERS = {};
+JSJAC_JINGLE_SENDERS[JSJAC_JINGLE_SENDERS_BOTH.jingle] = JSJAC_JINGLE_SENDERS_BOTH.sdp;
+JSJAC_JINGLE_SENDERS[JSJAC_JINGLE_SENDERS_INITIATOR.jingle] = JSJAC_JINGLE_SENDERS_INITIATOR.sdp;
+JSJAC_JINGLE_SENDERS[JSJAC_JINGLE_SENDERS_NONE.jingle] = JSJAC_JINGLE_SENDERS_NONE.sdp;
+JSJAC_JINGLE_SENDERS[JSJAC_JINGLE_SENDERS_RESPONDER.jingle] = JSJAC_JINGLE_SENDERS_RESPONDER.sdp;
/**
* @constant
@@ -4055,14 +4295,9 @@ JSJAC_JINGLE_CREATORS[JSJAC_JINGLE_CREATOR_RESPONDER] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_STATUSES = {};
-JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_INACTIVE] = 1;
-JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_INITIATING] = 1;
-JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_INITIATED] = 1;
-JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_ACCEPTING] = 1;
-JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_ACCEPTED] = 1;
-JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_TERMINATING] = 1;
-JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_TERMINATED] = 1;
+var JSJAC_JINGLE_CREATORS = {};
+JSJAC_JINGLE_CREATORS[JSJAC_JINGLE_CREATOR_INITIATOR] = 1;
+JSJAC_JINGLE_CREATORS[JSJAC_JINGLE_CREATOR_RESPONDER] = 1;
/**
* @constant
@@ -4072,22 +4307,14 @@ JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_TERMINATED] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_ACTIONS = {};
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_CONTENT_ACCEPT] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_CONTENT_ADD] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_CONTENT_MODIFY] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_CONTENT_REJECT] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_CONTENT_REMOVE] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_DESCRIPTION_INFO] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_SECURITY_INFO] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_SESSION_ACCEPT] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_SESSION_INFO] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_SESSION_INITIATE] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_SESSION_TERMINATE] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_TRANSPORT_ACCEPT] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_TRANSPORT_INFO] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_TRANSPORT_REJECT] = 1;
-JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_TRANSPORT_REPLACE] = 1;
+var JSJAC_JINGLE_STATUSES = {};
+JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_INACTIVE] = 1;
+JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_INITIATING] = 1;
+JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_INITIATED] = 1;
+JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_ACCEPTING] = 1;
+JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_ACCEPTED] = 1;
+JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_TERMINATING] = 1;
+JSJAC_JINGLE_STATUSES[JSJAC_JINGLE_STATUS_TERMINATED] = 1;
/**
* @constant
@@ -4097,12 +4324,22 @@ JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_TRANSPORT_REPLACE] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_ERRORS = {};
-JSJAC_JINGLE_ERRORS[JSJAC_JINGLE_ERROR_OUT_OF_ORDER.jingle] = 1;
-JSJAC_JINGLE_ERRORS[JSJAC_JINGLE_ERROR_TIE_BREAK.jingle] = 1;
-JSJAC_JINGLE_ERRORS[JSJAC_JINGLE_ERROR_UNKNOWN_SESSION.jingle] = 1;
-JSJAC_JINGLE_ERRORS[JSJAC_JINGLE_ERROR_UNSUPPORTED_INFO.jingle] = 1;
-JSJAC_JINGLE_ERRORS[JSJAC_JINGLE_ERROR_SECURITY_REQUIRED.jingle] = 1;
+var JSJAC_JINGLE_ACTIONS = {};
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_CONTENT_ACCEPT] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_CONTENT_ADD] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_CONTENT_MODIFY] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_CONTENT_REJECT] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_CONTENT_REMOVE] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_DESCRIPTION_INFO] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_SECURITY_INFO] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_SESSION_ACCEPT] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_SESSION_INFO] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_SESSION_INITIATE] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_SESSION_TERMINATE] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_TRANSPORT_ACCEPT] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_TRANSPORT_INFO] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_TRANSPORT_REJECT] = 1;
+JSJAC_JINGLE_ACTIONS[JSJAC_JINGLE_ACTION_TRANSPORT_REPLACE] = 1;
/**
* @constant
@@ -4112,16 +4349,12 @@ JSJAC_JINGLE_ERRORS[JSJAC_JINGLE_ERROR_SECURITY_REQUIRED.jingle] = 1;
* @default
* @public
*/
-var XMPP_ERRORS = {};
-XMPP_ERRORS[XMPP_ERROR_UNEXPECTED_REQUEST.xmpp] = 1;
-XMPP_ERRORS[XMPP_ERROR_CONFLICT.xmpp] = 1;
-XMPP_ERRORS[XMPP_ERROR_ITEM_NOT_FOUND.xmpp] = 1;
-XMPP_ERRORS[XMPP_ERROR_NOT_ACCEPTABLE.xmpp] = 1;
-XMPP_ERRORS[XMPP_ERROR_FEATURE_NOT_IMPLEMENTED.xmpp] = 1;
-XMPP_ERRORS[XMPP_ERROR_SERVICE_UNAVAILABLE.xmpp] = 1;
-XMPP_ERRORS[XMPP_ERROR_REDIRECT.xmpp] = 1;
-XMPP_ERRORS[XMPP_ERROR_RESOURCE_CONSTRAINT.xmpp] = 1;
-XMPP_ERRORS[XMPP_ERROR_BAD_REQUEST.xmpp] = 1;
+var JSJAC_JINGLE_MESSAGE_ACTIONS = {};
+JSJAC_JINGLE_MESSAGE_ACTIONS[JSJAC_JINGLE_MESSAGE_ACTION_PROPOSE] = 1;
+JSJAC_JINGLE_MESSAGE_ACTIONS[JSJAC_JINGLE_MESSAGE_ACTION_RETRACT] = 1;
+JSJAC_JINGLE_MESSAGE_ACTIONS[JSJAC_JINGLE_MESSAGE_ACTION_ACCEPT] = 1;
+JSJAC_JINGLE_MESSAGE_ACTIONS[JSJAC_JINGLE_MESSAGE_ACTION_PROCEED] = 1;
+JSJAC_JINGLE_MESSAGE_ACTIONS[JSJAC_JINGLE_MESSAGE_ACTION_REJECT] = 1;
/**
* @constant
@@ -4131,24 +4364,12 @@ XMPP_ERRORS[XMPP_ERROR_BAD_REQUEST.xmpp] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_REASONS = {};
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_ALTERNATIVE_SESSION] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_BUSY] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_CANCEL] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_CONNECTIVITY_ERROR] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_DECLINE] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_EXPIRED] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_FAILED_APPLICATION] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_FAILED_TRANSPORT] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_GENERAL_ERROR] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_GONE] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_INCOMPATIBLE_PARAMETERS] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_MEDIA_ERROR] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_SECURITY_ERROR] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_SUCCESS] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_TIMEOUT] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_UNSUPPORTED_APPLICATIONS] = 1;
-JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_UNSUPPORTED_TRANSPORTS] = 1;
+var JSJAC_JINGLE_ERRORS = {};
+JSJAC_JINGLE_ERRORS[JSJAC_JINGLE_ERROR_OUT_OF_ORDER.jingle] = 1;
+JSJAC_JINGLE_ERRORS[JSJAC_JINGLE_ERROR_TIE_BREAK.jingle] = 1;
+JSJAC_JINGLE_ERRORS[JSJAC_JINGLE_ERROR_UNKNOWN_SESSION.jingle] = 1;
+JSJAC_JINGLE_ERRORS[JSJAC_JINGLE_ERROR_UNSUPPORTED_INFO.jingle] = 1;
+JSJAC_JINGLE_ERRORS[JSJAC_JINGLE_ERROR_SECURITY_REQUIRED.jingle] = 1;
/**
* @constant
@@ -4158,13 +4379,16 @@ JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_UNSUPPORTED_TRANSPORTS] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_SESSION_INFOS = {};
-JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_ACTIVE] = 1;
-JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_HOLD] = 1;
-JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_MUTE] = 1;
-JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_RINGING] = 1;
-JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_UNHOLD] = 1;
-JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_UNMUTE] = 1;
+var XMPP_ERRORS = {};
+XMPP_ERRORS[XMPP_ERROR_UNEXPECTED_REQUEST.xmpp] = 1;
+XMPP_ERRORS[XMPP_ERROR_CONFLICT.xmpp] = 1;
+XMPP_ERRORS[XMPP_ERROR_ITEM_NOT_FOUND.xmpp] = 1;
+XMPP_ERRORS[XMPP_ERROR_NOT_ACCEPTABLE.xmpp] = 1;
+XMPP_ERRORS[XMPP_ERROR_FEATURE_NOT_IMPLEMENTED.xmpp] = 1;
+XMPP_ERRORS[XMPP_ERROR_SERVICE_UNAVAILABLE.xmpp] = 1;
+XMPP_ERRORS[XMPP_ERROR_REDIRECT.xmpp] = 1;
+XMPP_ERRORS[XMPP_ERROR_RESOURCE_CONSTRAINT.xmpp] = 1;
+XMPP_ERRORS[XMPP_ERROR_BAD_REQUEST.xmpp] = 1;
/**
* @constant
@@ -4174,9 +4398,24 @@ JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_UNMUTE] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_MEDIAS = {};
-JSJAC_JINGLE_MEDIAS[JSJAC_JINGLE_MEDIA_AUDIO] = { label: '0' };
-JSJAC_JINGLE_MEDIAS[JSJAC_JINGLE_MEDIA_VIDEO] = { label: '1' };
+var JSJAC_JINGLE_REASONS = {};
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_ALTERNATIVE_SESSION] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_BUSY] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_CANCEL] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_CONNECTIVITY_ERROR] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_DECLINE] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_EXPIRED] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_FAILED_APPLICATION] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_FAILED_TRANSPORT] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_GENERAL_ERROR] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_GONE] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_INCOMPATIBLE_PARAMETERS] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_MEDIA_ERROR] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_SECURITY_ERROR] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_SUCCESS] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_TIMEOUT] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_UNSUPPORTED_APPLICATIONS] = 1;
+JSJAC_JINGLE_REASONS[JSJAC_JINGLE_REASON_UNSUPPORTED_TRANSPORTS] = 1;
/**
* @constant
@@ -4186,9 +4425,13 @@ JSJAC_JINGLE_MEDIAS[JSJAC_JINGLE_MEDIA_VIDEO] = { lab
* @default
* @public
*/
-var JSJAC_JINGLE_VIDEO_SOURCES = {};
-JSJAC_JINGLE_VIDEO_SOURCES[JSJAC_JINGLE_VIDEO_SOURCE_CAMERA] = 1;
-JSJAC_JINGLE_VIDEO_SOURCES[JSJAC_JINGLE_VIDEO_SOURCE_SCREEN] = 1;
+var JSJAC_JINGLE_SESSION_INFOS = {};
+JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_ACTIVE] = 1;
+JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_HOLD] = 1;
+JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_MUTE] = 1;
+JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_RINGING] = 1;
+JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_UNHOLD] = 1;
+JSJAC_JINGLE_SESSION_INFOS[JSJAC_JINGLE_SESSION_INFO_UNMUTE] = 1;
/**
* @constant
@@ -4198,13 +4441,9 @@ JSJAC_JINGLE_VIDEO_SOURCES[JSJAC_JINGLE_VIDEO_SOURCE_SCREEN] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_MESSAGE_TYPES = {};
-JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_ALL] = 1;
-JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_NORMAL] = 1;
-JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_CHAT] = 1;
-JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_HEADLINE] = 1;
-JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_GROUPCHAT] = 1;
-JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_ERROR] = 1;
+var JSJAC_JINGLE_MEDIAS = {};
+JSJAC_JINGLE_MEDIAS[JSJAC_JINGLE_MEDIA_AUDIO] = { label: '0' };
+JSJAC_JINGLE_MEDIAS[JSJAC_JINGLE_MEDIA_VIDEO] = { label: '1' };
/**
* @constant
@@ -4214,11 +4453,9 @@ JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_ERROR] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_PRESENCE_TYPES = {};
-JSJAC_JINGLE_PRESENCE_TYPES[JSJAC_JINGLE_PRESENCE_TYPE_ALL] = 1;
-JSJAC_JINGLE_PRESENCE_TYPES[JSJAC_JINGLE_PRESENCE_TYPE_AVAILABLE] = 1;
-JSJAC_JINGLE_PRESENCE_TYPES[JSJAC_JINGLE_PRESENCE_TYPE_UNAVAILABLE] = 1;
-JSJAC_JINGLE_PRESENCE_TYPES[JSJAC_JINGLE_PRESENCE_TYPE_ERROR] = 1;
+var JSJAC_JINGLE_VIDEO_SOURCES = {};
+JSJAC_JINGLE_VIDEO_SOURCES[JSJAC_JINGLE_VIDEO_SOURCE_CAMERA] = 1;
+JSJAC_JINGLE_VIDEO_SOURCES[JSJAC_JINGLE_VIDEO_SOURCE_SCREEN] = 1;
/**
* @constant
@@ -4228,12 +4465,42 @@ JSJAC_JINGLE_PRESENCE_TYPES[JSJAC_JINGLE_PRESENCE_TYPE_ERROR] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_IQ_TYPES = {};
-JSJAC_JINGLE_IQ_TYPES[JSJAC_JINGLE_IQ_TYPE_ALL] = 1;
-JSJAC_JINGLE_IQ_TYPES[JSJAC_JINGLE_IQ_TYPE_RESULT] = 1;
-JSJAC_JINGLE_IQ_TYPES[JSJAC_JINGLE_IQ_TYPE_SET] = 1;
-JSJAC_JINGLE_IQ_TYPES[JSJAC_JINGLE_IQ_TYPE_GET] = 1;
-JSJAC_JINGLE_IQ_TYPES[JSJAC_JINGLE_IQ_TYPE_ERROR] = 1;
+var JSJAC_JINGLE_MESSAGE_TYPES = {};
+JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_ALL] = 1;
+JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_NORMAL] = 1;
+JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_CHAT] = 1;
+JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_HEADLINE] = 1;
+JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_GROUPCHAT] = 1;
+JSJAC_JINGLE_MESSAGE_TYPES[JSJAC_JINGLE_MESSAGE_TYPE_ERROR] = 1;
+
+/**
+ * @constant
+ * @global
+ * @type {Object}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_PRESENCE_TYPES = {};
+JSJAC_JINGLE_PRESENCE_TYPES[JSJAC_JINGLE_PRESENCE_TYPE_ALL] = 1;
+JSJAC_JINGLE_PRESENCE_TYPES[JSJAC_JINGLE_PRESENCE_TYPE_AVAILABLE] = 1;
+JSJAC_JINGLE_PRESENCE_TYPES[JSJAC_JINGLE_PRESENCE_TYPE_UNAVAILABLE] = 1;
+JSJAC_JINGLE_PRESENCE_TYPES[JSJAC_JINGLE_PRESENCE_TYPE_ERROR] = 1;
+
+/**
+ * @constant
+ * @global
+ * @type {Object}
+ * @readonly
+ * @default
+ * @public
+ */
+var JSJAC_JINGLE_IQ_TYPES = {};
+JSJAC_JINGLE_IQ_TYPES[JSJAC_JINGLE_IQ_TYPE_ALL] = 1;
+JSJAC_JINGLE_IQ_TYPES[JSJAC_JINGLE_IQ_TYPE_RESULT] = 1;
+JSJAC_JINGLE_IQ_TYPES[JSJAC_JINGLE_IQ_TYPE_SET] = 1;
+JSJAC_JINGLE_IQ_TYPES[JSJAC_JINGLE_IQ_TYPE_GET] = 1;
+JSJAC_JINGLE_IQ_TYPES[JSJAC_JINGLE_IQ_TYPE_ERROR] = 1;
@@ -4249,7 +4516,7 @@ JSJAC_JINGLE_IQ_TYPES[JSJAC_JINGLE_IQ_TYPE_ERROR] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_MUJI_ACTIONS = {};
+var JSJAC_JINGLE_MUJI_ACTIONS = {};
JSJAC_JINGLE_MUJI_ACTIONS[JSJAC_JINGLE_MUJI_ACTION_PREPARE] = 1;
JSJAC_JINGLE_MUJI_ACTIONS[JSJAC_JINGLE_MUJI_ACTION_INITIATE] = 1;
JSJAC_JINGLE_MUJI_ACTIONS[JSJAC_JINGLE_MUJI_ACTION_LEAVE] = 1;
@@ -4262,7 +4529,7 @@ JSJAC_JINGLE_MUJI_ACTIONS[JSJAC_JINGLE_MUJI_ACTION_LEAVE] = 1;
* @default
* @public
*/
-var JSJAC_JINGLE_MUJI_STATUS = {};
+var JSJAC_JINGLE_MUJI_STATUS = {};
JSJAC_JINGLE_MUJI_STATUS[JSJAC_JINGLE_MUJI_STATUS_INACTIVE] = 1;
JSJAC_JINGLE_MUJI_STATUS[JSJAC_JINGLE_MUJI_STATUS_PREPARING] = 1;
JSJAC_JINGLE_MUJI_STATUS[JSJAC_JINGLE_MUJI_STATUS_PREPARED] = 1;
@@ -4270,6 +4537,7 @@ JSJAC_JINGLE_MUJI_STATUS[JSJAC_JINGLE_MUJI_STATUS_INITIATING] = 1;
JSJAC_JINGLE_MUJI_STATUS[JSJAC_JINGLE_MUJI_STATUS_INITIATED] = 1;
JSJAC_JINGLE_MUJI_STATUS[JSJAC_JINGLE_MUJI_STATUS_LEAVING] = 1;
JSJAC_JINGLE_MUJI_STATUS[JSJAC_JINGLE_MUJI_STATUS_LEFT] = 1;
+
/**
* @fileoverview JSJaC Jingle library - Storage layer
*
@@ -4320,6 +4588,13 @@ var JSJaCJingleStorage = new (ring.create(
this._sessions[JSJAC_JINGLE_SESSION_MUJI] = {};
/**
+ * @type {Object}
+ * @default
+ * @private
+ */
+ this._broadcast_ids = {};
+
+ /**
* @type {Function}
* @default
* @private
@@ -4331,6 +4606,27 @@ var JSJaCJingleStorage = new (ring.create(
* @default
* @private
*/
+ this._single_prepare = undefined;
+
+ /**
+ * @type {Function}
+ * @default
+ * @private
+ */
+ this._single_proceed = undefined;
+
+ /**
+ * @type {Function}
+ * @default
+ * @private
+ */
+ this._single_reject = undefined;
+
+ /**
+ * @type {Function}
+ * @default
+ * @private
+ */
this._muji_invite = undefined;
/**
@@ -4386,7 +4682,7 @@ var JSJaCJingleStorage = new (ring.create(
/**
- * JSJSAC JINGLE GETTERS
+ * JSJSAC JINGLE STORAGE GETTERS
*/
/**
@@ -4408,6 +4704,18 @@ var JSJaCJingleStorage = new (ring.create(
},
/**
+ * Gets the broadcast_ids storage
+ * @public
+ * @returns {Object} Broadcast ID medias
+ */
+ get_broadcast_ids: function(id) {
+ if(id in this._broadcast_ids)
+ return this._broadcast_ids[id];
+
+ return null;
+ },
+
+ /**
* Gets the Single initiate function
* @public
* @returns {Function} Single initiate
@@ -4429,6 +4737,42 @@ var JSJaCJingleStorage = new (ring.create(
},
/**
+ * Gets the Single prepare function
+ * @public
+ * @returns {Function} Single prepare
+ */
+ get_single_prepare: function() {
+ if(typeof this._single_prepare == 'function')
+ return this._single_prepare;
+
+ return function(stanza) {};
+ },
+
+ /**
+ * Gets the Single proceed function
+ * @public
+ * @returns {Function} Single proceed
+ */
+ get_single_proceed: function() {
+ if(typeof this._single_proceed == 'function')
+ return this._single_proceed;
+
+ return function(stanza) {};
+ },
+
+ /**
+ * Gets the Single reject function
+ * @public
+ * @returns {Function} Single reject
+ */
+ get_single_reject: function() {
+ if(typeof this._single_reject == 'function')
+ return this._single_reject;
+
+ return function(stanza) {};
+ },
+
+ /**
* Gets the Muji invite function
* @public
* @returns {Function} Muji invite
@@ -4497,7 +4841,7 @@ var JSJaCJingleStorage = new (ring.create(
/**
- * JSJSAC JINGLE SETTERS
+ * JSJSAC JINGLE STORAGE SETTERS
*/
/**
@@ -4519,6 +4863,20 @@ var JSJaCJingleStorage = new (ring.create(
},
/**
+ * Sets the broadcast IDs storage
+ * @public
+ * @param {String} id
+ * @param {Object} medias
+ * @param {Boolean} [proceed_unset]
+ */
+ set_broadcast_ids: function(id, medias, proceed_unset) {
+ this._broadcast_ids[id] = medias;
+
+ if(proceed_unset === true && id in this._broadcast_ids)
+ delete this._broadcast_ids[id];
+ },
+
+ /**
* Sets the Single initiate function
* @public
* @param {Function} Single initiate
@@ -4528,6 +4886,33 @@ var JSJaCJingleStorage = new (ring.create(
},
/**
+ * Sets the Single prepare function
+ * @public
+ * @param {Function} Single prepare
+ */
+ set_single_prepare: function(single_prepare) {
+ this._single_prepare = single_prepare;
+ },
+
+ /**
+ * Sets the Single proceed function
+ * @public
+ * @param {Function} Single proceed
+ */
+ set_single_proceed: function(single_proceed) {
+ this._single_proceed = single_proceed;
+ },
+
+ /**
+ * Sets the Single reject function
+ * @public
+ * @param {Function} Single reject
+ */
+ set_single_reject: function(single_reject) {
+ this._single_reject = single_reject;
+ },
+
+ /**
* Sets the Muji invite function
* @public
* @param {Function} Muji invite
@@ -4582,6 +4967,7 @@ var JSJaCJingleStorage = new (ring.create(
},
}
))();
+
/**
* @fileoverview JSJaC Jingle library - Utilities
*
@@ -5929,7 +6315,8 @@ var JSJaCJingleUtils = ring.create(
// Payload-type
if(cs_d_payload) {
var i, j,
- cur_ssrc_id, cur_cs_d_ssrc_group_semantics,
+ cur_ssrc_id,
+ cur_cs_d_ssrc_group_semantics, cur_cs_d_ssrc_group_semantics_sub,
cs_d_p, payload_type;
for(i in cs_d_payload) {
@@ -5975,18 +6362,22 @@ var JSJaCJingleUtils = ring.create(
if(cs_d_ssrc_group) {
for(cur_cs_d_ssrc_group_semantics in cs_d_ssrc_group) {
for(j in cs_d_ssrc_group[cur_cs_d_ssrc_group_semantics]) {
- var ssrc_group = description.appendChild(stanza.buildNode('ssrc-group', {
- 'semantics': cur_cs_d_ssrc_group_semantics,
- 'xmlns': NS_JINGLE_APPS_RTP_SSMA
- }));
-
- this.stanza_build_node(
- stanza,
- ssrc_group,
- cs_d_ssrc_group[cur_cs_d_ssrc_group_semantics][j].sources,
- 'source',
- NS_JINGLE_APPS_RTP_SSMA
- );
+ cur_cs_d_ssrc_group_semantics_sub = cs_d_ssrc_group[cur_cs_d_ssrc_group_semantics][j];
+
+ if(cur_cs_d_ssrc_group_semantics_sub !== undefined) {
+ var ssrc_group = description.appendChild(stanza.buildNode('ssrc-group', {
+ 'semantics': cur_cs_d_ssrc_group_semantics,
+ 'xmlns': NS_JINGLE_APPS_RTP_SSMA
+ }));
+
+ this.stanza_build_node(
+ stanza,
+ ssrc_group,
+ cur_cs_d_ssrc_group_semantics_sub.sources,
+ 'source',
+ NS_JINGLE_APPS_RTP_SSMA
+ );
+ }
}
}
}
@@ -6825,6 +7216,7 @@ var JSJaCJingleUtils = ring.create(
},
}
);
+
/**
* @fileoverview JSJaC Jingle library - SDP tools
*
@@ -7517,7 +7909,7 @@ var JSJaCJingleSDP = ring.create(
if(!sdp_candidate) return candidate;
var error = 0;
- var matches = R_WEBRTC_SDP_CANDIDATE.exec(sdp_candidate);
+ var matches = R_WEBRTC_DATA_CANDIDATE.exec(sdp_candidate);
// Matches!
if(matches) {
@@ -7556,15 +7948,16 @@ var JSJaCJingleSDP = ring.create(
// Convert SDP raw data to an object
var candidate_obj = this._parse_candidate(candidate_data);
+ var candidate_name = this.parent.utils.name_generate(candidate_media);
this.parent._set_candidates_local(
- this.parent.utils.name_generate(candidate_media),
+ candidate_name,
candidate_obj
);
// Enqueue candidate
this.parent._set_candidates_queue_local(
- this.parent.utils.name_generate(candidate_media),
+ candidate_name,
candidate_obj
);
},
@@ -8161,6 +8554,7 @@ var JSJaCJingleSDP = ring.create(
},
}
);
+
/**
* @fileoverview JSJaC Jingle library - Base call lib
*
@@ -8918,7 +9312,10 @@ var __JSJaCJingleBase = ring.create(
},
function(e) {
- if(_this.get_sdp_trace()) _this.get_debug().log('[JSJaCJingle:base] _peer_got_description > SDP (local:error)' + '\n\n' + (e.message || e.name || 'Unknown error'), 4);
+ var error_str = (typeof e == 'string') ? e : null;
+ error_str = (error_str || e.message || e.name || 'Unknown error');
+
+ if(_this.get_sdp_trace()) _this.get_debug().log('[JSJaCJingle:base] _peer_got_description > SDP (local:error)' + '\n\n' + error_str, 1);
// Error (descriptions are incompatible)
}
@@ -9918,6 +10315,7 @@ var __JSJaCJingleBase = ring.create(
},
}
);
+
/**
* @fileoverview JSJaC Jingle library - Single (one-to-one) call lib
*
@@ -9962,7 +10360,12 @@ var __JSJaCJingleBase = ring.create(
* @property {Function} [args.session_terminate_success] - The terminate success custom handler.
* @property {Function} [args.session_terminate_error] - The terminate error custom handler.
* @property {Function} [args.session_terminate_request] - The terminate request custom handler.
+ * @property {Function} [args.stream_add] - The stream add custom handler.
+ * @property {Function} [args.stream_remove] - The stream remove custom handler.
+ * @property {Function} [args.stream_connected] - The stream connected custom handler.
+ * @property {Function} [args.stream_disconnected] - The stream disconnected custom handler.
* @property {DOM} [args.remote_view] - The path to the remote stream view element.
+ * @property {DOM} [args.sid] - The session ID (forced).
*/
var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
/** @lends JSJaCJingleSingle.prototype */
@@ -10101,6 +10504,38 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
*/
this._session_terminate_request = args.session_terminate_request;
+ if(args && args.stream_add)
+ /**
+ * @member {Function}
+ * @default
+ * @private
+ */
+ this._stream_add = args.stream_add;
+
+ if(args && args.stream_remove)
+ /**
+ * @member {Function}
+ * @default
+ * @private
+ */
+ this._stream_remove = args.stream_remove;
+
+ if(args && args.stream_connected)
+ /**
+ * @member {Function}
+ * @default
+ * @private
+ */
+ this._stream_connected = args.stream_connected;
+
+ if(args && args.stream_disconnected)
+ /**
+ * @member {Function}
+ * @default
+ * @private
+ */
+ this._stream_disconnected = args.stream_disconnected;
+
if(args && args.remote_view)
/**
* @member {Object}
@@ -10109,6 +10544,14 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
*/
this._remote_view = [args.remote_view];
+ if(args && args.sid)
+ /**
+ * @member {String}
+ * @default
+ * @private
+ */
+ this._sid = [args.sid];
+
/**
* @member {Object}
* @default
@@ -10152,6 +10595,13 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
this._candidates_queue_remote = {};
/**
+ * @member {String|Object}
+ * @default
+ * @private
+ */
+ this._last_ice_state = null;
+
+ /**
* @constant
* @member {String}
* @default
@@ -10219,7 +10669,8 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
this._set_status(JSJAC_JINGLE_STATUS_INITIATING);
// Set session values
- this._set_sid(this.utils.generate_sid());
+ if(!this.get_sid()) this._set_sid(this.utils.generate_sid());
+
this._set_initiator(this.utils.connection_jid());
this._set_responder(this.get_to());
@@ -12317,6 +12768,24 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
* @type {Function}
*/
this.get_peer_connection().oniceconnectionstatechange = function(data) {
+ switch(this.iceConnectionState) {
+ case JSJAC_JINGLE_ICE_CONNECTION_STATE_CONNECTED:
+ case JSJAC_JINGLE_ICE_CONNECTION_STATE_COMPLETED:
+ if(_this.get_last_ice_state() !== JSJAC_JINGLE_ICE_CONNECTION_STATE_CONNECTED) {
+ /* @function */
+ (_this.get_stream_connected()).bind(this)(_this, data);
+ _this._set_last_ice_state(JSJAC_JINGLE_ICE_CONNECTION_STATE_CONNECTED);
+ } break;
+
+ case JSJAC_JINGLE_ICE_CONNECTION_STATE_DISCONNECTED:
+ case JSJAC_JINGLE_ICE_CONNECTION_STATE_CLOSED:
+ if(_this.get_last_ice_state() !== JSJAC_JINGLE_ICE_CONNECTION_STATE_DISCONNECTED) {
+ /* @function */
+ (_this.get_stream_disconnected()).bind(this)(_this, data);
+ _this._set_last_ice_state(JSJAC_JINGLE_ICE_CONNECTION_STATE_DISCONNECTED);
+ } break;
+ }
+
_this._peer_connection_callback_oniceconnectionstatechange.bind(this)(_this, data);
};
@@ -12326,6 +12795,8 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
* @type {Function}
*/
this.get_peer_connection().onaddstream = function(data) {
+ /* @function */
+ (_this.get_stream_add()).bind(this)(_this, data);
_this._peer_connection_callback_onaddstream.bind(this)(_this, data);
};
@@ -12335,6 +12806,8 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
* @type {Function}
*/
this.get_peer_connection().onremovestream = function(data) {
+ /* @function */
+ (_this.get_stream_remove()).bind(this)(_this, data);
_this._peer_connection_callback_onremovestream.bind(this)(_this, data);
};
} catch(e) {
@@ -12862,6 +13335,54 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
},
/**
+ * Gets the stream add event callback function
+ * @public
+ * @event JSJaCJingleSingle#stream_add
+ * @returns {Function} Callback function
+ */
+ get_stream_add: function() {
+ return this._shortcut_get_handler(
+ this._stream_add
+ );
+ },
+
+ /**
+ * Gets the stream remove event callback function
+ * @public
+ * @event JSJaCJingleSingle#stream_remove
+ * @returns {Function} Callback function
+ */
+ get_stream_remove: function() {
+ return this._shortcut_get_handler(
+ this._stream_remove
+ );
+ },
+
+ /**
+ * Gets the stream connected event callback function
+ * @public
+ * @event JSJaCJingleSingle#stream_connected
+ * @returns {Function} Callback function
+ */
+ get_stream_connected: function() {
+ return this._shortcut_get_handler(
+ this._stream_connected
+ );
+ },
+
+ /**
+ * Gets the stream disconnected event callback function
+ * @public
+ * @event JSJaCJingleSingle#stream_disconnected
+ * @returns {Function} Callback function
+ */
+ get_stream_disconnected: function() {
+ return this._shortcut_get_handler(
+ this._stream_disconnected
+ );
+ },
+
+ /**
* Gets the prepended ID
* @public
* @returns {String} Prepended ID value
@@ -12962,6 +13483,15 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
return this._candidates_queue_remote;
},
+ /**
+ * Gets the last ICE state value
+ * @public
+ * @returns {String|Object} Last ICE state value
+ */
+ get_last_ice_state: function() {
+ return this._last_ice_state;
+ },
+
/**
@@ -13113,6 +13643,42 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
},
/**
+ * Sets the stream add event callback function
+ * @private
+ * @param {Function} stream_add
+ */
+ _set_stream_add: function(stream_add) {
+ this._stream_add = stream_add;
+ },
+
+ /**
+ * Sets the stream remove event callback function
+ * @private
+ * @param {Function} stream_remove
+ */
+ _set_stream_remove: function(stream_remove) {
+ this._stream_remove = stream_remove;
+ },
+
+ /**
+ * Sets the stream connected event callback function
+ * @private
+ * @param {Function} stream_connected
+ */
+ _set_stream_connected: function(stream_connected) {
+ this._stream_connected = stream_connected;
+ },
+
+ /**
+ * Sets the stream disconnected event callback function
+ * @private
+ * @param {Function} stream_disconnected
+ */
+ _set_stream_disconnected: function(stream_disconnected) {
+ this._stream_disconnected = stream_disconnected;
+ },
+
+ /**
* Sets the termination reason
* @private
* @param {String} reason
@@ -13271,8 +13837,18 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
this.get_debug().log('[JSJaCJingle:single] _set_candidates_remote_add > ' + e, 1);
}
},
+
+ /**
+ * Sets the last ICE state value
+ * @private
+ * @param {String|Object} last_ice_state
+ */
+ _set_last_ice_state: function(last_ice_state) {
+ this._last_ice_state = last_ice_state;
+ },
}
);
+
/**
* @fileoverview JSJaC Jingle library - Multi-user call lib
*
@@ -13336,6 +13912,10 @@ var JSJaCJingleSingle = ring.create([__JSJaCJingleBase],
* @property {Function} [args.participant_session_terminate_success] - The participant session terminate success custom handler.
* @property {Function} [args.participant_session_terminate_error] - The participant session terminate error custom handler.
* @property {Function} [args.participant_session_terminate_request] - The participant session terminate request custom handler.
+ * @property {Function} [args.participant_stream_add] - The participant stream add custom handler.
+ * @property {Function} [args.participant_stream_remove] - The participant stream remove custom handler.
+ * @property {Function} [args.participant_stream_connected] - The participant stream connected custom handler.
+ * @property {Function} [args.participant_stream_disconnected] - The participant stream disconnected custom handler.
* @property {Function} [args.add_remote_view] - The remote view media add (audio/video) custom handler.
* @property {Function} [args.remove_remote_view] - The remote view media removal (audio/video) custom handler.
*/
@@ -13604,6 +14184,38 @@ var JSJaCJingleMuji = ring.create([__JSJaCJingleBase],
*/
this._participant_session_terminate_request = args.participant_session_terminate_request;
+ if(args && args.participant_stream_add)
+ /**
+ * @member {Function}
+ * @default
+ * @private
+ */
+ this._participant_stream_add = args.participant_stream_add;
+
+ if(args && args.participant_stream_remove)
+ /**
+ * @member {Function}
+ * @default
+ * @private
+ */
+ this._participant_stream_remove = args.participant_stream_remove;
+
+ if(args && args.participant_stream_connected)
+ /**
+ * @member {Function}
+ * @default
+ * @private
+ */
+ this._participant_stream_connected = args.participant_stream_connected;
+
+ if(args && args.participant_stream_disconnected)
+ /**
+ * @member {Function}
+ * @default
+ * @private
+ */
+ this._participant_stream_disconnected = args.participant_stream_disconnected;
+
if(args && args.add_remote_view)
/**
* @member {Function}
@@ -15226,6 +15838,78 @@ var JSJaCJingleMuji = ring.create([__JSJaCJingleBase],
}
},
+ /**
+ * Handles the stream add event
+ * @private
+ * @event JSJaCJingleMuji#_handle_participant_stream_add
+ * @param {JSJaCJingleSingle} session
+ * @param {MediaStreamEvent} data
+ */
+ _handle_participant_stream_add: function(session, data) {
+ this.get_debug().log('[JSJaCJingle:muji] _handle_participant_stream_add', 4);
+
+ try {
+ /* @function */
+ (this.get_participant_stream_add())(this, session, data);
+ } catch(e) {
+ this.get_debug().log('[JSJaCJingle:muji] _handle_participant_stream_add > ' + e, 1);
+ }
+ },
+
+ /**
+ * Handles the stream remove event
+ * @private
+ * @event JSJaCJingleMuji#_handle_participant_stream_remove
+ * @param {JSJaCJingleSingle} session
+ * @param {MediaStreamEvent} data
+ */
+ _handle_participant_stream_remove: function(session, data) {
+ this.get_debug().log('[JSJaCJingle:muji] _handle_participant_stream_remove', 4);
+
+ try {
+ /* @function */
+ (this.get_participant_stream_remove())(this, session, data);
+ } catch(e) {
+ this.get_debug().log('[JSJaCJingle:muji] _handle_participant_stream_remove > ' + e, 1);
+ }
+ },
+
+ /**
+ * Handles the stream connected event
+ * @private
+ * @event JSJaCJingleMuji#_handle_participant_stream_connected
+ * @param {JSJaCJingleSingle} session
+ * @param {MediaStreamEvent} data
+ */
+ _handle_participant_stream_connected: function(session, data) {
+ this.get_debug().log('[JSJaCJingle:muji] _handle_participant_stream_connected', 4);
+
+ try {
+ /* @function */
+ (this.get_participant_stream_connected())(this, session, data);
+ } catch(e) {
+ this.get_debug().log('[JSJaCJingle:muji] _handle_participant_stream_connected > ' + e, 1);
+ }
+ },
+
+ /**
+ * Handles the stream disconnected event
+ * @private
+ * @event JSJaCJingleMuji#_handle_participant_stream_disconnected
+ * @param {JSJaCJingleSingle} session
+ * @param {MediaStreamEvent} data
+ */
+ _handle_participant_stream_disconnected: function(session, data) {
+ this.get_debug().log('[JSJaCJingle:muji] _handle_participant_stream_disconnected', 4);
+
+ try {
+ /* @function */
+ (this.get_participant_stream_disconnected())(this, session, data);
+ } catch(e) {
+ this.get_debug().log('[JSJaCJingle:muji] _handle_participant_stream_disconnected > ' + e, 1);
+ }
+ },
+
/**
@@ -15747,6 +16431,11 @@ var JSJaCJingleMuji = ring.create([__JSJaCJingleBase],
args.session_terminate_success = this._handle_participant_session_terminate_success.bind(this);
args.session_terminate_error = this._handle_participant_session_terminate_error.bind(this);
args.session_terminate_request = this._handle_participant_session_terminate_request.bind(this);
+
+ args.stream_add = this._handle_participant_stream_add.bind(this);
+ args.stream_remove = this._handle_participant_stream_remove.bind(this);
+ args.stream_connected = this._handle_participant_stream_connected.bind(this);
+ args.stream_disconnected = this._handle_participant_stream_disconnected.bind(this);
} catch(e) {
this.get_debug().log('[JSJaCJingle:muji] _generate_participant_session_args > ' + e, 1);
} finally {
@@ -16328,6 +17017,54 @@ var JSJaCJingleMuji = ring.create([__JSJaCJingleBase],
},
/**
+ * Gets the participant stream add event callback function
+ * @public
+ * @event JSJaCJingleMuji#get_participant_stream_add
+ * @returns {Function} Participant stream add event callback function
+ */
+ get_participant_stream_add: function() {
+ return this._shortcut_get_handler(
+ this._participant_stream_add
+ );
+ },
+
+ /**
+ * Gets the participant stream remove event callback function
+ * @public
+ * @event JSJaCJingleMuji#get_participant_stream_remove
+ * @returns {Function} Participant stream remove event callback function
+ */
+ get_participant_stream_remove: function() {
+ return this._shortcut_get_handler(
+ this._participant_stream_remove
+ );
+ },
+
+ /**
+ * Gets the participant stream connected event callback function
+ * @public
+ * @event JSJaCJingleMuji#get_participant_stream_connected
+ * @returns {Function} Participant stream connected event callback function
+ */
+ get_participant_stream_connected: function() {
+ return this._shortcut_get_handler(
+ this._participant_stream_connected
+ );
+ },
+
+ /**
+ * Gets the participant stream disconnected event callback function
+ * @public
+ * @event JSJaCJingleMuji#get_participant_stream_disconnected
+ * @returns {Function} Participant stream disconnected event callback function
+ */
+ get_participant_stream_disconnected: function() {
+ return this._shortcut_get_handler(
+ this._participant_stream_disconnected
+ );
+ },
+
+ /**
* Gets the remote view add callback function
* @public
* @event JSJaCJingleMuji#get_add_remote_view
@@ -16709,6 +17446,42 @@ var JSJaCJingleMuji = ring.create([__JSJaCJingleBase],
},
/**
+ * Sets the participant stream add event callback function
+ * @private
+ * @param {Function} participant_stream_add
+ */
+ _set_participant_stream_add: function(participant_stream_add) {
+ this._participant_stream_add = participant_stream_add;
+ },
+
+ /**
+ * Sets the participant stream remove event callback function
+ * @private
+ * @param {Function} participant_stream_remove
+ */
+ _set_participant_stream_remove: function(participant_stream_remove) {
+ this._participant_stream_remove = participant_stream_remove;
+ },
+
+ /**
+ * Sets the participant stream connected event callback function
+ * @private
+ * @param {Function} participant_stream_connected
+ */
+ _set_participant_stream_connected: function(participant_stream_connected) {
+ this._participant_stream_connected = participant_stream_connected;
+ },
+
+ /**
+ * Sets the participant stream disconnected event callback function
+ * @private
+ * @param {Function} participant_stream_disconnected
+ */
+ _set_participant_stream_disconnected: function(participant_stream_disconnected) {
+ this._participant_stream_disconnected = participant_stream_disconnected;
+ },
+
+ /**
* Sets the add remote view callback function
* @private
* @param {Function} add_remote_view
@@ -16790,6 +17563,442 @@ var JSJaCJingleMuji = ring.create([__JSJaCJingleBase],
}
);
/**
+ * @fileoverview JSJaC Jingle library - Initialization broadcast lib (XEP-0353)
+ *
+ * @url https://github.com/valeriansaliou/jsjac-jingle
+ * @depends https://github.com/sstrigler/JSJaC
+ * @author Valérian Saliou https://valeriansaliou.name/
+ * @license Mozilla Public License v2.0 (MPL v2.0)
+ */
+
+
+/** @module jsjac-jingle/broadcast */
+/** @exports JSJaCJingleBroadcast */
+
+
+/**
+ * Library initialization class.
+ * @class
+ * @classdesc Initialization broadcast class.
+ * @requires nicolas-van/ring.js
+ * @requires jsjac-jingle/main
+ * @see {@link http://ringjs.neoname.eu/|Ring.js}
+ * @see {@link http://stefan-strigler.de/jsjac-1.3.4/doc/|JSJaC Documentation}
+ * @see {@link http://xmpp.org/extensions/xep-0353.html|XEP-0353: Jingle Message Initiation}
+ */
+var JSJaCJingleBroadcast = new (ring.create(
+ /** @lends JSJaCJingleBroadcast.prototype */
+ {
+ /**
+ * Proposes a call
+ * @public
+ * @param {String} to
+ * @param {Object} medias
+ */
+ propose: function(to, medias, cb_timeout) {
+ try {
+ var self = this;
+ var id = this._send_remote_propose(to, medias);
+
+ if(typeof cb_timeout == 'function') {
+ setTimeout(function() {
+ // Call answered
+ if(self._exists_id(id) === false) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] propose > Propose successful.', 4);
+ } else {
+ cb_timeout();
+
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] propose > Propose timeout.', 2);
+ }
+ }, (JSJAC_JINGLE_BROADCAST_TIMEOUT * 1000));
+ }
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] propose > ' + e, 1);
+ }
+ },
+
+ /**
+ * Retracts from a call
+ * @public
+ * @param {String} to
+ * @param {String} id
+ */
+ retract: function(to, id) {
+ try {
+ this._send_remote_retract(to, id);
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] retract > ' + e, 1);
+ }
+ },
+
+ /**
+ * Accepts a call
+ * @public
+ * @param {String} to
+ * @param {String} id
+ * @param {Object} medias
+ */
+ accept: function(to, id, medias) {
+ try {
+ this._register_id(id, medias);
+
+ this._send_local_accept(id);
+ this._send_remote_proceed(to, id);
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] accept > ' + e, 1);
+ }
+ },
+
+ /**
+ * Rejects a call
+ * @public
+ * @param {String} to
+ * @param {String} id
+ * @param {Object} medias
+ */
+ reject: function(to, id, medias) {
+ try {
+ this._register_id(id, medias);
+
+ this._send_local_reject(id);
+ this._send_remote_retract(to, id);
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] reject > ' + e, 1);
+ }
+ },
+
+ /**
+ * Handles a call
+ * @public
+ * @param {JSJaCPacket} stanza
+ */
+ handle: function(stanza) {
+ var i,
+ is_handled, stanza_child,
+ description, cur_description, cur_media,
+ proposed_medias,
+ id;
+
+ try {
+ is_handled = false;
+
+ stanza_child = stanza.getChild(
+ '*', NS_JINGLE_MESSAGE
+ );
+
+ if(stanza_child) {
+ switch(stanza_child.tagName) {
+ case JSJAC_JINGLE_MESSAGE_ACTION_PROPOSE:
+ proposed_medias = {};
+
+ description = stanza_child.getElementsByTagNameNS(
+ NS_JINGLE_APPS_RTP, 'description'
+ );
+
+ for(i = 0; i < description.length; i++) {
+ cur_description = description[i];
+
+ if(cur_description) {
+ cur_media = cur_description.getAttribute('media');
+
+ if(cur_media && cur_media in JSJAC_JINGLE_MEDIAS) {
+ proposed_medias[cur_media] = 1;
+ }
+ }
+ }
+
+ JSJaCJingleStorage.get_single_prepare()(stanza, proposed_medias);
+
+ is_handled = true; break;
+
+ case JSJAC_JINGLE_MESSAGE_ACTION_PROCEED:
+ JSJaCJingleStorage.get_single_proceed()(stanza);
+
+ id = this.get_call_id(stanza);
+ if(id) this._unregister_id(id);
+
+ is_handled = true; break;
+
+ case JSJAC_JINGLE_MESSAGE_ACTION_REJECT:
+ JSJaCJingleStorage.get_single_reject()(stanza);
+
+ id = this.get_call_id(stanza);
+ if(id) this._unregister_id(id);
+
+ is_handled = true; break;
+ }
+ }
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] handle > ' + e, 1);
+ } finally {
+ return is_handled;
+ }
+ },
+
+ /**
+ * Returns the call ID
+ * @public
+ * @param {JSJaCPacket} stanza
+ * @returns {String} Call ID
+ */
+ get_call_id: function(stanza) {
+ var call_id = null;
+
+ try {
+ var stanza_child = stanza.getChild(
+ '*', NS_JINGLE_MESSAGE
+ );
+
+ if(stanza_child) {
+ call_id = stanza_child.getAttribute('id') || null;
+ }
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] get_call_id > ' + e, 1);
+ } finally {
+ return call_id;
+ }
+ },
+
+ /**
+ * Returns the call medias
+ * @public
+ * @param {String} id
+ * @returns {Object} Call medias
+ */
+ get_call_medias: function(id) {
+ var call_medias = [];
+
+ try {
+ call_medias = JSJaCJingleStorage.get_broadcast_ids(id) || [];
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] get_call_medias > ' + e, 1);
+ } finally {
+ return call_medias;
+ }
+ },
+
+ /**
+ * Broadcasts a Jingle session proposal (remote packet)
+ * @private
+ * @see {@link http://xmpp.org/extensions/xep-0353.html#intent|XEP-0353 - Propose}
+ */
+ _send_remote_propose: function(to, medias) {
+ var i, cur_media, propose, id;
+
+ try {
+ id = this._register_id(null, medias);
+ propose = this._build_stanza(
+ to, id, JSJAC_JINGLE_MESSAGE_ACTION_PROPOSE
+ );
+
+ if(medias && typeof medias == 'object' && medias.length) {
+ for(i = 0; i < medias.length; i++) {
+ cur_media = medias[i];
+
+ if(cur_media) {
+ propose[1].appendChild(
+ propose[0].buildNode('description', {
+ 'xmlns': NS_JINGLE_APPS_RTP,
+ 'media': cur_media
+ })
+ );
+ }
+ }
+ }
+
+ this._send_stanza(propose);
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _send_remote_propose > ' + e, 1);
+ } finally {
+ return id;
+ }
+ },
+
+ /**
+ * Broadcasts a Jingle session retract (remote packet)
+ * @private
+ * @see {@link http://xmpp.org/extensions/xep-0353.html#retract|XEP-0353 - Retract}
+ */
+ _send_remote_retract: function(to, id) {
+ try {
+ if(this._exists_id(id) === true) {
+ var retract = this._build_stanza(
+ to, id, JSJAC_JINGLE_MESSAGE_ACTION_RETRACT
+ );
+
+ this._send_stanza(retract);
+ this._unregister_id(id);
+ } else {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _send_remote_retract > Cannot retract, target ID not existing.', 0);
+ }
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _send_remote_retract > ' + e, 1);
+ }
+ },
+
+ /**
+ * Broadcasts a Jingle session proceed (remote packet)
+ * @private
+ * @see {@link http://xmpp.org/extensions/xep-0353.html#accept|XEP-0353 - Accept}
+ */
+ _send_remote_proceed: function(to, id) {
+ try {
+ // ID shouldn't exist at this point since we're the receiving party
+ if(this._exists_id(id) === true) {
+ var proceed = this._build_stanza(
+ to, id, JSJAC_JINGLE_MESSAGE_ACTION_PROCEED
+ );
+
+ this._send_stanza(proceed);
+ this._unregister_id(id);
+ } else {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _send_remote_proceed > Cannot proceed, target ID not existing.', 0);
+ }
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _send_remote_proceed > ' + e, 1);
+ }
+ },
+
+ /**
+ * Broadcasts a Jingle session accept (local packet)
+ * @private-
+ * @see {@link http://xmpp.org/extensions/xep-0353.html#accept|XEP-0353 - Accept}
+ */
+ _send_local_accept: function(id) {
+ try {
+ // ID shouldn't exist at this point since we're the receiving party
+ if(this._exists_id(id) === true) {
+ var accept = this._build_stanza(
+ null, id, JSJAC_JINGLE_MESSAGE_ACTION_ACCEPT
+ );
+
+ this._send_stanza(accept);
+ } else {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _send_local_accept > Cannot accept, target ID not existing.', 0);
+ }
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _send_local_accept > ' + e, 1);
+ }
+ },
+
+ /**
+ * Broadcasts a Jingle session reject (local packet)
+ * @private
+ * @see {@link http://xmpp.org/extensions/xep-0353.html#reject|XEP-0353 - Reject}
+ */
+ _send_local_reject: function(id) {
+ try {
+ // ID shouldn't exist at this point since we're the receiving party
+ if(this._exists_id(id) === true) {
+ var reject = this._build_stanza(
+ null, id, JSJAC_JINGLE_MESSAGE_ACTION_REJECT
+ );
+
+ this._send_stanza(reject);
+ } else {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _send_local_reject > Cannot reject, target ID not existing.', 0);
+ }
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _send_local_reject > ' + e, 1);
+ }
+ },
+
+ /**
+ * Builds a XEP-0353 stanza
+ * @private
+ */
+ _build_stanza: function(to, id, action) {
+ stanza_arr = [];
+
+ try {
+ var connection, stanza, node;
+
+ stanza = new JSJaCMessage();
+
+ // Set to connection user?
+ if(to === null) {
+ connection = JSJaCJingleStorage.get_connection();
+ to = (connection.username + '@' + connection.domain);
+ }
+
+ stanza.setTo(to);
+
+ node = stanza.getNode().appendChild(
+ stanza.buildNode(action, {
+ 'xmlns': NS_JINGLE_MESSAGE,
+ 'id': id
+ })
+ );
+
+ stanza_arr = [stanza, node];
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _build_stanza > ' + e, 1);
+ } finally {
+ return stanza_arr;
+ }
+ },
+
+ /**
+ * Sends a XEP-0353 stanza
+ * @private
+ */
+ _send_stanza: function(stanza_arr) {
+ try {
+ JSJaCJingleStorage.get_connection().send(
+ stanza_arr[0]
+ );
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _send_stanza > ' + e, 1);
+ }
+ },
+
+ /**
+ * Returns whether an ID exists or not
+ * @private
+ */
+ _exists_id: function(id) {
+ var is_existing = false;
+
+ try {
+ is_existing = (JSJaCJingleStorage.get_broadcast_ids(id) !== null) && true;
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _exists_id > ' + e, 1);
+ } finally {
+ return is_existing;
+ }
+ },
+
+ /**
+ * Registers an ID
+ * @private
+ */
+ _register_id: function(id, medias) {
+ try {
+ id = id || JSJaCUtils.cnonce(16);
+
+ JSJaCJingleStorage.set_broadcast_ids(id, medias);
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _register_id > ' + e, 1);
+ } finally {
+ return id;
+ }
+ },
+
+ /**
+ * Unregisters an ID
+ * @private
+ */
+ _unregister_id: function(id) {
+ try {
+ JSJaCJingleStorage.set_broadcast_ids(id, null, true);
+ } catch(e) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:broadcast] _unregister_id > ' + e, 1);
+ }
+ },
+ }
+))();
+
+/**
* @fileoverview JSJaC Jingle library - Initialization components
*
* @url https://github.com/valeriansaliou/jsjac-jingle
@@ -17147,6 +18356,7 @@ var JSJaCJingle = new (ring.create(
* @param {Object} [args]
* @property {JSJaCConnection} [args.connection] - The connection to be attached to.
* @property {Function} [args.single_initiate] - The Jingle session initiate request custom handler.
+ * @property {Function} [args.single_prepare] - The Jingle session prepare request custom handler.
* @property {Function} [args.muji_invite] - The Muji session invite message custom handler.
* @property {JSJaCDebugger} [args.debug] - A reference to a debugger implementing the JSJaCDebugger interface.
* @property {Boolean} [args.extdisco] - Whether or not to discover external services as per XEP-0215.
@@ -17161,6 +18371,12 @@ var JSJaCJingle = new (ring.create(
JSJaCJingleStorage.set_connection(args.connection);
if(args && args.single_initiate)
JSJaCJingleStorage.set_single_initiate(args.single_initiate);
+ if(args && args.single_prepare)
+ JSJaCJingleStorage.set_single_prepare(args.single_prepare);
+ if(args && args.single_proceed)
+ JSJaCJingleStorage.set_single_proceed(args.single_proceed);
+ if(args && args.single_reject)
+ JSJaCJingleStorage.set_single_reject(args.single_reject);
if(args && args.muji_invite)
JSJaCJingleStorage.set_muji_invite(args.muji_invite);
if(args && args.debug)
@@ -17258,7 +18474,7 @@ var JSJaCJingle = new (ring.create(
if(jingle && !JSJAC_JINGLE_AVAILABLE) {
JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_iq > Dropped Jingle packet (WebRTC not available).', 0);
- (new JSJaCJingleSingle({ to: from })).send_error(stanza, XMPP_ERROR_SERVICE_UNAVAILABLE);
+ (new JSJaCJingleSingle({ to: from }))._send_error(stanza, XMPP_ERROR_SERVICE_UNAVAILABLE);
} else if(is_muji) {
var username, participant;
@@ -17282,7 +18498,7 @@ var JSJaCJingle = new (ring.create(
} else if(stanza.getType() == JSJAC_JINGLE_IQ_TYPE_SET && from) {
JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_iq > Unknown Muji participant session route (sid: ' + sid + ').', 0);
- (new JSJaCJingleSingle({ to: from })).send_error(stanza, JSJAC_JINGLE_ERROR_UNKNOWN_SESSION);
+ (new JSJaCJingleSingle({ to: from }))._send_error(stanza, JSJAC_JINGLE_ERROR_UNKNOWN_SESSION);
}
} else if(sid) {
if(action == JSJAC_JINGLE_ACTION_SESSION_INITIATE) {
@@ -17292,7 +18508,7 @@ var JSJaCJingle = new (ring.create(
} else if(stanza.getType() == JSJAC_JINGLE_IQ_TYPE_SET && from) {
JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_iq > Unknown Muji participant session (sid: ' + sid + ').', 0);
- (new JSJaCJingleSingle({ to: from })).send_error(stanza, JSJAC_JINGLE_ERROR_UNKNOWN_SESSION);
+ (new JSJaCJingleSingle({ to: from }))._send_error(stanza, JSJAC_JINGLE_ERROR_UNKNOWN_SESSION);
}
}
} else if(is_single) {
@@ -17311,7 +18527,7 @@ var JSJaCJingle = new (ring.create(
} else if(stanza.getType() == JSJAC_JINGLE_IQ_TYPE_SET && from) {
JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_iq > Unknown Jingle session (sid: ' + sid + ').', 0);
- (new JSJaCJingleSingle({ to: from })).send_error(stanza, JSJAC_JINGLE_ERROR_UNKNOWN_SESSION);
+ (new JSJaCJingleSingle({ to: from }))._send_error(stanza, JSJAC_JINGLE_ERROR_UNKNOWN_SESSION);
}
}
} else {
@@ -17330,47 +18546,56 @@ var JSJaCJingle = new (ring.create(
*/
_route_message: function(stanza) {
try {
- // Muji?
var from = stanza.getFrom();
if(from) {
var jid = new JSJaCJID(from);
- var room = jid.getNode() + '@' + jid.getDomain();
- var session_route = this._read(JSJAC_JINGLE_SESSION_MUJI, room);
+ // Broadcast message?
+ var is_handled_broadcast = JSJaCJingleBroadcast.handle(stanza);
- var x_conference = stanza.getChild('x', NS_JABBER_CONFERENCE);
- var x_invite = stanza.getChild('x', NS_MUJI_INVITE);
+ if(is_handled_broadcast === true) {
+ // XEP-0353: Jingle Message Initiation
+ // Nothing to do there.
+ } else {
+ // Muji?
+ var room = jid.getNode() + '@' + jid.getDomain();
- var is_invite = (x_conference && x_invite && true);
+ var session_route = this._read(JSJAC_JINGLE_SESSION_MUJI, room);
- if(is_invite === true) {
- if(session_route === null) {
- JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_message > Muji invite received (room: ' + room + ').', 2);
+ var x_conference = stanza.getChild('x', NS_JABBER_CONFERENCE);
+ var x_invite = stanza.getChild('x', NS_MUJI_INVITE);
- // Read invite data
- var err = 0;
- var args = {
- from : (from || err++),
- jid : (x_conference.getAttribute('jid') || err++),
- password : (x_conference.getAttribute('password') || null),
- reason : (x_conference.getAttribute('reason') || null),
- media : (x_invite.getAttribute('media') || err++)
- };
+ var is_invite = (x_conference && x_invite && true);
+
+ if(is_invite === true) {
+ if(session_route === null) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_message > Muji invite received (room: ' + room + ').', 2);
+
+ // Read invite data
+ var err = 0;
+ var args = {
+ from : (from || err++),
+ jid : (x_conference.getAttribute('jid') || err++),
+ password : (x_conference.getAttribute('password') || null),
+ reason : (x_conference.getAttribute('reason') || null),
+ media : (x_invite.getAttribute('media') || err++)
+ };
- if(err === 0) {
- JSJaCJingleStorage.get_muji_invite()(stanza, args);
+ if(err === 0) {
+ JSJaCJingleStorage.get_muji_invite()(stanza, args);
+ } else {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_message > Dropped invite because incomplete (room: ' + room + ').', 0);
+ }
} else {
- JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_message > Dropped invite because incomplete (room: ' + room + ').', 0);
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_message > Dropped invite because Muji already joined (room: ' + room + ').', 0);
}
} else {
- JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_message > Dropped invite because Muji already joined (room: ' + room + ').', 0);
- }
- } else {
- if(session_route !== null) {
- JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_message > Routed to Jingle session (room: ' + room + ').', 2);
+ if(session_route !== null) {
+ JSJaCJingleStorage.get_debug().log('[JSJaCJingle:main] _route_message > Routed to Jingle session (room: ' + room + ').', 2);
- session_route.handle_message(stanza);
+ session_route.handle_message(stanza);
+ }
}
}
}
@@ -17478,4 +18703,4 @@ var JSJaCJingle = new (ring.create(
}
},
}
-))(); \ No newline at end of file
+))();