PageRenderTime 81ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/example/public/application.js

http://github.com/gobhi/gbone.js
JavaScript | 4892 lines | 4106 code | 272 blank | 514 comment | 572 complexity | d759c46b510d0bc07d5e36681b40bba3 MD5 | raw file
  1. // Zepto.js
  2. // (c) 2010, 2011 Thomas Fuchs
  3. // Zepto.js may be freely distributed under the MIT license.
  4. (function(undefined){
  5. if (String.prototype.trim === undefined) // fix for iOS 3.2
  6. String.prototype.trim = function(){ return this.replace(/^\s+/, '').replace(/\s+$/, '') };
  7. // For iOS 3.x
  8. // from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
  9. if (Array.prototype.reduce === undefined)
  10. Array.prototype.reduce = function(fun){
  11. if(this === void 0 || this === null) throw new TypeError();
  12. var t = Object(this), len = t.length >>> 0, k = 0, accumulator;
  13. if(typeof fun != 'function') throw new TypeError();
  14. if(len == 0 && arguments.length == 1) throw new TypeError();
  15. if(arguments.length >= 2)
  16. accumulator = arguments[1];
  17. else
  18. do{
  19. if(k in t){
  20. accumulator = t[k++];
  21. break;
  22. }
  23. if(++k >= len) throw new TypeError();
  24. } while (true);
  25. while (k < len){
  26. if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t);
  27. k++;
  28. }
  29. return accumulator;
  30. };
  31. })();
  32. // Zepto.js
  33. // (c) 2010, 2011 Thomas Fuchs
  34. // Zepto.js may be freely distributed under the MIT license.
  35. var Zepto = (function() {
  36. var undefined, key, $$, classList, emptyArray = [], slice = emptyArray.slice,
  37. document = window.document,
  38. elementDisplay = {}, classCache = {},
  39. getComputedStyle = document.defaultView.getComputedStyle,
  40. cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 },
  41. fragmentRE = /^\s*<(\w+)[^>]*>/,
  42. elementTypes = [1, 9, 11],
  43. adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ],
  44. table = document.createElement('table'),
  45. tableRow = document.createElement('tr'),
  46. containers = {
  47. 'tr': document.createElement('tbody'),
  48. 'tbody': table, 'thead': table, 'tfoot': table,
  49. 'td': tableRow, 'th': tableRow,
  50. '*': document.createElement('div')
  51. },
  52. readyRE = /complete|loaded|interactive/,
  53. classSelectorRE = /^\.([\w-]+)$/,
  54. idSelectorRE = /^#([\w-]+)$/,
  55. tagSelectorRE = /^[\w-]+$/;
  56. function isF(value) { return ({}).toString.call(value) == "[object Function]" }
  57. function isO(value) { return value instanceof Object }
  58. function isA(value) { return value instanceof Array }
  59. function likeArray(obj) { return typeof obj.length == 'number' }
  60. function compact(array) { return array.filter(function(item){ return item !== undefined && item !== null }) }
  61. function flatten(array) { return array.length > 0 ? [].concat.apply([], array) : array }
  62. function camelize(str) { return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) }
  63. function dasherize(str){
  64. return str.replace(/::/g, '/')
  65. .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
  66. .replace(/([a-z\d])([A-Z])/g, '$1_$2')
  67. .replace(/_/g, '-')
  68. .toLowerCase();
  69. }
  70. function uniq(array) { return array.filter(function(item,index,array){ return array.indexOf(item) == index }) }
  71. function classRE(name){
  72. return name in classCache ?
  73. classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'));
  74. }
  75. function maybeAddPx(name, value) { return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value; }
  76. function defaultDisplay(nodeName) {
  77. var element, display;
  78. if (!elementDisplay[nodeName]) {
  79. element = document.createElement(nodeName);
  80. document.body.appendChild(element);
  81. display = getComputedStyle(element, '').getPropertyValue("display");
  82. element.parentNode.removeChild(element);
  83. display == "none" && (display = "block");
  84. elementDisplay[nodeName] = display;
  85. }
  86. return elementDisplay[nodeName];
  87. }
  88. function fragment(html, name) {
  89. if (name === undefined) fragmentRE.test(html) && RegExp.$1;
  90. if (!(name in containers)) name = '*';
  91. var container = containers[name];
  92. container.innerHTML = '' + html;
  93. return slice.call(container.childNodes);
  94. }
  95. function Z(dom, selector){
  96. dom = dom || emptyArray;
  97. dom.__proto__ = Z.prototype;
  98. dom.selector = selector || '';
  99. return dom;
  100. }
  101. function $(selector, context){
  102. if (!selector) return Z();
  103. if (context !== undefined) return $(context).find(selector);
  104. else if (isF(selector)) return $(document).ready(selector);
  105. else if (selector instanceof Z) return selector;
  106. else {
  107. var dom;
  108. if (isA(selector)) dom = compact(selector);
  109. else if (elementTypes.indexOf(selector.nodeType) >= 0 || selector === window)
  110. dom = [selector], selector = null;
  111. else if (fragmentRE.test(selector))
  112. dom = fragment(selector.trim(), RegExp.$1), selector = null;
  113. else if (selector.nodeType && selector.nodeType == 3) dom = [selector];
  114. else dom = $$(document, selector);
  115. return Z(dom, selector);
  116. }
  117. }
  118. $.extend = function(target){
  119. slice.call(arguments, 1).forEach(function(source) {
  120. for (key in source) target[key] = source[key];
  121. })
  122. return target;
  123. }
  124. $.qsa = $$ = function(element, selector){
  125. var found;
  126. return (element === document && idSelectorRE.test(selector)) ?
  127. ( (found = element.getElementById(RegExp.$1)) ? [found] : emptyArray ) :
  128. slice.call(
  129. classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) :
  130. tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) :
  131. element.querySelectorAll(selector)
  132. );
  133. }
  134. function filtered(nodes, selector){
  135. return selector === undefined ? $(nodes) : $(nodes).filter(selector);
  136. }
  137. function funcArg(context, arg, idx, payload){
  138. return isF(arg) ? arg.call(context, idx, payload) : arg;
  139. }
  140. $.isFunction = isF;
  141. $.isObject = isO;
  142. $.isArray = isA;
  143. $.map = function(elements, callback) {
  144. var value, values = [], i, key;
  145. if (likeArray(elements))
  146. for (i = 0; i < elements.length; i++) {
  147. value = callback(elements[i], i);
  148. if (value != null) values.push(value);
  149. }
  150. else
  151. for (key in elements) {
  152. value = callback(elements[key], key);
  153. if (value != null) values.push(value);
  154. }
  155. return flatten(values);
  156. }
  157. $.each = function(elements, callback) {
  158. var i, key;
  159. if (likeArray(elements))
  160. for(i = 0; i < elements.length; i++) {
  161. if(callback(i, elements[i]) === false) return elements;
  162. }
  163. else
  164. for(key in elements) {
  165. if(callback(key, elements[key]) === false) return elements;
  166. }
  167. return elements;
  168. }
  169. $.fn = {
  170. forEach: emptyArray.forEach,
  171. reduce: emptyArray.reduce,
  172. push: emptyArray.push,
  173. indexOf: emptyArray.indexOf,
  174. concat: emptyArray.concat,
  175. map: function(fn){
  176. return $.map(this, function(el, i){ return fn.call(el, i, el) });
  177. },
  178. slice: function(){
  179. return $(slice.apply(this, arguments));
  180. },
  181. ready: function(callback){
  182. if (readyRE.test(document.readyState)) callback($);
  183. else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false);
  184. return this;
  185. },
  186. get: function(idx){ return idx === undefined ? this : this[idx] },
  187. size: function(){ return this.length },
  188. remove: function () {
  189. return this.each(function () {
  190. if (this.parentNode != null) {
  191. this.parentNode.removeChild(this);
  192. }
  193. });
  194. },
  195. each: function(callback){
  196. this.forEach(function(el, idx){ callback.call(el, idx, el) });
  197. return this;
  198. },
  199. filter: function(selector){
  200. return $([].filter.call(this, function(element){
  201. return element.parentNode && $$(element.parentNode, selector).indexOf(element) >= 0;
  202. }));
  203. },
  204. end: function(){
  205. return this.prevObject || $();
  206. },
  207. andSelf:function(){
  208. return this.add(this.prevObject || $())
  209. },
  210. add:function(selector,context){
  211. return $(uniq(this.concat($(selector,context))));
  212. },
  213. is: function(selector){
  214. return this.length > 0 && $(this[0]).filter(selector).length > 0;
  215. },
  216. not: function(selector){
  217. var nodes=[];
  218. if (isF(selector) && selector.call !== undefined)
  219. this.each(function(idx){
  220. if (!selector.call(this,idx)) nodes.push(this);
  221. });
  222. else {
  223. var excludes = typeof selector == 'string' ? this.filter(selector) :
  224. (likeArray(selector) && isF(selector.item)) ? slice.call(selector) : $(selector);
  225. this.forEach(function(el){
  226. if (excludes.indexOf(el) < 0) nodes.push(el);
  227. });
  228. }
  229. return $(nodes);
  230. },
  231. eq: function(idx){
  232. return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1);
  233. },
  234. first: function(){ var el = this[0]; return el && !isO(el) ? el : $(el) },
  235. last: function(){ var el = this[this.length - 1]; return el && !isO(el) ? el : $(el) },
  236. find: function(selector){
  237. var result;
  238. if (this.length == 1) result = $$(this[0], selector);
  239. else result = this.map(function(){ return $$(this, selector) });
  240. return $(result);
  241. },
  242. closest: function(selector, context){
  243. var node = this[0], candidates = $$(context || document, selector);
  244. if (!candidates.length) node = null;
  245. while (node && candidates.indexOf(node) < 0)
  246. node = node !== context && node !== document && node.parentNode;
  247. return $(node);
  248. },
  249. parents: function(selector){
  250. var ancestors = [], nodes = this;
  251. while (nodes.length > 0)
  252. nodes = $.map(nodes, function(node){
  253. if ((node = node.parentNode) && node !== document && ancestors.indexOf(node) < 0) {
  254. ancestors.push(node);
  255. return node;
  256. }
  257. });
  258. return filtered(ancestors, selector);
  259. },
  260. parent: function(selector){
  261. return filtered(uniq(this.pluck('parentNode')), selector);
  262. },
  263. children: function(selector){
  264. return filtered(this.map(function(){ return slice.call(this.children) }), selector);
  265. },
  266. siblings: function(selector){
  267. return filtered(this.map(function(i, el){
  268. return slice.call(el.parentNode.children).filter(function(child){ return child!==el });
  269. }), selector);
  270. },
  271. empty: function(){ return this.each(function(){ this.innerHTML = '' }) },
  272. pluck: function(property){ return this.map(function(){ return this[property] }) },
  273. show: function(){
  274. return this.each(function() {
  275. this.style.display == "none" && (this.style.display = null);
  276. if (getComputedStyle(this, '').getPropertyValue("display") == "none") {
  277. this.style.display = defaultDisplay(this.nodeName)
  278. }
  279. })
  280. },
  281. replaceWith: function(newContent) {
  282. return this.each(function() {
  283. $(this).before(newContent).remove();
  284. });
  285. },
  286. wrap: function(newContent) {
  287. return this.each(function() {
  288. $(this).wrapAll($(newContent)[0].cloneNode(false));
  289. });
  290. },
  291. wrapAll: function(newContent) {
  292. if (this[0]) {
  293. $(this[0]).before(newContent = $(newContent));
  294. newContent.append(this);
  295. }
  296. return this;
  297. },
  298. unwrap: function(){
  299. this.parent().each(function(){
  300. $(this).replaceWith($(this).children());
  301. });
  302. return this;
  303. },
  304. hide: function(){
  305. return this.css("display", "none")
  306. },
  307. toggle: function(setting){
  308. return (setting === undefined ? this.css("display") == "none" : setting) ? this.show() : this.hide();
  309. },
  310. prev: function(){ return $(this.pluck('previousElementSibling')) },
  311. next: function(){ return $(this.pluck('nextElementSibling')) },
  312. html: function(html){
  313. return html === undefined ?
  314. (this.length > 0 ? this[0].innerHTML : null) :
  315. this.each(function (idx) {
  316. var originHtml = this.innerHTML;
  317. $(this).empty().append( funcArg(this, html, idx, originHtml) );
  318. });
  319. },
  320. text: function(text){
  321. return text === undefined ?
  322. (this.length > 0 ? this[0].textContent : null) :
  323. this.each(function(){ this.textContent = text });
  324. },
  325. attr: function(name, value){
  326. var res;
  327. return (typeof name == 'string' && value === undefined) ?
  328. (this.length == 0 ? undefined :
  329. (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() :
  330. (!(res = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : res
  331. ) :
  332. this.each(function(idx){
  333. if (isO(name)) for (key in name) this.setAttribute(key, name[key])
  334. else this.setAttribute(name, funcArg(this, value, idx, this.getAttribute(name)));
  335. });
  336. },
  337. removeAttr: function(name) {
  338. return this.each(function() { this.removeAttribute(name); });
  339. },
  340. data: function(name, value){
  341. return this.attr('data-' + name, value);
  342. },
  343. val: function(value){
  344. return (value === undefined) ?
  345. (this.length > 0 ? this[0].value : null) :
  346. this.each(function(idx){
  347. this.value = funcArg(this, value, idx, this.value);
  348. });
  349. },
  350. offset: function(){
  351. if(this.length==0) return null;
  352. var obj = this[0].getBoundingClientRect();
  353. return {
  354. left: obj.left + window.pageXOffset,
  355. top: obj.top + window.pageYOffset,
  356. width: obj.width,
  357. height: obj.height
  358. };
  359. },
  360. css: function(property, value){
  361. if (value === undefined && typeof property == 'string') {
  362. return(
  363. this.length == 0
  364. ? undefined
  365. : this[0].style[camelize(property)] || getComputedStyle(this[0], '').getPropertyValue(property)
  366. );
  367. }
  368. var css = '';
  369. for (key in property) css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';';
  370. if (typeof property == 'string') css = dasherize(property) + ":" + maybeAddPx(property, value);
  371. return this.each(function() { this.style.cssText += ';' + css });
  372. },
  373. index: function(element){
  374. return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]);
  375. },
  376. hasClass: function(name){
  377. if (this.length < 1) return false;
  378. else return classRE(name).test(this[0].className);
  379. },
  380. addClass: function(name){
  381. return this.each(function(idx) {
  382. classList = [];
  383. var cls = this.className, newName = funcArg(this, name, idx, cls);
  384. newName.split(/\s+/g).forEach(function(klass) {
  385. if (!$(this).hasClass(klass)) {
  386. classList.push(klass)
  387. }
  388. }, this);
  389. classList.length && (this.className += (cls ? " " : "") + classList.join(" "))
  390. });
  391. },
  392. removeClass: function(name){
  393. return this.each(function(idx) {
  394. if(name === undefined)
  395. return this.className = '';
  396. classList = this.className;
  397. funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass) {
  398. classList = classList.replace(classRE(klass), " ")
  399. });
  400. this.className = classList.trim()
  401. });
  402. },
  403. toggleClass: function(name, when){
  404. return this.each(function(idx){
  405. var newName = funcArg(this, name, idx, this.className);
  406. (when === undefined ? !$(this).hasClass(newName) : when) ?
  407. $(this).addClass(newName) : $(this).removeClass(newName);
  408. });
  409. }
  410. };
  411. 'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){
  412. var fn = $.fn[property];
  413. $.fn[property] = function() {
  414. var ret = fn.apply(this, arguments);
  415. ret.prevObject = this;
  416. return ret;
  417. }
  418. });
  419. ['width', 'height'].forEach(function(dimension){
  420. $.fn[dimension] = function(value) {
  421. var offset, Dimension = dimension.replace(/./, function(m) { return m[0].toUpperCase() });
  422. if (value === undefined) return this[0] == window ? window['inner' + Dimension] :
  423. this[0] == document ? document.documentElement['offset' + Dimension] :
  424. (offset = this.offset()) && offset[dimension];
  425. else return this.each(function(idx){
  426. var el = $(this);
  427. el.css(dimension, funcArg(this, value, idx, el[dimension]()));
  428. });
  429. }
  430. });
  431. function insert(operator, target, node) {
  432. var parent = (operator % 2) ? target : target.parentNode;
  433. parent && parent.insertBefore(node,
  434. !operator ? target.nextSibling : // after
  435. operator == 1 ? parent.firstChild : // prepend
  436. operator == 2 ? target : // before
  437. null); // append
  438. }
  439. function traverseNode (node, fun) {
  440. fun(node);
  441. for (var key in node.childNodes) {
  442. traverseNode(node.childNodes[key], fun);
  443. }
  444. }
  445. adjacencyOperators.forEach(function(key, operator) {
  446. $.fn[key] = function(html){
  447. var nodes = isO(html) ? html : fragment(html);
  448. if (!('length' in nodes) || nodes.nodeType) nodes = [nodes];
  449. if (nodes.length < 1) return this;
  450. var size = this.length, copyByClone = size > 1, inReverse = operator < 2;
  451. return this.each(function(index, target){
  452. for (var i = 0; i < nodes.length; i++) {
  453. var node = nodes[inReverse ? nodes.length-i-1 : i];
  454. traverseNode(node, function (node) {
  455. if (node.nodeName != null && node.nodeName.toUpperCase() === 'SCRIPT' && (!node.type || node.type === 'text/javascript')) {
  456. window['eval'].call(window, node.innerHTML);
  457. }
  458. });
  459. if (copyByClone && index < size - 1) node = node.cloneNode(true);
  460. insert(operator, target, node);
  461. }
  462. });
  463. };
  464. var reverseKey = (operator % 2) ? key+'To' : 'insert'+(operator ? 'Before' : 'After');
  465. $.fn[reverseKey] = function(html) {
  466. $(html)[key](this);
  467. return this;
  468. };
  469. });
  470. Z.prototype = $.fn;
  471. return $;
  472. })();
  473. window.Zepto = Zepto;
  474. '$' in window || (window.$ = Zepto);
  475. // Zepto.js
  476. // (c) 2010, 2011 Thomas Fuchs
  477. // Zepto.js may be freely distributed under the MIT license.
  478. (function($){
  479. var $$ = $.qsa, handlers = {}, _zid = 1, specialEvents={};
  480. specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents';
  481. function zid(element) {
  482. return element._zid || (element._zid = _zid++);
  483. }
  484. function findHandlers(element, event, fn, selector) {
  485. event = parse(event);
  486. if (event.ns) var matcher = matcherFor(event.ns);
  487. return (handlers[zid(element)] || []).filter(function(handler) {
  488. return handler
  489. && (!event.e || handler.e == event.e)
  490. && (!event.ns || matcher.test(handler.ns))
  491. && (!fn || handler.fn == fn)
  492. && (!selector || handler.sel == selector);
  493. });
  494. }
  495. function parse(event) {
  496. var parts = ('' + event).split('.');
  497. return {e: parts[0], ns: parts.slice(1).sort().join(' ')};
  498. }
  499. function matcherFor(ns) {
  500. return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)');
  501. }
  502. function eachEvent(events, fn, iterator){
  503. if ($.isObject(events)) $.each(events, iterator);
  504. else events.split(/\s/).forEach(function(type){ iterator(type, fn) });
  505. }
  506. function add(element, events, fn, selector, getDelegate){
  507. var id = zid(element), set = (handlers[id] || (handlers[id] = []));
  508. eachEvent(events, fn, function(event, fn){
  509. var delegate = getDelegate && getDelegate(fn, event),
  510. callback = delegate || fn;
  511. var proxyfn = function (event) {
  512. var result = callback.apply(element, [event].concat(event.data));
  513. if (result === false) event.preventDefault();
  514. return result;
  515. };
  516. var handler = $.extend(parse(event), {fn: fn, proxy: proxyfn, sel: selector, del: delegate, i: set.length});
  517. set.push(handler);
  518. element.addEventListener(handler.e, proxyfn, false);
  519. });
  520. }
  521. function remove(element, events, fn, selector){
  522. var id = zid(element);
  523. eachEvent(events || '', fn, function(event, fn){
  524. findHandlers(element, event, fn, selector).forEach(function(handler){
  525. delete handlers[id][handler.i];
  526. element.removeEventListener(handler.e, handler.proxy, false);
  527. });
  528. });
  529. }
  530. $.event = { add: add, remove: remove }
  531. $.fn.bind = function(event, callback){
  532. return this.each(function(){
  533. add(this, event, callback);
  534. });
  535. };
  536. $.fn.unbind = function(event, callback){
  537. return this.each(function(){
  538. remove(this, event, callback);
  539. });
  540. };
  541. $.fn.one = function(event, callback){
  542. return this.each(function(i, element){
  543. add(this, event, callback, null, function(fn, type){
  544. return function(){
  545. var result = fn.apply(element, arguments);
  546. remove(element, type, fn);
  547. return result;
  548. }
  549. });
  550. });
  551. };
  552. var returnTrue = function(){return true},
  553. returnFalse = function(){return false},
  554. eventMethods = {
  555. preventDefault: 'isDefaultPrevented',
  556. stopImmediatePropagation: 'isImmediatePropagationStopped',
  557. stopPropagation: 'isPropagationStopped'
  558. };
  559. function createProxy(event) {
  560. var proxy = $.extend({originalEvent: event}, event);
  561. $.each(eventMethods, function(name, predicate) {
  562. proxy[name] = function(){
  563. this[predicate] = returnTrue;
  564. return event[name].apply(event, arguments);
  565. };
  566. proxy[predicate] = returnFalse;
  567. })
  568. return proxy;
  569. }
  570. // emulates the 'defaultPrevented' property for browsers that have none
  571. function fix(event) {
  572. if (!('defaultPrevented' in event)) {
  573. event.defaultPrevented = false;
  574. var prevent = event.preventDefault;
  575. event.preventDefault = function() {
  576. this.defaultPrevented = true;
  577. prevent.call(this);
  578. }
  579. }
  580. }
  581. $.fn.delegate = function(selector, event, callback){
  582. return this.each(function(i, element){
  583. add(element, event, callback, selector, function(fn){
  584. return function(e){
  585. var evt, match = $(e.target).closest(selector, element).get(0);
  586. if (match) {
  587. evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element});
  588. return fn.apply(match, [evt].concat([].slice.call(arguments, 1)));
  589. }
  590. }
  591. });
  592. });
  593. };
  594. $.fn.undelegate = function(selector, event, callback){
  595. return this.each(function(){
  596. remove(this, event, callback, selector);
  597. });
  598. }
  599. $.fn.live = function(event, callback){
  600. $(document.body).delegate(this.selector, event, callback);
  601. return this;
  602. };
  603. $.fn.die = function(event, callback){
  604. $(document.body).undelegate(this.selector, event, callback);
  605. return this;
  606. };
  607. $.fn.on = function(event, selector, callback){
  608. return selector === undefined || $.isFunction(selector) ?
  609. this.bind(event, selector) : this.delegate(selector, event, callback);
  610. };
  611. $.fn.off = function(event, selector, callback){
  612. return selector === undefined || $.isFunction(selector) ?
  613. this.unbind(event, selector) : this.undelegate(selector, event, callback);
  614. };
  615. $.fn.trigger = function(event, data){
  616. if (typeof event == 'string') event = $.Event(event);
  617. fix(event);
  618. event.data = data;
  619. return this.each(function(){ this.dispatchEvent(event) });
  620. };
  621. // triggers event handlers on current element just as if an event occurred,
  622. // doesn't trigger an actual event, doesn't bubble
  623. $.fn.triggerHandler = function(event, data){
  624. var e, result;
  625. this.each(function(i, element){
  626. e = createProxy(typeof event == 'string' ? $.Event(event) : event);
  627. e.data = data; e.target = element;
  628. $.each(findHandlers(element, event.type || event), function(i, handler){
  629. result = handler.proxy(e);
  630. if (e.isImmediatePropagationStopped()) return false;
  631. });
  632. });
  633. return result;
  634. };
  635. // shortcut methods for `.bind(event, fn)` for each event type
  636. ('focusin focusout load resize scroll unload click dblclick '+
  637. 'mousedown mouseup mousemove mouseover mouseout '+
  638. 'change select keydown keypress keyup error').split(' ').forEach(function(event) {
  639. $.fn[event] = function(callback){ return this.bind(event, callback) };
  640. });
  641. ['focus', 'blur'].forEach(function(name) {
  642. $.fn[name] = function(callback) {
  643. if (callback) this.bind(name, callback);
  644. else if (this.length) try { this.get(0)[name]() } catch(e){};
  645. return this;
  646. };
  647. });
  648. $.Event = function(type, props) {
  649. var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true;
  650. if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]);
  651. event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null);
  652. return event;
  653. };
  654. })(Zepto);
  655. // Zepto.js
  656. // (c) 2010, 2011 Thomas Fuchs
  657. // Zepto.js may be freely distributed under the MIT license.
  658. (function($){
  659. function detect(ua){
  660. var os = (this.os = {}), browser = (this.browser = {}),
  661. webkit = ua.match(/WebKit\/([\d.]+)/),
  662. android = ua.match(/(Android)\s+([\d.]+)/),
  663. ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
  664. iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
  665. webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/),
  666. touchpad = webos && ua.match(/TouchPad/),
  667. blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/);
  668. if (webkit) browser.version = webkit[1];
  669. browser.webkit = !!webkit;
  670. if (android) os.android = true, os.version = android[2];
  671. if (iphone) os.ios = true, os.version = iphone[2].replace(/_/g, '.'), os.iphone = true;
  672. if (ipad) os.ios = true, os.version = ipad[2].replace(/_/g, '.'), os.ipad = true;
  673. if (webos) os.webos = true, os.version = webos[2];
  674. if (touchpad) os.touchpad = true;
  675. if (blackberry) os.blackberry = true, os.version = blackberry[2];
  676. }
  677. // ### $.os
  678. //
  679. // Object containing information about browser platform
  680. //
  681. // *Example:*
  682. //
  683. // $.os.ios // => true if running on Apple iOS
  684. // $.os.android // => true if running on Android
  685. // $.os.webos // => true if running on HP/Palm WebOS
  686. // $.os.touchpad // => true if running on a HP TouchPad
  687. // $.os.version // => string with a version number, e.g.
  688. // "4.0", "3.1.1", "2.1", etc.
  689. // $.os.iphone // => true if running on iPhone
  690. // $.os.ipad // => true if running on iPad
  691. // $.os.blackberry // => true if running on BlackBerry
  692. //
  693. // ### $.browser
  694. //
  695. // *Example:*
  696. //
  697. // $.browser.webkit // => true if the browser is WebKit-based
  698. // $.browser.version // => WebKit version string
  699. detect.call($, navigator.userAgent);
  700. // make available to unit tests
  701. $.__detect = detect;
  702. })(Zepto);
  703. // Zepto.js
  704. // (c) 2010, 2011 Thomas Fuchs
  705. // Zepto.js may be freely distributed under the MIT license.
  706. (function($, undefined){
  707. var prefix = '', eventPrefix, endEventName, endAnimationName,
  708. vendors = {Webkit: 'webkit', Moz: '', O: 'o', ms: 'MS'},
  709. document = window.document, testEl = document.createElement('div'),
  710. supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i;
  711. function downcase(str) { return str.toLowerCase() }
  712. function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : downcase(name) };
  713. $.each(vendors, function(vendor, event){
  714. if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
  715. prefix = '-' + downcase(vendor) + '-';
  716. eventPrefix = event;
  717. return false;
  718. }
  719. });
  720. $.fx = {
  721. off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined),
  722. cssPrefix: prefix,
  723. transitionEnd: normalizeEvent('TransitionEnd'),
  724. animationEnd: normalizeEvent('AnimationEnd')
  725. };
  726. $.fn.animate = function(properties, duration, ease, callback){
  727. if ($.isObject(duration))
  728. ease = duration.easing, callback = duration.complete, duration = duration.duration;
  729. if (duration) duration = duration / 1000;
  730. return this.anim(properties, duration, ease, callback);
  731. };
  732. $.fn.anim = function(properties, duration, ease, callback){
  733. var transforms, cssProperties = {}, key, that = this, wrappedCallback, endEvent = $.fx.transitionEnd;
  734. if (duration === undefined) duration = 0.4;
  735. if ($.fx.off) duration = 0;
  736. if (typeof properties == 'string') {
  737. // keyframe animation
  738. cssProperties[prefix + 'animation-name'] = properties;
  739. cssProperties[prefix + 'animation-duration'] = duration + 's';
  740. endEvent = $.fx.animationEnd;
  741. } else {
  742. // CSS transitions
  743. for (key in properties)
  744. if (supportedTransforms.test(key)) {
  745. transforms || (transforms = []);
  746. transforms.push(key + '(' + properties[key] + ')');
  747. }
  748. else cssProperties[key] = properties[key];
  749. if (transforms) cssProperties[prefix + 'transform'] = transforms.join(' ');
  750. if (!$.fx.off) cssProperties[prefix + 'transition'] = 'all ' + duration + 's ' + (ease || '');
  751. }
  752. wrappedCallback = function(){
  753. var props = {};
  754. props[prefix + 'transition'] = props[prefix + 'animation-name'] = 'none';
  755. $(this).css(props);
  756. callback && callback.call(this);
  757. }
  758. if (duration > 0) this.one(endEvent, wrappedCallback);
  759. setTimeout(function() {
  760. that.css(cssProperties);
  761. if (duration <= 0) setTimeout(function() {
  762. that.each(function(){ wrappedCallback.call(this) });
  763. }, 0);
  764. }, 0);
  765. return this;
  766. };
  767. testEl = null;
  768. })(Zepto);
  769. // Zepto.js
  770. // (c) 2010, 2011 Thomas Fuchs
  771. // Zepto.js may be freely distributed under the MIT license.
  772. (function($){
  773. var jsonpID = 0,
  774. isObject = $.isObject,
  775. document = window.document,
  776. key,
  777. name;
  778. // trigger a custom event and return false if it was cancelled
  779. function triggerAndReturn(context, eventName, data) {
  780. var event = $.Event(eventName);
  781. $(context).trigger(event, data);
  782. return !event.defaultPrevented;
  783. }
  784. // trigger an Ajax "global" event
  785. function triggerGlobal(settings, context, eventName, data) {
  786. if (settings.global) return triggerAndReturn(context || document, eventName, data);
  787. }
  788. // Number of active Ajax requests
  789. $.active = 0;
  790. function ajaxStart(settings) {
  791. if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart');
  792. }
  793. function ajaxStop(settings) {
  794. if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop');
  795. }
  796. // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable
  797. function ajaxBeforeSend(xhr, settings) {
  798. var context = settings.context;
  799. if (settings.beforeSend.call(context, xhr, settings) === false ||
  800. triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false)
  801. return false;
  802. triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]);
  803. }
  804. function ajaxSuccess(data, xhr, settings) {
  805. var context = settings.context, status = 'success';
  806. settings.success.call(context, data, status, xhr);
  807. triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]);
  808. ajaxComplete(status, xhr, settings);
  809. }
  810. // type: "timeout", "error", "abort", "parsererror"
  811. function ajaxError(error, type, xhr, settings) {
  812. var context = settings.context;
  813. settings.error.call(context, xhr, type, error);
  814. triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error]);
  815. ajaxComplete(type, xhr, settings);
  816. }
  817. // status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
  818. function ajaxComplete(status, xhr, settings) {
  819. var context = settings.context;
  820. settings.complete.call(context, xhr, status);
  821. triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]);
  822. ajaxStop(settings);
  823. }
  824. // Empty function, used as default callback
  825. function empty() {}
  826. // ### $.ajaxJSONP
  827. //
  828. // Load JSON from a server in a different domain (JSONP)
  829. //
  830. // *Arguments:*
  831. //
  832. // options — object that configure the request,
  833. // see avaliable options below
  834. //
  835. // *Avaliable options:*
  836. //
  837. // url — url to which the request is sent
  838. // success — callback that is executed if the request succeeds
  839. // error — callback that is executed if the server drops error
  840. // context — in which context to execute the callbacks in
  841. //
  842. // *Example:*
  843. //
  844. // $.ajaxJSONP({
  845. // url: 'http://example.com/projects?callback=?',
  846. // success: function (data) {
  847. // projects.push(json);
  848. // }
  849. // });
  850. //
  851. $.ajaxJSONP = function(options){
  852. var callbackName = 'jsonp' + (++jsonpID),
  853. script = document.createElement('script'),
  854. abort = function(){
  855. $(script).remove();
  856. if (callbackName in window) window[callbackName] = empty;
  857. ajaxComplete(xhr, options, 'abort');
  858. },
  859. xhr = { abort: abort }, abortTimeout;
  860. window[callbackName] = function(data){
  861. clearTimeout(abortTimeout);
  862. $(script).remove();
  863. delete window[callbackName];
  864. ajaxSuccess(data, xhr, options);
  865. };
  866. script.src = options.url.replace(/=\?/, '=' + callbackName);
  867. $('head').append(script);
  868. if (options.timeout > 0) abortTimeout = setTimeout(function(){
  869. xhr.abort();
  870. ajaxComplete(xhr, options, 'timeout');
  871. }, options.timeout);
  872. return xhr;
  873. };
  874. // ### $.ajaxSettings
  875. //
  876. // AJAX settings
  877. //
  878. $.ajaxSettings = {
  879. // Default type of request
  880. type: 'GET',
  881. // Callback that is executed before request
  882. beforeSend: empty,
  883. // Callback that is executed if the request succeeds
  884. success: empty,
  885. // Callback that is executed the the server drops error
  886. error: empty,
  887. // Callback that is executed on request complete (both: error and success)
  888. complete: empty,
  889. // The context for the callbacks
  890. context: null,
  891. // Whether to trigger "global" Ajax events
  892. global: true,
  893. // Transport
  894. xhr: function () {
  895. return new window.XMLHttpRequest();
  896. },
  897. // MIME types mapping
  898. accepts: {
  899. script: 'text/javascript, application/javascript',
  900. json: 'application/json',
  901. xml: 'application/xml, text/xml',
  902. html: 'text/html',
  903. text: 'text/plain'
  904. },
  905. // Whether the request is to another domain
  906. crossDomain: false,
  907. // Default timeout
  908. timeout: 0
  909. };
  910. // ### $.ajax
  911. //
  912. // Perform AJAX request
  913. //
  914. // *Arguments:*
  915. //
  916. // options — object that configure the request,
  917. // see avaliable options below
  918. //
  919. // *Avaliable options:*
  920. //
  921. // type ('GET') — type of request GET / POST
  922. // url (window.location) — url to which the request is sent
  923. // data — data to send to server,
  924. // can be string or object
  925. // dataType ('json') — what response type you accept from
  926. // the server:
  927. // 'json', 'xml', 'html', or 'text'
  928. // timeout (0) — request timeout
  929. // beforeSend — callback that is executed before
  930. // request send
  931. // complete — callback that is executed on request
  932. // complete (both: error and success)
  933. // success — callback that is executed if
  934. // the request succeeds
  935. // error — callback that is executed if
  936. // the server drops error
  937. // context — in which context to execute the
  938. // callbacks in
  939. //
  940. // *Example:*
  941. //
  942. // $.ajax({
  943. // type: 'POST',
  944. // url: '/projects',
  945. // data: { name: 'Zepto.js' },
  946. // dataType: 'html',
  947. // timeout: 100,
  948. // context: $('body'),
  949. // success: function (data) {
  950. // this.append(data);
  951. // },
  952. // error: function (xhr, type) {
  953. // alert('Error!');
  954. // }
  955. // });
  956. //
  957. $.ajax = function(options){
  958. var settings = $.extend({}, options || {});
  959. for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key];
  960. ajaxStart(settings);
  961. if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) &&
  962. RegExp.$2 != window.location.host;
  963. if (/=\?/.test(settings.url)) return $.ajaxJSONP(settings);
  964. if (!settings.url) settings.url = window.location.toString();
  965. if (settings.data && !settings.contentType) settings.contentType = 'application/x-www-form-urlencoded';
  966. if (isObject(settings.data)) settings.data = $.param(settings.data);
  967. if (settings.type.match(/get/i) && settings.data) {
  968. var queryString = settings.data;
  969. if (settings.url.match(/\?.*=/)) {
  970. queryString = '&' + queryString;
  971. } else if (queryString[0] != '?') {
  972. queryString = '?' + queryString;
  973. }
  974. settings.url += queryString;
  975. }
  976. var mime = settings.accepts[settings.dataType],
  977. baseHeaders = { },
  978. protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol,
  979. xhr = $.ajaxSettings.xhr(), abortTimeout;
  980. if (!settings.crossDomain) baseHeaders['X-Requested-With'] = 'XMLHttpRequest';
  981. if (mime) baseHeaders['Accept'] = mime;
  982. settings.headers = $.extend(baseHeaders, settings.headers || {});
  983. xhr.onreadystatechange = function(){
  984. if (xhr.readyState == 4) {
  985. clearTimeout(abortTimeout);
  986. var result, error = false;
  987. if ((xhr.status >= 200 && xhr.status < 300) || (xhr.status == 0 && protocol == 'file:')) {
  988. if (mime == 'application/json' && !(/^\s*$/.test(xhr.responseText))) {
  989. try { result = JSON.parse(xhr.responseText); }
  990. catch (e) { error = e; }
  991. }
  992. else result = xhr.responseText;
  993. if (error) ajaxError(error, 'parsererror', xhr, settings);
  994. else ajaxSuccess(result, xhr, settings);
  995. } else {
  996. ajaxError(null, 'error', xhr, settings);
  997. }
  998. }
  999. };
  1000. xhr.open(settings.type, settings.url, true);
  1001. if (settings.contentType) settings.headers['Content-Type'] = settings.contentType;
  1002. for (name in settings.headers) xhr.setRequestHeader(name, settings.headers[name]);
  1003. if (ajaxBeforeSend(xhr, settings) === false) {
  1004. xhr.abort();
  1005. return false;
  1006. }
  1007. if (settings.timeout > 0) abortTimeout = setTimeout(function(){
  1008. xhr.onreadystatechange = empty;
  1009. xhr.abort();
  1010. ajaxError(null, 'timeout', xhr, settings);
  1011. }, settings.timeout);
  1012. xhr.send(settings.data);
  1013. return xhr;
  1014. };
  1015. // ### $.get
  1016. //
  1017. // Load data from the server using a GET request
  1018. //
  1019. // *Arguments:*
  1020. //
  1021. // url — url to which the request is sent
  1022. // success — callback that is executed if the request succeeds
  1023. //
  1024. // *Example:*
  1025. //
  1026. // $.get(
  1027. // '/projects/42',
  1028. // function (data) {
  1029. // $('body').append(data);
  1030. // }
  1031. // );
  1032. //
  1033. $.get = function(url, success){ return $.ajax({ url: url, success: success }) };
  1034. // ### $.post
  1035. //
  1036. // Load data from the server using POST request
  1037. //
  1038. // *Arguments:*
  1039. //
  1040. // url — url to which the request is sent
  1041. // [data] — data to send to server, can be string or object
  1042. // [success] — callback that is executed if the request succeeds
  1043. // [dataType] — type of expected response
  1044. // 'json', 'xml', 'html', or 'text'
  1045. //
  1046. // *Example:*
  1047. //
  1048. // $.post(
  1049. // '/projects',
  1050. // { name: 'Zepto.js' },
  1051. // function (data) {
  1052. // $('body').append(data);
  1053. // },
  1054. // 'html'
  1055. // );
  1056. //
  1057. $.post = function(url, data, success, dataType){
  1058. if ($.isFunction(data)) dataType = dataType || success, success = data, data = null;
  1059. return $.ajax({ type: 'POST', url: url, data: data, success: success, dataType: dataType });
  1060. };
  1061. // ### $.getJSON
  1062. //
  1063. // Load JSON from the server using GET request
  1064. //
  1065. // *Arguments:*
  1066. //
  1067. // url — url to which the request is sent
  1068. // success — callback that is executed if the request succeeds
  1069. //
  1070. // *Example:*
  1071. //
  1072. // $.getJSON(
  1073. // '/projects/42',
  1074. // function (json) {
  1075. // projects.push(json);
  1076. // }
  1077. // );
  1078. //
  1079. $.getJSON = function(url, success){
  1080. return $.ajax({ url: url, success: success, dataType: 'json' });
  1081. };
  1082. // ### $.fn.load
  1083. //
  1084. // Load data from the server into an element
  1085. //
  1086. // *Arguments:*
  1087. //
  1088. // url — url to which the request is sent
  1089. // [success] — callback that is executed if the request succeeds
  1090. //
  1091. // *Examples:*
  1092. //
  1093. // $('#project_container').get(
  1094. // '/projects/42',
  1095. // function () {
  1096. // alert('Project was successfully loaded');
  1097. // }
  1098. // );
  1099. //
  1100. // $('#project_comments').get(
  1101. // '/projects/42 #comments',
  1102. // function () {
  1103. // alert('Comments was successfully loaded');
  1104. // }
  1105. // );
  1106. //
  1107. $.fn.load = function(url, success){
  1108. if (!this.length) return this;
  1109. var self = this, parts = url.split(/\s/), selector;
  1110. if (parts.length > 1) url = parts[0], selector = parts[1];
  1111. $.get(url, function(response){
  1112. self.html(selector ?
  1113. $(document.createElement('div')).html(response).find(selector).html()
  1114. : response);
  1115. success && success.call(self);
  1116. });
  1117. return this;
  1118. };
  1119. var escape = encodeURIComponent;
  1120. function serialize(params, obj, traditional, scope){
  1121. var array = $.isArray(obj);
  1122. $.each(obj, function(key, value) {
  1123. if (scope) key = traditional ? scope : scope + '[' + (array ? '' : key) + ']';
  1124. // handle data in serializeArray() format
  1125. if (!scope && array) params.add(value.name, value.value);
  1126. // recurse into nested objects
  1127. else if (traditional ? $.isArray(value) : isObject(value))
  1128. serialize(params, value, traditional, key);
  1129. else params.add(key, value);
  1130. });
  1131. }
  1132. // ### $.param
  1133. //
  1134. // Encode object as a string of URL-encoded key-value pairs
  1135. //
  1136. // *Arguments:*
  1137. //
  1138. // obj — object to serialize
  1139. // [traditional] — perform shallow serialization
  1140. //
  1141. // *Example:*
  1142. //
  1143. // $.param( { name: 'Zepto.js', version: '0.6' } );
  1144. //
  1145. $.param = function(obj, traditional){
  1146. var params = [];
  1147. params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) };
  1148. serialize(params, obj, traditional);
  1149. return params.join('&').replace('%20', '+');
  1150. };
  1151. })(Zepto);
  1152. // Zepto.js
  1153. // (c) 2010, 2011 Thomas Fuchs
  1154. // Zepto.js may be freely distributed under the MIT license.
  1155. (function ($) {
  1156. // ### $.fn.serializeArray
  1157. //
  1158. // Encode a set of form elements as an array of names and values
  1159. //
  1160. // *Example:*
  1161. //
  1162. // $('#login_form').serializeArray();
  1163. //
  1164. // returns
  1165. //
  1166. // [
  1167. // {
  1168. // name: 'email',
  1169. // value: 'koss@nocorp.me'
  1170. // },
  1171. // {
  1172. // name: 'password',
  1173. // value: '123456'
  1174. // }
  1175. // ]
  1176. //
  1177. $.fn.serializeArray = function () {
  1178. var result = [], el;
  1179. $( Array.prototype.slice.call(this.get(0).elements) ).each(function () {
  1180. el = $(this);
  1181. var type = el.attr('type');
  1182. if (
  1183. !this.disabled && type != 'submit' && type != 'reset' && type != 'button' &&
  1184. ((type != 'radio' && type != 'checkbox') || this.checked)
  1185. ) {
  1186. result.push({
  1187. name: el.attr('name'),
  1188. value: el.val()
  1189. });
  1190. }
  1191. });
  1192. return result;
  1193. };
  1194. // ### $.fn.serialize
  1195. //
  1196. //
  1197. // Encode a set of form elements as a string for submission
  1198. //
  1199. // *Example:*
  1200. //
  1201. // $('#login_form').serialize();
  1202. //
  1203. // returns
  1204. //
  1205. // "email=koss%40nocorp.me&password=123456"
  1206. //
  1207. $.fn.serialize = function () {
  1208. var result = [];
  1209. this.serializeArray().forEach(function (elm) {
  1210. result.push( encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value) );
  1211. });
  1212. return result.join('&');
  1213. };
  1214. // ### $.fn.submit
  1215. //
  1216. // Bind or trigger the submit event for a form
  1217. //
  1218. // *Examples:*
  1219. //
  1220. // To bind a handler for the submit event:
  1221. //
  1222. // $('#login_form').submit(function (e) {
  1223. // alert('Form was submitted!');
  1224. // e.preventDefault();
  1225. // });
  1226. //
  1227. // To trigger form submit:
  1228. //
  1229. // $('#login_form').submit();
  1230. //
  1231. $.fn.submit = function (callback) {
  1232. if (callback) this.bind('submit', callback)
  1233. else if (this.length) {
  1234. var event = $.Event('submit');
  1235. this.eq(0).trigger(event);
  1236. if (!event.defaultPrevented) this.get(0).submit()
  1237. }
  1238. return this;
  1239. }
  1240. })(Zepto);
  1241. // Zepto.js
  1242. // (c) 2010, 2011 Thomas Fuchs
  1243. // Zepto.js may be freely distributed under the MIT license.
  1244. (function($){
  1245. var touch = {}, touchTimeout;
  1246. function parentIfText(node){
  1247. return 'tagName' in node ? node : node.parentNode;
  1248. }
  1249. function swipeDirection(x1, x2, y1, y2){
  1250. var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2);
  1251. if (xDelta >= yDelta) {
  1252. return (x1 - x2 > 0 ? 'Left' : 'Right');
  1253. } else {
  1254. return (y1 - y2 > 0 ? 'Up' : 'Down');
  1255. }
  1256. }
  1257. var longTapDelay = 750;
  1258. function longTap(){
  1259. if (touch.last && (Date.now() - touch.last >= longTapDelay)) {
  1260. $(touch.target).trigger('longTap');
  1261. touch = {};
  1262. }
  1263. }
  1264. $(document).ready(function(){
  1265. $(document.body).bind('touchstart', function(e){
  1266. var now = Date.now(), delta = now - (touch.last || now);
  1267. touch.target = parentIfText(e.touches[0].target);
  1268. touchTimeout && clearTimeout(touchTimeout);
  1269. touch.x1 = e.touches[0].pageX;
  1270. touch.y1 = e.touches[0].pageY;
  1271. if (delta > 0 && delta <= 250) touch.isDoubleTap = true;
  1272. touch.last = now;
  1273. setTimeout(longTap, longTapDelay);
  1274. }).bind('touchmove', function(e){
  1275. touch.x2 = e.touches[0].pageX;
  1276. touch.y2 = e.touches[0].pageY;
  1277. }).bind('touchend', function(e){
  1278. if (touch.isDoubleTap) {
  1279. $(touch.target).trigger('doubleTap');
  1280. touch = {};
  1281. } else if (touch.x2 > 0 || touch.y2 > 0) {
  1282. (Math.abs(touch.x1 - touch.x2) > 30 || Math.abs(touch.y1 - touch.y2) > 30) &&
  1283. $(touch.target).trigger('swipe') &&
  1284. $(touch.target).trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)));
  1285. touch.x1 = touch.x2 = touch.y1 = touch.y2 = touch.last = 0;
  1286. } else if ('last' in touch) {
  1287. touchTimeout = setTimeout(function(){
  1288. touchTimeout = null;
  1289. $(touch.target).trigger('tap')
  1290. touch = {};
  1291. }, 250);
  1292. }
  1293. }).bind('touchcancel', function(){ touch = {} });
  1294. });
  1295. ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'longTap'].forEach(function(m){
  1296. $.fn[m] = function(callback){ return this.bind(m, callback) }
  1297. });
  1298. })(Zepto);
  1299. (function ($) {
  1300. var defaults = {
  1301. duration: 400,
  1302. easing: ''
  1303. },
  1304. vendor = (/webkit/i).test(navigator.appVersion) ? 'webkit' : 'moz',
  1305. prefix = '-' + vendor + '-',
  1306. vendorNames = n = {
  1307. transition: prefix + 'transition',
  1308. transform: prefix + 'transform',
  1309. transitionEnd: vendor + 'TransitionEnd'
  1310. },
  1311. transformTypes = [
  1312. 'scale', 'scaleX', 'scaleY', 'scale3d',
  1313. 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'rotate3d',
  1314. 'translate', 'translateX', 'translateY', 'translateZ', 'translate3d',
  1315. 'skew', 'skewX', 'skewY',
  1316. 'matrix', 'matrix3d', 'perspective'
  1317. ];
  1318. // Implement Array.prototype.indexOf if it's not. This is
  1319. // mainly Internet Explorer.
  1320. if (!Array.prototype.indexOf) {
  1321. Array.prototype.indexOf = function(obj, start) {
  1322. for (var i = (start || 0), j = this.length; i < j; i++) {
  1323. if (this[i] === obj) return i;
  1324. }
  1325. return -1;
  1326. }
  1327. }
  1328. // Helper function for easily adding transforms.
  1329. $.fn.transform = function (properties) {
  1330. var transforms = [];
  1331. for (var key in properties) {
  1332. if (transformTypes.indexOf(key) !== -1) {
  1333. transforms.push(key + '(' + properties[key] + ')');
  1334. delete properties[key];
  1335. }
  1336. }
  1337. if (transforms.length)
  1338. properties[n.transform] = transforms.join(' ');
  1339. return $(this).css(properties);
  1340. };
  1341. // Effects
  1342. $.fn.gfxPopIn = function (options, cb) {
  1343. var $that = $(this),
  1344. opts = $.extend({}, defaults, options || {});
  1345. opts.scale = opts.scale || 0.2;
  1346. $that.transform({
  1347. '-webkit-transform-origin': '50% 50%',
  1348. '-moz-transform-origin': '50% 50%',
  1349. scale: opts.scale,
  1350. opacity: '0',
  1351. display: 'block'
  1352. }).animate({scale: '1', opacity: '1'},
  1353. opts.duration, opts.easing, cb);
  1354. };
  1355. $.fn.gfxPopOut = function (options, cb) {
  1356. var $that = $(this),
  1357. opts = $.extend({}, defaults, options || {});
  1358. opts.scale = opts.scale || 0.2;
  1359. $that.transform({
  1360. '-webkit-transform-origin': '50% 50%',
  1361. '-moz-transform-origin': '50% 50%',
  1362. scale: '1',
  1363. opacity: '1'
  1364. }).animate({opacity: 0, scale: opts.scale},
  1365. opts.duration, opts.easing, function () {
  1366. $that.transform({display: 'none',
  1367. opacity: 1, scale: 1});
  1368. cb && cb();
  1369. });
  1370. };
  1371. $.fn.gfxFadeIn = function (options, cb) {
  1372. var $that = $(this),
  1373. opts = $.extend({}, defaults, options || {});
  1374. opts.duration = opts.duration || 1000;
  1375. $that.css({opacity: 0}).show().animate({opacity: 1}, opts.duration,
  1376. opts.easing, cb);
  1377. };
  1378. $.fn.gfxFadeOut = function (options, cb) {
  1379. var $that = $(this),
  1380. opts = $.extend({}, defaults, options || {});
  1381. $that.css({opacity: 1}).animate({opacity: 0}, opts.duration,
  1382. opts.easing, function () {
  1383. $that.hide().css({opacity: 1});
  1384. cb && cb();
  1385. });
  1386. };
  1387. $.fn.gfxShake = function (options, cb) {
  1388. var $that = $(this),
  1389. opts = $.extend({}, defaults, options || {}),
  1390. distance,
  1391. third = function () {
  1392. $that.animate({translateX: distance + 'px'},
  1393. opts.duration, opts.easing, function () {
  1394. $that.transform({translateX: 0});
  1395. cb && cb();
  1396. });
  1397. },
  1398. second = function () {
  1399. $that.animate({translateX: '-' + distance + 'px'},
  1400. opts.duration, opts.easing, third);
  1401. },
  1402. first = function () {
  1403. $that.animate({translateX: distance + 'px'},
  1404. opts.duration, opts.easing, second);
  1405. };
  1406. opts.duration = opts.duration || 100;
  1407. opts.easing = opts.easing || 'ease-out';
  1408. distance = opts.distance || 20;
  1409. $that.animate({translateX: '-' + distance + 'px'},
  1410. opts.duration, opts.easing, first);
  1411. };
  1412. $.fn.gfxBlip = function (options, cb) {
  1413. var $that = $(this),
  1414. opts = $.extend({}, defaults, options || {}),
  1415. first = function () {
  1416. $that.animate({scale: 1}, opts.duration,
  1417. opts.easing, cb);
  1418. };
  1419. opts.scale = opts.scale || 1.15;
  1420. $that.animate({scale: opts.scale}, opts.duration,
  1421. opts.easing, first);
  1422. };
  1423. $.fn.gfxExplodeIn = function (options, cb) {
  1424. var $that = $(this),
  1425. opts = $.extend({}, defaults, options || {});
  1426. opts.scale = opts.scale || 3;
  1427. $that.transform({scale: opts.scale, opacity: 0,
  1428. display: 'block'}).animate({scale: 1, opacity: 1},
  1429. opts.duration, opts.easing, cb);
  1430. };
  1431. $.fn.gfxExplodeOut = function (options, cb) {
  1432. var $that = $(this),
  1433. opts = $.extend({}, defaults, options || {}),
  1434. c = cb,
  1435. first = function() {
  1436. $that.transform({scale: 1, opacity: 1, display: 'none'});
  1437. cb && cb();
  1438. };
  1439. if (opts.reset) c = second;
  1440. opts.scale = opts.scale || 3;
  1441. $that.transform({scale: 1, opacity: 1})
  1442. .animate({scale: opts.scale, opacity: 0}, opts.duration,
  1443. opts.easing, c);
  1444. };
  1445. $.fn.gfxFlipIn = function (options, cb) {
  1446. var $that = $(this),
  1447. opts = $.extend({}, defaults, options || {});
  1448. $that.transform({rotateY: '180deg', scale: 0.8,
  1449. display: 'block'}).animate({rotateY: 0, scale: 1},
  1450. opts.duration, opts.easing, cb);
  1451. };
  1452. $.fn.gfxFlipOut = function (options, cb) {
  1453. var $that = $(this),
  1454. opts = $.extend({}, defaults, options || {}),
  1455. c = cb,
  1456. first = function () {
  1457. $that.transform({scale: 1, rotateY: 0, display: 'none'});
  1458. cb && cb();
  1459. };
  1460. if (opts.reset) c = first;
  1461. $that.transform({rotateY: 0, scale: 1})
  1462. .animate({rotateY: '-180deg', scale: 0.8}, opts.duration,
  1463. opts.easing, c);
  1464. };
  1465. $.fn.gfxRotateOut = function (options, cb) {
  1466. var $that = $(this),
  1467. opts = $.extend({}, defaults, options || {}),
  1468. c = cb
  1469. first = function () {
  1470. $that.transform({rotateY: 0, display: 'none'});
  1471. cb && cb();
  1472. };
  1473. if (opts.reset) c = first;
  1474. $that.transform({rotateY: 0}).animate({rotateY: '-180deg'},
  1475. opts.duration, opts.easing, c);
  1476. };
  1477. $.fn.gfxRotateIn = function (options, cb) {
  1478. var $that = $(this),
  1479. opts = $.extend({}, defaults, options || {});
  1480. $that.transform({rotateY: '180deg', display: 'block'})
  1481. .animate({rotateY: 0}, opts.duration, opts.easing, cb);
  1482. };
  1483. $.fn.gfxSlideOut = function (options, cb) {
  1484. var $that = $(this),
  1485. opts = $.extend({}, defaults, options || {}),
  1486. distance,
  1487. opacity;
  1488. opts.direction = opts.direction || 'right';
  1489. distance = opts.distance || 100;
  1490. if (opts.direction === 'left') distance *= -1;
  1491. distance += '%';
  1492. opacity = opts.fade ? 0 : 1;
  1493. $that.show().animate({translate3d: distance + ',0,0',
  1494. opacity: opacity}, opts.duration, opts.easing, function () {
  1495. $that.transform({translate3d: '0,0,0', opacity: 1}).hide();
  1496. cb && cb();
  1497. });
  1498. };
  1499. $.fn.gfxSlideIn = function (options, cb) {
  1500. var $that = $(this),
  1501. opts = $.extend({}, defaults, options || {}),
  1502. distance,
  1503. opacity;
  1504. opts.direction = opts.direction || 'right';
  1505. distance = opts.distance || 100;
  1506. if (opts.direction === 'left') distance *= -1;
  1507. distance += '%';
  1508. opacity = opts.fade ? 0 : 1;
  1509. $that.transform({translate3d: distance + ',0,0',
  1510. opacity: opacity}).show().animate({translate3d: '0,0,0',
  1511. opacity: 1}, opts.duration, opts.easing, cb);
  1512. };
  1513. })(Zepto);
  1514. // Underscore.js 1.2.2
  1515. // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
  1516. // Underscore is freely distributable under the MIT license.
  1517. // Portions of Underscore are inspired or borrowed from Prototype,
  1518. // Oliver Steele's Functional, and John Resig's Micro-Templating.
  1519. // For all details and documentation:
  1520. // http://documentcloud.github.com/underscore
  1521. (function() {
  1522. // Baseline setup
  1523. // --------------
  1524. // Establish the root object, `window` in the browser, or `global` on the server.
  1525. var root = this;
  1526. // Save the previous value of the `_` variable.
  1527. var previousUnderscore = root._;
  1528. // Establish the object that gets returned to break out of a loop iteration.
  1529. var breaker = {};
  1530. // Save bytes in the minified (but not gzipped) version:
  1531. var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
  1532. // Create quick reference variables for speed access to core prototypes.
  1533. var concat = ArrayProto.concat,
  1534. push = ArrayProto.push,
  1535. slice = ArrayProto.slice,
  1536. toString = ObjProto.toString,
  1537. hasOwnProperty = ObjProto.hasOwnProperty;
  1538. // All **ECMAScript 5** native function implementations that we hope to use
  1539. // are declared here.
  1540. var
  1541. nativeForEach = ArrayProto.forEach,
  1542. nativeMap = ArrayProto.map,
  1543. nativeReduce = ArrayProto.reduce,
  1544. nativeReduceRight = ArrayProto.reduceRight,
  1545. nativeFilter = ArrayProto.filter,
  1546. nativeEvery = ArrayProto.every,
  1547. nativeSome = ArrayProto.some,
  1548. nativeIndexOf = ArrayProto.indexOf,
  1549. nativeLastIndexOf = ArrayProto.lastIndexOf,
  1550. nativeIsArray = Array.isArray,
  1551. nativeKeys = Object.keys,
  1552. nativeBind = FuncProto.bind;
  1553. // Create a safe reference to the Underscore object for use below.
  1554. var _ = function(obj) { return new wrapper(obj); };
  1555. // Export the Underscore object for **Node.js** and **"CommonJS"**, with
  1556. // backwards-compatibility for the old `require()` API. If we're not in
  1557. // CommonJS, add `_` to the global object.
  1558. if (typeof exports !== 'undefined') {
  1559. if (typeof module !== 'undefined' && module.exports) {
  1560. exports = module.exports = _;
  1561. }
  1562. exports._ = _;
  1563. } else if (typeof define === 'function' && define.amd) {
  1564. // Register as a named module with AMD.
  1565. define('underscore', function() {
  1566. return _;
  1567. });
  1568. } else {
  1569. // Exported as a string, for Closure Compiler "advanced" mode.
  1570. root['_'] = _;
  1571. }
  1572. // Current version.
  1573. _.VERSION = '1.2.2';
  1574. // Collection Functions
  1575. // --------------------
  1576. // The cornerstone, an `each` implementation, aka `forEach`.
  1577. // Handles objects with the built-in `forEach`, arrays, and raw objects.
  1578. // Delegates to **ECMAScript 5**'s native `forEach` if available.
  1579. var each = _.each = _.forEach = function(obj, iterator, context) {
  1580. if (obj == null) return;
  1581. if (nativeForEach && obj.forEach === nativeForEach) {
  1582. obj.forEach(iterator, context);
  1583. } else {
  1584. var fn = iterator;
  1585. var i = -1;
  1586. var l = obj.length;
  1587. // We optimize for common use by only binding a context when it's passed.
  1588. if (context) {
  1589. iterator = function() { return fn.call(context, obj[i], i, obj); };
  1590. }
  1591. // If we're dealing with an array or array-like object...
  1592. if (l === l >>> 0) {
  1593. while (++i < l) {
  1594. if (i in obj && iterator(obj[i], i, obj) == breaker) return;
  1595. }
  1596. } else {
  1597. forProps(obj, iterator, true);
  1598. }
  1599. }
  1600. };
  1601. // A simple each, for dealing with non-sparse arrays and arguments objects.
  1602. var simpleEach = function(obj, iterator, index) {
  1603. index || (index = 0);
  1604. for (var l = obj.length; index < l; index++) {
  1605. iterator(obj[index]);
  1606. }
  1607. };
  1608. // List of possible shadowed properties on Object.prototype.
  1609. var shadowed = [
  1610. 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
  1611. 'toLocaleString', 'toString', 'valueOf'
  1612. ];
  1613. // IE < 9 makes properties, shadowing non-enumerable ones, non-enumerable too.
  1614. var forShadowed = !{valueOf:0}.propertyIsEnumerable('valueOf') &&
  1615. function(obj, iterator) {
  1616. // Because IE < 9 can't set the `[[Enumerable]]` attribute of an existing
  1617. // property and the `constructor` property of a prototype defaults to
  1618. // non-enumerable, we manually skip the `constructor` property when we
  1619. // think we are iterating over a `prototype` object.
  1620. var ctor = obj.constructor;
  1621. var skipCtor = ctor && ctor.prototype && ctor.prototype.constructor === ctor;
  1622. for (var key, i = 0; key = shadowed[i]; i++) {
  1623. if (!(skipCtor && key == 'constructor') &&
  1624. hasOwnProperty.call(obj, key) &&
  1625. iterator(obj[key], key, obj) === breaker) {
  1626. break;
  1627. }
  1628. }
  1629. };
  1630. // Iterates over an object's properties, executing the `callback` for each.
  1631. var forProps = function(obj, iterator, ownOnly) {
  1632. var done = !obj;
  1633. var skipProto = typeof obj == 'function';
  1634. for (var key in obj) {
  1635. // Firefox < 3.6, Opera > 9.50 - Opera < 12, and Safari < 5.1
  1636. // (if the prototype or a property on the prototype has been set)
  1637. // incorrectly set a function's `prototype` property [[Enumerable]] value
  1638. // to true. Because of this we standardize on skipping the the `prototype`
  1639. // property of functions regardless of their [[Enumerable]] value.
  1640. if (done =
  1641. !(skipProto && key == 'prototype') &&
  1642. (!ownOnly || ownOnly && hasOwnProperty.call(obj, key)) &&
  1643. iterator(obj[key], key, obj) === breaker) {
  1644. break;
  1645. }
  1646. }
  1647. if (!done && forShadowed) {
  1648. forShadowed(obj, iterator);
  1649. }
  1650. };
  1651. // Return the results of applying the iterator to each element.
  1652. // Delegates to **ECMAScript 5**'s native `map` if available.
  1653. _.map = function(obj, iterator, context) {
  1654. var results = [];
  1655. if (obj == null) return results;
  1656. if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
  1657. each(obj, function(value, index, list) {
  1658. results[results.length] = iterator.call(context, value, index, list);
  1659. });
  1660. return results;
  1661. };
  1662. // **Reduce** builds up a single result from a list of values, aka `inject`,
  1663. // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
  1664. _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
  1665. var initial = arguments.length > 2;
  1666. if (obj == null) obj = [];
  1667. if (nativeReduce && obj.reduce === nativeReduce) {
  1668. if (context) iterator = _.bind(iterator, context);
  1669. return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
  1670. }
  1671. each(obj, function(value, index, list) {
  1672. if (!initial) {
  1673. memo = value;
  1674. initial = true;
  1675. } else {
  1676. memo = iterator.call(context, memo, value, index, list);
  1677. }
  1678. });
  1679. if (!initial) throw new TypeError('Reduce of empty array with no initial value');
  1680. return memo;
  1681. };
  1682. // The right-associative version of reduce, also known as `foldr`.
  1683. // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
  1684. _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
  1685. var initial = arguments.length > 2;
  1686. if (obj == null) obj = [];
  1687. if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
  1688. if (context) iterator = _.bind(iterator, context);
  1689. return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
  1690. }
  1691. var reversed = _.toArray(obj).reverse();
  1692. if (context && !initial) iterator = _.bind(iterator, context);
  1693. return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
  1694. };
  1695. // Return the first value which passes a truth test. Aliased as `detect`.
  1696. _.find = _.detect = function(obj, iterator, context) {
  1697. var result;
  1698. any(obj, function(value, index, list) {
  1699. if (iterator.call(context, value, index, list)) {
  1700. result = value;
  1701. return true;
  1702. }
  1703. });
  1704. return result;
  1705. };
  1706. // Return all the elements that pass a truth test.
  1707. // Delegates to **ECMAScript 5**'s native `filter` if available.
  1708. // Aliased as `select`.
  1709. _.filter = _.select = function(obj, iterator, context) {
  1710. var results = [];
  1711. if (obj == null) return results;
  1712. if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
  1713. each(obj, function(value, index, list) {
  1714. if (iterator.call(context, value, index, list)) results[results.length] = value;
  1715. });
  1716. return results;
  1717. };
  1718. // Return all the elements for which a truth test fails.
  1719. _.reject = function(obj, iterator, context) {
  1720. var results = [];
  1721. if (obj == null) return results;
  1722. each(obj, function(value, index, list) {
  1723. if (!iterator.call(context, value, index, list)) results[results.length] = value;
  1724. });
  1725. return results;
  1726. };
  1727. // Determine whether all of the elements match a truth test.
  1728. // Delegates to **ECMAScript 5**'s native `every` if available.
  1729. // Aliased as `all`.
  1730. _.every = _.all = function(obj, iterator, context) {
  1731. var result = true;
  1732. if (obj == null) return result;
  1733. if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
  1734. each(obj, function(value, index, list) {
  1735. if (!(result = result && iterator.call(context, value, index, list))) return breaker;
  1736. });
  1737. return result;
  1738. };
  1739. // Determine if at least one element in the object matches a truth test.
  1740. // Delegates to **ECMAScript 5**'s native `some` if available.
  1741. // Aliased as `any`.
  1742. var any = _.some = _.any = function(obj, iterator, context) {
  1743. iterator || (iterator = _.identity);
  1744. var result = false;
  1745. if (obj == null) return result;
  1746. if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
  1747. each(obj, function(value, index, list) {
  1748. if (result || (result = iterator.call(context, value, index, list))) return breaker;
  1749. });
  1750. return !!result;
  1751. };
  1752. // Determine if a given value is included in the array or object using `===`.
  1753. // Aliased as `contains`.
  1754. _.include = _.contains = function(obj, target) {
  1755. var found = false;
  1756. if (obj == null) return found;
  1757. if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
  1758. found = any(obj, function(value) {
  1759. return value === target;
  1760. });
  1761. return found;
  1762. };
  1763. // Invoke a method (with arguments) on every item in a collection.
  1764. _.invoke = function(obj, method) {
  1765. var args = slice.call(arguments, 2);
  1766. return _.map(obj, function(value) {
  1767. return (method.call ? method || value : value[method]).apply(value, args);
  1768. });
  1769. };
  1770. // Convenience version of a common use case of `map`: fetching a property.
  1771. _.pluck = function(obj, key) {
  1772. return _.map(obj, function(value){ return value[key]; });
  1773. };
  1774. // Return the maximum element or (element-based computation).
  1775. _.max = function(obj, iterator, context) {
  1776. if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
  1777. if (!iterator && _.isEmpty(obj)) return -Infinity;
  1778. var result = {computed : -Infinity};
  1779. each(obj, function(value, index, list) {
  1780. var computed = iterator ? iterator.call(context, value, index, list) : value;
  1781. computed >= result.computed && (result = {value : value, computed : computed});
  1782. });
  1783. return result.value;
  1784. };
  1785. // Return the minimum element (or element-based computation).
  1786. _.min = function(obj, iterator, context) {
  1787. if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
  1788. if (!iterator && _.isEmpty(obj)) return Infinity;
  1789. var result = {computed : Infinity};
  1790. each(obj, function(value, index, list) {
  1791. var computed = iterator ? iterator.call(context, value, index, list) : value;
  1792. computed < result.computed && (result = {value : value, computed : computed});
  1793. });
  1794. return result.value;
  1795. };
  1796. // Shuffle an array.
  1797. _.shuffle = function(obj) {
  1798. var shuffled = [], rand;
  1799. each(obj, function(value, index, list) {
  1800. if (index == 0) {
  1801. shuffled[0] = value;
  1802. } else {
  1803. rand = Math.floor(Math.random() * (index + 1));
  1804. shuffled[index] = shuffled[rand];
  1805. shuffled[rand] = value;
  1806. }
  1807. });
  1808. return shuffled;
  1809. };
  1810. // Sort the object's values by a criterion produced by an iterator.
  1811. _.sortBy = function(obj, iterator, context) {
  1812. return _.pluck(_.map(obj, function(value, index, list) {
  1813. return {
  1814. value : value,
  1815. criteria : iterator.call(context, value, index, list)
  1816. };
  1817. }).sort(function(left, right) {
  1818. var a = left.criteria;
  1819. var b = right.criteria;
  1820. return a < b ? -1 : a > b ? 1 : 0;
  1821. }), 'value');
  1822. };
  1823. // Groups the object's values by a criterion. Pass either a string attribute
  1824. // to group by, or a function that returns the criterion.
  1825. _.groupBy = function(obj, val) {
  1826. var result = {};
  1827. var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
  1828. each(obj, function(value, index) {
  1829. var key = iterator(value, index);
  1830. (result[key] || (result[key] = [])).push(value);
  1831. });
  1832. return result;
  1833. };
  1834. // Use a comparator function to figure out at what index an object should
  1835. // be inserted so as to maintain order. Uses binary search.
  1836. _.sortedIndex = function(array, obj, iterator) {
  1837. iterator || (iterator = _.identity);
  1838. var low = 0;
  1839. var high = array.length;
  1840. while (low < high) {
  1841. var mid = (low + high) >> 1;
  1842. iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
  1843. }
  1844. return low;
  1845. };
  1846. // Safely convert anything iterable into a real, live array.
  1847. _.toArray = function(iterable) {
  1848. if (!iterable) return [];
  1849. if (iterable.toArray) return iterable.toArray();
  1850. if (_.isArray(iterable) || _.isArguments(iterable)) return slice.call(iterable);
  1851. return _.values(iterable);
  1852. };
  1853. // Return the number of elements in an object.
  1854. _.size = function(obj) {
  1855. return _.toArray(obj).length;
  1856. };
  1857. // Array Functions
  1858. // ---------------
  1859. // Get the first element of an array. Passing **n** will return the first N
  1860. // values in the array. Aliased as `head`. The **guard** check allows it to work
  1861. // with `_.map`.
  1862. _.first = _.head = function(array, n, guard) {
  1863. return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
  1864. };
  1865. // Returns everything but the last entry of the array. Especially useful on
  1866. // the arguments object. Passing **n** will return all the values in
  1867. // the array, excluding the last N. The **guard** check allows it to work with
  1868. // `_.map`.
  1869. _.initial = function(array, n, guard) {
  1870. return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
  1871. };
  1872. // Get the last element of an array. Passing **n** will return the last N
  1873. // values in the array. The **guard** check allows it to work with `_.map`.
  1874. _.last = function(array, n, guard) {
  1875. if ((n != null) && !guard) {
  1876. return slice.call(array, Math.max(array.length - n, 0));
  1877. } else {
  1878. return array[array.length - 1];
  1879. }
  1880. };
  1881. // Returns everything but the first entry of the array. Aliased as `tail`.
  1882. // Especially useful on the arguments object. Passing an **index** will return
  1883. // the rest of the values in the array from that index onward. The **guard**
  1884. // check allows it to work with `_.map`.
  1885. _.rest = _.tail = function(array, index, guard) {
  1886. return slice.call(array, (index == null) || guard ? 1 : index);
  1887. };
  1888. // Trim out all falsy values from an array.
  1889. _.compact = function(array) {
  1890. return _.filter(array, function(value){ return !!value; });
  1891. };
  1892. // Return a completely flattened version of an array.
  1893. _.flatten = function(array, shallow) {
  1894. return _.reduce(array, function(memo, value) {
  1895. if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
  1896. memo[memo.length] = value;
  1897. return memo;
  1898. }, []);
  1899. };
  1900. // Return a version of the array that does not contain the specified value(s).
  1901. _.without = function(array) {
  1902. return _.difference(array, slice.call(arguments, 1));
  1903. };
  1904. // Produce a duplicate-free version of the array. If the array has already
  1905. // been sorted, you have the option of using a faster algorithm.
  1906. // Aliased as `unique`.
  1907. _.uniq = _.unique = function(array, isSorted, iterator) {
  1908. var initial = iterator ? _.map(array, iterator) : array;
  1909. var result = [];
  1910. _.reduce(initial, function(memo, el, i) {
  1911. if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
  1912. memo[memo.length] = el;
  1913. result[result.length] = array[i];
  1914. }
  1915. return memo;
  1916. }, []);
  1917. return result;
  1918. };
  1919. // Produce an array that contains the union: each distinct element from all of
  1920. // the passed-in arrays.
  1921. _.union = function() {
  1922. return _.uniq(_.flatten(arguments, true));
  1923. };
  1924. // Produce an array that contains every item shared between all the
  1925. // passed-in arrays. (Aliased as "intersect" for back-compat.)
  1926. _.intersection = _.intersect = function(array) {
  1927. var rest = slice.call(arguments, 1);
  1928. return _.filter(_.uniq(array), function(item) {
  1929. return _.every(rest, function(other) {
  1930. return _.indexOf(other, item) >= 0;
  1931. });
  1932. });
  1933. };
  1934. // Take the difference between one array and a number of other arrays.
  1935. // Only the elements present in just the first array will remain.
  1936. _.difference = function(array) {
  1937. var rest = _.flatten(slice.call(arguments, 1));
  1938. return _.filter(array, function(value){ return !_.include(rest, value); });
  1939. };
  1940. // Take the symmetric difference between a list of arrays. Only the elements
  1941. // present in one of the input arrays will remain.
  1942. _.symDifference = function() {
  1943. return _.reduce(arguments, function(memo, array) {
  1944. return _.union(_.difference(memo, array), _.difference(array, memo));
  1945. });
  1946. };
  1947. // Zip together multiple lists into a single array -- elements that share
  1948. // an index go together.
  1949. _.zip = function() {
  1950. var length = _.max(_.pluck(arguments, 'length'));
  1951. var results = new Array(length);
  1952. for (var i = 0; i < length; i++) results[i] = _.pluck(arguments, i);
  1953. return results;
  1954. };
  1955. // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
  1956. // we need this function. Return the position of the first occurrence of an
  1957. // item in an array, or -1 if the item is not included in the array.
  1958. // Delegates to **ECMAScript 5**'s native `indexOf` if available.
  1959. // If the array is large and already in sort order, pass `true`
  1960. // for **isSorted** to use binary search.
  1961. _.indexOf = function(array, item, isSorted) {
  1962. if (array == null) return -1;
  1963. var i, l;
  1964. if (isSorted) {
  1965. i = _.sortedIndex(array, item);
  1966. return array[i] === item ? i : -1;
  1967. }
  1968. if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
  1969. for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
  1970. return -1;
  1971. };
  1972. // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
  1973. _.lastIndexOf = function(array, item) {
  1974. if (array == null) return -1;
  1975. if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
  1976. var i = array.length;
  1977. while (i--) if (i in array && array[i] === item) return i;
  1978. return -1;
  1979. };
  1980. // Generate an integer Array containing an arithmetic progression. A port of
  1981. // the native Python `range()` function. See
  1982. // [the Python documentation](http://docs.python.org/library/functions.html#range).
  1983. _.range = function(start, stop, step) {
  1984. if (arguments.length <= 1) {
  1985. stop = start || 0;
  1986. start = 0;
  1987. }
  1988. step = arguments[2] || 1;
  1989. var len = Math.max(Math.ceil((stop - start) / step), 0);
  1990. var idx = 0;
  1991. var range = new Array(len);
  1992. while(idx < len) {
  1993. range[idx++] = start;
  1994. start += step;
  1995. }
  1996. return range;
  1997. };
  1998. // Function (ahem) Functions
  1999. // ------------------
  2000. // Reusable constructor function for prototype setting.
  2001. var ctor = function(){};
  2002. // Create a function bound to a given object (assigning `this`, and arguments,
  2003. // optionally). Binding with arguments is also known as `curry`.
  2004. // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
  2005. // We check for `func.bind` first, to fail fast when `func` is undefined.
  2006. _.bind = function bind(func, context) {
  2007. var bound, args;
  2008. if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
  2009. if (!_.isFunction(func)) throw new TypeError;
  2010. args = slice.call(arguments, 2);
  2011. return bound = function() {
  2012. if (!(this instanceof bound)) return func.apply(context, concat.apply(args, arguments));
  2013. ctor.prototype = func.prototype;
  2014. var self = new ctor;
  2015. var result = func.apply(self, concat.apply(args, arguments));
  2016. if (Object(result) === result) return result;
  2017. return self;
  2018. };
  2019. };
  2020. // Bind all of an object's methods to that object. Useful for ensuring that
  2021. // all callbacks defined on an object belong to it.
  2022. _.bindAll = function(obj) {
  2023. var i = 1;
  2024. var funcs = arguments;
  2025. if (funcs.length < 2) {
  2026. i = 0;
  2027. funcs = _.functions(obj);
  2028. }
  2029. simpleEach(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }, i);
  2030. return obj;
  2031. };
  2032. // Memoize an expensive function by storing its results.
  2033. _.memoize = function(func, hasher) {
  2034. hasher || (hasher = _.identity);
  2035. var memo = {};
  2036. return function() {
  2037. var key = hasher.apply(this, arguments);
  2038. return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
  2039. };
  2040. };
  2041. // Delays a function for the given number of milliseconds, and then calls
  2042. // it with the arguments supplied.
  2043. _.delay = function(func, wait) {
  2044. var args = slice.call(arguments, 2);
  2045. return setTimeout(function(){ return func.apply(func, args); }, wait);
  2046. };
  2047. // Defers a function, scheduling it to run after the current call stack has
  2048. // cleared.
  2049. _.defer = function(func) {
  2050. return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
  2051. };
  2052. // Returns a function, that, when invoked, will only be triggered at most once
  2053. // during a given window of time.
  2054. _.throttle = function(func, wait) {
  2055. var context, args, timeout, throttling, more;
  2056. var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
  2057. return function() {
  2058. context = this; args = arguments;
  2059. var later = function() {
  2060. timeout = null;
  2061. if (more) func.apply(context, args);
  2062. whenDone();
  2063. };
  2064. if (!timeout) timeout = setTimeout(later, wait);
  2065. if (throttling) {
  2066. more = true;
  2067. } else {
  2068. func.apply(context, args);
  2069. }
  2070. whenDone();
  2071. throttling = true;
  2072. };
  2073. };
  2074. // Returns a function, that, as long as it continues to be invoked, will not
  2075. // be triggered. The function will be called after it stops being called for
  2076. // N milliseconds.
  2077. _.debounce = function(func, wait) {
  2078. var timeout;
  2079. return function() {
  2080. var context = this, args = arguments;
  2081. var later = function() {
  2082. timeout = null;
  2083. func.apply(context, args);
  2084. };
  2085. clearTimeout(timeout);
  2086. timeout = setTimeout(later, wait);
  2087. };
  2088. };
  2089. // Returns a function that will be executed at most one time, no matter how
  2090. // often you call it. Useful for lazy initialization.
  2091. _.once = function(func) {
  2092. var ran = false, memo;
  2093. return function() {
  2094. if (ran) return memo;
  2095. ran = true;
  2096. return memo = func.apply(this, arguments);
  2097. };
  2098. };
  2099. // Returns the first function passed as an argument to the second,
  2100. // allowing you to adjust arguments, run code before and after, and
  2101. // conditionally execute the original function.
  2102. _.wrap = function(func, wrapper) {
  2103. return function() {
  2104. var args = concat.apply([func], arguments);
  2105. return wrapper.apply(this, args);
  2106. };
  2107. };
  2108. // Returns a function that is the composition of a list of functions, each
  2109. // consuming the return value of the function that follows.
  2110. _.compose = function() {
  2111. var funcs = arguments;
  2112. return function() {
  2113. var args = arguments;
  2114. for (var i = funcs.length - 1; i >= 0; i--) {
  2115. args = [funcs[i].apply(this, args)];
  2116. }
  2117. return args[0];
  2118. };
  2119. };
  2120. // Returns a function that will only be executed after being called N times.
  2121. _.after = function(times, func) {
  2122. if (times <= 0) return func();
  2123. return function() {
  2124. if (--times < 1) { return func.apply(this, arguments); }
  2125. };
  2126. };
  2127. // Object Functions
  2128. // ----------------
  2129. // Retrieve the names of an object's properties.
  2130. // Delegates to **ECMAScript 5**'s native `Object.keys`
  2131. _.keys = nativeKeys || function(obj) {
  2132. if (obj !== Object(obj)) throw new TypeError('Invalid object');
  2133. var keys = [];
  2134. forProps(obj, function(value, key) {
  2135. keys[keys.length] = key;
  2136. }, true);
  2137. return keys;
  2138. };
  2139. // Retrieve the values of an object's properties.
  2140. _.values = function(obj) {
  2141. return _.map(obj, _.identity);
  2142. };
  2143. // Return a sorted list of the function names available on the object.
  2144. // Aliased as `methods`
  2145. _.functions = _.methods = function(obj) {
  2146. var names = [];
  2147. forProps(obj, function(value, key) {
  2148. if (_.isFunction(value)) names[names.length] = key;
  2149. });
  2150. return names.sort();
  2151. };
  2152. // Extend a given object with all the properties in passed-in object(s).
  2153. _.extend = function(obj) {
  2154. simpleEach(arguments, function(source) {
  2155. forProps(source, function(value, key) {
  2156. if (value !== void 0) obj[key] = value;
  2157. });
  2158. }, 1);
  2159. return obj;
  2160. };
  2161. // Fill in a given object with default properties.
  2162. _.defaults = function(obj) {
  2163. simpleEach(arguments, function(source) {
  2164. forProps(source, function(value, key) {
  2165. if (obj[key] == null) obj[key] = value;
  2166. });
  2167. }, 1);
  2168. return obj;
  2169. };
  2170. // Create a (shallow-cloned) duplicate of an object.
  2171. _.clone = function(obj) {
  2172. if (!_.isObject(obj)) return obj;
  2173. return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
  2174. };
  2175. // Invokes interceptor with the obj, and then returns obj.
  2176. // The primary purpose of this method is to "tap into" a method chain, in
  2177. // order to perform operations on intermediate results within the chain.
  2178. _.tap = function(obj, interceptor) {
  2179. interceptor(obj);
  2180. return obj;
  2181. };
  2182. // Internal recursive comparison function.
  2183. function eq(a, b, stack) {
  2184. // Identical objects are equal. `0 === -0`, but they aren't identical.
  2185. // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
  2186. if (a === b) return a !== 0 || 1 / a == 1 / b;
  2187. // A strict comparison is necessary because `null == undefined`.
  2188. if (a == null || b == null) return a === b;
  2189. // Unwrap any wrapped objects.
  2190. if (a._chain) a = a._wrapped;
  2191. if (b._chain) b = b._wrapped;
  2192. // Invoke a custom `isEqual` method if one is provided.
  2193. if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
  2194. if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
  2195. // Compare `[[Class]]` names.
  2196. var className = toString.call(a);
  2197. if (className != toString.call(b)) return false;
  2198. switch (className) {
  2199. // Strings, numbers, dates, and booleans are compared by value.
  2200. case '[object String]':
  2201. // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
  2202. // equivalent to `new String("5")`.
  2203. return a == String(b);
  2204. case '[object Number]':
  2205. // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
  2206. // other numeric values.
  2207. return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
  2208. case '[object Date]':
  2209. case '[object Boolean]':
  2210. // Coerce dates and booleans to numeric primitive values. Dates are compared by their
  2211. // millisecond representations. Note that invalid dates with millisecond representations
  2212. // of `NaN` are not equivalent.
  2213. return +a == +b;
  2214. // RegExps are compared by their source patterns and flags.
  2215. case '[object RegExp]':
  2216. return a.source == b.source &&
  2217. a.global == b.global &&
  2218. a.multiline == b.multiline &&
  2219. a.ignoreCase == b.ignoreCase;
  2220. }
  2221. if (typeof a != 'object' || typeof b != 'object') return false;
  2222. // Assume equality for cyclic structures. The algorithm for detecting cyclic
  2223. // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
  2224. var length = stack.length;
  2225. while (length--) {
  2226. // Linear search. Performance is inversely proportional to the number of
  2227. // unique nested structures.
  2228. if (stack[length] == a) return true;
  2229. }
  2230. // Add the first object to the stack of traversed objects.
  2231. stack.push(a);
  2232. var size = 0, result = true;
  2233. // Recursively compare objects and arrays.
  2234. if (className == '[object Array]') {
  2235. // Compare array lengths to determine if a deep comparison is necessary.
  2236. size = a.length;
  2237. result = size == b.length;
  2238. if (result) {
  2239. // Deep compare the contents, ignoring non-numeric properties.
  2240. while (size--) {
  2241. // Ensure commutative equality for sparse arrays.
  2242. if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
  2243. }
  2244. }
  2245. } else {
  2246. // Objects with different constructors are not equivalent.
  2247. if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
  2248. // Deep compare objects.
  2249. forProps(a, function(value, key) {
  2250. // Count the expected number of properties.
  2251. size++;
  2252. // Deep compare each member.
  2253. if (!(result = hasOwnProperty.call(b, key) && eq(value, b[key], stack))) return breaker;
  2254. }, true);
  2255. // Ensure that both objects contain the same number of properties.
  2256. if (result) {
  2257. forProps(b, function() {
  2258. return !(size--) && breaker;
  2259. }, true);
  2260. result = !size;
  2261. }
  2262. }
  2263. // Remove the first object from the stack of traversed objects.
  2264. stack.pop();
  2265. return result;
  2266. }
  2267. // Perform a deep comparison to check if two objects are equal.
  2268. _.isEqual = function(a, b) {
  2269. return eq(a, b, []);
  2270. };
  2271. // Is a given array, string, or object empty?
  2272. // An "empty" object has no enumerable own-properties.
  2273. _.isEmpty = function(obj) {
  2274. var result = toString.call(obj);
  2275. if (result == '[object Array]' || result == '[object String]') {
  2276. return !obj.length;
  2277. }
  2278. forProps(obj, function() {
  2279. result = false;
  2280. return breaker;
  2281. }, true);
  2282. return !!result;
  2283. };
  2284. // Is a given value a DOM element?
  2285. _.isElement = function(obj) {
  2286. return !!(obj && obj.nodeType == 1);
  2287. };
  2288. // Is a given value an array?
  2289. // Delegates to ECMA5's native Array.isArray
  2290. _.isArray = nativeIsArray || function(obj) {
  2291. return toString.call(obj) == '[object Array]';
  2292. };
  2293. // Is a given variable an object?
  2294. _.isObject = function(obj) {
  2295. return obj === Object(obj);
  2296. };
  2297. // Is a given variable an arguments object?
  2298. _.isArguments = function(obj) {
  2299. return toString.call(obj) == '[object Arguments]';
  2300. };
  2301. if (!_.isArguments(arguments)) {
  2302. _.isArguments = function(obj) {
  2303. return !!(obj && hasOwnProperty.call(obj, 'callee'));
  2304. };
  2305. }
  2306. // Is a given value a function?
  2307. _.isFunction = function(obj) {
  2308. return toString.call(obj) == '[object Function]';
  2309. };
  2310. // Is a given value a string?
  2311. _.isString = function(obj) {
  2312. return toString.call(obj) == '[object String]';
  2313. };
  2314. // Is a given value a number?
  2315. _.isNumber = function(obj) {
  2316. return toString.call(obj) == '[object Number]';
  2317. };
  2318. // Is the given value `NaN`?
  2319. _.isNaN = function(obj) {
  2320. // `NaN` is the only value for which `===` is not reflexive.
  2321. return obj !== obj;
  2322. };
  2323. // Is a given value a boolean?
  2324. _.isBoolean = function(obj) {
  2325. return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
  2326. };
  2327. // Is a given value a date?
  2328. _.isDate = function(obj) {
  2329. return toString.call(obj) == '[object Date]';
  2330. };
  2331. // Is the given value a regular expression?
  2332. _.isRegExp = function(obj) {
  2333. return toString.call(obj) == '[object RegExp]';
  2334. };
  2335. // Is a given value equal to null?
  2336. _.isNull = function(obj) {
  2337. return obj === null;
  2338. };
  2339. // Is a given variable undefined?
  2340. _.isUndefined = function(obj) {
  2341. return obj === void 0;
  2342. };
  2343. // Utility Functions
  2344. // -----------------
  2345. // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
  2346. // previous owner. Returns a reference to the Underscore object.
  2347. _.noConflict = function() {
  2348. root._ = previousUnderscore;
  2349. return this;
  2350. };
  2351. // Keep the identity function around for default iterators.
  2352. _.identity = function(value) {
  2353. return value;
  2354. };
  2355. // Run a function **n** times.
  2356. _.times = function (n, iterator, context) {
  2357. for (var i = 0; i < n; i++) iterator.call(context, i);
  2358. };
  2359. // Escape a string for HTML interpolation.
  2360. _.escape = function(string) {
  2361. return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
  2362. };
  2363. // Add your own custom functions to the Underscore object, ensuring that
  2364. // they're correctly added to the OOP wrapper as well.
  2365. _.mixin = function(obj) {
  2366. simpleEach(_.functions(obj), function(name){
  2367. addToWrapper(name, _[name] = obj[name]);
  2368. });
  2369. };
  2370. // Generate a unique integer id (unique within the entire client session).
  2371. // Useful for temporary DOM ids.
  2372. var idCounter = 0;
  2373. _.uniqueId = function(prefix) {
  2374. var id = idCounter++;
  2375. return prefix ? prefix + id : id;
  2376. };
  2377. // By default, Underscore uses ERB-style template delimiters, change the
  2378. // following template settings to use alternative delimiters.
  2379. _.templateSettings = {
  2380. evaluate : /<%([\s\S]+?)%>/g,
  2381. interpolate : /<%=([\s\S]+?)%>/g,
  2382. escape : /<%-([\s\S]+?)%>/g
  2383. };
  2384. // JavaScript micro-templating, similar to John Resig's implementation.
  2385. // Underscore templating handles arbitrary delimiters, preserves whitespace,
  2386. // and correctly escapes quotes within interpolated code.
  2387. _.template = function(str, data) {
  2388. var c = _.templateSettings;
  2389. var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
  2390. 'with(obj||{}){__p.push(\'' +
  2391. str.replace(/\\/g, '\\\\')
  2392. .replace(/'/g, "\\'")
  2393. .replace(c.escape, function(match, code) {
  2394. return "',_.escape(" + code.replace(/\\'/g, "'") + "),'";
  2395. })
  2396. .replace(c.interpolate, function(match, code) {
  2397. return "'," + code.replace(/\\'/g, "'") + ",'";
  2398. })
  2399. .replace(c.evaluate || null, function(match, code) {
  2400. return "');" + code.replace(/\\'/g, "'")
  2401. .replace(/[\r\n\t]/g, ' ') + ";__p.push('";
  2402. })
  2403. .replace(/\r/g, '\\r')
  2404. .replace(/\n/g, '\\n')
  2405. .replace(/\t/g, '\\t')
  2406. + "');}return __p.join('');";
  2407. var func = new Function('obj', '_', tmpl);
  2408. if (data) return func(data, _);
  2409. return function(data) {
  2410. return func.call(this, data, _);
  2411. };
  2412. };
  2413. // The OOP Wrapper
  2414. // ---------------
  2415. // If Underscore is called as a function, it returns a wrapped object that
  2416. // can be used OO-style. This wrapper holds altered versions of all the
  2417. // underscore functions. Wrapped objects may be chained.
  2418. var wrapper = function(obj) { this._wrapped = obj; };
  2419. // Expose `wrapper.prototype` as `_.prototype`
  2420. _.prototype = wrapper.prototype;
  2421. // Helper function to continue chaining intermediate results.
  2422. var result = function(obj, chain) {
  2423. return chain ? _(obj).chain() : obj;
  2424. };
  2425. // A method to easily add functions to the OOP wrapper.
  2426. var addToWrapper = function(name, func) {
  2427. wrapper.prototype[name] = function() {
  2428. var args = [this._wrapped];
  2429. push.apply(args, arguments);
  2430. return result(func.apply(_, args), this._chain);
  2431. };
  2432. };
  2433. // Add all of the Underscore functions to the wrapper object.
  2434. _.mixin(_);
  2435. // Add all mutator Array functions to the wrapper.
  2436. simpleEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
  2437. var method = ArrayProto[name];
  2438. wrapper.prototype[name] = function() {
  2439. method.apply(this._wrapped, arguments);
  2440. return result(this._wrapped, this._chain);
  2441. };
  2442. });
  2443. // Add all accessor Array functions to the wrapper.
  2444. simpleEach(['concat', 'join', 'slice'], function(name) {
  2445. var method = ArrayProto[name];
  2446. wrapper.prototype[name] = function() {
  2447. return result(method.apply(this._wrapped, arguments), this._chain);
  2448. };
  2449. });
  2450. // Start chaining a wrapped Underscore object.
  2451. wrapper.prototype.chain = function() {
  2452. this._chain = true;
  2453. return this;
  2454. };
  2455. // Extracts the result from a wrapped and chained object.
  2456. wrapper.prototype.value = function() {
  2457. return this._wrapped;
  2458. };
  2459. }).call(this);
  2460. // Backbone.js 0.5.3
  2461. // (c) 2010-2011 Jeremy Ashkenas, DocumentCloud Inc.
  2462. // Backbone may be freely distributed under the MIT license.
  2463. // For all details and documentation:
  2464. // http://documentcloud.github.com/backbone
  2465. (function(){
  2466. // Initial Setup
  2467. // -------------
  2468. // Save a reference to the global object.
  2469. var root = this;
  2470. // Save the previous value of the `Backbone` variable.
  2471. var previousBackbone = root.Backbone;
  2472. // Create a local reference to slice.
  2473. var slice = Array.prototype.slice;
  2474. // The top-level namespace. All public Backbone classes and modules will
  2475. // be attached to this. Exported for both CommonJS and the browser.
  2476. var Backbone;
  2477. if (typeof exports !== 'undefined') {
  2478. Backbone = exports;
  2479. } else {
  2480. Backbone = root.Backbone = {};
  2481. }
  2482. // Current version of the library. Keep in sync with `package.json`.
  2483. Backbone.VERSION = '0.5.3';
  2484. // Require Underscore, if we're on the server, and it's not already present.
  2485. var _ = root._;
  2486. if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._;
  2487. // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable.
  2488. var $ = root.jQuery || root.Zepto || root.ender;
  2489. // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
  2490. // to its previous owner. Returns a reference to this Backbone object.
  2491. Backbone.noConflict = function() {
  2492. root.Backbone = previousBackbone;
  2493. return this;
  2494. };
  2495. // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option will
  2496. // fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and set a
  2497. // `X-Http-Method-Override` header.
  2498. Backbone.emulateHTTP = false;
  2499. // Turn on `emulateJSON` to support legacy servers that can't deal with direct
  2500. // `application/json` requests ... will encode the body as
  2501. // `application/x-www-form-urlencoded` instead and will send the model in a
  2502. // form param named `model`.
  2503. Backbone.emulateJSON = false;
  2504. // Backbone.Events
  2505. // -----------------
  2506. // A module that can be mixed in to *any object* in order to provide it with
  2507. // custom events. You may `bind` or `unbind` a callback function to an event;
  2508. // `trigger`-ing an event fires all callbacks in succession.
  2509. //
  2510. // var object = {};
  2511. // _.extend(object, Backbone.Events);
  2512. // object.bind('expand', function(){ alert('expanded'); });
  2513. // object.trigger('expand');
  2514. //
  2515. Backbone.Events = {
  2516. // Bind an event, specified by a string name, `ev`, to a `callback` function.
  2517. // Passing `"all"` will bind the callback to all events fired.
  2518. bind : function(ev, callback, context) {
  2519. var calls = this._callbacks || (this._callbacks = {});
  2520. var list = calls[ev] || (calls[ev] = {});
  2521. var tail = list.tail || (list.tail = list.next = {});
  2522. tail.callback = callback;
  2523. tail.context = context;
  2524. list.tail = tail.next = {};
  2525. return this;
  2526. },
  2527. // Remove one or many callbacks. If `callback` is null, removes all
  2528. // callbacks for the event. If `ev` is null, removes all bound callbacks
  2529. // for all events.
  2530. unbind : function(ev, callback) {
  2531. var calls, node, prev;
  2532. if (!ev) {
  2533. this._callbacks = null;
  2534. } else if (calls = this._callbacks) {
  2535. if (!callback) {
  2536. calls[ev] = {};
  2537. } else if (node = calls[ev]) {
  2538. while ((prev = node) && (node = node.next)) {
  2539. if (node.callback !== callback) continue;
  2540. prev.next = node.next;
  2541. node.context = node.callback = null;
  2542. break;
  2543. }
  2544. }
  2545. }
  2546. return this;
  2547. },
  2548. // Trigger an event, firing all bound callbacks. Callbacks are passed the
  2549. // same arguments as `trigger` is, apart from the event name.
  2550. // Listening for `"all"` passes the true event name as the first argument.
  2551. trigger : function(eventName) {
  2552. var node, calls, callback, args, ev, events = ['all', eventName];
  2553. if (!(calls = this._callbacks)) return this;
  2554. while (ev = events.pop()) {
  2555. if (!(node = calls[ev])) continue;
  2556. args = ev == 'all' ? arguments : slice.call(arguments, 1);
  2557. while (node = node.next) if (callback = node.callback) callback.apply(node.context || this, args);
  2558. }
  2559. return this;
  2560. }
  2561. };
  2562. // Backbone.Model
  2563. // --------------
  2564. // Create a new model, with defined attributes. A client id (`cid`)
  2565. // is automatically generated and assigned for you.
  2566. Backbone.Model = function(attributes, options) {
  2567. var defaults;
  2568. attributes || (attributes = {});
  2569. if (options && options.parse) attributes = this.parse(attributes);
  2570. if (defaults = this.defaults) {
  2571. if (_.isFunction(defaults)) defaults = defaults.call(this);
  2572. attributes = _.extend({}, defaults, attributes);
  2573. }
  2574. this.attributes = {};
  2575. this._escapedAttributes = {};
  2576. this.cid = _.uniqueId('c');
  2577. this.set(attributes, {silent : true});
  2578. this._changed = false;
  2579. this._previousAttributes = _.clone(this.attributes);
  2580. if (options && options.collection) this.collection = options.collection;
  2581. this.initialize(attributes, options);
  2582. };
  2583. // Attach all inheritable methods to the Model prototype.
  2584. _.extend(Backbone.Model.prototype, Backbone.Events, {
  2585. // Has the item been changed since the last `"change"` event?
  2586. _changed : false,
  2587. // The default name for the JSON `id` attribute is `"id"`. MongoDB and
  2588. // CouchDB users may want to set this to `"_id"`.
  2589. idAttribute : 'id',
  2590. // Initialize is an empty function by default. Override it with your own
  2591. // initialization logic.
  2592. initialize : function(){},
  2593. // Return a copy of the model's `attributes` object.
  2594. toJSON : function() {
  2595. return _.clone(this.attributes);
  2596. },
  2597. // Get the value of an attribute.
  2598. get : function(attr) {
  2599. return this.attributes[attr];
  2600. },
  2601. // Get the HTML-escaped value of an attribute.
  2602. escape : function(attr) {
  2603. var html;
  2604. if (html = this._escapedAttributes[attr]) return html;
  2605. var val = this.attributes[attr];
  2606. return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val);
  2607. },
  2608. // Returns `true` if the attribute contains a value that is not null
  2609. // or undefined.
  2610. has : function(attr) {
  2611. return this.attributes[attr] != null;
  2612. },
  2613. // Set a hash of model attributes on the object, firing `"change"` unless you
  2614. // choose to silence it.
  2615. set : function(key, value, options) {
  2616. var attrs;
  2617. if (_.isObject(key)) {
  2618. attrs = key;
  2619. options = value;
  2620. } else {
  2621. attrs = {};
  2622. attrs[key] = value;
  2623. }
  2624. // Extract attributes and options.
  2625. options || (options = {});
  2626. if (!attrs) return this;
  2627. if (attrs.attributes) attrs = attrs.attributes;
  2628. if (options.unset) for (var attr in attrs) attrs[attr] = void 0;
  2629. var now = this.attributes, escaped = this._escapedAttributes;
  2630. // Run validation.
  2631. if (!options.silent && this.validate && !this._performValidation(attrs, options)) return false;
  2632. // Check for changes of `id`.
  2633. if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
  2634. // We're about to start triggering change events.
  2635. var alreadyChanging = this._changing;
  2636. this._changing = true;
  2637. // Update attributes.
  2638. for (var attr in attrs) {
  2639. var val = attrs[attr];
  2640. if (!_.isEqual(now[attr], val) || (options.unset && (attr in now))) {
  2641. options.unset ? delete now[attr] : now[attr] = val;
  2642. delete escaped[attr];
  2643. this._changed = true;
  2644. if (!options.silent) this.trigger('change:' + attr, this, val, options);
  2645. }
  2646. }
  2647. // Fire the `"change"` event, if the model has been changed.
  2648. if (!alreadyChanging) {
  2649. if (!options.silent && this._changed) this.change(options);
  2650. this._changing = false;
  2651. }
  2652. return this;
  2653. },
  2654. // Remove an attribute from the model, firing `"change"` unless you choose
  2655. // to silence it. `unset` is a noop if the attribute doesn't exist.
  2656. unset : function(attr, options) {
  2657. (options || (options = {})).unset = true;
  2658. return this.set(attr, null, options);
  2659. },
  2660. // Clear all attributes on the model, firing `"change"` unless you choose
  2661. // to silence it.
  2662. clear : function(options) {
  2663. (options || (options = {})).unset = true;
  2664. return this.set(_.clone(this.attributes), options);
  2665. },
  2666. // Fetch the model from the server. If the server's representation of the
  2667. // model differs from its current attributes, they will be overriden,
  2668. // triggering a `"change"` event.
  2669. fetch : function(options) {
  2670. options || (options = {});
  2671. var model = this;
  2672. var success = options.success;
  2673. options.success = function(resp, status, xhr) {
  2674. if (!model.set(model.parse(resp, xhr), options)) return false;
  2675. if (success) success(model, resp);
  2676. };
  2677. options.error = wrapError(options.error, model, options);
  2678. return (this.sync || Backbone.sync).call(this, 'read', this, options);
  2679. },
  2680. // Set a hash of model attributes, and sync the model to the server.
  2681. // If the server returns an attributes hash that differs, the model's
  2682. // state will be `set` again.
  2683. save : function(attrs, options) {
  2684. options || (options = {});
  2685. if (attrs && !this.set(attrs, options)) return false;
  2686. var model = this;
  2687. var success = options.success;
  2688. options.success = function(resp, status, xhr) {
  2689. if (!model.set(model.parse(resp, xhr), options)) return false;
  2690. if (success) success(model, resp, xhr);
  2691. };
  2692. options.error = wrapError(options.error, model, options);
  2693. var method = this.isNew() ? 'create' : 'update';
  2694. return (this.sync || Backbone.sync).call(this, method, this, options);
  2695. },
  2696. // Destroy this model on the server if it was already persisted. Upon success, the model is removed
  2697. // from its collection, if it has one.
  2698. destroy : function(options) {
  2699. options || (options = {});
  2700. if (this.isNew()) return this.trigger('destroy', this, this.collection, options);
  2701. var model = this;
  2702. var success = options.success;
  2703. options.success = function(resp) {
  2704. model.trigger('destroy', model, model.collection, options);
  2705. if (success) success(model, resp);
  2706. };
  2707. options.error = wrapError(options.error, model, options);
  2708. return (this.sync || Backbone.sync).call(this, 'delete', this, options);
  2709. },
  2710. // Default URL for the model's representation on the server -- if you're
  2711. // using Backbone's restful methods, override this to change the endpoint
  2712. // that will be called.
  2713. url : function() {
  2714. var base = getUrl(this.collection) || this.urlRoot || urlError();
  2715. if (this.isNew()) return base;
  2716. return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id);
  2717. },
  2718. // **parse** converts a response into the hash of attributes to be `set` on
  2719. // the model. The default implementation is just to pass the response along.
  2720. parse : function(resp, xhr) {
  2721. return resp;
  2722. },
  2723. // Create a new model with identical attributes to this one.
  2724. clone : function() {
  2725. return new this.constructor(this);
  2726. },
  2727. // A model is new if it has never been saved to the server, and lacks an id.
  2728. isNew : function() {
  2729. return this.id == null;
  2730. },
  2731. // Call this method to manually fire a `change` event for this model.
  2732. // Calling this will cause all objects observing the model to update.
  2733. change : function(options) {
  2734. this.trigger('change', this, options);
  2735. this._previousAttributes = _.clone(this.attributes);
  2736. this._changed = false;
  2737. },
  2738. // Determine if the model has changed since the last `"change"` event.
  2739. // If you specify an attribute name, determine if that attribute has changed.
  2740. hasChanged : function(attr) {
  2741. if (attr) return this._previousAttributes[attr] != this.attributes[attr];
  2742. return this._changed;
  2743. },
  2744. // Return an object containing all the attributes that have changed, or false
  2745. // if there are no changed attributes. Useful for determining what parts of a
  2746. // view need to be updated and/or what attributes need to be persisted to
  2747. // the server. Unset attributes will be set to undefined.
  2748. changedAttributes : function(now) {
  2749. if (!this._changed) return false;
  2750. now || (now = this.attributes);
  2751. var changed = false, old = this._previousAttributes;
  2752. for (var attr in now) {
  2753. if (_.isEqual(old[attr], now[attr])) continue;
  2754. (changed || (changed = {}))[attr] = now[attr];
  2755. }
  2756. for (var attr in old) {
  2757. if (!(attr in now)) (changed || (changed = {}))[attr] = void 0;
  2758. }
  2759. return changed;
  2760. },
  2761. // Get the previous value of an attribute, recorded at the time the last
  2762. // `"change"` event was fired.
  2763. previous : function(attr) {
  2764. if (!attr || !this._previousAttributes) return null;
  2765. return this._previousAttributes[attr];
  2766. },
  2767. // Get all of the attributes of the model at the time of the previous
  2768. // `"change"` event.
  2769. previousAttributes : function() {
  2770. return _.clone(this._previousAttributes);
  2771. },
  2772. // Run validation against a set of incoming attributes, returning `true`
  2773. // if all is well. If a specific `error` callback has been passed,
  2774. // call that instead of firing the general `"error"` event.
  2775. _performValidation : function(attrs, options) {
  2776. var error = this.validate(attrs, options);
  2777. if (error) {
  2778. if (options.error) {
  2779. options.error(this, error, options);
  2780. } else {
  2781. this.trigger('error', this, error, options);
  2782. }
  2783. return false;
  2784. }
  2785. return true;
  2786. }
  2787. });
  2788. // Backbone.Collection
  2789. // -------------------
  2790. // Provides a standard collection class for our sets of models, ordered
  2791. // or unordered. If a `comparator` is specified, the Collection will maintain
  2792. // its models in sort order, as they're added and removed.
  2793. Backbone.Collection = function(models, options) {
  2794. options || (options = {});
  2795. if (options.comparator) this.comparator = options.comparator;
  2796. _.bindAll(this, '_onModelEvent', '_removeReference');
  2797. this._reset();
  2798. if (models) this.reset(models, {silent: true});
  2799. this.initialize.apply(this, arguments);
  2800. };
  2801. // Define the Collection's inheritable methods.
  2802. _.extend(Backbone.Collection.prototype, Backbone.Events, {
  2803. // The default model for a collection is just a **Backbone.Model**.
  2804. // This should be overridden in most cases.
  2805. model : Backbone.Model,
  2806. // Initialize is an empty function by default. Override it with your own
  2807. // initialization logic.
  2808. initialize : function(){},
  2809. // The JSON representation of a Collection is an array of the
  2810. // models' attributes.
  2811. toJSON : function() {
  2812. return this.map(function(model){ return model.toJSON(); });
  2813. },
  2814. // Add a model, or list of models to the set. Pass **silent** to avoid
  2815. // firing the `added` event for every new model.
  2816. add : function(models, options) {
  2817. if (_.isArray(models)) {
  2818. for (var i = 0, l = models.length; i < l; i++) {
  2819. this._add(models[i], options);
  2820. }
  2821. } else {
  2822. this._add(models, options);
  2823. }
  2824. return this;
  2825. },
  2826. // Remove a model, or a list of models from the set. Pass silent to avoid
  2827. // firing the `removed` event for every model removed.
  2828. remove : function(models, options) {
  2829. if (_.isArray(models)) {
  2830. for (var i = 0, l = models.length; i < l; i++) {
  2831. this._remove(models[i], options);
  2832. }
  2833. } else {
  2834. this._remove(models, options);
  2835. }
  2836. return this;
  2837. },
  2838. // Get a model from the set by id.
  2839. get : function(id) {
  2840. if (id == null) return null;
  2841. return this._byId[id.id != null ? id.id : id];
  2842. },
  2843. // Get a model from the set by client id.
  2844. getByCid : function(cid) {
  2845. return cid && this._byCid[cid.cid || cid];
  2846. },
  2847. // Get the model at the given index.
  2848. at : function(index) {
  2849. return this.models[index];
  2850. },
  2851. // Force the collection to re-sort itself. You don't need to call this under normal
  2852. // circumstances, as the set will maintain sort order as each item is added.
  2853. sort : function(options) {
  2854. options || (options = {});
  2855. if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
  2856. this.models = this.sortBy(this.comparator);
  2857. if (!options.silent) this.trigger('reset', this, options);
  2858. return this;
  2859. },
  2860. // Pluck an attribute from each model in the collection.
  2861. pluck : function(attr) {
  2862. return _.map(this.models, function(model){ return model.get(attr); });
  2863. },
  2864. // When you have more items than you want to add or remove individually,
  2865. // you can reset the entire set with a new list of models, without firing
  2866. // any `added` or `removed` events. Fires `reset` when finished.
  2867. reset : function(models, options) {
  2868. models || (models = []);
  2869. options || (options = {});
  2870. this.each(this._removeReference);
  2871. this._reset();
  2872. this.add(models, {silent: true, parse: options.parse});
  2873. if (!options.silent) this.trigger('reset', this, options);
  2874. return this;
  2875. },
  2876. // Fetch the default set of models for this collection, resetting the
  2877. // collection when they arrive. If `add: true` is passed, appends the
  2878. // models to the collection instead of resetting.
  2879. fetch : function(options) {
  2880. options || (options = {});
  2881. if (options.parse === undefined) options.parse = true;
  2882. var collection = this;
  2883. var success = options.success;
  2884. options.success = function(resp, status, xhr) {
  2885. collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);
  2886. if (success) success(collection, resp);
  2887. };
  2888. options.error = wrapError(options.error, collection, options);
  2889. return (this.sync || Backbone.sync).call(this, 'read', this, options);
  2890. },
  2891. // Create a new instance of a model in this collection. After the model
  2892. // has been created on the server, it will be added to the collection.
  2893. // Returns the model, or 'false' if validation on a new model fails.
  2894. create : function(model, options) {
  2895. var coll = this;
  2896. options || (options = {});
  2897. model = this._prepareModel(model, options);
  2898. if (!model) return false;
  2899. var success = options.success;
  2900. options.success = function(nextModel, resp, xhr) {
  2901. coll.add(nextModel, options);
  2902. if (success) success(nextModel, resp, xhr);
  2903. };
  2904. model.save(null, options);
  2905. return model;
  2906. },
  2907. // **parse** converts a response into a list of models to be added to the
  2908. // collection. The default implementation is just to pass it through.
  2909. parse : function(resp, xhr) {
  2910. return resp;
  2911. },
  2912. // Proxy to _'s chain. Can't be proxied the same way the rest of the
  2913. // underscore methods are proxied because it relies on the underscore
  2914. // constructor.
  2915. chain : function () {
  2916. return _(this.models).chain();
  2917. },
  2918. // Reset all internal state. Called when the collection is reset.
  2919. _reset : function(options) {
  2920. this.length = 0;
  2921. this.models = [];
  2922. this._byId = {};
  2923. this._byCid = {};
  2924. },
  2925. // Prepare a model to be added to this collection
  2926. _prepareModel : function(model, options) {
  2927. if (!(model instanceof Backbone.Model)) {
  2928. var attrs = model;
  2929. model = new this.model(attrs, {collection: this, parse: options.parse});
  2930. if (model.validate && !model._performValidation(model.attributes, options)) model = false;
  2931. } else if (!model.collection) {
  2932. model.collection = this;
  2933. }
  2934. return model;
  2935. },
  2936. // Internal implementation of adding a single model to the set, updating
  2937. // hash indexes for `id` and `cid` lookups.
  2938. // Returns the model, or 'false' if validation on a new model fails.
  2939. _add : function(model, options) {
  2940. options || (options = {});
  2941. model = this._prepareModel(model, options);
  2942. if (!model) return false;
  2943. var already = this.getByCid(model);
  2944. if (already) throw new Error(["Can't add the same model to a set twice", already.id]);
  2945. this._byId[model.id] = model;
  2946. this._byCid[model.cid] = model;
  2947. var index = options.at != null ? options.at :
  2948. this.comparator ? this.sortedIndex(model, this.comparator) :
  2949. this.length;
  2950. this.models.splice(index, 0, model);
  2951. model.bind('all', this._onModelEvent);
  2952. this.length++;
  2953. options.index = index;
  2954. if (!options.silent) model.trigger('add', model, this, options);
  2955. return model;
  2956. },
  2957. // Internal implementation of removing a single model from the set, updating
  2958. // hash indexes for `id` and `cid` lookups.
  2959. _remove : function(model, options) {
  2960. options || (options = {});
  2961. model = this.getByCid(model) || this.get(model);
  2962. if (!model) return null;
  2963. delete this._byId[model.id];
  2964. delete this._byCid[model.cid];
  2965. var index = this.indexOf(model);
  2966. this.models.splice(index, 1);
  2967. this.length--;
  2968. options.index = index;
  2969. if (!options.silent) model.trigger('remove', model, this, options);
  2970. this._removeReference(model);
  2971. return model;
  2972. },
  2973. // Internal method to remove a model's ties to a collection.
  2974. _removeReference : function(model) {
  2975. if (this == model.collection) {
  2976. delete model.collection;
  2977. }
  2978. model.unbind('all', this._onModelEvent);
  2979. },
  2980. // Internal method called every time a model in the set fires an event.
  2981. // Sets need to update their indexes when models change ids. All other
  2982. // events simply proxy through. "add" and "remove" events that originate
  2983. // in other collections are ignored.
  2984. _onModelEvent : function(ev, model, collection, options) {
  2985. if ((ev == 'add' || ev == 'remove') && collection != this) return;
  2986. if (ev == 'destroy') {
  2987. this._remove(model, options);
  2988. }
  2989. if (model && ev === 'change:' + model.idAttribute) {
  2990. delete this._byId[model.previous(model.idAttribute)];
  2991. this._byId[model.id] = model;
  2992. }
  2993. this.trigger.apply(this, arguments);
  2994. }
  2995. });
  2996. // Underscore methods that we want to implement on the Collection.
  2997. var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 'detect',
  2998. 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
  2999. 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size',
  3000. 'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty', 'groupBy'];
  3001. // Mix in each Underscore method as a proxy to `Collection#models`.
  3002. _.each(methods, function(method) {
  3003. Backbone.Collection.prototype[method] = function() {
  3004. return _[method].apply(_, [this.models].concat(_.toArray(arguments)));
  3005. };
  3006. });
  3007. // Backbone.Router
  3008. // -------------------
  3009. // Routers map faux-URLs to actions, and fire events when routes are
  3010. // matched. Creating a new one sets its `routes` hash, if not set statically.
  3011. Backbone.Router = function(options) {
  3012. options || (options = {});
  3013. if (options.routes) this.routes = options.routes;
  3014. this._bindRoutes();
  3015. this.initialize.apply(this, arguments);
  3016. };
  3017. // Cached regular expressions for matching named param parts and splatted
  3018. // parts of route strings.
  3019. var namedParam = /:([\w\d]+)/g;
  3020. var splatParam = /\*([\w\d]+)/g;
  3021. var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g;
  3022. // Set up all inheritable **Backbone.Router** properties and methods.
  3023. _.extend(Backbone.Router.prototype, Backbone.Events, {
  3024. // Initialize is an empty function by default. Override it with your own
  3025. // initialization logic.
  3026. initialize : function(){},
  3027. // Manually bind a single named route to a callback. For example:
  3028. //
  3029. // this.route('search/:query/p:num', 'search', function(query, num) {
  3030. // ...
  3031. // });
  3032. //
  3033. route : function(route, name, callback) {
  3034. Backbone.history || (Backbone.history = new Backbone.History);
  3035. if (!_.isRegExp(route)) route = this._routeToRegExp(route);
  3036. Backbone.history.route(route, _.bind(function(fragment) {
  3037. var args = this._extractParameters(route, fragment);
  3038. callback && callback.apply(this, args);
  3039. this.trigger.apply(this, ['route:' + name].concat(args));
  3040. }, this));
  3041. },
  3042. // Simple proxy to `Backbone.history` to save a fragment into the history.
  3043. navigate : function(fragment, options) {
  3044. Backbone.history.navigate(fragment, options);
  3045. },
  3046. // Bind all defined routes to `Backbone.history`. We have to reverse the
  3047. // order of the routes here to support behavior where the most general
  3048. // routes can be defined at the bottom of the route map.
  3049. _bindRoutes : function() {
  3050. if (!this.routes) return;
  3051. var routes = [];
  3052. for (var route in this.routes) {
  3053. routes.unshift([route, this.routes[route]]);
  3054. }
  3055. for (var i = 0, l = routes.length; i < l; i++) {
  3056. this.route(routes[i][0], routes[i][1], this[routes[i][1]]);
  3057. }
  3058. },
  3059. // Convert a route string into a regular expression, suitable for matching
  3060. // against the current location hash.
  3061. _routeToRegExp : function(route) {
  3062. route = route.replace(escapeRegExp, "\\$&")
  3063. .replace(namedParam, "([^\/]*)")
  3064. .replace(splatParam, "(.*?)");
  3065. return new RegExp('^' + route + '$');
  3066. },
  3067. // Given a route, and a URL fragment that it matches, return the array of
  3068. // extracted parameters.
  3069. _extractParameters : function(route, fragment) {
  3070. return route.exec(fragment).slice(1);
  3071. }
  3072. });
  3073. // Backbone.History
  3074. // ----------------
  3075. // Handles cross-browser history management, based on URL fragments. If the
  3076. // browser does not support `onhashchange`, falls back to polling.
  3077. Backbone.History = function() {
  3078. this.handlers = [];
  3079. _.bindAll(this, 'checkUrl');
  3080. };
  3081. // Cached regex for cleaning hashes.
  3082. var hashStrip = /^#/;
  3083. // Cached regex for detecting MSIE.
  3084. var isExplorer = /msie [\w.]+/;
  3085. // Has the history handling already been started?
  3086. var historyStarted = false;
  3087. // Set up all inheritable **Backbone.History** properties and methods.
  3088. _.extend(Backbone.History.prototype, {
  3089. // The default interval to poll for hash changes, if necessary, is
  3090. // twenty times a second.
  3091. interval: 50,
  3092. // Get the cross-browser normalized URL fragment, either from the URL,
  3093. // the hash, or the override.
  3094. getFragment : function(fragment, forcePushState) {
  3095. if (fragment == null) {
  3096. if (this._hasPushState || forcePushState) {
  3097. fragment = window.location.pathname;
  3098. var search = window.location.search;
  3099. if (search) fragment += search;
  3100. } else {
  3101. fragment = window.location.hash;
  3102. }
  3103. }
  3104. fragment = decodeURIComponent(fragment.replace(hashStrip, ''));
  3105. if (!fragment.indexOf(this.options.root)) fragment = fragment.substr(this.options.root.length);
  3106. return fragment;
  3107. },
  3108. // Start the hash change handling, returning `true` if the current URL matches
  3109. // an existing route, and `false` otherwise.
  3110. start : function(options) {
  3111. // Figure out the initial configuration. Do we need an iframe?
  3112. // Is pushState desired ... is it available?
  3113. if (historyStarted) throw new Error("Backbone.history has already been started");
  3114. this.options = _.extend({}, {root: '/'}, this.options, options);
  3115. this._wantsPushState = !!this.options.pushState;
  3116. this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState);
  3117. var fragment = this.getFragment();
  3118. var docMode = document.documentMode;
  3119. var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
  3120. if (oldIE) {
  3121. this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
  3122. this.navigate(fragment);
  3123. }
  3124. // Depending on whether we're using pushState or hashes, and whether
  3125. // 'onhashchange' is supported, determine how we check the URL state.
  3126. if (this._hasPushState) {
  3127. $(window).bind('popstate', this.checkUrl);
  3128. } else if ('onhashchange' in window && !oldIE) {
  3129. $(window).bind('hashchange', this.checkUrl);
  3130. } else {
  3131. setInterval(this.checkUrl, this.interval);
  3132. }
  3133. // Determine if we need to change the base url, for a pushState link
  3134. // opened by a non-pushState browser.
  3135. this.fragment = fragment;
  3136. historyStarted = true;
  3137. var loc = window.location;
  3138. var atRoot = loc.pathname == this.options.root;
  3139. if (this._wantsPushState && !this._hasPushState && !atRoot) {
  3140. this.fragment = this.getFragment(null, true);
  3141. window.location.replace(this.options.root + '#' + this.fragment);
  3142. // Return immediately as browser will do redirect to new url
  3143. return true;
  3144. } else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
  3145. this.fragment = loc.hash.replace(hashStrip, '');
  3146. window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment);
  3147. }
  3148. if (!this.options.silent) {
  3149. return this.loadUrl();
  3150. }
  3151. },
  3152. // Add a route to be tested when the fragment changes. Routes added later may
  3153. // override previous routes.
  3154. route : function(route, callback) {
  3155. this.handlers.unshift({route : route, callback : callback});
  3156. },
  3157. // Checks the current URL to see if it has changed, and if it has,
  3158. // calls `loadUrl`, normalizing across the hidden iframe.
  3159. checkUrl : function(e) {
  3160. var current = this.getFragment();
  3161. if (current == this.fragment && this.iframe) current = this.getFragment(this.iframe.location.hash);
  3162. if (current == this.fragment || current == decodeURIComponent(this.fragment)) return false;
  3163. if (this.iframe) this.navigate(current);
  3164. this.loadUrl() || this.loadUrl(window.location.hash);
  3165. },
  3166. // Attempt to load the current URL fragment. If a route succeeds with a
  3167. // match, returns `true`. If no defined routes matches the fragment,
  3168. // returns `false`.
  3169. loadUrl : function(fragmentOverride) {
  3170. var fragment = this.fragment = this.getFragment(fragmentOverride);
  3171. var matched = _.any(this.handlers, function(handler) {
  3172. if (handler.route.test(fragment)) {
  3173. handler.callback(fragment);
  3174. return true;
  3175. }
  3176. });
  3177. return matched;
  3178. },
  3179. // Save a fragment into the hash history, or replace the URL state if the
  3180. // 'replace' option is passed. You are responsible for properly URL-encoding
  3181. // the fragment in advance.
  3182. //
  3183. // The options object can contain `trigger: true` if you wish to have the
  3184. // route callback be fired (not usually desirable), or `replace: true`, if
  3185. // you which to modify the current URL without adding an entry to the history.
  3186. navigate : function(fragment, options) {
  3187. if (!options || options === true) options = {trigger: options};
  3188. var frag = (fragment || '').replace(hashStrip, '');
  3189. if (this.fragment == frag || this.fragment == decodeURIComponent(frag)) return;
  3190. if (this._hasPushState) {
  3191. if (frag.indexOf(this.options.root) != 0) frag = this.options.root + frag;
  3192. this.fragment = frag;
  3193. window.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, frag);
  3194. } else {
  3195. this.fragment = frag;
  3196. this._updateHash(window.location, frag, options.replace);
  3197. if (this.iframe && (frag != this.getFragment(this.iframe.location.hash))) {
  3198. // Opening and closing the iframe tricks IE7 and earlier to push a history entry on hash-tag change.
  3199. // When replace is true, we don't want this.
  3200. if(!options.replace) this.iframe.document.open().close();
  3201. this._updateHash(this.iframe.location, frag, options.replace);
  3202. }
  3203. }
  3204. if (options.trigger) this.loadUrl(fragment);
  3205. },
  3206. // Update the hash location, either replacing the current entry, or adding
  3207. // a new one to the browser history.
  3208. _updateHash: function(location, fragment, replace) {
  3209. if (replace) {
  3210. location.replace(location.toString().replace(/(javascript:|#).*$/, "") + "#" + fragment);
  3211. } else {
  3212. location.hash = fragment;
  3213. }
  3214. }
  3215. });
  3216. // Backbone.View
  3217. // -------------
  3218. // Creating a Backbone.View creates its initial element outside of the DOM,
  3219. // if an existing element is not provided...
  3220. Backbone.View = function(options) {
  3221. this.cid = _.uniqueId('view');
  3222. this._configure(options || {});
  3223. this._ensureElement();
  3224. this.delegateEvents();
  3225. this.initialize.apply(this, arguments);
  3226. };
  3227. // Cached regex to split keys for `delegate`.
  3228. var eventSplitter = /^(\S+)\s*(.*)$/;
  3229. // List of view options to be merged as properties.
  3230. var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName'];
  3231. // Set up all inheritable **Backbone.View** properties and methods.
  3232. _.extend(Backbone.View.prototype, Backbone.Events, {
  3233. // The default `tagName` of a View's element is `"div"`.
  3234. tagName : 'div',
  3235. // jQuery delegate for element lookup, scoped to DOM elements within the
  3236. // current view. This should be prefered to global lookups where possible.
  3237. $ : function(selector) {
  3238. return (selector == null) ? $(this.el) : $(selector, this.el);
  3239. },
  3240. // Initialize is an empty function by default. Override it with your own
  3241. // initialization logic.
  3242. initialize : function(){},
  3243. // **render** is the core function that your view should override, in order
  3244. // to populate its element (`this.el`), with the appropriate HTML. The
  3245. // convention is for **render** to always return `this`.
  3246. render : function() {
  3247. return this;
  3248. },
  3249. // Remove this view from the DOM. Note that the view isn't present in the
  3250. // DOM by default, so calling this method may be a no-op.
  3251. remove : function() {
  3252. $(this.el).remove();
  3253. return this;
  3254. },
  3255. // For small amounts of DOM Elements, where a full-blown template isn't
  3256. // needed, use **make** to manufacture elements, one at a time.
  3257. //
  3258. // var el = this.make('li', {'class': 'row'}, this.model.escape('title'));
  3259. //
  3260. make : function(tagName, attributes, content) {
  3261. var el = document.createElement(tagName);
  3262. if (attributes) $(el).attr(attributes);
  3263. if (content) $(el).html(content);
  3264. return el;
  3265. },
  3266. // Set callbacks, where `this.events` is a hash of
  3267. //
  3268. // *{"event selector": "callback"}*
  3269. //
  3270. // {
  3271. // 'mousedown .title': 'edit',
  3272. // 'click .button': 'save'
  3273. // }
  3274. //
  3275. // pairs. Callbacks will be bound to the view, with `this` set properly.
  3276. // Uses event delegation for efficiency.
  3277. // Omitting the selector binds the event to `this.el`.
  3278. // This only works for delegate-able events: not `focus`, `blur`, and
  3279. // not `change`, `submit`, and `reset` in Internet Explorer.
  3280. delegateEvents : function(events) {
  3281. if (!(events || (events = this.events))) return;
  3282. if (_.isFunction(events)) events = events.call(this);
  3283. this.undelegateEvents();
  3284. for (var key in events) {
  3285. var method = this[events[key]];
  3286. if (!method) throw new Error('Event "' + events[key] + '" does not exist');
  3287. var match = key.match(eventSplitter);
  3288. var eventName = match[1], selector = match[2];
  3289. method = _.bind(method, this);
  3290. eventName += '.delegateEvents' + this.cid;
  3291. if (selector === '') {
  3292. $(this.el).bind(eventName, method);
  3293. } else {
  3294. $(this.el).delegate(selector, eventName, method);
  3295. }
  3296. }
  3297. },
  3298. // Clears all callbacks previously bound to the view with `delegateEvents`.
  3299. undelegateEvents : function() {
  3300. $(this.el).unbind('.delegateEvents' + this.cid);
  3301. },
  3302. // Performs the initial configuration of a View with a set of options.
  3303. // Keys with special meaning *(model, collection, id, className)*, are
  3304. // attached directly to the view.
  3305. _configure : function(options) {
  3306. if (this.options) options = _.extend({}, this.options, options);
  3307. for (var i = 0, l = viewOptions.length; i < l; i++) {
  3308. var attr = viewOptions[i];
  3309. if (options[attr]) this[attr] = options[attr];
  3310. }
  3311. this.options = options;
  3312. },
  3313. // Ensure that the View has a DOM element to render into.
  3314. // If `this.el` is a string, pass it through `$()`, take the first
  3315. // matching element, and re-assign it to `el`. Otherwise, create
  3316. // an element from the `id`, `className` and `tagName` properties.
  3317. _ensureElement : function() {
  3318. if (!this.el) {
  3319. var attrs = this.attributes || {};
  3320. if (this.id) attrs.id = this.id;
  3321. if (this.className) attrs['class'] = this.className;
  3322. this.el = this.make(this.tagName, attrs);
  3323. } else if (_.isString(this.el)) {
  3324. this.el = $(this.el).get(0);
  3325. }
  3326. }
  3327. });
  3328. // The self-propagating extend function that Backbone classes use.
  3329. var extend = function (protoProps, classProps) {
  3330. var child = inherits(this, protoProps, classProps);
  3331. child.extend = this.extend;
  3332. return child;
  3333. };
  3334. // Set up inheritance for the model, collection, and view.
  3335. Backbone.Model.extend = Backbone.Collection.extend =
  3336. Backbone.Router.extend = Backbone.View.extend = extend;
  3337. // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
  3338. var methodMap = {
  3339. 'create': 'POST',
  3340. 'update': 'PUT',
  3341. 'delete': 'DELETE',
  3342. 'read' : 'GET'
  3343. };
  3344. // Backbone.sync
  3345. // -------------
  3346. // Override this function to change the manner in which Backbone persists
  3347. // models to the server. You will be passed the type of request, and the
  3348. // model in question. By default, makes a RESTful Ajax request
  3349. // to the model's `url()`. Some possible customizations could be:
  3350. //
  3351. // * Use `setTimeout` to batch rapid-fire updates into a single request.
  3352. // * Send up the models as XML instead of JSON.
  3353. // * Persist models via WebSockets instead of Ajax.
  3354. //
  3355. // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
  3356. // as `POST`, with a `_method` parameter containing the true HTTP method,
  3357. // as well as all requests with the body as `application/x-www-form-urlencoded` instead of
  3358. // `application/json` with the model in a param named `model`.
  3359. // Useful when interfacing with server-side languages like **PHP** that make
  3360. // it difficult to read the body of `PUT` requests.
  3361. Backbone.sync = function(method, model, options) {
  3362. var type = methodMap[method];
  3363. // Default JSON-request options.
  3364. var params = {type : type, dataType : 'json'};
  3365. // Ensure that we have a URL.
  3366. if (!options.url) {
  3367. params.url = getUrl(model) || urlError();
  3368. }
  3369. // Ensure that we have the appropriate request data.
  3370. if (!options.data && model && (method == 'create' || method == 'update')) {
  3371. params.contentType = 'application/json';
  3372. params.data = JSON.stringify(model.toJSON());
  3373. }
  3374. // For older servers, emulate JSON by encoding the request into an HTML-form.
  3375. if (Backbone.emulateJSON) {
  3376. params.contentType = 'application/x-www-form-urlencoded';
  3377. params.data = params.data ? {model : params.data} : {};
  3378. }
  3379. // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
  3380. // And an `X-HTTP-Method-Override` header.
  3381. if (Backbone.emulateHTTP) {
  3382. if (type === 'PUT' || type === 'DELETE') {
  3383. if (Backbone.emulateJSON) params.data._method = type;
  3384. params.type = 'POST';
  3385. params.beforeSend = function(xhr) {
  3386. xhr.setRequestHeader('X-HTTP-Method-Override', type);
  3387. };
  3388. }
  3389. }
  3390. // Don't process data on a non-GET request.
  3391. if (params.type !== 'GET' && !Backbone.emulateJSON) {
  3392. params.processData = false;
  3393. }
  3394. // Make the request, allowing the user to override any Ajax options.
  3395. return $.ajax(_.extend(params, options));
  3396. };
  3397. // Helpers
  3398. // -------
  3399. // Shared empty constructor function to aid in prototype-chain creation.
  3400. var ctor = function(){};
  3401. // Helper function to correctly set up the prototype chain, for subclasses.
  3402. // Similar to `goog.inherits`, but uses a hash of prototype properties and
  3403. // class properties to be extended.
  3404. var inherits = function(parent, protoProps, staticProps) {
  3405. var child;
  3406. // The constructor function for the new subclass is either defined by you
  3407. // (the "constructor" property in your `extend` definition), or defaulted
  3408. // by us to simply call `super()`.
  3409. if (protoProps && protoProps.hasOwnProperty('constructor')) {
  3410. child = protoProps.constructor;
  3411. } else {
  3412. child = function(){ return parent.apply(this, arguments); };
  3413. }
  3414. // Inherit class (static) properties from parent.
  3415. _.extend(child, parent);
  3416. // Set the prototype chain to inherit from `parent`, without calling
  3417. // `parent`'s constructor function.
  3418. ctor.prototype = parent.prototype;
  3419. child.prototype = new ctor();
  3420. // Add prototype properties (instance properties) to the subclass,
  3421. // if supplied.
  3422. if (protoProps) _.extend(child.prototype, protoProps);
  3423. // Add static properties to the constructor function, if supplied.
  3424. if (staticProps) _.extend(child, staticProps);
  3425. // Correctly set child's `prototype.constructor`.
  3426. child.prototype.constructor = child;
  3427. // Set a convenience property in case the parent's prototype is needed later.
  3428. child.__super__ = parent.prototype;
  3429. return child;
  3430. };
  3431. // Helper function to get a URL from a Model or Collection as a property
  3432. // or as a function.
  3433. var getUrl = function(object) {
  3434. if (!(object && object.url)) return null;
  3435. return _.isFunction(object.url) ? object.url() : object.url;
  3436. };
  3437. // Throw an error when a URL is needed, and none is supplied.
  3438. var urlError = function() {
  3439. throw new Error('A "url" property or function must be specified');
  3440. };
  3441. // Wrap an optional error callback with a fallback error event.
  3442. var wrapError = function(onError, originalModel, options) {
  3443. return function(model, resp) {
  3444. var resp = model === originalModel ? resp : model;
  3445. if (onError) {
  3446. onError(model, resp, options);
  3447. } else {
  3448. originalModel.trigger('error', model, resp, options);
  3449. }
  3450. };
  3451. };
  3452. }).call(this);
  3453. // Gbone.js 0.1.0
  3454. // (c) 2011 Gobhi Theivendran
  3455. // Gbone.js may be freely distributed under the MIT license.
  3456. // For all details and documentation:
  3457. // https://github.com/gobhi/gbone.js
  3458. (function( root, factory ) {
  3459. // Set up Gbone appropriately for the environment.
  3460. if ( typeof exports !== 'undefined' ) {
  3461. // Node/CommonJS, no need for jQuery/Zepto in that case.
  3462. factory( root, require('backbone'), exports, require('underscore') );
  3463. } else if ( typeof define === 'function' && define.amd ) {
  3464. // AMD
  3465. define('gbone', ['underscore', 'backbone', 'zepto', 'exports'], function( _, Backbone, $, exports ) {
  3466. // Export global even in AMD case in case this script is loaded with
  3467. // others that may still expect a global GBone.
  3468. root.Gbone = factory( root, Backbone, exports, _, $ );
  3469. });
  3470. } else {
  3471. // Browser globals
  3472. root.Gbone = factory( root, Backbone, {}, root._, ( root.jQuery || root.Zepto || root.ender ) );
  3473. }
  3474. }(this, function( root, Backbone, Gbone, _, $ ) {
  3475. // Initial Setup
  3476. // -------------
  3477. // Mixins
  3478. var observer, cleanup, transitions, state,
  3479. // State machine for Panel Views.
  3480. Manager;
  3481. // Current version of the library.
  3482. Gbone.VERSION = '0.1.0';
  3483. // Mixins
  3484. // -----------------
  3485. // Each mixin operates on an object's `prototype`.
  3486. // The observer mixin contains behavior for binding to events in a fashion
  3487. // that can be cleaned up later.
  3488. // `this.bindTo(this.collection, 'change', this.render);`
  3489. // `this.unbindFromAll();`
  3490. //
  3491. observer = function (obj) {
  3492. // On top of binding `event` to `source`, keeps track of all the event
  3493. // handlers that are bound. A single call to `unbindFromAll()` will
  3494. // unbind them.
  3495. obj.bindTo = function (source, event, callback) {
  3496. source.bind(event, callback, this);
  3497. this.bindings = this.bindings || [];
  3498. this.bindings.push({ source: source, event: event, callback: callback });
  3499. };
  3500. // Unbind all events.
  3501. obj.unbindFromAll = function () {
  3502. _.each(this.bindings, function (binding) {
  3503. binding.source.unbind(binding.event, binding.callback);
  3504. });
  3505. this.bindings = [];
  3506. };
  3507. };
  3508. // The cleanup mixin contains set of helpers for adding/managing
  3509. // immediate child Views, cleaning up and housekeeping. Used with the
  3510. // observer mixin. Maintains an internal array of child Views.
  3511. //
  3512. cleanup = function (obj) {
  3513. // Cleanup child Views, DOM events, Model/Collection events
  3514. // and events from this View.
  3515. obj.cleanup = function () {
  3516. this.unbind();
  3517. if (this.unbindFromAll) this.unbindFromAll();
  3518. this._cleanupChildren();
  3519. this.removeFromParent();
  3520. this.remove();
  3521. };
  3522. // Append a child View into the given `view`.
  3523. obj.appendChild = function (view) {
  3524. this._addChild(view);
  3525. $(this.el).append(view.el);
  3526. };
  3527. // Append a child View into a specific `container` element in the
  3528. // given `view`.
  3529. obj.appendChildInto = function (view, container) {
  3530. this._addChild(view);
  3531. this.$(container).append(view.el);
  3532. };
  3533. obj._addChild = function (view) {
  3534. this.children = this.children || [];
  3535. this.children.push(view);
  3536. view.parent = this;
  3537. };
  3538. obj._cleanupChildren = function () {
  3539. _.each(this.children, function (view) {
  3540. if (view.cleanup) view.cleanup();
  3541. });
  3542. };
  3543. // Remove this View from its parent View.
  3544. obj.removeFromParent = function () {
  3545. this.parent && this.parent.removeChild(this);
  3546. };
  3547. // Remove the given child View `view` from this View.
  3548. obj.removeChild = function (view) {
  3549. var index = _.indexOf(this.children, view);
  3550. this.children.splice(index, 1);
  3551. };
  3552. };
  3553. // The transitions mixin contains functions and objects needed for
  3554. // doing transition effects when Panel Views are activated/deactivated.
  3555. // There are default transitions provided, but you can add your own
  3556. // by using the `addTransition` method. When adding a new transition,
  3557. // it must have a definition under `effects` and `reverseEffects` objects
  3558. // of the Panel. It must also take in an arugment `callback`, which
  3559. // is a function that will be called once the transition is complete.
  3560. // Check out the default transitions code below for an example of how to setup
  3561. // your own transitions.
  3562. // Note that if jQuery is used, the default transitions
  3563. // require GFX (http://maccman.github.com/gfx/), or if Zepto is used then
  3564. // Zepto-GFX (https://github.com/gobhi/zepto-gfx).
  3565. //
  3566. transitions = function (obj) {
  3567. // Animation options for the default transitions.
  3568. var effectOptions = {
  3569. duration: 450,
  3570. easing: 'cubic-bezier(.25, .1, .25, 1)'
  3571. },
  3572. // Helper function to handle the transitions for the default
  3573. // effects.
  3574. handleTransition = function (that, anim, options, callback) {
  3575. var l = that.transitionBindings.length,
  3576. // Helper function to animate a single element.
  3577. animate = function (container, ops, index) {
  3578. if (!$.fn[anim]) throw new Error('$.fn.' + anim + ' is not available');
  3579. if ($.fn.gfx) {
  3580. // Using GFX.
  3581. container[anim](ops);
  3582. // Only call the callback function if this is the last animation.
  3583. if (index === l-1) container.queueNext(callback);
  3584. } else {
  3585. // Using Zepto-GFX. Only call the callback function if this is
  3586. // the last animation.
  3587. (index === l-1) ? container[anim](ops, callback) : container[anim](ops);
  3588. }
  3589. };
  3590. // Animate each element.
  3591. _.each(that.transitionBindings, function(elm, index) {
  3592. var container = that.$(elm);
  3593. if (container.length === 0)
  3594. throw new Error('The container element to animate is not \
  3595. availabe in this view.');
  3596. animate(container, options, index);
  3597. });
  3598. };
  3599. // The default element(s) in the Panel to animate for the transitions.
  3600. // An array of elements/selectors of the form
  3601. // `['.header', '.container', '.footer', ...]`. Each element/selector
  3602. // in the `transitionBindings` array represents a child DOM element
  3603. // within the Panel that is to be animated. If `transitionBindings`
  3604. // is not overridden, the default child element that will be animated
  3605. // in the Panel View is `.container`.
  3606. obj.transitionBindings = ['.container'];
  3607. // Transition effects for activation.
  3608. obj.effects = {
  3609. // Slide in from the left.
  3610. left: function (callback) {
  3611. var $el = $(this.el),
  3612. options = _.extend({}, effectOptions, {direction: 'left'})
  3613. handleTransition(this, 'gfxSlideIn', options, callback);
  3614. },
  3615. // Slide in from the right.
  3616. right: function (callback) {
  3617. var $el = $(this.el),
  3618. options = _.extend({}, effectOptions, {direction: 'right'});
  3619. handleTransition(this, 'gfxSlideIn', options, callback);
  3620. }
  3621. };
  3622. // Transition effects for deactivation.
  3623. obj.reverseEffects = {
  3624. // Reverse transition for the slide in from
  3625. // left: slide out to the right.
  3626. left: function (callback) {
  3627. var $el = $(this.el),
  3628. options = _.extend({}, effectOptions, {direction: 'right'});
  3629. handleTransition(this, 'gfxSlideOut', options, callback);
  3630. },
  3631. // Reverse transition for the slide in from
  3632. // right: slide out to the left.
  3633. right: function (callback) {
  3634. var $el = $(this.el),
  3635. options = _.extend({}, effectOptions, {direction: 'left'});
  3636. handleTransition(this, 'gfxSlideOut', options, callback);
  3637. }
  3638. };
  3639. // Add a new transition. The `transition` argument is an object as follows:
  3640. // `transition.effects` - Object that contains the activation transitions to be added.
  3641. // `transition.reverseEffects` - Object that contains the deactivation transitions.
  3642. // See the default transition effects defined above for an example.
  3643. obj.addTransition = function (transition) {
  3644. if (!transition.effects) throw new Error('transition.effects is not set.');
  3645. if (!transition.reverseEffects) throw new Error('transition.reverseEffects \
  3646. is not set.');
  3647. _.extend(this.effects, transition.effects);
  3648. _.extend(this.reverseEffects, transition.reverseEffects);
  3649. };
  3650. };
  3651. // The state mixin contains methods used by the Manager to handle
  3652. // activating/deactivating the Views it manages.
  3653. //
  3654. state = function (obj) {
  3655. obj.active = function () {
  3656. // Add in `active` as the first argument.
  3657. Array.prototype.unshift.call(arguments, 'active');
  3658. this.trigger.apply(this, arguments);
  3659. };
  3660. obj.isActive = function () {
  3661. return $(this.el).hasClass('active');
  3662. };
  3663. obj._activate = function (params) {
  3664. var that = this;
  3665. $(this.el).addClass('active').show();
  3666. // Once the transition is completed (if any), trigger the activated
  3667. // event.
  3668. if (params && params.trans && this.effects &&
  3669. this.effects[params.trans]) {
  3670. this.effects[params.trans].call(this, function() {
  3671. that.trigger('activated');
  3672. });
  3673. } else {
  3674. this.trigger('activated');
  3675. }
  3676. };
  3677. obj._deactivate = function (params) {
  3678. if (!this.isActive()) return;
  3679. var that = this,
  3680. callback = function () {
  3681. $(that.el).removeClass('active').hide();
  3682. that.trigger('deactivated');
  3683. };
  3684. if (params && params.trans && this.reverseEffects &&
  3685. this.reverseEffects[params.trans]) {
  3686. this.reverseEffects[params.trans].call(this, callback);
  3687. } else {
  3688. callback();
  3689. }
  3690. };
  3691. };
  3692. // Manager
  3693. // -----------------
  3694. // The Manager class is a state machine for managing Views.
  3695. //
  3696. Manager = function () {
  3697. this._setup.apply(this, arguments);
  3698. };
  3699. _.extend(Manager.prototype, Backbone.Events, {
  3700. _setup: function () {
  3701. this.views = [];
  3702. this.bind('change', this._change, this);
  3703. this.add.apply(this, arguments);
  3704. },
  3705. // Add one or more Views.
  3706. // `add(panel1, panel2, ...)`
  3707. add: function () {
  3708. _.each(Array.prototype.slice.call(arguments), function (view) {
  3709. this.addOne(view);
  3710. }, this);
  3711. },
  3712. // Add a View.
  3713. addOne: function (view) {
  3714. view.bind('active', function () {
  3715. Array.prototype.unshift.call(arguments, view);
  3716. Array.prototype.unshift.call(arguments, 'change');
  3717. this.trigger.apply(this, arguments);
  3718. }, this);
  3719. this.views.push(view);
  3720. },
  3721. // Deactivate all managed Views.
  3722. deactivateAll: function () {
  3723. Array.prototype.unshift.call(arguments, false);
  3724. Array.prototype.unshift.call(arguments, 'change');
  3725. this.trigger.apply(this, arguments);
  3726. },
  3727. // For the View passed in - `current`, check if it's available in the
  3728. // internal Views array, activate it and deactivate the others.
  3729. _change: function (current) {
  3730. var args = Array.prototype.slice.call(arguments, 1);
  3731. _.each(this.views, function (view) {
  3732. if (view === current) {
  3733. view._activate.apply(view, args);
  3734. } else {
  3735. view._deactivate.apply(view, args);
  3736. }
  3737. }, this);
  3738. }
  3739. });
  3740. // Gbone.Stage
  3741. // -----------------
  3742. // A Stage is a essentially a View that covers the
  3743. // entire viewport. It has a default `template` (that can be
  3744. // overridden), transition support and contains Panel views
  3745. // that it manages using Manager.
  3746. // Stages generally cover the entire viewport. Panels are nested
  3747. // in a Stage and can be transitioned.
  3748. // An application usually displays one Stage and Panel at a time.
  3749. // The Stage's Panels can then transition in and out to show
  3750. // different parts of the application.
  3751. //
  3752. Gbone.Stage = function (options) {
  3753. this._setup(options, 'stage');
  3754. Backbone.View.call(this, options);
  3755. };
  3756. _.extend(Gbone.Stage.prototype,
  3757. Backbone.View.prototype, {
  3758. // The default html `skeleton` template to be used by the Stage.
  3759. // It's important that the class `viewport` be set in an element
  3760. // in the `skeleton`. This element will be used by the Stage to append its
  3761. // Panel Views.
  3762. skeleton: _.template('<header></header><article class="viewport"> \
  3763. </article><footer></footer>'),
  3764. // Add Panel(s) to this Stage.
  3765. add: function () {
  3766. this._manager = this._manager || new Manager();
  3767. this._manager.add.apply(this._manager, arguments);
  3768. this._append.apply(this, arguments);
  3769. },
  3770. // Retrieve a Panel with a name of `name` in this Stage (if any).
  3771. getPanel: function(name) {
  3772. // This Stage doesn't have any Panels.
  3773. if (!this._manager) return null;
  3774. var views = this._manager.views;
  3775. return _.find(this._manager.views, function (panel) {
  3776. return panel.name === name;
  3777. });
  3778. },
  3779. // Append Panel(s) to this Stage.
  3780. _append: function () {
  3781. if (this.$('.viewport').length === 0) {
  3782. throw new Error('The Stage must have an element with \
  3783. class \'viewport\' that will be used to append the Panels to.');
  3784. }
  3785. _.each(Array.prototype.slice.call(arguments), function (panel) {
  3786. if (panel.stage !== this) panel.stage = this;
  3787. this.appendChildInto(panel, '.viewport');
  3788. }, this);
  3789. },
  3790. // Called in the constructor during initialization.
  3791. _setup: function (options) {
  3792. _.bindAll(this);
  3793. options.el ? this.el = options.el : this._ensureElement();
  3794. // If a `name` is not provided, create one. The name is used
  3795. // primarily for setting up the routes.
  3796. this.name = options.name || _.uniqueId('stage-');
  3797. $(this.el).addClass('stage').html(this.skeleton());
  3798. // Create a Router if one is not provided.
  3799. this.router = options.router || Backbone.Router.extend();
  3800. }
  3801. });
  3802. observer(Gbone.Stage.prototype);
  3803. cleanup(Gbone.Stage.prototype);
  3804. // Gbone.Panel
  3805. // -----------------
  3806. // Similar to a Stage, a Panel is just a View with transition
  3807. // support whenever it is activated/deactivated. A Panel's
  3808. // parent is a Stage and that Stage is responsible for
  3809. // managing and activating/deactivating the Panel.
  3810. // Usually only one Panel is shown in the application at one time.
  3811. //
  3812. Gbone.Panel = function (options) {
  3813. this._setup(options, 'panel');
  3814. Backbone.View.call(this, options);
  3815. };
  3816. _.extend(Gbone.Panel.prototype,
  3817. Backbone.View.prototype, {
  3818. // The default html `skeleton` to be used by the Panel. This can be overridden
  3819. // when extending the Panel View.
  3820. skeleton: _.template('<div class="container"><header></header><article></article></div>'),
  3821. // Setup the routing for the Panel.
  3822. // The route for a Panel is as follows: `[stage name]/[panel name]/trans-:trans`
  3823. // where `trans-:trans` is optional and is used to set the transition effect.
  3824. // The `callback` gets called after the routing happens. Within the callback you
  3825. // should activate the Panel by calling the `active` method on it and/or
  3826. // `render`etc...
  3827. routePanel: function (callback) {
  3828. if (this.stage) {
  3829. this.stage.router.route(this.stage.name + '/' + this.name + '/trans-:trans', this.name, callback);
  3830. this.stage.router.route(this.stage.name + '/' + this.name, this.name, callback);
  3831. } else {
  3832. throw new Error('A Stage for this Panel is not available.');
  3833. }
  3834. },
  3835. // Called in the constructor during initialization.
  3836. _setup: function (options) {
  3837. _.bindAll(this);
  3838. options.el ? this.el = options.el : this._ensureElement();
  3839. // If a `name` is not provided, create one. The `name` is used
  3840. // primarily for setting up the routes.
  3841. this.name = options.name || _.uniqueId('panel-');
  3842. $(this.el).addClass('panel').html(this.skeleton());
  3843. if (options.stage) {
  3844. this.stage = options.stage;
  3845. this.stage.add(this);
  3846. }
  3847. }
  3848. });
  3849. observer(Gbone.Panel.prototype);
  3850. state(Gbone.Panel.prototype);
  3851. cleanup(Gbone.Panel.prototype);
  3852. transitions(Gbone.Panel.prototype);
  3853. Gbone.Stage.extend = Gbone.Panel.extend = Backbone.View.extend;
  3854. return Gbone;
  3855. }));
  3856. (function($){
  3857. App = {
  3858. Views: {},
  3859. Routers: {},
  3860. Models: {},
  3861. Collections: {},
  3862. Helpers: {
  3863. Transitions: {}
  3864. },
  3865. init: function () {
  3866. var $body = $('body'),
  3867. currency = App.Models.Currency.extend(),
  3868. converterData,
  3869. currencies = new App.Collections.Currencies(),
  3870. globalRouter = new App.Routers.GlobalRouter(),
  3871. globalStage,
  3872. currencyConverter,
  3873. currencyPicker,
  3874. currencyInfo;
  3875. // Disable click events.
  3876. $body.bind('click', function (event) {
  3877. event.preventDefault();
  3878. });
  3879. $body.bind('orientationchange', function (event) {
  3880. var orientation = 'portrait';
  3881. if (Math.abs(window.orientation) === 90) orientation = 'landscape'
  3882. $body.removeClass('portrait')
  3883. .removeClass('landscape')
  3884. .addClass(orientation)
  3885. .trigger('turn', {orientation: orientation});
  3886. });
  3887. currencies.fetch({
  3888. error: function () {
  3889. throw new Error('Error loading currencies.');
  3890. }
  3891. });
  3892. currencies.bind('reset', function () {
  3893. // Model for the currency converter. This simply acts as a state machine.
  3894. converterData = new App.Models.ConverterData(null, { currencies: currencies });
  3895. // Create the global Stage.
  3896. globalStage = new App.Views.GlobalStage({
  3897. name: 'global-stage',
  3898. router: globalRouter,
  3899. el: 'body'
  3900. });
  3901. // Currency converter Panel.
  3902. currencyConverter = new App.Views.CurrencyConverter({
  3903. name: 'currency-converter',
  3904. model: converterData,
  3905. stage: globalStage
  3906. });
  3907. // Currency picker Panel.
  3908. currencyPicker = new App.Views.CurrencyPicker({
  3909. collection: currencies,
  3910. converterData: converterData,
  3911. name: 'currency-picker',
  3912. title: 'Currencies',
  3913. stage: globalStage
  3914. });
  3915. // Application info Panel.
  3916. currencyInfo = new App.Views.CurrencyInfo({
  3917. name: 'currency-info',
  3918. stage: globalStage
  3919. });
  3920. // Setup additional transitions.
  3921. currencyConverter.addTransition(App.Helpers.Transitions.upDown);
  3922. currencyPicker.addTransition(App.Helpers.Transitions.upDown);
  3923. currencyInfo.addTransition(App.Helpers.Transitions.upDown);
  3924. // Setup the route for `currencyConverter`.
  3925. currencyConverter.routePanel(function (trans) {
  3926. // Hide the children initially to avoid flicker.
  3927. $(currencyConverter.el).children().hide();
  3928. currencyConverter.active({trans: trans || 'left'});
  3929. });
  3930. // Setup the route for `currencyPicker`.
  3931. currencyPicker.routePanel(function (trans) {
  3932. // Hide the children initially to avoid flicker.
  3933. $(currencyPicker.el).children().hide();
  3934. currencyPicker.active({trans: trans || 'right'});
  3935. });
  3936. // Setup the route for `currencyInfo`.
  3937. currencyInfo.routePanel(function (trans) {
  3938. currencyInfo.active({trans: trans});
  3939. });
  3940. Backbone.history.start();
  3941. });
  3942. }
  3943. };
  3944. }).call(this, this.Zepto);
  3945. (function($){
  3946. App.Models.ConverterData = Backbone.Model.extend({
  3947. initialize: function(attributes, options) {
  3948. this.currencies = options.currencies;
  3949. this.setDefaultCurrencies();
  3950. },
  3951. defaults: {
  3952. output: 0,
  3953. input: 0
  3954. },
  3955. setDefaultCurrencies: function () {
  3956. var dflt = this.currencies.find(function (currency) {
  3957. return currency.get('code') === 'USD';
  3958. }).toJSON();
  3959. this.set({to: dflt, from: dflt});
  3960. },
  3961. format: function (num, addPoint) {
  3962. num += '';
  3963. num = num.replace(/\B(?=(?:\d{3})+(?!\d))/g, ",");
  3964. return num + (addPoint ? '.' : '');
  3965. },
  3966. rate: function () {
  3967. return this.get('from').rate * (1 / this.get('to').rate);
  3968. },
  3969. getOutput: function (input) {
  3970. return input ? (input * this.rate()).toFixed(2) : 0;
  3971. }
  3972. });
  3973. }).call(this, this.Zepto);
  3974. App.Models.Currency = Backbone.Model.extend();
  3975. App.Collections.Currencies = Backbone.Collection.extend({
  3976. model: App.Models.Currency,
  3977. url: 'currencies.json'
  3978. });
  3979. (function($){
  3980. App.Routers.GlobalRouter = Backbone.Router.extend({
  3981. routes: {
  3982. '': 'index'
  3983. },
  3984. index: function () {
  3985. // Start with the currency converter Panel.
  3986. this.navigate('global-stage/currency-converter', true);
  3987. }
  3988. });
  3989. }).call(this, this.Zepto);
  3990. (function($){
  3991. App.Views.Currency = Backbone.View.extend({
  3992. className: 'item',
  3993. events: {
  3994. 'tap': 'click'
  3995. },
  3996. initialize: function (options) {
  3997. _.bindAll(this);
  3998. this.render();
  3999. },
  4000. render: function () {
  4001. $(this.el).html(JST.currency(this.model.toJSON()));
  4002. return this;
  4003. },
  4004. click: function () {
  4005. var currency = {},
  4006. converterData = this.parent.converterData;
  4007. if (this.parent.currencyChange) {
  4008. currency[this.parent.currencyChange] = this.model.toJSON();
  4009. converterData.set(currency, {silent: true});
  4010. converterData.set({
  4011. output: converterData.getOutput(converterData.get('input'))
  4012. });
  4013. }
  4014. this.parent.stage.router.navigate('global-stage/currency-converter', true);
  4015. },
  4016. });
  4017. }).call(this, this.Zepto);
  4018. (function($){
  4019. App.Views.CurrencyConverter = Gbone.Panel.extend({
  4020. skeleton: _.template('<header><button class="info">info</button></header><article></article>'),
  4021. transitionBindings: ['header', 'article'],
  4022. events: {
  4023. 'touchstart .pad div': 'enter',
  4024. 'touchstart .pad .clear': 'clear',
  4025. 'touchstart .pad .point': 'point',
  4026. 'tap .input': 'changeFrom',
  4027. 'tap .output': 'changeTo',
  4028. 'tap .flip': 'flip',
  4029. 'tap .info': 'info'
  4030. },
  4031. initialize: function (options) {
  4032. _.bindAll(this);
  4033. $(this.el).addClass('currencies');
  4034. this.bindEvents();
  4035. this.model.addPoint = false;
  4036. this.render();
  4037. },
  4038. render: function () {
  4039. this.$('article').html(JST.index({model: this.model}));
  4040. return this;
  4041. },
  4042. bindEvents: function () {
  4043. var $el = $(this.el);
  4044. this.bindTo(this.model, 'change', this.render);
  4045. $el.bind('touchmove', function (e) {
  4046. e.preventDefault();
  4047. });
  4048. },
  4049. clear: function () {
  4050. this.model.addPoint = false;
  4051. this.model.set({
  4052. input: 0,
  4053. output: this.model.getOutput()
  4054. });
  4055. },
  4056. enter: function (event) {
  4057. var num = $(event.currentTarget).data('num'),
  4058. input;
  4059. if (!num) return;
  4060. // Stop overflows
  4061. if ((this.model.get('input') + '').length > 8)
  4062. return;
  4063. if ((this.model.get('output') + '').length > 8)
  4064. return;
  4065. num += '';
  4066. // Prefix with decimal
  4067. if (this.model.addPoint) {
  4068. this.model.addPoint = false;
  4069. num = '.' + num;
  4070. }
  4071. input = parseFloat(this.model.get('input') + num);
  4072. this.model.set({
  4073. input: input,
  4074. output: this.model.getOutput(input)
  4075. });
  4076. },
  4077. changeFrom: function () {
  4078. this.stage.trigger('currencyChange', 'from');
  4079. this.stage.router.navigate('global-stage/currency-picker', true);
  4080. },
  4081. changeTo: function () {
  4082. this.stage.trigger('currencyChange', 'to');
  4083. this.stage.router.navigate('global-stage/currency-picker', true);
  4084. },
  4085. flip: function () {
  4086. this.model.set({
  4087. to: this.model.get('from'),
  4088. from: this.model.get('to')
  4089. }, {silent: true});
  4090. this.model.set({
  4091. output: this.model.getOutput(this.model.get('input'))
  4092. });
  4093. },
  4094. info: function () {
  4095. this.stage.router.navigate('global-stage/currency-info/trans-up', true);
  4096. },
  4097. point: function () {
  4098. var input = this.model.get('input');
  4099. // Return if already has point.
  4100. if ((input % 1) !== 0) return;
  4101. this.model.addPoint = true;
  4102. this.model.set({
  4103. output: this.model.getOutput(input)
  4104. });
  4105. this.model.trigger('change');
  4106. }
  4107. });
  4108. }).call(this, this.Zepto);
  4109. (function($){
  4110. App.Views.CurrencyInfo = Gbone.Panel.extend({
  4111. skeleton: function () { return JST.info; },
  4112. transitionBindings: ['article'],
  4113. className: 'info',
  4114. events: {
  4115. 'tap header .back': 'back'
  4116. },
  4117. initialize: function (options) {
  4118. _.bindAll(this);
  4119. this.render();
  4120. },
  4121. render: function () {
  4122. return this;
  4123. },
  4124. back: function () {
  4125. this.stage.router.navigate('global-stage/currency-converter/trans-down', true);
  4126. }
  4127. });
  4128. }).call(this, this.Zepto);
  4129. (function($){
  4130. App.Views.CurrencyPicker = Gbone.Panel.extend({
  4131. skeleton: function () { return JST.currencies; },
  4132. transitionBindings: ['header', 'article'],
  4133. className: 'currenciesPicker list',
  4134. events: {
  4135. 'tap header .back': 'back'
  4136. },
  4137. initialize: function (options) {
  4138. _.bindAll(this);
  4139. this.converterData = options.converterData;
  4140. this.title = options.title;
  4141. this.bindEvents();
  4142. this.render();
  4143. },
  4144. render: function () {
  4145. this.$('header h2').html(this.title);
  4146. this.addAll();
  4147. return this;
  4148. },
  4149. bindEvents: function () {
  4150. var that = this;
  4151. this.bindTo(this.stage, 'currencyChange', function (currencyChange) {
  4152. that.currencyChange = currencyChange;
  4153. });
  4154. },
  4155. addAll: function () {
  4156. this.collection.each(this.addOne);
  4157. },
  4158. addOne: function (model) {
  4159. var view = new App.Views.Currency({model: model});
  4160. view.render();
  4161. this.appendChildInto(view, 'article');
  4162. },
  4163. back: function () {
  4164. this.stage.router.navigate('global-stage/currency-converter', true);
  4165. }
  4166. });
  4167. }).call(this, this.Zepto);
  4168. (function($){
  4169. App.Views.GlobalStage = Gbone.Stage.extend({
  4170. skeleton: _.template('<article class="viewport"></article>')
  4171. });
  4172. }).call(this, this.Zepto);
  4173. // `up` and `down` Panel transitions.
  4174. //
  4175. (function($){
  4176. var EASING = 'cubic-bezier(.25, .1, .25, 1)',
  4177. DURATION = 500;
  4178. App.Helpers.Transitions.upDown = {
  4179. effects: {
  4180. up: function (callback) {
  4181. var that = this,
  4182. $el = $(that.el),
  4183. prevZ = $el.css('z-index'),
  4184. l = that.transitionBindings.length,
  4185. animate = function (container, index) {
  4186. container.transform({
  4187. translate3d: '0,' + document.height + 'px,0'
  4188. }).show().animate({
  4189. translate3d: '0,0,0'
  4190. }, DURATION, EASING, function () {
  4191. // Only call the calback function when all the animations are done.
  4192. if (index === l-1) {
  4193. $el.css('z-index', prevZ);
  4194. callback();
  4195. }
  4196. });
  4197. };
  4198. _.each(that.transitionBindings, function(elm, index) {
  4199. var container = that.$(elm);
  4200. if (container.length === 0)
  4201. throw new Error('The container element to animate is not \
  4202. availabe in this view.');
  4203. $el.css('z-index', 100);
  4204. animate(container, index);
  4205. });
  4206. },
  4207. down: function (callback) {
  4208. var that = this;
  4209. _.each(that.transitionBindings, function(elm) {
  4210. var container = that.$(elm);
  4211. if (container.length === 0)
  4212. throw new Error('The container element to animate is not \
  4213. availabe in this view.');
  4214. container.show();
  4215. });
  4216. }
  4217. },
  4218. reverseEffects: {
  4219. up: function (callback) {
  4220. var that = this;
  4221. _.each(that.transitionBindings, function(elm) {
  4222. var container = that.$(elm);
  4223. if (container.length === 0)
  4224. throw new Error('The container element to animate is not \
  4225. availabe in this view.');
  4226. setTimeout(function () {
  4227. container.hide();
  4228. callback();
  4229. }, DURATION);
  4230. });
  4231. },
  4232. down: function (callback) {
  4233. var that = this,
  4234. $el = $(that.el),
  4235. prevZ = $el.css('z-index'),
  4236. l = that.transitionBindings.length,
  4237. animate = function (container, index) {
  4238. container.show().animate({
  4239. translate3d: '0,' + document.height + 'px,0'
  4240. }, DURATION, EASING, function () {
  4241. container.hide().transform({translate3d: '0,0,0'});
  4242. // Only call the calback function when all the animations are done.
  4243. if (index === l-1) {
  4244. $el.css('z-index', prevZ);
  4245. callback();
  4246. }
  4247. });
  4248. };
  4249. _.each(that.transitionBindings, function(elm, index) {
  4250. var container = that.$(elm);
  4251. if (container.length === 0)
  4252. throw new Error('The container element to animate is not \
  4253. availabe in this view.');
  4254. $el.css('z-index', 100);
  4255. animate(container, index);
  4256. });
  4257. }
  4258. }
  4259. };
  4260. }).call(this, this.Zepto);(function(){
  4261. window.JST = window.JST || {};
  4262. window.JST['currencies'] = _.template('<header>\n <h2></h2>\n <button class="back">Back</button>\n</header>\n<article></article>');
  4263. window.JST['currency'] = _.template('<span><%= name %></span>\n<em><%= symbol %></em>\n<em>(<%= code %>)</em>');
  4264. window.JST['index'] = _.template('<section class="status">\n <span><%= model.get(\'from\').code %><em>?</em><%= model.get(\'to\').code %></span>\n</section>\n\n<section class="input">\n <h1><%= model.format(model.get(\'input\'), model.addPoint) %></h1>\n <h2><em><%= model.get(\'from\').symbol %></em> <%= model.get(\'from\').name %></h2>\n</section>\n\n<section class="flip">\n <button>Flip</button>\n</section>\n\n<section class="output">\n <h1><%= model.format(model.get(\'output\')) %></h1>\n <h2><em><%= model.get(\'to\').symbol %></em> <%= model.get(\'to\').name %></h2>\n</section>\n\n<article class="pad">\n <div data-num="1">1</div>\n <div data-num="2">2</div>\n <div data-num="3">3</div>\n <div data-num="0">0</div>\n <div data-num="4">4</div>\n <div data-num="5">5</div>\n <div data-num="6">6</div>\n <div class="point">.</div>\n <div data-num="7">7</div>\n <div data-num="8">8</div>\n <div data-num="9">9</div>\n <div class="clear">Clear</div>\n</article>');
  4265. window.JST['info'] = _.template('<article>\n <header>\n <h2>Info</h2>\n <button class="back">Back</button>\n </header>\n <div id="about">\n <p>\n This application is a re-write of the <a href="https://github.com/benschwarz/currency.io">currency.io</a> web application. It was written to demonstrate the Gbone.js framework.\n </p>\n <p>\n Best viewed on iOS5.\n </p>\n </div>\n</article>');
  4266. })();