diff --git a/demos/backbone/backbone-localstorage.js b/demos/backbone/backbone-localstorage.js
new file mode 100644
index 00000000..091d7f36
--- /dev/null
+++ b/demos/backbone/backbone-localstorage.js
@@ -0,0 +1,84 @@
+// A simple module to replace `Backbone.sync` with *localStorage*-based
+// persistence. Models are given GUIDS, and saved into a JSON object. Simple
+// as that.
+
+// Generate four random hex digits.
+function S4() {
+ return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
+};
+
+// Generate a pseudo-GUID by concatenating random hexadecimal.
+function guid() {
+ return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
+};
+
+// Our Store is represented by a single JS object in *localStorage*. Create it
+// with a meaningful name, like the name you'd give a table.
+var Store = function(name) {
+ this.name = name;
+ var store = localStorage.getItem(this.name);
+ this.data = (store && JSON.parse(store)) || {};
+};
+
+_.extend(Store.prototype, {
+
+ // Save the current state of the **Store** to *localStorage*.
+ save: function() {
+ localStorage.setItem(this.name, JSON.stringify(this.data));
+ },
+
+ // Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
+ // have an id of it's own.
+ create: function(model) {
+ if (!model.id) model.id = model.attributes.id = guid();
+ this.data[model.id] = model;
+ this.save();
+ return model;
+ },
+
+ // Update a model by replacing its copy in `this.data`.
+ update: function(model) {
+ this.data[model.id] = model;
+ this.save();
+ return model;
+ },
+
+ // Retrieve a model from `this.data` by id.
+ find: function(model) {
+ return this.data[model.id];
+ },
+
+ // Return the array of all models currently in storage.
+ findAll: function() {
+ return _.values(this.data);
+ },
+
+ // Delete a model from `this.data`, returning it.
+ destroy: function(model) {
+ delete this.data[model.id];
+ this.save();
+ return model;
+ }
+
+});
+
+// Override `Backbone.sync` to use delegate to the model or collection's
+// *localStorage* property, which should be an instance of `Store`.
+Backbone.sync = function(method, model, options) {
+
+ var resp;
+ var store = model.localStorage || model.collection.localStorage;
+
+ switch (method) {
+ case "read": resp = model.id ? store.find(model) : store.findAll(); break;
+ case "create": resp = store.create(model); break;
+ case "update": resp = store.update(model); break;
+ case "delete": resp = store.destroy(model); break;
+ }
+
+ if (resp) {
+ options.success(resp);
+ } else {
+ options.error("Record not found");
+ }
+};
\ No newline at end of file
diff --git a/demos/backbone/backbone-min.js b/demos/backbone/backbone-min.js
new file mode 100644
index 00000000..3f0d495d
--- /dev/null
+++ b/demos/backbone/backbone-min.js
@@ -0,0 +1,33 @@
+// Backbone.js 0.5.3
+// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
+// Backbone may be freely distributed under the MIT license.
+// For all details and documentation:
+// http://documentcloud.github.com/backbone
+(function(){var h=this,p=h.Backbone,e;e=typeof exports!=="undefined"?exports:h.Backbone={};e.VERSION="0.5.3";var f=h._;if(!f&&typeof require!=="undefined")f=require("underscore")._;var g=h.jQuery||h.Zepto;e.noConflict=function(){h.Backbone=p;return this};e.emulateHTTP=!1;e.emulateJSON=!1;e.Events={bind:function(a,b,c){var d=this._callbacks||(this._callbacks={});(d[a]||(d[a]=[])).push([b,c]);return this},unbind:function(a,b){var c;if(a){if(c=this._callbacks)if(b){c=c[a];if(!c)return this;for(var d=
+0,e=c.length;d/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")},has:function(a){return this.attributes[a]!=null},set:function(a,b){b||(b={});if(!a)return this;if(a.attributes)a=a.attributes;var c=this.attributes,d=this._escapedAttributes;if(!b.silent&&this.validate&&!this._performValidation(a,b))return!1;if(this.idAttribute in a)this.id=a[this.idAttribute];
+var e=this._changing;this._changing=!0;for(var g in a){var h=a[g];if(!f.isEqual(c[g],h))c[g]=h,delete d[g],this._changed=!0,b.silent||this.trigger("change:"+g,this,h,b)}!e&&!b.silent&&this._changed&&this.change(b);this._changing=!1;return this},unset:function(a,b){if(!(a in this.attributes))return this;b||(b={});var c={};c[a]=void 0;if(!b.silent&&this.validate&&!this._performValidation(c,b))return!1;delete this.attributes[a];delete this._escapedAttributes[a];a==this.idAttribute&&delete this.id;this._changed=
+!0;b.silent||(this.trigger("change:"+a,this,void 0,b),this.change(b));return this},clear:function(a){a||(a={});var b,c=this.attributes,d={};for(b in c)d[b]=void 0;if(!a.silent&&this.validate&&!this._performValidation(d,a))return!1;this.attributes={};this._escapedAttributes={};this._changed=!0;if(!a.silent){for(b in c)this.trigger("change:"+b,this,void 0,a);this.change(a)}return this},fetch:function(a){a||(a={});var b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&
+c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"read",this,a)},save:function(a,b){b||(b={});if(a&&!this.set(a,b))return!1;var c=this,d=b.success;b.success=function(a,e,f){if(!c.set(c.parse(a,f),b))return!1;d&&d(c,a,f)};b.error=i(b.error,c,b);var f=this.isNew()?"create":"update";return(this.sync||e.sync).call(this,f,this,b)},destroy:function(a){a||(a={});if(this.isNew())return this.trigger("destroy",this,this.collection,a);var b=this,c=a.success;a.success=function(d){b.trigger("destroy",
+b,b.collection,a);c&&c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"delete",this,a)},url:function(){var a=k(this.collection)||this.urlRoot||l();if(this.isNew())return a;return a+(a.charAt(a.length-1)=="/"?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this)},isNew:function(){return this.id==null},change:function(a){this.trigger("change",this,a);this._previousAttributes=f.clone(this.attributes);this._changed=!1},hasChanged:function(a){if(a)return this._previousAttributes[a]!=
+this.attributes[a];return this._changed},changedAttributes:function(a){a||(a=this.attributes);var b=this._previousAttributes,c=!1,d;for(d in a)f.isEqual(b[d],a[d])||(c=c||{},c[d]=a[d]);return c},previous:function(a){if(!a||!this._previousAttributes)return null;return this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},_performValidation:function(a,b){var c=this.validate(a);if(c)return b.error?b.error(this,c,b):this.trigger("error",this,c,b),!1;return!0}});
+e.Collection=function(a,b){b||(b={});if(b.comparator)this.comparator=b.comparator;f.bindAll(this,"_onModelEvent","_removeReference");this._reset();a&&this.reset(a,{silent:!0});this.initialize.apply(this,arguments)};f.extend(e.Collection.prototype,e.Events,{model:e.Model,initialize:function(){},toJSON:function(){return this.map(function(a){return a.toJSON()})},add:function(a,b){if(f.isArray(a))for(var c=0,d=a.length;c').hide().appendTo("body")[0].contentWindow,this.navigate(a);
+this._hasPushState?g(window).bind("popstate",this.checkUrl):"onhashchange"in window&&!b?g(window).bind("hashchange",this.checkUrl):setInterval(this.checkUrl,this.interval);this.fragment=a;m=!0;a=window.location;b=a.pathname==this.options.root;if(this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;else if(this._wantsPushState&&this._hasPushState&&b&&a.hash)this.fragment=a.hash.replace(j,""),window.history.replaceState({},
+document.title,a.protocol+"//"+a.host+this.options.root+this.fragment);if(!this.options.silent)return this.loadUrl()},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.iframe.location.hash));if(a==this.fragment||a==decodeURIComponent(this.fragment))return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(window.location.hash)},loadUrl:function(a){var b=this.fragment=this.getFragment(a);
+return f.any(this.handlers,function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){var c=(a||"").replace(j,"");if(!(this.fragment==c||this.fragment==decodeURIComponent(c))){if(this._hasPushState){var d=window.location;c.indexOf(this.options.root)!=0&&(c=this.options.root+c);this.fragment=c;window.history.pushState({},document.title,d.protocol+"//"+d.host+c)}else if(window.location.hash=this.fragment=c,this.iframe&&c!=this.getFragment(this.iframe.location.hash))this.iframe.document.open().close(),
+this.iframe.location.hash=c;b&&this.loadUrl(a)}}});e.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.delegateEvents();this.initialize.apply(this,arguments)};var u=/^(\S+)\s*(.*)$/,n=["model","collection","el","id","attributes","className","tagName"];f.extend(e.View.prototype,e.Events,{tagName:"div",$:function(a){return g(a,this.el)},initialize:function(){},render:function(){return this},remove:function(){g(this.el).remove();return this},make:function(a,
+b,c){a=document.createElement(a);b&&g(a).attr(b);c&&g(a).html(c);return a},delegateEvents:function(a){if(a||(a=this.events))for(var b in f.isFunction(a)&&(a=a.call(this)),g(this.el).unbind(".delegateEvents"+this.cid),a){var c=this[a[b]];if(!c)throw Error('Event "'+a[b]+'" does not exist');var d=b.match(u),e=d[1];d=d[2];c=f.bind(c,this);e+=".delegateEvents"+this.cid;d===""?g(this.el).bind(e,c):g(this.el).delegate(d,e,c)}},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=
+0,c=n.length;b
+
+
+
+ jQTouch and Backbone β
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demos/backbone/json2.js b/demos/backbone/json2.js
new file mode 100644
index 00000000..23602599
--- /dev/null
+++ b/demos/backbone/json2.js
@@ -0,0 +1,481 @@
+/*
+ http://www.JSON.org/json2.js
+ 2009-09-29
+
+ Public Domain.
+
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+ See http://www.JSON.org/js.html
+
+
+ This code should be minified before deployment.
+ See http://javascript.crockford.com/jsmin.html
+
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+ NOT CONTROL.
+
+
+ This file creates a global JSON object containing two methods: stringify
+ and parse.
+
+ JSON.stringify(value, replacer, space)
+ value any JavaScript value, usually an object or array.
+
+ replacer an optional parameter that determines how object
+ values are stringified for objects. It can be a
+ function or an array of strings.
+
+ space an optional parameter that specifies the indentation
+ of nested structures. If it is omitted, the text will
+ be packed without extra whitespace. If it is a number,
+ it will specify the number of spaces to indent at each
+ level. If it is a string (such as '\t' or ' '),
+ it contains the characters used to indent at each level.
+
+ This method produces a JSON text from a JavaScript value.
+
+ When an object value is found, if the object contains a toJSON
+ method, its toJSON method will be called and the result will be
+ stringified. A toJSON method does not serialize: it returns the
+ value represented by the name/value pair that should be serialized,
+ or undefined if nothing should be serialized. The toJSON method
+ will be passed the key associated with the value, and this will be
+ bound to the value
+
+ For example, this would serialize Dates as ISO strings.
+
+ Date.prototype.toJSON = function (key) {
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ return this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z';
+ };
+
+ You can provide an optional replacer method. It will be passed the
+ key and value of each member, with this bound to the containing
+ object. The value that is returned from your method will be
+ serialized. If your method returns undefined, then the member will
+ be excluded from the serialization.
+
+ If the replacer parameter is an array of strings, then it will be
+ used to select the members to be serialized. It filters the results
+ such that only members with keys listed in the replacer array are
+ stringified.
+
+ Values that do not have JSON representations, such as undefined or
+ functions, will not be serialized. Such values in objects will be
+ dropped; in arrays they will be replaced with null. You can use
+ a replacer function to replace those with JSON values.
+ JSON.stringify(undefined) returns undefined.
+
+ The optional space parameter produces a stringification of the
+ value that is filled with line breaks and indentation to make it
+ easier to read.
+
+ If the space parameter is a non-empty string, then that string will
+ be used for indentation. If the space parameter is a number, then
+ the indentation will be that many spaces.
+
+ Example:
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
+ // text is '["e",{"pluribus":"unum"}]'
+
+
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+ text = JSON.stringify([new Date()], function (key, value) {
+ return this[key] instanceof Date ?
+ 'Date(' + this[key] + ')' : value;
+ });
+ // text is '["Date(---current time---)"]'
+
+
+ JSON.parse(text, reviver)
+ This method parses a JSON text to produce an object or array.
+ It can throw a SyntaxError exception.
+
+ The optional reviver parameter is a function that can filter and
+ transform the results. It receives each of the keys and values,
+ and its return value is used instead of the original value.
+ If it returns what it received, then the structure is not modified.
+ If it returns undefined then the member is deleted.
+
+ Example:
+
+ // Parse the text. Values that look like ISO date strings will
+ // be converted to Date objects.
+
+ myData = JSON.parse(text, function (key, value) {
+ var a;
+ if (typeof value === 'string') {
+ a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+ if (a) {
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+ +a[5], +a[6]));
+ }
+ }
+ return value;
+ });
+
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+ var d;
+ if (typeof value === 'string' &&
+ value.slice(0, 5) === 'Date(' &&
+ value.slice(-1) === ')') {
+ d = new Date(value.slice(5, -1));
+ if (d) {
+ return d;
+ }
+ }
+ return value;
+ });
+
+
+ This is a reference implementation. You are free to copy, modify, or
+ redistribute.
+*/
+
+/*jslint evil: true, strict: false */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
+ test, toJSON, toString, valueOf
+*/
+
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (!this.JSON) {
+ this.JSON = {};
+}
+
+(function () {
+
+ function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+ }
+
+ if (typeof Date.prototype.toJSON !== 'function') {
+
+ Date.prototype.toJSON = function (key) {
+
+ return isFinite(this.valueOf()) ?
+ this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z' : null;
+ };
+
+ String.prototype.toJSON =
+ Number.prototype.toJSON =
+ Boolean.prototype.toJSON = function (key) {
+ return this.valueOf();
+ };
+ }
+
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ gap,
+ indent,
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ },
+ rep;
+
+
+ function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+ escapable.lastIndex = 0;
+ return escapable.test(string) ?
+ '"' + string.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === 'string' ? c :
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' :
+ '"' + string + '"';
+ }
+
+
+ function str(key, holder) {
+
+// Produce a string from holder[key].
+
+ var i, // The loop counter.
+ k, // The member key.
+ v, // The member value.
+ length,
+ mind = gap,
+ partial,
+ value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+ if (value && typeof value === 'object' &&
+ typeof value.toJSON === 'function') {
+ value = value.toJSON(key);
+ }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+ if (typeof rep === 'function') {
+ value = rep.call(holder, key, value);
+ }
+
+// What happens next depends on the value's type.
+
+ switch (typeof value) {
+ case 'string':
+ return quote(value);
+
+ case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+ return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+ case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+ if (!value) {
+ return 'null';
+ }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+ gap += indent;
+ partial = [];
+
+// Is the value an array?
+
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || 'null';
+ }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+ v = partial.length === 0 ? '[]' :
+ gap ? '[\n' + gap +
+ partial.join(',\n' + gap) + '\n' +
+ mind + ']' :
+ '[' + partial.join(',') + ']';
+ gap = mind;
+ return v;
+ }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+ if (rep && typeof rep === 'object') {
+ length = rep.length;
+ for (i = 0; i < length; i += 1) {
+ k = rep[i];
+ if (typeof k === 'string') {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+ for (k in value) {
+ if (Object.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+ v = partial.length === 0 ? '{}' :
+ gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+ mind + '}' : '{' + partial.join(',') + '}';
+ gap = mind;
+ return v;
+ }
+ }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+ if (typeof JSON.stringify !== 'function') {
+ JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+ var i;
+ gap = '';
+ indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+ if (typeof space === 'number') {
+ for (i = 0; i < space; i += 1) {
+ indent += ' ';
+ }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+ } else if (typeof space === 'string') {
+ indent = space;
+ }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+ rep = replacer;
+ if (replacer && typeof replacer !== 'function' &&
+ (typeof replacer !== 'object' ||
+ typeof replacer.length !== 'number')) {
+ throw new Error('JSON.stringify');
+ }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+ return str('', {'': value});
+ };
+ }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+ if (typeof JSON.parse !== 'function') {
+ JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+ var j;
+
+ function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+ var k, v, value = holder[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (Object.hasOwnProperty.call(value, k)) {
+ v = walk(value, k);
+ if (v !== undefined) {
+ value[k] = v;
+ } else {
+ delete value[k];
+ }
+ }
+ }
+ }
+ return reviver.call(holder, key, value);
+ }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+ cx.lastIndex = 0;
+ if (cx.test(text)) {
+ text = text.replace(cx, function (a) {
+ return '\\u' +
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+ if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+ j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+ return typeof reviver === 'function' ?
+ walk({'': j}, '') : j;
+ }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+ throw new SyntaxError('JSON.parse');
+ };
+ }
+}());
\ No newline at end of file
diff --git a/demos/backbone/todos.css b/demos/backbone/todos.css
new file mode 100644
index 00000000..283c21e6
--- /dev/null
+++ b/demos/backbone/todos.css
@@ -0,0 +1,36 @@
+/*
+#main {
+ display: none !important;
+}
+*/
+
+#todo-list .destroy {
+ display: none !important;
+}
+
+/*
+'destroy' functionality was once hover-oriented, so it's broken, and I don't want to rebuild the app to make it work.
+#todo-list li:hover .destroy {
+ display: block !important;
+}
+*/
+
+#todo-list li.editing .edit {
+ display: block !important;
+}
+
+#todo-list li.editing .view {
+ display: none !important;
+}
+
+#todo-list li .edit {
+ display: none !important;
+}
+
+#todoapp footer {
+ display: none !important;
+}
+
+.done{
+ text-decoration: line-through;
+}
\ No newline at end of file
diff --git a/demos/backbone/todos.js b/demos/backbone/todos.js
new file mode 100644
index 00000000..bedc59b6
--- /dev/null
+++ b/demos/backbone/todos.js
@@ -0,0 +1,245 @@
+// An example Backbone application contributed by
+// [Jérôme Gravel-Niquet](http://jgn.me/). This demo uses a simple
+// [LocalStorage adapter](backbone-localstorage.js)
+// to persist Backbone models within your browser.
+
+// Load the application once the DOM is ready, using `jQuery.ready`:
+$(function(){
+
+ // Todo Model
+ // ----------
+
+ // Our basic **Todo** model has `title`, `order`, and `done` attributes.
+ var Todo = Backbone.Model.extend({
+
+ // Default attributes for the todo item.
+ defaults: function() {
+ return {
+ title: "empty todo...",
+ order: Todos.nextOrder(),
+ done: false
+ };
+ },
+
+ // Ensure that each todo created has `title`.
+ initialize: function() {
+ if (!this.get("title")) {
+ this.set({"title": this.defaults.title});
+ }
+ },
+
+ // Toggle the `done` state of this todo item.
+ toggle: function() {
+ this.save({done: !this.get("done")});
+ },
+
+ // Remove this Todo from *localStorage* and delete its view.
+ clear: function() {
+ this.destroy();
+ }
+
+ });
+
+ // Todo Collection
+ // ---------------
+
+ // The collection of todos is backed by *localStorage* instead of a remote
+ // server.
+ var TodoList = Backbone.Collection.extend({
+
+ // Reference to this collection's model.
+ model: Todo,
+
+ // Save all of the todo items under the `"todos"` namespace.
+ localStorage: new Store("todos-backbone"),
+
+ // Filter down the list of all todo items that are finished.
+ done: function() {
+ return this.filter(function(todo){ return todo.get('done'); });
+ },
+
+ // Filter down the list to only todo items that are still not finished.
+ remaining: function() {
+ return this.without.apply(this, this.done());
+ },
+
+ // We keep the Todos in sequential order, despite being saved by unordered
+ // GUID in the database. This generates the next order number for new items.
+ nextOrder: function() {
+ if (!this.length) return 1;
+ return this.last().get('order') + 1;
+ },
+
+ // Todos are sorted by their original insertion order.
+ comparator: function(todo) {
+ return todo.get('order');
+ }
+
+ });
+
+ // Create our global collection of **Todos**.
+ var Todos = new TodoList;
+
+ // Todo Item View
+ // --------------
+
+ // The DOM element for a todo item...
+ var TodoView = Backbone.View.extend({
+
+ //... is a list tag.
+ tagName: "li",
+
+ // Cache the template function for a single item.
+ template: _.template($('#item-template').html()),
+
+ // The DOM events specific to an item.
+ events: {
+ "click .toggle" : "toggleDone",
+ "dblclick .view" : "edit",
+ "click a.destroy" : "clear",
+ "keypress .edit" : "updateOnEnter",
+ "blur .edit" : "close"
+ },
+
+ // The TodoView listens for changes to its model, re-rendering. Since there's
+ // a one-to-one correspondence between a **Todo** and a **TodoView** in this
+ // app, we set a direct reference on the model for convenience.
+ initialize: function() {
+ this.model.bind('change', this.render, this);
+ this.model.bind('destroy', this.remove, this);
+ },
+
+ // Re-render the titles of the todo item.
+ render: function() {
+ $(this.el).html(this.template(this.model.toJSON()));
+ $(this.el).toggleClass('done', this.model.get('done'));
+ this.input = this.$('.edit');
+ return this;
+ },
+
+ // Toggle the `"done"` state of the model.
+ toggleDone: function() {
+ this.model.toggle();
+ },
+
+ // Switch this view into `"editing"` mode, displaying the input field.
+ edit: function() {
+ $(this.el).addClass("editing");
+ this.input.focus();
+ },
+
+ // Close the `"editing"` mode, saving changes to the todo.
+ close: function() {
+ var value = this.input.val();
+ if (!value) this.clear();
+ this.model.save({title: value});
+ $(this.el).removeClass("editing");
+ },
+
+ // If you hit `enter`, we're through editing the item.
+ updateOnEnter: function(e) {
+ if (e.keyCode == 13) this.close();
+ },
+
+ // Remove the item, destroy the model.
+ clear: function() {
+ this.model.clear();
+ }
+
+ });
+
+ // The Application
+ // ---------------
+
+ // Our overall **AppView** is the top-level piece of UI.
+ var AppView = Backbone.View.extend({
+
+ // Instead of generating a new element, bind to the existing skeleton of
+ // the App already present in the HTML.
+ el: $("#todoapp"),
+
+ // Our template for the line of statistics at the bottom of the app.
+ statsTemplate: _.template($('#stats-template').html()),
+
+ // Delegated events for creating new items, and clearing completed ones.
+ events: {
+ "keypress #new-todo": "createOnEnter",
+ "click #clear-completed": "clearCompleted",
+ "click #toggle-all": "toggleAllComplete"
+ },
+
+ // At initialization we bind to the relevant events on the `Todos`
+ // collection, when items are added or changed. Kick things off by
+ // loading any preexisting todos that might be saved in *localStorage*.
+ initialize: function() {
+
+ this.input = this.$("#new-todo");
+ this.allCheckbox = this.$("#toggle-all")[0];
+
+ Todos.bind('add', this.addOne, this);
+ Todos.bind('reset', this.addAll, this);
+ Todos.bind('all', this.render, this);
+
+ this.footer = this.$('footer');
+ this.main = $('#main');
+
+ Todos.fetch();
+ },
+
+ // Re-rendering the App just means refreshing the statistics -- the rest
+ // of the app doesn't change.
+ render: function() {
+ var done = Todos.done().length;
+ var remaining = Todos.remaining().length;
+
+ if (Todos.length) {
+ this.main.show();
+ this.footer.show();
+ this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
+ } else {
+ this.main.hide();
+ this.footer.hide();
+ }
+
+ this.allCheckbox.checked = !remaining;
+ },
+
+ // Add a single todo item to the list by creating a view for it, and
+ // appending its element to the `
`.
+ addOne: function(todo) {
+ var view = new TodoView({model: todo});
+ this.$("#todo-list").append(view.render().el);
+ },
+
+ // Add all items in the **Todos** collection at once.
+ addAll: function() {
+ Todos.each(this.addOne);
+ },
+
+ // If you hit return in the main input field, create new **Todo** model,
+ // persisting it to *localStorage*.
+ createOnEnter: function(e) {
+ if (e.keyCode != 13) return;
+ if (!this.input.val()) return;
+
+ Todos.create({title: this.input.val()});
+ this.input.val('');
+ },
+
+ // Clear all done todo items, destroying their models.
+ clearCompleted: function() {
+ _.each(Todos.done(), function(todo){ todo.clear(); });
+ return false;
+ },
+
+ toggleAllComplete: function () {
+ var done = this.allCheckbox.checked;
+ Todos.each(function (todo) { todo.save({'done': done}); });
+ }
+
+ });
+
+ // Finally, we kick things off by creating the **App**.
+ var App = new AppView;
+
+});
diff --git a/demos/backbone/underscore-1.3.1.js b/demos/backbone/underscore-1.3.1.js
new file mode 100644
index 00000000..208d4cd8
--- /dev/null
+++ b/demos/backbone/underscore-1.3.1.js
@@ -0,0 +1,999 @@
+// Underscore.js 1.3.1
+// (c) 2009-2012 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 slice = ArrayProto.slice,
+ unshift = ArrayProto.unshift,
+ 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**, with
+ // backwards-compatibility for the old `require()` API. If we're in
+ // the browser, add `_` as a global object via a string identifier,
+ // for Closure Compiler "advanced" mode.
+ if (typeof exports !== 'undefined') {
+ if (typeof module !== 'undefined' && module.exports) {
+ exports = module.exports = _;
+ }
+ exports._ = _;
+ } else {
+ root['_'] = _;
+ }
+
+ // Current version.
+ _.VERSION = '1.3.1';
+
+ // 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 if (obj.length === +obj.length) {
+ for (var i = 0, l = obj.length; i < l; i++) {
+ if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
+ }
+ } else {
+ for (var key in obj) {
+ if (_.has(obj, key)) {
+ if (iterator.call(context, obj[key], key, obj) === breaker) return;
+ }
+ }
+ }
+ };
+
+ // Return the results of applying the iterator to each element.
+ // Delegates to **ECMAScript 5**'s native `map` if available.
+ _.map = _.collect = 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);
+ });
+ if (obj.length === +obj.length) results.length = obj.length;
+ 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 (_.isFunction(method) ? 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, 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, 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)) return slice.call(iterable);
+ if (_.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. Especcialy 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); });
+ };
+
+ // Zip together multiple lists into a single array -- elements that share
+ // an index go together.
+ _.zip = function() {
+ var args = slice.call(arguments);
+ var length = _.max(_.pluck(args, 'length'));
+ var results = new Array(length);
+ for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + 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, args.concat(slice.call(arguments)));
+ ctor.prototype = func.prototype;
+ var self = new ctor;
+ var result = func.apply(self, args.concat(slice.call(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 funcs = slice.call(arguments, 1);
+ if (funcs.length == 0) funcs = _.functions(obj);
+ each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
+ return obj;
+ };
+
+ // Memoize an expensive function by storing its results.
+ _.memoize = function(func, hasher) {
+ var memo = {};
+ hasher || (hasher = _.identity);
+ return function() {
+ var key = hasher.apply(this, arguments);
+ return _.has(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 = [func].concat(slice.call(arguments, 0));
+ 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 = [];
+ for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
+ 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 = [];
+ for (var key in obj) {
+ if (_.isFunction(obj[key])) names.push(key);
+ }
+ return names.sort();
+ };
+
+ // Extend a given object with all the properties in passed-in object(s).
+ _.extend = function(obj) {
+ each(slice.call(arguments, 1), function(source) {
+ for (var prop in source) {
+ obj[prop] = source[prop];
+ }
+ });
+ return obj;
+ };
+
+ // Fill in a given object with default properties.
+ _.defaults = function(obj) {
+ each(slice.call(arguments, 1), function(source) {
+ for (var prop in source) {
+ if (obj[prop] == null) obj[prop] = source[prop];
+ }
+ });
+ 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.
+ for (var key in a) {
+ if (_.has(a, key)) {
+ // Count the expected number of properties.
+ size++;
+ // Deep compare each member.
+ if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
+ }
+ }
+ // Ensure that both objects contain the same number of properties.
+ if (result) {
+ for (key in b) {
+ if (_.has(b, key) && !(size--)) break;
+ }
+ 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) {
+ if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
+ for (var key in obj) if (_.has(obj, key)) return false;
+ return true;
+ };
+
+ // 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 && _.has(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;
+ };
+
+ // Has own property?
+ _.has = function(obj, key) {
+ return hasOwnProperty.call(obj, key);
+ };
+
+ // 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,'/');
+ };
+
+ // Add your own custom functions to the Underscore object, ensuring that
+ // they're correctly added to the OOP wrapper as well.
+ _.mixin = function(obj) {
+ each(_.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
+ };
+
+ // When customizing `templateSettings`, if you don't want to define an
+ // interpolation, evaluation or escaping regex, we need one that is
+ // guaranteed not to match.
+ var noMatch = /.^/;
+
+ // Within an interpolation, evaluation, or escaping, remove HTML escaping
+ // that had been previously added.
+ var unescape = function(code) {
+ return code.replace(/\\\\/g, '\\').replace(/\\'/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 || noMatch, function(match, code) {
+ return "',_.escape(" + unescape(code) + "),'";
+ })
+ .replace(c.interpolate || noMatch, function(match, code) {
+ return "'," + unescape(code) + ",'";
+ })
+ .replace(c.evaluate || noMatch, function(match, code) {
+ return "');" + unescape(code).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, _);
+ };
+ };
+
+ // Add a "chain" function, which will delegate to the wrapper.
+ _.chain = function(obj) {
+ return _(obj).chain();
+ };
+
+ // 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 = slice.call(arguments);
+ unshift.call(args, this._wrapped);
+ 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.
+ each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+ var method = ArrayProto[name];
+ wrapper.prototype[name] = function() {
+ var wrapped = this._wrapped;
+ method.apply(wrapped, arguments);
+ var length = wrapped.length;
+ if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
+ return result(wrapped, this._chain);
+ };
+ });
+
+ // Add all accessor Array functions to the wrapper.
+ each(['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);
diff --git a/src/jqtouch.js b/src/jqtouch.js
index 926d68b9..f496d20a 100644
--- a/src/jqtouch.js
+++ b/src/jqtouch.js
@@ -58,6 +58,7 @@
useAnimations: true,
useFastTouch: true,
useTouchScroll: true,
+ handleRoutingAndHistory: true,
animations: [ // highest to lowest priority
{name:'cubeleft', selector:'.cubeleft, .cube', is3d: true},
{name:'cuberight', selector:'.cuberight', is3d: true},
@@ -126,7 +127,7 @@
// Prevent default if we found an internal link (relative or absolute)
if ($el && $el.prop('href') && !$el.isExternalLink()) {
warn('Need to prevent default click behavior');
- e.preventDefault();
+ // e.preventDefault();
} else {
warn('No need to prevent default click behavior');
}
@@ -138,7 +139,6 @@
warn('Converting click event to a tap event because touch handlers are not present or off');
$(e.target).trigger('tap', e);
}
-
}
function doNavigation(fromPage, toPage, animation, goingBack) {
@@ -171,14 +171,9 @@
animation.name = jQTSettings.defaultAnimation;
}
- // Reverse animation if need be
var finalAnimationName = animation.name,
is3d = animation.is3d ? 'animating3d' : '';
- if (goingBack) {
- finalAnimationName = finalAnimationName.replace(/left|right|up|down|in|out/, reverseAnimation );
- }
-
warn('finalAnimationName is ' + finalAnimationName);
// Bind internal "cleanup" callback
@@ -251,7 +246,9 @@
fromPage.unselect();
- setHash($currentPage.attr('id'));
+ if(jQTSettings.handleRoutingAndHistory){
+ setHash($currentPage.attr('id'));
+ }
// Trigger custom events
toPage.trigger('pageAnimationEnd', { direction:'in', animation: animation});
@@ -298,9 +295,15 @@
}
}
- function goTo(toPage, animation) {
+ function goTo(toPage, animation, fromPage) {
- var fromPage = history[0].page;
+ fromPage = fromPage || history[0].page;
+
+ var goback = false;
+ if (history[1] && toPage.attr("id") && toPage.attr("id") == history[1].page.attr("id")) {
+ animation = animation.replace(/left|right|up|down|in|out/, reverseAnimation );
+ goback = true;
+ }
if (typeof animation === 'string') {
for (var i=0, max=animations.length; i < max; i++) {
@@ -323,7 +326,7 @@
toPage = nextPage;
}
}
- if (doNavigation(fromPage, toPage, animation)) {
+ if (doNavigation(fromPage, toPage, animation, goback)) {
return publicObj;
} else {
warn('Could not animate pages.');
@@ -331,20 +334,22 @@
}
}
function hashChangeHandler(e) {
- if (location.hash === history[0].hash) {
- warn('We are on the right panel');
- return true;
- } else if (location.hash === '') {
- goBack();
- return true;
- } else {
- if( (history[1] && location.hash === history[1].hash) ) {
+ if (jQTSettings.handleRoutingAndHistory){
+ if (location.hash === history[0].hash) {
+ warn('We are on the right panel');
+ return true;
+ } else if (location.hash === '') {
goBack();
return true;
} else {
- // Lastly, just try going to the ID...
- warn('Could not find ID in history, just forwarding to DOM element.');
- goTo($(location.hash), jQTSettings.defaultAnimation);
+ if( (history[1] && location.hash === history[1].hash) ) {
+ goBack();
+ return true;
+ } else {
+ // Lastly, just try going to the ID...
+ warn('Could not find ID in history, just forwarding to DOM element.');
+ goTo($(location.hash), jQTSettings.defaultAnimation);
+ }
}
}
}
@@ -441,10 +446,10 @@
}
function orientationChangeHandler() {
- $body.css('minHeight', 1000);
+ $body.css('min-height', 1000);
scrollTo(0,0);
var bodyHeight = window.innerHeight;
- $body.css('minHeight', bodyHeight);
+ $body.css('min-height', bodyHeight - 44);
orientation = Math.abs(window.orientation) == 90 ? 'landscape' : 'portrait';
$body.removeClass('portrait landscape').addClass(orientation).trigger('turn', {orientation: orientation});
@@ -598,54 +603,60 @@
return false;
}
- // Init some vars
- var target = $el.attr('target'),
- hash = $el.prop('hash'),
- href = $el.prop('href'),
- animation = null;
-
+ // Unselect this element and return if this is an external link
if ($el.isExternalLink()) {
$el.unselect();
return true;
-
- } else if ($el.is(jQTSettings.backSelector)) {
- // User clicked or tapped a back button
- goBack(hash);
-
- } else if ($el.is(jQTSettings.submitSelector)) {
- // User clicked or tapped a submit element
- submitParentForm($el);
-
- } else if (target === '_webapp') {
- // User clicked or tapped an internal link, fullscreen mode
- window.location = href;
- return false;
-
- } else if (href === '#') {
- // Allow tap on item with no href
- $el.unselect();
- return true;
- } else {
- animation = getAnimation($el);
-
- if (hash && hash !== '#') {
- // Internal href
- $el.addClass('active');
- goTo($(hash).data('referrer', $el), animation, $el.hasClass('reverse'));
+ }
+
+ if (jQTSettings.handleRoutingAndHistory){
+ // Init some vars
+ var target = $el.attr('target'),
+ hash = $el.prop('hash'),
+ href = $el.attr('href'),
+ animation = null;
+
+ if ($el.is(jQTSettings.backSelector)) {
+ // User clicked or tapped a back button
+ goBack(hash);
+
+ } else if ($el.is(jQTSettings.submitSelector)) {
+ // User clicked or tapped a submit element
+ submitParentForm($el);
+
+ } else if (target === '_webapp') {
+ // User clicked or tapped an internal link, fullscreen mode
+ window.location = href;
return false;
+
+ } else if (href === '#') {
+ // Allow tap on item with no href
+ $el.unselect();
+ return true;
} else {
- // External href
- $el.addClass('loading active');
- showPageByHref($el.prop('href'), {
- animation: animation,
- callback: function() {
- $el.removeClass('loading');
- setTimeout($.fn.unselect, 250, $el);
- },
- $referrer: $el
- });
- return false;
+ animation = getAnimation($el);
+
+ if (hash && hash !== '#') {
+ // Internal href
+ $el.addClass('active');
+ goTo($(hash).data('referrer', $el), animation, $el.hasClass('reverse'));
+ return false;
+ } else {
+ // External href
+ $el.addClass('loading active');
+ showPageByHref($el.attr('href'), {
+ animation: animation,
+ callback: function() {
+ $el.removeClass('loading');
+ setTimeout($.fn.unselect, 250, $el);
+ },
+ $referrer: $el
+ });
+ return false;
+ }
}
+ } else {
+ return true; // someone else is handling routing and history; carry on as normal.
}
}
@@ -741,25 +752,27 @@
.bind( $.support.touch ? 'touchstart' : 'mousedown', touchStartHandler)
.trigger('orientationchange');
- $(window).bind('hashchange', hashChangeHandler);
-
- var startHash = location.hash;
-
- // Determine what the initial view should be
- if ($('#jqt > .current').length === 0) {
- $currentPage = $('#jqt > *:first-child').addClass('current');
- } else {
- $currentPage = $('#jqt > .current');
- }
-
- setHash($currentPage.attr('id'));
- addPageToHistory($currentPage);
-
- if ($(startHash).length === 1) {
- goTo(startHash);
+ if (jQTSettings.handleRoutingAndHistory){
+ $(window).bind('hashchange', hashChangeHandler);
+ var startHash = location.hash;
+ // Determine what the initial view should be
+ if ($('#jqt > .current').length === 0) {
+ $currentPage = $('#jqt > *:first-child').addClass('current');
+ } else {
+ $currentPage = $('#jqt > .current');
+ }
+
+ setHash($currentPage.attr('id'));
+ addPageToHistory($currentPage);
+
+ if ($(startHash).length === 1) {
+ goTo(startHash);
+ }
}
});
+
+
// Expose public methods and properties
publicObj = {
addAnimation: addAnimation,
@@ -770,7 +783,8 @@
goTo: goTo,
history: history,
settings: jQTSettings,
- submitForm: submitHandler
+ submitForm: submitHandler,
+ addPageToHistory: addPageToHistory
};
return publicObj;
};
diff --git a/themes/css/jqtouch.css b/themes/css/jqtouch.css
index 0c39a111..e1748218 100644
--- a/themes/css/jqtouch.css
+++ b/themes/css/jqtouch.css
@@ -1 +1,1354 @@
-/* normalize.css 2011-07-12T10:51 UTC · http://github.com/necolas/normalize.css */.webfont-icon-base{color:transparent;-webkit-background-clip:text;background-clip:text;position:absolute;top:0;left:0;text-indent:0;text-shadow:none;-webkit-user-select:none;user-select:none}*{margin:0;padding:0}body{-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-touch-callout:none}#jqt{-webkit-text-size-adjust:none;-webkit-user-select:none;user-select:none;font-family:"Helvetica Neue", Helvetica;position:absolute;right:0;top:0;left:0;bottom:0}#jqt a{-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-user-drag:none}#jqt .selectable,#jqt input,#jqt textarea{-webkit-user-select:auto}#jqt.notransform{-webkit-transform:none !important}#jqt>*{display:block;left:0;top:0;min-height:100%;width:100%;overflow-x:hidden;position:absolute;z-index:0;display:-webkit-box;display:box;-webkit-box-orient:vertical;box-orient:vertical;-webkit-box-flex:1;box-flex:1}#jqt>.current{z-index:10}#jqt>:not(.current){display:none}#jqt.touchscroll:not(.animating3d){overflow-y:auto;-webkit-overflow-scrolling:touch}#jqt.touchscroll:not(.animating3d)>*{height:100%}#jqt.touchscroll:not(.animating3d) .scroll{position:relative;-webkit-box-flex:1;box-flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch}#jqt .scroll{-webkit-margin-collapse:separate}#jqt .in,#jqt .out{-webkit-animation-duration:250ms;-webkit-animation-fill-mode:both;-webkit-animation-timing-function:ease-in-out}#jqt .in{z-index:10}#jqt .in:after{content:"";position:absolute;display:block;top:0;left:0;bottom:0;right:0}#jqt .out{z-index:0 !important}#jqt.supports3d{-webkit-perspective:1000}#jqt.supports3d>*{-webkit-transform:translate3d(0, 0, 0) rotate(0) scale(1)}#jqt .fade.in{-webkit-animation-name:fadeIn}#jqt .fade.out{z-index:10;-webkit-animation-name:fadeOut}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:1}}#jqt .dissolve.in{-webkit-animation-name:dissolveIn}#jqt .dissolve.out{-webkit-animation-name:dissolveOut}@-webkit-keyframes dissolveIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes dissolveOut{0%{opacity:1}100%{opacity:0}}#jqt .pop.in{-webkit-animation-name:popIn}#jqt .pop.out{-webkit-animation-name:popOut}@-webkit-keyframes popIn{0%{-webkit-transform:scale(0.2);opacity:0}100%{-webkit-transform:scale(1);opacity:1}}@-webkit-keyframes popOut{0%{-webkit-transform:scale(1);opacity:1}100%{-webkit-transform:scale(0.2);opacity:0}}#jqt .slideleft.in{-webkit-animation-name:slideLeftIn}#jqt .slideleft.out{-webkit-animation-name:slideLeftOut}@-webkit-keyframes slideLeftIn{0%{-webkit-transform:translateX(100%)}100%{-webkit-transform:translateX(0)}}@-webkit-keyframes slideLeftOut{0%{-webkit-transform:translateX(0px)}100%{-webkit-transform:translateX(-100%)}}#jqt .slideright.in{-webkit-animation-name:slideRightIn}#jqt .slideright.out{-webkit-animation-name:slideRightOut}@-webkit-keyframes slideRightIn{0%{-webkit-transform:translateX(-100%)}100%{-webkit-transform:translateX(0)}}@-webkit-keyframes slideRightOut{0%{-webkit-transform:translateX(0)}100%{-webkit-transform:translateX(100%)}}#jqt .slideup.in{z-index:10;-webkit-animation-name:slideUpIn}#jqt .slideup.out{z-index:0;-webkit-animation-name:slideUpOut}@-webkit-keyframes slideUpIn{0%{-webkit-transform:translateY(100%)}100%{-webkit-transform:translateY(0)}}@-webkit-keyframes slideUpOut{0%{-webkit-transform:translateY(0)}100%{-webkit-transform:translateY(0)}}#jqt .slidedown.in{z-index:0;-webkit-animation-name:slideDownIn}#jqt .slidedown.out{z-index:10;-webkit-animation-name:slideDownOut}@-webkit-keyframes slideDownIn{0%{-webkit-transform:translateY(0)}100%{-webkit-transform:translateY(0)}}@-webkit-keyframes slideDownOut{0%{-webkit-transform:translateY(0)}100%{-webkit-transform:translateY(100%)}}#jqt .flipleft{-webkit-backface-visibility:hidden}#jqt .flipleft.in{-webkit-animation-name:flipLeftIn}#jqt .flipleft.out{-webkit-animation-name:flipLeftOut}@-webkit-keyframes flipLeftIn{0%{-webkit-transform:rotateY(180deg) scale(0.8)}100%{-webkit-transform:rotateY(0deg) scale(1)}}@-webkit-keyframes flipLeftOut{0%{-webkit-transform:rotateY(0deg) scale(1)}100%{-webkit-transform:rotateY(-180deg) scale(0.8)}}#jqt .flipright{-webkit-backface-visibility:hidden}#jqt .flipright.in{-webkit-animation-name:flipRightIn}#jqt .flipright.out{-webkit-animation-name:flipRightOut}@-webkit-keyframes flipRightIn{0%{-webkit-transform:rotateY(-180deg) scale(0.8)}100%{-webkit-transform:rotateY(0deg) scale(1)}}@-webkit-keyframes flipRightOut{0%{-webkit-transform:rotateY(0deg) scale(1)}100%{-webkit-transform:rotateY(180deg) scale(0.8)}}#jqt .swapright{-webkit-animation-duration:.7s;-webkit-transform:perspective(800);-webkit-animation-timing-function:ease-out}#jqt .swapright.in{-webkit-animation-name:swapRightIn}#jqt .swapright.out{-webkit-animation-name:swapRightOut}@-webkit-keyframes swapRightIn{0%{-webkit-transform:translate3d(0px, 0px, -800px) rotateY(70deg);opacity:0}35%{-webkit-transform:translate3d(-180px, 0px, -400px) rotateY(20deg);opacity:1}100%{-webkit-transform:translate3d(0px, 0px, 0px) rotateY(0deg);opacity:1}}@-webkit-keyframes swapRightOut{0%{-webkit-transform:translate3d(0px, 0px, 0px) rotateY(0deg);opacity:1}35%{-webkit-transform:translate3d(180px, 0px, -400px) rotateY(-20deg);opacity:.5}100%{-webkit-transform:translate3d(0px, 0px, -800px) rotateY(-70deg);opacity:0}}#jqt .swapleft{-webkit-animation-duration:.7s;-webkit-transform:perspective(800);-webkit-animation-timing-function:ease-out}#jqt .swapleft.in{-webkit-animation-name:swapLeftIn}#jqt .swapleft.out{-webkit-animation-name:swapLeftOut}@-webkit-keyframes swapLeftIn{0%{-webkit-transform:translate3d(0px, 0px, -800px) rotateY(-70deg);opacity:0}35%{-webkit-transform:translate3d(180px, 0px, -400px) rotateY(-20deg);opacity:1}100%{opacity:1;-webkit-transform:translate3d(0px, 0px, 0px) rotateY(0deg)}}@-webkit-keyframes swapLeftOut{0%{-webkit-transform:translate3d(0px, 0px, 0px) rotateY(0deg);opacity:1}35%{-webkit-transform:translate3d(-180px, 0px, -400px) rotateY(20deg);opacity:.5}100%{-webkit-transform:translate3d(0px, 0px, -800px) rotateY(70deg);opacity:0}}#jqt .cubeleft.in,#jqt .cubeleft.out,#jqt .cuberight.in,#jqt .cuberight.out{-webkit-animation-duration:.6s;-webkit-transform:perspective(800)}#jqt .cubeleft.in{-webkit-transform-origin:0% 50%;-webkit-animation-name:cubeLeftIn}#jqt .cubeleft.out{-webkit-transform-origin:100% 50%;-webkit-animation-name:cubeLeftOut}@-webkit-keyframes cubeLeftIn{0%{-webkit-transform:rotateY(90deg) translateZ(320px);opacity:.5}100%{-webkit-transform:rotateY(0deg) translateZ(0) translateX(0);opacity:1}}@-webkit-keyframes cubeLeftOut{0%{-webkit-transform:rotateY(0deg) translateZ(0) translateX(0);opacity:1}100%{-webkit-transform:rotateY(-90deg) translateZ(320px);opacity:.5}}#jqt .cuberight.in{-webkit-transform-origin:100% 50%;-webkit-animation-name:cubeRightIn}#jqt .cuberight.out{-webkit-transform-origin:0% 50%;-webkit-animation-name:cubeRightOut}@-webkit-keyframes cubeRightIn{0%{-webkit-transform:rotateY(-90deg) translateZ(320px);opacity:.5}100%{-webkit-transform:rotateY(0deg) translateZ(0) translateX(0);opacity:1}}@-webkit-keyframes cubeRightOut{0%{-webkit-transform:rotateY(0deg) translateZ(0) translateX(0);opacity:1}100%{-webkit-transform:rotateY(90deg) translateZ(320px);opacity:.5}}body{background:#000}.base-chevron,#jqt ul li.arrow:after,#jqt ul li.forward:after{content:'›';width:22px;height:100%;vertical-align:middle;font-size:30px;line-height:38px;font-family:Futura, "Futura Condensed", Helvetica, Arial, sans-serif;font-weight:bold;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=60);opacity:0.6;position:absolute;right:0;top:0;pointer-events:none;z-index:10}.base-flatlists,#jqt ul.metal,#jqt ul.edgetoedge,#jqt ul.plastic{margin:0;padding:0;border-width:0 0 0 1px;-webkit-border-radius:0;border-radius:0}#jqt h1,#jqt h2{font:bold 18px "Helvetica Neue",Helvetica;margin:10px 20px 6px;color:#bbbcbe;text-shadow:#3c3d3e 0 -1px 0}#jqt .toolbar{-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:rgba(0,0,0,0.4) 0 1px 6px;box-shadow:rgba(0,0,0,0.4) 0 1px 6px;border-bottom:1px solid #000;z-index:10;position:relative;padding:10px;height:44px}#jqt .toolbar>h1{position:absolute;overflow:hidden;left:50%;bottom:9px;margin:1px 0 0 -75px;width:150px;font-size:20px;font-weight:bold;line-height:1.3em;text-align:center;text-overflow:ellipsis;white-space:nowrap;color:#fff;text-shadow:#161717 0 -1px 0}#jqt.black-translucent .toolbar{padding-top:30px;height:64px}#jqt.landscape .toolbar>h1{margin-left:-125px;width:250px}#jqt .button,#jqt .back,#jqt .cancel,#jqt .add{position:absolute;overflow:hidden;width:auto;height:30px;font-family:inherit;font-size:12px;font-weight:bold;line-height:30px;text-overflow:ellipsis;text-decoration:none;white-space:nowrap;background:none;bottom:6px;right:10px;margin:0;padding:0 10px;color:#e2e3e3;text-shadow:#000 0 -1px 0;-webkit-box-shadow:rgba(255,255,255,0.2) 0 1px 0,rgba(0,0,0,0.2) 0 1px 2px inset;box-shadow:rgba(255,255,255,0.2) 0 1px 0,rgba(0,0,0,0.2) 0 1px 2px inset;border:1px solid #000;-webkit-border-radius:5px;border-radius:5px;background-image:none;background-color:#0a0a0a;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #2f3031), color-stop(50%, #161717), color-stop(51%, #0a0a0a), color-stop(100%, #000000));background-image:-webkit-linear-gradient(top, #2f3031,#161717 50%,#0a0a0a 51%,#000000);background-image:linear-gradient(top, #2f3031,#161717 50%,#0a0a0a 51%,#000000)}#jqt .button.active,#jqt .back.active,#jqt .cancel.active,#jqt .add.active{border-color:#000;background-image:none;background-color:#000;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #262627), color-stop(50%, #0d0d0d), color-stop(51%, #000000), color-stop(100%, #000000));background-image:-webkit-linear-gradient(top, #262627,#0d0d0d 50%,#000000 51%,#000000);background-image:linear-gradient(top, #262627,#0d0d0d 50%,#000000 51%,#000000);color:#d8d9d9;text-shadow:#000 0 -1px 0}#jqt .back{max-width:60px;margin-left:15px;overflow:visible;padding-left:5px}#jqt .back:after,#jqt .back:before{content:'';position:absolute;width:20px;height:20px;top:1px;left:1px;-webkit-transform:rotate(45deg) translate3d(0.2px, 0, 0);transform:rotate(45deg) translate3d(0.2px, 0, 0);-webkit-transform-origin:0 0;transform-origin:0 0;background-image:none;background-color:#0a0a0a;background-image:-webkit-gradient(linear, 0% 0%, 100% 100%, color-stop(0%, #2f3031), color-stop(50%, #161717), color-stop(51%, #0a0a0a), color-stop(100%, #000000));background-image:-webkit-linear-gradient(top left, #2f3031,#161717 50%,#0a0a0a 51%,#000000);background-image:linear-gradient(top left, #2f3031,#161717 50%,#0a0a0a 51%,#000000);background-size:100% 98%;-webkit-border-radius:0 0 0 2px;border-radius:0 0 0 2px;-webkit-mask-image:-webkit-linear-gradient(45deg, #000, #000 15px, rgba(0,0,0,0) 15px);-webkit-mask-image:-webkit-gradient(linear, left bottom, right top, from(#000), color-stop(50%, #000), color-stop(50%, rgba(0,0,0,0)), to(rgba(0,0,0,0)));-webkit-mask-clip:border-box;-webkit-background-clip:content-box}#jqt .back:after{-webkit-box-shadow:rgba(0,0,0,0.2) 1px 0 0 inset,rgba(0,0,0,0.2) 0 -1px 0 inset;box-shadow:rgba(0,0,0,0.2) 1px 0 0 inset,rgba(0,0,0,0.2) 0 -1px 0 inset}#jqt .back:before{margin-left:-1px;background:#000 none}#jqt .back.active:after{background-image:none;background-color:#000;background-image:-webkit-gradient(linear, 0% 0%, 100% 100%, color-stop(0%, #262627), color-stop(50%, #0d0d0d), color-stop(51%, #000000), color-stop(100%, #000000));background-image:-webkit-linear-gradient(left top, #262627,#0d0d0d 50%,#000000 51%,#000000);background-image:linear-gradient(left top, #262627,#0d0d0d 50%,#000000 51%,#000000)}#jqt .back.active:before{background-color:#000}#jqt .blueButton{background-image:#2f7ce3,glossy;color:#fff;text-shadow:#1a63c5 0 -1px 0}#jqt .whiteButton,#jqt .grayButton,#jqt .redButton,#jqt .blueButton,#jqt .greenButton{display:block;font-size:20px;font-weight:bold;margin:10px 20px;padding:10px;text-align:center;text-decoration:inherit;-webkit-border-radius:8px;border-radius:8px;-webkit-box-shadow:rgba(0,0,0,0.4) 0 1px 3px,rgba(0,0,0,0.4) 0 0 0 5px,rgba(255,255,255,0.3) 0 1px 0 5px;box-shadow:rgba(0,0,0,0.4) 0 1px 3px,rgba(0,0,0,0.4) 0 0 0 5px,rgba(255,255,255,0.3) 0 1px 0 5px}#jqt .whiteButton.active,#jqt .whiteButton:active,#jqt .grayButton.active,#jqt .grayButton:active,#jqt .redButton.active,#jqt .redButton:active,#jqt .blueButton.active,#jqt .blueButton:active,#jqt .greenButton.active,#jqt .greenButton:active{background-image:none;background-color:#3c8101;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #5fcd01), color-stop(50%, #479b01), color-stop(51%, #3c8101), color-stop(100%, #306801));background-image:-webkit-linear-gradient(top, #5fcd01,#479b01 50%,#3c8101 51%,#306801);background-image:linear-gradient(top, #5fcd01,#479b01 50%,#3c8101 51%,#306801);color:#fff;text-shadow:#244f00 0 -1px 0}#jqt .whiteButton{background-image:none;background-color:#eee;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(50%, #fbfbfb), color-stop(51%, #eeeeee), color-stop(100%, #e1e1e1));background-image:-webkit-linear-gradient(top, #ffffff,#fbfbfb 50%,#eeeeee 51%,#e1e1e1);background-image:linear-gradient(top, #ffffff,#fbfbfb 50%,#eeeeee 51%,#e1e1e1);color:#151515;text-shadow:#fff 0 1px 0}#jqt .grayButton{background-image:none;background-color:#444;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #6a6a6a), color-stop(50%, #515151), color-stop(51%, #444444), color-stop(100%, #373737));background-image:-webkit-linear-gradient(top, #6a6a6a,#515151 50%,#444444 51%,#373737);background-image:linear-gradient(top, #6a6a6a,#515151 50%,#444444 51%,#373737);color:#fff;text-shadow:#2b2b2b 0 -1px 0}#jqt .redButton{background-image:none;background-color:#d83b38;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #e57a78), color-stop(50%, #dc504d), color-stop(51%, #d83b38), color-stop(100%, #ce2c28));background-image:-webkit-linear-gradient(top, #e57a78,#dc504d 50%,#d83b38 51%,#ce2c28);background-image:linear-gradient(top, #e57a78,#dc504d 50%,#d83b38 51%,#ce2c28);color:#fff;text-shadow:#b92724 0 -1px 0}#jqt .redButton.active,#jqt .redButton:active{background-image:none;background-color:#c12926;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #de5856), color-stop(50%, #d52e2b), color-stop(51%, #c12926), color-stop(100%, #ac2422));background-image:-webkit-linear-gradient(top, #de5856,#d52e2b 50%,#c12926 51%,#ac2422);background-image:linear-gradient(top, #de5856,#d52e2b 50%,#c12926 51%,#ac2422);color:#fff;text-shadow:#97201e 0 -1px 0}#jqt .greenButton{background-image:none;background-color:#53b401;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #76fe04), color-stop(50%, #5fcd01), color-stop(51%, #53b401), color-stop(100%, #479b01));background-image:-webkit-linear-gradient(top, #76fe04,#5fcd01 50%,#53b401 51%,#479b01);background-image:linear-gradient(top, #76fe04,#5fcd01 50%,#53b401 51%,#479b01);color:#fff;text-shadow:#3c8101 0 -1px 0}#jqt .leftButton,#jqt .cancel,#jqt .back{left:6px;right:auto}#jqt .add{font-size:24px;line-height:24px;font-weight:bold}#jqt ul{padding:0;margin:5px 10px 10px 10px;-webkit-margin-collapse:separate}#jqt ul,#jqt ul.individual li{background-color:#555658;color:#d5d6d7;text-shadow:#3c3d3e 0 -1px 0;border:1px solid #2f3031;font:bold 18px "Helvetica Neue",Helvetica}#jqt ul:first-child{margin-top:15px}#jqt ul li{border-top:1px solid #48494b;list-style-type:none;overflow:hidden;padding:10px;-webkit-transform:translate3d(0, 0, 0)}#jqt ul li a{text-decoration:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;padding:10px;margin:-10px;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-transform:translateZ(0);color:#fff;text-shadow:#3c3d3e 0 -1px 0}#jqt ul li a.active,#jqt ul li a.selected{background-image:none;background-color:#53b401;-webkit-box-shadow:#5cc801 0 1px 0px inset;box-shadow:#5cc801 0 1px 0px inset;color:#fff;text-shadow:#3c8101 0 -1px 0}#jqt ul li a.active small,#jqt ul li a.selected small{color:#a0fe50;text-shadow:#000 0 -1px 0}#jqt ul li a.active small.counter,#jqt ul li a.selected small.counter{background-color:#306801}#jqt ul li small{color:#53b401;font:16px "Helvetica Neue",Helvetica;text-align:right;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;width:23%;position:relative;z-index:20;float:right;line-height:16px;padding:2px 8px 4px 8px}#jqt ul li small.counter{background:#2f3031;color:#949698;text-shadow:#0a0a0a 0 -1px 0;z-index:10;font-size:16px;font-weight:bold;-webkit-border-radius:3px;border-radius:3px;display:block;width:auto}#jqt ul li ::-webkit-input-placeholder{color:#949698;text-shadow:#3c3d3e 0 -1px 0}#jqt ul li input[type="text"],#jqt ul li input[type="password"],#jqt ul li input[type="tel"],#jqt ul li input[type="number"],#jqt ul li input[type="search"],#jqt ul li input[type="email"],#jqt ul li input[type="url"],#jqt ul li textarea,#jqt ul li select{color:#fff;text-shadow:#3c3d3e 0 -1px 0;background:transparent url("data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==");border:0;font:normal 17px "Helvetica Neue",Helvetica;padding:0;display:inline-block;margin-left:0px;width:100%;-webkit-appearance:textarea}#jqt ul li textarea{height:120px;padding:0;text-indent:-2px}#jqt ul li input[type="checkbox"],#jqt ul li input[type="radio"]{margin:0;padding:10px}#jqt ul li input[type="checkbox"]:after,#jqt ul li input[type="radio"]:after{content:attr(title);position:absolute;display:block;width:0;left:21px;top:12px;font-family:"Helvetica Neue",Helvetica;font-size:17px;line-height:21px;width:246px;margin:0 0 0 17px;color:#fff;text-shadow:#3c3d3e 0 -1px 0}#jqt ul li input[type='submit']{-webkit-border-radius:4px;border-radius:4px;background:-webkit-gradient(linear, 0% 0%, 0% 100%, from(#eee), to(#9c9ea0));border:1px outset #aaa;display:block;font-size:inherit;font-weight:inherit;padding:10px}#jqt ul li.arrow small,#jqt ul li.forward small{margin-right:24px}#jqt ul li.forward:before{content:"";position:absolute;display:block;width:24px;height:24px;top:50%;right:6px;margin-top:-12px;width:24px;height:24px;-webkit-border-radius:12px;border-radius:12px;background-image:none;background-color:#53b401;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #76fe04), color-stop(50%, #5fcd01), color-stop(51%, #53b401), color-stop(100%, #479b01));background-image:-webkit-linear-gradient(top, #76fe04,#5fcd01 50%,#53b401 51%,#479b01);background-image:linear-gradient(top, #76fe04,#5fcd01 50%,#53b401 51%,#479b01);border:2px solid #fff;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.3);box-shadow:0 1px 2px rgba(0,0,0,0.3);-webkit-box-sizing:border-box;padding:0;z-index:10;line-height:0;pointer-events:none}#jqt ul li.forward:after{color:#fff;text-shadow:#3c8101 0 -1px 0;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);opacity:1;font-size:24px}#jqt ul.rounded,#jqt ul.individual li{-webkit-box-shadow:rgba(255,255,255,0.15) 0 1px 0;box-shadow:rgba(255,255,255,0.15) 0 1px 0;border:1px solid #2f3031}#jqt ul.rounded{-webkit-border-radius:8px;border-radius:8px}#jqt ul.rounded li:first-child,#jqt ul.rounded li:first-child a{border-top:0;-webkit-border-top-left-radius:8px;border-top-left-radius:8px;-webkit-border-top-right-radius:8px;border-top-right-radius:8px}#jqt ul.rounded li:last-child,#jqt ul.rounded li:last-child a{-webkit-border-bottom-left-radius:8px;border-bottom-left-radius:8px;-webkit-border-bottom-right-radius:8px;border-bottom-right-radius:8px}#jqt ul.form li{padding:7px 10px}#jqt ul.form li.error{border:2px solid red}#jqt ul.form li.error+#jqt ul.form li.error{border-top:0}#jqt ul.metal li{background-image:none;border-top:1px solid #fff;border-bottom:1px solid #666;font-size:26px}#jqt ul.metal li a{line-height:26px;margin:0;padding:13px 0}#jqt ul.metal li em{display:block;font-size:14px;font-style:normal;width:50%;line-height:14px}#jqt ul.metal li small{float:right;position:relative;margin-top:10px;font-weight:bold}#jqt ul.edgetoedge li{font-size:20px}#jqt ul.edgetoedge li:first-child{border-top:0}#jqt ul.edgetoedge li.sep{font-size:16px;padding:2px 10px}#jqt ul.edgetoedge li em{font-weight:normal;font-style:normal}#jqt ul.plastic{font-size:18px}#jqt ul.plastic li{border-width:1px 0;border-style:solid;background-image:none;background-color:#2a2b2c;border-top-color:#2f3031;border-bottom-color:#232324}#jqt ul.plastic li:nth-child(odd){background-image:none;background-color:#2f3031}#jqt ul.plastic li a.active.loading{background-image:url(img/loading.gif);background-position:95% center;background-repeat:no-repeat}#jqt ul.plastic li small{color:#949698;font-size:13px;font-weight:bold;text-transform:uppercase}#jqt ul.individual{border:0;background:none;clear:both;overflow:hidden}#jqt ul.individual li{font-size:14px;text-align:center;-webkit-border-radius:8px;border-radius:8px;-webkit-box-sizing:border-box;box-sizing:border-box;width:48%;float:left;display:block;padding:11px 10px 14px 10px}#jqt ul.individual li+li{float:right}#jqt ul.individual a{line-height:16px;margin:-11px -10px -14px -10px;padding:11px 10px 14px 10px;-webkit-border-radius:8px;border-radius:8px}#jqt .toggle{width:94px;position:relative;height:27px;display:block;overflow:hidden;float:right}#jqt .toggle input[type="checkbox"]{margin:0;-webkit-border-radius:5px;border-radius:5px;height:27px;overflow:hidden;width:149px;border:0;-webkit-transition:left 0.15s ease-in-out;transition:left 0.15s ease-in-out;position:absolute;top:0;left:-55px;-webkit-appearance:textarea;-webkit-tap-highlight-color:rgba(0,0,0,0)}#jqt .toggle input[type="checkbox"]:checked{left:0px}#jqt .info{font-size:12px;line-height:16px;text-align:center;color:#444;padding:15px;font-weight:bold}#jqt>*{background-image:url('data:image/png;base64,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'),-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #7b7c7f), color-stop(30%, #696a6d), color-stop(65%, #555658), color-stop(100%, #464748));background-image:url('data:image/png;base64,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'),-webkit-linear-gradient(top, #7b7c7f,#696a6d 30%,#555658 65%,#464748);background-image:url('data:image/png;base64,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'),linear-gradient(top, #7b7c7f,#696a6d 30%,#555658 65%,#464748)}#jqt .toolbar{background-image:-webkit-gradient(radial, 50% 50%, 0, 50% 50%, 12.5, color-stop(12.5%, #181819), color-stop(12.5%, rgba(11,11,12,0))),-webkit-gradient(radial, 50% 50%, 0, 50% 50%, 12.5, color-stop(12.5%, #181819), color-stop(12.5%, rgba(11,11,12,0))),-webkit-gradient(radial, 50% 50%, 0, 50% 50%, 12.5, color-stop(12.5%, rgba(255,255,255,0.1)), color-stop(12.5%, rgba(255,255,255,0))),-webkit-gradient(radial, 50% 50%, 0, 50% 50%, 12.5, color-stop(12.5%, rgba(255,255,255,0.1)), color-stop(12.5%, rgba(255,255,255,0)));background-image:-webkit-radial-gradient(#181819 12.5%,rgba(11,11,12,0) 12.5%),-webkit-radial-gradient(#181819 12.5%,rgba(11,11,12,0) 12.5%),-webkit-radial-gradient(rgba(255,255,255,0.1) 12.5%,rgba(255,255,255,0) 12.5%),-webkit-radial-gradient(rgba(255,255,255,0.1) 12.5%,rgba(255,255,255,0) 12.5%);background-image:radial-gradient(#181819 12.5%,rgba(11,11,12,0) 12.5%),radial-gradient(#181819 12.5%,rgba(11,11,12,0) 12.5%),radial-gradient(rgba(255,255,255,0.1) 12.5%,rgba(255,255,255,0) 12.5%),radial-gradient(rgba(255,255,255,0.1) 12.5%,rgba(255,255,255,0) 12.5%);background-repeat:repeat;background-position:0 0,8px 8px,0 1px,8px 9px;background-color:#2f3031;background-size:16px 16px}#jqt ul li{border-top:1px solid #48494b;background-image:none;background-color:rgba(85,86,88,0.2);background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(123,124,127,0.2)), color-stop(3%, rgba(98,99,101,0.2)), color-stop(100%, rgba(72,73,75,0.2)));background-image:-webkit-linear-gradient(top, rgba(123,124,127,0.2),rgba(98,99,101,0.2) 3%,rgba(72,73,75,0.2));background-image:linear-gradient(top, rgba(123,124,127,0.2),rgba(98,99,101,0.2) 3%,rgba(72,73,75,0.2))}#jqt ul li a{color:#fff;text-shadow:#3c3d3e 0 -1px 0}#jqt ul li .toggle input[type="checkbox"]{-webkit-border-radius:5px;border-radius:5px;background:#fff url(../img/jqt/on_off.png) 0 0 no-repeat}#jqt ul li input[type='submit']{background-image:none;background-color:#fff;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(50%, #ffffff), color-stop(51%, #ffffff), color-stop(100%, #f2f2f2));background-image:-webkit-linear-gradient(top, #ffffff,#ffffff 50%,#ffffff 51%,#f2f2f2);background-image:linear-gradient(top, #ffffff,#ffffff 50%,#ffffff 51%,#f2f2f2);border:1px outset black}#jqt ul li small.counter{-webkit-box-shadow:rgba(255,255,255,0.1) 0 1px 0;box-shadow:rgba(255,255,255,0.1) 0 1px 0}#jqt ul.metal li{background-image:none;border-top:1px solid #fff;border-bottom:1px solid #666;background:-webkit-gradient(linear, 0% 0%, 0% 100%, from(#eee), to(#9c9ea0))}#jqt ul.metal li a{text-shadow:#fff 0 1px 0}#jqt ul.metal li a.active{color:#000}#jqt ul.metal li em{color:#444}#jqt ul.edgetoedge li{background-image:none;background-color:#3c3d3e;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #282829), color-stop(100%, #2f3031));background-image:-webkit-linear-gradient(top, #282829,#2f3031);background-image:linear-gradient(top, #282829,#2f3031);border-bottom:1px solid #1e1e1f;border-top:1px solid #343536}#jqt ul.edgetoedge li.sep{background-image:none;background-color:rgba(0,0,0,0.3);background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(38,38,38,0.3)), color-stop(30%, rgba(20,20,20,0.3)), color-stop(65%, rgba(0,0,0,0.3)), color-stop(100%, rgba(0,0,0,0.3)));background-image:-webkit-linear-gradient(top, rgba(38,38,38,0.3),rgba(20,20,20,0.3) 30%,rgba(0,0,0,0.3) 65%,rgba(0,0,0,0.3));background-image:linear-gradient(top, rgba(38,38,38,0.3),rgba(20,20,20,0.3) 30%,rgba(0,0,0,0.3) 65%,rgba(0,0,0,0.3));color:#949698;text-shadow:#000 0 -1px 0}#jqt .info{background:-webkit-gradient(linear, 0% 0%, 0% 100%, from(#ccc), to(#aaa), color-stop(0.6, #ccc));text-shadow:rgba(255,255,255,0.8) 0 1px 0;color:#444;border-top:1px solid rgba(255,255,255,0.2)}
+/**
+ * Background noise recipe
+ *
+ * This recipe use a sass function to generate a .png file
+ *
+ * Inspired by a jQuery plugin "Noisy" by Daniel Rapp @DanielRapp
+ * @link https://github.com/DanielRapp/Noisy
+ *
+ * Converted using Sass by Aaron Russell @aaronrussell & Philipp Bosch @philippbosch
+ * @link https://gist.github.com/1021332
+ *
+ * Ported to a sass gem by Antti Salonen @antsa
+ * @link https://github.com/antsa/sassy_noise
+ *
+ * Mixin: background-noise
+ * Function: background_noise
+ *
+ * @author Daniel Rapp @DanielRapp
+ * @author Aaron Russell @aaronrussell
+ * @author Philipp Bosch @philippbosch
+ * @author Antti Salonen @antsa
+ * @author Maxime Thirouin maxime.thirouin@gmail.com @MoOx
+ */
+/**
+ *
+ * @class Gradients
+ * @author David Kaneda http://www.davidkaneda.com/
+ *
+ */
+/**
+ * Adds a background gradient into a specified selector.
+ *
+ * @include background-gradient(#444, 'glossy');
+ *
+ * You can also use color-stops if you want full control of the gradient:
+ *
+ * @include background-gradient(#444, color-stops(#333, #222, #111));
+ *
+ * @param {color} $bg-color
+ * The base color of the gradient.
+ *
+ * @param {string/list} $type
+ * The style of the gradient, one of five pre-defined options: matte, bevel, glossy, recessed, or linear:
+ *
+ * @include background-gradient(red, 'glossy');
+ *
+ * It can also accept a list of color-stop values:;
+ *
+ * @include background-gradient(black, color-stops(#333, #111, #000));
+ *
+ * @param {string} $direction
+ * The direction of the gradient.
+ */
+/**
+ * Blueprint grid background pattern
+ *
+ * @link http://lea.verou.me/css3patterns/#blueprint-grid
+ *
+ * @author Lea Verou http://lea.verou.me/ for the original pattern
+ * @author Maxime Thirouin maxime.thirouin@gmail.com @MoOx for the sass mixin
+ */
+/**
+ * Background overlay inspired by Google Chrome modal overlay
+ *
+ * @author Maxime Thirouin @MoOx maxime.thirouin@gmail.com
+ */
+/**
+ * Striped background pattern
+ *
+ * @link http://lea.verou.me/css3patterns/
+ *
+ * @author Lea Verou http://lea.verou.me/ for the original pattern
+ * @author David Kaneda http://www.davidkaneda.com @davidkaneda for the sass mixin
+ */
+/**
+ *
+ * Before compass 0.11.5, you need to add
+ * Compass::BrowserSupport.add_support("repeating-linear-gradient", "webkit", "moz", "o", "ms")
+ * To your configuration (config.rb)
+ * @see https://github.com/chriseppstein/compass/issues/401
+ *
+ * @link http://lea.verou.me/css3patterns/#tartan
+ *
+ * @author Marta Armada http://swwweet.com/ for the original pattern
+ * @author Maxime Thirouin maxime.thirouin@gmail.com @MoOx for the sass mixin
+ */
+/**
+ * Carbon Fiber background pattern
+ *
+ * @author Lea Verou http://lea.verou.me/ for the original pattern
+ * @author David Kaneda http://www.davidkaneda.com/ for the Sass mixin
+ *
+ * @link http://lea.verou.me/css3patterns/
+ *
+ */
+/**
+ * Normalize.css
+ * Opposite approche from CSS reset
+ *
+ * Based on normalize.css commit 9576d48fc234c5224b1fc4dccba2f5965243843d
+ *
+ * @link http://github.com/necolas/normalize.css
+ */
+/* normalize.css 2011-07-12T10:51 UTC · http://github.com/necolas/normalize.css */
+/* =============================================================================
+ HTML5 element display
+ ========================================================================== */
+/* =============================================================================
+ Base
+ ========================================================================== */
+/* =============================================================================
+ Links
+ ========================================================================== */
+/* =============================================================================
+ Typography
+ ========================================================================== */
+/* =============================================================================
+ Lists
+ ========================================================================== */
+/* =============================================================================
+ Embedded content
+ ========================================================================== */
+/* =============================================================================
+ Figures
+ ========================================================================== */
+/* =============================================================================
+ Forms
+ ========================================================================== */
+/* =============================================================================
+ Tables
+ ========================================================================== */
+/**
+ *
+ * @author David Kaneda - http://www.davidkaneda.com
+ *
+ */
+/**
+ * @class Color
+ */
+/**
+ * Returns the brightness (out of 100) of a specified color.
+ * @todo explain why this is useful
+ * @param {color} $color The color you want the brightness value of
+ * @return {measurement}
+ */
+/**
+ * Returns the luminosity for a specified color
+ * @todo explain why this is useful
+ * @param {color} The color to check
+ * @return {measurement}
+ */
+/**
+ * Glass effect
+ * Use this on image for better effect render
+ *
+ * Inspired from Simurai's IMDB redisign
+ *
+ * @link http://lab.simurai.com/redesign/imdb
+ * @thanks Simurai @simurai
+ */
+/**
+ * Note IE7/6 doesn't understand pseudo element as ::before and ::after
+ * IE8 need to have :before and not ::before
+ * So use only : and not :: if you want to support IE8
+ * IE9 Webkit Firefox Opera understand ::
+ */
+/**
+ * Scotch tape effect with pure CSS
+ *
+ * @thanks Nick La @nickla for original concept
+ * @link http://webdesignerwall.com/tutorials/css3-image-styles
+ *
+ * @author David Kaneda http://www.davidkaneda.com
+ *
+ */
+/**
+ * Note IE7/6 doesn't understand pseudo element as ::before and ::after
+ * IE8 need to have :before and not ::before
+ * So use only : and not :: if you want to support IE8
+ * IE9 Webkit Firefox Opera understand ::
+ */
+/**
+ * Corner folded with pure CSS
+ *
+ * Known support: Firefox 3.5+, Chrome 4+, Safari 4+, Opera 10+, IE 9+.
+ * IE8 is not supported because it not render properly box-shadow and
+ * pseudo element should be selected with ::element and not :element
+ *
+ * @thanks Nicolas Gallagher @necolas
+ * @link http://nicolasgallagher.com/pure-css-folded-corner-effect/demo/
+ * @todo Nix in .4
+ */
+/**
+ * Note IE7/6 doesn't understand pseudo element as ::before and ::after
+ * IE8 need to have :before and not ::before
+ * So use only : and not :: if you want to support IE8
+ * IE9 Webkit Firefox Opera understand ::
+ */
+/**
+ * Corner folded with pure CSS
+ *
+ * Known support: Firefox 3.5+, Chrome 4+, Safari 4+, Opera 10+, IE 9+.
+ * IE8 is not supported because it not render properly box-shadow and
+ * pseudo element should be selected with ::element and not :element
+ *
+ * @thanks Nicolas Gallagher @necolas
+ * @link http://nicolasgallagher.com/pure-css-folded-corner-effect/demo/
+ */
+/**
+ * Note IE7/6 doesn't understand pseudo element as ::before and ::after
+ * IE8 need to have :before and not ::before
+ * So use only : and not :: if you want to support IE8
+ * IE9 Webkit Firefox Opera understand ::
+ */
+/**
+ * Form element inline mixin
+ * This mixin allow you to have a label inline with your input
+ * It's simply based on inline-block behavior
+ *
+ * @author Maxime Thirouin maxime.thirouin@gmail.com @MoOx
+ */
+/**
+ * Vertical alignement for page
+ * Inspired by http://css-tricks.com/snippets/css/center-div-with-dynamic-height/
+ *
+ * Usage:
+ *
+ * SCSS
+ * @include vertical-align-requirement;
+ * .v-align-container { @include vertical-align-container }
+ * .v-align-content-container { @include vertical-align-content-container }
+ * .v-align-content { @include vertical-align-content }
+ *
+ * HTML
+ *
+ *