· 8 years ago · Jan 24, 2017, 04:28 PM
1// Underscore.js 1.4.4
2// ===================
3
4// > http://underscorejs.org
5// > (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
6// > Underscore may be freely distributed under the MIT license.
7
8// Baseline setup
9// --------------
10(function() {
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 push = ArrayProto.push,
26 slice = ArrayProto.slice,
27 concat = ArrayProto.concat,
28 toString = ObjProto.toString,
29 hasOwnProperty = ObjProto.hasOwnProperty;
30
31 // All **ECMAScript 5** native function implementations that we hope to use
32 // are declared here.
33 var
34 nativeForEach = ArrayProto.forEach,
35 nativeMap = ArrayProto.map,
36 nativeReduce = ArrayProto.reduce,
37 nativeReduceRight = ArrayProto.reduceRight,
38 nativeFilter = ArrayProto.filter,
39 nativeEvery = ArrayProto.every,
40 nativeSome = ArrayProto.some,
41 nativeIndexOf = ArrayProto.indexOf,
42 nativeLastIndexOf = ArrayProto.lastIndexOf,
43 nativeIsArray = Array.isArray,
44 nativeKeys = Object.keys,
45 nativeBind = FuncProto.bind;
46
47 // Create a safe reference to the Underscore object for use below.
48 var _ = function(obj) {
49 if (obj instanceof _) return obj;
50 if (!(this instanceof _)) return new _(obj);
51 this._wrapped = obj;
52 };
53
54 // Export the Underscore object for **Node.js**, with
55 // backwards-compatibility for the old `require()` API. If we're in
56 // the browser, add `_` as a global object via a string identifier,
57 // for Closure Compiler "advanced" mode.
58 if (typeof exports !== 'undefined') {
59 if (typeof module !== 'undefined' && module.exports) {
60 exports = module.exports = _;
61 }
62 exports._ = _;
63 } else {
64 root._ = _;
65 }
66
67 // Current version.
68 _.VERSION = '1.4.4';
69
70 // Collection Functions
71 // --------------------
72
73 // The cornerstone, an `each` implementation, aka `forEach`.
74 // Handles objects with the built-in `forEach`, arrays, and raw objects.
75 // Delegates to **ECMAScript 5**'s native `forEach` if available.
76 var each = _.each = _.forEach = function(obj, iterator, context) {
77 if (obj == null) return;
78 if (nativeForEach && obj.forEach === nativeForEach) {
79 obj.forEach(iterator, context);
80 } else if (obj.length === +obj.length) {
81 for (var i = 0, l = obj.length; i < l; i++) {
82 if (iterator.call(context, obj[i], i, obj) === breaker) return;
83 }
84 } else {
85 for (var key in obj) {
86 if (_.has(obj, key)) {
87 if (iterator.call(context, obj[key], key, obj) === breaker) return;
88 }
89 }
90 }
91 };
92
93 // Return the results of applying the iterator to each element.
94 // Delegates to **ECMAScript 5**'s native `map` if available.
95 _.map = _.collect = function(obj, iterator, context) {
96 var results = [];
97 if (obj == null) return results;
98 if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
99 each(obj, function(value, index, list) {
100 results[results.length] = iterator.call(context, value, index, list);
101 });
102 return results;
103 };
104
105 var reduceError = 'Reduce of empty array with no initial value';
106
107 // **Reduce** builds up a single result from a list of values, aka `inject`,
108 // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
109 _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
110 var initial = arguments.length > 2;
111 if (obj == null) obj = [];
112 if (nativeReduce && obj.reduce === nativeReduce) {
113 if (context) iterator = _.bind(iterator, context);
114 return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
115 }
116 each(obj, function(value, index, list) {
117 if (!initial) {
118 memo = value;
119 initial = true;
120 } else {
121 memo = iterator.call(context, memo, value, index, list);
122 }
123 });
124 if (!initial) throw new TypeError(reduceError);
125 return memo;
126 };
127
128 // The right-associative version of reduce, also known as `foldr`.
129 // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
130 _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
131 var initial = arguments.length > 2;
132 if (obj == null) obj = [];
133 if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
134 if (context) iterator = _.bind(iterator, context);
135 return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
136 }
137 var length = obj.length;
138 if (length !== +length) {
139 var keys = _.keys(obj);
140 length = keys.length;
141 }
142 each(obj, function(value, index, list) {
143 index = keys ? keys[--length] : --length;
144 if (!initial) {
145 memo = obj[index];
146 initial = true;
147 } else {
148 memo = iterator.call(context, memo, obj[index], index, list);
149 }
150 });
151 if (!initial) throw new TypeError(reduceError);
152 return memo;
153 };
154
155 // Return the first value which passes a truth test. Aliased as `detect`.
156 _.find = _.detect = function(obj, iterator, context) {
157 var result;
158 any(obj, function(value, index, list) {
159 if (iterator.call(context, value, index, list)) {
160 result = value;
161 return true;
162 }
163 });
164 return result;
165 };
166
167 // Return all the elements that pass a truth test.
168 // Delegates to **ECMAScript 5**'s native `filter` if available.
169 // Aliased as `select`.
170 _.filter = _.select = function(obj, iterator, context) {
171 var results = [];
172 if (obj == null) return results;
173 if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
174 each(obj, function(value, index, list) {
175 if (iterator.call(context, value, index, list)) results[results.length] = value;
176 });
177 return results;
178 };
179
180 // Return all the elements for which a truth test fails.
181 _.reject = function(obj, iterator, context) {
182 return _.filter(obj, function(value, index, list) {
183 return !iterator.call(context, value, index, list);
184 }, context);
185 };
186
187 // Determine whether all of the elements match a truth test.
188 // Delegates to **ECMAScript 5**'s native `every` if available.
189 // Aliased as `all`.
190 _.every = _.all = function(obj, iterator, context) {
191 iterator || (iterator = _.identity);
192 var result = true;
193 if (obj == null) return result;
194 if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
195 each(obj, function(value, index, list) {
196 if (!(result = result && iterator.call(context, value, index, list))) return breaker;
197 });
198 return !!result;
199 };
200
201 // Determine if at least one element in the object matches a truth test.
202 // Delegates to **ECMAScript 5**'s native `some` if available.
203 // Aliased as `any`.
204 var any = _.some = _.any = function(obj, iterator, context) {
205 iterator || (iterator = _.identity);
206 var result = false;
207 if (obj == null) return result;
208 if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
209 each(obj, function(value, index, list) {
210 if (result || (result = iterator.call(context, value, index, list))) return breaker;
211 });
212 return !!result;
213 };
214
215 // Determine if the array or object contains a given value (using `===`).
216 // Aliased as `include`.
217 _.contains = _.include = function(obj, target) {
218 if (obj == null) return false;
219 if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
220 return any(obj, function(value) {
221 return value === target;
222 });
223 };
224
225 // Invoke a method (with arguments) on every item in a collection.
226 _.invoke = function(obj, method) {
227 var args = slice.call(arguments, 2);
228 var isFunc = _.isFunction(method);
229 return _.map(obj, function(value) {
230 return (isFunc ? method : value[method]).apply(value, args);
231 });
232 };
233
234 // Convenience version of a common use case of `map`: fetching a property.
235 _.pluck = function(obj, key) {
236 return _.map(obj, function(value){ return value[key]; });
237 };
238
239 // Convenience version of a common use case of `filter`: selecting only objects
240 // containing specific `key:value` pairs.
241 _.where = function(obj, attrs, first) {
242 if (_.isEmpty(attrs)) return first ? null : [];
243 return _[first ? 'find' : 'filter'](obj, function(value) {
244 for (var key in attrs) {
245 if (attrs[key] !== value[key]) return false;
246 }
247 return true;
248 });
249 };
250
251 // Convenience version of a common use case of `find`: getting the first object
252 // containing specific `key:value` pairs.
253 _.findWhere = function(obj, attrs) {
254 return _.where(obj, attrs, true);
255 };
256
257 // Return the maximum element or (element-based computation).
258 // Can't optimize arrays of integers longer than 65,535 elements.
259 // See: https://bugs.webkit.org/show_bug.cgi?id=80797
260 _.max = function(obj, iterator, context) {
261 if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
262 return Math.max.apply(Math, obj);
263 }
264 if (!iterator && _.isEmpty(obj)) return -Infinity;
265 var result = {computed : -Infinity, value: -Infinity};
266 each(obj, function(value, index, list) {
267 var computed = iterator ? iterator.call(context, value, index, list) : value;
268 computed >= result.computed && (result = {value : value, computed : computed});
269 });
270 return result.value;
271 };
272
273 // Return the minimum element (or element-based computation).
274 _.min = function(obj, iterator, context) {
275 if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
276 return Math.min.apply(Math, obj);
277 }
278 if (!iterator && _.isEmpty(obj)) return Infinity;
279 var result = {computed : Infinity, value: Infinity};
280 each(obj, function(value, index, list) {
281 var computed = iterator ? iterator.call(context, value, index, list) : value;
282 computed < result.computed && (result = {value : value, computed : computed});
283 });
284 return result.value;
285 };
286
287 // Shuffle an array.
288 _.shuffle = function(obj) {
289 var rand;
290 var index = 0;
291 var shuffled = [];
292 each(obj, function(value) {
293 rand = _.random(index++);
294 shuffled[index - 1] = shuffled[rand];
295 shuffled[rand] = value;
296 });
297 return shuffled;
298 };
299
300 // An internal function to generate lookup iterators.
301 var lookupIterator = function(value) {
302 return _.isFunction(value) ? value : function(obj){ return obj[value]; };
303 };
304
305 // Sort the object's values by a criterion produced by an iterator.
306 _.sortBy = function(obj, value, context) {
307 var iterator = lookupIterator(value);
308 return _.pluck(_.map(obj, function(value, index, list) {
309 return {
310 value : value,
311 index : index,
312 criteria : iterator.call(context, value, index, list)
313 };
314 }).sort(function(left, right) {
315 var a = left.criteria;
316 var b = right.criteria;
317 if (a !== b) {
318 if (a > b || a === void 0) return 1;
319 if (a < b || b === void 0) return -1;
320 }
321 return left.index < right.index ? -1 : 1;
322 }), 'value');
323 };
324
325 // An internal function used for aggregate "group by" operations.
326 var group = function(obj, value, context, behavior) {
327 var result = {};
328 var iterator = lookupIterator(value || _.identity);
329 each(obj, function(value, index) {
330 var key = iterator.call(context, value, index, obj);
331 behavior(result, key, value);
332 });
333 return result;
334 };
335
336 // Groups the object's values by a criterion. Pass either a string attribute
337 // to group by, or a function that returns the criterion.
338 _.groupBy = function(obj, value, context) {
339 return group(obj, value, context, function(result, key, value) {
340 (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
341 });
342 };
343
344 // Counts instances of an object that group by a certain criterion. Pass
345 // either a string attribute to count by, or a function that returns the
346 // criterion.
347 _.countBy = function(obj, value, context) {
348 return group(obj, value, context, function(result, key) {
349 if (!_.has(result, key)) result[key] = 0;
350 result[key]++;
351 });
352 };
353
354 // Use a comparator function to figure out the smallest index at which
355 // an object should be inserted so as to maintain order. Uses binary search.
356 _.sortedIndex = function(array, obj, iterator, context) {
357 iterator = iterator == null ? _.identity : lookupIterator(iterator);
358 var value = iterator.call(context, obj);
359 var low = 0, high = array.length;
360 while (low < high) {
361 var mid = (low + high) >>> 1;
362 iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
363 }
364 return low;
365 };
366
367 // Safely convert anything iterable into a real, live array.
368 _.toArray = function(obj) {
369 if (!obj) return [];
370 if (_.isArray(obj)) return slice.call(obj);
371 if (obj.length === +obj.length) return _.map(obj, _.identity);
372 return _.values(obj);
373 };
374
375 // Return the number of elements in an object.
376 _.size = function(obj) {
377 if (obj == null) return 0;
378 return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
379 };
380
381 // Array Functions
382 // ---------------
383
384 // Get the first element of an array. Passing **n** will return the first N
385 // values in the array. Aliased as `head` and `take`. The **guard** check
386 // allows it to work with `_.map`.
387 _.first = _.head = _.take = function(array, n, guard) {
388 if (array == null) return void 0;
389 return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
390 };
391
392 // Returns everything but the last entry of the array. Especially useful on
393 // the arguments object. Passing **n** will return all the values in
394 // the array, excluding the last N. The **guard** check allows it to work with
395 // `_.map`.
396 _.initial = function(array, n, guard) {
397 return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
398 };
399
400 // Get the last element of an array. Passing **n** will return the last N
401 // values in the array. The **guard** check allows it to work with `_.map`.
402 _.last = function(array, n, guard) {
403 if (array == null) return void 0;
404 if ((n != null) && !guard) {
405 return slice.call(array, Math.max(array.length - n, 0));
406 } else {
407 return array[array.length - 1];
408 }
409 };
410
411 // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
412 // Especially useful on the arguments object. Passing an **n** will return
413 // the rest N values in the array. The **guard**
414 // check allows it to work with `_.map`.
415 _.rest = _.tail = _.drop = function(array, n, guard) {
416 return slice.call(array, (n == null) || guard ? 1 : n);
417 };
418
419 // Trim out all falsy values from an array.
420 _.compact = function(array) {
421 return _.filter(array, _.identity);
422 };
423
424 // Internal implementation of a recursive `flatten` function.
425 var flatten = function(input, shallow, output) {
426 each(input, function(value) {
427 if (_.isArray(value)) {
428 shallow ? push.apply(output, value) : flatten(value, shallow, output);
429 } else {
430 output.push(value);
431 }
432 });
433 return output;
434 };
435
436 // Return a completely flattened version of an array.
437 _.flatten = function(array, shallow) {
438 return flatten(array, shallow, []);
439 };
440
441 // Return a version of the array that does not contain the specified value(s).
442 _.without = function(array) {
443 return _.difference(array, slice.call(arguments, 1));
444 };
445
446 // Produce a duplicate-free version of the array. If the array has already
447 // been sorted, you have the option of using a faster algorithm.
448 // Aliased as `unique`.
449 _.uniq = _.unique = function(array, isSorted, iterator, context) {
450 if (_.isFunction(isSorted)) {
451 context = iterator;
452 iterator = isSorted;
453 isSorted = false;
454 }
455 var initial = iterator ? _.map(array, iterator, context) : array;
456 var results = [];
457 var seen = [];
458 each(initial, function(value, index) {
459 if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
460 seen.push(value);
461 results.push(array[index]);
462 }
463 });
464 return results;
465 };
466
467 // Produce an array that contains the union: each distinct element from all of
468 // the passed-in arrays.
469 _.union = function() {
470 return _.uniq(concat.apply(ArrayProto, arguments));
471 };
472
473 // Produce an array that contains every item shared between all the
474 // passed-in arrays.
475 _.intersection = function(array) {
476 var rest = slice.call(arguments, 1);
477 return _.filter(_.uniq(array), function(item) {
478 return _.every(rest, function(other) {
479 return _.indexOf(other, item) >= 0;
480 });
481 });
482 };
483
484 // Take the difference between one array and a number of other arrays.
485 // Only the elements present in just the first array will remain.
486 _.difference = function(array) {
487 var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
488 return _.filter(array, function(value){ return !_.contains(rest, value); });
489 };
490
491 // Zip together multiple lists into a single array -- elements that share
492 // an index go together.
493 _.zip = function() {
494 var args = slice.call(arguments);
495 var length = _.max(_.pluck(args, 'length'));
496 var results = new Array(length);
497 for (var i = 0; i < length; i++) {
498 results[i] = _.pluck(args, "" + i);
499 }
500 return results;
501 };
502
503 // Converts lists into objects. Pass either a single array of `[key, value]`
504 // pairs, or two parallel arrays of the same length -- one of keys, and one of
505 // the corresponding values.
506 _.object = function(list, values) {
507 if (list == null) return {};
508 var result = {};
509 for (var i = 0, l = list.length; i < l; i++) {
510 if (values) {
511 result[list[i]] = values[i];
512 } else {
513 result[list[i][0]] = list[i][1];
514 }
515 }
516 return result;
517 };
518
519 // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
520 // we need this function. Return the position of the first occurrence of an
521 // item in an array, or -1 if the item is not included in the array.
522 // Delegates to **ECMAScript 5**'s native `indexOf` if available.
523 // If the array is large and already in sort order, pass `true`
524 // for **isSorted** to use binary search.
525 _.indexOf = function(array, item, isSorted) {
526 if (array == null) return -1;
527 var i = 0, l = array.length;
528 if (isSorted) {
529 if (typeof isSorted == 'number') {
530 i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
531 } else {
532 i = _.sortedIndex(array, item);
533 return array[i] === item ? i : -1;
534 }
535 }
536 if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
537 for (; i < l; i++) if (array[i] === item) return i;
538 return -1;
539 };
540
541 // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
542 _.lastIndexOf = function(array, item, from) {
543 if (array == null) return -1;
544 var hasIndex = from != null;
545 if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
546 return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
547 }
548 var i = (hasIndex ? from : array.length);
549 while (i--) if (array[i] === item) return i;
550 return -1;
551 };
552
553 // Generate an integer Array containing an arithmetic progression. A port of
554 // the native Python `range()` function. See
555 // [the Python documentation](http://docs.python.org/library/functions.html#range).
556 _.range = function(start, stop, step) {
557 if (arguments.length <= 1) {
558 stop = start || 0;
559 start = 0;
560 }
561 step = arguments[2] || 1;
562
563 var len = Math.max(Math.ceil((stop - start) / step), 0);
564 var idx = 0;
565 var range = new Array(len);
566
567 while(idx < len) {
568 range[idx++] = start;
569 start += step;
570 }
571
572 return range;
573 };
574
575 // Function (ahem) Functions
576 // ------------------
577
578 // Create a function bound to a given object (assigning `this`, and arguments,
579 // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
580 // available.
581 _.bind = function(func, context) {
582 if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
583 var args = slice.call(arguments, 2);
584 return function() {
585 return func.apply(context, args.concat(slice.call(arguments)));
586 };
587 };
588
589 // Partially apply a function by creating a version that has had some of its
590 // arguments pre-filled, without changing its dynamic `this` context.
591 _.partial = function(func) {
592 var args = slice.call(arguments, 1);
593 return function() {
594 return func.apply(this, args.concat(slice.call(arguments)));
595 };
596 };
597
598 // Bind all of an object's methods to that object. Useful for ensuring that
599 // all callbacks defined on an object belong to it.
600 _.bindAll = function(obj) {
601 var funcs = slice.call(arguments, 1);
602 if (funcs.length === 0) funcs = _.functions(obj);
603 each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
604 return obj;
605 };
606
607 // Memoize an expensive function by storing its results.
608 _.memoize = function(func, hasher) {
609 var memo = {};
610 hasher || (hasher = _.identity);
611 return function() {
612 var key = hasher.apply(this, arguments);
613 return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
614 };
615 };
616
617 // Delays a function for the given number of milliseconds, and then calls
618 // it with the arguments supplied.
619 _.delay = function(func, wait) {
620 var args = slice.call(arguments, 2);
621 return setTimeout(function(){ return func.apply(null, args); }, wait);
622 };
623
624 // Defers a function, scheduling it to run after the current call stack has
625 // cleared.
626 _.defer = function(func) {
627 return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
628 };
629
630 // Returns a function, that, when invoked, will only be triggered at most once
631 // during a given window of time.
632 _.throttle = function(func, wait) {
633 var context, args, timeout, result;
634 var previous = 0;
635 var later = function() {
636 previous = new Date;
637 timeout = null;
638 result = func.apply(context, args);
639 };
640 return function() {
641 var now = new Date;
642 var remaining = wait - (now - previous);
643 context = this;
644 args = arguments;
645 if (remaining <= 0) {
646 clearTimeout(timeout);
647 timeout = null;
648 previous = now;
649 result = func.apply(context, args);
650 } else if (!timeout) {
651 timeout = setTimeout(later, remaining);
652 }
653 return result;
654 };
655 };
656
657 // Returns a function, that, as long as it continues to be invoked, will not
658 // be triggered. The function will be called after it stops being called for
659 // N milliseconds. If `immediate` is passed, trigger the function on the
660 // leading edge, instead of the trailing.
661 _.debounce = function(func, wait, immediate) {
662 var timeout, result;
663 return function() {
664 var context = this, args = arguments;
665 var later = function() {
666 timeout = null;
667 if (!immediate) result = func.apply(context, args);
668 };
669 var callNow = immediate && !timeout;
670 clearTimeout(timeout);
671 timeout = setTimeout(later, wait);
672 if (callNow) result = func.apply(context, args);
673 return result;
674 };
675 };
676
677 // Returns a function that will be executed at most one time, no matter how
678 // often you call it. Useful for lazy initialization.
679 _.once = function(func) {
680 var ran = false, memo;
681 return function() {
682 if (ran) return memo;
683 ran = true;
684 memo = func.apply(this, arguments);
685 func = null;
686 return memo;
687 };
688 };
689
690 // Returns the first function passed as an argument to the second,
691 // allowing you to adjust arguments, run code before and after, and
692 // conditionally execute the original function.
693 _.wrap = function(func, wrapper) {
694 return function() {
695 var args = [func];
696 push.apply(args, arguments);
697 return wrapper.apply(this, args);
698 };
699 };
700
701 // Returns a function that is the composition of a list of functions, each
702 // consuming the return value of the function that follows.
703 _.compose = function() {
704 var funcs = arguments;
705 return function() {
706 var args = arguments;
707 for (var i = funcs.length - 1; i >= 0; i--) {
708 args = [funcs[i].apply(this, args)];
709 }
710 return args[0];
711 };
712 };
713
714 // Returns a function that will only be executed after being called N times.
715 _.after = function(times, func) {
716 if (times <= 0) return func();
717 return function() {
718 if (--times < 1) {
719 return func.apply(this, arguments);
720 }
721 };
722 };
723
724 // Object Functions
725 // ----------------
726
727 // Retrieve the names of an object's properties.
728 // Delegates to **ECMAScript 5**'s native `Object.keys`
729 _.keys = nativeKeys || function(obj) {
730 if (obj !== Object(obj)) throw new TypeError('Invalid object');
731 var keys = [];
732 for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
733 return keys;
734 };
735
736 // Retrieve the values of an object's properties.
737 _.values = function(obj) {
738 var values = [];
739 for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
740 return values;
741 };
742
743 // Convert an object into a list of `[key, value]` pairs.
744 _.pairs = function(obj) {
745 var pairs = [];
746 for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
747 return pairs;
748 };
749
750 // Invert the keys and values of an object. The values must be serializable.
751 _.invert = function(obj) {
752 var result = {};
753 for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
754 return result;
755 };
756
757 // Return a sorted list of the function names available on the object.
758 // Aliased as `methods`
759 _.functions = _.methods = function(obj) {
760 var names = [];
761 for (var key in obj) {
762 if (_.isFunction(obj[key])) names.push(key);
763 }
764 return names.sort();
765 };
766
767 // Extend a given object with all the properties in passed-in object(s).
768 _.extend = function(obj) {
769 each(slice.call(arguments, 1), function(source) {
770 if (source) {
771 for (var prop in source) {
772 obj[prop] = source[prop];
773 }
774 }
775 });
776 return obj;
777 };
778
779 // Return a copy of the object only containing the whitelisted properties.
780 _.pick = function(obj) {
781 var copy = {};
782 var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
783 each(keys, function(key) {
784 if (key in obj) copy[key] = obj[key];
785 });
786 return copy;
787 };
788
789 // Return a copy of the object without the blacklisted properties.
790 _.omit = function(obj) {
791 var copy = {};
792 var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
793 for (var key in obj) {
794 if (!_.contains(keys, key)) copy[key] = obj[key];
795 }
796 return copy;
797 };
798
799 // Fill in a given object with default properties.
800 _.defaults = function(obj) {
801 each(slice.call(arguments, 1), function(source) {
802 if (source) {
803 for (var prop in source) {
804 if (obj[prop] == null) obj[prop] = source[prop];
805 }
806 }
807 });
808 return obj;
809 };
810
811 // Create a (shallow-cloned) duplicate of an object.
812 _.clone = function(obj) {
813 if (!_.isObject(obj)) return obj;
814 return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
815 };
816
817 // Invokes interceptor with the obj, and then returns obj.
818 // The primary purpose of this method is to "tap into" a method chain, in
819 // order to perform operations on intermediate results within the chain.
820 _.tap = function(obj, interceptor) {
821 interceptor(obj);
822 return obj;
823 };
824
825 // Internal recursive comparison function for `isEqual`.
826 var eq = function(a, b, aStack, bStack) {
827 // Identical objects are equal. `0 === -0`, but they aren't identical.
828 // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
829 if (a === b) return a !== 0 || 1 / a == 1 / b;
830 // A strict comparison is necessary because `null == undefined`.
831 if (a == null || b == null) return a === b;
832 // Unwrap any wrapped objects.
833 if (a instanceof _) a = a._wrapped;
834 if (b instanceof _) b = b._wrapped;
835 // Compare `[[Class]]` names.
836 var className = toString.call(a);
837 if (className != toString.call(b)) return false;
838 switch (className) {
839 // Strings, numbers, dates, and booleans are compared by value.
840 case '[object String]':
841 // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
842 // equivalent to `new String("5")`.
843 return a == String(b);
844 case '[object Number]':
845 // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
846 // other numeric values.
847 return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
848 case '[object Date]':
849 case '[object Boolean]':
850 // Coerce dates and booleans to numeric primitive values. Dates are compared by their
851 // millisecond representations. Note that invalid dates with millisecond representations
852 // of `NaN` are not equivalent.
853 return +a == +b;
854 // RegExps are compared by their source patterns and flags.
855 case '[object RegExp]':
856 return a.source == b.source &&
857 a.global == b.global &&
858 a.multiline == b.multiline &&
859 a.ignoreCase == b.ignoreCase;
860 }
861 if (typeof a != 'object' || typeof b != 'object') return false;
862 // Assume equality for cyclic structures. The algorithm for detecting cyclic
863 // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
864 var length = aStack.length;
865 while (length--) {
866 // Linear search. Performance is inversely proportional to the number of
867 // unique nested structures.
868 if (aStack[length] == a) return bStack[length] == b;
869 }
870 // Add the first object to the stack of traversed objects.
871 aStack.push(a);
872 bStack.push(b);
873 var size = 0, result = true;
874 // Recursively compare objects and arrays.
875 if (className == '[object Array]') {
876 // Compare array lengths to determine if a deep comparison is necessary.
877 size = a.length;
878 result = size == b.length;
879 if (result) {
880 // Deep compare the contents, ignoring non-numeric properties.
881 while (size--) {
882 if (!(result = eq(a[size], b[size], aStack, bStack))) break;
883 }
884 }
885 } else {
886 // Objects with different constructors are not equivalent, but `Object`s
887 // from different frames are.
888 var aCtor = a.constructor, bCtor = b.constructor;
889 if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
890 _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
891 return false;
892 }
893 // Deep compare objects.
894 for (var key in a) {
895 if (_.has(a, key)) {
896 // Count the expected number of properties.
897 size++;
898 // Deep compare each member.
899 if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
900 }
901 }
902 // Ensure that both objects contain the same number of properties.
903 if (result) {
904 for (key in b) {
905 if (_.has(b, key) && !(size--)) break;
906 }
907 result = !size;
908 }
909 }
910 // Remove the first object from the stack of traversed objects.
911 aStack.pop();
912 bStack.pop();
913 return result;
914 };
915
916 // Perform a deep comparison to check if two objects are equal.
917 _.isEqual = function(a, b) {
918 return eq(a, b, [], []);
919 };
920
921 // Is a given array, string, or object empty?
922 // An "empty" object has no enumerable own-properties.
923 _.isEmpty = function(obj) {
924 if (obj == null) return true;
925 if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
926 for (var key in obj) if (_.has(obj, key)) return false;
927 return true;
928 };
929
930 // Is a given value a DOM element?
931 _.isElement = function(obj) {
932 return !!(obj && obj.nodeType === 1);
933 };
934
935 // Is a given value an array?
936 // Delegates to ECMA5's native Array.isArray
937 _.isArray = nativeIsArray || function(obj) {
938 return toString.call(obj) == '[object Array]';
939 };
940
941 // Is a given variable an object?
942 _.isObject = function(obj) {
943 return obj === Object(obj);
944 };
945
946 // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
947 each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
948 _['is' + name] = function(obj) {
949 return toString.call(obj) == '[object ' + name + ']';
950 };
951 });
952
953 // Define a fallback version of the method in browsers (ahem, IE), where
954 // there isn't any inspectable "Arguments" type.
955 if (!_.isArguments(arguments)) {
956 _.isArguments = function(obj) {
957 return !!(obj && _.has(obj, 'callee'));
958 };
959 }
960
961 // Optimize `isFunction` if appropriate.
962 if (typeof (/./) !== 'function') {
963 _.isFunction = function(obj) {
964 return typeof obj === 'function';
965 };
966 }
967
968 // Is a given object a finite number?
969 _.isFinite = function(obj) {
970 return isFinite(obj) && !isNaN(parseFloat(obj));
971 };
972
973 // Is the given value `NaN`? (NaN is the only number which does not equal itself).
974 _.isNaN = function(obj) {
975 return _.isNumber(obj) && obj != +obj;
976 };
977
978 // Is a given value a boolean?
979 _.isBoolean = function(obj) {
980 return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
981 };
982
983 // Is a given value equal to null?
984 _.isNull = function(obj) {
985 return obj === null;
986 };
987
988 // Is a given variable undefined?
989 _.isUndefined = function(obj) {
990 return obj === void 0;
991 };
992
993 // Shortcut function for checking if an object has a given property directly
994 // on itself (in other words, not on a prototype).
995 _.has = function(obj, key) {
996 return hasOwnProperty.call(obj, key);
997 };
998
999 // Utility Functions
1000 // -----------------
1001
1002 // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
1003 // previous owner. Returns a reference to the Underscore object.
1004 _.noConflict = function() {
1005 root._ = previousUnderscore;
1006 return this;
1007 };
1008
1009 // Keep the identity function around for default iterators.
1010 _.identity = function(value) {
1011 return value;
1012 };
1013
1014 // Run a function **n** times.
1015 _.times = function(n, iterator, context) {
1016 var accum = Array(n);
1017 for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
1018 return accum;
1019 };
1020
1021 // Return a random integer between min and max (inclusive).
1022 _.random = function(min, max) {
1023 if (max == null) {
1024 max = min;
1025 min = 0;
1026 }
1027 return min + Math.floor(Math.random() * (max - min + 1));
1028 };
1029
1030 // List of HTML entities for escaping.
1031 var entityMap = {
1032 escape: {
1033 '&': '&',
1034 '<': '<',
1035 '>': '>',
1036 '"': '"',
1037 "'": ''',
1038 '/': '/'
1039 }
1040 };
1041 entityMap.unescape = _.invert(entityMap.escape);
1042
1043 // Regexes containing the keys and values listed immediately above.
1044 var entityRegexes = {
1045 escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
1046 unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
1047 };
1048
1049 // Functions for escaping and unescaping strings to/from HTML interpolation.
1050 _.each(['escape', 'unescape'], function(method) {
1051 _[method] = function(string) {
1052 if (string == null) return '';
1053 return ('' + string).replace(entityRegexes[method], function(match) {
1054 return entityMap[method][match];
1055 });
1056 };
1057 });
1058
1059 // If the value of the named property is a function then invoke it;
1060 // otherwise, return it.
1061 _.result = function(object, property) {
1062 if (object == null) return null;
1063 var value = object[property];
1064 return _.isFunction(value) ? value.call(object) : value;
1065 };
1066
1067 // Add your own custom functions to the Underscore object.
1068 _.mixin = function(obj) {
1069 each(_.functions(obj), function(name){
1070 var func = _[name] = obj[name];
1071 _.prototype[name] = function() {
1072 var args = [this._wrapped];
1073 push.apply(args, arguments);
1074 return result.call(this, func.apply(_, args));
1075 };
1076 });
1077 };
1078
1079 // Generate a unique integer id (unique within the entire client session).
1080 // Useful for temporary DOM ids.
1081 var idCounter = 0;
1082 _.uniqueId = function(prefix) {
1083 var id = ++idCounter + '';
1084 return prefix ? prefix + id : id;
1085 };
1086
1087 // By default, Underscore uses ERB-style template delimiters, change the
1088 // following template settings to use alternative delimiters.
1089 _.templateSettings = {
1090 evaluate : /<%([\s\S]+?)%>/g,
1091 interpolate : /<%=([\s\S]+?)%>/g,
1092 escape : /<%-([\s\S]+?)%>/g
1093 };
1094
1095 // When customizing `templateSettings`, if you don't want to define an
1096 // interpolation, evaluation or escaping regex, we need one that is
1097 // guaranteed not to match.
1098 var noMatch = /(.)^/;
1099
1100 // Certain characters need to be escaped so that they can be put into a
1101 // string literal.
1102 var escapes = {
1103 "'": "'",
1104 '\\': '\\',
1105 '\r': 'r',
1106 '\n': 'n',
1107 '\t': 't',
1108 '\u2028': 'u2028',
1109 '\u2029': 'u2029'
1110 };
1111
1112 var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
1113
1114 // JavaScript micro-templating, similar to John Resig's implementation.
1115 // Underscore templating handles arbitrary delimiters, preserves whitespace,
1116 // and correctly escapes quotes within interpolated code.
1117 _.template = function(text, data, settings) {
1118 var render;
1119 settings = _.defaults({}, settings, _.templateSettings);
1120
1121 // Combine delimiters into one regular expression via alternation.
1122 var matcher = new RegExp([
1123 (settings.escape || noMatch).source,
1124 (settings.interpolate || noMatch).source,
1125 (settings.evaluate || noMatch).source
1126 ].join('|') + '|$', 'g');
1127
1128 // Compile the template source, escaping string literals appropriately.
1129 var index = 0;
1130 var source = "__p+='";
1131 text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
1132 source += text.slice(index, offset)
1133 .replace(escaper, function(match) { return '\\' + escapes[match]; });
1134
1135 if (escape) {
1136 source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
1137 }
1138 if (interpolate) {
1139 source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
1140 }
1141 if (evaluate) {
1142 source += "';\n" + evaluate + "\n__p+='";
1143 }
1144 index = offset + match.length;
1145 return match;
1146 });
1147 source += "';\n";
1148
1149 // If a variable is not specified, place data values in local scope.
1150 if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
1151
1152 source = "var __t,__p='',__j=Array.prototype.join," +
1153 "print=function(){__p+=__j.call(arguments,'');};\n" +
1154 source + "return __p;\n";
1155
1156 try {
1157 render = new Function(settings.variable || 'obj', '_', source);
1158 } catch (e) {
1159 e.source = source;
1160 throw e;
1161 }
1162
1163 if (data) return render(data, _);
1164 var template = function(data) {
1165 return render.call(this, data, _);
1166 };
1167
1168 // Provide the compiled function source as a convenience for precompilation.
1169 template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
1170
1171 return template;
1172 };
1173
1174 // Add a "chain" function, which will delegate to the wrapper.
1175 _.chain = function(obj) {
1176 return _(obj).chain();
1177 };
1178
1179 // OOP
1180 // ---------------
1181 // If Underscore is called as a function, it returns a wrapped object that
1182 // can be used OO-style. This wrapper holds altered versions of all the
1183 // underscore functions. Wrapped objects may be chained.
1184
1185 // Helper function to continue chaining intermediate results.
1186 var result = function(obj) {
1187 return this._chain ? _(obj).chain() : obj;
1188 };
1189
1190 // Add all of the Underscore functions to the wrapper object.
1191 _.mixin(_);
1192
1193 // Add all mutator Array functions to the wrapper.
1194 each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
1195 var method = ArrayProto[name];
1196 _.prototype[name] = function() {
1197 var obj = this._wrapped;
1198 method.apply(obj, arguments);
1199 if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
1200 return result.call(this, obj);
1201 };
1202 });
1203
1204 // Add all accessor Array functions to the wrapper.
1205 each(['concat', 'join', 'slice'], function(name) {
1206 var method = ArrayProto[name];
1207 _.prototype[name] = function() {
1208 return result.call(this, method.apply(this._wrapped, arguments));
1209 };
1210 });
1211
1212 _.extend(_.prototype, {
1213
1214 // Start chaining a wrapped Underscore object.
1215 chain: function() {
1216 this._chain = true;
1217 return this;
1218 },
1219
1220 // Extracts the result from a wrapped and chained object.
1221 value: function() {
1222 return this._wrapped;
1223 }
1224
1225 });
1226
1227}).call(this);
1228// Backbone.js
1229// ===========
1230
1231// > (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
1232// > Backbone may be freely distributed under the MIT license.
1233// > For all details and documentation: http://backbonejs.org
1234
1235// Initial Setup
1236// -------------
1237
1238(function(){
1239
1240 // Save a reference to the global object (`window` in the browser, `exports`
1241 // on the server).
1242 var root = this;
1243
1244 // Save the previous value of the `Backbone` variable, so that it can be
1245 // restored later on, if `noConflict` is used.
1246 var previousBackbone = root.Backbone;
1247
1248 // Create a local reference to array methods.
1249 var array = [];
1250 var push = array.push;
1251 var slice = array.slice;
1252 var splice = array.splice;
1253
1254 // The top-level namespace. All public Backbone classes and modules will
1255 // be attached to this. Exported for both CommonJS and the browser.
1256 var Backbone;
1257 if (typeof exports !== 'undefined') {
1258 Backbone = exports;
1259 } else {
1260 Backbone = root.Backbone = {};
1261 }
1262
1263 // Current version of the library. Keep in sync with `package.json`.
1264 Backbone.VERSION = '0.9.10';
1265
1266 // Require Underscore, if we're on the server, and it's not already present.
1267 var _ = root._;
1268 if (!_ && (typeof require !== 'undefined')) _ = require('underscore');
1269
1270 // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable.
1271 Backbone.$ = root.jQuery || root.Zepto || root.ender;
1272
1273 // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
1274 // to its previous owner. Returns a reference to this Backbone object.
1275 Backbone.noConflict = function() {
1276 root.Backbone = previousBackbone;
1277 return this;
1278 };
1279
1280 // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
1281 // will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and
1282 // set a `X-Http-Method-Override` header.
1283 Backbone.emulateHTTP = false;
1284
1285 // Turn on `emulateJSON` to support legacy servers that can't deal with direct
1286 // `application/json` requests ... will encode the body as
1287 // `application/x-www-form-urlencoded` instead and will send the model in a
1288 // form param named `model`.
1289 Backbone.emulateJSON = false;
1290
1291 // Backbone.Events
1292 // ---------------
1293
1294 // Regular expression used to split event strings.
1295 var eventSplitter = /\s+/;
1296
1297 // Implement fancy features of the Events API such as multiple event
1298 // names `"change blur"` and jQuery-style event maps `{change: action}`
1299 // in terms of the existing API.
1300 var eventsApi = function(obj, action, name, rest) {
1301 if (!name) return true;
1302 if (typeof name === 'object') {
1303 for (var key in name) {
1304 obj[action].apply(obj, [key, name[key]].concat(rest));
1305 }
1306 } else if (eventSplitter.test(name)) {
1307 var names = name.split(eventSplitter);
1308 for (var i = 0, l = names.length; i < l; i++) {
1309 obj[action].apply(obj, [names[i]].concat(rest));
1310 }
1311 } else {
1312 return true;
1313 }
1314 };
1315
1316 // Optimized internal dispatch function for triggering events. Tries to
1317 // keep the usual cases speedy (most Backbone events have 3 arguments).
1318 var triggerEvents = function(events, args) {
1319 var ev, i = -1, l = events.length;
1320 switch (args.length) {
1321 case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx);
1322 return;
1323 case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0]);
1324 return;
1325 case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1]);
1326 return;
1327 case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1], args[2]);
1328 return;
1329 default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args);
1330 }
1331 };
1332
1333 // A module that can be mixed in to *any object* in order to provide it with
1334 // custom events. You may bind with `on` or remove with `off` callback
1335 // functions to an event; `trigger`-ing an event fires all callbacks in
1336 // succession.
1337 var Events = Backbone.Events = {
1338
1339 // Bind one or more space separated events, or an events map,
1340 // to a `callback` function. Passing `"all"` will bind the callback to
1341 // all events fired.
1342 on: function(name, callback, context) {
1343 if (!(eventsApi(this, 'on', name, [callback, context]) && callback)) return this;
1344 this._events || (this._events = {});
1345 var list = this._events[name] || (this._events[name] = []);
1346 list.push({callback: callback, context: context, ctx: context || this});
1347 return this;
1348 },
1349
1350 // Bind events to only be triggered a single time. After the first time
1351 // the callback is invoked, it will be removed.
1352 once: function(name, callback, context) {
1353 if (!(eventsApi(this, 'once', name, [callback, context]) && callback)) return this;
1354 var self = this;
1355 var once = _.once(function() {
1356 self.off(name, once);
1357 callback.apply(this, arguments);
1358 });
1359 once._callback = callback;
1360 this.on(name, once, context);
1361 return this;
1362 },
1363
1364 // Remove one or many callbacks. If `context` is null, removes all
1365 // callbacks with that function. If `callback` is null, removes all
1366 // callbacks for the event. If `name` is null, removes all bound
1367 // callbacks for all events.
1368 off: function(name, callback, context) {
1369 var list, ev, events, names, i, l, j, k;
1370 if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
1371 if (!name && !callback && !context) {
1372 this._events = {};
1373 return this;
1374 }
1375
1376 names = name ? [name] : _.keys(this._events);
1377 for (i = 0, l = names.length; i < l; i++) {
1378 name = names[i];
1379 if (list = this._events[name]) {
1380 events = [];
1381 if (callback || context) {
1382 for (j = 0, k = list.length; j < k; j++) {
1383 ev = list[j];
1384 if ((callback && callback !== ev.callback &&
1385 callback !== ev.callback._callback) ||
1386 (context && context !== ev.context)) {
1387 events.push(ev);
1388 }
1389 }
1390 }
1391 this._events[name] = events;
1392 }
1393 }
1394
1395 return this;
1396 },
1397
1398 // Trigger one or many events, firing all bound callbacks. Callbacks are
1399 // passed the same arguments as `trigger` is, apart from the event name
1400 // (unless you're listening on `"all"`, which will cause your callback to
1401 // receive the true name of the event as the first argument).
1402 trigger: function(name) {
1403 if (!this._events) return this;
1404 var args = slice.call(arguments, 1);
1405 if (!eventsApi(this, 'trigger', name, args)) return this;
1406 var events = this._events[name];
1407 var allEvents = this._events.all;
1408 if (events) triggerEvents(events, args);
1409 if (allEvents) triggerEvents(allEvents, arguments);
1410 return this;
1411 },
1412
1413 // An inversion-of-control version of `on`. Tell *this* object to listen to
1414 // an event in another object ... keeping track of what it's listening to.
1415 listenTo: function(obj, name, callback) {
1416 var listeners = this._listeners || (this._listeners = {});
1417 var id = obj._listenerId || (obj._listenerId = _.uniqueId('l'));
1418 listeners[id] = obj;
1419 obj.on(name, typeof name === 'object' ? this : callback, this);
1420 return this;
1421 },
1422
1423 // Tell this object to stop listening to either specific events ... or
1424 // to every object it's currently listening to.
1425 stopListening: function(obj, name, callback) {
1426 var listeners = this._listeners;
1427 if (!listeners) return;
1428 if (obj) {
1429 obj.off(name, typeof name === 'object' ? this : callback, this);
1430 if (!name && !callback) delete listeners[obj._listenerId];
1431 } else {
1432 if (typeof name === 'object') callback = this;
1433 for (var id in listeners) {
1434 listeners[id].off(name, callback, this);
1435 }
1436 this._listeners = {};
1437 }
1438 return this;
1439 }
1440 };
1441
1442 // Aliases for backwards compatibility.
1443 Events.bind = Events.on;
1444 Events.unbind = Events.off;
1445
1446 // Allow the `Backbone` object to serve as a global event bus, for folks who
1447 // want global "pubsub" in a convenient place.
1448 _.extend(Backbone, Events);
1449
1450 // Backbone.Model
1451 // --------------
1452
1453 // Create a new model, with defined attributes. A client id (`cid`)
1454 // is automatically generated and assigned for you.
1455 var Model = Backbone.Model = function(attributes, options) {
1456 var defaults;
1457 var attrs = attributes || {};
1458 this.cid = _.uniqueId('c');
1459 this.attributes = {};
1460 if (options && options.collection) this.collection = options.collection;
1461 if (options && options.parse) attrs = this.parse(attrs, options) || {};
1462 if (defaults = _.result(this, 'defaults')) {
1463 attrs = _.defaults({}, attrs, defaults);
1464 }
1465 this.set(attrs, options);
1466 this.changed = {};
1467 this.initialize.apply(this, arguments);
1468 };
1469
1470 // Attach all inheritable methods to the Model prototype.
1471 _.extend(Model.prototype, Events, {
1472
1473 // A hash of attributes whose current and previous value differ.
1474 changed: null,
1475
1476 // The default name for the JSON `id` attribute is `"id"`. MongoDB and
1477 // CouchDB users may want to set this to `"_id"`.
1478 idAttribute: 'id',
1479
1480 // Initialize is an empty function by default. Override it with your own
1481 // initialization logic.
1482 initialize: function(){},
1483
1484 // Return a copy of the model's `attributes` object.
1485 toJSON: function(options) {
1486 return _.clone(this.attributes);
1487 },
1488
1489 // Proxy `Backbone.sync` by default.
1490 sync: function() {
1491 return Backbone.sync.apply(this, arguments);
1492 },
1493
1494 // Get the value of an attribute.
1495 get: function(attr) {
1496 return this.attributes[attr];
1497 },
1498
1499 // Get the HTML-escaped value of an attribute.
1500 escape: function(attr) {
1501 return _.escape(this.get(attr));
1502 },
1503
1504 // Returns `true` if the attribute contains a value that is not null
1505 // or undefined.
1506 has: function(attr) {
1507 return this.get(attr) != null;
1508 },
1509
1510 // ----------------------------------------------------------------------
1511
1512 // Set a hash of model attributes on the object, firing `"change"` unless
1513 // you choose to silence it.
1514 set: function(key, val, options) {
1515 var attr, attrs, unset, changes, silent, changing, prev, current;
1516 if (key == null) return this;
1517
1518 // Handle both `"key", value` and `{key: value}` -style arguments.
1519 if (typeof key === 'object') {
1520 attrs = key;
1521 options = val;
1522 } else {
1523 (attrs = {})[key] = val;
1524 }
1525
1526 options || (options = {});
1527
1528 // Run validation.
1529 if (!this._validate(attrs, options)) return false;
1530
1531 // Extract attributes and options.
1532 unset = options.unset;
1533 silent = options.silent;
1534 changes = [];
1535 changing = this._changing;
1536 this._changing = true;
1537
1538 if (!changing) {
1539 this._previousAttributes = _.clone(this.attributes);
1540 this.changed = {};
1541 }
1542 current = this.attributes, prev = this._previousAttributes;
1543
1544 // Check for changes of `id`.
1545 if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
1546
1547 // For each `set` attribute, update or delete the current value.
1548 for (attr in attrs) {
1549 val = attrs[attr];
1550 if (!_.isEqual(current[attr], val)) changes.push(attr);
1551 if (!_.isEqual(prev[attr], val)) {
1552 this.changed[attr] = val;
1553 } else {
1554 delete this.changed[attr];
1555 }
1556 unset ? delete current[attr] : current[attr] = val;
1557 }
1558
1559 // Trigger all relevant attribute changes.
1560 if (!silent) {
1561 if (changes.length) this._pending = true;
1562 for (var i = 0, l = changes.length; i < l; i++) {
1563 this.trigger('change:' + changes[i], this, current[changes[i]], options);
1564 }
1565 }
1566
1567 if (changing) return this;
1568 if (!silent) {
1569 while (this._pending) {
1570 this._pending = false;
1571 this.trigger('change', this, options);
1572 }
1573 }
1574 this._pending = false;
1575 this._changing = false;
1576 return this;
1577 },
1578
1579 // Remove an attribute from the model, firing `"change"` unless you choose
1580 // to silence it. `unset` is a noop if the attribute doesn't exist.
1581 unset: function(attr, options) {
1582 return this.set(attr, void 0, _.extend({}, options, {unset: true}));
1583 },
1584
1585 // Clear all attributes on the model, firing `"change"` unless you choose
1586 // to silence it.
1587 clear: function(options) {
1588 var attrs = {};
1589 for (var key in this.attributes) attrs[key] = void 0;
1590 return this.set(attrs, _.extend({}, options, {unset: true}));
1591 },
1592
1593 // Determine if the model has changed since the last `"change"` event.
1594 // If you specify an attribute name, determine if that attribute has changed.
1595 hasChanged: function(attr) {
1596 if (attr == null) return !_.isEmpty(this.changed);
1597 return _.has(this.changed, attr);
1598 },
1599
1600 // Return an object containing all the attributes that have changed, or
1601 // false if there are no changed attributes. Useful for determining what
1602 // parts of a view need to be updated and/or what attributes need to be
1603 // persisted to the server. Unset attributes will be set to undefined.
1604 // You can also pass an attributes object to diff against the model,
1605 // determining if there *would be* a change.
1606 changedAttributes: function(diff) {
1607 if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
1608 var val, changed = false;
1609 var old = this._changing ? this._previousAttributes : this.attributes;
1610 for (var attr in diff) {
1611 if (_.isEqual(old[attr], (val = diff[attr]))) continue;
1612 (changed || (changed = {}))[attr] = val;
1613 }
1614 return changed;
1615 },
1616
1617 // Get the previous value of an attribute, recorded at the time the last
1618 // `"change"` event was fired.
1619 previous: function(attr) {
1620 if (attr == null || !this._previousAttributes) return null;
1621 return this._previousAttributes[attr];
1622 },
1623
1624 // Get all of the attributes of the model at the time of the previous
1625 // `"change"` event.
1626 previousAttributes: function() {
1627 return _.clone(this._previousAttributes);
1628 },
1629
1630 // ---------------------------------------------------------------------
1631
1632 // Fetch the model from the server. If the server's representation of the
1633 // model differs from its current attributes, they will be overriden,
1634 // triggering a `"change"` event.
1635 fetch: function(options) {
1636 options = options ? _.clone(options) : {};
1637 if (options.parse === void 0) options.parse = true;
1638 var success = options.success;
1639 options.success = function(model, resp, options) {
1640 if (!model.set(model.parse(resp, options), options)) return false;
1641 if (success) success(model, resp, options);
1642 };
1643 return this.sync('read', this, options);
1644 },
1645
1646 // Set a hash of model attributes, and sync the model to the server.
1647 // If the server returns an attributes hash that differs, the model's
1648 // state will be `set` again.
1649 save: function(key, val, options) {
1650 var attrs, success, method, xhr, attributes = this.attributes;
1651
1652 // Handle both `"key", value` and `{key: value}` -style arguments.
1653 if (key == null || typeof key === 'object') {
1654 attrs = key;
1655 options = val;
1656 } else {
1657 (attrs = {})[key] = val;
1658 }
1659
1660 // If we're not waiting and attributes exist, save acts as `set(attr).save(null, opts)`.
1661 if (attrs && (!options || !options.wait) && !this.set(attrs, options)) return false;
1662
1663 options = _.extend({validate: true}, options);
1664
1665 // Do not persist invalid models.
1666 if (!this._validate(attrs, options)) return false;
1667
1668 // Set temporary attributes if `{wait: true}`.
1669 if (attrs && options.wait) {
1670 this.attributes = _.extend({}, attributes, attrs);
1671 }
1672
1673 // After a successful server-side save, the client is (optionally)
1674 // updated with the server-side state.
1675 if (options.parse === void 0) options.parse = true;
1676 success = options.success;
1677 options.success = function(model, resp, options) {
1678 // Ensure attributes are restored during synchronous saves.
1679 model.attributes = attributes;
1680 var serverAttrs = model.parse(resp, options);
1681 if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
1682 if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
1683 return false;
1684 }
1685 if (success) success(model, resp, options);
1686 };
1687
1688 // Finish configuring and sending the Ajax request.
1689 method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
1690 if (method === 'patch') options.attrs = attrs;
1691 xhr = this.sync(method, this, options);
1692
1693 // Restore attributes.
1694 if (attrs && options.wait) this.attributes = attributes;
1695
1696 return xhr;
1697 },
1698
1699 // Destroy this model on the server if it was already persisted.
1700 // Optimistically removes the model from its collection, if it has one.
1701 // If `wait: true` is passed, waits for the server to respond before removal.
1702 destroy: function(options) {
1703 options = options ? _.clone(options) : {};
1704 var model = this;
1705 var success = options.success;
1706
1707 var destroy = function() {
1708 model.trigger('destroy', model, model.collection, options);
1709 };
1710
1711 options.success = function(model, resp, options) {
1712 if (options.wait || model.isNew()) destroy();
1713 if (success) success(model, resp, options);
1714 };
1715
1716 if (this.isNew()) {
1717 options.success(this, null, options);
1718 return false;
1719 }
1720
1721 var xhr = this.sync('delete', this, options);
1722 if (!options.wait) destroy();
1723 return xhr;
1724 },
1725
1726 // Default URL for the model's representation on the server -- if you're
1727 // using Backbone's restful methods, override this to change the endpoint
1728 // that will be called.
1729 url: function() {
1730 var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
1731 if (this.isNew()) return base;
1732 return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);
1733 },
1734
1735 // **parse** converts a response into the hash of attributes to be `set` on
1736 // the model. The default implementation is just to pass the response along.
1737 parse: function(resp, options) {
1738 return resp;
1739 },
1740
1741 // Create a new model with identical attributes to this one.
1742 clone: function() {
1743 return new this.constructor(this.attributes);
1744 },
1745
1746 // A model is new if it has never been saved to the server, and lacks an id.
1747 isNew: function() {
1748 return this.id == null;
1749 },
1750
1751 // Check if the model is currently in a valid state.
1752 isValid: function(options) {
1753 return !this.validate || !this.validate(this.attributes, options);
1754 },
1755
1756 // Run validation against the next complete set of model attributes,
1757 // returning `true` if all is well. Otherwise, fire a general
1758 // `"error"` event and call the error callback, if specified.
1759 _validate: function(attrs, options) {
1760 if (!options.validate || !this.validate) return true;
1761 attrs = _.extend({}, this.attributes, attrs);
1762 var error = this.validationError = this.validate(attrs, options) || null;
1763 if (!error) return true;
1764 this.trigger('invalid', this, error, options || {});
1765 return false;
1766 }
1767
1768 });
1769
1770 // Backbone.Collection
1771 // -------------------
1772
1773 // Provides a standard collection class for our sets of models, ordered
1774 // or unordered. If a `comparator` is specified, the Collection will maintain
1775 // its models in sort order, as they're added and removed.
1776 var Collection = Backbone.Collection = function(models, options) {
1777 options || (options = {});
1778 if (options.model) this.model = options.model;
1779 if (options.comparator !== void 0) this.comparator = options.comparator;
1780 this.models = [];
1781 this._reset();
1782 this.initialize.apply(this, arguments);
1783 if (models) this.reset(models, _.extend({silent: true}, options));
1784 };
1785
1786 // Define the Collection's inheritable methods.
1787 _.extend(Collection.prototype, Events, {
1788
1789 // The default model for a collection is just a **Backbone.Model**.
1790 // This should be overridden in most cases.
1791 model: Model,
1792
1793 // Initialize is an empty function by default. Override it with your own
1794 // initialization logic.
1795 initialize: function(){},
1796
1797 // The JSON representation of a Collection is an array of the
1798 // models' attributes.
1799 toJSON: function(options) {
1800 return this.map(function(model){ return model.toJSON(options); });
1801 },
1802
1803 // Proxy `Backbone.sync` by default.
1804 sync: function() {
1805 return Backbone.sync.apply(this, arguments);
1806 },
1807
1808 // Add a model, or list of models to the set.
1809 add: function(models, options) {
1810 models = _.isArray(models) ? models.slice() : [models];
1811 options || (options = {});
1812 var i, l, model, attrs, existing, doSort, add, at, sort, sortAttr;
1813 add = [];
1814 at = options.at;
1815 sort = this.comparator && (at == null) && options.sort != false;
1816 sortAttr = _.isString(this.comparator) ? this.comparator : null;
1817
1818 // Turn bare objects into model references, and prevent invalid models
1819 // from being added.
1820 for (i = 0, l = models.length; i < l; i++) {
1821 if (!(model = this._prepareModel(attrs = models[i], options))) {
1822 this.trigger('invalid', this, attrs, options);
1823 continue;
1824 }
1825
1826 // If a duplicate is found, prevent it from being added and
1827 // optionally merge it into the existing model.
1828 if (existing = this.get(model)) {
1829 if (options.merge) {
1830 existing.set(attrs === model ? model.attributes : attrs, options);
1831 if (sort && !doSort && existing.hasChanged(sortAttr)) doSort = true;
1832 }
1833 continue;
1834 }
1835
1836 // This is a new model, push it to the `add` list.
1837 add.push(model);
1838
1839 // Listen to added models' events, and index models for lookup by
1840 // `id` and by `cid`.
1841 model.on('all', this._onModelEvent, this);
1842 this._byId[model.cid] = model;
1843 if (model.id != null) this._byId[model.id] = model;
1844 }
1845
1846 // See if sorting is needed, update `length` and splice in new models.
1847 if (add.length) {
1848 if (sort) doSort = true;
1849 this.length += add.length;
1850 if (at != null) {
1851 splice.apply(this.models, [at, 0].concat(add));
1852 } else {
1853 push.apply(this.models, add);
1854 }
1855 }
1856
1857 // Silently sort the collection if appropriate.
1858 if (doSort) this.sort({silent: true});
1859
1860 if (options.silent) return this;
1861
1862 // Trigger `add` events.
1863 for (i = 0, l = add.length; i < l; i++) {
1864 (model = add[i]).trigger('add', model, this, options);
1865 }
1866
1867 // Trigger `sort` if the collection was sorted.
1868 if (doSort) this.trigger('sort', this, options);
1869
1870 return this;
1871 },
1872
1873 // Remove a model, or a list of models from the set.
1874 remove: function(models, options) {
1875 models = _.isArray(models) ? models.slice() : [models];
1876 options || (options = {});
1877 var i, l, index, model;
1878 for (i = 0, l = models.length; i < l; i++) {
1879 model = this.get(models[i]);
1880 if (!model) continue;
1881 delete this._byId[model.id];
1882 delete this._byId[model.cid];
1883 index = this.indexOf(model);
1884 this.models.splice(index, 1);
1885 this.length--;
1886 if (!options.silent) {
1887 options.index = index;
1888 model.trigger('remove', model, this, options);
1889 }
1890 this._removeReference(model);
1891 }
1892 return this;
1893 },
1894
1895 // Add a model to the end of the collection.
1896 push: function(model, options) {
1897 model = this._prepareModel(model, options);
1898 this.add(model, _.extend({at: this.length}, options));
1899 return model;
1900 },
1901
1902 // Remove a model from the end of the collection.
1903 pop: function(options) {
1904 var model = this.at(this.length - 1);
1905 this.remove(model, options);
1906 return model;
1907 },
1908
1909 // Add a model to the beginning of the collection.
1910 unshift: function(model, options) {
1911 model = this._prepareModel(model, options);
1912 this.add(model, _.extend({at: 0}, options));
1913 return model;
1914 },
1915
1916 // Remove a model from the beginning of the collection.
1917 shift: function(options) {
1918 var model = this.at(0);
1919 this.remove(model, options);
1920 return model;
1921 },
1922
1923 // Slice out a sub-array of models from the collection.
1924 slice: function(begin, end) {
1925 return this.models.slice(begin, end);
1926 },
1927
1928 // Get a model from the set by id.
1929 get: function(obj) {
1930 if (obj == null) return void 0;
1931 this._idAttr || (this._idAttr = this.model.prototype.idAttribute);
1932 return this._byId[obj.id || obj.cid || obj[this._idAttr] || obj];
1933 },
1934
1935 // Get the model at the given index.
1936 at: function(index) {
1937 return this.models[index];
1938 },
1939
1940 // Return models with matching attributes. Useful for simple cases of `filter`.
1941 where: function(attrs) {
1942 if (_.isEmpty(attrs)) return [];
1943 return this.filter(function(model) {
1944 for (var key in attrs) {
1945 if (attrs[key] !== model.get(key)) return false;
1946 }
1947 return true;
1948 });
1949 },
1950
1951 // Force the collection to re-sort itself. You don't need to call this under
1952 // normal circumstances, as the set will maintain sort order as each item
1953 // is added.
1954 sort: function(options) {
1955 if (!this.comparator) {
1956 throw new Error('Cannot sort a set without a comparator');
1957 }
1958 options || (options = {});
1959
1960 // Run sort based on type of `comparator`.
1961 if (_.isString(this.comparator) || this.comparator.length === 1) {
1962 this.models = this.sortBy(this.comparator, this);
1963 } else {
1964 this.models.sort(_.bind(this.comparator, this));
1965 }
1966
1967 if (!options.silent) this.trigger('sort', this, options);
1968 return this;
1969 },
1970
1971 // Pluck an attribute from each model in the collection.
1972 pluck: function(attr) {
1973 return _.invoke(this.models, 'get', attr);
1974 },
1975
1976 // Smartly update a collection with a change set of models, adding,
1977 // removing, and merging as necessary.
1978 update: function(models, options) {
1979 options = _.extend({add: true, merge: true, remove: true}, options);
1980 if (options.parse) models = this.parse(models, options);
1981 var model, i, l, existing;
1982 var add = [], remove = [], modelMap = {};
1983
1984 // Allow a single model (or no argument) to be passed.
1985 if (!_.isArray(models)) models = models ? [models] : [];
1986
1987 // Proxy to `add` for this case, no need to iterate...
1988 if (options.add && !options.remove) return this.add(models, options);
1989
1990 // Determine which models to add and merge, and which to remove.
1991 for (i = 0, l = models.length; i < l; i++) {
1992 model = models[i];
1993 existing = this.get(model);
1994 if (options.remove && existing) modelMap[existing.cid] = true;
1995 if ((options.add && !existing) || (options.merge && existing)) {
1996 add.push(model);
1997 }
1998 }
1999 if (options.remove) {
2000 for (i = 0, l = this.models.length; i < l; i++) {
2001 model = this.models[i];
2002 if (!modelMap[model.cid]) remove.push(model);
2003 }
2004 }
2005
2006 // Remove models (if applicable) before we add and merge the rest.
2007 if (remove.length) this.remove(remove, options);
2008 if (add.length) this.add(add, options);
2009 return this;
2010 },
2011
2012 // When you have more items than you want to add or remove individually,
2013 // you can reset the entire set with a new list of models, without firing
2014 // any `add` or `remove` events. Fires `reset` when finished.
2015 reset: function(models, options) {
2016 options || (options = {});
2017 if (options.parse) models = this.parse(models, options);
2018 for (var i = 0, l = this.models.length; i < l; i++) {
2019 this._removeReference(this.models[i]);
2020 }
2021 options.previousModels = this.models.slice();
2022 this._reset();
2023 if (models) this.add(models, _.extend({silent: true}, options));
2024 if (!options.silent) this.trigger('reset', this, options);
2025 return this;
2026 },
2027
2028 // Fetch the default set of models for this collection, resetting the
2029 // collection when they arrive. If `update: true` is passed, the response
2030 // data will be passed through the `update` method instead of `reset`.
2031 fetch: function(options) {
2032 options = options ? _.clone(options) : {};
2033 if (options.parse === void 0) options.parse = true;
2034 var success = options.success;
2035 options.success = function(collection, resp, options) {
2036 var method = options.update ? 'update' : 'reset';
2037 collection[method](resp, options);
2038 if (success) success(collection, resp, options);
2039 };
2040 return this.sync('read', this, options);
2041 },
2042
2043 // Create a new instance of a model in this collection. Add the model to the
2044 // collection immediately, unless `wait: true` is passed, in which case we
2045 // wait for the server to agree.
2046 create: function(model, options) {
2047 options = options ? _.clone(options) : {};
2048 if (!(model = this._prepareModel(model, options))) return false;
2049 if (!options.wait) this.add(model, options);
2050 var collection = this;
2051 var success = options.success;
2052 options.success = function(model, resp, options) {
2053 if (options.wait) collection.add(model, options);
2054 if (success) success(model, resp, options);
2055 };
2056 model.save(null, options);
2057 return model;
2058 },
2059
2060 // **parse** converts a response into a list of models to be added to the
2061 // collection. The default implementation is just to pass it through.
2062 parse: function(resp, options) {
2063 return resp;
2064 },
2065
2066 // Create a new collection with an identical list of models as this one.
2067 clone: function() {
2068 return new this.constructor(this.models);
2069 },
2070
2071 // Reset all internal state. Called when the collection is reset.
2072 _reset: function() {
2073 this.length = 0;
2074 this.models.length = 0;
2075 this._byId = {};
2076 },
2077
2078 // Prepare a model or hash of attributes to be added to this collection.
2079 _prepareModel: function(attrs, options) {
2080 if (attrs instanceof Model) {
2081 if (!attrs.collection) attrs.collection = this;
2082 return attrs;
2083 }
2084 options || (options = {});
2085 options.collection = this;
2086 var model = new this.model(attrs, options);
2087 if (!model._validate(attrs, options)) return false;
2088 return model;
2089 },
2090
2091 // Internal method to remove a model's ties to a collection.
2092 _removeReference: function(model) {
2093 if (this === model.collection) delete model.collection;
2094 model.off('all', this._onModelEvent, this);
2095 },
2096
2097 // Internal method called every time a model in the set fires an event.
2098 // Sets need to update their indexes when models change ids. All other
2099 // events simply proxy through. "add" and "remove" events that originate
2100 // in other collections are ignored.
2101 _onModelEvent: function(event, model, collection, options) {
2102 if ((event === 'add' || event === 'remove') && collection !== this) return;
2103 if (event === 'destroy') this.remove(model, options);
2104 if (model && event === 'change:' + model.idAttribute) {
2105 delete this._byId[model.previous(model.idAttribute)];
2106 if (model.id != null) this._byId[model.id] = model;
2107 }
2108 this.trigger.apply(this, arguments);
2109 },
2110
2111 sortedIndex: function (model, value, context) {
2112 value || (value = this.comparator);
2113 var iterator = _.isFunction(value) ? value : function(model) {
2114 return model.get(value);
2115 };
2116 return _.sortedIndex(this.models, model, iterator, context);
2117 }
2118
2119 });
2120
2121 // Underscore methods that we want to implement on the Collection.
2122 var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
2123 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
2124 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
2125 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
2126 'tail', 'drop', 'last', 'without', 'indexOf', 'shuffle', 'lastIndexOf',
2127 'isEmpty', 'chain'];
2128
2129 // Mix in each Underscore method as a proxy to `Collection#models`.
2130 _.each(methods, function(method) {
2131 Collection.prototype[method] = function() {
2132 var args = slice.call(arguments);
2133 args.unshift(this.models);
2134 return _[method].apply(_, args);
2135 };
2136 });
2137
2138 // Underscore methods that take a property name as an argument.
2139 var attributeMethods = ['groupBy', 'countBy', 'sortBy'];
2140
2141 // Use attributes instead of properties.
2142 _.each(attributeMethods, function(method) {
2143 Collection.prototype[method] = function(value, context) {
2144 var iterator = _.isFunction(value) ? value : function(model) {
2145 return model.get(value);
2146 };
2147 return _[method](this.models, iterator, context);
2148 };
2149 });
2150
2151 // Backbone.Router
2152 // ---------------
2153
2154 // Routers map faux-URLs to actions, and fire events when routes are
2155 // matched. Creating a new one sets its `routes` hash, if not set statically.
2156 var Router = Backbone.Router = function(options) {
2157 options || (options = {});
2158 if (options.routes) this.routes = options.routes;
2159 this._bindRoutes();
2160 this.initialize.apply(this, arguments);
2161 };
2162
2163 // Cached regular expressions for matching named param parts and splatted
2164 // parts of route strings.
2165 var optionalParam = /\((.*?)\)/g;
2166 var namedParam = /(\(\?)?:\w+/g;
2167 var splatParam = /\*\w+/g;
2168 var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
2169
2170 // Set up all inheritable **Backbone.Router** properties and methods.
2171 _.extend(Router.prototype, Events, {
2172
2173 // Initialize is an empty function by default. Override it with your own
2174 // initialization logic.
2175 initialize: function(){},
2176
2177 // Manually bind a single named route to a callback function.
2178 route: function(route, name, callback) {
2179 if (!_.isRegExp(route)) route = this._routeToRegExp(route);
2180 if (!callback) callback = this[name];
2181 Backbone.history.route(route, _.bind(function(fragment) {
2182 var args = this._extractParameters(route, fragment);
2183 callback && callback.apply(this, args);
2184 this.trigger.apply(this, ['route:' + name].concat(args));
2185 this.trigger('route', name, args);
2186 Backbone.history.trigger('route', this, name, args);
2187 }, this));
2188 return this;
2189 },
2190
2191 // Simple proxy to `Backbone.history` to save a fragment into the history.
2192 navigate: function(fragment, options) {
2193 Backbone.history.navigate(fragment, options);
2194 return this;
2195 },
2196
2197 // Bind all defined routes to `Backbone.history`. We have to reverse the
2198 // order of the routes here to support behavior where the most general
2199 // routes can be defined at the bottom of the route map.
2200 _bindRoutes: function() {
2201 if (!this.routes) return;
2202 var route, routes = _.keys(this.routes);
2203 while ((route = routes.pop()) != null) {
2204 this.route(route, this.routes[route]);
2205 }
2206 },
2207
2208 // Convert a route string into a regular expression, suitable for matching
2209 // against the current location hash.
2210 _routeToRegExp: function(route) {
2211 route = route.replace(escapeRegExp, '\\$&')
2212 .replace(optionalParam, '(?:$1)?')
2213 .replace(namedParam, function(match, optional){
2214 return optional ? match : '([^\/]+)';
2215 })
2216 .replace(splatParam, '(.*?)');
2217 return new RegExp('^' + route + '$');
2218 },
2219
2220 // Given a route, and a URL fragment that it matches, return the array of
2221 // extracted parameters.
2222 _extractParameters: function(route, fragment) {
2223 return route.exec(fragment).slice(1);
2224 }
2225
2226 });
2227
2228 // Backbone.History
2229 // ----------------
2230
2231 // Handles cross-browser history management, based on URL fragments. If the
2232 // browser does not support `onhashchange`, falls back to polling.
2233 var History = Backbone.History = function() {
2234 this.handlers = [];
2235 _.bindAll(this, 'checkUrl');
2236
2237 // Ensure that `History` can be used outside of the browser.
2238 if (typeof window !== 'undefined') {
2239 this.location = window.location;
2240 this.history = window.history;
2241 }
2242 };
2243
2244 // Cached regex for stripping a leading hash/slash and trailing space.
2245 var routeStripper = /^[#\/]|\s+$/g;
2246
2247 // Cached regex for stripping leading and trailing slashes.
2248 var rootStripper = /^\/+|\/+$/g;
2249
2250 // Cached regex for detecting MSIE.
2251 var isExplorer = /msie [\w.]+/;
2252
2253 // Cached regex for removing a trailing slash.
2254 var trailingSlash = /\/$/;
2255
2256 // Has the history handling already been started?
2257 History.started = false;
2258
2259 // Set up all inheritable **Backbone.History** properties and methods.
2260 _.extend(History.prototype, Events, {
2261
2262 // The default interval to poll for hash changes, if necessary, is
2263 // twenty times a second.
2264 interval: 50,
2265
2266 // Gets the true hash value. Cannot use location.hash directly due to bug
2267 // in Firefox where location.hash will always be decoded.
2268 getHash: function(window) {
2269 var match = (window || this).location.href.match(/#(.*)$/);
2270 return match ? match[1] : '';
2271 },
2272
2273 // Get the cross-browser normalized URL fragment, either from the URL,
2274 // the hash, or the override.
2275 getFragment: function(fragment, forcePushState) {
2276 if (fragment == null) {
2277 if (this._hasPushState || !this._wantsHashChange || forcePushState) {
2278 fragment = this.location.pathname;
2279 var root = this.root.replace(trailingSlash, '');
2280 if (!fragment.indexOf(root)) fragment = fragment.substr(root.length);
2281 } else {
2282 fragment = this.getHash();
2283 }
2284 }
2285 return fragment.replace(routeStripper, '');
2286 },
2287
2288 // Start the hash change handling, returning `true` if the current URL matches
2289 // an existing route, and `false` otherwise.
2290 start: function(options) {
2291 if (History.started) throw new Error("Backbone.history has already been started");
2292 History.started = true;
2293
2294 // Figure out the initial configuration. Do we need an iframe?
2295 // Is pushState desired ... is it available?
2296 this.options = _.extend({}, {root: '/'}, this.options, options);
2297 this.root = this.options.root;
2298 this._wantsHashChange = this.options.hashChange !== false;
2299 this._wantsPushState = !!this.options.pushState;
2300 this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState);
2301 var fragment = this.getFragment();
2302 var docMode = document.documentMode;
2303 var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
2304
2305 // Normalize root to always include a leading and trailing slash.
2306 this.root = ('/' + this.root + '/').replace(rootStripper, '/');
2307
2308 if (oldIE && this._wantsHashChange) {
2309 this.iframe = Backbone.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
2310 this.navigate(fragment);
2311 }
2312
2313 // Depending on whether we're using pushState or hashes, and whether
2314 // 'onhashchange' is supported, determine how we check the URL state.
2315 if (this._hasPushState) {
2316 Backbone.$(window).on('popstate', this.checkUrl);
2317 } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
2318 Backbone.$(window).on('hashchange', this.checkUrl);
2319 } else if (this._wantsHashChange) {
2320 this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
2321 }
2322
2323 // Determine if we need to change the base url, for a pushState link
2324 // opened by a non-pushState browser.
2325 this.fragment = fragment;
2326 var loc = this.location;
2327 var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === this.root;
2328
2329 // If we've started off with a route from a `pushState`-enabled browser,
2330 // but we're currently in a browser that doesn't support it...
2331 if (this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) {
2332 this.fragment = this.getFragment(null, true);
2333 this.location.replace(this.root + this.location.search + '#' + this.fragment);
2334 // Return immediately as browser will do redirect to new url
2335 return true;
2336
2337 // Or if we've started out with a hash-based route, but we're currently
2338 // in a browser where it could be `pushState`-based instead...
2339 } else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
2340 this.fragment = this.getHash().replace(routeStripper, '');
2341 this.history.replaceState({}, document.title, this.root + this.fragment + loc.search);
2342 }
2343
2344 if (!this.options.silent) return this.loadUrl();
2345 },
2346
2347 // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
2348 // but possibly useful for unit testing Routers.
2349 stop: function() {
2350 Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
2351 clearInterval(this._checkUrlInterval);
2352 History.started = false;
2353 },
2354
2355 // Add a route to be tested when the fragment changes. Routes added later
2356 // may override previous routes.
2357 route: function(route, callback) {
2358 this.handlers.unshift({route: route, callback: callback});
2359 },
2360
2361 // Checks the current URL to see if it has changed, and if it has,
2362 // calls `loadUrl`, normalizing across the hidden iframe.
2363 checkUrl: function(e) {
2364 var current = this.getFragment();
2365 if (current === this.fragment && this.iframe) {
2366 current = this.getFragment(this.getHash(this.iframe));
2367 }
2368 if (current === this.fragment) return false;
2369 if (this.iframe) this.navigate(current);
2370 this.loadUrl() || this.loadUrl(this.getHash());
2371 },
2372
2373 // Attempt to load the current URL fragment. If a route succeeds with a
2374 // match, returns `true`. If no defined routes matches the fragment,
2375 // returns `false`.
2376 loadUrl: function(fragmentOverride) {
2377 var fragment = this.fragment = this.getFragment(fragmentOverride);
2378 var matched = _.any(this.handlers, function(handler) {
2379 if (handler.route.test(fragment)) {
2380 handler.callback(fragment);
2381 return true;
2382 }
2383 });
2384 return matched;
2385 },
2386
2387 // Save a fragment into the hash history, or replace the URL state if the
2388 // 'replace' option is passed. You are responsible for properly URL-encoding
2389 // the fragment in advance.
2390 //
2391 // The options object can contain `trigger: true` if you wish to have the
2392 // route callback be fired (not usually desirable), or `replace: true`, if
2393 // you wish to modify the current URL without adding an entry to the history.
2394 navigate: function(fragment, options) {
2395 if (!History.started) return false;
2396 if (!options || options === true) options = {trigger: options};
2397 fragment = this.getFragment(fragment || '');
2398 if (this.fragment === fragment) return;
2399 this.fragment = fragment;
2400 var url = this.root + fragment;
2401
2402 // If pushState is available, we use it to set the fragment as a real URL.
2403 if (this._hasPushState) {
2404 this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
2405
2406 // If hash changes haven't been explicitly disabled, update the hash
2407 // fragment to store history.
2408 } else if (this._wantsHashChange) {
2409 this._updateHash(this.location, fragment, options.replace);
2410 if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
2411 // Opening and closing the iframe tricks IE7 and earlier to push a
2412 // history entry on hash-tag change. When replace is true, we don't
2413 // want this.
2414 if(!options.replace) this.iframe.document.open().close();
2415 this._updateHash(this.iframe.location, fragment, options.replace);
2416 }
2417
2418 // If you've told us that you explicitly don't want fallback hashchange-
2419 // based history, then `navigate` becomes a page refresh.
2420 } else {
2421 return this.location.assign(url);
2422 }
2423 if (options.trigger) this.loadUrl(fragment);
2424 },
2425
2426 // Update the hash location, either replacing the current entry, or adding
2427 // a new one to the browser history.
2428 _updateHash: function(location, fragment, replace) {
2429 if (replace) {
2430 var href = location.href.replace(/(javascript:|#).*$/, '');
2431 location.replace(href + '#' + fragment);
2432 } else {
2433 // Some browsers require that `hash` contains a leading #.
2434 location.hash = '#' + fragment;
2435 }
2436 }
2437
2438 });
2439
2440 // Create the default Backbone.history.
2441 Backbone.history = new History;
2442
2443 // Backbone.View
2444 // -------------
2445
2446 // Creating a Backbone.View creates its initial element outside of the DOM,
2447 // if an existing element is not provided...
2448 var View = Backbone.View = function(options) {
2449 this.cid = _.uniqueId('view');
2450 this._configure(options || {});
2451 this._ensureElement();
2452 this.initialize.apply(this, arguments);
2453 this.delegateEvents();
2454 };
2455
2456 // Cached regex to split keys for `delegate`.
2457 var delegateEventSplitter = /^(\S+)\s*(.*)$/;
2458
2459 // List of view options to be merged as properties.
2460 var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
2461
2462 // Set up all inheritable **Backbone.View** properties and methods.
2463 _.extend(View.prototype, Events, {
2464
2465 // The default `tagName` of a View's element is `"div"`.
2466 tagName: 'div',
2467
2468 // jQuery delegate for element lookup, scoped to DOM elements within the
2469 // current view. This should be prefered to global lookups where possible.
2470 $: function(selector) {
2471 return this.$el.find(selector);
2472 },
2473
2474 // Initialize is an empty function by default. Override it with your own
2475 // initialization logic.
2476 initialize: function(){},
2477
2478 // **render** is the core function that your view should override, in order
2479 // to populate its element (`this.el`), with the appropriate HTML. The
2480 // convention is for **render** to always return `this`.
2481 render: function() {
2482 return this;
2483 },
2484
2485 // Remove this view by taking the element out of the DOM, and removing any
2486 // applicable Backbone.Events listeners.
2487 remove: function() {
2488 this.$el.remove();
2489 this.stopListening();
2490 return this;
2491 },
2492
2493 // Change the view's element (`this.el` property), including event
2494 // re-delegation.
2495 setElement: function(element, delegate) {
2496 if (this.$el) this.undelegateEvents();
2497 this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
2498 this.el = this.$el[0];
2499 if (delegate !== false) this.delegateEvents();
2500 return this;
2501 },
2502
2503 // Set callbacks, where `this.events` is a hash of
2504 //
2505 // *{"event selector": "callback"}*
2506 //
2507 // {
2508 // 'mousedown .title': 'edit',
2509 // 'click .button': 'save'
2510 // 'click .open': function(e) { ... }
2511 // }
2512 //
2513 // pairs. Callbacks will be bound to the view, with `this` set properly.
2514 // Uses event delegation for efficiency.
2515 // Omitting the selector binds the event to `this.el`.
2516 // This only works for delegate-able events: not `focus`, `blur`, and
2517 // not `change`, `submit`, and `reset` in Internet Explorer.
2518 delegateEvents: function(events) {
2519 if (!(events || (events = _.result(this, 'events')))) return;
2520 this.undelegateEvents();
2521 for (var key in events) {
2522 var method = events[key];
2523 if (!_.isFunction(method)) method = this[events[key]];
2524 if (!method) throw new Error('Method "' + events[key] + '" does not exist');
2525 var match = key.match(delegateEventSplitter);
2526 var eventName = match[1], selector = match[2];
2527 method = _.bind(method, this);
2528 eventName += '.delegateEvents' + this.cid;
2529 if (selector === '') {
2530 this.$el.on(eventName, method);
2531 } else {
2532 this.$el.on(eventName, selector, method);
2533 }
2534 }
2535 },
2536
2537 // Clears all callbacks previously bound to the view with `delegateEvents`.
2538 // You usually don't need to use this, but may wish to if you have multiple
2539 // Backbone views attached to the same DOM element.
2540 undelegateEvents: function() {
2541 this.$el.off('.delegateEvents' + this.cid);
2542 },
2543
2544 // Performs the initial configuration of a View with a set of options.
2545 // Keys with special meaning *(model, collection, id, className)*, are
2546 // attached directly to the view.
2547 _configure: function(options) {
2548 if (this.options) options = _.extend({}, _.result(this, 'options'), options);
2549 _.extend(this, _.pick(options, viewOptions));
2550 this.options = options;
2551 },
2552
2553 // Ensure that the View has a DOM element to render into.
2554 // If `this.el` is a string, pass it through `$()`, take the first
2555 // matching element, and re-assign it to `el`. Otherwise, create
2556 // an element from the `id`, `className` and `tagName` properties.
2557 _ensureElement: function() {
2558 if (!this.el) {
2559 var attrs = _.extend({}, _.result(this, 'attributes'));
2560 if (this.id) attrs.id = _.result(this, 'id');
2561 if (this.className) attrs['class'] = _.result(this, 'className');
2562 var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
2563 this.setElement($el, false);
2564 } else {
2565 this.setElement(_.result(this, 'el'), false);
2566 }
2567 }
2568
2569 });
2570
2571 // Backbone.sync
2572 // -------------
2573
2574 // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
2575 var methodMap = {
2576 'create': 'POST',
2577 'update': 'PUT',
2578 'patch': 'PATCH',
2579 'delete': 'DELETE',
2580 'read': 'GET'
2581 };
2582
2583 // Override this function to change the manner in which Backbone persists
2584 // models to the server. You will be passed the type of request, and the
2585 // model in question. By default, makes a RESTful Ajax request
2586 // to the model's `url()`. Some possible customizations could be:
2587 //
2588 // * Use `setTimeout` to batch rapid-fire updates into a single request.
2589 // * Send up the models as XML instead of JSON.
2590 // * Persist models via WebSockets instead of Ajax.
2591 //
2592 // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
2593 // as `POST`, with a `_method` parameter containing the true HTTP method,
2594 // as well as all requests with the body as `application/x-www-form-urlencoded`
2595 // instead of `application/json` with the model in a param named `model`.
2596 // Useful when interfacing with server-side languages like **PHP** that make
2597 // it difficult to read the body of `PUT` requests.
2598 Backbone.sync = function(method, model, options) {
2599 var type = methodMap[method];
2600
2601 // Default options, unless specified.
2602 _.defaults(options || (options = {}), {
2603 emulateHTTP: Backbone.emulateHTTP,
2604 emulateJSON: Backbone.emulateJSON
2605 });
2606
2607 // Default JSON-request options.
2608 var params = {type: type, dataType: 'json'};
2609
2610 // Ensure that we have a URL.
2611 if (!options.url) {
2612 params.url = _.result(model, 'url') || urlError();
2613 }
2614
2615 // Ensure that we have the appropriate request data.
2616 if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
2617 params.contentType = 'application/json';
2618 params.data = JSON.stringify(options.attrs || model.toJSON(options));
2619 }
2620
2621 // For older servers, emulate JSON by encoding the request into an HTML-form.
2622 if (options.emulateJSON) {
2623 params.contentType = 'application/x-www-form-urlencoded';
2624 params.data = params.data ? {model: params.data} : {};
2625 }
2626
2627 // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
2628 // And an `X-HTTP-Method-Override` header.
2629 if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
2630 params.type = 'POST';
2631 if (options.emulateJSON) params.data._method = type;
2632 var beforeSend = options.beforeSend;
2633 options.beforeSend = function(xhr) {
2634 xhr.setRequestHeader('X-HTTP-Method-Override', type);
2635 if (beforeSend) return beforeSend.apply(this, arguments);
2636 };
2637 }
2638
2639 // Don't process data on a non-GET request.
2640 if (params.type !== 'GET' && !options.emulateJSON) {
2641 params.processData = false;
2642 }
2643
2644 var success = options.success;
2645 options.success = function(resp) {
2646 if (success) success(model, resp, options);
2647 model.trigger('sync', model, resp, options);
2648 };
2649
2650 var error = options.error;
2651 options.error = function(xhr) {
2652 if (error) error(model, xhr, options);
2653 model.trigger('error', model, xhr, options);
2654 };
2655
2656 // Make the request, allowing the user to override any Ajax options.
2657 var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
2658 model.trigger('request', model, xhr, options);
2659 return xhr;
2660 };
2661
2662 // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
2663 Backbone.ajax = function() {
2664 return Backbone.$.ajax.apply(Backbone.$, arguments);
2665 };
2666
2667 // Helpers
2668 // -------
2669
2670 // Helper function to correctly set up the prototype chain, for subclasses.
2671 // Similar to `goog.inherits`, but uses a hash of prototype properties and
2672 // class properties to be extended.
2673 var extend = function(protoProps, staticProps) {
2674 var parent = this;
2675 var child;
2676
2677 // The constructor function for the new subclass is either defined by you
2678 // (the "constructor" property in your `extend` definition), or defaulted
2679 // by us to simply call the parent's constructor.
2680 if (protoProps && _.has(protoProps, 'constructor')) {
2681 child = protoProps.constructor;
2682 } else {
2683 child = function(){ return parent.apply(this, arguments); };
2684 }
2685
2686 // Add static properties to the constructor function, if supplied.
2687 _.extend(child, parent, staticProps);
2688
2689 // Set the prototype chain to inherit from `parent`, without calling
2690 // `parent`'s constructor function.
2691 var Surrogate = function(){ this.constructor = child; };
2692 Surrogate.prototype = parent.prototype;
2693 child.prototype = new Surrogate;
2694
2695 // Add prototype properties (instance properties) to the subclass,
2696 // if supplied.
2697 if (protoProps) _.extend(child.prototype, protoProps);
2698
2699 // Set a convenience property in case the parent's prototype is needed
2700 // later.
2701 child.__super__ = parent.prototype;
2702
2703 return child;
2704 };
2705
2706 // Set up inheritance for the model, collection, router, view and history.
2707 Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
2708
2709 // Throw an error when a URL is needed, and none is supplied.
2710 var urlError = function() {
2711 throw new Error('A "url" property or function must be specified');
2712 };
2713
2714}).call(this);
2715/*
2716UriTemplates Template Processor - Version: @VERSION - Dated: @DATE
2717(c) marc.portier@gmail.com - 2011-2012
2718Licensed under ALPv2
2719*/
2720
2721;
2722var uritemplate = (function() {
2723
2724// Below are the functions we originally used from jQuery.
2725// The implementations below are often more naive then what is inside jquery, but they suffice for our needs.
2726
2727function isFunction(fn) {
2728 return typeof fn == 'function';
2729}
2730
2731function isEmptyObject (obj) {
2732 for(var name in obj){
2733 return false;
2734 }
2735 return true;
2736}
2737
2738function extend(base, newprops) {
2739 for (var name in newprops) {
2740 base[name] = newprops[name];
2741 }
2742 return base;
2743}
2744
2745/**
2746 * Create a runtime cache around retrieved values from the context.
2747 * This allows for dynamic (function) results to be kept the same for multiple
2748 * occuring expansions within one template.
2749 * Note: Uses key-value tupples to be able to cache null values as well.
2750 */
2751 //TODO move this into prep-processing
2752function CachingContext(context) {
2753 this.raw = context;
2754 this.cache = {};
2755}
2756CachingContext.prototype.get = function(key) {
2757 var val = this.lookupRaw(key);
2758 var result = val;
2759
2760 if (isFunction(val)) { // check function-result-cache
2761 var tupple = this.cache[key];
2762 if (tupple !== null && tupple !== undefined) {
2763 result = tupple.val;
2764 } else {
2765 result = val(this.raw);
2766 this.cache[key] = {key: key, val: result};
2767 // NOTE: by storing tupples we make sure a null return is validly consistent too in expansions
2768 }
2769 }
2770 return result;
2771};
2772
2773CachingContext.prototype.lookupRaw = function(key) {
2774 return CachingContext.lookup(this, this.raw, key);
2775};
2776
2777CachingContext.lookup = function(me, context, key) {
2778 var result = context[key];
2779 if (result !== undefined) {
2780 return result;
2781 } else {
2782 var keyparts = key.split('.');
2783 var i = 0, keysplits = keyparts.length - 1;
2784 for (i = 0; i<keysplits; i++) {
2785 var leadKey = keyparts.slice(0, keysplits - i).join('.');
2786 var trailKey = keyparts.slice(-i-1).join('.');
2787 var leadContext = context[leadKey];
2788 if (leadContext !== undefined) {
2789 return CachingContext.lookup(me, leadContext, trailKey);
2790 }
2791 }
2792 return undefined;
2793 }
2794};
2795
2796
2797function UriTemplate(set) {
2798 this.set = set;
2799}
2800
2801UriTemplate.prototype.expand = function(context) {
2802 var cache = new CachingContext(context);
2803 var res = "";
2804 var i = 0, cnt = this.set.length;
2805 for (i = 0; i<cnt; i++ ) {
2806 res += this.set[i].expand(cache);
2807 }
2808 return res;
2809};
2810
2811//TODO: change since draft-0.6 about characters in literals
2812/* extract:
2813The characters outside of expressions in a URI Template string are intended to be copied literally to the URI-reference if the character is allowed in a URI (reserved / unreserved / pct-encoded) or, if not allowed, copied to the URI-reference in its UTF-8 pct-encoded form.
2814*/
2815function Literal(txt ) {
2816 this.txt = txt;
2817}
2818
2819Literal.prototype.expand = function() {
2820 return this.txt;
2821};
2822
2823
2824
2825var RESERVEDCHARS_RE = new RegExp("[:/?#\\[\\]@!$&()*+,;=']","g");
2826function encodeNormal(val) {
2827 return encodeURIComponent(val).replace(RESERVEDCHARS_RE, function(s) {return escape(s);} );
2828}
2829
2830//var SELECTEDCHARS_RE = new RegExp("[]","g");
2831function encodeReserved(val) {
2832 //return encodeURI(val).replace(SELECTEDCHARS_RE, function(s) {return escape(s)} );
2833 return encodeURI(val); // no need for additional replace if selected-chars is empty
2834}
2835
2836
2837function addUnNamed(name, key, val) {
2838 return key + (key.length > 0 ? "=" : "") + val;
2839}
2840
2841function addNamed(name, key, val, noName) {
2842 noName = noName || false;
2843 if (noName) { name = ""; }
2844
2845 if (!key || key.length === 0) {
2846 key = name;
2847 }
2848 return key + (key.length > 0 ? "=" : "") + val;
2849}
2850
2851function addLabeled(name, key, val, noName) {
2852 noName = noName || false;
2853 if (noName) { name = ""; }
2854
2855 if (!key || key.length === 0) {
2856 key = name;
2857 }
2858 return key + (key.length > 0 && val ? "=" : "") + val;
2859}
2860
2861
2862var simpleConf = {
2863 prefix : "", joiner : ",", encode : encodeNormal, builder : addUnNamed
2864};
2865var reservedConf = {
2866 prefix : "", joiner : ",", encode : encodeReserved, builder : addUnNamed
2867};
2868var fragmentConf = {
2869 prefix : "#", joiner : ",", encode : encodeReserved, builder : addUnNamed
2870};
2871var pathParamConf = {
2872 prefix : ";", joiner : ";", encode : encodeNormal, builder : addLabeled
2873};
2874var formParamConf = {
2875 prefix : "?", joiner : "&", encode : encodeNormal, builder : addNamed
2876};
2877var formContinueConf = {
2878 prefix : "&", joiner : "&", encode : encodeNormal, builder : addNamed
2879};
2880var pathHierarchyConf = {
2881 prefix : "/", joiner : "/", encode : encodeNormal, builder : addUnNamed
2882};
2883var labelConf = {
2884 prefix : ".", joiner : ".", encode : encodeNormal, builder : addUnNamed
2885};
2886
2887
2888function Expression(conf, vars ) {
2889 extend(this, conf);
2890 this.vars = vars;
2891}
2892
2893Expression.build = function(ops, vars) {
2894 var conf;
2895 switch(ops) {
2896 case '' : conf = simpleConf; break;
2897 case '+' : conf = reservedConf; break;
2898 case '#' : conf = fragmentConf; break;
2899 case ';' : conf = pathParamConf; break;
2900 case '?' : conf = formParamConf; break;
2901 case '&' : conf = formContinueConf; break;
2902 case '/' : conf = pathHierarchyConf; break;
2903 case '.' : conf = labelConf; break;
2904 default : throw "Unexpected operator: '"+ops+"'";
2905 }
2906 return new Expression(conf, vars);
2907};
2908
2909Expression.prototype.expand = function(context) {
2910 var joiner = this.prefix;
2911 var nextjoiner = this.joiner;
2912 var buildSegment = this.builder;
2913 var res = "";
2914 var i = 0, cnt = this.vars.length;
2915
2916 for (i = 0 ; i< cnt; i++) {
2917 var varspec = this.vars[i];
2918 varspec.addValues(context, this.encode, function(key, val, noName) {
2919 var segm = buildSegment(varspec.name, key, val, noName);
2920 if (segm !== null && segm !== undefined) {
2921 res += joiner + segm;
2922 joiner = nextjoiner;
2923 }
2924 });
2925 }
2926 return res;
2927};
2928
2929
2930
2931var UNBOUND = {};
2932
2933/**
2934 * Helper class to help grow a string of (possibly encoded) parts until limit is reached
2935 */
2936function Buffer(limit) {
2937 this.str = "";
2938 if (limit === UNBOUND) {
2939 this.appender = Buffer.UnboundAppend;
2940 } else {
2941 this.len = 0;
2942 this.limit = limit;
2943 this.appender = Buffer.BoundAppend;
2944 }
2945}
2946
2947Buffer.prototype.append = function(part, encoder) {
2948 return this.appender(this, part, encoder);
2949};
2950
2951Buffer.UnboundAppend = function(me, part, encoder) {
2952 part = encoder ? encoder(part) : part;
2953 me.str += part;
2954 return me;
2955};
2956
2957Buffer.BoundAppend = function(me, part, encoder) {
2958 part = part.substring(0, me.limit - me.len);
2959 me.len += part.length;
2960
2961 part = encoder ? encoder(part) : part;
2962 me.str += part;
2963 return me;
2964};
2965
2966
2967function arrayToString(arr, encoder, maxLength) {
2968 var buffer = new Buffer(maxLength);
2969 var joiner = "";
2970
2971 var i = 0, cnt = arr.length;
2972 for (i=0; i<cnt; i++) {
2973 if (arr[i] !== null && arr[i] !== undefined) {
2974 buffer.append(joiner).append(arr[i], encoder);
2975 joiner = ",";
2976 }
2977 }
2978 return buffer.str;
2979}
2980
2981function objectToString(obj, encoder, maxLength) {
2982 var buffer = new Buffer(maxLength);
2983 var joiner = "";
2984 var k;
2985
2986 for (k in obj) {
2987 if (obj.hasOwnProperty(k) ) {
2988 if (obj[k] !== null && obj[k] !== undefined) {
2989 buffer.append(joiner + k + ',').append(obj[k], encoder);
2990 joiner = ",";
2991 }
2992 }
2993 }
2994 return buffer.str;
2995}
2996
2997
2998function simpleValueHandler(me, val, valprops, encoder, adder) {
2999 var result;
3000
3001 if (valprops.isArr) {
3002 result = arrayToString(val, encoder, me.maxLength);
3003 } else if (valprops.isObj) {
3004 result = objectToString(val, encoder, me.maxLength);
3005 } else {
3006 var buffer = new Buffer(me.maxLength);
3007 result = buffer.append(val, encoder).str;
3008 }
3009
3010 adder("", result);
3011}
3012
3013function explodeValueHandler(me, val, valprops, encoder, adder) {
3014 if (valprops.isArr) {
3015 var i = 0, cnt = val.length;
3016 for (i = 0; i<cnt; i++) {
3017 adder("", encoder(val[i]) );
3018 }
3019 } else if (valprops.isObj) {
3020 var k;
3021 for (k in val) {
3022 if (val.hasOwnProperty(k)) {
3023 adder(k, encoder(val[k]) );
3024 }
3025 }
3026 } else { // explode-requested, but single value
3027 adder("", encoder(val));
3028 }
3029}
3030
3031function valueProperties(val) {
3032 var isArr = false;
3033 var isObj = false;
3034 var isUndef = true; //note: "" is empty but not undef
3035
3036 if (val !== null && val !== undefined) {
3037 isArr = (val.constructor === Array);
3038 isObj = (val.constructor === Object);
3039 isUndef = (isArr && val.length === 0) || (isObj && isEmptyObject(val));
3040 }
3041
3042 return {isArr: isArr, isObj: isObj, isUndef: isUndef};
3043}
3044
3045
3046function VarSpec (name, vhfn, nums) {
3047 this.name = unescape(name);
3048 this.valueHandler = vhfn;
3049 this.maxLength = nums;
3050}
3051
3052
3053VarSpec.build = function(name, expl, part, nums) {
3054 var valueHandler, valueModifier;
3055
3056 if (!!expl) { //interprete as boolean
3057 valueHandler = explodeValueHandler;
3058 } else {
3059 valueHandler = simpleValueHandler;
3060 }
3061
3062 if (!part) {
3063 nums = UNBOUND;
3064 }
3065
3066 return new VarSpec(name, valueHandler, nums);
3067};
3068
3069
3070VarSpec.prototype.addValues = function(context, encoder, adder) {
3071 var val = context.get(this.name);
3072 var valprops = valueProperties(val);
3073 if (valprops.isUndef) { return; } // ignore empty values
3074 this.valueHandler(this, val, valprops, encoder, adder);
3075};
3076
3077
3078
3079//----------------------------------------------parsing logic
3080// How each varspec should look like
3081var VARSPEC_RE=/([^*:]*)((\*)|(:)([0-9]+))?/;
3082
3083var match2varspec = function(m) {
3084 var name = m[1];
3085 var expl = m[3];
3086 var part = m[4];
3087 var nums = parseInt(m[5], 10);
3088
3089 return VarSpec.build(name, expl, part, nums);
3090};
3091
3092
3093// Splitting varspecs in list with:
3094var LISTSEP=",";
3095
3096// How each template should look like
3097var TEMPL_RE=/(\{([+#.;?&\/])?(([^.*:,{}|@!=$()][^*:,{}$()]*)(\*|:([0-9]+))?(,([^.*:,{}][^*:,{}]*)(\*|:([0-9]+))?)*)\})/g;
3098// Note: reserved operators: |!@ are left out of the regexp in order to make those templates degrade into literals
3099// (as expected by the spec - see tests.html "reserved operators")
3100
3101
3102var match2expression = function(m) {
3103 var expr = m[0];
3104 var ops = m[2] || '';
3105 var vars = m[3].split(LISTSEP);
3106 var i = 0, len = vars.length;
3107 for (i = 0; i<len; i++) {
3108 var match;
3109 if ( (match = vars[i].match(VARSPEC_RE)) === null) {
3110 throw "unexpected parse error in varspec: " + vars[i];
3111 }
3112 vars[i] = match2varspec(match);
3113 }
3114
3115 return Expression.build(ops, vars);
3116};
3117
3118
3119var pushLiteralSubstr = function(set, src, from, to) {
3120 if (from < to) {
3121 var literal = src.substr(from, to - from);
3122 set.push(new Literal(literal));
3123 }
3124};
3125
3126var parse = function(str) {
3127 var lastpos = 0;
3128 var comp = [];
3129
3130 var match;
3131 var pattern = TEMPL_RE;
3132 pattern.lastIndex = 0; // just to be sure
3133 while ((match = pattern.exec(str)) !== null) {
3134 var newpos = match.index;
3135 pushLiteralSubstr(comp, str, lastpos, newpos);
3136
3137 comp.push(match2expression(match));
3138 lastpos = pattern.lastIndex;
3139 }
3140 pushLiteralSubstr(comp, str, lastpos, str.length);
3141
3142 return new UriTemplate(comp);
3143};
3144
3145
3146//-------------------------------------------comments and ideas
3147
3148//TODO: consider building cache of previously parsed uris or even parsed expressions?
3149
3150return parse;
3151
3152}());
3153/*! URI.js v1.14.1 http://medialize.github.io/URI.js/ */
3154/* build contains: IPv6.js, punycode.js, SecondLevelDomains.js, URI.js, URITemplate.js */
3155(function(f,l){"object"===typeof exports?module.exports=l():"function"===typeof define&&define.amd?define(l):f.IPv6=l(f)})(this,function(f){var l=f&&f.IPv6;return{best:function(g){g=g.toLowerCase().split(":");var m=g.length,b=8;""===g[0]&&""===g[1]&&""===g[2]?(g.shift(),g.shift()):""===g[0]&&""===g[1]?g.shift():""===g[m-1]&&""===g[m-2]&&g.pop();m=g.length;-1!==g[m-1].indexOf(".")&&(b=7);var k;for(k=0;k<m&&""!==g[k];k++);if(k<b)for(g.splice(k,1,"0000");g.length<b;)g.splice(k,0,"0000");for(k=0;k<b;k++){for(var m=
3156g[k].split(""),f=0;3>f;f++)if("0"===m[0]&&1<m.length)m.splice(0,1);else break;g[k]=m.join("")}var m=-1,l=f=0,h=-1,r=!1;for(k=0;k<b;k++)r?"0"===g[k]?l+=1:(r=!1,l>f&&(m=h,f=l)):"0"===g[k]&&(r=!0,h=k,l=1);l>f&&(m=h,f=l);1<f&&g.splice(m,f,"");m=g.length;b="";""===g[0]&&(b=":");for(k=0;k<m;k++){b+=g[k];if(k===m-1)break;b+=":"}""===g[m-1]&&(b+=":");return b},noConflict:function(){f.IPv6===this&&(f.IPv6=l);return this}}});
3157(function(f){function l(b){throw RangeError(v[b]);}function g(b,e){for(var h=b.length;h--;)b[h]=e(b[h]);return b}function m(b,e){return g(b.split(u),e).join(".")}function b(b){for(var e=[],h=0,a=b.length,c,d;h<a;)c=b.charCodeAt(h++),55296<=c&&56319>=c&&h<a?(d=b.charCodeAt(h++),56320==(d&64512)?e.push(((c&1023)<<10)+(d&1023)+65536):(e.push(c),h--)):e.push(c);return e}function k(b){return g(b,function(b){var e="";65535<b&&(b-=65536,e+=A(b>>>10&1023|55296),b=56320|b&1023);return e+=A(b)}).join("")}function y(b,
3158e){return b+22+75*(26>b)-((0!=e)<<5)}function p(b,e,h){var a=0;b=h?q(b/700):b>>1;for(b+=q(b/e);455<b;a+=36)b=q(b/35);return q(a+36*b/(b+38))}function h(b){var e=[],h=b.length,a,c=0,d=128,t=72,w,x,g,f,m;w=b.lastIndexOf("-");0>w&&(w=0);for(x=0;x<w;++x)128<=b.charCodeAt(x)&&l("not-basic"),e.push(b.charCodeAt(x));for(w=0<w?w+1:0;w<h;){x=c;a=1;for(g=36;;g+=36){w>=h&&l("invalid-input");f=b.charCodeAt(w++);f=10>f-48?f-22:26>f-65?f-65:26>f-97?f-97:36;(36<=f||f>q((2147483647-c)/a))&&l("overflow");c+=f*a;m=
3159g<=t?1:g>=t+26?26:g-t;if(f<m)break;f=36-m;a>q(2147483647/f)&&l("overflow");a*=f}a=e.length+1;t=p(c-x,a,0==x);q(c/a)>2147483647-d&&l("overflow");d+=q(c/a);c%=a;e.splice(c++,0,d)}return k(e)}function r(e){var h,g,a,c,d,t,w,x,f,m=[],r,k,n;e=b(e);r=e.length;h=128;g=0;d=72;for(t=0;t<r;++t)f=e[t],128>f&&m.push(A(f));for((a=c=m.length)&&m.push("-");a<r;){w=2147483647;for(t=0;t<r;++t)f=e[t],f>=h&&f<w&&(w=f);k=a+1;w-h>q((2147483647-g)/k)&&l("overflow");g+=(w-h)*k;h=w;for(t=0;t<r;++t)if(f=e[t],f<h&&2147483647<
3160++g&&l("overflow"),f==h){x=g;for(w=36;;w+=36){f=w<=d?1:w>=d+26?26:w-d;if(x<f)break;n=x-f;x=36-f;m.push(A(y(f+n%x,0)));x=q(n/x)}m.push(A(y(x,0)));d=p(g,k,a==c);g=0;++a}++g;++h}return m.join("")}var B="object"==typeof exports&&exports,C="object"==typeof module&&module&&module.exports==B&&module,z="object"==typeof global&&global;if(z.global===z||z.window===z)f=z;var s,n=/^xn--/,e=/[^ -~]/,u=/\x2E|\u3002|\uFF0E|\uFF61/g,v={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)",
3161"invalid-input":"Invalid input"},q=Math.floor,A=String.fromCharCode,D;s={version:"1.2.3",ucs2:{decode:b,encode:k},decode:h,encode:r,toASCII:function(b){return m(b,function(b){return e.test(b)?"xn--"+r(b):b})},toUnicode:function(b){return m(b,function(b){return n.test(b)?h(b.slice(4).toLowerCase()):b})}};if("function"==typeof define&&"object"==typeof define.amd&&define.amd)define(function(){return s});else if(B&&!B.nodeType)if(C)C.exports=s;else for(D in s)s.hasOwnProperty(D)&&(B[D]=s[D]);else f.punycode=
3162s})(this);
3163(function(f,l){"object"===typeof exports?module.exports=l():"function"===typeof define&&define.amd?define(l):f.SecondLevelDomains=l(f)})(this,function(f){var l=f&&f.SecondLevelDomains,g={list:{ac:" com gov mil net org ",ae:" ac co gov mil name net org pro sch ",af:" com edu gov net org ",al:" com edu gov mil net org ",ao:" co ed gv it og pb ",ar:" com edu gob gov int mil net org tur ",at:" ac co gv or ",au:" asn com csiro edu gov id net org ",ba:" co com edu gov mil net org rs unbi unmo unsa untz unze ",bb:" biz co com edu gov info net org store tv ",
3164bh:" biz cc com edu gov info net org ",bn:" com edu gov net org ",bo:" com edu gob gov int mil net org tv ",br:" adm adv agr am arq art ato b bio blog bmd cim cng cnt com coop ecn edu eng esp etc eti far flog fm fnd fot fst g12 ggf gov imb ind inf jor jus lel mat med mil mus net nom not ntr odo org ppg pro psc psi qsl rec slg srv tmp trd tur tv vet vlog wiki zlg ",bs:" com edu gov net org ",bz:" du et om ov rg ",ca:" ab bc mb nb nf nl ns nt nu on pe qc sk yk ",ck:" biz co edu gen gov info net org ",
3165cn:" ac ah bj com cq edu fj gd gov gs gx gz ha hb he hi hl hn jl js jx ln mil net nm nx org qh sc sd sh sn sx tj tw xj xz yn zj ",co:" com edu gov mil net nom org ",cr:" ac c co ed fi go or sa ",cy:" ac biz com ekloges gov ltd name net org parliament press pro tm ","do":" art com edu gob gov mil net org sld web ",dz:" art asso com edu gov net org pol ",ec:" com edu fin gov info med mil net org pro ",eg:" com edu eun gov mil name net org sci ",er:" com edu gov ind mil net org rochest w ",es:" com edu gob nom org ",
3166et:" biz com edu gov info name net org ",fj:" ac biz com info mil name net org pro ",fk:" ac co gov net nom org ",fr:" asso com f gouv nom prd presse tm ",gg:" co net org ",gh:" com edu gov mil org ",gn:" ac com gov net org ",gr:" com edu gov mil net org ",gt:" com edu gob ind mil net org ",gu:" com edu gov net org ",hk:" com edu gov idv net org ",hu:" 2000 agrar bolt casino city co erotica erotika film forum games hotel info ingatlan jogasz konyvelo lakas media news org priv reklam sex shop sport suli szex tm tozsde utazas video ",
3167id:" ac co go mil net or sch web ",il:" ac co gov idf k12 muni net org ","in":" ac co edu ernet firm gen gov i ind mil net nic org res ",iq:" com edu gov i mil net org ",ir:" ac co dnssec gov i id net org sch ",it:" edu gov ",je:" co net org ",jo:" com edu gov mil name net org sch ",jp:" ac ad co ed go gr lg ne or ",ke:" ac co go info me mobi ne or sc ",kh:" com edu gov mil net org per ",ki:" biz com de edu gov info mob net org tel ",km:" asso com coop edu gouv k medecin mil nom notaires pharmaciens presse tm veterinaire ",
3168kn:" edu gov net org ",kr:" ac busan chungbuk chungnam co daegu daejeon es gangwon go gwangju gyeongbuk gyeonggi gyeongnam hs incheon jeju jeonbuk jeonnam k kg mil ms ne or pe re sc seoul ulsan ",kw:" com edu gov net org ",ky:" com edu gov net org ",kz:" com edu gov mil net org ",lb:" com edu gov net org ",lk:" assn com edu gov grp hotel int ltd net ngo org sch soc web ",lr:" com edu gov net org ",lv:" asn com conf edu gov id mil net org ",ly:" com edu gov id med net org plc sch ",ma:" ac co gov m net org press ",
3169mc:" asso tm ",me:" ac co edu gov its net org priv ",mg:" com edu gov mil nom org prd tm ",mk:" com edu gov inf name net org pro ",ml:" com edu gov net org presse ",mn:" edu gov org ",mo:" com edu gov net org ",mt:" com edu gov net org ",mv:" aero biz com coop edu gov info int mil museum name net org pro ",mw:" ac co com coop edu gov int museum net org ",mx:" com edu gob net org ",my:" com edu gov mil name net org sch ",nf:" arts com firm info net other per rec store web ",ng:" biz com edu gov mil mobi name net org sch ",
3170ni:" ac co com edu gob mil net nom org ",np:" com edu gov mil net org ",nr:" biz com edu gov info net org ",om:" ac biz co com edu gov med mil museum net org pro sch ",pe:" com edu gob mil net nom org sld ",ph:" com edu gov i mil net ngo org ",pk:" biz com edu fam gob gok gon gop gos gov net org web ",pl:" art bialystok biz com edu gda gdansk gorzow gov info katowice krakow lodz lublin mil net ngo olsztyn org poznan pwr radom slupsk szczecin torun warszawa waw wroc wroclaw zgora ",pr:" ac biz com edu est gov info isla name net org pro prof ",
3171ps:" com edu gov net org plo sec ",pw:" belau co ed go ne or ",ro:" arts com firm info nom nt org rec store tm www ",rs:" ac co edu gov in org ",sb:" com edu gov net org ",sc:" com edu gov net org ",sh:" co com edu gov net nom org ",sl:" com edu gov net org ",st:" co com consulado edu embaixada gov mil net org principe saotome store ",sv:" com edu gob org red ",sz:" ac co org ",tr:" av bbs bel biz com dr edu gen gov info k12 name net org pol tel tsk tv web ",tt:" aero biz cat co com coop edu gov info int jobs mil mobi museum name net org pro tel travel ",
3172tw:" club com ebiz edu game gov idv mil net org ",mu:" ac co com gov net or org ",mz:" ac co edu gov org ",na:" co com ",nz:" ac co cri geek gen govt health iwi maori mil net org parliament school ",pa:" abo ac com edu gob ing med net nom org sld ",pt:" com edu gov int net nome org publ ",py:" com edu gov mil net org ",qa:" com edu gov mil net org ",re:" asso com nom ",ru:" ac adygeya altai amur arkhangelsk astrakhan bashkiria belgorod bir bryansk buryatia cbg chel chelyabinsk chita chukotka chuvashia com dagestan e-burg edu gov grozny int irkutsk ivanovo izhevsk jar joshkar-ola kalmykia kaluga kamchatka karelia kazan kchr kemerovo khabarovsk khakassia khv kirov koenig komi kostroma kranoyarsk kuban kurgan kursk lipetsk magadan mari mari-el marine mil mordovia mosreg msk murmansk nalchik net nnov nov novosibirsk nsk omsk orenburg org oryol penza perm pp pskov ptz rnd ryazan sakhalin samara saratov simbirsk smolensk spb stavropol stv surgut tambov tatarstan tom tomsk tsaritsyn tsk tula tuva tver tyumen udm udmurtia ulan-ude vladikavkaz vladimir vladivostok volgograd vologda voronezh vrn vyatka yakutia yamal yekaterinburg yuzhno-sakhalinsk ",
3173rw:" ac co com edu gouv gov int mil net ",sa:" com edu gov med net org pub sch ",sd:" com edu gov info med net org tv ",se:" a ac b bd c d e f g h i k l m n o org p parti pp press r s t tm u w x y z ",sg:" com edu gov idn net org per ",sn:" art com edu gouv org perso univ ",sy:" com edu gov mil net news org ",th:" ac co go in mi net or ",tj:" ac biz co com edu go gov info int mil name net nic org test web ",tn:" agrinet com defense edunet ens fin gov ind info intl mincom nat net org perso rnrt rns rnu tourism ",
3174tz:" ac co go ne or ",ua:" biz cherkassy chernigov chernovtsy ck cn co com crimea cv dn dnepropetrovsk donetsk dp edu gov if in ivano-frankivsk kh kharkov kherson khmelnitskiy kiev kirovograd km kr ks kv lg lugansk lutsk lviv me mk net nikolaev od odessa org pl poltava pp rovno rv sebastopol sumy te ternopil uzhgorod vinnica vn zaporizhzhe zhitomir zp zt ",ug:" ac co go ne or org sc ",uk:" ac bl british-library co cym gov govt icnet jet lea ltd me mil mod national-library-scotland nel net nhs nic nls org orgn parliament plc police sch scot soc ",
3175us:" dni fed isa kids nsn ",uy:" com edu gub mil net org ",ve:" co com edu gob info mil net org web ",vi:" co com k12 net org ",vn:" ac biz com edu gov health info int name net org pro ",ye:" co com gov ltd me net org plc ",yu:" ac co edu gov org ",za:" ac agric alt bourse city co cybernet db edu gov grondar iaccess imt inca landesign law mil net ngo nis nom olivetti org pix school tm web ",zm:" ac co com edu gov net org sch "},has:function(f){var b=f.lastIndexOf(".");if(0>=b||b>=f.length-1)return!1;
3176var k=f.lastIndexOf(".",b-1);if(0>=k||k>=b-1)return!1;var l=g.list[f.slice(b+1)];return l?0<=l.indexOf(" "+f.slice(k+1,b)+" "):!1},is:function(f){var b=f.lastIndexOf(".");if(0>=b||b>=f.length-1||0<=f.lastIndexOf(".",b-1))return!1;var k=g.list[f.slice(b+1)];return k?0<=k.indexOf(" "+f.slice(0,b)+" "):!1},get:function(f){var b=f.lastIndexOf(".");if(0>=b||b>=f.length-1)return null;var k=f.lastIndexOf(".",b-1);if(0>=k||k>=b-1)return null;var l=g.list[f.slice(b+1)];return!l||0>l.indexOf(" "+f.slice(k+
31771,b)+" ")?null:f.slice(k+1)},noConflict:function(){f.SecondLevelDomains===this&&(f.SecondLevelDomains=l);return this}};return g});
3178(function(f,l){"object"===typeof exports?module.exports=l(require("./punycode"),require("./IPv6"),require("./SecondLevelDomains")):"function"===typeof define&&define.amd?define(["./punycode","./IPv6","./SecondLevelDomains"],l):f.URI=l(f.punycode,f.IPv6,f.SecondLevelDomains,f)})(this,function(f,l,g,m){function b(a,c){if(!(this instanceof b))return new b(a,c);void 0===a&&(a="undefined"!==typeof location?location.href+"":"");this.href(a);return void 0!==c?this.absoluteTo(c):this}function k(a){return a.replace(/([.*+?^=!:${}()|[\]\/\\])/g,
3179"\\$1")}function y(a){return void 0===a?"Undefined":String(Object.prototype.toString.call(a)).slice(8,-1)}function p(a){return"Array"===y(a)}function h(a,c){var d,b;if(p(c)){d=0;for(b=c.length;d<b;d++)if(!h(a,c[d]))return!1;return!0}var e=y(c);d=0;for(b=a.length;d<b;d++)if("RegExp"===e){if("string"===typeof a[d]&&a[d].match(c))return!0}else if(a[d]===c)return!0;return!1}function r(a,c){if(!p(a)||!p(c)||a.length!==c.length)return!1;a.sort();c.sort();for(var d=0,b=a.length;d<b;d++)if(a[d]!==c[d])return!1;
3180return!0}function B(a){return escape(a)}function C(a){return encodeURIComponent(a).replace(/[!'()*]/g,B).replace(/\*/g,"%2A")}function z(a){return function(c,d){if(void 0===c)return this._parts[a]||"";this._parts[a]=c||null;this.build(!d);return this}}function s(a,c){return function(d,b){if(void 0===d)return this._parts[a]||"";null!==d&&(d+="",d.charAt(0)===c&&(d=d.substring(1)));this._parts[a]=d;this.build(!b);return this}}var n=m&&m.URI;b.version="1.14.1";var e=b.prototype,u=Object.prototype.hasOwnProperty;
3181b._parts=function(){return{protocol:null,username:null,password:null,hostname:null,urn:null,port:null,path:null,query:null,fragment:null,duplicateQueryParameters:b.duplicateQueryParameters,escapeQuerySpace:b.escapeQuerySpace}};b.duplicateQueryParameters=!1;b.escapeQuerySpace=!0;b.protocol_expression=/^[a-z][a-z0-9.+-]*$/i;b.idn_expression=/[^a-z0-9\.-]/i;b.punycode_expression=/(xn--)/i;b.ip4_expression=/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;b.ip6_expression=/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
3182b.find_uri_expression=/\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?\u00ab\u00bb\u201c\u201d\u2018\u2019]))/ig;b.findUri={start:/\b(?:([a-z][a-z0-9.+-]*:\/\/)|www\.)/gi,end:/[\s\r\n]|$/,trim:/[`!()\[\]{};:'".,<>?\u00ab\u00bb\u201c\u201d\u201e\u2018\u2019]+$/};b.defaultPorts={http:"80",https:"443",ftp:"21",gopher:"70",ws:"80",wss:"443"};b.invalid_hostname_characters=
3183/[^a-zA-Z0-9\.-]/;b.domAttributes={a:"href",blockquote:"cite",link:"href",base:"href",script:"src",form:"action",img:"src",area:"href",iframe:"src",embed:"src",source:"src",track:"src",input:"src",audio:"src",video:"src"};b.getDomAttribute=function(a){if(a&&a.nodeName){var c=a.nodeName.toLowerCase();return"input"===c&&"image"!==a.type?void 0:b.domAttributes[c]}};b.encode=C;b.decode=decodeURIComponent;b.iso8859=function(){b.encode=escape;b.decode=unescape};b.unicode=function(){b.encode=C;b.decode=
3184decodeURIComponent};b.characters={pathname:{encode:{expression:/%(24|26|2B|2C|3B|3D|3A|40)/ig,map:{"%24":"$","%26":"&","%2B":"+","%2C":",","%3B":";","%3D":"=","%3A":":","%40":"@"}},decode:{expression:/[\/\?#]/g,map:{"/":"%2F","?":"%3F","#":"%23"}}},reserved:{encode:{expression:/%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig,map:{"%3A":":","%2F":"/","%3F":"?","%23":"#","%5B":"[","%5D":"]","%40":"@","%21":"!","%24":"$","%26":"&","%27":"'","%28":"(","%29":")","%2A":"*","%2B":"+","%2C":",",
3185"%3B":";","%3D":"="}}}};b.encodeQuery=function(a,c){var d=b.encode(a+"");void 0===c&&(c=b.escapeQuerySpace);return c?d.replace(/%20/g,"+"):d};b.decodeQuery=function(a,c){a+="";void 0===c&&(c=b.escapeQuerySpace);try{return b.decode(c?a.replace(/\+/g,"%20"):a)}catch(d){return a}};b.recodePath=function(a){a=(a+"").split("/");for(var c=0,d=a.length;c<d;c++)a[c]=b.encodePathSegment(b.decode(a[c]));return a.join("/")};b.decodePath=function(a){a=(a+"").split("/");for(var c=0,d=a.length;c<d;c++)a[c]=b.decodePathSegment(a[c]);
3186return a.join("/")};var v={encode:"encode",decode:"decode"},q,A=function(a,c){return function(d){try{return b[c](d+"").replace(b.characters[a][c].expression,function(d){return b.characters[a][c].map[d]})}catch(t){return d}}};for(q in v)b[q+"PathSegment"]=A("pathname",v[q]);b.encodeReserved=A("reserved","encode");b.parse=function(a,c){var d;c||(c={});d=a.indexOf("#");-1<d&&(c.fragment=a.substring(d+1)||null,a=a.substring(0,d));d=a.indexOf("?");-1<d&&(c.query=a.substring(d+1)||null,a=a.substring(0,
3187d));"//"===a.substring(0,2)?(c.protocol=null,a=a.substring(2),a=b.parseAuthority(a,c)):(d=a.indexOf(":"),-1<d&&(c.protocol=a.substring(0,d)||null,c.protocol&&!c.protocol.match(b.protocol_expression)?c.protocol=void 0:"//"===a.substring(d+1,d+3)?(a=a.substring(d+3),a=b.parseAuthority(a,c)):(a=a.substring(d+1),c.urn=!0)));c.path=a;return c};b.parseHost=function(a,c){var d=a.indexOf("/"),b;-1===d&&(d=a.length);"["===a.charAt(0)?(b=a.indexOf("]"),c.hostname=a.substring(1,b)||null,c.port=a.substring(b+
31882,d)||null,"/"===c.port&&(c.port=null)):a.indexOf(":")!==a.lastIndexOf(":")?(c.hostname=a.substring(0,d)||null,c.port=null):(b=a.substring(0,d).split(":"),c.hostname=b[0]||null,c.port=b[1]||null);c.hostname&&"/"!==a.substring(d).charAt(0)&&(d++,a="/"+a);return a.substring(d)||"/"};b.parseAuthority=function(a,c){a=b.parseUserinfo(a,c);return b.parseHost(a,c)};b.parseUserinfo=function(a,c){var d=a.indexOf("/"),t=a.lastIndexOf("@",-1<d?d:a.length-1);-1<t&&(-1===d||t<d)?(d=a.substring(0,t).split(":"),
3189c.username=d[0]?b.decode(d[0]):null,d.shift(),c.password=d[0]?b.decode(d.join(":")):null,a=a.substring(t+1)):(c.username=null,c.password=null);return a};b.parseQuery=function(a,c){if(!a)return{};a=a.replace(/&+/g,"&").replace(/^\?*&*|&+$/g,"");if(!a)return{};for(var d={},t=a.split("&"),e=t.length,f,h,g=0;g<e;g++)f=t[g].split("="),h=b.decodeQuery(f.shift(),c),f=f.length?b.decodeQuery(f.join("="),c):null,u.call(d,h)?("string"===typeof d[h]&&(d[h]=[d[h]]),d[h].push(f)):d[h]=f;return d};b.build=function(a){var c=
3190"";a.protocol&&(c+=a.protocol+":");a.urn||!c&&!a.hostname||(c+="//");c+=b.buildAuthority(a)||"";"string"===typeof a.path&&("/"!==a.path.charAt(0)&&"string"===typeof a.hostname&&(c+="/"),c+=a.path);"string"===typeof a.query&&a.query&&(c+="?"+a.query);"string"===typeof a.fragment&&a.fragment&&(c+="#"+a.fragment);return c};b.buildHost=function(a){var c="";if(a.hostname)c=b.ip6_expression.test(a.hostname)?c+("["+a.hostname+"]"):c+a.hostname;else return"";a.port&&(c+=":"+a.port);return c};b.buildAuthority=
3191function(a){return b.buildUserinfo(a)+b.buildHost(a)};b.buildUserinfo=function(a){var c="";a.username&&(c+=b.encode(a.username),a.password&&(c+=":"+b.encode(a.password)),c+="@");return c};b.buildQuery=function(a,c,d){var t="",e,f,h,g;for(f in a)if(u.call(a,f)&&f)if(p(a[f]))for(e={},h=0,g=a[f].length;h<g;h++)void 0!==a[f][h]&&void 0===e[a[f][h]+""]&&(t+="&"+b.buildQueryParameter(f,a[f][h],d),!0!==c&&(e[a[f][h]+""]=!0));else void 0!==a[f]&&(t+="&"+b.buildQueryParameter(f,a[f],d));return t.substring(1)};
3192b.buildQueryParameter=function(a,c,d){return b.encodeQuery(a,d)+(null!==c?"="+b.encodeQuery(c,d):"")};b.addQuery=function(a,c,d){if("object"===typeof c)for(var e in c)u.call(c,e)&&b.addQuery(a,e,c[e]);else if("string"===typeof c)void 0===a[c]?a[c]=d:("string"===typeof a[c]&&(a[c]=[a[c]]),p(d)||(d=[d]),a[c]=(a[c]||[]).concat(d));else throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");};b.removeQuery=function(a,c,d){var e;if(p(c))for(d=0,e=c.length;d<e;d++)a[c[d]]=
3193void 0;else if("object"===typeof c)for(e in c)u.call(c,e)&&b.removeQuery(a,e,c[e]);else if("string"===typeof c)if(void 0!==d)if(a[c]===d)a[c]=void 0;else{if(p(a[c])){e=a[c];var f={},h,g;if(p(d))for(h=0,g=d.length;h<g;h++)f[d[h]]=!0;else f[d]=!0;h=0;for(g=e.length;h<g;h++)void 0!==f[e[h]]&&(e.splice(h,1),g--,h--);a[c]=e}}else a[c]=void 0;else throw new TypeError("URI.addQuery() accepts an object, string as the first parameter");};b.hasQuery=function(a,c,d,e){if("object"===typeof c){for(var f in c)if(u.call(c,
3194f)&&!b.hasQuery(a,f,c[f]))return!1;return!0}if("string"!==typeof c)throw new TypeError("URI.hasQuery() accepts an object, string as the name parameter");switch(y(d)){case "Undefined":return c in a;case "Boolean":return a=Boolean(p(a[c])?a[c].length:a[c]),d===a;case "Function":return!!d(a[c],c,a);case "Array":return p(a[c])?(e?h:r)(a[c],d):!1;case "RegExp":return p(a[c])?e?h(a[c],d):!1:Boolean(a[c]&&a[c].match(d));case "Number":d=String(d);case "String":return p(a[c])?e?h(a[c],d):!1:a[c]===d;default:throw new TypeError("URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter");
3195}};b.commonPath=function(a,c){var d=Math.min(a.length,c.length),b;for(b=0;b<d;b++)if(a.charAt(b)!==c.charAt(b)){b--;break}if(1>b)return a.charAt(0)===c.charAt(0)&&"/"===a.charAt(0)?"/":"";if("/"!==a.charAt(b)||"/"!==c.charAt(b))b=a.substring(0,b).lastIndexOf("/");return a.substring(0,b+1)};b.withinString=function(a,c,d){d||(d={});var e=d.start||b.findUri.start,f=d.end||b.findUri.end,h=d.trim||b.findUri.trim,g=/[a-z0-9-]=["']?$/i;for(e.lastIndex=0;;){var r=e.exec(a);if(!r)break;r=r.index;if(d.ignoreHtml){var k=
3196a.slice(Math.max(r-3,0),r);if(k&&g.test(k))continue}var k=r+a.slice(r).search(f),m=a.slice(r,k).replace(h,"");d.ignore&&d.ignore.test(m)||(k=r+m.length,m=c(m,r,k,a),a=a.slice(0,r)+m+a.slice(k),e.lastIndex=r+m.length)}e.lastIndex=0;return a};b.ensureValidHostname=function(a){if(a.match(b.invalid_hostname_characters)){if(!f)throw new TypeError('Hostname "'+a+'" contains characters other than [A-Z0-9.-] and Punycode.js is not available');if(f.toASCII(a).match(b.invalid_hostname_characters))throw new TypeError('Hostname "'+
3197a+'" contains characters other than [A-Z0-9.-]');}};b.noConflict=function(a){if(a)return a={URI:this.noConflict()},m.URITemplate&&"function"===typeof m.URITemplate.noConflict&&(a.URITemplate=m.URITemplate.noConflict()),m.IPv6&&"function"===typeof m.IPv6.noConflict&&(a.IPv6=m.IPv6.noConflict()),m.SecondLevelDomains&&"function"===typeof m.SecondLevelDomains.noConflict&&(a.SecondLevelDomains=m.SecondLevelDomains.noConflict()),a;m.URI===this&&(m.URI=n);return this};e.build=function(a){if(!0===a)this._deferred_build=
3198!0;else if(void 0===a||this._deferred_build)this._string=b.build(this._parts),this._deferred_build=!1;return this};e.clone=function(){return new b(this)};e.valueOf=e.toString=function(){return this.build(!1)._string};e.protocol=z("protocol");e.username=z("username");e.password=z("password");e.hostname=z("hostname");e.port=z("port");e.query=s("query","?");e.fragment=s("fragment","#");e.search=function(a,c){var d=this.query(a,c);return"string"===typeof d&&d.length?"?"+d:d};e.hash=function(a,c){var d=
3199this.fragment(a,c);return"string"===typeof d&&d.length?"#"+d:d};e.pathname=function(a,c){if(void 0===a||!0===a){var d=this._parts.path||(this._parts.hostname?"/":"");return a?b.decodePath(d):d}this._parts.path=a?b.recodePath(a):"/";this.build(!c);return this};e.path=e.pathname;e.href=function(a,c){var d;if(void 0===a)return this.toString();this._string="";this._parts=b._parts();var e=a instanceof b,f="object"===typeof a&&(a.hostname||a.path||a.pathname);a.nodeName&&(f=b.getDomAttribute(a),a=a[f]||
3200"",f=!1);!e&&f&&void 0!==a.pathname&&(a=a.toString());if("string"===typeof a||a instanceof String)this._parts=b.parse(String(a),this._parts);else if(e||f)for(d in e=e?a._parts:a,e)u.call(this._parts,d)&&(this._parts[d]=e[d]);else throw new TypeError("invalid input");this.build(!c);return this};e.is=function(a){var c=!1,d=!1,e=!1,f=!1,h=!1,r=!1,k=!1,m=!this._parts.urn;this._parts.hostname&&(m=!1,d=b.ip4_expression.test(this._parts.hostname),e=b.ip6_expression.test(this._parts.hostname),c=d||e,h=(f=
3201!c)&&g&&g.has(this._parts.hostname),r=f&&b.idn_expression.test(this._parts.hostname),k=f&&b.punycode_expression.test(this._parts.hostname));switch(a.toLowerCase()){case "relative":return m;case "absolute":return!m;case "domain":case "name":return f;case "sld":return h;case "ip":return c;case "ip4":case "ipv4":case "inet4":return d;case "ip6":case "ipv6":case "inet6":return e;case "idn":return r;case "url":return!this._parts.urn;case "urn":return!!this._parts.urn;case "punycode":return k}return null};
3202var D=e.protocol,E=e.port,F=e.hostname;e.protocol=function(a,c){if(void 0!==a&&a&&(a=a.replace(/:(\/\/)?$/,""),!a.match(b.protocol_expression)))throw new TypeError('Protocol "'+a+"\" contains characters other than [A-Z0-9.+-] or doesn't start with [A-Z]");return D.call(this,a,c)};e.scheme=e.protocol;e.port=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a&&(0===a&&(a=null),a&&(a+="",":"===a.charAt(0)&&(a=a.substring(1)),a.match(/[^0-9]/))))throw new TypeError('Port "'+a+'" contains characters other than [0-9]');
3203return E.call(this,a,c)};e.hostname=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a){var d={};b.parseHost(a,d);a=d.hostname}return F.call(this,a,c)};e.host=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a)return this._parts.hostname?b.buildHost(this._parts):"";b.parseHost(a,this._parts);this.build(!c);return this};e.authority=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a)return this._parts.hostname?b.buildAuthority(this._parts):
3204"";b.parseAuthority(a,this._parts);this.build(!c);return this};e.userinfo=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.username)return"";var d=b.buildUserinfo(this._parts);return d.substring(0,d.length-1)}"@"!==a[a.length-1]&&(a+="@");b.parseUserinfo(a,this._parts);this.build(!c);return this};e.resource=function(a,c){var d;if(void 0===a)return this.path()+this.search()+this.hash();d=b.parse(a);this._parts.path=d.path;this._parts.query=d.query;this._parts.fragment=
3205d.fragment;this.build(!c);return this};e.subdomain=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var d=this._parts.hostname.length-this.domain().length-1;return this._parts.hostname.substring(0,d)||""}d=this._parts.hostname.length-this.domain().length;d=this._parts.hostname.substring(0,d);d=new RegExp("^"+k(d));a&&"."!==a.charAt(a.length-1)&&(a+=".");a&&b.ensureValidHostname(a);this._parts.hostname=this._parts.hostname.replace(d,
3206a);this.build(!c);return this};e.domain=function(a,c){if(this._parts.urn)return void 0===a?"":this;"boolean"===typeof a&&(c=a,a=void 0);if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var d=this._parts.hostname.match(/\./g);if(d&&2>d.length)return this._parts.hostname;d=this._parts.hostname.length-this.tld(c).length-1;d=this._parts.hostname.lastIndexOf(".",d-1)+1;return this._parts.hostname.substring(d)||""}if(!a)throw new TypeError("cannot set domain empty");b.ensureValidHostname(a);
3207!this._parts.hostname||this.is("IP")?this._parts.hostname=a:(d=new RegExp(k(this.domain())+"$"),this._parts.hostname=this._parts.hostname.replace(d,a));this.build(!c);return this};e.tld=function(a,c){if(this._parts.urn)return void 0===a?"":this;"boolean"===typeof a&&(c=a,a=void 0);if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var d=this._parts.hostname.lastIndexOf("."),d=this._parts.hostname.substring(d+1);return!0!==c&&g&&g.list[d.toLowerCase()]?g.get(this._parts.hostname)||d:d}if(a)if(a.match(/[^a-zA-Z0-9-]/))if(g&&
3208g.is(a))d=new RegExp(k(this.tld())+"$"),this._parts.hostname=this._parts.hostname.replace(d,a);else throw new TypeError('TLD "'+a+'" contains characters other than [A-Z0-9]');else{if(!this._parts.hostname||this.is("IP"))throw new ReferenceError("cannot set TLD on non-domain host");d=new RegExp(k(this.tld())+"$");this._parts.hostname=this._parts.hostname.replace(d,a)}else throw new TypeError("cannot set TLD empty");this.build(!c);return this};e.directory=function(a,c){if(this._parts.urn)return void 0===
3209a?"":this;if(void 0===a||!0===a){if(!this._parts.path&&!this._parts.hostname)return"";if("/"===this._parts.path)return"/";var d=this._parts.path.length-this.filename().length-1,d=this._parts.path.substring(0,d)||(this._parts.hostname?"/":"");return a?b.decodePath(d):d}d=this._parts.path.length-this.filename().length;d=this._parts.path.substring(0,d);d=new RegExp("^"+k(d));this.is("relative")||(a||(a="/"),"/"!==a.charAt(0)&&(a="/"+a));a&&"/"!==a.charAt(a.length-1)&&(a+="/");a=b.recodePath(a);this._parts.path=
3210this._parts.path.replace(d,a);this.build(!c);return this};e.filename=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path||"/"===this._parts.path)return"";var d=this._parts.path.lastIndexOf("/"),d=this._parts.path.substring(d+1);return a?b.decodePathSegment(d):d}d=!1;"/"===a.charAt(0)&&(a=a.substring(1));a.match(/\.?\//)&&(d=!0);var e=new RegExp(k(this.filename())+"$");a=b.recodePath(a);this._parts.path=this._parts.path.replace(e,a);d?this.normalizePath(c):
3211this.build(!c);return this};e.suffix=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path||"/"===this._parts.path)return"";var d=this.filename(),e=d.lastIndexOf(".");if(-1===e)return"";d=d.substring(e+1);d=/^[a-z0-9%]+$/i.test(d)?d:"";return a?b.decodePathSegment(d):d}"."===a.charAt(0)&&(a=a.substring(1));if(d=this.suffix())e=a?new RegExp(k(d)+"$"):new RegExp(k("."+d)+"$");else{if(!a)return this;this._parts.path+="."+b.recodePath(a)}e&&(a=b.recodePath(a),
3212this._parts.path=this._parts.path.replace(e,a));this.build(!c);return this};e.segment=function(a,c,d){var b=this._parts.urn?":":"/",e=this.path(),f="/"===e.substring(0,1),e=e.split(b);void 0!==a&&"number"!==typeof a&&(d=c,c=a,a=void 0);if(void 0!==a&&"number"!==typeof a)throw Error('Bad segment "'+a+'", must be 0-based integer');f&&e.shift();0>a&&(a=Math.max(e.length+a,0));if(void 0===c)return void 0===a?e:e[a];if(null===a||void 0===e[a])if(p(c)){e=[];a=0;for(var h=c.length;a<h;a++)if(c[a].length||
3213e.length&&e[e.length-1].length)e.length&&!e[e.length-1].length&&e.pop(),e.push(c[a])}else{if(c||"string"===typeof c)""===e[e.length-1]?e[e.length-1]=c:e.push(c)}else c?e[a]=c:e.splice(a,1);f&&e.unshift("");return this.path(e.join(b),d)};e.segmentCoded=function(a,c,d){var e,f;"number"!==typeof a&&(d=c,c=a,a=void 0);if(void 0===c){a=this.segment(a,c,d);if(p(a))for(e=0,f=a.length;e<f;e++)a[e]=b.decode(a[e]);else a=void 0!==a?b.decode(a):void 0;return a}if(p(c))for(e=0,f=c.length;e<f;e++)c[e]=b.decode(c[e]);
3214else c="string"===typeof c||c instanceof String?b.encode(c):c;return this.segment(a,c,d)};var G=e.query;e.query=function(a,c){if(!0===a)return b.parseQuery(this._parts.query,this._parts.escapeQuerySpace);if("function"===typeof a){var d=b.parseQuery(this._parts.query,this._parts.escapeQuerySpace),e=a.call(this,d);this._parts.query=b.buildQuery(e||d,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);this.build(!c);return this}return void 0!==a&&"string"!==typeof a?(this._parts.query=
3215b.buildQuery(a,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace),this.build(!c),this):G.call(this,a,c)};e.setQuery=function(a,c,d){var e=b.parseQuery(this._parts.query,this._parts.escapeQuerySpace);if("string"===typeof a||a instanceof String)e[a]=void 0!==c?c:null;else if("object"===typeof a)for(var f in a)u.call(a,f)&&(e[f]=a[f]);else throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");this._parts.query=b.buildQuery(e,this._parts.duplicateQueryParameters,
3216this._parts.escapeQuerySpace);"string"!==typeof a&&(d=c);this.build(!d);return this};e.addQuery=function(a,c,d){var e=b.parseQuery(this._parts.query,this._parts.escapeQuerySpace);b.addQuery(e,a,void 0===c?null:c);this._parts.query=b.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(d=c);this.build(!d);return this};e.removeQuery=function(a,c,d){var e=b.parseQuery(this._parts.query,this._parts.escapeQuerySpace);b.removeQuery(e,a,c);this._parts.query=
3217b.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(d=c);this.build(!d);return this};e.hasQuery=function(a,c,d){var e=b.parseQuery(this._parts.query,this._parts.escapeQuerySpace);return b.hasQuery(e,a,c,d)};e.setSearch=e.setQuery;e.addSearch=e.addQuery;e.removeSearch=e.removeQuery;e.hasSearch=e.hasQuery;e.normalize=function(){return this._parts.urn?this.normalizeProtocol(!1).normalizeQuery(!1).normalizeFragment(!1).build():this.normalizeProtocol(!1).normalizeHostname(!1).normalizePort(!1).normalizePath(!1).normalizeQuery(!1).normalizeFragment(!1).build()};
3218e.normalizeProtocol=function(a){"string"===typeof this._parts.protocol&&(this._parts.protocol=this._parts.protocol.toLowerCase(),this.build(!a));return this};e.normalizeHostname=function(a){this._parts.hostname&&(this.is("IDN")&&f?this._parts.hostname=f.toASCII(this._parts.hostname):this.is("IPv6")&&l&&(this._parts.hostname=l.best(this._parts.hostname)),this._parts.hostname=this._parts.hostname.toLowerCase(),this.build(!a));return this};e.normalizePort=function(a){"string"===typeof this._parts.protocol&&
3219this._parts.port===b.defaultPorts[this._parts.protocol]&&(this._parts.port=null,this.build(!a));return this};e.normalizePath=function(a){if(this._parts.urn||!this._parts.path||"/"===this._parts.path)return this;var c,d=this._parts.path,e="",f,h;"/"!==d.charAt(0)&&(c=!0,d="/"+d);d=d.replace(/(\/(\.\/)+)|(\/\.$)/g,"/").replace(/\/{2,}/g,"/");c&&(e=d.substring(1).match(/^(\.\.\/)+/)||"")&&(e=e[0]);for(;;){f=d.indexOf("/..");if(-1===f)break;else if(0===f){d=d.substring(3);continue}h=d.substring(0,f).lastIndexOf("/");
3220-1===h&&(h=f);d=d.substring(0,h)+d.substring(f+3)}c&&this.is("relative")&&(d=e+d.substring(1));d=b.recodePath(d);this._parts.path=d;this.build(!a);return this};e.normalizePathname=e.normalizePath;e.normalizeQuery=function(a){"string"===typeof this._parts.query&&(this._parts.query.length?this.query(b.parseQuery(this._parts.query,this._parts.escapeQuerySpace)):this._parts.query=null,this.build(!a));return this};e.normalizeFragment=function(a){this._parts.fragment||(this._parts.fragment=null,this.build(!a));
3221return this};e.normalizeSearch=e.normalizeQuery;e.normalizeHash=e.normalizeFragment;e.iso8859=function(){var a=b.encode,c=b.decode;b.encode=escape;b.decode=decodeURIComponent;this.normalize();b.encode=a;b.decode=c;return this};e.unicode=function(){var a=b.encode,c=b.decode;b.encode=C;b.decode=unescape;this.normalize();b.encode=a;b.decode=c;return this};e.readable=function(){var a=this.clone();a.username("").password("").normalize();var c="";a._parts.protocol&&(c+=a._parts.protocol+"://");a._parts.hostname&&
3222(a.is("punycode")&&f?(c+=f.toUnicode(a._parts.hostname),a._parts.port&&(c+=":"+a._parts.port)):c+=a.host());a._parts.hostname&&a._parts.path&&"/"!==a._parts.path.charAt(0)&&(c+="/");c+=a.path(!0);if(a._parts.query){for(var d="",e=0,h=a._parts.query.split("&"),g=h.length;e<g;e++){var r=(h[e]||"").split("="),d=d+("&"+b.decodeQuery(r[0],this._parts.escapeQuerySpace).replace(/&/g,"%26"));void 0!==r[1]&&(d+="="+b.decodeQuery(r[1],this._parts.escapeQuerySpace).replace(/&/g,"%26"))}c+="?"+d.substring(1)}return c+=
3223b.decodeQuery(a.hash(),!0)};e.absoluteTo=function(a){var c=this.clone(),d=["protocol","username","password","hostname","port"],e,f;if(this._parts.urn)throw Error("URNs do not have any generally defined hierarchical components");a instanceof b||(a=new b(a));c._parts.protocol||(c._parts.protocol=a._parts.protocol);if(this._parts.hostname)return c;for(e=0;f=d[e];e++)c._parts[f]=a._parts[f];c._parts.path?".."===c._parts.path.substring(-2)&&(c._parts.path+="/"):(c._parts.path=a._parts.path,c._parts.query||
3224(c._parts.query=a._parts.query));"/"!==c.path().charAt(0)&&(a=a.directory(),c._parts.path=(a?a+"/":"")+c._parts.path,c.normalizePath());c.build();return c};e.relativeTo=function(a){var c=this.clone().normalize(),d,e,f,h;if(c._parts.urn)throw Error("URNs do not have any generally defined hierarchical components");a=(new b(a)).normalize();d=c._parts;e=a._parts;f=c.path();h=a.path();if("/"!==f.charAt(0))throw Error("URI is already relative");if("/"!==h.charAt(0))throw Error("Cannot calculate a URI relative to another relative URI");
3225d.protocol===e.protocol&&(d.protocol=null);if(d.username===e.username&&d.password===e.password&&null===d.protocol&&null===d.username&&null===d.password&&d.hostname===e.hostname&&d.port===e.port)d.hostname=null,d.port=null;else return c.build();if(f===h)return d.path="",c.build();a=b.commonPath(c.path(),a.path());if(!a)return c.build();e=e.path.substring(a.length).replace(/[^\/]*$/,"").replace(/.*?\//g,"../");d.path=e+d.path.substring(a.length);return c.build()};e.equals=function(a){var c=this.clone();
3226a=new b(a);var d={},e={},f={},h;c.normalize();a.normalize();if(c.toString()===a.toString())return!0;d=c.query();e=a.query();c.query("");a.query("");if(c.toString()!==a.toString()||d.length!==e.length)return!1;d=b.parseQuery(d,this._parts.escapeQuerySpace);e=b.parseQuery(e,this._parts.escapeQuerySpace);for(h in d)if(u.call(d,h)){if(!p(d[h])){if(d[h]!==e[h])return!1}else if(!r(d[h],e[h]))return!1;f[h]=!0}for(h in e)if(u.call(e,h)&&!f[h])return!1;return!0};e.duplicateQueryParameters=function(a){this._parts.duplicateQueryParameters=
3227!!a;return this};e.escapeQuerySpace=function(a){this._parts.escapeQuerySpace=!!a;return this};return b});
3228(function(f,l){"object"===typeof exports?module.exports=l(require("./URI")):"function"===typeof define&&define.amd?define(["./URI"],l):f.URITemplate=l(f.URI,f)})(this,function(f,l){function g(b){if(g._cache[b])return g._cache[b];if(!(this instanceof g))return new g(b);this.expression=b;g._cache[b]=this;return this}function m(b){this.data=b;this.cache={}}var b=l&&l.URITemplate,k=Object.prototype.hasOwnProperty,y=g.prototype,p={"":{prefix:"",separator:",",named:!1,empty_name_separator:!1,encode:"encode"},
3229"+":{prefix:"",separator:",",named:!1,empty_name_separator:!1,encode:"encodeReserved"},"#":{prefix:"#",separator:",",named:!1,empty_name_separator:!1,encode:"encodeReserved"},".":{prefix:".",separator:".",named:!1,empty_name_separator:!1,encode:"encode"},"/":{prefix:"/",separator:"/",named:!1,empty_name_separator:!1,encode:"encode"},";":{prefix:";",separator:";",named:!0,empty_name_separator:!1,encode:"encode"},"?":{prefix:"?",separator:"&",named:!0,empty_name_separator:!0,encode:"encode"},"&":{prefix:"&",
3230separator:"&",named:!0,empty_name_separator:!0,encode:"encode"}};g._cache={};g.EXPRESSION_PATTERN=/\{([^a-zA-Z0-9%_]?)([^\}]+)(\}|$)/g;g.VARIABLE_PATTERN=/^([^*:]+)((\*)|:(\d+))?$/;g.VARIABLE_NAME_PATTERN=/[^a-zA-Z0-9%_]/;g.expand=function(b,f){var k=p[b.operator],m=k.named?"Named":"Unnamed",l=b.variables,s=[],n,e,u;for(u=0;e=l[u];u++)n=f.get(e.name),n.val.length?s.push(g["expand"+m](n,k,e.explode,e.explode&&k.separator||",",e.maxlength,e.name)):n.type&&s.push("");return s.length?k.prefix+s.join(k.separator):
3231""};g.expandNamed=function(b,g,k,m,l,s){var n="",e=g.encode;g=g.empty_name_separator;var u=!b[e].length,v=2===b.type?"":f[e](s),q,p,y;p=0;for(y=b.val.length;p<y;p++)l?(q=f[e](b.val[p][1].substring(0,l)),2===b.type&&(v=f[e](b.val[p][0].substring(0,l)))):u?(q=f[e](b.val[p][1]),2===b.type?(v=f[e](b.val[p][0]),b[e].push([v,q])):b[e].push([void 0,q])):(q=b[e][p][1],2===b.type&&(v=b[e][p][0])),n&&(n+=m),k?n+=v+(g||q?"=":"")+q:(p||(n+=f[e](s)+(g||q?"=":"")),2===b.type&&(n+=v+","),n+=q);return n};g.expandUnnamed=
3232function(b,g,k,m,l){var s="",n=g.encode;g=g.empty_name_separator;var e=!b[n].length,p,v,q,y;q=0;for(y=b.val.length;q<y;q++)l?v=f[n](b.val[q][1].substring(0,l)):e?(v=f[n](b.val[q][1]),b[n].push([2===b.type?f[n](b.val[q][0]):void 0,v])):v=b[n][q][1],s&&(s+=m),2===b.type&&(p=l?f[n](b.val[q][0].substring(0,l)):b[n][q][0],s+=p,s=k?s+(g||v?"=":""):s+","),s+=v;return s};g.noConflict=function(){l.URITemplate===g&&(l.URITemplate=b);return g};y.expand=function(b){var f="";this.parts&&this.parts.length||this.parse();
3233b instanceof m||(b=new m(b));for(var k=0,l=this.parts.length;k<l;k++)f+="string"===typeof this.parts[k]?this.parts[k]:g.expand(this.parts[k],b);return f};y.parse=function(){var b=this.expression,f=g.EXPRESSION_PATTERN,k=g.VARIABLE_PATTERN,m=g.VARIABLE_NAME_PATTERN,l=[],s=0,n,e,u;for(f.lastIndex=0;;){e=f.exec(b);if(null===e){l.push(b.substring(s));break}else l.push(b.substring(s,e.index)),s=e.index+e[0].length;if(!p[e[1]])throw Error('Unknown Operator "'+e[1]+'" in "'+e[0]+'"');if(!e[3])throw Error('Unclosed Expression "'+
3234e[0]+'"');n=e[2].split(",");for(var v=0,q=n.length;v<q;v++){u=n[v].match(k);if(null===u)throw Error('Invalid Variable "'+n[v]+'" in "'+e[0]+'"');if(u[1].match(m))throw Error('Invalid Variable Name "'+u[1]+'" in "'+e[0]+'"');n[v]={name:u[1],explode:!!u[3],maxlength:u[4]&&parseInt(u[4],10)}}if(!n.length)throw Error('Expression Missing Variable(s) "'+e[0]+'"');l.push({expression:e[0],operator:e[1],variables:n})}l.length||l.push(b);this.parts=l;return this};m.prototype.get=function(b){var f=this.data,
3235g={type:0,val:[],encode:[],encodeReserved:[]},l;if(void 0!==this.cache[b])return this.cache[b];this.cache[b]=g;f="[object Function]"===String(Object.prototype.toString.call(f))?f(b):"[object Function]"===String(Object.prototype.toString.call(f[b]))?f[b](b):f[b];if(void 0!==f&&null!==f)if("[object Array]"===String(Object.prototype.toString.call(f))){l=0;for(b=f.length;l<b;l++)void 0!==f[l]&&null!==f[l]&&g.val.push([void 0,String(f[l])]);g.val.length&&(g.type=3)}else if("[object Object]"===String(Object.prototype.toString.call(f))){for(l in f)k.call(f,
3236l)&&void 0!==f[l]&&null!==f[l]&&g.val.push([l,String(f[l])]);g.val.length&&(g.type=2)}else g.type=1,g.val.push([void 0,String(f)]);return g};f.expand=function(b,k){var l=(new g(b)).expand(k);return new f(l)};return g});
3237/* ===================================================
3238 * bootstrap-transition.js v2.3.1
3239 * http://twitter.github.com/bootstrap/javascript.html#transitions
3240 * ===================================================
3241 * Copyright 2012 Twitter, Inc.
3242 *
3243 * Licensed under the Apache License, Version 2.0 (the "License");
3244 * you may not use this file except in compliance with the License.
3245 * You may obtain a copy of the License at
3246 *
3247 * http://www.apache.org/licenses/LICENSE-2.0
3248 *
3249 * Unless required by applicable law or agreed to in writing, software
3250 * distributed under the License is distributed on an "AS IS" BASIS,
3251 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3252 * See the License for the specific language governing permissions and
3253 * limitations under the License.
3254 * ========================================================== */
3255
3256
3257!function ($) {
3258
3259 "use strict"; // jshint ;_;
3260
3261
3262 /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
3263 * ======================================================= */
3264
3265 $(function () {
3266
3267 $.support.transition = (function () {
3268
3269 var transitionEnd = (function () {
3270
3271 var el = document.createElement('bootstrap')
3272 , transEndEventNames = {
3273 'WebkitTransition' : 'webkitTransitionEnd'
3274 , 'MozTransition' : 'transitionend'
3275 , 'OTransition' : 'oTransitionEnd otransitionend'
3276 , 'transition' : 'transitionend'
3277 }
3278 , name
3279
3280 for (name in transEndEventNames){
3281 if (el.style[name] !== undefined) {
3282 return transEndEventNames[name]
3283 }
3284 }
3285
3286 }())
3287
3288 return transitionEnd && {
3289 end: transitionEnd
3290 }
3291
3292 })()
3293
3294 })
3295
3296}(window.jQuery);/* ==========================================================
3297 * bootstrap-alert.js v2.3.1
3298 * http://twitter.github.com/bootstrap/javascript.html#alerts
3299 * ==========================================================
3300 * Copyright 2012 Twitter, Inc.
3301 *
3302 * Licensed under the Apache License, Version 2.0 (the "License");
3303 * you may not use this file except in compliance with the License.
3304 * You may obtain a copy of the License at
3305 *
3306 * http://www.apache.org/licenses/LICENSE-2.0
3307 *
3308 * Unless required by applicable law or agreed to in writing, software
3309 * distributed under the License is distributed on an "AS IS" BASIS,
3310 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3311 * See the License for the specific language governing permissions and
3312 * limitations under the License.
3313 * ========================================================== */
3314
3315
3316!function ($) {
3317
3318 "use strict"; // jshint ;_;
3319
3320
3321 /* ALERT CLASS DEFINITION
3322 * ====================== */
3323
3324 var dismiss = '[data-dismiss="alert"]'
3325 , Alert = function (el) {
3326 $(el).on('click', dismiss, this.close)
3327 }
3328
3329 Alert.prototype.close = function (e) {
3330 var $this = $(this)
3331 , selector = $this.attr('data-target')
3332 , $parent
3333
3334 if (!selector) {
3335 selector = $this.attr('href')
3336 selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
3337 }
3338
3339 $parent = $(selector)
3340
3341 e && e.preventDefault()
3342
3343 $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
3344
3345 $parent.trigger(e = $.Event('close'))
3346
3347 if (e.isDefaultPrevented()) return
3348
3349 $parent.removeClass('in')
3350
3351 function removeElement() {
3352 $parent
3353 .trigger('closed')
3354 .remove()
3355 }
3356
3357 $.support.transition && $parent.hasClass('fade') ?
3358 $parent.on($.support.transition.end, removeElement) :
3359 removeElement()
3360 }
3361
3362
3363 /* ALERT PLUGIN DEFINITION
3364 * ======================= */
3365
3366 var old = $.fn.alert
3367
3368 $.fn.alert = function (option) {
3369 return this.each(function () {
3370 var $this = $(this)
3371 , data = $this.data('alert')
3372 if (!data) $this.data('alert', (data = new Alert(this)))
3373 if (typeof option == 'string') data[option].call($this)
3374 })
3375 }
3376
3377 $.fn.alert.Constructor = Alert
3378
3379
3380 /* ALERT NO CONFLICT
3381 * ================= */
3382
3383 $.fn.alert.noConflict = function () {
3384 $.fn.alert = old
3385 return this
3386 }
3387
3388
3389 /* ALERT DATA-API
3390 * ============== */
3391
3392 $(document).on('click.alert.data-api', dismiss, Alert.prototype.close)
3393
3394}(window.jQuery);/* ============================================================
3395 * bootstrap-button.js v2.3.1
3396 * http://twitter.github.com/bootstrap/javascript.html#buttons
3397 * ============================================================
3398 * Copyright 2012 Twitter, Inc.
3399 *
3400 * Licensed under the Apache License, Version 2.0 (the "License");
3401 * you may not use this file except in compliance with the License.
3402 * You may obtain a copy of the License at
3403 *
3404 * http://www.apache.org/licenses/LICENSE-2.0
3405 *
3406 * Unless required by applicable law or agreed to in writing, software
3407 * distributed under the License is distributed on an "AS IS" BASIS,
3408 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3409 * See the License for the specific language governing permissions and
3410 * limitations under the License.
3411 * ============================================================ */
3412
3413
3414!function ($) {
3415
3416 "use strict"; // jshint ;_;
3417
3418
3419 /* BUTTON PUBLIC CLASS DEFINITION
3420 * ============================== */
3421
3422 var Button = function (element, options) {
3423 this.$element = $(element)
3424 this.options = $.extend({}, $.fn.button.defaults, options)
3425 }
3426
3427 Button.prototype.setState = function (state) {
3428 var d = 'disabled'
3429 , $el = this.$element
3430 , data = $el.data()
3431 , val = $el.is('input') ? 'val' : 'html'
3432
3433 state = state + 'Text'
3434 data.resetText || $el.data('resetText', $el[val]())
3435
3436 $el[val](data[state] || this.options[state])
3437
3438 // push to event loop to allow forms to submit
3439 setTimeout(function () {
3440 state == 'loadingText' ?
3441 $el.addClass(d).attr(d, d) :
3442 $el.removeClass(d).removeAttr(d)
3443 }, 0)
3444 }
3445
3446 Button.prototype.toggle = function () {
3447 var $parent = this.$element.closest('[data-toggle="buttons-radio"]')
3448
3449 $parent && $parent
3450 .find('.active')
3451 .removeClass('active')
3452
3453 this.$element.toggleClass('active')
3454 }
3455
3456
3457 /* BUTTON PLUGIN DEFINITION
3458 * ======================== */
3459
3460 var old = $.fn.button
3461
3462 $.fn.button = function (option) {
3463 return this.each(function () {
3464 var $this = $(this)
3465 , data = $this.data('button')
3466 , options = typeof option == 'object' && option
3467 if (!data) $this.data('button', (data = new Button(this, options)))
3468 if (option == 'toggle') data.toggle()
3469 else if (option) data.setState(option)
3470 })
3471 }
3472
3473 $.fn.button.defaults = {
3474 loadingText: 'loading...'
3475 }
3476
3477 $.fn.button.Constructor = Button
3478
3479
3480 /* BUTTON NO CONFLICT
3481 * ================== */
3482
3483 $.fn.button.noConflict = function () {
3484 $.fn.button = old
3485 return this
3486 }
3487
3488
3489 /* BUTTON DATA-API
3490 * =============== */
3491
3492 $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) {
3493 var $btn = $(e.target)
3494 if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
3495 $btn.button('toggle')
3496 })
3497
3498}(window.jQuery);/* ==========================================================
3499 * bootstrap-carousel.js v2.3.1
3500 * http://twitter.github.com/bootstrap/javascript.html#carousel
3501 * ==========================================================
3502 * Copyright 2012 Twitter, Inc.
3503 *
3504 * Licensed under the Apache License, Version 2.0 (the "License");
3505 * you may not use this file except in compliance with the License.
3506 * You may obtain a copy of the License at
3507 *
3508 * http://www.apache.org/licenses/LICENSE-2.0
3509 *
3510 * Unless required by applicable law or agreed to in writing, software
3511 * distributed under the License is distributed on an "AS IS" BASIS,
3512 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3513 * See the License for the specific language governing permissions and
3514 * limitations under the License.
3515 * ========================================================== */
3516
3517
3518!function ($) {
3519
3520 "use strict"; // jshint ;_;
3521
3522
3523 /* CAROUSEL CLASS DEFINITION
3524 * ========================= */
3525
3526 var Carousel = function (element, options) {
3527 this.$element = $(element)
3528 this.$indicators = this.$element.find('.carousel-indicators')
3529 this.options = options
3530 this.options.pause == 'hover' && this.$element
3531 .on('mouseenter', $.proxy(this.pause, this))
3532 .on('mouseleave', $.proxy(this.cycle, this))
3533 }
3534
3535 Carousel.prototype = {
3536
3537 cycle: function (e) {
3538 if (!e) this.paused = false
3539 if (this.interval) clearInterval(this.interval);
3540 this.options.interval
3541 && !this.paused
3542 && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
3543 return this
3544 }
3545
3546 , getActiveIndex: function () {
3547 this.$active = this.$element.find('.item.active')
3548 this.$items = this.$active.parent().children()
3549 return this.$items.index(this.$active)
3550 }
3551
3552 , to: function (pos) {
3553 var activeIndex = this.getActiveIndex()
3554 , that = this
3555
3556 if (pos > (this.$items.length - 1) || pos < 0) return
3557
3558 if (this.sliding) {
3559 return this.$element.one('slid', function () {
3560 that.to(pos)
3561 })
3562 }
3563
3564 if (activeIndex == pos) {
3565 return this.pause().cycle()
3566 }
3567
3568 return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
3569 }
3570
3571 , pause: function (e) {
3572 if (!e) this.paused = true
3573 if (this.$element.find('.next, .prev').length && $.support.transition.end) {
3574 this.$element.trigger($.support.transition.end)
3575 this.cycle(true)
3576 }
3577 clearInterval(this.interval)
3578 this.interval = null
3579 return this
3580 }
3581
3582 , next: function () {
3583 if (this.sliding) return
3584 return this.slide('next')
3585 }
3586
3587 , prev: function () {
3588 if (this.sliding) return
3589 return this.slide('prev')
3590 }
3591
3592 , slide: function (type, next) {
3593 var $active = this.$element.find('.item.active')
3594 , $next = next || $active[type]()
3595 , isCycling = this.interval
3596 , direction = type == 'next' ? 'left' : 'right'
3597 , fallback = type == 'next' ? 'first' : 'last'
3598 , that = this
3599 , e
3600
3601 this.sliding = true
3602
3603 isCycling && this.pause()
3604
3605 $next = $next.length ? $next : this.$element.find('.item')[fallback]()
3606
3607 e = $.Event('slide', {
3608 relatedTarget: $next[0]
3609 , direction: direction
3610 })
3611
3612 if ($next.hasClass('active')) return
3613
3614 if (this.$indicators.length) {
3615 this.$indicators.find('.active').removeClass('active')
3616 this.$element.one('slid', function () {
3617 var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
3618 $nextIndicator && $nextIndicator.addClass('active')
3619 })
3620 }
3621
3622 if ($.support.transition && this.$element.hasClass('slide')) {
3623 this.$element.trigger(e)
3624 if (e.isDefaultPrevented()) return
3625 $next.addClass(type)
3626 $next[0].offsetWidth // force reflow
3627 $active.addClass(direction)
3628 $next.addClass(direction)
3629 this.$element.one($.support.transition.end, function () {
3630 $next.removeClass([type, direction].join(' ')).addClass('active')
3631 $active.removeClass(['active', direction].join(' '))
3632 that.sliding = false
3633 setTimeout(function () { that.$element.trigger('slid') }, 0)
3634 })
3635 } else {
3636 this.$element.trigger(e)
3637 if (e.isDefaultPrevented()) return
3638 $active.removeClass('active')
3639 $next.addClass('active')
3640 this.sliding = false
3641 this.$element.trigger('slid')
3642 }
3643
3644 isCycling && this.cycle()
3645
3646 return this
3647 }
3648
3649 }
3650
3651
3652 /* CAROUSEL PLUGIN DEFINITION
3653 * ========================== */
3654
3655 var old = $.fn.carousel
3656
3657 $.fn.carousel = function (option) {
3658 return this.each(function () {
3659 var $this = $(this)
3660 , data = $this.data('carousel')
3661 , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
3662 , action = typeof option == 'string' ? option : options.slide
3663 if (!data) $this.data('carousel', (data = new Carousel(this, options)))
3664 if (typeof option == 'number') data.to(option)
3665 else if (action) data[action]()
3666 else if (options.interval) data.pause().cycle()
3667 })
3668 }
3669
3670 $.fn.carousel.defaults = {
3671 interval: 5000
3672 , pause: 'hover'
3673 }
3674
3675 $.fn.carousel.Constructor = Carousel
3676
3677
3678 /* CAROUSEL NO CONFLICT
3679 * ==================== */
3680
3681 $.fn.carousel.noConflict = function () {
3682 $.fn.carousel = old
3683 return this
3684 }
3685
3686 /* CAROUSEL DATA-API
3687 * ================= */
3688
3689 $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
3690 var $this = $(this), href
3691 , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
3692 , options = $.extend({}, $target.data(), $this.data())
3693 , slideIndex
3694
3695 $target.carousel(options)
3696
3697 if (slideIndex = $this.attr('data-slide-to')) {
3698 $target.data('carousel').pause().to(slideIndex).cycle()
3699 }
3700
3701 e.preventDefault()
3702 })
3703
3704}(window.jQuery);/* =============================================================
3705 * bootstrap-collapse.js v2.3.1
3706 * http://twitter.github.com/bootstrap/javascript.html#collapse
3707 * =============================================================
3708 * Copyright 2012 Twitter, Inc.
3709 *
3710 * Licensed under the Apache License, Version 2.0 (the "License");
3711 * you may not use this file except in compliance with the License.
3712 * You may obtain a copy of the License at
3713 *
3714 * http://www.apache.org/licenses/LICENSE-2.0
3715 *
3716 * Unless required by applicable law or agreed to in writing, software
3717 * distributed under the License is distributed on an "AS IS" BASIS,
3718 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3719 * See the License for the specific language governing permissions and
3720 * limitations under the License.
3721 * ============================================================ */
3722
3723
3724!function ($) {
3725
3726 "use strict"; // jshint ;_;
3727
3728
3729 /* COLLAPSE PUBLIC CLASS DEFINITION
3730 * ================================ */
3731
3732 var Collapse = function (element, options) {
3733 this.$element = $(element)
3734 this.options = $.extend({}, $.fn.collapse.defaults, options)
3735
3736 if (this.options.parent) {
3737 this.$parent = $(this.options.parent)
3738 }
3739
3740 this.options.toggle && this.toggle()
3741 }
3742
3743 Collapse.prototype = {
3744
3745 constructor: Collapse
3746
3747 , dimension: function () {
3748 var hasWidth = this.$element.hasClass('width')
3749 return hasWidth ? 'width' : 'height'
3750 }
3751
3752 , show: function () {
3753 var dimension
3754 , scroll
3755 , actives
3756 , hasData
3757
3758 if (this.transitioning || this.$element.hasClass('in')) return
3759
3760 dimension = this.dimension()
3761 scroll = $.camelCase(['scroll', dimension].join('-'))
3762 actives = this.$parent && this.$parent.find('> .accordion-group > .in')
3763
3764 if (actives && actives.length) {
3765 hasData = actives.data('collapse')
3766 if (hasData && hasData.transitioning) return
3767 actives.collapse('hide')
3768 hasData || actives.data('collapse', null)
3769 }
3770
3771 this.$element[dimension](0)
3772 this.transition('addClass', $.Event('show'), 'shown')
3773 $.support.transition && this.$element[dimension](this.$element[0][scroll])
3774 }
3775
3776 , hide: function () {
3777 var dimension
3778 if (this.transitioning || !this.$element.hasClass('in')) return
3779 dimension = this.dimension()
3780 this.reset(this.$element[dimension]())
3781 this.transition('removeClass', $.Event('hide'), 'hidden')
3782 this.$element[dimension](0)
3783 }
3784
3785 , reset: function (size) {
3786 var dimension = this.dimension()
3787
3788 this.$element
3789 .removeClass('collapse')
3790 [dimension](size || 'auto')
3791 [0].offsetWidth
3792
3793 this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
3794
3795 return this
3796 }
3797
3798 , transition: function (method, startEvent, completeEvent) {
3799 var that = this
3800 , complete = function () {
3801 if (startEvent.type == 'show') that.reset()
3802 that.transitioning = 0
3803 that.$element.trigger(completeEvent)
3804 }
3805
3806 this.$element.trigger(startEvent)
3807
3808 if (startEvent.isDefaultPrevented()) return
3809
3810 this.transitioning = 1
3811
3812 this.$element[method]('in')
3813
3814 $.support.transition && this.$element.hasClass('collapse') ?
3815 this.$element.one($.support.transition.end, complete) :
3816 complete()
3817 }
3818
3819 , toggle: function () {
3820 this[this.$element.hasClass('in') ? 'hide' : 'show']()
3821 }
3822
3823 }
3824
3825
3826 /* COLLAPSE PLUGIN DEFINITION
3827 * ========================== */
3828
3829 var old = $.fn.collapse
3830
3831 $.fn.collapse = function (option) {
3832 return this.each(function () {
3833 var $this = $(this)
3834 , data = $this.data('collapse')
3835 , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option)
3836 if (!data) $this.data('collapse', (data = new Collapse(this, options)))
3837 if (typeof option == 'string') data[option]()
3838 })
3839 }
3840
3841 $.fn.collapse.defaults = {
3842 toggle: true
3843 }
3844
3845 $.fn.collapse.Constructor = Collapse
3846
3847
3848 /* COLLAPSE NO CONFLICT
3849 * ==================== */
3850
3851 $.fn.collapse.noConflict = function () {
3852 $.fn.collapse = old
3853 return this
3854 }
3855
3856
3857 /* COLLAPSE DATA-API
3858 * ================= */
3859
3860 $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) {
3861 var $this = $(this), href
3862 , target = $this.attr('data-target')
3863 || e.preventDefault()
3864 || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
3865 , option = $(target).data('collapse') ? 'toggle' : $this.data()
3866 $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
3867 $(target).collapse(option)
3868 })
3869
3870}(window.jQuery);/* ============================================================
3871 * bootstrap-dropdown.js v2.3.1
3872 * http://twitter.github.com/bootstrap/javascript.html#dropdowns
3873 * ============================================================
3874 * Copyright 2012 Twitter, Inc.
3875 *
3876 * Licensed under the Apache License, Version 2.0 (the "License");
3877 * you may not use this file except in compliance with the License.
3878 * You may obtain a copy of the License at
3879 *
3880 * http://www.apache.org/licenses/LICENSE-2.0
3881 *
3882 * Unless required by applicable law or agreed to in writing, software
3883 * distributed under the License is distributed on an "AS IS" BASIS,
3884 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3885 * See the License for the specific language governing permissions and
3886 * limitations under the License.
3887 * ============================================================ */
3888
3889
3890!function ($) {
3891
3892 "use strict"; // jshint ;_;
3893
3894
3895 /* DROPDOWN CLASS DEFINITION
3896 * ========================= */
3897
3898 var toggle = '[data-toggle=dropdown]'
3899 , Dropdown = function (element) {
3900 var $el = $(element).on('click.dropdown.data-api', this.toggle)
3901 $('html').on('click.dropdown.data-api', function () {
3902 $el.parent().removeClass('open')
3903 })
3904 }
3905
3906 Dropdown.prototype = {
3907
3908 constructor: Dropdown
3909
3910 , toggle: function (e) {
3911 var $this = $(this)
3912 , $parent
3913 , isActive
3914
3915 if ($this.is('.disabled, :disabled')) return
3916
3917 $parent = getParent($this)
3918
3919 isActive = $parent.hasClass('open')
3920
3921 clearMenus()
3922
3923 if (!isActive) {
3924 $parent.toggleClass('open')
3925 }
3926
3927 $this.focus()
3928
3929 return false
3930 }
3931
3932 , keydown: function (e) {
3933 var $this
3934 , $items
3935 , $active
3936 , $parent
3937 , isActive
3938 , index
3939
3940 if (!/(38|40|27)/.test(e.keyCode)) return
3941
3942 $this = $(this)
3943
3944 e.preventDefault()
3945 e.stopPropagation()
3946
3947 if ($this.is('.disabled, :disabled')) return
3948
3949 $parent = getParent($this)
3950
3951 isActive = $parent.hasClass('open')
3952
3953 if (!isActive || (isActive && e.keyCode == 27)) {
3954 if (e.which == 27) $parent.find(toggle).focus()
3955 return $this.click()
3956 }
3957
3958 $items = $('[role=menu] li:not(.divider):visible a', $parent)
3959
3960 if (!$items.length) return
3961
3962 index = $items.index($items.filter(':focus'))
3963
3964 if (e.keyCode == 38 && index > 0) index-- // up
3965 if (e.keyCode == 40 && index < $items.length - 1) index++ // down
3966 if (!~index) index = 0
3967
3968 $items
3969 .eq(index)
3970 .focus()
3971 }
3972
3973 }
3974
3975 function clearMenus() {
3976 $(toggle).each(function () {
3977 getParent($(this)).removeClass('open')
3978 })
3979 }
3980
3981 function getParent($this) {
3982 var selector = $this.attr('data-target')
3983 , $parent
3984
3985 if (!selector) {
3986 selector = $this.attr('href')
3987 selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
3988 }
3989
3990 $parent = selector && $(selector)
3991
3992 if (!$parent || !$parent.length) $parent = $this.parent()
3993
3994 return $parent
3995 }
3996
3997
3998 /* DROPDOWN PLUGIN DEFINITION
3999 * ========================== */
4000
4001 var old = $.fn.dropdown
4002
4003 $.fn.dropdown = function (option) {
4004 return this.each(function () {
4005 var $this = $(this)
4006 , data = $this.data('dropdown')
4007 if (!data) $this.data('dropdown', (data = new Dropdown(this)))
4008 if (typeof option == 'string') data[option].call($this)
4009 })
4010 }
4011
4012 $.fn.dropdown.Constructor = Dropdown
4013
4014
4015 /* DROPDOWN NO CONFLICT
4016 * ==================== */
4017
4018 $.fn.dropdown.noConflict = function () {
4019 $.fn.dropdown = old
4020 return this
4021 }
4022
4023
4024 /* APPLY TO STANDARD DROPDOWN ELEMENTS
4025 * =================================== */
4026
4027 $(document)
4028 .on('click.dropdown.data-api', clearMenus)
4029 .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
4030 .on('click.dropdown-menu', function (e) { e.stopPropagation() })
4031 .on('click.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
4032 .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
4033
4034}(window.jQuery);
4035/* =========================================================
4036 * bootstrap-modal.js v2.3.1
4037 * http://twitter.github.com/bootstrap/javascript.html#modals
4038 * =========================================================
4039 * Copyright 2012 Twitter, Inc.
4040 *
4041 * Licensed under the Apache License, Version 2.0 (the "License");
4042 * you may not use this file except in compliance with the License.
4043 * You may obtain a copy of the License at
4044 *
4045 * http://www.apache.org/licenses/LICENSE-2.0
4046 *
4047 * Unless required by applicable law or agreed to in writing, software
4048 * distributed under the License is distributed on an "AS IS" BASIS,
4049 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4050 * See the License for the specific language governing permissions and
4051 * limitations under the License.
4052 * ========================================================= */
4053
4054
4055!function ($) {
4056
4057 "use strict"; // jshint ;_;
4058
4059
4060 /* MODAL CLASS DEFINITION
4061 * ====================== */
4062
4063 var Modal = function (element, options) {
4064 this.options = options
4065 this.$element = $(element)
4066 .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
4067 this.options.remote && this.$element.find('.modal-body').load(this.options.remote)
4068 }
4069
4070 Modal.prototype = {
4071
4072 constructor: Modal
4073
4074 , toggle: function () {
4075 return this[!this.isShown ? 'show' : 'hide']()
4076 }
4077
4078 , show: function () {
4079 var that = this
4080 , e = $.Event('show')
4081
4082 this.$element.trigger(e)
4083
4084 if (this.isShown || e.isDefaultPrevented()) return
4085
4086 this.isShown = true
4087
4088 this.escape()
4089
4090 this.backdrop(function () {
4091 var transition = $.support.transition && that.$element.hasClass('fade')
4092
4093 if (!that.$element.parent().length) {
4094 that.$element.appendTo(document.body) //don't move modals dom position
4095 }
4096
4097 that.$element.show()
4098
4099 if (transition) {
4100 that.$element[0].offsetWidth // force reflow
4101 }
4102
4103 that.$element
4104 .addClass('in')
4105 .attr('aria-hidden', false)
4106
4107 that.enforceFocus()
4108
4109 transition ?
4110 that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) :
4111 that.$element.focus().trigger('shown')
4112
4113 })
4114 }
4115
4116 , hide: function (e) {
4117 e && e.preventDefault()
4118
4119 var that = this
4120
4121 e = $.Event('hide')
4122
4123 this.$element.trigger(e)
4124
4125 if (!this.isShown || e.isDefaultPrevented()) return
4126
4127 this.isShown = false
4128
4129 this.escape()
4130
4131 $(document).off('focusin.modal')
4132
4133 this.$element
4134 .removeClass('in')
4135 .attr('aria-hidden', true)
4136
4137 $.support.transition && this.$element.hasClass('fade') ?
4138 this.hideWithTransition() :
4139 this.hideModal()
4140 }
4141
4142 , enforceFocus: function () {
4143 var that = this
4144 $(document).on('focusin.modal', function (e) {
4145 if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
4146 that.$element.focus()
4147 }
4148 })
4149 }
4150
4151 , escape: function () {
4152 var that = this
4153 if (this.isShown && this.options.keyboard) {
4154 this.$element.on('keyup.dismiss.modal', function ( e ) {
4155 e.which == 27 && that.hide()
4156 })
4157 } else if (!this.isShown) {
4158 this.$element.off('keyup.dismiss.modal')
4159 }
4160 }
4161
4162 , hideWithTransition: function () {
4163 var that = this
4164 , timeout = setTimeout(function () {
4165 that.$element.off($.support.transition.end)
4166 that.hideModal()
4167 }, 500)
4168
4169 this.$element.one($.support.transition.end, function () {
4170 clearTimeout(timeout)
4171 that.hideModal()
4172 })
4173 }
4174
4175 , hideModal: function () {
4176 var that = this
4177 this.$element.hide()
4178 this.backdrop(function () {
4179 that.removeBackdrop()
4180 that.$element.trigger('hidden')
4181 })
4182 }
4183
4184 , removeBackdrop: function () {
4185 this.$backdrop && this.$backdrop.remove()
4186 this.$backdrop = null
4187 }
4188
4189 , backdrop: function (callback) {
4190 var that = this
4191 , animate = this.$element.hasClass('fade') ? 'fade' : ''
4192
4193 if (this.isShown && this.options.backdrop) {
4194 var doAnimate = $.support.transition && animate
4195
4196 this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
4197 .appendTo(document.body)
4198
4199 this.$backdrop.click(
4200 this.options.backdrop == 'static' ?
4201 $.proxy(this.$element[0].focus, this.$element[0])
4202 : $.proxy(this.hide, this)
4203 )
4204
4205 if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
4206
4207 this.$backdrop.addClass('in')
4208
4209 if (!callback) return
4210
4211 doAnimate ?
4212 this.$backdrop.one($.support.transition.end, callback) :
4213 callback()
4214
4215 } else if (!this.isShown && this.$backdrop) {
4216 this.$backdrop.removeClass('in')
4217
4218 $.support.transition && this.$element.hasClass('fade')?
4219 this.$backdrop.one($.support.transition.end, callback) :
4220 callback()
4221
4222 } else if (callback) {
4223 callback()
4224 }
4225 }
4226 }
4227
4228
4229 /* MODAL PLUGIN DEFINITION
4230 * ======================= */
4231
4232 var old = $.fn.modal
4233
4234 $.fn.modal = function (option) {
4235 return this.each(function () {
4236 var $this = $(this)
4237 , data = $this.data('modal')
4238 , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
4239 if (!data) $this.data('modal', (data = new Modal(this, options)))
4240 if (typeof option == 'string') data[option]()
4241 else if (options.show) data.show()
4242 })
4243 }
4244
4245 $.fn.modal.defaults = {
4246 backdrop: true
4247 , keyboard: true
4248 , show: true
4249 }
4250
4251 $.fn.modal.Constructor = Modal
4252
4253
4254 /* MODAL NO CONFLICT
4255 * ================= */
4256
4257 $.fn.modal.noConflict = function () {
4258 $.fn.modal = old
4259 return this
4260 }
4261
4262
4263 /* MODAL DATA-API
4264 * ============== */
4265
4266 $(document).on('click.modal.data-api', '[data-toggle="modal"]', function (e) {
4267 var $this = $(this)
4268 , href = $this.attr('href')
4269 , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
4270 , option = $target.data('modal') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $target.data(), $this.data())
4271
4272 e.preventDefault()
4273
4274 $target
4275 .modal(option)
4276 .one('hide', function () {
4277 $this.focus()
4278 })
4279 })
4280
4281}(window.jQuery);
4282/* ===========================================================
4283 * bootstrap-tooltip.js v2.3.1
4284 * http://twitter.github.com/bootstrap/javascript.html#tooltips
4285 * Inspired by the original jQuery.tipsy by Jason Frame
4286 * ===========================================================
4287 * Copyright 2012 Twitter, Inc.
4288 *
4289 * Licensed under the Apache License, Version 2.0 (the "License");
4290 * you may not use this file except in compliance with the License.
4291 * You may obtain a copy of the License at
4292 *
4293 * http://www.apache.org/licenses/LICENSE-2.0
4294 *
4295 * Unless required by applicable law or agreed to in writing, software
4296 * distributed under the License is distributed on an "AS IS" BASIS,
4297 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4298 * See the License for the specific language governing permissions and
4299 * limitations under the License.
4300 * ========================================================== */
4301
4302
4303!function ($) {
4304
4305 "use strict"; // jshint ;_;
4306
4307
4308 /* TOOLTIP PUBLIC CLASS DEFINITION
4309 * =============================== */
4310
4311 var Tooltip = function (element, options) {
4312 this.init('tooltip', element, options)
4313 }
4314
4315 Tooltip.prototype = {
4316
4317 constructor: Tooltip
4318
4319 , init: function (type, element, options) {
4320 var eventIn
4321 , eventOut
4322 , triggers
4323 , trigger
4324 , i
4325
4326 this.type = type
4327 this.$element = $(element)
4328 this.options = this.getOptions(options)
4329 this.enabled = true
4330
4331 triggers = this.options.trigger.split(' ')
4332
4333 for (i = triggers.length; i--;) {
4334 trigger = triggers[i]
4335 if (trigger == 'click') {
4336 this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
4337 } else if (trigger != 'manual') {
4338 eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
4339 eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
4340 this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
4341 this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
4342 }
4343 }
4344
4345 this.options.selector ?
4346 (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
4347 this.fixTitle()
4348 }
4349
4350 , getOptions: function (options) {
4351 options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options)
4352
4353 if (options.delay && typeof options.delay == 'number') {
4354 options.delay = {
4355 show: options.delay
4356 , hide: options.delay
4357 }
4358 }
4359
4360 return options
4361 }
4362
4363 , enter: function (e) {
4364 var defaults = $.fn[this.type].defaults
4365 , options = {}
4366 , self
4367
4368 this._options && $.each(this._options, function (key, value) {
4369 if (defaults[key] != value) options[key] = value
4370 }, this)
4371
4372 self = $(e.currentTarget)[this.type](options).data(this.type)
4373
4374 if (!self.options.delay || !self.options.delay.show) return self.show()
4375
4376 clearTimeout(this.timeout)
4377 self.hoverState = 'in'
4378 this.timeout = setTimeout(function() {
4379 if (self.hoverState == 'in') self.show()
4380 }, self.options.delay.show)
4381 }
4382
4383 , leave: function (e) {
4384 var self = $(e.currentTarget)[this.type](this._options).data(this.type)
4385
4386 if (this.timeout) clearTimeout(this.timeout)
4387 if (!self.options.delay || !self.options.delay.hide) return self.hide()
4388
4389 self.hoverState = 'out'
4390 this.timeout = setTimeout(function() {
4391 if (self.hoverState == 'out') self.hide()
4392 }, self.options.delay.hide)
4393 }
4394
4395 , show: function () {
4396 var $tip
4397 , pos
4398 , actualWidth
4399 , actualHeight
4400 , placement
4401 , tp
4402 , e = $.Event('show')
4403
4404 if (this.hasContent() && this.enabled) {
4405 this.$element.trigger(e)
4406 if (e.isDefaultPrevented()) return
4407 $tip = this.tip()
4408 this.setContent()
4409
4410 if (this.options.animation) {
4411 $tip.addClass('fade')
4412 }
4413
4414 placement = typeof this.options.placement == 'function' ?
4415 this.options.placement.call(this, $tip[0], this.$element[0]) :
4416 this.options.placement
4417
4418 $tip
4419 .detach()
4420 .css({ top: 0, left: 0, display: 'block' })
4421
4422 this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
4423
4424 pos = this.getPosition()
4425
4426 actualWidth = $tip[0].offsetWidth
4427 actualHeight = $tip[0].offsetHeight
4428
4429 switch (placement) {
4430 case 'bottom':
4431 tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
4432 break
4433 case 'top':
4434 tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
4435 break
4436 case 'left':
4437 tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
4438 break
4439 case 'right':
4440 tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
4441 break
4442 }
4443
4444 this.applyPlacement(tp, placement)
4445 this.$element.trigger('shown')
4446 }
4447 }
4448
4449 , applyPlacement: function(offset, placement){
4450 var $tip = this.tip()
4451 , width = $tip[0].offsetWidth
4452 , height = $tip[0].offsetHeight
4453 , actualWidth
4454 , actualHeight
4455 , delta
4456 , replace
4457
4458 $tip
4459 .offset(offset)
4460 .addClass(placement)
4461 .addClass('in')
4462
4463 actualWidth = $tip[0].offsetWidth
4464 actualHeight = $tip[0].offsetHeight
4465
4466 if (placement == 'top' && actualHeight != height) {
4467 offset.top = offset.top + height - actualHeight
4468 replace = true
4469 }
4470
4471 if (placement == 'bottom' || placement == 'top') {
4472 delta = 0
4473
4474 if (offset.left < 0){
4475 delta = offset.left * -2
4476 offset.left = 0
4477 $tip.offset(offset)
4478 actualWidth = $tip[0].offsetWidth
4479 actualHeight = $tip[0].offsetHeight
4480 }
4481
4482 this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
4483 } else {
4484 this.replaceArrow(actualHeight - height, actualHeight, 'top')
4485 }
4486
4487 if (replace) $tip.offset(offset)
4488 }
4489
4490 , replaceArrow: function(delta, dimension, position){
4491 this
4492 .arrow()
4493 .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
4494 }
4495
4496 , setContent: function () {
4497 var $tip = this.tip()
4498 , title = this.getTitle()
4499
4500 $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
4501 $tip.removeClass('fade in top bottom left right')
4502 }
4503
4504 , hide: function () {
4505 var that = this
4506 , $tip = this.tip()
4507 , e = $.Event('hide')
4508
4509 this.$element.trigger(e)
4510 if (e.isDefaultPrevented()) return
4511
4512 $tip.removeClass('in')
4513
4514 function removeWithAnimation() {
4515 var timeout = setTimeout(function () {
4516 $tip.off($.support.transition.end).detach()
4517 }, 500)
4518
4519 $tip.one($.support.transition.end, function () {
4520 clearTimeout(timeout)
4521 $tip.detach()
4522 })
4523 }
4524
4525 $.support.transition && this.$tip.hasClass('fade') ?
4526 removeWithAnimation() :
4527 $tip.detach()
4528
4529 this.$element.trigger('hidden')
4530
4531 return this
4532 }
4533
4534 , fixTitle: function () {
4535 var $e = this.$element
4536 if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
4537 $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
4538 }
4539 }
4540
4541 , hasContent: function () {
4542 return this.getTitle()
4543 }
4544
4545 , getPosition: function () {
4546 var el = this.$element[0]
4547 return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
4548 width: el.offsetWidth
4549 , height: el.offsetHeight
4550 }, this.$element.offset())
4551 }
4552
4553 , getTitle: function () {
4554 var title
4555 , $e = this.$element
4556 , o = this.options
4557
4558 title = $e.attr('data-original-title')
4559 || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
4560
4561 return title
4562 }
4563
4564 , tip: function () {
4565 return this.$tip = this.$tip || $(this.options.template)
4566 }
4567
4568 , arrow: function(){
4569 return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
4570 }
4571
4572 , validate: function () {
4573 if (!this.$element[0].parentNode) {
4574 this.hide()
4575 this.$element = null
4576 this.options = null
4577 }
4578 }
4579
4580 , enable: function () {
4581 this.enabled = true
4582 }
4583
4584 , disable: function () {
4585 this.enabled = false
4586 }
4587
4588 , toggleEnabled: function () {
4589 this.enabled = !this.enabled
4590 }
4591
4592 , toggle: function (e) {
4593 var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this
4594 self.tip().hasClass('in') ? self.hide() : self.show()
4595 }
4596
4597 , destroy: function () {
4598 this.hide().$element.off('.' + this.type).removeData(this.type)
4599 }
4600
4601 }
4602
4603
4604 /* TOOLTIP PLUGIN DEFINITION
4605 * ========================= */
4606
4607 var old = $.fn.tooltip
4608
4609 $.fn.tooltip = function ( option ) {
4610 return this.each(function () {
4611 var $this = $(this)
4612 , data = $this.data('tooltip')
4613 , options = typeof option == 'object' && option
4614 if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
4615 if (typeof option == 'string') data[option]()
4616 })
4617 }
4618
4619 $.fn.tooltip.Constructor = Tooltip
4620
4621 $.fn.tooltip.defaults = {
4622 animation: true
4623 , placement: 'top'
4624 , selector: false
4625 , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
4626 , trigger: 'hover focus'
4627 , title: ''
4628 , delay: 0
4629 , html: false
4630 , container: false
4631 }
4632
4633
4634 /* TOOLTIP NO CONFLICT
4635 * =================== */
4636
4637 $.fn.tooltip.noConflict = function () {
4638 $.fn.tooltip = old
4639 return this
4640 }
4641
4642}(window.jQuery);
4643/* ===========================================================
4644 * bootstrap-popover.js v2.3.1
4645 * http://twitter.github.com/bootstrap/javascript.html#popovers
4646 * ===========================================================
4647 * Copyright 2012 Twitter, Inc.
4648 *
4649 * Licensed under the Apache License, Version 2.0 (the "License");
4650 * you may not use this file except in compliance with the License.
4651 * You may obtain a copy of the License at
4652 *
4653 * http://www.apache.org/licenses/LICENSE-2.0
4654 *
4655 * Unless required by applicable law or agreed to in writing, software
4656 * distributed under the License is distributed on an "AS IS" BASIS,
4657 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4658 * See the License for the specific language governing permissions and
4659 * limitations under the License.
4660 * =========================================================== */
4661
4662
4663!function ($) {
4664
4665 "use strict"; // jshint ;_;
4666
4667
4668 /* POPOVER PUBLIC CLASS DEFINITION
4669 * =============================== */
4670
4671 var Popover = function (element, options) {
4672 this.init('popover', element, options)
4673 }
4674
4675
4676 /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
4677 ========================================== */
4678
4679 Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
4680
4681 constructor: Popover
4682
4683 , setContent: function () {
4684 var $tip = this.tip()
4685 , title = this.getTitle()
4686 , content = this.getContent()
4687
4688 $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
4689 $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
4690
4691 $tip.removeClass('fade top bottom left right in')
4692 }
4693
4694 , hasContent: function () {
4695 return this.getTitle() || this.getContent()
4696 }
4697
4698 , getContent: function () {
4699 var content
4700 , $e = this.$element
4701 , o = this.options
4702
4703 content = (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
4704 || $e.attr('data-content')
4705
4706 return content
4707 }
4708
4709 , tip: function () {
4710 if (!this.$tip) {
4711 this.$tip = $(this.options.template)
4712 }
4713 return this.$tip
4714 }
4715
4716 , destroy: function () {
4717 this.hide().$element.off('.' + this.type).removeData(this.type)
4718 }
4719
4720 })
4721
4722
4723 /* POPOVER PLUGIN DEFINITION
4724 * ======================= */
4725
4726 var old = $.fn.popover
4727
4728 $.fn.popover = function (option) {
4729 return this.each(function () {
4730 var $this = $(this)
4731 , data = $this.data('popover')
4732 , options = typeof option == 'object' && option
4733 if (!data) $this.data('popover', (data = new Popover(this, options)))
4734 if (typeof option == 'string') data[option]()
4735 })
4736 }
4737
4738 $.fn.popover.Constructor = Popover
4739
4740 $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
4741 placement: 'right'
4742 , trigger: 'click'
4743 , content: ''
4744 , template: '<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
4745 })
4746
4747
4748 /* POPOVER NO CONFLICT
4749 * =================== */
4750
4751 $.fn.popover.noConflict = function () {
4752 $.fn.popover = old
4753 return this
4754 }
4755
4756}(window.jQuery);
4757/* =============================================================
4758 * bootstrap-scrollspy.js v2.3.1
4759 * http://twitter.github.com/bootstrap/javascript.html#scrollspy
4760 * =============================================================
4761 * Copyright 2012 Twitter, Inc.
4762 *
4763 * Licensed under the Apache License, Version 2.0 (the "License");
4764 * you may not use this file except in compliance with the License.
4765 * You may obtain a copy of the License at
4766 *
4767 * http://www.apache.org/licenses/LICENSE-2.0
4768 *
4769 * Unless required by applicable law or agreed to in writing, software
4770 * distributed under the License is distributed on an "AS IS" BASIS,
4771 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4772 * See the License for the specific language governing permissions and
4773 * limitations under the License.
4774 * ============================================================== */
4775
4776
4777!function ($) {
4778
4779 "use strict"; // jshint ;_;
4780
4781
4782 /* SCROLLSPY CLASS DEFINITION
4783 * ========================== */
4784
4785 function ScrollSpy(element, options) {
4786 var process = $.proxy(this.process, this)
4787 , $element = $(element).is('body') ? $(window) : $(element)
4788 , href
4789 this.options = $.extend({}, $.fn.scrollspy.defaults, options)
4790 this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process)
4791 this.selector = (this.options.target
4792 || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
4793 || '') + ' .nav li > a'
4794 this.$body = $('body')
4795 this.refresh()
4796 this.process()
4797 }
4798
4799 ScrollSpy.prototype = {
4800
4801 constructor: ScrollSpy
4802
4803 , refresh: function () {
4804 var self = this
4805 , $targets
4806
4807 this.offsets = $([])
4808 this.targets = $([])
4809
4810 $targets = this.$body
4811 .find(this.selector)
4812 .map(function () {
4813 var $el = $(this)
4814 , href = $el.data('target') || $el.attr('href')
4815 , $href = /^#\w/.test(href) && $(href)
4816 return ( $href
4817 && $href.length
4818 && [[ $href.position().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]] ) || null
4819 })
4820 .sort(function (a, b) { return a[0] - b[0] })
4821 .each(function () {
4822 self.offsets.push(this[0])
4823 self.targets.push(this[1])
4824 })
4825 }
4826
4827 , process: function () {
4828 var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
4829 , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
4830 , maxScroll = scrollHeight - this.$scrollElement.height()
4831 , offsets = this.offsets
4832 , targets = this.targets
4833 , activeTarget = this.activeTarget
4834 , i
4835
4836 if (scrollTop >= maxScroll) {
4837 return activeTarget != (i = targets.last()[0])
4838 && this.activate ( i )
4839 }
4840
4841 for (i = offsets.length; i--;) {
4842 activeTarget != targets[i]
4843 && scrollTop >= offsets[i]
4844 && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
4845 && this.activate( targets[i] )
4846 }
4847 }
4848
4849 , activate: function (target) {
4850 var active
4851 , selector
4852
4853 this.activeTarget = target
4854
4855 $(this.selector)
4856 .parent('.active')
4857 .removeClass('active')
4858
4859 selector = this.selector
4860 + '[data-target="' + target + '"],'
4861 + this.selector + '[href="' + target + '"]'
4862
4863 active = $(selector)
4864 .parent('li')
4865 .addClass('active')
4866
4867 if (active.parent('.dropdown-menu').length) {
4868 active = active.closest('li.dropdown').addClass('active')
4869 }
4870
4871 active.trigger('activate')
4872 }
4873
4874 }
4875
4876
4877 /* SCROLLSPY PLUGIN DEFINITION
4878 * =========================== */
4879
4880 var old = $.fn.scrollspy
4881
4882 $.fn.scrollspy = function (option) {
4883 return this.each(function () {
4884 var $this = $(this)
4885 , data = $this.data('scrollspy')
4886 , options = typeof option == 'object' && option
4887 if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
4888 if (typeof option == 'string') data[option]()
4889 })
4890 }
4891
4892 $.fn.scrollspy.Constructor = ScrollSpy
4893
4894 $.fn.scrollspy.defaults = {
4895 offset: 10
4896 }
4897
4898
4899 /* SCROLLSPY NO CONFLICT
4900 * ===================== */
4901
4902 $.fn.scrollspy.noConflict = function () {
4903 $.fn.scrollspy = old
4904 return this
4905 }
4906
4907
4908 /* SCROLLSPY DATA-API
4909 * ================== */
4910
4911 $(window).on('load', function () {
4912 $('[data-spy="scroll"]').each(function () {
4913 var $spy = $(this)
4914 $spy.scrollspy($spy.data())
4915 })
4916 })
4917
4918}(window.jQuery);/* ========================================================
4919 * bootstrap-tab.js v2.3.1
4920 * http://twitter.github.com/bootstrap/javascript.html#tabs
4921 * ========================================================
4922 * Copyright 2012 Twitter, Inc.
4923 *
4924 * Licensed under the Apache License, Version 2.0 (the "License");
4925 * you may not use this file except in compliance with the License.
4926 * You may obtain a copy of the License at
4927 *
4928 * http://www.apache.org/licenses/LICENSE-2.0
4929 *
4930 * Unless required by applicable law or agreed to in writing, software
4931 * distributed under the License is distributed on an "AS IS" BASIS,
4932 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4933 * See the License for the specific language governing permissions and
4934 * limitations under the License.
4935 * ======================================================== */
4936
4937
4938!function ($) {
4939
4940 "use strict"; // jshint ;_;
4941
4942
4943 /* TAB CLASS DEFINITION
4944 * ==================== */
4945
4946 var Tab = function (element) {
4947 this.element = $(element)
4948 }
4949
4950 Tab.prototype = {
4951
4952 constructor: Tab
4953
4954 , show: function () {
4955 var $this = this.element
4956 , $ul = $this.closest('ul:not(.dropdown-menu)')
4957 , selector = $this.attr('data-target')
4958 , previous
4959 , $target
4960 , e
4961
4962 if (!selector) {
4963 selector = $this.attr('href')
4964 selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
4965 }
4966
4967 if ( $this.parent('li').hasClass('active') ) return
4968
4969 previous = $ul.find('.active:last a')[0]
4970
4971 e = $.Event('show', {
4972 relatedTarget: previous
4973 })
4974
4975 $this.trigger(e)
4976
4977 if (e.isDefaultPrevented()) return
4978
4979 $target = $(selector)
4980
4981 this.activate($this.parent('li'), $ul)
4982 this.activate($target, $target.parent(), function () {
4983 $this.trigger({
4984 type: 'shown'
4985 , relatedTarget: previous
4986 })
4987 })
4988 }
4989
4990 , activate: function ( element, container, callback) {
4991 var $active = container.find('> .active')
4992 , transition = callback
4993 && $.support.transition
4994 && $active.hasClass('fade')
4995
4996 function next() {
4997 $active
4998 .removeClass('active')
4999 .find('> .dropdown-menu > .active')
5000 .removeClass('active')
5001
5002 element.addClass('active')
5003
5004 if (transition) {
5005 element[0].offsetWidth // reflow for transition
5006 element.addClass('in')
5007 } else {
5008 element.removeClass('fade')
5009 }
5010
5011 if ( element.parent('.dropdown-menu') ) {
5012 element.closest('li.dropdown').addClass('active')
5013 }
5014
5015 callback && callback()
5016 }
5017
5018 transition ?
5019 $active.one($.support.transition.end, next) :
5020 next()
5021
5022 $active.removeClass('in')
5023 }
5024 }
5025
5026
5027 /* TAB PLUGIN DEFINITION
5028 * ===================== */
5029
5030 var old = $.fn.tab
5031
5032 $.fn.tab = function ( option ) {
5033 return this.each(function () {
5034 var $this = $(this)
5035 , data = $this.data('tab')
5036 if (!data) $this.data('tab', (data = new Tab(this)))
5037 if (typeof option == 'string') data[option]()
5038 })
5039 }
5040
5041 $.fn.tab.Constructor = Tab
5042
5043
5044 /* TAB NO CONFLICT
5045 * =============== */
5046
5047 $.fn.tab.noConflict = function () {
5048 $.fn.tab = old
5049 return this
5050 }
5051
5052
5053 /* TAB DATA-API
5054 * ============ */
5055
5056 $(document).on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
5057 e.preventDefault()
5058 $(this).tab('show')
5059 })
5060
5061}(window.jQuery);/* =============================================================
5062 * bootstrap-typeahead.js v2.3.1
5063 * http://twitter.github.com/bootstrap/javascript.html#typeahead
5064 * =============================================================
5065 * Copyright 2012 Twitter, Inc.
5066 *
5067 * Licensed under the Apache License, Version 2.0 (the "License");
5068 * you may not use this file except in compliance with the License.
5069 * You may obtain a copy of the License at
5070 *
5071 * http://www.apache.org/licenses/LICENSE-2.0
5072 *
5073 * Unless required by applicable law or agreed to in writing, software
5074 * distributed under the License is distributed on an "AS IS" BASIS,
5075 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5076 * See the License for the specific language governing permissions and
5077 * limitations under the License.
5078 * ============================================================ */
5079
5080
5081!function($){
5082
5083 "use strict"; // jshint ;_;
5084
5085
5086 /* TYPEAHEAD PUBLIC CLASS DEFINITION
5087 * ================================= */
5088
5089 var Typeahead = function (element, options) {
5090 this.$element = $(element)
5091 this.options = $.extend({}, $.fn.typeahead.defaults, options)
5092 this.matcher = this.options.matcher || this.matcher
5093 this.sorter = this.options.sorter || this.sorter
5094 this.highlighter = this.options.highlighter || this.highlighter
5095 this.updater = this.options.updater || this.updater
5096 this.source = this.options.source
5097 this.$menu = $(this.options.menu)
5098 this.shown = false
5099 this.listen()
5100 }
5101
5102 Typeahead.prototype = {
5103
5104 constructor: Typeahead
5105
5106 , select: function () {
5107 var val = this.$menu.find('.active').attr('data-value')
5108 this.$element
5109 .val(this.updater(val))
5110 .change()
5111 return this.hide()
5112 }
5113
5114 , updater: function (item) {
5115 return item
5116 }
5117
5118 , show: function () {
5119 var pos = $.extend({}, this.$element.position(), {
5120 height: this.$element[0].offsetHeight
5121 })
5122
5123 this.$menu
5124 .insertAfter(this.$element)
5125 .css({
5126 top: pos.top + pos.height
5127 , left: pos.left
5128 })
5129 .show()
5130
5131 this.shown = true
5132 return this
5133 }
5134
5135 , hide: function () {
5136 this.$menu.hide()
5137 this.shown = false
5138 return this
5139 }
5140
5141 , lookup: function (event) {
5142 var items
5143
5144 this.query = this.$element.val()
5145
5146 if (!this.query || this.query.length < this.options.minLength) {
5147 return this.shown ? this.hide() : this
5148 }
5149
5150 items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
5151
5152 return items ? this.process(items) : this
5153 }
5154
5155 , process: function (items) {
5156 var that = this
5157
5158 items = $.grep(items, function (item) {
5159 return that.matcher(item)
5160 })
5161
5162 items = this.sorter(items)
5163
5164 if (!items.length) {
5165 return this.shown ? this.hide() : this
5166 }
5167
5168 return this.render(items.slice(0, this.options.items)).show()
5169 }
5170
5171 , matcher: function (item) {
5172 return ~item.toLowerCase().indexOf(this.query.toLowerCase())
5173 }
5174
5175 , sorter: function (items) {
5176 var beginswith = []
5177 , caseSensitive = []
5178 , caseInsensitive = []
5179 , item
5180
5181 while (item = items.shift()) {
5182 if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
5183 else if (~item.indexOf(this.query)) caseSensitive.push(item)
5184 else caseInsensitive.push(item)
5185 }
5186
5187 return beginswith.concat(caseSensitive, caseInsensitive)
5188 }
5189
5190 , highlighter: function (item) {
5191 var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
5192 return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
5193 return '<strong>' + match + '</strong>'
5194 })
5195 }
5196
5197 , render: function (items) {
5198 var that = this
5199
5200 items = $(items).map(function (i, item) {
5201 i = $(that.options.item).attr('data-value', item)
5202 i.find('a').html(that.highlighter(item))
5203 return i[0]
5204 })
5205
5206 items.first().addClass('active')
5207 this.$menu.html(items)
5208 return this
5209 }
5210
5211 , next: function (event) {
5212 var active = this.$menu.find('.active').removeClass('active')
5213 , next = active.next()
5214
5215 if (!next.length) {
5216 next = $(this.$menu.find('li')[0])
5217 }
5218
5219 next.addClass('active')
5220 }
5221
5222 , prev: function (event) {
5223 var active = this.$menu.find('.active').removeClass('active')
5224 , prev = active.prev()
5225
5226 if (!prev.length) {
5227 prev = this.$menu.find('li').last()
5228 }
5229
5230 prev.addClass('active')
5231 }
5232
5233 , listen: function () {
5234 this.$element
5235 .on('focus', $.proxy(this.focus, this))
5236 .on('blur', $.proxy(this.blur, this))
5237 .on('keypress', $.proxy(this.keypress, this))
5238 .on('keyup', $.proxy(this.keyup, this))
5239
5240 if (this.eventSupported('keydown')) {
5241 this.$element.on('keydown', $.proxy(this.keydown, this))
5242 }
5243
5244 this.$menu
5245 .on('click', $.proxy(this.click, this))
5246 .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
5247 .on('mouseleave', 'li', $.proxy(this.mouseleave, this))
5248 }
5249
5250 , eventSupported: function(eventName) {
5251 var isSupported = eventName in this.$element
5252 if (!isSupported) {
5253 this.$element.setAttribute(eventName, 'return;')
5254 isSupported = typeof this.$element[eventName] === 'function'
5255 }
5256 return isSupported
5257 }
5258
5259 , move: function (e) {
5260 if (!this.shown) return
5261
5262 switch(e.keyCode) {
5263 case 9: // tab
5264 case 13: // enter
5265 case 27: // escape
5266 e.preventDefault()
5267 break
5268
5269 case 38: // up arrow
5270 e.preventDefault()
5271 this.prev()
5272 break
5273
5274 case 40: // down arrow
5275 e.preventDefault()
5276 this.next()
5277 break
5278 }
5279
5280 e.stopPropagation()
5281 }
5282
5283 , keydown: function (e) {
5284 this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
5285 this.move(e)
5286 }
5287
5288 , keypress: function (e) {
5289 if (this.suppressKeyPressRepeat) return
5290 this.move(e)
5291 }
5292
5293 , keyup: function (e) {
5294 switch(e.keyCode) {
5295 case 40: // down arrow
5296 case 38: // up arrow
5297 case 16: // shift
5298 case 17: // ctrl
5299 case 18: // alt
5300 break
5301
5302 case 9: // tab
5303 case 13: // enter
5304 if (!this.shown) return
5305 this.select()
5306 break
5307
5308 case 27: // escape
5309 if (!this.shown) return
5310 this.hide()
5311 break
5312
5313 default:
5314 this.lookup()
5315 }
5316
5317 e.stopPropagation()
5318 e.preventDefault()
5319 }
5320
5321 , focus: function (e) {
5322 this.focused = true
5323 }
5324
5325 , blur: function (e) {
5326 this.focused = false
5327 if (!this.mousedover && this.shown) this.hide()
5328 }
5329
5330 , click: function (e) {
5331 e.stopPropagation()
5332 e.preventDefault()
5333 this.select()
5334 this.$element.focus()
5335 }
5336
5337 , mouseenter: function (e) {
5338 this.mousedover = true
5339 this.$menu.find('.active').removeClass('active')
5340 $(e.currentTarget).addClass('active')
5341 }
5342
5343 , mouseleave: function (e) {
5344 this.mousedover = false
5345 if (!this.focused && this.shown) this.hide()
5346 }
5347
5348 }
5349
5350
5351 /* TYPEAHEAD PLUGIN DEFINITION
5352 * =========================== */
5353
5354 var old = $.fn.typeahead
5355
5356 $.fn.typeahead = function (option) {
5357 return this.each(function () {
5358 var $this = $(this)
5359 , data = $this.data('typeahead')
5360 , options = typeof option == 'object' && option
5361 if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
5362 if (typeof option == 'string') data[option]()
5363 })
5364 }
5365
5366 $.fn.typeahead.defaults = {
5367 source: []
5368 , items: 8
5369 , menu: '<ul class="typeahead dropdown-menu"></ul>'
5370 , item: '<li><a href="#"></a></li>'
5371 , minLength: 1
5372 }
5373
5374 $.fn.typeahead.Constructor = Typeahead
5375
5376
5377 /* TYPEAHEAD NO CONFLICT
5378 * =================== */
5379
5380 $.fn.typeahead.noConflict = function () {
5381 $.fn.typeahead = old
5382 return this
5383 }
5384
5385
5386 /* TYPEAHEAD DATA-API
5387 * ================== */
5388
5389 $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
5390 var $this = $(this)
5391 if ($this.data('typeahead')) return
5392 $this.typeahead($this.data())
5393 })
5394
5395}(window.jQuery);
5396/* ==========================================================
5397 * bootstrap-affix.js v2.3.1
5398 * http://twitter.github.com/bootstrap/javascript.html#affix
5399 * ==========================================================
5400 * Copyright 2012 Twitter, Inc.
5401 *
5402 * Licensed under the Apache License, Version 2.0 (the "License");
5403 * you may not use this file except in compliance with the License.
5404 * You may obtain a copy of the License at
5405 *
5406 * http://www.apache.org/licenses/LICENSE-2.0
5407 *
5408 * Unless required by applicable law or agreed to in writing, software
5409 * distributed under the License is distributed on an "AS IS" BASIS,
5410 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5411 * See the License for the specific language governing permissions and
5412 * limitations under the License.
5413 * ========================================================== */
5414
5415
5416!function ($) {
5417
5418 "use strict"; // jshint ;_;
5419
5420
5421 /* AFFIX CLASS DEFINITION
5422 * ====================== */
5423
5424 var Affix = function (element, options) {
5425 this.options = $.extend({}, $.fn.affix.defaults, options)
5426 this.$window = $(window)
5427 .on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
5428 .on('click.affix.data-api', $.proxy(function () { setTimeout($.proxy(this.checkPosition, this), 1) }, this))
5429 this.$element = $(element)
5430 this.checkPosition()
5431 }
5432
5433 Affix.prototype.checkPosition = function () {
5434 if (!this.$element.is(':visible')) return
5435
5436 var scrollHeight = $(document).height()
5437 , scrollTop = this.$window.scrollTop()
5438 , position = this.$element.offset()
5439 , offset = this.options.offset
5440 , offsetBottom = offset.bottom
5441 , offsetTop = offset.top
5442 , reset = 'affix affix-top affix-bottom'
5443 , affix
5444
5445 if (typeof offset != 'object') offsetBottom = offsetTop = offset
5446 if (typeof offsetTop == 'function') offsetTop = offset.top()
5447 if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
5448
5449 affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
5450 false : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
5451 'bottom' : offsetTop != null && scrollTop <= offsetTop ?
5452 'top' : false
5453
5454 if (this.affixed === affix) return
5455
5456 this.affixed = affix
5457 this.unpin = affix == 'bottom' ? position.top - scrollTop : null
5458
5459 this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
5460 }
5461
5462
5463 /* AFFIX PLUGIN DEFINITION
5464 * ======================= */
5465
5466 var old = $.fn.affix
5467
5468 $.fn.affix = function (option) {
5469 return this.each(function () {
5470 var $this = $(this)
5471 , data = $this.data('affix')
5472 , options = typeof option == 'object' && option
5473 if (!data) $this.data('affix', (data = new Affix(this, options)))
5474 if (typeof option == 'string') data[option]()
5475 })
5476 }
5477
5478 $.fn.affix.Constructor = Affix
5479
5480 $.fn.affix.defaults = {
5481 offset: 0
5482 }
5483
5484
5485 /* AFFIX NO CONFLICT
5486 * ================= */
5487
5488 $.fn.affix.noConflict = function () {
5489 $.fn.affix = old
5490 return this
5491 }
5492
5493
5494 /* AFFIX DATA-API
5495 * ============== */
5496
5497 $(window).on('load', function () {
5498 $('[data-spy="affix"]').each(function () {
5499 var $spy = $(this)
5500 , data = $spy.data()
5501
5502 data.offset = data.offset || {}
5503
5504 data.offsetBottom && (data.offset.bottom = data.offsetBottom)
5505 data.offsetTop && (data.offset.top = data.offsetTop)
5506
5507 $spy.affix(data)
5508 })
5509 })
5510
5511
5512}(window.jQuery);(function() {
5513 var urlRegex = /(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
5514
5515 function isCurie(string) {
5516 return string.split(':').length > 1;
5517 };
5518
5519 var HAL = {
5520 Models: {},
5521 Views: {},
5522 Http: {},
5523 currentDocument: {},
5524 jsonIndent: 2,
5525 isUrl: function(str) {
5526 return str.match(urlRegex) || isCurie(str);
5527 },
5528 isFollowableHeader: function(headerName) {
5529 return headerName === 'Location' || headerName === 'Content-Location';
5530 },
5531 truncateIfUrl: function(str) {
5532 var replaceRegex = /(http|https):\/\/([^\/]*)\//;
5533 return str.replace(replaceRegex, '.../');
5534 },
5535 normalizeUrl: function(rel) {
5536 var cur = location.hash.slice(1);
5537 var uri = new URI(rel)
5538 var norm = uri.absoluteTo(cur);
5539
5540 return norm
5541 },
5542 buildUrl: function(rel) {
5543 if (!HAL.currentDocument._links) {
5544 return rel;
5545 }
5546 if (!rel.match(urlRegex) && isCurie(rel) && HAL.currentDocument._links.curies) {
5547 var parts = rel.split(':');
5548 var curies = HAL.currentDocument._links.curies;
5549 for (var i=0; i<curies.length; i++) {
5550 if (curies[i].name == parts[0]) {
5551 var tmpl = uritemplate(curies[i].href);
5552 return tmpl.expand({ rel: parts[1] });
5553 }
5554 }
5555 }
5556 else if (!rel.match(urlRegex) && isCurie(rel) && HAL.currentDocument._links.curie) {
5557 // Backward compatibility with <04 version of spec.
5558 var tmpl = uritemplate(HAL.currentDocument._links.curie.href);
5559 return tmpl.expand({ rel: rel.split(':')[1] });
5560 }
5561 else {
5562 return rel;
5563 }
5564 },
5565 parseHeaders: function(string) {
5566 var header_lines = string.split("\n");
5567 var headers = {};
5568 _.each(header_lines, function(line) {
5569 var parts = line.split(':');
5570 if (parts.length > 1) {
5571 var name = parts.shift().trim();
5572 var value = parts.join(':').trim();
5573 headers[name] = value;
5574 }
5575 });
5576 return headers;
5577 },
5578 customPostForm: undefined
5579 };
5580
5581 window.HAL = HAL;
5582})();
5583HAL.Browser = Backbone.Router.extend({
5584 initialize: function(opts) {
5585 opts = opts || {};
5586
5587 var vent = _.extend({}, Backbone.Events),
5588 $container = opts.container || $('#browser');
5589
5590 this.entryPoint = opts.entryPoint || '/';
5591
5592 // TODO: don't hang currentDoc off namespace
5593 vent.bind('response', function(e) {
5594 window.HAL.currentDocument = e.resource || {};
5595 });
5596
5597 vent.bind('location-go', _.bind(this.loadUrl, this));
5598
5599 HAL.client = new HAL.Http.Client({ vent: vent });
5600
5601 var browser = new HAL.Views.Browser({ vent: vent, entryPoint: this.entryPoint });
5602 browser.render()
5603
5604 $container.html(browser.el);
5605 vent.trigger('app:loaded');
5606
5607 if (window.location.hash === '') {
5608 window.location.hash = this.entryPoint;
5609 }
5610
5611 if(location.hash.slice(1,9) === 'NON-GET:') {
5612 new HAL.Views.NonSafeRequestDialog({
5613 href: location.hash.slice(9),
5614 vent: vent
5615 }).render({});
5616 }
5617 },
5618
5619 routes: {
5620 '*url': 'resourceRoute'
5621 },
5622
5623 loadUrl: function(url) {
5624 if (this.getHash() === url) {
5625 HAL.client.get(url);
5626 } else {
5627 window.location.hash = url;
5628 }
5629 },
5630
5631 getHash: function() {
5632 return window.location.hash.slice(1);
5633 },
5634
5635 resourceRoute: function() {
5636 url = location.hash.slice(1);
5637 console.log('target url changed to: ' + url);
5638 if (url.slice(0,8) !== 'NON-GET:') {
5639 HAL.client.get(url);
5640 }
5641 }
5642});
5643HAL.Http.Client = function(opts) {
5644 this.vent = opts.vent;
5645 this.defaultHeaders = { 'Accept': 'application/hal+json, application/json, */*; q=0.01' };
5646 this.headers = this.defaultHeaders;
5647};
5648
5649HAL.Http.Client.prototype.get = function(url) {
5650 var self = this;
5651 this.vent.trigger('location-change', { url: url });
5652 var jqxhr = $.ajax({
5653 url: url,
5654 dataType: 'json',
5655 xhrFields: {
5656 withCredentials: true
5657 },
5658 headers: this.headers,
5659 success: function(resource, textStatus, jqXHR) {
5660 self.vent.trigger('response', {
5661 resource: resource,
5662 jqxhr: jqXHR,
5663 headers: jqXHR.getAllResponseHeaders()
5664 });
5665 }
5666 }).error(function() {
5667 self.vent.trigger('fail-response', { jqxhr: jqxhr });
5668 });
5669};
5670
5671HAL.Http.Client.prototype.request = function(opts) {
5672 var self = this;
5673 opts.dataType = 'json';
5674 opts.xhrFields = opts.xhrFields || {};
5675 opts.xhrFields.withCredentials = opts.xhrFields.withCredentials || true;
5676 var jwt = JSON.parse(localStorage.token);
5677 opts.headers = {
5678 Authorization: jwt.token_type + ' ' + jwt.access_token,
5679 };
5680 self.vent.trigger('location-change', { url: opts.url });
5681 return jqxhr = $.ajax(opts);
5682};
5683
5684HAL.Http.Client.prototype.updateHeaders = function(headers) {
5685 this.headers = headers;
5686};
5687
5688HAL.Http.Client.prototype.getHeaders = function() {
5689 return this.headers;
5690};
5691HAL.Models.Resource = Backbone.Model.extend({
5692 initialize: function(representation) {
5693 representation = representation || {};
5694 this.links = representation._links;
5695 this.title = representation.title;
5696 if(representation._embedded !== undefined) {
5697 this.embeddedResources = this.buildEmbeddedResources(representation._embedded);
5698 }
5699 this.set(representation);
5700 this.unset('_embedded', { silent: true });
5701 this.unset('_links', { silent: true });
5702 },
5703
5704 buildEmbeddedResources: function(embeddedResources) {
5705 var result = {};
5706 _.each(embeddedResources, function(obj, rel) {
5707 if($.isArray(obj)) {
5708 var arr = [];
5709 _.each(obj, function(resource, i) {
5710 var newResource = new HAL.Models.Resource(resource);
5711 newResource.identifier = rel + '[' + i + ']';
5712 newResource.embed_rel = rel;
5713 arr.push(newResource);
5714 });
5715 result[rel] = arr;
5716 } else {
5717 var newResource = new HAL.Models.Resource(obj);
5718 newResource.identifier = rel;
5719 newResource.embed_rel = rel;
5720 result[rel] = newResource;
5721 }
5722 });
5723 return result;
5724 }
5725});
5726HAL.Views.Browser = Backbone.View.extend({
5727 initialize: function(opts) {
5728 var self = this;
5729 this.vent = opts.vent;
5730 this.entryPoint = opts.entryPoint;
5731 this.explorerView = new HAL.Views.Explorer({ vent: this.vent });
5732 this.inspectorView = new HAL.Views.Inspector({ vent: this.vent });
5733 },
5734
5735 className: 'hal-browser row-fluid',
5736
5737 render: function() {
5738 this.$el.empty();
5739
5740 this.inspectorView.render();
5741 this.explorerView.render();
5742
5743 this.$el.html(this.explorerView.el);
5744 this.$el.append(this.inspectorView.el);
5745
5746 var entryPoint = this.entryPoint;
5747
5748 $("#entryPointLink").click(function(event) {
5749 event.preventDefault();
5750 window.location.hash = entryPoint;
5751 });
5752 return this;
5753 }
5754});
5755HAL.Views.Explorer = Backbone.View.extend({
5756 initialize: function(opts) {
5757 var self = this;
5758 this.vent = opts.vent;
5759 this.navigationView = new HAL.Views.Navigation({ vent: this.vent });
5760 this.resourceView = new HAL.Views.Resource({ vent: this.vent });
5761 },
5762
5763 className: 'explorer span6',
5764
5765 render: function() {
5766 this.navigationView.render();
5767
5768 this.$el.html(this.template());
5769
5770 this.$el.append(this.navigationView.el);
5771 this.$el.append(this.resourceView.el);
5772 },
5773
5774 template: function() {
5775 return '<h1>Explorer</h1>';
5776 }
5777});
5778HAL.Views.Inspector = Backbone.View.extend({
5779 initialize: function(opts) {
5780 this.vent = opts.vent;
5781
5782 _.bindAll(this, 'renderDocumentation');
5783 _.bindAll(this, 'renderResponse');
5784
5785 this.vent.bind('show-docs', this.renderDocumentation);
5786 this.vent.bind('response', this.renderResponse);
5787 },
5788
5789 className: 'inspector span6',
5790
5791 render: function() {
5792 this.$el.html(this.template());
5793 },
5794
5795 renderResponse: function(response) {
5796 var responseView = new HAL.Views.Response({ vent: this.vent });
5797
5798 this.render();
5799 responseView.render(response);
5800
5801 this.$el.append(responseView.el);
5802 },
5803
5804 renderDocumentation: function(e) {
5805 var docView = new HAL.Views.Documenation({ vent: this.vent });
5806
5807 this.render();
5808 docView.render(e.url);
5809
5810 this.$el.append(docView.el);
5811 },
5812
5813 template: function() {
5814 return '<h1>Inspector</h1>';
5815 }
5816});
5817HAL.Views.Navigation = Backbone.View.extend({
5818 initialize: function(opts) {
5819 this.vent = opts.vent;
5820 this.locationBar = new HAL.Views.LocationBar({ vent: this.vent });
5821 this.requestHeadersView = new HAL.Views.RequestHeaders({ vent: this.vent });
5822 },
5823
5824 className: 'navigation',
5825
5826 render: function() {
5827 this.$el.empty();
5828
5829 this.locationBar.render();
5830 this.requestHeadersView.render();
5831
5832 this.$el.append(this.locationBar.el);
5833 this.$el.append(this.requestHeadersView.el);
5834 }
5835});
5836HAL.Views.LocationBar = Backbone.View.extend({
5837 initialize: function(opts) {
5838 this.vent = opts.vent;
5839 _.bindAll(this, 'render');
5840 _.bindAll(this, 'onButtonClick');
5841 this.vent.bind('location-change', this.render);
5842 this.vent.bind('location-change', _.bind(this.showSpinner, this));
5843 this.vent.bind('response', _.bind(this.hideSpinner, this));
5844 },
5845
5846 events: {
5847 'submit form': 'onButtonClick'
5848 },
5849
5850 className: 'address',
5851
5852 render: function(e) {
5853 e = e || { url: '' };
5854 this.$el.html(this.template(e));
5855 },
5856
5857 onButtonClick: function(e) {
5858 e.preventDefault();
5859 this.vent.trigger('location-go', this.getLocation());
5860 },
5861
5862 getLocation: function() {
5863 return this.$el.find('input').val();
5864 },
5865
5866 showSpinner: function() {
5867 this.$el.find('.ajax-loader').addClass('visible');
5868 },
5869
5870 hideSpinner: function() {
5871 this.$el.find('.ajax-loader').removeClass('visible');
5872 },
5873
5874 template: _.template($('#location-bar-template').html())
5875});
5876HAL.Views.RequestHeaders = Backbone.View.extend({
5877 initialize: function(opts) {
5878 var self = this;
5879 this.vent = opts.vent;
5880
5881 _.bindAll(this, 'updateRequestHeaders');
5882
5883 this.vent.bind('app:loaded', function() {
5884 self.updateRequestHeaders();
5885 });
5886 },
5887
5888 className: 'request-headers',
5889
5890 events: {
5891 'blur textarea': 'updateRequestHeaders'
5892 },
5893
5894 updateRequestHeaders: function(e) {
5895 var inputText = this.$('textarea').val() || '';
5896 headers = HAL.parseHeaders(inputText);
5897 HAL.client.updateHeaders(_.defaults(headers, HAL.client.defaultHeaders))
5898 },
5899
5900 render: function() {
5901 this.$el.html(this.template());
5902 },
5903
5904 template: _.template($('#request-headers-template').html())
5905});
5906HAL.Views.Resource = Backbone.View.extend({
5907 initialize: function(opts) {
5908 var self = this;
5909
5910 this.vent = opts.vent;
5911
5912 this.vent.bind('response', function(e) {
5913 self.render(new HAL.Models.Resource(e.resource));
5914 });
5915
5916 this.vent.bind('fail-response', function(e) {
5917 try {
5918 resource = JSON.parse(e.jqxhr.responseText);
5919 } catch(err) {
5920 resource = null;
5921 }
5922 self.vent.trigger('response', { resource: resource, jqxhr: e.jqxhr });
5923 });
5924 },
5925
5926 className: 'resource',
5927
5928 render: function(resource) {
5929 var linksView = new HAL.Views.Links({ vent: this.vent }),
5930 propertiesView = new HAL.Views.Properties({ vent: this.vent }),
5931 embeddedResourcesView
5932
5933 propertiesView.render(resource.toJSON());
5934 linksView.render(resource.links);
5935
5936 this.$el.empty();
5937 this.$el.append(propertiesView.el);
5938 this.$el.append(linksView.el);
5939
5940 if (resource.embeddedResources) {
5941 embeddedResourcesView = new HAL.Views.EmbeddedResources({ vent: this.vent });
5942 embeddedResourcesView.render(resource.embeddedResources);
5943 this.$el.append(embeddedResourcesView.el);
5944 }
5945
5946 return this;
5947 }
5948});
5949HAL.Views.Properties = Backbone.View.extend({
5950 initialize: function(opts) {
5951 this.vent = opts.vent;
5952 _.bindAll(this, 'render');
5953 },
5954
5955 className: 'properties',
5956
5957 render: function(props) {
5958 this.$el.html(this.template({ properties: props }));
5959 },
5960
5961 template: _.template($('#properties-template').html())
5962});
5963HAL.Views.Links = Backbone.View.extend({
5964 initialize: function(opts) {
5965 this.vent = opts.vent;
5966 },
5967
5968 events: {
5969 'click .follow': 'followLink',
5970 'click .non-get': 'showNonSafeRequestDialog',
5971 'click .query': 'showUriQueryDialog',
5972 'click .dox': 'showDocs'
5973 },
5974
5975 className: 'links',
5976
5977 followLink: function(e) {
5978 e.preventDefault();
5979 var $target = $(e.currentTarget);
5980 var uri = $target.attr('href');
5981 window.location.hash = uri;
5982 },
5983
5984 showUriQueryDialog: function(e) {
5985 e.preventDefault();
5986
5987 var $target = $(e.currentTarget);
5988 var uri = $target.attr('href');
5989
5990 new HAL.Views.QueryUriDialog({
5991 href: uri
5992 }).render({});
5993 },
5994
5995 showNonSafeRequestDialog: function(e) {
5996 e.preventDefault();
5997
5998 var postForm = (HAL.customPostForm !== undefined) ? HAL.customPostForm : HAL.Views.NonSafeRequestDialog;
5999 var d = new postForm({
6000 href: $(e.currentTarget).attr('href'),
6001 vent: this.vent
6002 }).render({})
6003 },
6004
6005 showDocs: function(e) {
6006 e.preventDefault();
6007 var $target = $(e.target);
6008 var uri = $target.attr('href') || $target.parent().attr('href');
6009 this.vent.trigger('show-docs', { url: uri });
6010 },
6011
6012 template: _.template($('#links-template').html()),
6013
6014 render: function(links) {
6015 this.$el.html(this.template({ links: links }));
6016 }
6017});
6018HAL.Views.EmbeddedResources = Backbone.View.extend({
6019 initialize: function(opts) {
6020 this.vent = opts.vent;
6021 _.bindAll(this, 'render');
6022 },
6023
6024 className: 'embedded-resources accordion',
6025
6026 render: function(resources) {
6027 var self = this,
6028 resourceViews = [],
6029 buildView = function(resource) {
6030 return new HAL.Views.EmbeddedResource({
6031 resource: resource,
6032 vent: self.vent
6033 });
6034 };
6035
6036 _.each(resources, function(prop) {
6037 if ($.isArray(prop)) {
6038 _.each(prop, function(resource) {
6039 resourceViews.push(buildView(resource));
6040 });
6041 } else {
6042 resourceViews.push(buildView(prop));
6043 }
6044 });
6045
6046 this.$el.html(this.template());
6047
6048 _.each(resourceViews, function(view) {
6049 view.render();
6050 self.$el.append(view.el);
6051 });
6052
6053
6054 return this;
6055 },
6056
6057 template: _.template($('#embedded-resources-template').html())
6058});
6059HAL.Views.EmbeddedResource = Backbone.View.extend({
6060 initialize: function(opts) {
6061 this.vent = opts.vent;
6062 this.resource = opts.resource;
6063
6064 this.propertiesView = new HAL.Views.Properties({});
6065 this.linksView = new HAL.Views.Links({
6066 vent: this.vent
6067 });
6068
6069 _.bindAll(this, 'onToggleClick');
6070 _.bindAll(this, 'onDoxClick');
6071 },
6072
6073 events: {
6074 'click a.accordion-toggle': 'onToggleClick',
6075 'click span.dox': 'onDoxClick'
6076 },
6077
6078 className: 'embedded-resource accordion-group',
6079
6080 onToggleClick: function(e) {
6081 e.preventDefault();
6082 this.$accordionBody.collapse('toggle');
6083 },
6084
6085 onDoxClick: function(e) {
6086 e.preventDefault();
6087 this.vent.trigger('show-docs', {
6088 url: $(e.currentTarget).data('href')
6089 });
6090 return false;
6091 },
6092
6093 render: function() {
6094 this.$el.empty();
6095
6096 this.propertiesView.render(this.resource.toJSON());
6097 this.linksView.render(this.resource.links);
6098
6099 this.$el.append(this.template({
6100 resource: this.resource
6101 }));
6102
6103 var $inner = $('<div class="accordion-inner"></div>');
6104 $inner.append(this.propertiesView.el);
6105 $inner.append(this.linksView.el);
6106
6107 this.$accordionBody = $('<div class="accordion-body collapse"></div>');
6108 this.$accordionBody.append($inner)
6109
6110 this.$el.append(this.$accordionBody);
6111 },
6112
6113 template: _.template($('#embedded-resource-template').html())
6114});
6115HAL.Views.NonSafeRequestDialog = Backbone.View.extend({
6116 initialize: function(opts) {
6117 this.href = opts.href;
6118 this.vent = opts.vent;
6119 this.uriTemplate = uritemplate(this.href);
6120 _.bindAll(this, 'submitQuery');
6121 },
6122
6123 events: {
6124 'submit form': 'submitQuery'
6125 },
6126
6127 className: 'modal fade',
6128
6129 submitQuery: function(e) {
6130 e.preventDefault();
6131
6132 var self = this,
6133 opts = {
6134 url: this.$('.url').val(),
6135 headers: HAL.parseHeaders(this.$('.headers').val()),
6136 method: this.$('.method').val(),
6137 data: this.$('.body').val()
6138 };
6139
6140 var request = HAL.client.request(opts);
6141 request.done(function(response) {
6142 self.vent.trigger('response', { resource: response, jqxhr: jqxhr });
6143 }).fail(function(response) {
6144 self.vent.trigger('fail-response', { jqxhr: jqxhr });
6145 }).always(function() {
6146 self.vent.trigger('response-headers', { jqxhr: jqxhr });
6147 window.location.hash = 'NON-GET:' + opts.url;
6148 });
6149
6150 this.$el.modal('hide');
6151 },
6152
6153 render: function(opts) {
6154 var headers = HAL.client.getHeaders(),
6155 headersString = '';
6156
6157 _.each(headers, function(value, name) {
6158 headersString += name + ': ' + value + '\n';
6159 });
6160
6161 this.$el.html(this.template({ href: this.href, user_defined_headers: headersString }));
6162 this.$el.modal();
6163 return this;
6164 },
6165
6166 template: _.template($('#non-safe-request-template').html())
6167});
6168HAL.Views.QueryUriDialog = Backbone.View.extend({
6169 initialize: function(opts) {
6170 this.href = opts.href;
6171 this.uriTemplate = uritemplate(this.href);
6172 _.bindAll(this, 'submitQuery');
6173 _.bindAll(this, 'renderPreview');
6174 },
6175
6176 className: 'modal fade',
6177
6178 events: {
6179 'submit form': 'submitQuery',
6180 'keyup textarea': 'renderPreview',
6181 'change textarea': 'renderPreview'
6182 },
6183
6184 submitQuery: function(e) {
6185 e.preventDefault();
6186 var input;
6187 try {
6188 input = JSON.parse(this.$('textarea').val());
6189 } catch(err) {
6190 input = {};
6191 }
6192 this.$el.modal('hide');
6193 window.location.hash = this.uriTemplate.expand(this.cleanInput(input));
6194 },
6195
6196 renderPreview: function(e) {
6197 var input, result;
6198 try {
6199 input = JSON.parse($(e.target).val());
6200 result = this.uriTemplate.expand(this.cleanInput(input));
6201 } catch (err) {
6202 result = 'Invalid json input';
6203 }
6204 this.$('.preview').text(result);
6205 },
6206
6207 extractExpressionNames: function (template) {
6208 var names = [];
6209 for (var i=0; i<template.set.length; i++) {
6210 if (template.set[i].vars) {
6211 for (var j=0; j<template.set[i].vars.length; j++) {
6212 names.push(template.set[i].vars[j].name);
6213 }
6214 }
6215 }
6216 return names;
6217 },
6218
6219 createDefaultInput: function (expressionNames) {
6220 var defaultInput = {};
6221 for (var i=0; i<expressionNames.length; i++) {
6222 defaultInput[expressionNames[i]] = '';
6223 }
6224 return JSON.stringify(defaultInput, null, HAL.jsonIndent);
6225 },
6226
6227 render: function(opts) {
6228 var input = this.createDefaultInput(this.extractExpressionNames(this.uriTemplate));
6229 this.$el.html(this.template({ href: this.href, input: input }));
6230 this.$('textarea').trigger('keyup');
6231 this.$el.modal(opts);
6232 return this;
6233 },
6234
6235 cleanInput: function(inputObj) {
6236 var obj = {}
6237 for(var k in inputObj) {
6238 if(inputObj.hasOwnProperty(k) && inputObj[k] != null && String(inputObj[k]).trim() != '') {
6239 obj[k] = inputObj[k]
6240 }
6241 }
6242 return obj
6243 },
6244
6245 template: _.template($('#query-uri-template').html())
6246});
6247HAL.Views.Response = Backbone.View.extend({
6248 initialize: function(opts) {
6249 this.vent = opts.vent;
6250
6251 this.headersView = new HAL.Views.ResponseHeaders({ vent: this.vent });
6252 this.bodyView = new HAL.Views.ResponseBody({ vent: this.vent });
6253
6254 _.bindAll(this, 'render');
6255
6256 this.vent.bind('response', this.render);
6257 },
6258
6259 className: 'response',
6260
6261 render: function(e) {
6262 this.$el.html();
6263
6264 this.headersView.render(e);
6265 this.bodyView.render(e);
6266
6267 this.$el.append(this.headersView.el);
6268 this.$el.append(this.bodyView.el);
6269 }
6270});
6271HAL.Views.ResponseHeaders = Backbone.View.extend({
6272 initialize: function(opts) {
6273 this.vent = opts.vent;
6274 },
6275
6276 events: {
6277 'click .follow': 'followLink'
6278 },
6279
6280 className: 'response-headers',
6281
6282 followLink: function(e) {
6283 e.preventDefault();
6284 var $target = $(e.currentTarget);
6285 var uri = $target.attr('href');
6286 window.location.hash = uri;
6287 },
6288
6289 render: function(e) {
6290 this.$el.html(this.template({
6291 status: {
6292 code: e.jqxhr.status,
6293 text: e.jqxhr.statusText
6294 },
6295 headers: HAL.parseHeaders(e.jqxhr.getAllResponseHeaders())
6296 }));
6297 },
6298
6299 template: _.template($('#response-headers-template').html())
6300});
6301HAL.Views.ResponseBody = Backbone.View.extend({
6302 initialize: function(opts) {
6303 this.vent = opts.vent;
6304 },
6305
6306 className: 'response-headers',
6307
6308 render: function(e) {
6309 this.$el.html(this.template({
6310 body: this._bodyAsStringFromEvent(e)
6311 }));
6312 },
6313
6314 template: _.template($('#response-body-template').html()),
6315
6316 _bodyAsStringFromEvent: function(e) {
6317 var output = 'n/a';
6318 if(e.resource !== null) {
6319 output = JSON.stringify(e.resource, null, HAL.jsonIndent);
6320 } else {
6321 // The Ajax request "failed", but there may still be an
6322 // interesting response body (possibly JSON) to show.
6323 var content_type = e.jqxhr.getResponseHeader('content-type');
6324 var responseText = e.jqxhr.responseText;
6325 if(content_type == null || content_type.indexOf('text/') == 0) {
6326 output = responseText;
6327 } else if(content_type.indexOf('json') != -1) {
6328 // Looks like json... try to parse it.
6329 try {
6330 var obj = JSON.parse(responseText);
6331 output = JSON.stringify(obj, null, HAL.jsonIndent);
6332 } catch (err) {
6333 // JSON parse failed. Just show the raw text.
6334 output = responseText;
6335 }
6336 }
6337 }
6338 return output
6339 }
6340});
6341HAL.Views.Documenation = Backbone.View.extend({
6342 className: 'documentation',
6343
6344 render: function(url) {
6345 this.$el.html('<iframe src=' + url + '></iframe>');
6346 }
6347});