Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

* Commit

  • Loading branch information...
commit ed0972fbd5b7a438f6c477850c2442f5440d8da7 1 parent 8581404
@bnolan authored
View
248 application.coffee
@@ -0,0 +1,248 @@
+
+#
+# Some helper methods
+#
+
+app =
+ activePage: ->
+ $(".ui-page-active")
+
+ reapplyStyles: (el) ->
+ el.find('ul[data-role]').listview();
+ el.find('div[data-role="fieldcontain"]').fieldcontain();
+ el.find('button[data-role="button"]').button();
+ el.find('input,textarea').textinput();
+ el.page()
+
+ redirectTo: (page) ->
+ $.mobile.changePage page
+
+
+#
+# Venue class
+#
+
+class Venue extends Backbone.Model
+ getName: ->
+ @get('name')
+
+ getAddress: ->
+ [@get('address'), @get('city'), @get('state')].join ", "
+
+ getImageUrl: ->
+ @get('photo_url')
+
+ getLatitude: ->
+ @get('geolat')
+
+ getLongitude: ->
+ @get('geolong')
+
+ getMapUrl: (width, height) ->
+ width ||= 300
+ height ||= 220
+
+ "http://maps.google.com/maps/api/staticmap?center=#{@getLatitude()},#{@getLongitude()}&zoom=14&size=#{width}x#{height}&maptype=terrain&markers=color:red|#{@getLatitude()},#{@getLongitude()}&sensor=false"
+
+#
+# Venue Collection
+#
+
+class VenueCollection extends Backbone.Collection
+ model : Venue
+
+ constructor: ->
+ @refresh($FOURSQUARE_JSON)
+
+this.Venues = new VenueCollection
+
+#
+# Edit Venue View
+#
+
+class EditVenueView extends Backbone.View
+ constructor: ->
+ super
+
+ # Get the active page from jquery mobile. We need to keep track of what this
+ # dom element is so that we can refresh the page when the page is no longer active.
+ @el = app.activePage()
+
+ @template = _.template('''
+ <form action="#venue-<%= venue.cid %>-update" method="post">
+
+ <div data-role="fieldcontain">
+ <label>Name</label>
+ <input type="text" value="<%= venue.getName() %>" name="name" />
+ </div>
+
+ <div data-role="fieldcontain">
+ <label>Address</label>
+ <input type="text" value="<%= venue.get('address') %>" name="address" />
+ </div>
+
+ <div data-role="fieldcontain">
+ <label>City</label>
+ <input type="text" value="<%= venue.get('city') %>" name="city" />
+ </div>
+
+ <div data-role="fieldcontain">
+ <label>State</label>
+ <input type="text" value="<%= venue.get('state') %>" name="state" />
+ </div>
+
+ <button type="submit" data-role="button">Save</button>
+ </form>
+ ''')
+
+ # Watch for changes to the model and redraw the view
+ @model.bind 'change', @render
+
+ # Draw the view
+ @render()
+
+ events : {
+ "submit form" : "onSubmit"
+ }
+
+ onSubmit: (e) ->
+ @model.set {
+ name : $("input[name='name']").val(),
+ address : $("input[name='address']").val(),
+ city : $("input[name='city']").val(),
+ state : $("input[name='state']").val()
+ }
+
+ @model.trigger('change')
+
+ app.redirectTo "#venues-#{@model.cid}"
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ render: =>
+ # Set the name of the page
+ @el.find('h1').text("Editing #{@model.getName()}")
+
+ # Render the content
+ @el.find('.ui-content').html(@template({venue : @model}))
+
+ # A hacky way of reapplying the jquery mobile styles
+ app.reapplyStyles(@el)
+
+ # Delegate from the events hash
+ @delegateEvents()
+
+#
+# Show Venue View
+#
+
+class ShowVenueView extends Backbone.View
+ constructor: ->
+ super
+
+ # Get the active page from jquery mobile. We need to keep track of what this
+ # dom element is so that we can refresh the page when the page is no longer active.
+ @el = app.activePage()
+
+ @template = _.template('''
+ <div>
+
+ <p>
+ <img style="width: 100%" src="<%= venue.getMapUrl() %>" />
+ </p>
+
+ <address>
+ <%= venue.getAddress() %>
+ </address>
+
+ <ul data-role="listview" data-inset="true">
+ <li data-role="list-divider">Actions</li>
+ <li><a rel="external" href="openmap:q=<%= encodeURIComponent(venue.getAddress) %>">Open Map</li>
+ <li><a href="#venues-<%= venue.cid %>-edit">Edit</a></li>
+ </ul>
+ </div>
+ ''')
+
+ # Watch for changes to the model and redraw the view
+ @model.bind 'change', @render
+
+ # Draw the view
+ @render()
+
+ render: =>
+ # Set the name of the page
+ @el.find('h1').text(@model.getName())
+
+ # Render the content
+ @el.find('.ui-content').html(@template({venue : @model}))
+
+ # A hacky way of reapplying the jquery mobile styles
+ app.reapplyStyles(@el)
+
+#
+# Home View
+#
+
+class HomeView extends Backbone.View
+ constructor: ->
+ super
+
+ @el = app.activePage()
+
+ @template = _.template('''
+ <div>
+
+ <ul data-role="listview" data-theme="c" data-filter="true">
+ <% venues.each(function(venue){ %>
+ <li><a href="#venues-<%= venue.cid %>"><%= venue.getName() %></a></li>
+ <% }); %>
+ </ul>
+
+ </div>
+ ''')
+
+ @render()
+
+ render: =>
+ # Render the content
+ @el.find('.ui-content').html(@template({venues : Venues}))
+
+ # A hacky way of reapplying the jquery mobile styles
+ app.reapplyStyles(@el)
+
+
+#
+# Our only controller
+#
+
+class HomeController extends Backbone.Controller
+ routes :
+ "venues-:cid-edit" : "edit"
+ "venues-:cid" : "show"
+ "home" : "home"
+
+ constructor: ->
+ super
+ @_views = {}
+
+ home : ->
+ @_views['home'] ||= new HomeView
+
+ show: (cid) ->
+ @_views["venues-#{cid}"] ||= new ShowVenueView { model : Venues.getByCid(cid) }
+
+ edit: (cid) ->
+ @_views["venues-#{cid}-edit"] ||= new EditVenueView { model : Venues.getByCid(cid) }
+
+app.homeController = new HomeController()
+
+#
+# Start the app
+#
+
+$(document).ready ->
+ Backbone.history.start()
+ app.homeController.home()
+
+@app = app
View
178 application.js
@@ -0,0 +1,178 @@
+(function() {
+ var EditVenueView, HomeController, HomeView, ShowVenueView, Venue, VenueCollection, app;
+ var __extends = function(child, parent) {
+ function ctor() { this.constructor = child; }
+ ctor.prototype = parent.prototype;
+ child.prototype = new ctor;
+ if (typeof parent.extended === "function") parent.extended(child);
+ child.__super__ = parent.prototype;
+ };
+ app = {
+ activePage: function() {
+ return $(".ui-page-active");
+ },
+ reapplyStyles: function(el) {
+ el.find('ul[data-role]').listview();
+ el.find('div[data-role="fieldcontain"]').fieldcontain();
+ el.find('button[data-role="button"]').button();
+ el.find('input,textarea').textinput();
+ return el.page();
+ },
+ redirectTo: function(page) {
+ return $.mobile.changePage(page);
+ }
+ };
+ Venue = (function() {
+ function Venue() {
+ return Backbone.Model.apply(this, arguments);
+ }
+ return Venue;
+ })();
+ __extends(Venue, Backbone.Model);
+ Venue.prototype.getName = function() {
+ return this.get('name');
+ };
+ Venue.prototype.getAddress = function() {
+ return [this.get('address'), this.get('city'), this.get('state')].join(", ");
+ };
+ Venue.prototype.getImageUrl = function() {
+ return this.get('photo_url');
+ };
+ Venue.prototype.getLatitude = function() {
+ return this.get('geolat');
+ };
+ Venue.prototype.getLongitude = function() {
+ return this.get('geolong');
+ };
+ Venue.prototype.getMapUrl = function(width, height) {
+ width || (width = 300);
+ height || (height = 220);
+ return "http://maps.google.com/maps/api/staticmap?center=" + (this.getLatitude()) + "," + (this.getLongitude()) + "&zoom=14&size=" + width + "x" + height + "&maptype=terrain&markers=color:red|" + (this.getLatitude()) + "," + (this.getLongitude()) + "&sensor=false";
+ };
+ VenueCollection = (function() {
+ function VenueCollection() {
+ this.refresh($FOURSQUARE_JSON);
+ return this;
+ }
+ return VenueCollection;
+ })();
+ __extends(VenueCollection, Backbone.Collection);
+ VenueCollection.prototype.model = Venue;
+ this.Venues = new VenueCollection;
+ EditVenueView = (function() {
+ function EditVenueView() {
+ var _this;
+ _this = this;
+ this.render = function() { return EditVenueView.prototype.render.apply(_this, arguments); };
+ EditVenueView.__super__.constructor.apply(this, arguments);
+ this.el = app.activePage();
+ this.template = _.template('<form action="#venue-<%= venue.cid %>-update" method="post">\n\n <div data-role="fieldcontain">\n <label>Name</label>\n <input type="text" value="<%= venue.getName() %>" name="name" />\n </div>\n \n <div data-role="fieldcontain">\n <label>Address</label>\n <input type="text" value="<%= venue.get(\'address\') %>" name="address" />\n </div>\n \n <div data-role="fieldcontain">\n <label>City</label>\n <input type="text" value="<%= venue.get(\'city\') %>" name="city" />\n </div>\n \n <div data-role="fieldcontain">\n <label>State</label>\n <input type="text" value="<%= venue.get(\'state\') %>" name="state" />\n </div>\n \n <button type="submit" data-role="button">Save</button>\n</form>');
+ this.model.bind('change', this.render);
+ this.render();
+ return this;
+ }
+ return EditVenueView;
+ })();
+ __extends(EditVenueView, Backbone.View);
+ EditVenueView.prototype.events = {
+ "submit form": "onSubmit"
+ };
+ EditVenueView.prototype.onSubmit = function(e) {
+ this.model.set({
+ name: $("input[name='name']").val(),
+ address: $("input[name='address']").val(),
+ city: $("input[name='city']").val(),
+ state: $("input[name='state']").val()
+ });
+ this.model.trigger('change');
+ app.redirectTo("#venues-" + this.model.cid);
+ e.preventDefault();
+ return e.stopPropagation();
+ };
+ EditVenueView.prototype.render = function() {
+ this.el.find('h1').text("Editing " + (this.model.getName()));
+ this.el.find('.ui-content').html(this.template({
+ venue: this.model
+ }));
+ app.reapplyStyles(this.el);
+ return this.delegateEvents();
+ };
+ ShowVenueView = (function() {
+ function ShowVenueView() {
+ var _this;
+ _this = this;
+ this.render = function() { return ShowVenueView.prototype.render.apply(_this, arguments); };
+ ShowVenueView.__super__.constructor.apply(this, arguments);
+ this.el = app.activePage();
+ this.template = _.template('<div>\n \n <p>\n <img style="width: 100%" src="<%= venue.getMapUrl() %>" />\n </p>\n \n <address>\n <%= venue.getAddress() %>\n </address>\n\n <ul data-role="listview" data-inset="true">\n <li data-role="list-divider">Actions</li>\n <li><a rel="external" href="openmap:q=<%= encodeURIComponent(venue.getAddress) %>">Open Map</li>\n <li><a href="#venues-<%= venue.cid %>-edit">Edit</a></li>\n </ul>\n</div>');
+ this.model.bind('change', this.render);
+ this.render();
+ return this;
+ }
+ return ShowVenueView;
+ })();
+ __extends(ShowVenueView, Backbone.View);
+ ShowVenueView.prototype.render = function() {
+ this.el.find('h1').text(this.model.getName());
+ this.el.find('.ui-content').html(this.template({
+ venue: this.model
+ }));
+ return app.reapplyStyles(this.el);
+ };
+ HomeView = (function() {
+ function HomeView() {
+ var _this;
+ _this = this;
+ this.render = function() { return HomeView.prototype.render.apply(_this, arguments); };
+ HomeView.__super__.constructor.apply(this, arguments);
+ this.el = app.activePage();
+ this.template = _.template('<div>\n\n<ul data-role="listview" data-theme="c" data-filter="true">\n <% venues.each(function(venue){ %>\n <li><a href="#venues-<%= venue.cid %>"><%= venue.getName() %></a></li>\n <% }); %>\n</ul>\n\n</div>');
+ this.render();
+ return this;
+ }
+ return HomeView;
+ })();
+ __extends(HomeView, Backbone.View);
+ HomeView.prototype.render = function() {
+ this.el.find('.ui-content').html(this.template({
+ venues: Venues
+ }));
+ return app.reapplyStyles(this.el);
+ };
+ HomeController = (function() {
+ function HomeController() {
+ HomeController.__super__.constructor.apply(this, arguments);
+ this._views = {};
+ return this;
+ }
+ return HomeController;
+ })();
+ __extends(HomeController, Backbone.Controller);
+ HomeController.prototype.routes = {
+ "venues-:cid-edit": "edit",
+ "venues-:cid": "show",
+ "home": "home"
+ };
+ HomeController.prototype.home = function() {
+ var _base;
+ return (_base = this._views)['home'] || (_base['home'] = new HomeView);
+ };
+ HomeController.prototype.show = function(cid) {
+ var _base, _name;
+ return (_base = this._views)[_name = "venues-" + cid] || (_base[_name] = new ShowVenueView({
+ model: Venues.getByCid(cid)
+ }));
+ };
+ HomeController.prototype.edit = function(cid) {
+ var _base, _name;
+ return (_base = this._views)[_name = "venues-" + cid + "-edit"] || (_base[_name] = new EditVenueView({
+ model: Venues.getByCid(cid)
+ }));
+ };
+ app.homeController = new HomeController();
+ $(document).ready(function() {
+ Backbone.history.start();
+ return app.homeController.home();
+ });
+ this.app = app;
+}).call(this);
View
975 backbone.js
@@ -0,0 +1,975 @@
+// Backbone.js 0.3.1
+// (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(){
+
+ // Initial Setup
+ // -------------
+
+ // The top-level namespace. All public Backbone classes and modules will
+ // be attached to this. Exported for both CommonJS and the browser.
+ var Backbone;
+ if (typeof exports !== 'undefined') {
+ Backbone = exports;
+ } else {
+ Backbone = this.Backbone = {};
+ }
+
+ // Current version of the library. Keep in sync with `package.json`.
+ Backbone.VERSION = '0.3.1';
+
+ // Require Underscore, if we're on the server, and it's not already present.
+ var _ = this._;
+ if (!_ && (typeof require !== 'undefined')) _ = require("underscore")._;
+
+ // For Backbone's purposes, jQuery owns the `$` variable.
+ var $ = this.jQuery;
+
+ // Turn on `emulateHTTP` to use support legacy HTTP servers. Setting this option will
+ // fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and set a
+ // `X-Http-Method-Override` header.
+ Backbone.emulateHTTP = false;
+
+ // Turn on `emulateJSON` to support legacy servers that can't deal with direct
+ // `application/json` requests ... will encode the body as
+ // `application/x-www-form-urlencoded` instead and will send the model in a
+ // form param named `model`.
+ Backbone.emulateJSON = false;
+
+ // Backbone.Events
+ // -----------------
+
+ // A module that can be mixed in to *any object* in order to provide it with
+ // custom events. You may `bind` or `unbind` a callback function to an event;
+ // `trigger`-ing an event fires all callbacks in succession.
+ //
+ // var object = {};
+ // _.extend(object, Backbone.Events);
+ // object.bind('expand', function(){ alert('expanded'); });
+ // object.trigger('expand');
+ //
+ Backbone.Events = {
+
+ // Bind an event, specified by a string name, `ev`, to a `callback` function.
+ // Passing `"all"` will bind the callback to all events fired.
+ bind : function(ev, callback) {
+ var calls = this._callbacks || (this._callbacks = {});
+ var list = this._callbacks[ev] || (this._callbacks[ev] = []);
+ list.push(callback);
+ return this;
+ },
+
+ // Remove one or many callbacks. If `callback` is null, removes all
+ // callbacks for the event. If `ev` is null, removes all bound callbacks
+ // for all events.
+ unbind : function(ev, callback) {
+ var calls;
+ if (!ev) {
+ this._callbacks = {};
+ } else if (calls = this._callbacks) {
+ if (!callback) {
+ calls[ev] = [];
+ } else {
+ var list = calls[ev];
+ if (!list) return this;
+ for (var i = 0, l = list.length; i < l; i++) {
+ if (callback === list[i]) {
+ list.splice(i, 1);
+ break;
+ }
+ }
+ }
+ }
+ return this;
+ },
+
+ // Trigger an event, firing all bound callbacks. Callbacks are passed the
+ // same arguments as `trigger` is, apart from the event name.
+ // Listening for `"all"` passes the true event name as the first argument.
+ trigger : function(ev) {
+ var list, calls, i, l;
+ if (!(calls = this._callbacks)) return this;
+ if (list = calls[ev]) {
+ for (i = 0, l = list.length; i < l; i++) {
+ list[i].apply(this, Array.prototype.slice.call(arguments, 1));
+ }
+ }
+
+ /*
+ * Patched by @bnolan - see issue https://github.com/documentcloud/backbone/issues/issue/91
+ */
+ if (list = calls['all']) {
+ for (i = 0, l = list.length; i < l; i++) {
+ if(list[i]){
+ list[i].apply(this, arguments);
+ }
+ }
+ }
+ /*
+ * End patched code
+ */
+ return this;
+ }
+
+ };
+
+ // Backbone.Model
+ // --------------
+
+ // Create a new model, with defined attributes. A client id (`cid`)
+ // is automatically generated and assigned for you.
+ Backbone.Model = function(attributes, options) {
+ this.attributes = {};
+ this.cid = _.uniqueId('c');
+ this.set(attributes || {}, {silent : true});
+ this._previousAttributes = _.clone(this.attributes);
+ if (options && options.collection) this.collection = options.collection;
+ if (this.initialize) this.initialize(attributes, options);
+ };
+
+ // Attach all inheritable methods to the Model prototype.
+ _.extend(Backbone.Model.prototype, Backbone.Events, {
+
+ // A snapshot of the model's previous attributes, taken immediately
+ // after the last `"change"` event was fired.
+ _previousAttributes : null,
+
+ // Has the item been changed since the last `"change"` event?
+ _changed : false,
+
+ // Return a copy of the model's `attributes` object.
+ toJSON : function() {
+ return _.clone(this.attributes);
+ },
+
+ // Get the value of an attribute.
+ get : function(attr) {
+ return this.attributes[attr];
+ },
+
+ // Set a hash of model attributes on the object, firing `"change"` unless you
+ // choose to silence it.
+ set : function(attrs, options) {
+
+ // Extract attributes and options.
+ options || (options = {});
+ if (!attrs) return this;
+ if (attrs.attributes) attrs = attrs.attributes;
+ var now = this.attributes;
+
+ // Run validation.
+ if (!options.silent && this.validate && !this._performValidation(attrs, options)) return false;
+
+ // Check for changes of `id`.
+ if ('id' in attrs) this.id = attrs.id;
+
+ // Update attributes.
+ for (var attr in attrs) {
+ var val = attrs[attr];
+ if (!_.isEqual(now[attr], val)) {
+ now[attr] = val;
+ if (!options.silent) {
+ this._changed = true;
+ this.trigger('change:' + attr, this, val);
+ }
+ }
+ }
+
+ // Fire the `"change"` event, if the model has been changed.
+ if (!options.silent && this._changed) this.change();
+ return this;
+ },
+
+ // Remove an attribute from the model, firing `"change"` unless you choose
+ // to silence it.
+ unset : function(attr, options) {
+ options || (options = {});
+ var value = this.attributes[attr];
+
+ // Run validation.
+ var validObj = {};
+ validObj[attr] = void 0;
+ if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
+
+ // Remove the attribute.
+ delete this.attributes[attr];
+ if (!options.silent) {
+ this._changed = true;
+ this.trigger('change:' + attr, this);
+ this.change();
+ }
+ return this;
+ },
+
+ // Clear all attributes on the model, firing `"change"` unless you choose
+ // to silence it.
+ clear : function(options) {
+ options || (options = {});
+ var old = this.attributes;
+
+ // Run validation.
+ var validObj = {};
+ for (attr in old) validObj[attr] = void 0;
+ if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
+
+ this.attributes = {};
+ if (!options.silent) {
+ this._changed = true;
+ for (attr in old) {
+ this.trigger('change:' + attr, this);
+ }
+ this.change();
+ }
+ return this;
+ },
+
+ // Fetch the model from the server. If the server's representation of the
+ // model differs from its current attributes, they will be overriden,
+ // triggering a `"change"` event.
+ fetch : function(options) {
+ options || (options = {});
+ var model = this;
+ var success = function(resp) {
+ if (!model.set(model.parse(resp), options)) return false;
+ if (options.success) options.success(model, resp);
+ };
+ var error = options.error && _.bind(options.error, null, model);
+ Backbone.sync('read', this, success, error);
+ return this;
+ },
+
+ // Set a hash of model attributes, and sync the model to the server.
+ // If the server returns an attributes hash that differs, the model's
+ // state will be `set` again.
+ save : function(attrs, options) {
+ attrs || (attrs = {});
+ options || (options = {});
+ if (!this.set(attrs, options)) return false;
+ var model = this;
+ var success = function(resp) {
+ if (!model.set(model.parse(resp), options)) return false;
+ if (options.success) options.success(model, resp);
+ };
+ var error = options.error && _.bind(options.error, null, model);
+ var method = this.isNew() ? 'create' : 'update';
+ Backbone.sync(method, this, success, error);
+ return this;
+ },
+
+ // Destroy this model on the server. Upon success, the model is removed
+ // from its collection, if it has one.
+ destroy : function(options) {
+ options || (options = {});
+ var model = this;
+ var success = function(resp) {
+ if (model.collection) model.collection.remove(model);
+ if (options.success) options.success(model, resp);
+ };
+ var error = options.error && _.bind(options.error, null, model);
+ Backbone.sync('delete', this, success, error);
+ return this;
+ },
+
+ // Default URL for the model's representation on the server -- if you're
+ // using Backbone's restful methods, override this to change the endpoint
+ // that will be called.
+ url : function() {
+ var base = getUrl(this.collection);
+ if (this.isNew()) return base;
+ return base + '/' + this.id;
+ },
+
+ // **parse** converts a response into the hash of attributes to be `set` on
+ // the model. The default implementation is just to pass the response along.
+ parse : function(resp) {
+ return resp;
+ },
+
+ // Create a new model with identical attributes to this one.
+ clone : function() {
+ return new this.constructor(this);
+ },
+
+ // A model is new if it has never been saved to the server, and has a negative
+ // ID.
+ isNew : function() {
+ return !this.id;
+ },
+
+ // Call this method to manually fire a `change` event for this model.
+ // Calling this will cause all objects observing the model to update.
+ change : function() {
+ this.trigger('change', this);
+ this._previousAttributes = _.clone(this.attributes);
+ this._changed = false;
+ },
+
+ // Determine if the model has changed since the last `"change"` event.
+ // If you specify an attribute name, determine if that attribute has changed.
+ hasChanged : function(attr) {
+ if (attr) return this._previousAttributes[attr] != this.attributes[attr];
+ return this._changed;
+ },
+
+ // Return an object containing all the attributes that have changed, or false
+ // if there are no changed attributes. Useful for determining what parts of a
+ // view need to be updated and/or what attributes need to be persisted to
+ // the server.
+ changedAttributes : function(now) {
+ now || (now = this.attributes);
+ var old = this._previousAttributes;
+ var changed = false;
+ for (var attr in now) {
+ if (!_.isEqual(old[attr], now[attr])) {
+ changed = changed || {};
+ changed[attr] = now[attr];
+ }
+ }
+ return changed;
+ },
+
+ // Get the previous value of an attribute, recorded at the time the last
+ // `"change"` event was fired.
+ previous : function(attr) {
+ if (!attr || !this._previousAttributes) return null;
+ return this._previousAttributes[attr];
+ },
+
+ // Get all of the attributes of the model at the time of the previous
+ // `"change"` event.
+ previousAttributes : function() {
+ return _.clone(this._previousAttributes);
+ },
+
+ // Run validation against a set of incoming attributes, returning `true`
+ // if all is well. If a specific `error` callback has been passed,
+ // call that instead of firing the general `"error"` event.
+ _performValidation : function(attrs, options) {
+ var error = this.validate(attrs);
+ if (error) {
+ if (options.error) {
+ options.error(this, error);
+ } else {
+ this.trigger('error', this, error);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ });
+
+ // Backbone.Collection
+ // -------------------
+
+ // Provides a standard collection class for our sets of models, ordered
+ // or unordered. If a `comparator` is specified, the Collection will maintain
+ // its models in sort order, as they're added and removed.
+ Backbone.Collection = function(models, options) {
+ options || (options = {});
+ if (options.comparator) {
+ this.comparator = options.comparator;
+ delete options.comparator;
+ }
+ this._boundOnModelEvent = _.bind(this._onModelEvent, this);
+ this._reset();
+ if (models) this.refresh(models, {silent: true});
+ if (this.initialize) this.initialize(models, options);
+ };
+
+ // Define the Collection's inheritable methods.
+ _.extend(Backbone.Collection.prototype, Backbone.Events, {
+
+ // The default model for a collection is just a **Backbone.Model**.
+ // This should be overridden in most cases.
+ model : Backbone.Model,
+
+ // The JSON representation of a Collection is an array of the
+ // models' attributes.
+ toJSON : function() {
+ return this.map(function(model){ return model.toJSON(); });
+ },
+
+ // Add a model, or list of models to the set. Pass **silent** to avoid
+ // firing the `added` event for every new model.
+ add : function(models, options) {
+ if (_.isArray(models)) {
+ for (var i = 0, l = models.length; i < l; i++) {
+ this._add(models[i], options);
+ }
+ } else {
+ this._add(models, options);
+ }
+ return this;
+ },
+
+ // Remove a model, or a list of models from the set. Pass silent to avoid
+ // firing the `removed` event for every model removed.
+ remove : function(models, options) {
+ if (_.isArray(models)) {
+ for (var i = 0, l = models.length; i < l; i++) {
+ this._remove(models[i], options);
+ }
+ } else {
+ this._remove(models, options);
+ }
+ return this;
+ },
+
+ // Get a model from the set by id.
+ get : function(id) {
+ return id && this._byId[id.id != null ? id.id : id];
+ },
+
+ // Get a model from the set by client id.
+ getByCid : function(cid) {
+ return cid && this._byCid[cid.cid || cid];
+ },
+
+ // Get the model at the given index.
+ at: function(index) {
+ return this.models[index];
+ },
+
+ // Force the collection to re-sort itself. You don't need to call this under normal
+ // circumstances, as the set will maintain sort order as each item is added.
+ sort : function(options) {
+ options || (options = {});
+ if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
+ this.models = this.sortBy(this.comparator);
+ if (!options.silent) this.trigger('refresh', this);
+ return this;
+ },
+
+ // Pluck an attribute from each model in the collection.
+ pluck : function(attr) {
+ return _.map(this.models, function(model){ return model.get(attr); });
+ },
+
+ // When you have more items than you want to add or remove individually,
+ // you can refresh the entire set with a new list of models, without firing
+ // any `added` or `removed` events. Fires `refresh` when finished.
+ refresh : function(models, options) {
+ models || (models = []);
+ options || (options = {});
+ this._reset();
+ this.add(models, {silent: true});
+ if (!options.silent) this.trigger('refresh', this);
+ return this;
+ },
+
+ // Fetch the default set of models for this collection, refreshing the
+ // collection when they arrive.
+ fetch : function(options) {
+ options || (options = {});
+ var collection = this;
+ var success = function(resp) {
+ collection.refresh(collection.parse(resp));
+ if (options.success) options.success(collection, resp);
+ };
+ var error = options.error && _.bind(options.error, null, collection);
+ Backbone.sync('read', this, success, error);
+ return this;
+ },
+
+ // Create a new instance of a model in this collection. After the model
+ // has been created on the server, it will be added to the collection.
+ create : function(model, options) {
+ var coll = this;
+ options || (options = {});
+ if (!(model instanceof Backbone.Model)) {
+ model = new this.model(model, {collection: coll});
+ } else {
+ model.collection = coll;
+ }
+ var success = function(nextModel, resp) {
+ coll.add(nextModel);
+ if (options.success) options.success(nextModel, resp);
+ };
+ return model.save(null, {success : success, error : options.error});
+ },
+
+ // **parse** converts a response into a list of models to be added to the
+ // collection. The default implementation is just to pass it through.
+ parse : function(resp) {
+ return resp;
+ },
+
+ // Proxy to _'s chain. Can't be proxied the same way the rest of the
+ // underscore methods are proxied because it relies on the underscore
+ // constructor.
+ chain: function () {
+ return _(this.models).chain();
+ },
+
+ // Reset all internal state. Called when the collection is refreshed.
+ _reset : function(options) {
+ this.length = 0;
+ this.models = [];
+ this._byId = {};
+ this._byCid = {};
+ },
+
+ // Internal implementation of adding a single model to the set, updating
+ // hash indexes for `id` and `cid` lookups.
+ _add : function(model, options) {
+ options || (options = {});
+ if (!(model instanceof Backbone.Model)) {
+ model = new this.model(model, {collection: this});
+ }
+ var already = this.getByCid(model);
+ if (already) throw new Error(["Can't add the same model to a set twice", already.id]);
+ this._byId[model.id] = model;
+ this._byCid[model.cid] = model;
+ model.collection = this;
+ var index = this.comparator ? this.sortedIndex(model, this.comparator) : this.length;
+ this.models.splice(index, 0, model);
+ model.bind('all', this._boundOnModelEvent);
+ this.length++;
+ if (!options.silent) model.trigger('add', model, this);
+ return model;
+ },
+
+ // Internal implementation of removing a single model from the set, updating
+ // hash indexes for `id` and `cid` lookups.
+ _remove : function(model, options) {
+ options || (options = {});
+ model = this.getByCid(model);
+ if (!model) return null;
+ delete this._byId[model.id];
+ delete this._byCid[model.cid];
+ delete model.collection;
+ this.models.splice(this.indexOf(model), 1);
+ this.length--;
+ if (!options.silent) model.trigger('remove', model, this);
+ model.unbind('all', this._boundOnModelEvent);
+ return model;
+ },
+
+ // Internal method called every time a model in the set fires an event.
+ // Sets need to update their indexes when models change ids. All other
+ // events simply proxy through.
+ _onModelEvent : function(ev, model) {
+ if (ev === 'change:id') {
+ delete this._byId[model.previous('id')];
+ this._byId[model.id] = model;
+ }
+ this.trigger.apply(this, arguments);
+ }
+
+ });
+
+ // Underscore methods that we want to implement on the Collection.
+ var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 'detect',
+ 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
+ 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size',
+ 'first', 'rest', 'last', 'without', 'indexOf', 'lastIndexOf', 'isEmpty'];
+
+ // Mix in each Underscore method as a proxy to `Collection#models`.
+ _.each(methods, function(method) {
+ Backbone.Collection.prototype[method] = function() {
+ return _[method].apply(_, [this.models].concat(_.toArray(arguments)));
+ };
+ });
+
+ // Backbone.Controller
+ // -------------------
+
+ // Controllers map faux-URLs to actions, and fire events when routes are
+ // matched. Creating a new one sets its `routes` hash, if not set statically.
+ Backbone.Controller = function(options) {
+ options || (options = {});
+ if (options.routes) this.routes = options.routes;
+ this._bindRoutes();
+ if (this.initialize) this.initialize(options);
+ };
+
+ // Cached regular expressions for matching named param parts and splatted
+ // parts of route strings.
+ var namedParam = /:([\w\d]+)/g;
+ var splatParam = /\*([\w\d]+)/g;
+
+ // Set up all inheritable **Backbone.Controller** properties and methods.
+ _.extend(Backbone.Controller.prototype, Backbone.Events, {
+
+ // Manually bind a single named route to a callback. For example:
+ //
+ // this.route('search/:query/p:num', 'search', function(query, num) {
+ // ...
+ // });
+ //
+ route : function(route, name, callback) {
+ Backbone.history || (Backbone.history = new Backbone.History);
+ if (!_.isRegExp(route)) route = this._routeToRegExp(route);
+ Backbone.history.route(route, _.bind(function(fragment) {
+ var args = this._extractParameters(route, fragment);
+ callback.apply(this, args);
+ this.trigger.apply(this, ['route:' + name].concat(args));
+ }, this));
+ },
+
+ // Simple proxy to `Backbone.history` to save a fragment into the history,
+ // without triggering routes.
+ saveLocation : function(fragment) {
+ Backbone.history.saveLocation(fragment);
+ },
+
+ // Bind all defined routes to `Backbone.history`.
+ _bindRoutes : function() {
+ if (!this.routes) return;
+ for (var route in this.routes) {
+ var name = this.routes[route];
+ this.route(route, name, this[name]);
+ }
+ },
+
+ // Convert a route string into a regular expression, suitable for matching
+ // against the current location fragment.
+ _routeToRegExp : function(route) {
+ route = route.replace(namedParam, "([^\/]*)").replace(splatParam, "(.*?)");
+ return new RegExp('^' + route + '$');
+ },
+
+ // Given a route, and a URL fragment that it matches, return the array of
+ // extracted parameters.
+ _extractParameters : function(route, fragment) {
+ return route.exec(fragment).slice(1);
+ }
+
+ });
+
+ // Backbone.History
+ // ----------------
+
+ // Handles cross-browser history management, based on URL hashes. If the
+ // browser does not support `onhashchange`, falls back to polling.
+ Backbone.History = function() {
+ this.handlers = [];
+ this.fragment = this.getFragment();
+ _.bindAll(this, 'checkUrl');
+ };
+
+ // Cached regex for cleaning hashes.
+ var hashStrip = /^#*/;
+
+ // Set up all inheritable **Backbone.History** properties and methods.
+ _.extend(Backbone.History.prototype, {
+
+ // The default interval to poll for hash changes, if necessary, is
+ // twenty times a second.
+ interval: 50,
+
+ // Get the cross-browser normalized URL fragment.
+ getFragment : function(loc) {
+ return (loc || window.location).hash.replace(hashStrip, '');
+ },
+
+ // Start the hash change handling, returning `true` if the current URL matches
+ // an existing route, and `false` otherwise.
+ start : function() {
+ var docMode = document.documentMode;
+ var oldIE = ($.browser.msie && docMode < 7);
+ if (oldIE) {
+ this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
+ }
+ if ('onhashchange' in window && !oldIE) {
+ $(window).bind('hashchange', this.checkUrl);
+ } else {
+ setInterval(this.checkUrl, this.interval);
+ }
+ return this.loadUrl();
+ },
+
+ // Add a route to be tested when the hash changes. Routes are matched in the
+ // order they are added.
+ route : function(route, callback) {
+ this.handlers.push({route : route, callback : callback});
+ },
+
+ // Checks the current URL to see if it has changed, and if it has,
+ // calls `loadUrl`, normalizing across the hidden iframe.
+ checkUrl : function() {
+ var current = this.getFragment();
+ if (current == this.fragment && this.iframe) {
+ current = this.getFragment(this.iframe.location);
+ }
+ if (current == this.fragment ||
+ current == decodeURIComponent(this.fragment)) return false;
+ if (this.iframe) {
+ window.location.hash = this.iframe.location.hash = current;
+ }
+ this.loadUrl();
+ },
+
+ // Attempt to load the current URL fragment. If a route succeeds with a
+ // match, returns `true`. If no defined routes matches the fragment,
+ // returns `false`.
+ loadUrl : function() {
+ var fragment = this.fragment = this.getFragment();
+ var matched = _.any(this.handlers, function(handler) {
+ if (handler.route.test(fragment)) {
+ handler.callback(fragment);
+ return true;
+ }
+ });
+ return matched;
+ },
+
+ // Save a fragment into the hash history. You are responsible for properly
+ // URL-encoding the fragment in advance. This does not trigger
+ // a `hashchange` event.
+ saveLocation : function(fragment) {
+ fragment = (fragment || '').replace(hashStrip, '');
+ if (this.fragment == fragment) return;
+ window.location.hash = this.fragment = fragment;
+ if (this.iframe && (fragment != this.getFragment(this.iframe.location))) {
+ this.iframe.document.open().close();
+ this.iframe.location.hash = fragment;
+ }
+ }
+
+ });
+
+ // Backbone.View
+ // -------------
+
+ // Creating a Backbone.View creates its initial element outside of the DOM,
+ // if an existing element is not provided...
+ Backbone.View = function(options) {
+ this._configure(options || {});
+ this._ensureElement();
+ this.delegateEvents();
+ if (this.initialize) this.initialize(options);
+ };
+
+ // jQuery lookup, scoped to DOM elements within the current view.
+ // This should be prefered to global jQuery lookups, if you're dealing with
+ // a specific view.
+ var jQueryDelegate = function(selector) {
+ return $(selector, this.el);
+ };
+
+ // Cached regex to split keys for `delegate`.
+ var eventSplitter = /^(\w+)\s*(.*)$/;
+
+ // Set up all inheritable **Backbone.View** properties and methods.
+ _.extend(Backbone.View.prototype, Backbone.Events, {
+
+ // The default `tagName` of a View's element is `"div"`.
+ tagName : 'div',
+
+ // Attach the jQuery function as the `$` and `jQuery` properties.
+ $ : jQueryDelegate,
+ jQuery : jQueryDelegate,
+
+ // **render** is the core function that your view should override, in order
+ // to populate its element (`this.el`), with the appropriate HTML. The
+ // convention is for **render** to always return `this`.
+ render : function() {
+ return this;
+ },
+
+ // Remove this view from the DOM. Note that the view isn't present in the
+ // DOM by default, so calling this method may be a no-op.
+ remove : function() {
+ $(this.el).remove();
+ return this;
+ },
+
+ // For small amounts of DOM Elements, where a full-blown template isn't
+ // needed, use **make** to manufacture elements, one at a time.
+ //
+ // var el = this.make('li', {'class': 'row'}, this.model.get('title'));
+ //
+ make : function(tagName, attributes, content) {
+ var el = document.createElement(tagName);
+ if (attributes) $(el).attr(attributes);
+ if (content) $(el).html(content);
+ return el;
+ },
+
+ // Set callbacks, where `this.callbacks` is a hash of
+ //
+ // *{"event selector": "callback"}*
+ //
+ // {
+ // 'mousedown .title': 'edit',
+ // 'click .button': 'save'
+ // }
+ //
+ // pairs. Callbacks will be bound to the view, with `this` set properly.
+ // Uses jQuery event delegation for efficiency.
+ // Omitting the selector binds the event to `this.el`.
+ // This only works for delegate-able events: not `focus`, `blur`, and
+ // not `change`, `submit`, and `reset` in Internet Explorer.
+ delegateEvents : function(events) {
+ if (!(events || (events = this.events))) return;
+ $(this.el).unbind();
+ for (var key in events) {
+ var methodName = events[key];
+ var match = key.match(eventSplitter);
+ var eventName = match[1], selector = match[2];
+ var method = _.bind(this[methodName], this);
+ if (selector === '') {
+ $(this.el).bind(eventName, method);
+ } else {
+ $(this.el).delegate(selector, eventName, method);
+ }
+ }
+ },
+
+ // Performs the initial configuration of a View with a set of options.
+ // Keys with special meaning *(model, collection, id, className)*, are
+ // attached directly to the view.
+ _configure : function(options) {
+ if (this.options) options = _.extend({}, this.options, options);
+ if (options.model) this.model = options.model;
+ if (options.collection) this.collection = options.collection;
+ if (options.el) this.el = options.el;
+ if (options.id) this.id = options.id;
+ if (options.className) this.className = options.className;
+ if (options.tagName) this.tagName = options.tagName;
+ this.options = options;
+ },
+
+ // Ensure that the View has a DOM element to render into.
+ _ensureElement : function() {
+ if (this.el) return;
+ var attrs = {};
+ if (this.id) attrs.id = this.id;
+ if (this.className) attrs.className = this.className;
+ this.el = this.make(this.tagName, attrs);
+ }
+
+ });
+
+ // The self-propagating extend function that Backbone classes use.
+ var extend = function (protoProps, classProps) {
+ var child = inherits(this, protoProps, classProps);
+ child.extend = extend;
+ return child;
+ };
+
+ // Set up inheritance for the model, collection, and view.
+ Backbone.Model.extend = Backbone.Collection.extend =
+ Backbone.Controller.extend = Backbone.View.extend = extend;
+
+ // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
+ var methodMap = {
+ 'create': 'POST',
+ 'update': 'PUT',
+ 'delete': 'DELETE',
+ 'read' : 'GET'
+ };
+
+ // Backbone.sync
+ // -------------
+
+ // Override this function to change the manner in which Backbone persists
+ // models to the server. You will be passed the type of request, and the
+ // model in question. By default, uses jQuery to make a RESTful Ajax request
+ // to the model's `url()`. Some possible customizations could be:
+ //
+ // * Use `setTimeout` to batch rapid-fire updates into a single request.
+ // * Send up the models as XML instead of JSON.
+ // * Persist models via WebSockets instead of Ajax.
+ //
+ // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
+ // as `POST`, with a `_method` parameter containing the true HTTP method,
+ // as well as all requests with the body as `application/x-www-form-urlencoded` instead of
+ // `application/json` with the model in a param named `model`.
+ // Useful when interfacing with server-side languages like **PHP** that make
+ // it difficult to read the body of `PUT` requests.
+ Backbone.sync = function(method, model, success, error) {
+ var type = methodMap[method];
+ var modelJSON = (method === 'create' || method === 'update') ?
+ JSON.stringify(model.toJSON()) : null;
+
+ // Default JSON-request options.
+ var params = {
+ url: getUrl(model),
+ type: type,
+ contentType: 'application/json',
+ data: modelJSON,
+ dataType: 'json',
+ processData: false,
+ success: success,
+ error: error
+ };
+
+ // For older servers, emulate JSON by encoding the request into an HTML-form.
+ if (Backbone.emulateJSON) {
+ params.contentType = 'application/x-www-form-urlencoded';
+ params.processData = true;
+ params.data = modelJSON ? {model : modelJSON} : {};
+ }
+
+ // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
+ // And an `X-HTTP-Method-Override` header.
+ if (Backbone.emulateHTTP) {
+ if (type === 'PUT' || type === 'DELETE') {
+ if (Backbone.emulateJSON) params.data._method = type;
+ params.type = 'POST';
+ params.beforeSend = function(xhr) {
+ xhr.setRequestHeader("X-HTTP-Method-Override", type);
+ };
+ }
+ }
+
+ // Make the request.
+ $.ajax(params);
+ };
+
+ // Helpers
+ // -------
+
+ // Shared empty constructor function to aid in prototype-chain creation.
+ var ctor = function(){};
+
+ // Helper function to correctly set up the prototype chain, for subclasses.
+ // Similar to `goog.inherits`, but uses a hash of prototype properties and
+ // class properties to be extended.
+ var inherits = function(parent, protoProps, staticProps) {
+ var child;
+
+ // The constructor function for the new subclass is either defined by you
+ // (the "constructor" property in your `extend` definition), or defaulted
+ // by us to simply call `super()`.
+ if (protoProps && protoProps.hasOwnProperty('constructor')) {
+ child = protoProps.constructor;
+ } else {
+ child = function(){ return parent.apply(this, arguments); };
+ }
+
+ // Set the prototype chain to inherit from `parent`, without calling
+ // `parent`'s constructor function.
+ ctor.prototype = parent.prototype;
+ child.prototype = new ctor();
+
+ // Add prototype properties (instance properties) to the subclass,
+ // if supplied.
+ if (protoProps) _.extend(child.prototype, protoProps);
+
+ // Add static properties to the constructor function, if supplied.
+ if (staticProps) _.extend(child, staticProps);
+
+ // Correctly set child's `prototype.constructor`, for `instanceof`.
+ child.prototype.constructor = child;
+
+ // Set a convenience property in case the parent's prototype is needed later.
+ child.__super__ = parent.prototype;
+
+ return child;
+ };
+
+ // Helper function to get a URL from a Model or Collection as a property
+ // or as a function.
+ var getUrl = function(object) {
+ if (!(object && object.url)) throw new Error("A 'url' property or function must be specified");
+ return _.isFunction(object.url) ? object.url() : object.url;
+ };
+
+})();
View
1  foursquare.json
@@ -0,0 +1 @@
+$FOURSQUARE_JSON = [{"id":311014,"name":"Floriditas","primarycategory":{"id":79049,"fullpathname":"Food:Café","nodename":"Café","iconurl":"http://foursquare.com/img/categories/food/cafe.png"},"address":"161 Cuba St","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.293874,"geolong":174.775546,"stats":{"herenow":"0"},"distance":94},{"id":305548,"name":"Midnight Espresso","primarycategory":{"id":79049,"fullpathname":"Food:Café","nodename":"Café","iconurl":"http://foursquare.com/img/categories/food/cafe.png"},"address":"178 Cuba Street, Wellington 6011","crossstreet":"btw Vivian & Ghuznee","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.29455102860177,"geolong":174.77487713098526,"stats":{"herenow":"0"},"distance":7},{"id":307220,"name":"S&M's Cocktail Bar and Lounge","primarycategory":{"id":79159,"fullpathname":"Nightlife:Gay Bar","nodename":"Gay Bar","iconurl":"http://foursquare.com/img/categories/nightlife/gaybar.png"},"address":"176 Cuba st","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.294506693839566,"geolong":174.77489322423935,"stats":{"herenow":"0"},"distance":10},{"id":717061,"name":"The Flying Burrito Brothers","primarycategory":{"id":79076,"fullpathname":"Food:Mexican","nodename":"Mexican","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"Cuba St","crossstreet":"at Vivian St","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.2946316371831,"geolong":174.7747939825058,"stats":{"herenow":"0"},"distance":13},{"id":284159,"name":"San Francisco Bath House","primarycategory":{"id":78971,"fullpathname":"Arts & Entertainment:Music Venue:Rock Club","nodename":"Rock Club","iconurl":"http://foursquare.com/img/categories/arts_entertainment/musicvenue_rockclub.png"},"address":"171 Cuba Street","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.294143953744296,"geolong":174.7755128145218,"stats":{"herenow":"0"},"distance":68},{"id":387080,"name":"The Fringe Bar","primarycategory":{"id":78964,"fullpathname":"Arts & Entertainment:Comedy Club","nodename":"Comedy Club","iconurl":"http://foursquare.com/img/categories/arts_entertainment/comedyclub.png"},"address":"191 Cuba Street","crossstreet":"Vivian Street","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.29469209355374,"geolong":174.77510511875153,"stats":{"herenow":"0"},"phone":"6448015007","twitter":"TheFringeBarNZ","distance":17},{"id":428772,"name":"Wasabi Sushi","primarycategory":{"id":79104,"fullpathname":"Food:Sushi","nodename":"Sushi","iconurl":"http://foursquare.com/img/categories/food/sushi.png"},"address":"173a Cuba St","crossstreet":"btw Ghuznee & Vivian","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.29411775577042,"geolong":174.77538138628006,"stats":{"herenow":"0"},"phone":"006443848822","distance":64},{"id":284130,"name":"Olive Cafe","primarycategory":{"id":79049,"fullpathname":"Food:Café","nodename":"Café","iconurl":"http://foursquare.com/img/categories/food/cafe.png"},"address":"170-172 Cuba Street","city":"Wellington","state":"New Zealand","zip":"6001","verified":false,"geolat":-41.29439586,"geolong":174.77494955,"stats":{"herenow":"0"},"distance":21},{"id":764925,"name":"St Pierre's Sushi","primarycategory":{"id":79104,"fullpathname":"Food:Sushi","nodename":"Sushi","iconurl":"http://foursquare.com/img/categories/food/sushi.png"},"address":"189 Cuba St","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.294546,"geolong":174.775158,"stats":{"herenow":"0"},"distance":18},{"id":306410,"name":"Duke Carvell's","primarycategory":{"id":79049,"fullpathname":"Food:Café","nodename":"Café","iconurl":"http://foursquare.com/img/categories/food/cafe.png"},"address":"6 Swan Lane","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.293972659109244,"geolong":174.7758400440216,"stats":{"herenow":"0"},"phone":"006443852240","distance":101},{"id":284078,"name":"Logan Brown Restaurant & Bar","primarycategory":{"id":79080,"fullpathname":"Food:Other - Food","nodename":"Other - Food","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"192 Cuba Street","crossstreet":"corner of Cuba and Vivian Street.","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.29483718861466,"geolong":174.77467596530914,"stats":{"herenow":"0"},"distance":35},{"id":587304,"name":"Simply Paris","primarycategory":{"id":79063,"fullpathname":"Food:French","nodename":"French","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"181 Cuba St","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.294337,"geolong":174.775295,"stats":{"herenow":"0"},"distance":40},{"id":284375,"name":"Cafe Istanbul","primarycategory":{"id":79077,"fullpathname":"Food:Middle Eastern","nodename":"Middle Eastern","iconurl":"http://foursquare.com/img/categories/food/middleeastern.png"},"address":"156 Cuba Street","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.294077451174694,"geolong":174.77514535188675,"stats":{"herenow":"0"},"distance":59},{"id":326169,"name":"Rasa","primarycategory":{"id":96350,"fullpathname":"Food:Malaysian","nodename":"Malaysian","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"200 Cuba St","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.295042,"geolong":174.774587,"stats":{"herenow":"0"},"distance":58},{"id":434597,"name":"Satay Palace","primarycategory":{"id":96350,"fullpathname":"Food:Malaysian","nodename":"Malaysian","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"165 Cuba St","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.293947,"geolong":174.775489,"stats":{"herenow":"0"},"distance":84},{"id":3737483,"name":"Roxy Café","primarycategory":{"id":79080,"fullpathname":"Food:Other - Food","nodename":"Other - Food","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"203 Cuba St","city":"Wellington","state":"Wellington","zip":"6011","verified":false,"geolat":-41.295073,"geolong":174.774874,"stats":{"herenow":"0"},"distance":53},{"id":317838,"name":"Peoples Coffee","primarycategory":{"id":79049,"fullpathname":"Food:Café","nodename":"Café","iconurl":"http://foursquare.com/img/categories/food/cafe.png"},"address":"13 Garrett St","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.293619,"geolong":174.774572,"stats":{"herenow":"0"},"twitter":"peoplescoffeenz","distance":112},{"id":284484,"name":"Slow Boat Records","primarycategory":{"id":79247,"fullpathname":"Shops:Record Shop","nodename":"Record Shop","iconurl":"http://foursquare.com/img/categories/shops/record_shop.png"},"address":"183 Cuba Street","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.29434950671261,"geolong":174.77524727582932,"stats":{"herenow":"0"},"distance":36},{"id":305098,"name":"Aunty Mena Vegetarian Cafe","primarycategory":{"id":79109,"fullpathname":"Food:Vegetarian / Vegan","nodename":"Vegetarian / Vegan","iconurl":"http://foursquare.com/img/categories/food/vegetarian.png"},"address":"167 Cuba St","crossstreet":"Ghuznee","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.293983,"geolong":174.775478,"stats":{"herenow":"0"},"distance":81},{"id":571658,"name":"Kayu Manis","primarycategory":{"id":96350,"fullpathname":"Food:Malaysian","nodename":"Malaysian","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"201 Cuba St","city":"Wellington","state":"Wellington","zip":"6011","verified":false,"geolat":-41.295001,"geolong":174.774908,"stats":{"herenow":"0"},"distance":45},{"id":6045971,"name":"Cobblestone Park","primarycategory":{"id":79194,"fullpathname":"Parks & Outdoors:Park","nodename":"Park","iconurl":"http://foursquare.com/img/categories/parks_outdoors/default.png"},"address":"139 Vivian St","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.295240228757145,"geolong":174.77577030658722,"stats":{"herenow":"0"},"distance":99},{"id":11286560,"name":"S & M's","primarycategory":{"id":79156,"fullpathname":"Nightlife:Cocktails / Mixology","nodename":"Cocktails / Mixology","iconurl":"http://foursquare.com/img/categories/nightlife/cocktails.png"},"address":"Cuba St","city":"","state":"","verified":false,"geolat":-41.294594,"geolong":174.774948,"stats":{"herenow":"0"},"distance":0},{"id":408221,"name":"Medusa Bar","primarycategory":{"id":79153,"fullpathname":"Nightlife:Bar","nodename":"Bar","iconurl":"http://foursquare.com/img/categories/nightlife/default.png"},"address":"154 Vivian Street","crossstreet":"btw Cuba and Taranaki","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.2948,"geolong":174.775336,"stats":{"herenow":"0"},"phone":"6443854361","distance":39},{"id":359738,"name":"Caffe Italiano","primarycategory":{"id":79069,"fullpathname":"Food:Italian","nodename":"Italian","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"229 Cuba St","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.295519,"geolong":174.774623,"stats":{"herenow":"0"},"phone":"6443852703","distance":106},{"id":6440033,"name":"Alc Headquarters","primarycategory":{"id":79209,"fullpathname":"Shops:Apparel:Boutique","nodename":"Boutique","iconurl":"http://foursquare.com/img/categories/shops/apparel.png"},"address":"","city":"","state":"","verified":false,"geolat":-41.294417,"geolong":174.775047,"stats":{"herenow":"0"},"distance":21},{"id":2148343,"name":"Rams","primarycategory":{"id":79042,"fullpathname":"Food:Asian","nodename":"Asian","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"171 Cuba St","city":"Wellington","state":"Wellington","zip":"6011","verified":false,"geolat":-41.294099618705424,"geolong":174.77541089057922,"stats":{"herenow":"0"},"distance":67},{"id":1219251,"name":"R & S Satay Noodle House","primarycategory":{"id":96350,"fullpathname":"Food:Malaysian","nodename":"Malaysian","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"148 Cuba St","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.29389406494968,"geolong":174.77523654699326,"stats":{"herenow":"0"},"distance":81},{"id":1409423,"name":"Comfort Hotel Wellington","primarycategory":{"id":79273,"fullpathname":"Travel:Hotel","nodename":"Hotel","iconurl":"http://foursquare.com/img/categories/travel/hotel.png"},"address":"213 Cuba St","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.29518,"geolong":174.774829,"stats":{"herenow":"0"},"distance":66},{"id":10489046,"name":"Alistair's Music","primarycategory":{"id":79243,"fullpathname":"Shops:Music / Instruments","nodename":"Music / Instruments","iconurl":"http://foursquare.com/img/categories/shops/music_instruments.png"},"address":"","city":"","state":"","verified":false,"geolat":-41.294706,"geolong":174.775,"stats":{"herenow":"0"},"distance":13},{"id":340567,"name":"Le Metropolitain","primarycategory":{"id":79063,"fullpathname":"Food:French","nodename":"French","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"146 Cuba Street","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.293661,"geolong":174.77535,"stats":{"herenow":"0"},"phone":"6448018007","distance":109},{"id":284591,"name":"Spacesuit","primarycategory":{"id":79208,"fullpathname":"Shops:Apparel","nodename":"Apparel","iconurl":"http://foursquare.com/img/categories/shops/apparel.png"},"address":"164 Cuba Street","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.2942913,"geolong":174.7751129,"stats":{"herenow":"0"},"distance":36},{"id":382635,"name":"Quality Hotel","primarycategory":{"id":79273,"fullpathname":"Travel:Hotel","nodename":"Hotel","iconurl":"http://foursquare.com/img/categories/travel/hotel.png"},"address":"233 Cuba Street","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.295609,"geolong":174.774566,"stats":{"herenow":"0"},"distance":117},{"id":335291,"name":"Chilli Marketing","primarycategory":{"id":79115,"fullpathname":"Home / Work / Other:Corporate / Office","nodename":"Corporate / Office","iconurl":"http://foursquare.com/img/categories/building/default.png"},"address":"Level 1, 170 Cuba St","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.29436965892954,"geolong":174.77484494447708,"stats":{"herenow":"0"},"distance":26},{"id":284422,"name":"Frutti","primarycategory":{"id":79208,"fullpathname":"Shops:Apparel","nodename":"Apparel","iconurl":"http://foursquare.com/img/categories/shops/apparel.png"},"address":"176 Cuba Street","city":"Wellington","state":"New Zealand","zip":"6001","verified":false,"geolat":-41.29455908946438,"geolong":174.77486371994019,"stats":{"herenow":"0"},"distance":7},{"id":902282,"name":"CQ Cafe","primarycategory":{"id":79049,"fullpathname":"Food:Café","nodename":"Café","iconurl":"http://foursquare.com/img/categories/food/cafe.png"},"address":"213 Cuba St","city":"Wellington","state":"","zip":"6011","verified":false,"geolat":-41.29554250722976,"geolong":174.77490663528442,"stats":{"herenow":"0"},"distance":105},{"id":475175,"name":"Babylon Kebab","primarycategory":{"id":79077,"fullpathname":"Food:Middle Eastern","nodename":"Middle Eastern","iconurl":"http://foursquare.com/img/categories/food/middleeastern.png"},"address":"193 Cuba St","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.29461,"geolong":174.775136,"stats":{"herenow":"0"},"distance":15},{"id":7489372,"name":"Ghuznee Street","address":"","city":"Wellington","state":"","verified":false,"geolat":-41.294574,"geolong":174.775288,"stats":{"herenow":"0"},"distance":28},{"id":8619979,"name":"Phoenician Cuisine","primarycategory":{"id":79061,"fullpathname":"Food:Falafel","nodename":"Falafel","iconurl":"http://foursquare.com/img/categories/food/default.png"},"address":"Cuba Street","crossstreet":"Abel Smith","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.293842,"geolong":174.775259,"stats":{"herenow":"0"},"distance":87},{"id":9645230,"name":"Parsonson Architects","primarycategory":{"id":79115,"fullpathname":"Home / Work / Other:Corporate / Office","nodename":"Corporate / Office","iconurl":"http://foursquare.com/img/categories/building/default.png"},"address":"181 Cuba Street","city":"Wellington","state":"","verified":false,"geolat":-41.294765,"geolong":174.77561,"stats":{"herenow":"0"},"phone":"6443842969","distance":58},{"id":4333454,"name":"Comrades","primarycategory":{"id":79208,"fullpathname":"Shops:Apparel","nodename":"Apparel","iconurl":"http://foursquare.com/img/categories/shops/apparel.png"},"address":"150 Cuba St","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.293849729740984,"geolong":174.7752070426941,"stats":{"herenow":"0"},"distance":85},{"id":284477,"name":"The Met Shop","primarycategory":{"id":114953,"fullpathname":"Shops:Gift Shop","nodename":"Gift Shop","iconurl":"http://foursquare.com/img/categories/shops/giftshop.png"},"address":"5 Swan Lane","city":"Wellington","state":"New Zealand","zip":"6011","verified":false,"geolat":-41.293946461066575,"geolong":174.77573812007904,"stats":{"herenow":"0"},"distance":97},{"id":610845,"name":"Wellington Trawling Sea Market","primarycategory":{"id":79233,"fullpathname":"Shops:Food & Drink:Fish Market","nodename":"Fish Market","iconurl":"http://foursquare.com/img/categories/shops/fish_market.png"},"address":"220 Cuba Street","city":"Wellington","state":"","verified":false,"geolat":-41.29550220351435,"geolong":174.77426290512085,"stats":{"herenow":"0"},"distance":116},{"id":489254,"name":"Lavage","primarycategory":{"id":79216,"fullpathname":"Shops:Beauty / Cosmetic","nodename":"Beauty / Cosmetic","iconurl":"http://foursquare.com/img/categories/shops/beauty_cosmetic.png"},"address":"174 Cuba St","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.294469,"geolong":174.774905,"stats":{"herenow":"0"},"distance":14},{"id":5830903,"name":"Madame Fancy Pants","primarycategory":{"id":114953,"fullpathname":"Shops:Gift Shop","nodename":"Gift Shop","iconurl":"http://foursquare.com/img/categories/shops/giftshop.png"},"address":"217 Cuba St","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.29525433511703,"geolong":174.77475374937057,"stats":{"herenow":"0"},"distance":75},{"id":9166211,"name":"Uni stop","address":"45 Kelburn parade","city":"Wellington","state":"","verified":false,"geolat":-41.294127,"geolong":174.774144,"stats":{"herenow":"0"},"distance":84},{"id":2146737,"name":"Wildilocks @ Cuba","primarycategory":{"id":79248,"fullpathname":"Shops:Salon / Barbershop","nodename":"Salon / Barbershop","iconurl":"http://foursquare.com/img/categories/shops/salon_barber.png"},"address":"225 Cuba St","crossstreet":"btw Vivian & Abel Smith","city":"Wellington","state":"New Zealand","zip":"6011","verified":true,"geolat":-41.295438,"geolong":174.774666,"stats":{"herenow":"0"},"distance":96},{"id":5247691,"name":"Carly Harris","primarycategory":{"id":79209,"fullpathname":"Shops:Apparel:Boutique","nodename":"Boutique","iconurl":"http://foursquare.com/img/categories/shops/apparel.png"},"address":"154 Cuba St","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.294006,"geolong":174.775156,"stats":{"herenow":"0"},"distance":67},{"id":340124,"name":"Miss Demeanour","primarycategory":{"id":79208,"fullpathname":"Shops:Apparel","nodename":"Apparel","iconurl":"http://foursquare.com/img/categories/shops/apparel.png"},"address":"160 Cuba Street","city":"Wellington","state":"New Zealand","verified":false,"geolat":-41.294169,"geolong":174.775077,"stats":{"herenow":"0"},"phone":"6448033590","distance":48},{"id":284131,"name":"Vegetarian Cafe","primarycategory":{"id":79049,"fullpathname":"Food:Café","nodename":"Café","iconurl":"http://foursquare.com/img/categories/food/cafe.png"},"address":"179 Cuba Street","city":"Wellington","state":"New Zealand","zip":"6001","verified":false,"geolat":-41.2942689,"geolong":174.77533579,"stats":{"herenow":"0"},"distance":48},{"id":4399095,"name":"Wellington Eye Centre","primarycategory":{"id":79134,"fullpathname":"Home / Work / Other:Medical","nodename":"Medical","iconurl":"http://foursquare.com/img/categories/building/medical.png"},"address":"148 Cuba Street","city":"Wellington","state":"","verified":false,"geolat":-41.29385376021574,"geolong":174.77507829666138,"stats":{"herenow":"0"},"distance":83}];
View
BIN  images/ajax-loader.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  images/form-check-off.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  images/form-check-on.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  images/form-radio-off.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  images/form-radio-on.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  images/icon-search-black.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  images/icons-18-black.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  images/icons-18-white.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  images/icons-36-black.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  images/icons-36-white.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
44 index.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Backbone Mobile</title>
+ <meta name="viewport" content="width=default-width; user-scalable=no" />
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+
+ <script type="text/javascript" charset="utf-8" src="jquery.js"></script>
+ <script type="text/javascript" charset="utf-8" src="jquery-mobile.js"></script>
+ <script type="text/javascript" charset="utf-8" src="underscore.js"></script>
+ <script type="text/javascript" charset="utf-8" src="backbone.js"></script>
+ <script type="text/javascript" charset="utf-8" src="foursquare.json"></script>
+ <script type="text/javascript" charset="utf-8" src="application.js"></script>
+
+ <link rel="stylesheet" href="jquery-mobile.css" />
+
+ <script type="text/javascript" charset="utf-8">
+ function onBodyLoad(){
+ document.addEventListener("deviceready",onDeviceReady,false);
+ }
+
+ function onDeviceReady(){
+ }
+ </script>
+
+</head>
+ <body onload="onBodyLoad()">
+
+ <div data-role="page" id="home">
+
+ <div data-role="header">
+ <h1>Backbone Mobile</h1>
+ </div>
+
+ <div data-role="content">
+ </div>
+
+ <div data-role="footer">
+ <h4><a rel="external" href="http://bennolan.com/">@bnolan</a></h4>
+ </div>
+ </div>
+
+ </body>
+</html>
View
866 jquery-mobile.css
@@ -0,0 +1,866 @@
+/*!
+ * jQuery Mobile v1.0a2
+ * http://jquerymobile.com/
+ *
+ * Copyright 2010, jQuery Project
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+* Note: Code is in draft form and is subject to change
+*/
+
+
+
+/* theme bar,body,btn containers
+----------------------------------*/
+.ui-bar-a { border: 1px solid #2A2A2A; background: #111111; color: #fff; font-weight: bold; text-shadow: 0 -1px 1px #000; background-image: -moz-linear-gradient(top, #3c3c3c, #111111); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #3c3c3c),color-stop(1, #111111)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#3c3c3c', EndColorStr='#111111')"; }
+.ui-bar-a, .ui-bar-a input, .ui-bar-a select, .ui-bar-a textarea, .ui-bar-a button { font-family: Helvetica, Arial, sans-serif; }
+.ui-bar-a .ui-link-inherit { color: #fff; }
+.ui-bar-a .ui-link { color: #7cc4e7; font-weight: bold; }
+
+.ui-body-a { border: 1px solid #2A2A2A; background: #222222; color: #fff; text-shadow: 0 1px 0 #000; font-weight: normal; background-image: -moz-linear-gradient(top, #666666, #222222); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #666666),color-stop(1, #222222)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#666666', EndColorStr='#222222)')"; }
+.ui-body-a, .ui-body-a input, .ui-body-a select, .ui-body-a textarea, .ui-body-a button { font-family: Helvetica, Arial, sans-serif; }
+.ui-body-a .ui-link-inherit { color: #fff; }
+.ui-body-a .ui-link { color: #2489CE; font-weight: bold; }
+.ui-br { border-bottom: 1px solid rgba(130,130,130,.3); }
+
+.ui-btn-up-a { border: 1px solid #222; background: #333333; font-weight: bold; color: #fff; cursor: pointer; text-shadow: 0 -1px 1px #000; text-decoration: none; background-image: -moz-linear-gradient(top, #555555, #333333); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #555555),color-stop(1, #333333)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#555555', EndColorStr='#333333')"; }
+.ui-btn-up-a a.ui-link-inherit { color: #fff; }
+.ui-btn-hover-a { border: 1px solid #000; background: #444444; font-weight: bold; color: #fff; text-shadow: 0 -1px 1px #000; text-decoration: none; background-image: -moz-linear-gradient(top, #666666, #444444); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #666666),color-stop(1, #444444)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#666666', EndColorStr='#444444')"; }
+.ui-btn-hover-a a.ui-link-inherit { color: #fff; }
+.ui-btn-down-a { border: 1px solid #000; background: #3d3d3d; font-weight: bold; color: #fff; text-shadow: 0 -1px 1px #000; background-image: -moz-linear-gradient(top, #333333, #5a5a5a); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #333333),color-stop(1, #5a5a5a)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#333333', EndColorStr='#5a5a5a')"; }
+.ui-btn-down-a a.ui-link-inherit { color: #fff; }
+.ui-btn-up-a, .ui-btn-hover-a, .ui-btn-down-a { font-family: Helvetica, Arial, sans-serif; }
+
+
+
+
+.ui-bar-b { border: 1px solid #456f9a; background: #5e87b0; color: #fff; font-weight: bold; text-shadow: 0 -1px 1px #254f7a; background-image: -moz-linear-gradient(top, #81a8ce, #5e87b0); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #81a8ce),color-stop(1, #5e87b0)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#81a8ce', EndColorStr='#5e87b0')"; }
+.ui-bar-b, .ui-bar-b input, .ui-bar-b select, .ui-bar-b textarea, .ui-bar-b button { font-family: Helvetica, Arial, sans-serif; }
+.ui-bar-b .ui-link-inherit { color: #fff; }
+.ui-bar-b .ui-link { color: #7cc4e7; font-weight: bold; }
+
+.ui-body-b { border: 1px solid #C6C6C6; background: #cccccc; color: #333333; text-shadow: 0 1px 0 #fff; font-weight: normal; background-image: -moz-linear-gradient(top, #e6e6e6, #cccccc); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #e6e6e6),color-stop(1, #cccccc)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#e6e6e6', EndColorStr='#cccccc')"; }
+.ui-body-b, .ui-body-b input, .ui-body-b select, .ui-body-b textarea, .ui-body-b button { font-family: Helvetica, Arial, sans-serif; }
+.ui-body-b .ui-link-inherit { color: #333333; }
+.ui-body-b .ui-link { color: #2489CE; font-weight: bold; }
+
+.ui-btn-up-b { border: 1px solid #145072; background: #2567ab; font-weight: bold; color: #fff; cursor: pointer; text-shadow: 0 -1px 1px #145072; text-decoration: none; background-image: -moz-linear-gradient(top, #4e89c5, #2567ab); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #5f9cc5),color-stop(1, #396b9e)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#4e89c5', EndColorStr='#2567ab')"; }
+.ui-btn-up-b a.ui-link-inherit { color: #fff; }
+.ui-btn-hover-b { border: 1px solid #00516e; background: #4b88b6; font-weight: bold; color: #fff; text-shadow: 0 -1px 1px #014D68; background-image: -moz-linear-gradient(top, #72b0d4, #4b88b6); text-decoration: none; background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #72b0d4),color-stop(1, #4b88b6)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#72b0d4', EndColorStr='#4b88b6')"; }
+.ui-btn-hover-b a.ui-link-inherit { color: #fff; }
+.ui-btn-down-b { border: 1px solid #225377; background: #4e89c5; font-weight: bold; color: #fff; text-shadow: 0 -1px 1px #225377; background-image: -moz-linear-gradient(top, #396b9e, #4e89c5); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #396b9e),color-stop(1, #4e89c5)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#396b9e', EndColorStr='#4e89c5')"; }
+.ui-btn-down-b a.ui-link-inherit { color: #fff; }
+.ui-btn-up-b, .ui-btn-hover-b, .ui-btn-down-b { font-family: Helvetica, Arial, sans-serif; }
+
+
+
+
+.ui-bar-c { border: 1px solid #B3B3B3; background: #e9eaeb; color: #3E3E3E; font-weight: bold; text-shadow: 0 1px 1px #fff; background-image: -moz-linear-gradient(top, #f0f0f0, #e9eaeb); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #f0f0f0),color-stop(1, #e9eaeb)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#f0f0f0', EndColorStr='#e9eaeb')"; }
+.ui-bar-c, .ui-bar-c input, .ui-bar-c select, .ui-bar-c textarea, .ui-bar-c button { font-family: Helvetica, Arial, sans-serif; }
+
+.ui-body-c { border: 1px solid #B3B3B3; color: #333333; text-shadow: 0 1px 0 #fff; background: #f0f0f0; background-image: -moz-linear-gradient(top, #fff, #f0f0f0); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fff),color-stop(1, #f0f0f0)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#f0f0f0')"; }
+.ui-body-c, .ui-body-c input, .ui-body-c select, .ui-body-c textarea, .ui-body-c button { font-family: Helvetica, Arial, sans-serif; }
+.ui-body-c .ui-link-inherit { color: #333333; }
+.ui-body-c .ui-link { color: #2489CE; font-weight: bold; }
+
+.ui-btn-up-c { border: 1px solid #ccc; background: #eee; font-weight: bold; color: #444; cursor: pointer; text-shadow: 0 1px 1px #f6f6f6; text-decoration: none; background-image: -moz-linear-gradient(top, #fdfdfd, #eeeeee); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fdfdfd),color-stop(1, #eeeeee)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fdfdfd', EndColorStr='#eeeeee')"; }
+.ui-btn-up-c a.ui-link-inherit { color: #2F3E46; }
+
+.ui-btn-hover-c { border: 1px solid #aaa; background: #f5f5f5; font-weight: bold; color: #111111; text-decoration: none; text-shadow: 0 1px 1px #fff; background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #ffffff),color-stop(1, #f5f5f5)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#f5f5f5')"; }
+.ui-btn-hover-c a.ui-link-inherit { color: #2F3E46; }
+
+.ui-btn-down-c { border: 1px solid #808080; background: #fdfdfd; font-weight: bold; color: #111111; text-shadow: 0 1px 1px #ffffff; background-image: -moz-linear-gradient(top, #eeeeee, #fdfdfd); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #eeeeee),color-stop(1, #fdfdfd)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee', EndColorStr='#fdfdfd')"; }
+.ui-btn-down-c a.ui-link-inherit { color: #2F3E46; }
+.ui-btn-up-c, .ui-btn-hover-c, .ui-btn-down-c { font-family: Helvetica, Arial, sans-serif; }
+
+
+.ui-bar-d { border: 1px solid #ccc; background: #bbb; color: #333; text-shadow: 0 1px 0 #eee; background-image: -moz-linear-gradient(top, #ddd, #bbb); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #ddd),color-stop(1, #bbb)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ddd', EndColorStr='#bbb')"; }
+.ui-bar-d, .ui-bar-d input, .ui-bar-d select, .ui-bar-d textarea, .ui-bar-d button { font-family: Helvetica, Arial, sans-serif; }
+.ui-bar-d .ui-link-inherit { color: #333; }
+.ui-bar-d .ui-link { color: #2489CE; font-weight: bold; }
+
+.ui-body-d { border: 1px solid #ccc; color: #333333; text-shadow: 0 1px 0 #fff; background: #ffffff; }
+.ui-body-d, .ui-body-d input, .ui-body-d select, .ui-body-d textarea, .ui-body-d button { font-family: Helvetica, Arial, sans-serif; }
+.ui-body-d .ui-link-inherit { color: #333333; }
+.ui-body-d .ui-link { color: #2489CE; font-weight: bold; }
+
+.ui-btn-up-d { border: 1px solid #ccc; background: #fff; font-weight: bold; color: #444; text-decoration: none; text-shadow: 0 1px 1px #fff; }
+.ui-btn-up-d a.ui-link-inherit { color: #333; }
+.ui-btn-hover-d { border: 1px solid #aaa; background: #eeeeee; font-weight: bold; color: #222; cursor: pointer; text-shadow: 0 1px 1px #fff; text-decoration: none; background-image: -moz-linear-gradient(top, #fdfdfd, #eeeeee); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fdfdfd),color-stop(1, #eeeeee)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fdfdfd', EndColorStr='#eeeeee')"; }
+.ui-btn-hover-d a.ui-link-inherit { color: #222; }
+
+.ui-btn-down-d { border: 1px solid #aaaaaa; background: #ffffff; font-weight: bold; color: #111; text-shadow: 0 1px 1px #ffffff; background-image: -moz-linear-gradient(top, #eeeeee, #ffffff); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #eeeeee),color-stop(1, #ffffff)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee', EndColorStr='#ffffff')"; }
+.ui-btn-down-d a.ui-link-inherit { border: 1px solid #808080; background: #ced0d2; font-weight: bold; color: #111; text-shadow: none; background-image: -moz-linear-gradient(top, #cccccc, #eeeeee); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #cccccc),color-stop(1, #eeeeee)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#cccccc', EndColorStr='#eeeeee')"; }
+.ui-btn-up-d, .ui-btn-hover-d, .ui-btn-down-d { font-family: Helvetica, Arial, sans-serif; }
+
+
+.ui-bar-e { border: 1px solid #F7C942; background: #fadb4e; color: #333; text-shadow: 0 1px 0 #fff; background-image: -moz-linear-gradient(top, #fceda7, #fadb4e); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fceda7),color-stop(1, #fadb4e)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fceda7', EndColorStr='#fadb4e')"; }
+.ui-bar-e, .ui-bar-e input, .ui-bar-e select, .ui-bar-e textarea, .ui-bar-d button { font-family: Helvetica, Arial, sans-serif; }
+.ui-bar-e .ui-link-inherit { color: #333; }
+.ui-bar-e .ui-link { color: #2489CE; font-weight: bold; }
+
+.ui-body-e { border: 1px solid #F7C942; color: #333333; text-shadow: 0 1px 0 #fff; background: #faeb9e; background-image: -moz-linear-gradient(top, #fff, #faeb9e); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fff),color-stop(1, #faeb9e)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#faeb9e')"; }
+.ui-body-e, .ui-body-e input, .ui-body-e select, .ui-body-e textarea, .ui-body-e button { font-family: Helvetica, Arial, sans-serif; }
+.ui-body-e .ui-link-inherit { color: #333333; }
+.ui-body-e .ui-link { color: #2489CE; font-weight: bold; }
+
+
+.ui-btn-up-e { border: 1px solid #F7C942; background: #fadb4e; font-weight: bold; color: #333; cursor: pointer; text-shadow: 0 1px 1px #fe3; text-decoration: none; text-shadow: 0 1px 0 #fff; background-image: -moz-linear-gradient(top, #fceda7, #fadb4e); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fceda7),color-stop(1, #fadb4e)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fceda7', EndColorStr='#fadb4e')"; }
+.ui-btn-up-e a.ui-link-inherit { color: #333; }
+
+.ui-btn-hover-e { border: 1px solid #e79952; background: #fbe26f; font-weight: bold; color: #111; text-decoration: none; text-shadow: 0 1px 1px #fff; background-image: -moz-linear-gradient(top, #fcf0b5, #fbe26f); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fcf0b5),color-stop(1, #fbe26f)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fcf0b5', EndColorStr='#fbe26f')"; }
+
+.ui-btn-hover-e a.ui-link-inherit { color: #333; }
+.ui-btn-down-e { border: 1px solid #F7C942; background: #fceda7; font-weight: bold; color: #111; text-shadow: 0 1px 1px #ffffff; background-image: -moz-linear-gradient(top, #fadb4e, #fceda7); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fadb4e),color-stop(1, #fceda7)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fadb4e', EndColorStr='#fceda7')"; }
+.ui-btn-down-e a.ui-link-inherit { color: #333; }
+.ui-btn-up-e, .ui-btn-hover-e, .ui-btn-down-e { font-family: Helvetica, Arial, sans-serif; }
+
+
+/* links within "buttons" */
+a.ui-link-inherit { text-decoration: none !important; }
+
+/* Active class used as the "on" state across all themes */
+.ui-btn-active { border: 1px solid #155678; background: #4596ce; font-weight: bold; color: #fff; cursor: pointer; text-shadow: 0 -1px 1px #145072; text-decoration: none; background-image: -moz-linear-gradient(top, #85bae4, #5393c5); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #85bae4),color-stop(1, #5393c5)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#85bae4', EndColorStr='#5393c5')"; }
+.ui-btn-active a.ui-link-inherit { color: #fff; }
+
+/* button inner top highlight */
+.ui-btn-inner { border-top: 1px solid #fff; border-color: rgba(255,255,255,.3); }
+
+
+/* Container Corner radius */
+.ui-corner-tl { -moz-border-radius-topleft: .6em; -webkit-border-top-left-radius: .6em; border-top-left-radius: .6em; }
+.ui-corner-tr { -moz-border-radius-topright: .6em; -webkit-border-top-right-radius: .6em; border-top-right-radius: .6em; }
+.ui-corner-bl { -moz-border-radius-bottomleft: .6em; -webkit-border-bottom-left-radius: .6em; border-bottom-left-radius: .6em; }
+.ui-corner-br { -moz-border-radius-bottomright: .6em; -webkit-border-bottom-right-radius: .6em; border-bottom-right-radius: .6em; }
+.ui-corner-top { -moz-border-radius-topleft: .6em; -webkit-border-top-left-radius: .6em; border-top-left-radius: .6em; -moz-border-radius-topright: .6em; -webkit-border-top-right-radius: .6em; border-top-right-radius: .6em; }
+.ui-corner-bottom { -moz-border-radius-bottomleft: .6em; -webkit-border-bottom-left-radius: .6em; border-bottom-left-radius: .6em; -moz-border-radius-bottomright: .6em; -webkit-border-bottom-right-radius: .6em; border-bottom-right-radius: .6em; }
+.ui-corner-right { -moz-border-radius-topright: .6em; -webkit-border-top-right-radius: .6em; border-top-right-radius: .6em; -moz-border-radius-bottomright: .6em; -webkit-border-bottom-right-radius: .6em; border-bottom-right-radius: .6em; }
+.ui-corner-left { -moz-border-radius-topleft: .6em; -webkit-border-top-left-radius: .6em; border-top-left-radius: .6em; -moz-border-radius-bottomleft: .6em; -webkit-border-bottom-left-radius: .6em; border-bottom-left-radius: .6em; }
+.ui-corner-all { -moz-border-radius: .6em; -webkit-border-radius: .6em; border-radius: .6em; }
+
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-disabled { cursor: default !important; opacity: .3; }
+
+/* Icons
+----------------------------------*/
+/* .ui-icon { background-position: 50% 50%; background-repeat: no-repeat; background-color: #fff; background-color: rgba(0,0,0,.4); -moz-border-radius: 9px; -webkit-border-radius: 9px; border-radius: 9px; } */
+
+.ui-icon { background-image: url(images/icons-18-white.png); background-repeat: no-repeat; background-color: #666; background-color: rgba(0,0,0,.4); -moz-border-radius: 9px; -webkit-border-radius: 9px; border-radius: 9px; }
+.ui-icon-disc { background-color: #666; background-color: rgba(0,0,0,.3); -moz-border-radius: 9px; -webkit-border-radius: 9px; border-radius: 9px; }
+
+/* alt color */
+.ui-icon-black { background-image: url(images/icons-18-black.png); }
+.ui-icon-black-disc { background-color: #fff; background-color: rgba(255,255,255,.3); -moz-border-radius: 9px; -webkit-border-radius: 9px; border-radius: 9px; }
+
+/* retina */
+@media screen and (-webkit-min-device-pixel-ratio: 2), screen and (max--moz-device-pixel-ratio: 2) {
+ .ui-icon { background-image: url(images/icons-36-white.png); background-size: 558px 18px; }
+ .ui-icon-black { background-image: url(images/icons-36-black.png); }
+}
+
+/*plus minus*/
+.ui-icon-plus { background-position: -0 0; }
+.ui-icon-minus { background-position: -36px 0; }
+
+/* delete/close */
+.ui-icon-delete { background-position: -72px 0; }
+
+/*arrows*/
+.ui-icon-arrow-r { background-position: -108px 0; }
+.ui-icon-arrow-l { background-position: -144px 0; }
+.ui-icon-arrow-u { background-position: -180px 0; }
+.ui-icon-arrow-d { background-position: -216px 0; }
+
+.ui-icon-check { background-position: -252px 0; }
+.ui-icon-gear { background-position: -288px 0; }
+.ui-icon-refresh { background-position: -324px 0; }
+.ui-icon-forward { background-position: -360px 0; }
+.ui-icon-back { background-position: -396px 0; }
+
+.ui-icon-grid { background-position: -432px 0; }
+.ui-icon-star { background-position: -468px 0; }
+.ui-icon-alert { background-position: -504px 0; }
+.ui-icon-info { background-position: -540px 0; }
+
+/*checks,radios*/
+.ui-icon-checkbox-off,
+.ui-icon-checkbox-on,
+.ui-icon-radio-off,
+.ui-icon-radio-on { background-color: transparent; -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; background-size: 20px 20px; }
+
+.ui-icon-checkbox-off { background-image: url(images/form-check-off.png); }
+.ui-icon-checkbox-on { background-image: url(images/form-check-on.png); }
+.ui-icon-radio-off { background-image: url(images/form-radio-off.png);}
+.ui-icon-radio-on { background-image: url(images/form-radio-on.png); }
+
+.ui-icon-search { background-image: url(images/icon-search-black.png); background-size: 16px 16px; }
+
+/* loading icon */
+.ui-icon-loading { background-image: url(images/ajax-loader.png); width: 40px; height: 40px; -moz-border-radius: 20px; -webkit-border-radius: 20px; border-radius: 20px; background-size: 35px 35px; }
+
+/* btn Corner radius */
+.ui-btn-corner-tl { -moz-border-radius-topleft: 1em; -webkit-border-top-left-radius: 1em; border-top-left-radius: 1em; }
+.ui-btn-corner-tr { -moz-border-radius-topright: 1em; -webkit-border-top-right-radius: 1em; border-top-right-radius: 1em; }
+.ui-btn-corner-bl { -moz-border-radius-bottomleft: 1em; -webkit-border-bottom-left-radius: 1em; border-bottom-left-radius: 1em; }
+.ui-btn-corner-br { -moz-border-radius-bottomright: 1em; -webkit-border-bottom-right-radius: 1em; border-bottom-right-radius: 1em; }
+.ui-btn-corner-top { -moz-border-radius-topleft: 1em; -webkit-border-top-left-radius: 1em; border-top-left-radius: 1em; -moz-border-radius-topright: 1em; -webkit-border-top-right-radius: 1em; border-top-right-radius: 1em; }
+.ui-btn-corner-bottom { -moz-border-radius-bottomleft: 1em; -webkit-border-bottom-left-radius: 1em; border-bottom-left-radius: 1em; -moz-border-radius-bottomright: 1em; -webkit-border-bottom-right-radius: 1em; border-bottom-right-radius: 1em; }
+.ui-btn-corner-right { -moz-border-radius-topright: 1em; -webkit-border-top-right-radius: 1em; border-top-right-radius: 1em; -moz-border-radius-bottomright: 1em; -webkit-border-bottom-right-radius: 1em; border-bottom-right-radius: 1em; }
+.ui-btn-corner-left { -moz-border-radius-topleft: 1em; -webkit-border-top-left-radius: 1em; border-top-left-radius: 1em; -moz-border-radius-bottomleft: 1em; -webkit-border-bottom-left-radius: 1em; border-bottom-left-radius: 1em; }
+.ui-btn-corner-all { -moz-border-radius: 1em; -webkit-border-radius: 1em; border-radius: 1em;}
+
+/* radius clip */
+.ui-corner-tl, .ui-corner-tr, .ui-corner-bl,
+.ui-corner-br, .ui-corner-top, .ui-corner-bottom,
+.ui-corner-right, .ui-corner-left, .ui-corner-all,
+.ui-btn-corner-tl, .ui-btn-corner-tr, .ui-btn-corner-bl,
+.ui-btn-corner-br, .ui-btn-corner-top, .ui-btn-corner-bottom,
+.ui-btn-corner-right, .ui-btn-corner-left, .ui-btn-corner-all {
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding-box;
+ background-clip: padding-box;
+}
+
+/* Overlays */
+.ui-overlay { background: #666; opacity: .5; filter:Alpha(Opacity=50); position: absolute; width: 100%; height: 100%; }
+.ui-overlay-shadow { -moz-box-shadow: 0px 0px 12px rgba(0,0,0,.6); -webkit-box-shadow: 0px 0px 12px rgba(0,0,0,.6); box-shadow: 0px 0px 12px rgba(0,0,0,.6); }
+
+.ui-shadow { -moz-box-shadow: 0px 1px 4px rgba(0,0,0,.3); -webkit-box-shadow: 0px 1px 4px rgba(0,0,0,.3); box-shadow: 0px 1px 4px rgba(0,0,0,.3); }
+.ui-bar-a .ui-shadow, .ui-bar-b .ui-shadow , .ui-bar-c .ui-shadow { -moz-box-shadow: 0px 1px 0 rgba(255,255,255,.3); -webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.3); box-shadow: 0px 1px 0 rgba(255,255,255,.3); }
+.ui-shadow-inset { -moz-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); -webkit-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); }
+.ui-icon-shadow { -moz-box-shadow: 0px 1px 0 rgba(255,255,255,.4); -webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.4); box-shadow: 0px 1px 0 rgba(255,255,255,.4); }
+
+/* set focus state last */
+.ui-focus { outline-width: 0; -moz-box-shadow: 0px 0px 12px #387bbe; -webkit-box-shadow: 0px 0px 12px #387bbe; box-shadow: 0px 0px 12px #387bbe; }
+
+/* unset box shadow in browsers that don't do it right */
+.ui-mobile-nosupport-boxshadow * { -moz-box-shadow: none !important; -webkit-box-shadow: none !important; box-shadow: none !important; }
+.ui-mobile-nosupport-boxshadow .ui-focus { outline-width: 2px; }
+/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+* Note: Code is in draft form and is subject to change
+*/
+
+/* some unsets - more probably needed */
+.ui-mobile fieldset, .ui-page { padding: 0; margin: 0; }
+.ui-mobile a img, .ui-mobile fieldset { border: 0; }
+
+/* responsive page widths */
+.ui-mobile-viewport { margin: 0; overflow-x: hidden; -webkit-text-size-adjust: none; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
+
+/*orientations from js are available */
+.portrait { }
+.landscape { }
+
+/* "page" containers - full-screen views, one should always be in view post-pageload */
+.ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; }
+.ui-page-active { display: block; overflow: visible; min-height: 100%; }
+
+/* loading screen */
+.ui-loading .ui-mobile-viewport { overflow: hidden !important; }
+.ui-loading .ui-loader { display: block; }
+.ui-loading .ui-page { overflow: hidden; }
+.ui-loader { display: none; position: absolute; opacity: .85; z-index: 10; top: 75px; left: 50%; width: 200px; margin-left: -130px; padding: 20px 30px; }
+.ui-loader h1 { font-size: 15px; text-align: center; }
+.ui-loader .ui-icon { position: static; display: block; opacity: .9; margin: 0 auto; width: 35px; height: 35px; background-color: transparent; }
+
+/*fouc*/
+.ui-mobile-rendering > * { visibility: hidden; }
+
+/*headers, content panels*/
+.ui-bar, .ui-body { position: relative; padding: .4em 15px; overflow: hidden; display: block; clear:both; }
+.ui-bar { font-size: 16px; margin: 0; }
+.ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 16px; display: inline-block; }
+
+.ui-header, .ui-footer { display: block; }
+.ui-page .ui-header, .ui-page .ui-footer { position: relative; }
+.ui-header .ui-btn-left { position: absolute; left: 10px; top: .4em; }
+.ui-header .ui-title, .ui-footer .ui-title { text-align: center; font-size: 16px; display: block; margin: .6em 90px .8em; padding: 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; }
+.ui-header .ui-btn-right { position: absolute; right: 10px; top: .4em; }
+
+/*content area*/
+.ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 15px; }
+.ui-page-fullscreen .ui-content { padding:0; }
+
+/* icons sizing */
+.ui-icon { width: 18px; height: 18px; }
+
+/* fullscreen class on ui-content div */
+.ui-fullscreen { }
+.ui-fullscreen img { max-width: 100%; }
+
+/* non-js content hiding */
+.ui-nojs { position: absolute; left: -9999px; }
+/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.spin {
+ -webkit-transform: rotate(360deg);
+ -webkit-animation-name: spin;
+ -webkit-animation-duration: 1s;
+ -webkit-animation-iteration-count: infinite;
+}
+@-webkit-keyframes spin {
+ from {-webkit-transform: rotate(0deg);}
+ to {-webkit-transform: rotate(360deg);}
+}
+
+/* Transitions from jQtouch (with small modifications): http://www.jqtouch.com/
+Built by David Kaneda and maintained by Jonathan Stark.
+*/
+.in, .out {
+ -webkit-animation-timing-function: ease-in-out;
+ -webkit-animation-duration: 350ms;
+}
+
+.slide.in {
+ -webkit-transform: translateX(0);
+ -webkit-animation-name: slideinfromright;
+}
+
+.slide.out {
+ -webkit-transform: translateX(-100%);
+ -webkit-animation-name: slideouttoleft;
+}
+
+.slide.in.reverse {
+ -webkit-transform: translateX(0);
+ -webkit-animation-name: slideinfromleft;
+}
+
+.slide.out.reverse {
+ -webkit-transform: translateX(100%);
+ -webkit-animation-name: slideouttoright;
+}
+
+.slideup.in {
+ -webkit-transform: translateY(0);
+ -webkit-animation-name: slideinfrombottom;
+ z-index: 10;
+}
+
+.slideup.out {
+ -webkit-animation-name: dontmove;
+ z-index: 0;
+}
+
+.slideup.out.reverse {
+ -webkit-transform: translateY(100%);
+ z-index: 10;
+ -webkit-animation-name: slideouttobottom;
+}
+
+.slideup.in.reverse {
+ z-index: 0;
+ -webkit-animation-name: dontmove;
+}
+.slidedown.in {
+ -webkit-transform: translateY(0);
+ -webkit-animation-name: slideinfromtop;
+ z-index: 10;
+}
+
+.slidedown.out {
+ -webkit-animation-name: dontmove;
+ z-index: 0;
+}
+
+.slidedown.out.reverse {
+ -webkit-transform: translateY(-100%);
+ z-index: 10;
+ -webkit-animation-name: slideouttotop;
+}
+
+.slidedown.in.reverse {
+ z-index: 0;
+ -webkit-animation-name: dontmove;
+}
+
+@-webkit-keyframes slideinfromright {
+ from { -webkit-transform: translateX(100%); }
+ to { -webkit-transform: translateX(0); }
+}
+
+@-webkit-keyframes slideinfromleft {
+ from { -webkit-transform: translateX(-100%); }
+ to { -webkit-transform: translateX(0); }
+}
+
+@-webkit-keyframes slideouttoleft {
+ from { -webkit-transform: translateX(0); }
+ to { -webkit-transform: translateX(-100%); }
+}
+
+@-webkit-keyframes slideouttoright {
+ from { -webkit-transform: translateX(0); }
+ to { -webkit-transform: translateX(100%); }
+}
+
+
+@-webkit-keyframes slideinfromtop {
+ from { -webkit-transform: translateY(-100%); }
+ to { -webkit-transform: translateY(0); }
+}
+
+@-webkit-keyframes slideinfrombottom {
+ from { -webkit-transform: translateY(100%); }
+ to { -webkit-transform: translateY(0); }
+}
+
+@-webkit-keyframes slideouttobottom {
+ from { -webkit-transform: translateY(0); }
+ to { -webkit-transform: translateY(100%); }
+}
+
+@-webkit-keyframes slideouttotop {
+ from { -webkit-transform: translateY(0); }
+ to { -webkit-transform: translateY(-100%); }
+}
+@-webkit-keyframes fadein {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+@-webkit-keyframes fadeout {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+
+.fade.in {
+ opacity: 1;
+ z-index: 10;
+ -webkit-animation-name: fadein;
+}
+.fade.out {
+ z-index: 0;
+}
+
+/* The properties in this body rule are only necessary for the 'flip' transition.
+ * We need specify the perspective to create a projection matrix. This will add
+ * some depth as the element flips. The depth number represents the distance of
+ * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate
+ * value.
+ */
+.ui-mobile-viewport-transitioning {
+ -webkit-perspective: 1000;
+ position: absolute;
+}
+
+.ui-mobile-viewport-transitioning,
+.ui-mobile-viewport-transitioning .ui-page {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+
+.flip {
+ -webkit-animation-duration: .65s;
+ -webkit-backface-visibility:hidden;
+ -webkit-transform:translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */
+}
+
+.flip.in {
+ -webkit-transform: rotateY(0) scale(1);
+ -webkit-animation-name: flipinfromleft;
+}
+
+.flip.out {
+ -webkit-transform: rotateY(-180deg) scale(.8);
+ -webkit-animation-name: flipouttoleft;
+}
+
+/* Shake it all about */
+
+.flip.in.reverse {
+ -webkit-transform: rotateY(0) scale(1);
+ -webkit-animation-name: flipinfromright;
+}
+
+.flip.out.reverse {
+ -webkit-transform: rotateY(180deg) scale(.8);
+ -webkit-animation-name: flipouttoright;
+}
+
+@-webkit-keyframes flipinfromright {
+ from { -webkit-transform: rotateY(-180deg) scale(.8); }
+ to { -webkit-transform: rotateY(0) scale(1); }
+}
+
+@-webkit-keyframes flipinfromleft {
+ from { -webkit-transform: rotateY(180deg) scale(.8); }
+ to { -webkit-transform: rotateY(0) scale(1); }
+}
+
+@-webkit-keyframes flipouttoleft {
+ from { -webkit-transform: rotateY(0) scale(1); }
+ to { -webkit-transform: rotateY(-180deg) scale(.8); }
+}
+
+@-webkit-keyframes flipouttoright {
+ from { -webkit-transform: rotateY(0) scale(1); }
+ to { -webkit-transform: rotateY(180deg) scale(.8); }
+}
+
+
+/* Hackish, but reliable. */
+
+@-webkit-keyframes dontmove {
+ from { opacity: 1; }
+ to { opacity: 1; }
+}
+
+.pop {
+ -webkit-transform-origin: 50% 50%;
+}
+
+.pop.in {
+ -webkit-transform: scale(1);
+ opacity: 1;
+ -webkit-animation-name: popin;
+ z-index: 10;
+}
+
+.pop.out.reverse {
+ -webkit-transform: scale(.2);
+ opacity: 0;
+ -webkit-animation-name: popout;
+ z-index: 10;
+}
+
+.pop.in.reverse {
+ z-index: 0;
+ -webkit-animation-name: dontmove;
+}
+
+@-webkit-keyframes popin {
+ from {
+ -webkit-transform: scale(.2);
+ opacity: 0;
+ }
+ to {
+ -webkit-transform: scale(1);
+ opacity: 1;
+ }
+}
+
+@-webkit-keyframes popout {
+ from {
+ -webkit-transform: scale(1);
+ opacity: 1;
+ }
+ to {
+ -webkit-transform: scale(.2);
+ opacity: 0;
+ }
+}/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* content configurations. */
+.ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d { overflow: hidden; }
+.ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; }
+
+/* grid a: 50/50 */
+.ui-grid-a .ui-block-a, .ui-grid-a .ui-block-b { width: 50%; }
+.ui-grid-a .ui-block-a { clear: left; }
+
+/* grid b: 33/33/33 */
+.ui-grid-b .ui-block-a, .ui-grid-b .ui-block-b, .ui-grid-b .ui-block-c { width: 33.333%; }
+.ui-grid-b .ui-block-a { clear: left; }
+
+/* grid c: 25/25/25/25 */
+.ui-grid-c .ui-block-a, .ui-grid-c .ui-block-b, .ui-grid-c .ui-block-c, .ui-grid-c .ui-block-d { width: 25%; }
+.ui-grid-c .ui-block-a { clear: left; }
+
+/* grid d: 20/20/20/20/20 */
+.ui-grid-d .ui-block-a, .ui-grid-d .ui-block-b, .ui-grid-d .ui-block-c, .ui-grid-d .ui-block-d, .ui-grid-d .ui-block-e { width: 20%; }
+.ui-grid-d .ui-block-a { clear: left; }/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+/* fixed page header & footer configuration */
+.ui-header, .ui-footer, .ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { position: absolute; overflow: hidden; width: 100%; border-left-width: 0; border-right-width: 0; }
+.ui-header-fixed, .ui-footer-fixed {
+ z-index: 1000;
+ -webkit-transform: translateZ(0); /* Force header/footer rendering to go through the same rendering pipeline as native page scrolling. */
+}
+.ui-footer-duplicate, .ui-page-fullscreen .ui-fixed-inline { display: none; }
+.ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { opacity: .9; }
+/*
+* jQuery Mobile Framework
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses.
+*/
+.ui-navbar { overflow: hidden; }
+.ui-navbar ul, .ui-navbar-expanded ul { list-style:none; padding: 0; margin: 0; position: relative; display: block; border: 0;}
+.ui-navbar-collapsed ul { float: left; width: 75%; margin-right: -2px; }
+.ui-navbar-collapsed .ui-navbar-toggle { float: left; width: 25%; }
+.ui-navbar li.ui-navbar-truncate { position: absolute; left: -99999px; top: -99999px; }
+.ui-navbar li .ui-btn, .ui-navbar .ui-navbar-toggle .ui-btn { display: block; font-size: 12px; text-align: center; margin: 0; outline: none; border-right-width: 0; }
+.ui-navbar li .ui-btn { margin-right: -1px; }
+.ui-navbar li .ui-btn:last-child { margin-right: 0; }
+.ui-header .ui-navbar li .ui-btn, .ui-header .ui-navbar .ui-navbar-toggle .ui-btn,
+.ui-footer .ui-navbar li .ui-btn, .ui-footer .ui-navbar .ui-navbar-toggle .ui-btn { border-top-width: 0; border-bottom-width: 0; }
+.ui-navbar .ui-btn-inner { padding-left: 2px; padding-right: 2px; }
+.ui-navbar-noicons li .ui-btn .ui-btn-inner, .ui-navbar-noicons .ui-navbar-toggle .ui-btn-inner { padding-top: .8em; padding-bottom: .9em; }
+/*expanded page styles*/
+.ui-navbar-expanded .ui-btn { margin: 0; font-size: 14px; }
+.ui-navbar-expanded .ui-btn-inner { padding-left: 5px; padding-right: 5px; }
+.ui-navbar-expanded .ui-btn-icon-top .ui-btn-inner { padding: 45px 5px 15px; text-align: center; }
+.ui-navbar-expanded .ui-btn-icon-top .ui-icon { top: 15px; }
+.ui-navbar-expanded