Update functionality

This commit is contained in:
peteruithoven 2014-05-23 19:09:46 +02:00
parent d4e37432d8
commit 730000103f
7 changed files with 572 additions and 6 deletions

View File

@ -99,6 +99,7 @@ module.exports = function(grunt) {
NetworkAPI: true,
InfoAPI: true,
ConfigAPI: true,
UpdateAPI:true,
addToHomescreen: true
},
browser: true,

View File

@ -12,12 +12,13 @@
var _title;
var _intro;
var _drawItem;
//var _updateItem;
var _updateItem;
var _joinNetworkItem;
var _defaultItems;
var _networkStatus;
var _networkAPI = new NetworkAPI();
var _updateAPI = new UpdateAPI();
var _boxData = {};
var _retryRetrieveStatusDelay;
var _retryRetrieveStatusDelayTime = 3000;
@ -34,7 +35,7 @@
_defaultItems = _list.children();
_drawItem = _list.find("#drawItem");
//_updateItem = _list.find("#updateItem");
_updateItem = _list.find("#updateItem");
_joinNetworkItem = _list.find("#joinNetworkItem");
// make sure draw link is opened in same WebApp (added to homescreen)
@ -59,6 +60,8 @@
_networkAPI.init(boxURL);
retrieveNetworkStatus();
_updateAPI.init(boxURL);
retrieveUpdateStatus();
});
$.mobile.document.on( "pagebeforehide", PAGE_ID, function( event, data ) {
clearTimeout(_retryRetrieveStatusDelay);
@ -74,7 +77,6 @@
_retryRetrieveStatusDelay = setTimeout(_self.retrieveStatus, _retryRetrieveStatusDelayTime); // retry after delay
});
}
function setNetworkStatus(status) {
console.log(PAGE_ID+":setNetworkStatus: ",status);
var introText = "";
@ -85,7 +87,10 @@
// display the right buttons
_defaultItems.toggleClass("ui-screen-hidden",false);
_joinNetworkItem.toggleClass("ui-screen-hidden",true);
// ToDo: retrieve update information
var updateLink = _updateItem.find("a").attr("href");
updateLink = d3d.util.replaceURLParameters(updateLink,_boxData);
_updateItem.find("a").attr("href",updateLink);
} else { // offline
//console.log("offline");
@ -111,4 +116,16 @@
_list.listview('refresh'); // jQuery mobile enhance content
_networkStatus = status;
}
function retrieveUpdateStatus() {
console.log(PAGE_ID+":retrieveUpdateStatus");
var updateCounter = _list.find("#updateItem .ui-li-count");
updateCounter.hide();
_updateAPI.status(function(data) { // completed
console.log("UpdateAPI:refresh:completed");
var canUpdate = data.can_update;
updateCounter.text(canUpdate? 1 : 0);
updateCounter.show();
});
}
})(window);

200
js/UpdatePage.js Normal file
View File

@ -0,0 +1,200 @@
/*
* 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 _form;
var _statusField;
var _infoField;
var _retainConfigurationCheckbox;
var _includeBetasCheckbox;
var _submitButton;
var _updateAPI = new UpdateAPI();
var _configAPI = new ConfigAPI();
var _pageData = {};
var _updateStatus = {};
var PAGE_ID = "#update";
var _self = this;
$.mobile.document.on( "pageinit", PAGE_ID, function( event, data ) {
//console.log(PAGE_ID+":pageinit");
_page = $(this);
_statusField = _page.find("#status");
_infoField = _page.find("#info");
_form = _page.find("form");
_retainConfigurationCheckbox = _form.find("#retainConfiguration");
_includeBetasCheckbox = _form.find("#includeBetas");
_submitButton = _form.find("input[type=submit]");
_retainConfigurationCheckbox.change(retainConfigurationChanged);
_includeBetasCheckbox.change(includeBetasChanged);
_form.submit(update);
// make sure links in (checkbox) labels are clickable
_form.find("label a").click(function(event) {
event.stopPropagation();
});
});
$.mobile.document.on( "pagebeforeshow", PAGE_ID, function( event, data ) {
//console.log(PAGE_ID+":pagebeforeshow");
_pageData = d3d.util.getPageParams(PAGE_ID);
if(_pageData === undefined) {
$.mobile.changePage("#boxes");
return;
}
var boxURL = "http://"+_pageData.localip;
_statusField.text("");
_submitButton.button('disable');
_submitButton.val("Update");
_submitButton.button("refresh");
_infoField.html("");
_updateAPI.init(boxURL);
retrieveUpdateStatus();
_configAPI.init(boxURL);
});
$.mobile.document.on( "pagebeforehide", PAGE_ID, function( event, data ) {
//console.log(PAGE_ID+":pagebeforehide");
});
function retrieveUpdateStatus() {
console.log(PAGE_ID+":retrieveUpdateStatus");
_submitButton.button('disable');
_updateAPI.status(function(data) { // completed
console.log(PAGE_ID+":retrieveUpdateStatus:completed");
updatePage(data);
_updateStatus = data;
});
}
function updatePage(data) {
// Status
var status = "";
switch(data.state_code){
case UpdateAPI.STATUS.NONE:
if(data.can_update) {
status = "Update available";
} else {
status = "You're up to date.";
}
break;
case UpdateAPI.STATUS.DOWNLOADING:
status = "Downloading update...";
break;
case UpdateAPI.STATUS.DOWNLOAD_FAILED:
status = "Downloading update failed.";
break;
case UpdateAPI.STATUS.IMAGE_READY:
status = "Update downloaded.";
break;
case UpdateAPI.STATUS.INSTALLING:
status = "Installing update... ";
break;
case UpdateAPI.STATUS.INSTALLED:
status = "Update complete!";
break;
case UpdateAPI.STATUS.INSTALL_FAILED:
status = "Installing update failed.";
break;
}
_statusField.text(status);
// Button
updateButton(data);
// Info
var html = 'Current version: ' + data.current_version;
if (data.current_release_date) {
html += ' (released: ' + formatDate(data.current_release_date) + ')';
}
var localReleasenotes = "http://"+_pageData.localip+"/ReleaseNotes.html";
html += ' (<a target="d3d-curr-relnotes" href="'+localReleasenotes+'">release notes</a>).';
if(data.can_update) {
html += '<br/>Latest version: ' + data.newest_version;
if (data.newest_release_date) {
html += ' (released: ' + formatDate(data.newest_release_date) + ')';
}
html += ' (<a target="d3d-new-relnotes" href="http://doodle3d.com/releasenotes/">release notes</a>).';
}
_infoField.html(html);
}
function updateButton(data) {
console.log(PAGE_ID+":updateButton");
var retain = _retainConfigurationCheckbox.prop('checked');
var buttonText = "Update";
_submitButton.button('disable');
switch(data.state_code) {
case UpdateAPI.STATUS.NONE:
case UpdateAPI.STATUS.IMAGE_READY:
case UpdateAPI.STATUS.DOWNLOAD_FAILED:
case UpdateAPI.STATUS.INSTALL_FAILED:
if(data.can_update || !retain) {
_submitButton.button('enable');
if (data.newest_version_is_beta) {
if(retain) {
buttonText = "Update to beta";
} else {
buttonText = "Clean update to beta";
}
} else if (data.current_version_is_beta && !data.newest_version_is_newer) {
if(retain) {
buttonText = "Revert to latest stable release";
} else {
buttonText = "Clean revert to latest stable release";
}
} else if (!retain){
if(data.newest_version_is_newer) {
buttonText = "Clean update";
} else {
buttonText = "Reinstall";
}
}
}
break;
}
_submitButton.val(buttonText);
_submitButton.button("refresh");
}
function formatDate(ts) {
if (!ts || ts.length !== 8 || !/^[0-9]+$/.test(ts)) { return null; }
var fields = [ ts.substr(0, 4), ts.substr(4, 2), ts.substr(6, 2) ];
if (!fields || fields.length !== 3 || fields[1] > 12) { return null; }
var abbrMonths = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Sep', 'Aug', 'Oct', 'Nov', 'Dec' ];
return abbrMonths[fields[1] - 1] + " " + fields[2] + ", " + fields[0];
}
function retainConfigurationChanged () {
console.log(PAGE_ID+":retainConfigurationChanged");
updateButton(_updateStatus);
}
function includeBetasChanged () {
console.log(PAGE_ID+":includeBetasChanged");
var settings = {};
settings[_includeBetasCheckbox.attr('name')] = _includeBetasCheckbox.prop('checked');
_configAPI.save(settings,function() {
console.log(" saved");
retrieveUpdateStatus();
});
}
function update() {
console.log(PAGE_ID+":update");
var submitLink = _form.data("target");
submitLink = d3d.util.replaceURLParameters(submitLink,_pageData);
$.mobile.changePage(submitLink);
return false;
}
})(window);

154
js/UpdatingPage.js Normal file
View File

@ -0,0 +1,154 @@
/*
* 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 _descriptionField;
var _updateAPI = new UpdateAPI();
var _configAPI = new ConfigAPI();
var _pageData = {};
var _formData = {};
var _updateStatus = {};
var _installing = false;
var UPDATED_REDIRECT_DELAY = 5000;
var _updatedRedirectDelay;
var PAGE_ID = "#updating";
var _self = this;
$.mobile.document.on( "pageinit", PAGE_ID, function( event, data ) {
//console.log(PAGE_ID+":pageinit");
_page = $(this);
_statusField = _page.find("#status");
_descriptionField = _page.find("#description");
});
$.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;
}
_formData = d3d.util.getFormData(form);
var boxURL = "http://"+_pageData.localip;
_updateAPI.init(boxURL);
_updateAPI.refreshing = onRefreshing;
_updateAPI.updated = onStatusUpdated;
downloadUpdate();
_updateAPI.startAutoRefresh();
});
$.mobile.document.on( "pagebeforehide", PAGE_ID, function( event, data ) {
//console.log(PAGE_ID+":pagebeforehide");
_updateAPI.stopAutoRefresh();
clearTimeout(_updatedRedirectDelay);
});
function downloadUpdate() {
console.log(PAGE_ID+":downloadUpdate");
_updateAPI.download();
// override state
_updateStatus.state_code = UpdateAPI.STATUS.DOWNLOADING;
updatePage(_updateStatus);
}
function installUpdate() {
console.log(PAGE_ID+":installUpdate");
var no_retain = !(_formData.retain);
console.log(" no_retain: ",no_retain);
_updateAPI.install(no_retain);
_installing = true;
}
function onRefreshing() {
//console.log("ConnectingToNetworkPage:onRefreshing");
d3d.util.showLoader(true);
}
function onStatusUpdated(data) {
console.log(PAGE_ID+": onStatusUpdated ");
console.log(" state_code: ",data.state_code," text: ",data.state_text);
switch(data.state_code) {
case UpdateAPI.STATUS.IMAGE_READY:
console.log(" _installing: ",_installing);
if(!_installing) {
installUpdate();
data.state_code = UpdateAPI.STATUS.INSTALLING;
}
break;
case UpdateAPI.STATUS.NONE:
console.log(" _installing: ",_installing);
if(_installing) {
data.state_code = UpdateAPI.STATUS.INSTALLED;
_updateAPI.stopAutoRefresh();
clearTimeout(_updatedRedirectDelay);
_updatedRedirectDelay = setTimeout(function () {
// redirect to box page
console.log(" redirect to box");
// replace this page with boxes page in history
window.history.replaceState(null, "", "#boxes");
var link = "#box";
link = d3d.util.replaceURLParameters(link,_pageData);
$.mobile.changePage(link);
},UPDATED_REDIRECT_DELAY);
}
break;
}
updatePage(data);
_updateStatus = data;
}
function updatePage(data) {
console.log(PAGE_ID+": updatePage state: ",data.state_code);
var status = "";
switch(data.state_code){
case UpdateAPI.STATUS.DOWNLOADING:
status = "Downloading update...";
break;
case UpdateAPI.STATUS.DOWNLOAD_FAILED:
status = "Downloading update failed";
break;
case UpdateAPI.STATUS.IMAGE_READY:
status = "Update downloaded";
break;
case UpdateAPI.STATUS.INSTALLING:
status = "Installing update... ";
break;
case UpdateAPI.STATUS.INSTALLED:
status = "Update complete!";
break;
case UpdateAPI.STATUS.INSTALL_FAILED:
status = "Installing update failed";
break;
}
console.log(" status: ",status);
_statusField.text(status);
// description
var description = "";
switch(data.state_code){
case UpdateAPI.STATUS.INSTALLING:
description = "Do not remove power from the WiFi-Box.";
break;
case UpdateAPI.STATUS.DOWNLOAD_FAILED:
case UpdateAPI.STATUS.INSTALL_FAILED:
description = data.state_text;
break;
}
console.log(" description: ",description);
_descriptionField.text(description);
}
})(window);

144
js/api/UpdateAPI.js Normal file
View File

@ -0,0 +1,144 @@
/*
* 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 UpdateAPI() {
// states from api, see Doodle3D firmware src/script/d3d-updater.lua
UpdateAPI.STATUS = {
NONE: 1, // default state
DOWNLOADING: 2,
DOWNLOAD_FAILED:3,
IMAGE_READY: 4, // download successful and checked
INSTALLING: 5,
INSTALLED: 6,
INSTALL_FAILED: 7
};
var _apiPath = "/d3dapi";
var _apiCGIPath = "/cgi-bin"+_apiPath;
var _wifiboxURL;
var _wifiboxCGIBinURL;
var _timeoutTime = 3000;
this.state; // update state from api
this.stateText = ""; // update state text from api
var _autoRefreshing = false;
var _refreshDelay;
this.refreshDelayTime = 2000;
//callbacks
this.refreshing; // I'm refreshing
this.updated; // New network status info
var _self = this;
this.init = function(wifiboxURL) {
_wifiboxURL = wifiboxURL+_apiPath;
_wifiboxCGIBinURL = wifiboxURL+_apiCGIPath;
}
this.status = function(completeHandler,failedHandler) {
//console.log("UpdateAPI:status");
$.ajax({
url: _wifiboxURL + "/update/status",
type: "GET",
dataType: 'json',
timeout: _timeoutTime,
success: function(response){
//console.log("UpdateAPI:status response: ",response);
if(response.status == "error" || response.status == "fail") {
if(failedHandler) failedHandler(response);
} else {
var data = response.data;
data.current_version_is_beta = versionIsBeta(data.current_version);
data.newest_version_is_beta = versionIsBeta(data.newest_version);
if(data.newest_release_date && data.current_release_date) {
data.newest_version_is_newer = (data.newest_release_date - data.current_release_date > 0);
} else {
data.newest_version_is_newer = true;
}
completeHandler(response.data);
}
}
}).fail(function() {
if(failedHandler) failedHandler();
});
}
this.download = function(completeHandler,failedHandler) {
//console.log("UpdateAPI:download");
$.ajax({
url: _wifiboxURL + "/update/download",
type: "POST",
dataType: 'json',
success: function(response){
//console.log("UpdatePanel:downloadUpdate response: ",response);
if(response.status == "error" || response.status == "fail") {
if(failedHandler) failedHandler(response);
} else {
var data = response.data;
completeHandler(response.data);
}
}
}).fail(function() {
//console.log("UpdatePanel:downloadUpdate: failed");
if(failedHandler) failedHandler();
});
}
this.install = function(noRetain, completeHandler,failedHandler) {
//console.log("UpdateAPI:install");
var postData = {no_retain:noRetain};
$.ajax({
url: _wifiboxURL + "/update/install",
type: "POST",
data: postData,
dataType: 'json',
success: function(response){
//console.log("UpdatePanel:installUpdate response: ",response);
}
}).fail(function() {
//console.log("UpdatePanel:installUpdate: no respons (there shouldn't be)");
});
}
this.startAutoRefresh = function(delay,refreshingHandler,updatedHandler) {
if(delay !== undefined) { _self.refreshDelayTime = delay; }
if(refreshingHandler !== undefined) { _self.refreshing = refreshingHandler; }
if(updatedHandler !== undefined) { _self.updated = updatedHandler; }
_autoRefreshing = true;
_self.refresh();
}
this.stopAutoRefresh = function() {
_autoRefreshing = false;
clearTimeout(_refreshDelay);
}
this.refresh = function() {
//console.log("UpdateAPI:refresh");
if(_self.refreshing) { _self.refreshing(); }
_self.status(function(data) { // completed
//console.log("UpdateAPI:refresh:completed");
if(_self.updated !== undefined &&
_self.state !== data.state_code) {
_self.state = data.state_code;
_self.updated(data);
}
if(_autoRefreshing) {
clearTimeout(_refreshDelay);
_refreshDelay = setTimeout(_self.refresh, _self.refreshDelayTime);
}
},function() { // failed
if(_autoRefreshing) {
// retry
clearTimeout(_refreshDelay);
_refreshDelay = setTimeout(_self.refresh, _self.refreshDelayTime);
}
});
}
function versionIsBeta(version) {
return version ? /.*-.*/g.test(version) : null;
}
}

View File

@ -74,6 +74,16 @@ body.ui-mobile-viewport {
}
}
form .ui-input-btn {
background-color: #60ACF0;
color: #fff;
text-shadow: 0px 1px 0px rgb(92, 152, 198);
&:hover {
background-color: #3F87D9;
color: #fff;
text-shadow: 0px 1px 0px rgb(92, 152, 198);
}
}
}
/* Active button */
.ui-page-theme-a .ui-btn.ui-btn-active,

View File

@ -53,7 +53,7 @@
<p class="intro"></p>
<ul data-role="listview">
<li id="drawItem"><a href="#draw">Draw</a></li>
<!-- <li id="updateItem"><a href="#update">Update</a></li> -->
<li id="updateItem"><a href="#update">Update<span class="ui-li-count"></span></a></li>
<li id="joinNetworkItem"><a href="#join_network">Join network</a></li>
<li><a href="#yourapp">Your app here?</a></li>
</ul>
@ -183,10 +183,37 @@
<div data-role="page" id="update">
<a href="#boxes" id="logo"><img src="img/logo_full.png"></a>
<div data-role="header">
<a href="../toolbar/" data-rel="back" class="ui-btn ui-btn-left ui-nodisc-icon ui-corner-all ui-btn-icon-notext ui-icon-carat-l">Back</a>
<a href="../toolbar/" data-rel="back"
class="ui-btn ui-btn-left ui-nodisc-icon ui-corner-all ui-btn-icon-notext ui-icon-carat-l">Back</a>
<h1>Update</h1>
</div><!-- /header -->
<div role="main" class="ui-content">
<h3 id="status"></h3>
<form data-ajax="false" data-target="#updating">
<fieldset class="" data-role="collapsible">
<h3>Options</h3>
<input id="retainConfiguration" type="checkbox" name="retain" value="retainConfiguration" checked="true">
<label for="retainConfiguration">Preserve personal sketches and settings</label>
<input id="includeBetas" type="checkbox" name="doodle3d.update.includeBetas" value="includeBetas">
<label for="includeBetas">Include beta releases (<a href="#beta_info">more info</a>)</label>
</fieldset>
<input type=submit value="Update">
</form>
<small id="info"></small>
</div><!-- /content -->
</div><!-- /page -->
<div data-role="page" id="updating">
<a href="#boxes" id="logo"><img src="img/logo_full.png"></a>
<div data-role="header">
<a href="../toolbar/" data-rel="back"
class="ui-btn ui-btn-left ui-nodisc-icon ui-corner-all ui-btn-icon-notext ui-icon-carat-l">Back</a>
<h1>Updating...</h1>
</div><!-- /header -->
<div role="main" class="ui-content">
<h3 id="status"></h3>
<p id=description></p>
</div><!-- /content -->
</div><!-- /page -->
@ -203,6 +230,19 @@
</div><!-- /page -->
<div data-role="page" id="beta_info">
<a href="#boxes" id="logo"><img src="img/logo_full.png"></a>
<div data-role="header">
<a href="../toolbar/" data-rel="back" class="ui-btn ui-btn-left ui-nodisc-icon ui-corner-all ui-btn-icon-notext ui-icon-carat-l">Back</a>
<h1>Beta info</h1>
</div><!-- /header -->
<div role="main" class="ui-content">
<p>You can subscribe for Doodle3D beta software, this means you'll get the latest versions that are still under development. Only use this when you're prepared to deal with some bugs.</p>
<p>This allows you to test new features and give us earlier feedback. The more people that test these versions the more stable the regular version will be.</p>
</div><!-- /content -->
</div><!-- /page -->
<script>
$(function(){
$( "[data-role='header'], [data-role='footer']" ).toolbar();