diff --git a/Makefile b/Makefile index 62c01a0..9be84e2 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,9 @@ endif endef define Package/doodle3d-client/install + $(INSTALL_DIR) $(1)/www + $(INSTALL_DIR) $(1)/www/filemanager $(INSTALL_DIR) $(1)/www/css $(INSTALL_DIR) $(1)/www/img #$(INSTALL_DIR) $(1)/www/js @@ -69,6 +71,7 @@ define Package/doodle3d-client/install $(CP) $(PKG_BUILD_DIR)/www/css/styles.min.css $(1)/www/css/ $(CP) $(PKG_BUILD_DIR)/www/img/* $(1)/www/img/ + $(CP) $(PKG_BUILD_DIR)/www/filemanager/* $(1)/www/filemanager/ ifeq ($(CONFIG_DOODLE3D_CLIENT_MINIFY_JS),y) $(CP) $(PKG_BUILD_DIR)/www/js/doodle3d-client.min.js $(1)/www/js/ diff --git a/README.md b/README.md index 3c85697..e1808cf 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,10 @@ Doodle3D client app # How to build ## Prerequisites -- get `npm`, the Node.js package manager, for instance using macports on OSX. -- (prerequisite) install Grunt: `sudo pm install -g grunt-cli`. -- run `npm install` in the project root to install project dependencies +- install npm, the Node.js package manager: `sudo port install npm` +- install Grunt: `sudo npm install -g grunt-cli` +- run `npm install` in the **project root** to install project dependencies -Finally run grunt to build minified css and js files. By default, it will keep +## Build +- run `grunt` in the project root to to build minified css and js files. By default, it will keep running to automatically rebuild when source files are changed. diff --git a/js/AddScanDialog.js b/js/AddScanDialog.js new file mode 100644 index 0000000..4e59ccf --- /dev/null +++ b/js/AddScanDialog.js @@ -0,0 +1,41 @@ +//var shapeResolution=3; +var shapePopup; + +function initScanDialog() { + scanPopup = new Popup($("#popupScan"), $("#popupMask")); + $("#btnScanOk").on("onButtonClick", onBtnScanOk); + $("#btnCloseScan").on("onButtonClick", onBtnCloseScan); +} + +function onBtnCloseScan() { + $('#imgGuide').hide(); + $('#btnCloseScan').hide(); +} + +function onBtnScanOk() { + scanPopup.commit(); +} + +function showScanDialog() { + scanPopup.open(); +} + +function readURL(input) { + + if (input.files && input.files[0]) { + var reader = new FileReader(); + + reader.onload = function (e) { + $('#imgGuide').attr('src', e.target.result); + $('#imgGuide').show(); + $('#btnCloseScan').show(); + scanPopup.commit(); + } + + reader.readAsDataURL(input.files[0]); + } +} + +$("#fileScan").change(function(){ + readURL(this); +}); diff --git a/js/Keyboard.js b/js/Keyboard.js index 825e514..7559414 100644 --- a/js/Keyboard.js +++ b/js/Keyboard.js @@ -49,8 +49,8 @@ function initKeyboard() { var ch = String.fromCharCode(event.which); switch (ch) { - case 'c': clearDoodle(); break; - case 'n': clearDoodle(); break; + case 'c': newSketch(); break; + case 'n': newSketch(); break; case 'p': print(); break; case 'u': oopsUndo(); break; case 'g': settingsWindow.downloadGcode(); break; @@ -59,13 +59,14 @@ function initKeyboard() { case 'h': previewUp(true); break; case 'H': previewDown(true); break; case 's': saveSketch(); break; - case 'L': nextDoodle(); break; - case 'l': prevDoodle(); break; + case 'L': nextSketch(); break; + case 'l': prevSketch(); break; case '[': previewTwistLeft(); break; case ']': previewTwistRight(); break; case '|': resetTwist(); break; case 't': showWordArtDialog(); break; case 'i': showShapeDialog(); break; + case 'T': showScanDialog(); break; case ';': moveShape(-5,0); break; case '\'': moveShape(5,0); break; diff --git a/js/Svg.js b/js/Svg.js index b93c90f..1f93ac6 100644 --- a/js/Svg.js +++ b/js/Svg.js @@ -56,6 +56,8 @@ function loadFromSvg(svgData) { clearDoodle(); + svgData = svgData.replace("M0,0 ",""); //RC: hack + var p = svgData.indexOf(" numSavedSketches) currentSketchId = numSavedSketches; - if (currentSketchId < 1) currentSketchId = 1; - - //update textbox - //$("#txtSketch").val(currentSketchId); - - updatePrevNextButtonState(); -} - -function updatePrevNextButtonStateOnClear() { - if (numSavedSketches > 0) btnPrevious.enable(); - btnNext.disable(); - currentSketchId = numSavedSketches+1; //after the end of the list - btnSave.disable(); -} - -function updatePrevNextButtonState() { - - //btnPrevious state - if (numSavedSketches==0 || currentSketchId < 2) { - btnPrevious.disable(); - } else { - btnPrevious.enable(); + if (isModified) { + btnSave.enable(); + } + else { + btnSave.disable(); } - //btnNext state - if (numSavedSketches==0 || currentSketchId >= numSavedSketches) { - btnNext.disable(); - } else { + if (curSketch0) { + btnPrevious.enable(); + } else { + btnPrevious.disable(); + } + } -function loadSketch(sketchId) { +function loadSketch(_curSketch) { + curSketch = _curSketch; - $.ajax({ - url: wifiboxURL + "/sketch/" + sketchId, - dataType: 'json', - type: 'GET', -// timeout: this.timeoutTime, - success: function(response) { - if (response.status == 'error' || response.status == 'fail') { - console.log("loadSketch fail/error: " + response.msg + " -- ", response); - } else { - console.log("loadSketch success: loaded id #" + response.data.id, response); - //console.log("sketch content:\n" + response.data.data); - if (loadFromSvg(response.data.data)) { - setSketchModified(false, true); - setCurrentSketchId(response.data.id); - } - } + if (curSketch<0) curSketch=0; + if (curSketch>sketches.length-1) curSketch=sketches.length-1; + + var id = sketches[curSketch]; + + console.log('sketch: loadSketch curSketch',curSketch,'id',id); + + $.get(wifiboxURL + "/sketch", {id:id}, function(response) { + if (response.status=='success') { + console.log('sketch: loaded',response); + var svgData = response.data.data; + loadFromSvg(svgData); + setSketchModified(false); + } else { + console.log('error loading sketch: ',response); + listSketches(); } - }).fail(function() { - console.log("loadSketch failed: ", response); - }); + + }) } function saveSketch() { - svg = saveToSvg(); - console.log("generated SVG [" + _points.length + " points, " + svg.length + " bytes]:\n" + svg); + console.log("sketch: saveSketch"); + var svgData = saveToSvg(); + + $.post(wifiboxURL + "/sketch", {data: svgData}, function(response) { + console.log("sketch: saveSketch: response",response); + listSketches(); + }) - $.ajax({ - url: wifiboxURL + "/sketch", - dataType: 'json', - type: 'POST', - data: { data: svg }, - //timeout: this.timeoutTime, - success: function(response) { - if (response.status == 'error' || response.status == 'fail') { - console.log("saveSketch fail/error: " + response.msg + " -- ", response); - } else { - console.log("saveSketch success: saved with id #" + response.data.id, response); - setSketchModified(false, true); - numSavedSketches = response.data.id; - setCurrentSketchId(response.data.id); - } - } - }).fail(function() { - console.log("saveSketch failed: ", response); - }); -} - -function prevDoodle(e) { - console.log("f:prevDoodle(): " + currentSketchId + " / " + numSavedSketches); - //alert('prev ' + numSavedSketches); - //return; - - //TODO: if (enabled) { - var sketchId = (currentSketchId > 0) ? currentSketchId : numSavedSketches; - if (sketchId > 1) sketchId--; - - - //alert(sketchId); - - loadSketch(sketchId); - //} -} - -function nextDoodle(e) { - console.log("f:nextDoodle()"); - //alert('next ' + numSavedSketches); - //return; - - //TODO: if (enabled) { - var sketchId = (currentSketchId > 0) ? currentSketchId : numSavedSketches; - if (sketchId < numSavedSketches) sketchId++; - loadSketch(sketchId); - //} } diff --git a/less/base_centerpanel.less b/less/base_centerpanel.less index 56cf46a..085c4c9 100644 --- a/less/base_centerpanel.less +++ b/less/base_centerpanel.less @@ -57,6 +57,7 @@ left: 0; width: 78%; height: 100%; + text-align: center; } #mycanvas { @@ -127,4 +128,24 @@ } +#mycanvas { + position: absolute; + left: 0px; + top: 0px; +} +#imgGuide { + // z-index: -1000; + // position: absolute; + // -webkit-filter: contrast(400%); + //-webkit-filter: brightness(100%); + // opacity: 50%; + opacity: 0.4; + filter: alpha(opacity=40); + pointer-events:none; + + max-width: 100%; + max-height: 100%; + height: auto; + margin-left: auto; +} diff --git a/less/buttons.less b/less/buttons.less index 1c4f032..388186d 100644 --- a/less/buttons.less +++ b/less/buttons.less @@ -130,6 +130,16 @@ left: 4px; position: absolute; } + +#btnCloseScan { + top: 4px; + right: 8px; + position: absolute; + display: none; + opacity: 0.7; + filter: alpha(opacity=70); +} + #buttonGroupEdit { position: absolute; top: 5px; @@ -163,10 +173,10 @@ margin-top: -60%; margin-left: 70%; width: 200%; - max-width: 140px; /*fixme: can this grow based on it's content?*/ + max-width: 240px; /*fixme: can this grow based on it's content?*/ padding: 5% 0 5% 5%; - #btnWordArt, #btnShape { - width: 45%; + #btnWordArt, #btnShape, #btnScan { + width: 30%; } } diff --git a/less/popup.less b/less/popup.less index 93646b8..38700d0 100644 --- a/less/popup.less +++ b/less/popup.less @@ -47,6 +47,21 @@ } } +#popupScan { + width: 330px; + height:210px; + margin-left: -165px; + margin-top: -105px; + + input[type="file"] { + width: 98%; + } + #btnScanOk { + float: right; + margin: 15px 0 0 0; + } +} + #popupShape { width: 310px; margin-left: -155px; diff --git a/www/css/settings.css b/www/css/settings.css index 01b4425..d4174a8 100644 --- a/www/css/settings.css +++ b/www/css/settings.css @@ -17,6 +17,7 @@ body,th,td { /*min-width: 370px;*/ width: 100%; height: 100%; + overflow: scroll; } /*form#settingsForm {*/ /*width: 100% auto;*/ diff --git a/www/filemanager/css/style.css b/www/filemanager/css/style.css new file mode 100644 index 0000000..1a91b0b --- /dev/null +++ b/www/filemanager/css/style.css @@ -0,0 +1,49 @@ +body { + font-family: Helvetica, Abel, Arial; + font-size: 1em; + + -webkit-user-select: none; /* webkit (safari, chrome) browsers */ + -moz-user-select: none; /* mozilla browsers */ + -khtml-user-select: none; /* webkit (konqueror) browsers */ + -ms-user-select: none; /* IE10+ */ +} + +button { + font-size: 1em; +} + +div.item { + width:150px; + height:130px; + border:1px solid black; + display: inline-block; + overflow: hidden; + background-color: white; + margin-right: 5px; +} + +div.item.selected { + background-color: #7cf; +} + +div.item input[type='checkbox'] { + position: absolute; +} + +#frmUpload { + display: inline; +} + +input[type='file'] { + width: 1px; + opacity:0 +} + +#txtInfo { + float: right; + /*display: inline-block;*/ +} + +img#logo { + cursor: pointer; +} \ No newline at end of file diff --git a/www/filemanager/index.html b/www/filemanager/index.html new file mode 100644 index 0000000..a55bcf0 --- /dev/null +++ b/www/filemanager/index.html @@ -0,0 +1,30 @@ + + + + Doodle3D + + + + + +
+ + + + + +
+ + + +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/www/filemanager/js/main.js b/www/filemanager/js/main.js new file mode 100644 index 0000000..6771203 --- /dev/null +++ b/www/filemanager/js/main.js @@ -0,0 +1,267 @@ +/* + * This file is part of the Doodle3D project (http://doodle3d.com). + * + * Copyright (c) 2014, 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. + */ + +// http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery +function getURLParameter(name) { + return decodeURI((new RegExp('[&?]'+name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1]); +} + +var wifiboxURL = ""; + +if (getURLParameter("r") != "null") wifiboxURL = 'http://192.168.5.1'; +if (getURLParameter("wifiboxURL") != "null") wifiboxURL = getURLParameter("wifiboxURL"); + +var api = wifiboxURL+'/d3dapi/sketch/'; + +$("#logo").click(onLogoClick) +$("#btnDelete").click(deleteSelectedSketches); +$("#btnSelectAll").click(selectAll); +$("#btnDeselectAll").click(deselectAll); +$("#uploads").change(upload); +$("#btnDownload").click(download); + +$("#btnUpload").click(function(e) { + e.preventDefault(); + $("#uploads").trigger('click'); +}); + +var isBusy = true; + +updateButtonStates(); + +$.get(api+'list', function(data) { //?id=00003 + + if (data.status=='success') { + var list = data.data.list; + // list.reverse(); + + isBusy = true; + updateButtonStates(); + updateStatusMessage('loading '+list.length+' sketches...'); + + loadSketch(list, function() { + console.log('done'); + isBusy = false; + updateFreeSpace(); + updateButtonStates(); + }); + + } else { + console.log('failure',data) + } + +}).fail(function(status) { + alert("Error ("+status.status+") connecting to "+api+'list'); + console.log(status); +}); + +function onLogoClick() { + location.href='/'+location.search; +} + +function loadSketch(list,cb) { + var id = list.pop(); + + $.get(api+'?id='+id, function(data) { + + if (data.status=='success') { + addItem(id,data.data.data); + } + + updateStatusMessage('loading '+list.length+' sketches...'); + + if (list.length>0) { + loadSketch(list,function() { + cb(); + }) + } else { + cb(); + } + }); +} + +function addItem(id,svgData,doPrepend) { + var path; + + if (!svgData) path = ""; + else if (typeof(svgData)!='string') path = ""; + else if (svgData.indexOf("CDATA")==-1) path = ""; + else path = svgData.split('d="')[1].split('"')[0]; + + var item = $('
'); + var svg = ''; + + item.click(function() { + $(this).toggleClass('selected'); + console.log($(this).attr("data")); + updateButtonStates(); + }) + item.append(svg); + + if (doPrepend) $('#svgContainer').prepend(item); + else $('#svgContainer').append(item); + item.hide().fadeIn(); + + updateButtonStates(); +} + +function deleteSketches(list,cb) { + var id = list.pop(); + + $.post(api+'delete', {id:id}, function(data) { + + $('.item[data='+id+']').fadeOut('slow',function() { + $(this).remove(); //remove when effect is finished + }); + + updateStatusMessage("Deleting " + list.length + ' sketches...'); + + if (list.length>0) { + deleteSketches(list,cb); + } else { + cb(); + } + + }); +} + +function deleteSelectedSketches() { + if (confirm('Do you want to delete '+$('.item.selected').length+' drawings?')) { + + var ids = []; + $('.item.selected').map(function(){ + ids.push($(this).attr('data')); + }); + + isBusy = true; + updateButtonStates(); + + deleteSketches(ids,function() { + console.log('done deleting sketches'); + isBusy = false; + updateButtonStates(); + updateFreeSpace(); + }); + + deselectAll(); + updateButtonStates(); + } +} + +function selectAll() { + $('.item').addClass('selected'); + updateButtonStates(); +} + +function deselectAll() { + $('.item').removeClass('selected'); + updateButtonStates(); +} + +function updateButtonStates() { + var numItems = $('.item').length; + var numSelected = $('.item.selected').length; + var noSelection = numSelected==0; + + $("#btnDelete").attr("disabled",isBusy || noSelection); + $("#btnDownload").attr("disabled",isBusy || noSelection); + $("#btnDeselectAll").attr("disabled",isBusy || noSelection); + $("#btnSelectAll").attr("disabled",isBusy || numItems==0); + $("#btnUpload").attr("disabled",isBusy || !noSelection); + $("#btnDelete").text("Delete" + (noSelection ? "" : " ("+numSelected+")")); + $("#btnDownload").text("Download" + (noSelection ? "" : " ("+numSelected+")")); +} + +function uploadFile(files, index, next) { + var reader = new FileReader(); + reader.readAsText(files[index], "UTF-8"); + reader.onload = function (evt) { + console.log("onload",index); //,files[index],evt.target); + + //process file + var svg = convertSvg(evt.target.result); + + $.post(api, {data:svg}, function(data) { + if (data.status=='success') { + var id = data.data.id; + addItem(id,svg,true); + + updateStatusMessage('uploading '+(files.length-index)+' sketches...'); + + if (index-1) return svg; //assume this SVG is already ok + + //this fixes SVG's created by the kunstcentraal app + var re = /([a-zA-Z])\s?([0-9]{1,}) ([0-9]{1,})/g; + svg = svg.replace(re,"$1$2,$3"); + re = /<\/svg>/g; + svg = svg.replace(re,"\n"); + + svg = svg.replace("M0,0 ",""); //RC: hack + + return svg; +} + +function download() { + $('.item.selected').each(function() { + var id = $(this).attr('data'); + var svgData = $(this).html(); + console.log('downloading',id); + $('')[0].click(); + }); +} + diff --git a/www/img/buttons/btnClose.png b/www/img/buttons/btnClose.png new file mode 100644 index 0000000..290ae3f Binary files /dev/null and b/www/img/buttons/btnClose.png differ diff --git a/www/img/buttons/btnGuide.png b/www/img/buttons/btnGuide.png new file mode 100644 index 0000000..c64ded2 Binary files /dev/null and b/www/img/buttons/btnGuide.png differ diff --git a/www/img/buttons/btnScan.png b/www/img/buttons/btnScan.png new file mode 100644 index 0000000..3ce983d Binary files /dev/null and b/www/img/buttons/btnScan.png differ diff --git a/www/index.html b/www/index.html index cab8fab..54a2400 100644 --- a/www/index.html +++ b/www/index.html @@ -40,6 +40,7 @@
+
@@ -75,10 +76,12 @@
-
+
+ +
@@ -125,6 +128,15 @@
+ +