/example/public/application.js
JavaScript | 4892 lines | 4106 code | 272 blank | 514 comment | 572 complexity | d759c46b510d0bc07d5e36681b40bba3 MD5 | raw file
- // Zepto.js
- // (c) 2010, 2011 Thomas Fuchs
- // Zepto.js may be freely distributed under the MIT license.
- (function(undefined){
- if (String.prototype.trim === undefined) // fix for iOS 3.2
- String.prototype.trim = function(){ return this.replace(/^\s+/, '').replace(/\s+$/, '') };
- // For iOS 3.x
- // from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
- if (Array.prototype.reduce === undefined)
- Array.prototype.reduce = function(fun){
- if(this === void 0 || this === null) throw new TypeError();
- var t = Object(this), len = t.length >>> 0, k = 0, accumulator;
- if(typeof fun != 'function') throw new TypeError();
- if(len == 0 && arguments.length == 1) throw new TypeError();
- if(arguments.length >= 2)
- accumulator = arguments[1];
- else
- do{
- if(k in t){
- accumulator = t[k++];
- break;
- }
- if(++k >= len) throw new TypeError();
- } while (true);
- while (k < len){
- if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t);
- k++;
- }
- return accumulator;
- };
- })();
- // Zepto.js
- // (c) 2010, 2011 Thomas Fuchs
- // Zepto.js may be freely distributed under the MIT license.
- var Zepto = (function() {
- var undefined, key, $$, classList, emptyArray = [], slice = emptyArray.slice,
- document = window.document,
- elementDisplay = {}, classCache = {},
- getComputedStyle = document.defaultView.getComputedStyle,
- cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 },
- fragmentRE = /^\s*<(\w+)[^>]*>/,
- elementTypes = [1, 9, 11],
- adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ],
- table = document.createElement('table'),
- tableRow = document.createElement('tr'),
- containers = {
- 'tr': document.createElement('tbody'),
- 'tbody': table, 'thead': table, 'tfoot': table,
- 'td': tableRow, 'th': tableRow,
- '*': document.createElement('div')
- },
- readyRE = /complete|loaded|interactive/,
- classSelectorRE = /^\.([\w-]+)$/,
- idSelectorRE = /^#([\w-]+)$/,
- tagSelectorRE = /^[\w-]+$/;
- function isF(value) { return ({}).toString.call(value) == "[object Function]" }
- function isO(value) { return value instanceof Object }
- function isA(value) { return value instanceof Array }
- function likeArray(obj) { return typeof obj.length == 'number' }
- function compact(array) { return array.filter(function(item){ return item !== undefined && item !== null }) }
- function flatten(array) { return array.length > 0 ? [].concat.apply([], array) : array }
- function camelize(str) { return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) }
- function dasherize(str){
- return str.replace(/::/g, '/')
- .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
- .replace(/([a-z\d])([A-Z])/g, '$1_$2')
- .replace(/_/g, '-')
- .toLowerCase();
- }
- function uniq(array) { return array.filter(function(item,index,array){ return array.indexOf(item) == index }) }
- function classRE(name){
- return name in classCache ?
- classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'));
- }
- function maybeAddPx(name, value) { return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value; }
- function defaultDisplay(nodeName) {
- var element, display;
- if (!elementDisplay[nodeName]) {
- element = document.createElement(nodeName);
- document.body.appendChild(element);
- display = getComputedStyle(element, '').getPropertyValue("display");
- element.parentNode.removeChild(element);
- display == "none" && (display = "block");
- elementDisplay[nodeName] = display;
- }
- return elementDisplay[nodeName];
- }
- function fragment(html, name) {
- if (name === undefined) fragmentRE.test(html) && RegExp.$1;
- if (!(name in containers)) name = '*';
- var container = containers[name];
- container.innerHTML = '' + html;
- return slice.call(container.childNodes);
- }
- function Z(dom, selector){
- dom = dom || emptyArray;
- dom.__proto__ = Z.prototype;
- dom.selector = selector || '';
- return dom;
- }
- function $(selector, context){
- if (!selector) return Z();
- if (context !== undefined) return $(context).find(selector);
- else if (isF(selector)) return $(document).ready(selector);
- else if (selector instanceof Z) return selector;
- else {
- var dom;
- if (isA(selector)) dom = compact(selector);
- else if (elementTypes.indexOf(selector.nodeType) >= 0 || selector === window)
- dom = [selector], selector = null;
- else if (fragmentRE.test(selector))
- dom = fragment(selector.trim(), RegExp.$1), selector = null;
- else if (selector.nodeType && selector.nodeType == 3) dom = [selector];
- else dom = $$(document, selector);
- return Z(dom, selector);
- }
- }
- $.extend = function(target){
- slice.call(arguments, 1).forEach(function(source) {
- for (key in source) target[key] = source[key];
- })
- return target;
- }
- $.qsa = $$ = function(element, selector){
- var found;
- return (element === document && idSelectorRE.test(selector)) ?
- ( (found = element.getElementById(RegExp.$1)) ? [found] : emptyArray ) :
- slice.call(
- classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) :
- tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) :
- element.querySelectorAll(selector)
- );
- }
- function filtered(nodes, selector){
- return selector === undefined ? $(nodes) : $(nodes).filter(selector);
- }
- function funcArg(context, arg, idx, payload){
- return isF(arg) ? arg.call(context, idx, payload) : arg;
- }
- $.isFunction = isF;
- $.isObject = isO;
- $.isArray = isA;
- $.map = function(elements, callback) {
- var value, values = [], i, key;
- if (likeArray(elements))
- for (i = 0; i < elements.length; i++) {
- value = callback(elements[i], i);
- if (value != null) values.push(value);
- }
- else
- for (key in elements) {
- value = callback(elements[key], key);
- if (value != null) values.push(value);
- }
- return flatten(values);
- }
- $.each = function(elements, callback) {
- var i, key;
- if (likeArray(elements))
- for(i = 0; i < elements.length; i++) {
- if(callback(i, elements[i]) === false) return elements;
- }
- else
- for(key in elements) {
- if(callback(key, elements[key]) === false) return elements;
- }
- return elements;
- }
- $.fn = {
- forEach: emptyArray.forEach,
- reduce: emptyArray.reduce,
- push: emptyArray.push,
- indexOf: emptyArray.indexOf,
- concat: emptyArray.concat,
- map: function(fn){
- return $.map(this, function(el, i){ return fn.call(el, i, el) });
- },
- slice: function(){
- return $(slice.apply(this, arguments));
- },
- ready: function(callback){
- if (readyRE.test(document.readyState)) callback($);
- else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false);
- return this;
- },
- get: function(idx){ return idx === undefined ? this : this[idx] },
- size: function(){ return this.length },
- remove: function () {
- return this.each(function () {
- if (this.parentNode != null) {
- this.parentNode.removeChild(this);
- }
- });
- },
- each: function(callback){
- this.forEach(function(el, idx){ callback.call(el, idx, el) });
- return this;
- },
- filter: function(selector){
- return $([].filter.call(this, function(element){
- return element.parentNode && $$(element.parentNode, selector).indexOf(element) >= 0;
- }));
- },
- end: function(){
- return this.prevObject || $();
- },
- andSelf:function(){
- return this.add(this.prevObject || $())
- },
- add:function(selector,context){
- return $(uniq(this.concat($(selector,context))));
- },
- is: function(selector){
- return this.length > 0 && $(this[0]).filter(selector).length > 0;
- },
- not: function(selector){
- var nodes=[];
- if (isF(selector) && selector.call !== undefined)
- this.each(function(idx){
- if (!selector.call(this,idx)) nodes.push(this);
- });
- else {
- var excludes = typeof selector == 'string' ? this.filter(selector) :
- (likeArray(selector) && isF(selector.item)) ? slice.call(selector) : $(selector);
- this.forEach(function(el){
- if (excludes.indexOf(el) < 0) nodes.push(el);
- });
- }
- return $(nodes);
- },
- eq: function(idx){
- return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1);
- },
- first: function(){ var el = this[0]; return el && !isO(el) ? el : $(el) },
- last: function(){ var el = this[this.length - 1]; return el && !isO(el) ? el : $(el) },
- find: function(selector){
- var result;
- if (this.length == 1) result = $$(this[0], selector);
- else result = this.map(function(){ return $$(this, selector) });
- return $(result);
- },
- closest: function(selector, context){
- var node = this[0], candidates = $$(context || document, selector);
- if (!candidates.length) node = null;
- while (node && candidates.indexOf(node) < 0)
- node = node !== context && node !== document && node.parentNode;
- return $(node);
- },
- parents: function(selector){
- var ancestors = [], nodes = this;
- while (nodes.length > 0)
- nodes = $.map(nodes, function(node){
- if ((node = node.parentNode) && node !== document && ancestors.indexOf(node) < 0) {
- ancestors.push(node);
- return node;
- }
- });
- return filtered(ancestors, selector);
- },
- parent: function(selector){
- return filtered(uniq(this.pluck('parentNode')), selector);
- },
- children: function(selector){
- return filtered(this.map(function(){ return slice.call(this.children) }), selector);
- },
- siblings: function(selector){
- return filtered(this.map(function(i, el){
- return slice.call(el.parentNode.children).filter(function(child){ return child!==el });
- }), selector);
- },
- empty: function(){ return this.each(function(){ this.innerHTML = '' }) },
- pluck: function(property){ return this.map(function(){ return this[property] }) },
- show: function(){
- return this.each(function() {
- this.style.display == "none" && (this.style.display = null);
- if (getComputedStyle(this, '').getPropertyValue("display") == "none") {
- this.style.display = defaultDisplay(this.nodeName)
- }
- })
- },
- replaceWith: function(newContent) {
- return this.each(function() {
- $(this).before(newContent).remove();
- });
- },
- wrap: function(newContent) {
- return this.each(function() {
- $(this).wrapAll($(newContent)[0].cloneNode(false));
- });
- },
- wrapAll: function(newContent) {
- if (this[0]) {
- $(this[0]).before(newContent = $(newContent));
- newContent.append(this);
- }
- return this;
- },
- unwrap: function(){
- this.parent().each(function(){
- $(this).replaceWith($(this).children());
- });
- return this;
- },
- hide: function(){
- return this.css("display", "none")
- },
- toggle: function(setting){
- return (setting === undefined ? this.css("display") == "none" : setting) ? this.show() : this.hide();
- },
- prev: function(){ return $(this.pluck('previousElementSibling')) },
- next: function(){ return $(this.pluck('nextElementSibling')) },
- html: function(html){
- return html === undefined ?
- (this.length > 0 ? this[0].innerHTML : null) :
- this.each(function (idx) {
- var originHtml = this.innerHTML;
- $(this).empty().append( funcArg(this, html, idx, originHtml) );
- });
- },
- text: function(text){
- return text === undefined ?
- (this.length > 0 ? this[0].textContent : null) :
- this.each(function(){ this.textContent = text });
- },
- attr: function(name, value){
- var res;
- return (typeof name == 'string' && value === undefined) ?
- (this.length == 0 ? undefined :
- (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() :
- (!(res = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : res
- ) :
- this.each(function(idx){
- if (isO(name)) for (key in name) this.setAttribute(key, name[key])
- else this.setAttribute(name, funcArg(this, value, idx, this.getAttribute(name)));
- });
- },
- removeAttr: function(name) {
- return this.each(function() { this.removeAttribute(name); });
- },
- data: function(name, value){
- return this.attr('data-' + name, value);
- },
- val: function(value){
- return (value === undefined) ?
- (this.length > 0 ? this[0].value : null) :
- this.each(function(idx){
- this.value = funcArg(this, value, idx, this.value);
- });
- },
- offset: function(){
- if(this.length==0) return null;
- var obj = this[0].getBoundingClientRect();
- return {
- left: obj.left + window.pageXOffset,
- top: obj.top + window.pageYOffset,
- width: obj.width,
- height: obj.height
- };
- },
- css: function(property, value){
- if (value === undefined && typeof property == 'string') {
- return(
- this.length == 0
- ? undefined
- : this[0].style[camelize(property)] || getComputedStyle(this[0], '').getPropertyValue(property)
- );
- }
- var css = '';
- for (key in property) css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';';
- if (typeof property == 'string') css = dasherize(property) + ":" + maybeAddPx(property, value);
- return this.each(function() { this.style.cssText += ';' + css });
- },
- index: function(element){
- return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]);
- },
- hasClass: function(name){
- if (this.length < 1) return false;
- else return classRE(name).test(this[0].className);
- },
- addClass: function(name){
- return this.each(function(idx) {
- classList = [];
- var cls = this.className, newName = funcArg(this, name, idx, cls);
- newName.split(/\s+/g).forEach(function(klass) {
- if (!$(this).hasClass(klass)) {
- classList.push(klass)
- }
- }, this);
- classList.length && (this.className += (cls ? " " : "") + classList.join(" "))
- });
- },
- removeClass: function(name){
- return this.each(function(idx) {
- if(name === undefined)
- return this.className = '';
- classList = this.className;
- funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass) {
- classList = classList.replace(classRE(klass), " ")
- });
- this.className = classList.trim()
- });
- },
- toggleClass: function(name, when){
- return this.each(function(idx){
- var newName = funcArg(this, name, idx, this.className);
- (when === undefined ? !$(this).hasClass(newName) : when) ?
- $(this).addClass(newName) : $(this).removeClass(newName);
- });
- }
- };
- 'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){
- var fn = $.fn[property];
- $.fn[property] = function() {
- var ret = fn.apply(this, arguments);
- ret.prevObject = this;
- return ret;
- }
- });
- ['width', 'height'].forEach(function(dimension){
- $.fn[dimension] = function(value) {
- var offset, Dimension = dimension.replace(/./, function(m) { return m[0].toUpperCase() });
- if (value === undefined) return this[0] == window ? window['inner' + Dimension] :
- this[0] == document ? document.documentElement['offset' + Dimension] :
- (offset = this.offset()) && offset[dimension];
- else return this.each(function(idx){
- var el = $(this);
- el.css(dimension, funcArg(this, value, idx, el[dimension]()));
- });
- }
- });
- function insert(operator, target, node) {
- var parent = (operator % 2) ? target : target.parentNode;
- parent && parent.insertBefore(node,
- !operator ? target.nextSibling : // after
- operator == 1 ? parent.firstChild : // prepend
- operator == 2 ? target : // before
- null); // append
- }
- function traverseNode (node, fun) {
- fun(node);
- for (var key in node.childNodes) {
- traverseNode(node.childNodes[key], fun);
- }
- }
- adjacencyOperators.forEach(function(key, operator) {
- $.fn[key] = function(html){
- var nodes = isO(html) ? html : fragment(html);
- if (!('length' in nodes) || nodes.nodeType) nodes = [nodes];
- if (nodes.length < 1) return this;
- var size = this.length, copyByClone = size > 1, inReverse = operator < 2;
- return this.each(function(index, target){
- for (var i = 0; i < nodes.length; i++) {
- var node = nodes[inReverse ? nodes.length-i-1 : i];
- traverseNode(node, function (node) {
- if (node.nodeName != null && node.nodeName.toUpperCase() === 'SCRIPT' && (!node.type || node.type === 'text/javascript')) {
- window['eval'].call(window, node.innerHTML);
- }
- });
- if (copyByClone && index < size - 1) node = node.cloneNode(true);
- insert(operator, target, node);
- }
- });
- };
- var reverseKey = (operator % 2) ? key+'To' : 'insert'+(operator ? 'Before' : 'After');
- $.fn[reverseKey] = function(html) {
- $(html)[key](this);
- return this;
- };
- });
- Z.prototype = $.fn;
- return $;
- })();
- window.Zepto = Zepto;
- '$' in window || (window.$ = Zepto);
- // Zepto.js
- // (c) 2010, 2011 Thomas Fuchs
- // Zepto.js may be freely distributed under the MIT license.
- (function($){
- var $$ = $.qsa, handlers = {}, _zid = 1, specialEvents={};
- specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents';
- function zid(element) {
- return element._zid || (element._zid = _zid++);
- }
- function findHandlers(element, event, fn, selector) {
- event = parse(event);
- if (event.ns) var matcher = matcherFor(event.ns);
- return (handlers[zid(element)] || []).filter(function(handler) {
- return handler
- && (!event.e || handler.e == event.e)
- && (!event.ns || matcher.test(handler.ns))
- && (!fn || handler.fn == fn)
- && (!selector || handler.sel == selector);
- });
- }
- function parse(event) {
- var parts = ('' + event).split('.');
- return {e: parts[0], ns: parts.slice(1).sort().join(' ')};
- }
- function matcherFor(ns) {
- return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)');
- }
- function eachEvent(events, fn, iterator){
- if ($.isObject(events)) $.each(events, iterator);
- else events.split(/\s/).forEach(function(type){ iterator(type, fn) });
- }
- function add(element, events, fn, selector, getDelegate){
- var id = zid(element), set = (handlers[id] || (handlers[id] = []));
- eachEvent(events, fn, function(event, fn){
- var delegate = getDelegate && getDelegate(fn, event),
- callback = delegate || fn;
- var proxyfn = function (event) {
- var result = callback.apply(element, [event].concat(event.data));
- if (result === false) event.preventDefault();
- return result;
- };
- var handler = $.extend(parse(event), {fn: fn, proxy: proxyfn, sel: selector, del: delegate, i: set.length});
- set.push(handler);
- element.addEventListener(handler.e, proxyfn, false);
- });
- }
- function remove(element, events, fn, selector){
- var id = zid(element);
- eachEvent(events || '', fn, function(event, fn){
- findHandlers(element, event, fn, selector).forEach(function(handler){
- delete handlers[id][handler.i];
- element.removeEventListener(handler.e, handler.proxy, false);
- });
- });
- }
- $.event = { add: add, remove: remove }
- $.fn.bind = function(event, callback){
- return this.each(function(){
- add(this, event, callback);
- });
- };
- $.fn.unbind = function(event, callback){
- return this.each(function(){
- remove(this, event, callback);
- });
- };
- $.fn.one = function(event, callback){
- return this.each(function(i, element){
- add(this, event, callback, null, function(fn, type){
- return function(){
- var result = fn.apply(element, arguments);
- remove(element, type, fn);
- return result;
- }
- });
- });
- };
- var returnTrue = function(){return true},
- returnFalse = function(){return false},
- eventMethods = {
- preventDefault: 'isDefaultPrevented',
- stopImmediatePropagation: 'isImmediatePropagationStopped',
- stopPropagation: 'isPropagationStopped'
- };
- function createProxy(event) {
- var proxy = $.extend({originalEvent: event}, event);
- $.each(eventMethods, function(name, predicate) {
- proxy[name] = function(){
- this[predicate] = returnTrue;
- return event[name].apply(event, arguments);
- };
- proxy[predicate] = returnFalse;
- })
- return proxy;
- }
- // emulates the 'defaultPrevented' property for browsers that have none
- function fix(event) {
- if (!('defaultPrevented' in event)) {
- event.defaultPrevented = false;
- var prevent = event.preventDefault;
- event.preventDefault = function() {
- this.defaultPrevented = true;
- prevent.call(this);
- }
- }
- }
- $.fn.delegate = function(selector, event, callback){
- return this.each(function(i, element){
- add(element, event, callback, selector, function(fn){
- return function(e){
- var evt, match = $(e.target).closest(selector, element).get(0);
- if (match) {
- evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element});
- return fn.apply(match, [evt].concat([].slice.call(arguments, 1)));
- }
- }
- });
- });
- };
- $.fn.undelegate = function(selector, event, callback){
- return this.each(function(){
- remove(this, event, callback, selector);
- });
- }
- $.fn.live = function(event, callback){
- $(document.body).delegate(this.selector, event, callback);
- return this;
- };
- $.fn.die = function(event, callback){
- $(document.body).undelegate(this.selector, event, callback);
- return this;
- };
- $.fn.on = function(event, selector, callback){
- return selector === undefined || $.isFunction(selector) ?
- this.bind(event, selector) : this.delegate(selector, event, callback);
- };
- $.fn.off = function(event, selector, callback){
- return selector === undefined || $.isFunction(selector) ?
- this.unbind(event, selector) : this.undelegate(selector, event, callback);
- };
- $.fn.trigger = function(event, data){
- if (typeof event == 'string') event = $.Event(event);
- fix(event);
- event.data = data;
- return this.each(function(){ this.dispatchEvent(event) });
- };
- // triggers event handlers on current element just as if an event occurred,
- // doesn't trigger an actual event, doesn't bubble
- $.fn.triggerHandler = function(event, data){
- var e, result;
- this.each(function(i, element){
- e = createProxy(typeof event == 'string' ? $.Event(event) : event);
- e.data = data; e.target = element;
- $.each(findHandlers(element, event.type || event), function(i, handler){
- result = handler.proxy(e);
- if (e.isImmediatePropagationStopped()) return false;
- });
- });
- return result;
- };
- // shortcut methods for `.bind(event, fn)` for each event type
- ('focusin focusout load resize scroll unload click dblclick '+
- 'mousedown mouseup mousemove mouseover mouseout '+
- 'change select keydown keypress keyup error').split(' ').forEach(function(event) {
- $.fn[event] = function(callback){ return this.bind(event, callback) };
- });
- ['focus', 'blur'].forEach(function(name) {
- $.fn[name] = function(callback) {
- if (callback) this.bind(name, callback);
- else if (this.length) try { this.get(0)[name]() } catch(e){};
- return this;
- };
- });
- $.Event = function(type, props) {
- var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true;
- if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]);
- event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null);
- return event;
- };
- })(Zepto);
- // Zepto.js
- // (c) 2010, 2011 Thomas Fuchs
- // Zepto.js may be freely distributed under the MIT license.
- (function($){
- function detect(ua){
- var os = (this.os = {}), browser = (this.browser = {}),
- webkit = ua.match(/WebKit\/([\d.]+)/),
- android = ua.match(/(Android)\s+([\d.]+)/),
- ipad = ua.match(/(iPad).*OS\s([\d_]+)/),
- iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/),
- webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/),
- touchpad = webos && ua.match(/TouchPad/),
- blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/);
- if (webkit) browser.version = webkit[1];
- browser.webkit = !!webkit;
- if (android) os.android = true, os.version = android[2];
- if (iphone) os.ios = true, os.version = iphone[2].replace(/_/g, '.'), os.iphone = true;
- if (ipad) os.ios = true, os.version = ipad[2].replace(/_/g, '.'), os.ipad = true;
- if (webos) os.webos = true, os.version = webos[2];
- if (touchpad) os.touchpad = true;
- if (blackberry) os.blackberry = true, os.version = blackberry[2];
- }
- // ### $.os
- //
- // Object containing information about browser platform
- //
- // *Example:*
- //
- // $.os.ios // => true if running on Apple iOS
- // $.os.android // => true if running on Android
- // $.os.webos // => true if running on HP/Palm WebOS
- // $.os.touchpad // => true if running on a HP TouchPad
- // $.os.version // => string with a version number, e.g.
- // "4.0", "3.1.1", "2.1", etc.
- // $.os.iphone // => true if running on iPhone
- // $.os.ipad // => true if running on iPad
- // $.os.blackberry // => true if running on BlackBerry
- //
- // ### $.browser
- //
- // *Example:*
- //
- // $.browser.webkit // => true if the browser is WebKit-based
- // $.browser.version // => WebKit version string
- detect.call($, navigator.userAgent);
- // make available to unit tests
- $.__detect = detect;
- })(Zepto);
- // Zepto.js
- // (c) 2010, 2011 Thomas Fuchs
- // Zepto.js may be freely distributed under the MIT license.
- (function($, undefined){
- var prefix = '', eventPrefix, endEventName, endAnimationName,
- vendors = {Webkit: 'webkit', Moz: '', O: 'o', ms: 'MS'},
- document = window.document, testEl = document.createElement('div'),
- supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i;
- function downcase(str) { return str.toLowerCase() }
- function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : downcase(name) };
- $.each(vendors, function(vendor, event){
- if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
- prefix = '-' + downcase(vendor) + '-';
- eventPrefix = event;
- return false;
- }
- });
- $.fx = {
- off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined),
- cssPrefix: prefix,
- transitionEnd: normalizeEvent('TransitionEnd'),
- animationEnd: normalizeEvent('AnimationEnd')
- };
- $.fn.animate = function(properties, duration, ease, callback){
- if ($.isObject(duration))
- ease = duration.easing, callback = duration.complete, duration = duration.duration;
- if (duration) duration = duration / 1000;
- return this.anim(properties, duration, ease, callback);
- };
- $.fn.anim = function(properties, duration, ease, callback){
- var transforms, cssProperties = {}, key, that = this, wrappedCallback, endEvent = $.fx.transitionEnd;
- if (duration === undefined) duration = 0.4;
- if ($.fx.off) duration = 0;
- if (typeof properties == 'string') {
- // keyframe animation
- cssProperties[prefix + 'animation-name'] = properties;
- cssProperties[prefix + 'animation-duration'] = duration + 's';
- endEvent = $.fx.animationEnd;
- } else {
- // CSS transitions
- for (key in properties)
- if (supportedTransforms.test(key)) {
- transforms || (transforms = []);
- transforms.push(key + '(' + properties[key] + ')');
- }
- else cssProperties[key] = properties[key];
- if (transforms) cssProperties[prefix + 'transform'] = transforms.join(' ');
- if (!$.fx.off) cssProperties[prefix + 'transition'] = 'all ' + duration + 's ' + (ease || '');
- }
- wrappedCallback = function(){
- var props = {};
- props[prefix + 'transition'] = props[prefix + 'animation-name'] = 'none';
- $(this).css(props);
- callback && callback.call(this);
- }
- if (duration > 0) this.one(endEvent, wrappedCallback);
- setTimeout(function() {
- that.css(cssProperties);
- if (duration <= 0) setTimeout(function() {
- that.each(function(){ wrappedCallback.call(this) });
- }, 0);
- }, 0);
- return this;
- };
- testEl = null;
- })(Zepto);
- // Zepto.js
- // (c) 2010, 2011 Thomas Fuchs
- // Zepto.js may be freely distributed under the MIT license.
- (function($){
- var jsonpID = 0,
- isObject = $.isObject,
- document = window.document,
- key,
- name;
- // trigger a custom event and return false if it was cancelled
- function triggerAndReturn(context, eventName, data) {
- var event = $.Event(eventName);
- $(context).trigger(event, data);
- return !event.defaultPrevented;
- }
- // trigger an Ajax "global" event
- function triggerGlobal(settings, context, eventName, data) {
- if (settings.global) return triggerAndReturn(context || document, eventName, data);
- }
- // Number of active Ajax requests
- $.active = 0;
- function ajaxStart(settings) {
- if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart');
- }
- function ajaxStop(settings) {
- if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop');
- }
- // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable
- function ajaxBeforeSend(xhr, settings) {
- var context = settings.context;
- if (settings.beforeSend.call(context, xhr, settings) === false ||
- triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false)
- return false;
- triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]);
- }
- function ajaxSuccess(data, xhr, settings) {
- var context = settings.context, status = 'success';
- settings.success.call(context, data, status, xhr);
- triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]);
- ajaxComplete(status, xhr, settings);
- }
- // type: "timeout", "error", "abort", "parsererror"
- function ajaxError(error, type, xhr, settings) {
- var context = settings.context;
- settings.error.call(context, xhr, type, error);
- triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error]);
- ajaxComplete(type, xhr, settings);
- }
- // status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
- function ajaxComplete(status, xhr, settings) {
- var context = settings.context;
- settings.complete.call(context, xhr, status);
- triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]);
- ajaxStop(settings);
- }
- // Empty function, used as default callback
- function empty() {}
- // ### $.ajaxJSONP
- //
- // Load JSON from a server in a different domain (JSONP)
- //
- // *Arguments:*
- //
- // options — object that configure the request,
- // see avaliable options below
- //
- // *Avaliable options:*
- //
- // url — url to which the request is sent
- // success — callback that is executed if the request succeeds
- // error — callback that is executed if the server drops error
- // context — in which context to execute the callbacks in
- //
- // *Example:*
- //
- // $.ajaxJSONP({
- // url: 'http://example.com/projects?callback=?',
- // success: function (data) {
- // projects.push(json);
- // }
- // });
- //
- $.ajaxJSONP = function(options){
- var callbackName = 'jsonp' + (++jsonpID),
- script = document.createElement('script'),
- abort = function(){
- $(script).remove();
- if (callbackName in window) window[callbackName] = empty;
- ajaxComplete(xhr, options, 'abort');
- },
- xhr = { abort: abort }, abortTimeout;
- window[callbackName] = function(data){
- clearTimeout(abortTimeout);
- $(script).remove();
- delete window[callbackName];
- ajaxSuccess(data, xhr, options);
- };
- script.src = options.url.replace(/=\?/, '=' + callbackName);
- $('head').append(script);
- if (options.timeout > 0) abortTimeout = setTimeout(function(){
- xhr.abort();
- ajaxComplete(xhr, options, 'timeout');
- }, options.timeout);
- return xhr;
- };
- // ### $.ajaxSettings
- //
- // AJAX settings
- //
- $.ajaxSettings = {
- // Default type of request
- type: 'GET',
- // Callback that is executed before request
- beforeSend: empty,
- // Callback that is executed if the request succeeds
- success: empty,
- // Callback that is executed the the server drops error
- error: empty,
- // Callback that is executed on request complete (both: error and success)
- complete: empty,
- // The context for the callbacks
- context: null,
- // Whether to trigger "global" Ajax events
- global: true,
- // Transport
- xhr: function () {
- return new window.XMLHttpRequest();
- },
- // MIME types mapping
- accepts: {
- script: 'text/javascript, application/javascript',
- json: 'application/json',
- xml: 'application/xml, text/xml',
- html: 'text/html',
- text: 'text/plain'
- },
- // Whether the request is to another domain
- crossDomain: false,
- // Default timeout
- timeout: 0
- };
- // ### $.ajax
- //
- // Perform AJAX request
- //
- // *Arguments:*
- //
- // options — object that configure the request,
- // see avaliable options below
- //
- // *Avaliable options:*
- //
- // type ('GET') — type of request GET / POST
- // url (window.location) — url to which the request is sent
- // data — data to send to server,
- // can be string or object
- // dataType ('json') — what response type you accept from
- // the server:
- // 'json', 'xml', 'html', or 'text'
- // timeout (0) — request timeout
- // beforeSend — callback that is executed before
- // request send
- // complete — callback that is executed on request
- // complete (both: error and success)
- // success — callback that is executed if
- // the request succeeds
- // error — callback that is executed if
- // the server drops error
- // context — in which context to execute the
- // callbacks in
- //
- // *Example:*
- //
- // $.ajax({
- // type: 'POST',
- // url: '/projects',
- // data: { name: 'Zepto.js' },
- // dataType: 'html',
- // timeout: 100,
- // context: $('body'),
- // success: function (data) {
- // this.append(data);
- // },
- // error: function (xhr, type) {
- // alert('Error!');
- // }
- // });
- //
- $.ajax = function(options){
- var settings = $.extend({}, options || {});
- for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key];
- ajaxStart(settings);
- if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) &&
- RegExp.$2 != window.location.host;
- if (/=\?/.test(settings.url)) return $.ajaxJSONP(settings);
- if (!settings.url) settings.url = window.location.toString();
- if (settings.data && !settings.contentType) settings.contentType = 'application/x-www-form-urlencoded';
- if (isObject(settings.data)) settings.data = $.param(settings.data);
- if (settings.type.match(/get/i) && settings.data) {
- var queryString = settings.data;
- if (settings.url.match(/\?.*=/)) {
- queryString = '&' + queryString;
- } else if (queryString[0] != '?') {
- queryString = '?' + queryString;
- }
- settings.url += queryString;
- }
- var mime = settings.accepts[settings.dataType],
- baseHeaders = { },
- protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol,
- xhr = $.ajaxSettings.xhr(), abortTimeout;
- if (!settings.crossDomain) baseHeaders['X-Requested-With'] = 'XMLHttpRequest';
- if (mime) baseHeaders['Accept'] = mime;
- settings.headers = $.extend(baseHeaders, settings.headers || {});
- xhr.onreadystatechange = function(){
- if (xhr.readyState == 4) {
- clearTimeout(abortTimeout);
- var result, error = false;
- if ((xhr.status >= 200 && xhr.status < 300) || (xhr.status == 0 && protocol == 'file:')) {
- if (mime == 'application/json' && !(/^\s*$/.test(xhr.responseText))) {
- try { result = JSON.parse(xhr.responseText); }
- catch (e) { error = e; }
- }
- else result = xhr.responseText;
- if (error) ajaxError(error, 'parsererror', xhr, settings);
- else ajaxSuccess(result, xhr, settings);
- } else {
- ajaxError(null, 'error', xhr, settings);
- }
- }
- };
- xhr.open(settings.type, settings.url, true);
- if (settings.contentType) settings.headers['Content-Type'] = settings.contentType;
- for (name in settings.headers) xhr.setRequestHeader(name, settings.headers[name]);
- if (ajaxBeforeSend(xhr, settings) === false) {
- xhr.abort();
- return false;
- }
- if (settings.timeout > 0) abortTimeout = setTimeout(function(){
- xhr.onreadystatechange = empty;
- xhr.abort();
- ajaxError(null, 'timeout', xhr, settings);
- }, settings.timeout);
- xhr.send(settings.data);
- return xhr;
- };
- // ### $.get
- //
- // Load data from the server using a GET request
- //
- // *Arguments:*
- //
- // url — url to which the request is sent
- // success — callback that is executed if the request succeeds
- //
- // *Example:*
- //
- // $.get(
- // '/projects/42',
- // function (data) {
- // $('body').append(data);
- // }
- // );
- //
- $.get = function(url, success){ return $.ajax({ url: url, success: success }) };
- // ### $.post
- //
- // Load data from the server using POST request
- //
- // *Arguments:*
- //
- // url — url to which the request is sent
- // [data] — data to send to server, can be string or object
- // [success] — callback that is executed if the request succeeds
- // [dataType] — type of expected response
- // 'json', 'xml', 'html', or 'text'
- //
- // *Example:*
- //
- // $.post(
- // '/projects',
- // { name: 'Zepto.js' },
- // function (data) {
- // $('body').append(data);
- // },
- // 'html'
- // );
- //
- $.post = function(url, data, success, dataType){
- if ($.isFunction(data)) dataType = dataType || success, success = data, data = null;
- return $.ajax({ type: 'POST', url: url, data: data, success: success, dataType: dataType });
- };
- // ### $.getJSON
- //
- // Load JSON from the server using GET request
- //
- // *Arguments:*
- //
- // url — url to which the request is sent
- // success — callback that is executed if the request succeeds
- //
- // *Example:*
- //
- // $.getJSON(
- // '/projects/42',
- // function (json) {
- // projects.push(json);
- // }
- // );
- //
- $.getJSON = function(url, success){
- return $.ajax({ url: url, success: success, dataType: 'json' });
- };
- // ### $.fn.load
- //
- // Load data from the server into an element
- //
- // *Arguments:*
- //
- // url — url to which the request is sent
- // [success] — callback that is executed if the request succeeds
- //
- // *Examples:*
- //
- // $('#project_container').get(
- // '/projects/42',
- // function () {
- // alert('Project was successfully loaded');
- // }
- // );
- //
- // $('#project_comments').get(
- // '/projects/42 #comments',
- // function () {
- // alert('Comments was successfully loaded');
- // }
- // );
- //
- $.fn.load = function(url, success){
- if (!this.length) return this;
- var self = this, parts = url.split(/\s/), selector;
- if (parts.length > 1) url = parts[0], selector = parts[1];
- $.get(url, function(response){
- self.html(selector ?
- $(document.createElement('div')).html(response).find(selector).html()
- : response);
- success && success.call(self);
- });
- return this;
- };
- var escape = encodeURIComponent;
- function serialize(params, obj, traditional, scope){
- var array = $.isArray(obj);
- $.each(obj, function(key, value) {
- if (scope) key = traditional ? scope : scope + '[' + (array ? '' : key) + ']';
- // handle data in serializeArray() format
- if (!scope && array) params.add(value.name, value.value);
- // recurse into nested objects
- else if (traditional ? $.isArray(value) : isObject(value))
- serialize(params, value, traditional, key);
- else params.add(key, value);
- });
- }
- // ### $.param
- //
- // Encode object as a string of URL-encoded key-value pairs
- //
- // *Arguments:*
- //
- // obj — object to serialize
- // [traditional] — perform shallow serialization
- //
- // *Example:*
- //
- // $.param( { name: 'Zepto.js', version: '0.6' } );
- //
- $.param = function(obj, traditional){
- var params = [];
- params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) };
- serialize(params, obj, traditional);
- return params.join('&').replace('%20', '+');
- };
- })(Zepto);
- // Zepto.js
- // (c) 2010, 2011 Thomas Fuchs
- // Zepto.js may be freely distributed under the MIT license.
- (function ($) {
- // ### $.fn.serializeArray
- //
- // Encode a set of form elements as an array of names and values
- //
- // *Example:*
- //
- // $('#login_form').serializeArray();
- //
- // returns
- //
- // [
- // {
- // name: 'email',
- // value: 'koss@nocorp.me'
- // },
- // {
- // name: 'password',
- // value: '123456'
- // }
- // ]
- //
- $.fn.serializeArray = function () {
- var result = [], el;
- $( Array.prototype.slice.call(this.get(0).elements) ).each(function () {
- el = $(this);
- var type = el.attr('type');
- if (
- !this.disabled && type != 'submit' && type != 'reset' && type != 'button' &&
- ((type != 'radio' && type != 'checkbox') || this.checked)
- ) {
- result.push({
- name: el.attr('name'),
- value: el.val()
- });
- }
- });
- return result;
- };
- // ### $.fn.serialize
- //
- //
- // Encode a set of form elements as a string for submission
- //
- // *Example:*
- //
- // $('#login_form').serialize();
- //
- // returns
- //
- // "email=koss%40nocorp.me&password=123456"
- //
- $.fn.serialize = function () {
- var result = [];
- this.serializeArray().forEach(function (elm) {
- result.push( encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value) );
- });
- return result.join('&');
- };
- // ### $.fn.submit
- //
- // Bind or trigger the submit event for a form
- //
- // *Examples:*
- //
- // To bind a handler for the submit event:
- //
- // $('#login_form').submit(function (e) {
- // alert('Form was submitted!');
- // e.preventDefault();
- // });
- //
- // To trigger form submit:
- //
- // $('#login_form').submit();
- //
- $.fn.submit = function (callback) {
- if (callback) this.bind('submit', callback)
- else if (this.length) {
- var event = $.Event('submit');
- this.eq(0).trigger(event);
- if (!event.defaultPrevented) this.get(0).submit()
- }
- return this;
- }
- })(Zepto);
- // Zepto.js
- // (c) 2010, 2011 Thomas Fuchs
- // Zepto.js may be freely distributed under the MIT license.
- (function($){
- var touch = {}, touchTimeout;
- function parentIfText(node){
- return 'tagName' in node ? node : node.parentNode;
- }
- function swipeDirection(x1, x2, y1, y2){
- var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2);
- if (xDelta >= yDelta) {
- return (x1 - x2 > 0 ? 'Left' : 'Right');
- } else {
- return (y1 - y2 > 0 ? 'Up' : 'Down');
- }
- }
- var longTapDelay = 750;
- function longTap(){
- if (touch.last && (Date.now() - touch.last >= longTapDelay)) {
- $(touch.target).trigger('longTap');
- touch = {};
- }
- }
- $(document).ready(function(){
- $(document.body).bind('touchstart', function(e){
- var now = Date.now(), delta = now - (touch.last || now);
- touch.target = parentIfText(e.touches[0].target);
- touchTimeout && clearTimeout(touchTimeout);
- touch.x1 = e.touches[0].pageX;
- touch.y1 = e.touches[0].pageY;
- if (delta > 0 && delta <= 250) touch.isDoubleTap = true;
- touch.last = now;
- setTimeout(longTap, longTapDelay);
- }).bind('touchmove', function(e){
- touch.x2 = e.touches[0].pageX;
- touch.y2 = e.touches[0].pageY;
- }).bind('touchend', function(e){
- if (touch.isDoubleTap) {
- $(touch.target).trigger('doubleTap');
- touch = {};
- } else if (touch.x2 > 0 || touch.y2 > 0) {
- (Math.abs(touch.x1 - touch.x2) > 30 || Math.abs(touch.y1 - touch.y2) > 30) &&
- $(touch.target).trigger('swipe') &&
- $(touch.target).trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)));
- touch.x1 = touch.x2 = touch.y1 = touch.y2 = touch.last = 0;
- } else if ('last' in touch) {
- touchTimeout = setTimeout(function(){
- touchTimeout = null;
- $(touch.target).trigger('tap')
- touch = {};
- }, 250);
- }
- }).bind('touchcancel', function(){ touch = {} });
- });
- ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'longTap'].forEach(function(m){
- $.fn[m] = function(callback){ return this.bind(m, callback) }
- });
- })(Zepto);
- (function ($) {
- var defaults = {
- duration: 400,
- easing: ''
- },
-
- vendor = (/webkit/i).test(navigator.appVersion) ? 'webkit' : 'moz',
-
- prefix = '-' + vendor + '-',
-
- vendorNames = n = {
- transition: prefix + 'transition',
- transform: prefix + 'transform',
- transitionEnd: vendor + 'TransitionEnd'
- },
-
- transformTypes = [
- 'scale', 'scaleX', 'scaleY', 'scale3d',
- 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'rotate3d',
- 'translate', 'translateX', 'translateY', 'translateZ', 'translate3d',
- 'skew', 'skewX', 'skewY',
- 'matrix', 'matrix3d', 'perspective'
- ];
-
- // Implement Array.prototype.indexOf if it's not. This is
- // mainly Internet Explorer.
- if (!Array.prototype.indexOf) {
- Array.prototype.indexOf = function(obj, start) {
- for (var i = (start || 0), j = this.length; i < j; i++) {
- if (this[i] === obj) return i;
- }
- return -1;
- }
- }
-
- // Helper function for easily adding transforms.
- $.fn.transform = function (properties) {
- var transforms = [];
-
- for (var key in properties) {
- if (transformTypes.indexOf(key) !== -1) {
- transforms.push(key + '(' + properties[key] + ')');
- delete properties[key];
- }
- }
- if (transforms.length)
- properties[n.transform] = transforms.join(' ');
-
- return $(this).css(properties);
- };
-
- // Effects
-
- $.fn.gfxPopIn = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {});
-
- opts.scale = opts.scale || 0.2;
-
- $that.transform({
- '-webkit-transform-origin': '50% 50%',
- '-moz-transform-origin': '50% 50%',
- scale: opts.scale,
- opacity: '0',
- display: 'block'
- }).animate({scale: '1', opacity: '1'},
- opts.duration, opts.easing, cb);
- };
-
- $.fn.gfxPopOut = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {});
-
- opts.scale = opts.scale || 0.2;
-
- $that.transform({
- '-webkit-transform-origin': '50% 50%',
- '-moz-transform-origin': '50% 50%',
- scale: '1',
- opacity: '1'
- }).animate({opacity: 0, scale: opts.scale},
- opts.duration, opts.easing, function () {
-
- $that.transform({display: 'none',
- opacity: 1, scale: 1});
-
- cb && cb();
- });
- };
-
- $.fn.gfxFadeIn = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {});
-
- opts.duration = opts.duration || 1000;
-
- $that.css({opacity: 0}).show().animate({opacity: 1}, opts.duration,
- opts.easing, cb);
- };
-
- $.fn.gfxFadeOut = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {});
-
- $that.css({opacity: 1}).animate({opacity: 0}, opts.duration,
- opts.easing, function () {
-
- $that.hide().css({opacity: 1});
- cb && cb();
- });
- };
-
- $.fn.gfxShake = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {}),
- distance,
- third = function () {
- $that.animate({translateX: distance + 'px'},
- opts.duration, opts.easing, function () {
-
- $that.transform({translateX: 0});
- cb && cb();
- });
- },
- second = function () {
- $that.animate({translateX: '-' + distance + 'px'},
- opts.duration, opts.easing, third);
- },
- first = function () {
- $that.animate({translateX: distance + 'px'},
- opts.duration, opts.easing, second);
- };
-
- opts.duration = opts.duration || 100;
- opts.easing = opts.easing || 'ease-out';
- distance = opts.distance || 20;
-
- $that.animate({translateX: '-' + distance + 'px'},
- opts.duration, opts.easing, first);
- };
-
- $.fn.gfxBlip = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {}),
- first = function () {
- $that.animate({scale: 1}, opts.duration,
- opts.easing, cb);
- };
-
- opts.scale = opts.scale || 1.15;
-
- $that.animate({scale: opts.scale}, opts.duration,
- opts.easing, first);
- };
-
- $.fn.gfxExplodeIn = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {});
-
- opts.scale = opts.scale || 3;
-
- $that.transform({scale: opts.scale, opacity: 0,
- display: 'block'}).animate({scale: 1, opacity: 1},
- opts.duration, opts.easing, cb);
- };
-
- $.fn.gfxExplodeOut = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {}),
- c = cb,
- first = function() {
- $that.transform({scale: 1, opacity: 1, display: 'none'});
- cb && cb();
- };
-
- if (opts.reset) c = second;
- opts.scale = opts.scale || 3;
-
- $that.transform({scale: 1, opacity: 1})
- .animate({scale: opts.scale, opacity: 0}, opts.duration,
- opts.easing, c);
- };
-
- $.fn.gfxFlipIn = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {});
- $that.transform({rotateY: '180deg', scale: 0.8,
- display: 'block'}).animate({rotateY: 0, scale: 1},
- opts.duration, opts.easing, cb);
- };
-
- $.fn.gfxFlipOut = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {}),
- c = cb,
- first = function () {
- $that.transform({scale: 1, rotateY: 0, display: 'none'});
- cb && cb();
- };
-
- if (opts.reset) c = first;
-
- $that.transform({rotateY: 0, scale: 1})
- .animate({rotateY: '-180deg', scale: 0.8}, opts.duration,
- opts.easing, c);
- };
-
- $.fn.gfxRotateOut = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {}),
- c = cb
- first = function () {
- $that.transform({rotateY: 0, display: 'none'});
- cb && cb();
- };
-
- if (opts.reset) c = first;
-
- $that.transform({rotateY: 0}).animate({rotateY: '-180deg'},
- opts.duration, opts.easing, c);
- };
-
- $.fn.gfxRotateIn = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {});
-
- $that.transform({rotateY: '180deg', display: 'block'})
- .animate({rotateY: 0}, opts.duration, opts.easing, cb);
- };
-
- $.fn.gfxSlideOut = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {}),
- distance,
- opacity;
-
- opts.direction = opts.direction || 'right';
- distance = opts.distance || 100;
- if (opts.direction === 'left') distance *= -1;
- distance += '%';
-
- opacity = opts.fade ? 0 : 1;
-
- $that.show().animate({translate3d: distance + ',0,0',
- opacity: opacity}, opts.duration, opts.easing, function () {
-
- $that.transform({translate3d: '0,0,0', opacity: 1}).hide();
- cb && cb();
- });
- };
-
- $.fn.gfxSlideIn = function (options, cb) {
- var $that = $(this),
- opts = $.extend({}, defaults, options || {}),
- distance,
- opacity;
-
- opts.direction = opts.direction || 'right';
- distance = opts.distance || 100;
- if (opts.direction === 'left') distance *= -1;
- distance += '%';
-
- opacity = opts.fade ? 0 : 1;
-
- $that.transform({translate3d: distance + ',0,0',
- opacity: opacity}).show().animate({translate3d: '0,0,0',
- opacity: 1}, opts.duration, opts.easing, cb);
- };
-
- })(Zepto);
- // Underscore.js 1.2.2
- // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
- // Underscore is freely distributable under the MIT license.
- // Portions of Underscore are inspired or borrowed from Prototype,
- // Oliver Steele's Functional, and John Resig's Micro-Templating.
- // For all details and documentation:
- // http://documentcloud.github.com/underscore
- (function() {
- // Baseline setup
- // --------------
- // Establish the root object, `window` in the browser, or `global` on the server.
- var root = this;
- // Save the previous value of the `_` variable.
- var previousUnderscore = root._;
- // Establish the object that gets returned to break out of a loop iteration.
- var breaker = {};
- // Save bytes in the minified (but not gzipped) version:
- var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
- // Create quick reference variables for speed access to core prototypes.
- var concat = ArrayProto.concat,
- push = ArrayProto.push,
- slice = ArrayProto.slice,
- toString = ObjProto.toString,
- hasOwnProperty = ObjProto.hasOwnProperty;
- // All **ECMAScript 5** native function implementations that we hope to use
- // are declared here.
- var
- nativeForEach = ArrayProto.forEach,
- nativeMap = ArrayProto.map,
- nativeReduce = ArrayProto.reduce,
- nativeReduceRight = ArrayProto.reduceRight,
- nativeFilter = ArrayProto.filter,
- nativeEvery = ArrayProto.every,
- nativeSome = ArrayProto.some,
- nativeIndexOf = ArrayProto.indexOf,
- nativeLastIndexOf = ArrayProto.lastIndexOf,
- nativeIsArray = Array.isArray,
- nativeKeys = Object.keys,
- nativeBind = FuncProto.bind;
- // Create a safe reference to the Underscore object for use below.
- var _ = function(obj) { return new wrapper(obj); };
- // Export the Underscore object for **Node.js** and **"CommonJS"**, with
- // backwards-compatibility for the old `require()` API. If we're not in
- // CommonJS, add `_` to the global object.
- if (typeof exports !== 'undefined') {
- if (typeof module !== 'undefined' && module.exports) {
- exports = module.exports = _;
- }
- exports._ = _;
- } else if (typeof define === 'function' && define.amd) {
- // Register as a named module with AMD.
- define('underscore', function() {
- return _;
- });
- } else {
- // Exported as a string, for Closure Compiler "advanced" mode.
- root['_'] = _;
- }
- // Current version.
- _.VERSION = '1.2.2';
- // Collection Functions
- // --------------------
- // The cornerstone, an `each` implementation, aka `forEach`.
- // Handles objects with the built-in `forEach`, arrays, and raw objects.
- // Delegates to **ECMAScript 5**'s native `forEach` if available.
- var each = _.each = _.forEach = function(obj, iterator, context) {
- if (obj == null) return;
- if (nativeForEach && obj.forEach === nativeForEach) {
- obj.forEach(iterator, context);
- } else {
- var fn = iterator;
- var i = -1;
- var l = obj.length;
- // We optimize for common use by only binding a context when it's passed.
- if (context) {
- iterator = function() { return fn.call(context, obj[i], i, obj); };
- }
- // If we're dealing with an array or array-like object...
- if (l === l >>> 0) {
- while (++i < l) {
- if (i in obj && iterator(obj[i], i, obj) == breaker) return;
- }
- } else {
- forProps(obj, iterator, true);
- }
- }
- };
- // A simple each, for dealing with non-sparse arrays and arguments objects.
- var simpleEach = function(obj, iterator, index) {
- index || (index = 0);
- for (var l = obj.length; index < l; index++) {
- iterator(obj[index]);
- }
- };
- // List of possible shadowed properties on Object.prototype.
- var shadowed = [
- 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
- 'toLocaleString', 'toString', 'valueOf'
- ];
- // IE < 9 makes properties, shadowing non-enumerable ones, non-enumerable too.
- var forShadowed = !{valueOf:0}.propertyIsEnumerable('valueOf') &&
- function(obj, iterator) {
- // Because IE < 9 can't set the `[[Enumerable]]` attribute of an existing
- // property and the `constructor` property of a prototype defaults to
- // non-enumerable, we manually skip the `constructor` property when we
- // think we are iterating over a `prototype` object.
- var ctor = obj.constructor;
- var skipCtor = ctor && ctor.prototype && ctor.prototype.constructor === ctor;
- for (var key, i = 0; key = shadowed[i]; i++) {
- if (!(skipCtor && key == 'constructor') &&
- hasOwnProperty.call(obj, key) &&
- iterator(obj[key], key, obj) === breaker) {
- break;
- }
- }
- };
- // Iterates over an object's properties, executing the `callback` for each.
- var forProps = function(obj, iterator, ownOnly) {
- var done = !obj;
- var skipProto = typeof obj == 'function';
- for (var key in obj) {
- // Firefox < 3.6, Opera > 9.50 - Opera < 12, and Safari < 5.1
- // (if the prototype or a property on the prototype has been set)
- // incorrectly set a function's `prototype` property [[Enumerable]] value
- // to true. Because of this we standardize on skipping the the `prototype`
- // property of functions regardless of their [[Enumerable]] value.
- if (done =
- !(skipProto && key == 'prototype') &&
- (!ownOnly || ownOnly && hasOwnProperty.call(obj, key)) &&
- iterator(obj[key], key, obj) === breaker) {
- break;
- }
- }
- if (!done && forShadowed) {
- forShadowed(obj, iterator);
- }
- };
- // Return the results of applying the iterator to each element.
- // Delegates to **ECMAScript 5**'s native `map` if available.
- _.map = function(obj, iterator, context) {
- var results = [];
- if (obj == null) return results;
- if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
- each(obj, function(value, index, list) {
- results[results.length] = iterator.call(context, value, index, list);
- });
- return results;
- };
- // **Reduce** builds up a single result from a list of values, aka `inject`,
- // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
- _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
- var initial = arguments.length > 2;
- if (obj == null) obj = [];
- if (nativeReduce && obj.reduce === nativeReduce) {
- if (context) iterator = _.bind(iterator, context);
- return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
- }
- each(obj, function(value, index, list) {
- if (!initial) {
- memo = value;
- initial = true;
- } else {
- memo = iterator.call(context, memo, value, index, list);
- }
- });
- if (!initial) throw new TypeError('Reduce of empty array with no initial value');
- return memo;
- };
- // The right-associative version of reduce, also known as `foldr`.
- // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
- _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
- var initial = arguments.length > 2;
- if (obj == null) obj = [];
- if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
- if (context) iterator = _.bind(iterator, context);
- return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
- }
- var reversed = _.toArray(obj).reverse();
- if (context && !initial) iterator = _.bind(iterator, context);
- return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
- };
- // Return the first value which passes a truth test. Aliased as `detect`.
- _.find = _.detect = function(obj, iterator, context) {
- var result;
- any(obj, function(value, index, list) {
- if (iterator.call(context, value, index, list)) {
- result = value;
- return true;
- }
- });
- return result;
- };
- // Return all the elements that pass a truth test.
- // Delegates to **ECMAScript 5**'s native `filter` if available.
- // Aliased as `select`.
- _.filter = _.select = function(obj, iterator, context) {
- var results = [];
- if (obj == null) return results;
- if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
- each(obj, function(value, index, list) {
- if (iterator.call(context, value, index, list)) results[results.length] = value;
- });
- return results;
- };
- // Return all the elements for which a truth test fails.
- _.reject = function(obj, iterator, context) {
- var results = [];
- if (obj == null) return results;
- each(obj, function(value, index, list) {
- if (!iterator.call(context, value, index, list)) results[results.length] = value;
- });
- return results;
- };
- // Determine whether all of the elements match a truth test.
- // Delegates to **ECMAScript 5**'s native `every` if available.
- // Aliased as `all`.
- _.every = _.all = function(obj, iterator, context) {
- var result = true;
- if (obj == null) return result;
- if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
- each(obj, function(value, index, list) {
- if (!(result = result && iterator.call(context, value, index, list))) return breaker;
- });
- return result;
- };
- // Determine if at least one element in the object matches a truth test.
- // Delegates to **ECMAScript 5**'s native `some` if available.
- // Aliased as `any`.
- var any = _.some = _.any = function(obj, iterator, context) {
- iterator || (iterator = _.identity);
- var result = false;
- if (obj == null) return result;
- if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
- each(obj, function(value, index, list) {
- if (result || (result = iterator.call(context, value, index, list))) return breaker;
- });
- return !!result;
- };
- // Determine if a given value is included in the array or object using `===`.
- // Aliased as `contains`.
- _.include = _.contains = function(obj, target) {
- var found = false;
- if (obj == null) return found;
- if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
- found = any(obj, function(value) {
- return value === target;
- });
- return found;
- };
- // Invoke a method (with arguments) on every item in a collection.
- _.invoke = function(obj, method) {
- var args = slice.call(arguments, 2);
- return _.map(obj, function(value) {
- return (method.call ? method || value : value[method]).apply(value, args);
- });
- };
- // Convenience version of a common use case of `map`: fetching a property.
- _.pluck = function(obj, key) {
- return _.map(obj, function(value){ return value[key]; });
- };
- // Return the maximum element or (element-based computation).
- _.max = function(obj, iterator, context) {
- if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
- if (!iterator && _.isEmpty(obj)) return -Infinity;
- var result = {computed : -Infinity};
- each(obj, function(value, index, list) {
- var computed = iterator ? iterator.call(context, value, index, list) : value;
- computed >= result.computed && (result = {value : value, computed : computed});
- });
- return result.value;
- };
- // Return the minimum element (or element-based computation).
- _.min = function(obj, iterator, context) {
- if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
- if (!iterator && _.isEmpty(obj)) return Infinity;
- var result = {computed : Infinity};
- each(obj, function(value, index, list) {
- var computed = iterator ? iterator.call(context, value, index, list) : value;
- computed < result.computed && (result = {value : value, computed : computed});
- });
- return result.value;
- };
- // Shuffle an array.
- _.shuffle = function(obj) {
- var shuffled = [], rand;
- each(obj, function(value, index, list) {
- if (index == 0) {
- shuffled[0] = value;
- } else {
- rand = Math.floor(Math.random() * (index + 1));
- shuffled[index] = shuffled[rand];
- shuffled[rand] = value;
- }
- });
- return shuffled;
- };
- // Sort the object's values by a criterion produced by an iterator.
- _.sortBy = function(obj, iterator, context) {
- return _.pluck(_.map(obj, function(value, index, list) {
- return {
- value : value,
- criteria : iterator.call(context, value, index, list)
- };
- }).sort(function(left, right) {
- var a = left.criteria;
- var b = right.criteria;
- return a < b ? -1 : a > b ? 1 : 0;
- }), 'value');
- };
- // Groups the object's values by a criterion. Pass either a string attribute
- // to group by, or a function that returns the criterion.
- _.groupBy = function(obj, val) {
- var result = {};
- var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
- each(obj, function(value, index) {
- var key = iterator(value, index);
- (result[key] || (result[key] = [])).push(value);
- });
- return result;
- };
- // Use a comparator function to figure out at what index an object should
- // be inserted so as to maintain order. Uses binary search.
- _.sortedIndex = function(array, obj, iterator) {
- iterator || (iterator = _.identity);
- var low = 0;
- var high = array.length;
- while (low < high) {
- var mid = (low + high) >> 1;
- iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
- }
- return low;
- };
- // Safely convert anything iterable into a real, live array.
- _.toArray = function(iterable) {
- if (!iterable) return [];
- if (iterable.toArray) return iterable.toArray();
- if (_.isArray(iterable) || _.isArguments(iterable)) return slice.call(iterable);
- return _.values(iterable);
- };
- // Return the number of elements in an object.
- _.size = function(obj) {
- return _.toArray(obj).length;
- };
- // Array Functions
- // ---------------
- // Get the first element of an array. Passing **n** will return the first N
- // values in the array. Aliased as `head`. The **guard** check allows it to work
- // with `_.map`.
- _.first = _.head = function(array, n, guard) {
- return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
- };
- // Returns everything but the last entry of the array. Especially useful on
- // the arguments object. Passing **n** will return all the values in
- // the array, excluding the last N. The **guard** check allows it to work with
- // `_.map`.
- _.initial = function(array, n, guard) {
- return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
- };
- // Get the last element of an array. Passing **n** will return the last N
- // values in the array. The **guard** check allows it to work with `_.map`.
- _.last = function(array, n, guard) {
- if ((n != null) && !guard) {
- return slice.call(array, Math.max(array.length - n, 0));
- } else {
- return array[array.length - 1];
- }
- };
- // Returns everything but the first entry of the array. Aliased as `tail`.
- // Especially useful on the arguments object. Passing an **index** will return
- // the rest of the values in the array from that index onward. The **guard**
- // check allows it to work with `_.map`.
- _.rest = _.tail = function(array, index, guard) {
- return slice.call(array, (index == null) || guard ? 1 : index);
- };
- // Trim out all falsy values from an array.
- _.compact = function(array) {
- return _.filter(array, function(value){ return !!value; });
- };
- // Return a completely flattened version of an array.
- _.flatten = function(array, shallow) {
- return _.reduce(array, function(memo, value) {
- if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
- memo[memo.length] = value;
- return memo;
- }, []);
- };
- // Return a version of the array that does not contain the specified value(s).
- _.without = function(array) {
- return _.difference(array, slice.call(arguments, 1));
- };
- // Produce a duplicate-free version of the array. If the array has already
- // been sorted, you have the option of using a faster algorithm.
- // Aliased as `unique`.
- _.uniq = _.unique = function(array, isSorted, iterator) {
- var initial = iterator ? _.map(array, iterator) : array;
- var result = [];
- _.reduce(initial, function(memo, el, i) {
- if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
- memo[memo.length] = el;
- result[result.length] = array[i];
- }
- return memo;
- }, []);
- return result;
- };
- // Produce an array that contains the union: each distinct element from all of
- // the passed-in arrays.
- _.union = function() {
- return _.uniq(_.flatten(arguments, true));
- };
- // Produce an array that contains every item shared between all the
- // passed-in arrays. (Aliased as "intersect" for back-compat.)
- _.intersection = _.intersect = function(array) {
- var rest = slice.call(arguments, 1);
- return _.filter(_.uniq(array), function(item) {
- return _.every(rest, function(other) {
- return _.indexOf(other, item) >= 0;
- });
- });
- };
- // Take the difference between one array and a number of other arrays.
- // Only the elements present in just the first array will remain.
- _.difference = function(array) {
- var rest = _.flatten(slice.call(arguments, 1));
- return _.filter(array, function(value){ return !_.include(rest, value); });
- };
- // Take the symmetric difference between a list of arrays. Only the elements
- // present in one of the input arrays will remain.
- _.symDifference = function() {
- return _.reduce(arguments, function(memo, array) {
- return _.union(_.difference(memo, array), _.difference(array, memo));
- });
- };
- // Zip together multiple lists into a single array -- elements that share
- // an index go together.
- _.zip = function() {
- var length = _.max(_.pluck(arguments, 'length'));
- var results = new Array(length);
- for (var i = 0; i < length; i++) results[i] = _.pluck(arguments, i);
- return results;
- };
- // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
- // we need this function. Return the position of the first occurrence of an
- // item in an array, or -1 if the item is not included in the array.
- // Delegates to **ECMAScript 5**'s native `indexOf` if available.
- // If the array is large and already in sort order, pass `true`
- // for **isSorted** to use binary search.
- _.indexOf = function(array, item, isSorted) {
- if (array == null) return -1;
- var i, l;
- if (isSorted) {
- i = _.sortedIndex(array, item);
- return array[i] === item ? i : -1;
- }
- if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
- for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
- return -1;
- };
- // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
- _.lastIndexOf = function(array, item) {
- if (array == null) return -1;
- if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
- var i = array.length;
- while (i--) if (i in array && array[i] === item) return i;
- return -1;
- };
- // Generate an integer Array containing an arithmetic progression. A port of
- // the native Python `range()` function. See
- // [the Python documentation](http://docs.python.org/library/functions.html#range).
- _.range = function(start, stop, step) {
- if (arguments.length <= 1) {
- stop = start || 0;
- start = 0;
- }
- step = arguments[2] || 1;
- var len = Math.max(Math.ceil((stop - start) / step), 0);
- var idx = 0;
- var range = new Array(len);
- while(idx < len) {
- range[idx++] = start;
- start += step;
- }
- return range;
- };
- // Function (ahem) Functions
- // ------------------
- // Reusable constructor function for prototype setting.
- var ctor = function(){};
- // Create a function bound to a given object (assigning `this`, and arguments,
- // optionally). Binding with arguments is also known as `curry`.
- // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
- // We check for `func.bind` first, to fail fast when `func` is undefined.
- _.bind = function bind(func, context) {
- var bound, args;
- if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
- if (!_.isFunction(func)) throw new TypeError;
- args = slice.call(arguments, 2);
- return bound = function() {
- if (!(this instanceof bound)) return func.apply(context, concat.apply(args, arguments));
- ctor.prototype = func.prototype;
- var self = new ctor;
- var result = func.apply(self, concat.apply(args, arguments));
- if (Object(result) === result) return result;
- return self;
- };
- };
- // Bind all of an object's methods to that object. Useful for ensuring that
- // all callbacks defined on an object belong to it.
- _.bindAll = function(obj) {
- var i = 1;
- var funcs = arguments;
- if (funcs.length < 2) {
- i = 0;
- funcs = _.functions(obj);
- }
- simpleEach(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }, i);
- return obj;
- };
- // Memoize an expensive function by storing its results.
- _.memoize = function(func, hasher) {
- hasher || (hasher = _.identity);
- var memo = {};
- return function() {
- var key = hasher.apply(this, arguments);
- return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
- };
- };
- // Delays a function for the given number of milliseconds, and then calls
- // it with the arguments supplied.
- _.delay = function(func, wait) {
- var args = slice.call(arguments, 2);
- return setTimeout(function(){ return func.apply(func, args); }, wait);
- };
- // Defers a function, scheduling it to run after the current call stack has
- // cleared.
- _.defer = function(func) {
- return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
- };
- // Returns a function, that, when invoked, will only be triggered at most once
- // during a given window of time.
- _.throttle = function(func, wait) {
- var context, args, timeout, throttling, more;
- var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
- return function() {
- context = this; args = arguments;
- var later = function() {
- timeout = null;
- if (more) func.apply(context, args);
- whenDone();
- };
- if (!timeout) timeout = setTimeout(later, wait);
- if (throttling) {
- more = true;
- } else {
- func.apply(context, args);
- }
- whenDone();
- throttling = true;
- };
- };
- // Returns a function, that, as long as it continues to be invoked, will not
- // be triggered. The function will be called after it stops being called for
- // N milliseconds.
- _.debounce = function(func, wait) {
- var timeout;
- return function() {
- var context = this, args = arguments;
- var later = function() {
- timeout = null;
- func.apply(context, args);
- };
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- };
- };
- // Returns a function that will be executed at most one time, no matter how
- // often you call it. Useful for lazy initialization.
- _.once = function(func) {
- var ran = false, memo;
- return function() {
- if (ran) return memo;
- ran = true;
- return memo = func.apply(this, arguments);
- };
- };
- // Returns the first function passed as an argument to the second,
- // allowing you to adjust arguments, run code before and after, and
- // conditionally execute the original function.
- _.wrap = function(func, wrapper) {
- return function() {
- var args = concat.apply([func], arguments);
- return wrapper.apply(this, args);
- };
- };
- // Returns a function that is the composition of a list of functions, each
- // consuming the return value of the function that follows.
- _.compose = function() {
- var funcs = arguments;
- return function() {
- var args = arguments;
- for (var i = funcs.length - 1; i >= 0; i--) {
- args = [funcs[i].apply(this, args)];
- }
- return args[0];
- };
- };
- // Returns a function that will only be executed after being called N times.
- _.after = function(times, func) {
- if (times <= 0) return func();
- return function() {
- if (--times < 1) { return func.apply(this, arguments); }
- };
- };
- // Object Functions
- // ----------------
- // Retrieve the names of an object's properties.
- // Delegates to **ECMAScript 5**'s native `Object.keys`
- _.keys = nativeKeys || function(obj) {
- if (obj !== Object(obj)) throw new TypeError('Invalid object');
- var keys = [];
- forProps(obj, function(value, key) {
- keys[keys.length] = key;
- }, true);
- return keys;
- };
- // Retrieve the values of an object's properties.
- _.values = function(obj) {
- return _.map(obj, _.identity);
- };
- // Return a sorted list of the function names available on the object.
- // Aliased as `methods`
- _.functions = _.methods = function(obj) {
- var names = [];
- forProps(obj, function(value, key) {
- if (_.isFunction(value)) names[names.length] = key;
- });
- return names.sort();
- };
- // Extend a given object with all the properties in passed-in object(s).
- _.extend = function(obj) {
- simpleEach(arguments, function(source) {
- forProps(source, function(value, key) {
- if (value !== void 0) obj[key] = value;
- });
- }, 1);
- return obj;
- };
- // Fill in a given object with default properties.
- _.defaults = function(obj) {
- simpleEach(arguments, function(source) {
- forProps(source, function(value, key) {
- if (obj[key] == null) obj[key] = value;
- });
- }, 1);
- return obj;
- };
- // Create a (shallow-cloned) duplicate of an object.
- _.clone = function(obj) {
- if (!_.isObject(obj)) return obj;
- return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
- };
- // Invokes interceptor with the obj, and then returns obj.
- // The primary purpose of this method is to "tap into" a method chain, in
- // order to perform operations on intermediate results within the chain.
- _.tap = function(obj, interceptor) {
- interceptor(obj);
- return obj;
- };
- // Internal recursive comparison function.
- function eq(a, b, stack) {
- // Identical objects are equal. `0 === -0`, but they aren't identical.
- // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
- if (a === b) return a !== 0 || 1 / a == 1 / b;
- // A strict comparison is necessary because `null == undefined`.
- if (a == null || b == null) return a === b;
- // Unwrap any wrapped objects.
- if (a._chain) a = a._wrapped;
- if (b._chain) b = b._wrapped;
- // Invoke a custom `isEqual` method if one is provided.
- if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
- if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
- // Compare `[[Class]]` names.
- var className = toString.call(a);
- if (className != toString.call(b)) return false;
- switch (className) {
- // Strings, numbers, dates, and booleans are compared by value.
- case '[object String]':
- // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
- // equivalent to `new String("5")`.
- return a == String(b);
- case '[object Number]':
- // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
- // other numeric values.
- return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
- case '[object Date]':
- case '[object Boolean]':
- // Coerce dates and booleans to numeric primitive values. Dates are compared by their
- // millisecond representations. Note that invalid dates with millisecond representations
- // of `NaN` are not equivalent.
- return +a == +b;
- // RegExps are compared by their source patterns and flags.
- case '[object RegExp]':
- return a.source == b.source &&
- a.global == b.global &&
- a.multiline == b.multiline &&
- a.ignoreCase == b.ignoreCase;
- }
- if (typeof a != 'object' || typeof b != 'object') return false;
- // Assume equality for cyclic structures. The algorithm for detecting cyclic
- // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
- var length = stack.length;
- while (length--) {
- // Linear search. Performance is inversely proportional to the number of
- // unique nested structures.
- if (stack[length] == a) return true;
- }
- // Add the first object to the stack of traversed objects.
- stack.push(a);
- var size = 0, result = true;
- // Recursively compare objects and arrays.
- if (className == '[object Array]') {
- // Compare array lengths to determine if a deep comparison is necessary.
- size = a.length;
- result = size == b.length;
- if (result) {
- // Deep compare the contents, ignoring non-numeric properties.
- while (size--) {
- // Ensure commutative equality for sparse arrays.
- if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
- }
- }
- } else {
- // Objects with different constructors are not equivalent.
- if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
- // Deep compare objects.
- forProps(a, function(value, key) {
- // Count the expected number of properties.
- size++;
- // Deep compare each member.
- if (!(result = hasOwnProperty.call(b, key) && eq(value, b[key], stack))) return breaker;
- }, true);
- // Ensure that both objects contain the same number of properties.
- if (result) {
- forProps(b, function() {
- return !(size--) && breaker;
- }, true);
- result = !size;
- }
- }
- // Remove the first object from the stack of traversed objects.
- stack.pop();
- return result;
- }
- // Perform a deep comparison to check if two objects are equal.
- _.isEqual = function(a, b) {
- return eq(a, b, []);
- };
- // Is a given array, string, or object empty?
- // An "empty" object has no enumerable own-properties.
- _.isEmpty = function(obj) {
- var result = toString.call(obj);
- if (result == '[object Array]' || result == '[object String]') {
- return !obj.length;
- }
- forProps(obj, function() {
- result = false;
- return breaker;
- }, true);
- return !!result;
- };
- // Is a given value a DOM element?
- _.isElement = function(obj) {
- return !!(obj && obj.nodeType == 1);
- };
- // Is a given value an array?
- // Delegates to ECMA5's native Array.isArray
- _.isArray = nativeIsArray || function(obj) {
- return toString.call(obj) == '[object Array]';
- };
- // Is a given variable an object?
- _.isObject = function(obj) {
- return obj === Object(obj);
- };
- // Is a given variable an arguments object?
- _.isArguments = function(obj) {
- return toString.call(obj) == '[object Arguments]';
- };
- if (!_.isArguments(arguments)) {
- _.isArguments = function(obj) {
- return !!(obj && hasOwnProperty.call(obj, 'callee'));
- };
- }
- // Is a given value a function?
- _.isFunction = function(obj) {
- return toString.call(obj) == '[object Function]';
- };
- // Is a given value a string?
- _.isString = function(obj) {
- return toString.call(obj) == '[object String]';
- };
- // Is a given value a number?
- _.isNumber = function(obj) {
- return toString.call(obj) == '[object Number]';
- };
- // Is the given value `NaN`?
- _.isNaN = function(obj) {
- // `NaN` is the only value for which `===` is not reflexive.
- return obj !== obj;
- };
- // Is a given value a boolean?
- _.isBoolean = function(obj) {
- return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
- };
- // Is a given value a date?
- _.isDate = function(obj) {
- return toString.call(obj) == '[object Date]';
- };
- // Is the given value a regular expression?
- _.isRegExp = function(obj) {
- return toString.call(obj) == '[object RegExp]';
- };
- // Is a given value equal to null?
- _.isNull = function(obj) {
- return obj === null;
- };
- // Is a given variable undefined?
- _.isUndefined = function(obj) {
- return obj === void 0;
- };
- // Utility Functions
- // -----------------
- // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
- // previous owner. Returns a reference to the Underscore object.
- _.noConflict = function() {
- root._ = previousUnderscore;
- return this;
- };
- // Keep the identity function around for default iterators.
- _.identity = function(value) {
- return value;
- };
- // Run a function **n** times.
- _.times = function (n, iterator, context) {
- for (var i = 0; i < n; i++) iterator.call(context, i);
- };
- // Escape a string for HTML interpolation.
- _.escape = function(string) {
- return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
- };
- // Add your own custom functions to the Underscore object, ensuring that
- // they're correctly added to the OOP wrapper as well.
- _.mixin = function(obj) {
- simpleEach(_.functions(obj), function(name){
- addToWrapper(name, _[name] = obj[name]);
- });
- };
- // Generate a unique integer id (unique within the entire client session).
- // Useful for temporary DOM ids.
- var idCounter = 0;
- _.uniqueId = function(prefix) {
- var id = idCounter++;
- return prefix ? prefix + id : id;
- };
- // By default, Underscore uses ERB-style template delimiters, change the
- // following template settings to use alternative delimiters.
- _.templateSettings = {
- evaluate : /<%([\s\S]+?)%>/g,
- interpolate : /<%=([\s\S]+?)%>/g,
- escape : /<%-([\s\S]+?)%>/g
- };
- // JavaScript micro-templating, similar to John Resig's implementation.
- // Underscore templating handles arbitrary delimiters, preserves whitespace,
- // and correctly escapes quotes within interpolated code.
- _.template = function(str, data) {
- var c = _.templateSettings;
- var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
- 'with(obj||{}){__p.push(\'' +
- str.replace(/\\/g, '\\\\')
- .replace(/'/g, "\\'")
- .replace(c.escape, function(match, code) {
- return "',_.escape(" + code.replace(/\\'/g, "'") + "),'";
- })
- .replace(c.interpolate, function(match, code) {
- return "'," + code.replace(/\\'/g, "'") + ",'";
- })
- .replace(c.evaluate || null, function(match, code) {
- return "');" + code.replace(/\\'/g, "'")
- .replace(/[\r\n\t]/g, ' ') + ";__p.push('";
- })
- .replace(/\r/g, '\\r')
- .replace(/\n/g, '\\n')
- .replace(/\t/g, '\\t')
- + "');}return __p.join('');";
- var func = new Function('obj', '_', tmpl);
- if (data) return func(data, _);
- return function(data) {
- return func.call(this, data, _);
- };
- };
- // The OOP Wrapper
- // ---------------
- // If Underscore is called as a function, it returns a wrapped object that
- // can be used OO-style. This wrapper holds altered versions of all the
- // underscore functions. Wrapped objects may be chained.
- var wrapper = function(obj) { this._wrapped = obj; };
- // Expose `wrapper.prototype` as `_.prototype`
- _.prototype = wrapper.prototype;
- // Helper function to continue chaining intermediate results.
- var result = function(obj, chain) {
- return chain ? _(obj).chain() : obj;
- };
- // A method to easily add functions to the OOP wrapper.
- var addToWrapper = function(name, func) {
- wrapper.prototype[name] = function() {
- var args = [this._wrapped];
- push.apply(args, arguments);
- return result(func.apply(_, args), this._chain);
- };
- };
- // Add all of the Underscore functions to the wrapper object.
- _.mixin(_);
- // Add all mutator Array functions to the wrapper.
- simpleEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
- var method = ArrayProto[name];
- wrapper.prototype[name] = function() {
- method.apply(this._wrapped, arguments);
- return result(this._wrapped, this._chain);
- };
- });
- // Add all accessor Array functions to the wrapper.
- simpleEach(['concat', 'join', 'slice'], function(name) {
- var method = ArrayProto[name];
- wrapper.prototype[name] = function() {
- return result(method.apply(this._wrapped, arguments), this._chain);
- };
- });
- // Start chaining a wrapped Underscore object.
- wrapper.prototype.chain = function() {
- this._chain = true;
- return this;
- };
- // Extracts the result from a wrapped and chained object.
- wrapper.prototype.value = function() {
- return this._wrapped;
- };
- }).call(this);
- // Backbone.js 0.5.3
- // (c) 2010-2011 Jeremy Ashkenas, DocumentCloud Inc.
- // Backbone may be freely distributed under the MIT license.
- // For all details and documentation:
- // http://documentcloud.github.com/backbone
- (function(){
- // Initial Setup
- // -------------
- // Save a reference to the global object.
- var root = this;
- // Save the previous value of the `Backbone` variable.
- var previousBackbone = root.Backbone;
- // Create a local reference to slice.
- var slice = Array.prototype.slice;
- // The top-level namespace. All public Backbone classes and modules will
- // be attached to this. Exported for both CommonJS and the browser.
- var Backbone;
- if (typeof exports !== 'undefined') {
- Backbone = exports;
- } else {
- Backbone = root.Backbone = {};
- }
- // Current version of the library. Keep in sync with `package.json`.
- Backbone.VERSION = '0.5.3';
- // Require Underscore, if we're on the server, and it's not already present.
- var _ = root._;
- if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._;
- // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable.
- var $ = root.jQuery || root.Zepto || root.ender;
- // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
- // to its previous owner. Returns a reference to this Backbone object.
- Backbone.noConflict = function() {
- root.Backbone = previousBackbone;
- return this;
- };
- // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option will
- // fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and set a
- // `X-Http-Method-Override` header.
- Backbone.emulateHTTP = false;
- // Turn on `emulateJSON` to support legacy servers that can't deal with direct
- // `application/json` requests ... will encode the body as
- // `application/x-www-form-urlencoded` instead and will send the model in a
- // form param named `model`.
- Backbone.emulateJSON = false;
- // Backbone.Events
- // -----------------
- // A module that can be mixed in to *any object* in order to provide it with
- // custom events. You may `bind` or `unbind` a callback function to an event;
- // `trigger`-ing an event fires all callbacks in succession.
- //
- // var object = {};
- // _.extend(object, Backbone.Events);
- // object.bind('expand', function(){ alert('expanded'); });
- // object.trigger('expand');
- //
- Backbone.Events = {
- // Bind an event, specified by a string name, `ev`, to a `callback` function.
- // Passing `"all"` will bind the callback to all events fired.
- bind : function(ev, callback, context) {
- var calls = this._callbacks || (this._callbacks = {});
- var list = calls[ev] || (calls[ev] = {});
- var tail = list.tail || (list.tail = list.next = {});
- tail.callback = callback;
- tail.context = context;
- list.tail = tail.next = {};
- return this;
- },
- // Remove one or many callbacks. If `callback` is null, removes all
- // callbacks for the event. If `ev` is null, removes all bound callbacks
- // for all events.
- unbind : function(ev, callback) {
- var calls, node, prev;
- if (!ev) {
- this._callbacks = null;
- } else if (calls = this._callbacks) {
- if (!callback) {
- calls[ev] = {};
- } else if (node = calls[ev]) {
- while ((prev = node) && (node = node.next)) {
- if (node.callback !== callback) continue;
- prev.next = node.next;
- node.context = node.callback = null;
- break;
- }
- }
- }
- return this;
- },
- // Trigger an event, firing all bound callbacks. Callbacks are passed the
- // same arguments as `trigger` is, apart from the event name.
- // Listening for `"all"` passes the true event name as the first argument.
- trigger : function(eventName) {
- var node, calls, callback, args, ev, events = ['all', eventName];
- if (!(calls = this._callbacks)) return this;
- while (ev = events.pop()) {
- if (!(node = calls[ev])) continue;
- args = ev == 'all' ? arguments : slice.call(arguments, 1);
- while (node = node.next) if (callback = node.callback) callback.apply(node.context || this, args);
- }
- return this;
- }
- };
- // Backbone.Model
- // --------------
- // Create a new model, with defined attributes. A client id (`cid`)
- // is automatically generated and assigned for you.
- Backbone.Model = function(attributes, options) {
- var defaults;
- attributes || (attributes = {});
- if (options && options.parse) attributes = this.parse(attributes);
- if (defaults = this.defaults) {
- if (_.isFunction(defaults)) defaults = defaults.call(this);
- attributes = _.extend({}, defaults, attributes);
- }
- this.attributes = {};
- this._escapedAttributes = {};
- this.cid = _.uniqueId('c');
- this.set(attributes, {silent : true});
- this._changed = false;
- this._previousAttributes = _.clone(this.attributes);
- if (options && options.collection) this.collection = options.collection;
- this.initialize(attributes, options);
- };
- // Attach all inheritable methods to the Model prototype.
- _.extend(Backbone.Model.prototype, Backbone.Events, {
- // Has the item been changed since the last `"change"` event?
- _changed : false,
- // The default name for the JSON `id` attribute is `"id"`. MongoDB and
- // CouchDB users may want to set this to `"_id"`.
- idAttribute : 'id',
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize : function(){},
- // Return a copy of the model's `attributes` object.
- toJSON : function() {
- return _.clone(this.attributes);
- },
- // Get the value of an attribute.
- get : function(attr) {
- return this.attributes[attr];
- },
- // Get the HTML-escaped value of an attribute.
- escape : function(attr) {
- var html;
- if (html = this._escapedAttributes[attr]) return html;
- var val = this.attributes[attr];
- return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val);
- },
- // Returns `true` if the attribute contains a value that is not null
- // or undefined.
- has : function(attr) {
- return this.attributes[attr] != null;
- },
- // Set a hash of model attributes on the object, firing `"change"` unless you
- // choose to silence it.
- set : function(key, value, options) {
- var attrs;
- if (_.isObject(key)) {
- attrs = key;
- options = value;
- } else {
- attrs = {};
- attrs[key] = value;
- }
- // Extract attributes and options.
- options || (options = {});
- if (!attrs) return this;
- if (attrs.attributes) attrs = attrs.attributes;
- if (options.unset) for (var attr in attrs) attrs[attr] = void 0;
- var now = this.attributes, escaped = this._escapedAttributes;
- // Run validation.
- if (!options.silent && this.validate && !this._performValidation(attrs, options)) return false;
- // Check for changes of `id`.
- if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
- // We're about to start triggering change events.
- var alreadyChanging = this._changing;
- this._changing = true;
- // Update attributes.
- for (var attr in attrs) {
- var val = attrs[attr];
- if (!_.isEqual(now[attr], val) || (options.unset && (attr in now))) {
- options.unset ? delete now[attr] : now[attr] = val;
- delete escaped[attr];
- this._changed = true;
- if (!options.silent) this.trigger('change:' + attr, this, val, options);
- }
- }
- // Fire the `"change"` event, if the model has been changed.
- if (!alreadyChanging) {
- if (!options.silent && this._changed) this.change(options);
- this._changing = false;
- }
- return this;
- },
- // Remove an attribute from the model, firing `"change"` unless you choose
- // to silence it. `unset` is a noop if the attribute doesn't exist.
- unset : function(attr, options) {
- (options || (options = {})).unset = true;
- return this.set(attr, null, options);
- },
- // Clear all attributes on the model, firing `"change"` unless you choose
- // to silence it.
- clear : function(options) {
- (options || (options = {})).unset = true;
- return this.set(_.clone(this.attributes), options);
- },
- // Fetch the model from the server. If the server's representation of the
- // model differs from its current attributes, they will be overriden,
- // triggering a `"change"` event.
- fetch : function(options) {
- options || (options = {});
- var model = this;
- var success = options.success;
- options.success = function(resp, status, xhr) {
- if (!model.set(model.parse(resp, xhr), options)) return false;
- if (success) success(model, resp);
- };
- options.error = wrapError(options.error, model, options);
- return (this.sync || Backbone.sync).call(this, 'read', this, options);
- },
- // Set a hash of model attributes, and sync the model to the server.
- // If the server returns an attributes hash that differs, the model's
- // state will be `set` again.
- save : function(attrs, options) {
- options || (options = {});
- if (attrs && !this.set(attrs, options)) return false;
- var model = this;
- var success = options.success;
- options.success = function(resp, status, xhr) {
- if (!model.set(model.parse(resp, xhr), options)) return false;
- if (success) success(model, resp, xhr);
- };
- options.error = wrapError(options.error, model, options);
- var method = this.isNew() ? 'create' : 'update';
- return (this.sync || Backbone.sync).call(this, method, this, options);
- },
- // Destroy this model on the server if it was already persisted. Upon success, the model is removed
- // from its collection, if it has one.
- destroy : function(options) {
- options || (options = {});
- if (this.isNew()) return this.trigger('destroy', this, this.collection, options);
- var model = this;
- var success = options.success;
- options.success = function(resp) {
- model.trigger('destroy', model, model.collection, options);
- if (success) success(model, resp);
- };
- options.error = wrapError(options.error, model, options);
- return (this.sync || Backbone.sync).call(this, 'delete', this, options);
- },
- // Default URL for the model's representation on the server -- if you're
- // using Backbone's restful methods, override this to change the endpoint
- // that will be called.
- url : function() {
- var base = getUrl(this.collection) || this.urlRoot || urlError();
- if (this.isNew()) return base;
- return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id);
- },
- // **parse** converts a response into the hash of attributes to be `set` on
- // the model. The default implementation is just to pass the response along.
- parse : function(resp, xhr) {
- return resp;
- },
- // Create a new model with identical attributes to this one.
- clone : function() {
- return new this.constructor(this);
- },
- // A model is new if it has never been saved to the server, and lacks an id.
- isNew : function() {
- return this.id == null;
- },
- // Call this method to manually fire a `change` event for this model.
- // Calling this will cause all objects observing the model to update.
- change : function(options) {
- this.trigger('change', this, options);
- this._previousAttributes = _.clone(this.attributes);
- this._changed = false;
- },
- // Determine if the model has changed since the last `"change"` event.
- // If you specify an attribute name, determine if that attribute has changed.
- hasChanged : function(attr) {
- if (attr) return this._previousAttributes[attr] != this.attributes[attr];
- return this._changed;
- },
- // Return an object containing all the attributes that have changed, or false
- // if there are no changed attributes. Useful for determining what parts of a
- // view need to be updated and/or what attributes need to be persisted to
- // the server. Unset attributes will be set to undefined.
- changedAttributes : function(now) {
- if (!this._changed) return false;
- now || (now = this.attributes);
- var changed = false, old = this._previousAttributes;
- for (var attr in now) {
- if (_.isEqual(old[attr], now[attr])) continue;
- (changed || (changed = {}))[attr] = now[attr];
- }
- for (var attr in old) {
- if (!(attr in now)) (changed || (changed = {}))[attr] = void 0;
- }
- return changed;
- },
- // Get the previous value of an attribute, recorded at the time the last
- // `"change"` event was fired.
- previous : function(attr) {
- if (!attr || !this._previousAttributes) return null;
- return this._previousAttributes[attr];
- },
- // Get all of the attributes of the model at the time of the previous
- // `"change"` event.
- previousAttributes : function() {
- return _.clone(this._previousAttributes);
- },
- // Run validation against a set of incoming attributes, returning `true`
- // if all is well. If a specific `error` callback has been passed,
- // call that instead of firing the general `"error"` event.
- _performValidation : function(attrs, options) {
- var error = this.validate(attrs, options);
- if (error) {
- if (options.error) {
- options.error(this, error, options);
- } else {
- this.trigger('error', this, error, options);
- }
- return false;
- }
- return true;
- }
- });
- // Backbone.Collection
- // -------------------
- // Provides a standard collection class for our sets of models, ordered
- // or unordered. If a `comparator` is specified, the Collection will maintain
- // its models in sort order, as they're added and removed.
- Backbone.Collection = function(models, options) {
- options || (options = {});
- if (options.comparator) this.comparator = options.comparator;
- _.bindAll(this, '_onModelEvent', '_removeReference');
- this._reset();
- if (models) this.reset(models, {silent: true});
- this.initialize.apply(this, arguments);
- };
- // Define the Collection's inheritable methods.
- _.extend(Backbone.Collection.prototype, Backbone.Events, {
- // The default model for a collection is just a **Backbone.Model**.
- // This should be overridden in most cases.
- model : Backbone.Model,
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize : function(){},
- // The JSON representation of a Collection is an array of the
- // models' attributes.
- toJSON : function() {
- return this.map(function(model){ return model.toJSON(); });
- },
- // Add a model, or list of models to the set. Pass **silent** to avoid
- // firing the `added` event for every new model.
- add : function(models, options) {
- if (_.isArray(models)) {
- for (var i = 0, l = models.length; i < l; i++) {
- this._add(models[i], options);
- }
- } else {
- this._add(models, options);
- }
- return this;
- },
- // Remove a model, or a list of models from the set. Pass silent to avoid
- // firing the `removed` event for every model removed.
- remove : function(models, options) {
- if (_.isArray(models)) {
- for (var i = 0, l = models.length; i < l; i++) {
- this._remove(models[i], options);
- }
- } else {
- this._remove(models, options);
- }
- return this;
- },
- // Get a model from the set by id.
- get : function(id) {
- if (id == null) return null;
- return this._byId[id.id != null ? id.id : id];
- },
- // Get a model from the set by client id.
- getByCid : function(cid) {
- return cid && this._byCid[cid.cid || cid];
- },
- // Get the model at the given index.
- at : function(index) {
- return this.models[index];
- },
- // Force the collection to re-sort itself. You don't need to call this under normal
- // circumstances, as the set will maintain sort order as each item is added.
- sort : function(options) {
- options || (options = {});
- if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
- this.models = this.sortBy(this.comparator);
- if (!options.silent) this.trigger('reset', this, options);
- return this;
- },
- // Pluck an attribute from each model in the collection.
- pluck : function(attr) {
- return _.map(this.models, function(model){ return model.get(attr); });
- },
- // When you have more items than you want to add or remove individually,
- // you can reset the entire set with a new list of models, without firing
- // any `added` or `removed` events. Fires `reset` when finished.
- reset : function(models, options) {
- models || (models = []);
- options || (options = {});
- this.each(this._removeReference);
- this._reset();
- this.add(models, {silent: true, parse: options.parse});
- if (!options.silent) this.trigger('reset', this, options);
- return this;
- },
- // Fetch the default set of models for this collection, resetting the
- // collection when they arrive. If `add: true` is passed, appends the
- // models to the collection instead of resetting.
- fetch : function(options) {
- options || (options = {});
- if (options.parse === undefined) options.parse = true;
- var collection = this;
- var success = options.success;
- options.success = function(resp, status, xhr) {
- collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);
- if (success) success(collection, resp);
- };
- options.error = wrapError(options.error, collection, options);
- return (this.sync || Backbone.sync).call(this, 'read', this, options);
- },
- // Create a new instance of a model in this collection. After the model
- // has been created on the server, it will be added to the collection.
- // Returns the model, or 'false' if validation on a new model fails.
- create : function(model, options) {
- var coll = this;
- options || (options = {});
- model = this._prepareModel(model, options);
- if (!model) return false;
- var success = options.success;
- options.success = function(nextModel, resp, xhr) {
- coll.add(nextModel, options);
- if (success) success(nextModel, resp, xhr);
- };
- model.save(null, options);
- return model;
- },
- // **parse** converts a response into a list of models to be added to the
- // collection. The default implementation is just to pass it through.
- parse : function(resp, xhr) {
- return resp;
- },
- // Proxy to _'s chain. Can't be proxied the same way the rest of the
- // underscore methods are proxied because it relies on the underscore
- // constructor.
- chain : function () {
- return _(this.models).chain();
- },
- // Reset all internal state. Called when the collection is reset.
- _reset : function(options) {
- this.length = 0;
- this.models = [];
- this._byId = {};
- this._byCid = {};
- },
- // Prepare a model to be added to this collection
- _prepareModel : function(model, options) {
- if (!(model instanceof Backbone.Model)) {
- var attrs = model;
- model = new this.model(attrs, {collection: this, parse: options.parse});
- if (model.validate && !model._performValidation(model.attributes, options)) model = false;
- } else if (!model.collection) {
- model.collection = this;
- }
- return model;
- },
- // Internal implementation of adding a single model to the set, updating
- // hash indexes for `id` and `cid` lookups.
- // Returns the model, or 'false' if validation on a new model fails.
- _add : function(model, options) {
- options || (options = {});
- model = this._prepareModel(model, options);
- if (!model) return false;
- var already = this.getByCid(model);
- if (already) throw new Error(["Can't add the same model to a set twice", already.id]);
- this._byId[model.id] = model;
- this._byCid[model.cid] = model;
- var index = options.at != null ? options.at :
- this.comparator ? this.sortedIndex(model, this.comparator) :
- this.length;
- this.models.splice(index, 0, model);
- model.bind('all', this._onModelEvent);
- this.length++;
- options.index = index;
- if (!options.silent) model.trigger('add', model, this, options);
- return model;
- },
- // Internal implementation of removing a single model from the set, updating
- // hash indexes for `id` and `cid` lookups.
- _remove : function(model, options) {
- options || (options = {});
- model = this.getByCid(model) || this.get(model);
- if (!model) return null;
- delete this._byId[model.id];
- delete this._byCid[model.cid];
- var index = this.indexOf(model);
- this.models.splice(index, 1);
- this.length--;
- options.index = index;
- if (!options.silent) model.trigger('remove', model, this, options);
- this._removeReference(model);
- return model;
- },
- // Internal method to remove a model's ties to a collection.
- _removeReference : function(model) {
- if (this == model.collection) {
- delete model.collection;
- }
- model.unbind('all', this._onModelEvent);
- },
- // Internal method called every time a model in the set fires an event.
- // Sets need to update their indexes when models change ids. All other
- // events simply proxy through. "add" and "remove" events that originate
- // in other collections are ignored.
- _onModelEvent : function(ev, model, collection, options) {
- if ((ev == 'add' || ev == 'remove') && collection != this) return;
- if (ev == 'destroy') {
- this._remove(model, options);
- }
- if (model && ev === 'change:' + model.idAttribute) {
- delete this._byId[model.previous(model.idAttribute)];
- this._byId[model.id] = model;
- }
- this.trigger.apply(this, arguments);
- }
- });
- // Underscore methods that we want to implement on the Collection.
- var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 'detect',
- 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
- 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size',
- 'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty', 'groupBy'];
- // Mix in each Underscore method as a proxy to `Collection#models`.
- _.each(methods, function(method) {
- Backbone.Collection.prototype[method] = function() {
- return _[method].apply(_, [this.models].concat(_.toArray(arguments)));
- };
- });
- // Backbone.Router
- // -------------------
- // Routers map faux-URLs to actions, and fire events when routes are
- // matched. Creating a new one sets its `routes` hash, if not set statically.
- Backbone.Router = function(options) {
- options || (options = {});
- if (options.routes) this.routes = options.routes;
- this._bindRoutes();
- this.initialize.apply(this, arguments);
- };
- // Cached regular expressions for matching named param parts and splatted
- // parts of route strings.
- var namedParam = /:([\w\d]+)/g;
- var splatParam = /\*([\w\d]+)/g;
- var escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g;
- // Set up all inheritable **Backbone.Router** properties and methods.
- _.extend(Backbone.Router.prototype, Backbone.Events, {
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize : function(){},
- // Manually bind a single named route to a callback. For example:
- //
- // this.route('search/:query/p:num', 'search', function(query, num) {
- // ...
- // });
- //
- route : function(route, name, callback) {
- Backbone.history || (Backbone.history = new Backbone.History);
- if (!_.isRegExp(route)) route = this._routeToRegExp(route);
- Backbone.history.route(route, _.bind(function(fragment) {
- var args = this._extractParameters(route, fragment);
- callback && callback.apply(this, args);
- this.trigger.apply(this, ['route:' + name].concat(args));
- }, this));
- },
- // Simple proxy to `Backbone.history` to save a fragment into the history.
- navigate : function(fragment, options) {
- Backbone.history.navigate(fragment, options);
- },
- // Bind all defined routes to `Backbone.history`. We have to reverse the
- // order of the routes here to support behavior where the most general
- // routes can be defined at the bottom of the route map.
- _bindRoutes : function() {
- if (!this.routes) return;
- var routes = [];
- for (var route in this.routes) {
- routes.unshift([route, this.routes[route]]);
- }
- for (var i = 0, l = routes.length; i < l; i++) {
- this.route(routes[i][0], routes[i][1], this[routes[i][1]]);
- }
- },
- // Convert a route string into a regular expression, suitable for matching
- // against the current location hash.
- _routeToRegExp : function(route) {
- route = route.replace(escapeRegExp, "\\$&")
- .replace(namedParam, "([^\/]*)")
- .replace(splatParam, "(.*?)");
- return new RegExp('^' + route + '$');
- },
- // Given a route, and a URL fragment that it matches, return the array of
- // extracted parameters.
- _extractParameters : function(route, fragment) {
- return route.exec(fragment).slice(1);
- }
- });
- // Backbone.History
- // ----------------
- // Handles cross-browser history management, based on URL fragments. If the
- // browser does not support `onhashchange`, falls back to polling.
- Backbone.History = function() {
- this.handlers = [];
- _.bindAll(this, 'checkUrl');
- };
- // Cached regex for cleaning hashes.
- var hashStrip = /^#/;
- // Cached regex for detecting MSIE.
- var isExplorer = /msie [\w.]+/;
- // Has the history handling already been started?
- var historyStarted = false;
- // Set up all inheritable **Backbone.History** properties and methods.
- _.extend(Backbone.History.prototype, {
- // The default interval to poll for hash changes, if necessary, is
- // twenty times a second.
- interval: 50,
- // Get the cross-browser normalized URL fragment, either from the URL,
- // the hash, or the override.
- getFragment : function(fragment, forcePushState) {
- if (fragment == null) {
- if (this._hasPushState || forcePushState) {
- fragment = window.location.pathname;
- var search = window.location.search;
- if (search) fragment += search;
- } else {
- fragment = window.location.hash;
- }
- }
- fragment = decodeURIComponent(fragment.replace(hashStrip, ''));
- if (!fragment.indexOf(this.options.root)) fragment = fragment.substr(this.options.root.length);
- return fragment;
- },
- // Start the hash change handling, returning `true` if the current URL matches
- // an existing route, and `false` otherwise.
- start : function(options) {
- // Figure out the initial configuration. Do we need an iframe?
- // Is pushState desired ... is it available?
- if (historyStarted) throw new Error("Backbone.history has already been started");
- this.options = _.extend({}, {root: '/'}, this.options, options);
- this._wantsPushState = !!this.options.pushState;
- this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState);
- var fragment = this.getFragment();
- var docMode = document.documentMode;
- var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
- if (oldIE) {
- this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
- this.navigate(fragment);
- }
- // Depending on whether we're using pushState or hashes, and whether
- // 'onhashchange' is supported, determine how we check the URL state.
- if (this._hasPushState) {
- $(window).bind('popstate', this.checkUrl);
- } else if ('onhashchange' in window && !oldIE) {
- $(window).bind('hashchange', this.checkUrl);
- } else {
- setInterval(this.checkUrl, this.interval);
- }
- // Determine if we need to change the base url, for a pushState link
- // opened by a non-pushState browser.
- this.fragment = fragment;
- historyStarted = true;
- var loc = window.location;
- var atRoot = loc.pathname == this.options.root;
- if (this._wantsPushState && !this._hasPushState && !atRoot) {
- this.fragment = this.getFragment(null, true);
- window.location.replace(this.options.root + '#' + this.fragment);
- // Return immediately as browser will do redirect to new url
- return true;
- } else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
- this.fragment = loc.hash.replace(hashStrip, '');
- window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment);
- }
- if (!this.options.silent) {
- return this.loadUrl();
- }
- },
- // Add a route to be tested when the fragment changes. Routes added later may
- // override previous routes.
- route : function(route, callback) {
- this.handlers.unshift({route : route, callback : callback});
- },
- // Checks the current URL to see if it has changed, and if it has,
- // calls `loadUrl`, normalizing across the hidden iframe.
- checkUrl : function(e) {
- var current = this.getFragment();
- if (current == this.fragment && this.iframe) current = this.getFragment(this.iframe.location.hash);
- if (current == this.fragment || current == decodeURIComponent(this.fragment)) return false;
- if (this.iframe) this.navigate(current);
- this.loadUrl() || this.loadUrl(window.location.hash);
- },
- // Attempt to load the current URL fragment. If a route succeeds with a
- // match, returns `true`. If no defined routes matches the fragment,
- // returns `false`.
- loadUrl : function(fragmentOverride) {
- var fragment = this.fragment = this.getFragment(fragmentOverride);
- var matched = _.any(this.handlers, function(handler) {
- if (handler.route.test(fragment)) {
- handler.callback(fragment);
- return true;
- }
- });
- return matched;
- },
- // Save a fragment into the hash history, or replace the URL state if the
- // 'replace' option is passed. You are responsible for properly URL-encoding
- // the fragment in advance.
- //
- // The options object can contain `trigger: true` if you wish to have the
- // route callback be fired (not usually desirable), or `replace: true`, if
- // you which to modify the current URL without adding an entry to the history.
- navigate : function(fragment, options) {
- if (!options || options === true) options = {trigger: options};
- var frag = (fragment || '').replace(hashStrip, '');
- if (this.fragment == frag || this.fragment == decodeURIComponent(frag)) return;
- if (this._hasPushState) {
- if (frag.indexOf(this.options.root) != 0) frag = this.options.root + frag;
- this.fragment = frag;
- window.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, frag);
- } else {
- this.fragment = frag;
- this._updateHash(window.location, frag, options.replace);
- if (this.iframe && (frag != this.getFragment(this.iframe.location.hash))) {
- // Opening and closing the iframe tricks IE7 and earlier to push a history entry on hash-tag change.
- // When replace is true, we don't want this.
- if(!options.replace) this.iframe.document.open().close();
- this._updateHash(this.iframe.location, frag, options.replace);
- }
- }
- if (options.trigger) this.loadUrl(fragment);
- },
- // Update the hash location, either replacing the current entry, or adding
- // a new one to the browser history.
- _updateHash: function(location, fragment, replace) {
- if (replace) {
- location.replace(location.toString().replace(/(javascript:|#).*$/, "") + "#" + fragment);
- } else {
- location.hash = fragment;
- }
- }
- });
- // Backbone.View
- // -------------
- // Creating a Backbone.View creates its initial element outside of the DOM,
- // if an existing element is not provided...
- Backbone.View = function(options) {
- this.cid = _.uniqueId('view');
- this._configure(options || {});
- this._ensureElement();
- this.delegateEvents();
- this.initialize.apply(this, arguments);
- };
- // Cached regex to split keys for `delegate`.
- var eventSplitter = /^(\S+)\s*(.*)$/;
- // List of view options to be merged as properties.
- var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName'];
- // Set up all inheritable **Backbone.View** properties and methods.
- _.extend(Backbone.View.prototype, Backbone.Events, {
- // The default `tagName` of a View's element is `"div"`.
- tagName : 'div',
- // jQuery delegate for element lookup, scoped to DOM elements within the
- // current view. This should be prefered to global lookups where possible.
- $ : function(selector) {
- return (selector == null) ? $(this.el) : $(selector, this.el);
- },
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize : function(){},
- // **render** is the core function that your view should override, in order
- // to populate its element (`this.el`), with the appropriate HTML. The
- // convention is for **render** to always return `this`.
- render : function() {
- return this;
- },
- // Remove this view from the DOM. Note that the view isn't present in the
- // DOM by default, so calling this method may be a no-op.
- remove : function() {
- $(this.el).remove();
- return this;
- },
- // For small amounts of DOM Elements, where a full-blown template isn't
- // needed, use **make** to manufacture elements, one at a time.
- //
- // var el = this.make('li', {'class': 'row'}, this.model.escape('title'));
- //
- make : function(tagName, attributes, content) {
- var el = document.createElement(tagName);
- if (attributes) $(el).attr(attributes);
- if (content) $(el).html(content);
- return el;
- },
- // Set callbacks, where `this.events` is a hash of
- //
- // *{"event selector": "callback"}*
- //
- // {
- // 'mousedown .title': 'edit',
- // 'click .button': 'save'
- // }
- //
- // pairs. Callbacks will be bound to the view, with `this` set properly.
- // Uses event delegation for efficiency.
- // Omitting the selector binds the event to `this.el`.
- // This only works for delegate-able events: not `focus`, `blur`, and
- // not `change`, `submit`, and `reset` in Internet Explorer.
- delegateEvents : function(events) {
- if (!(events || (events = this.events))) return;
- if (_.isFunction(events)) events = events.call(this);
- this.undelegateEvents();
- for (var key in events) {
- var method = this[events[key]];
- if (!method) throw new Error('Event "' + events[key] + '" does not exist');
- var match = key.match(eventSplitter);
- var eventName = match[1], selector = match[2];
- method = _.bind(method, this);
- eventName += '.delegateEvents' + this.cid;
- if (selector === '') {
- $(this.el).bind(eventName, method);
- } else {
- $(this.el).delegate(selector, eventName, method);
- }
- }
- },
- // Clears all callbacks previously bound to the view with `delegateEvents`.
- undelegateEvents : function() {
- $(this.el).unbind('.delegateEvents' + this.cid);
- },
- // Performs the initial configuration of a View with a set of options.
- // Keys with special meaning *(model, collection, id, className)*, are
- // attached directly to the view.
- _configure : function(options) {
- if (this.options) options = _.extend({}, this.options, options);
- for (var i = 0, l = viewOptions.length; i < l; i++) {
- var attr = viewOptions[i];
- if (options[attr]) this[attr] = options[attr];
- }
- this.options = options;
- },
- // Ensure that the View has a DOM element to render into.
- // If `this.el` is a string, pass it through `$()`, take the first
- // matching element, and re-assign it to `el`. Otherwise, create
- // an element from the `id`, `className` and `tagName` properties.
- _ensureElement : function() {
- if (!this.el) {
- var attrs = this.attributes || {};
- if (this.id) attrs.id = this.id;
- if (this.className) attrs['class'] = this.className;
- this.el = this.make(this.tagName, attrs);
- } else if (_.isString(this.el)) {
- this.el = $(this.el).get(0);
- }
- }
- });
- // The self-propagating extend function that Backbone classes use.
- var extend = function (protoProps, classProps) {
- var child = inherits(this, protoProps, classProps);
- child.extend = this.extend;
- return child;
- };
- // Set up inheritance for the model, collection, and view.
- Backbone.Model.extend = Backbone.Collection.extend =
- Backbone.Router.extend = Backbone.View.extend = extend;
- // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
- var methodMap = {
- 'create': 'POST',
- 'update': 'PUT',
- 'delete': 'DELETE',
- 'read' : 'GET'
- };
- // Backbone.sync
- // -------------
- // Override this function to change the manner in which Backbone persists
- // models to the server. You will be passed the type of request, and the
- // model in question. By default, makes a RESTful Ajax request
- // to the model's `url()`. Some possible customizations could be:
- //
- // * Use `setTimeout` to batch rapid-fire updates into a single request.
- // * Send up the models as XML instead of JSON.
- // * Persist models via WebSockets instead of Ajax.
- //
- // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
- // as `POST`, with a `_method` parameter containing the true HTTP method,
- // as well as all requests with the body as `application/x-www-form-urlencoded` instead of
- // `application/json` with the model in a param named `model`.
- // Useful when interfacing with server-side languages like **PHP** that make
- // it difficult to read the body of `PUT` requests.
- Backbone.sync = function(method, model, options) {
- var type = methodMap[method];
- // Default JSON-request options.
- var params = {type : type, dataType : 'json'};
- // Ensure that we have a URL.
- if (!options.url) {
- params.url = getUrl(model) || urlError();
- }
- // Ensure that we have the appropriate request data.
- if (!options.data && model && (method == 'create' || method == 'update')) {
- params.contentType = 'application/json';
- params.data = JSON.stringify(model.toJSON());
- }
- // For older servers, emulate JSON by encoding the request into an HTML-form.
- if (Backbone.emulateJSON) {
- params.contentType = 'application/x-www-form-urlencoded';
- params.data = params.data ? {model : params.data} : {};
- }
- // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
- // And an `X-HTTP-Method-Override` header.
- if (Backbone.emulateHTTP) {
- if (type === 'PUT' || type === 'DELETE') {
- if (Backbone.emulateJSON) params.data._method = type;
- params.type = 'POST';
- params.beforeSend = function(xhr) {
- xhr.setRequestHeader('X-HTTP-Method-Override', type);
- };
- }
- }
- // Don't process data on a non-GET request.
- if (params.type !== 'GET' && !Backbone.emulateJSON) {
- params.processData = false;
- }
- // Make the request, allowing the user to override any Ajax options.
- return $.ajax(_.extend(params, options));
- };
- // Helpers
- // -------
- // Shared empty constructor function to aid in prototype-chain creation.
- var ctor = function(){};
- // Helper function to correctly set up the prototype chain, for subclasses.
- // Similar to `goog.inherits`, but uses a hash of prototype properties and
- // class properties to be extended.
- var inherits = function(parent, protoProps, staticProps) {
- var child;
- // The constructor function for the new subclass is either defined by you
- // (the "constructor" property in your `extend` definition), or defaulted
- // by us to simply call `super()`.
- if (protoProps && protoProps.hasOwnProperty('constructor')) {
- child = protoProps.constructor;
- } else {
- child = function(){ return parent.apply(this, arguments); };
- }
- // Inherit class (static) properties from parent.
- _.extend(child, parent);
- // Set the prototype chain to inherit from `parent`, without calling
- // `parent`'s constructor function.
- ctor.prototype = parent.prototype;
- child.prototype = new ctor();
- // Add prototype properties (instance properties) to the subclass,
- // if supplied.
- if (protoProps) _.extend(child.prototype, protoProps);
- // Add static properties to the constructor function, if supplied.
- if (staticProps) _.extend(child, staticProps);
- // Correctly set child's `prototype.constructor`.
- child.prototype.constructor = child;
- // Set a convenience property in case the parent's prototype is needed later.
- child.__super__ = parent.prototype;
- return child;
- };
- // Helper function to get a URL from a Model or Collection as a property
- // or as a function.
- var getUrl = function(object) {
- if (!(object && object.url)) return null;
- return _.isFunction(object.url) ? object.url() : object.url;
- };
- // Throw an error when a URL is needed, and none is supplied.
- var urlError = function() {
- throw new Error('A "url" property or function must be specified');
- };
- // Wrap an optional error callback with a fallback error event.
- var wrapError = function(onError, originalModel, options) {
- return function(model, resp) {
- var resp = model === originalModel ? resp : model;
- if (onError) {
- onError(model, resp, options);
- } else {
- originalModel.trigger('error', model, resp, options);
- }
- };
- };
- }).call(this);
- // Gbone.js 0.1.0
- // (c) 2011 Gobhi Theivendran
- // Gbone.js may be freely distributed under the MIT license.
- // For all details and documentation:
- // https://github.com/gobhi/gbone.js
- (function( root, factory ) {
- // Set up Gbone appropriately for the environment.
- if ( typeof exports !== 'undefined' ) {
- // Node/CommonJS, no need for jQuery/Zepto in that case.
- factory( root, require('backbone'), exports, require('underscore') );
- } else if ( typeof define === 'function' && define.amd ) {
- // AMD
- define('gbone', ['underscore', 'backbone', 'zepto', 'exports'], function( _, Backbone, $, exports ) {
- // Export global even in AMD case in case this script is loaded with
- // others that may still expect a global GBone.
- root.Gbone = factory( root, Backbone, exports, _, $ );
- });
- } else {
- // Browser globals
- root.Gbone = factory( root, Backbone, {}, root._, ( root.jQuery || root.Zepto || root.ender ) );
- }
- }(this, function( root, Backbone, Gbone, _, $ ) {
-
- // Initial Setup
- // -------------
-
- // Mixins
- var observer, cleanup, transitions, state,
- // State machine for Panel Views.
- Manager;
- // Current version of the library.
- Gbone.VERSION = '0.1.0';
- // Mixins
- // -----------------
- // Each mixin operates on an object's `prototype`.
- // The observer mixin contains behavior for binding to events in a fashion
- // that can be cleaned up later.
- // `this.bindTo(this.collection, 'change', this.render);`
- // `this.unbindFromAll();`
- //
- observer = function (obj) {
- // On top of binding `event` to `source`, keeps track of all the event
- // handlers that are bound. A single call to `unbindFromAll()` will
- // unbind them.
- obj.bindTo = function (source, event, callback) {
- source.bind(event, callback, this);
- this.bindings = this.bindings || [];
- this.bindings.push({ source: source, event: event, callback: callback });
- };
- // Unbind all events.
- obj.unbindFromAll = function () {
- _.each(this.bindings, function (binding) {
- binding.source.unbind(binding.event, binding.callback);
- });
- this.bindings = [];
- };
- };
-
-
- // The cleanup mixin contains set of helpers for adding/managing
- // immediate child Views, cleaning up and housekeeping. Used with the
- // observer mixin. Maintains an internal array of child Views.
- //
- cleanup = function (obj) {
-
- // Cleanup child Views, DOM events, Model/Collection events
- // and events from this View.
- obj.cleanup = function () {
- this.unbind();
- if (this.unbindFromAll) this.unbindFromAll();
- this._cleanupChildren();
- this.removeFromParent();
- this.remove();
- };
-
- // Append a child View into the given `view`.
- obj.appendChild = function (view) {
- this._addChild(view);
- $(this.el).append(view.el);
- };
- // Append a child View into a specific `container` element in the
- // given `view`.
- obj.appendChildInto = function (view, container) {
- this._addChild(view);
- this.$(container).append(view.el);
- };
- obj._addChild = function (view) {
- this.children = this.children || [];
- this.children.push(view);
- view.parent = this;
- };
- obj._cleanupChildren = function () {
- _.each(this.children, function (view) {
- if (view.cleanup) view.cleanup();
- });
- };
- // Remove this View from its parent View.
- obj.removeFromParent = function () {
- this.parent && this.parent.removeChild(this);
- };
- // Remove the given child View `view` from this View.
- obj.removeChild = function (view) {
- var index = _.indexOf(this.children, view);
- this.children.splice(index, 1);
- };
- };
-
-
- // The transitions mixin contains functions and objects needed for
- // doing transition effects when Panel Views are activated/deactivated.
- // There are default transitions provided, but you can add your own
- // by using the `addTransition` method. When adding a new transition,
- // it must have a definition under `effects` and `reverseEffects` objects
- // of the Panel. It must also take in an arugment `callback`, which
- // is a function that will be called once the transition is complete.
- // Check out the default transitions code below for an example of how to setup
- // your own transitions.
- // Note that if jQuery is used, the default transitions
- // require GFX (http://maccman.github.com/gfx/), or if Zepto is used then
- // Zepto-GFX (https://github.com/gobhi/zepto-gfx).
- //
- transitions = function (obj) {
-
- // Animation options for the default transitions.
- var effectOptions = {
- duration: 450,
- easing: 'cubic-bezier(.25, .1, .25, 1)'
- },
-
- // Helper function to handle the transitions for the default
- // effects.
- handleTransition = function (that, anim, options, callback) {
-
- var l = that.transitionBindings.length,
- // Helper function to animate a single element.
- animate = function (container, ops, index) {
- if (!$.fn[anim]) throw new Error('$.fn.' + anim + ' is not available');
-
- if ($.fn.gfx) {
- // Using GFX.
- container[anim](ops);
- // Only call the callback function if this is the last animation.
- if (index === l-1) container.queueNext(callback);
- } else {
- // Using Zepto-GFX. Only call the callback function if this is
- // the last animation.
- (index === l-1) ? container[anim](ops, callback) : container[anim](ops);
- }
- };
-
- // Animate each element.
- _.each(that.transitionBindings, function(elm, index) {
- var container = that.$(elm);
- if (container.length === 0)
- throw new Error('The container element to animate is not \
- availabe in this view.');
-
- animate(container, options, index);
- });
- };
-
- // The default element(s) in the Panel to animate for the transitions.
- // An array of elements/selectors of the form
- // `['.header', '.container', '.footer', ...]`. Each element/selector
- // in the `transitionBindings` array represents a child DOM element
- // within the Panel that is to be animated. If `transitionBindings`
- // is not overridden, the default child element that will be animated
- // in the Panel View is `.container`.
- obj.transitionBindings = ['.container'];
-
- // Transition effects for activation.
- obj.effects = {
-
- // Slide in from the left.
- left: function (callback) {
- var $el = $(this.el),
- options = _.extend({}, effectOptions, {direction: 'left'})
-
- handleTransition(this, 'gfxSlideIn', options, callback);
- },
- // Slide in from the right.
- right: function (callback) {
- var $el = $(this.el),
- options = _.extend({}, effectOptions, {direction: 'right'});
-
- handleTransition(this, 'gfxSlideIn', options, callback);
- }
- };
-
-
- // Transition effects for deactivation.
- obj.reverseEffects = {
- // Reverse transition for the slide in from
- // left: slide out to the right.
- left: function (callback) {
- var $el = $(this.el),
- options = _.extend({}, effectOptions, {direction: 'right'});
-
- handleTransition(this, 'gfxSlideOut', options, callback);
- },
-
- // Reverse transition for the slide in from
- // right: slide out to the left.
- right: function (callback) {
- var $el = $(this.el),
- options = _.extend({}, effectOptions, {direction: 'left'});
-
- handleTransition(this, 'gfxSlideOut', options, callback);
- }
- };
-
- // Add a new transition. The `transition` argument is an object as follows:
- // `transition.effects` - Object that contains the activation transitions to be added.
- // `transition.reverseEffects` - Object that contains the deactivation transitions.
- // See the default transition effects defined above for an example.
- obj.addTransition = function (transition) {
- if (!transition.effects) throw new Error('transition.effects is not set.');
- if (!transition.reverseEffects) throw new Error('transition.reverseEffects \
- is not set.');
- _.extend(this.effects, transition.effects);
- _.extend(this.reverseEffects, transition.reverseEffects);
- };
-
- };
-
-
- // The state mixin contains methods used by the Manager to handle
- // activating/deactivating the Views it manages.
- //
- state = function (obj) {
-
- obj.active = function () {
- // Add in `active` as the first argument.
- Array.prototype.unshift.call(arguments, 'active');
- this.trigger.apply(this, arguments);
- };
- obj.isActive = function () {
- return $(this.el).hasClass('active');
- };
- obj._activate = function (params) {
- var that = this;
-
- $(this.el).addClass('active').show();
- // Once the transition is completed (if any), trigger the activated
- // event.
- if (params && params.trans && this.effects &&
- this.effects[params.trans]) {
- this.effects[params.trans].call(this, function() {
- that.trigger('activated');
- });
- } else {
- this.trigger('activated');
- }
- };
- obj._deactivate = function (params) {
- if (!this.isActive()) return;
- var that = this,
- callback = function () {
- $(that.el).removeClass('active').hide();
- that.trigger('deactivated');
- };
- if (params && params.trans && this.reverseEffects &&
- this.reverseEffects[params.trans]) {
- this.reverseEffects[params.trans].call(this, callback);
- } else {
- callback();
- }
- };
- };
-
-
- // Manager
- // -----------------
- // The Manager class is a state machine for managing Views.
- //
- Manager = function () {
- this._setup.apply(this, arguments);
- };
- _.extend(Manager.prototype, Backbone.Events, {
-
- _setup: function () {
- this.views = [];
- this.bind('change', this._change, this);
- this.add.apply(this, arguments);
- },
- // Add one or more Views.
- // `add(panel1, panel2, ...)`
- add: function () {
- _.each(Array.prototype.slice.call(arguments), function (view) {
- this.addOne(view);
- }, this);
- },
- // Add a View.
- addOne: function (view) {
- view.bind('active', function () {
- Array.prototype.unshift.call(arguments, view);
- Array.prototype.unshift.call(arguments, 'change');
- this.trigger.apply(this, arguments);
- }, this);
- this.views.push(view);
- },
-
- // Deactivate all managed Views.
- deactivateAll: function () {
- Array.prototype.unshift.call(arguments, false);
- Array.prototype.unshift.call(arguments, 'change');
- this.trigger.apply(this, arguments);
- },
-
- // For the View passed in - `current`, check if it's available in the
- // internal Views array, activate it and deactivate the others.
- _change: function (current) {
- var args = Array.prototype.slice.call(arguments, 1);
- _.each(this.views, function (view) {
- if (view === current) {
- view._activate.apply(view, args);
- } else {
- view._deactivate.apply(view, args);
- }
- }, this);
- }
- });
-
-
- // Gbone.Stage
- // -----------------
- // A Stage is a essentially a View that covers the
- // entire viewport. It has a default `template` (that can be
- // overridden), transition support and contains Panel views
- // that it manages using Manager.
- // Stages generally cover the entire viewport. Panels are nested
- // in a Stage and can be transitioned.
- // An application usually displays one Stage and Panel at a time.
- // The Stage's Panels can then transition in and out to show
- // different parts of the application.
- //
- Gbone.Stage = function (options) {
- this._setup(options, 'stage');
- Backbone.View.call(this, options);
- };
- _.extend(Gbone.Stage.prototype,
- Backbone.View.prototype, {
-
- // The default html `skeleton` template to be used by the Stage.
- // It's important that the class `viewport` be set in an element
- // in the `skeleton`. This element will be used by the Stage to append its
- // Panel Views.
- skeleton: _.template('<header></header><article class="viewport"> \
- </article><footer></footer>'),
-
-
- // Add Panel(s) to this Stage.
- add: function () {
- this._manager = this._manager || new Manager();
- this._manager.add.apply(this._manager, arguments);
- this._append.apply(this, arguments);
- },
-
- // Retrieve a Panel with a name of `name` in this Stage (if any).
- getPanel: function(name) {
- // This Stage doesn't have any Panels.
- if (!this._manager) return null;
- var views = this._manager.views;
- return _.find(this._manager.views, function (panel) {
- return panel.name === name;
- });
- },
- // Append Panel(s) to this Stage.
- _append: function () {
- if (this.$('.viewport').length === 0) {
- throw new Error('The Stage must have an element with \
- class \'viewport\' that will be used to append the Panels to.');
- }
-
- _.each(Array.prototype.slice.call(arguments), function (panel) {
- if (panel.stage !== this) panel.stage = this;
- this.appendChildInto(panel, '.viewport');
- }, this);
-
- },
-
- // Called in the constructor during initialization.
- _setup: function (options) {
- _.bindAll(this);
- options.el ? this.el = options.el : this._ensureElement();
-
- // If a `name` is not provided, create one. The name is used
- // primarily for setting up the routes.
- this.name = options.name || _.uniqueId('stage-');
- $(this.el).addClass('stage').html(this.skeleton());
-
- // Create a Router if one is not provided.
- this.router = options.router || Backbone.Router.extend();
- }
-
- });
-
- observer(Gbone.Stage.prototype);
- cleanup(Gbone.Stage.prototype);
-
-
- // Gbone.Panel
- // -----------------
- // Similar to a Stage, a Panel is just a View with transition
- // support whenever it is activated/deactivated. A Panel's
- // parent is a Stage and that Stage is responsible for
- // managing and activating/deactivating the Panel.
- // Usually only one Panel is shown in the application at one time.
- //
- Gbone.Panel = function (options) {
- this._setup(options, 'panel');
- Backbone.View.call(this, options);
- };
- _.extend(Gbone.Panel.prototype,
- Backbone.View.prototype, {
-
- // The default html `skeleton` to be used by the Panel. This can be overridden
- // when extending the Panel View.
- skeleton: _.template('<div class="container"><header></header><article></article></div>'),
- // Setup the routing for the Panel.
- // The route for a Panel is as follows: `[stage name]/[panel name]/trans-:trans`
- // where `trans-:trans` is optional and is used to set the transition effect.
- // The `callback` gets called after the routing happens. Within the callback you
- // should activate the Panel by calling the `active` method on it and/or
- // `render`etc...
- routePanel: function (callback) {
- if (this.stage) {
- this.stage.router.route(this.stage.name + '/' + this.name + '/trans-:trans', this.name, callback);
- this.stage.router.route(this.stage.name + '/' + this.name, this.name, callback);
- } else {
- throw new Error('A Stage for this Panel is not available.');
- }
- },
-
- // Called in the constructor during initialization.
- _setup: function (options) {
- _.bindAll(this);
- options.el ? this.el = options.el : this._ensureElement();
- // If a `name` is not provided, create one. The `name` is used
- // primarily for setting up the routes.
- this.name = options.name || _.uniqueId('panel-');
- $(this.el).addClass('panel').html(this.skeleton());
- if (options.stage) {
- this.stage = options.stage;
- this.stage.add(this);
- }
- }
- });
- observer(Gbone.Panel.prototype);
- state(Gbone.Panel.prototype);
- cleanup(Gbone.Panel.prototype);
- transitions(Gbone.Panel.prototype);
-
- Gbone.Stage.extend = Gbone.Panel.extend = Backbone.View.extend;
- return Gbone;
- }));
- (function($){
- App = {
-
- Views: {},
- Routers: {},
- Models: {},
- Collections: {},
- Helpers: {
- Transitions: {}
- },
-
- init: function () {
- var $body = $('body'),
- currency = App.Models.Currency.extend(),
- converterData,
- currencies = new App.Collections.Currencies(),
- globalRouter = new App.Routers.GlobalRouter(),
- globalStage,
- currencyConverter,
- currencyPicker,
- currencyInfo;
-
- // Disable click events.
- $body.bind('click', function (event) {
- event.preventDefault();
- });
-
- $body.bind('orientationchange', function (event) {
- var orientation = 'portrait';
- if (Math.abs(window.orientation) === 90) orientation = 'landscape'
-
- $body.removeClass('portrait')
- .removeClass('landscape')
- .addClass(orientation)
- .trigger('turn', {orientation: orientation});
- });
-
- currencies.fetch({
- error: function () {
- throw new Error('Error loading currencies.');
- }
- });
-
- currencies.bind('reset', function () {
-
- // Model for the currency converter. This simply acts as a state machine.
- converterData = new App.Models.ConverterData(null, { currencies: currencies });
-
- // Create the global Stage.
- globalStage = new App.Views.GlobalStage({
- name: 'global-stage',
- router: globalRouter,
- el: 'body'
- });
-
- // Currency converter Panel.
- currencyConverter = new App.Views.CurrencyConverter({
- name: 'currency-converter',
- model: converterData,
- stage: globalStage
- });
-
- // Currency picker Panel.
- currencyPicker = new App.Views.CurrencyPicker({
- collection: currencies,
- converterData: converterData,
- name: 'currency-picker',
- title: 'Currencies',
- stage: globalStage
- });
-
- // Application info Panel.
- currencyInfo = new App.Views.CurrencyInfo({
- name: 'currency-info',
- stage: globalStage
- });
- // Setup additional transitions.
- currencyConverter.addTransition(App.Helpers.Transitions.upDown);
- currencyPicker.addTransition(App.Helpers.Transitions.upDown);
- currencyInfo.addTransition(App.Helpers.Transitions.upDown);
- // Setup the route for `currencyConverter`.
- currencyConverter.routePanel(function (trans) {
- // Hide the children initially to avoid flicker.
- $(currencyConverter.el).children().hide();
- currencyConverter.active({trans: trans || 'left'});
- });
-
- // Setup the route for `currencyPicker`.
- currencyPicker.routePanel(function (trans) {
- // Hide the children initially to avoid flicker.
- $(currencyPicker.el).children().hide();
- currencyPicker.active({trans: trans || 'right'});
- });
-
- // Setup the route for `currencyInfo`.
- currencyInfo.routePanel(function (trans) {
- currencyInfo.active({trans: trans});
- });
- Backbone.history.start();
- });
-
- }
- };
- }).call(this, this.Zepto);
- (function($){
- App.Models.ConverterData = Backbone.Model.extend({
-
- initialize: function(attributes, options) {
- this.currencies = options.currencies;
- this.setDefaultCurrencies();
- },
-
- defaults: {
- output: 0,
- input: 0
- },
-
- setDefaultCurrencies: function () {
- var dflt = this.currencies.find(function (currency) {
- return currency.get('code') === 'USD';
- }).toJSON();
-
- this.set({to: dflt, from: dflt});
- },
-
- format: function (num, addPoint) {
- num += '';
- num = num.replace(/\B(?=(?:\d{3})+(?!\d))/g, ",");
- return num + (addPoint ? '.' : '');
- },
-
- rate: function () {
- return this.get('from').rate * (1 / this.get('to').rate);
- },
-
- getOutput: function (input) {
- return input ? (input * this.rate()).toFixed(2) : 0;
- }
-
- });
- }).call(this, this.Zepto);
- App.Models.Currency = Backbone.Model.extend();
- App.Collections.Currencies = Backbone.Collection.extend({
- model: App.Models.Currency,
- url: 'currencies.json'
- });
- (function($){
- App.Routers.GlobalRouter = Backbone.Router.extend({
- routes: {
- '': 'index'
- },
-
- index: function () {
- // Start with the currency converter Panel.
- this.navigate('global-stage/currency-converter', true);
- }
- });
- }).call(this, this.Zepto);
- (function($){
- App.Views.Currency = Backbone.View.extend({
-
- className: 'item',
-
- events: {
- 'tap': 'click'
- },
-
- initialize: function (options) {
- _.bindAll(this);
- this.render();
- },
- render: function () {
- $(this.el).html(JST.currency(this.model.toJSON()));
- return this;
- },
-
- click: function () {
- var currency = {},
- converterData = this.parent.converterData;
-
- if (this.parent.currencyChange) {
- currency[this.parent.currencyChange] = this.model.toJSON();
- converterData.set(currency, {silent: true});
- converterData.set({
- output: converterData.getOutput(converterData.get('input'))
- });
- }
-
- this.parent.stage.router.navigate('global-stage/currency-converter', true);
- },
- });
- }).call(this, this.Zepto);
- (function($){
- App.Views.CurrencyConverter = Gbone.Panel.extend({
-
- skeleton: _.template('<header><button class="info">info</button></header><article></article>'),
-
- transitionBindings: ['header', 'article'],
-
- events: {
- 'touchstart .pad div': 'enter',
- 'touchstart .pad .clear': 'clear',
- 'touchstart .pad .point': 'point',
- 'tap .input': 'changeFrom',
- 'tap .output': 'changeTo',
- 'tap .flip': 'flip',
- 'tap .info': 'info'
- },
-
- initialize: function (options) {
- _.bindAll(this);
-
- $(this.el).addClass('currencies');
- this.bindEvents();
- this.model.addPoint = false;
- this.render();
- },
-
- render: function () {
- this.$('article').html(JST.index({model: this.model}));
- return this;
- },
-
- bindEvents: function () {
- var $el = $(this.el);
-
- this.bindTo(this.model, 'change', this.render);
-
- $el.bind('touchmove', function (e) {
- e.preventDefault();
- });
- },
-
- clear: function () {
- this.model.addPoint = false;
- this.model.set({
- input: 0,
- output: this.model.getOutput()
- });
- },
-
- enter: function (event) {
- var num = $(event.currentTarget).data('num'),
- input;
-
- if (!num) return;
-
- // Stop overflows
- if ((this.model.get('input') + '').length > 8)
- return;
- if ((this.model.get('output') + '').length > 8)
- return;
-
- num += '';
-
- // Prefix with decimal
- if (this.model.addPoint) {
- this.model.addPoint = false;
- num = '.' + num;
- }
-
- input = parseFloat(this.model.get('input') + num);
- this.model.set({
- input: input,
- output: this.model.getOutput(input)
- });
- },
-
- changeFrom: function () {
- this.stage.trigger('currencyChange', 'from');
- this.stage.router.navigate('global-stage/currency-picker', true);
- },
-
- changeTo: function () {
- this.stage.trigger('currencyChange', 'to');
- this.stage.router.navigate('global-stage/currency-picker', true);
- },
-
- flip: function () {
- this.model.set({
- to: this.model.get('from'),
- from: this.model.get('to')
- }, {silent: true});
-
- this.model.set({
- output: this.model.getOutput(this.model.get('input'))
- });
- },
-
- info: function () {
- this.stage.router.navigate('global-stage/currency-info/trans-up', true);
- },
-
- point: function () {
- var input = this.model.get('input');
- // Return if already has point.
- if ((input % 1) !== 0) return;
- this.model.addPoint = true;
- this.model.set({
- output: this.model.getOutput(input)
- });
- this.model.trigger('change');
- }
-
- });
- }).call(this, this.Zepto);
- (function($){
- App.Views.CurrencyInfo = Gbone.Panel.extend({
-
- skeleton: function () { return JST.info; },
-
- transitionBindings: ['article'],
-
- className: 'info',
-
- events: {
- 'tap header .back': 'back'
- },
-
- initialize: function (options) {
- _.bindAll(this);
- this.render();
- },
-
- render: function () {
- return this;
- },
-
- back: function () {
- this.stage.router.navigate('global-stage/currency-converter/trans-down', true);
- }
- });
- }).call(this, this.Zepto);
- (function($){
- App.Views.CurrencyPicker = Gbone.Panel.extend({
-
- skeleton: function () { return JST.currencies; },
-
- transitionBindings: ['header', 'article'],
-
- className: 'currenciesPicker list',
-
- events: {
- 'tap header .back': 'back'
- },
-
- initialize: function (options) {
- _.bindAll(this);
- this.converterData = options.converterData;
- this.title = options.title;
- this.bindEvents();
- this.render();
- },
-
- render: function () {
- this.$('header h2').html(this.title);
- this.addAll();
- return this;
- },
-
- bindEvents: function () {
- var that = this;
- this.bindTo(this.stage, 'currencyChange', function (currencyChange) {
- that.currencyChange = currencyChange;
- });
- },
-
- addAll: function () {
- this.collection.each(this.addOne);
- },
-
- addOne: function (model) {
- var view = new App.Views.Currency({model: model});
- view.render();
- this.appendChildInto(view, 'article');
- },
-
- back: function () {
- this.stage.router.navigate('global-stage/currency-converter', true);
- }
- });
- }).call(this, this.Zepto);
- (function($){
- App.Views.GlobalStage = Gbone.Stage.extend({
- skeleton: _.template('<article class="viewport"></article>')
- });
- }).call(this, this.Zepto);
- // `up` and `down` Panel transitions.
- //
- (function($){
-
- var EASING = 'cubic-bezier(.25, .1, .25, 1)',
- DURATION = 500;
-
- App.Helpers.Transitions.upDown = {
-
- effects: {
-
- up: function (callback) {
- var that = this,
- $el = $(that.el),
- prevZ = $el.css('z-index'),
- l = that.transitionBindings.length,
- animate = function (container, index) {
- container.transform({
- translate3d: '0,' + document.height + 'px,0'
- }).show().animate({
- translate3d: '0,0,0'
- }, DURATION, EASING, function () {
- // Only call the calback function when all the animations are done.
- if (index === l-1) {
- $el.css('z-index', prevZ);
- callback();
- }
- });
- };
-
- _.each(that.transitionBindings, function(elm, index) {
- var container = that.$(elm);
- if (container.length === 0)
- throw new Error('The container element to animate is not \
- availabe in this view.');
-
- $el.css('z-index', 100);
- animate(container, index);
- });
- },
-
- down: function (callback) {
- var that = this;
-
- _.each(that.transitionBindings, function(elm) {
- var container = that.$(elm);
- if (container.length === 0)
- throw new Error('The container element to animate is not \
- availabe in this view.');
-
- container.show();
- });
- }
- },
-
- reverseEffects: {
-
- up: function (callback) {
- var that = this;
-
- _.each(that.transitionBindings, function(elm) {
- var container = that.$(elm);
- if (container.length === 0)
- throw new Error('The container element to animate is not \
- availabe in this view.');
-
- setTimeout(function () {
- container.hide();
- callback();
- }, DURATION);
- });
- },
-
- down: function (callback) {
- var that = this,
- $el = $(that.el),
- prevZ = $el.css('z-index'),
- l = that.transitionBindings.length,
- animate = function (container, index) {
- container.show().animate({
- translate3d: '0,' + document.height + 'px,0'
- }, DURATION, EASING, function () {
- container.hide().transform({translate3d: '0,0,0'});
- // Only call the calback function when all the animations are done.
- if (index === l-1) {
- $el.css('z-index', prevZ);
- callback();
- }
- });
- };
-
- _.each(that.transitionBindings, function(elm, index) {
- var container = that.$(elm);
- if (container.length === 0)
- throw new Error('The container element to animate is not \
- availabe in this view.');
-
- $el.css('z-index', 100);
- animate(container, index);
- });
- }
-
- }
- };
- }).call(this, this.Zepto);(function(){
- window.JST = window.JST || {};
- window.JST['currencies'] = _.template('<header>\n <h2></h2>\n <button class="back">Back</button>\n</header>\n<article></article>');
- window.JST['currency'] = _.template('<span><%= name %></span>\n<em><%= symbol %></em>\n<em>(<%= code %>)</em>');
- 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>');
- 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>');
- })();