'}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery);
--------------------------------------------------------------------------------
/resources/public/js/lib/underscore.js:
--------------------------------------------------------------------------------
1 | // Underscore.js 1.5.0
2 | // http://underscorejs.org
3 | // (c) 2009-2011 Jeremy Ashkenas, DocumentCloud Inc.
4 | // (c) 2011-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
5 | // Underscore may be freely distributed under the MIT license.
6 |
7 | (function() {
8 |
9 | // Baseline setup
10 | // --------------
11 |
12 | // Establish the root object, `window` in the browser, or `global` on the server.
13 | var root = this;
14 |
15 | // Save the previous value of the `_` variable.
16 | var previousUnderscore = root._;
17 |
18 | // Establish the object that gets returned to break out of a loop iteration.
19 | var breaker = {};
20 |
21 | // Save bytes in the minified (but not gzipped) version:
22 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
23 |
24 | // Create quick reference variables for speed access to core prototypes.
25 | var
26 | push = ArrayProto.push,
27 | slice = ArrayProto.slice,
28 | concat = ArrayProto.concat,
29 | toString = ObjProto.toString,
30 | hasOwnProperty = ObjProto.hasOwnProperty;
31 |
32 | // All **ECMAScript 5** native function implementations that we hope to use
33 | // are declared here.
34 | var
35 | nativeForEach = ArrayProto.forEach,
36 | nativeMap = ArrayProto.map,
37 | nativeReduce = ArrayProto.reduce,
38 | nativeReduceRight = ArrayProto.reduceRight,
39 | nativeFilter = ArrayProto.filter,
40 | nativeEvery = ArrayProto.every,
41 | nativeSome = ArrayProto.some,
42 | nativeIndexOf = ArrayProto.indexOf,
43 | nativeLastIndexOf = ArrayProto.lastIndexOf,
44 | nativeIsArray = Array.isArray,
45 | nativeKeys = Object.keys,
46 | nativeBind = FuncProto.bind;
47 |
48 | // Create a safe reference to the Underscore object for use below.
49 | var _ = function(obj) {
50 | if (obj instanceof _) return obj;
51 | if (!(this instanceof _)) return new _(obj);
52 | this._wrapped = obj;
53 | };
54 |
55 | // Export the Underscore object for **Node.js**, with
56 | // backwards-compatibility for the old `require()` API. If we're in
57 | // the browser, add `_` as a global object via a string identifier,
58 | // for Closure Compiler "advanced" mode.
59 | if (typeof exports !== 'undefined') {
60 | if (typeof module !== 'undefined' && module.exports) {
61 | exports = module.exports = _;
62 | }
63 | exports._ = _;
64 | } else {
65 | root._ = _;
66 | }
67 |
68 | // Current version.
69 | _.VERSION = '1.5.0';
70 |
71 | // Collection Functions
72 | // --------------------
73 |
74 | // The cornerstone, an `each` implementation, aka `forEach`.
75 | // Handles objects with the built-in `forEach`, arrays, and raw objects.
76 | // Delegates to **ECMAScript 5**'s native `forEach` if available.
77 | var each = _.each = _.forEach = function(obj, iterator, context) {
78 | if (obj == null) return;
79 | if (nativeForEach && obj.forEach === nativeForEach) {
80 | obj.forEach(iterator, context);
81 | } else if (obj.length === +obj.length) {
82 | for (var i = 0, l = obj.length; i < l; i++) {
83 | if (iterator.call(context, obj[i], i, obj) === breaker) return;
84 | }
85 | } else {
86 | for (var key in obj) {
87 | if (_.has(obj, key)) {
88 | if (iterator.call(context, obj[key], key, obj) === breaker) return;
89 | }
90 | }
91 | }
92 | };
93 |
94 | // Return the results of applying the iterator to each element.
95 | // Delegates to **ECMAScript 5**'s native `map` if available.
96 | _.map = _.collect = function(obj, iterator, context) {
97 | var results = [];
98 | if (obj == null) return results;
99 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
100 | each(obj, function(value, index, list) {
101 | results.push(iterator.call(context, value, index, list));
102 | });
103 | return results;
104 | };
105 |
106 | var reduceError = 'Reduce of empty array with no initial value';
107 |
108 | // **Reduce** builds up a single result from a list of values, aka `inject`,
109 | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
110 | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
111 | var initial = arguments.length > 2;
112 | if (obj == null) obj = [];
113 | if (nativeReduce && obj.reduce === nativeReduce) {
114 | if (context) iterator = _.bind(iterator, context);
115 | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
116 | }
117 | each(obj, function(value, index, list) {
118 | if (!initial) {
119 | memo = value;
120 | initial = true;
121 | } else {
122 | memo = iterator.call(context, memo, value, index, list);
123 | }
124 | });
125 | if (!initial) throw new TypeError(reduceError);
126 | return memo;
127 | };
128 |
129 | // The right-associative version of reduce, also known as `foldr`.
130 | // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
131 | _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
132 | var initial = arguments.length > 2;
133 | if (obj == null) obj = [];
134 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
135 | if (context) iterator = _.bind(iterator, context);
136 | return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
137 | }
138 | var length = obj.length;
139 | if (length !== +length) {
140 | var keys = _.keys(obj);
141 | length = keys.length;
142 | }
143 | each(obj, function(value, index, list) {
144 | index = keys ? keys[--length] : --length;
145 | if (!initial) {
146 | memo = obj[index];
147 | initial = true;
148 | } else {
149 | memo = iterator.call(context, memo, obj[index], index, list);
150 | }
151 | });
152 | if (!initial) throw new TypeError(reduceError);
153 | return memo;
154 | };
155 |
156 | // Return the first value which passes a truth test. Aliased as `detect`.
157 | _.find = _.detect = function(obj, iterator, context) {
158 | var result;
159 | any(obj, function(value, index, list) {
160 | if (iterator.call(context, value, index, list)) {
161 | result = value;
162 | return true;
163 | }
164 | });
165 | return result;
166 | };
167 |
168 | // Return all the elements that pass a truth test.
169 | // Delegates to **ECMAScript 5**'s native `filter` if available.
170 | // Aliased as `select`.
171 | _.filter = _.select = function(obj, iterator, context) {
172 | var results = [];
173 | if (obj == null) return results;
174 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
175 | each(obj, function(value, index, list) {
176 | if (iterator.call(context, value, index, list)) results.push(value);
177 | });
178 | return results;
179 | };
180 |
181 | // Return all the elements for which a truth test fails.
182 | _.reject = function(obj, iterator, context) {
183 | return _.filter(obj, function(value, index, list) {
184 | return !iterator.call(context, value, index, list);
185 | }, context);
186 | };
187 |
188 | // Determine whether all of the elements match a truth test.
189 | // Delegates to **ECMAScript 5**'s native `every` if available.
190 | // Aliased as `all`.
191 | _.every = _.all = function(obj, iterator, context) {
192 | iterator || (iterator = _.identity);
193 | var result = true;
194 | if (obj == null) return result;
195 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
196 | each(obj, function(value, index, list) {
197 | if (!(result = result && iterator.call(context, value, index, list))) return breaker;
198 | });
199 | return !!result;
200 | };
201 |
202 | // Determine if at least one element in the object matches a truth test.
203 | // Delegates to **ECMAScript 5**'s native `some` if available.
204 | // Aliased as `any`.
205 | var any = _.some = _.any = function(obj, iterator, context) {
206 | iterator || (iterator = _.identity);
207 | var result = false;
208 | if (obj == null) return result;
209 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
210 | each(obj, function(value, index, list) {
211 | if (result || (result = iterator.call(context, value, index, list))) return breaker;
212 | });
213 | return !!result;
214 | };
215 |
216 | // Determine if the array or object contains a given value (using `===`).
217 | // Aliased as `include`.
218 | _.contains = _.include = function(obj, target) {
219 | if (obj == null) return false;
220 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
221 | return any(obj, function(value) {
222 | return value === target;
223 | });
224 | };
225 |
226 | // Invoke a method (with arguments) on every item in a collection.
227 | _.invoke = function(obj, method) {
228 | var args = slice.call(arguments, 2);
229 | var isFunc = _.isFunction(method);
230 | return _.map(obj, function(value) {
231 | return (isFunc ? method : value[method]).apply(value, args);
232 | });
233 | };
234 |
235 | // Convenience version of a common use case of `map`: fetching a property.
236 | _.pluck = function(obj, key) {
237 | return _.map(obj, function(value){ return value[key]; });
238 | };
239 |
240 | // Convenience version of a common use case of `filter`: selecting only objects
241 | // containing specific `key:value` pairs.
242 | _.where = function(obj, attrs, first) {
243 | if (_.isEmpty(attrs)) return first ? void 0 : [];
244 | return _[first ? 'find' : 'filter'](obj, function(value) {
245 | for (var key in attrs) {
246 | if (attrs[key] !== value[key]) return false;
247 | }
248 | return true;
249 | });
250 | };
251 |
252 | // Convenience version of a common use case of `find`: getting the first object
253 | // containing specific `key:value` pairs.
254 | _.findWhere = function(obj, attrs) {
255 | return _.where(obj, attrs, true);
256 | };
257 |
258 | // Return the maximum element or (element-based computation).
259 | // Can't optimize arrays of integers longer than 65,535 elements.
260 | // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
261 | _.max = function(obj, iterator, context) {
262 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
263 | return Math.max.apply(Math, obj);
264 | }
265 | if (!iterator && _.isEmpty(obj)) return -Infinity;
266 | var result = {computed : -Infinity, value: -Infinity};
267 | each(obj, function(value, index, list) {
268 | var computed = iterator ? iterator.call(context, value, index, list) : value;
269 | computed > result.computed && (result = {value : value, computed : computed});
270 | });
271 | return result.value;
272 | };
273 |
274 | // Return the minimum element (or element-based computation).
275 | _.min = function(obj, iterator, context) {
276 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
277 | return Math.min.apply(Math, obj);
278 | }
279 | if (!iterator && _.isEmpty(obj)) return Infinity;
280 | var result = {computed : Infinity, value: Infinity};
281 | each(obj, function(value, index, list) {
282 | var computed = iterator ? iterator.call(context, value, index, list) : value;
283 | computed < result.computed && (result = {value : value, computed : computed});
284 | });
285 | return result.value;
286 | };
287 |
288 | // Shuffle an array.
289 | _.shuffle = function(obj) {
290 | var rand;
291 | var index = 0;
292 | var shuffled = [];
293 | each(obj, function(value) {
294 | rand = _.random(index++);
295 | shuffled[index - 1] = shuffled[rand];
296 | shuffled[rand] = value;
297 | });
298 | return shuffled;
299 | };
300 |
301 | // An internal function to generate lookup iterators.
302 | var lookupIterator = function(value) {
303 | return _.isFunction(value) ? value : function(obj){ return obj[value]; };
304 | };
305 |
306 | // Sort the object's values by a criterion produced by an iterator.
307 | _.sortBy = function(obj, value, context) {
308 | var iterator = lookupIterator(value);
309 | return _.pluck(_.map(obj, function(value, index, list) {
310 | return {
311 | value : value,
312 | index : index,
313 | criteria : iterator.call(context, value, index, list)
314 | };
315 | }).sort(function(left, right) {
316 | var a = left.criteria;
317 | var b = right.criteria;
318 | if (a !== b) {
319 | if (a > b || a === void 0) return 1;
320 | if (a < b || b === void 0) return -1;
321 | }
322 | return left.index < right.index ? -1 : 1;
323 | }), 'value');
324 | };
325 |
326 | // An internal function used for aggregate "group by" operations.
327 | var group = function(obj, value, context, behavior) {
328 | var result = {};
329 | var iterator = lookupIterator(value == null ? _.identity : value);
330 | each(obj, function(value, index) {
331 | var key = iterator.call(context, value, index, obj);
332 | behavior(result, key, value);
333 | });
334 | return result;
335 | };
336 |
337 | // Groups the object's values by a criterion. Pass either a string attribute
338 | // to group by, or a function that returns the criterion.
339 | _.groupBy = function(obj, value, context) {
340 | return group(obj, value, context, function(result, key, value) {
341 | (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
342 | });
343 | };
344 |
345 | // Counts instances of an object that group by a certain criterion. Pass
346 | // either a string attribute to count by, or a function that returns the
347 | // criterion.
348 | _.countBy = function(obj, value, context) {
349 | return group(obj, value, context, function(result, key) {
350 | if (!_.has(result, key)) result[key] = 0;
351 | result[key]++;
352 | });
353 | };
354 |
355 | // Use a comparator function to figure out the smallest index at which
356 | // an object should be inserted so as to maintain order. Uses binary search.
357 | _.sortedIndex = function(array, obj, iterator, context) {
358 | iterator = iterator == null ? _.identity : lookupIterator(iterator);
359 | var value = iterator.call(context, obj);
360 | var low = 0, high = array.length;
361 | while (low < high) {
362 | var mid = (low + high) >>> 1;
363 | iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
364 | }
365 | return low;
366 | };
367 |
368 | // Safely create a real, live array from anything iterable.
369 | _.toArray = function(obj) {
370 | if (!obj) return [];
371 | if (_.isArray(obj)) return slice.call(obj);
372 | if (obj.length === +obj.length) return _.map(obj, _.identity);
373 | return _.values(obj);
374 | };
375 |
376 | // Return the number of elements in an object.
377 | _.size = function(obj) {
378 | if (obj == null) return 0;
379 | return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
380 | };
381 |
382 | // Array Functions
383 | // ---------------
384 |
385 | // Get the first element of an array. Passing **n** will return the first N
386 | // values in the array. Aliased as `head` and `take`. The **guard** check
387 | // allows it to work with `_.map`.
388 | _.first = _.head = _.take = function(array, n, guard) {
389 | if (array == null) return void 0;
390 | return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
391 | };
392 |
393 | // Returns everything but the last entry of the array. Especially useful on
394 | // the arguments object. Passing **n** will return all the values in
395 | // the array, excluding the last N. The **guard** check allows it to work with
396 | // `_.map`.
397 | _.initial = function(array, n, guard) {
398 | return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
399 | };
400 |
401 | // Get the last element of an array. Passing **n** will return the last N
402 | // values in the array. The **guard** check allows it to work with `_.map`.
403 | _.last = function(array, n, guard) {
404 | if (array == null) return void 0;
405 | if ((n != null) && !guard) {
406 | return slice.call(array, Math.max(array.length - n, 0));
407 | } else {
408 | return array[array.length - 1];
409 | }
410 | };
411 |
412 | // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
413 | // Especially useful on the arguments object. Passing an **n** will return
414 | // the rest N values in the array. The **guard**
415 | // check allows it to work with `_.map`.
416 | _.rest = _.tail = _.drop = function(array, n, guard) {
417 | return slice.call(array, (n == null) || guard ? 1 : n);
418 | };
419 |
420 | // Trim out all falsy values from an array.
421 | _.compact = function(array) {
422 | return _.filter(array, _.identity);
423 | };
424 |
425 | // Internal implementation of a recursive `flatten` function.
426 | var flatten = function(input, shallow, output) {
427 | if (shallow && _.every(input, _.isArray)) {
428 | return concat.apply(output, input);
429 | }
430 | each(input, function(value) {
431 | if (_.isArray(value) || _.isArguments(value)) {
432 | shallow ? push.apply(output, value) : flatten(value, shallow, output);
433 | } else {
434 | output.push(value);
435 | }
436 | });
437 | return output;
438 | };
439 |
440 | // Return a completely flattened version of an array.
441 | _.flatten = function(array, shallow) {
442 | return flatten(array, shallow, []);
443 | };
444 |
445 | // Return a version of the array that does not contain the specified value(s).
446 | _.without = function(array) {
447 | return _.difference(array, slice.call(arguments, 1));
448 | };
449 |
450 | // Produce a duplicate-free version of the array. If the array has already
451 | // been sorted, you have the option of using a faster algorithm.
452 | // Aliased as `unique`.
453 | _.uniq = _.unique = function(array, isSorted, iterator, context) {
454 | if (_.isFunction(isSorted)) {
455 | context = iterator;
456 | iterator = isSorted;
457 | isSorted = false;
458 | }
459 | var initial = iterator ? _.map(array, iterator, context) : array;
460 | var results = [];
461 | var seen = [];
462 | each(initial, function(value, index) {
463 | if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
464 | seen.push(value);
465 | results.push(array[index]);
466 | }
467 | });
468 | return results;
469 | };
470 |
471 | // Produce an array that contains the union: each distinct element from all of
472 | // the passed-in arrays.
473 | _.union = function() {
474 | return _.uniq(_.flatten(arguments, true));
475 | };
476 |
477 | // Produce an array that contains every item shared between all the
478 | // passed-in arrays.
479 | _.intersection = function(array) {
480 | var rest = slice.call(arguments, 1);
481 | return _.filter(_.uniq(array), function(item) {
482 | return _.every(rest, function(other) {
483 | return _.indexOf(other, item) >= 0;
484 | });
485 | });
486 | };
487 |
488 | // Take the difference between one array and a number of other arrays.
489 | // Only the elements present in just the first array will remain.
490 | _.difference = function(array) {
491 | var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
492 | return _.filter(array, function(value){ return !_.contains(rest, value); });
493 | };
494 |
495 | // Zip together multiple lists into a single array -- elements that share
496 | // an index go together.
497 | _.zip = function() {
498 | return _.unzip.apply(_, slice.call(arguments));
499 | };
500 |
501 | // The inverse operation to `_.zip`. If given an array of pairs it
502 | // returns an array of the paired elements split into two left and
503 | // right element arrays, if given an array of triples it returns a
504 | // three element array and so on. For example, `_.unzip` given
505 | // `[['a',1],['b',2],['c',3]]` returns the array
506 | // [['a','b','c'],[1,2,3]].
507 | _.unzip = function() {
508 | var length = _.max(_.pluck(arguments, "length").concat(0));
509 | var results = new Array(length);
510 | for (var i = 0; i < length; i++) {
511 | results[i] = _.pluck(arguments, '' + i);
512 | }
513 | return results;
514 | };
515 |
516 | // Converts lists into objects. Pass either a single array of `[key, value]`
517 | // pairs, or two parallel arrays of the same length -- one of keys, and one of
518 | // the corresponding values.
519 | _.object = function(list, values) {
520 | if (list == null) return {};
521 | var result = {};
522 | for (var i = 0, l = list.length; i < l; i++) {
523 | if (values) {
524 | result[list[i]] = values[i];
525 | } else {
526 | result[list[i][0]] = list[i][1];
527 | }
528 | }
529 | return result;
530 | };
531 |
532 | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
533 | // we need this function. Return the position of the first occurrence of an
534 | // item in an array, or -1 if the item is not included in the array.
535 | // Delegates to **ECMAScript 5**'s native `indexOf` if available.
536 | // If the array is large and already in sort order, pass `true`
537 | // for **isSorted** to use binary search.
538 | _.indexOf = function(array, item, isSorted) {
539 | if (array == null) return -1;
540 | var i = 0, l = array.length;
541 | if (isSorted) {
542 | if (typeof isSorted == 'number') {
543 | i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
544 | } else {
545 | i = _.sortedIndex(array, item);
546 | return array[i] === item ? i : -1;
547 | }
548 | }
549 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
550 | for (; i < l; i++) if (array[i] === item) return i;
551 | return -1;
552 | };
553 |
554 | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
555 | _.lastIndexOf = function(array, item, from) {
556 | if (array == null) return -1;
557 | var hasIndex = from != null;
558 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
559 | return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
560 | }
561 | var i = (hasIndex ? from : array.length);
562 | while (i--) if (array[i] === item) return i;
563 | return -1;
564 | };
565 |
566 | // Generate an integer Array containing an arithmetic progression. A port of
567 | // the native Python `range()` function. See
568 | // [the Python documentation](http://docs.python.org/library/functions.html#range).
569 | _.range = function(start, stop, step) {
570 | if (arguments.length <= 1) {
571 | stop = start || 0;
572 | start = 0;
573 | }
574 | step = arguments[2] || 1;
575 |
576 | var len = Math.max(Math.ceil((stop - start) / step), 0);
577 | var idx = 0;
578 | var range = new Array(len);
579 |
580 | while(idx < len) {
581 | range[idx++] = start;
582 | start += step;
583 | }
584 |
585 | return range;
586 | };
587 |
588 | // Function (ahem) Functions
589 | // ------------------
590 |
591 | // Reusable constructor function for prototype setting.
592 | var ctor = function(){};
593 |
594 | // Create a function bound to a given object (assigning `this`, and arguments,
595 | // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
596 | // available.
597 | _.bind = function(func, context) {
598 | var args, bound;
599 | if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
600 | if (!_.isFunction(func)) throw new TypeError;
601 | args = slice.call(arguments, 2);
602 | return bound = function() {
603 | if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
604 | ctor.prototype = func.prototype;
605 | var self = new ctor;
606 | ctor.prototype = null;
607 | var result = func.apply(self, args.concat(slice.call(arguments)));
608 | if (Object(result) === result) return result;
609 | return self;
610 | };
611 | };
612 |
613 | // Partially apply a function by creating a version that has had some of its
614 | // arguments pre-filled, without changing its dynamic `this` context.
615 | _.partial = function(func) {
616 | var args = slice.call(arguments, 1);
617 | return function() {
618 | return func.apply(this, args.concat(slice.call(arguments)));
619 | };
620 | };
621 |
622 | // Bind all of an object's methods to that object. Useful for ensuring that
623 | // all callbacks defined on an object belong to it.
624 | _.bindAll = function(obj) {
625 | var funcs = slice.call(arguments, 1);
626 | if (funcs.length === 0) throw new Error("bindAll must be passed function names");
627 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
628 | return obj;
629 | };
630 |
631 | // Memoize an expensive function by storing its results.
632 | _.memoize = function(func, hasher) {
633 | var memo = {};
634 | hasher || (hasher = _.identity);
635 | return function() {
636 | var key = hasher.apply(this, arguments);
637 | return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
638 | };
639 | };
640 |
641 | // Delays a function for the given number of milliseconds, and then calls
642 | // it with the arguments supplied.
643 | _.delay = function(func, wait) {
644 | var args = slice.call(arguments, 2);
645 | return setTimeout(function(){ return func.apply(null, args); }, wait);
646 | };
647 |
648 | // Defers a function, scheduling it to run after the current call stack has
649 | // cleared.
650 | _.defer = function(func) {
651 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
652 | };
653 |
654 | // Returns a function, that, when invoked, will only be triggered at most once
655 | // during a given window of time. Normally, the throttled function will run
656 | // as much as it can, without ever going more than once per `wait` duration;
657 | // but if you'd like to disable the execution on the leading edge, pass
658 | // `{leading: false}`. To disable execution on the trailing edge, ditto.
659 | _.throttle = function(func, wait, options) {
660 | var context, args, result;
661 | var timeout = null;
662 | var previous = 0;
663 | options || (options = {});
664 | var later = function() {
665 | previous = new Date;
666 | timeout = null;
667 | result = func.apply(context, args);
668 | };
669 | return function() {
670 | var now = new Date;
671 | if (!previous && options.leading === false) previous = now;
672 | var remaining = wait - (now - previous);
673 | context = this;
674 | args = arguments;
675 | if (remaining <= 0) {
676 | clearTimeout(timeout);
677 | timeout = null;
678 | previous = now;
679 | result = func.apply(context, args);
680 | } else if (!timeout && options.trailing !== false) {
681 | timeout = setTimeout(later, remaining);
682 | }
683 | return result;
684 | };
685 | };
686 |
687 | // Returns a function, that, as long as it continues to be invoked, will not
688 | // be triggered. The function will be called after it stops being called for
689 | // N milliseconds. If `immediate` is passed, trigger the function on the
690 | // leading edge, instead of the trailing.
691 | _.debounce = function(func, wait, immediate) {
692 | var result;
693 | var timeout = null;
694 | return function() {
695 | var context = this, args = arguments;
696 | var later = function() {
697 | timeout = null;
698 | if (!immediate) result = func.apply(context, args);
699 | };
700 | var callNow = immediate && !timeout;
701 | clearTimeout(timeout);
702 | timeout = setTimeout(later, wait);
703 | if (callNow) result = func.apply(context, args);
704 | return result;
705 | };
706 | };
707 |
708 | // Returns a function that will be executed at most one time, no matter how
709 | // often you call it. Useful for lazy initialization.
710 | _.once = function(func) {
711 | var ran = false, memo;
712 | return function() {
713 | if (ran) return memo;
714 | ran = true;
715 | memo = func.apply(this, arguments);
716 | func = null;
717 | return memo;
718 | };
719 | };
720 |
721 | // Returns the first function passed as an argument to the second,
722 | // allowing you to adjust arguments, run code before and after, and
723 | // conditionally execute the original function.
724 | _.wrap = function(func, wrapper) {
725 | return function() {
726 | var args = [func];
727 | push.apply(args, arguments);
728 | return wrapper.apply(this, args);
729 | };
730 | };
731 |
732 | // Returns a function that is the composition of a list of functions, each
733 | // consuming the return value of the function that follows.
734 | _.compose = function() {
735 | var funcs = arguments;
736 | return function() {
737 | var args = arguments;
738 | for (var i = funcs.length - 1; i >= 0; i--) {
739 | args = [funcs[i].apply(this, args)];
740 | }
741 | return args[0];
742 | };
743 | };
744 |
745 | // Returns a function that will only be executed after being called N times.
746 | _.after = function(times, func) {
747 | return function() {
748 | if (--times < 1) {
749 | return func.apply(this, arguments);
750 | }
751 | };
752 | };
753 |
754 | // Object Functions
755 | // ----------------
756 |
757 | // Retrieve the names of an object's properties.
758 | // Delegates to **ECMAScript 5**'s native `Object.keys`
759 | _.keys = nativeKeys || function(obj) {
760 | if (obj !== Object(obj)) throw new TypeError('Invalid object');
761 | var keys = [];
762 | for (var key in obj) if (_.has(obj, key)) keys.push(key);
763 | return keys;
764 | };
765 |
766 | // Retrieve the values of an object's properties.
767 | _.values = function(obj) {
768 | var values = [];
769 | for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
770 | return values;
771 | };
772 |
773 | // Convert an object into a list of `[key, value]` pairs.
774 | _.pairs = function(obj) {
775 | var pairs = [];
776 | for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
777 | return pairs;
778 | };
779 |
780 | // Invert the keys and values of an object. The values must be serializable.
781 | _.invert = function(obj) {
782 | var result = {};
783 | for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
784 | return result;
785 | };
786 |
787 | // Return a sorted list of the function names available on the object.
788 | // Aliased as `methods`
789 | _.functions = _.methods = function(obj) {
790 | var names = [];
791 | for (var key in obj) {
792 | if (_.isFunction(obj[key])) names.push(key);
793 | }
794 | return names.sort();
795 | };
796 |
797 | // Extend a given object with all the properties in passed-in object(s).
798 | _.extend = function(obj) {
799 | each(slice.call(arguments, 1), function(source) {
800 | if (source) {
801 | for (var prop in source) {
802 | obj[prop] = source[prop];
803 | }
804 | }
805 | });
806 | return obj;
807 | };
808 |
809 | // Return a copy of the object only containing the whitelisted properties.
810 | _.pick = function(obj) {
811 | var copy = {};
812 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
813 | each(keys, function(key) {
814 | if (key in obj) copy[key] = obj[key];
815 | });
816 | return copy;
817 | };
818 |
819 | // Return a copy of the object without the blacklisted properties.
820 | _.omit = function(obj) {
821 | var copy = {};
822 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
823 | for (var key in obj) {
824 | if (!_.contains(keys, key)) copy[key] = obj[key];
825 | }
826 | return copy;
827 | };
828 |
829 | // Fill in a given object with default properties.
830 | _.defaults = function(obj) {
831 | each(slice.call(arguments, 1), function(source) {
832 | if (source) {
833 | for (var prop in source) {
834 | if (obj[prop] === void 0) obj[prop] = source[prop];
835 | }
836 | }
837 | });
838 | return obj;
839 | };
840 |
841 | // Create a (shallow-cloned) duplicate of an object.
842 | _.clone = function(obj) {
843 | if (!_.isObject(obj)) return obj;
844 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
845 | };
846 |
847 | // Invokes interceptor with the obj, and then returns obj.
848 | // The primary purpose of this method is to "tap into" a method chain, in
849 | // order to perform operations on intermediate results within the chain.
850 | _.tap = function(obj, interceptor) {
851 | interceptor(obj);
852 | return obj;
853 | };
854 |
855 | // Internal recursive comparison function for `isEqual`.
856 | var eq = function(a, b, aStack, bStack) {
857 | // Identical objects are equal. `0 === -0`, but they aren't identical.
858 | // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
859 | if (a === b) return a !== 0 || 1 / a == 1 / b;
860 | // A strict comparison is necessary because `null == undefined`.
861 | if (a == null || b == null) return a === b;
862 | // Unwrap any wrapped objects.
863 | if (a instanceof _) a = a._wrapped;
864 | if (b instanceof _) b = b._wrapped;
865 | // Compare `[[Class]]` names.
866 | var className = toString.call(a);
867 | if (className != toString.call(b)) return false;
868 | switch (className) {
869 | // Strings, numbers, dates, and booleans are compared by value.
870 | case '[object String]':
871 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
872 | // equivalent to `new String("5")`.
873 | return a == String(b);
874 | case '[object Number]':
875 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
876 | // other numeric values.
877 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
878 | case '[object Date]':
879 | case '[object Boolean]':
880 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their
881 | // millisecond representations. Note that invalid dates with millisecond representations
882 | // of `NaN` are not equivalent.
883 | return +a == +b;
884 | // RegExps are compared by their source patterns and flags.
885 | case '[object RegExp]':
886 | return a.source == b.source &&
887 | a.global == b.global &&
888 | a.multiline == b.multiline &&
889 | a.ignoreCase == b.ignoreCase;
890 | }
891 | if (typeof a != 'object' || typeof b != 'object') return false;
892 | // Assume equality for cyclic structures. The algorithm for detecting cyclic
893 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
894 | var length = aStack.length;
895 | while (length--) {
896 | // Linear search. Performance is inversely proportional to the number of
897 | // unique nested structures.
898 | if (aStack[length] == a) return bStack[length] == b;
899 | }
900 | // Objects with different constructors are not equivalent, but `Object`s
901 | // from different frames are.
902 | var aCtor = a.constructor, bCtor = b.constructor;
903 | if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
904 | _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
905 | return false;
906 | }
907 | // Add the first object to the stack of traversed objects.
908 | aStack.push(a);
909 | bStack.push(b);
910 | var size = 0, result = true;
911 | // Recursively compare objects and arrays.
912 | if (className == '[object Array]') {
913 | // Compare array lengths to determine if a deep comparison is necessary.
914 | size = a.length;
915 | result = size == b.length;
916 | if (result) {
917 | // Deep compare the contents, ignoring non-numeric properties.
918 | while (size--) {
919 | if (!(result = eq(a[size], b[size], aStack, bStack))) break;
920 | }
921 | }
922 | } else {
923 | // Deep compare objects.
924 | for (var key in a) {
925 | if (_.has(a, key)) {
926 | // Count the expected number of properties.
927 | size++;
928 | // Deep compare each member.
929 | if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
930 | }
931 | }
932 | // Ensure that both objects contain the same number of properties.
933 | if (result) {
934 | for (key in b) {
935 | if (_.has(b, key) && !(size--)) break;
936 | }
937 | result = !size;
938 | }
939 | }
940 | // Remove the first object from the stack of traversed objects.
941 | aStack.pop();
942 | bStack.pop();
943 | return result;
944 | };
945 |
946 | // Perform a deep comparison to check if two objects are equal.
947 | _.isEqual = function(a, b) {
948 | return eq(a, b, [], []);
949 | };
950 |
951 | // Is a given array, string, or object empty?
952 | // An "empty" object has no enumerable own-properties.
953 | _.isEmpty = function(obj) {
954 | if (obj == null) return true;
955 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
956 | for (var key in obj) if (_.has(obj, key)) return false;
957 | return true;
958 | };
959 |
960 | // Is a given value a DOM element?
961 | _.isElement = function(obj) {
962 | return !!(obj && obj.nodeType === 1);
963 | };
964 |
965 | // Is a given value an array?
966 | // Delegates to ECMA5's native Array.isArray
967 | _.isArray = nativeIsArray || function(obj) {
968 | return toString.call(obj) == '[object Array]';
969 | };
970 |
971 | // Is a given variable an object?
972 | _.isObject = function(obj) {
973 | return obj === Object(obj);
974 | };
975 |
976 | // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
977 | each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
978 | _['is' + name] = function(obj) {
979 | return toString.call(obj) == '[object ' + name + ']';
980 | };
981 | });
982 |
983 | // Define a fallback version of the method in browsers (ahem, IE), where
984 | // there isn't any inspectable "Arguments" type.
985 | if (!_.isArguments(arguments)) {
986 | _.isArguments = function(obj) {
987 | return !!(obj && _.has(obj, 'callee'));
988 | };
989 | }
990 |
991 | // Optimize `isFunction` if appropriate.
992 | if (typeof (/./) !== 'function') {
993 | _.isFunction = function(obj) {
994 | return typeof obj === 'function';
995 | };
996 | }
997 |
998 | // Is a given object a finite number?
999 | _.isFinite = function(obj) {
1000 | return isFinite(obj) && !isNaN(parseFloat(obj));
1001 | };
1002 |
1003 | // Is the given value `NaN`? (NaN is the only number which does not equal itself).
1004 | _.isNaN = function(obj) {
1005 | return _.isNumber(obj) && obj != +obj;
1006 | };
1007 |
1008 | // Is a given value a boolean?
1009 | _.isBoolean = function(obj) {
1010 | return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
1011 | };
1012 |
1013 | // Is a given value equal to null?
1014 | _.isNull = function(obj) {
1015 | return obj === null;
1016 | };
1017 |
1018 | // Is a given variable undefined?
1019 | _.isUndefined = function(obj) {
1020 | return obj === void 0;
1021 | };
1022 |
1023 | // Shortcut function for checking if an object has a given property directly
1024 | // on itself (in other words, not on a prototype).
1025 | _.has = function(obj, key) {
1026 | return hasOwnProperty.call(obj, key);
1027 | };
1028 |
1029 | // Utility Functions
1030 | // -----------------
1031 |
1032 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
1033 | // previous owner. Returns a reference to the Underscore object.
1034 | _.noConflict = function() {
1035 | root._ = previousUnderscore;
1036 | return this;
1037 | };
1038 |
1039 | // Keep the identity function around for default iterators.
1040 | _.identity = function(value) {
1041 | return value;
1042 | };
1043 |
1044 | // Run a function **n** times.
1045 | _.times = function(n, iterator, context) {
1046 | var accum = Array(Math.max(0, n));
1047 | for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
1048 | return accum;
1049 | };
1050 |
1051 | // Return a random integer between min and max (inclusive).
1052 | _.random = function(min, max) {
1053 | if (max == null) {
1054 | max = min;
1055 | min = 0;
1056 | }
1057 | return min + Math.floor(Math.random() * (max - min + 1));
1058 | };
1059 |
1060 | // List of HTML entities for escaping.
1061 | var entityMap = {
1062 | escape: {
1063 | '&': '&',
1064 | '<': '<',
1065 | '>': '>',
1066 | '"': '"',
1067 | "'": ''',
1068 | '/': '/'
1069 | }
1070 | };
1071 | entityMap.unescape = _.invert(entityMap.escape);
1072 |
1073 | // Regexes containing the keys and values listed immediately above.
1074 | var entityRegexes = {
1075 | escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
1076 | unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
1077 | };
1078 |
1079 | // Functions for escaping and unescaping strings to/from HTML interpolation.
1080 | _.each(['escape', 'unescape'], function(method) {
1081 | _[method] = function(string) {
1082 | if (string == null) return '';
1083 | return ('' + string).replace(entityRegexes[method], function(match) {
1084 | return entityMap[method][match];
1085 | });
1086 | };
1087 | });
1088 |
1089 | // If the value of the named `property` is a function then invoke it with the
1090 | // `object` as context; otherwise, return it.
1091 | _.result = function(object, property) {
1092 | if (object == null) return void 0;
1093 | var value = object[property];
1094 | return _.isFunction(value) ? value.call(object) : value;
1095 | };
1096 |
1097 | // Add your own custom functions to the Underscore object.
1098 | _.mixin = function(obj) {
1099 | each(_.functions(obj), function(name){
1100 | var func = _[name] = obj[name];
1101 | _.prototype[name] = function() {
1102 | var args = [this._wrapped];
1103 | push.apply(args, arguments);
1104 | return result.call(this, func.apply(_, args));
1105 | };
1106 | });
1107 | };
1108 |
1109 | // Generate a unique integer id (unique within the entire client session).
1110 | // Useful for temporary DOM ids.
1111 | var idCounter = 0;
1112 | _.uniqueId = function(prefix) {
1113 | var id = ++idCounter + '';
1114 | return prefix ? prefix + id : id;
1115 | };
1116 |
1117 | // By default, Underscore uses ERB-style template delimiters, change the
1118 | // following template settings to use alternative delimiters.
1119 | _.templateSettings = {
1120 | evaluate : /<%([\s\S]+?)%>/g,
1121 | interpolate : /<%=([\s\S]+?)%>/g,
1122 | escape : /<%-([\s\S]+?)%>/g
1123 | };
1124 |
1125 | // When customizing `templateSettings`, if you don't want to define an
1126 | // interpolation, evaluation or escaping regex, we need one that is
1127 | // guaranteed not to match.
1128 | var noMatch = /(.)^/;
1129 |
1130 | // Certain characters need to be escaped so that they can be put into a
1131 | // string literal.
1132 | var escapes = {
1133 | "'": "'",
1134 | '\\': '\\',
1135 | '\r': 'r',
1136 | '\n': 'n',
1137 | '\t': 't',
1138 | '\u2028': 'u2028',
1139 | '\u2029': 'u2029'
1140 | };
1141 |
1142 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
1143 |
1144 | // JavaScript micro-templating, similar to John Resig's implementation.
1145 | // Underscore templating handles arbitrary delimiters, preserves whitespace,
1146 | // and correctly escapes quotes within interpolated code.
1147 | _.template = function(text, data, settings) {
1148 | var render;
1149 | settings = _.defaults({}, settings, _.templateSettings);
1150 |
1151 | // Combine delimiters into one regular expression via alternation.
1152 | var matcher = new RegExp([
1153 | (settings.escape || noMatch).source,
1154 | (settings.interpolate || noMatch).source,
1155 | (settings.evaluate || noMatch).source
1156 | ].join('|') + '|$', 'g');
1157 |
1158 | // Compile the template source, escaping string literals appropriately.
1159 | var index = 0;
1160 | var source = "__p+='";
1161 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
1162 | source += text.slice(index, offset)
1163 | .replace(escaper, function(match) { return '\\' + escapes[match]; });
1164 |
1165 | if (escape) {
1166 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
1167 | }
1168 | if (interpolate) {
1169 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
1170 | }
1171 | if (evaluate) {
1172 | source += "';\n" + evaluate + "\n__p+='";
1173 | }
1174 | index = offset + match.length;
1175 | return match;
1176 | });
1177 | source += "';\n";
1178 |
1179 | // If a variable is not specified, place data values in local scope.
1180 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
1181 |
1182 | source = "var __t,__p='',__j=Array.prototype.join," +
1183 | "print=function(){__p+=__j.call(arguments,'');};\n" +
1184 | source + "return __p;\n";
1185 |
1186 | try {
1187 | render = new Function(settings.variable || 'obj', '_', source);
1188 | } catch (e) {
1189 | e.source = source;
1190 | throw e;
1191 | }
1192 |
1193 | if (data) return render(data, _);
1194 | var template = function(data) {
1195 | return render.call(this, data, _);
1196 | };
1197 |
1198 | // Provide the compiled function source as a convenience for precompilation.
1199 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
1200 |
1201 | return template;
1202 | };
1203 |
1204 | // Add a "chain" function, which will delegate to the wrapper.
1205 | _.chain = function(obj) {
1206 | return _(obj).chain();
1207 | };
1208 |
1209 | // OOP
1210 | // ---------------
1211 | // If Underscore is called as a function, it returns a wrapped object that
1212 | // can be used OO-style. This wrapper holds altered versions of all the
1213 | // underscore functions. Wrapped objects may be chained.
1214 |
1215 | // Helper function to continue chaining intermediate results.
1216 | var result = function(obj) {
1217 | return this._chain ? _(obj).chain() : obj;
1218 | };
1219 |
1220 | // Add all of the Underscore functions to the wrapper object.
1221 | _.mixin(_);
1222 |
1223 | // Add all mutator Array functions to the wrapper.
1224 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
1225 | var method = ArrayProto[name];
1226 | _.prototype[name] = function() {
1227 | var obj = this._wrapped;
1228 | method.apply(obj, arguments);
1229 | if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
1230 | return result.call(this, obj);
1231 | };
1232 | });
1233 |
1234 | // Add all accessor Array functions to the wrapper.
1235 | each(['concat', 'join', 'slice'], function(name) {
1236 | var method = ArrayProto[name];
1237 | _.prototype[name] = function() {
1238 | return result.call(this, method.apply(this._wrapped, arguments));
1239 | };
1240 | });
1241 |
1242 | _.extend(_.prototype, {
1243 |
1244 | // Start chaining a wrapped Underscore object.
1245 | chain: function() {
1246 | this._chain = true;
1247 | return this;
1248 | },
1249 |
1250 | // Extracts the result from a wrapped and chained object.
1251 | value: function() {
1252 | return this._wrapped;
1253 | }
1254 |
1255 | });
1256 |
1257 | }).call(this);
1258 |
--------------------------------------------------------------------------------
/resources/public/js/lib/typeahead.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * typeahead.js 0.9.3
3 | * https://github.com/twitter/typeahead
4 | * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
5 | */
6 |
7 | (function($) {
8 | var VERSION = "0.9.3";
9 | var utils = {
10 | isMsie: function() {
11 | var match = /(msie) ([\w.]+)/i.exec(navigator.userAgent);
12 | return match ? parseInt(match[2], 10) : false;
13 | },
14 | isBlankString: function(str) {
15 | return !str || /^\s*$/.test(str);
16 | },
17 | escapeRegExChars: function(str) {
18 | return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
19 | },
20 | isString: function(obj) {
21 | return typeof obj === "string";
22 | },
23 | isNumber: function(obj) {
24 | return typeof obj === "number";
25 | },
26 | isArray: $.isArray,
27 | isFunction: $.isFunction,
28 | isObject: $.isPlainObject,
29 | isUndefined: function(obj) {
30 | return typeof obj === "undefined";
31 | },
32 | bind: $.proxy,
33 | bindAll: function(obj) {
34 | var val;
35 | for (var key in obj) {
36 | $.isFunction(val = obj[key]) && (obj[key] = $.proxy(val, obj));
37 | }
38 | },
39 | indexOf: function(haystack, needle) {
40 | for (var i = 0; i < haystack.length; i++) {
41 | if (haystack[i] === needle) {
42 | return i;
43 | }
44 | }
45 | return -1;
46 | },
47 | each: $.each,
48 | map: $.map,
49 | filter: $.grep,
50 | every: function(obj, test) {
51 | var result = true;
52 | if (!obj) {
53 | return result;
54 | }
55 | $.each(obj, function(key, val) {
56 | if (!(result = test.call(null, val, key, obj))) {
57 | return false;
58 | }
59 | });
60 | return !!result;
61 | },
62 | some: function(obj, test) {
63 | var result = false;
64 | if (!obj) {
65 | return result;
66 | }
67 | $.each(obj, function(key, val) {
68 | if (result = test.call(null, val, key, obj)) {
69 | return false;
70 | }
71 | });
72 | return !!result;
73 | },
74 | mixin: $.extend,
75 | getUniqueId: function() {
76 | var counter = 0;
77 | return function() {
78 | return counter++;
79 | };
80 | }(),
81 | defer: function(fn) {
82 | setTimeout(fn, 0);
83 | },
84 | debounce: function(func, wait, immediate) {
85 | var timeout, result;
86 | return function() {
87 | var context = this, args = arguments, later, callNow;
88 | later = function() {
89 | timeout = null;
90 | if (!immediate) {
91 | result = func.apply(context, args);
92 | }
93 | };
94 | callNow = immediate && !timeout;
95 | clearTimeout(timeout);
96 | timeout = setTimeout(later, wait);
97 | if (callNow) {
98 | result = func.apply(context, args);
99 | }
100 | return result;
101 | };
102 | },
103 | throttle: function(func, wait) {
104 | var context, args, timeout, result, previous, later;
105 | previous = 0;
106 | later = function() {
107 | previous = new Date();
108 | timeout = null;
109 | result = func.apply(context, args);
110 | };
111 | return function() {
112 | var now = new Date(), remaining = wait - (now - previous);
113 | context = this;
114 | args = arguments;
115 | if (remaining <= 0) {
116 | clearTimeout(timeout);
117 | timeout = null;
118 | previous = now;
119 | result = func.apply(context, args);
120 | } else if (!timeout) {
121 | timeout = setTimeout(later, remaining);
122 | }
123 | return result;
124 | };
125 | },
126 | tokenizeQuery: function(str) {
127 | return $.trim(str).toLowerCase().split(/[\s]+/);
128 | },
129 | tokenizeText: function(str) {
130 | return $.trim(str).toLowerCase().split(/[\s\-_]+/);
131 | },
132 | getProtocol: function() {
133 | return location.protocol;
134 | },
135 | noop: function() {}
136 | };
137 | var EventTarget = function() {
138 | var eventSplitter = /\s+/;
139 | return {
140 | on: function(events, callback) {
141 | var event;
142 | if (!callback) {
143 | return this;
144 | }
145 | this._callbacks = this._callbacks || {};
146 | events = events.split(eventSplitter);
147 | while (event = events.shift()) {
148 | this._callbacks[event] = this._callbacks[event] || [];
149 | this._callbacks[event].push(callback);
150 | }
151 | return this;
152 | },
153 | trigger: function(events, data) {
154 | var event, callbacks;
155 | if (!this._callbacks) {
156 | return this;
157 | }
158 | events = events.split(eventSplitter);
159 | while (event = events.shift()) {
160 | if (callbacks = this._callbacks[event]) {
161 | for (var i = 0; i < callbacks.length; i += 1) {
162 | callbacks[i].call(this, {
163 | type: event,
164 | data: data
165 | });
166 | }
167 | }
168 | }
169 | return this;
170 | }
171 | };
172 | }();
173 | var EventBus = function() {
174 | var namespace = "typeahead:";
175 | function EventBus(o) {
176 | if (!o || !o.el) {
177 | $.error("EventBus initialized without el");
178 | }
179 | this.$el = $(o.el);
180 | }
181 | utils.mixin(EventBus.prototype, {
182 | trigger: function(type) {
183 | var args = [].slice.call(arguments, 1);
184 | this.$el.trigger(namespace + type, args);
185 | }
186 | });
187 | return EventBus;
188 | }();
189 | var PersistentStorage = function() {
190 | var ls, methods;
191 | try {
192 | ls = window.localStorage;
193 | ls.setItem("~~~", "!");
194 | ls.removeItem("~~~");
195 | } catch (err) {
196 | ls = null;
197 | }
198 | function PersistentStorage(namespace) {
199 | this.prefix = [ "__", namespace, "__" ].join("");
200 | this.ttlKey = "__ttl__";
201 | this.keyMatcher = new RegExp("^" + this.prefix);
202 | }
203 | if (ls && window.JSON) {
204 | methods = {
205 | _prefix: function(key) {
206 | return this.prefix + key;
207 | },
208 | _ttlKey: function(key) {
209 | return this._prefix(key) + this.ttlKey;
210 | },
211 | get: function(key) {
212 | if (this.isExpired(key)) {
213 | this.remove(key);
214 | }
215 | return decode(ls.getItem(this._prefix(key)));
216 | },
217 | set: function(key, val, ttl) {
218 | if (utils.isNumber(ttl)) {
219 | ls.setItem(this._ttlKey(key), encode(now() + ttl));
220 | } else {
221 | ls.removeItem(this._ttlKey(key));
222 | }
223 | return ls.setItem(this._prefix(key), encode(val));
224 | },
225 | remove: function(key) {
226 | ls.removeItem(this._ttlKey(key));
227 | ls.removeItem(this._prefix(key));
228 | return this;
229 | },
230 | clear: function() {
231 | var i, key, keys = [], len = ls.length;
232 | for (i = 0; i < len; i++) {
233 | if ((key = ls.key(i)).match(this.keyMatcher)) {
234 | keys.push(key.replace(this.keyMatcher, ""));
235 | }
236 | }
237 | for (i = keys.length; i--; ) {
238 | this.remove(keys[i]);
239 | }
240 | return this;
241 | },
242 | isExpired: function(key) {
243 | var ttl = decode(ls.getItem(this._ttlKey(key)));
244 | return utils.isNumber(ttl) && now() > ttl ? true : false;
245 | }
246 | };
247 | } else {
248 | methods = {
249 | get: utils.noop,
250 | set: utils.noop,
251 | remove: utils.noop,
252 | clear: utils.noop,
253 | isExpired: utils.noop
254 | };
255 | }
256 | utils.mixin(PersistentStorage.prototype, methods);
257 | return PersistentStorage;
258 | function now() {
259 | return new Date().getTime();
260 | }
261 | function encode(val) {
262 | return JSON.stringify(utils.isUndefined(val) ? null : val);
263 | }
264 | function decode(val) {
265 | return JSON.parse(val);
266 | }
267 | }();
268 | var RequestCache = function() {
269 | function RequestCache(o) {
270 | utils.bindAll(this);
271 | o = o || {};
272 | this.sizeLimit = o.sizeLimit || 10;
273 | this.cache = {};
274 | this.cachedKeysByAge = [];
275 | }
276 | utils.mixin(RequestCache.prototype, {
277 | get: function(url) {
278 | return this.cache[url];
279 | },
280 | set: function(url, resp) {
281 | var requestToEvict;
282 | if (this.cachedKeysByAge.length === this.sizeLimit) {
283 | requestToEvict = this.cachedKeysByAge.shift();
284 | delete this.cache[requestToEvict];
285 | }
286 | this.cache[url] = resp;
287 | this.cachedKeysByAge.push(url);
288 | }
289 | });
290 | return RequestCache;
291 | }();
292 | var Transport = function() {
293 | var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests, requestCache;
294 | function Transport(o) {
295 | utils.bindAll(this);
296 | o = utils.isString(o) ? {
297 | url: o
298 | } : o;
299 | requestCache = requestCache || new RequestCache();
300 | maxPendingRequests = utils.isNumber(o.maxParallelRequests) ? o.maxParallelRequests : maxPendingRequests || 6;
301 | this.url = o.url;
302 | this.wildcard = o.wildcard || "%QUERY";
303 | this.filter = o.filter;
304 | this.replace = o.replace;
305 | this.ajaxSettings = {
306 | type: "get",
307 | cache: o.cache,
308 | timeout: o.timeout,
309 | dataType: o.dataType || "json",
310 | beforeSend: o.beforeSend
311 | };
312 | this._get = (/^throttle$/i.test(o.rateLimitFn) ? utils.throttle : utils.debounce)(this._get, o.rateLimitWait || 300);
313 | }
314 | utils.mixin(Transport.prototype, {
315 | _get: function(url, cb) {
316 | var that = this;
317 | if (belowPendingRequestsThreshold()) {
318 | this._sendRequest(url).done(done);
319 | } else {
320 | this.onDeckRequestArgs = [].slice.call(arguments, 0);
321 | }
322 | function done(resp) {
323 | var data = that.filter ? that.filter(resp) : resp;
324 | cb && cb(data);
325 | requestCache.set(url, resp);
326 | }
327 | },
328 | _sendRequest: function(url) {
329 | var that = this, jqXhr = pendingRequests[url];
330 | if (!jqXhr) {
331 | incrementPendingRequests();
332 | jqXhr = pendingRequests[url] = $.ajax(url, this.ajaxSettings).always(always);
333 | }
334 | return jqXhr;
335 | function always() {
336 | decrementPendingRequests();
337 | pendingRequests[url] = null;
338 | if (that.onDeckRequestArgs) {
339 | that._get.apply(that, that.onDeckRequestArgs);
340 | that.onDeckRequestArgs = null;
341 | }
342 | }
343 | },
344 | get: function(query, cb) {
345 | var that = this, encodedQuery = encodeURIComponent(query || ""), url, resp;
346 | cb = cb || utils.noop;
347 | url = this.replace ? this.replace(this.url, encodedQuery) : this.url.replace(this.wildcard, encodedQuery);
348 | if (resp = requestCache.get(url)) {
349 | utils.defer(function() {
350 | cb(that.filter ? that.filter(resp) : resp);
351 | });
352 | } else {
353 | this._get(url, cb);
354 | }
355 | return !!resp;
356 | }
357 | });
358 | return Transport;
359 | function incrementPendingRequests() {
360 | pendingRequestsCount++;
361 | }
362 | function decrementPendingRequests() {
363 | pendingRequestsCount--;
364 | }
365 | function belowPendingRequestsThreshold() {
366 | return pendingRequestsCount < maxPendingRequests;
367 | }
368 | }();
369 | var Dataset = function() {
370 | var keys = {
371 | thumbprint: "thumbprint",
372 | protocol: "protocol",
373 | itemHash: "itemHash",
374 | adjacencyList: "adjacencyList"
375 | };
376 | function Dataset(o) {
377 | utils.bindAll(this);
378 | if (utils.isString(o.template) && !o.engine) {
379 | $.error("no template engine specified");
380 | }
381 | if (!o.local && !o.prefetch && !o.remote) {
382 | $.error("one of local, prefetch, or remote is required");
383 | }
384 | this.name = o.name || utils.getUniqueId();
385 | this.limit = o.limit || 5;
386 | this.minLength = o.minLength || 1;
387 | this.header = o.header;
388 | this.footer = o.footer;
389 | this.valueKey = o.valueKey || "value";
390 | this.template = compileTemplate(o.template, o.engine, this.valueKey);
391 | this.local = o.local;
392 | this.prefetch = o.prefetch;
393 | this.remote = o.remote;
394 | this.itemHash = {};
395 | this.adjacencyList = {};
396 | this.storage = o.name ? new PersistentStorage(o.name) : null;
397 | }
398 | utils.mixin(Dataset.prototype, {
399 | _processLocalData: function(data) {
400 | this._mergeProcessedData(this._processData(data));
401 | },
402 | _loadPrefetchData: function(o) {
403 | var that = this, thumbprint = VERSION + (o.thumbprint || ""), storedThumbprint, storedProtocol, storedItemHash, storedAdjacencyList, isExpired, deferred;
404 | if (this.storage) {
405 | storedThumbprint = this.storage.get(keys.thumbprint);
406 | storedProtocol = this.storage.get(keys.protocol);
407 | storedItemHash = this.storage.get(keys.itemHash);
408 | storedAdjacencyList = this.storage.get(keys.adjacencyList);
409 | }
410 | isExpired = storedThumbprint !== thumbprint || storedProtocol !== utils.getProtocol();
411 | o = utils.isString(o) ? {
412 | url: o
413 | } : o;
414 | o.ttl = utils.isNumber(o.ttl) ? o.ttl : 24 * 60 * 60 * 1e3;
415 | if (storedItemHash && storedAdjacencyList && !isExpired) {
416 | this._mergeProcessedData({
417 | itemHash: storedItemHash,
418 | adjacencyList: storedAdjacencyList
419 | });
420 | deferred = $.Deferred().resolve();
421 | } else {
422 | deferred = $.getJSON(o.url).done(processPrefetchData);
423 | }
424 | return deferred;
425 | function processPrefetchData(data) {
426 | var filteredData = o.filter ? o.filter(data) : data, processedData = that._processData(filteredData), itemHash = processedData.itemHash, adjacencyList = processedData.adjacencyList;
427 | if (that.storage) {
428 | that.storage.set(keys.itemHash, itemHash, o.ttl);
429 | that.storage.set(keys.adjacencyList, adjacencyList, o.ttl);
430 | that.storage.set(keys.thumbprint, thumbprint, o.ttl);
431 | that.storage.set(keys.protocol, utils.getProtocol(), o.ttl);
432 | }
433 | that._mergeProcessedData(processedData);
434 | }
435 | },
436 | _transformDatum: function(datum) {
437 | var value = utils.isString(datum) ? datum : datum[this.valueKey], tokens = datum.tokens || utils.tokenizeText(value), item = {
438 | value: value,
439 | tokens: tokens
440 | };
441 | if (utils.isString(datum)) {
442 | item.datum = {};
443 | item.datum[this.valueKey] = datum;
444 | } else {
445 | item.datum = datum;
446 | }
447 | item.tokens = utils.filter(item.tokens, function(token) {
448 | return !utils.isBlankString(token);
449 | });
450 | item.tokens = utils.map(item.tokens, function(token) {
451 | return token.toLowerCase();
452 | });
453 | return item;
454 | },
455 | _processData: function(data) {
456 | var that = this, itemHash = {}, adjacencyList = {};
457 | utils.each(data, function(i, datum) {
458 | var item = that._transformDatum(datum), id = utils.getUniqueId(item.value);
459 | itemHash[id] = item;
460 | utils.each(item.tokens, function(i, token) {
461 | var character = token.charAt(0), adjacency = adjacencyList[character] || (adjacencyList[character] = [ id ]);
462 | !~utils.indexOf(adjacency, id) && adjacency.push(id);
463 | });
464 | });
465 | return {
466 | itemHash: itemHash,
467 | adjacencyList: adjacencyList
468 | };
469 | },
470 | _mergeProcessedData: function(processedData) {
471 | var that = this;
472 | utils.mixin(this.itemHash, processedData.itemHash);
473 | utils.each(processedData.adjacencyList, function(character, adjacency) {
474 | var masterAdjacency = that.adjacencyList[character];
475 | that.adjacencyList[character] = masterAdjacency ? masterAdjacency.concat(adjacency) : adjacency;
476 | });
477 | },
478 | _getLocalSuggestions: function(terms) {
479 | var that = this, firstChars = [], lists = [], shortestList, suggestions = [];
480 | utils.each(terms, function(i, term) {
481 | var firstChar = term.charAt(0);
482 | !~utils.indexOf(firstChars, firstChar) && firstChars.push(firstChar);
483 | });
484 | utils.each(firstChars, function(i, firstChar) {
485 | var list = that.adjacencyList[firstChar];
486 | if (!list) {
487 | return false;
488 | }
489 | lists.push(list);
490 | if (!shortestList || list.length < shortestList.length) {
491 | shortestList = list;
492 | }
493 | });
494 | if (lists.length < firstChars.length) {
495 | return [];
496 | }
497 | utils.each(shortestList, function(i, id) {
498 | var item = that.itemHash[id], isCandidate, isMatch;
499 | isCandidate = utils.every(lists, function(list) {
500 | return ~utils.indexOf(list, id);
501 | });
502 | isMatch = isCandidate && utils.every(terms, function(term) {
503 | return utils.some(item.tokens, function(token) {
504 | return token.indexOf(term) === 0;
505 | });
506 | });
507 | isMatch && suggestions.push(item);
508 | });
509 | return suggestions;
510 | },
511 | initialize: function() {
512 | var deferred;
513 | this.local && this._processLocalData(this.local);
514 | this.transport = this.remote ? new Transport(this.remote) : null;
515 | deferred = this.prefetch ? this._loadPrefetchData(this.prefetch) : $.Deferred().resolve();
516 | this.local = this.prefetch = this.remote = null;
517 | this.initialize = function() {
518 | return deferred;
519 | };
520 | return deferred;
521 | },
522 | getSuggestions: function(query, cb) {
523 | var that = this, terms, suggestions, cacheHit = false;
524 | if (query.length < this.minLength) {
525 | return;
526 | }
527 | terms = utils.tokenizeQuery(query);
528 | suggestions = this._getLocalSuggestions(terms).slice(0, this.limit);
529 | if (suggestions.length < this.limit && this.transport) {
530 | cacheHit = this.transport.get(query, processRemoteData);
531 | }
532 | !cacheHit && cb && cb(suggestions);
533 | function processRemoteData(data) {
534 | suggestions = suggestions.slice(0);
535 | utils.each(data, function(i, datum) {
536 | var item = that._transformDatum(datum), isDuplicate;
537 | isDuplicate = utils.some(suggestions, function(suggestion) {
538 | return item.value === suggestion.value;
539 | });
540 | !isDuplicate && suggestions.push(item);
541 | return suggestions.length < that.limit;
542 | });
543 | cb && cb(suggestions);
544 | }
545 | }
546 | });
547 | return Dataset;
548 | function compileTemplate(template, engine, valueKey) {
549 | var renderFn, compiledTemplate;
550 | if (utils.isFunction(template)) {
551 | renderFn = template;
552 | } else if (utils.isString(template)) {
553 | compiledTemplate = engine.compile(template);
554 | renderFn = utils.bind(compiledTemplate.render, compiledTemplate);
555 | } else {
556 | renderFn = function(context) {
557 | return "" + context[valueKey] + "
";
558 | };
559 | }
560 | return renderFn;
561 | }
562 | }();
563 | var InputView = function() {
564 | function InputView(o) {
565 | var that = this;
566 | utils.bindAll(this);
567 | this.specialKeyCodeMap = {
568 | 9: "tab",
569 | 27: "esc",
570 | 37: "left",
571 | 39: "right",
572 | 13: "enter",
573 | 38: "up",
574 | 40: "down"
575 | };
576 | this.$hint = $(o.hint);
577 | this.$input = $(o.input).on("blur.tt", this._handleBlur).on("focus.tt", this._handleFocus).on("keydown.tt", this._handleSpecialKeyEvent);
578 | if (!utils.isMsie()) {
579 | this.$input.on("input.tt", this._compareQueryToInputValue);
580 | } else {
581 | this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) {
582 | if (that.specialKeyCodeMap[$e.which || $e.keyCode]) {
583 | return;
584 | }
585 | utils.defer(that._compareQueryToInputValue);
586 | });
587 | }
588 | this.query = this.$input.val();
589 | this.$overflowHelper = buildOverflowHelper(this.$input);
590 | }
591 | utils.mixin(InputView.prototype, EventTarget, {
592 | _handleFocus: function() {
593 | this.trigger("focused");
594 | },
595 | _handleBlur: function() {
596 | this.trigger("blured");
597 | },
598 | _handleSpecialKeyEvent: function($e) {
599 | var keyName = this.specialKeyCodeMap[$e.which || $e.keyCode];
600 | keyName && this.trigger(keyName + "Keyed", $e);
601 | },
602 | _compareQueryToInputValue: function() {
603 | var inputValue = this.getInputValue(), isSameQuery = compareQueries(this.query, inputValue), isSameQueryExceptWhitespace = isSameQuery ? this.query.length !== inputValue.length : false;
604 | if (isSameQueryExceptWhitespace) {
605 | this.trigger("whitespaceChanged", {
606 | value: this.query
607 | });
608 | } else if (!isSameQuery) {
609 | this.trigger("queryChanged", {
610 | value: this.query = inputValue
611 | });
612 | }
613 | },
614 | destroy: function() {
615 | this.$hint.off(".tt");
616 | this.$input.off(".tt");
617 | this.$hint = this.$input = this.$overflowHelper = null;
618 | },
619 | focus: function() {
620 | this.$input.focus();
621 | },
622 | blur: function() {
623 | this.$input.blur();
624 | },
625 | getQuery: function() {
626 | return this.query;
627 | },
628 | setQuery: function(query) {
629 | this.query = query;
630 | },
631 | getInputValue: function() {
632 | return this.$input.val();
633 | },
634 | setInputValue: function(value, silent) {
635 | this.$input.val(value);
636 | !silent && this._compareQueryToInputValue();
637 | },
638 | getHintValue: function() {
639 | return this.$hint.val();
640 | },
641 | setHintValue: function(value) {
642 | this.$hint.val(value);
643 | },
644 | getLanguageDirection: function() {
645 | return (this.$input.css("direction") || "ltr").toLowerCase();
646 | },
647 | isOverflow: function() {
648 | this.$overflowHelper.text(this.getInputValue());
649 | return this.$overflowHelper.width() > this.$input.width();
650 | },
651 | isCursorAtEnd: function() {
652 | var valueLength = this.$input.val().length, selectionStart = this.$input[0].selectionStart, range;
653 | if (utils.isNumber(selectionStart)) {
654 | return selectionStart === valueLength;
655 | } else if (document.selection) {
656 | range = document.selection.createRange();
657 | range.moveStart("character", -valueLength);
658 | return valueLength === range.text.length;
659 | }
660 | return true;
661 | }
662 | });
663 | return InputView;
664 | function buildOverflowHelper($input) {
665 | return $("").css({
666 | position: "absolute",
667 | left: "-9999px",
668 | visibility: "hidden",
669 | whiteSpace: "nowrap",
670 | fontFamily: $input.css("font-family"),
671 | fontSize: $input.css("font-size"),
672 | fontStyle: $input.css("font-style"),
673 | fontVariant: $input.css("font-variant"),
674 | fontWeight: $input.css("font-weight"),
675 | wordSpacing: $input.css("word-spacing"),
676 | letterSpacing: $input.css("letter-spacing"),
677 | textIndent: $input.css("text-indent"),
678 | textRendering: $input.css("text-rendering"),
679 | textTransform: $input.css("text-transform")
680 | }).insertAfter($input);
681 | }
682 | function compareQueries(a, b) {
683 | a = (a || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
684 | b = (b || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
685 | return a === b;
686 | }
687 | }();
688 | var DropdownView = function() {
689 | var html = {
690 | suggestionsList: ''
691 | }, css = {
692 | suggestionsList: {
693 | display: "block"
694 | },
695 | suggestion: {
696 | whiteSpace: "nowrap",
697 | cursor: "pointer"
698 | },
699 | suggestionChild: {
700 | whiteSpace: "normal"
701 | }
702 | };
703 | function DropdownView(o) {
704 | utils.bindAll(this);
705 | this.isOpen = false;
706 | this.isEmpty = true;
707 | this.isMouseOverDropdown = false;
708 | this.$menu = $(o.menu).on("mouseenter.tt", this._handleMouseenter).on("mouseleave.tt", this._handleMouseleave).on("click.tt", ".tt-suggestion", this._handleSelection).on("mouseover.tt", ".tt-suggestion", this._handleMouseover);
709 | }
710 | utils.mixin(DropdownView.prototype, EventTarget, {
711 | _handleMouseenter: function() {
712 | this.isMouseOverDropdown = true;
713 | },
714 | _handleMouseleave: function() {
715 | this.isMouseOverDropdown = false;
716 | },
717 | _handleMouseover: function($e) {
718 | var $suggestion = $($e.currentTarget);
719 | this._getSuggestions().removeClass("tt-is-under-cursor");
720 | $suggestion.addClass("tt-is-under-cursor");
721 | },
722 | _handleSelection: function($e) {
723 | var $suggestion = $($e.currentTarget);
724 | this.trigger("suggestionSelected", extractSuggestion($suggestion));
725 | },
726 | _show: function() {
727 | this.$menu.css("display", "block");
728 | },
729 | _hide: function() {
730 | this.$menu.hide();
731 | },
732 | _moveCursor: function(increment) {
733 | var $suggestions, $cur, nextIndex, $underCursor;
734 | if (!this.isVisible()) {
735 | return;
736 | }
737 | $suggestions = this._getSuggestions();
738 | $cur = $suggestions.filter(".tt-is-under-cursor");
739 | $cur.removeClass("tt-is-under-cursor");
740 | nextIndex = $suggestions.index($cur) + increment;
741 | nextIndex = (nextIndex + 1) % ($suggestions.length + 1) - 1;
742 | if (nextIndex === -1) {
743 | this.trigger("cursorRemoved");
744 | return;
745 | } else if (nextIndex < -1) {
746 | nextIndex = $suggestions.length - 1;
747 | }
748 | $underCursor = $suggestions.eq(nextIndex).addClass("tt-is-under-cursor");
749 | this._ensureVisibility($underCursor);
750 | this.trigger("cursorMoved", extractSuggestion($underCursor));
751 | },
752 | _getSuggestions: function() {
753 | return this.$menu.find(".tt-suggestions > .tt-suggestion");
754 | },
755 | _ensureVisibility: function($el) {
756 | var menuHeight = this.$menu.height() + parseInt(this.$menu.css("paddingTop"), 10) + parseInt(this.$menu.css("paddingBottom"), 10), menuScrollTop = this.$menu.scrollTop(), elTop = $el.position().top, elBottom = elTop + $el.outerHeight(true);
757 | if (elTop < 0) {
758 | this.$menu.scrollTop(menuScrollTop + elTop);
759 | } else if (menuHeight < elBottom) {
760 | this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight));
761 | }
762 | },
763 | destroy: function() {
764 | this.$menu.off(".tt");
765 | this.$menu = null;
766 | },
767 | isVisible: function() {
768 | return this.isOpen && !this.isEmpty;
769 | },
770 | closeUnlessMouseIsOverDropdown: function() {
771 | if (!this.isMouseOverDropdown) {
772 | this.close();
773 | }
774 | },
775 | close: function() {
776 | if (this.isOpen) {
777 | this.isOpen = false;
778 | this.isMouseOverDropdown = false;
779 | this._hide();
780 | this.$menu.find(".tt-suggestions > .tt-suggestion").removeClass("tt-is-under-cursor");
781 | this.trigger("closed");
782 | }
783 | },
784 | open: function() {
785 | if (!this.isOpen) {
786 | this.isOpen = true;
787 | !this.isEmpty && this._show();
788 | this.trigger("opened");
789 | }
790 | },
791 | setLanguageDirection: function(dir) {
792 | var ltrCss = {
793 | left: "0",
794 | right: "auto"
795 | }, rtlCss = {
796 | left: "auto",
797 | right: " 0"
798 | };
799 | dir === "ltr" ? this.$menu.css(ltrCss) : this.$menu.css(rtlCss);
800 | },
801 | moveCursorUp: function() {
802 | this._moveCursor(-1);
803 | },
804 | moveCursorDown: function() {
805 | this._moveCursor(+1);
806 | },
807 | getSuggestionUnderCursor: function() {
808 | var $suggestion = this._getSuggestions().filter(".tt-is-under-cursor").first();
809 | return $suggestion.length > 0 ? extractSuggestion($suggestion) : null;
810 | },
811 | getFirstSuggestion: function() {
812 | var $suggestion = this._getSuggestions().first();
813 | return $suggestion.length > 0 ? extractSuggestion($suggestion) : null;
814 | },
815 | renderSuggestions: function(dataset, suggestions) {
816 | var datasetClassName = "tt-dataset-" + dataset.name, wrapper = '%body
', compiledHtml, $suggestionsList, $dataset = this.$menu.find("." + datasetClassName), elBuilder, fragment, $el;
817 | if ($dataset.length === 0) {
818 | $suggestionsList = $(html.suggestionsList).css(css.suggestionsList);
819 | $dataset = $("").addClass(datasetClassName).append(dataset.header).append($suggestionsList).append(dataset.footer).appendTo(this.$menu);
820 | }
821 | if (suggestions.length > 0) {
822 | this.isEmpty = false;
823 | this.isOpen && this._show();
824 | elBuilder = document.createElement("div");
825 | fragment = document.createDocumentFragment();
826 | utils.each(suggestions, function(i, suggestion) {
827 | suggestion.dataset = dataset.name;
828 | compiledHtml = dataset.template(suggestion.datum);
829 | elBuilder.innerHTML = wrapper.replace("%body", compiledHtml);
830 | $el = $(elBuilder.firstChild).css(css.suggestion).data("suggestion", suggestion);
831 | $el.children().each(function() {
832 | $(this).css(css.suggestionChild);
833 | });
834 | fragment.appendChild($el[0]);
835 | });
836 | $dataset.show().find(".tt-suggestions").html(fragment);
837 | } else {
838 | this.clearSuggestions(dataset.name);
839 | }
840 | this.trigger("suggestionsRendered");
841 | },
842 | clearSuggestions: function(datasetName) {
843 | var $datasets = datasetName ? this.$menu.find(".tt-dataset-" + datasetName) : this.$menu.find('[class^="tt-dataset-"]'), $suggestions = $datasets.find(".tt-suggestions");
844 | $datasets.hide();
845 | $suggestions.empty();
846 | if (this._getSuggestions().length === 0) {
847 | this.isEmpty = true;
848 | this._hide();
849 | }
850 | }
851 | });
852 | return DropdownView;
853 | function extractSuggestion($el) {
854 | return $el.data("suggestion");
855 | }
856 | }();
857 | var TypeaheadView = function() {
858 | var html = {
859 | wrapper: '',
860 | hint: '',
861 | dropdown: ''
862 | }, css = {
863 | wrapper: {
864 | position: "relative",
865 | display: "inline-block"
866 | },
867 | hint: {
868 | position: "absolute",
869 | top: "0",
870 | left: "0",
871 | borderColor: "transparent",
872 | boxShadow: "none"
873 | },
874 | query: {
875 | position: "relative",
876 | verticalAlign: "top",
877 | backgroundColor: "transparent"
878 | },
879 | dropdown: {
880 | position: "absolute",
881 | top: "100%",
882 | left: "0",
883 | zIndex: "100",
884 | display: "none"
885 | }
886 | };
887 | if (utils.isMsie()) {
888 | utils.mixin(css.query, {
889 | backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
890 | });
891 | }
892 | if (utils.isMsie() && utils.isMsie() <= 7) {
893 | utils.mixin(css.wrapper, {
894 | display: "inline",
895 | zoom: "1"
896 | });
897 | utils.mixin(css.query, {
898 | marginTop: "-1px"
899 | });
900 | }
901 | function TypeaheadView(o) {
902 | var $menu, $input, $hint;
903 | utils.bindAll(this);
904 | this.$node = buildDomStructure(o.input);
905 | this.datasets = o.datasets;
906 | this.dir = null;
907 | this.eventBus = o.eventBus;
908 | $menu = this.$node.find(".tt-dropdown-menu");
909 | $input = this.$node.find(".tt-query");
910 | $hint = this.$node.find(".tt-hint");
911 | this.dropdownView = new DropdownView({
912 | menu: $menu
913 | }).on("suggestionSelected", this._handleSelection).on("cursorMoved", this._clearHint).on("cursorMoved", this._setInputValueToSuggestionUnderCursor).on("cursorRemoved", this._setInputValueToQuery).on("cursorRemoved", this._updateHint).on("suggestionsRendered", this._updateHint).on("opened", this._updateHint).on("closed", this._clearHint).on("opened closed", this._propagateEvent);
914 | this.inputView = new InputView({
915 | input: $input,
916 | hint: $hint
917 | }).on("focused", this._openDropdown).on("blured", this._closeDropdown).on("blured", this._setInputValueToQuery).on("enterKeyed tabKeyed", this._handleSelection).on("queryChanged", this._clearHint).on("queryChanged", this._clearSuggestions).on("queryChanged", this._getSuggestions).on("whitespaceChanged", this._updateHint).on("queryChanged whitespaceChanged", this._openDropdown).on("queryChanged whitespaceChanged", this._setLanguageDirection).on("escKeyed", this._closeDropdown).on("escKeyed", this._setInputValueToQuery).on("tabKeyed upKeyed downKeyed", this._managePreventDefault).on("upKeyed downKeyed", this._moveDropdownCursor).on("upKeyed downKeyed", this._openDropdown).on("tabKeyed leftKeyed rightKeyed", this._autocomplete);
918 | }
919 | utils.mixin(TypeaheadView.prototype, EventTarget, {
920 | _managePreventDefault: function(e) {
921 | var $e = e.data, hint, inputValue, preventDefault = false;
922 | switch (e.type) {
923 | case "tabKeyed":
924 | hint = this.inputView.getHintValue();
925 | inputValue = this.inputView.getInputValue();
926 | preventDefault = hint && hint !== inputValue;
927 | break;
928 |
929 | case "upKeyed":
930 | case "downKeyed":
931 | preventDefault = !$e.shiftKey && !$e.ctrlKey && !$e.metaKey;
932 | break;
933 | }
934 | preventDefault && $e.preventDefault();
935 | },
936 | _setLanguageDirection: function() {
937 | var dir = this.inputView.getLanguageDirection();
938 | if (dir !== this.dir) {
939 | this.dir = dir;
940 | this.$node.css("direction", dir);
941 | this.dropdownView.setLanguageDirection(dir);
942 | }
943 | },
944 | _updateHint: function() {
945 | var suggestion = this.dropdownView.getFirstSuggestion(), hint = suggestion ? suggestion.value : null, dropdownIsVisible = this.dropdownView.isVisible(), inputHasOverflow = this.inputView.isOverflow(), inputValue, query, escapedQuery, beginsWithQuery, match;
946 | if (hint && dropdownIsVisible && !inputHasOverflow) {
947 | inputValue = this.inputView.getInputValue();
948 | query = inputValue.replace(/\s{2,}/g, " ").replace(/^\s+/g, "");
949 | escapedQuery = utils.escapeRegExChars(query);
950 | beginsWithQuery = new RegExp("^(?:" + escapedQuery + ")(.*$)", "i");
951 | match = beginsWithQuery.exec(hint);
952 | this.inputView.setHintValue(inputValue + (match ? match[1] : ""));
953 | }
954 | },
955 | _clearHint: function() {
956 | this.inputView.setHintValue("");
957 | },
958 | _clearSuggestions: function() {
959 | this.dropdownView.clearSuggestions();
960 | },
961 | _setInputValueToQuery: function() {
962 | this.inputView.setInputValue(this.inputView.getQuery());
963 | },
964 | _setInputValueToSuggestionUnderCursor: function(e) {
965 | var suggestion = e.data;
966 | this.inputView.setInputValue(suggestion.value, true);
967 | },
968 | _openDropdown: function() {
969 | this.dropdownView.open();
970 | },
971 | _closeDropdown: function(e) {
972 | this.dropdownView[e.type === "blured" ? "closeUnlessMouseIsOverDropdown" : "close"]();
973 | },
974 | _moveDropdownCursor: function(e) {
975 | var $e = e.data;
976 | if (!$e.shiftKey && !$e.ctrlKey && !$e.metaKey) {
977 | this.dropdownView[e.type === "upKeyed" ? "moveCursorUp" : "moveCursorDown"]();
978 | }
979 | },
980 | _handleSelection: function(e) {
981 | var byClick = e.type === "suggestionSelected", suggestion = byClick ? e.data : this.dropdownView.getSuggestionUnderCursor();
982 | if (suggestion) {
983 | this.inputView.setInputValue(suggestion.value);
984 | byClick ? this.inputView.focus() : e.data.preventDefault();
985 | byClick && utils.isMsie() ? utils.defer(this.dropdownView.close) : this.dropdownView.close();
986 | this.eventBus.trigger("selected", suggestion.datum, suggestion.dataset);
987 | }
988 | },
989 | _getSuggestions: function() {
990 | var that = this, query = this.inputView.getQuery();
991 | if (utils.isBlankString(query)) {
992 | return;
993 | }
994 | utils.each(this.datasets, function(i, dataset) {
995 | dataset.getSuggestions(query, function(suggestions) {
996 | if (query === that.inputView.getQuery()) {
997 | that.dropdownView.renderSuggestions(dataset, suggestions);
998 | }
999 | });
1000 | });
1001 | },
1002 | _autocomplete: function(e) {
1003 | var isCursorAtEnd, ignoreEvent, query, hint, suggestion;
1004 | if (e.type === "rightKeyed" || e.type === "leftKeyed") {
1005 | isCursorAtEnd = this.inputView.isCursorAtEnd();
1006 | ignoreEvent = this.inputView.getLanguageDirection() === "ltr" ? e.type === "leftKeyed" : e.type === "rightKeyed";
1007 | if (!isCursorAtEnd || ignoreEvent) {
1008 | return;
1009 | }
1010 | }
1011 | query = this.inputView.getQuery();
1012 | hint = this.inputView.getHintValue();
1013 | if (hint !== "" && query !== hint) {
1014 | suggestion = this.dropdownView.getFirstSuggestion();
1015 | this.inputView.setInputValue(suggestion.value);
1016 | this.eventBus.trigger("autocompleted", suggestion.datum, suggestion.dataset);
1017 | }
1018 | },
1019 | _propagateEvent: function(e) {
1020 | this.eventBus.trigger(e.type);
1021 | },
1022 | destroy: function() {
1023 | this.inputView.destroy();
1024 | this.dropdownView.destroy();
1025 | destroyDomStructure(this.$node);
1026 | this.$node = null;
1027 | },
1028 | setQuery: function(query) {
1029 | this.inputView.setQuery(query);
1030 | this.inputView.setInputValue(query);
1031 | this._clearHint();
1032 | this._clearSuggestions();
1033 | this._getSuggestions();
1034 | }
1035 | });
1036 | return TypeaheadView;
1037 | function buildDomStructure(input) {
1038 | var $wrapper = $(html.wrapper), $dropdown = $(html.dropdown), $input = $(input), $hint = $(html.hint);
1039 | $wrapper = $wrapper.css(css.wrapper);
1040 | $dropdown = $dropdown.css(css.dropdown);
1041 | $hint.css(css.hint).css({
1042 | backgroundAttachment: $input.css("background-attachment"),
1043 | backgroundClip: $input.css("background-clip"),
1044 | backgroundColor: $input.css("background-color"),
1045 | backgroundImage: $input.css("background-image"),
1046 | backgroundOrigin: $input.css("background-origin"),
1047 | backgroundPosition: $input.css("background-position"),
1048 | backgroundRepeat: $input.css("background-repeat"),
1049 | backgroundSize: $input.css("background-size")
1050 | });
1051 | $input.data("ttAttrs", {
1052 | dir: $input.attr("dir"),
1053 | autocomplete: $input.attr("autocomplete"),
1054 | spellcheck: $input.attr("spellcheck"),
1055 | style: $input.attr("style")
1056 | });
1057 | $input.addClass("tt-query").attr({
1058 | autocomplete: "off",
1059 | spellcheck: false
1060 | }).css(css.query);
1061 | try {
1062 | !$input.attr("dir") && $input.attr("dir", "auto");
1063 | } catch (e) {}
1064 | return $input.wrap($wrapper).parent().prepend($hint).append($dropdown);
1065 | }
1066 | function destroyDomStructure($node) {
1067 | var $input = $node.find(".tt-query");
1068 | utils.each($input.data("ttAttrs"), function(key, val) {
1069 | utils.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
1070 | });
1071 | $input.detach().removeData("ttAttrs").removeClass("tt-query").insertAfter($node);
1072 | $node.remove();
1073 | }
1074 | }();
1075 | (function() {
1076 | var cache = {}, viewKey = "ttView", methods;
1077 | methods = {
1078 | initialize: function(datasetDefs) {
1079 | var datasets;
1080 | datasetDefs = utils.isArray(datasetDefs) ? datasetDefs : [ datasetDefs ];
1081 | if (datasetDefs.length === 0) {
1082 | $.error("no datasets provided");
1083 | }
1084 | datasets = utils.map(datasetDefs, function(o) {
1085 | var dataset = cache[o.name] ? cache[o.name] : new Dataset(o);
1086 | if (o.name) {
1087 | cache[o.name] = dataset;
1088 | }
1089 | return dataset;
1090 | });
1091 | return this.each(initialize);
1092 | function initialize() {
1093 | var $input = $(this), deferreds, eventBus = new EventBus({
1094 | el: $input
1095 | });
1096 | deferreds = utils.map(datasets, function(dataset) {
1097 | return dataset.initialize();
1098 | });
1099 | $input.data(viewKey, new TypeaheadView({
1100 | input: $input,
1101 | eventBus: eventBus = new EventBus({
1102 | el: $input
1103 | }),
1104 | datasets: datasets
1105 | }));
1106 | $.when.apply($, deferreds).always(function() {
1107 | utils.defer(function() {
1108 | eventBus.trigger("initialized");
1109 | });
1110 | });
1111 | }
1112 | },
1113 | destroy: function() {
1114 | return this.each(destroy);
1115 | function destroy() {
1116 | var $this = $(this), view = $this.data(viewKey);
1117 | if (view) {
1118 | view.destroy();
1119 | $this.removeData(viewKey);
1120 | }
1121 | }
1122 | },
1123 | setQuery: function(query) {
1124 | return this.each(setQuery);
1125 | function setQuery() {
1126 | var view = $(this).data(viewKey);
1127 | view && view.setQuery(query);
1128 | }
1129 | }
1130 | };
1131 | jQuery.fn.typeahead = function(method) {
1132 | if (methods[method]) {
1133 | return methods[method].apply(this, [].slice.call(arguments, 1));
1134 | } else {
1135 | return methods.initialize.apply(this, arguments);
1136 | }
1137 | };
1138 | })();
1139 | })(window.jQuery);
--------------------------------------------------------------------------------