-
- Can’t find your box?
- Maybe your box isn’t connected to your network yet, try to connect to a Doodle3D-... WiFi network.
- Otherwise, make sure you’re on the same WiFi network.
- You can always connect your box to your computer using an ethernet cable.
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/js/Box.js b/js/Box.js
deleted file mode 100644
index 42999e4..0000000
--- a/js/Box.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * This file is part of the Doodle3D project (http://doodle3d.com).
- *
- * Copyright (c) 2013, Doodle3D
- * This software is licensed under the terms of the GNU GPL v2 or later.
- * See file LICENSE.txt or visit http://www.gnu.org/licenses/gpl.html for full license details.
- */
-function Box() {
-
- this.localip;
- this.wifiboxid;
- this.connecting = false;
- this.destroyedHandler;
-
- var _element;
- var _networkPanel;
- var _delayedDestroy;
- var _self = this;
-
- this.init = function(boxData,parentElement) {
-
- _self.localip = boxData.localip;
- _self.wifiboxid = boxData.wifiboxid;
- var url = "http://"+_self.localip;
-
- // create box dom element
- var link = (boxData.link)? boxData.link : url;
- var linkElement = $(""+_self.wifiboxid+"");
- _element = $("");
- _element.append(linkElement);
- _element.hide().appendTo(parentElement).fadeIn(500);
-
- // create network panel dom element
- var networkPanelElement = $("#networkForm").clone();
- networkPanelElement.addClass(networkPanelElement.attr("id"));
- networkPanelElement.removeAttr("id");
- _element.append(networkPanelElement);
-
- // create network panel
- _networkPanel = new NetworkPanel();
- _networkPanel.id = _self.localip;
- _networkPanel.init(url,networkPanelElement, networkStatusChangeHandler);
-
- }
- function networkStatusChangeHandler(networkStatus) {
- console.log("Box:networkStatusChangeHandler: ",networkStatus);
- _self.connecting = (networkStatus == NetworkAPI.STATUS.CONNECTING);
-
- // because openwrt can be slow to update it's ssid, a box might
- // report it failed connecting but is then slightly later connects
- // so we correct CONNECTING_FAILED to CONNECTED unless the box is connected by wire
- if(_self.localip != "192.168.5.1" && networkStatus == NetworkAPI.STATUS.CONNECTING_FAILED) {
- networkStatus = NetworkAPI.STATUS.CONNECTED;
- }
-
- _element.toggleClass("complex",(networkStatus !== NetworkAPI.STATUS.CONNECTED));
-
- if(_self.connecting) {
- clearTimeout(_delayedDestroy);
- _delayedDestroy = setTimeout(function() {
- console.log("delayed remove");
- //removeBox(box,true);
- _self.destroy()
- }, 10000);
- }
- }
- this.destroy = function() {
- console.log("Box:destroy");
- clearTimeout(_delayedDestroy);
-
- _networkPanel.destroy();
-
- _element.fadeOut(500,function() {
- _element.remove();
- });
-
- if(_self.destroyedHandler) _self.destroyedHandler(_self);
- }
-}
\ No newline at end of file
diff --git a/js/BoxPage.js b/js/BoxPage.js
new file mode 100644
index 0000000..021c591
--- /dev/null
+++ b/js/BoxPage.js
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the Doodle3D project (http://doodle3d.com).
+ *
+ * Copyright (c) 2013, Doodle3D
+ * This software is licensed under the terms of the GNU GPL v2 or later.
+ * See file LICENSE.txt or visit http://www.gnu.org/licenses/gpl.html for full license details.
+ */
+
+(function (w) {
+ var _page;
+ var _list;
+ var _title;
+ var _intro;
+ var _drawItem;
+ //var _updateItem;
+ var _joinNetworkItem;
+ var _defaultItems;
+
+ var _networkStatus;
+ var _networkAPI = new NetworkAPI();
+ var _boxData = {};
+ var _retryRetrieveStatusDelay;
+ var _retryRetrieveStatusDelayTime = 3000;
+ var PAGE_ID = "#box";
+
+ var _self = this;
+
+ $.mobile.document.on( "pageinit", PAGE_ID, function( event, data ) {
+ //console.log("Box page pageinit");
+ _page = $(this);
+ _list = _page.find("ul[data-role=listview]");
+ _title = _page.find(".ui-title");
+ _intro = _page.find(".intro");
+
+ _defaultItems = _list.children();
+ _drawItem = _list.find("#drawItem");
+ //_updateItem = _list.find("#updateItem");
+ _joinNetworkItem = _list.find("#joinNetworkItem");
+
+ // make sure draw link is opened in same WebApp (added to homescreen)
+ // and it doesn't start a browser
+ $.stayInWebApp("#box #drawItem a",true);
+ });
+ $.mobile.document.on( "pagebeforeshow", PAGE_ID, function( event, data ) {
+ console.log("Box page pagebeforeshow");
+ _boxData = d3d.util.getPageParams(PAGE_ID);
+ if(_boxData === undefined) {
+ $.mobile.changePage("#boxes");
+ return;
+ }
+ var boxURL = "http://"+_boxData.localip;
+ //console.log(" _boxData: ",_boxData);
+
+ _title.text(_boxData.wifiboxid);
+ setNetworkStatus(NetworkAPI.STATUS.CONNECTED);
+
+ var drawLink = (_boxData.link)? _boxData.link : boxURL;
+ _page.find("#drawItem a").attr("href",drawLink);
+
+ _networkAPI.init(boxURL);
+ retrieveNetworkStatus();
+ });
+ $.mobile.document.on( "pagebeforehide", PAGE_ID, function( event, data ) {
+ clearTimeout(_retryRetrieveStatusDelay);
+ });
+
+
+ function retrieveNetworkStatus() {
+ _networkAPI.status(function(data) {
+ data.status = parseInt(data.status,10);
+ setNetworkStatus(data.status);
+ }, function() {
+ clearTimeout(_retryRetrieveStatusDelay);
+ _retryRetrieveStatusDelay = setTimeout(_self.retrieveStatus, _retryRetrieveStatusDelayTime); // retry after delay
+ });
+ }
+
+ function setNetworkStatus(status) {
+ console.log(PAGE_ID+":setNetworkStatus: ",status);
+ var introText = "";
+ if(status === NetworkAPI.STATUS.CONNECTED) { // online
+ //console.log("online");
+ _drawItem.find("a").text("Draw");
+
+ // display the right buttons
+ _defaultItems.toggleClass("ui-screen-hidden",false);
+ _joinNetworkItem.toggleClass("ui-screen-hidden",true);
+ // ToDo: retrieve update information
+
+ } else { // offline
+ //console.log("offline");
+ introText = "Please connect your WiFi-Box to the internet. You can also use it offline, but then you won't be able to update.";
+
+ _drawItem.find("a").text("Draw (offline)");
+
+ // display the right buttons
+ _defaultItems.toggleClass("ui-screen-hidden",true);
+ _drawItem.toggleClass("ui-screen-hidden",false);
+ _joinNetworkItem.toggleClass("ui-screen-hidden",false);
+
+ var joinLink = _joinNetworkItem.find("a").attr("href");
+ joinLink = d3d.util.replaceURLParameters(joinLink,_boxData);
+ _joinNetworkItem.find("a").attr("href",joinLink);
+ }
+
+ _intro.text(introText);
+ _intro.toggleClass("ui-screen-hidden",(introText === ""));
+
+ // ToDo: update footer with network info
+
+ _list.listview('refresh'); // jQuery mobile enhance content
+ _networkStatus = status;
+ }
+})(window);
\ No newline at end of file
diff --git a/js/BoxesPage.js b/js/BoxesPage.js
new file mode 100644
index 0000000..0644db8
--- /dev/null
+++ b/js/BoxesPage.js
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the Doodle3D project (http://doodle3d.com).
+ *
+ * Copyright (c) 2013, Doodle3D
+ * This software is licensed under the terms of the GNU GPL v2 or later.
+ * See file LICENSE.txt or visit http://www.gnu.org/licenses/gpl.html for full license details.
+ */
+
+(function (w) {
+ var _connectAPI = new ConnectAPI();
+
+ var _page;
+ var _list;
+ var _findItem;
+ var PAGE_ID = "#boxes";
+
+ $.mobile.document.on( "pageinit", PAGE_ID, function( event, data ) {
+ //console.log("Boxes page pageinit");
+ _page = $(this);
+ _list = _page.find("#boxeslist");
+ _findItem = _list.find("#findItem");
+
+ _connectAPI.refreshing = onRefreshing;
+ _connectAPI.listFailed = onListFailed;
+ _connectAPI.listSuccess = onlistSuccess;
+ _connectAPI.listUpdated = onListUpdated;
+ _connectAPI.boxAppeared = onBoxAppeared;
+ _connectAPI.boxDisapeared = onBoxDisapeared;
+ });
+ $.mobile.document.on( "pagebeforeshow", PAGE_ID, function( event, data ) {
+ //console.log("Boxes page pagebeforeshow");
+ _connectAPI.start();
+ });
+ $.mobile.document.on( "pageshow", PAGE_ID, function( event, data ) {
+ //console.log("Boxes page pageshow");
+ addToHomescreen(/*{
+ debug: true, // activate debug mode in ios emulation
+ skipFirstVisit: false, // show at first access
+ startDelay: 0, // display the message right away
+ lifespan: 0, // do not automatically kill the call out
+ displayPace: 0, // do not obey the display pace
+ maxDisplayCount: 0 // do not obey the max display count
+ }*/);
+ });
+ $.mobile.document.on( "pagebeforehide", PAGE_ID, function( event, data ) {
+ //console.log("Boxes page pagehide");
+ _connectAPI.stop();
+ });
+
+ function onRefreshing() {
+ //console.log("onRefreshing");
+ d3d.util.showLoader(true);
+ }
+ function onListFailed() {
+ d3d.util.enableRefreshPrevention();
+ d3d.util.enableLeaveWarning("You're not connecting to the internet, leaving now will interrupt the connection proces");
+ }
+ function onlistSuccess() {
+ d3d.util.disableRefreshPrevention();
+ d3d.util.disableLeaveWarning();
+ }
+ function onListUpdated(boxesData) {
+ //console.log("onListUpdated: ",boxesData);
+ }
+ function onBoxAppeared(boxData) {
+ console.log("onBoxAppeared: ",boxData.localip);
+
+ var linkParams = {localip: boxData.localip,wifiboxid: boxData.wifiboxid};
+ if(boxData.link) { linkParams.link = boxData.link; }
+ var link = "#box";
+ link = d3d.util.replaceURLParameters(link,linkParams);
+ var id = boxData.localip.replace(/\./g,"-");
+ var linkElement = $(""+boxData.wifiboxid+"");
+ var box = $("");
+ box.append(linkElement);
+ box.hide().appendTo(_list).fadeIn(500);
+ _list.append(_findItem); // make sure find is the last item
+ _list.listview('refresh'); // jQuery mobile enhance content
+ }
+ function onBoxDisapeared(boxData) {
+ console.log("onBoxDisapeared: ",boxData.localip);
+
+ var id = boxData.localip.replace(/\./g,"-");
+ var box = _list.find("#"+id);
+ console.log(" box: ",box);
+ box.fadeOut(500,function() {
+ box.remove();
+ //_list.listview('refresh');
+ });
+ }
+})(window);
\ No newline at end of file
diff --git a/js/ConnectingToNetworkPage.js b/js/ConnectingToNetworkPage.js
new file mode 100644
index 0000000..bc66e4f
--- /dev/null
+++ b/js/ConnectingToNetworkPage.js
@@ -0,0 +1,202 @@
+/*
+ * This file is part of the Doodle3D project (http://doodle3d.com).
+ *
+ * Copyright (c) 2013, Doodle3D
+ * This software is licensed under the terms of the GNU GPL v2 or later.
+ * See file LICENSE.txt or visit http://www.gnu.org/licenses/gpl.html for full license details.
+ */
+
+(function (w) {
+
+ var _page;
+ var _statusField;
+ var _actionField;
+ var _networkAPI = new NetworkAPI();
+ var _connectAPI = new ConnectAPI();
+ var _connectedBoxNetworkAPI = new NetworkAPI();
+ var _infoAPI = new InfoAPI();
+ var _pageData = {};
+ var _formData;
+ var _wifiboxid;
+ var _wifiboxSSID;
+ var _connectedChecking = false;
+
+ var CONNECTED_REDIRECT_DELAY = 5000;
+ var _connectedRedirectDelay;
+ var BACKUP_REDIRECT_DELAY = 10*1000; // when the wifiboxid isn't retrievable we want to redirect anyway
+ var _backupRedirectDelay;
+ var PAGE_ID = "#connecting_to_network";
+
+ var _self = this;
+
+ $.mobile.document.on( "pageinit", PAGE_ID, function( event, data ) {
+ console.log(PAGE_ID+": pageinit");
+ _page = $(this);
+ _statusField = _page.find("#status");
+ _actionField = _page.find("#action");
+ });
+ $.mobile.document.on( "pagebeforeshow", PAGE_ID, function( event, data ) {
+ console.log(PAGE_ID+": pagebeforeshow");
+ _pageData = d3d.util.getPageParams(PAGE_ID);
+ var form = data.prevPage.find("form");
+ // check if there are url params and a form from a prev page
+ if(_pageData === undefined || form.length === 0) {
+ $.mobile.changePage("#boxes");
+ return;
+ }
+ var boxURL = "http://"+_pageData.localip;
+ _formData = d3d.util.getFormData(form);
+ _infoAPI.init(boxURL);
+ _networkAPI.init(boxURL);
+ retrieveWiFiBoxID(function() {
+ console.log(" _wifiboxid: ",_wifiboxid);
+ console.log(" _wifiboxSSID: ",_wifiboxSSID);
+ joinNetwork();
+ _networkAPI.refreshing = onRefreshing;
+ _networkAPI.updated = onStatusUpdated;
+ _networkAPI.startAutoRefresh();
+ });
+ });
+ $.mobile.document.on( "pagebeforehide", PAGE_ID, function( event, data ) {
+ console.log(PAGE_ID+": pagebeforehide");
+ _networkAPI.stopAutoRefresh();
+ _connectAPI.stop();
+ _connectedBoxNetworkAPI.stopAutoRefresh();
+ clearTimeout(_connectedRedirectDelay);
+ clearTimeout(_backupRedirectDelay);
+ });
+ function retrieveWiFiBoxID(completeHandler) {
+ console.log(PAGE_ID+":retrieveWiFiBoxID");
+ _infoAPI.getInfo(function(infoData) {
+ _wifiboxid = infoData.wifiboxid;
+ _wifiboxSSID = infoData.substituted_ssid;
+ completeHandler();
+ },function() {
+ _wifiboxid = undefined;
+ _wifiboxSSID = undefined;
+ // try connecting anyway (making sure wifiboxid retrieval isn't blocking)
+ completeHandler();
+ });
+ }
+ function joinNetwork() {
+ console.log(PAGE_ID+":joinNetwork");
+ _networkAPI.associate(_pageData.ssid,_formData.password,true);
+ _connectedChecking = false;
+ }
+ function onRefreshing() {
+ //console.log("ConnectingToNetworkPage:onRefreshing");
+ d3d.util.showLoader(true);
+ }
+ function onStatusUpdated(data) {
+ console.log("ConnectingToNetworkPage:onStatusUpdated");
+ //console.log(" data: ",data);
+ data.status = parseInt(data.status,10);
+ console.log(" data.status: ",data.status);
+
+ // update texts
+ var statusText = "";
+ var actionText = "";
+ switch(data.status) {
+ case NetworkAPI.STATUS.CONNECTING:
+ statusText = "Connecting to "+_pageData.ssid+"...";
+ //actionText = "Please reconnect yourself to "+_pageData.ssid+". Once you are connected return to this page.";
+ actionText = "Please reconnect yourself to "+_pageData.ssid+". Once you are connected return to this page.";
+ _actionField.attr("class","notice");
+ break;
+ case NetworkAPI.STATUS.CONNECTING_FAILED:
+ statusText = "Could not connect...";
+ actionText = "Please check password and try again";
+ _actionField.attr("class","error");
+ break;
+ case NetworkAPI.STATUS.CONNECTED:
+ statusText = "Connected to "+_pageData.ssid;
+ actionText = "Please reconnect yourself to "+_pageData.ssid+". Once you are connected return to this page.";
+ _actionField.attr("class","notice");
+ break;
+ default:
+ actionText = "Something went wrong, please try again";
+ _actionField.attr("class","error");
+ break;
+ }
+ // TODO ignore connected?
+ _statusField.html(statusText);
+ _actionField.html(actionText);
+
+ // When the box is connecting we start checking connect.doodle3d.com
+ // for a box with the same wifiboxid
+ if(data.status === NetworkAPI.STATUS.CONNECTING && !_connectedChecking) {
+ if(_wifiboxid !== undefined || _wifiboxSSID !== undefined) {
+ console.log(" start checking for same box");
+ _connectAPI.boxAppeared = onBoxAppeared;
+ } else {
+ // if there is no wifiboxid or ssid available we'll check if we're online
+ console.log(" start checking for internet");
+ _connectAPI.listSuccess = onListSuccess;
+ }
+ _connectAPI.checkLocal = false;
+ _connectAPI.start();
+ _connectedChecking = true;
+ }
+ }
+ function onBoxAppeared(boxData) {
+ console.log(PAGE_ID+":onBoxAppeared: ",boxData.localip,boxData.wifiboxid);
+ // if same box is found...
+ if(_wifiboxid !== undefined && boxData.wifiboxid === _wifiboxid) {
+ console.log("found _wifiboxid");
+ checkBox(boxData);
+ // wifiboxid of older firmware isn't available, fallback to ssid
+ } else if(_wifiboxSSID !== undefined){
+ console.log("no _wifiboxid, falling back to _wifiboxSSID comparison");
+ var connectedBoxConfigAPI = new ConfigAPI();
+ connectedBoxConfigAPI.init("http://"+boxData.localip);
+ connectedBoxConfigAPI.save({},function(saveResponseData) {
+ if(saveResponseData.substituted_ssid === _wifiboxSSID) {
+ checkBox(boxData);
+ }
+ });
+ }
+ }
+ function checkBox(boxData) {
+ // check if it finished connecting
+ var boxURL = "http://"+boxData.localip;
+ _connectedBoxNetworkAPI = new NetworkAPI();
+ _connectedBoxNetworkAPI.init(boxURL);
+ _connectedBoxNetworkAPI.updated = function(data) {
+ data.status = parseInt(data.status,10);
+ console.log(PAGE_ID+":connectedBoxNetworkAPI:onStatusUpdated: ",data.status);
+ // if box finished connecting
+ if(data.status === NetworkAPI.STATUS.CONNECTED) {
+ console.log(" found connected box");
+ _statusField.html("Connected to "+_pageData.ssid);
+ _actionField.html("Congratulations the box is connected to "+_pageData.ssid+". You will be redirected in a moment...");
+ _actionField.attr("class","info");
+ // prevent status changes by wired box
+ _networkAPI.stopAutoRefresh();
+
+ _connectedRedirectDelay = setTimeout(function () {
+ // redirect to it's box page
+ console.log(" redirect to box");
+ // replace this page with boxes page in history
+ window.history.replaceState(null, "", "#boxes");
+ var linkParams = {localip: boxData.localip,wifiboxid: boxData.wifiboxid};
+ var link = "#box";
+ link = d3d.util.replaceURLParameters(link,linkParams);
+ $.mobile.changePage(link);
+ _connectedBoxNetworkAPI.stopAutoRefresh();
+
+ // disable warnings that are enabled on boxes page
+ d3d.util.disableRefreshPrevention();
+ d3d.util.disableLeaveWarning();
+ },CONNECTED_REDIRECT_DELAY);
+ }
+ };
+ _connectedBoxNetworkAPI.startAutoRefresh();
+ }
+ // when no wifiboxid or wifiboxSSID is available but we are online, we redirect to the boxes page
+ function onListSuccess() {
+ console.log(PAGE_ID+":onListSuccess");
+ _backupRedirectDelay = setTimeout(function () {
+ $.mobile.changePage("#boxes");
+ },BACKUP_REDIRECT_DELAY);
+ }
+})(window);
\ No newline at end of file
diff --git a/js/JoinNetworkPage.js b/js/JoinNetworkPage.js
new file mode 100644
index 0000000..c250aca
--- /dev/null
+++ b/js/JoinNetworkPage.js
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the Doodle3D project (http://doodle3d.com).
+ *
+ * Copyright (c) 2013, Doodle3D
+ * This software is licensed under the terms of the GNU GPL v2 or later.
+ * See file LICENSE.txt or visit http://www.gnu.org/licenses/gpl.html for full license details.
+ */
+
+(function (w) {
+
+ var _page;
+ var _list;
+ //var _joinOtherItem;
+ var _networks;
+ var _networkAPI = new NetworkAPI();
+ var _boxData = {};
+ var _refreshDelay;
+ var _refreshDelayTime = 3000;
+ var PAGE_ID = "#join_network";
+
+ var _self = this;
+
+ $.mobile.document.on( "pageinit", PAGE_ID, function( event, data ) {
+ console.log("Join network page pageinit");
+ _page = $(this);
+ _list = _page.find("ul[data-role=listview]");
+ //_joinOtherItem = _list.find("#joinOther");
+ });
+ $.mobile.document.on( "pagebeforeshow", PAGE_ID, function( event, data ) {
+ console.log("Join network page pagebeforeshow");
+ _boxData = d3d.util.getPageParams(PAGE_ID);
+ if(_boxData === undefined) {
+ $.mobile.changePage("#boxes");
+ return;
+ }
+ var boxURL = "http://"+_boxData.localip;
+ console.log(" _boxData: ",_boxData);
+
+ _networkAPI.init(boxURL);
+ refreshNetworks();
+ });
+ $.mobile.document.on( "pagebeforehide", PAGE_ID, function( event, data ) {
+ console.log("Join network page pagehide");
+ clearTimeout(_refreshDelay);
+ });
+ function refreshNetworks() {
+ //console.log("JoinNetwork:refreshNetworks");
+ d3d.util.showLoader();
+ _networkAPI.scan(function(data) { // completed
+ //console.log("JoinNetwork:refreshNetworks:scanned");
+ d3d.util.hideLoader();
+ _networks = {};
+ $.each(data.networks, function(index,network) {
+ _networks[network.ssid] = network;
+ });
+ // update list
+ updateList();
+ // keep refreshing
+ clearTimeout(_refreshDelay);
+ _refreshDelay = setTimeout(refreshNetworks, _refreshDelayTime);
+ });
+ }
+ function updateList() {
+ _list.empty();
+ var baseConnectingLink = _list.data("connecting-target");
+ var baseSecuredLink = _list.data("secured-target");
+ var linkParams = $.extend({}, _boxData);
+ //console.log(" linkParams: ",linkParams);
+ $.each(_networks, function(index,network) {
+ //console.log(" network: ",network);
+ linkParams.ssid = network.ssid;
+
+ var secured = (network.encryption !== "none" && network.encryption !== "");
+ var link;
+ var icon = "";
+ if(secured) {
+ linkParams.encryption = network.encryption;
+ link = d3d.util.replaceURLParameters(baseSecuredLink,linkParams);
+ icon = "lock";
+ } else {
+ link = d3d.util.replaceURLParameters(baseConnectingLink,linkParams);
+ }
+ //console.log(" link: ",link);
+ _list.append(
+ $('
The Ajax-based navigation used throughout the jQuery Mobile docs may need to be viewed on a web server to work in certain browsers. If you see an error message when you click a link, please try a different browser.
The Ajax-based navigation used throughout the jQuery Mobile docs may need to be viewed on a web server to work in certain browsers. If you see an error message when you click a link, please try a different browser.
jQuery Mobile provides an HTML5-based user interface for all popular mobile device platforms, but it does not influence how you organize and structure your app's JavaScript. Many jQuery Mobile users turn to a variety of other popular third-party libraries, including MV* frameworks and dependency management tools, to help structure their code.
+
+
+
Backbone and Require.js
+
Backbone.js and Require.js are two of the most popular third-party libraries that are used with jQuery Mobile to provide a rich JavaScript tech stack for developers.
+
+
Backbone.js is a great client-side MV* JavaScript framework that provides structure to JavaScript applications by providing View, Model, Collection, Router, and Event class objects.
+
+
Require.js serves a few different purposes than Backbone.js. Require.js is an AMD (Asynchronous Module Definition) script loader that asynchronously loads your JavaScript to improve page load performance, while also helping with script dependency managagement and allowing you to organize your JavaScript into self contained modules (files).
+
+
Although there is a high amount of developer interest with using jQuery Mobile, Backbone.js, and Require.js together, there is a high barrier of entry. Many users are confused about how to use the Backbone.js Router class object with the jQuery Mobile routing system.
The technique used in this example page is by no means the only technique available, but it is one of the most elegant. The Backbone.js router is used exclusively to handle all hashchange events, and the jQuery Mobile $.mobile.changePage() method is used to programmatically change the page.
+
+
Below are two internal jQuery Mobile properties that are turned off to allow this to happen:
+
+
+
$.mobile.linkBindingEnabled
+
+
jQuery Mobile will automatically bind the clicks on anchor tags in your document. Setting this option to false will prevent all anchor click handling including the addition of active button state and alternate link blurring. This should only be used when attempting to delegate the click management to another library or custom code.
+
+
+
$.mobile.hashListeningEnabled
+
+
jQuery Mobile will automatically listen and handle changes to the location.hash. Disabling this will prevent jQuery Mobile from handling hash changes, which allows you to handle them yourself or use simple deep-links within a document that scroll to a particular id.
+
+
+
+
+
+
Example page code
+
To illustrate how the above internal jQuery Mobile properties are turned off, let's examine our example page code.
+
+
Inside of the head section of our index.html page, we first include the Require.js JavaScript library and set the data-main attribute of our script tag to the JavaScript file that we want Require.js to include on the page first (this file will contain all of our Require.js configurations). In this example, we are telling Require.js to look inside of the js folder and then load mobile.js.
+
If we look inside of mobile.js, we will find that the $.mobile.linkBindingEnabled and $.mobile.hashListeningEnabled jQuery Mobile attributes are set to false.
+
+
+
+ // Sets the require.js configuration for your application.
+ require.config( {
+
+ // 3rd party script alias names
+ paths: {
+
+ // Core Libraries
+ "jquery": "../../../js/jquery",
+ "jquerymobile": "libs/jquerymobile",
+ "underscore": "libs/lodash",
+ "backbone": "libs/backbone"
+
+ },
+
+ // Sets the configuration for your third party scripts that are not AMD compatible
+ shim: {
+
+ "backbone": {
+ "deps": [ "underscore", "jquery" ],
+ "exports": "Backbone" //attaches "Backbone" to the window object
+ }
+
+ } // end Shim Configuration
+
+ } );
+
+ // Includes File Dependencies
+ require([ "jquery","backbone","routers/mobileRouter","jquerymobile" ], function( $, Backbone, Mobile ) {
+
+ // Prevents all anchor click handling
+ $.mobile.linkBindingEnabled = false;
+
+ // Disabling this will prevent jQuery Mobile from handling hash changes
+ $.mobile.hashListeningEnabled = false;
+
+ // Instantiates a new Backbone.js Mobile Router
+ this.router = new Mobile();
+
+ } );
+
+
+
Next, inside of the Backbone.js Router class object, we can respond to haschange events and manually call the jQuery Mobile changePage() method. Below is a small snippet of mobileRouter.js.
+
+
+ // Backbone.js Routes
+ routes: {
+
+ // When there is no hash bang on the url, the home method is called
+ "": "home",
+
+ // When #category? is on the url, the category method is called
+ "category?:type": "category"
+
+ },
+
+ // Home method
+ home: function() {
+
+ // Programatically changes to the categories page
+ $.mobile.changePage( "#categories" , { reverse: false, changeHash: false } );
+
+ }
+
+
+
+
The example page illustrates how to render a jQuery Mobile ListView that is populated with dynamic JSON data asynchronously. Feel free to take a deeper look into the source code to see how Require.js and Backbone.js are used.
jQuery Mobile provides an HTML5-based user interface for all popular mobile device platforms, but it does not influence how you organize and structure your app's JavaScript. Many jQuery Mobile users turn to a variety of other popular third-party libraries, including MV* frameworks and dependency management tools, to help structure their code.
+
+
+
Backbone and Require.js
+
Backbone.js and Require.js are two of the most popular third-party libraries that are used with jQuery Mobile to provide a rich JavaScript tech stack for developers.
+
+
Backbone.js is a great client-side MV* JavaScript framework that provides structure to JavaScript applications by providing View, Model, Collection, Router, and Event class objects.
+
+
Require.js serves a few different purposes than Backbone.js. Require.js is an AMD (Asynchronous Module Definition) script loader that asynchronously loads your JavaScript to improve page load performance, while also helping with script dependency managagement and allowing you to organize your JavaScript into self contained modules (files).
+
+
Although there is a high amount of developer interest with using jQuery Mobile, Backbone.js, and Require.js together, there is a high barrier of entry. Many users are confused about how to use the Backbone.js Router class object with the jQuery Mobile routing system.
The technique used in this example page is by no means the only technique available, but it is one of the most elegant. The Backbone.js router is used exclusively to handle all hashchange events, and the jQuery Mobile $.mobile.changePage() method is used to programmatically change the page.
+
+
Below are two internal jQuery Mobile properties that are turned off to allow this to happen:
+
+
+
$.mobile.linkBindingEnabled
+
+
jQuery Mobile will automatically bind the clicks on anchor tags in your document. Setting this option to false will prevent all anchor click handling including the addition of active button state and alternate link blurring. This should only be used when attempting to delegate the click management to another library or custom code.
+
+
+
$.mobile.hashListeningEnabled
+
+
jQuery Mobile will automatically listen and handle changes to the location.hash. Disabling this will prevent jQuery Mobile from handling hash changes, which allows you to handle them yourself or use simple deep-links within a document that scroll to a particular id.
+
+
+
+
+
+
Example page code
+
To illustrate how the above internal jQuery Mobile properties are turned off, let's examine our example page code.
+
+
Inside of the head section of our index.html page, we first include the Require.js JavaScript library and set the data-main attribute of our script tag to the JavaScript file that we want Require.js to include on the page first (this file will contain all of our Require.js configurations). In this example, we are telling Require.js to look inside of the js folder and then load mobile.js.
+
If we look inside of mobile.js, we will find that the $.mobile.linkBindingEnabled and $.mobile.hashListeningEnabled jQuery Mobile attributes are set to false.
+
+
+
+ // Sets the require.js configuration for your application.
+ require.config( {
+
+ // 3rd party script alias names
+ paths: {
+
+ // Core Libraries
+ "jquery": "../../../js/jquery",
+ "jquerymobile": "libs/jquerymobile",
+ "underscore": "libs/lodash",
+ "backbone": "libs/backbone"
+
+ },
+
+ // Sets the configuration for your third party scripts that are not AMD compatible
+ shim: {
+
+ "backbone": {
+ "deps": [ "underscore", "jquery" ],
+ "exports": "Backbone" //attaches "Backbone" to the window object
+ }
+
+ } // end Shim Configuration
+
+ } );
+
+ // Includes File Dependencies
+ require([ "jquery","backbone","routers/mobileRouter","jquerymobile" ], function( $, Backbone, Mobile ) {
+
+ // Prevents all anchor click handling
+ $.mobile.linkBindingEnabled = false;
+
+ // Disabling this will prevent jQuery Mobile from handling hash changes
+ $.mobile.hashListeningEnabled = false;
+
+ // Instantiates a new Backbone.js Mobile Router
+ this.router = new Mobile();
+
+ } );
+
+
+
Next, inside of the Backbone.js Router class object, we can respond to haschange events and manually call the jQuery Mobile changePage() method. Below is a small snippet of mobileRouter.js.
+
+
+ // Backbone.js Routes
+ routes: {
+
+ // When there is no hash bang on the url, the home method is called
+ "": "home",
+
+ // When #category? is on the url, the category method is called
+ "category?:type": "category"
+
+ },
+
+ // Home method
+ home: function() {
+
+ // Programatically changes to the categories page
+ $.mobile.changePage( "#categories" , { reverse: false, changeHash: false } );
+
+ }
+
+
+
+
The example page illustrates how to render a jQuery Mobile ListView that is populated with dynamic JSON data asynchronously. Feel free to take a deeper look into the source code to see how Require.js and Backbone.js are used.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/js/libs/.jquery.mobile/demos/backbone-requirejs/js/collections/CategoriesCollection.js b/js/libs/.jquery.mobile/demos/backbone-requirejs/js/collections/CategoriesCollection.js
new file mode 100644
index 0000000..957f71b
--- /dev/null
+++ b/js/libs/.jquery.mobile/demos/backbone-requirejs/js/collections/CategoriesCollection.js
@@ -0,0 +1,101 @@
+// Category Collection
+// ===================
+
+// Includes file dependencies
+define([
+ "jquery",
+ "backbone",
+ "../models/CategoryModel" ], function( $, Backbone, CategoryModel ) {
+
+ // Extends Backbone.Router
+ var Collection = Backbone.Collection.extend( {
+
+ // The Collection constructor
+ initialize: function( models, options ) {
+
+ // Sets the type instance property (ie. animals)
+ this.type = options.type;
+
+ },
+
+ // Sets the Collection model property to be a Category Model
+ model: CategoryModel,
+
+ // Sample JSON data that in a real app will most likely come from a REST web service
+ jsonArray: [
+
+ { "category": "animals", "type": "Pets" },
+
+ { "category": "animals", "type": "Farm Animals" },
+
+ { "category": "animals", "type": "Wild Animals" },
+
+ { "category": "colors", "type": "Blue" },
+
+ { "category": "colors", "type": "Green" },
+
+ { "category": "colors", "type": "Orange" },
+
+ { "category": "colors", "type": "Purple" },
+
+ { "category": "colors", "type": "Red" },
+
+ { "category": "colors", "type": "Yellow" },
+
+ { "category": "colors", "type": "Violet" },
+
+ { "category": "vehicles", "type": "Cars" },
+
+ { "category": "vehicles", "type": "Planes" },
+
+ { "category": "vehicles", "type": "Construction" }
+
+ ],
+
+ // Overriding the Backbone.sync method (the Backbone.fetch method calls the sync method when trying to fetch data)
+ sync: function( method, model, options ) {
+
+ // Local Variables
+ // ===============
+
+ // Instantiates an empty array
+ var categories = [],
+
+ // Stores the this context in the self variable
+ self = this,
+
+ // Creates a jQuery Deferred Object
+ deferred = $.Deferred();
+
+ // Uses a setTimeout to mimic a real world application that retrieves data asynchronously
+ setTimeout( function() {
+
+ // Filters the above sample JSON data to return an array of only the correct category type
+ categories = _.filter( self.jsonArray, function( row ) {
+
+ return row.category === self.type;
+
+ } );
+
+ // Calls the options.success method and passes an array of objects (Internally saves these objects as models to the current collection)
+ options.success( categories );
+
+ // Triggers the custom `added` method (which the Category View listens for)
+ self.trigger( "added" );
+
+ // Resolves the deferred object (this triggers the changePage method inside of the Category Router)
+ deferred.resolve();
+
+ }, 1000);
+
+ // Returns the deferred object
+ return deferred;
+
+ }
+
+ } );
+
+ // Returns the Model class
+ return Collection;
+
+} );
\ No newline at end of file
diff --git a/js/libs/.jquery.mobile/demos/backbone-requirejs/js/main.js b/js/libs/.jquery.mobile/demos/backbone-requirejs/js/main.js
new file mode 100644
index 0000000..6002f40
--- /dev/null
+++ b/js/libs/.jquery.mobile/demos/backbone-requirejs/js/main.js
@@ -0,0 +1,55 @@
+// Sets the require.js configuration for your application.
+require.config( {
+
+ baseUrl: "../js",
+
+ // 3rd party script alias names
+ paths: {
+
+ // Core Libraries
+ "jquery": "jquery",
+ "jquerymobile": "jquery.mobile-1.4.2",
+ "underscore": "//rawgithub.com/lodash/lodash/2.4.1/dist/lodash",
+ "backbone": "//rawgithub.com/jashkenas/backbone/0.9.2/backbone",
+
+ "backbone-requirejs-demos": "../backbone-requirejs/js"
+ },
+
+ // Sets the configuration for your third party scripts that are not AMD compatible
+ shim: {
+
+ "backbone": {
+ "deps": [ "underscore", "jquery" ],
+ "exports": "Backbone"
+ }
+
+ }
+
+});
+
+// Includes File Dependencies
+require([
+ "jquery",
+ "backbone",
+ "backbone-requirejs-demos/routers/mobileRouter"
+], function ( $, Backbone, Mobile ) {
+
+ $( document ).on( "mobileinit",
+
+ // Set up the "mobileinit" handler before requiring jQuery Mobile's module
+ function () {
+
+ // Prevents all anchor click handling including the addition of active button state and alternate link bluring.
+ $.mobile.linkBindingEnabled = false;
+
+ // Disabling this will prevent jQuery Mobile from handling hash changes
+ $.mobile.hashListeningEnabled = false;
+ }
+ )
+
+ require( [ "jquerymobile" ], function () {
+
+ // Instantiates a new Backbone.js Mobile Router
+ this.router = new Mobile();
+ });
+});
diff --git a/js/libs/.jquery.mobile/demos/backbone-requirejs/js/models/CategoryModel.js b/js/libs/.jquery.mobile/demos/backbone-requirejs/js/models/CategoryModel.js
new file mode 100644
index 0000000..06f7845
--- /dev/null
+++ b/js/libs/.jquery.mobile/demos/backbone-requirejs/js/models/CategoryModel.js
@@ -0,0 +1,18 @@
+// Category Model
+// ==============
+
+// Includes file dependencies
+define([
+ "jquery",
+ "backbone"
+], function( $, Backbone ) {
+
+ // The Model constructor
+ var Model = Backbone.Model.extend( {
+
+ } );
+
+ // Returns the Model class
+ return Model;
+
+} );
\ No newline at end of file
diff --git a/js/libs/.jquery.mobile/demos/backbone-requirejs/js/routers/mobileRouter.js b/js/libs/.jquery.mobile/demos/backbone-requirejs/js/routers/mobileRouter.js
new file mode 100644
index 0000000..44c0b57
--- /dev/null
+++ b/js/libs/.jquery.mobile/demos/backbone-requirejs/js/routers/mobileRouter.js
@@ -0,0 +1,89 @@
+// Mobile Router
+// =============
+
+// Includes file dependencies
+define([
+ "jquery",
+ "backbone",
+ "../models/CategoryModel",
+ "../collections/CategoriesCollection",
+ "../views/CategoryView"
+], function( $, Backbone, CategoryModel, CategoriesCollection, CategoryView ) {
+
+ // Extends Backbone.Router
+ var CategoryRouter = Backbone.Router.extend( {
+
+ // The Router constructor
+ initialize: function() {
+
+ // Instantiates a new Animal Category View
+ this.animalsView = new CategoryView( { el: "#animals", collection: new CategoriesCollection( [] , { type: "animals" } ) } );
+
+ // Instantiates a new Colors Category View
+ this.colorsView = new CategoryView( { el: "#colors", collection: new CategoriesCollection( [] , { type: "colors" } ) } );
+
+ // Instantiates a new Vehicles Category View
+ this.vehiclesView = new CategoryView( { el: "#vehicles", collection: new CategoriesCollection( [] , { type: "vehicles" } ) } );
+
+ // Tells Backbone to start watching for hashchange events
+ Backbone.history.start();
+
+ },
+
+ // Backbone.js Routes
+ routes: {
+
+ // When there is no hash bang on the url, the home method is called
+ "": "home",
+
+ // When #category? is on the url, the category method is called
+ "category?:type": "category"
+
+ },
+
+ // Home method
+ home: function() {
+
+ // Programatically changes to the categories page
+ $.mobile.changePage( "#categories" , { reverse: false, changeHash: false } );
+
+ },
+
+ // Category method that passes in the type that is appended to the url hash
+ category: function(type) {
+
+ // Stores the current Category View inside of the currentView variable
+ var currentView = this[ type + "View" ];
+
+ // If there are no collections in the current Category View
+ if(!currentView.collection.length) {
+
+ // Show's the jQuery Mobile loading icon
+ $.mobile.loading( "show" );
+
+ // Fetches the Collection of Category Models for the current Category View
+ currentView.collection.fetch().done( function() {
+
+ // Programatically changes to the current categories page
+ $.mobile.changePage( "#" + type, { reverse: false, changeHash: false } );
+
+ } );
+
+ }
+
+ // If there already collections in the current Category View
+ else {
+
+ // Programatically changes to the current categories page
+ $.mobile.changePage( "#" + type, { reverse: false, changeHash: false } );
+
+ }
+
+ }
+
+ } );
+
+ // Returns the Router class
+ return CategoryRouter;
+
+} );
\ No newline at end of file
diff --git a/js/libs/.jquery.mobile/demos/backbone-requirejs/js/views/CategoryView.js b/js/libs/.jquery.mobile/demos/backbone-requirejs/js/views/CategoryView.js
new file mode 100644
index 0000000..e2e9c32
--- /dev/null
+++ b/js/libs/.jquery.mobile/demos/backbone-requirejs/js/views/CategoryView.js
@@ -0,0 +1,41 @@
+// Category View
+// =============
+
+// Includes file dependencies
+define([
+ "jquery",
+ "backbone",
+ "../models/CategoryModel"
+], function( $, Backbone, CategoryModel ) {
+
+ // Extends Backbone.View
+ var CategoryView = Backbone.View.extend( {
+
+ // The View Constructor
+ initialize: function() {
+
+ // The render method is called when Category Models are added to the Collection
+ this.collection.on( "added", this.render, this );
+
+ },
+
+ // Renders all of the Category models on the UI
+ render: function() {
+
+ // Sets the view's template property
+ this.template = _.template( $( "script#categoryItems" ).html(), { "collection": this.collection } );
+
+ // Renders the view's template inside of the current listview element
+ this.$el.find("ul").html(this.template);
+
+ // Maintains chainability
+ return this;
+
+ }
+
+ } );
+
+ // Returns the View class
+ return CategoryView;
+
+} );
\ No newline at end of file
diff --git a/js/libs/.jquery.mobile/demos/body-bar-classes/index.html b/js/libs/.jquery.mobile/demos/body-bar-classes/index.html
new file mode 100644
index 0000000..e901336
--- /dev/null
+++ b/js/libs/.jquery.mobile/demos/body-bar-classes/index.html
@@ -0,0 +1,492 @@
+
+
+
+
+
+ Grouping and dividing content - jQuery Mobile Demos
+
+
+
+
+
+
+
+
+
+
+
jQuery Mobile provides classes ui-bar and ui-body for subdividing and for visually grouping content.
+
Add class ui-bar to create a full-width heading or a separator between sections of content. Additionally, classes ui-bar-[a-z] add the appropriate swatch from your theme.
+
Add class ui-body to visual group and/or emphasize a section of content. Additionally, classes ui-body-[a-z] add the appropriate swatch from your theme.
+
+
Heading and accompanying section
+
+
+
Heading
+
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse accumsan blandit fermentum. Pellentesque cursus mauris purus, auctor commodo mi ullamcorper nec. Donec semper mattis eros, nec condimentum ante sollicitudin quis. Etiam orci sem, porttitor ut tellus nec, blandit posuere urna. Proin a arcu non lacus pretium faucibus. Aliquam sed est porttitor, ullamcorper urna nec, vehicula lorem. Cras porttitor est lorem, non venenatis diam convallis congue.
+
+
+
+
Heading with rounded corners and accompanying section
+
+
+
Heading
+
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse accumsan blandit fermentum. Pellentesque cursus mauris purus, auctor commodo mi ullamcorper nec. Donec semper mattis eros, nec condimentum ante sollicitudin quis. Etiam orci sem, porttitor ut tellus nec, blandit posuere urna. Proin a arcu non lacus pretium faucibus. Aliquam sed est porttitor, ullamcorper urna nec, vehicula lorem. Cras porttitor est lorem, non venenatis diam convallis congue.
+
+
+
+
Heading with rounded corners accompanying themed section with rounded corners
+
+
+
Heading
+
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse accumsan blandit fermentum. Pellentesque cursus mauris purus, auctor commodo mi ullamcorper nec. Donec semper mattis eros, nec condimentum ante sollicitudin quis. Etiam orci sem, porttitor ut tellus nec, blandit posuere urna. Proin a arcu non lacus pretium faucibus. Aliquam sed est porttitor, ullamcorper urna nec, vehicula lorem. Cras porttitor est lorem, non venenatis diam convallis congue.
+
+
+
+
Combined heading and accompanying themed section with rounded corners
+
+
+
+
Heading
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse accumsan blandit fermentum. Pellentesque cursus mauris purus, auctor commodo mi ullamcorper nec. Donec semper mattis eros, nec condimentum ante sollicitudin quis. Etiam orci sem, porttitor ut tellus nec, blandit posuere urna. Proin a arcu non lacus pretium faucibus. Aliquam sed est porttitor, ullamcorper urna nec, vehicula lorem. Cras porttitor est lorem, non venenatis diam convallis congue.
+
+
+
+
Heading attached to section - custom rounded corners
Add classes to style a and button elements. input buttons are enhanced by the button widget. See this page for examples.
+
+
Note that in 1.4 data- attributes will still work. The deprecated buttonMarkup method will add the applicable classes to a (with data-role="button") and button elements. This method also adds the role="button" attribute to anchor elements.
Note: This styling option is deprecated in jQuery Mobile 1.4.0 and will be removed in 1.5.0. Accordingly, we changed the default for framework-enhanced buttons to false.
Examples of how to style input buttons; input elements with type="button", type="submit", or type="reset". See button markup for examples of a and button elements.
+
+
Default
+
+
+
+
+
+
Enhanced
+
+
+
+
+
+
Corners
+
+
+
+
+
+
Icon-only buttons are round by default. Here we show how you can set the same border-radius as other buttons.
Checkbox inputs are used to provide a list of options where more than one can be selected. Checkbox buttons are enhanced by the checkboxradio widget.
+
+
Basic markup
+
+
To create a single checkbox, add an input with a type="checkbox" attribute and a corresponding label. If the input isn't wrapped in its corresponding label, be sure to set the for attribute of the label to match the id of the input so they are semantically associated.
+
+
+
+
+
+
Mini size
+
+
For a more compact version that is useful in toolbars and tight spaces, add the data-mini="true" attribute to the element to create a mini version.
+
+
+
+
+
+
Vertical group
+
+
Typically, there are multiple checkboxes listed under a question title. To visually integrate multiple checkboxes into a grouped button set, the framework will automatically remove all margins between buttons and round only the top and bottom corners of the set if there is a data-role="controlgroup" attribute on the fieldset.
+
+
+
+
+
+
Horizontal group
+
+
Checkboxes can also be used for grouped button sets where more than one button can be selected at once, such as the bold, italic and underline button group seen in word processors. To make a horizontal button set, add the data-type="horizontal" to the fieldset.
+
+
+
+
+
+
Icon position
+
+
To swap the position of the check icon from the default position on the left, add the data-iconpos="right" attribute to the fieldset to create a mini version.
+
+
+
+
+
+
Theme
+
+
To set the theme, add the data-theme attribute on the fieldset to the individual checkbox inputs.