Merge branch 'update'

This commit is contained in:
peteruithoven 2015-06-16 17:40:28 +02:00
commit 5e451d13c1
7 changed files with 625 additions and 8 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,7 @@
_networkAPI.init(boxURL);
retrieveNetworkStatus();
_updateAPI.init(boxURL);
});
$.mobile.document.on( "pagebeforehide", PAGE_ID, function( event, data ) {
clearTimeout(_retryRetrieveStatusDelay);
@ -74,7 +76,6 @@
_retryRetrieveStatusDelay = setTimeout(_self.retrieveStatus, _retryRetrieveStatusDelayTime); // retry after delay
});
}
function setNetworkStatus(status) {
console.log(PAGE_ID+":setNetworkStatus: ",status);
var introText = "";
@ -85,7 +86,12 @@
// 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);
retrieveUpdateStatus();
} else { // offline
//console.log("offline");
@ -111,4 +117,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 _noRetainCheckbox;
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");
_noRetainCheckbox = _form.find("#noRetainConfiguration");
_includeBetasCheckbox = _form.find("#includeBetas");
_submitButton = _form.find("input[type=submit]");
_noRetainCheckbox.change(noRetainCheckboxChanged);
_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 noRetain = _noRetainCheckbox.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 || noRetain) {
_submitButton.button('enable');
if (data.newest_version_is_beta) {
if(noRetain) {
buttonText = "Clean update to beta";
} else {
buttonText = "Update to beta";
}
} else if (data.current_version_is_beta && !data.newest_version_is_newer) {
if(noRetain) {
buttonText = "Clean revert to latest stable release";
} else {
buttonText = "Revert to latest stable release";
}
} else if (noRetain){
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 noRetainCheckboxChanged () {
//console.log(PAGE_ID+":noRetainCheckboxChanged");
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);

151
js/UpdatingPage.js Normal file
View File

@ -0,0 +1,151 @@
/*
* 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 _installing = false;
var UPDATED_REDIRECT_DELAY = 5000;
var _updatedRedirectDelay;
var _no_retain;
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);
//console.log(" _formData: ",_formData);
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();
}
function installUpdate() {
//console.log(PAGE_ID+":installUpdate");
//console.log(" _formData: ",_formData);
_no_retain = (_formData.no_retain)? true : false;
//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);
updatePage(data);
switch(data.state_code) {
case UpdateAPI.STATUS.IMAGE_READY:
if(!_installing) {
installUpdate();
}
break;
case UpdateAPI.STATUS.INSTALLED:
_updateAPI.stopAutoRefresh();
clearTimeout(_updatedRedirectDelay);
_updatedRedirectDelay = setTimeout(function () {
if(_no_retain) {
//console.log(" redirect to boxes");
$.mobile.changePage("#boxes");
} else {
//console.log(" redirect to box");
// replace this page with boxes page in history
window.history.replaceState(null, "", "#boxes");
var link = d3d.util.replaceURLParameters("#box",_pageData);
$.mobile.changePage(link);
}
},UPDATED_REDIRECT_DELAY);
break;
}
}
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.";
if(_no_retain) {
description += "<br/><small>Because you didnt preserve your personal sketches and settings you will need to reconnect your WiFi-Box to your WiFi network. <br/>After an estimated update time you will be redirected to the boxes page. When it does, please connect your device to the WiFi network of the WiFi-Box and return to the boxes page. </small>";
}
break;
case UpdateAPI.STATUS.DOWNLOAD_FAILED:
case UpdateAPI.STATUS.INSTALL_FAILED:
description = data.state_text;
break;
}
console.log(" description: ",description);
_descriptionField.html(description);
}
})(window);

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

@ -0,0 +1,192 @@
/*
* 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;
var _installing = false;
var _ignoreNextStatusResponse = false;
// When the updater doesn't preserve settings the box can't reconnect
// to the same network, so we can't retrieve whether the update was
// successfull, so we override the state to INSTALLED after a fixed delay
var INSTALL_TIME = 90*1000;
var _installedDelayer; // setTimout instance
//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(_ignoreNextStatusResponse) {
_ignoreNextStatusResponse = false;
return;
}
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;
}
if(_installing && data.state_code === UpdateAPI.STATUS.NONE) {
data.state_code = UpdateAPI.STATUS.INSTALLED;
_installing = false;
}
completeHandler(data);
}
}
}).fail(function() {
if(_ignoreNextStatusResponse) {
_ignoreNextStatusResponse = false;
return;
}
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;
if(completeHandler) completeHandler(response.data);
}
}
}).fail(function() {
//console.log("UpdatePanel:downloadUpdate: failed");
if(failedHandler) failedHandler();
});
overrideStatus(UpdateAPI.STATUS.DOWNLOADING);
}
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);
if(response.status == "error" || response.status == "fail") {
if(failedHandler) failedHandler(response);
} else {
var data = response.data;
if(completeHandler) completeHandler(response.data);
}
}
}).fail(function() {
//console.log("UpdatePanel:installUpdate: no respons (there shouldn't be)");
});
overrideStatus(UpdateAPI.STATUS.INSTALLING);
clearTimeout(_installedDelayer);
if(noRetain) {
_installedDelayer = setTimeout(function() {
overrideStatus(UpdateAPI.STATUS.INSTALLED);
},INSTALL_TIME);
}
_installing = true;
}
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 overrideStatus(status) {
_self.state = status;
var data = {state_code:status,override:true};
if(_self.updated) {
_self.updated(data);
}
if(_autoRefreshing) {
_ignoreNextStatusResponse = true;
_self.refresh();
}
}
function versionIsBeta(version) {
return version ? /.*-.*/g.test(version) : null;
}
}

View File

@ -74,6 +74,32 @@ 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);
}
}
.warning {
dispay: none;
display: none;
font-weight: normal;
&.off {
display: block;
}
}
.ui-checkbox-on .warning {
display: block;
&.off {
display: none;
}
}
}
}
/* Active button */
.ui-page-theme-a .ui-btn.ui-btn-active,

View File

@ -1,4 +1,3 @@
<!DOCTYPE html>
<html>
<head>
@ -53,7 +52,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 +182,41 @@
<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="noRetainConfiguration" type="checkbox" name="no_retain" value="noRetainConfiguration" />
<label for="noRetainConfiguration">Remove sketches and settings<small class="warning">You're personal sketches and settings will be removed when you update, this will mean youll need to reconnect your WiFi-Box to your WiFi network. </small></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>)-->
<small class="warning">You'll get the latest features that are still under development. <br/>
This allows you give us earlier feedback. The more people that test these versions the more stable the regular version will be. Only use this when you're prepared to deal with some issues.
</small>
</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 -->
@ -202,7 +232,6 @@
</div><!-- /content -->
</div><!-- /page -->
<script>
$(function(){
$( "[data-role='header'], [data-role='footer']" ).toolbar();