5592 lines
215 KiB
JavaScript
5592 lines
215 KiB
JavaScript
/**
|
||
* @license
|
||
* Selectivity.js 3.0.6 <https://arendjr.github.io/selectivity/>
|
||
* Copyright (c) 2014-2016 Arend van Beelen jr.
|
||
* (c) 2016 Speakap BV
|
||
* Available under MIT license <https://github.com/arendjr/selectivity/blob/master/LICENSE>
|
||
*/
|
||
(function (f) {
|
||
if (typeof exports === "object" && typeof module !== "undefined") {
|
||
module.exports = f()
|
||
} else if (typeof define === "function" && define.amd) {
|
||
define([], f)
|
||
} else {
|
||
var g;
|
||
if (typeof window !== "undefined") {
|
||
g = window
|
||
} else if (typeof global !== "undefined") {
|
||
g = global
|
||
} else if (typeof self !== "undefined") {
|
||
g = self
|
||
} else {
|
||
g = this
|
||
}
|
||
g.selectivity = f()
|
||
}
|
||
})(function () {
|
||
var define, module, exports;
|
||
return (function e(t, n, r) {
|
||
function s(o, u) {
|
||
if (!n[o]) {
|
||
if (!t[o]) {
|
||
var a = typeof require == "function" && require;
|
||
if (!u && a) return a(o, !0);
|
||
if (i) return i(o, !0);
|
||
var f = new Error("Cannot find module '" + o + "'");
|
||
throw f.code = "MODULE_NOT_FOUND", f
|
||
}
|
||
var l = n[o] = {exports: {}};
|
||
t[o][0].call(l.exports, function (e) {
|
||
var n = t[o][1][e];
|
||
return s(n ? n : e)
|
||
}, l, l.exports, e, t, n, r)
|
||
}
|
||
return n[o].exports
|
||
}
|
||
|
||
var i = typeof require == "function" && require;
|
||
for (var o = 0; o < r.length; o++) s(r[o]);
|
||
return s
|
||
})({
|
||
1: [function (_dereq_, module, exports) {
|
||
var root = _dereq_(10);
|
||
|
||
/** Built-in value references. */
|
||
var Symbol = root.Symbol;
|
||
|
||
module.exports = Symbol;
|
||
|
||
}, {"10": 10}], 2: [function (_dereq_, module, exports) {
|
||
/**
|
||
* A specialized version of `_.map` for arrays without support for iteratee
|
||
* shorthands.
|
||
*
|
||
* @private
|
||
* @param {Array} [array] The array to iterate over.
|
||
* @param {Function} iteratee The function invoked per iteration.
|
||
* @returns {Array} Returns the new mapped array.
|
||
*/
|
||
function arrayMap(array, iteratee) {
|
||
var index = -1,
|
||
length = array == null ? 0 : array.length,
|
||
result = Array(length);
|
||
|
||
while (++index < length) {
|
||
result[index] = iteratee(array[index], index, array);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
module.exports = arrayMap;
|
||
|
||
}, {}], 3: [function (_dereq_, module, exports) {
|
||
var Symbol = _dereq_(1),
|
||
getRawTag = _dereq_(8),
|
||
objectToString = _dereq_(9);
|
||
|
||
/** `Object#toString` result references. */
|
||
var nullTag = '[object Null]',
|
||
undefinedTag = '[object Undefined]';
|
||
|
||
/** Built-in value references. */
|
||
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
|
||
|
||
/**
|
||
* The base implementation of `getTag` without fallbacks for buggy environments.
|
||
*
|
||
* @private
|
||
* @param {*} value The value to query.
|
||
* @returns {string} Returns the `toStringTag`.
|
||
*/
|
||
function baseGetTag(value) {
|
||
if (value == null) {
|
||
return value === undefined ? undefinedTag : nullTag;
|
||
}
|
||
return (symToStringTag && symToStringTag in Object(value))
|
||
? getRawTag(value)
|
||
: objectToString(value);
|
||
}
|
||
|
||
module.exports = baseGetTag;
|
||
|
||
}, {"1": 1, "8": 8, "9": 9}], 4: [function (_dereq_, module, exports) {
|
||
/**
|
||
* The base implementation of `_.propertyOf` without support for deep paths.
|
||
*
|
||
* @private
|
||
* @param {Object} object The object to query.
|
||
* @returns {Function} Returns the new accessor function.
|
||
*/
|
||
function basePropertyOf(object) {
|
||
return function (key) {
|
||
return object == null ? undefined : object[key];
|
||
};
|
||
}
|
||
|
||
module.exports = basePropertyOf;
|
||
|
||
}, {}], 5: [function (_dereq_, module, exports) {
|
||
var Symbol = _dereq_(1),
|
||
arrayMap = _dereq_(2),
|
||
isArray = _dereq_(13),
|
||
isSymbol = _dereq_(17);
|
||
|
||
/** Used as references for various `Number` constants. */
|
||
var INFINITY = 1 / 0;
|
||
|
||
/** Used to convert symbols to primitives and strings. */
|
||
var symbolProto = Symbol ? Symbol.prototype : undefined,
|
||
symbolToString = symbolProto ? symbolProto.toString : undefined;
|
||
|
||
/**
|
||
* The base implementation of `_.toString` which doesn't convert nullish
|
||
* values to empty strings.
|
||
*
|
||
* @private
|
||
* @param {*} value The value to process.
|
||
* @returns {string} Returns the string.
|
||
*/
|
||
function baseToString(value) {
|
||
// Exit early for strings to avoid a performance hit in some environments.
|
||
if (typeof value == 'string') {
|
||
return value;
|
||
}
|
||
if (isArray(value)) {
|
||
// Recursively convert values (susceptible to call stack limits).
|
||
return arrayMap(value, baseToString) + '';
|
||
}
|
||
if (isSymbol(value)) {
|
||
return symbolToString ? symbolToString.call(value) : '';
|
||
}
|
||
var result = (value + '');
|
||
return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
|
||
}
|
||
|
||
module.exports = baseToString;
|
||
|
||
}, {"1": 1, "13": 13, "17": 17, "2": 2}], 6: [function (_dereq_, module, exports) {
|
||
var basePropertyOf = _dereq_(4);
|
||
|
||
/** Used to map characters to HTML entities. */
|
||
var htmlEscapes = {
|
||
'&': '&',
|
||
'<': '<',
|
||
'>': '>',
|
||
'"': '"',
|
||
"'": '''
|
||
};
|
||
|
||
/**
|
||
* Used by `_.escape` to convert characters to HTML entities.
|
||
*
|
||
* @private
|
||
* @param {string} chr The matched character to escape.
|
||
* @returns {string} Returns the escaped character.
|
||
*/
|
||
var escapeHtmlChar = basePropertyOf(htmlEscapes);
|
||
|
||
module.exports = escapeHtmlChar;
|
||
|
||
}, {"4": 4}], 7: [function (_dereq_, module, exports) {
|
||
(function (global) {
|
||
/** Detect free variable `global` from Node.js. */
|
||
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
|
||
|
||
module.exports = freeGlobal;
|
||
|
||
}).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||
}, {}], 8: [function (_dereq_, module, exports) {
|
||
var Symbol = _dereq_(1);
|
||
|
||
/** Used for built-in method references. */
|
||
var objectProto = Object.prototype;
|
||
|
||
/** Used to check objects for own properties. */
|
||
var hasOwnProperty = objectProto.hasOwnProperty;
|
||
|
||
/**
|
||
* Used to resolve the
|
||
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
|
||
* of values.
|
||
*/
|
||
var nativeObjectToString = objectProto.toString;
|
||
|
||
/** Built-in value references. */
|
||
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
|
||
|
||
/**
|
||
* A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
|
||
*
|
||
* @private
|
||
* @param {*} value The value to query.
|
||
* @returns {string} Returns the raw `toStringTag`.
|
||
*/
|
||
function getRawTag(value) {
|
||
var isOwn = hasOwnProperty.call(value, symToStringTag),
|
||
tag = value[symToStringTag];
|
||
|
||
try {
|
||
value[symToStringTag] = undefined;
|
||
var unmasked = true;
|
||
} catch (e) {
|
||
}
|
||
|
||
var result = nativeObjectToString.call(value);
|
||
if (unmasked) {
|
||
if (isOwn) {
|
||
value[symToStringTag] = tag;
|
||
} else {
|
||
delete value[symToStringTag];
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
module.exports = getRawTag;
|
||
|
||
}, {"1": 1}], 9: [function (_dereq_, module, exports) {
|
||
/** Used for built-in method references. */
|
||
var objectProto = Object.prototype;
|
||
|
||
/**
|
||
* Used to resolve the
|
||
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
|
||
* of values.
|
||
*/
|
||
var nativeObjectToString = objectProto.toString;
|
||
|
||
/**
|
||
* Converts `value` to a string using `Object.prototype.toString`.
|
||
*
|
||
* @private
|
||
* @param {*} value The value to convert.
|
||
* @returns {string} Returns the converted string.
|
||
*/
|
||
function objectToString(value) {
|
||
return nativeObjectToString.call(value);
|
||
}
|
||
|
||
module.exports = objectToString;
|
||
|
||
}, {}], 10: [function (_dereq_, module, exports) {
|
||
var freeGlobal = _dereq_(7);
|
||
|
||
/** Detect free variable `self`. */
|
||
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
|
||
|
||
/** Used as a reference to the global object. */
|
||
var root = freeGlobal || freeSelf || Function('return this')();
|
||
|
||
module.exports = root;
|
||
|
||
}, {"7": 7}], 11: [function (_dereq_, module, exports) {
|
||
var isObject = _dereq_(14),
|
||
now = _dereq_(18),
|
||
toNumber = _dereq_(19);
|
||
|
||
/** Error message constants. */
|
||
var FUNC_ERROR_TEXT = 'Expected a function';
|
||
|
||
/* Built-in method references for those with the same name as other `lodash` methods. */
|
||
var nativeMax = Math.max,
|
||
nativeMin = Math.min;
|
||
|
||
/**
|
||
* Creates a debounced function that delays invoking `func` until after `wait`
|
||
* milliseconds have elapsed since the last time the debounced function was
|
||
* invoked. The debounced function comes with a `cancel` method to cancel
|
||
* delayed `func` invocations and a `flush` method to immediately invoke them.
|
||
* Provide `options` to indicate whether `func` should be invoked on the
|
||
* leading and/or trailing edge of the `wait` timeout. The `func` is invoked
|
||
* with the last arguments provided to the debounced function. Subsequent
|
||
* calls to the debounced function return the result of the last `func`
|
||
* invocation.
|
||
*
|
||
* **Note:** If `leading` and `trailing` options are `true`, `func` is
|
||
* invoked on the trailing edge of the timeout only if the debounced function
|
||
* is invoked more than once during the `wait` timeout.
|
||
*
|
||
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
|
||
* until to the next tick, similar to `setTimeout` with a timeout of `0`.
|
||
*
|
||
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||
* for details over the differences between `_.debounce` and `_.throttle`.
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 0.1.0
|
||
* @category Function
|
||
* @param {Function} func The function to debounce.
|
||
* @param {number} [wait=0] The number of milliseconds to delay.
|
||
* @param {Object} [options={}] The options object.
|
||
* @param {boolean} [options.leading=false]
|
||
* Specify invoking on the leading edge of the timeout.
|
||
* @param {number} [options.maxWait]
|
||
* The maximum time `func` is allowed to be delayed before it's invoked.
|
||
* @param {boolean} [options.trailing=true]
|
||
* Specify invoking on the trailing edge of the timeout.
|
||
* @returns {Function} Returns the new debounced function.
|
||
* @example
|
||
*
|
||
* // Avoid costly calculations while the window size is in flux.
|
||
* jQuery(window).on('resize', _.debounce(calculateLayout, 150));
|
||
*
|
||
* // Invoke `sendMail` when clicked, debouncing subsequent calls.
|
||
* jQuery(element).on('click', _.debounce(sendMail, 300, {
|
||
* 'leading': true,
|
||
* 'trailing': false
|
||
* }));
|
||
*
|
||
* // Ensure `batchLog` is invoked once after 1 second of debounced calls.
|
||
* var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
|
||
* var source = new EventSource('/stream');
|
||
* jQuery(source).on('message', debounced);
|
||
*
|
||
* // Cancel the trailing debounced invocation.
|
||
* jQuery(window).on('popstate', debounced.cancel);
|
||
*/
|
||
function debounce(func, wait, options) {
|
||
var lastArgs,
|
||
lastThis,
|
||
maxWait,
|
||
result,
|
||
timerId,
|
||
lastCallTime,
|
||
lastInvokeTime = 0,
|
||
leading = false,
|
||
maxing = false,
|
||
trailing = true;
|
||
|
||
if (typeof func != 'function') {
|
||
throw new TypeError(FUNC_ERROR_TEXT);
|
||
}
|
||
wait = toNumber(wait) || 0;
|
||
if (isObject(options)) {
|
||
leading = !!options.leading;
|
||
maxing = 'maxWait' in options;
|
||
maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
|
||
trailing = 'trailing' in options ? !!options.trailing : trailing;
|
||
}
|
||
|
||
function invokeFunc(time) {
|
||
var args = lastArgs,
|
||
thisArg = lastThis;
|
||
|
||
lastArgs = lastThis = undefined;
|
||
lastInvokeTime = time;
|
||
result = func.apply(thisArg, args);
|
||
return result;
|
||
}
|
||
|
||
function leadingEdge(time) {
|
||
// Reset any `maxWait` timer.
|
||
lastInvokeTime = time;
|
||
// Start the timer for the trailing edge.
|
||
timerId = setTimeout(timerExpired, wait);
|
||
// Invoke the leading edge.
|
||
return leading ? invokeFunc(time) : result;
|
||
}
|
||
|
||
function remainingWait(time) {
|
||
var timeSinceLastCall = time - lastCallTime,
|
||
timeSinceLastInvoke = time - lastInvokeTime,
|
||
result = wait - timeSinceLastCall;
|
||
|
||
return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
|
||
}
|
||
|
||
function shouldInvoke(time) {
|
||
var timeSinceLastCall = time - lastCallTime,
|
||
timeSinceLastInvoke = time - lastInvokeTime;
|
||
|
||
// Either this is the first call, activity has stopped and we're at the
|
||
// trailing edge, the system time has gone backwards and we're treating
|
||
// it as the trailing edge, or we've hit the `maxWait` limit.
|
||
return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
|
||
(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
|
||
}
|
||
|
||
function timerExpired() {
|
||
var time = now();
|
||
if (shouldInvoke(time)) {
|
||
return trailingEdge(time);
|
||
}
|
||
// Restart the timer.
|
||
timerId = setTimeout(timerExpired, remainingWait(time));
|
||
}
|
||
|
||
function trailingEdge(time) {
|
||
timerId = undefined;
|
||
|
||
// Only invoke if we have `lastArgs` which means `func` has been
|
||
// debounced at least once.
|
||
if (trailing && lastArgs) {
|
||
return invokeFunc(time);
|
||
}
|
||
lastArgs = lastThis = undefined;
|
||
return result;
|
||
}
|
||
|
||
function cancel() {
|
||
if (timerId !== undefined) {
|
||
clearTimeout(timerId);
|
||
}
|
||
lastInvokeTime = 0;
|
||
lastArgs = lastCallTime = lastThis = timerId = undefined;
|
||
}
|
||
|
||
function flush() {
|
||
return timerId === undefined ? result : trailingEdge(now());
|
||
}
|
||
|
||
function debounced() {
|
||
var time = now(),
|
||
isInvoking = shouldInvoke(time);
|
||
|
||
lastArgs = arguments;
|
||
lastThis = this;
|
||
lastCallTime = time;
|
||
|
||
if (isInvoking) {
|
||
if (timerId === undefined) {
|
||
return leadingEdge(lastCallTime);
|
||
}
|
||
if (maxing) {
|
||
// Handle invocations in a tight loop.
|
||
timerId = setTimeout(timerExpired, wait);
|
||
return invokeFunc(lastCallTime);
|
||
}
|
||
}
|
||
if (timerId === undefined) {
|
||
timerId = setTimeout(timerExpired, wait);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
debounced.cancel = cancel;
|
||
debounced.flush = flush;
|
||
return debounced;
|
||
}
|
||
|
||
module.exports = debounce;
|
||
|
||
}, {"14": 14, "18": 18, "19": 19}], 12: [function (_dereq_, module, exports) {
|
||
var escapeHtmlChar = _dereq_(6),
|
||
toString = _dereq_(20);
|
||
|
||
/** Used to match HTML entities and HTML characters. */
|
||
var reUnescapedHtml = /[&<>"']/g,
|
||
reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
|
||
|
||
/**
|
||
* Converts the characters "&", "<", ">", '"', and "'" in `string` to their
|
||
* corresponding HTML entities.
|
||
*
|
||
* **Note:** No other characters are escaped. To escape additional
|
||
* characters use a third-party library like [_he_](https://mths.be/he).
|
||
*
|
||
* Though the ">" character is escaped for symmetry, characters like
|
||
* ">" and "/" don't need escaping in HTML and have no special meaning
|
||
* unless they're part of a tag or unquoted attribute value. See
|
||
* [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)
|
||
* (under "semi-related fun fact") for more details.
|
||
*
|
||
* When working with HTML you should always
|
||
* [quote attribute values](http://wonko.com/post/html-escaping) to reduce
|
||
* XSS vectors.
|
||
*
|
||
* @static
|
||
* @since 0.1.0
|
||
* @memberOf _
|
||
* @category String
|
||
* @param {string} [string=''] The string to escape.
|
||
* @returns {string} Returns the escaped string.
|
||
* @example
|
||
*
|
||
* _.escape('fred, barney, & pebbles');
|
||
* // => 'fred, barney, & pebbles'
|
||
*/
|
||
function escape(string) {
|
||
string = toString(string);
|
||
return (string && reHasUnescapedHtml.test(string))
|
||
? string.replace(reUnescapedHtml, escapeHtmlChar)
|
||
: string;
|
||
}
|
||
|
||
module.exports = escape;
|
||
|
||
}, {"20": 20, "6": 6}], 13: [function (_dereq_, module, exports) {
|
||
/**
|
||
* Checks if `value` is classified as an `Array` object.
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 0.1.0
|
||
* @category Lang
|
||
* @param {*} value The value to check.
|
||
* @returns {boolean} Returns `true` if `value` is an array, else `false`.
|
||
* @example
|
||
*
|
||
* _.isArray([1, 2, 3]);
|
||
* // => true
|
||
*
|
||
* _.isArray(document.body.children);
|
||
* // => false
|
||
*
|
||
* _.isArray('abc');
|
||
* // => false
|
||
*
|
||
* _.isArray(_.noop);
|
||
* // => false
|
||
*/
|
||
var isArray = Array.isArray;
|
||
|
||
module.exports = isArray;
|
||
|
||
}, {}], 14: [function (_dereq_, module, exports) {
|
||
/**
|
||
* Checks if `value` is the
|
||
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
|
||
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 0.1.0
|
||
* @category Lang
|
||
* @param {*} value The value to check.
|
||
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
|
||
* @example
|
||
*
|
||
* _.isObject({});
|
||
* // => true
|
||
*
|
||
* _.isObject([1, 2, 3]);
|
||
* // => true
|
||
*
|
||
* _.isObject(_.noop);
|
||
* // => true
|
||
*
|
||
* _.isObject(null);
|
||
* // => false
|
||
*/
|
||
function isObject(value) {
|
||
var type = typeof value;
|
||
return value != null && (type == 'object' || type == 'function');
|
||
}
|
||
|
||
module.exports = isObject;
|
||
|
||
}, {}], 15: [function (_dereq_, module, exports) {
|
||
/**
|
||
* Checks if `value` is object-like. A value is object-like if it's not `null`
|
||
* and has a `typeof` result of "object".
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 4.0.0
|
||
* @category Lang
|
||
* @param {*} value The value to check.
|
||
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
|
||
* @example
|
||
*
|
||
* _.isObjectLike({});
|
||
* // => true
|
||
*
|
||
* _.isObjectLike([1, 2, 3]);
|
||
* // => true
|
||
*
|
||
* _.isObjectLike(_.noop);
|
||
* // => false
|
||
*
|
||
* _.isObjectLike(null);
|
||
* // => false
|
||
*/
|
||
function isObjectLike(value) {
|
||
return value != null && typeof value == 'object';
|
||
}
|
||
|
||
module.exports = isObjectLike;
|
||
|
||
}, {}], 16: [function (_dereq_, module, exports) {
|
||
var baseGetTag = _dereq_(3),
|
||
isArray = _dereq_(13),
|
||
isObjectLike = _dereq_(15);
|
||
|
||
/** `Object#toString` result references. */
|
||
var stringTag = '[object String]';
|
||
|
||
/**
|
||
* Checks if `value` is classified as a `String` primitive or object.
|
||
*
|
||
* @static
|
||
* @since 0.1.0
|
||
* @memberOf _
|
||
* @category Lang
|
||
* @param {*} value The value to check.
|
||
* @returns {boolean} Returns `true` if `value` is a string, else `false`.
|
||
* @example
|
||
*
|
||
* _.isString('abc');
|
||
* // => true
|
||
*
|
||
* _.isString(1);
|
||
* // => false
|
||
*/
|
||
function isString(value) {
|
||
return typeof value == 'string' ||
|
||
(!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag);
|
||
}
|
||
|
||
module.exports = isString;
|
||
|
||
}, {"13": 13, "15": 15, "3": 3}], 17: [function (_dereq_, module, exports) {
|
||
var baseGetTag = _dereq_(3),
|
||
isObjectLike = _dereq_(15);
|
||
|
||
/** `Object#toString` result references. */
|
||
var symbolTag = '[object Symbol]';
|
||
|
||
/**
|
||
* Checks if `value` is classified as a `Symbol` primitive or object.
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 4.0.0
|
||
* @category Lang
|
||
* @param {*} value The value to check.
|
||
* @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
|
||
* @example
|
||
*
|
||
* _.isSymbol(Symbol.iterator);
|
||
* // => true
|
||
*
|
||
* _.isSymbol('abc');
|
||
* // => false
|
||
*/
|
||
function isSymbol(value) {
|
||
return typeof value == 'symbol' ||
|
||
(isObjectLike(value) && baseGetTag(value) == symbolTag);
|
||
}
|
||
|
||
module.exports = isSymbol;
|
||
|
||
}, {"15": 15, "3": 3}], 18: [function (_dereq_, module, exports) {
|
||
var root = _dereq_(10);
|
||
|
||
/**
|
||
* Gets the timestamp of the number of milliseconds that have elapsed since
|
||
* the Unix epoch (1 January 1970 00:00:00 UTC).
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 2.4.0
|
||
* @category Date
|
||
* @returns {number} Returns the timestamp.
|
||
* @example
|
||
*
|
||
* _.defer(function(stamp) {
|
||
* console.log(_.now() - stamp);
|
||
* }, _.now());
|
||
* // => Logs the number of milliseconds it took for the deferred invocation.
|
||
*/
|
||
var now = function () {
|
||
return root.Date.now();
|
||
};
|
||
|
||
module.exports = now;
|
||
|
||
}, {"10": 10}], 19: [function (_dereq_, module, exports) {
|
||
var isObject = _dereq_(14),
|
||
isSymbol = _dereq_(17);
|
||
|
||
/** Used as references for various `Number` constants. */
|
||
var NAN = 0 / 0;
|
||
|
||
/** Used to match leading and trailing whitespace. */
|
||
var reTrim = /^\s+|\s+$/g;
|
||
|
||
/** Used to detect bad signed hexadecimal string values. */
|
||
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
|
||
|
||
/** Used to detect binary string values. */
|
||
var reIsBinary = /^0b[01]+$/i;
|
||
|
||
/** Used to detect octal string values. */
|
||
var reIsOctal = /^0o[0-7]+$/i;
|
||
|
||
/** Built-in method references without a dependency on `root`. */
|
||
var freeParseInt = parseInt;
|
||
|
||
/**
|
||
* Converts `value` to a number.
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 4.0.0
|
||
* @category Lang
|
||
* @param {*} value The value to process.
|
||
* @returns {number} Returns the number.
|
||
* @example
|
||
*
|
||
* _.toNumber(3.2);
|
||
* // => 3.2
|
||
*
|
||
* _.toNumber(Number.MIN_VALUE);
|
||
* // => 5e-324
|
||
*
|
||
* _.toNumber(Infinity);
|
||
* // => Infinity
|
||
*
|
||
* _.toNumber('3.2');
|
||
* // => 3.2
|
||
*/
|
||
function toNumber(value) {
|
||
if (typeof value == 'number') {
|
||
return value;
|
||
}
|
||
if (isSymbol(value)) {
|
||
return NAN;
|
||
}
|
||
if (isObject(value)) {
|
||
var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
|
||
value = isObject(other) ? (other + '') : other;
|
||
}
|
||
if (typeof value != 'string') {
|
||
return value === 0 ? value : +value;
|
||
}
|
||
value = value.replace(reTrim, '');
|
||
var isBinary = reIsBinary.test(value);
|
||
return (isBinary || reIsOctal.test(value))
|
||
? freeParseInt(value.slice(2), isBinary ? 2 : 8)
|
||
: (reIsBadHex.test(value) ? NAN : +value);
|
||
}
|
||
|
||
module.exports = toNumber;
|
||
|
||
}, {"14": 14, "17": 17}], 20: [function (_dereq_, module, exports) {
|
||
var baseToString = _dereq_(5);
|
||
|
||
/**
|
||
* Converts `value` to a string. An empty string is returned for `null`
|
||
* and `undefined` values. The sign of `-0` is preserved.
|
||
*
|
||
* @static
|
||
* @memberOf _
|
||
* @since 4.0.0
|
||
* @category Lang
|
||
* @param {*} value The value to convert.
|
||
* @returns {string} Returns the converted string.
|
||
* @example
|
||
*
|
||
* _.toString(null);
|
||
* // => ''
|
||
*
|
||
* _.toString(-0);
|
||
* // => '-0'
|
||
*
|
||
* _.toString([1, 2, 3]);
|
||
* // => '1,2,3'
|
||
*/
|
||
function toString(value) {
|
||
return value == null ? '' : baseToString(value);
|
||
}
|
||
|
||
module.exports = toString;
|
||
|
||
}, {"5": 5}], 21: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var $ = (window.jQuery || window.Zepto);
|
||
var isString = _dereq_(16);
|
||
|
||
var Selectivity = _dereq_(38);
|
||
|
||
var EVENT_PROPERTIES = {
|
||
change: ['added', 'removed', 'value'],
|
||
'selectivity-change': ['added', 'removed', 'value'],
|
||
'selectivity-highlight': ['id', 'item'],
|
||
'selectivity-selected': ['id', 'item'],
|
||
'selectivity-selecting': ['id', 'item']
|
||
};
|
||
|
||
// create event listeners that will copy the custom properties from the native events
|
||
// to the jQuery events, so jQuery users can use them seamlessly
|
||
function patchEvents($el) {
|
||
$.each(EVENT_PROPERTIES, function (eventName, properties) {
|
||
$el.on(eventName, function (event) {
|
||
if (event.originalEvent) {
|
||
properties.forEach(function (propertyName) {
|
||
event[propertyName] = event.originalEvent[propertyName];
|
||
});
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Create a new Selectivity instance or invoke a method on an instance.
|
||
*
|
||
* @param methodName Optional name of a method to call. If omitted, a Selectivity instance is
|
||
* created for each element in the set of matched elements. If an element in the
|
||
* set already has a Selectivity instance, the result is the same as if the
|
||
* setOptions() method is called. If a method name is given, the options
|
||
* parameter is ignored and any additional parameters are passed to the given
|
||
* method.
|
||
* @param options Options object to pass to the constructor or the setOptions() method. In case
|
||
* a new instance is being created, the following properties are used:
|
||
* inputType - The input type to use. Default inputs include 'Multiple' and 'Single',
|
||
* but you can add custom inputs to the Selectivity.Inputs map or just
|
||
* specify one here as a function. The default value is 'Multiple' if
|
||
* `multiple` is true and 'Single' otherwise.
|
||
* multiple - Boolean determining whether multiple items may be selected
|
||
* (default: false). If true, the default `inputType` is set to
|
||
* 'Multiple'.
|
||
*
|
||
* @return If the given method returns a value, this method returns the value of that method
|
||
* executed on the first element in the set of matched elements.
|
||
*/
|
||
$.fn.selectivity = function selectivity(methodName, options) {
|
||
var methodArgs = Array.prototype.slice.call(arguments, 1);
|
||
var result;
|
||
|
||
this.each(function () {
|
||
var instance = this.selectivity;
|
||
|
||
if (instance) {
|
||
if (methodName === 'data') {
|
||
methodName = methodArgs.length ? 'setData' : 'getData';
|
||
} else if (methodName === 'val' || methodName === 'value') {
|
||
methodName = methodArgs.length ? 'setValue' : 'getValue';
|
||
} else if (!isString(methodName)) {
|
||
methodArgs = [methodName];
|
||
methodName = 'setOptions';
|
||
}
|
||
|
||
if ($.isFunction(instance[methodName])) {
|
||
if (result === undefined) {
|
||
result = instance[methodName].apply(instance, methodArgs);
|
||
}
|
||
} else {
|
||
throw new Error('Unknown method: ' + methodName);
|
||
}
|
||
} else if (isString(methodName)) {
|
||
if (methodName !== 'destroy') {
|
||
throw new Error('Cannot call method on element without Selectivity instance');
|
||
}
|
||
} else {
|
||
options = $.extend({}, methodName, {element: this});
|
||
|
||
// this is a one-time hack to facilitate the "traditional" plugin, because
|
||
// the plugin is not able to hook this early into creation of the instance
|
||
var $this = $(this);
|
||
if ($this.is('select') && $this.prop('multiple')) {
|
||
options.multiple = true;
|
||
}
|
||
|
||
var Inputs = Selectivity.Inputs;
|
||
var InputType = options.inputType || (options.multiple ? 'Multiple' : 'Single');
|
||
if (!$.isFunction(InputType)) {
|
||
if (Inputs[InputType]) {
|
||
InputType = Inputs[InputType];
|
||
} else {
|
||
throw new Error('Unknown Selectivity input type: ' + InputType);
|
||
}
|
||
}
|
||
|
||
this.selectivity = new InputType(options);
|
||
$this = $(this.selectivity.el);
|
||
|
||
patchEvents($this);
|
||
|
||
if (result === undefined) {
|
||
result = $this;
|
||
}
|
||
}
|
||
});
|
||
|
||
return result === undefined ? this : result;
|
||
};
|
||
|
||
Selectivity.patchEvents = patchEvents;
|
||
|
||
$.Selectivity = Selectivity;
|
||
|
||
}, {"16": 16, "38": 38, "jquery": "jquery"}], 22: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var assign = (window.jQuery || window.Zepto).extend;
|
||
|
||
var EventListener = _dereq_(23);
|
||
var getItemSelector = _dereq_(41);
|
||
var matchesSelector = _dereq_(43);
|
||
var parseElement = _dereq_(44);
|
||
var removeElement = _dereq_(45);
|
||
var stopPropagation = _dereq_(46);
|
||
var toggleClass = _dereq_(47);
|
||
|
||
var Selectivity = _dereq_(38);
|
||
|
||
var HIGHLIGHT_CLASS = 'highlight';
|
||
var HIGHLIGHT_SELECTOR = '.' + HIGHLIGHT_CLASS;
|
||
var LOAD_MORE_SELECTOR = '.selectivity-load-more';
|
||
var RESULT_ITEM_SELECTOR = '.selectivity-result-item';
|
||
|
||
var SCROLL_EVENTS = ['scroll', 'touchend', 'touchmove'];
|
||
|
||
function findClosestElementMatchingSelector(el, selector) {
|
||
while (el && !matchesSelector(el, selector)) {
|
||
el = el.parentElement;
|
||
}
|
||
return el || null;
|
||
}
|
||
|
||
/**
|
||
* Selectivity Dropdown Constructor.
|
||
*
|
||
* @param selectivity Selectivity instance to which the dropdown belongs.
|
||
* @param options Options object. Should have the following properties:
|
||
* highlightFirstItem - Set to false if you don't want the first item to be
|
||
* automatically highlighted (optional).
|
||
* items - Array of items to display.
|
||
* position - Callback for positioning the dropdown.
|
||
* query - Callback to fetch the items to display.
|
||
* showSearchInput - Boolean whether a search input should be shown.
|
||
*/
|
||
function SelectivityDropdown(selectivity, options) {
|
||
this.el = parseElement(
|
||
selectivity.template('dropdown', {
|
||
dropdownCssClass: selectivity.options.dropdownCssClass,
|
||
searchInputPlaceholder: selectivity.options.searchInputPlaceholder,
|
||
showSearchInput: options.showSearchInput
|
||
})
|
||
);
|
||
|
||
/**
|
||
* DOM element to add the results to.
|
||
*/
|
||
this.resultsContainer = this.$('.selectivity-results-container');
|
||
|
||
/**
|
||
* Boolean indicating whether more results are available than currently displayed in the
|
||
* dropdown.
|
||
*/
|
||
this.hasMore = false;
|
||
|
||
/**
|
||
* The currently highlighted result item.
|
||
*/
|
||
this.highlightedResult = null;
|
||
|
||
/**
|
||
* Boolean whether the load more link is currently highlighted.
|
||
*/
|
||
this.loadMoreHighlighted = false;
|
||
|
||
/**
|
||
* Options passed to the dropdown constructor.
|
||
*/
|
||
this.options = options;
|
||
|
||
/**
|
||
* The results displayed in the dropdown.
|
||
*/
|
||
this.results = [];
|
||
|
||
/**
|
||
* Selectivity instance.
|
||
*/
|
||
this.selectivity = selectivity;
|
||
|
||
this._closed = false;
|
||
this._lastMousePosition = {};
|
||
|
||
this.close = this.close.bind(this);
|
||
this.position = this.position.bind(this);
|
||
|
||
if (selectivity.options.closeOnSelect !== false) {
|
||
selectivity.events.on('selectivity-selecting', this.close);
|
||
}
|
||
|
||
this.addToDom();
|
||
this.showLoading();
|
||
|
||
if (options.showSearchInput) {
|
||
selectivity.initInput(this.$('.selectivity-search-input'));
|
||
selectivity.focus();
|
||
}
|
||
|
||
var events = {};
|
||
events['click ' + LOAD_MORE_SELECTOR] = this._loadMoreClicked;
|
||
events['click ' + RESULT_ITEM_SELECTOR] = this._resultClicked;
|
||
events['mouseenter ' + LOAD_MORE_SELECTOR] = this._loadMoreHovered;
|
||
events['mouseenter ' + RESULT_ITEM_SELECTOR] = this._resultHovered;
|
||
|
||
this.events = new EventListener(this.el, this);
|
||
this.events.on(events);
|
||
|
||
this._attachScrollListeners();
|
||
this._suppressWheel();
|
||
|
||
setTimeout(this.triggerOpen.bind(this), 1);
|
||
}
|
||
|
||
/**
|
||
* Methods.
|
||
*/
|
||
assign(SelectivityDropdown.prototype, {
|
||
/**
|
||
* Convenience shortcut for this.el.querySelector(selector).
|
||
*/
|
||
$: function (selector) {
|
||
return this.el.querySelector(selector);
|
||
},
|
||
|
||
/**
|
||
* Adds the dropdown to the DOM.
|
||
*/
|
||
addToDom: function () {
|
||
this.selectivity.el.appendChild(this.el);
|
||
},
|
||
|
||
/**
|
||
* Closes the dropdown.
|
||
*/
|
||
close: function () {
|
||
if (!this._closed) {
|
||
this._closed = true;
|
||
|
||
removeElement(this.el);
|
||
|
||
this.selectivity.events.off('selectivity-selecting', this.close);
|
||
|
||
this.triggerClose();
|
||
|
||
this._removeScrollListeners();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Highlights a result item.
|
||
*
|
||
* @param item The item to highlight.
|
||
* @param options Optional options object that may contain the following property:
|
||
* reason - The reason why the result item is being highlighted. Possible
|
||
* values: 'current_value', 'first_result', 'hovered'.
|
||
*/
|
||
highlight: function (item, options) {
|
||
toggleClass(this.$(HIGHLIGHT_SELECTOR), HIGHLIGHT_CLASS, false);
|
||
toggleClass(this.$(getItemSelector(RESULT_ITEM_SELECTOR, item.id)), HIGHLIGHT_CLASS, true);
|
||
|
||
this.highlightedResult = item;
|
||
this.loadMoreHighlighted = false;
|
||
|
||
this.selectivity.triggerEvent('selectivity-highlight', {
|
||
item: item,
|
||
id: item.id,
|
||
reason: (options && options.reason) || 'unspecified'
|
||
});
|
||
},
|
||
|
||
/**
|
||
* Highlights the load more link.
|
||
*
|
||
* @param item The item to highlight.
|
||
*/
|
||
highlightLoadMore: function () {
|
||
toggleClass(this.$(HIGHLIGHT_SELECTOR), HIGHLIGHT_CLASS, false);
|
||
toggleClass(this.$(LOAD_MORE_SELECTOR), HIGHLIGHT_CLASS, true);
|
||
|
||
this.highlightedResult = null;
|
||
this.loadMoreHighlighted = true;
|
||
},
|
||
|
||
/**
|
||
* Loads a follow-up page with results after a search.
|
||
*
|
||
* This method should only be called after a call to search() when the callback has indicated
|
||
* more results are available.
|
||
*/
|
||
loadMore: function () {
|
||
removeElement(this.$(LOAD_MORE_SELECTOR));
|
||
this.resultsContainer.innerHTML += this.selectivity.template('loading');
|
||
|
||
this.options.query({
|
||
callback: function (response) {
|
||
if (response && response.results) {
|
||
this._showResults(Selectivity.processItems(response.results), {
|
||
add: true,
|
||
hasMore: !!response.more
|
||
});
|
||
} else {
|
||
throw new Error('callback must be passed a response object');
|
||
}
|
||
}.bind(this),
|
||
error: this._showResults.bind(this, [], {add: true}),
|
||
offset: this.results.length,
|
||
selectivity: this.selectivity,
|
||
term: this.term
|
||
});
|
||
},
|
||
|
||
/**
|
||
* Positions the dropdown inside the DOM.
|
||
*/
|
||
position: function () {
|
||
var position = this.options.position;
|
||
if (position) {
|
||
position(this.el, this.selectivity.el);
|
||
}
|
||
|
||
this._scrolled();
|
||
},
|
||
|
||
/**
|
||
* Renders an array of result items.
|
||
*
|
||
* @param items Array of result items.
|
||
*
|
||
* @return HTML-formatted string to display the result items.
|
||
*/
|
||
renderItems: function (items) {
|
||
var selectivity = this.selectivity;
|
||
return items
|
||
.map(function (item) {
|
||
var result = selectivity.template(item.id ? 'resultItem' : 'resultLabel', item);
|
||
if (item.children) {
|
||
result += selectivity.template('resultChildren', {
|
||
childrenHtml: this.renderItems(item.children)
|
||
});
|
||
}
|
||
return result;
|
||
}, this)
|
||
.join('');
|
||
},
|
||
|
||
/**
|
||
* Searches for results based on the term given.
|
||
*
|
||
* If an items array has been passed with the options to the Selectivity instance, a local
|
||
* search will be performed among those items. Otherwise, the query function specified in the
|
||
* options will be used to perform the search. If neither is defined, nothing happens.
|
||
*
|
||
* @param term Term to search for.
|
||
*/
|
||
search: function (term) {
|
||
this.term = term;
|
||
|
||
if (this.options.items) {
|
||
term = Selectivity.transformText(term);
|
||
var matcher = this.selectivity.options.matcher || Selectivity.matcher;
|
||
this._showResults(
|
||
this.options.items
|
||
.map(function (item) {
|
||
return matcher(item, term);
|
||
})
|
||
.filter(function (item) {
|
||
return !!item;
|
||
}),
|
||
{term: term}
|
||
);
|
||
} else if (this.options.query) {
|
||
this.options.query({
|
||
callback: function (response) {
|
||
if (response && response.results) {
|
||
this._showResults(Selectivity.processItems(response.results), {
|
||
hasMore: !!response.more,
|
||
term: term
|
||
});
|
||
} else {
|
||
throw new Error('callback must be passed a response object');
|
||
}
|
||
}.bind(this),
|
||
error: this.showError.bind(this),
|
||
offset: 0,
|
||
selectivity: this.selectivity,
|
||
term: term
|
||
});
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Selects the highlighted item.
|
||
*/
|
||
selectHighlight: function () {
|
||
if (this.highlightedResult) {
|
||
this.selectItem(this.highlightedResult.id);
|
||
} else if (this.loadMoreHighlighted) {
|
||
this.loadMore();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Selects the item with the given ID.
|
||
*
|
||
* @param id ID of the item to select.
|
||
*/
|
||
selectItem: function (id) {
|
||
var item = Selectivity.findNestedById(this.results, id);
|
||
if (item && !item.disabled && item.selectable !== false) {
|
||
var options = {id: id, item: item};
|
||
if (this.selectivity.triggerEvent('selectivity-selecting', options)) {
|
||
this.selectivity.triggerEvent('selectivity-selected', options);
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Shows an error message.
|
||
*
|
||
* @param message Error message to display.
|
||
* @param options Options object. May contain the following property:
|
||
* escape - Set to false to disable HTML-escaping of the message. Useful if you
|
||
* want to set raw HTML as the message, but may open you up to XSS
|
||
* attacks if you're not careful with escaping user input.
|
||
*/
|
||
showError: function (message, options) {
|
||
this.resultsContainer.innerHTML = this.selectivity.template('error', {
|
||
escape: !options || options.escape !== false,
|
||
message: message
|
||
});
|
||
|
||
this.hasMore = false;
|
||
this.results = [];
|
||
|
||
this.highlightedResult = null;
|
||
this.loadMoreHighlighted = false;
|
||
|
||
this.position();
|
||
},
|
||
|
||
/**
|
||
* Shows a loading indicator in the dropdown.
|
||
*/
|
||
showLoading: function () {
|
||
this.resultsContainer.innerHTML = this.selectivity.template('loading');
|
||
|
||
this.hasMore = false;
|
||
this.results = [];
|
||
|
||
this.highlightedResult = null;
|
||
this.loadMoreHighlighted = false;
|
||
|
||
this.position();
|
||
},
|
||
|
||
/**
|
||
* Shows the results from a search query.
|
||
*
|
||
* @param results Array of result items.
|
||
* @param options Options object. May contain the following properties:
|
||
* add - True if the results should be added to any already shown results.
|
||
* dropdown - The dropdown instance for which the results are meant.
|
||
* hasMore - Boolean whether more results can be fetched using the query()
|
||
* function.
|
||
* term - The search term for which the results are displayed.
|
||
*/
|
||
showResults: function (results, options) {
|
||
if (options.add) {
|
||
removeElement(this.$('.selectivity-loading'));
|
||
} else {
|
||
this.resultsContainer.innerHTML = '';
|
||
}
|
||
|
||
var filteredResults = this.selectivity.filterResults(results);
|
||
var resultsHtml = this.renderItems(filteredResults);
|
||
if (options.hasMore) {
|
||
resultsHtml += this.selectivity.template('loadMore');
|
||
} else if (!resultsHtml && !options.add) {
|
||
resultsHtml = this.selectivity.template('noResults', {term: options.term});
|
||
}
|
||
this.resultsContainer.innerHTML += resultsHtml;
|
||
|
||
this.results = options.add ? this.results.concat(results) : results;
|
||
|
||
this.hasMore = options.hasMore;
|
||
|
||
var value = this.selectivity.getValue();
|
||
if (value && !Array.isArray(value)) {
|
||
var item = Selectivity.findNestedById(results, value);
|
||
if (item) {
|
||
this.highlight(item, {reason: 'current_value'});
|
||
}
|
||
} else if (
|
||
this.options.highlightFirstItem !== false &&
|
||
(!options.add || this.loadMoreHighlighted)
|
||
) {
|
||
this._highlightFirstItem(filteredResults);
|
||
}
|
||
|
||
this.position();
|
||
},
|
||
|
||
/**
|
||
* Triggers the 'selectivity-close' event.
|
||
*/
|
||
triggerClose: function () {
|
||
this.selectivity.triggerEvent('selectivity-close');
|
||
},
|
||
|
||
/**
|
||
* Triggers the 'selectivity-open' event.
|
||
*/
|
||
triggerOpen: function () {
|
||
this.selectivity.triggerEvent('selectivity-open');
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_attachScrollListeners: function () {
|
||
for (var i = 0; i < SCROLL_EVENTS.length; i++) {
|
||
window.addEventListener(SCROLL_EVENTS[i], this.position, true);
|
||
}
|
||
window.addEventListener('resize', this.position);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_highlightFirstItem: function (results) {
|
||
function findFirstItem(results) {
|
||
for (var i = 0, length = results.length; i < length; i++) {
|
||
var result = results[i];
|
||
if (result.id) {
|
||
return result;
|
||
} else if (result.children) {
|
||
var item = findFirstItem(result.children);
|
||
if (item) {
|
||
return item;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
var firstItem = findFirstItem(results);
|
||
if (firstItem) {
|
||
this.highlight(firstItem, {reason: 'first_result'});
|
||
} else {
|
||
this.highlightedResult = null;
|
||
this.loadMoreHighlighted = false;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_loadMoreClicked: function (event) {
|
||
this.loadMore();
|
||
|
||
stopPropagation(event);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_loadMoreHovered: function (event) {
|
||
if (
|
||
event.screenX === undefined ||
|
||
event.screenX !== this._lastMousePosition.x ||
|
||
event.screenY === undefined ||
|
||
event.screenY !== this._lastMousePosition.y
|
||
) {
|
||
this.highlightLoadMore();
|
||
|
||
this._recordMousePosition(event);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_recordMousePosition: function (event) {
|
||
this._lastMousePosition = {x: event.screenX, y: event.screenY};
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_removeScrollListeners: function () {
|
||
for (var i = 0; i < SCROLL_EVENTS.length; i++) {
|
||
window.removeEventListener(SCROLL_EVENTS[i], this.position, true);
|
||
}
|
||
window.removeEventListener('resize', this.position);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_resultClicked: function (event) {
|
||
this.selectItem(this.selectivity.getRelatedItemId(event));
|
||
|
||
stopPropagation(event);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_resultHovered: function (event) {
|
||
if (
|
||
!event.screenX ||
|
||
event.screenX !== this._lastMousePosition.x ||
|
||
!event.screenY ||
|
||
event.screenY !== this._lastMousePosition.y
|
||
) {
|
||
var id = this.selectivity.getRelatedItemId(event);
|
||
var item = Selectivity.findNestedById(this.results, id);
|
||
if (item && !item.disabled) {
|
||
this.highlight(item, {reason: 'hovered'});
|
||
}
|
||
|
||
this._recordMousePosition(event);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_scrolled: function () {
|
||
var el = this.$(LOAD_MORE_SELECTOR);
|
||
if (el && el.offsetTop - this.resultsContainer.scrollTop < this.el.clientHeight) {
|
||
this.loadMore();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_showResults: function (results, options) {
|
||
this.showResults(results, assign({dropdown: this}, options));
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_suppressWheel: function () {
|
||
var suppressWheelSelector = this.selectivity.options.suppressWheelSelector;
|
||
if (suppressWheelSelector === null) {
|
||
return;
|
||
}
|
||
|
||
var selector = suppressWheelSelector || '.selectivity-results-container';
|
||
this.events.on('wheel', selector, function (event) {
|
||
// Thanks to Troy Alford:
|
||
// http://stackoverflow.com/questions/5802467/prevent-scrolling-of-parent-element
|
||
|
||
var delta = event.deltaMode === 0 ? event.deltaY : event.deltaY * 40;
|
||
var el = findClosestElementMatchingSelector(event.target, selector);
|
||
var height = el.clientHeight;
|
||
var scrollHeight = el.scrollHeight;
|
||
var scrollTop = el.scrollTop;
|
||
|
||
function prevent() {
|
||
stopPropagation(event);
|
||
event.preventDefault();
|
||
}
|
||
|
||
if (scrollHeight > height) {
|
||
if (delta < -scrollTop) {
|
||
// Scrolling up, but this will take us past the top.
|
||
el.scrollTop = 0;
|
||
prevent();
|
||
} else if (delta > scrollHeight - height - scrollTop) {
|
||
// Scrolling down, but this will take us past the bottom.
|
||
el.scrollTop = scrollHeight;
|
||
prevent();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
module.exports = Selectivity.Dropdown = SelectivityDropdown;
|
||
|
||
}, {
|
||
"23": 23,
|
||
"38": 38,
|
||
"41": 41,
|
||
"43": 43,
|
||
"44": 44,
|
||
"45": 45,
|
||
"46": 46,
|
||
"47": 47,
|
||
"lodash/assign": "lodash/assign"
|
||
}], 23: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var assign = (window.jQuery || window.Zepto).extend;
|
||
var isString = _dereq_(16);
|
||
|
||
var matchesSelector = _dereq_(43);
|
||
|
||
var CAPTURED_EVENTS = ['blur', 'focus', 'mouseenter', 'mouseleave', 'scroll'];
|
||
|
||
/**
|
||
* Listens to events dispatched to an element or its children.
|
||
*
|
||
* @param el The element to listen to.
|
||
* @param context Optional context in which to execute the callbacks.
|
||
*/
|
||
function EventListener(el, context) {
|
||
this.context = context || null;
|
||
|
||
this.el = el;
|
||
|
||
this.events = {};
|
||
|
||
this._onEvent = this._onEvent.bind(this);
|
||
}
|
||
|
||
assign(EventListener.prototype, {
|
||
/**
|
||
* Destructor.
|
||
*
|
||
* Removes all event listeners and cleans up all references.
|
||
*/
|
||
destruct: function () {
|
||
Object.keys(this.events).forEach(function (eventName) {
|
||
var useCapture = CAPTURED_EVENTS.indexOf(eventName) > -1;
|
||
this.el.removeEventListener(eventName, this._onEvent, useCapture);
|
||
}, this);
|
||
|
||
this.context = null;
|
||
this.el = null;
|
||
this.events = null;
|
||
},
|
||
|
||
/**
|
||
* Stops listening to an event.
|
||
*
|
||
* The arguments are the same as for on(), but when no callback is given, all callbacks for the
|
||
* given event and class are discarded.
|
||
*/
|
||
off: function (eventName, selector, callback) {
|
||
if (!isString(selector)) {
|
||
callback = selector;
|
||
selector = '';
|
||
}
|
||
|
||
if (callback) {
|
||
var events = this.events[eventName];
|
||
if (events) {
|
||
events = events[selector];
|
||
if (events) {
|
||
for (var i = 0; i < events.length; i++) {
|
||
if (events[i] === callback) {
|
||
events.splice(i, 1);
|
||
i--;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
this.events[eventName][selector] = [];
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Starts listening to an event.
|
||
*
|
||
* @param eventName Name of the event to listen to, in lower-case.
|
||
* @param selector Optional CSS selector. If given, only events inside a child element matching
|
||
* the selector are caught.
|
||
* @param callback Callback to invoke when the event is caught.
|
||
*
|
||
* Alternatively, the arguments may be provided using a map to start listening to multiple
|
||
* events at once. Here, the keys of the map are eventNames and the values are callbacks.
|
||
* Selectors may be specified by separating them from the event name with a space. For example:
|
||
*
|
||
* .on({
|
||
* 'blur': this._blurred,
|
||
* 'click .some-input': this._inputClicked,
|
||
* })
|
||
*/
|
||
on: function (eventName, selector, callback) {
|
||
if (!isString(eventName)) {
|
||
var eventsMap = eventName;
|
||
for (var key in eventsMap) {
|
||
if (eventsMap.hasOwnProperty(key)) {
|
||
var split = key.split(' ');
|
||
if (split.length > 1) {
|
||
this.on(split[0], split[1], eventsMap[key]);
|
||
} else {
|
||
this.on(split[0], eventsMap[key]);
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (!isString(selector)) {
|
||
callback = selector;
|
||
selector = '';
|
||
}
|
||
|
||
if (!this.events.hasOwnProperty(eventName)) {
|
||
var useCapture = CAPTURED_EVENTS.indexOf(eventName) > -1;
|
||
this.el.addEventListener(eventName, this._onEvent, useCapture);
|
||
|
||
this.events[eventName] = {};
|
||
}
|
||
|
||
if (!this.events[eventName].hasOwnProperty(selector)) {
|
||
this.events[eventName][selector] = [];
|
||
}
|
||
|
||
if (this.events[eventName][selector].indexOf(callback) < 0) {
|
||
this.events[eventName][selector].push(callback);
|
||
}
|
||
},
|
||
|
||
_onEvent: function (event) {
|
||
var isPropagationStopped = false;
|
||
var stopPropagation = event.stopPropagation;
|
||
event.stopPropagation = function () {
|
||
stopPropagation.call(event);
|
||
isPropagationStopped = true;
|
||
};
|
||
|
||
var context = this.context;
|
||
|
||
function callAll(callbacks) {
|
||
for (var i = 0; i < callbacks.length; i++) {
|
||
callbacks[i].call(context, event);
|
||
}
|
||
}
|
||
|
||
var target = event.target;
|
||
var events = this.events[event.type.toLowerCase()];
|
||
while (target && target !== this.el && !isPropagationStopped) {
|
||
for (var selector in events) {
|
||
if (
|
||
selector &&
|
||
events.hasOwnProperty(selector) &&
|
||
matchesSelector(target, selector)
|
||
) {
|
||
callAll(events[selector]);
|
||
}
|
||
}
|
||
target = target.parentElement;
|
||
}
|
||
|
||
if (!isPropagationStopped && events.hasOwnProperty('')) {
|
||
callAll(events['']);
|
||
}
|
||
}
|
||
});
|
||
|
||
module.exports = EventListener;
|
||
|
||
}, {"16": 16, "43": 43, "lodash/assign": "lodash/assign"}], 24: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var assign = (window.jQuery || window.Zepto).extend;
|
||
|
||
var MultipleInput = _dereq_(25);
|
||
var Selectivity = _dereq_(38);
|
||
|
||
function isValidEmail(email) {
|
||
var atIndex = email.indexOf('@');
|
||
if (atIndex === -1 || email.indexOf(' ') > -1) {
|
||
return false; // email needs to have an '@', and may not contain any spaces
|
||
}
|
||
|
||
var dotIndex = email.lastIndexOf('.');
|
||
if (dotIndex === -1) {
|
||
// no dot is fine, as long as the '@' is followed by at least two more characters
|
||
return atIndex < email.length - 2;
|
||
}
|
||
|
||
// but if there is a dot after the '@', it must be followed by at least two more characters
|
||
return dotIndex > atIndex ? dotIndex < email.length - 2 : true;
|
||
}
|
||
|
||
function lastWord(token, length) {
|
||
length = length === undefined ? token.length : length;
|
||
for (var i = length - 1; i >= 0; i--) {
|
||
if (/\s/.test(token[i])) {
|
||
return token.slice(i + 1, length);
|
||
}
|
||
}
|
||
return token.slice(0, length);
|
||
}
|
||
|
||
function stripEnclosure(token, enclosure) {
|
||
if (token.charAt(0) === enclosure[0] && token.slice(-1) === enclosure[1]) {
|
||
return token.slice(1, -1).trim();
|
||
} else {
|
||
return token.trim();
|
||
}
|
||
}
|
||
|
||
function createEmailItem(token) {
|
||
var email = lastWord(token);
|
||
var name = token.slice(0, -email.length).trim();
|
||
if (isValidEmail(email)) {
|
||
email = stripEnclosure(stripEnclosure(email, '()'), '<>');
|
||
name = stripEnclosure(name, '""').trim() || email;
|
||
return {id: email, text: name};
|
||
} else {
|
||
return token.trim() ? {id: token, text: token} : null;
|
||
}
|
||
}
|
||
|
||
function emailTokenizer(input, selection, createToken) {
|
||
function hasToken(input) {
|
||
if (input) {
|
||
for (var i = 0, length = input.length; i < length; i++) {
|
||
switch (input[i]) {
|
||
case ';':
|
||
case ',':
|
||
case '\n':
|
||
return true;
|
||
case ' ':
|
||
case '\t':
|
||
if (isValidEmail(lastWord(input, i))) {
|
||
return true;
|
||
}
|
||
break;
|
||
case '"':
|
||
do {
|
||
i++;
|
||
} while (i < length && input[i] !== '"');
|
||
break;
|
||
default:
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function takeToken(input) {
|
||
for (var i = 0, length = input.length; i < length; i++) {
|
||
switch (input[i]) {
|
||
case ';':
|
||
case ',':
|
||
case '\n':
|
||
return {term: input.slice(0, i), input: input.slice(i + 1)};
|
||
case ' ':
|
||
case '\t':
|
||
if (isValidEmail(lastWord(input, i))) {
|
||
return {term: input.slice(0, i), input: input.slice(i + 1)};
|
||
}
|
||
break;
|
||
case '"':
|
||
do {
|
||
i++;
|
||
} while (i < length && input[i] !== '"');
|
||
break;
|
||
default:
|
||
continue;
|
||
}
|
||
}
|
||
return {};
|
||
}
|
||
|
||
while (hasToken(input)) {
|
||
var token = takeToken(input);
|
||
if (token.term) {
|
||
var item = createEmailItem(token.term);
|
||
if (item && !(item.id && Selectivity.findById(selection, item.id))) {
|
||
createToken(item);
|
||
}
|
||
}
|
||
input = token.input;
|
||
}
|
||
|
||
return input;
|
||
}
|
||
|
||
/**
|
||
* EmailInput Constructor.
|
||
*
|
||
* @param options Options object. Accepts all options from the MultipleInput Constructor.
|
||
*/
|
||
function EmailInput(options) {
|
||
MultipleInput.call(
|
||
this,
|
||
assign(
|
||
{
|
||
createTokenItem: createEmailItem,
|
||
showDropdown: false,
|
||
tokenizer: emailTokenizer
|
||
},
|
||
options
|
||
)
|
||
);
|
||
|
||
this.events.on('blur', function () {
|
||
var input = this.input;
|
||
if (input && isValidEmail(lastWord(input.value))) {
|
||
this.add(createEmailItem(input.value));
|
||
}
|
||
});
|
||
}
|
||
|
||
Selectivity.inherits(EmailInput, MultipleInput);
|
||
|
||
module.exports = Selectivity.Inputs.Email = EmailInput;
|
||
|
||
}, {"25": 25, "38": 38, "lodash/assign": "lodash/assign"}], 25: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var assign = (window.jQuery || window.Zepto).extend;
|
||
var isString = _dereq_(16);
|
||
|
||
var Selectivity = _dereq_(38);
|
||
var getItemSelector = _dereq_(41);
|
||
var getKeyCode = _dereq_(42);
|
||
var parseElement = _dereq_(44);
|
||
var removeElement = _dereq_(45);
|
||
var stopPropagation = _dereq_(46);
|
||
var toggleClass = _dereq_(47);
|
||
|
||
var KEY_BACKSPACE = 8;
|
||
var KEY_DELETE = 46;
|
||
var KEY_ENTER = 13;
|
||
|
||
var INPUT_SELECTOR = '.selectivity-multiple-input';
|
||
var SELECTED_ITEM_SELECTOR = '.selectivity-multiple-selected-item';
|
||
|
||
var hasTouch = 'ontouchstart' in window;
|
||
|
||
/**
|
||
* MultipleInput Constructor.
|
||
*/
|
||
function MultipleInput(options) {
|
||
Selectivity.call(
|
||
this,
|
||
assign(
|
||
{
|
||
// dropdowns for multiple-value inputs should open below the select box,
|
||
// unless there is not enough space below, but there is space enough above, then it should
|
||
// open upwards
|
||
positionDropdown: function (el, selectEl) {
|
||
var rect = selectEl.getBoundingClientRect();
|
||
var dropdownHeight = el.clientHeight;
|
||
var openUpwards =
|
||
rect.bottom + dropdownHeight > window.innerHeight &&
|
||
rect.top - dropdownHeight > 0;
|
||
|
||
assign(el.style, {
|
||
left: rect.left + 'px',
|
||
top: (openUpwards ? rect.top - dropdownHeight : rect.bottom) + 'px',
|
||
width: rect.width + 'px'
|
||
});
|
||
},
|
||
|
||
showSearchInputInDropdown: false
|
||
},
|
||
options
|
||
)
|
||
);
|
||
|
||
this._reset();
|
||
|
||
var events = {
|
||
change: this.rerenderSelection,
|
||
click: this._clicked,
|
||
'selectivity-selected': this._resultSelected
|
||
};
|
||
events['change ' + INPUT_SELECTOR] = stopPropagation;
|
||
events['click ' + SELECTED_ITEM_SELECTOR] = this._itemClicked;
|
||
events['click ' + SELECTED_ITEM_SELECTOR + '-remove'] = this._itemRemoveClicked;
|
||
events['keydown ' + INPUT_SELECTOR] = this._keyHeld;
|
||
events['keyup ' + INPUT_SELECTOR] = this._keyReleased;
|
||
events['paste ' + INPUT_SELECTOR] = this._onPaste;
|
||
|
||
this.events.on(events);
|
||
}
|
||
|
||
/**
|
||
* Methods.
|
||
*/
|
||
var callSuper = Selectivity.inherits(MultipleInput, Selectivity, {
|
||
/**
|
||
* Adds an item to the selection, if it's not selected yet.
|
||
*
|
||
* @param item The item to add. May be an item with 'id' and 'text' properties or just an ID.
|
||
*/
|
||
add: function (item) {
|
||
var itemIsId = Selectivity.isValidId(item);
|
||
var id = itemIsId ? item : this.validateItem(item) && item.id;
|
||
|
||
if (this._value.indexOf(id) === -1) {
|
||
this._value.push(id);
|
||
|
||
if (itemIsId && this.options.initSelection) {
|
||
this.options.initSelection(
|
||
[id],
|
||
function (data) {
|
||
if (this._value.indexOf(id) > -1) {
|
||
item = this.validateItem(data[0]);
|
||
this._data.push(item);
|
||
|
||
this.triggerChange({added: item});
|
||
}
|
||
}.bind(this)
|
||
);
|
||
} else {
|
||
if (itemIsId) {
|
||
item = this.getItemForId(id);
|
||
}
|
||
this._data.push(item);
|
||
|
||
this.triggerChange({added: item});
|
||
}
|
||
}
|
||
|
||
this.input.value = '';
|
||
this._updateInputWidth();
|
||
},
|
||
|
||
/**
|
||
* Clears the data and value.
|
||
*/
|
||
clear: function () {
|
||
this.setData([]);
|
||
},
|
||
|
||
/**
|
||
* @inherit
|
||
*/
|
||
filterResults: function (results) {
|
||
var hasChildren = results.some(function (item) {
|
||
return item.children;
|
||
});
|
||
if (hasChildren) {
|
||
results = results.map(function (item) {
|
||
return {
|
||
id: item.id,
|
||
text: item.text,
|
||
children: this.filterResults(item.children)
|
||
};
|
||
}, this);
|
||
}
|
||
|
||
return results.filter(function (item) {
|
||
return !Selectivity.findById(this._data, item.id);
|
||
}, this);
|
||
},
|
||
|
||
/**
|
||
* Returns the correct data for a given value.
|
||
*
|
||
* @param value The value to get the data for. Should be an array of IDs.
|
||
*
|
||
* @return The corresponding data. Will be an array of objects with 'id' and 'text' properties.
|
||
* Note that if no items are defined, this method assumes the text labels will be equal
|
||
* to the IDs.
|
||
*/
|
||
getDataForValue: function (value) {
|
||
return value.map(this.getItemForId, this).filter(function (item) {
|
||
return !!item;
|
||
});
|
||
},
|
||
|
||
/**
|
||
* Returns the correct value for the given data.
|
||
*
|
||
* @param data The data to get the value for. Should be an array of objects with 'id' and 'text'
|
||
* properties.
|
||
*
|
||
* @return The corresponding value. Will be an array of IDs.
|
||
*/
|
||
getValueForData: function (data) {
|
||
return data.map(function (item) {
|
||
return item.id;
|
||
});
|
||
},
|
||
|
||
/**
|
||
* Removes an item from the selection, if it is selected.
|
||
*
|
||
* @param item The item to remove. May be an item with 'id' and 'text' properties or just an ID.
|
||
*/
|
||
remove: function (item) {
|
||
var id = item.id || item;
|
||
|
||
var removedItem;
|
||
var index = Selectivity.findIndexById(this._data, id);
|
||
if (index > -1) {
|
||
removedItem = this._data[index];
|
||
this._data.splice(index, 1);
|
||
}
|
||
|
||
if (this._value[index] !== id) {
|
||
index = this._value.indexOf(id);
|
||
}
|
||
if (index > -1) {
|
||
this._value.splice(index, 1);
|
||
}
|
||
|
||
if (removedItem) {
|
||
this.triggerChange({removed: removedItem});
|
||
}
|
||
|
||
if (id === this._highlightedItemId) {
|
||
this._highlightedItemId = null;
|
||
}
|
||
|
||
this._updateInputWidth();
|
||
},
|
||
|
||
/**
|
||
* Re-renders the selection.
|
||
*
|
||
* Normally the UI is automatically updated whenever the selection changes, but you may want to
|
||
* call this method explicitly if you've updated the selection with the triggerChange option set
|
||
* to false.
|
||
*/
|
||
rerenderSelection: function (event) {
|
||
event = event || {};
|
||
|
||
if (event.added) {
|
||
this._renderSelectedItem(event.added);
|
||
|
||
this._scrollToBottom();
|
||
} else if (event.removed) {
|
||
removeElement(this.$(getItemSelector(SELECTED_ITEM_SELECTOR, event.removed.id)));
|
||
} else {
|
||
this._forEachSelectedItem(removeElement);
|
||
|
||
this._data.forEach(this._renderSelectedItem, this);
|
||
|
||
this._updateInputWidth();
|
||
}
|
||
|
||
if (event.added || event.removed) {
|
||
if (this.dropdown) {
|
||
this.dropdown.showResults(this.filterResults(this.dropdown.results), {
|
||
hasMore: this.dropdown.hasMore
|
||
});
|
||
}
|
||
|
||
if (!hasTouch) {
|
||
this.focus();
|
||
}
|
||
}
|
||
|
||
this.positionDropdown();
|
||
|
||
this._updatePlaceholder();
|
||
},
|
||
|
||
/**
|
||
* @inherit
|
||
*/
|
||
search: function (term) {
|
||
if (this.options.tokenizer) {
|
||
term = this.options.tokenizer(term, this._data, this.add.bind(this), this.options);
|
||
|
||
if (isString(term) && term !== this.input.value) {
|
||
this.input.value = term;
|
||
}
|
||
}
|
||
|
||
this._updateInputWidth();
|
||
|
||
if (this.dropdown) {
|
||
callSuper(this, 'search', term);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @inherit
|
||
*/
|
||
setOptions: function (options) {
|
||
var wasEnabled = this.enabled;
|
||
|
||
callSuper(this, 'setOptions', options);
|
||
|
||
if (wasEnabled !== this.enabled) {
|
||
this._reset();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Validates data to set. Throws an exception if the data is invalid.
|
||
*
|
||
* @param data The data to validate. Should be an array of objects with 'id' and 'text'
|
||
* properties.
|
||
*
|
||
* @return The validated data. This may differ from the input data.
|
||
*/
|
||
validateData: function (data) {
|
||
if (data === null) {
|
||
return [];
|
||
} else if (Array.isArray(data)) {
|
||
return data.map(this.validateItem, this);
|
||
} else {
|
||
throw new Error('Data for MultiSelectivity instance should be an array');
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Validates a value to set. Throws an exception if the value is invalid.
|
||
*
|
||
* @param value The value to validate. Should be an array of IDs.
|
||
*
|
||
* @return The validated value. This may differ from the input value.
|
||
*/
|
||
validateValue: function (value) {
|
||
if (value === null) {
|
||
return [];
|
||
} else if (Array.isArray(value)) {
|
||
if (value.every(Selectivity.isValidId)) {
|
||
return value;
|
||
} else {
|
||
throw new Error('Value contains invalid IDs');
|
||
}
|
||
} else {
|
||
throw new Error('Value for MultiSelectivity instance should be an array');
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_backspacePressed: function () {
|
||
if (this.options.backspaceHighlightsBeforeDelete) {
|
||
if (this._highlightedItemId) {
|
||
this._deletePressed();
|
||
} else if (this._value.length) {
|
||
this._highlightItem(this._value.slice(-1)[0]);
|
||
}
|
||
} else if (this._value.length) {
|
||
this.remove(this._value.slice(-1)[0]);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_clicked: function (event) {
|
||
if (this.enabled) {
|
||
if (this.options.showDropdown !== false) {
|
||
this.open();
|
||
} else {
|
||
this.focus();
|
||
}
|
||
|
||
stopPropagation(event);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_createToken: function () {
|
||
var term = this.input.value;
|
||
var createTokenItem = this.options.createTokenItem;
|
||
|
||
if (term && createTokenItem) {
|
||
var item = createTokenItem(term);
|
||
if (item) {
|
||
this.add(item);
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_deletePressed: function () {
|
||
if (this._highlightedItemId) {
|
||
this.remove(this._highlightedItemId);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_forEachSelectedItem: function (callback) {
|
||
Array.prototype.forEach.call(this.el.querySelectorAll(SELECTED_ITEM_SELECTOR), callback);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_highlightItem: function (id) {
|
||
this._highlightedItemId = id;
|
||
|
||
this._forEachSelectedItem(function (el) {
|
||
toggleClass(el, 'highlighted', el.getAttribute('data-item-id') === id);
|
||
});
|
||
|
||
if (!hasTouch) {
|
||
this.focus();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_itemClicked: function (event) {
|
||
if (this.enabled) {
|
||
this._highlightItem(this.getRelatedItemId(event));
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_itemRemoveClicked: function (event) {
|
||
this.remove(this.getRelatedItemId(event));
|
||
|
||
stopPropagation(event);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_keyHeld: function (event) {
|
||
this._originalValue = this.input.value;
|
||
|
||
if (getKeyCode(event) === KEY_ENTER && !event.ctrlKey) {
|
||
event.preventDefault();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_keyReleased: function (event) {
|
||
var inputHadText = !!this._originalValue;
|
||
var keyCode = getKeyCode(event);
|
||
|
||
if (keyCode === KEY_ENTER && !event.ctrlKey) {
|
||
this._createToken();
|
||
} else if (keyCode === KEY_BACKSPACE && !inputHadText) {
|
||
this._backspacePressed();
|
||
} else if (keyCode === KEY_DELETE && !inputHadText) {
|
||
this._deletePressed();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_onPaste: function () {
|
||
setTimeout(
|
||
function () {
|
||
this.search(this.input.value);
|
||
|
||
this._createToken();
|
||
}.bind(this),
|
||
10
|
||
);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_renderSelectedItem: function (item) {
|
||
var el = parseElement(
|
||
this.template(
|
||
'multipleSelectedItem',
|
||
assign(
|
||
{
|
||
highlighted: item.id === this._highlightedItemId,
|
||
removable: !this.options.readOnly
|
||
},
|
||
item
|
||
)
|
||
)
|
||
);
|
||
|
||
this.input.parentNode.insertBefore(el, this.input);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_reset: function () {
|
||
this.el.innerHTML = this.template('multipleSelectInput', {enabled: this.enabled});
|
||
|
||
this._highlightedItemId = null;
|
||
|
||
this.initInput(this.$(INPUT_SELECTOR));
|
||
|
||
this.rerenderSelection();
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_resultSelected: function (event) {
|
||
if (this._value.indexOf(event.id) === -1) {
|
||
this.add(event.item);
|
||
} else {
|
||
this.remove(event.item);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_scrollToBottom: function () {
|
||
var inputContainer = this.$(INPUT_SELECTOR + '-container');
|
||
inputContainer.scrollTop = inputContainer.clientHeight;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_updateInputWidth: function () {
|
||
if (this.enabled) {
|
||
var inputContent =
|
||
this.input.value || (!this._data.length && this.options.placeholder) || '';
|
||
this.input.setAttribute('size', inputContent.length + 2);
|
||
|
||
this.positionDropdown();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_updatePlaceholder: function () {
|
||
var placeholder = (!this._data.length && this.options.placeholder) || '';
|
||
if (this.enabled) {
|
||
this.input.setAttribute('placeholder', placeholder);
|
||
} else {
|
||
this.$('.selectivity-placeholder').textContent = placeholder;
|
||
}
|
||
}
|
||
});
|
||
|
||
module.exports = Selectivity.Inputs.Multiple = MultipleInput;
|
||
|
||
}, {
|
||
"16": 16,
|
||
"38": 38,
|
||
"41": 41,
|
||
"42": 42,
|
||
"44": 44,
|
||
"45": 45,
|
||
"46": 46,
|
||
"47": 47,
|
||
"lodash/assign": "lodash/assign"
|
||
}], 26: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var assign = (window.jQuery || window.Zepto).extend;
|
||
|
||
var Selectivity = _dereq_(38);
|
||
var stopPropagation = _dereq_(46);
|
||
|
||
/**
|
||
* SingleInput Constructor.
|
||
*/
|
||
function SingleInput(options) {
|
||
Selectivity.call(
|
||
this,
|
||
assign(
|
||
{
|
||
// Dropdowns for single-value inputs should open below the select box, unless there
|
||
// is not enough space below, in which case the dropdown should be moved up just
|
||
// enough so it fits in the window, but never so much that it reaches above the top.
|
||
positionDropdown: function (el, selectEl) {
|
||
var rect = selectEl.getBoundingClientRect();
|
||
var dropdownTop = rect.bottom;
|
||
|
||
var deltaUp = Math.min(
|
||
Math.max(dropdownTop + el.clientHeight - window.innerHeight, 0),
|
||
rect.top + rect.height
|
||
);
|
||
|
||
assign(el.style, {
|
||
left: rect.left + 'px',
|
||
top: dropdownTop - deltaUp + 'px',
|
||
width: rect.width + 'px'
|
||
});
|
||
}
|
||
},
|
||
options
|
||
)
|
||
);
|
||
|
||
this.rerender();
|
||
|
||
if (options.showSearchInputInDropdown === false) {
|
||
this.initInput(this.$('.selectivity-single-select-input'), {search: false});
|
||
}
|
||
|
||
this.events.on({
|
||
change: this.rerenderSelection,
|
||
click: this._clicked,
|
||
'click .selectivity-search-input': stopPropagation,
|
||
'click .selectivity-single-selected-item-remove': this._itemRemoveClicked,
|
||
'focus .selectivity-single-select-input': this._focused,
|
||
'selectivity-selected': this._resultSelected
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Methods.
|
||
*/
|
||
var callSuper = Selectivity.inherits(SingleInput, Selectivity, {
|
||
/**
|
||
* Clears the data and value.
|
||
*/
|
||
clear: function () {
|
||
this.setData(null);
|
||
},
|
||
|
||
/**
|
||
* @inherit
|
||
*
|
||
* @param options Optional options object. May contain the following property:
|
||
* keepFocus - If true, the focus will remain on the input.
|
||
*/
|
||
close: function (options) {
|
||
this._closing = true;
|
||
|
||
callSuper(this, 'close');
|
||
|
||
if (options && options.keepFocus && this.input) {
|
||
this.input.focus();
|
||
}
|
||
|
||
this._closing = false;
|
||
},
|
||
|
||
/**
|
||
* Returns the correct data for a given value.
|
||
*
|
||
* @param value The value to get the data for. Should be an ID.
|
||
*
|
||
* @return The corresponding data. Will be an object with 'id' and 'text' properties. Note that
|
||
* if no items are defined, this method assumes the text label will be equal to the ID.
|
||
*/
|
||
getDataForValue: function (value) {
|
||
return this.getItemForId(value);
|
||
},
|
||
|
||
/**
|
||
* Returns the correct value for the given data.
|
||
*
|
||
* @param data The data to get the value for. Should be an object with 'id' and 'text'
|
||
* properties or null.
|
||
*
|
||
* @return The corresponding value. Will be an ID or null.
|
||
*/
|
||
getValueForData: function (data) {
|
||
return data ? data.id : null;
|
||
},
|
||
|
||
/**
|
||
* Rerenders the entire component.
|
||
*/
|
||
rerender: function () {
|
||
this.el.innerHTML = this.template('singleSelectInput', this.options);
|
||
|
||
this.rerenderSelection();
|
||
},
|
||
|
||
/**
|
||
* Re-renders the selection.
|
||
*
|
||
* Normally the UI is automatically updated whenever the selection changes, but you may want to
|
||
* call this method explicitly if you've updated the selection with the triggerChange option set
|
||
* to false.
|
||
*/
|
||
rerenderSelection: function () {
|
||
var template = this._data ? 'singleSelectedItem' : 'singleSelectPlaceholder';
|
||
var options = this._data
|
||
? assign(
|
||
{
|
||
removable: this.options.allowClear && !this.options.readOnly
|
||
},
|
||
this._data
|
||
)
|
||
: {placeholder: this.options.placeholder};
|
||
|
||
this.el.querySelector('input').value = this._value;
|
||
this.$('.selectivity-single-result-container').innerHTML = this.template(template, options);
|
||
},
|
||
|
||
/**
|
||
* @inherit
|
||
*/
|
||
setOptions: function (options) {
|
||
var wasEnabled = this.enabled;
|
||
|
||
callSuper(this, 'setOptions', options);
|
||
|
||
if (wasEnabled !== this.enabled) {
|
||
this.rerender();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Validates data to set. Throws an exception if the data is invalid.
|
||
*
|
||
* @param data The data to validate. Should be an object with 'id' and 'text' properties or null
|
||
* to indicate no item is selected.
|
||
*
|
||
* @return The validated data. This may differ from the input data.
|
||
*/
|
||
validateData: function (data) {
|
||
return data === null ? data : this.validateItem(data);
|
||
},
|
||
|
||
/**
|
||
* Validates a value to set. Throws an exception if the value is invalid.
|
||
*
|
||
* @param value The value to validate. Should be null or a valid ID.
|
||
*
|
||
* @return The validated value. This may differ from the input value.
|
||
*/
|
||
validateValue: function (value) {
|
||
if (value === null || Selectivity.isValidId(value)) {
|
||
return value;
|
||
} else {
|
||
throw new Error('Value for SingleSelectivity instance should be a valid ID or null');
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_clicked: function () {
|
||
if (this.enabled) {
|
||
if (this.dropdown) {
|
||
this.close({keepFocus: true});
|
||
} else if (this.options.showDropdown !== false) {
|
||
this.open();
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_focused: function () {
|
||
if (
|
||
this.enabled &&
|
||
!this._closing &&
|
||
!this._opening &&
|
||
this.options.showDropdown !== false
|
||
) {
|
||
this.open();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_itemRemoveClicked: function (event) {
|
||
this.setData(null);
|
||
|
||
stopPropagation(event);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_resultSelected: function (event) {
|
||
this.setData(event.item);
|
||
|
||
this.close({keepFocus: true});
|
||
}
|
||
});
|
||
|
||
module.exports = Selectivity.Inputs.Single = SingleInput;
|
||
|
||
}, {"38": 38, "46": 46, "lodash/assign": "lodash/assign"}], 27: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var escape = _dereq_(12);
|
||
|
||
var Selectivity = _dereq_(38);
|
||
|
||
/**
|
||
* Localizable elements of the Selectivity Templates.
|
||
*
|
||
* Be aware that these strings are added straight to the HTML output of the templates, so any
|
||
* non-safe strings should be escaped.
|
||
*/
|
||
module.exports = Selectivity.Locale = {
|
||
loading: 'Loading...',
|
||
loadMore: 'Load more...',
|
||
noResults: 'No results found',
|
||
|
||
ajaxError: function (term) {
|
||
if (term) {
|
||
return 'Failed to fetch results for <b>' + escape(term) + '</b>';
|
||
} else {
|
||
return 'Failed to fetch results';
|
||
}
|
||
},
|
||
|
||
needMoreCharacters: function (numCharacters) {
|
||
return 'Enter ' + numCharacters + ' more characters to search';
|
||
},
|
||
|
||
noResultsForTerm: function (term) {
|
||
return 'No results for <b>' + escape(term) + '</b>';
|
||
}
|
||
};
|
||
|
||
}, {"12": 12, "38": 38}], 28: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var debounce = _dereq_(11);
|
||
|
||
var Selectivity = _dereq_(38);
|
||
var Locale = _dereq_(27);
|
||
|
||
function addUrlParam(url, key, value) {
|
||
return url + (url.indexOf('?') > -1 ? '&' : '?') + key + '=' + encodeURIComponent(value);
|
||
}
|
||
|
||
function pick(object, keys) {
|
||
var result = {};
|
||
keys.forEach(function (key) {
|
||
if (object[key] !== undefined) {
|
||
result[key] = object[key];
|
||
}
|
||
});
|
||
return result;
|
||
}
|
||
|
||
function doFetch(ajax, queryOptions) {
|
||
var fetch = ajax.fetch || window.fetch;
|
||
var term = queryOptions.term;
|
||
|
||
var url = typeof ajax.url === 'function' ? ajax.url(queryOptions) : ajax.url;
|
||
if (ajax.params) {
|
||
var params = ajax.params(term, queryOptions.offset || 0);
|
||
for (var key in params) {
|
||
if (params.hasOwnProperty(key)) {
|
||
url = addUrlParam(url, key, params[key]);
|
||
}
|
||
}
|
||
}
|
||
|
||
var init = pick(ajax, [
|
||
'body',
|
||
'cache',
|
||
'credentials',
|
||
'headers',
|
||
'integrity',
|
||
'method',
|
||
'mode',
|
||
'redirect',
|
||
'referrer',
|
||
'referrerPolicy'
|
||
]);
|
||
|
||
fetch(url, init, queryOptions)
|
||
.then(function (response) {
|
||
if (response.ok) {
|
||
return response.json();
|
||
} else if (Array.isArray(response) || response.results) {
|
||
return response;
|
||
} else {
|
||
throw new Error('Unexpected AJAX response');
|
||
}
|
||
})
|
||
.then(function (response) {
|
||
if (Array.isArray(response)) {
|
||
queryOptions.callback({results: response, more: false});
|
||
} else {
|
||
queryOptions.callback({results: response.results, more: !!response.more});
|
||
}
|
||
})
|
||
.catch(function (error) {
|
||
var formatError = ajax.formatError || Locale.ajaxError;
|
||
queryOptions.error(formatError(term, error), {escape: false});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Option listener that implements a convenience query function for performing AJAX requests.
|
||
*/
|
||
Selectivity.OptionListeners.unshift(function (selectivity, options) {
|
||
var ajax = options.ajax;
|
||
if (ajax && ajax.url) {
|
||
var fetch = ajax.quietMillis ? debounce(doFetch, ajax.quietMillis) : doFetch;
|
||
|
||
options.query = function (queryOptions) {
|
||
var numCharsNeeded = ajax.minimumInputLength - queryOptions.term.length;
|
||
if (numCharsNeeded > 0) {
|
||
queryOptions.error(Locale.needMoreCharacters(numCharsNeeded));
|
||
return;
|
||
}
|
||
|
||
fetch(ajax, queryOptions);
|
||
};
|
||
}
|
||
});
|
||
|
||
}, {"11": 11, "27": 27, "38": 38}], 29: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var Selectivity = _dereq_(38);
|
||
|
||
var latestQueryNum = 0;
|
||
|
||
/**
|
||
* Option listener that will discard any callbacks from the query function if another query has
|
||
* been called afterwards. This prevents responses from remote sources arriving out-of-order.
|
||
*/
|
||
Selectivity.OptionListeners.push(function (selectivity, options) {
|
||
var query = options.query;
|
||
if (query && !query._async) {
|
||
options.query = function (queryOptions) {
|
||
latestQueryNum++;
|
||
var queryNum = latestQueryNum;
|
||
|
||
var callback = queryOptions.callback;
|
||
var error = queryOptions.error;
|
||
queryOptions.callback = function () {
|
||
if (queryNum === latestQueryNum) {
|
||
callback.apply(null, arguments);
|
||
}
|
||
};
|
||
queryOptions.error = function () {
|
||
if (queryNum === latestQueryNum) {
|
||
error.apply(null, arguments);
|
||
}
|
||
};
|
||
query(queryOptions);
|
||
};
|
||
options.query._async = true;
|
||
}
|
||
});
|
||
|
||
}, {"38": 38}], 30: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var DIACRITICS = {
|
||
'\u24B6': 'A',
|
||
A: 'A',
|
||
À: 'A',
|
||
Á: 'A',
|
||
Â: 'A',
|
||
Ầ: 'A',
|
||
Ấ: 'A',
|
||
Ẫ: 'A',
|
||
Ẩ: 'A',
|
||
Ã: 'A',
|
||
Ā: 'A',
|
||
Ă: 'A',
|
||
Ằ: 'A',
|
||
Ắ: 'A',
|
||
Ẵ: 'A',
|
||
Ẳ: 'A',
|
||
Ȧ: 'A',
|
||
Ǡ: 'A',
|
||
Ä: 'A',
|
||
Ǟ: 'A',
|
||
Ả: 'A',
|
||
Å: 'A',
|
||
Ǻ: 'A',
|
||
Ǎ: 'A',
|
||
Ȁ: 'A',
|
||
Ȃ: 'A',
|
||
Ạ: 'A',
|
||
Ậ: 'A',
|
||
Ặ: 'A',
|
||
Ḁ: 'A',
|
||
Ą: 'A',
|
||
Ⱥ: 'A',
|
||
Ɐ: 'A',
|
||
Ꜳ: 'AA',
|
||
Æ: 'AE',
|
||
Ǽ: 'AE',
|
||
Ǣ: 'AE',
|
||
Ꜵ: 'AO',
|
||
Ꜷ: 'AU',
|
||
Ꜹ: 'AV',
|
||
Ꜻ: 'AV',
|
||
Ꜽ: 'AY',
|
||
'\u24B7': 'B',
|
||
B: 'B',
|
||
Ḃ: 'B',
|
||
Ḅ: 'B',
|
||
Ḇ: 'B',
|
||
Ƀ: 'B',
|
||
Ƃ: 'B',
|
||
Ɓ: 'B',
|
||
'\u24B8': 'C',
|
||
C: 'C',
|
||
Ć: 'C',
|
||
Ĉ: 'C',
|
||
Ċ: 'C',
|
||
Č: 'C',
|
||
Ç: 'C',
|
||
Ḉ: 'C',
|
||
Ƈ: 'C',
|
||
Ȼ: 'C',
|
||
Ꜿ: 'C',
|
||
'\u24B9': 'D',
|
||
D: 'D',
|
||
Ḋ: 'D',
|
||
Ď: 'D',
|
||
Ḍ: 'D',
|
||
Ḑ: 'D',
|
||
Ḓ: 'D',
|
||
Ḏ: 'D',
|
||
Đ: 'D',
|
||
Ƌ: 'D',
|
||
Ɗ: 'D',
|
||
Ɖ: 'D',
|
||
Ꝺ: 'D',
|
||
DZ: 'DZ',
|
||
DŽ: 'DZ',
|
||
Dz: 'Dz',
|
||
Dž: 'Dz',
|
||
'\u24BA': 'E',
|
||
E: 'E',
|
||
È: 'E',
|
||
É: 'E',
|
||
Ê: 'E',
|
||
Ề: 'E',
|
||
Ế: 'E',
|
||
Ễ: 'E',
|
||
Ể: 'E',
|
||
Ẽ: 'E',
|
||
Ē: 'E',
|
||
Ḕ: 'E',
|
||
Ḗ: 'E',
|
||
Ĕ: 'E',
|
||
Ė: 'E',
|
||
Ë: 'E',
|
||
Ẻ: 'E',
|
||
Ě: 'E',
|
||
Ȅ: 'E',
|
||
Ȇ: 'E',
|
||
Ẹ: 'E',
|
||
Ệ: 'E',
|
||
Ȩ: 'E',
|
||
Ḝ: 'E',
|
||
Ę: 'E',
|
||
Ḙ: 'E',
|
||
Ḛ: 'E',
|
||
Ɛ: 'E',
|
||
Ǝ: 'E',
|
||
'\u24BB': 'F',
|
||
F: 'F',
|
||
Ḟ: 'F',
|
||
Ƒ: 'F',
|
||
Ꝼ: 'F',
|
||
'\u24BC': 'G',
|
||
G: 'G',
|
||
Ǵ: 'G',
|
||
Ĝ: 'G',
|
||
Ḡ: 'G',
|
||
Ğ: 'G',
|
||
Ġ: 'G',
|
||
Ǧ: 'G',
|
||
Ģ: 'G',
|
||
Ǥ: 'G',
|
||
Ɠ: 'G',
|
||
Ꞡ: 'G',
|
||
Ᵹ: 'G',
|
||
Ꝿ: 'G',
|
||
'\u24BD': 'H',
|
||
H: 'H',
|
||
Ĥ: 'H',
|
||
Ḣ: 'H',
|
||
Ḧ: 'H',
|
||
Ȟ: 'H',
|
||
Ḥ: 'H',
|
||
Ḩ: 'H',
|
||
Ḫ: 'H',
|
||
Ħ: 'H',
|
||
Ⱨ: 'H',
|
||
Ⱶ: 'H',
|
||
Ɥ: 'H',
|
||
'\u24BE': 'I',
|
||
I: 'I',
|
||
Ì: 'I',
|
||
Í: 'I',
|
||
Î: 'I',
|
||
Ĩ: 'I',
|
||
Ī: 'I',
|
||
Ĭ: 'I',
|
||
İ: 'I',
|
||
Ï: 'I',
|
||
Ḯ: 'I',
|
||
Ỉ: 'I',
|
||
Ǐ: 'I',
|
||
Ȉ: 'I',
|
||
Ȋ: 'I',
|
||
Ị: 'I',
|
||
Į: 'I',
|
||
Ḭ: 'I',
|
||
Ɨ: 'I',
|
||
'\u24BF': 'J',
|
||
J: 'J',
|
||
Ĵ: 'J',
|
||
Ɉ: 'J',
|
||
'\u24C0': 'K',
|
||
K: 'K',
|
||
Ḱ: 'K',
|
||
Ǩ: 'K',
|
||
Ḳ: 'K',
|
||
Ķ: 'K',
|
||
Ḵ: 'K',
|
||
Ƙ: 'K',
|
||
Ⱪ: 'K',
|
||
Ꝁ: 'K',
|
||
Ꝃ: 'K',
|
||
Ꝅ: 'K',
|
||
Ꞣ: 'K',
|
||
'\u24C1': 'L',
|
||
L: 'L',
|
||
Ŀ: 'L',
|
||
Ĺ: 'L',
|
||
Ľ: 'L',
|
||
Ḷ: 'L',
|
||
Ḹ: 'L',
|
||
Ļ: 'L',
|
||
Ḽ: 'L',
|
||
Ḻ: 'L',
|
||
Ł: 'L',
|
||
Ƚ: 'L',
|
||
Ɫ: 'L',
|
||
Ⱡ: 'L',
|
||
Ꝉ: 'L',
|
||
Ꝇ: 'L',
|
||
Ꞁ: 'L',
|
||
LJ: 'LJ',
|
||
Lj: 'Lj',
|
||
'\u24C2': 'M',
|
||
M: 'M',
|
||
Ḿ: 'M',
|
||
Ṁ: 'M',
|
||
Ṃ: 'M',
|
||
Ɱ: 'M',
|
||
Ɯ: 'M',
|
||
'\u24C3': 'N',
|
||
N: 'N',
|
||
Ǹ: 'N',
|
||
Ń: 'N',
|
||
Ñ: 'N',
|
||
Ṅ: 'N',
|
||
Ň: 'N',
|
||
Ṇ: 'N',
|
||
Ņ: 'N',
|
||
Ṋ: 'N',
|
||
Ṉ: 'N',
|
||
Ƞ: 'N',
|
||
Ɲ: 'N',
|
||
Ꞑ: 'N',
|
||
Ꞥ: 'N',
|
||
NJ: 'NJ',
|
||
Nj: 'Nj',
|
||
'\u24C4': 'O',
|
||
O: 'O',
|
||
Ò: 'O',
|
||
Ó: 'O',
|
||
Ô: 'O',
|
||
Ồ: 'O',
|
||
Ố: 'O',
|
||
Ỗ: 'O',
|
||
Ổ: 'O',
|
||
Õ: 'O',
|
||
Ṍ: 'O',
|
||
Ȭ: 'O',
|
||
Ṏ: 'O',
|
||
Ō: 'O',
|
||
Ṑ: 'O',
|
||
Ṓ: 'O',
|
||
Ŏ: 'O',
|
||
Ȯ: 'O',
|
||
Ȱ: 'O',
|
||
Ö: 'O',
|
||
Ȫ: 'O',
|
||
Ỏ: 'O',
|
||
Ő: 'O',
|
||
Ǒ: 'O',
|
||
Ȍ: 'O',
|
||
Ȏ: 'O',
|
||
Ơ: 'O',
|
||
Ờ: 'O',
|
||
Ớ: 'O',
|
||
Ỡ: 'O',
|
||
Ở: 'O',
|
||
Ợ: 'O',
|
||
Ọ: 'O',
|
||
Ộ: 'O',
|
||
Ǫ: 'O',
|
||
Ǭ: 'O',
|
||
Ø: 'O',
|
||
Ǿ: 'O',
|
||
Ɔ: 'O',
|
||
Ɵ: 'O',
|
||
Ꝋ: 'O',
|
||
Ꝍ: 'O',
|
||
Ƣ: 'OI',
|
||
Ꝏ: 'OO',
|
||
Ȣ: 'OU',
|
||
'\u24C5': 'P',
|
||
P: 'P',
|
||
Ṕ: 'P',
|
||
Ṗ: 'P',
|
||
Ƥ: 'P',
|
||
Ᵽ: 'P',
|
||
Ꝑ: 'P',
|
||
Ꝓ: 'P',
|
||
Ꝕ: 'P',
|
||
'\u24C6': 'Q',
|
||
Q: 'Q',
|
||
Ꝗ: 'Q',
|
||
Ꝙ: 'Q',
|
||
Ɋ: 'Q',
|
||
'\u24C7': 'R',
|
||
R: 'R',
|
||
Ŕ: 'R',
|
||
Ṙ: 'R',
|
||
Ř: 'R',
|
||
Ȑ: 'R',
|
||
Ȓ: 'R',
|
||
Ṛ: 'R',
|
||
Ṝ: 'R',
|
||
Ŗ: 'R',
|
||
Ṟ: 'R',
|
||
Ɍ: 'R',
|
||
Ɽ: 'R',
|
||
Ꝛ: 'R',
|
||
Ꞧ: 'R',
|
||
Ꞃ: 'R',
|
||
'\u24C8': 'S',
|
||
S: 'S',
|
||
ẞ: 'S',
|
||
Ś: 'S',
|
||
Ṥ: 'S',
|
||
Ŝ: 'S',
|
||
Ṡ: 'S',
|
||
Š: 'S',
|
||
Ṧ: 'S',
|
||
Ṣ: 'S',
|
||
Ṩ: 'S',
|
||
Ș: 'S',
|
||
Ş: 'S',
|
||
Ȿ: 'S',
|
||
Ꞩ: 'S',
|
||
Ꞅ: 'S',
|
||
'\u24C9': 'T',
|
||
T: 'T',
|
||
Ṫ: 'T',
|
||
Ť: 'T',
|
||
Ṭ: 'T',
|
||
Ț: 'T',
|
||
Ţ: 'T',
|
||
Ṱ: 'T',
|
||
Ṯ: 'T',
|
||
Ŧ: 'T',
|
||
Ƭ: 'T',
|
||
Ʈ: 'T',
|
||
Ⱦ: 'T',
|
||
Ꞇ: 'T',
|
||
Ꜩ: 'TZ',
|
||
'\u24CA': 'U',
|
||
U: 'U',
|
||
Ù: 'U',
|
||
Ú: 'U',
|
||
Û: 'U',
|
||
Ũ: 'U',
|
||
Ṹ: 'U',
|
||
Ū: 'U',
|
||
Ṻ: 'U',
|
||
Ŭ: 'U',
|
||
Ü: 'U',
|
||
Ǜ: 'U',
|
||
Ǘ: 'U',
|
||
Ǖ: 'U',
|
||
Ǚ: 'U',
|
||
Ủ: 'U',
|
||
Ů: 'U',
|
||
Ű: 'U',
|
||
Ǔ: 'U',
|
||
Ȕ: 'U',
|
||
Ȗ: 'U',
|
||
Ư: 'U',
|
||
Ừ: 'U',
|
||
Ứ: 'U',
|
||
Ữ: 'U',
|
||
Ử: 'U',
|
||
Ự: 'U',
|
||
Ụ: 'U',
|
||
Ṳ: 'U',
|
||
Ų: 'U',
|
||
Ṷ: 'U',
|
||
Ṵ: 'U',
|
||
Ʉ: 'U',
|
||
'\u24CB': 'V',
|
||
V: 'V',
|
||
Ṽ: 'V',
|
||
Ṿ: 'V',
|
||
Ʋ: 'V',
|
||
Ꝟ: 'V',
|
||
Ʌ: 'V',
|
||
Ꝡ: 'VY',
|
||
'\u24CC': 'W',
|
||
W: 'W',
|
||
Ẁ: 'W',
|
||
Ẃ: 'W',
|
||
Ŵ: 'W',
|
||
Ẇ: 'W',
|
||
Ẅ: 'W',
|
||
Ẉ: 'W',
|
||
Ⱳ: 'W',
|
||
'\u24CD': 'X',
|
||
X: 'X',
|
||
Ẋ: 'X',
|
||
Ẍ: 'X',
|
||
'\u24CE': 'Y',
|
||
Y: 'Y',
|
||
Ỳ: 'Y',
|
||
Ý: 'Y',
|
||
Ŷ: 'Y',
|
||
Ỹ: 'Y',
|
||
Ȳ: 'Y',
|
||
Ẏ: 'Y',
|
||
Ÿ: 'Y',
|
||
Ỷ: 'Y',
|
||
Ỵ: 'Y',
|
||
Ƴ: 'Y',
|
||
Ɏ: 'Y',
|
||
Ỿ: 'Y',
|
||
'\u24CF': 'Z',
|
||
Z: 'Z',
|
||
Ź: 'Z',
|
||
Ẑ: 'Z',
|
||
Ż: 'Z',
|
||
Ž: 'Z',
|
||
Ẓ: 'Z',
|
||
Ẕ: 'Z',
|
||
Ƶ: 'Z',
|
||
Ȥ: 'Z',
|
||
Ɀ: 'Z',
|
||
Ⱬ: 'Z',
|
||
Ꝣ: 'Z',
|
||
'\u24D0': 'a',
|
||
a: 'a',
|
||
ẚ: 'a',
|
||
à: 'a',
|
||
á: 'a',
|
||
â: 'a',
|
||
ầ: 'a',
|
||
ấ: 'a',
|
||
ẫ: 'a',
|
||
ẩ: 'a',
|
||
ã: 'a',
|
||
ā: 'a',
|
||
ă: 'a',
|
||
ằ: 'a',
|
||
ắ: 'a',
|
||
ẵ: 'a',
|
||
ẳ: 'a',
|
||
ȧ: 'a',
|
||
ǡ: 'a',
|
||
ä: 'a',
|
||
ǟ: 'a',
|
||
ả: 'a',
|
||
å: 'a',
|
||
ǻ: 'a',
|
||
ǎ: 'a',
|
||
ȁ: 'a',
|
||
ȃ: 'a',
|
||
ạ: 'a',
|
||
ậ: 'a',
|
||
ặ: 'a',
|
||
ḁ: 'a',
|
||
ą: 'a',
|
||
ⱥ: 'a',
|
||
ɐ: 'a',
|
||
ꜳ: 'aa',
|
||
æ: 'ae',
|
||
ǽ: 'ae',
|
||
ǣ: 'ae',
|
||
ꜵ: 'ao',
|
||
ꜷ: 'au',
|
||
ꜹ: 'av',
|
||
ꜻ: 'av',
|
||
ꜽ: 'ay',
|
||
'\u24D1': 'b',
|
||
b: 'b',
|
||
ḃ: 'b',
|
||
ḅ: 'b',
|
||
ḇ: 'b',
|
||
ƀ: 'b',
|
||
ƃ: 'b',
|
||
ɓ: 'b',
|
||
'\u24D2': 'c',
|
||
c: 'c',
|
||
ć: 'c',
|
||
ĉ: 'c',
|
||
ċ: 'c',
|
||
č: 'c',
|
||
ç: 'c',
|
||
ḉ: 'c',
|
||
ƈ: 'c',
|
||
ȼ: 'c',
|
||
ꜿ: 'c',
|
||
ↄ: 'c',
|
||
'\u24D3': 'd',
|
||
d: 'd',
|
||
ḋ: 'd',
|
||
ď: 'd',
|
||
ḍ: 'd',
|
||
ḑ: 'd',
|
||
ḓ: 'd',
|
||
ḏ: 'd',
|
||
đ: 'd',
|
||
ƌ: 'd',
|
||
ɖ: 'd',
|
||
ɗ: 'd',
|
||
ꝺ: 'd',
|
||
dz: 'dz',
|
||
dž: 'dz',
|
||
'\u24D4': 'e',
|
||
e: 'e',
|
||
è: 'e',
|
||
é: 'e',
|
||
ê: 'e',
|
||
ề: 'e',
|
||
ế: 'e',
|
||
ễ: 'e',
|
||
ể: 'e',
|
||
ẽ: 'e',
|
||
ē: 'e',
|
||
ḕ: 'e',
|
||
ḗ: 'e',
|
||
ĕ: 'e',
|
||
ė: 'e',
|
||
ë: 'e',
|
||
ẻ: 'e',
|
||
ě: 'e',
|
||
ȅ: 'e',
|
||
ȇ: 'e',
|
||
ẹ: 'e',
|
||
ệ: 'e',
|
||
ȩ: 'e',
|
||
ḝ: 'e',
|
||
ę: 'e',
|
||
ḙ: 'e',
|
||
ḛ: 'e',
|
||
ɇ: 'e',
|
||
ɛ: 'e',
|
||
ǝ: 'e',
|
||
'\u24D5': 'f',
|
||
f: 'f',
|
||
ḟ: 'f',
|
||
ƒ: 'f',
|
||
ꝼ: 'f',
|
||
'\u24D6': 'g',
|
||
g: 'g',
|
||
ǵ: 'g',
|
||
ĝ: 'g',
|
||
ḡ: 'g',
|
||
ğ: 'g',
|
||
ġ: 'g',
|
||
ǧ: 'g',
|
||
ģ: 'g',
|
||
ǥ: 'g',
|
||
ɠ: 'g',
|
||
ꞡ: 'g',
|
||
ᵹ: 'g',
|
||
ꝿ: 'g',
|
||
'\u24D7': 'h',
|
||
h: 'h',
|
||
ĥ: 'h',
|
||
ḣ: 'h',
|
||
ḧ: 'h',
|
||
ȟ: 'h',
|
||
ḥ: 'h',
|
||
ḩ: 'h',
|
||
ḫ: 'h',
|
||
ẖ: 'h',
|
||
ħ: 'h',
|
||
ⱨ: 'h',
|
||
ⱶ: 'h',
|
||
ɥ: 'h',
|
||
ƕ: 'hv',
|
||
'\u24D8': 'i',
|
||
i: 'i',
|
||
ì: 'i',
|
||
í: 'i',
|
||
î: 'i',
|
||
ĩ: 'i',
|
||
ī: 'i',
|
||
ĭ: 'i',
|
||
ï: 'i',
|
||
ḯ: 'i',
|
||
ỉ: 'i',
|
||
ǐ: 'i',
|
||
ȉ: 'i',
|
||
ȋ: 'i',
|
||
ị: 'i',
|
||
į: 'i',
|
||
ḭ: 'i',
|
||
ɨ: 'i',
|
||
ı: 'i',
|
||
'\u24D9': 'j',
|
||
j: 'j',
|
||
ĵ: 'j',
|
||
ǰ: 'j',
|
||
ɉ: 'j',
|
||
'\u24DA': 'k',
|
||
k: 'k',
|
||
ḱ: 'k',
|
||
ǩ: 'k',
|
||
ḳ: 'k',
|
||
ķ: 'k',
|
||
ḵ: 'k',
|
||
ƙ: 'k',
|
||
ⱪ: 'k',
|
||
ꝁ: 'k',
|
||
ꝃ: 'k',
|
||
ꝅ: 'k',
|
||
ꞣ: 'k',
|
||
'\u24DB': 'l',
|
||
l: 'l',
|
||
ŀ: 'l',
|
||
ĺ: 'l',
|
||
ľ: 'l',
|
||
ḷ: 'l',
|
||
ḹ: 'l',
|
||
ļ: 'l',
|
||
ḽ: 'l',
|
||
ḻ: 'l',
|
||
ſ: 'l',
|
||
ł: 'l',
|
||
ƚ: 'l',
|
||
ɫ: 'l',
|
||
ⱡ: 'l',
|
||
ꝉ: 'l',
|
||
ꞁ: 'l',
|
||
ꝇ: 'l',
|
||
lj: 'lj',
|
||
'\u24DC': 'm',
|
||
m: 'm',
|
||
ḿ: 'm',
|
||
ṁ: 'm',
|
||
ṃ: 'm',
|
||
ɱ: 'm',
|
||
ɯ: 'm',
|
||
'\u24DD': 'n',
|
||
n: 'n',
|
||
ǹ: 'n',
|
||
ń: 'n',
|
||
ñ: 'n',
|
||
ṅ: 'n',
|
||
ň: 'n',
|
||
ṇ: 'n',
|
||
ņ: 'n',
|
||
ṋ: 'n',
|
||
ṉ: 'n',
|
||
ƞ: 'n',
|
||
ɲ: 'n',
|
||
ʼn: 'n',
|
||
ꞑ: 'n',
|
||
ꞥ: 'n',
|
||
nj: 'nj',
|
||
'\u24DE': 'o',
|
||
o: 'o',
|
||
ò: 'o',
|
||
ó: 'o',
|
||
ô: 'o',
|
||
ồ: 'o',
|
||
ố: 'o',
|
||
ỗ: 'o',
|
||
ổ: 'o',
|
||
õ: 'o',
|
||
ṍ: 'o',
|
||
ȭ: 'o',
|
||
ṏ: 'o',
|
||
ō: 'o',
|
||
ṑ: 'o',
|
||
ṓ: 'o',
|
||
ŏ: 'o',
|
||
ȯ: 'o',
|
||
ȱ: 'o',
|
||
ö: 'o',
|
||
ȫ: 'o',
|
||
ỏ: 'o',
|
||
ő: 'o',
|
||
ǒ: 'o',
|
||
ȍ: 'o',
|
||
ȏ: 'o',
|
||
ơ: 'o',
|
||
ờ: 'o',
|
||
ớ: 'o',
|
||
ỡ: 'o',
|
||
ở: 'o',
|
||
ợ: 'o',
|
||
ọ: 'o',
|
||
ộ: 'o',
|
||
ǫ: 'o',
|
||
ǭ: 'o',
|
||
ø: 'o',
|
||
ǿ: 'o',
|
||
ɔ: 'o',
|
||
ꝋ: 'o',
|
||
ꝍ: 'o',
|
||
ɵ: 'o',
|
||
ƣ: 'oi',
|
||
ȣ: 'ou',
|
||
ꝏ: 'oo',
|
||
'\u24DF': 'p',
|
||
p: 'p',
|
||
ṕ: 'p',
|
||
ṗ: 'p',
|
||
ƥ: 'p',
|
||
ᵽ: 'p',
|
||
ꝑ: 'p',
|
||
ꝓ: 'p',
|
||
ꝕ: 'p',
|
||
'\u24E0': 'q',
|
||
q: 'q',
|
||
ɋ: 'q',
|
||
ꝗ: 'q',
|
||
ꝙ: 'q',
|
||
'\u24E1': 'r',
|
||
r: 'r',
|
||
ŕ: 'r',
|
||
ṙ: 'r',
|
||
ř: 'r',
|
||
ȑ: 'r',
|
||
ȓ: 'r',
|
||
ṛ: 'r',
|
||
ṝ: 'r',
|
||
ŗ: 'r',
|
||
ṟ: 'r',
|
||
ɍ: 'r',
|
||
ɽ: 'r',
|
||
ꝛ: 'r',
|
||
ꞧ: 'r',
|
||
ꞃ: 'r',
|
||
'\u24E2': 's',
|
||
s: 's',
|
||
ß: 's',
|
||
ś: 's',
|
||
ṥ: 's',
|
||
ŝ: 's',
|
||
ṡ: 's',
|
||
š: 's',
|
||
ṧ: 's',
|
||
ṣ: 's',
|
||
ṩ: 's',
|
||
ș: 's',
|
||
ş: 's',
|
||
ȿ: 's',
|
||
ꞩ: 's',
|
||
ꞅ: 's',
|
||
ẛ: 's',
|
||
'\u24E3': 't',
|
||
t: 't',
|
||
ṫ: 't',
|
||
ẗ: 't',
|
||
ť: 't',
|
||
ṭ: 't',
|
||
ț: 't',
|
||
ţ: 't',
|
||
ṱ: 't',
|
||
ṯ: 't',
|
||
ŧ: 't',
|
||
ƭ: 't',
|
||
ʈ: 't',
|
||
ⱦ: 't',
|
||
ꞇ: 't',
|
||
ꜩ: 'tz',
|
||
'\u24E4': 'u',
|
||
u: 'u',
|
||
ù: 'u',
|
||
ú: 'u',
|
||
û: 'u',
|
||
ũ: 'u',
|
||
ṹ: 'u',
|
||
ū: 'u',
|
||
ṻ: 'u',
|
||
ŭ: 'u',
|
||
ü: 'u',
|
||
ǜ: 'u',
|
||
ǘ: 'u',
|
||
ǖ: 'u',
|
||
ǚ: 'u',
|
||
ủ: 'u',
|
||
ů: 'u',
|
||
ű: 'u',
|
||
ǔ: 'u',
|
||
ȕ: 'u',
|
||
ȗ: 'u',
|
||
ư: 'u',
|
||
ừ: 'u',
|
||
ứ: 'u',
|
||
ữ: 'u',
|
||
ử: 'u',
|
||
ự: 'u',
|
||
ụ: 'u',
|
||
ṳ: 'u',
|
||
ų: 'u',
|
||
ṷ: 'u',
|
||
ṵ: 'u',
|
||
ʉ: 'u',
|
||
'\u24E5': 'v',
|
||
v: 'v',
|
||
ṽ: 'v',
|
||
ṿ: 'v',
|
||
ʋ: 'v',
|
||
ꝟ: 'v',
|
||
ʌ: 'v',
|
||
ꝡ: 'vy',
|
||
'\u24E6': 'w',
|
||
w: 'w',
|
||
ẁ: 'w',
|
||
ẃ: 'w',
|
||
ŵ: 'w',
|
||
ẇ: 'w',
|
||
ẅ: 'w',
|
||
ẘ: 'w',
|
||
ẉ: 'w',
|
||
ⱳ: 'w',
|
||
'\u24E7': 'x',
|
||
x: 'x',
|
||
ẋ: 'x',
|
||
ẍ: 'x',
|
||
'\u24E8': 'y',
|
||
y: 'y',
|
||
ỳ: 'y',
|
||
ý: 'y',
|
||
ŷ: 'y',
|
||
ỹ: 'y',
|
||
ȳ: 'y',
|
||
ẏ: 'y',
|
||
ÿ: 'y',
|
||
ỷ: 'y',
|
||
ẙ: 'y',
|
||
ỵ: 'y',
|
||
ƴ: 'y',
|
||
ɏ: 'y',
|
||
ỿ: 'y',
|
||
'\u24E9': 'z',
|
||
z: 'z',
|
||
ź: 'z',
|
||
ẑ: 'z',
|
||
ż: 'z',
|
||
ž: 'z',
|
||
ẓ: 'z',
|
||
ẕ: 'z',
|
||
ƶ: 'z',
|
||
ȥ: 'z',
|
||
ɀ: 'z',
|
||
ⱬ: 'z',
|
||
ꝣ: 'z',
|
||
Ά: '\u0391',
|
||
Έ: '\u0395',
|
||
Ή: '\u0397',
|
||
Ί: '\u0399',
|
||
Ϊ: '\u0399',
|
||
Ό: '\u039F',
|
||
Ύ: '\u03A5',
|
||
Ϋ: '\u03A5',
|
||
Ώ: '\u03A9',
|
||
ά: '\u03B1',
|
||
έ: '\u03B5',
|
||
ή: '\u03B7',
|
||
ί: '\u03B9',
|
||
ϊ: '\u03B9',
|
||
ΐ: '\u03B9',
|
||
ό: '\u03BF',
|
||
ύ: '\u03C5',
|
||
ϋ: '\u03C5',
|
||
ΰ: '\u03C5',
|
||
ω: '\u03C9',
|
||
ς: '\u03C3'
|
||
};
|
||
|
||
var Selectivity = _dereq_(38);
|
||
var previousTransform = Selectivity.transformText;
|
||
|
||
/**
|
||
* Extended version of the transformText() function that simplifies diacritics to their latin1
|
||
* counterparts.
|
||
*
|
||
* Note that if all query functions fetch their results from a remote server, you may not need this
|
||
* function, because it makes sense to remove diacritics server-side in such cases.
|
||
*/
|
||
Selectivity.transformText = function (string) {
|
||
var result = '';
|
||
for (var i = 0, length = string.length; i < length; i++) {
|
||
var character = string[i];
|
||
result += DIACRITICS[character] || character;
|
||
}
|
||
return previousTransform(result);
|
||
};
|
||
|
||
}, {"38": 38}], 31: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var $ = (window.jQuery || window.Zepto);
|
||
|
||
var Selectivity = _dereq_(38);
|
||
|
||
/**
|
||
* Option listener that implements a convenience query function for performing AJAX requests.
|
||
*/
|
||
Selectivity.OptionListeners.unshift(function (selectivity, options) {
|
||
var ajax = options.ajax;
|
||
if (ajax && ajax.url && !ajax.fetch && $.Deferred) {
|
||
ajax.fetch = function (url, init) {
|
||
return $.ajax(url, {
|
||
cache: init.cache !== 'no-cache',
|
||
headers: init.headers || null,
|
||
method: init.method || 'GET',
|
||
xhrFields: init.credentials === 'include' ? {withCredentials: true} : null
|
||
}).then(
|
||
function (data) {
|
||
return {
|
||
results: $.map(data, function (result) {
|
||
return result;
|
||
}),
|
||
more: false
|
||
};
|
||
},
|
||
function (jqXHR, textStatus, errorThrown) {
|
||
throw new Error(
|
||
'AJAX request returned: ' +
|
||
textStatus +
|
||
(errorThrown ? ', ' + errorThrown : '')
|
||
);
|
||
}
|
||
);
|
||
};
|
||
}
|
||
});
|
||
|
||
}, {"38": 38, "jquery": "jquery"}], 32: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var $ = (window.jQuery || window.Zepto);
|
||
|
||
var Selectivity = _dereq_(38);
|
||
|
||
function createSelectivityNextToSelectElement($el, options) {
|
||
var data = options.multiple ? [] : null;
|
||
|
||
var mapOptions = function () {
|
||
var $this = $(this);
|
||
if ($this.is('option')) {
|
||
var text = $this.text();
|
||
var id = $this.attr('value');
|
||
if (id === undefined) {
|
||
id = text;
|
||
}
|
||
if ($this.prop('selected')) {
|
||
var item = {id: id, text: text};
|
||
if (options.multiple) {
|
||
data.push(item);
|
||
} else {
|
||
data = item;
|
||
}
|
||
}
|
||
|
||
return {
|
||
id: id,
|
||
text: $this.attr('label') || text
|
||
};
|
||
} else {
|
||
return {
|
||
text: $this.attr('label'),
|
||
children: $this
|
||
.children('option,optgroup')
|
||
.map(mapOptions)
|
||
.get()
|
||
};
|
||
}
|
||
};
|
||
|
||
options.allowClear = 'allowClear' in options ? options.allowClear : !$el.prop('required');
|
||
|
||
var items = $el
|
||
.children('option,optgroup')
|
||
.map(mapOptions)
|
||
.get();
|
||
options.data = data;
|
||
|
||
options.items = options.query ? null : items;
|
||
|
||
options.placeholder = options.placeholder || $el.data('placeholder') || '';
|
||
|
||
options.tabIndex =
|
||
options.tabIndex === undefined ? $el.attr('tabindex') || 0 : options.tabIndex;
|
||
|
||
var classes = ($el.attr('class') || 'selectivity-input').split(' ');
|
||
if (classes.indexOf('selectivity-input') < 0) {
|
||
classes.push('selectivity-input');
|
||
}
|
||
|
||
var $div = $('<div>').attr({
|
||
id: 's9y_' + $el.attr('id'),
|
||
class: classes.join(' '),
|
||
style: $el.attr('style'),
|
||
'data-name': $el.attr('name')
|
||
});
|
||
$div.insertAfter($el);
|
||
$el.hide();
|
||
return $div[0];
|
||
}
|
||
|
||
function bindTraditionalSelectEvents(selectivity) {
|
||
var $el = $(selectivity.el);
|
||
$el.on('change', function (event) {
|
||
var value = event.originalEvent.value;
|
||
$el
|
||
.prev('select')
|
||
.val($.type(value) === 'array' ? value.slice(0) : value)
|
||
.trigger(event);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Option listener providing support for converting traditional <select> boxes into Selectivity
|
||
* instances.
|
||
*/
|
||
Selectivity.OptionListeners.push(function (selectivity, options) {
|
||
var $el = $(selectivity.el);
|
||
if ($el.is('select')) {
|
||
if ($el.attr('autofocus')) {
|
||
setTimeout(function () {
|
||
selectivity.focus();
|
||
}, 1);
|
||
}
|
||
|
||
selectivity.el = createSelectivityNextToSelectElement($el, options);
|
||
selectivity.el.selectivity = selectivity;
|
||
|
||
Selectivity.patchEvents($el);
|
||
|
||
bindTraditionalSelectEvents(selectivity);
|
||
}
|
||
});
|
||
|
||
}, {"38": 38, "jquery": "jquery"}], 33: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var Selectivity = _dereq_(38);
|
||
var findResultItem = _dereq_(40);
|
||
var getKeyCode = _dereq_(42);
|
||
|
||
var KEY_BACKSPACE = 8;
|
||
var KEY_DOWN_ARROW = 40;
|
||
var KEY_ENTER = 13;
|
||
var KEY_ESCAPE = 27;
|
||
var KEY_TAB = 9;
|
||
var KEY_UP_ARROW = 38;
|
||
|
||
/**
|
||
* Search input listener providing keyboard support for navigating the dropdown.
|
||
*/
|
||
function listener(selectivity, input) {
|
||
var keydownCanceled = false;
|
||
var closeSubmenu = null;
|
||
|
||
/**
|
||
* Moves a dropdown's highlight to the next or previous result item.
|
||
*
|
||
* @param delta Either 1 to move to the next item, or -1 to move to the previous item.
|
||
*/
|
||
function moveHighlight(dropdown, delta) {
|
||
var results = dropdown.results;
|
||
if (!results.length) {
|
||
return;
|
||
}
|
||
|
||
var resultItems = [].slice.call(dropdown.el.querySelectorAll('.selectivity-result-item'));
|
||
|
||
function scrollToHighlight() {
|
||
var el;
|
||
if (dropdown.highlightedResult) {
|
||
el = findResultItem(resultItems, dropdown.highlightedResult.id);
|
||
} else if (dropdown.loadMoreHighlighted) {
|
||
el = dropdown.$('.selectivity-load-more');
|
||
}
|
||
|
||
if (el && el.scrollIntoView) {
|
||
el.scrollIntoView(delta < 0);
|
||
}
|
||
}
|
||
|
||
if (dropdown.submenu) {
|
||
moveHighlight(dropdown.submenu, delta);
|
||
return;
|
||
}
|
||
|
||
var defaultIndex = delta > 0 ? 0 : resultItems.length - 1;
|
||
var index = defaultIndex;
|
||
var highlightedResult = dropdown.highlightedResult;
|
||
if (highlightedResult) {
|
||
var highlightedResultItem = findResultItem(resultItems, highlightedResult.id);
|
||
index = resultItems.indexOf(highlightedResultItem) + delta;
|
||
if (delta > 0 ? index >= resultItems.length : index < 0) {
|
||
if (dropdown.hasMore) {
|
||
dropdown.highlightLoadMore();
|
||
scrollToHighlight();
|
||
return;
|
||
} else {
|
||
index = defaultIndex;
|
||
}
|
||
}
|
||
}
|
||
|
||
var resultItem = resultItems[index];
|
||
var result = Selectivity.findNestedById(results, selectivity.getRelatedItemId(resultItem));
|
||
if (result) {
|
||
dropdown.highlight(result, {delay: !!result.submenu});
|
||
scrollToHighlight();
|
||
}
|
||
}
|
||
|
||
function keyHeld(event) {
|
||
var dropdown = selectivity.dropdown;
|
||
if (dropdown) {
|
||
var keyCode = getKeyCode(event);
|
||
if (keyCode === KEY_BACKSPACE) {
|
||
if (!input.value) {
|
||
if (dropdown.submenu) {
|
||
var submenu = dropdown.submenu;
|
||
while (submenu.submenu) {
|
||
submenu = submenu.submenu;
|
||
}
|
||
closeSubmenu = submenu;
|
||
}
|
||
|
||
event.preventDefault();
|
||
keydownCanceled = true;
|
||
}
|
||
} else if (keyCode === KEY_DOWN_ARROW) {
|
||
moveHighlight(dropdown, 1);
|
||
} else if (keyCode === KEY_UP_ARROW) {
|
||
moveHighlight(dropdown, -1);
|
||
} else if (keyCode === KEY_TAB) {
|
||
setTimeout(function () {
|
||
selectivity.close();
|
||
}, 1);
|
||
} else if (keyCode === KEY_ENTER) {
|
||
event.preventDefault(); // don't submit forms on keydown
|
||
}
|
||
}
|
||
}
|
||
|
||
function keyReleased(event) {
|
||
function open() {
|
||
if (selectivity.options.showDropdown !== false) {
|
||
selectivity.open();
|
||
}
|
||
}
|
||
|
||
var dropdown = selectivity.dropdown;
|
||
var keyCode = getKeyCode(event);
|
||
if (keydownCanceled) {
|
||
event.preventDefault();
|
||
keydownCanceled = false;
|
||
|
||
if (closeSubmenu) {
|
||
closeSubmenu.close();
|
||
selectivity.focus();
|
||
closeSubmenu = null;
|
||
}
|
||
} else if (keyCode === KEY_BACKSPACE) {
|
||
if (!dropdown && selectivity.options.allowClear) {
|
||
selectivity.clear();
|
||
}
|
||
} else if (keyCode === KEY_ENTER && !event.ctrlKey) {
|
||
if (dropdown) {
|
||
dropdown.selectHighlight();
|
||
} else if (selectivity.options.showDropdown !== false) {
|
||
open();
|
||
}
|
||
|
||
event.preventDefault();
|
||
} else if (keyCode === KEY_ESCAPE) {
|
||
selectivity.close();
|
||
|
||
event.preventDefault();
|
||
} else if (keyCode === KEY_DOWN_ARROW || keyCode === KEY_UP_ARROW) {
|
||
// handled in keyHeld() because the response feels faster and it works with repeated
|
||
// events if the user holds the key for a longer period
|
||
// still, we issue an open() call here in case the dropdown was not yet open...
|
||
open();
|
||
|
||
event.preventDefault();
|
||
} else {
|
||
open();
|
||
}
|
||
}
|
||
|
||
input.addEventListener('keydown', keyHeld);
|
||
input.addEventListener('keyup', keyReleased);
|
||
}
|
||
|
||
Selectivity.InputListeners.push(listener);
|
||
|
||
}, {"38": 38, "40": 40, "42": 42}], 34: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var Selectivity = _dereq_(38);
|
||
|
||
var allowedOptions = {
|
||
allowClear: 'boolean',
|
||
backspaceHighlightsBeforeDelete: 'boolean',
|
||
closeOnSelect: 'boolean',
|
||
createTokenItem: 'function',
|
||
dropdown: 'function|null',
|
||
initSelection: 'function|null',
|
||
inputListeners: 'array',
|
||
items: 'array|null',
|
||
matcher: 'function|null',
|
||
placeholder: 'string',
|
||
positionDropdown: 'function|null',
|
||
query: 'function|null',
|
||
readOnly: 'boolean',
|
||
removeOnly: 'boolean',
|
||
shouldOpenSubmenu: 'function',
|
||
showSearchInputInDropdown: 'boolean',
|
||
suppressWheelSelector: 'string|null',
|
||
tabIndex: 'number',
|
||
templates: 'object',
|
||
tokenizer: 'function'
|
||
};
|
||
|
||
/**
|
||
* Option listener that validates the options being set. This is useful during debugging to quickly
|
||
* get notified if you're passing invalid options.
|
||
*/
|
||
Selectivity.OptionListeners.unshift(function (selectivity, options) {
|
||
for (var key in options) {
|
||
if (!options.hasOwnProperty(key)) {
|
||
continue;
|
||
}
|
||
|
||
var value = options[key];
|
||
var type = allowedOptions[key];
|
||
if (
|
||
type &&
|
||
!type.split('|').some(function (type) {
|
||
if (type === 'null') {
|
||
return value === null;
|
||
} else if (type === 'array') {
|
||
return Array.isArray(value);
|
||
} else {
|
||
return value !== null && value !== undefined && typeof value === type;
|
||
}
|
||
})
|
||
) {
|
||
throw new Error(key + ' must be of type ' + type);
|
||
}
|
||
}
|
||
});
|
||
|
||
}, {"38": 38}], 35: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var Dropdown = _dereq_(22);
|
||
var Selectivity = _dereq_(38);
|
||
|
||
var findResultItem = _dereq_(40);
|
||
|
||
/**
|
||
* Extended dropdown that supports submenus.
|
||
*/
|
||
function SubmenuPlugin(selectivity, options) {
|
||
/**
|
||
* Optional parent dropdown menu from which this dropdown was opened.
|
||
*/
|
||
this.parentMenu = options.parentMenu;
|
||
|
||
Dropdown.call(this, selectivity, options);
|
||
|
||
this._closeSubmenuTimeout = 0;
|
||
|
||
this._openSubmenuTimeout = 0;
|
||
}
|
||
|
||
var callSuper = Selectivity.inherits(SubmenuPlugin, Dropdown, {
|
||
/**
|
||
* @inherit
|
||
*/
|
||
close: function () {
|
||
if (this.submenu) {
|
||
this.submenu.close();
|
||
}
|
||
|
||
callSuper(this, 'close');
|
||
|
||
if (this.parentMenu) {
|
||
this.parentMenu.submenu = null;
|
||
this.parentMenu = null;
|
||
}
|
||
|
||
clearTimeout(this._closeSubmenuTimeout);
|
||
clearTimeout(this._openSubmenuTimeout);
|
||
},
|
||
|
||
/**
|
||
* @inherit
|
||
*
|
||
* @param options Optional options object. May contain the following properties:
|
||
* delay - If true, indicates any submenu should not be opened until after some
|
||
* delay.
|
||
* openSubmenu - If false, no submenu will be automatically opened for the
|
||
* highlighted item.
|
||
* reason - The reason why the result item is being highlighted. See
|
||
* Dropdown#highlight().
|
||
*/
|
||
highlight: function (item, options) {
|
||
options = options || {};
|
||
var reason = options.reason || 'unspecified';
|
||
|
||
if (options.delay) {
|
||
callSuper(this, 'highlight', item);
|
||
|
||
clearTimeout(this._openSubmenuTimeout);
|
||
this._openSubmenuTimeout = setTimeout(this._doHighlight.bind(this, item, reason), 300);
|
||
} else if (this.submenu) {
|
||
if (this.highlightedResult && this.highlightedResult.id === item.id) {
|
||
this._doHighlight(item, reason);
|
||
} else {
|
||
clearTimeout(this._closeSubmenuTimeout);
|
||
this._closeSubmenuTimeout = setTimeout(
|
||
this._closeSubmenuAndHighlight.bind(this, item, reason),
|
||
100
|
||
);
|
||
}
|
||
} else {
|
||
if (this.parentMenu && this.parentMenu._closeSubmenuTimeout) {
|
||
clearTimeout(this.parentMenu._closeSubmenuTimeout);
|
||
this.parentMenu._closeSubmenuTimeout = 0;
|
||
}
|
||
|
||
if (options.openSubmenu === false) {
|
||
callSuper(this, 'highlight', item);
|
||
} else {
|
||
this._doHighlight(item, reason);
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @inherit
|
||
*/
|
||
search: function (term) {
|
||
if (this.submenu) {
|
||
var searchInput = this.$('.selectivity-search-input');
|
||
if (searchInput && searchInput === document.activeElement) {
|
||
this.submenu.close();
|
||
} else {
|
||
this.submenu.search(term);
|
||
return;
|
||
}
|
||
}
|
||
|
||
callSuper(this, 'search', term);
|
||
},
|
||
|
||
/**
|
||
* @inherit
|
||
*/
|
||
selectHighlight: function () {
|
||
if (this.submenu) {
|
||
this.submenu.selectHighlight();
|
||
} else {
|
||
callSuper(this, 'selectHighlight');
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @inherit
|
||
*/
|
||
showResults: function (results, options) {
|
||
// makes sure any result item with a submenu that's not explicitly
|
||
// set as selectable becomes unselectable
|
||
function setSelectable(item) {
|
||
if (item.children) {
|
||
item.children.forEach(setSelectable);
|
||
}
|
||
if (item.submenu) {
|
||
item.selectable = !!item.selectable;
|
||
}
|
||
}
|
||
|
||
if (this.submenu && options.dropdown !== this) {
|
||
this.submenu.showResults(results, options);
|
||
} else {
|
||
results.forEach(setSelectable);
|
||
callSuper(this, 'showResults', results, options);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @inherit
|
||
*/
|
||
triggerClose: function () {
|
||
if (this.parentMenu) {
|
||
this.selectivity.triggerEvent('selectivity-close-submenu');
|
||
} else {
|
||
callSuper(this, 'triggerClose');
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @inherit
|
||
*/
|
||
triggerOpen: function () {
|
||
if (this.parentMenu) {
|
||
this.selectivity.triggerEvent('selectivity-open-submenu');
|
||
} else {
|
||
callSuper(this, 'triggerOpen');
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_closeSubmenuAndHighlight: function (item, reason) {
|
||
if (this.submenu) {
|
||
this.submenu.close();
|
||
}
|
||
|
||
this._doHighlight(item, reason);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_doHighlight: function (item, reason) {
|
||
callSuper(this, 'highlight', item);
|
||
|
||
var options = this.selectivity.options;
|
||
if (
|
||
!item.submenu ||
|
||
this.submenu ||
|
||
(options.shouldOpenSubmenu && options.shouldOpenSubmenu(item, reason) === false)
|
||
) {
|
||
return;
|
||
}
|
||
|
||
var Dropdown = options.dropdown || Selectivity.Dropdown;
|
||
if (Dropdown) {
|
||
var resultItems = this.el.querySelectorAll('.selectivity-result-item');
|
||
var resultItem = findResultItem(resultItems, item.id);
|
||
var dropdownEl = this.el;
|
||
|
||
this.submenu = new Dropdown(this.selectivity, {
|
||
highlightFirstItem: !item.selectable,
|
||
items: item.submenu.items || null,
|
||
parentMenu: this,
|
||
position: function (el, selectEl) {
|
||
if (item.submenu.positionDropdown) {
|
||
item.submenu.positionDropdown(el, selectEl, resultItem, dropdownEl);
|
||
} else {
|
||
var rect = dropdownEl.getBoundingClientRect();
|
||
var left = rect.right;
|
||
var width = rect.width;
|
||
if (left + width > document.body.clientWidth && rect.left - width > 0) {
|
||
// Open the submenu on the left-hand side if there's no sufficient
|
||
// space on the right side.
|
||
// Use a little margin to prevent awkward-looking overlaps.
|
||
left = rect.left - width + 10;
|
||
}
|
||
|
||
// Move the submenu up so it fits in the window, if necessary and possible.
|
||
var submenuTop = resultItem.getBoundingClientRect().top;
|
||
var deltaUp = Math.min(
|
||
Math.max(submenuTop + el.clientHeight - window.innerHeight, 0),
|
||
rect.top + rect.height
|
||
);
|
||
|
||
el.style.left = left + 'px';
|
||
el.style.top = submenuTop - deltaUp + 'px';
|
||
el.style.width = width + 'px';
|
||
}
|
||
},
|
||
query: item.submenu.query || null,
|
||
showSearchInput: item.submenu.showSearchInput
|
||
});
|
||
|
||
this.submenu.search('');
|
||
}
|
||
}
|
||
});
|
||
|
||
Selectivity.Dropdown = SubmenuPlugin;
|
||
|
||
module.exports = SubmenuPlugin;
|
||
|
||
}, {"22": 22, "38": 38, "40": 40}], 36: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var assign = (window.jQuery || window.Zepto).extend;
|
||
|
||
var Selectivity = _dereq_(38);
|
||
|
||
function defaultTokenizer(input, selection, createToken, options) {
|
||
var createTokenItem =
|
||
options.createTokenItem ||
|
||
function (token) {
|
||
return token ? {id: token, text: token} : null;
|
||
};
|
||
|
||
var separators = options.tokenSeparators;
|
||
|
||
function hasToken(input) {
|
||
return input
|
||
? separators.some(function (separator) {
|
||
return input.indexOf(separator) > -1;
|
||
})
|
||
: false;
|
||
}
|
||
|
||
function takeToken(input) {
|
||
for (var i = 0, length = input.length; i < length; i++) {
|
||
if (separators.indexOf(input[i]) > -1) {
|
||
return {term: input.slice(0, i), input: input.slice(i + 1)};
|
||
}
|
||
}
|
||
return {};
|
||
}
|
||
|
||
while (hasToken(input)) {
|
||
var token = takeToken(input);
|
||
if (token.term) {
|
||
var item = createTokenItem(token.term);
|
||
if (item && !Selectivity.findById(selection, item.id)) {
|
||
createToken(item);
|
||
}
|
||
}
|
||
input = token.input;
|
||
}
|
||
|
||
return input;
|
||
}
|
||
|
||
/**
|
||
* Option listener that provides a default tokenizer which is used when the tokenSeparators option
|
||
* is specified.
|
||
*
|
||
* @param options Options object. In addition to the options supported in the multi-input
|
||
* implementation, this may contain the following property:
|
||
* tokenSeparators - Array of string separators which are used to separate the search
|
||
* string into tokens. If specified and the tokenizer property is
|
||
* not set, the tokenizer property will be set to a function which
|
||
* splits the search term into tokens separated by any of the given
|
||
* separators. The tokens will be converted into selectable items
|
||
* using the 'createTokenItem' function. The default tokenizer also
|
||
* filters out already selected items.
|
||
*/
|
||
Selectivity.OptionListeners.push(function (selectivity, options) {
|
||
if (options.tokenSeparators) {
|
||
options.allowedTypes = assign({tokenSeparators: 'array'}, options.allowedTypes);
|
||
|
||
options.tokenizer = options.tokenizer || defaultTokenizer;
|
||
}
|
||
});
|
||
|
||
}, {"38": 38, "lodash/assign": "lodash/assign"}], 37: [function (_dereq_, module, exports) {
|
||
_dereq_(22);
|
||
_dereq_(24);
|
||
_dereq_(25);
|
||
_dereq_(26);
|
||
_dereq_(27);
|
||
_dereq_(28);
|
||
_dereq_(29);
|
||
_dereq_(30);
|
||
_dereq_(31);
|
||
_dereq_(32);
|
||
_dereq_(33);
|
||
_dereq_(34);
|
||
_dereq_(35);
|
||
_dereq_(36);
|
||
_dereq_(39);
|
||
_dereq_(21);
|
||
}, {
|
||
"21": 21,
|
||
"22": 22,
|
||
"24": 24,
|
||
"25": 25,
|
||
"26": 26,
|
||
"27": 27,
|
||
"28": 28,
|
||
"29": 29,
|
||
"30": 30,
|
||
"31": 31,
|
||
"32": 32,
|
||
"33": 33,
|
||
"34": 34,
|
||
"35": 35,
|
||
"36": 36,
|
||
"39": 39
|
||
}], 38: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var assign = (window.jQuery || window.Zepto).extend;
|
||
var isString = _dereq_(16);
|
||
|
||
var EventListener = _dereq_(23);
|
||
var toggleClass = _dereq_(47);
|
||
|
||
/**
|
||
* Selectivity Base Constructor.
|
||
*
|
||
* You will never use this constructor directly. Instead, you use $(selector).selectivity(options)
|
||
* to create an instance of either MultipleSelectivity or SingleSelectivity. This class defines all
|
||
* functionality that is common between both.
|
||
*
|
||
* @param options Options object. Accepts the same options as the setOptions method(), in addition
|
||
* to the following ones:
|
||
* data - Initial selection data to set. This should be an array of objects with 'id'
|
||
* and 'text' properties. This option is mutually exclusive with 'value'.
|
||
* element - The DOM element to which to attach the Selectivity instance. This
|
||
* property is set by the API wrapper.
|
||
* value - Initial value to set. This should be an array of IDs. This property is
|
||
* mutually exclusive with 'data'.
|
||
*/
|
||
function Selectivity(options) {
|
||
/**
|
||
* Reference to the currently open dropdown.
|
||
*/
|
||
this.dropdown = null;
|
||
|
||
/**
|
||
* DOM element to which this instance is attached.
|
||
*/
|
||
this.el = options.element;
|
||
|
||
/**
|
||
* Whether the input is enabled.
|
||
*
|
||
* This is false when the option readOnly is false or the option removeOnly is false.
|
||
*/
|
||
this.enabled = !options.readOnly && !options.removeOnly;
|
||
|
||
/**
|
||
* DOM element for the input.
|
||
*
|
||
* May be null as long as there is no visible input. It is set by initInput().
|
||
*/
|
||
this.input = null;
|
||
|
||
/**
|
||
* Array of items from which to select. If set, this will be an array of objects with 'id' and
|
||
* 'text' properties.
|
||
*
|
||
* If given, all items are expected to be available locally and all selection operations operate
|
||
* on this local array only. If null, items are not available locally, and a query function
|
||
* should be provided to fetch remote data.
|
||
*/
|
||
this.items = null;
|
||
|
||
/**
|
||
* Options passed to the Selectivity instance or set through setOptions().
|
||
*/
|
||
this.options = {};
|
||
|
||
/**
|
||
* Mapping of templates.
|
||
*
|
||
* Custom templates can be specified in the options object.
|
||
*/
|
||
this.templates = assign({}, Selectivity.Templates);
|
||
|
||
/**
|
||
* The last used search term.
|
||
*/
|
||
this.term = '';
|
||
|
||
this.setOptions(options);
|
||
|
||
if (options.value) {
|
||
this.setValue(options.value, {triggerChange: false});
|
||
} else {
|
||
this.setData(options.data || null, {triggerChange: false});
|
||
}
|
||
|
||
this.el.setAttribute('tabindex', options.tabIndex || 0);
|
||
|
||
this.events = new EventListener(this.el, this);
|
||
this.events.on({
|
||
blur: this._blur,
|
||
mouseenter: this._mouseenter,
|
||
mouseleave: this._mouseleave,
|
||
'selectivity-close': this._closed
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Methods.
|
||
*/
|
||
assign(Selectivity.prototype, {
|
||
/**
|
||
* Convenience shortcut for this.el.querySelector(selector).
|
||
*/
|
||
$: function (selector) {
|
||
return this.el.querySelector(selector);
|
||
},
|
||
|
||
/**
|
||
* Closes the dropdown.
|
||
*/
|
||
close: function () {
|
||
this._clearCloseTimeout();
|
||
|
||
if (this.dropdown) {
|
||
this.dropdown.close();
|
||
this.dropdown = null;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Destroys the Selectivity instance.
|
||
*/
|
||
destroy: function () {
|
||
this.events.destruct();
|
||
|
||
var el = this.el;
|
||
while (el.firstChild) {
|
||
el.removeChild(el.firstChild);
|
||
}
|
||
el.selectivity = null;
|
||
},
|
||
|
||
/**
|
||
* Filters the results to be displayed in the dropdown.
|
||
*
|
||
* The default implementation simply returns the results unfiltered, but the MultipleSelectivity
|
||
* class overrides this method to filter out any items that have already been selected.
|
||
*
|
||
* @param results Array of items with 'id' and 'text' properties.
|
||
*
|
||
* @return The filtered array.
|
||
*/
|
||
filterResults: function (results) {
|
||
return results;
|
||
},
|
||
|
||
/**
|
||
* Applies focus to the input.
|
||
*/
|
||
focus: function () {
|
||
this._clearCloseTimeout();
|
||
|
||
this._focusing = true;
|
||
|
||
if (this.input) {
|
||
this.input.focus();
|
||
}
|
||
|
||
this._focusing = false;
|
||
},
|
||
|
||
/**
|
||
* Returns the selection data.
|
||
*/
|
||
getData: function () {
|
||
return this._data;
|
||
},
|
||
|
||
/**
|
||
* Returns the correct item for a given ID.
|
||
*
|
||
* @param id The ID to get the item for.
|
||
*
|
||
* @return The corresponding item. Will be an object with 'id' and 'text' properties or null if
|
||
* the item cannot be found. Note that if no items are defined, this method assumes the
|
||
* text labels will be equal to the IDs.
|
||
*/
|
||
getItemForId: function (id) {
|
||
var items = this.items;
|
||
if (items) {
|
||
return Selectivity.findNestedById(items, id);
|
||
} else if (id === null) {
|
||
return null;
|
||
} else {
|
||
return {id: id, text: '' + id};
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Returns the item ID related to an element or event target.
|
||
*
|
||
* @param elementOrEvent The DOM element or event to get the item ID for.
|
||
*
|
||
* @return Item ID or null if no ID could be found.
|
||
*/
|
||
getRelatedItemId: function (elementOrEvent) {
|
||
var el = elementOrEvent.target || elementOrEvent;
|
||
while (el) {
|
||
if (el.hasAttribute('data-item-id')) {
|
||
break;
|
||
}
|
||
el = el.parentNode;
|
||
}
|
||
|
||
if (!el) {
|
||
return null;
|
||
}
|
||
|
||
var id = el.getAttribute('data-item-id');
|
||
|
||
// IDs can be either numbers or strings, but attribute values are always strings, so we
|
||
// will have to find out whether the item ID ought to be a number or string ourselves.
|
||
if (Selectivity.findById(this._data || [], id)) {
|
||
return id;
|
||
} else {
|
||
var dropdown = this.dropdown;
|
||
while (dropdown) {
|
||
if (Selectivity.findNestedById(dropdown.results, id)) {
|
||
return id;
|
||
}
|
||
// FIXME: reference to submenu plugin doesn't belong in base
|
||
dropdown = dropdown.submenu;
|
||
}
|
||
var number = parseInt(id, 10);
|
||
return '' + number === id ? number : id;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Returns the value of the selection.
|
||
*/
|
||
getValue: function () {
|
||
return this._value;
|
||
},
|
||
|
||
/**
|
||
* Initializes the input element.
|
||
*
|
||
* Sets the input property, invokes all input listeners and (by default) attaches the action of
|
||
* searching when something is typed.
|
||
*
|
||
* @param input Input element.
|
||
* @param options Optional options object. May contain the following property:
|
||
* search - If false, no event handlers are setup to initiate searching when the
|
||
* user types in the input field. This is useful if you want to use the
|
||
* input only to handle keyboard support.
|
||
*/
|
||
initInput: function (input, options) {
|
||
this.input = input;
|
||
|
||
var selectivity = this;
|
||
var inputListeners = this.options.inputListeners || Selectivity.InputListeners;
|
||
inputListeners.forEach(function (listener) {
|
||
listener(selectivity, input, options);
|
||
});
|
||
|
||
if (!options || options.search !== false) {
|
||
input.addEventListener('keyup', function (event) {
|
||
if (!event.defaultPrevented) {
|
||
selectivity.search(event.target.value);
|
||
}
|
||
});
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Opens the dropdown.
|
||
*/
|
||
open: function () {
|
||
if (this._opening || this.dropdown || !this.triggerEvent('selectivity-opening')) {
|
||
return;
|
||
}
|
||
|
||
this._opening = true;
|
||
|
||
var Dropdown = this.options.dropdown || Selectivity.Dropdown;
|
||
if (Dropdown) {
|
||
this.dropdown = new Dropdown(this, {
|
||
items: this.items,
|
||
position: this.options.positionDropdown,
|
||
query: this.options.query,
|
||
showSearchInput: this.options.showSearchInputInDropdown !== false
|
||
});
|
||
}
|
||
|
||
this.search('');
|
||
|
||
this.focus();
|
||
|
||
toggleClass(this.el, 'open', true);
|
||
|
||
this._opening = false;
|
||
},
|
||
|
||
/**
|
||
* (Re-)positions the dropdown.
|
||
*/
|
||
positionDropdown: function () {
|
||
if (this.dropdown) {
|
||
this.dropdown.position();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Searches for results based on the term given.
|
||
*
|
||
* If an items array has been passed with the options to the Selectivity instance, a local
|
||
* search will be performed among those items. Otherwise, the query function specified in the
|
||
* options will be used to perform the search. If neither is defined, nothing happens.
|
||
*
|
||
* @param term Term to search for.
|
||
*/
|
||
search: function (term) {
|
||
this.open();
|
||
|
||
if (this.dropdown) {
|
||
this.dropdown.search(term);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Sets the selection data.
|
||
*
|
||
* The selection data contains both IDs and text labels. If you only want to set or get the IDs,
|
||
* you should use the value() method.
|
||
*
|
||
* @param newData New data to set. For a MultipleSelectivity instance the data must be an array
|
||
* of objects with 'id' and 'text' properties, for a SingleSelectivity instance
|
||
* the data must be a single such object or null to indicate no item is selected.
|
||
* @param options Optional options object. May contain the following property:
|
||
* triggerChange - Set to false to suppress the "change" event being triggered.
|
||
* Note this will also cause the UI to not update automatically;
|
||
* so you may want to call rerenderSelection() manually when
|
||
* using this option.
|
||
*/
|
||
setData: function (newData, options) {
|
||
options = options || {};
|
||
|
||
newData = this.validateData(newData);
|
||
|
||
this._data = newData;
|
||
this._value = this.getValueForData(newData);
|
||
|
||
if (options.triggerChange !== false) {
|
||
this.triggerChange();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Sets one or more options on this Selectivity instance.
|
||
*
|
||
* @param options Options object. May contain one or more of the following properties:
|
||
* closeOnSelect - Set to false to keep the dropdown open after the user has
|
||
* selected an item. This is useful if you want to allow the user
|
||
* to quickly select multiple items. The default value is true.
|
||
* dropdown - Custom dropdown implementation to use for this instance.
|
||
* initSelection - Function to map values by ID to selection data. This function
|
||
* receives two arguments, 'value' and 'callback'. The value is
|
||
* the current value of the selection, which is an ID or an array
|
||
* of IDs depending on the input type. The callback should be
|
||
* invoked with an object or array of objects, respectively,
|
||
* containing 'id' and 'text' properties.
|
||
* inputListeners - Array of search input listeners. By default, the global
|
||
* array Selectivity.InputListeners is used.
|
||
* items - Array of items from which to select. Should be an array of objects
|
||
* with 'id' and 'text' properties. As convenience, you may also pass an
|
||
* array of strings, in which case the same string is used for both the
|
||
* 'id' and 'text' properties. If items are given, all items are expected
|
||
* to be available locally and all selection operations operate on this
|
||
* local array only. If null, items are not available locally, and a
|
||
* query function should be provided to fetch remote data.
|
||
* matcher - Function to determine whether text matches a given search term. Note
|
||
* this function is only used if you have specified an array of items.
|
||
* Receives two arguments:
|
||
* item - The item that should match the search term.
|
||
* term - The search term. Note that for performance reasons, the term
|
||
* has always been already processed using
|
||
* Selectivity.transformText().
|
||
* The method should return the item if it matches, and null otherwise.
|
||
* If the item has a children array, the matcher is expected to filter
|
||
* those itself (be sure to only return the filtered array of children
|
||
* in the returned item and not to modify the children of the item
|
||
* argument).
|
||
* placeholder - Placeholder text to display when the element has no focus and
|
||
* no selected items.
|
||
* positionDropdown - Function to position the dropdown. Receives two arguments:
|
||
* dropdownEl - The element to be positioned.
|
||
* selectEl - The element of the Selectivity instance, that
|
||
* you can position the dropdown to.
|
||
* The default implementation positions the dropdown element
|
||
* under the Selectivity's element and gives it the same
|
||
* width.
|
||
* query - Function to use for querying items. Receives a single object as
|
||
* argument with the following properties:
|
||
* callback - Callback to invoke when the results are available. This
|
||
* callback should be passed a single object as argument with
|
||
* the following properties:
|
||
* more - Boolean that can be set to true to indicate there
|
||
* are more results available. Additional results may
|
||
* be fetched by the user through pagination.
|
||
* results - Array of result items. The format for the result
|
||
* items is the same as for passing local items.
|
||
* offset - This property is only used for pagination and indicates how
|
||
* many results should be skipped when returning more results.
|
||
* selectivity - The Selectivity instance the query function is used on.
|
||
* term - The search term the user is searching for. Unlike with the
|
||
* matcher function, the term has not been processed using
|
||
* Selectivity.transformText().
|
||
* readOnly - If true, disables any modification of the input.
|
||
* removeOnly - If true, disables any modification of the input except removing
|
||
* of selected items.
|
||
* shouldOpenSubmenu - Function to call that will decide whether a submenu should
|
||
* be opened. Receives two parameters:
|
||
* item - The currently highlighted result item.
|
||
* reason - The reason why the item is being highlighted.
|
||
* See Dropdown#highlight() for possible values.
|
||
* showDropdown - Set to false if you don't want to use any dropdown (you can
|
||
* still open it programmatically using open()).
|
||
* showSearchInputInDropdown - Set to false to remove the search input used in
|
||
* dropdowns. The default is true for single-value
|
||
* inputs.
|
||
* templates - Object with instance-specific templates to override the global
|
||
* templates assigned to Selectivity.Templates.
|
||
*/
|
||
setOptions: function (options) {
|
||
options = options || {};
|
||
|
||
var selectivity = this;
|
||
Selectivity.OptionListeners.forEach(function (listener) {
|
||
listener(selectivity, options);
|
||
});
|
||
|
||
if ('items' in options) {
|
||
this.items = options.items ? Selectivity.processItems(options.items) : null;
|
||
}
|
||
if ('templates' in options) {
|
||
assign(this.templates, options.templates);
|
||
}
|
||
|
||
assign(this.options, options);
|
||
|
||
this.enabled = !this.options.readOnly && !this.options.removeOnly;
|
||
},
|
||
|
||
/**
|
||
* Sets the value of the selection.
|
||
*
|
||
* The value of the selection only concerns the IDs of the selection items. If you are
|
||
* interested in the IDs and the text labels, you should use the data() method.
|
||
*
|
||
* Note that if neither the items option nor the initSelection option have been set, Selectivity
|
||
* will have no way to determine what text labels should be used with the given IDs in which
|
||
* case it will assume the text is equal to the ID. This is useful if you're working with tags,
|
||
* or selecting e-mail addresses for instance, but may not always be what you want.
|
||
*
|
||
* @param newValue New value to set. For a MultipleSelectivity instance the value must be an
|
||
* array of IDs, for a SingleSelectivity instance the value must be a single ID
|
||
* (a string or a number) or null to indicate no item is selected.
|
||
* @param options Optional options object. May contain the following property:
|
||
* triggerChange - Set to false to suppress the "change" event being triggered.
|
||
* Note this will also cause the UI to not update automatically;
|
||
* so you may want to call rerenderSelection() manually when
|
||
* using this option.
|
||
*/
|
||
setValue: function (newValue, options) {
|
||
options = options || {};
|
||
|
||
newValue = this.validateValue(newValue);
|
||
|
||
this._value = newValue;
|
||
|
||
if (this.options.initSelection) {
|
||
this.options.initSelection(
|
||
newValue,
|
||
function (data) {
|
||
if (this._value === newValue) {
|
||
this._data = this.validateData(data);
|
||
|
||
if (options.triggerChange !== false) {
|
||
this.triggerChange();
|
||
}
|
||
}
|
||
}.bind(this)
|
||
);
|
||
} else {
|
||
this._data = this.getDataForValue(newValue);
|
||
|
||
if (options.triggerChange !== false) {
|
||
this.triggerChange();
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Returns the result of the given template.
|
||
*
|
||
* @param templateName Name of the template to process.
|
||
* @param options Options to pass to the template.
|
||
*
|
||
* @return String containing HTML.
|
||
*/
|
||
template: function (templateName, options) {
|
||
var template = this.templates[templateName];
|
||
if (!template) {
|
||
throw new Error('Unknown template: ' + templateName);
|
||
}
|
||
|
||
if (typeof template === 'function') {
|
||
return template(options);
|
||
} else if (template.render) {
|
||
return template.render(options);
|
||
} else {
|
||
return template.toString();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Triggers the change event.
|
||
*
|
||
* The event object at least contains the following property:
|
||
* value - The new value of the Selectivity instance.
|
||
*
|
||
* @param Optional additional options added to the event object.
|
||
*/
|
||
triggerChange: function (options) {
|
||
var data = assign({value: this._value}, options);
|
||
this.triggerEvent('change', data);
|
||
this.triggerEvent('selectivity-change', data);
|
||
},
|
||
|
||
/**
|
||
* Triggers an event on the instance's element.
|
||
*
|
||
* @param eventName Name of the event to trigger.
|
||
* @param data Optional event data to be added to the event object.
|
||
*
|
||
* @return Whether the default action of the event may be executed, ie. returns false if
|
||
* preventDefault() has been called.
|
||
*/
|
||
triggerEvent: function (eventName, data) {
|
||
var event = document.createEvent('Event');
|
||
event.initEvent(eventName, /* bubbles: */ false, /* cancelable: */ true);
|
||
assign(event, data);
|
||
this.el.dispatchEvent(event);
|
||
return !event.defaultPrevented;
|
||
},
|
||
|
||
/**
|
||
* Validates a single item. Throws an exception if the item is invalid.
|
||
*
|
||
* @param item The item to validate.
|
||
*
|
||
* @return The validated item. May differ from the input item.
|
||
*/
|
||
validateItem: function (item) {
|
||
if (item && Selectivity.isValidId(item.id) && isString(item.text)) {
|
||
return item;
|
||
} else {
|
||
throw new Error('Item should have id (number or string) and text (string) properties');
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_blur: function () {
|
||
if (!this._focusing && !this.el.classList.contains('hover')) {
|
||
// Without the timeout it appears clicks on result items are not always properly
|
||
// handled, especially when the user doesn't click exactly on the text of the result
|
||
// item. I don't understand really why that happens, or why the timeout has to be so
|
||
// large, but after trial and error, this now seems to work reliably...
|
||
this._clearCloseTimeout();
|
||
this._closeTimeout = setTimeout(this.close.bind(this), 166);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_clearCloseTimeout: function () {
|
||
if (this._closeTimeout) {
|
||
clearTimeout(this._closeTimeout);
|
||
this._closeTimeout = 0;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_closed: function () {
|
||
this.dropdown = null;
|
||
|
||
toggleClass(this.el, 'open', false);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_mouseleave: function (event) {
|
||
// If mouseleave happens on any selectivity related element, remove hover class
|
||
if (!this.el.contains(event.relatedTarget)) {
|
||
toggleClass(this.el, 'hover', false);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_mouseenter: function () {
|
||
toggleClass(this.el, 'hover', true);
|
||
}
|
||
});
|
||
|
||
/**
|
||
* Dropdown class to use for displaying dropdowns.
|
||
*
|
||
* The default implementation of a dropdown is defined in the selectivity-dropdown module.
|
||
*/
|
||
Selectivity.Dropdown = null;
|
||
|
||
/**
|
||
* Array of input listeners.
|
||
*
|
||
* Input listeners are invoked when initInput() is called (typically right after the input is
|
||
* created). Every listener receives three arguments:
|
||
*
|
||
* selectivity - The Selectivity instance.
|
||
* input - DOM element of the input.
|
||
* options - Options that were passed to initInput().
|
||
*
|
||
* An example of a search input listener is the selectivity-keyboard module.
|
||
*/
|
||
Selectivity.InputListeners = [];
|
||
|
||
/**
|
||
* Mapping of input types.
|
||
*/
|
||
Selectivity.Inputs = {};
|
||
|
||
/**
|
||
* Array of option listeners.
|
||
*
|
||
* Option listeners are invoked when setOptions() is called. Every listener receives two arguments:
|
||
*
|
||
* selectivity - The Selectivity instance.
|
||
* options - The options that are about to be set. The listener may modify this options object.
|
||
*
|
||
* An example of an option listener is the selectivity-traditional module.
|
||
*/
|
||
Selectivity.OptionListeners = [];
|
||
|
||
/**
|
||
* Mapping with templates to use for rendering select boxes and dropdowns. See
|
||
* selectivity-templates.js for a useful set of default templates, as well as for documentation of
|
||
* the individual templates.
|
||
*/
|
||
Selectivity.Templates = {};
|
||
|
||
/**
|
||
* Finds an item in the given array with the specified ID.
|
||
*
|
||
* @param array Array to search in.
|
||
* @param id ID to search for.
|
||
*
|
||
* @return The item in the array with the given ID, or null if the item was not found.
|
||
*/
|
||
Selectivity.findById = function (array, id) {
|
||
var index = Selectivity.findIndexById(array, id);
|
||
return index > -1 ? array[index] : null;
|
||
};
|
||
|
||
/**
|
||
* Finds the index of an item in the given array with the specified ID.
|
||
*
|
||
* @param array Array to search in.
|
||
* @param id ID to search for.
|
||
*
|
||
* @return The index of the item in the array with the given ID, or -1 if the item was not found.
|
||
*/
|
||
Selectivity.findIndexById = function (array, id) {
|
||
for (var i = 0, length = array.length; i < length; i++) {
|
||
if (array[i].id === id) {
|
||
return i;
|
||
}
|
||
}
|
||
return -1;
|
||
};
|
||
|
||
/**
|
||
* Finds an item in the given array with the specified ID. Items in the array may contain 'children'
|
||
* properties which in turn will be searched for the item.
|
||
*
|
||
* @param array Array to search in.
|
||
* @param id ID to search for.
|
||
*
|
||
* @return The item in the array with the given ID, or null if the item was not found.
|
||
*/
|
||
Selectivity.findNestedById = function (array, id) {
|
||
for (var i = 0, length = array.length; i < length; i++) {
|
||
var item = array[i],
|
||
result;
|
||
if (item.id === id) {
|
||
result = item;
|
||
} else if (item.children) {
|
||
result = Selectivity.findNestedById(item.children, id);
|
||
} else if (item.submenu && item.submenu.items) {
|
||
// FIXME: reference to submenu plugin doesn't belong in base
|
||
result = Selectivity.findNestedById(item.submenu.items, id);
|
||
}
|
||
if (result) {
|
||
return result;
|
||
}
|
||
}
|
||
return null;
|
||
};
|
||
|
||
/**
|
||
* Utility method for inheriting another class.
|
||
*
|
||
* @param SubClass Constructor function of the subclass.
|
||
* @param SuperClass Constructor function of the superclass.
|
||
* @param prototype Object with methods you want to add to the subclass prototype.
|
||
*
|
||
* @return A utility function for calling the methods of the superclass. This function receives two
|
||
* arguments: The this object on which you want to execute the method and the name of the
|
||
* method. Any arguments past those are passed to the superclass method.
|
||
*/
|
||
Selectivity.inherits = function (SubClass, SuperClass, prototype) {
|
||
SubClass.prototype = assign(
|
||
Object.create(SuperClass.prototype),
|
||
{constructor: SubClass},
|
||
prototype
|
||
);
|
||
|
||
return function (self, methodName) {
|
||
SuperClass.prototype[methodName].apply(self, Array.prototype.slice.call(arguments, 2));
|
||
};
|
||
};
|
||
|
||
/**
|
||
* Checks whether a value can be used as a valid ID for selection items. Only numbers and strings
|
||
* are accepted to be used as IDs.
|
||
*
|
||
* @param id The value to check whether it is a valid ID.
|
||
*
|
||
* @return true if the value is a valid ID, false otherwise.
|
||
*/
|
||
Selectivity.isValidId = function (id) {
|
||
return typeof id === 'number' || isString(id);
|
||
};
|
||
|
||
/**
|
||
* Decides whether a given item matches a search term. The default implementation simply
|
||
* checks whether the term is contained within the item's text, after transforming them using
|
||
* transformText().
|
||
*
|
||
* @param item The item that should match the search term.
|
||
* @param term The search term. Note that for performance reasons, the term has always been already
|
||
* processed using transformText().
|
||
*
|
||
* @return true if the text matches the term, false otherwise.
|
||
*/
|
||
Selectivity.matcher = function (item, term) {
|
||
var result = null;
|
||
if (Selectivity.transformText(item.text).indexOf(term) > -1) {
|
||
result = item;
|
||
} else if (item.children) {
|
||
var matchingChildren = item.children
|
||
.map(function (child) {
|
||
return Selectivity.matcher(child, term);
|
||
})
|
||
.filter(function (child) {
|
||
return !!child;
|
||
});
|
||
if (matchingChildren.length) {
|
||
result = {id: item.id, text: item.text, children: matchingChildren};
|
||
}
|
||
}
|
||
return result;
|
||
};
|
||
|
||
/**
|
||
* Helper function for processing items.
|
||
*
|
||
* @param item The item to process, either as object containing 'id' and 'text' properties or just
|
||
* as ID. The 'id' property of an item is optional if it has a 'children' property
|
||
* containing an array of items.
|
||
*
|
||
* @return Object containing 'id' and 'text' properties.
|
||
*/
|
||
Selectivity.processItem = function (item) {
|
||
if (Selectivity.isValidId(item)) {
|
||
return {id: item, text: '' + item};
|
||
} else if (item && (Selectivity.isValidId(item.id) || item.children) && isString(item.text)) {
|
||
if (item.children) {
|
||
item.children = Selectivity.processItems(item.children);
|
||
}
|
||
|
||
return item;
|
||
} else {
|
||
throw new Error('invalid item');
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Helper function for processing an array of items.
|
||
*
|
||
* @param items Array of items to process. See processItem() for details about a single item.
|
||
*
|
||
* @return Array with items.
|
||
*/
|
||
Selectivity.processItems = function (items) {
|
||
if (Array.isArray(items)) {
|
||
return items.map(Selectivity.processItem);
|
||
} else {
|
||
throw new Error('invalid items');
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Transforms text in order to find matches. The default implementation casts all strings to
|
||
* lower-case so that any matches found will be case-insensitive.
|
||
*
|
||
* @param string The string to transform.
|
||
*
|
||
* @return The transformed string.
|
||
*/
|
||
Selectivity.transformText = function (string) {
|
||
return string.toLowerCase();
|
||
};
|
||
|
||
module.exports = Selectivity;
|
||
|
||
}, {"16": 16, "23": 23, "47": 47, "lodash/assign": "lodash/assign"}], 39: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
var escape = _dereq_(12);
|
||
|
||
var Selectivity = _dereq_(38);
|
||
var Locale = _dereq_(27);
|
||
|
||
/**
|
||
* Default set of templates to use with Selectivity.js.
|
||
*
|
||
* Template can be defined as either a string, a function returning a string (like Handlebars
|
||
* templates, for instance), an object containing a render function (like Hogan.js templates, fo
|
||
* instance) or as a function returning a DOM element.
|
||
*
|
||
* Every template must return a single root element.
|
||
*/
|
||
Selectivity.Templates = {
|
||
/**
|
||
* Renders the dropdown.
|
||
*
|
||
* The template is expected to have at least one element with the class
|
||
* 'selectivity-results-container', which is where all results will be added to.
|
||
*
|
||
* @param options Options object containing the following properties:
|
||
* dropdownCssClass - Optional CSS class to add to the top-level element.
|
||
* searchInputPlaceholder - Optional placeholder text to display in the search
|
||
* input in the dropdown.
|
||
* showSearchInput - Boolean whether a search input should be shown. If true,
|
||
* an input element with the 'selectivity-search-input' is
|
||
* expected.
|
||
*/
|
||
dropdown: function (options) {
|
||
var extraClass = options.dropdownCssClass ? ' ' + options.dropdownCssClass : '',
|
||
searchInput = '';
|
||
if (options.showSearchInput) {
|
||
extraClass += ' has-search-input';
|
||
|
||
var placeholder = options.searchInputPlaceholder;
|
||
searchInput =
|
||
'<div class="selectivity-search-input-container">' +
|
||
'<input type="text" class="selectivity-search-input"' +
|
||
(placeholder ? ' placeholder="' + escape(placeholder) + '"' : '') +
|
||
'>' +
|
||
'</div>';
|
||
}
|
||
return (
|
||
'<div class="selectivity-dropdown' +
|
||
extraClass +
|
||
'">' +
|
||
searchInput +
|
||
'<div class="selectivity-results-container"></div>' +
|
||
'</div>'
|
||
);
|
||
},
|
||
|
||
/**
|
||
* Renders an error message in the dropdown.
|
||
*
|
||
* @param options Options object containing the following properties:
|
||
* escape - Boolean whether the message should be HTML-escaped.
|
||
* message - The message to display.
|
||
*/
|
||
error: function (options) {
|
||
return (
|
||
'<div class="selectivity-error">' +
|
||
(options.escape ? escape(options.message) : options.message) +
|
||
'</div>'
|
||
);
|
||
},
|
||
|
||
/**
|
||
* Renders a loading indicator in the dropdown.
|
||
*
|
||
* This template is expected to have an element with a 'selectivity-loading' class which may be
|
||
* replaced with actual results.
|
||
*/
|
||
loading: function () {
|
||
return '<div class="selectivity-loading">' + Locale.loading + '</div>';
|
||
},
|
||
|
||
/**
|
||
* Load more indicator.
|
||
*
|
||
* This template is expected to have an element with a 'selectivity-load-more' class which, when
|
||
* clicked, will load more results.
|
||
*/
|
||
loadMore: function () {
|
||
return '<div class="selectivity-load-more">' + Locale.loadMore + '</div>';
|
||
},
|
||
|
||
/**
|
||
* Renders multi-selection input boxes.
|
||
*
|
||
* The template is expected to have at least have elements with the following classes:
|
||
* 'selectivity-multiple-input-container' - The element containing all the selected items and
|
||
* the input for selecting additional items.
|
||
* 'selectivity-multiple-input' - The actual input element that allows the user to type to
|
||
* search for more items. When selected items are added, they are
|
||
* inserted right before this element.
|
||
*
|
||
* @param options Options object containing the following property:
|
||
* enabled - Boolean whether the input is enabled.
|
||
*/
|
||
multipleSelectInput: function (options) {
|
||
return (
|
||
'<div class="selectivity-multiple-input-container">' +
|
||
(options.enabled
|
||
? '<input type="text" autocomplete="off" autocorrect="off" ' +
|
||
'autocapitalize="off" class="selectivity-multiple-input">'
|
||
: '<div class="selectivity-multiple-input ' + 'selectivity-placeholder"></div>') +
|
||
'<div class="selectivity-clearfix"></div>' +
|
||
'</div>'
|
||
);
|
||
},
|
||
|
||
/**
|
||
* Renders a selected item in multi-selection input boxes.
|
||
*
|
||
* The template is expected to have a top-level element with the class
|
||
* 'selectivity-multiple-selected-item'. This element is also required to have a 'data-item-id'
|
||
* attribute with the ID set to that passed through the options object.
|
||
*
|
||
* An element with the class 'selectivity-multiple-selected-item-remove' should be present
|
||
* which, when clicked, will cause the element to be removed.
|
||
*
|
||
* @param options Options object containing the following properties:
|
||
* highlighted - Boolean whether this item is currently highlighted.
|
||
* id - Identifier for the item.
|
||
* removable - Boolean whether a remove icon should be displayed.
|
||
* text - Text label which the user sees.
|
||
*/
|
||
multipleSelectedItem: function (options) {
|
||
var extraClass = options.highlighted ? ' highlighted' : '';
|
||
return (
|
||
'<span class="selectivity-multiple-selected-item' +
|
||
extraClass +
|
||
'" ' +
|
||
'data-item-id="' +
|
||
escape(options.id) +
|
||
'">' +
|
||
(options.removable
|
||
? '<a class="selectivity-multiple-selected-item-remove">' +
|
||
'<i class="fa fa-remove"></i>' +
|
||
'</a>'
|
||
: '') +
|
||
escape(options.text) +
|
||
'</span>'
|
||
);
|
||
},
|
||
|
||
/**
|
||
* Renders a message there are no results for the given query.
|
||
*
|
||
* @param options Options object containing the following property:
|
||
* term - Search term the user is searching for.
|
||
*/
|
||
noResults: function (options) {
|
||
return (
|
||
'<div class="selectivity-error">' +
|
||
(options.term ? Locale.noResultsForTerm(options.term) : Locale.noResults) +
|
||
'</div>'
|
||
);
|
||
},
|
||
|
||
/**
|
||
* Renders a container for item children.
|
||
*
|
||
* The template is expected to have an element with the class 'selectivity-result-children'.
|
||
*
|
||
* @param options Options object containing the following property:
|
||
* childrenHtml - Rendered HTML for the children.
|
||
*/
|
||
resultChildren: function (options) {
|
||
return '<div class="selectivity-result-children">' + options.childrenHtml + '</div>';
|
||
},
|
||
|
||
/**
|
||
* Render a result item in the dropdown.
|
||
*
|
||
* The template is expected to have a top-level element with the class
|
||
* 'selectivity-result-item'. This element is also required to have a 'data-item-id' attribute
|
||
* with the ID set to that passed through the options object.
|
||
*
|
||
* @param options Options object containing the following properties:
|
||
* id - Identifier for the item.
|
||
* text - Text label which the user sees.
|
||
* disabled - Truthy if the item should be disabled.
|
||
* submenu - Truthy if the result item has a menu with subresults.
|
||
*/
|
||
resultItem: function (options) {
|
||
return (
|
||
'<div class="selectivity-result-item' +
|
||
(options.disabled ? ' disabled' : '') +
|
||
'"' +
|
||
' data-item-id="' +
|
||
escape(options.id) +
|
||
'"><b>' +
|
||
escape(options.text) +
|
||
(options.submenu
|
||
? '<i class="selectivity-submenu-icon fa fa-chevron-right"></i>'
|
||
: '') +
|
||
'</b></div>'
|
||
);
|
||
},
|
||
|
||
/**
|
||
* Render a result label in the dropdown.
|
||
*
|
||
* The template is expected to have a top-level element with the class
|
||
* 'selectivity-result-label'.
|
||
*
|
||
* @param options Options object containing the following properties:
|
||
* text - Text label.
|
||
*/
|
||
resultLabel: function (options) {
|
||
return '<div class="selectivity-result-label">' + escape(options.text) + '</div>';
|
||
},
|
||
|
||
/**
|
||
* Renders single-select input boxes.
|
||
*
|
||
* The template is expected to have at least one element with the class
|
||
* 'selectivity-single-result-container' which is the element containing the selected item or
|
||
* the placeholder.
|
||
*/
|
||
singleSelectInput: function (options) {
|
||
return (
|
||
'<div class="selectivity-single-select">' +
|
||
'<input type="text" class="selectivity-single-select-input"' +
|
||
(options.required ? ' required' : '') +
|
||
'>' +
|
||
'<div class="selectivity-single-result-container"></div>' +
|
||
'<i class="fa fa-sort-desc selectivity-caret"></i>' +
|
||
'</div>'
|
||
);
|
||
},
|
||
|
||
/**
|
||
* Renders the placeholder for single-select input boxes.
|
||
*
|
||
* The template is expected to have a top-level element with the class
|
||
* 'selectivity-placeholder'.
|
||
*
|
||
* @param options Options object containing the following property:
|
||
* placeholder - The placeholder text.
|
||
*/
|
||
singleSelectPlaceholder: function (options) {
|
||
return '<div class="selectivity-placeholder">' + escape(options.placeholder) + '</div>';
|
||
},
|
||
|
||
/**
|
||
* Renders the selected item in single-select input boxes.
|
||
*
|
||
* The template is expected to have a top-level element with the class
|
||
* 'selectivity-single-selected-item'. This element is also required to have a 'data-item-id'
|
||
* attribute with the ID set to that passed through the options object.
|
||
*
|
||
* @param options Options object containing the following properties:
|
||
* id - Identifier for the item.
|
||
* removable - Boolean whether a remove icon should be displayed.
|
||
* text - Text label which the user sees.
|
||
*/
|
||
singleSelectedItem: function (options) {
|
||
return (
|
||
'<span class="selectivity-single-selected-item" ' +
|
||
'data-item-id="' +
|
||
escape(options.id) +
|
||
'">' +
|
||
(options.removable
|
||
? '<a class="selectivity-single-selected-item-remove">' +
|
||
'<i class="fa fa-remove"></i>' +
|
||
'</a>'
|
||
: '') +
|
||
escape(options.text) +
|
||
'</span>'
|
||
);
|
||
},
|
||
|
||
/**
|
||
* Renders select-box inside single-select input that was initialized on
|
||
* traditional <select> element.
|
||
*
|
||
* @param options Options object containing the following properties:
|
||
* name - Name of the <select> element.
|
||
* mode - Mode in which select exists, single or multiple.
|
||
*/
|
||
selectCompliance: function (options) {
|
||
var mode = options.mode;
|
||
var name = options.name;
|
||
if (mode === 'multiple' && name.slice(-2) !== '[]') {
|
||
name += '[]';
|
||
}
|
||
return (
|
||
'<select name="' + name + '"' + (mode === 'multiple' ? ' multiple' : '') + '></select>'
|
||
);
|
||
},
|
||
|
||
/**
|
||
* Renders the selected item in compliance <select> element as <option>.
|
||
*
|
||
* @param options Options object containing the following properties
|
||
* id - Identifier for the item.
|
||
* text - Text label which the user sees.
|
||
*/
|
||
selectOptionCompliance: function (options) {
|
||
return (
|
||
'<option value="' +
|
||
escape(options.id) +
|
||
'" selected>' +
|
||
escape(options.text) +
|
||
'</option>'
|
||
);
|
||
}
|
||
};
|
||
|
||
}, {"12": 12, "27": 27, "38": 38}], 40: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
/**
|
||
* Returns a result item with a given item ID.
|
||
*
|
||
* @param resultItems Array of DOM elements representing result items.
|
||
* @param itemId ID of the item to return.
|
||
*
|
||
* @param DOM element of the result item with the given item ID, or null if not found.
|
||
*/
|
||
module.exports = function (resultItems, itemId) {
|
||
for (var i = 0, length = resultItems.length; i < length; i++) {
|
||
var resultItem = resultItems[i];
|
||
var resultId = resultItem.getAttribute('data-item-id');
|
||
if ((typeof itemId === 'number' ? parseInt(resultId, 10) : resultId) === itemId) {
|
||
return resultItem;
|
||
}
|
||
}
|
||
return null;
|
||
};
|
||
|
||
}, {}], 41: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
/**
|
||
* Returns the CSS selector for selecting a specific item by ID.
|
||
*
|
||
* @param selector Generic CSS selector to identify items.
|
||
* @param id ID of the item to select.
|
||
*/
|
||
module.exports = function (selector, id) {
|
||
var quotedId = '"' + ('' + id).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
|
||
return selector + '[data-item-id=' + quotedId + ']';
|
||
};
|
||
|
||
}, {}], 42: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
/**
|
||
* Returns the keyCode value of the given event.
|
||
*/
|
||
module.exports = function (event) {
|
||
return event.which || event.keyCode || 0;
|
||
};
|
||
|
||
}, {}], 43: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
/**
|
||
* Returns whether the given element matches the given selector.
|
||
*/
|
||
module.exports = function (el, selector) {
|
||
var method =
|
||
el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;
|
||
return method.call(el, selector);
|
||
};
|
||
|
||
}, {}], 44: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
/**
|
||
* Parses an HTML string and returns the resulting DOM element.
|
||
*
|
||
* @param html HTML representation of the element to parse.
|
||
*/
|
||
module.exports = function (html) {
|
||
var div = document.createElement('div');
|
||
div.innerHTML = html;
|
||
return div.firstChild;
|
||
};
|
||
|
||
}, {}], 45: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
/**
|
||
* Removes a DOM element.
|
||
*
|
||
* @param el The element to remove.
|
||
*/
|
||
module.exports = function (el) {
|
||
if (el && el.parentNode) {
|
||
el.parentNode.removeChild(el);
|
||
}
|
||
};
|
||
|
||
}, {}], 46: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
/**
|
||
* Stops event propagation.
|
||
*
|
||
* @param event The event to stop from propagating.
|
||
*/
|
||
module.exports = function (event) {
|
||
event.stopPropagation();
|
||
};
|
||
|
||
}, {}], 47: [function (_dereq_, module, exports) {
|
||
'use strict';
|
||
|
||
/**
|
||
* Toggles a CSS class on an element.
|
||
*
|
||
* @param el The element on which to toggle the CSS class.
|
||
* @param className The CSS class to toggle.
|
||
* @param force If true, the class is added. If false, the class is removed.
|
||
*/
|
||
module.exports = function (el, className, force) {
|
||
if (el) {
|
||
el.classList[force ? 'add' : 'remove'](className);
|
||
}
|
||
};
|
||
|
||
}, {}]
|
||
}, {}, [37])(37)
|
||
}); |