├── .gitignore ├── public ├── stylesheets │ └── style.less └── js │ ├── jquery.tmpl.min.js │ ├── underscore-1.1.0.js │ ├── backbone.js │ └── jquery-1.4.4.min.js ├── test └── app.test.js ├── views ├── layout.ejs ├── index.ejs └── examples │ ├── simple-list.ejs │ ├── click-counter.ejs │ └── hello-world.ejs ├── app.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.bak 3 | .DS_Store 4 | *.swo 5 | *.swp 6 | .*.swp 7 | .*.swo 8 | .*.bak 9 | !.gitignore 10 | -------------------------------------------------------------------------------- /public/stylesheets/style.less: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } -------------------------------------------------------------------------------- /test/app.test.js: -------------------------------------------------------------------------------- 1 | 2 | // Run $ expresso 3 | 4 | /** 5 | * Module dependencies. 6 | */ 7 | 8 | var app = require('../app'); 9 | 10 | 11 | module.exports = { 12 | 'GET /': function(assert){ 13 | assert.response(app, 14 | { url: '/' }, 15 | { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8' }}, 16 | function(res){ 17 | assert.includes(res.body, '
This project contains Knockout examples ported to Backbone. My main motivation 2 | is to learn enough about each to determine which framework best suits my 3 | style.
4 | 5 |My initial impression is Knockout is the more elegant framework. However, 6 | everything the author of Backbone has done has been pretty darn good so 7 | there is likely a good reason why Backbone appears more complicated.
8 | 9 |4 | Knockout's Example 5 |
6 | 7 | 14 | 15 | 60 | -------------------------------------------------------------------------------- /views/examples/click-counter.ejs: -------------------------------------------------------------------------------- 1 |4 | Knockout's Example 5 |
6 | 7 |First name:
8 |Last name:
9 |=0;i--)m(j[i]);m(k)}function m(j){var p,i=j,k,e,m;if(m=j.getAttribute(d)){while(i.parentNode&&(i=i.parentNode).nodeType===1&&!(p=i.getAttribute(d)));if(p!==m){i=i.parentNode?i.nodeType===11?0:i.getAttribute(d)||0:0;if(!(e=b[m])){e=f[m];e=g(e,b[i]||f[i],null,true);e.key=++h;b[h]=e}c&&o(m)}j.removeAttribute(d)}else if(c&&(e=a.data(j,"tmplItem"))){o(e.key);b[e.key]=e;i=a.data(j.parentNode,"tmplItem");i=i?i.key:0}if(e){k=e;while(k&&k.key!=i){k.nodes.push(j);k=k.parent}delete e._ctnt;delete e._wrap;a.data(j,"tmplItem",e)}function o(a){a=a+n;e=l[a]=l[a]||g(e,b[e.parent.key+n]||e.parent,null,true)}}}function u(a,d,c,b){if(!a)return l.pop();l.push({_:a,tmpl:d,item:this,data:c,options:b})}function w(d,c,b){return a.tmpl(a.template(d),c,b,this)}function x(b,d){var c=b.options||{};c.wrapped=d;return a.tmpl(a.template(b.tmpl),b.data,c,b.item)}function v(d,c){var b=this._wrap;return a.map(a(a.isArray(b)?b.join(""):b).filter(d||"*"),function(a){return c?a.innerText||a.textContent:a.outerHTML||s(a)})}function t(){var b=this.nodes;a.tmpl(null,null,null,this).insertBefore(b[0]);a(b).remove()}})(jQuery)
--------------------------------------------------------------------------------
/public/js/underscore-1.1.0.js:
--------------------------------------------------------------------------------
1 | // Underscore.js
2 | // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
3 | // Underscore is freely distributable under the terms of the MIT license.
4 | // Portions of Underscore are inspired by or borrowed from Prototype.js,
5 | // Oliver Steele's Functional, and John Resig's Micro-Templating.
6 | // For all details and documentation:
7 | // http://documentcloud.github.com/underscore
8 |
9 | (function() {
10 | // ------------------------- Baseline setup ---------------------------------
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 thrown to break out of a loop iteration.
19 | var breaker = typeof StopIteration !== 'undefined' ? StopIteration : '__break__';
20 |
21 | // Quick regexp-escaping function, because JS doesn't have RegExp.escape().
22 | var escapeRegExp = function(s) { return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); };
23 |
24 | // Save bytes in the minified (but not gzipped) version:
25 | var ArrayProto = Array.prototype, ObjProto = Object.prototype;
26 |
27 | // Create quick reference variables for speed access to core prototypes.
28 | var slice = ArrayProto.slice,
29 | unshift = ArrayProto.unshift,
30 | toString = ObjProto.toString,
31 | hasOwnProperty = ObjProto.hasOwnProperty,
32 | propertyIsEnumerable = ObjProto.propertyIsEnumerable;
33 |
34 | // All ECMA5 native implementations we hope to use are declared here.
35 | var
36 | nativeForEach = ArrayProto.forEach,
37 | nativeMap = ArrayProto.map,
38 | nativeReduce = ArrayProto.reduce,
39 | nativeReduceRight = ArrayProto.reduceRight,
40 | nativeFilter = ArrayProto.filter,
41 | nativeEvery = ArrayProto.every,
42 | nativeSome = ArrayProto.some,
43 | nativeIndexOf = ArrayProto.indexOf,
44 | nativeLastIndexOf = ArrayProto.lastIndexOf,
45 | nativeIsArray = Array.isArray,
46 | nativeKeys = Object.keys;
47 |
48 | // Create a safe reference to the Underscore object for use below.
49 | var _ = function(obj) { return new wrapper(obj); };
50 |
51 | // Export the Underscore object for CommonJS.
52 | if (typeof exports !== 'undefined') exports._ = _;
53 |
54 | // Export underscore to global scope.
55 | root._ = _;
56 |
57 | // Current version.
58 | _.VERSION = '1.1.0';
59 |
60 | // ------------------------ Collection Functions: ---------------------------
61 |
62 | // The cornerstone, an each implementation.
63 | // Handles objects implementing forEach, arrays, and raw objects.
64 | // Delegates to JavaScript 1.6's native forEach if available.
65 | var each = _.forEach = function(obj, iterator, context) {
66 | try {
67 | if (nativeForEach && obj.forEach === nativeForEach) {
68 | obj.forEach(iterator, context);
69 | } else if (_.isNumber(obj.length)) {
70 | for (var i = 0, l = obj.length; i < l; i++) iterator.call(context, obj[i], i, obj);
71 | } else {
72 | for (var key in obj) {
73 | if (hasOwnProperty.call(obj, key)) iterator.call(context, obj[key], key, obj);
74 | }
75 | }
76 | } catch(e) {
77 | if (e != breaker) throw e;
78 | }
79 | return obj;
80 | };
81 |
82 | // Return the results of applying the iterator to each element.
83 | // Delegates to JavaScript 1.6's native map if available.
84 | _.map = function(obj, iterator, context) {
85 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
86 | var results = [];
87 | each(obj, function(value, index, list) {
88 | results.push(iterator.call(context, value, index, list));
89 | });
90 | return results;
91 | };
92 |
93 | // Reduce builds up a single result from a list of values, aka inject, or foldl.
94 | // Delegates to JavaScript 1.8's native reduce if available.
95 | _.reduce = function(obj, iterator, memo, context) {
96 | if (nativeReduce && obj.reduce === nativeReduce) {
97 | if (context) iterator = _.bind(iterator, context);
98 | return obj.reduce(iterator, memo);
99 | }
100 | each(obj, function(value, index, list) {
101 | memo = iterator.call(context, memo, value, index, list);
102 | });
103 | return memo;
104 | };
105 |
106 | // The right-associative version of reduce, also known as foldr. Uses
107 | // Delegates to JavaScript 1.8's native reduceRight if available.
108 | _.reduceRight = function(obj, iterator, memo, context) {
109 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
110 | if (context) iterator = _.bind(iterator, context);
111 | return obj.reduceRight(iterator, memo);
112 | }
113 | var reversed = _.clone(_.toArray(obj)).reverse();
114 | return _.reduce(reversed, iterator, memo, context);
115 | };
116 |
117 | // Return the first value which passes a truth test.
118 | _.detect = function(obj, iterator, context) {
119 | var result;
120 | each(obj, function(value, index, list) {
121 | if (iterator.call(context, value, index, list)) {
122 | result = value;
123 | _.breakLoop();
124 | }
125 | });
126 | return result;
127 | };
128 |
129 | // Return all the elements that pass a truth test.
130 | // Delegates to JavaScript 1.6's native filter if available.
131 | _.filter = function(obj, iterator, context) {
132 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
133 | var results = [];
134 | each(obj, function(value, index, list) {
135 | iterator.call(context, value, index, list) && results.push(value);
136 | });
137 | return results;
138 | };
139 |
140 | // Return all the elements for which a truth test fails.
141 | _.reject = function(obj, iterator, context) {
142 | var results = [];
143 | each(obj, function(value, index, list) {
144 | !iterator.call(context, value, index, list) && results.push(value);
145 | });
146 | return results;
147 | };
148 |
149 | // Determine whether all of the elements match a truth test.
150 | // Delegates to JavaScript 1.6's native every if available.
151 | _.every = function(obj, iterator, context) {
152 | iterator = iterator || _.identity;
153 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
154 | var result = true;
155 | each(obj, function(value, index, list) {
156 | if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
157 | });
158 | return result;
159 | };
160 |
161 | // Determine if at least one element in the object matches a truth test.
162 | // Delegates to JavaScript 1.6's native some if available.
163 | _.some = function(obj, iterator, context) {
164 | iterator = iterator || _.identity;
165 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
166 | var result = false;
167 | each(obj, function(value, index, list) {
168 | if (result = iterator.call(context, value, index, list)) _.breakLoop();
169 | });
170 | return result;
171 | };
172 |
173 | // Determine if a given value is included in the array or object using '==='.
174 | _.include = function(obj, target) {
175 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
176 | var found = false;
177 | each(obj, function(value) {
178 | if (found = value === target) _.breakLoop();
179 | });
180 | return found;
181 | };
182 |
183 | // Invoke a method with arguments on every item in a collection.
184 | _.invoke = function(obj, method) {
185 | var args = _.rest(arguments, 2);
186 | return _.map(obj, function(value) {
187 | return (method ? value[method] : value).apply(value, args);
188 | });
189 | };
190 |
191 | // Convenience version of a common use case of map: fetching a property.
192 | _.pluck = function(obj, key) {
193 | return _.map(obj, function(value){ return value[key]; });
194 | };
195 |
196 | // Return the maximum item or (item-based computation).
197 | _.max = function(obj, iterator, context) {
198 | if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
199 | var result = {computed : -Infinity};
200 | each(obj, function(value, index, list) {
201 | var computed = iterator ? iterator.call(context, value, index, list) : value;
202 | computed >= result.computed && (result = {value : value, computed : computed});
203 | });
204 | return result.value;
205 | };
206 |
207 | // Return the minimum element (or element-based computation).
208 | _.min = function(obj, iterator, context) {
209 | if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
210 | var result = {computed : Infinity};
211 | each(obj, function(value, index, list) {
212 | var computed = iterator ? iterator.call(context, value, index, list) : value;
213 | computed < result.computed && (result = {value : value, computed : computed});
214 | });
215 | return result.value;
216 | };
217 |
218 | // Sort the object's values by a criterion produced by an iterator.
219 | _.sortBy = function(obj, iterator, context) {
220 | return _.pluck(_.map(obj, function(value, index, list) {
221 | return {
222 | value : value,
223 | criteria : iterator.call(context, value, index, list)
224 | };
225 | }).sort(function(left, right) {
226 | var a = left.criteria, b = right.criteria;
227 | return a < b ? -1 : a > b ? 1 : 0;
228 | }), 'value');
229 | };
230 |
231 | // Use a comparator function to figure out at what index an object should
232 | // be inserted so as to maintain order. Uses binary search.
233 | _.sortedIndex = function(array, obj, iterator) {
234 | iterator = iterator || _.identity;
235 | var low = 0, high = array.length;
236 | while (low < high) {
237 | var mid = (low + high) >> 1;
238 | iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
239 | }
240 | return low;
241 | };
242 |
243 | // Convert anything iterable into a real, live array.
244 | _.toArray = function(iterable) {
245 | if (!iterable) return [];
246 | if (iterable.toArray) return iterable.toArray();
247 | if (_.isArray(iterable)) return iterable;
248 | if (_.isArguments(iterable)) return slice.call(iterable);
249 | return _.values(iterable);
250 | };
251 |
252 | // Return the number of elements in an object.
253 | _.size = function(obj) {
254 | return _.toArray(obj).length;
255 | };
256 |
257 | // -------------------------- Array Functions: ------------------------------
258 |
259 | // Get the first element of an array. Passing "n" will return the first N
260 | // values in the array. Aliased as "head". The "guard" check allows it to work
261 | // with _.map.
262 | _.first = function(array, n, guard) {
263 | return n && !guard ? slice.call(array, 0, n) : array[0];
264 | };
265 |
266 | // Returns everything but the first entry of the array. Aliased as "tail".
267 | // Especially useful on the arguments object. Passing an "index" will return
268 | // the rest of the values in the array from that index onward. The "guard"
269 | //check allows it to work with _.map.
270 | _.rest = function(array, index, guard) {
271 | return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
272 | };
273 |
274 | // Get the last element of an array.
275 | _.last = function(array) {
276 | return array[array.length - 1];
277 | };
278 |
279 | // Trim out all falsy values from an array.
280 | _.compact = function(array) {
281 | return _.filter(array, function(value){ return !!value; });
282 | };
283 |
284 | // Return a completely flattened version of an array.
285 | _.flatten = function(array) {
286 | return _.reduce(array, function(memo, value) {
287 | if (_.isArray(value)) return memo.concat(_.flatten(value));
288 | memo.push(value);
289 | return memo;
290 | }, []);
291 | };
292 |
293 | // Return a version of the array that does not contain the specified value(s).
294 | _.without = function(array) {
295 | var values = _.rest(arguments);
296 | return _.filter(array, function(value){ return !_.include(values, value); });
297 | };
298 |
299 | // Produce a duplicate-free version of the array. If the array has already
300 | // been sorted, you have the option of using a faster algorithm.
301 | _.uniq = function(array, isSorted) {
302 | return _.reduce(array, function(memo, el, i) {
303 | if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
304 | return memo;
305 | }, []);
306 | };
307 |
308 | // Produce an array that contains every item shared between all the
309 | // passed-in arrays.
310 | _.intersect = function(array) {
311 | var rest = _.rest(arguments);
312 | return _.filter(_.uniq(array), function(item) {
313 | return _.every(rest, function(other) {
314 | return _.indexOf(other, item) >= 0;
315 | });
316 | });
317 | };
318 |
319 | // Zip together multiple lists into a single array -- elements that share
320 | // an index go together.
321 | _.zip = function() {
322 | var args = _.toArray(arguments);
323 | var length = _.max(_.pluck(args, 'length'));
324 | var results = new Array(length);
325 | for (var i = 0; i < length; i++) results[i] = _.pluck(args, String(i));
326 | return results;
327 | };
328 |
329 | // If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
330 | // we need this function. Return the position of the first occurence of an
331 | // item in an array, or -1 if the item is not included in the array.
332 | // Delegates to JavaScript 1.8's native indexOf if available.
333 | _.indexOf = function(array, item) {
334 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
335 | for (var i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
336 | return -1;
337 | };
338 |
339 |
340 | // Delegates to JavaScript 1.6's native lastIndexOf if available.
341 | _.lastIndexOf = function(array, item) {
342 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
343 | var i = array.length;
344 | while (i--) if (array[i] === item) return i;
345 | return -1;
346 | };
347 |
348 | // Generate an integer Array containing an arithmetic progression. A port of
349 | // the native Python range() function. See:
350 | // http://docs.python.org/library/functions.html#range
351 | _.range = function(start, stop, step) {
352 | var a = _.toArray(arguments);
353 | var solo = a.length <= 1;
354 | var start = solo ? 0 : a[0], stop = solo ? a[0] : a[1], step = a[2] || 1;
355 | var len = Math.ceil((stop - start) / step);
356 | if (len <= 0) return [];
357 | var range = new Array(len);
358 | for (var i = start, idx = 0; true; i += step) {
359 | if ((step > 0 ? i - stop : stop - i) >= 0) return range;
360 | range[idx++] = i;
361 | }
362 | };
363 |
364 | // ----------------------- Function Functions: ------------------------------
365 |
366 | // Create a function bound to a given object (assigning 'this', and arguments,
367 | // optionally). Binding with arguments is also known as 'curry'.
368 | _.bind = function(func, obj) {
369 | var args = _.rest(arguments, 2);
370 | return function() {
371 | return func.apply(obj || {}, args.concat(_.toArray(arguments)));
372 | };
373 | };
374 |
375 | // Bind all of an object's methods to that object. Useful for ensuring that
376 | // all callbacks defined on an object belong to it.
377 | _.bindAll = function(obj) {
378 | var funcs = _.rest(arguments);
379 | if (funcs.length == 0) funcs = _.functions(obj);
380 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
381 | return obj;
382 | };
383 |
384 | // Memoize an expensive function by storing its results.
385 | _.memoize = function(func, hasher) {
386 | var memo = {};
387 | hasher = hasher || _.identity;
388 | return function() {
389 | var key = hasher.apply(this, arguments);
390 | return key in memo ? memo[key] : (memo[key] = func.apply(this, arguments));
391 | };
392 | };
393 |
394 | // Delays a function for the given number of milliseconds, and then calls
395 | // it with the arguments supplied.
396 | _.delay = function(func, wait) {
397 | var args = _.rest(arguments, 2);
398 | return setTimeout(function(){ return func.apply(func, args); }, wait);
399 | };
400 |
401 | // Defers a function, scheduling it to run after the current call stack has
402 | // cleared.
403 | _.defer = function(func) {
404 | return _.delay.apply(_, [func, 1].concat(_.rest(arguments)));
405 | };
406 |
407 | // Returns the first function passed as an argument to the second,
408 | // allowing you to adjust arguments, run code before and after, and
409 | // conditionally execute the original function.
410 | _.wrap = function(func, wrapper) {
411 | return function() {
412 | var args = [func].concat(_.toArray(arguments));
413 | return wrapper.apply(wrapper, args);
414 | };
415 | };
416 |
417 | // Returns a function that is the composition of a list of functions, each
418 | // consuming the return value of the function that follows.
419 | _.compose = function() {
420 | var funcs = _.toArray(arguments);
421 | return function() {
422 | var args = _.toArray(arguments);
423 | for (var i=funcs.length-1; i >= 0; i--) {
424 | args = [funcs[i].apply(this, args)];
425 | }
426 | return args[0];
427 | };
428 | };
429 |
430 | // ------------------------- Object Functions: ------------------------------
431 |
432 | // Retrieve the names of an object's properties.
433 | // Delegates to ECMA5's native Object.keys
434 | _.keys = nativeKeys || function(obj) {
435 | if (_.isArray(obj)) return _.range(0, obj.length);
436 | var keys = [];
437 | for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
438 | return keys;
439 | };
440 |
441 | // Retrieve the values of an object's properties.
442 | _.values = function(obj) {
443 | return _.map(obj, _.identity);
444 | };
445 |
446 | // Return a sorted list of the function names available on the object.
447 | _.functions = function(obj) {
448 | return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
449 | };
450 |
451 | // Extend a given object with all the properties in passed-in object(s).
452 | _.extend = function(obj) {
453 | each(_.rest(arguments), function(source) {
454 | for (var prop in source) obj[prop] = source[prop];
455 | });
456 | return obj;
457 | };
458 |
459 | // Create a (shallow-cloned) duplicate of an object.
460 | _.clone = function(obj) {
461 | if (_.isArray(obj)) return obj.slice(0);
462 | return _.extend({}, obj);
463 | };
464 |
465 | // Invokes interceptor with the obj, and then returns obj.
466 | // The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
467 | _.tap = function(obj, interceptor) {
468 | interceptor(obj);
469 | return obj;
470 | };
471 |
472 | // Perform a deep comparison to check if two objects are equal.
473 | _.isEqual = function(a, b) {
474 | // Check object identity.
475 | if (a === b) return true;
476 | // Different types?
477 | var atype = typeof(a), btype = typeof(b);
478 | if (atype != btype) return false;
479 | // Basic equality test (watch out for coercions).
480 | if (a == b) return true;
481 | // One is falsy and the other truthy.
482 | if ((!a && b) || (a && !b)) return false;
483 | // One of them implements an isEqual()?
484 | if (a.isEqual) return a.isEqual(b);
485 | // Check dates' integer values.
486 | if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
487 | // Both are NaN?
488 | if (_.isNaN(a) && _.isNaN(b)) return false;
489 | // Compare regular expressions.
490 | if (_.isRegExp(a) && _.isRegExp(b))
491 | return a.source === b.source &&
492 | a.global === b.global &&
493 | a.ignoreCase === b.ignoreCase &&
494 | a.multiline === b.multiline;
495 | // If a is not an object by this point, we can't handle it.
496 | if (atype !== 'object') return false;
497 | // Check for different array lengths before comparing contents.
498 | if (a.length && (a.length !== b.length)) return false;
499 | // Nothing else worked, deep compare the contents.
500 | var aKeys = _.keys(a), bKeys = _.keys(b);
501 | // Different object sizes?
502 | if (aKeys.length != bKeys.length) return false;
503 | // Recursive comparison of contents.
504 | for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
505 | return true;
506 | };
507 |
508 | // Is a given array or object empty?
509 | _.isEmpty = function(obj) {
510 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
511 | for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
512 | return true;
513 | };
514 |
515 | // Is a given value a DOM element?
516 | _.isElement = function(obj) {
517 | return !!(obj && obj.nodeType == 1);
518 | };
519 |
520 | // Is a given value an array?
521 | // Delegates to ECMA5's native Array.isArray
522 | _.isArray = nativeIsArray || function(obj) {
523 | return !!(obj && obj.concat && obj.unshift && !obj.callee);
524 | };
525 |
526 | // Is a given variable an arguments object?
527 | _.isArguments = function(obj) {
528 | return obj && obj.callee;
529 | };
530 |
531 | // Is a given value a function?
532 | _.isFunction = function(obj) {
533 | return !!(obj && obj.constructor && obj.call && obj.apply);
534 | };
535 |
536 | // Is a given value a string?
537 | _.isString = function(obj) {
538 | return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
539 | };
540 |
541 | // Is a given value a number?
542 | _.isNumber = function(obj) {
543 | return (obj === +obj) || (toString.call(obj) === '[object Number]');
544 | };
545 |
546 | // Is a given value a boolean?
547 | _.isBoolean = function(obj) {
548 | return obj === true || obj === false;
549 | };
550 |
551 | // Is a given value a date?
552 | _.isDate = function(obj) {
553 | return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
554 | };
555 |
556 | // Is the given value a regular expression?
557 | _.isRegExp = function(obj) {
558 | return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
559 | };
560 |
561 | // Is the given value NaN -- this one is interesting. NaN != NaN, and
562 | // isNaN(undefined) == true, so we make sure it's a number first.
563 | _.isNaN = function(obj) {
564 | return _.isNumber(obj) && isNaN(obj);
565 | };
566 |
567 | // Is a given value equal to null?
568 | _.isNull = function(obj) {
569 | return obj === null;
570 | };
571 |
572 | // Is a given variable undefined?
573 | _.isUndefined = function(obj) {
574 | return typeof obj == 'undefined';
575 | };
576 |
577 | // -------------------------- Utility Functions: ----------------------------
578 |
579 | // Run Underscore.js in noConflict mode, returning the '_' variable to its
580 | // previous owner. Returns a reference to the Underscore object.
581 | _.noConflict = function() {
582 | root._ = previousUnderscore;
583 | return this;
584 | };
585 |
586 | // Keep the identity function around for default iterators.
587 | _.identity = function(value) {
588 | return value;
589 | };
590 |
591 | // Run a function n times.
592 | _.times = function (n, iterator, context) {
593 | for (var i = 0; i < n; i++) iterator.call(context, i);
594 | };
595 |
596 | // Break out of the middle of an iteration.
597 | _.breakLoop = function() {
598 | throw breaker;
599 | };
600 |
601 | // Add your own custom functions to the Underscore object, ensuring that
602 | // they're correctly added to the OOP wrapper as well.
603 | _.mixin = function(obj) {
604 | each(_.functions(obj), function(name){
605 | addToWrapper(name, _[name] = obj[name]);
606 | });
607 | };
608 |
609 | // Generate a unique integer id (unique within the entire client session).
610 | // Useful for temporary DOM ids.
611 | var idCounter = 0;
612 | _.uniqueId = function(prefix) {
613 | var id = idCounter++;
614 | return prefix ? prefix + id : id;
615 | };
616 |
617 | // By default, Underscore uses ERB-style template delimiters, change the
618 | // following template settings to use alternative delimiters.
619 | _.templateSettings = {
620 | start : '<%',
621 | end : '%>',
622 | interpolate : /<%=(.+?)%>/g
623 | };
624 |
625 | // JavaScript templating a-la ERB, pilfered from John Resig's
626 | // "Secrets of the JavaScript Ninja", page 83.
627 | // Single-quote fix from Rick Strahl's version.
628 | // With alterations for arbitrary delimiters, and to preserve whitespace.
629 | _.template = function(str, data) {
630 | var c = _.templateSettings;
631 | var endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g");
632 | var fn = new Function('obj',
633 | 'var p=[],print=function(){p.push.apply(p,arguments);};' +
634 | 'with(obj||{}){p.push(\'' +
635 | str.replace(/\r/g, '\\r')
636 | .replace(/\n/g, '\\n')
637 | .replace(/\t/g, '\\t')
638 | .replace(endMatch,"✄")
639 | .split("'").join("\\'")
640 | .split("✄").join("'")
641 | .replace(c.interpolate, "',$1,'")
642 | .split(c.start).join("');")
643 | .split(c.end).join("p.push('")
644 | + "');}return p.join('');");
645 | return data ? fn(data) : fn;
646 | };
647 |
648 | // ------------------------------- Aliases ----------------------------------
649 |
650 | _.each = _.forEach;
651 | _.foldl = _.inject = _.reduce;
652 | _.foldr = _.reduceRight;
653 | _.select = _.filter;
654 | _.all = _.every;
655 | _.any = _.some;
656 | _.contains = _.include;
657 | _.head = _.first;
658 | _.tail = _.rest;
659 | _.methods = _.functions;
660 |
661 | // ------------------------ Setup the OOP Wrapper: --------------------------
662 |
663 | // If Underscore is called as a function, it returns a wrapped object that
664 | // can be used OO-style. This wrapper holds altered versions of all the
665 | // underscore functions. Wrapped objects may be chained.
666 | var wrapper = function(obj) { this._wrapped = obj; };
667 |
668 | // Helper function to continue chaining intermediate results.
669 | var result = function(obj, chain) {
670 | return chain ? _(obj).chain() : obj;
671 | };
672 |
673 | // A method to easily add functions to the OOP wrapper.
674 | var addToWrapper = function(name, func) {
675 | wrapper.prototype[name] = function() {
676 | var args = _.toArray(arguments);
677 | unshift.call(args, this._wrapped);
678 | return result(func.apply(_, args), this._chain);
679 | };
680 | };
681 |
682 | // Add all of the Underscore functions to the wrapper object.
683 | _.mixin(_);
684 |
685 | // Add all mutator Array functions to the wrapper.
686 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
687 | var method = ArrayProto[name];
688 | wrapper.prototype[name] = function() {
689 | method.apply(this._wrapped, arguments);
690 | return result(this._wrapped, this._chain);
691 | };
692 | });
693 |
694 | // Add all accessor Array functions to the wrapper.
695 | each(['concat', 'join', 'slice'], function(name) {
696 | var method = ArrayProto[name];
697 | wrapper.prototype[name] = function() {
698 | return result(method.apply(this._wrapped, arguments), this._chain);
699 | };
700 | });
701 |
702 | // Start chaining a wrapped Underscore object.
703 | wrapper.prototype.chain = function() {
704 | this._chain = true;
705 | return this;
706 | };
707 |
708 | // Extracts the result from a wrapped and chained object.
709 | wrapper.prototype.value = function() {
710 | return this._wrapped;
711 | };
712 |
713 | })();
714 |
--------------------------------------------------------------------------------
/public/js/backbone.js:
--------------------------------------------------------------------------------
1 | // Backbone.js 0.3.1
2 | // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
3 | // Backbone may be freely distributed under the MIT license.
4 | // For all details and documentation:
5 | // http://documentcloud.github.com/backbone
6 |
7 | (function(){
8 |
9 | // Initial Setup
10 | // -------------
11 |
12 | // The top-level namespace. All public Backbone classes and modules will
13 | // be attached to this. Exported for both CommonJS and the browser.
14 | var Backbone;
15 | if (typeof exports !== 'undefined') {
16 | Backbone = exports;
17 | } else {
18 | Backbone = this.Backbone = {};
19 | }
20 |
21 | // Current version of the library. Keep in sync with `package.json`.
22 | Backbone.VERSION = '0.3.1';
23 |
24 | // Require Underscore, if we're on the server, and it's not already present.
25 | var _ = this._;
26 | if (!_ && (typeof require !== 'undefined')) _ = require("underscore")._;
27 |
28 | // For Backbone's purposes, jQuery owns the `$` variable.
29 | var $ = this.jQuery;
30 |
31 | // Turn on `emulateHTTP` to use support legacy HTTP servers. Setting this option will
32 | // fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and set a
33 | // `X-Http-Method-Override` header.
34 | Backbone.emulateHTTP = false;
35 |
36 | // Turn on `emulateJSON` to support legacy servers that can't deal with direct
37 | // `application/json` requests ... will encode the body as
38 | // `application/x-www-form-urlencoded` instead and will send the model in a
39 | // form param named `model`.
40 | Backbone.emulateJSON = false;
41 |
42 | // Backbone.Events
43 | // -----------------
44 |
45 | // A module that can be mixed in to *any object* in order to provide it with
46 | // custom events. You may `bind` or `unbind` a callback function to an event;
47 | // `trigger`-ing an event fires all callbacks in succession.
48 | //
49 | // var object = {};
50 | // _.extend(object, Backbone.Events);
51 | // object.bind('expand', function(){ alert('expanded'); });
52 | // object.trigger('expand');
53 | //
54 | Backbone.Events = {
55 |
56 | // Bind an event, specified by a string name, `ev`, to a `callback` function.
57 | // Passing `"all"` will bind the callback to all events fired.
58 | bind : function(ev, callback) {
59 | var calls = this._callbacks || (this._callbacks = {});
60 | var list = this._callbacks[ev] || (this._callbacks[ev] = []);
61 | list.push(callback);
62 | return this;
63 | },
64 |
65 | // Remove one or many callbacks. If `callback` is null, removes all
66 | // callbacks for the event. If `ev` is null, removes all bound callbacks
67 | // for all events.
68 | unbind : function(ev, callback) {
69 | var calls;
70 | if (!ev) {
71 | this._callbacks = {};
72 | } else if (calls = this._callbacks) {
73 | if (!callback) {
74 | calls[ev] = [];
75 | } else {
76 | var list = calls[ev];
77 | if (!list) return this;
78 | for (var i = 0, l = list.length; i < l; i++) {
79 | if (callback === list[i]) {
80 | list.splice(i, 1);
81 | break;
82 | }
83 | }
84 | }
85 | }
86 | return this;
87 | },
88 |
89 | // Trigger an event, firing all bound callbacks. Callbacks are passed the
90 | // same arguments as `trigger` is, apart from the event name.
91 | // Listening for `"all"` passes the true event name as the first argument.
92 | trigger : function(ev) {
93 | var list, calls, i, l;
94 | if (!(calls = this._callbacks)) return this;
95 | if (list = calls[ev]) {
96 | for (i = 0, l = list.length; i < l; i++) {
97 | list[i].apply(this, Array.prototype.slice.call(arguments, 1));
98 | }
99 | }
100 | if (list = calls['all']) {
101 | for (i = 0, l = list.length; i < l; i++) {
102 | list[i].apply(this, arguments);
103 | }
104 | }
105 | return this;
106 | }
107 |
108 | };
109 |
110 | // Backbone.Model
111 | // --------------
112 |
113 | // Create a new model, with defined attributes. A client id (`cid`)
114 | // is automatically generated and assigned for you.
115 | Backbone.Model = function(attributes, options) {
116 | this.attributes = {};
117 | this.cid = _.uniqueId('c');
118 | this.set(attributes || {}, {silent : true});
119 | this._previousAttributes = _.clone(this.attributes);
120 | if (options && options.collection) this.collection = options.collection;
121 | this.initialize(attributes, options);
122 | };
123 |
124 | // Attach all inheritable methods to the Model prototype.
125 | _.extend(Backbone.Model.prototype, Backbone.Events, {
126 |
127 | // A snapshot of the model's previous attributes, taken immediately
128 | // after the last `"change"` event was fired.
129 | _previousAttributes : null,
130 |
131 | // Has the item been changed since the last `"change"` event?
132 | _changed : false,
133 |
134 | // Initialize is an empty function by default. Override it with your own
135 | // initialization logic.
136 | initialize : function(){},
137 |
138 | // Return a copy of the model's `attributes` object.
139 | toJSON : function() {
140 | return _.clone(this.attributes);
141 | },
142 |
143 | // Get the value of an attribute.
144 | get : function(attr) {
145 | return this.attributes[attr];
146 | },
147 |
148 | // Set a hash of model attributes on the object, firing `"change"` unless you
149 | // choose to silence it.
150 | set : function(attrs, options) {
151 |
152 | // Extract attributes and options.
153 | options || (options = {});
154 | if (!attrs) return this;
155 | if (attrs.attributes) attrs = attrs.attributes;
156 | var now = this.attributes;
157 |
158 | // Run validation.
159 | if (!options.silent && this.validate && !this._performValidation(attrs, options)) return false;
160 |
161 | // Check for changes of `id`.
162 | if ('id' in attrs) this.id = attrs.id;
163 |
164 | // Update attributes.
165 | for (var attr in attrs) {
166 | var val = attrs[attr];
167 | if (!_.isEqual(now[attr], val)) {
168 | now[attr] = val;
169 | if (!options.silent) {
170 | this._changed = true;
171 | this.trigger('change:' + attr, this, val);
172 | }
173 | }
174 | }
175 |
176 | // Fire the `"change"` event, if the model has been changed.
177 | if (!options.silent && this._changed) this.change();
178 | return this;
179 | },
180 |
181 | // Remove an attribute from the model, firing `"change"` unless you choose
182 | // to silence it.
183 | unset : function(attr, options) {
184 | options || (options = {});
185 | var value = this.attributes[attr];
186 |
187 | // Run validation.
188 | var validObj = {};
189 | validObj[attr] = void 0;
190 | if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
191 |
192 | // Remove the attribute.
193 | delete this.attributes[attr];
194 | if (!options.silent) {
195 | this._changed = true;
196 | this.trigger('change:' + attr, this);
197 | this.change();
198 | }
199 | return this;
200 | },
201 |
202 | // Clear all attributes on the model, firing `"change"` unless you choose
203 | // to silence it.
204 | clear : function(options) {
205 | options || (options = {});
206 | var old = this.attributes;
207 |
208 | // Run validation.
209 | var validObj = {};
210 | for (attr in old) validObj[attr] = void 0;
211 | if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
212 |
213 | this.attributes = {};
214 | if (!options.silent) {
215 | this._changed = true;
216 | for (attr in old) {
217 | this.trigger('change:' + attr, this);
218 | }
219 | this.change();
220 | }
221 | return this;
222 | },
223 |
224 | // Fetch the model from the server. If the server's representation of the
225 | // model differs from its current attributes, they will be overriden,
226 | // triggering a `"change"` event.
227 | fetch : function(options) {
228 | options || (options = {});
229 | var model = this;
230 | var success = function(resp) {
231 | if (!model.set(model.parse(resp), options)) return false;
232 | if (options.success) options.success(model, resp);
233 | };
234 | var error = options.error && _.bind(options.error, null, model);
235 | Backbone.sync('read', this, success, error);
236 | return this;
237 | },
238 |
239 | // Set a hash of model attributes, and sync the model to the server.
240 | // If the server returns an attributes hash that differs, the model's
241 | // state will be `set` again.
242 | save : function(attrs, options) {
243 | attrs || (attrs = {});
244 | options || (options = {});
245 | if (!this.set(attrs, options)) return false;
246 | var model = this;
247 | var success = function(resp) {
248 | if (!model.set(model.parse(resp), options)) return false;
249 | if (options.success) options.success(model, resp);
250 | };
251 | var error = options.error && _.bind(options.error, null, model);
252 | var method = this.isNew() ? 'create' : 'update';
253 | Backbone.sync(method, this, success, error);
254 | return this;
255 | },
256 |
257 | // Destroy this model on the server. Upon success, the model is removed
258 | // from its collection, if it has one.
259 | destroy : function(options) {
260 | options || (options = {});
261 | var model = this;
262 | var success = function(resp) {
263 | if (model.collection) model.collection.remove(model);
264 | if (options.success) options.success(model, resp);
265 | };
266 | var error = options.error && _.bind(options.error, null, model);
267 | Backbone.sync('delete', this, success, error);
268 | return this;
269 | },
270 |
271 | // Default URL for the model's representation on the server -- if you're
272 | // using Backbone's restful methods, override this to change the endpoint
273 | // that will be called.
274 | url : function() {
275 | var base = getUrl(this.collection);
276 | if (this.isNew()) return base;
277 | return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
278 | },
279 |
280 | // **parse** converts a response into the hash of attributes to be `set` on
281 | // the model. The default implementation is just to pass the response along.
282 | parse : function(resp) {
283 | return resp;
284 | },
285 |
286 | // Create a new model with identical attributes to this one.
287 | clone : function() {
288 | return new this.constructor(this);
289 | },
290 |
291 | // A model is new if it has never been saved to the server, and has a negative
292 | // ID.
293 | isNew : function() {
294 | return !this.id;
295 | },
296 |
297 | // Call this method to manually fire a `change` event for this model.
298 | // Calling this will cause all objects observing the model to update.
299 | change : function() {
300 | this.trigger('change', this);
301 | this._previousAttributes = _.clone(this.attributes);
302 | this._changed = false;
303 | },
304 |
305 | // Determine if the model has changed since the last `"change"` event.
306 | // If you specify an attribute name, determine if that attribute has changed.
307 | hasChanged : function(attr) {
308 | if (attr) return this._previousAttributes[attr] != this.attributes[attr];
309 | return this._changed;
310 | },
311 |
312 | // Return an object containing all the attributes that have changed, or false
313 | // if there are no changed attributes. Useful for determining what parts of a
314 | // view need to be updated and/or what attributes need to be persisted to
315 | // the server.
316 | changedAttributes : function(now) {
317 | now || (now = this.attributes);
318 | var old = this._previousAttributes;
319 | var changed = false;
320 | for (var attr in now) {
321 | if (!_.isEqual(old[attr], now[attr])) {
322 | changed = changed || {};
323 | changed[attr] = now[attr];
324 | }
325 | }
326 | return changed;
327 | },
328 |
329 | // Get the previous value of an attribute, recorded at the time the last
330 | // `"change"` event was fired.
331 | previous : function(attr) {
332 | if (!attr || !this._previousAttributes) return null;
333 | return this._previousAttributes[attr];
334 | },
335 |
336 | // Get all of the attributes of the model at the time of the previous
337 | // `"change"` event.
338 | previousAttributes : function() {
339 | return _.clone(this._previousAttributes);
340 | },
341 |
342 | // Run validation against a set of incoming attributes, returning `true`
343 | // if all is well. If a specific `error` callback has been passed,
344 | // call that instead of firing the general `"error"` event.
345 | _performValidation : function(attrs, options) {
346 | var error = this.validate(attrs);
347 | if (error) {
348 | if (options.error) {
349 | options.error(this, error);
350 | } else {
351 | this.trigger('error', this, error);
352 | }
353 | return false;
354 | }
355 | return true;
356 | }
357 |
358 | });
359 |
360 | // Backbone.Collection
361 | // -------------------
362 |
363 | // Provides a standard collection class for our sets of models, ordered
364 | // or unordered. If a `comparator` is specified, the Collection will maintain
365 | // its models in sort order, as they're added and removed.
366 | Backbone.Collection = function(models, options) {
367 | options || (options = {});
368 | if (options.comparator) {
369 | this.comparator = options.comparator;
370 | delete options.comparator;
371 | }
372 | this._boundOnModelEvent = _.bind(this._onModelEvent, this);
373 | this._reset();
374 | if (models) this.refresh(models, {silent: true});
375 | this.initialize(models, options);
376 | };
377 |
378 | // Define the Collection's inheritable methods.
379 | _.extend(Backbone.Collection.prototype, Backbone.Events, {
380 |
381 | // The default model for a collection is just a **Backbone.Model**.
382 | // This should be overridden in most cases.
383 | model : Backbone.Model,
384 |
385 | // Initialize is an empty function by default. Override it with your own
386 | // initialization logic.
387 | initialize : function(){},
388 |
389 | // The JSON representation of a Collection is an array of the
390 | // models' attributes.
391 | toJSON : function() {
392 | return this.map(function(model){ return model.toJSON(); });
393 | },
394 |
395 | // Add a model, or list of models to the set. Pass **silent** to avoid
396 | // firing the `added` event for every new model.
397 | add : function(models, options) {
398 | if (_.isArray(models)) {
399 | for (var i = 0, l = models.length; i < l; i++) {
400 | this._add(models[i], options);
401 | }
402 | } else {
403 | this._add(models, options);
404 | }
405 | return this;
406 | },
407 |
408 | // Remove a model, or a list of models from the set. Pass silent to avoid
409 | // firing the `removed` event for every model removed.
410 | remove : function(models, options) {
411 | if (_.isArray(models)) {
412 | for (var i = 0, l = models.length; i < l; i++) {
413 | this._remove(models[i], options);
414 | }
415 | } else {
416 | this._remove(models, options);
417 | }
418 | return this;
419 | },
420 |
421 | // Get a model from the set by id.
422 | get : function(id) {
423 | if (id == null) return null;
424 | return this._byId[id.id != null ? id.id : id];
425 | },
426 |
427 | // Get a model from the set by client id.
428 | getByCid : function(cid) {
429 | return cid && this._byCid[cid.cid || cid];
430 | },
431 |
432 | // Get the model at the given index.
433 | at: function(index) {
434 | return this.models[index];
435 | },
436 |
437 | // Force the collection to re-sort itself. You don't need to call this under normal
438 | // circumstances, as the set will maintain sort order as each item is added.
439 | sort : function(options) {
440 | options || (options = {});
441 | if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
442 | this.models = this.sortBy(this.comparator);
443 | if (!options.silent) this.trigger('refresh', this);
444 | return this;
445 | },
446 |
447 | // Pluck an attribute from each model in the collection.
448 | pluck : function(attr) {
449 | return _.map(this.models, function(model){ return model.get(attr); });
450 | },
451 |
452 | // When you have more items than you want to add or remove individually,
453 | // you can refresh the entire set with a new list of models, without firing
454 | // any `added` or `removed` events. Fires `refresh` when finished.
455 | refresh : function(models, options) {
456 | models || (models = []);
457 | options || (options = {});
458 | this._reset();
459 | this.add(models, {silent: true});
460 | if (!options.silent) this.trigger('refresh', this);
461 | return this;
462 | },
463 |
464 | // Fetch the default set of models for this collection, refreshing the
465 | // collection when they arrive.
466 | fetch : function(options) {
467 | options || (options = {});
468 | var collection = this;
469 | var success = function(resp) {
470 | collection.refresh(collection.parse(resp));
471 | if (options.success) options.success(collection, resp);
472 | };
473 | var error = options.error && _.bind(options.error, null, collection);
474 | Backbone.sync('read', this, success, error);
475 | return this;
476 | },
477 |
478 | // Create a new instance of a model in this collection. After the model
479 | // has been created on the server, it will be added to the collection.
480 | create : function(model, options) {
481 | var coll = this;
482 | options || (options = {});
483 | if (!(model instanceof Backbone.Model)) {
484 | model = new this.model(model, {collection: coll});
485 | } else {
486 | model.collection = coll;
487 | }
488 | var success = function(nextModel, resp) {
489 | coll.add(nextModel);
490 | if (options.success) options.success(nextModel, resp);
491 | };
492 | return model.save(null, {success : success, error : options.error});
493 | },
494 |
495 | // **parse** converts a response into a list of models to be added to the
496 | // collection. The default implementation is just to pass it through.
497 | parse : function(resp) {
498 | return resp;
499 | },
500 |
501 | // Proxy to _'s chain. Can't be proxied the same way the rest of the
502 | // underscore methods are proxied because it relies on the underscore
503 | // constructor.
504 | chain: function () {
505 | return _(this.models).chain();
506 | },
507 |
508 | // Reset all internal state. Called when the collection is refreshed.
509 | _reset : function(options) {
510 | this.length = 0;
511 | this.models = [];
512 | this._byId = {};
513 | this._byCid = {};
514 | },
515 |
516 | // Internal implementation of adding a single model to the set, updating
517 | // hash indexes for `id` and `cid` lookups.
518 | _add : function(model, options) {
519 | options || (options = {});
520 | if (!(model instanceof Backbone.Model)) {
521 | model = new this.model(model, {collection: this});
522 | }
523 | var already = this.getByCid(model);
524 | if (already) throw new Error(["Can't add the same model to a set twice", already.id]);
525 | this._byId[model.id] = model;
526 | this._byCid[model.cid] = model;
527 | model.collection = this;
528 | var index = this.comparator ? this.sortedIndex(model, this.comparator) : this.length;
529 | this.models.splice(index, 0, model);
530 | model.bind('all', this._boundOnModelEvent);
531 | this.length++;
532 | if (!options.silent) model.trigger('add', model, this);
533 | return model;
534 | },
535 |
536 | // Internal implementation of removing a single model from the set, updating
537 | // hash indexes for `id` and `cid` lookups.
538 | _remove : function(model, options) {
539 | options || (options = {});
540 | model = this.getByCid(model) || this.get(model);
541 | if (!model) return null;
542 | delete this._byId[model.id];
543 | delete this._byCid[model.cid];
544 | delete model.collection;
545 | this.models.splice(this.indexOf(model), 1);
546 | this.length--;
547 | if (!options.silent) model.trigger('remove', model, this);
548 | model.unbind('all', this._boundOnModelEvent);
549 | return model;
550 | },
551 |
552 | // Internal method called every time a model in the set fires an event.
553 | // Sets need to update their indexes when models change ids. All other
554 | // events simply proxy through.
555 | _onModelEvent : function(ev, model) {
556 | if (ev === 'change:id') {
557 | delete this._byId[model.previous('id')];
558 | this._byId[model.id] = model;
559 | }
560 | this.trigger.apply(this, arguments);
561 | }
562 |
563 | });
564 |
565 | // Underscore methods that we want to implement on the Collection.
566 | var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 'detect',
567 | 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
568 | 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size',
569 | 'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty'];
570 |
571 | // Mix in each Underscore method as a proxy to `Collection#models`.
572 | _.each(methods, function(method) {
573 | Backbone.Collection.prototype[method] = function() {
574 | return _[method].apply(_, [this.models].concat(_.toArray(arguments)));
575 | };
576 | });
577 |
578 | // Backbone.Controller
579 | // -------------------
580 |
581 | // Controllers map faux-URLs to actions, and fire events when routes are
582 | // matched. Creating a new one sets its `routes` hash, if not set statically.
583 | Backbone.Controller = function(options) {
584 | options || (options = {});
585 | if (options.routes) this.routes = options.routes;
586 | this._bindRoutes();
587 | this.initialize(options);
588 | };
589 |
590 | // Cached regular expressions for matching named param parts and splatted
591 | // parts of route strings.
592 | var namedParam = /:([\w\d]+)/g;
593 | var splatParam = /\*([\w\d]+)/g;
594 |
595 | // Set up all inheritable **Backbone.Controller** properties and methods.
596 | _.extend(Backbone.Controller.prototype, Backbone.Events, {
597 |
598 | // Initialize is an empty function by default. Override it with your own
599 | // initialization logic.
600 | initialize : function(){},
601 |
602 | // Manually bind a single named route to a callback. For example:
603 | //
604 | // this.route('search/:query/p:num', 'search', function(query, num) {
605 | // ...
606 | // });
607 | //
608 | route : function(route, name, callback) {
609 | Backbone.history || (Backbone.history = new Backbone.History);
610 | if (!_.isRegExp(route)) route = this._routeToRegExp(route);
611 | Backbone.history.route(route, _.bind(function(fragment) {
612 | var args = this._extractParameters(route, fragment);
613 | callback.apply(this, args);
614 | this.trigger.apply(this, ['route:' + name].concat(args));
615 | }, this));
616 | },
617 |
618 | // Simple proxy to `Backbone.history` to save a fragment into the history,
619 | // without triggering routes.
620 | saveLocation : function(fragment) {
621 | Backbone.history.saveLocation(fragment);
622 | },
623 |
624 | // Bind all defined routes to `Backbone.history`.
625 | _bindRoutes : function() {
626 | if (!this.routes) return;
627 | for (var route in this.routes) {
628 | var name = this.routes[route];
629 | this.route(route, name, this[name]);
630 | }
631 | },
632 |
633 | // Convert a route string into a regular expression, suitable for matching
634 | // against the current location fragment.
635 | _routeToRegExp : function(route) {
636 | route = route.replace(namedParam, "([^\/]*)").replace(splatParam, "(.*?)");
637 | return new RegExp('^' + route + '$');
638 | },
639 |
640 | // Given a route, and a URL fragment that it matches, return the array of
641 | // extracted parameters.
642 | _extractParameters : function(route, fragment) {
643 | return route.exec(fragment).slice(1);
644 | }
645 |
646 | });
647 |
648 | // Backbone.History
649 | // ----------------
650 |
651 | // Handles cross-browser history management, based on URL hashes. If the
652 | // browser does not support `onhashchange`, falls back to polling.
653 | Backbone.History = function() {
654 | this.handlers = [];
655 | this.fragment = this.getFragment();
656 | _.bindAll(this, 'checkUrl');
657 | };
658 |
659 | // Cached regex for cleaning hashes.
660 | var hashStrip = /^#*/;
661 |
662 | // Set up all inheritable **Backbone.History** properties and methods.
663 | _.extend(Backbone.History.prototype, {
664 |
665 | // The default interval to poll for hash changes, if necessary, is
666 | // twenty times a second.
667 | interval: 50,
668 |
669 | // Get the cross-browser normalized URL fragment.
670 | getFragment : function(loc) {
671 | return (loc || window.location).hash.replace(hashStrip, '');
672 | },
673 |
674 | // Start the hash change handling, returning `true` if the current URL matches
675 | // an existing route, and `false` otherwise.
676 | start : function() {
677 | var docMode = document.documentMode;
678 | var oldIE = ($.browser.msie && docMode < 7);
679 | if (oldIE) {
680 | this.iframe = $('').hide().appendTo('body')[0].contentWindow;
681 | }
682 | if ('onhashchange' in window && !oldIE) {
683 | $(window).bind('hashchange', this.checkUrl);
684 | } else {
685 | setInterval(this.checkUrl, this.interval);
686 | }
687 | return this.loadUrl();
688 | },
689 |
690 | // Add a route to be tested when the hash changes. Routes are matched in the
691 | // order they are added.
692 | route : function(route, callback) {
693 | this.handlers.push({route : route, callback : callback});
694 | },
695 |
696 | // Checks the current URL to see if it has changed, and if it has,
697 | // calls `loadUrl`, normalizing across the hidden iframe.
698 | checkUrl : function() {
699 | var current = this.getFragment();
700 | if (current == this.fragment && this.iframe) {
701 | current = this.getFragment(this.iframe.location);
702 | }
703 | if (current == this.fragment ||
704 | current == decodeURIComponent(this.fragment)) return false;
705 | if (this.iframe) {
706 | window.location.hash = this.iframe.location.hash = current;
707 | }
708 | this.loadUrl();
709 | },
710 |
711 | // Attempt to load the current URL fragment. If a route succeeds with a
712 | // match, returns `true`. If no defined routes matches the fragment,
713 | // returns `false`.
714 | loadUrl : function() {
715 | var fragment = this.fragment = this.getFragment();
716 | var matched = _.any(this.handlers, function(handler) {
717 | if (handler.route.test(fragment)) {
718 | handler.callback(fragment);
719 | return true;
720 | }
721 | });
722 | return matched;
723 | },
724 |
725 | // Save a fragment into the hash history. You are responsible for properly
726 | // URL-encoding the fragment in advance. This does not trigger
727 | // a `hashchange` event.
728 | saveLocation : function(fragment) {
729 | fragment = (fragment || '').replace(hashStrip, '');
730 | if (this.fragment == fragment) return;
731 | window.location.hash = this.fragment = fragment;
732 | if (this.iframe && (fragment != this.getFragment(this.iframe.location))) {
733 | this.iframe.document.open().close();
734 | this.iframe.location.hash = fragment;
735 | }
736 | }
737 |
738 | });
739 |
740 | // Backbone.View
741 | // -------------
742 |
743 | // Creating a Backbone.View creates its initial element outside of the DOM,
744 | // if an existing element is not provided...
745 | Backbone.View = function(options) {
746 | this._configure(options || {});
747 | this._ensureElement();
748 | this.delegateEvents();
749 | this.initialize(options);
750 | };
751 |
752 | // jQuery lookup, scoped to DOM elements within the current view.
753 | // This should be prefered to global jQuery lookups, if you're dealing with
754 | // a specific view.
755 | var jQueryDelegate = function(selector) {
756 | return $(selector, this.el);
757 | };
758 |
759 | // Cached regex to split keys for `delegate`.
760 | var eventSplitter = /^(\w+)\s*(.*)$/;
761 |
762 | // Set up all inheritable **Backbone.View** properties and methods.
763 | _.extend(Backbone.View.prototype, Backbone.Events, {
764 |
765 | // The default `tagName` of a View's element is `"div"`.
766 | tagName : 'div',
767 |
768 | // Attach the jQuery function as the `$` and `jQuery` properties.
769 | $ : jQueryDelegate,
770 | jQuery : jQueryDelegate,
771 |
772 | // Initialize is an empty function by default. Override it with your own
773 | // initialization logic.
774 | initialize : function(){},
775 |
776 | // **render** is the core function that your view should override, in order
777 | // to populate its element (`this.el`), with the appropriate HTML. The
778 | // convention is for **render** to always return `this`.
779 | render : function() {
780 | return this;
781 | },
782 |
783 | // Remove this view from the DOM. Note that the view isn't present in the
784 | // DOM by default, so calling this method may be a no-op.
785 | remove : function() {
786 | $(this.el).remove();
787 | return this;
788 | },
789 |
790 | // For small amounts of DOM Elements, where a full-blown template isn't
791 | // needed, use **make** to manufacture elements, one at a time.
792 | //
793 | // var el = this.make('li', {'class': 'row'}, this.model.get('title'));
794 | //
795 | make : function(tagName, attributes, content) {
796 | var el = document.createElement(tagName);
797 | if (attributes) $(el).attr(attributes);
798 | if (content) $(el).html(content);
799 | return el;
800 | },
801 |
802 | // Set callbacks, where `this.callbacks` is a hash of
803 | //
804 | // *{"event selector": "callback"}*
805 | //
806 | // {
807 | // 'mousedown .title': 'edit',
808 | // 'click .button': 'save'
809 | // }
810 | //
811 | // pairs. Callbacks will be bound to the view, with `this` set properly.
812 | // Uses jQuery event delegation for efficiency.
813 | // Omitting the selector binds the event to `this.el`.
814 | // This only works for delegate-able events: not `focus`, `blur`, and
815 | // not `change`, `submit`, and `reset` in Internet Explorer.
816 | delegateEvents : function(events) {
817 | if (!(events || (events = this.events))) return;
818 | $(this.el).unbind();
819 | for (var key in events) {
820 | var methodName = events[key];
821 | var match = key.match(eventSplitter);
822 | var eventName = match[1], selector = match[2];
823 | var method = _.bind(this[methodName], this);
824 | if (selector === '') {
825 | $(this.el).bind(eventName, method);
826 | } else {
827 | $(this.el).delegate(selector, eventName, method);
828 | }
829 | }
830 | },
831 |
832 | // Performs the initial configuration of a View with a set of options.
833 | // Keys with special meaning *(model, collection, id, className)*, are
834 | // attached directly to the view.
835 | _configure : function(options) {
836 | if (this.options) options = _.extend({}, this.options, options);
837 | if (options.model) this.model = options.model;
838 | if (options.collection) this.collection = options.collection;
839 | if (options.el) this.el = options.el;
840 | if (options.id) this.id = options.id;
841 | if (options.className) this.className = options.className;
842 | if (options.tagName) this.tagName = options.tagName;
843 | this.options = options;
844 | },
845 |
846 | // Ensure that the View has a DOM element to render into.
847 | _ensureElement : function() {
848 | if (this.el) return;
849 | var attrs = {};
850 | if (this.id) attrs.id = this.id;
851 | if (this.className) attrs.className = this.className;
852 | this.el = this.make(this.tagName, attrs);
853 | }
854 |
855 | });
856 |
857 | // The self-propagating extend function that Backbone classes use.
858 | var extend = function (protoProps, classProps) {
859 | var child = inherits(this, protoProps, classProps);
860 | child.extend = extend;
861 | return child;
862 | };
863 |
864 | // Set up inheritance for the model, collection, and view.
865 | Backbone.Model.extend = Backbone.Collection.extend =
866 | Backbone.Controller.extend = Backbone.View.extend = extend;
867 |
868 | // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
869 | var methodMap = {
870 | 'create': 'POST',
871 | 'update': 'PUT',
872 | 'delete': 'DELETE',
873 | 'read' : 'GET'
874 | };
875 |
876 | // Backbone.sync
877 | // -------------
878 |
879 | // Override this function to change the manner in which Backbone persists
880 | // models to the server. You will be passed the type of request, and the
881 | // model in question. By default, uses jQuery to make a RESTful Ajax request
882 | // to the model's `url()`. Some possible customizations could be:
883 | //
884 | // * Use `setTimeout` to batch rapid-fire updates into a single request.
885 | // * Send up the models as XML instead of JSON.
886 | // * Persist models via WebSockets instead of Ajax.
887 | //
888 | // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
889 | // as `POST`, with a `_method` parameter containing the true HTTP method,
890 | // as well as all requests with the body as `application/x-www-form-urlencoded` instead of
891 | // `application/json` with the model in a param named `model`.
892 | // Useful when interfacing with server-side languages like **PHP** that make
893 | // it difficult to read the body of `PUT` requests.
894 | Backbone.sync = function(method, model, success, error) {
895 | var type = methodMap[method];
896 | var modelJSON = (method === 'create' || method === 'update') ?
897 | JSON.stringify(model.toJSON()) : null;
898 |
899 | // Default JSON-request options.
900 | var params = {
901 | url: getUrl(model),
902 | type: type,
903 | contentType: 'application/json',
904 | data: modelJSON,
905 | dataType: 'json',
906 | processData: false,
907 | success: success,
908 | error: error
909 | };
910 |
911 | // For older servers, emulate JSON by encoding the request into an HTML-form.
912 | if (Backbone.emulateJSON) {
913 | params.contentType = 'application/x-www-form-urlencoded';
914 | params.processData = true;
915 | params.data = modelJSON ? {model : modelJSON} : {};
916 | }
917 |
918 | // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
919 | // And an `X-HTTP-Method-Override` header.
920 | if (Backbone.emulateHTTP) {
921 | if (type === 'PUT' || type === 'DELETE') {
922 | if (Backbone.emulateJSON) params.data._method = type;
923 | params.type = 'POST';
924 | params.beforeSend = function(xhr) {
925 | xhr.setRequestHeader("X-HTTP-Method-Override", type);
926 | };
927 | }
928 | }
929 |
930 | // Make the request.
931 | $.ajax(params);
932 | };
933 |
934 | // Helpers
935 | // -------
936 |
937 | // Shared empty constructor function to aid in prototype-chain creation.
938 | var ctor = function(){};
939 |
940 | // Helper function to correctly set up the prototype chain, for subclasses.
941 | // Similar to `goog.inherits`, but uses a hash of prototype properties and
942 | // class properties to be extended.
943 | var inherits = function(parent, protoProps, staticProps) {
944 | var child;
945 |
946 | // The constructor function for the new subclass is either defined by you
947 | // (the "constructor" property in your `extend` definition), or defaulted
948 | // by us to simply call `super()`.
949 | if (protoProps && protoProps.hasOwnProperty('constructor')) {
950 | child = protoProps.constructor;
951 | } else {
952 | child = function(){ return parent.apply(this, arguments); };
953 | }
954 |
955 | // Set the prototype chain to inherit from `parent`, without calling
956 | // `parent`'s constructor function.
957 | ctor.prototype = parent.prototype;
958 | child.prototype = new ctor();
959 |
960 | // Add prototype properties (instance properties) to the subclass,
961 | // if supplied.
962 | if (protoProps) _.extend(child.prototype, protoProps);
963 |
964 | // Add static properties to the constructor function, if supplied.
965 | if (staticProps) _.extend(child, staticProps);
966 |
967 | // Correctly set child's `prototype.constructor`, for `instanceof`.
968 | child.prototype.constructor = child;
969 |
970 | // Set a convenience property in case the parent's prototype is needed later.
971 | child.__super__ = parent.prototype;
972 |
973 | return child;
974 | };
975 |
976 | // Helper function to get a URL from a Model or Collection as a property
977 | // or as a function.
978 | var getUrl = function(object) {
979 | if (!(object && object.url)) throw new Error("A 'url' property or function must be specified");
980 | return _.isFunction(object.url) ? object.url() : object.url;
981 | };
982 |
983 | })();
984 |
--------------------------------------------------------------------------------
/public/js/jquery-1.4.4.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery JavaScript Library v1.4.4
3 | * http://jquery.com/
4 | *
5 | * Copyright 2010, John Resig
6 | * Dual licensed under the MIT or GPL Version 2 licenses.
7 | * http://jquery.org/license
8 | *
9 | * Includes Sizzle.js
10 | * http://sizzlejs.com/
11 | * Copyright 2010, The Dojo Foundation
12 | * Released under the MIT, BSD, and GPL Licenses.
13 | *
14 | * Date: Thu Nov 11 19:04:53 2010 -0500
15 | */
16 | (function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h=
17 | h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;k0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload",
30 | b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&&
31 | !F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&&
32 | l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H
a";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"),
38 | k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false,
39 | scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent=
40 | false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom=
41 | 1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="
";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display=
42 | "none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h=
43 | c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando);
44 | else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;ht ":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p
=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n,
89 | m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===
90 | true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===
91 | g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return i
","
"],tr:[2,"","
"],td:[3,"
"],col:[2,"","
"],area:[1,""],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div"&&!x?r.childNodes:[];for(o=k.length-
123 | 1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script"))));
124 | d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i,
125 | jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true,
126 | zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b),
127 | h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b);
128 | if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f=
129 | d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left;
130 | e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/