0
0
mirror of https://github.com/Doodle3D/doodle3d-client.git synced 2025-06-11 17:23:17 +02:00

Merge branch 'text-experiment' into develop

Conflicts:
	js/Help.js
	js/buttonbehaviors.js
	js/main.js
	js_src/Keyboard.js
	js_src/Shape.js
	js_src/verticalShapes.js
	www/index.html
This commit is contained in:
Wouter R
2014-01-17 13:21:02 +01:00
150 changed files with 3325 additions and 9559 deletions

81
js/AddShapeDialog.js Normal file
View File

@ -0,0 +1,81 @@
var shapeResolution=3;
var shapePopup;
function initShapeDialog() {
$("#btnShapeOk").on("onButtonClick",onShapeOk);
$("#btnShapeCancel").on("onButtonClick",onShapeCancel);
$("#btnShapePlus").on("onButtonHold",onShapePlus);
$("#btnShapeMin").on("onButtonHold",onShapeMin);
updateShapePreview();
shapePopup = new Popup($("#popupShape"), $("#popupMask"));
shapePopup.setEscapeKeyHandler(onShapeCancel);
shapePopup.setEnterKeyHandler(onShapeOk);
}
function showShapeDialog() {
shapePopup.open();
}
function onShapeCancel() {
shapePopup.close();
}
function onShapeOk() {
shapePopup.close();
var res = shapeResolution;
if (res!=undefined) {
if (isNaN(res)) res=3;
if (res<2) res=2;
if (res>100) res=100;
drawCircle(canvasWidth/2,canvasHeight/2,80,res);
}
}
function onShapePlus() {
shapeResolution++;
if (shapeResolution>50) shapeResolution=50;
updateShapePreview();
}
function onShapeMin() {
shapeResolution--;
if (shapeResolution<2) shapeResolution=2;
updateShapePreview();
}
function updateShapePreview() {
$(".lblShapeResolution").text(shapeResolution + " sides");
var canvas = $("#shapePreview")[0];
var c = canvas.getContext('2d');
var w = canvas.width;
var h = canvas.height;
console.log(w,h);
var r = w/2 - 20;
var x0 = w/2;
var y0 = h/2;
var res = shapeResolution;
var step = Math.PI * 2.0 / res;
c.save();
c.clearRect(0,0,canvas.width, canvas.height);
c.restore();
c.beginPath();
for (var a=0; a<Math.PI*2; a+=step) {
var x = Math.sin(a+Math.PI) * r + x0;
var y = Math.cos(a+Math.PI) * r + y0;
if (a==0) c.moveTo(x,y);
else c.lineTo(x,y);
}
//close shape
var x = Math.sin(0+Math.PI) * r + x0;
var y = Math.cos(0+Math.PI) * r + y0;
c.lineTo(x,y);
//draw shape
c.lineWidth = 2;
c.stroke();
}

139
js/Button.js Normal file
View File

@ -0,0 +1,139 @@
(function($) {
var clickEnabled = true;
$.fn.Button = function() {
return $(this).each(function(){
$.Button($(this)[0]);
});
};
$.Button = function(element) {
var downTimerFPS = 20;
var _timer = undefined;
var _x,_y;
var isDown = false;
var updateCursor = function(e) {
// retrieve cursor position relative to element
if (e.offsetX != undefined) {
_x = e.offsetX;
_y = e.offsetY;
} else if(e.pageX != undefined) {
// http://www.quirksmode.org/mobile/tableViewport_desktop.html#t11
var offset = $(element).offset();
_x = e.pageX - offset.left;
_y = e.pageY - offset.top;
} else if(e.originalEvent != undefined && e.originalEvent.pageX != undefined) {
//http://css-tricks.com/the-javascript-behind-touch-friendly-sliders/
var offset = $(element).offset();
_x = e.originalEvent.pageX - offset.left;
_y = e.originalEvent.pageY - offset.top;
}
//android+chrome-specific hack
if (e.originalEvent.changedTouches != undefined) {
var offset = $(element).offset();
_x = e.originalEvent.changedTouches[0].pageX - offset.left;
_y = e.originalEvent.changedTouches[0].pageY - offset.top;
}
}
var startDownTimer = function() {
if (_timer==undefined) {
_timer = setInterval(onDownTimerInterval, 1000/downTimerFPS);
isDown = true;
}
}
var stopDownTimer = function() {
clearInterval(_timer);
_timer = undefined;
isDown = false;
// _x = undefined;
// _y = undefined;
}
var onDownTimerInterval = function() {
if (_x!=undefined && _y!=undefined) {
$(element).trigger("onButtonHold",{x:_x,y:_y});
} else {
console.log("Button: warning... _x or _y not set...");
}
}
var onTouchStart = function(e) {
clickEnabled = false;
updateCursor(e);
startDownTimer();
$(element).trigger("onButtonClick",{x:_x,y:_y});
e.preventDefault();
}
var onTouchEnd = function(e) {
updateCursor(e);
stopDownTimer();
}
var onTouchMove = function(e) {
updateCursor(e);
startDownTimer();
}
var onMouseDown = function(e) {
updateCursor(e);
startDownTimer();
}
var onMouseUp = function(e) {
updateCursor(e);
stopDownTimer();
}
var onMouseMove = function(e) {
updateCursor(e);
if (isDown) onMouseDrag(e);
}
var onMouseDrag = function(e) {
updateCursor(e);
}
var onDocumentMouseUp = function(e) {
stopDownTimer();
}
var onClick = function(e) {
if(!clickEnabled) return;
updateCursor(e);
stopDownTimer();
$(element).trigger("onButtonClick",{x:_x,y:_y});
}
var onStartDrag = function(e) {
}
var onContextMenu = function(e) {
e.preventDefault();
}
//this needs to be done after the function declarations
$(element).bind({
touchstart: onTouchStart,
touchend: onTouchEnd,
touchmove: onTouchMove,
mousedown: onMouseDown,
mouseup: onMouseUp,
mousemove: onMouseMove,
contextmenu: onContextMenu,
click: onClick
});
$(document).on("mouseup", onDocumentMouseUp);
$(element).css("-webkit-user-select","none");
$(element).css("-webkit-touch-callout","none");
}
}(jQuery));

64
js/Class.js Normal file
View File

@ -0,0 +1,64 @@
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();

342
js/Help.js Normal file
View File

@ -0,0 +1,342 @@
/*
* 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.
*/
var grandTour;
function GrandTour(_name) {
console.log("GrandTour");
this.tour = "";
this.name = _name;
this.active = false;
var self = this;
this.init = function() {
console.log("GrandTour >> f:init()");
this.tour = function() {
$('#help_d3dIntro').joyride({
autoStart: false,
modal: true,
expose: true,
'tipAdjustmentX': 15,
'tipAdjustmentY': 15,
'tipLocation': 'bottom', // 'top' or 'bottom' in relation to parent
'nubPosition': 'auto', // override on a per tooltip bases
'scrollSpeed': 300, // Page scrolling speed in ms
// 'timer': 2000, // 0 = off, all other numbers = time(ms)
// 'startTimerOnClick': true, // true/false to start timer on first click
'nextButton': true, // true/false for next button visibility
'tipAnimation': 'fade', // 'pop' or 'fade' in each tip
// 'pauseAfter': [], // array of indexes where to pause the tour after
'tipAnimationFadeSpeed': 350, // if 'fade'- speed in ms of transition
// 'cookieMonster': true, // true/false for whether cookies are used
// 'cookieDomain': false, // set to false or yoursite.com
// 'cookieName': 'Doodle3DFirstTime', // choose your own cookie name
// 'localStorage': true, //
// 'localStorageKey': 'Doodle3DFirstTime', // choose your own cookie name
'preRideCallback' : self.preRideCallback,
'preStepCallback': self.preStepCallback, // A method to call before each step
'postStepCallback': self.postStepCallback, // A method to call after each step
'postRideCallback': self.postRideCallback // a method to call once the tour closes
});
};
this.tour();
};
this.preRideCallback = function(index, tip) {
console.log("GrandTour >> f:preRideCallback() >> index: " + index);
if (index == 0 && $.cookie("Doodle3DFirstTime") == "ridden") {
console.log("GrandTour >> f:preRideCallback() >> we've been here before...");
if ($.cookie("grandTourFinished")) {
// grand tour was previously finished (eh.. is that useful?)
// executing this 3 times because there doesn't seem to be a 'go to step X' method
// $(this).joyride('set_li', false);
$(this).joyride('set_li', false);
// $(this).joyride('set_li', false);
} else {
$(this).joyride('set_li', false);
}
}
// Overrule printer to tour mode, pausing status updates
printer.overruleState(Printer.TOUR_STATE);
// bring up thermometer and progressbar to explain them
thermometer.show();
progressbar.show();
message.hide();
};
this.preStepCallback = function(index, tip) {
// console.log("GrandTour >> f:preStepCallback() >> index: " + index);
// console.log("GrandTour >> f:preStepCallback() >> tip: " , tip);
// console.log("GrandTour >> f:preStepCallback() >> $(this): " , $(this));
// console.log("GrandTour >> f:preStepCallback() >> tipsettings: " , $(this)[0].tipSettings);
var dataset = $(this)[0].$li[0].dataset;
if (dataset.action != undefined) {
switch (dataset.action) {
case "showMessage":
console.log(" action: showMessage");
message.set("This is a status message...", Message.NOTICE);
break;
}
}
};
this.postStepCallback = function(index, tip) {
//console.log("GrandTour >> f:postStepCallback() >> index: " + index);
// var dataset = $(this)[0].$li[0].dataset;
};
this.postRideCallback = function(index, tip) {
// console.log("GrandTour >> f:postRideCallback() >> index: " + index + ", self.active: " + self.active);
// console.log("GrandTour >> f:postRideCallback() >> this: " , self);
self.active = false;
$(document).trigger(helpTours.TOURFINISHED, self.name);
// hide the elements which were summoned for the purposes of the tour
// thermometer.hide();
// progressbar.hide();
// message.hide();
// after seeing the grand tour for the first time ever, set cookie 'Doodle3DFirstTime' to true
if (!$.cookie("Doodle3DFirstTime")) {
$.cookie("Doodle3DFirstTime", 'ridden', { expires: 365, domain: false, path: '/' });
}
if (index < $(this)[0].$tip_content.length - 1) {
console.log("GrandTour >> f:postRideCallback() >> tour terminated before its true end");
// tour wasn't finished
// tour was ended prematurely. For only the first few visits, nag the user about being able to revisit the tour..
if (parseInt($.cookie("Doodle3DVisitCounter")) < helpTours.numTimesToShowNagPopup) {
helpTours.startTour(helpTours.INFOREMINDER, helpTours);
}
// infoReminderTour.start();
} else {
// tour was finished
console.log("GrandTour >> f:postRideCallback() >> tour ended at its true end");
// we should be at the end...
if (!$.cookie("grandTourFinished") && parseInt($.cookie("Doodle3DVisitCounter")) < helpTours.numTimesToShowNagPopup) {
helpTours.startTour(helpTours.INFOREMINDER, helpTours);
}
$.cookie("grandTourFinished", 'yes', { expires: 365, domain: false, path: '/' });
}
};
this.start = function() {
console.log("GrandTour >> f:start() >> this: " , this);
this.active = true;
$(window).joyride('restart');
// self.tour();
};
}
var infoReminderTour;
function InfoReminderTour(_name) {
console.log("InfoReminderTour");
this.tour = "";
this.name = _name;
this.active = false;
var self = this;
this.init = function(callback) {
console.log("InfoReminderTour >> f:init()");
this.tour = function() {
$('#help_InfoReminder').joyride({
autoStart: false,
modal: true,
expose: true,
'tipAdjustmentX': 15,
'tipAdjustmentY': 15,
'tipLocation': 'bottom', // 'top' or 'bottom' in relation to parent
'nubPosition': 'auto', // override on a per tooltip bases
'scrollSpeed': 300, // Page scrolling speed in ms
'nextButton': true, // true/false for next button visibility
'tipAnimation': 'fade', // 'pop' or 'fade' in each tip
'tipAnimationFadeSpeed': 350, // if 'fade'- speed in ms of transition
'preRideCallback' : self.preRideCallback,
'postStepCallback': self.postStepCallback, // A method to call after each step
'postRideCallback': self.postRideCallback // a method to call once the tour closes
});
}
this.tour();
if (callback != undefined) callback();
};
this.preRideCallback = function(index, tip) {
console.log("InfoReminderTour >> f:preRideCallback() >> index: " + index + ", tip: " , tip);
};
this.postStepCallback = function(index, tip) {
console.log("InfoReminderTour >> f:postStepCallback() >> index: " + index + ", tip: " , tip);
};
this.postRideCallback = function(index, tip) {
console.log("InfoReminderTour >> f:postRideCallback() >> index: " + index + ", tip: " , tip);
this.active = false;
$(document).trigger(helpTours.TOURFINISHED, self.name);
};
this.start = function() {
console.log("InfoReminderTour >> f:start()");
this.active = true;
$(window).joyride('restart');
// self.tour();
};
}
function initHelp() {
console.log("f:initHelp()");
// track number of visits of this user
if ($.cookie("Doodle3DVisitCounter") == null) {
$.cookie("Doodle3DVisitCounter", '0');
} else {
$.cookie("Doodle3DVisitCounter", parseInt($.cookie("Doodle3DVisitCounter")) + 1);
}
// load the html file which describes the tour contents
$("#helpContainer").load("helpcontent.html", function() {
console.log("helpContent loaded");
helpTours = new HelpTours();
helpTours.init( function () {
if (parseInt($.cookie("Doodle3DVisitCounter")) < helpTours.numTimesToShowNagPopup) {
console.log("initHelp >> Doodle3DFirstTime cookie is set, Doodle3DVisitCounter is < 4");
if ($.cookie("Doodle3DFirstTime") != "ridden") {
setTimeout(helpTours.startTour, 750, helpTours.tours.grandTour, helpTours);
} else {
setTimeout(helpTours.startTour, 750, helpTours.tours.infoReminderTour, helpTours);
}
// remind user of our nifty tour
} else if (parseInt($.cookie("Doodle3DVisitCounter")) == helpTours.numTimesToShowNagPopup && $.cookie("Doodle3DFirstTime") != "ridden") {
// remind
setTimeout(helpTours.startTour, 750, helpTours.tours.infoReminderTour, helpTours);
}
// // only trigger starttour if user is seeing Doodle3D for the first time
// if ($.cookie("Doodle3DFirstTime") != "ridden") {
// console.log("initHelp >> intro tour has not been given yet > let's go!");
// setTimeout(helpTours.startTour, 750, helpTours.tours.grandTour, helpTours);
// } else if (parseInt($.cookie("Doodle3DVisitCounter")) < helpTours.numTimesToShowNagPopup) {
// console.log("initHelp >> Doodle3DFirstTime cookie is set, Doodle3DVisitCounter is < 4");
// // remind user of our nifty tour
// setTimeout(helpTours.startTour, 750, helpTours.tours.infoReminderTour, helpTours);
// }
});
});
}
var helpTours;
function HelpTours() {
console.log("HelpTours");
this.numTimesToShowNagPopup = 2;
this.WELCOMETOUR = "welcometour";
this.INFOREMINDER = "inforeminder";
this.TOURFINISHED = "tourfinished";
this.tours = {
'grandTour' : this.WELCOMETOUR,
'infoReminderTour' : this.INFOREMINDER
};
this.currActiveTour = "";
this.tourActive = false;
var self = this;
this.init = function(callback) {
console.log("HelpTours >> f:init >> self: " + self);
$(document).on(this.TOURFINISHED, this.tourEnded);
grandTour = new GrandTour(this.WELCOMETOUR);
infoReminderTour = new InfoReminderTour(this.INFOREMINDER);
// this.tours["grandTour"] = self.WELCOMETOUR;
// this.tours["infoReminderTour "]= self.INFOREMINDER;
console.log("HelpTours >> f:init >> this.tours: " , this.tours);
if (callback != undefined) callback();
};
this.startTour = function(which, scope) {
if (scope == undefined) scope = this;
// console.log("HelpTours >> f:startTour >> scope: " , scope);
// console.log("HelpTours >> f:startTour >> currActiveTour: " , scope.currActiveTour.name);
// console.log("HelpTours >> f:startTour >> currActiveTour.active: " , scope.currActiveTour.active);
// console.log("HelpTours >> f:startTour >> target to start: '" + which);
switch (which) {
case scope.WELCOMETOUR:
// do welcometour
// console.log("HelpTours >> f:startTour >> case this.WELCOMETOUR >> scope.tourActive = " + scope.tourActive);
console.log("HelpTours >> f:startTour >> case this.WELCOMETOUR");
if (scope.tourActive) {
if (scope.currActiveTour.active == true) {
$(window).joyride('end');
scope.currActiveTour = undefined;
}
scope.tourActive = false;
}
$(window).joyride('destroy');
// var self = this;
grandTour.init();
setTimeout(function(scp) {
grandTour.start();
scp.currActiveTour = grandTour;
scp.tourActive = true;
}, 250, scope);
// $(window).joyride('restart');
break;
case self.INFOREMINDER:
// do info reminder
// console.log("HelpTours >> f:startTour >> case self.INFOREMINDER >> scope.tourActive = " + scope.tourActive);
console.log("HelpTours >> f:startTour >> case self.INFOREMINDER");
if (scope.tourActive) {
// console.log(" killing previous joyride... ");
if (scope.currActiveTour.active == true) {
$(window).joyride('end');
scope.currActiveTour = undefined;
}
// console.log(" setting tourActive to false....");
scope.tourActive = false;
// console.log(" scope.tourActive: " + scope.tourActive);
}
$(window).joyride('destroy');
// var self = this;
infoReminderTour.init();
setTimeout(function(scp) {
infoReminderTour.start();
scp.currActiveTour = infoReminderTour;
scp.tourActive = true;
}, 250, scope);
break;
}
}
this.tourEnded = function(e, n) {
console.log("HelpTours >> f:tourEnded >> self.tourActive: " + self.tourActive + ", name: " + n);
$(window).joyride('destroy');
self.currActiveTour = undefined;
self.tourActive = false;
message.hide();
printer.checkStatus();
}
}

97
js/Keyboard.js Normal file
View File

@ -0,0 +1,97 @@
var keyboardShortcutsEnabled = true;
var keyboardEscapeEnterEnabled = false;
var wordBuffer = "";
var wordFuncs = {
"idbeholdl": function() {
alert("Light!");
},
"idspispopd": function() {
drawTextOnCanvas("Im in ur kanvas drawin' ur stuffz.");
},
"dia": function() {
var cx = canvasWidth / 2;
var cy = canvasHeight /2;
drawCircle(cx, cy, 50, 4);
shapeMoveTo(cx - 20, cy);
shapeLineTo(cx + 20, cy);
shapeMoveTo(cx, cy - 20);
shapeLineTo(cx, cy + 20);
}
};
function initKeyboard() {
$(document).keypress(function(event) {
if (keyboardEscapeEnterEnabled) {
switch (event.keyCode) {
case 13:
$(document).trigger("onEnterKey");
break;
case 27:
$(document).trigger("onEscapeKey");
break;
}
}
if (!keyboardShortcutsEnabled) return;
if (event.ctrlKey && event.altKey && ! event.metaKey) processWords(event);
if (event.altKey || event.ctrlKey || event.metaKey) return; //ignore key presses with modifier keys except shift
var ch = String.fromCharCode(event.which);
switch (ch) {
case 'c': clearDoodle(); break;
case 'n': clearDoodle(); break;
case 'p': print(); break;
case 'u': oopsUndo(); break;
case 'g': settingsWindow.downloadGcode(); break;
case 'q': stopPrint(); break;
case ',': openSettingsWindow(); break;
case 'h': previewUp(true); break;
case 'H': previewDown(true); break;
case 's': saveSketch(); break;
case 'L': nextDoodle(); break;
case 'l': prevDoodle(); break;
case '[': previewTwistLeft(); break;
case ']': previewTwistRight(); break;
case '|': resetTwist(); break;
case 't': showWordArtDialog(); break;
case 'i': showShapeDialog(); break;
case ';': moveShape(-5,0); break;
case '\'': moveShape(5,0); break;
case '-': zoomShape(.95); break;
case '+': zoomShape(1.05); break;
case 'r': rotateShape(.1); break;
case 'R': rotateShape(-.1); break;
//default: console.log("Key: '" + ch + "' (" + event.which + ")");
}
if(event.which != 13) { // don't prevent enter usage, it's used in tour
event.preventDefault(); //prevents the character to end up in a focussed textfield
}
})
}
function processWords(e) {
wordBuffer += String.fromCharCode(e.which);
var match = false;
for (var k in wordFuncs) {
if (k.indexOf(wordBuffer) == 0) {
if (k.length == wordBuffer.length) match = wordFuncs[k];
else match = true;
break;
}
}
if (typeof(match) == 'function') {
match();
wordBuffer = "";
} else if (!match) {
wordBuffer = "";
}
}

60
js/Message.js Normal file
View File

@ -0,0 +1,60 @@
/*
* 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 Message() {
Message.ERROR = "error";
Message.WARNING = "warning";
Message.NOTICE = "notice";
Message.INFO = "info";
this.mode = "";
this.$element;
var self = this;
var autoHideDelay = 5000;
var autohideTimeout;
this.init = function($element) {
this.$element = $element;
}
this.set = function(text,mode,autoHide,disableEffect) {
console.log("Message:set: ",text,mode,autoHide,disableEffect);
if(disableEffect) {
self.fill(text,mode,autoHide)
} else{
self.hide(function() {
self.show();
self.fill(text,mode,autoHide)
});
}
}
this.fill = function(text,mode,autoHide) {
//console.log("Message:fill: ",text,mode,autoHide);
self.clear();
self.$element.text(text);
self.$element.addClass(mode);
self.mode = mode;
clearTimeout(autohideTimeout);
if(autoHide) {
autohideTimeout = setTimeout(function(){ self.hide()},autoHideDelay);
}
}
this.clear = function($element) {
this.$element.text("");
this.$element.removeClass(this.mode);
}
this.show = function() {
this.$element.fadeIn(200);
}
this.hide = function(complete) {
this.$element.fadeOut(200,complete);
}
}

34
js/Popup.js Normal file
View File

@ -0,0 +1,34 @@
function Popup(element, mask) {
var self = this;
var escapeKeyHandler = null;
var enterKeyHandler = null;
this.open = function(complete, disableMaskClick) {
mask.fadeIn(POPUP_SHOW_DURATION);
element.fadeIn(POPUP_SHOW_DURATION, complete);
keyboardShortcutsEnabled = false;
keyboardEscapeEnterEnabled = true;
document.body.removeEventListener('touchmove', prevent, false);
mask.bind("onButtonClick", function() { self.close() });
if (escapeKeyHandler) $(document).bind("onEscapeKey", escapeKeyHandler);
if (enterKeyHandler) $(document).bind("onEnterKey", enterKeyHandler);
}
this.close = function(complete) {
mask.fadeOut(POPUP_SHOW_DURATION);
element.fadeOut(POPUP_SHOW_DURATION, complete);
keyboardShortcutsEnabled = true;
keyboardEscapeEnterEnabled = false;
document.body.addEventListener('touchmove', prevent, false);
mask.unbind("onButtonClick");
if (escapeKeyHandler) $(document).unbind("onEscapeKey", escapeKeyHandler);
if (enterKeyHandler) $(document).unbind("onEnterKey", enterKeyHandler);
}
this.setEscapeKeyHandler = function(hnd) { escapeKeyHandler = hnd; }
this.setEnterKeyHandler = function(hnd) { enterKeyHandler = hnd; }
}

357
js/Printer.js Normal file
View File

@ -0,0 +1,357 @@
/*
* 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.
*/
/* not using this now
var $printProgressContainer = $("#printProgressContainer");
var $progressbar = $("#progressbar");
var $progressAmount = $(".progressAmount");
function setPrintprogress(val) {
if (isNaN(val)) return;
// console.log("f:setPrintprogress() >> val " + val);
$progressbar.css("width", val*100 + "%");
$progressAmount.text(Math.floor(val*100) + "%");
}
//*/
function Printer() {
Printer.WIFIBOX_DISCONNECTED_STATE = "wifibox disconnected";
Printer.UNKNOWN_STATE = "unknown"; // happens when a printer is connection but there isn't communication yet
Printer.DISCONNECTED_STATE = "disconnected"; // printer disconnected
Printer.IDLE_STATE = "idle"; // printer found, but idle
Printer.BUFFERING_STATE = "buffering"; // printer is buffering (recieving) data, but not yet printing
Printer.PRINTING_STATE = "printing";
Printer.STOPPING_STATE = "stopping"; // when you stop (abort) a print it prints the endcode
Printer.TOUR_STATE = "tour"; // when in joyride mode
Printer.ON_BEFORE_UNLOAD_MESSAGE = "You're doodle is still being send to the printer, leaving will result in a incomplete 3D print";
this.temperature = 0;
this.targetTemperature = 0;
this.currentLine = 0;
this.totalLines = 0;
this.bufferedLines = 0;
this.state = Printer.UNKNOWN_STATE;
this.hasControl = true; // whether this client has control access
this.wifiboxURL;
this.checkStatusInterval = 3000;
this.checkStatusDelay;
this.timeoutTime = 3000;
this.sendPrintPartTimeoutTime = 5000;
this.gcode; // gcode to be printed
this.sendLength = 500; // max amount of gcode lines per post (limited because WiFi box can't handle to much)
this.retryDelay = 2000; // retry setTimout delay
this.retrySendPrintPartDelay; // retry setTimout instance
this.retryCheckStatusDelay; // retry setTimout instance
this.retryStopDelay; // retry setTimout instance
this.retryPreheatDelay; // retry setTimout instance
Printer.MAX_GCODE_SIZE = 10; // max size of gcode in MB's (estimation)
this.stateOverruled = false;
// Events
Printer.UPDATE = "update";
var self = this;
this.init = function() {
console.log("Printer:init");
//this.wifiboxURL = "http://" + window.location.host + "/cgi-bin/d3dapi";
//this.wifiboxURL = "http://192.168.5.1/cgi-bin/d3dapi";
this.wifiboxURL = wifiboxURL;
//this.wifiboxURL = "proxy5.php";
console.log(" wifiboxURL: ",this.wifiboxURL);
if(autoUpdate) {
this.startStatusCheckInterval();
}
}
this.preheat = function() {
console.log("Printer:preheat");
if( this.state == Printer.BUFFERING_STATE ||
this.state == Printer.PRINTING_STATE ||
this.state == Printer.STOPPING_STATE) {
return;
}
var self = this;
if (communicateWithWifibox) {
$.ajax({
url: this.wifiboxURL + "/printer/heatup",
type: "POST",
dataType: 'json',
timeout: this.timeoutTime,
success: function(data){
console.log("Printer:preheat response: ",data);
if(data.status != "success") {
clearTimeout(self.retryPreheatDelay);
self.retryPreheatDelay = setTimeout(function() { self.preheat() },self.retryDelay); // retry after delay
}
}
}).fail(function() {
console.log("Printer:preheat: failed");
clearTimeout(self.retryPreheatDelay);
self.retryPreheatDelay = setTimeout(function() { self.preheat() },self.retryDelay); // retry after delay
});
} else {
console.log ("Printer >> f:preheat() >> communicateWithWifibox is false, so not executing this function");
}
}
this.print = function(gcode) {
console.log("Printer:print");
console.log(" gcode total # of lines: " + gcode.length);
message.set("Sending doodle to printer...",Message.NOTICE);
self.addLeaveWarning();
/*for (i = 0; i < gcode.length; i++) {
gcode[i] += " (" + i + ")";
}*/
this.sendIndex = 0;
this.gcode = gcode;
//console.log(" gcode[20]: ",gcode[20]);
var gcodeLineSize = this.byteSize(gcode[20]);
//console.log(" gcodeLineSize: ",gcodeLineSize);
var gcodeSize = gcodeLineSize*gcode.length/1024/1024; // estimate gcode size in MB's
console.log(" gcodeSize: ",gcodeSize);
if(gcodeSize > Printer.MAX_GCODE_SIZE) {
alert("Error: Printer:print: gcode file is probably too big ("+gcodeSize+"MB) (max: "+Printer.MAX_GCODE_SIZE+"MB)");
console.log("Error: Printer:print: gcode file is probably too big ("+gcodeSize+"MB) (max: "+Printer.MAX_GCODE_SIZE+"MB)");
this.overruleState(Printer.IDLE_STATE);
this.startStatusCheckInterval();
message.hide();
self.removeLeaveWarning();
return;
}
//this.targetTemperature = settings["printer.temperature"]; // slight hack
this.sendPrintPart(this.sendIndex, this.sendLength);
}
this.byteSize = function(s){
return~-encodeURI(s).split(/%..|./).length;
}
this.sendPrintPart = function(sendIndex,sendLength) {
console.log("Printer:sendPrintPart sendIndex: " + sendIndex + "/" + this.gcode.length + ", sendLength: " + sendLength);
var sendPercentage = Math.round(sendIndex/this.gcode.length*100);
message.set("Sending doodle to printer: "+sendPercentage+"%",Message.NOTICE,false,true);
var firstOne = (sendIndex == 0)? true : false;
var start = firstOne; // start printing right away
message.set("Sending doodle to printer..."+sendIndex,Message.NOTICE);
var completed = false;
if (this.gcode.length < (sendIndex + sendLength)) {
console.log(" sending less than max sendLength (and last)");
sendLength = this.gcode.length - sendIndex;
//lastOne = true;
completed = true;
}
var gcodePart = this.gcode.slice(sendIndex, sendIndex+sendLength);
var postData = { gcode: gcodePart.join("\n"), first: firstOne, start: start};
var self = this;
if (communicateWithWifibox) {
$.ajax({
url: this.wifiboxURL + "/printer/print",
type: "POST",
data: postData,
dataType: 'json',
timeout: this.sendPrintPartTimeoutTime,
success: function(data){
console.log("Printer:sendPrintPart response: ",data);
if(data.status == "success") {
if (completed) {
console.log("Printer:sendPrintPart:gcode sending completed");
this.gcode = [];
btnStop.css("display","block"); // hack
self.removeLeaveWarning();
message.set("Doodle has been sent to printer...",Message.INFO,true);
//self.targetTemperature = settings["printer.temperature"]; // slight hack
} else {
// only if the state hasn't bin changed (by for example pressing stop) we send more gcode
console.log("Printer:sendPrintPart:gcode part received (state: ",self.state,")");
if(self.state == Printer.PRINTING_STATE || self.state == Printer.BUFFERING_STATE) {
console.log("Printer:sendPrintPart:sending next part");
self.sendPrintPart(sendIndex + sendLength, sendLength);
}
}
}
// after we know the first gcode packed has bin received or failed
// (and the driver had time to update the printer.state)
// we start checking the status again
if(sendIndex == 0) {
self.startStatusCheckInterval();
}
}
}).fail(function() {
console.log("Printer:sendPrintPart: failed");
clearTimeout(self.retrySendPrintPartDelay);
self.retrySendPrintPartDelay = setTimeout(function() {
console.log("request printer:sendPrintPart failed retry");
self.sendPrintPart(sendIndex, sendLength)
},self.retryDelay); // retry after delay
// after we know the gcode packed has bin received or failed
// (and the driver had time to update the printer.state)
// we start checking the status again
self.startStatusCheckInterval();
});
} else {
console.log ("Printer >> f:sendPrintPart() >> communicateWithWifibox is false, so not executing this function");
}
}
this.stop = function() {
console.log("Printer:stop");
endCode = generateEndCode();
console.log(" endCode: ",endCode);
var postData = { gcode: endCode.join("\n")};
var self = this;
if (communicateWithWifibox) {
$.ajax({
url: this.wifiboxURL + "/printer/stop",
type: "POST",
data: postData,
dataType: 'json',
timeout: this.timeoutTime,
success: function(data){
console.log("Printer:stop response: ", data);
// after we know the stop has bin received or failed
// (and the driver had time to update the printer.state)
// we start checking the status again
self.startStatusCheckInterval();
}
}).fail(function() {
console.log("Printer:stop: failed");
clearTimeout(self.retryStopDelay);
self.retryStopDelay = setTimeout(function() { self.stop() },self.retryDelay); // retry after delay
// after we know the stop has bin received or failed
// (and the driver had time to update the printer.state)
// we start checking the status again
self.startStatusCheckInterval();
});
} else {
console.log ("Printer >> f:communicateWithWifibox() >> communicateWithWifibox is false, so not executing this function");
}
}
this.startStatusCheckInterval = function() {
console.log("Printer:startStatusCheckInterval");
self.checkStatus();
clearTimeout(self.checkStatusDelay);
clearTimeout(self.retryCheckStatusDelay);
self.checkStatusDelay = setTimeout(function() { self.checkStatus() }, self.checkStatusInterval);
}
this.stopStatusCheckInterval = function() {
console.log("Printer:stopStatusCheckInterval");
clearTimeout(self.checkStatusDelay);
clearTimeout(self.retryCheckStatusDelay);
}
this.checkStatus = function() {
console.log("Printer:checkStatus");
this.stateOverruled = false;
//console.log(" stateOverruled: ",this.stateOverruled);
var self = this;
if (communicateWithWifibox) {
$.ajax({
url: this.wifiboxURL + "/info/status",
dataType: 'json',
timeout: this.timeoutTime,
success: function(response){
//console.log(" Printer:status: ",response.data.state); //," response: ",response);
self.handleStatusUpdate(response);
clearTimeout(self.checkStatusDelay);
clearTimeout(self.retryCheckStatusDelay);
self.checkStatusDelay = setTimeout(function() { self.checkStatus() }, self.checkStatusInterval);
}
}).fail(function() {
console.log("Printer:checkStatus: failed");
self.state = Printer.WIFIBOX_DISCONNECTED_STATE;
clearTimeout(self.checkStatusDelay);
clearTimeout(self.retryCheckStatusDelay);
self.retryCheckStatusDelay = setTimeout(function() { self.checkStatus() },self.retryDelay); // retry after delay
$(document).trigger(Printer.UPDATE);
});
} else {
console.log ("Printer >> f:checkStatus() >> communicateWithWifibox is false, so not executing this function");
}
}
this.handleStatusUpdate = function(response) {
console.log("Printer:handleStatusUpdate response: ",response);
var data = response.data;
if(response.status != "success") {
self.state = Printer.UNKNOWN_STATE;
} else {
// state
//console.log(" stateOverruled: ",this.stateOverruled);
if(!this.stateOverruled) {
self.state = data.state;
//console.log(" state > ",self.state);
}
// temperature
self.temperature = data.hotend;
self.targetTemperature = data.hotend_target;
// progress
self.currentLine = data.current_line;
self.totalLines = data.total_lines;
self.bufferedLines = data.buffered_lines
// access
self.hasControl = data.has_control;
if(self.state == Printer.PRINTING_STATE || self.state == Printer.STOPPING_STATE) {
console.log("progress: ",self.currentLine+"/"+self.totalLines+" ("+self.bufferedLines+") ("+self.state+")");
}
}
$(document).trigger(Printer.UPDATE);
}
this.overruleState = function(newState) {
this.stateOverruled = true;
console.log(" stateOverruled: ",this.stateOverruled);
self.state = newState;
$(document).trigger(Printer.UPDATE);
this.stopStatusCheckInterval();
}
this.removeLeaveWarning = function() {
window.onbeforeunload = null;
}
this.addLeaveWarning = function() {
window.onbeforeunload = function() {
console.log("WARNING:"+Printer.ON_BEFORE_UNLOAD_MESSAGE);
return Printer.ON_BEFORE_UNLOAD_MESSAGE;
};
}
}

84
js/PrinterPanel.js Normal file
View File

@ -0,0 +1,84 @@
/*
* 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 PrinterPanel() {
this.wifiboxURL;
this.element;
this.retryDelay = 1000;
this.retryDelayer; // setTimout instance
//this.timeoutTime = 3000;
this.printerType;
this.printerSettingsNames;
var self = this;
this.init = function(wifiboxURL,element) {
self.wifiboxURL = wifiboxURL;
self.element = element;
self.printerSelector = element.find("#printerType");
self.printerSelector.change(self.printerSelectorChanged);
var formElements = element.find("[name]");
self.printerSettingsNames = [];
formElements.each( function(index,element) {
self.printerSettingsNames.push(element.name);
});
var gcodePanel = element.find("#gcodePanel");
gcodePanel.coolfieldset({collapsed:true});
}
this.printerSelectorChanged = function(e) {
console.log("PrinterPanel:printerSelectorChanged");
console.log("self: ", self);
self.printerType = self.printerSelector.find("option:selected").val();
self.savePrinterType(self.loadPrinterSettings);
}
this.savePrinterType = function(complete) {
console.log("PrinterPanel:savePrinterType");
var postData = {};
postData[self.printerSelector.attr("name")] = self.printerType;
console.log("postData: ",postData);
$.ajax({
url: self.wifiboxURL + "/config/",
type: "POST",
dataType: 'json',
data: postData,
success: function(response){
console.log("PrinterPanel:savePrinterType response: ",response);
if(complete) complete();
}
}).fail(function() {
console.log("PrinterPanel:savePrinterType: failed");
});
}
this.loadPrinterSettings = function() {
console.log("PrinterPanel:loadPrinterSettings");
console.log(" self.printerSettingsNames: ",self.printerSettingsNames);
var getData = {};
$.each(self.printerSettingsNames, function(key, val) {
getData[val] = "";
});
console.log("getData: ",getData);
$.ajax({
url: self.wifiboxURL + "/config/",
dataType: 'json',
data: getData,
success: function(response){
console.log("PrinterPanel:loadPrinterSettings response: ",response);
self.fillForm(response.data,self.element);
}
}).fail(function() {
console.log("PrinterPanel:loadPrinterSettings: failed");
});
}
}

117
js/Progressbar.js Normal file
View File

@ -0,0 +1,117 @@
/*
* 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 Progressbar() {
this.currProgress = 0; // default val
this.progressbarFGImg = new Image();
this.progressbarFGImgSrc = "img/progress_fg.png";
this.progressbarBGImg = new Image();
this.progressbarBGImgSrc = "img/progress_bg.png";
this.progressWidth= 93;
this.progressHeight = 82;
this.quartPI = .5 * Math.PI;
this.twoPI = 2 * Math.PI;
// To make the progressbar start with a minimal amount of 'progress'
// so that you can visually see that there is progress
this.progressPadding = Math.PI * .1;
this.$canvas;
this.canvas;
this.context;
this.$container;
this.isInitted = false;
this.enabled = true;
this.init = function(targCanvas, targCanvasContainer) {
console.log("Thermometer.init()");
this.$container = targCanvasContainer;
this.$canvas = targCanvas;
this.canvas = this.$canvas[0];
this.context = this.canvas.getContext('2d');
var self = this;
this.progressbarBGImg.onload = function() {
console.log("progressbarBGImg img loaded");
// self.isInitted = true;
// self.update(self.currentTemperature, self.targetTemperature);
self.progressbarFGImg.onload = function() {
console.log("progressbarFGImg img loaded");
self.isInitted = true;
self.update(0, 100);
};
self.progressbarFGImg.src = self.progressbarFGImgSrc;
};
this.progressbarBGImg.src = this.progressbarBGImgSrc;
}
this.update = function(part, total) {
//console.log("Progressbar.update(" + part + "," + total + ")");
var pct = part / total;
if (this.isInitted) {
if (part == undefined) part = 0;
if (total== undefined) total = 100; // prevent divide by zero
var progress = part / total;
progress = Math.min(progress, 1.0);
progress = Math.max(progress, 0);
//console.log("progressbar >> f:update() >> progress: " + progress);
// clear
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.context.drawImage(this.progressbarBGImg, 0, 0);
this.context.font = "7pt sans-serif";
// draw the progressbar foreground's clipping path
this.context.save();
this.context.beginPath();
this.context.moveTo(45, 45);
this.context.lineTo(45, 0);
this.context.arc(45, 45, 45, -this.quartPI, -this.quartPI + this.progressPadding + (progress * (this.twoPI - this.progressPadding)), false); // circle bottom of thermometer
this.context.lineTo(45, 45);
this.context.clip();
this.context.drawImage(this.progressbarFGImg, 0, 0);
this.context.restore();
if (debugMode) {
this.context.fillStyle = '#222';
this.context.strokeStyle = '#fff';
this.context.lineWidth = 3;
this.context.textAlign="center";
this.context.strokeText(part + " / " + total, 45, 45, 90);
this.context.fillText(part + " / " + total, 45, 45, 90);
}
} else {
console.log("Progressbar.setTemperature() -> thermometer not initialized!");
}
}
this.show = function() {
this.$container.addClass("progressbarAppear");
// this.$container.show();
this.enabled = true;
}
this.hide = function() {
this.$container.removeClass("progressbarAppear");
// this.$container.hide();
this.enabled = false;
}
}

778
js/SettingsWindow.js Normal file
View File

@ -0,0 +1,778 @@
/*
* 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.
*/
//these settings are defined in the firmware (conf_defaults.lua) and will be initialized in loadSettings()
var settings = { }
var settingsPopup;
//wrapper to prevent scoping issues in showSettings()
function openSettingsWindow() {
settingsWindow.loadSettings(function() { // reload settings
settingsPopup.open();
});
}
function SettingsWindow() {
this.wifiboxURL;
this.wifiboxCGIBinURL
this.window;
this.btnOK;
this.form;
this.timeoutTime = 3000;
this.saveSettingsTimeoutTime = 8000;
this.retryDelay = 2000; // retry setTimout delay
this.retryRetrieveNetworkStatusDelayTime = 1000;// retry setTimout delay
this.retryLoadSettingsDelay; // retry setTimout instance
this.retrySaveSettingsDelay; // retry setTimout instance
this.retryResetSettingsDelay // retry setTimout instance
this.retryRetrieveNetworkStatusDelay;// retry setTimout instance
this.apFieldSet;
this.clientFieldSet;
this.restoreStateField;
this.networks;
this.currentNetwork; // the ssid of the network the box is on
this.selectedNetwork; // the ssid of the selected network in the client mode settings
this.currentLocalIP = "";
this.clientModeState = SettingsWindow.NOT_CONNECTED;
this.currentAP;
this.apModeState = SettingsWindow.NO_AP;
// after switching wifi network or creating a access point we delay the status retrieval
// because the webserver needs time to switch
this.retrieveNetworkStatusDelay; // setTimout delay
this.retrieveNetworkStatusDelayTime = 1000;
this.restoredStateHideDelayTime = 3000;
this.restoredStateHideDelay // setTimout instance
// Events
SettingsWindow.SETTINGS_LOADED = "settingsLoaded";
// network client mode states
SettingsWindow.NOT_CONNECTED = "not connected"; // also used as first item in networks list
SettingsWindow.CONNECTED = "connected";
SettingsWindow.CONNECTING = "connecting";
SettingsWindow.CONNECTING_FAILED = "connecting failed"
// network access point mode states
SettingsWindow.NO_AP = "no ap";
SettingsWindow.AP = "ap";
SettingsWindow.CREATING_AP = "creating ap";
SettingsWindow.API_CONNECTING_FAILED = -1
SettingsWindow.API_NOT_CONNECTED = 0
SettingsWindow.API_CONNECTING = 1
SettingsWindow.API_CONNECTED = 2
SettingsWindow.API_CREATING = 3
SettingsWindow.API_CREATED = 4
// network mode
SettingsWindow.NETWORK_MODE_NEITHER = "neither";
SettingsWindow.NETWORK_MODE_CLIENT = "clientMode";
SettingsWindow.NETWORK_MODE_ACCESS_POINT = "accessPointMode";
this.networkMode = SettingsWindow.NETWORK_MODE_NEITHER;
this.updatePanel = new UpdatePanel();
this.printerPanel = new PrinterPanel();
var self = this;
this.init = function(wifiboxURL,wifiboxCGIBinURL) {
this.wifiboxURL = wifiboxURL;
this.wifiboxCGIBinURL = wifiboxCGIBinURL;
this.window = $("#popupSettings");
this.btnOK = this.window.find(".btnOK");
enableButton(this.btnOK,this.submitwindow);
settingsPopup = new Popup($("#popupSettings"), $("#popupMask"));
this.window.find("#settingsContainer").load("settings.html", function() {
console.log("Settings:finished loading settings.html, now loading settings...");
self.form = self.window.find("form");
self.form.submit(function (e) { self.submitwindow(e) });
$.ajax({
url: self.wifiboxURL + "/printer/listall",
dataType: 'json',
timeout: self.timeoutTime,
success: function(response) {
console.log("Settings:printer/listall response: ",response.data.printers);
$.each(response.data.printers, function(key, value) {
// console.log(key,value);
$('#printerType').append($('<option>').text(value).attr('value', key));
});
self.loadSettings();
var btnAP = self.form.find("label[for='ap']");
var btnClient = self.form.find("label[for='client']");
var btnRefresh = self.form.find("#refreshNetworks");
var btnConnect = self.form.find("#connectToNetwork");
var btnCreate = self.form.find("#createAP");
var networkSelector = self.form.find("#network");
self.apFieldSet = self.form.find("#apSettings");
self.clientFieldSet = self.form.find("#clientSettings");
self.btnRestoreSettings = self.form.find("#restoreSettings");
self.restoreStateField = self.form.find("#restoreState");
btnAP.on('touchstart mousedown',self.showAPSettings);
btnClient.on('touchstart mousedown',self.showClientSettings);
btnRefresh.on('touchstart mousedown',self.refreshNetworks);
btnConnect.on('touchstart mousedown',self.connectToNetwork);
btnCreate.on('touchstart mousedown',self.createAP);
networkSelector.change(self.networkSelectorChanged);
self.btnRestoreSettings.on('touchstart mousedown',self.resetSettings);
// update panel
var $updatePanelElement = self.form.find("#updatePanel");
self.updatePanel.init(wifiboxURL,$updatePanelElement);
// printer panel
var $printerPanelElement = self.form.find("#printerPanel");
self.printerPanel.init(wifiboxURL,$printerPanelElement);
self.printerPanel.fillForm = self.fillForm;
}
}).fail(function() {
console.log("FATAL ERROR: Settings:printer/listall failed");
});
}); //this.window.find
} //this.init
this.openSettings = function() {
self.loadSettings(function() { // reload settings
settingsPopup.open();
});
}
this.closeSettings = function(complete) {
settingsPopup.close(complete);
}
this.submitwindow = function(e) {
disableButton(self.btnOK,self.submitwindow);
e.preventDefault();
e.stopPropagation();
self.saveSettings(self.readForm(),function(success){
if(success) {
self.closeSettings(function() {
enableButton(self.btnOK,self.submitwindow);
});
self.signin();
} else {
enableButton(self.btnOK,self.submitwindow);
}
});
clearTimeout(self.retryRetrieveNetworkStatusDelay);
}
this.loadSettings = function(complete) {
if (!communicateWithWifibox) {
console.log(" communicateWithWifibox is false: settings aren't being loaded from wifibox...")
return;
}
console.log("Settings:loadSettings() >> getting new data...");
$.ajax({
url: this.wifiboxURL + "/config/all",
dataType: 'json',
timeout: this.timeoutTime,
success: function(response){
console.log("Settings:loadSettings response: ",response);
settings = response.data;
console.log(" settings: ",settings);
self.fillForm(settings);
$(document).trigger(SettingsWindow.SETTINGS_LOADED);
if(complete) complete();
}
}).fail(function() {
console.log("Settings:loadSettings: failed");
clearTimeout(self.retryLoadSettingsDelay);
self.retryLoadSettingsDelay = setTimeout(function() { self.loadSettings() },self.retryDelay); // retry after delay
});
this.refreshNetworks();
this.retrieveNetworkStatus(false);
}
this.fillForm = function(settings,form) {
if(!form) form = this.form; // if no form specified, fill whole form
//fill form with loaded settings
var selects = form.find("select");
selects.each( function(index,element) {
var element = $(element);
element.val(settings[element.attr('name')]);
});
var inputs = form.find("input");
inputs.each( function(index,element) {
var element = $(element);
//console.log("printer setting input: ",index,element.attr("type"),element.attr('name')); //,element);
switch(element.attr("type")) {
case "text":
case "number":
element.val(settings[element.attr('name')]);
break;
case "checkbox":
element.prop('checked', settings[element.attr('name')]);
break;
}
});
var textareas = form.find("textarea");
textareas.each( function(index,element) {
var element = $(element);
var value = settings[element.attr('name')];
element.val(value);
});
}
this.saveSettings = function(newSettings,complete) {
settings = newSettings; // store new settings in global settings
if (communicateWithWifibox) {
$.ajax({
url: self.wifiboxCGIBinURL + "/config",
type: "POST",
data: newSettings,
dataType: 'json',
timeout: self.saveSettingsTimeoutTime,
success: function(response){
console.log("Settings:saveSettings response: ",response);
if(response.status == "error") {
clearTimeout(self.retrySaveSettingsDelay);
self.retrySaveSettingsDelay = setTimeout(function() { self.saveSettings(settings,complete) },self.retryDelay); // retry after delay
} else {
var data = response.data;
var validation = data.validation;
self.clearValidationErrors();
var validated = true;
$.each(validation, function(key, val) {
if (val != "ok") {
console.log("ERROR: setting '" + key + "' not successfully set. Message: " + val);
self.displayValidationError(key,val);
validated = false;
}
});
settings.substituted_ssid = data.substituted_ssid;
if(complete) complete(validated);
}
}
}).fail(function() {
console.log("Settings:saveSettings: failed");
clearTimeout(self.retrySaveSettingsDelay);
self.retrySaveSettingsDelay = setTimeout(function() { self.saveSettings(settings,complete) },self.retryDelay); // retry after delay
});
}
}
this.resetSettings = function() {
console.log("resetSettings");
//$("#restoreSettings").addClass("disabled");
self.btnRestoreSettings.attr("disabled", true);
clearTimeout(self.restoredStateHideDelay);
self.setRestoreState("Restoring...");
//console.log(" self.wifiboxURL: ",self.wifiboxURL);
if (communicateWithWifibox) {
$.ajax({
url: self.wifiboxCGIBinURL + "/config/resetall",
type: "POST",
dataType: 'json',
timeout: this.timeoutTime,
success: function(response){
console.log("Settings:resetSettings response: ",response);
if(response.status == "error") {
clearTimeout(self.retryResetSettingsDelay);
self.retryResetSettingsDelay = setTimeout(function() { self.resetSettings() },self.retryDelay); // retry after delay
} else {
settings = response.data;
console.log(" settings: ",settings);
self.fillForm(settings);
$(document).trigger(SettingsWindow.SETTINGS_LOADED);
self.btnRestoreSettings.removeAttr("disabled");
self.setRestoreState("Settings restored");
// auto hide status
clearTimeout(self.restoredStateHideDelay);
self.restoredStateHideDelay = setTimeout(function() { self.setRestoreState(""); },self.restoredStateHideDelayTime);
}
}
}).fail(function() {
console.log("Settings:resetSettings: failed");
clearTimeout(self.retryResetSettingsDelay);
self.retryResetSettingsDelay = setTimeout(function() { self.resetSettings() },self.retryDelay); // retry after delay
});
}
}
this.setRestoreState = function(text) {
self.restoreStateField.html(text);
}
this.displayValidationError = function(key,msg) {
var formElement = self.form.find("[name|='"+key+"']");
formElement.addClass("error");
var errorMsg = "<p class='errorMsg'>"+msg+"</p>"
formElement.after(errorMsg);
}
this.clearValidationErrors = function() {
self.form.find(".errorMsg").remove();
self.form.find(".error").removeClass("error");
}
this.readForm = function() {
//console.log("SettingsWindow:readForm");
var settings = {};
var selects = self.form.find("select");
selects.each( function(index,element) {
var element = $(element);
var fieldName = element.attr('name');
if(element.attr('name') != "") {
settings[element.attr('name')] = element.val();
}
});
var inputs = self.form.find("input");
inputs.each( function(index,element) {
var element = $(element);
if(element.attr('name') != "") {
switch(element.attr("type")) {
case "text":
case "number":
settings[element.attr('name')] = element.val();
break;
case "checkbox":
settings[element.attr('name')] = element.prop('checked')
break;
}
}
});
var textareas = self.form.find("textarea");
textareas.each( function(index,element) {
var element = $(element);
settings[element.attr('name')] = element.val();
});
//console.log(settings);
return settings;
}
this.signin = function() {
$.ajax({
url: self.wifiboxCGIBinURL + "/network/signin",
type: "GET",
dataType: 'json',
timeout: self.timeoutTime,
success: function(response){
console.log("Settings:signin response: ",response);
}
}).fail(function() {
console.log("Settings:signin: failed");
});
}
this.downloadlogs = function() {
window.location.href = self.wifiboxURL + "/info/logfiles"
}
this.downloadGcode = function() {
var gcode = generate_gcode();
if (gcode!=undefined) {
var blob = new Blob([gcode.join("\n")], {type: "text/plain;charset=utf-8"});
saveAs(blob, "doodle3d.gcode");
}
}
this.downloadSvg = function() {
var svg = saveToSvg();
if (svg!=undefined) {
var blob = new Blob([svg], {type: "text/plain;charset=utf-8"});
saveAs(blob, "doodle3d.svg");
}
}
/*
* Networks ui
*/
this.showAPSettings = function() {
self.apFieldSet.show();
self.clientFieldSet.hide();
}
this.showClientSettings = function() {
self.clientFieldSet.show();
self.apFieldSet.hide();
}
this.refreshNetworks = function() {
console.log("Settings:refreshNetworks");
if (communicateWithWifibox) {
$.ajax({
url: self.wifiboxURL + "/network/scan",
type: "GET",
dataType: 'json',
timeout: self.timeoutTime,
success: function(response){
console.log("Settings:refreshNetworks response: ",response);
if(response.status == "error") {
//clearTimeout(self.retrySaveSettingsDelay);
//self.retrySaveSettingsDelay = setTimeout(function() { self.saveSettings() },self.retryDelay); // retry after delay
} else {
var networks = response.data.networks
self.networks = {};
var foundCurrentNetwork = false;
var networkSelector = self.form.find("#network");
networkSelector.empty();
networkSelector.append(
$("<option></option>").val(SettingsWindow.NOT_CONNECTED).html("not connected")
);
$.each(networks, function(index,element) {
if(element.ssid == self.currentNetwork) {
foundCurrentNetwork = true;
}
networkSelector.append(
$("<option></option>").val(element.ssid).html(element.ssid)
);
self.networks[element.ssid] = element;
});
if(foundCurrentNetwork) {
networkSelector.val(self.currentNetwork);
self.selectNetwork(self.currentNetwork);
}
}
}
}).fail(function() {
});
}
}
this.retrieveNetworkStatus = function(connecting) {
//console.log("Settings:retrieveNetworkStatus");
if (communicateWithWifibox) {
$.ajax({
url: self.wifiboxURL + "/network/status",
type: "GET",
dataType: 'json',
timeout: self.timeoutTime,
success: function(response){
console.log("Settings:retrieveNetworkStatus response: ",response);
if(response.status == "error") {
} else {
var data = response.data;
if(typeof data.status === 'string') {
data.status = parseInt(data.status);
}
//console.log(" data.status: ",data.status,data.statusMessage);
// Determine which network settings to show
switch(data.status) {
case SettingsWindow.API_NOT_CONNECTED:
//console.log(" not connected & not a access point");
self.apFieldSet.show();
self.clientFieldSet.show();
self.networkMode = SettingsWindow.NETWORK_MODE_NEITHER;
break;
case SettingsWindow.API_CONNECTING_FAILED:
case SettingsWindow.API_CONNECTING:
case SettingsWindow.API_CONNECTED:
//console.log(" client mode");
self.form.find("#client").prop('checked',true);
self.apFieldSet.hide();
self.clientFieldSet.show();
if(data.status == SettingsWindow.API_CONNECTED) {
var networkSelector = self.form.find("#network");
networkSelector.val(data.ssid);
self.currentNetwork = data.ssid;
self.currentLocalIP = data.localip;
self.selectNetwork(data.ssid);
} else {
self.currentLocalIP = ""
}
self.networkMode = SettingsWindow.NETWORK_MODE_CLIENT;
break;
case SettingsWindow.API_CREATING:
case SettingsWindow.API_CREATED:
//console.log(" access point mode");
self.form.find("#ap").prop('checked',true);
self.apFieldSet.show();
self.clientFieldSet.hide();
self.currentNetwork = undefined;
self.selectNetwork(SettingsWindow.NOT_CONNECTED);
var networkSelector = self.form.find("#network");
networkSelector.val(SettingsWindow.NOT_CONNECTED);
if(data.ssid && data.status == SettingsWindow.API_CREATED) {
self.currentAP = data.ssid;
}
self.networkMode = SettingsWindow.NETWORK_MODE_ACCESS_POINT;
break;
}
self.updatePanel.setNetworkMode(self.networkMode);
// update status message
switch(data.status) {
case SettingsWindow.API_CONNECTING_FAILED:
self.setClientModeState(SettingsWindow.CONNECTING_FAILED,data.statusMessage);
self.setAPModeState(SettingsWindow.NO_AP,"");
break;
case SettingsWindow.API_NOT_CONNECTED:
self.setClientModeState(SettingsWindow.NOT_CONNECTED,"");
self.setAPModeState(SettingsWindow.NO_AP,"");
break;
case SettingsWindow.API_CONNECTING:
self.setClientModeState(SettingsWindow.CONNECTING,"");
self.setAPModeState(SettingsWindow.NO_AP,"");
break;
case SettingsWindow.API_CONNECTED:
self.setClientModeState(SettingsWindow.CONNECTED,"");
self.setAPModeState(SettingsWindow.NO_AP,"");
break;
case SettingsWindow.API_CREATING:
self.setClientModeState(SettingsWindow.NOT_CONNECTED,"");
self.setAPModeState(SettingsWindow.CREATING_AP,"");
break;
case SettingsWindow.API_CREATED:
self.setClientModeState(SettingsWindow.NOT_CONNECTED,"");
self.setAPModeState(SettingsWindow.AP,"");
break;
}
// Keep checking for updates?
if(connecting) {
switch(data.status) {
case SettingsWindow.API_CONNECTING:
case SettingsWindow.API_CREATING:
clearTimeout(self.retryRetrieveNetworkStatusDelay);
self.retryRetrieveNetworkStatusDelay = setTimeout(function() { self.retrieveNetworkStatus(connecting) },self.retryRetrieveNetworkStatusDelayTime); // retry after delay
break;
}
}
}
}
}).fail(function() {
console.log("Settings:retrieveNetworkStatus: failed");
clearTimeout(self.retryRetrieveNetworkStatusDelay);
self.retryRetrieveNetworkStatusDelay = setTimeout(function() { self.retrieveNetworkStatus(connecting) },self.retryDelay); // retry after delay
});
}
}
this.networkSelectorChanged = function(e) {
var selectedOption = $(this).find("option:selected");
self.selectNetwork(selectedOption.val());
}
this.selectNetwork = function(ssid) {
console.log("select network: ",ssid);
if(ssid == "") return;
console.log(" checked");
this.selectedNetwork = ssid;
if(this.networks == undefined || ssid == SettingsWindow.NOT_CONNECTED) {
this.hideWiFiPassword();
} else {
var network = this.networks[ssid];
if(network.encryption == "none") {
this.hideWiFiPassword();
} else {
this.showWiFiPassword();
}
this.form.find("#password").val("");
}
}
this.showWiFiPassword = function() {
this.form.find("#passwordLabel").show();
this.form.find("#password").show();
}
this.hideWiFiPassword = function() {
this.form.find("#passwordLabel").hide();
this.form.find("#password").hide();
}
this.setClientModeState = function(state,msg) {
var field = this.form.find("#clientModeState");
var btnConnect = self.form.find("#connectToNetwork");
switch(state) {
case SettingsWindow.NOT_CONNECTED:
btnConnect.removeAttr("disabled");
field.html("Not connected");
break;
case SettingsWindow.CONNECTED:
btnConnect.removeAttr("disabled");
var fieldText = "Connected to: <b>"+this.currentNetwork+"</b>.";
if(this.currentLocalIP != undefined && this.currentLocalIP != "") {
var a = "<a href='http://"+this.currentLocalIP+"' target='_black'>"+this.currentLocalIP+"</a>";
fieldText += " (IP: "+a+")";
}
field.html(fieldText);
break;
case SettingsWindow.CONNECTING:
btnConnect.attr("disabled", true);
field.html("Connecting... Reconnect by connecting your device to <b>"+this.selectedNetwork+"</b> and going to <a href='http://connect.doodle3d.com'>connect.doodle3d.com</a>");
break;
case SettingsWindow.CONNECTING_FAILED:
btnConnect.removeAttr("disabled");
field.html(msg);
break;
}
this.clientModeState = state;
}
this.setAPModeState = function(state,msg) {
var field = this.form.find("#apModeState");
var btnCreate = this.form.find("#createAP");
switch(state) {
case SettingsWindow.NO_AP:
btnCreate.removeAttr("disabled");
field.html("Not currently a access point");
break;
case SettingsWindow.AP:
btnCreate.removeAttr("disabled");
field.html("Is access point: <b>"+this.currentAP+"</b>");
break;
case SettingsWindow.CREATING_AP:
btnCreate.attr("disabled", true);
field.html("Creating access point... Reconnect by connecting your device to <b>"+settings.substituted_ssid+"</b> and going to <a href='http://draw.doodle3d.com'>draw.doodle3d.com</a>");
break;
}
this.apModeState = state;
}
this.connectToNetwork = function() {
console.log("connectToNetwork");
if(self.selectedNetwork == undefined) return;
var postData = {
ssid:self.selectedNetwork,
phrase:self.form.find("#password").val(),
recreate:true
}
console.log(" postData: ",postData);
if (communicateWithWifibox) {
// save network related settings and on complete, connect to network
self.saveSettings(self.readForm(),function(success) {
if(!success) return;
$.ajax({
url: self.wifiboxCGIBinURL + "/network/associate",
type: "POST",
data: postData,
dataType: 'json',
timeout: self.timeoutTime,
success: function(response){
console.log("Settings:connectToNetwork response: ",response);
}
}).fail(function() {
console.log("Settings:connectToNetwork: timeout (normal behavior)");
//clearTimeout(self.retrySaveSettingsDelay);
//self.retrySaveSettingsDelay = setTimeout(function() { self.saveSettings() },self.retryDelay); // retry after delay
});
});
}
self.setClientModeState(SettingsWindow.CONNECTING,"");
// after switching wifi network or creating a access point we delay the status retrieval
// because the webserver needs time to switch
clearTimeout(self.retrieveNetworkStatusDelay);
self.retrieveNetworkStatusDelay = setTimeout(function() { self.retrieveNetworkStatus(true) },self.retrieveNetworkStatusDelayTime);
}
this.createAP = function() {
console.log("createAP");
if (communicateWithWifibox) {
// save network related settings and on complete, create access point
self.saveSettings(self.readForm(),function(success) {
if(!success) return;
self.setAPModeState(SettingsWindow.CREATING_AP); // get latest substituted ssid
$.ajax({
url: self.wifiboxCGIBinURL + "/network/openap",
type: "POST",
dataType: 'json',
timeout: self.timeoutTime,
success: function(response){
console.log("Settings:createAP response: ",response);
}
}).fail(function() {
console.log("Settings:createAP: timeout (normal behavior)");
//clearTimeout(self.retrySaveSettingsDelay);
//self.retrySaveSettingsDelay = setTimeout(function() { self.saveSettings() },self.retryDelay); // retry after delay
});
self.setAPModeState(SettingsWindow.CREATING_AP,"");
// after switching wifi network or creating a access point we delay the status retrieval
// because the webserver needs time to switch
clearTimeout(self.retrieveNetworkStatusDelay);
self.retrieveNetworkStatusDelay = setTimeout(function() { self.retrieveNetworkStatus(true) },self.retrieveNetworkStatusDelayTime);
});
}
}
}
/*************************
*
*
* FROM DOODLE3D.INI
*
*/
//TODO: find all references to these variables, replace them and finally remove these.
var objectHeight = 20;
var layerHeight = .2;
//var wallThickness = .5;
//var hop = 0;
//var speed = 70;
//var travelSpeed = 200;
var enableTraveling = true;
//var filamentThickness = 2.89;
var minScale = .3;
var maxScale = 1;
var shape = "%";
var twists = 0;
//var useSubLayers = true;
//var debug = false; // debug moved to main.js
var loglevel = 2;
//var zOffset = 0;
var serverport = 8888;
var autoLoadImage = "hand.txt";
var loadOffset = [0, 0]; // x en y ?
var showWarmUp = true;
var loopAlways = false;
var firstLayerSlow = true;
var useSubpathColors = false;
var autoWarmUp = true;
//var maxObjectHeight = 150;
var maxScaleDifference = .1;
var frameRate = 60;
var quitOnEscape = true;
var screenToMillimeterScale = .3; // 0.3
//var targetTemperature = 220;
//var simplifyiterations = 10;
//var simplifyminNumPoints = 15;
//var simplifyminDistance = 3;
//var retractionspeed = 50;
//var retractionminDistance = 5;
//var retractionamount = 3;
var sideis3D = true;
var sidevisible = true;
var sidebounds = [900, 210, 131, 390];
var sideborder = [880, 169, 2, 471];
var windowbounds = [0, 0, 800, 500];
var windowcenter = true;
var windowfullscreen = false;
var autoWarmUpCommand = "M104 S230";
//var checkTemperatureInterval = 3;
var autoWarmUpDelay = 3;

183
js/Shape.js Normal file
View File

@ -0,0 +1,183 @@
function drawCircle(x0,y0,r,res) {
if (res==undefined) res = 50; //circle resolution
beginShape();
var step = Math.PI * 2.0 / res;
for (var a=0; a<Math.PI*2; a+=step) {
var x = Math.sin(a+Math.PI) * r + x0;
var y = Math.cos(a+Math.PI) * r + y0;
if (a==0) shapeMoveTo(x,y);
else shapeLineTo(x,y);
}
//close shape
var x = Math.sin(0+Math.PI) * r + x0;
var y = Math.cos(0+Math.PI) * r + y0;
shapeLineTo(x,y);
endShape();
}
function beginShape(x,y) {
setSketchModified(true);
}
function shapeMoveTo(x,y) {
_points.push([x, y, true]);
adjustBounds(x, y);
adjustPreviewTransformation();
draw(x, y, .5);
}
function shapeLineTo(x,y) {
_points.push([x, y, false]);
adjustBounds(x, y);
adjustPreviewTransformation();
draw(x, y);
}
function endShape() {
renderToImageDataPreview();
}
function getBounds(points) {
var xMin=9999,xMax=-9999,yMin=9999,yMax=-9999;
for (var i=0; i<points.length; i++) {
var p = points[i];
xMin = Math.min(xMin,p[0]);
xMax = Math.max(xMax,p[0]);
yMin = Math.min(yMin,p[1]);
yMax = Math.max(yMax,p[1]);
}
return {x:xMin,y:yMin,width:xMax-xMin,height:yMax-yMin};
}
function translatePoints(points,x,y) {
for (var i=0; i<points.length; i++) {
points[i][0] += x;
points[i][1] += y;
}
}
function scalePoints(points,x,y) {
if (y==undefined) y = x;
for (var i=0; i<points.length; i++) {
points[i][0] *= x;
points[i][1] *= y;
}
}
function rotatePoints(points, radians, cx, cy) {
if (cx==undefined) cx = 0;
if (cy==undefined) cy = 0;
var cos = Math.cos(radians);
var sin = Math.sin(radians);
for (var i=0; i<points.length; i++) {
var x = points[i][0];
var y = points[i][1];
var nx = (cos * (x - cx)) - (sin * (y - cy)) + cx;
var ny = (sin * (x - cx)) + (cos * (y - cy)) + cy;
points[i][0] = nx;
points[i][1] = ny;
}
}
function moveShape(x,y) {
var bounds = getBounds(_points);
var delta = reduceTransformToFit(x, y, 1.0, bounds);
if (delta.x != 0 || delta.y != 0) {
translatePoints(_points, delta.x, delta.y);
updateView();
}
}
//TODO: reduction of zoomValue is still not completely correct (but acceptable?)
//TODO: bounds should be cached and marked dirty on modification of points array; translations could be combined in several places
function zoomShape(zoomValue) {
var bounds = getBounds(_points);
var transform = reduceTransformToFit(0, 0, zoomValue, bounds);
translatePoints(_points, transform.x, transform.y); //move points towards center as far as necessary to avoid clipping
translatePoints(_points, -bounds.x, -bounds.y);
translatePoints(_points, -bounds.width / 2, -bounds.height / 2);
scalePoints(_points, transform.zf, transform.zf);
translatePoints(_points, bounds.width / 2, bounds.height / 2);
translatePoints(_points, bounds.x, bounds.y);
updateView();
}
function rotateShape(radians) {
var bounds = getBounds(_points);
var cx = bounds.x + bounds.width/2;
var cy = bounds.y + bounds.height/2;
rotatePoints(_points, radians, cx, cy);
var bounds = getBounds(_points);
var transform = reduceTransformToFit(0, 0, 1.0, bounds);
translatePoints(_points, transform.x, transform.y);
scalePoints(_points, transform.zf, transform.zf);
updateView();
}
function updateView() {
setSketchModified(true);
redrawDoodle();
adjustPreviewTransformation();
renderToImageDataPreview();
if (debugMode) {
var bounds = getBounds(_points);
drawCircleTemp(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2, 5, 'red');
}
}
//when x,y!=0,0: reduces them such that transformed bounds will still fit on canvas (given that they fit prior to the transform)
//otherwise: calculate translation + zoom reduce such that given bounds will fit on canvas after transformation
function reduceTransformToFit(x, y, zf, bounds) {
var zw = bounds.width * zf; zh = bounds.height * zf;
var newBounds = { x: bounds.x - (zw - bounds.width) / 2, y: bounds.y - (zh - bounds.height) / 2, width: zw, height: zh };
// console.log("bounds: " + bounds.x + ", " + bounds.y + ", " + bounds.width + ", " + bounds.height);
// console.log("newBounds: " + newBounds.x + ", " + newBounds.y + ", " + newBounds.width + ", " + newBounds.height);
var ldx = Math.max(x, -newBounds.x);
var rdx = Math.min(x, canvasWidth - (newBounds.x + newBounds.width));
var tdy = Math.max(y, -newBounds.y);
var bdy = Math.min(y, canvasHeight - (newBounds.y + newBounds.height));
if (x != 0 || y != 0) { //movement was requested
return { x: nearestZero(ldx, rdx), y: nearestZero(tdy, bdy) };
} else { //no movement requested
var delta = { x: ldx + rdx, y: tdy + bdy };
if (ldx != 0 && rdx != 0) delta.x /= 2;
if (tdy != 0 && bdy != 0) delta.y /= 2;
delta.x /= zf;
delta.y /= zf;
var zxMax = Math.min(zf, canvasWidth / newBounds.width);
var zyMax = Math.min(zf, canvasHeight / newBounds.height);
var oldZF = zf;
// var dir = zf >= 1.0 ? 1 : 0;
zf = Math.min(zxMax, zyMax);
// if (dir == 1 && zf < 1.0) zf = 1;
console.log("orgZF, zxMax, zyMax, finZF: " + oldZF + ", " + zxMax + ", " + zyMax + ", " + zf);
return { x: delta.x, y: delta.y, zf: zf };
}
}
function nearestZero(v1, v2) { return Math.abs(v1) < Math.abs(v2) ? v1 : v2; }
//*draws* a circle (i.e. it is not added as points to shape)
function drawCircleTemp(x, y, r, color) {
ctx.beginPath();
ctx.lineWidth = 1;
ctx.fillStyle = color;
ctx.arc(x, y, r, 0, 2 * Math.PI, false);
ctx.fill();
ctx.stroke();
ctx.fillStyle = 'black';
}

140
js/Svg.js Normal file
View File

@ -0,0 +1,140 @@
/*
* 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.
*/
//SVG validator: http://validator.w3.org/
//SVG viewer: http://svg-edit.googlecode.com/svn/branches/2.6/editor/svg-editor.html
function saveToSvg() {
var lastX = 0, lastY = 0, lastIsMove;
var svg = '';
var boundsWidth = doodleBounds[2] - doodleBounds[0];
var boundsHeight = doodleBounds[3] - doodleBounds[1];
svg += '<?xml version="1.0" standalone="no"?>\n';
svg += '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n';
svg += '<svg width="' + boundsWidth + '" height="' + boundsHeight + '" version="1.1" xmlns="http://www.w3.org/2000/svg">\n';
svg += '\t<desc>Doodle 3D sketch</desc>\n';
var data = '';
for (var i = 0; i < _points.length; ++i) {
var x = _points[i][0], y = _points[i][1], isMove = _points[i][2];
var dx = x - lastX, dy = y - lastY;
if (i == 0)
data += 'M'; //emit absolute move on first pair of coordinates
else if (isMove != lastIsMove)
data += isMove ? 'm' : 'l';
data += dx + ',' + dy + ' ';
lastX = x;
lastY = y;
lastIsMove = isMove;
}
svg += '\t<path transform="translate(' + -doodleBounds[0] + ',' + -doodleBounds[1] + ')" d="' + data + '" fill="none" stroke="black" stroke-width="2" />\n';
var fields = JSON.stringify({'height': numLayers, 'outlineShape': VERTICALSHAPE, 'twist': rStep});
svg += '\t<!--<![CDATA[d3d-keys ' + fields + ']]>-->\n';
svg += '</svg>\n';
return svg;
}
//TODO: use local variables instead of _points,numLayers,VERTICALSHAPE and rStep so we can leave a current doodle in tact if an error occurs while parsing
function loadFromSvg(svgData) {
var mode = '', x = 0, y = 0;
console.log("loading " + svgData.length + " bytes of data...");
clearDoodle();
var p = svgData.indexOf("<path");
if (p == -1) { console.log("loadFromSvg: could not find parsing start point"); return false; }
p = svgData.indexOf('d="', p);
if (p == -1) { console.log("loadFromSvg: could not find parsing start point"); return false; }
p += 3; //skip 'd="'
var skipSpace = function() { while (svgData.charAt(p) == ' ') p++; }
var parseCommand = function() {
while (true) {
skipSpace();
var c = svgData.charAt(p);
if (c == 'M' || c == 'm' || c == 'l') { //new command letter
mode = c;
} else if (c == '"') { //end of command chain
return true;
} else { //something else, must be a pair of coordinates...
var tx = 0, ty = 0, numberEnd = 0, len = 0;
numberEnd = svgData.indexOf(',', p);
if (numberEnd == -1) { console.log("could not find comma in coordinate pair"); return false; }
len = numberEnd - p;
tx = parseInt(svgData.substr(p, len));
p += len + 1;
skipSpace();
numberEnd = svgData.indexOf(' ', p);
if (numberEnd == -1) { console.log("could not find space after coordinate pair"); return false; }
len = numberEnd - p;
ty = parseInt(svgData.substr(p, len));
p += len;
if (mode == 'M' || mode == 'L') {
x = tx; y = ty;
} else if (mode == 'm' || mode == 'l') {
x += tx; y += ty;
} else {
console.log("loadFromSvg: found coordinate pair but mode was never set");
return false;
}
var isMove = mode == 'm' || mode == 'M';
//TODO: create script-wide function for adding points?
//console.log("inserting "+x+","+y+" ",isMove);
updatePrevX = x;
updatePrevY = y;
_points.push([x, y, isMove]);
adjustBounds(x, y);
adjustPreviewTransformation();
if (isMove) draw(x, y, .5);
else draw(x, y);
}
p++;
}
return true;
};
parseCommand(); //depends on value of p, so don't move this without taking that into consideration
const fieldDefMarker = "<!--<![CDATA[d3d-keys";
p = svgData.indexOf(fieldDefMarker);
if (p == -1) { console.log("loadFromSvg: could not find metadata marker"); return false; }
p += fieldDefMarker.length;
skipSpace();
var endP = svgData.indexOf("]]>-->", p);
if (endP == -1) { console.log("loadFromSvg: could not find metadata end-marker"); return false; }
var metaFields = JSON.parse(svgData.substr(p, endP - p));
//TODO: log error and return false if parsing failed
for (var k in metaFields) {
var v = metaFields[k];
switch (k) {
case "height": numLayers = v; break;
case "outlineShape": VERTICALSHAPE = v; break;
case "twist": rStep = v; break;
}
}
renderToImageDataPreview();
return true;
}

146
js/Thermometer.js Normal file
View File

@ -0,0 +1,146 @@
/*
* 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.
*/
// TODO assess if this var is still necessary
var $displayThermometer = $("#thermometerContainer");
//TODO 2013-09-18 allow displaying temperatures HIGHER than the targTemp (it's now being capped at targTemp).
function Thermometer() {
this.currentTemperature = 0; // default val
this.targetTemperature = 0; // default val
this.thermoOverlayImg = new Image();
this.thermoOverlayImgSrc = "img/thermometer_fg_overlay.png"; // ../img/thermometer_fg_overlay.png
this.thermoWidth= 40;
this.thermoHeight = 100;
this.$canvas;
this.canvas;
this.context;
this.$container;
this.isInitted = false;
this.enabled = true;
this.thermoColors = [
[50, 200, 244], // 'cold'
[244, 190, 10], // 'warming up'
[244, 50, 50] // 'ready / hot'
];
this.init = function(targCanvas, targCanvasContainer) {
console.log("Thermometer.init()");
this.$container = targCanvasContainer;
this.$canvas = targCanvas;
this.canvas = this.$canvas[0];
this.context = this.canvas.getContext('2d');
var self = this;
this.thermoOverlayImg.onload = function() {
console.log("canvasThermoOverlay img loaded");
self.isInitted = true;
self.update(self.currentTemperature, self.targetTemperature);
};
this.thermoOverlayImg.src = this.thermoOverlayImgSrc;
}
this.update = function(curr, targ) {
// console.log("Thermometer.update(" + curr + "," + targ + ")");
if (this.isInitted) {
if(!this.enabled) return;
if (curr == undefined) curr = 0;
if (targ== undefined) targ = 180; // prevent divide by zero
var progress = curr / targ;
// progress = Math.min(progress, 1.0);
progress = Math.max(progress, 0);
var h = this.thermoHeight; // 94 // px
var paddingUnder = 15; // how far is beginpoint from bottom of thermometer
var paddingAbove = 25; // how far is endpoint from top of thermometer
var endPoint = h * .8;
var p = Math.floor((h - paddingUnder - paddingAbove) * progress); // %
// var tempHeight =
var currColor = this.thermoColors[0];
if (progress > 0.98) {
currColor = this.thermoColors[2];
} else if (progress > 0.25) {
currColor = this.thermoColors[1];
}
// clear
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.context.font = "10pt sans-serif";
// draw the thermometer clipping path
this.context.save();
this.context.beginPath();
this.context.arc(40, 80, 16, 0, 2 * Math.PI, false); // circle bottom of thermometer
this.context.arc(40, 10, 4, 0, 2 * Math.PI, false); // circle at top of thermometer tube
this.context.rect(36, 11, 8, 70); // thermometer tube
this.context.fillStyle = '#fff';
this.context.fill();
this.context.clip();
// draw rectangle which represents temperature
// rect will be clipped by the thermometer outlines
this.context.beginPath();
this.context.rect(20, h - paddingUnder - p, 60, p + paddingUnder);
//console.log(" currColor: " + currColor);
//todo Math.floor??
this.context.fillStyle = "rgb(" + currColor[0] + "," + currColor[1] + "," + currColor[2] + ")";
this.context.fill();
this.context.restore();
// additional text labels
this.context.save();
this.context.beginPath();
this.context.moveTo(32, paddingAbove);
this.context.lineTo(52, paddingAbove);
this.context.lineWidth = 2;
this.context.strokeStyle = '#000';
this.context.stroke();
this.context.fillStyle = '#000';
this.context.textAlign = "left";
this.context.textBaseline = "middle";
this.context.fillText(targ + "°", 55, paddingAbove);
this.context.restore();
// the thermometer outline png
this.context.drawImage(this.thermoOverlayImg, 20, 0);
// text
this.context.fillStyle = '#000';
this.context.textAlign="center";
this.context.fillText(curr + "°", 40, h + paddingUnder);
} else {
console.log("Thermometer.setTemperature() -> thermometer not initialized!");
}
}
this.show = function() {
this.$container.addClass("thermometerAppear");
// $("#progressbarCanvasContainer").addClass("thermometerAppear");
// this.$container.show();
this.enabled = true;
}
this.hide = function() {
this.$container.removeClass("thermometerAppear");
// $("#progressbarCanvasContainer").removeClass("thermometerAppear");
// this.$container.hide();
this.enabled = false;
}
}

262
js/UpdatePanel.js Normal file
View File

@ -0,0 +1,262 @@
/*
* 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 UpdatePanel() {
this.wifiboxURL;
this.element;
this.statusCheckInterval = 1000;
this.statusCheckDelayer; // setTimout instance
this.installedDelay = 90*1000; // Since we can't retrieve status during installation we show the installed text after a fixed delay
this.installedDelayer; // setTimout instance
this.retryDelay = 1000;
this.retryDelayer; // setTimout instance
//this.timeoutTime = 3000;
this.canUpdate = false;
this.currentVersion = "";
this.newestVersion;
this.progress;
this.imageSize;
// states from api, see Doodle3D firmware src/script/d3d-updater.lua
UpdatePanel.NONE = 1; // default state
UpdatePanel.DOWNLOADING = 2;
UpdatePanel.DOWNLOAD_FAILED = 3;
UpdatePanel.IMAGE_READY = 4; // download successfull and checked
UpdatePanel.INSTALLING = 5;
UpdatePanel.INSTALLED = 6;
UpdatePanel.INSTALL_FAILED = 7;
this.state; // update state from api
this.stateText = ""; // update state text from api
this.networkMode; // network modes from SettingsWindow
var self = this;
this.init = function(wifiboxURL,updatePanelElement) {
this.wifiboxURL = wifiboxURL;
this.element = updatePanelElement;
this.retainCheckbox = this.element.find("#retainConfiguration");
this.btnUpdate = this.element.find("#update");
this.statusDisplay = this.element.find("#updateState");
this.infoDisplay = this.element.find("#updateInfo");
this.retainCheckbox.change(this.retainChanged);
this.btnUpdate.click(this.update);
this.checkStatus(false);
}
this.retainChanged = function(e) {
console.log("UpdatePanel:retainChanged");
self.setState(self.state,true);
}
this.update = function() {
console.log("UpdatePanel:update");
self.downloadUpdate();
}
this.downloadUpdate = function() {
console.log("UpdatePanel:downloadUpdate");
$.ajax({
url: self.wifiboxURL + "/update/download",
type: "POST",
dataType: 'json',
success: function(response){
console.log("UpdatePanel:downloadUpdate response: ",response);
}
}).fail(function() {
console.log("UpdatePanel:downloadUpdate: failed");
});
self.setState(UpdatePanel.DOWNLOADING);
self.startCheckingStatus();
}
this.installUpdate = function() {
console.log("UpdatePanel:installUpdate");
// should personal sketches and settings be retained over update?
var retain = self.retainCheckbox.prop('checked');
console.log(" retain: ",retain);
self.stopCheckingStatus();
postData = {no_retain:!retain}
$.ajax({
url: self.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)");
});
self.setState(UpdatePanel.INSTALLING);
clearTimeout(self.installedDelayer);
self.installedDelayer = setTimeout(function() { self.setState(UpdatePanel.INSTALLED) },self.installedDelay);
}
this.startCheckingStatus = function() {
clearTimeout(self.statusCheckDelayer);
clearTimeout(self.retryDelayer);
self.statusCheckDelayer = setTimeout(function() { self.checkStatus(true) },self.statusCheckInterval);
}
this.stopCheckingStatus = function() {
clearTimeout(self.statusCheckDelayer);
clearTimeout(self.retryDelayer);
}
this.checkStatus = function(keepChecking) {
if (!communicateWithWifibox) return;
$.ajax({
url: self.wifiboxURL + "/update/status",
type: "GET",
dataType: 'json',
//timeout: self.timeoutTime,
success: function(response){
console.log("UpdatePanel:checkStatus response: ",response);
// Keep checking ?
if(keepChecking) {
switch(self.state){
case UpdatePanel.DOWNLOADING:
case UpdatePanel.INSTALLING:
clearTimeout(self.statusCheckDelayer);
self.statusCheckDelayer = setTimeout(function() { self.checkStatus(keepChecking) },self.statusCheckInterval);
break;
}
}
if(response.status != "error") {
var data = response.data;
self.handleStatusData(data);
}
}
}).fail(function() {
//console.log("UpdatePanel:checkStatus: failed");
if(keepChecking) {
clearTimeout(self.retryDelayer);
self.retryDelayer = setTimeout(function() { self.checkStatus(keepChecking) },self.retryDelay); // retry after delay
}
});
}
this.handleStatusData = function(data) {
//console.log("UpdatePanel:handleStatusData");
self.canUpdate = data.can_update;
if(self.currentVersion != data.current_version || self.newestVersion != data.newest_version) {
self.currentVersion = data.current_version;
self.newestVersion = data.newest_version;
self.updateInfoDisplay();
}
self.stateText = data.state_text;
self.progress = data.progress; // not always available
self.imageSize = data.image_size; // not always available
self.setState(data.state_code);
switch(this.state){
case UpdatePanel.IMAGE_READY:
self.installUpdate();
break;
}
}
this.setState = function(newState,refresh) {
console.log("UpdatePanel:setState");
if(!refresh && this.state == newState) return;
console.log("UpdatePanel:setState: ",this.state," > ",newState,"(",this.stateText,") (networkMode: ",self.networkMode,") (newestVersion: ",self.newestVersion,") (refresh: ",refresh,")");
this.state = newState;
// should personal sketches and settings be retained over update?
var retain = self.retainCheckbox.prop('checked');
console.log(" retain", retain);
// download button
// if there isn't newestVersion data something went wrong,
// probably accessing the internet
console.log(" self.newestVersion: ",self.newestVersion);
if(self.newestVersion != undefined) {
console.log(" this.state: ",this.state);
switch(this.state){
case UpdatePanel.NONE:
case UpdatePanel.DOWNLOAD_FAILED:
case UpdatePanel.INSTALL_FAILED:
console.log(" self.canUpdate: ",self.canUpdate);
if(self.canUpdate || !retain) {
self.btnUpdate.removeAttr("disabled");
} else {
self.btnUpdate.attr("disabled", true);
}
break;
default:
self.btnUpdate.attr("disabled", true);
break;
}
} else {
self.btnUpdate.attr("disabled", true);
}
this.updateStatusDisplay();
}
this.updateStatusDisplay = function() {
var text = "";
if(self.newestVersion != undefined) {
switch(this.state){
case UpdatePanel.NONE:
if(self.canUpdate) {
text = "Update(s) available.";
} else {
text = "You're up to date.";
}
break;
case UpdatePanel.DOWNLOADING:
text = "Downloading update...";
break;
case UpdatePanel.DOWNLOAD_FAILED:
text = "Downloading update failed.";
break;
case UpdatePanel.IMAGE_READY:
text = "Update downloaded.";
break;
case UpdatePanel.INSTALLING:
text = "Installing update... (will take a minute)";
break;
case UpdatePanel.INSTALLED:
text = "Update complete, please reconnect by connecting your device to the access point of your WiFi box and going to <a href='http://draw.doodle3d.com'>draw.doodle3d.com</a>";
//text = "Update complete, please <a href='javascript:location.reload(true);'>refresh Page</a>.";
break;
case UpdatePanel.INSTALL_FAILED:
text = "Installing update failed.";
break;
}
} else {
if(self.networkMode == SettingsWindow.NETWORK_MODE_ACCESS_POINT) {
text = "Can't access internet in access point mode.";
} else {
text = "Can't access internet.";
}
}
this.statusDisplay.html(text);
}
this.updateInfoDisplay = function() {
var html = 'Current version: ' + self.currentVersion +
' (<a target="d3d-curr-relnotes" href="ReleaseNotes.html">release notes</a>). ';
if(self.canUpdate) {
html += 'Latest version: ' + self.newestVersion +
' (<a target="d3d-new-relnotes" href="http://doodle3d.com/updates/images/ReleaseNotes.md">release notes</a>).';
}
self.infoDisplay.html(html);
}
this.setNetworkMode = function(networkMode) {
self.networkMode = networkMode;
}
}

132
js/WordArt.js Normal file
View File

@ -0,0 +1,132 @@
var wordArtPopup;
function initWordArt() {
$("body").append('<div id="svgfont" style="display:none"></div>');
$("#svgfont").load("img/font.svg?");
$("#btnWordArtOk").on("onButtonClick",onWordArtOk);
$("#btnWordArtCancel").on("onButtonClick",onWordArtCancel);
wordArtPopup = new Popup($("#popupWordArt"),$("#popupMask"));
wordArtPopup.setEscapeKeyHandler(onWordArtCancel);
wordArtPopup.setEnterKeyHandler(onWordArtOk);
}
function showWordArtDialog() {
buttonGroupAdd.hide();
wordArtPopup.open();
$("#txtWordArt").focus();
$("#txtWordArt").val(""); //clear textbox
}
function onWordArtCancel() {
wordArtPopup.close();
}
function onWordArtOk() {
wordArtPopup.close();
var s = $("#txtWordArt").val();
drawTextOnCanvas(s);
}
function drawTextOnCanvas(text) {
if (typeof(text) == 'string') {
var points = getStringAsPoints(text);
var bounds = getBounds(points);
var scaleX = (canvasWidth-50) / bounds.width;
var scaleY = (canvasHeight-50) / bounds.height;
var scale = Math.min(scaleX,scaleY);
scalePoints(points,scale);
var bounds = getBounds(points);
translatePoints(points,-bounds.x,-bounds.y); //left top of text is (0,0)
translatePoints(points,-bounds.width/2,-bounds.height/2); //anchor point center
translatePoints(points,canvasWidth/2,canvasHeight/2); //center in canvas
canvasDrawPoints(canvas,points);
}
}
function getStringAsPoints(text) {
var allPoints = [];
var xPos = 0;
for (var i=0; i<text.length; i++) {
if (text[i]==" ") { //space
xPos += 8;
} else { //other characters
var path = getPathFromChar(text[i]);
var points = getPointsFromPath(path);
if (points.length==0) continue;
translatePoints(points,-points[0][0],0);
var bounds = getBounds(points);
translatePoints(points,-bounds.x,0);
translatePoints(points,xPos,0);
xPos+=bounds.width;
xPos+=2;
for (var j=0; j<points.length; j++) {
allPoints.push(points[j]);
}
}
}
return allPoints;
}
function getPathFromChar(ch) {
var index = ch.charCodeAt(0)-33;
var element = $("#svgfont path")[index];
if (element==undefined) return "";
return $("#svgfont path")[index].attributes["d"].nodeValue;
}
function getPointsFromPath(path) {
var points = [];
var cmds = path.split(' ');
var cursor = { x:0.0, y:0.0 };
var move = false;
var prevCmd = "";
for (var i=0; i<cmds.length; i++) {
var cmd = cmds[i];
var xy = cmd.split(",");
if (cmd=='m') move = true;
if (xy.length==2) { // if there are two parts (a comma) we asume it's a l command. (So L is not supported)
cursor.x += parseFloat(xy[0]);
cursor.y += parseFloat(xy[1]);
points.push([cursor.x,cursor.y,move]);
move = false;
} else if (prevCmd == "h"){
cursor.x += parseFloat(cmd);
points.push([cursor.x,cursor.y,move]);
} else if (prevCmd == "v"){
cursor.y += parseFloat(cmd);
points.push([cursor.x,cursor.y,move]);
} else if (prevCmd == "H"){
cursor.x = parseFloat(cmd);
points.push([cursor.x,cursor.y,move]);
} else if (prevCmd == "V"){
cursor.y = parseFloat(cmd);
points.push([cursor.x,cursor.y,move]);
}
prevCmd = cmd;
}
return points;
}
function canvasDrawPoints(canvas,points) {
beginShape();
for (var i=0; i<points.length; i++) {
var p = points[i];
if (points[i][2]) shapeMoveTo(p[0],p[1]);
else shapeLineTo(p[0],p[1]);
}
endShape();
}

445
js/buttonbehaviors.js Normal file
View File

@ -0,0 +1,445 @@
/*
* 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.
*/
var twistIncrement = Math.PI/1800;
var btnNew, btnPrevious, btnNext, btnOops, btnStop, btnInfo;
var btnSettings, btnWordArt;
var btnToggleEdit, buttonGroupEdit, btnZoom, btnMove, btnRotate;
var btnToggleVerticalShapes, btnHeight, btnTwist, btnShape, btnConv, btnStraight, btnSine, btnDiv;
var buttonGroupAdd, popupWordArt;
var state;
var prevState;
var hasControl;
var gcodeGenerateDelayer;
var gcodeGenerateDelay = 50;
function initButtonBehavior() {
console.log("f:initButtonBehavior");
$(".btn").Button(); //initalizes all buttons
btnOops = $("#btnOops");
btnInfo = $("#btnInfo");
btnSettings = $("#btnSettings");
btnNew = $("#btnNew");
btnPrint= $("#btnPrint");
btnStop = $("#btnStop");
btnPrevious = $("#btnPrevious");
btnNext = $("#btnNext");
btnSave = $("#btnSave");
buttonGroupAdd = $("#buttonGroupAdd");
btnShape = $("#btnShape");
btnWordArt = $("#btnWordArt");
popupWordArt = $("#popupWordArt");
popupShape = $("#popupShape");
popupMask = $("#popupMask");
logoPanel = $("#logopanel");
btnToggleEdit = $("#btnToggleEdit");
buttonGroupEdit = $("#buttonGroupEdit");
btnZoom = $("#btnZoom");
btnMove = $("#btnMove");
btnRotate = $("#btnRotate");
btnToggleVerticalShapes = $("#btnToggleVerticalShapes");
buttonGroupVerticalShapes = $("#buttonGroupVerticalShapes");
btnHeight = $("#btnHeight");
btnTwist = $("#btnTwist");
btnStraight = $("#btnStraight");
btnDiv = $("#btnDiv");
btnConv = $("#btnConv");
btnSine = $("#btnSine");
btnAdd = $("#btnAdd");
logoPanel.on("onButtonClick", onLogo);
btnNew.on("onButtonClick", onBtnNew);
btnAdd.on("onButtonClick", onBtnAdd);
btnWordArt.on("onButtonClick", onBtnWordArt);
btnShape.on("onButtonClick", onBtnShape);
btnPrint.on("onButtonClick", onBtnPrint);
btnOops.on("onButtonHold", onBtnOops);
// vertical shape buttons
btnToggleVerticalShapes.on("onButtonClick", onBtnToggleVerticalShapes);
btnHeight.on("onButtonHold", onBtnHeight);
btnTwist.on("onButtonHold", onBtnTwist);
btnStraight.on("onButtonClick", onBtnStraight);
btnDiv.on("onButtonClick", onBtnDiv);
btnConv.on("onButtonClick", onBtnConv);
btnSine.on("onButtonClick", onBtnSine);
btnToggleEdit.on("onButtonClick", onBtnToggleEdit);
btnMove.on("onButtonHold", onBtnMove);
btnZoom.on("onButtonHold", onBtnZoom);
btnRotate.on("onButtonHold", onBtnRotate);
getSavedSketchStatus();
setSketchModified(false);
function onBtnToggleVerticalShapes() {
var btnImg;
if(buttonGroupVerticalShapes.is(":hidden")) {
btnImg = "img/buttons/btnArrowClose.png";
} else {
btnImg = "img/buttons/btnArrowOpen.png";
}
btnToggleVerticalShapes.attr("src",btnImg);
buttonGroupVerticalShapes.fadeToggle(BUTTON_GROUP_SHOW_DURATION);
}
function onLogo() {
location.reload();
}
function onBtnAdd() {
buttonGroupAdd.fadeToggle(BUTTON_GROUP_SHOW_DURATION);
}
function onBtnStraight() {
setVerticalShape(verticalShapes.NONE);
}
function onBtnDiv() {
setVerticalShape(verticalShapes.DIVERGING);
}
function onBtnConv() {
setVerticalShape(verticalShapes.CONVERGING);
}
function onBtnSine() {
setVerticalShape(verticalShapes.SINUS);
}
function hitTest(cursor,button,radius) {
return distance(cursor.x,cursor.y,button.x,button.y)<radius;
}
function onBtnToggleEdit() {
var btnImg;
if(buttonGroupEdit.is(":hidden")) {
btnImg = "img/buttons/btnArrowClose.png";
} else {
btnImg = "img/buttons/btnArrowOpen.png";
}
btnToggleEdit.attr("src",btnImg);
buttonGroupEdit.fadeToggle(BUTTON_GROUP_SHOW_DURATION);
}
function onBtnMove(e,cursor) {
var w = btnMove.width();
var h = btnMove.height();
var speedX = (cursor.x-w/2)*0.3;
var speedY = (cursor.y-h/2)*0.3;
console.log("move speed: ",speedX,speedY);
moveShape(speedX,speedY);
}
function onBtnZoom(e,cursor) {
var h = btnZoom.height();
var multiplier = (h/2-cursor.y)*0.003 + 1;
zoomShape(multiplier);
}
function onBtnRotate(e,cursor) {
var h = btnZoom.height();
var multiplier = (h/2-cursor.y)*0.003;
rotateShape(-multiplier);
}
function onBtnHeight(e,cursor) {
var h = btnHeight.height();
if(cursor.y < h/2) {
previewUp(true);
} else {
previewDown(true);
}
}
function onBtnTwist(e,cursor) {
var h = btnTwist.height();
var multiplier = (cursor.y-h/2)*0.0005;
previewTwist(multiplier,true);
}
function onBtnOops(e) {
oopsUndo();
}
function onBtnNew(e) {
clearDoodle();
}
function onBtnPrint(e) {
print();
}
function onBtnWordArt(e) {
showWordArtDialog();
}
function onBtnShape(e) {
showShapeDialog();
buttonGroupAdd.fadeOut();
}
enableButton(btnSettings, openSettingsWindow);
// 29-okt-2013 - we're not doing help for smartphones at the moment
if (clientInfo.isSmartphone) {
btnInfo.addClass("disabled");
} else {
function onBtnInfo(e) {
helpTours.startTour(helpTours.WELCOMETOUR);
}
enableButton(btnInfo, onBtnInfo);
}
}
function stopPrint() {
console.log("f:stopPrint() >> sendPrintCommands = " + sendPrintCommands);
//if (!confirm("Weet je zeker dat je huidige print wilt stoppen?")) return;
if (sendPrintCommands) printer.stop();
//setState(Printer.STOPPING_STATE,printer.hasControl);
printer.overruleState(Printer.STOPPING_STATE);
}
function print(e) {
console.log("f:print() >> sendPrintCommands = " + sendPrintCommands);
//$(".btnPrint").css("display","none");
if (_points.length > 2) {
//setState(Printer.BUFFERING_STATE,printer.hasControl);
printer.overruleState(Printer.BUFFERING_STATE);
btnStop.css("display","none"); // hack
// we put the gcode generation in a little delay
// so that for example the print button is disabled right away
clearTimeout(gcodeGenerateDelayer);
gcodeGenerateDelayer = setTimeout(function() {
var gcode = generate_gcode();
if (sendPrintCommands) {
if(gcode.length > 0) {
printer.print(gcode);
} else {
printer.overruleState(Printer.IDLE_STATE);
printer.startStatusCheckInterval();
}
} else {
console.log("sendPrintCommands is false: not sending print command to 3dprinter");
}
// if (debugMode) {
// $("#textdump").text("");
// $("#textdump").text(gcode.join("\n"));
// }
}, gcodeGenerateDelay);
} else {
console.log("f:print >> not enough points!");
}
//alert("Je tekening zal nu geprint worden");
//$(".btnPrint").css("display","block");
// $.post("/doodle3d.of", { data:output }, function(data) {
// btnPrint.disabled = false;
// });
}
function clearMainView() {
// console.log("f:clearMainView()");
ctx.save();
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.restore();
}
function resetPreview() {
// console.log("f:resetPreview()");
// clear preview canvas
previewCtx.save();
previewCtx.clearRect(0,0,canvas.width, canvas.height);
previewCtx.restore();
// also make new Image, otherwise the previously cached preview can be redrawn with move up/down or twist left/right
doodleImageCapture = new Image();
// reset height and rotation to default values
numLayers = previewDefaults.numLayers; // current number of preview layers
rStep = previewDefaults.rotation; // Math.PI/180; //Math.PI/40; //
}
function oopsUndo() {
// console.log("f:oopsUndo()");
_points.pop();
if (clientInfo.isSmartphone) {
// do not recalc the whole preview's bounds during undo if client device is a smartphone
redrawDoodle(false);
} else {
// recalc the whole preview's bounds during if client device is not a smartphone
redrawDoodle(true);
}
redrawPreview();
}
function previewUp(redrawLess) {
// console.log("f:previewUp()");
if (numLayers < maxNumLayers) {
numLayers++;
}
setSketchModified(true);
// redrawPreview(redrawLess);
redrawRenderedPreview(redrawLess);
}
function previewDown(redrawLess) {
// console.log("f:previewDown()");
if (numLayers > minNumLayers) {
numLayers--;
}
setSketchModified(true);
// redrawPreview(redrawLess);
redrawRenderedPreview(redrawLess);
}
function previewTwistLeft(redrawLess) {
previewTwist(-twistIncrement,true)
}
function previewTwistRight(redrawLess) {
previewTwist(twistIncrement,true)
}
function previewTwist(increment,redrawLess) {
console.log("previewTwist: ",increment);
if (redrawLess == undefined) redrawLess = false;
rStep += increment;
if(rStep < -previewRotationLimit) rStep = -previewRotationLimit;
else if(rStep > previewRotationLimit) rStep = previewRotationLimit;
redrawRenderedPreview(redrawLess);
setSketchModified(true);
}
function resetTwist() {
rStep = 0;
redrawRenderedPreview();
setSketchModified(true);
}
function update() {
setState(printer.state,printer.hasControl);
thermometer.update(printer.temperature, printer.targetTemperature);
progressbar.update(printer.currentLine, printer.totalLines);
}
function setState(newState,newHasControl) {
if(newState == state && newHasControl == hasControl) return;
prevState = state;
console.log("setState: ",prevState," > ",newState," ( ",newHasControl,")");
setDebugText("State: "+newState);
// print button
var printEnabled = (newState == Printer.IDLE_STATE && newHasControl);
if(printEnabled) {
btnPrint.removeClass("disabled"); // enable print button
btnPrint.unbind('touchstart mousedown');
btnPrint.bind('touchstart mousedown',print);
} else {
btnPrint.addClass("disabled"); // disable print button
btnPrint.unbind('touchstart mousedown');
}
// stop button
var stopEnabled = ((newState == Printer.PRINTING_STATE || newState == Printer.BUFFERING_STATE) && newHasControl);
if(stopEnabled) {
btnStop.removeClass("disabled");
btnStop.unbind('touchstart mousedown');
btnStop.bind('touchstart mousedown',stopPrint);
} else {
btnStop.addClass("disabled");
btnStop.unbind('touchstart mousedown');
}
// thermometer
switch(newState) {
case Printer.IDLE_STATE: /* fall-through */
case Printer.BUFFERING_STATE: /* fall-through */
case Printer.PRINTING_STATE: /* fall-through */
case Printer.STOPPING_STATE:
thermometer.show();
break;
default:
thermometer.hide();
break;
}
// progress indicator
switch(newState) {
case Printer.PRINTING_STATE:
progressbar.show();
break;
default:
progressbar.hide();
break;
}
/* settings button */
switch(newState) {
case Printer.IDLE_STATE:
enableButton(btnSettings, openSettingsWindow);
break;
case Printer.WIFIBOX_DISCONNECTED_STATE: /* fall-through */
case Printer.BUFFERING_STATE: /* fall-through */
case Printer.PRINTING_STATE: /* fall-through */
case Printer.STOPPING_STATE:
disableButton(btnSettings);
break;
default:
enableButton(btnSettings, openSettingsWindow);
break;
}
/* save, next and prev buttons */
switch(newState) {
case Printer.WIFIBOX_DISCONNECTED_STATE:
disableButton(btnPrevious);
disableButton(btnNext);
disableButton(btnSave);
break;
default:
updatePrevNextButtonState();
if (isModified) enableButton(btnSave, saveSketch);
break;
}
if(newState == Printer.WIFIBOX_DISCONNECTED_STATE) {
message.set("Lost connection to WiFi box",Message.ERROR);
} else if(prevState == Printer.WIFIBOX_DISCONNECTED_STATE) {
message.set("Connected to WiFi box",Message.INFO,true);
} else if(newState == Printer.DISCONNECTED_STATE) {
message.set("Printer disconnected",Message.WARNING,true);
} else if(prevState == Printer.DISCONNECTED_STATE && newState == Printer.IDLE_STATE ||
prevState == Printer.UNKNOWN_STATE && newState == Printer.IDLE_STATE) {
message.set("Printer connected",Message.INFO,true);
} else if(prevState == Printer.PRINTING_STATE && newState == Printer.STOPPING_STATE) {
console.log("stopmsg show");
message.set("Printer stopping",Message.INFO,false);
} else if(prevState == Printer.STOPPING_STATE && newState == Printer.IDLE_STATE) {
console.log("stopmsg hide");
message.hide();
}
state = newState;
hasControl = newHasControl;
}

490
js/canvasDrawing.js Normal file
View File

@ -0,0 +1,490 @@
/*
* 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.
*/
/* * * * * * * * * *
*
* VARS
*
* * * * * * * * * */
var preview;
var previewCtx;
var svgPathRegExp = /[LM]\d* \d*/ig;
var svgPathParamsRegExp = /([LM])(\d*) (\d*)/;
var dragging = false;
var $canvas, canvas, ctx;
var canvasWidth, canvasHeight;
var drawCanvas;
var drawCanvasTopLeftCoords = [0, 0];
var doodleBounds = [-1, -1, -1, -1]; // left, top, right, bottom
// var doodleScaleVals = [[0, 0], [1.0, 1.0]]; // [ [x, y], [scaleX, scaleY] ]
var doodleTransform = [0, 0, 1.0, 1.0]; // [ x, y, scaleX, scaleY ]
var _points = [];
var prevCountingTime = 0;
var movementCounter = 0;
var drawVariableLineWeight = false; // set to true to have the momentum of the mouse/touch movement result in larger/smaller strokes
var lineweight = 2;
var isModified = false;
/* * * * * * * * * *
*
* INIT
*
* * * * * * * * * */
function initDoodleDrawing() {
console.log("f:initDoodleDrawing()");
$canvas = $("#mycanvas");
canvas = $canvas[0];
ctx = canvas.getContext('2d');
canvasWidth = canvas.width;
canvasHeight = canvas.height;
//*
//TODO make these jquery eventhandlers (works for all)
if (!canvas.addEventListener) {
canvas.attachEvent('onmousedown',onCanvasMouseDown);
canvas.attachEvent('onmousemove',onCanvasMouseMove);
canvas.attachEvent('onmouseup',onCanvasMouseUp);
canvas.attachEvent('ontouchstart',onCanvasTouchDown);
canvas.attachEvent('ontouchmove',onCanvasTouchMove);
canvas.attachEvent('ontouchend',onCanvasTouchEnd);
document.body.attachEvent('ontouchmove',prevent);
} else {
canvas.addEventListener('mousedown',onCanvasMouseDown,false);
canvas.addEventListener('mousemove',onCanvasMouseMove,false);
canvas.addEventListener('mouseup',onCanvasMouseUp,false);
canvas.addEventListener('touchstart',onCanvasTouchDown,false);
canvas.addEventListener('touchmove',onCanvasTouchMove,false);
canvas.addEventListener('touchend',onCanvasTouchEnd,false);
if (!debugMode) document.body.addEventListener('touchmove',prevent,false);
}
//*/
// drawCanvas = $(".drawareacontainer");
drawCanvas = $("#mycanvasContainer"); // $("#drawAreaContainer")
console.log("drawCanvasTopLeftCoords: " + drawCanvasTopLeftCoords);
// drawCanvasTopLeftCoords[0] = drawCanvas.css("left").match(/[0-9]/g).join("");
// drawCanvasTopLeftCoords[1] = drawCanvas.css("top").match(/[0-9]/g).join("");
drawCanvasTopLeftCoords[0] = drawCanvas.offset().left;
drawCanvasTopLeftCoords[1] = drawCanvas.offset().top;
// drawCanvasTopLeftCoords[0] = drawCanvas[0].offsetParent.offsetLeft;
// drawCanvasTopLeftCoords[1] = drawCanvas[0].offsetParent.offsetTop;
console.log("f:initDoodleDrawing() >> canvasWidth: " + canvasWidth);
console.log("f:initDoodleDrawing() >> canvasHeight: " + canvasHeight);
}
/* * * * * * * * * *
*
* CANVAS DRAWING FUNCTION
*
* * * * * * * * * */
function draw(_x, _y, _width) {
// console.log("f:draw() >> _width: " + _width);
if (prevX == 0 && prevY ==0) {
prevX = _x;
prevY = _y;
}
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(_x, _y);
if (_width != undefined) {
ctx.lineWidth = _width;
} else {
if (drawVariableLineWeight) {
var dist = Math.sqrt(Math.pow((prevX - _x), 2) + Math.pow((prevY - _y), 2));
if (dist < 10) {
lineweight += .25;
} else if (dist < 20) {
lineweight += .5;
} else if (dist < 30) {
lineweight += .75;
} else if (dist < 50) {
lineweight += 1;
} else if (dist < 80) {
lineweight += 1.5;
} else if (dist < 120) {
lineweight += 2.25;
} else if (dist < 170) {
lineweight += 3.5;
} else {
lineweight += 2;
}
lineweight = Math.min(lineweight, 30);
lineweight *= 0.90;
lineweight = Math.max(lineweight, 1.0);
} else {
lineweight = 2;
}
ctx.lineWidth = lineweight;
}
ctx.lineCap = 'round';
ctx.stroke();
prevX = _x;
prevY = _y;
}
/* * * * * * * * * *
*
* SUPPORTING FUNCTIONS
*
* * * * * * * * * */
function clearDoodle() {
console.log("f:clearDoodle");
updatePrevNextButtonStateOnClear();
_points = [];
prevX = 0;
prevY = 0;
updatePrevX = -1;
updatePrevY = -1;
doodleBounds = [-1, -1, -1, -1]; // left, top, right, bottom
doodleTransform = [0, 0, 1.0, 1.0]; // [ x, y, scaleX, scaleY ]
dragging = false;
clearMainView();
resetPreview();
resetVerticalShapes();
setSketchModified(false);
}
function redrawDoodle(recalcBoundsAndTransforms) {
if (recalcBoundsAndTransforms == undefined) recalcBoundsAndTransforms = false;
// console.log("f:redrawDoodle() >> recalcBoundsAndTransforms = " + recalcBoundsAndTransforms);
if (recalcBoundsAndTransforms == true) {
doodleBounds = [-1, -1, -1, -1]; // left, top, right, bottom
doodleTransform = [0, 0, 1.0, 1.0]; // [ x, y, scaleX, scaleY ]
for (var i = 0; i < _points.length; i++) {
adjustBounds(_points[i][0], _points[i][1]);
adjustPreviewTransformation();
}
}
clearMainView();
prevX = 0;
prevY = 0;
for (var i = 0; i < _points.length; i++) {
// console.log(" drawing points " + _points[i]);
if (_points[i][2] == true) {
draw(_points[i][0], _points[i][1], 0.5);
} else {
draw(_points[i][0], _points[i][1]);
}
}
}
function adjustBounds(x, y) {
var newPointsOutsideOfCurrentBounds = false;
// console.log("f:adjustBounds("+x+","+y+")");
if (doodleBounds[0] == -1) {
// if doodleBounds[0] is -1 then it isn't initted yet, so x and y are both the min and max vals
doodleBounds[0] = x;
doodleBounds[1] = y;
doodleBounds[2] = x;
doodleBounds[3] = y;
return;
}
if (x < doodleBounds[0]) {
doodleBounds[0] = x;
newPointsOutsideOfCurrentBounds = true;
}
if (x > doodleBounds[2]) {
doodleBounds[2] = x;
newPointsOutsideOfCurrentBounds = true;
}
if (y < doodleBounds[1]) {
doodleBounds[1] = y;
newPointsOutsideOfCurrentBounds = true;
}
if (y > doodleBounds[3]) {
doodleBounds[3] = y;
newPointsOutsideOfCurrentBounds = true;
}
return newPointsOutsideOfCurrentBounds;
}
// does what exactly?
function adjustPreviewTransformation() {
// console.log("f:adjustPreviewTransformation()");
doodleTransform[0] = doodleBounds[0];
doodleTransform[1] = doodleBounds[1];
var sclX, sclY, finalScl;
if (_points.length < 2) {
// console.log(_points);
sclX = 1.0;
sclY = 1.0;
finalScl = Math.min(sclX, sclY);
} else {
sclX = canvasWidth / (doodleBounds[2] - doodleBounds[0]);
sclY = canvasHeight / (doodleBounds[3] - doodleBounds[1]);
// TODO this shouldn't be a matter if choosing the smallest but should probably involve maintaining aspect ratio??
finalScl = Math.min(sclX, sclY);
}
doodleTransform[2] = finalScl;
doodleTransform[3] = finalScl;
}
/* * * * * * * * * *
*
* MOUSE/TOUCH EVENTHANDLERS
*
* * * * * * * * * */
function onCanvasMouseDown(e) {
setSketchModified(true);
// console.log("f:onCanvasMouseDown()");
// console.log("onCanvasMouseDown >> e.offsetX,e.offsetY = " + e.offsetX+","+e.offsetY);
// console.log("onCanvasMouseDown >> e.layerX,e.layerY= " + e.layerX+","+e.layerY);
// console.log("onCanvasMouseDown >> e: " , e);
dragging = true;
prevCountingTime = new Date().getTime();
movementCounter = 0
var x, y;
if (e.offsetX != undefined) {
x = e.offsetX;
y = e.offsetY;
} else {
x = e.layerX;
y = e.layerY;
}
// console.log(" x: " + x + ", y: " + y);
_points.push([x, y, true]);
adjustBounds(x, y);
adjustPreviewTransformation();
draw(x, y, 0.5);
}
var prevPoint = {x:-1, y:-1};
function onCanvasMouseMove(e) {
// console.log("f:onCanvasMouseMove()");
if (!dragging) return;
setSketchModified(true);
// console.log("onmousemove");
var x, y;
if (e.offsetX != undefined) {
x = e.offsetX;
y = e.offsetY;
} else {
x = e.layerX;
y = e.layerY;
}
if (prevPoint.x != -1 || prevPoint.y != -1) {
var dist = Math.sqrt(((prevPoint.x - x) * (prevPoint.x - x)) + ((prevPoint.y - y) * (prevPoint.y - y)));
if (dist > 5) { // replace by setting: doodle3d.simplify.minDistance
_points.push([x, y, false]);
adjustBounds(x, y);
adjustPreviewTransformation();
draw(x, y);
prevPoint.x = x;
prevPoint.y = y;
}
} else {
// this is called once, every time you start to draw a line
_points.push([x, y, false]);
adjustBounds(x, y);
adjustPreviewTransformation();
draw(x, y);
prevPoint.x = x;
prevPoint.y = y;
}
// DEBUG
// $("#textdump").text("");
// $("#textdump").append("doodlebounds:" + doodleBounds + "\n");
// $("#textdump").append("doodletransform:" + doodleTransform + "\n");
if (new Date().getTime() - prevRedrawTime > redrawInterval) {
// redrawing the whole preview the first X points ensures that the doodleBounds is set well
prevRedrawTime = new Date().getTime();
// Keep fully updating the preview if device is not a smartphone.
// (An assumption is made here that anything greater than a smartphone will have sufficient
// performance to always redraw the preview.)
if (_points.length < 50 || !clientInfo.isSmartphone) {
redrawPreview();
} else {
updatePreview(x, y, true);
}
}
}
prevUpdateFullPreview = 0; // 0 is not a timeframe but refers to the _points array
prevUpdateFullPreviewInterval = 25; // refers to number of points, not a timeframe
function onCanvasMouseUp(e) {
// console.log("f:onCanvasMouseUp()");
// console.log("onmouseup");
dragging = false;
console.log("doodleBounds: " + doodleBounds);
console.log("doodleTransform: " + doodleTransform);
// ctx.stroke();
console.log("_points.length :" + _points.length);
// console.log(_points);
// DEBUG
// $("#textdump").text("");
// $("#textdump").append("doodlebounds:" + doodleBounds + "\n");
// $("#textdump").append("doodletransform:" + doodleTransform + "\n");
// redrawPreview();
renderToImageDataPreview();
}
function onCanvasTouchDown(e) {
setSketchModified(true);
e.preventDefault();
console.log("f:onCanvasTouchDown >> e: " , e);
// var x = e.touches[0].pageX - e.touches[0].target.offsetLeft;
// var y = e.touches[0].pageY - e.touches[0].target.offsetTop;
var x = e.touches[0].pageX - drawCanvasTopLeftCoords[0];
var y = e.touches[0].pageY - drawCanvasTopLeftCoords[1];
// var x = e.touches[0].pageX;
// var y = e.touches[0].pageY;
// var x = e.touches[0].layerX;
// var y = e.touches[0].layerY;
_points.push([x, y, true]);
adjustBounds(x, y);
adjustPreviewTransformation();
draw(x, y, .5);
movementCounter = 0;
prevRedrawTime = new Date().getTime();
}
function onCanvasTouchMove(e) {
setSketchModified(true);
e.preventDefault();
// var x = e.touches[0].pageX - e.touches[0].target.offsetLeft;
// var y = e.touches[0].pageY - e.touches[0].target.offsetTop;
var x = e.touches[0].pageX - drawCanvasTopLeftCoords[0];
var y = e.touches[0].pageY - drawCanvasTopLeftCoords[1];
// var x = e.touches[0].layerX;
// var y = e.touches[0].layerY;
// var x = e.touches[0].layerX;
// var y = e.touches[0].layerY;
//console.log("f:onCanvasTouchMove >> x,y = "+x+","+y+" , e: " , e);
if (prevPoint.x != -1 || prevPoint.y != -1) {
var dist = Math.sqrt(Math.pow((prevPoint.x - x), 2) + Math.pow((prevPoint.y - y), 2));
if (dist > 5) {
_points.push([x, y, false]);
adjustBounds(x, y)
adjustPreviewTransformation();
draw(x, y);
prevPoint.x = x;
prevPoint.y = y;
}
} else {
_points.push([x, y, false]);
adjustBounds(x, y)
adjustPreviewTransformation();
draw(x, y);
prevPoint.x = x;
prevPoint.y = y;
}
// update counter -> this was for getting a handle on how often the Canvas fires a move-event
/*
movementCounter++;
if (new Date().getTime() - prevCountingTime > 1000) {
// console.log("number of moves in 1sec: " + movementCounter)
prevCountingTime= new Date().getTime();
$("#numtimes").text(movementCounter + " times");
movementCounter = 0;
}
//*/
if (new Date().getTime() - prevRedrawTime > redrawInterval) {
// redrawing the whole preview the first X points ensures that the doodleBounds is set well
if (_points.length < 50) {
redrawPreview();
} else {
updatePreview(x, y, true);
/*
if (_points.length - prevUpdateFullPreview > prevUpdateFullPreviewInterval) {
console.log("f:onTouchMove >> passed prevUpdateFullPreviewInterval, updating full preview");
redrawPreview();
prevUpdateFullPreview = _points.length;
} else {
updatePreview(x, y, true);
}
//*/
}
prevRedrawTime = new Date().getTime();
}
}
function onCanvasTouchEnd(e) {
console.log("f:onCanvasTouchEnd()");
console.log("doodleBounds: " + doodleBounds);
console.log("doodleTransform: " + doodleTransform);
// ctx.stroke();
console.log("_points.length :" + _points.length);
// redrawPreview();
renderToImageDataPreview();
}
function prevent(e) {
e.preventDefault();
}

18
js/d3dServerInterfacing.js vendored Normal file
View File

@ -0,0 +1,18 @@
/*
* 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 setTemperature(callback) {
if (callback != undefined) callback();
}
function setTemperature(callback) {
if (callback != undefined) callback();
}

306
js/gcodeGenerating.js Normal file
View File

@ -0,0 +1,306 @@
/*
* 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.
*/
var MAX_POINTS_TO_PRINT = 200000
var gcode = [];
function generate_gcode() {
console.log("f:generategcode()");
gcode = [];
console.log("settings: ",settings);
var speed = settings["printer.speed"];
var normalSpeed = speed;
var bottomSpeed = settings["printer.bottomLayerSpeed"];
var firstLayerSlow = settings["printer.firstLayerSlow"];
var bottomFlowRate = settings["printer.bottomFlowRate"];
var travelSpeed = settings["printer.travelSpeed"]
var filamentThickness = settings["printer.filamentThickness"];
var wallThickness = settings["printer.wallThickness"];
var screenToMillimeterScale = settings["printer.screenToMillimeterScale"];
var layerHeight = settings["printer.layerHeight"];
var temperature = settings["printer.temperature"];
var bedTemperature = settings["printer.bed.temperature"];
var useSubLayers = settings["printer.useSubLayers"];
var enableTraveling = settings["printer.enableTraveling"];
var retractionEnabled = settings["printer.retraction.enabled"];
var retractionspeed = settings["printer.retraction.speed"];
var retractionminDistance = settings["printer.retraction.minDistance"];
var retractionamount = settings["printer.retraction.amount"];
var preheatTemperature = settings["printer.heatup.temperature"];
var preheatBedTemperature = settings["printer.heatup.bed.temperature"];
var printerDimensionsX = settings["printer.dimensions.x"];
var printerDimensionsY = settings["printer.dimensions.y"];
var printerDimensionsZ = settings["printer.dimensions.z"];
var gCodeOffsetX = printerDimensionsX/2;
var gCodeOffsetY = printerDimensionsY/2;
var startCode = generateStartCode();
var endCode = generateEndCode();
// max amount of real world layers
var layers = printerDimensionsZ / layerHeight; //maxObjectHeight instead of objectHeight
// translate numLayers in preview to objectHeight in real world
objectHeight = Math.round(numLayers/maxNumLayers*printerDimensionsZ);
// translate preview rotation (per layer) to real world rotation
var rStepGCode = rStep * maxNumLayers/layers; ///maxNumLayers*maxObjectHeight;
// correct direction
rStepGCode = -rStepGCode;
// copy array without reference -> http://stackoverflow.com/questions/9885821/copying-of-an-array-of-objects-to-another-array-without-object-reference-in-java
var points = JSON.parse(JSON.stringify(_points));
// add gcode begin commands
gcode = gcode.concat(startCode);
var layers = printerDimensionsZ / layerHeight; //maxObjectHeight instead of objectHeight
var extruder = 0.0;
var prev = new Point(); prev.set(0, 0);
// replacement (and improvement) for ofxGetCenterofMass
var centerOfDoodle = {
x: doodleBounds[0] + (doodleBounds[2]- doodleBounds[0])/2,
y: doodleBounds[1] + (doodleBounds[3] - doodleBounds[1])/2
}
console.log("f:generategcode() >> layers: " + layers);
if (layers == Infinity) return;
// check feasibility of design
var pointsToPrint = points.length * layers*(objectHeight/printerDimensionsZ)
console.log("pointsToPrint: ",pointsToPrint);
if(pointsToPrint > MAX_POINTS_TO_PRINT) {
alert("Sorry, your doodle is too complex or too high. Please try to simplify it.");
console.log("ERROR: to many points too convert to gcode");
return [];
}
for (var layer = 0; layer < layers; layer++) {
//gcode.push(";LAYER:"+layer); //this will be added in a next release to support GCODE previewing in CURA
var p = JSON.parse(JSON.stringify(points)); // [].concat(points);
if (p.length < 2) return;
var even = (layer % 2 == 0);
var progress = layer / layers;
var layerScale = scaleFunction(progress);
// if begin point this row and end point last row are close enough, isLoop is true
var isLoop = lineLength(points[0][0], points[0][1], points[points.length-1][0], points[points.length-1][1]) < 3;
// set center of doodle as middle (ie subtract to that)
pointsTranslate(p, -centerOfDoodle.x, -centerOfDoodle.y);
pointsScale(p, screenToMillimeterScale,-screenToMillimeterScale);
pointsScale(p, layerScale, layerScale);
pointsRotate(p, rStepGCode * layer);
if (layer == 0) {
//gcode.push("M107"); //fan off
if (firstLayerSlow) {
//gcode.push("M220 S20"); //slow speed
speed = bottomSpeed;
//console.log("> speed: ",speed);
}
} else if (layer == 2) { ////////LET OP, pas bij layer 2 weer op normale snelheid ipv layer 1
gcode.push("M106"); //fan on
//gcode.push("M220 S100"); //normal speed
speed = normalSpeed;
//console.log("> speed: ",speed);
}
var curLayerCommand = 0;
var totalLayerCommands = p.length;
var layerProgress = 0;
var paths = [];
var pathCounter = -1;
// var points = [];
for (var i = 0; i < p.length; i++) {
if (p[i][2] == true) {
pathCounter++;
paths.push([]);
paths[pathCounter].push([p[i][0], p[i][1]]);
} else {
paths[pathCounter].push([p[i][0], p[i][1]]);
}
}
// loop over the subpaths (the separately drawn lines)
for (var j = 0; j < paths.length; j++) { // TODO paths > subpaths
var commands = paths[j];
// loop over the coordinates of the subpath
for (var i = 0; i < commands.length; i++) {
var last = commands.length - 1;
var to = new Point(); to.set(commands[i][0], commands[i][1]);
to.x += gCodeOffsetX;
to.y += gCodeOffsetY;
var sublayer = (layer == 0) ? 0.0 : layer + (useSubLayers ? (curLayerCommand/totalLayerCommands) : 0);
var z = (sublayer + 1) * layerHeight; // 2013-09-06 removed zOffset (seemed to be useless)
var isTraveling = !isLoop && i==0;
var doRetract = retractionEnabled && prev.distance(to) > retractionminDistance;
var firstPointEver = (layer == 0 && i == 0 && j == 0);
if (firstPointEver || layer > 2 && enableTraveling && isTraveling) { //always travel to first point, then disable traveling for first two layers and use settings for remainder of print
if (!firstPointEver && doRetract) gcode.push("G0 E" + (extruder - retractionamount).toFixed(3) + " F" + (retractionspeed * 60).toFixed(3)); //retract
gcode.push("G0 X" + to.x.toFixed(3) + " Y" + to.y.toFixed(3) + " Z" + z.toFixed(3) + " F" + (travelSpeed * 60).toFixed(3));
if (!firstPointEver && doRetract) gcode.push("G0 E" + extruder.toFixed(3) + " F" + (retractionspeed * 60).toFixed(3)); // return to normal
} else {
var f = (layer < 2) ? bottomFlowRate : 1;
extruder += prev.distance(to) * wallThickness * layerHeight / (Math.pow((filamentThickness/2), 2) * Math.PI) * f;
gcode.push("G1 X" + to.x.toFixed(3) + " Y" + to.y.toFixed(3) + " Z" + z.toFixed(3) + " F" + (speed * 60).toFixed(3) + " E" + extruder.toFixed(3));
}
curLayerCommand++;
layerProgress = curLayerCommand/totalLayerCommands;
prev = to;
}
}
if ((layer/layers) > (objectHeight/printerDimensionsZ)) {
console.log("f:generategcode() >> (layer/layers) > (objectHeight/printerDimensionsZ) is true -> breaking at layer " + (layer + 1));
break;
}
}
// add gcode end commands
gcode = gcode.concat(endCode);
return gcode;
}
function generateStartCode() {
var printerType = settings["printer.type"];
var startCode = settings["printer.startcode"];
startCode = subsituteVariables(startCode);
startCode = startCode.split("\n");
return startCode;
}
function generateEndCode() {
var printerType = settings["printer.type"];
var endCode = settings["printer.endcode"];
endCode = subsituteVariables(endCode);
endCode = endCode.split("\n");
return endCode;
}
function subsituteVariables(gcode) {
//,temperature,bedTemperature,preheatTemperature,preheatBedTemperature
var temperature = settings["printer.temperature"];
var bedTemperature = settings["printer.bed.temperature"];
var preheatTemperature = settings["printer.heatup.temperature"];
var preheatBedTemperature = settings["printer.heatup.bed.temperature"];
var printerType = settings["printer.type"];
var heatedbed = settings["printer.heatedbed"];
switch (printerType) {
case "makerbot_replicator2": printerType = "r2"; break;
case "makerbot_replicator2x": printerType = "r2x"; break;
case "makerbot_thingomatic": printerType = "t6"; break;
case "makerbot_generic": printerType = "r2"; break;
}
var heatedBedReplacement = (heatedbed)? "" : ";";
gcode = gcode.replace(/{printingTemp}/gi ,temperature);
gcode = gcode.replace(/{printingBedTemp}/gi ,bedTemperature);
gcode = gcode.replace(/{preheatTemp}/gi ,preheatTemperature);
gcode = gcode.replace(/{preheatBedTemp}/gi ,preheatBedTemperature);
gcode = gcode.replace(/{printerType}/gi ,printerType);
gcode = gcode.replace(/{if heatedBed}/gi ,heatedBedReplacement);
return gcode;
}
function scaleFunction(percent) {
var r = 1.0;
switch (VERTICALSHAPE) {
case verticalShapes.NONE:
r = 1.0;
break;
case verticalShapes.DIVERGING:
r = .5 + (percent * .5);
break;
case verticalShapes.CONVERGING:
r = 1.0 - (percent * .8);
break;
case verticalShapes.SINUS:
r = (Math.cos(percent * Math.PI * 4) * .25) + .75;
break;
}
// return 1.0 - (percent *.8);
return r;
}
pointsTranslate = function(p, x, y) {
for (var i = 0; i < p.length; i++) {
p[i][0] += x;
p[i][1] += y;
}
}
pointsScale = function(p, sx, sy) {
for (var i = 0; i < p.length; i++) {
p[i][0] *= sx;
p[i][1] *= sy;
}
}
// rotates around point 0,0 (origin).
// Not the prettiest kind of rotation solution but in our case we're assuming that the points have just been translated to origin
pointsRotate = function(p, ang) {
var _ang, dist;
for (var i = 0; i < p.length; i++) {
dist = Math.sqrt(p[i][0] * p[i][0] + p[i][1] * p[i][1]);
_ang = Math.atan2(p[i][1], p[i][0]);
p[i][0] = Math.cos(_ang + ang) * dist;
p[i][1] = Math.sin(_ang + ang) * dist;
}
}
//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/math/line-length [rev. #1]
lineLength = function(x, y, x0, y0){
return Math.sqrt((x -= x0) * x + (y -= y0) * y);
};
var Point = function() {};
Point.prototype = {
x: 0,
y: 0,
set: function(_x, _y) {
this.x = _x;
this.y = _y;
},
distance: function(p) {
var d = -1;
if (p instanceof Point) {
d = Math.sqrt((p.x - this.x) * (p.x - this.x) + (p.y - this.y) * (p.y - this.y));
}
return d;
},
toString: function() {
console.log("x:" + this.x + ", y:" + this.y);
}
}

69
js/init_layout.js Normal file
View File

@ -0,0 +1,69 @@
/*
* 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.
*/
// TODO refactor this stuff, there's much to wipe
var drawAreaContainerMinHeight = 300;
var drawAreaContainerMaxHeight = 450;
function doOnResize() {
// console.log("doOnResize() >> " + new Date().getTime());
canvas.width = $canvas.width();
canvas.height = $canvas.height(); // canvas.clientHeight;
preview.width = $preview.width();
preview.height = $drawAreaContainer.height();
canvasWidth = canvas.width;
canvasHeight = canvas.height;
// console.log(" preview.width: " + preview.width + ", $preview.width(): " + $preview.width());
calcPreviewCanvasProperties();
drawCanvasTopLeftCoords[0] = drawCanvas.offset().left;
drawCanvasTopLeftCoords[1] = drawCanvas.offset().top;
redrawDoodle();
redrawPreview();
}
function initLayouting() {
console.log("f:initLayouting()");
$drawAreaContainer = $("#drawareacontainer");
canvas.width = $canvas.width();
canvas.height = $canvas.height(); // canvas.clientHeight;
preview.width = $preview.width();
preview.height = $drawAreaContainer.height();
canvasWidth = canvas.width;
canvasHeight = canvas.height;
$drawAreaContainer.show();
// window.innerHeight
console.log("window.innerHeight: " + window.innerHeight);
console.log("window.innerWidth: " + window.innerWidth);
console.log("$drawAreaContainer.innerHeight(): " + $drawAreaContainer.innerHeight());
console.log("$drawAreaContainer.offset().top: " + $drawAreaContainer.offset().top);
// timeout because it SEEMS to be beneficial for initting the layout
// 2013-09-18 seems beneficial since when?
setTimeout(_startOrientationAndChangeEventListening, 1000);
}
function _startOrientationAndChangeEventListening() {
// Initial execution if needed
$(window).on('resize', doOnResize);
// is it necessary to call these? Aren't they called by the above eventhandlers?
doOnResize();
}

232
js/libs/FileSaver.js Normal file
View File

@ -0,0 +1,232 @@
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 2013-10-21
*
* By Eli Grey, http://eligrey.com
* License: X11/MIT
* See LICENSE.md
*/
/*global self */
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
plusplus: true */
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs = saveAs
|| (typeof navigator !== 'undefined' && navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator))
|| (function(view) {
"use strict";
var
doc = view.document
// only get URL when necessary in case BlobBuilder.js hasn't overridden it yet
, get_URL = function() {
return view.URL || view.webkitURL || view;
}
, URL = view.URL || view.webkitURL || view
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
, can_use_save_link = !view.externalHost && "download" in save_link
, click = function(node) {
var event = doc.createEvent("MouseEvents");
event.initMouseEvent(
"click", true, false, view, 0, 0, 0, 0, 0
, false, false, false, false, 0, null
);
node.dispatchEvent(event);
}
, webkit_req_fs = view.webkitRequestFileSystem
, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
, throw_outside = function (ex) {
(view.setImmediate || view.setTimeout)(function() {
throw ex;
}, 0);
}
, force_saveable_type = "application/octet-stream"
, fs_min_size = 0
, deletion_queue = []
, process_deletion_queue = function() {
var i = deletion_queue.length;
while (i--) {
var file = deletion_queue[i];
if (typeof file === "string") { // file is an object URL
URL.revokeObjectURL(file);
} else { // file is a File
file.remove();
}
}
deletion_queue.length = 0; // clear queue
}
, dispatch = function(filesaver, event_types, event) {
event_types = [].concat(event_types);
var i = event_types.length;
while (i--) {
var listener = filesaver["on" + event_types[i]];
if (typeof listener === "function") {
try {
listener.call(filesaver, event || filesaver);
} catch (ex) {
throw_outside(ex);
}
}
}
}
, FileSaver = function(blob, name) {
// First try a.download, then web filesystem, then object URLs
var
filesaver = this
, type = blob.type
, blob_changed = false
, object_url
, target_view
, get_object_url = function() {
var object_url = get_URL().createObjectURL(blob);
deletion_queue.push(object_url);
return object_url;
}
, dispatch_all = function() {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
, fs_error = function() {
// don't create more object URLs than needed
if (blob_changed || !object_url) {
object_url = get_object_url(blob);
}
if (target_view) {
target_view.location.href = object_url;
} else {
window.open(object_url, "_blank");
}
filesaver.readyState = filesaver.DONE;
dispatch_all();
}
, abortable = function(func) {
return function() {
if (filesaver.readyState !== filesaver.DONE) {
return func.apply(this, arguments);
}
};
}
, create_if_not_found = {create: true, exclusive: false}
, slice
;
filesaver.readyState = filesaver.INIT;
if (!name) {
name = "download";
}
if (can_use_save_link) {
object_url = get_object_url(blob);
// FF for Android has a nasty garbage collection mechanism
// that turns all objects that are not pure javascript into 'deadObject'
// this means `doc` and `save_link` are unusable and need to be recreated
// `view` is usable though:
doc = view.document;
save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a");
save_link.href = object_url;
save_link.download = name;
var event = doc.createEvent("MouseEvents");
event.initMouseEvent(
"click", true, false, view, 0, 0, 0, 0, 0
, false, false, false, false, 0, null
);
save_link.dispatchEvent(event);
filesaver.readyState = filesaver.DONE;
dispatch_all();
return;
}
// Object and web filesystem URLs have a problem saving in Google Chrome when
// viewed in a tab, so I force save with application/octet-stream
// http://code.google.com/p/chromium/issues/detail?id=91158
if (view.chrome && type && type !== force_saveable_type) {
slice = blob.slice || blob.webkitSlice;
blob = slice.call(blob, 0, blob.size, force_saveable_type);
blob_changed = true;
}
// Since I can't be sure that the guessed media type will trigger a download
// in WebKit, I append .download to the filename.
// https://bugs.webkit.org/show_bug.cgi?id=65440
if (webkit_req_fs && name !== "download") {
name += ".download";
}
if (type === force_saveable_type || webkit_req_fs) {
target_view = view;
}
if (!req_fs) {
fs_error();
return;
}
fs_min_size += blob.size;
req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
var save = function() {
dir.getFile(name, create_if_not_found, abortable(function(file) {
file.createWriter(abortable(function(writer) {
writer.onwriteend = function(event) {
target_view.location.href = file.toURL();
deletion_queue.push(file);
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "writeend", event);
};
writer.onerror = function() {
var error = writer.error;
if (error.code !== error.ABORT_ERR) {
fs_error();
}
};
"writestart progress write abort".split(" ").forEach(function(event) {
writer["on" + event] = filesaver["on" + event];
});
writer.write(blob);
filesaver.abort = function() {
writer.abort();
filesaver.readyState = filesaver.DONE;
};
filesaver.readyState = filesaver.WRITING;
}), fs_error);
}), fs_error);
};
dir.getFile(name, {create: false}, abortable(function(file) {
// delete file if it already exists
file.remove();
save();
}), abortable(function(ex) {
if (ex.code === ex.NOT_FOUND_ERR) {
save();
} else {
fs_error();
}
}));
}), fs_error);
}), fs_error);
}
, FS_proto = FileSaver.prototype
, saveAs = function(blob, name) {
return new FileSaver(blob, name);
}
;
FS_proto.abort = function() {
var filesaver = this;
filesaver.readyState = filesaver.DONE;
dispatch(filesaver, "abort");
};
FS_proto.readyState = FS_proto.INIT = 0;
FS_proto.WRITING = 1;
FS_proto.DONE = 2;
FS_proto.error =
FS_proto.onwritestart =
FS_proto.onprogress =
FS_proto.onwrite =
FS_proto.onabort =
FS_proto.onerror =
FS_proto.onwriteend =
null;
view.addEventListener("unload", process_deletion_queue, false);
return saveAs;
}(this.self || this.window || this.content));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window
if (typeof module !== 'undefined') module.exports = saveAs;

9597
js/libs/jquery-1-9-1.js vendored Normal file

File diff suppressed because it is too large Load Diff

72
js/libs/jquery-cookie.js vendored Normal file
View File

@ -0,0 +1,72 @@
/*jshint eqnull:true */
/*!
* jQuery Cookie Plugin v1.2
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2011, Klaus Hartl
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://www.opensource.org/licenses/mit-license.php
* http://www.opensource.org/licenses/GPL-2.0
*/
(function ($, document, undefined) {
var pluses = /\+/g;
function raw(s) {
return s;
}
function decoded(s) {
return decodeURIComponent(s.replace(pluses, ' '));
}
$.cookie = function (key, value, options) {
// key and at least value given, set cookie...
if (value !== undefined && !/Object/.test(Object.prototype.toString.call(value))) {
options = $.extend({}, $.cookie.defaults, options);
if (value === null) {
options.expires = -1;
}
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setDate(t.getDate() + days);
}
value = String(value);
return (document.cookie = [
encodeURIComponent(key), '=', options.raw ? value : encodeURIComponent(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// key and possibly options given, get cookie...
options = value || $.cookie.defaults || {};
var decode = options.raw ? raw : decoded;
var cookies = document.cookie.split('; ');
for (var i = 0, parts; (parts = cookies[i] && cookies[i].split('=')); i++) {
if (decode(parts.shift()) === key) {
return decode(parts.join('='));
}
}
return null;
};
$.cookie.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key, options) !== null) {
$.cookie(key, null, options);
return true;
}
return false;
};
})(jQuery, document);

58
js/libs/jquery-coolfieldset.js vendored Normal file
View File

@ -0,0 +1,58 @@
/**
* jQuery Plugin for creating collapsible fieldset
* @requires jQuery 1.2 or later
*
* Copyright (c) 2010 Lucky <bogeyman2007@gmail.com>
* Licensed under the GPL license:
* http://www.gnu.org/licenses/gpl.html
*
* "animation" and "speed" options are added by Mitch Kuppinger <dpneumo@gmail.com>
*/
(function($) {
function hideFieldsetContent(obj, options){
if(options.animation==true)
obj.find('div').slideUp(options.speed);
else
obj.find('div').hide();
obj.removeClass("expanded");
obj.addClass("collapsed");
}
function showFieldsetContent(obj, options){
if(options.animation==true)
obj.find('div').slideDown(options.speed);
else
obj.find('div').show();
obj.removeClass("collapsed");
obj.addClass("expanded");
}
$.fn.coolfieldset = function(options){
var setting={collapsed:false, animation:true, speed:'medium'};
$.extend(setting, options);
this.each(function(){
var fieldset=$(this);
fieldset.addClass("collapsible");
var legend=fieldset.children('legend');
if(setting.collapsed==true){
hideFieldsetContent(fieldset, setting);
} else {
showFieldsetContent(fieldset, setting);
}
// legend.bind('touchstart mousedown', function() {
legend.bind('click', function() {
if(fieldset.hasClass("collapsed")) {
showFieldsetContent(fieldset, setting);
} else {
hideFieldsetContent(fieldset, setting);
}
});
});
}
})(jQuery);

101
js/libs/jquery-fastclick.js vendored Normal file
View File

@ -0,0 +1,101 @@
/**
* jQuery.fastClick.js
*
* Work around the 300ms delay for the click event in some mobile browsers.
*
* Code based on <http://code.google.com/mobile/articles/fast_buttons.html>
*
* @usage
* $('button').fastClick(function() {alert('clicked!');});
*
* @license MIT
* @author Dave Hulbert (dave1010)
* @version 1.0.0 2013-01-17
*/
/*global document, window, jQuery, Math */
(function($) {
$.fn.fastClick = function(handler) {
return $(this).each(function(){
$.FastButton($(this)[0], handler);
});
};
$.FastButton = function(element, handler) {
var startX, startY;
var reset = function() {
$(element).unbind('touchend');
$("body").unbind('touchmove.fastClick');
};
var onClick = function(event) {
event.stopPropagation();
reset();
handler.call(this, event);
if (event.type === 'touchend') {
$.clickbuster.preventGhostClick(startX, startY);
}
};
var onTouchMove = function(event) {
if (Math.abs(event.originalEvent.touches[0].clientX - startX) > 10 ||
Math.abs(event.originalEvent.touches[0].clientY - startY) > 10) {
reset();
}
};
var onTouchStart = function(event) {
event.stopPropagation();
$(element).bind('touchend', onClick);
$("body").bind('touchmove.fastClick', onTouchMove);
startX = event.originalEvent.touches[0].clientX;
startY = event.originalEvent.touches[0].clientY;
};
$(element).bind({
touchstart: onTouchStart,
click: onClick
});
};
$.clickbuster = {
coordinates: [],
preventGhostClick: function(x, y) {
$.clickbuster.coordinates.push(x, y);
window.setTimeout($.clickbuster.pop, 2500);
},
pop: function() {
$.clickbuster.coordinates.splice(0, 2);
},
onClick: function(event) {
var x, y, i;
for (i = 0; i < $.clickbuster.coordinates.length; i += 2) {
x = $.clickbuster.coordinates[i];
y = $.clickbuster.coordinates[i + 1];
if (Math.abs(event.clientX - x) < 25 && Math.abs(event.clientY - y) < 25) {
event.stopPropagation();
event.preventDefault();
}
}
}
};
$(function(){
if (document.addEventListener){
document.addEventListener('click', $.clickbuster.onClick, true);
} else if (document.attachEvent){
// for IE 7/8
document.attachEvent('onclick', $.clickbuster.onClick);
}
});
}(jQuery));

933
js/libs/jquery-joyride-2-1.js vendored Executable file
View File

@ -0,0 +1,933 @@
/*
* jQuery Foundation Joyride Plugin 2.1
* http://foundation.zurb.com
* Copyright 2013, ZURB
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
/*jslint unparam: true, browser: true, indent: 2 */
;(function ($, window, undefined) {
'use strict';
var defaults = {
'version' : '2.1',
'tipLocation' : 'bottom', // 'top' or 'bottom' in relation to parent
'nubPosition' : 'auto', // override on a per tooltip bases
'scroll' : true, // whether to scroll to tips
'scrollSpeed' : 300, // Page scrolling speed in milliseconds
'timer' : 0, // 0 = no timer , all other numbers = timer in milliseconds
'autoStart' : false, // true or false - false tour starts when restart called
'startTimerOnClick' : true, // true or false - true requires clicking the first button start the timer
'startOffset' : 0, // the index of the tooltip you want to start on (index of the li)
'nextButton' : true, // true or false to control whether a next button is used
'tipAnimation' : 'fade', // 'pop' or 'fade' in each tip
'pauseAfter' : [], // array of indexes where to pause the tour after
'tipAnimationFadeSpeed': 300, // when tipAnimation = 'fade' this is speed in milliseconds for the transition
'cookieMonster' : false, // true or false to control whether cookies are used
'cookieName' : 'joyride', // Name the cookie you'll use
'cookieDomain' : false, // Will this cookie be attached to a domain, ie. '.notableapp.com'
'cookiePath' : false, // Set to '/' if you want the cookie for the whole website
'localStorage' : false, // true or false to control whether localstorage is used
'localStorageKey' : 'joyride', // Keyname in localstorage
'tipContainer' : 'body', // Where will the tip be attached
'modal' : false, // Whether to cover page with modal during the tour
'expose' : false, // Whether to expose the elements at each step in the tour (requires modal:true)
'postExposeCallback' : $.noop, // A method to call after an element has been exposed
'preRideCallback' : $.noop, // A method to call before the tour starts (passed index, tip, and cloned exposed element)
'postRideCallback' : $.noop, // A method to call once the tour closes (canceled or complete)
'preStepCallback' : $.noop, // A method to call before each step
'postStepCallback' : $.noop, // A method to call after each step
'template' : { // HTML segments for tip layout
'link' : '<a href="#close" class="joyride-close-tip">X</a>',
'timer' : '<div class="joyride-timer-indicator-wrap"><span class="joyride-timer-indicator"></span></div>',
'tip' : '<div class="joyride-tip-guide"><span class="joyride-nub"></span></div>',
'wrapper' : '<div class="joyride-content-wrapper" role="dialog"></div>',
'button' : '<a href="#" class="joyride-next-tip"></a>',
'modal' : '<div class="joyride-modal-bg"></div>',
'expose' : '<div class="joyride-expose-wrapper"></div>',
'exposeCover': '<div class="joyride-expose-cover"></div>'
}
},
Modernizr = Modernizr || false,
settings = {},
methods = {
init : function (opts) {
return this.each(function () {
if ($.isEmptyObject(settings)) {
settings = $.extend(true, defaults, opts);
// non configurable settings
settings.document = window.document;
settings.$document = $(settings.document);
settings.$window = $(window);
settings.$content_el = $(this);
settings.$body = $(settings.tipContainer);
settings.body_offset = $(settings.tipContainer).position();
settings.$tip_content = $('> li', settings.$content_el);
settings.paused = false;
settings.attempts = 0;
settings.tipLocationPatterns = {
top: ['bottom'],
bottom: [], // bottom should not need to be repositioned
left: ['right', 'top', 'bottom'],
right: ['left', 'top', 'bottom']
};
// are we using jQuery 1.7+
methods.jquery_check();
// can we create cookies?
if (!$.isFunction($.cookie)) {
settings.cookieMonster = false;
}
// generate the tips and insert into dom.
if ( (!settings.cookieMonster || !$.cookie(settings.cookieName) ) &&
(!settings.localStorage || !methods.support_localstorage() || !localStorage.getItem(settings.localStorageKey) ) ) {
settings.$tip_content.each(function (index) {
methods.create({$li : $(this), index : index});
});
// show first tip
if(settings.autoStart)
{
if (!settings.startTimerOnClick && settings.timer > 0) {
methods.show('init');
methods.startTimer();
} else {
methods.show('init');
}
}
}
settings.$document.on('click.joyride', '.joyride-next-tip, .joyride-modal-bg', function (e) {
e.preventDefault();
if (settings.$li.next().length < 1) {
methods.end();
} else if (settings.timer > 0) {
clearTimeout(settings.automate);
methods.hide();
methods.show();
methods.startTimer();
} else {
methods.hide();
methods.show();
}
});
settings.$document.on('click.joyride', '.joyride-close-tip', function (e) {
e.preventDefault();
methods.end();
});
settings.$window.bind('resize.joyride', function (e) {
if(settings.$li){
if(settings.exposed && settings.exposed.length>0){
var $els = $(settings.exposed);
$els.each(function(){
var $this = $(this);
methods.un_expose($this);
methods.expose($this);
});
}
if (methods.is_phone()) {
methods.pos_phone();
} else {
methods.pos_default();
}
}
});
} else {
methods.restart();
}
});
},
// call this method when you want to resume the tour
resume : function () {
methods.set_li();
methods.show();
},
nextTip: function(){
if (settings.$li.next().length < 1) {
methods.end();
} else if (settings.timer > 0) {
clearTimeout(settings.automate);
methods.hide();
methods.show();
methods.startTimer();
} else {
methods.hide();
methods.show();
}
},
tip_template : function (opts) {
var $blank, content, $wrapper;
opts.tip_class = opts.tip_class || '';
$blank = $(settings.template.tip).addClass(opts.tip_class);
content = $.trim($(opts.li).html()) +
methods.button_text(opts.button_text) +
settings.template.link +
methods.timer_instance(opts.index);
$wrapper = $(settings.template.wrapper);
if (opts.li.attr('data-aria-labelledby')) {
$wrapper.attr('aria-labelledby', opts.li.attr('data-aria-labelledby'))
}
if (opts.li.attr('data-aria-describedby')) {
$wrapper.attr('aria-describedby', opts.li.attr('data-aria-describedby'))
}
$blank.append($wrapper);
$blank.first().attr('data-index', opts.index);
$('.joyride-content-wrapper', $blank).append(content);
return $blank[0];
},
timer_instance : function (index) {
var txt;
if ((index === 0 && settings.startTimerOnClick && settings.timer > 0) || settings.timer === 0) {
txt = '';
} else {
txt = methods.outerHTML($(settings.template.timer)[0]);
}
return txt;
},
button_text : function (txt) {
if (settings.nextButton) {
txt = $.trim(txt) || 'Next';
txt = methods.outerHTML($(settings.template.button).append(txt)[0]);
} else {
txt = '';
}
return txt;
},
create : function (opts) {
// backwards compatibility with data-text attribute
var buttonText = opts.$li.attr('data-button') || opts.$li.attr('data-text'),
tipClass = opts.$li.attr('class'),
$tip_content = $(methods.tip_template({
tip_class : tipClass,
index : opts.index,
button_text : buttonText,
li : opts.$li
}));
$(settings.tipContainer).append($tip_content);
},
show : function (init) {
var opts = {}, ii, opts_arr = [], opts_len = 0, p,
$timer = null;
// are we paused?
if (settings.$li === undefined || ($.inArray(settings.$li.index(), settings.pauseAfter) === -1)) {
// don't go to the next li if the tour was paused
if (settings.paused) {
settings.paused = false;
} else {
methods.set_li(init);
}
settings.attempts = 0;
if (settings.$li.length && settings.$target.length > 0) {
if(init){ //run when we first start
settings.preRideCallback(settings.$li.index(), settings.$next_tip );
if(settings.modal){
methods.show_modal();
}
}
settings.preStepCallback(settings.$li.index(), settings.$next_tip );
// parse options
opts_arr = (settings.$li.data('options') || ':').split(';');
opts_len = opts_arr.length;
for (ii = opts_len - 1; ii >= 0; ii--) {
p = opts_arr[ii].split(':');
if (p.length === 2) {
opts[$.trim(p[0])] = $.trim(p[1]);
}
}
settings.tipSettings = $.extend({}, settings, opts);
settings.tipSettings.tipLocationPattern = settings.tipLocationPatterns[settings.tipSettings.tipLocation];
if(settings.modal && settings.expose){
methods.expose();
}
// scroll if not modal
if (!/body/i.test(settings.$target.selector) && settings.scroll) {
methods.scroll_to();
}
if (methods.is_phone()) {
methods.pos_phone(true);
} else {
methods.pos_default(true);
}
$timer = $('.joyride-timer-indicator', settings.$next_tip);
if (/pop/i.test(settings.tipAnimation)) {
$timer.outerWidth(0);
if (settings.timer > 0) {
settings.$next_tip.show();
$timer.animate({
width: $('.joyride-timer-indicator-wrap', settings.$next_tip).outerWidth()
}, settings.timer);
} else {
settings.$next_tip.show();
}
} else if (/fade/i.test(settings.tipAnimation)) {
$timer.outerWidth(0);
if (settings.timer > 0) {
settings.$next_tip.fadeIn(settings.tipAnimationFadeSpeed);
settings.$next_tip.show();
$timer.animate({
width: $('.joyride-timer-indicator-wrap', settings.$next_tip).outerWidth()
}, settings.timer);
} else {
settings.$next_tip.fadeIn(settings.tipAnimationFadeSpeed);
}
}
settings.$current_tip = settings.$next_tip;
// Focus next button for keyboard users.
$('.joyride-next-tip', settings.$current_tip).focus();
methods.tabbable(settings.$current_tip);
// skip non-existent targets
} else if (settings.$li && settings.$target.length < 1) {
methods.show();
} else {
methods.end();
}
} else {
settings.paused = true;
}
},
// detect phones with media queries if supported.
is_phone : function () {
return false; // 2013-10-29 Adriaan Wormgoor - hard-falsed this
if (Modernizr) {
return Modernizr.mq('only screen and (max-width: 767px)');
}
return (settings.$window.width() < 767) ? true : false;
},
support_localstorage : function () {
if (Modernizr) {
return Modernizr.localstorage;
} else {
return !!window.localStorage;
}
},
hide : function () {
if(settings.modal && settings.expose){
methods.un_expose();
}
if(!settings.modal){
$('.joyride-modal-bg').hide();
}
settings.$current_tip.hide();
settings.postStepCallback(settings.$li.index(), settings.$current_tip);
},
set_li : function (init) {
if (init) {
settings.$li = settings.$tip_content.eq(settings.startOffset);
methods.set_next_tip();
settings.$current_tip = settings.$next_tip;
} else {
settings.$li = settings.$li.next();
methods.set_next_tip();
}
methods.set_target();
},
set_next_tip : function () {
settings.$next_tip = $('.joyride-tip-guide[data-index=' + settings.$li.index() + ']');
},
set_target : function () {
var cl = settings.$li.attr('data-class'),
id = settings.$li.attr('data-id'),
$sel = function () {
if (id) {
return $(settings.document.getElementById(id));
} else if (cl) {
return $('.' + cl).filter(":visible").first();
} else {
return $('body');
}
};
settings.$target = $sel();
},
scroll_to : function () {
var window_half, tipOffset;
window_half = settings.$window.height() / 2;
tipOffset = Math.ceil(settings.$target.offset().top - window_half + settings.$next_tip.outerHeight());
$("html, body").stop().animate({
scrollTop: tipOffset
}, settings.scrollSpeed);
},
paused : function () {
if (($.inArray((settings.$li.index() + 1), settings.pauseAfter) === -1)) {
return true;
}
return false;
},
destroy : function () {
if(!$.isEmptyObject(settings)){
settings.$document.off('.joyride');
}
$(window).off('.joyride');
$('.joyride-close-tip, .joyride-next-tip, .joyride-modal-bg').off('.joyride');
$('.joyride-tip-guide, .joyride-modal-bg').remove();
clearTimeout(settings.automate);
settings = {};
},
restart : function () {
if(!settings.autoStart)
{
if (!settings.startTimerOnClick && settings.timer > 0) {
methods.show('init');
methods.startTimer();
} else {
methods.show('init');
}
settings.autoStart = true;
}
else
{
methods.hide();
settings.$li = undefined;
methods.show('init');
}
},
pos_default : function (init) {
var half_fold = Math.ceil(settings.$window.height() / 2),
tip_position = settings.$next_tip.offset(),
$nub = $('.joyride-nub', settings.$next_tip),
nub_width = Math.ceil($nub.outerWidth() / 2),
nub_height = Math.ceil($nub.outerHeight() / 2),
toggle = init || false;
// tip must not be "display: none" to calculate position
if (toggle) {
settings.$next_tip.css('visibility', 'hidden');
settings.$next_tip.show();
}
if (!/body/i.test(settings.$target.selector)) {
var
topAdjustment = settings.tipSettings.tipAdjustmentY ? parseInt(settings.tipSettings.tipAdjustmentY) : 0,
leftAdjustment = settings.tipSettings.tipAdjustmentX ? parseInt(settings.tipSettings.tipAdjustmentX) : 0;
if (methods.bottom()) {
settings.$next_tip.css({
top: (settings.$target.offset().top + nub_height + settings.$target.outerHeight() + topAdjustment),
left: settings.$target.offset().left - (settings.$next_tip.outerWidth() * 0.70) + leftAdjustment});
if (/right/i.test(settings.tipSettings.nubPosition)) {
settings.$next_tip.css('left', settings.$target.offset().left + settings.$target.outerWidth());
}
methods.nub_position($nub, settings.tipSettings.nubPosition, 'top');
} else if (methods.top()) {
settings.$next_tip.css({
top: (settings.$target.offset().top - settings.$next_tip.outerHeight() - nub_height - topAdjustment),
left: settings.$target.offset().left - (settings.$next_tip.outerWidth() * .8) + leftAdjustment});
methods.nub_position($nub, settings.tipSettings.nubPosition, 'bottom');
} else if (methods.right()) {
settings.$next_tip.css({
top: settings.$target.offset().top + (topAdjustment*0.5), // HACK
left: (settings.$target.outerWidth() + settings.$target.offset().left + nub_width) + leftAdjustment});
methods.nub_position($nub, settings.tipSettings.nubPosition, 'left');
} else if (methods.left()) {
settings.$next_tip.css({
top: settings.$target.offset().top + (topAdjustment*0.5), // HACK
left: (settings.$target.offset().left - settings.$next_tip.outerWidth() - nub_width) - leftAdjustment});
methods.nub_position($nub, settings.tipSettings.nubPosition, 'right');
}
if (!methods.visible(methods.corners(settings.$next_tip)) && settings.attempts < settings.tipSettings.tipLocationPattern.length) {
$nub.removeClass('bottom')
.removeClass('top')
.removeClass('right')
.removeClass('left');
settings.tipSettings.tipLocation = settings.tipSettings.tipLocationPattern[settings.attempts];
settings.attempts++;
methods.pos_default(true);
}
} else if (settings.$li.length) {
methods.pos_modal($nub);
}
if (toggle) {
settings.$next_tip.hide();
settings.$next_tip.css('visibility', 'visible');
}
},
pos_phone : function (init) {
var tip_height = settings.$next_tip.outerHeight(),
tip_offset = settings.$next_tip.offset(),
target_height = settings.$target.outerHeight(),
$nub = $('.joyride-nub', settings.$next_tip),
nub_height = Math.ceil($nub.outerHeight() / 2),
toggle = init || false;
$nub.removeClass('bottom')
.removeClass('top')
.removeClass('right')
.removeClass('left');
if (toggle) {
settings.$next_tip.css('visibility', 'hidden');
settings.$next_tip.show();
}
if (!/body/i.test(settings.$target.selector)) {
if (methods.top()) {
settings.$next_tip.offset({top: settings.$target.offset().top - tip_height - nub_height});
$nub.addClass('bottom');
} else {
settings.$next_tip.offset({top: settings.$target.offset().top + target_height + nub_height});
$nub.addClass('top');
}
} else if (settings.$li.length) {
methods.pos_modal($nub);
}
if (toggle) {
settings.$next_tip.hide();
settings.$next_tip.css('visibility', 'visible');
}
},
pos_modal : function ($nub) {
methods.center();
$nub.hide();
methods.show_modal();
},
show_modal : function() {
if ($('.joyride-modal-bg').length < 1) {
$('body').append(settings.template.modal).show();
}
if (/pop/i.test(settings.tipAnimation)) {
$('.joyride-modal-bg').show();
} else {
$('.joyride-modal-bg').fadeIn(settings.tipAnimationFadeSpeed);
}
},
expose: function(){
var expose,
exposeCover,
el,
origCSS,
randId = 'expose-'+Math.floor(Math.random()*10000);
if (arguments.length>0 && arguments[0] instanceof $){
el = arguments[0];
} else if(settings.$target && !/body/i.test(settings.$target.selector)){
el = settings.$target;
} else {
return false;
}
if(el.length < 1){
if(window.console){
console.error('element not valid', el);
}
return false;
}
expose = $(settings.template.expose);
settings.$body.append(expose);
// console.log("JOYRIDE >> EXPOSE INFO >> el[0].clientWidth: " + el[0].clientWidth + ", el: " , el , ", el[0]: " , el[0] , ", el.offset: " , el.offset() , ", el.clientWidth: " + el.clientWidth + ", el.outerWidth: " + el.outerWidth(true))
// console.log("JOYRIDE >> EXPOSE INFO >> el.css('margin'): " + el.css('margin'));
// console.log("JOYRIDE >> EXPOSE INFO >> el.css('margin-left'): " + el.css('margin-left'));
expose.css({
// margin: el.css('margin'),
top: el.offset().top - 2, // CAVEMAN PADDING!
left: el.offset().left - 2, // CAVEMAN PADDING!
width: el[0].clientWidth + 4, // CAVEMAN PADDING!
height: el[0].clientHeight + 4 // CAVEMAN PADDING!
// width: el.outerWidth(true),
// height: el.outerHeight(true)
});
exposeCover = $(settings.template.exposeCover);
origCSS = {
zIndex: el.css('z-index'),
position: el.css('position')
};
el.css('z-index',expose.css('z-index')*1+1);
if(origCSS.position == 'static'){
el.css('position','relative');
}
el.data('expose-css',origCSS);
exposeCover.css({
top: el.offset().top - 2, // CAVEMAN PADDING!
left: el.offset().left - 2, // CAVEMAN PADDING!
width: el[0].clientWidth + 4, // CAVEMAN PADDING!
height: el[0].clientHeight + 4 // CAVEMAN PADDING!
// top: el.offset().top,
// left: el.offset().left,
// width: el.outerWidth(true),
// height: el.outerHeight(true)
});
settings.$body.append(exposeCover);
expose.addClass(randId);
exposeCover.addClass(randId);
if(settings.tipSettings['exposeClass']){
expose.addClass(settings.tipSettings['exposeClass']);
exposeCover.addClass(settings.tipSettings['exposeClass']);
}
el.data('expose', randId);
settings.postExposeCallback(settings.$li.index(), settings.$next_tip, el);
methods.add_exposed(el);
},
un_expose: function(){
var exposeId,
el,
expose ,
origCSS,
clearAll = false;
if (arguments.length>0 && arguments[0] instanceof $){
el = arguments[0];
} else if(settings.$target && !/body/i.test(settings.$target.selector)){
el = settings.$target;
} else {
return false;
}
if(el.length < 1){
if(window.console){
console.error('element not valid', el);
}
return false;
}
exposeId = el.data('expose');
expose = $('.'+exposeId);
if(arguments.length>1){
clearAll = arguments[1];
}
if(clearAll === true){
$('.joyride-expose-wrapper,.joyride-expose-cover').remove();
} else {
expose.remove();
}
origCSS = el.data('expose-css');
if(origCSS.zIndex == 'auto'){
el.css('z-index', '');
} else {
el.css('z-index',origCSS.zIndex);
}
if(origCSS.position != el.css('position')){
if(origCSS.position == 'static'){// this is default, no need to set it.
el.css('position', '');
} else {
el.css('position',origCSS.position);
}
}
el.removeData('expose');
el.removeData('expose-z-index');
methods.remove_exposed(el);
},
add_exposed: function(el){
settings.exposed = settings.exposed || [];
if(el instanceof $){
settings.exposed.push(el[0]);
} else if(typeof el == 'string'){
settings.exposed.push(el);
}
},
remove_exposed: function(el){
var search;
if(el instanceof $){
search = el[0]
} else if (typeof el == 'string'){
search = el;
}
settings.exposed = settings.exposed || [];
for(var i=0; i<settings.exposed.length; i++){
if(settings.exposed[i] == search){
settings.exposed.splice(i,1);
return;
}
}
},
center : function () {
var $w = settings.$window;
settings.$next_tip.css({
top : ((($w.height() - settings.$next_tip.outerHeight()) / 2) + $w.scrollTop()),
left : ((($w.width() - settings.$next_tip.outerWidth()) / 2) + $w.scrollLeft())
});
return true;
},
bottom : function () {
return /bottom/i.test(settings.tipSettings.tipLocation);
},
top : function () {
return /top/i.test(settings.tipSettings.tipLocation);
},
right : function () {
return /right/i.test(settings.tipSettings.tipLocation);
},
left : function () {
return /left/i.test(settings.tipSettings.tipLocation);
},
corners : function (el) {
var w = settings.$window,
window_half = w.height() / 2,
tipOffset = Math.ceil(settings.$target.offset().top - window_half + settings.$next_tip.outerHeight()),//using this to calculate since scroll may not have finished yet.
right = w.width() + w.scrollLeft(),
offsetBottom = w.height() + tipOffset,
bottom = w.height() + w.scrollTop(),
top = w.scrollTop();
if(tipOffset < top){
if (tipOffset <0 ){
top = 0;
} else {
top = tipOffset;
}
}
if(offsetBottom > bottom){
bottom = offsetBottom;
}
return [
el.offset().top < top,
right < el.offset().left + el.outerWidth(),
bottom < el.offset().top + el.outerHeight(),
w.scrollLeft() > el.offset().left
];
},
visible : function (hidden_corners) {
var i = hidden_corners.length;
while (i--) {
if (hidden_corners[i]) return false;
}
return true;
},
nub_position : function (nub, pos, def) {
if (pos === 'auto') {
nub.addClass(def);
} else {
nub.addClass(pos);
}
},
startTimer : function () {
if (settings.$li.length) {
settings.automate = setTimeout(function () {
methods.hide();
methods.show();
methods.startTimer();
}, settings.timer);
} else {
clearTimeout(settings.automate);
}
},
end : function () {
if (settings.cookieMonster) {
$.cookie(settings.cookieName, 'ridden', { expires: 365, domain: settings.cookieDomain, path: settings.cookiePath });
}
if (settings.localStorage) {
localStorage.setItem(settings.localStorageKey, true);
}
if (settings.timer > 0) {
clearTimeout(settings.automate);
}
if(settings.modal && settings.expose){
methods.un_expose();
}
if (settings.$current_tip) {
settings.$current_tip.hide();
}
if (settings.$li) {
settings.postStepCallback(settings.$li.index(), settings.$current_tip);
settings.postRideCallback(settings.$li.index(), settings.$current_tip);
}
$('.joyride-modal-bg').hide();
},
jquery_check : function () {
// define on() and off() for older jQuery
if (!$.isFunction($.fn.on)) {
$.fn.on = function (types, sel, fn) {
return this.delegate(sel, types, fn);
};
$.fn.off = function (types, sel, fn) {
return this.undelegate(sel, types, fn);
};
return false;
}
return true;
},
outerHTML : function (el) {
// support FireFox < 11
return el.outerHTML || new XMLSerializer().serializeToString(el);
},
version : function () {
return settings.version;
},
tabbable : function (el) {
$(el).on('keydown', function( event ) {
if (!event.isDefaultPrevented() && event.keyCode &&
// Escape key.
event.keyCode === 27 ) {
event.preventDefault();
methods.end();
return;
}
// Prevent tabbing out of tour items.
if ( event.keyCode !== 9 ) {
return;
}
var tabbables = $(el).find(":tabbable"),
first = tabbables.filter(":first"),
last = tabbables.filter(":last");
if ( event.target === last[0] && !event.shiftKey ) {
first.focus( 1 );
event.preventDefault();
} else if ( event.target === first[0] && event.shiftKey ) {
last.focus( 1 );
event.preventDefault();
}
});
}
};
$.fn.joyride = function (method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.joyride');
}
};
}(jQuery, this));

184
js/main.js Normal file
View File

@ -0,0 +1,184 @@
/*
* 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.
*/
var debugMode = false; // debug mode
var sendPrintCommands = true; // if Doodle3d should send print commands to the 3d printer
var communicateWithWifibox = true; // if Doodle3d should try interfacing with the wifibox (in case one is not connected)
var wifiboxIsRemote = false; // when you want to run the client on a computer and have it remotely connect to the wifibox
var autoUpdate = true; // auto retrieve updates about temperature and progress from printer
var printer = new Printer();
var progressbar = new Progressbar();
var thermometer = new Thermometer();
var settingsWindow = new SettingsWindow();
var message = new Message();
var firstTimeSettingsLoaded = true;
var wifiboxURL; // Using the uhttpd lua handler as default, because of better performance
var wifiboxCGIBinURL; // CGI-bin, for some network stuff, where it needs to restart the webserver for example
var $drawAreaContainer, $doodleCanvas, doodleCanvas, doodleCanvasContext, $previewContainer;
var showhideInterval;
var showOrHide = false;
var clientInfo = {};
var POPUP_SHOW_DURATION = 175;
var BUTTON_GROUP_SHOW_DURATION = 80;
$(function() {
console.log("ready");
if (getURLParameter("d") != "null") debugMode = (getURLParameter("d") == "1");
if (getURLParameter("p") != "null") sendPrintCommands = (getURLParameter("p") == "1");
if (getURLParameter("c") != "null") communicateWithWifibox = (getURLParameter("c") == "1");
if (getURLParameter("r") != "null") wifiboxIsRemote = (getURLParameter("r") == "1");
if (getURLParameter("u") != "null") autoUpdate = (getURLParameter("u") == "1");
if (wifiboxIsRemote) {
// var hostname = "http://10.0.0.45";
var hostname = "http://192.168.5.1";
wifiboxURL = hostname+"/d3dapi";
wifiboxCGIBinURL = hostname+"/cgi-bin/d3dapi";
} else {
wifiboxURL = "http://" + window.location.host + "/d3dapi";
wifiboxCGIBinURL = "http://" + window.location.host + "/cgi-bin/d3dapi";
}
if (!communicateWithWifibox) {
sendPrintCommands = false; // 'communicateWithWifibox = false' implies this
}
console.log("debugMode: " + debugMode);
console.log("sendPrintCommands: " + sendPrintCommands);
console.log("communicateWithWifibox: " + communicateWithWifibox);
console.log("wifiboxIsRemote: " + wifiboxIsRemote);
console.log("wifibox URL: " + wifiboxURL);
// rudimentary client info
clientInfo.isMobileDevice = isMobileDevice();
clientInfo.isSmartphone = isSmartphone();
initDoodleDrawing();
initPreviewRendering();
initLayouting();
// initSidebars();
initButtonBehavior();
initKeyboard();
// initVerticalShapes();
initWordArt();
initShapeDialog();
disableDragging();
if (!clientInfo.isSmartphone) initHelp();
thermometer.init($("#thermometerCanvas"), $("#thermometerContainer"));
progressbar.init($("#progressbarCanvas"), $("#progressbarCanvasContainer"));
message.init($("#message"));
printer.init();
$(document).on(Printer.UPDATE,update);
settingsWindow.init(wifiboxURL,wifiboxCGIBinURL);
$(document).on(SettingsWindow.SETTINGS_LOADED, settingsLoaded);
if(debugMode) {
console.log("debug mode is true");
$("body").css("overflow", "auto");
$("#debug_textArea").css("display", "block");
//$("#preview_tmp").css("display", "block");
$("#debug_display").css("display", "block");
// show and hide the progressguage and thermometer
//showhideInterval = setInterval(showOrHideThermo, 2500);
// $("#debugContainer").css("display", "block");
/* TEMP CODE!! -> artificially populates the startgcode and endgcode textareas in the settings window */
// todo remove this temporary code...
/*
setTimeout(function() {
$("#startgcode").text("");
$("#startgcode").append("G21 (mm) \n");
$("#startgcode").append("G91 (relative) \n");
$("#startgcode").append("G28 X0 Y0 Z0 (physical home) \n");
$("#startgcode").append("M104 S230 (temperature) \n");
$("#startgcode").append("G1 E10 F250 (flow) \n");
$("#startgcode").append("G92 X-100 Y-100 Z0 E10 \n");
$("#startgcode").append("G1 Z3 F5000 (prevent diagonal line) \n");
$("#startgcode").append("G90 (absolute) \n");
$("#startgcode").append("M106 (fan on)");
console.log("$('#startgcode'): " + $("#startgcode").val());
$("#endgcode").text("");
$("#endgcode").append("G1 X-100 Y-100 F15000 (fast homing) \n");
$("#endgcode").append("M107 \n");
$("#endgcode").append("M84 (disable axes) \n");
console.log("$('#endgcode'): " + $("#endgcode").val());
}, 1000);
//*/
}
});
function disableDragging() {
$(document).bind("dragstart", function(event) {
console.log("dragstart");
event.preventDefault();
});
}
function enableButton(elem, handler) {
//var elem = $('#'+domId);
elem.removeClass("disabled");
elem.unbind('onButtonClick');
elem.bind('onButtonClick', handler);
}
function disableButton(elem) {
//var elem = $('#'+domId);
elem.addClass("disabled");
elem.unbind('onButtonClick');
}
function showOrHideThermo() {
console.log("f:showOrHideThermo()");
if (showOrHide) {
thermometer.hide();
progressbar.hide();
} else {
thermometer.show();
progressbar.show();
}
showOrHide = !showOrHide;
}
function settingsLoaded() {
console.log("settingsLoaded");
console.log("autoHeatup: ",settings["printer.heatup.enabled"]);
if(firstTimeSettingsLoaded) {
if(settings["printer.heatup.enabled"]) {
printer.preheat();
}
console.log("doodle3d.tour.enabled: ",settings["doodle3d.tour.enabled"]);
if(settings["doodle3d.tour.enabled"] && !clientInfo.isSmartphone) {
console.log("show tour");
initHelp();
}
firstTimeSettingsLoaded = false;
}
}
function setDebugText(text) {
$("#debug_display").text(text);
}

357
js/previewRendering.js Normal file
View File

@ -0,0 +1,357 @@
/*
* 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.
*/
//*
var $preview;
var preview;
var previewCtx;
var preview_tmp;
var previewCtx_tmp;
var previewDefaults = {
rotation: 0, //Math.PI/90,
numLayers: 10
}
var svgPathRegExp = /[LM]\d* \d*/ig;
var svgPathParamsRegExp = /([LM])(\d*) (\d*)/;
var prevRedrawTime = new Date().getTime();
var redrawInterval = 1000 / 30; // ms
function initPreviewRendering() {
console.log("f:initPreviewRendering()");
$preview = $("#preview");
preview = $preview[0];
previewCtx = preview.getContext('2d');
// DEBUG --> mbt preview_tmp (voor de toImageData truc)
var _ratio = preview.width / canvas.width;
preview_tmp = document.getElementById('preview_tmp');
preview_tmp.width = preview.width;
preview_tmp.height = canvas.height * _ratio;
$("#preview_tmp").css("top", -preview_tmp.height);
previewCtx_tmp = preview_tmp.getContext('2d');
// doodleImageCapture = new Image();
calcPreviewCanvasProperties();
redrawPreview();
// needed to
// doodleImageCapture = new Image();
}
function calcPreviewCanvasProperties() {
// console.log("f:calcPreviewCanvasProperties()");
globalScale = preview.width / canvasWidth;
layerCX = (canvasWidth / 2) * globalScale; // defined in canvasDrawing_v01.js
layerCY = (canvasHeight / 2) * globalScale; // defined in canvasDrawing_v01.js
// layerOffsetY = preview.height - 1.75 * layerCY;
layerOffsetY = preview.height * (1 - previewVerticalPadding.bottom);
yStep = (preview.height - (preview.height * (previewVerticalPadding.top + previewVerticalPadding.bottom))) / maxNumLayers;
}
// TODO (perhaps) : make the twist limit dynamic, depending on what's printable (w.r.t. overlapping)
var previewRotationLimit = Math.PI / 30; // rough estimate
var numLayers = previewDefaults.numLayers; // current number of preview layers
var maxNumLayers= 100; // maximum number of preview layers
var minNumLayers= 2; // minimum number of preview layers
var globalScale = 0.3; // global scale of preview (width preview / width canvas)
var globalAlpha = 0.20; // global alpha of preview
var scaleY = 0.4; // additional vertical scale per path for 3d effect
var viewerScale = 0.65; // additional scale to fit into preview nicely (otherwise is fills out totally)
var previewVerticalPadding = { "top" : .15, "bottom" : 0.12 }; // %
var strokeWidth = 2; //4;
//var rStep = Math.PI/40; //Math.PI/40; //
var rStep = previewDefaults.rotation; // Math.PI/180; //Math.PI/40; //
var yStep;// = preview.height / 150; // 3; //6;
//var svgWidth = 500; // 650 //parseInt($(svg).css("width"));
//var svgHeight = 450; //450; //parseInt($(svg).css("height"));
var layerCX, layerCY;
//var layerCX = (canvasWidth / 2) * globalScale; // defined in canvasDrawing_v01.js
//var layerCY = (canvasHeight / 2) * globalScale; // defined in canvasDrawing_v01.js
var layerOffsetY; //= preview.height - 1.75 * layerCY; // 330; // previewHeight - 120
var prevX = 0;
var prevY = 0;
var highlight = true; //highlight bottom, middle and top layers
var linesRaw = "";
var debug_redrawSimplification = 6;
function redrawPreview(redrawLess) {
if (redrawLess == undefined) redrawLess = false;
if (_points.length < 2) {
previewCtx.clearRect(0, 0, preview.width, preview.height);
return;
}
if (!redrawLess) {
//debug_redrawSimplification = Math.round(_points.length / 65);
//*
if (_points.length < 100) {
debug_redrawSimplification = 6;
} else if (_points.length < 250) {
debug_redrawSimplification = 7;
} else if (_points.length < 400) {
debug_redrawSimplification = 8;
} else if (_points.length < 550) {
debug_redrawSimplification = 9;
} else if (_points.length < 700) {
debug_redrawSimplification = 10;
} else {
debug_redrawSimplification = 11;
}
//*/
// console.log("debug_redrawSimplification: " + debug_redrawSimplification);
}
var y = 0;
var r = 0;
//preview.width = preview.width;
previewCtx.clearRect(0, 0, preview.width, preview.height);
previewCtx.lineWidth = strokeWidth;
previewCtx.strokeStyle = '#f00'; //"rgba(255,255,0,0)";
for(var i = 0; i < numLayers; i++) {
var verticalScaleFactor = scaleFunction(i / maxNumLayers);
if(i == 0 || i == Math.floor(numLayers/2) || i == numLayers-1) {
previewCtx.globalAlpha = 1;
} else {
previewCtx.globalAlpha = globalAlpha;
}
if (redrawLess && i%debug_redrawSimplification != 0 && !(i == 0 || i == Math.floor(numLayers/2) || i == numLayers-1) ) {
y -= yStep;
r += rStep;
continue;
}
previewCtx.save();
// previewCtx.translate(layerCX, layerOffsetY + layerCY + y);
previewCtx.translate(layerCX, layerOffsetY + y);
// previewCtx.setTransform(1, 0, 0, scaleY, layerCX, layerOffsetY+layerCY+y);
previewCtx.scale(viewerScale * verticalScaleFactor, scaleY * viewerScale * verticalScaleFactor);
previewCtx.rotate(r);
previewCtx.translate((-doodleTransform[0]) * (globalScale * doodleTransform[2]), (-doodleTransform[1]) * (globalScale * doodleTransform[3]));
var adjustedDoodlePoint = centeredAndScaledDoodlePoint(_points[0]);
previewCtx.beginPath();
previewCtx.moveTo(adjustedDoodlePoint.x, adjustedDoodlePoint.y);
for(var j = 1; j < _points.length; j++) {
adjustedDoodlePoint = centeredAndScaledDoodlePoint(_points[j])
if (redrawLess && j%debug_redrawSimplification != 0 ) continue;
previewCtx.lineTo(adjustedDoodlePoint.x, adjustedDoodlePoint.y);
}
previewCtx.stroke();
y -= yStep;
r += rStep;
previewCtx.restore();
}
previewCtx.globalAlpha = globalAlpha;
}
function renderToImageDataPreview() {
if (_points.length < 2) return;
//*
// the first step
previewCtx_tmp.clearRect(0, 0, preview.width, preview.height);
previewCtx_tmp.lineWidth = strokeWidth;
previewCtx_tmp.strokeStyle = '#f00'; //"rgba(255,255,0,0)";
previewCtx_tmp.save();
previewCtx_tmp.translate(layerCX, layerCY);
previewCtx_tmp.scale(viewerScale, viewerScale);
previewCtx_tmp.translate((-doodleTransform[0]) * (globalScale * doodleTransform[2]), (-doodleTransform[1]) * (globalScale * doodleTransform[3]));
var adjustedDoodlePt = centeredAndScaledDoodlePoint(_points[0]);
previewCtx_tmp.beginPath();
previewCtx_tmp.moveTo(adjustedDoodlePt.x, adjustedDoodlePt.y);
for(var j = 1; j < _points.length; j++) {
adjustedDoodlePt = centeredAndScaledDoodlePoint(_points[j])
previewCtx_tmp.lineTo(adjustedDoodlePt.x, adjustedDoodlePt.y);
}
previewCtx_tmp.stroke();
previewCtx_tmp.closePath();
previewCtx_tmp.restore();
//*/
// var saved_rect = previewCtx_tmp.getImageData(0, 0, layerCX*2, layerCY*2);
var saved_rect_todataurl = preview_tmp.toDataURL();
doodleImageCapture = new Image();
doodleImageCapture.onload = function() {
previewCtx.clearRect(0, 0, preview.width, preview.height);
previewCtx.lineWidth = strokeWidth;
previewCtx.strokeStyle = '#f00'; //"rgba(255,255,0,0)";
var y = 0;
var r = 0;
for(var i=0;i<numLayers;i++) {
var verticalScaleFactor = scaleFunction(i / maxNumLayers);
if(i == 0 || i == Math.floor(numLayers/2) || i == numLayers-1){
previewCtx.globalAlpha = 1;
} else {
previewCtx.globalAlpha = globalAlpha;
}
previewCtx.save();
previewCtx.translate(layerCX,layerOffsetY+y);
// previewCtx.scale(1, scaleY)
previewCtx.scale(verticalScaleFactor, scaleY * verticalScaleFactor)
previewCtx.rotate(r);
previewCtx.translate(-layerCX,-layerCY);
previewCtx.drawImage(doodleImageCapture, 0, 0);
y -= yStep;
r += rStep;
previewCtx.restore();
}
};
doodleImageCapture.src = saved_rect_todataurl;
previewCtx.globalAlpha = globalAlpha;
}
// called by the move up/down or twist left/right buttons
// it is assumed that the preview has been rendered to an Image object, which will be used to draw the preview with (much better performance)
function redrawRenderedPreview(redrawLess) {
if (redrawLess == undefined) redrawLess = false;
// console.log("f:redrawRenderedPreview()");
previewCtx.clearRect(0, 0, preview.width, preview.height);
previewCtx.lineWidth = strokeWidth;
previewCtx.strokeStyle = '#f00'; //"rgba(255,255,0,0)";
var y = 0;
var r = 0;
for(var i = 0; i < numLayers; i++) {
var verticalScaleFactor = scaleFunction(i / maxNumLayers);
if(i == 0 || i == Math.floor(numLayers/2) || i == numLayers-1){
previewCtx.globalAlpha = 1;
} else {
previewCtx.globalAlpha = globalAlpha;
}
if (redrawLess && i%2 != 0 && !(i == 0 || i == Math.floor(numLayers/2) || i == numLayers-1) ) {
y -= yStep;
r += rStep;
continue;
}
previewCtx.save();
previewCtx.translate(layerCX,layerOffsetY+y);
// previewCtx.scale(1, scaleY)
previewCtx.scale(verticalScaleFactor, scaleY * verticalScaleFactor);
previewCtx.rotate(r);
previewCtx.translate(-layerCX,-layerCY);
previewCtx.drawImage(doodleImageCapture, 0, 0);
y -= yStep;
r += rStep;
previewCtx.restore();
}
}
function centeredAndScaledDoodlePoint(p) {
var obj = { x: 0, y: 0};
obj.x = (p[0] - ((doodleBounds[2] - doodleBounds[0])/2)) * (globalScale * doodleTransform[2]);
obj.y = (p[1] - ((doodleBounds[3] - doodleBounds[1])/2)) * (globalScale * doodleTransform[3]);
// obj.x = (p[0] - (doodleBounds[2] - doodleBounds[0])) * (globalScale * doodleTransform[2]);
// obj.y = (p[1] - (doodleBounds[3] - doodleBounds[1])) * (globalScale * doodleTransform[3]);
// obj.x = (p[0] - doodleTransform[0]) * (globalScale * doodleTransform[2]);
// obj.y = (p[1] - doodleTransform[1]) * (globalScale * doodleTransform[3]);
return obj;
}
//*
var updatePrevX = -1;
var updatePrevY = -1;
function updatePreview(_x, _y, redrawLess) {
if (redrawLess == undefined) redrawLess = false;
redrawLess = false;
if (_points.length < 2) return;
if (updatePrevX == -1 || updatePrevY == -1) {
updatePrevX = _x;
updatePrevY = _y;
return;
}
// if (_points.length < 16 && Math.sqrt(Math.pow((updatePrevX - _x), 2) + Math.pow((updatePrevY - _y), 2)) < 8) return;
var y = 0;
var r = 0;
previewCtx.lineWidth = strokeWidth;
previewCtx.strokeStyle = '#f00'; //"rgba(255,255,0,0)";
for(var i = 0; i < numLayers; i++) {
if(i == 0 || i == Math.floor(numLayers/2) || i == numLayers-1) {
previewCtx.globalAlpha = 1;
} else {
previewCtx.globalAlpha = globalAlpha;
}
if (redrawLess && i%debug_redrawSimplification != 0 && !(i == 0 || i == Math.floor(numLayers/2) || i == numLayers-1) ) {
y -= yStep;
r += rStep;
continue;
}
previewCtx.save();
// previewCtx.translate(layerCX, layerOffsetY + layerCY + y);
previewCtx.translate(layerCX, layerOffsetY + y);
previewCtx.scale(viewerScale, scaleY * viewerScale);
previewCtx.rotate(r);
previewCtx.translate((-doodleTransform[0]) * (globalScale * doodleTransform[2]), (-doodleTransform[1]) * (globalScale * doodleTransform[3]));
previewCtx.beginPath();
var prevPoint = centeredAndScaledDoodlePoint([updatePrevX, updatePrevY]);
previewCtx.moveTo(prevPoint.x, prevPoint.y);
var adjustedDoodlePoint = centeredAndScaledDoodlePoint([_x, _y]);
previewCtx.lineTo(adjustedDoodlePoint.x, adjustedDoodlePoint.y);
previewCtx.stroke();
y -= yStep;
r += rStep;
previewCtx.restore();
}
previewCtx.globalAlpha = globalAlpha;
updatePrevX = _x;
updatePrevY = _y;
}
//*/

70
js/sidebar.js Normal file
View File

@ -0,0 +1,70 @@
/*
* 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.
*/
var sidebarLeft;
var sidebarRight;
function initSidebars() {
console.log("f:initSidebars()");
sidebarLeft = new SideBar();
sidebarLeft.init("#leftpanel", "hideleft", function() {
$("#leftpanel").show();
});
sidebarRight = new SideBar();
sidebarRight.init("#rightpanel", "hideright", function() {
$("#rightpanel").show();
});
}
function SideBar() {
this.initted = false;
this.$contentTarg = undefined;
this.$sideBtn = undefined;
this.contentHidden = false;
this.hideClass = "";
this.init = function(targ, hideClass, callback) {
console.log("SideBar >> f:init >> targ: " , $(targ) , ", hideClass: " + hideClass);
this.$contentTarg = $(targ);
this.hideClass = hideClass;
this.$contentTarg.addClass(this.hideClass);
this.contentHidden = true;
this.$contentTarg.append("<div class='sidebutton'></div>");
this.$sideBtn = $(targ +" .sidebutton");
var self = this;
this.$sideBtn.on('click', function(e) {
console.log("sidebutton");
self.toggleShowHide();
});
this.initted = true;
callback();
}
this.toggleShowHide = function() {
if (this.contentHidden) {
this.contentHidden = false;
this.$contentTarg.removeClass(this.hideClass);
// self.$sideBtn.addClass("sidebuttonin");
this.$sideBtn.addClass("sidebuttonin");
} else {
this.contentHidden = true;
this.$contentTarg.addClass(this.hideClass);
// self.$sideBtn.removeClass("sidebuttonin");
this.$sideBtn.removeClass("sidebuttonin");
}
}
}

164
js/sketches.js Normal file
View File

@ -0,0 +1,164 @@
/*
* 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.
*/
var currentSketchId = 0;
var numSavedSketches = 0;
function getSavedSketchStatus() {
$.ajax({
url: wifiboxURL + "/sketch/status",
dataType: 'json',
type: 'GET',
//timeout: this.timeoutTime,
success: function(response) {
if (response.status == 'error' || response.status == 'fail') {
console.log("getSavedSketchStatus fail/error: " + response.msg + " -- ", response);
} else {
console.log("getSavedSketchStatus success: num. saved: " + response.data.number_of_sketches + ", space available: " + response.data.available);
numSavedSketches = response.data.number_of_sketches;
updatePrevNextButtonStateOnClear();
}
}
}).fail(function() {
console.log("getSavedSketchStatus failed");
});
}
function setSketchModified(_isModified, doNotClearCurrent) {
isModified = _isModified;
// alert("isModified: " + isModified);
//console.log("setModified: " + isModified + (typeof(doNotClearCurrent) !== 'undefined' ? " (doNotClearCurrent: "+doNotClearCurrent+")" : "")); //TEMP
if (isModified) enableButton(btnSave, saveSketch);
else disableButton(btnSave);
//if (typeof(doNotClearCurrent) !== 'undefined' && !doNotClearCurrent) setCurrentSketchId(-1);
//sketchModified = isModified; /// ERROR?
}
function setCurrentSketchId(sId) {
console.log("setCurrentSketchId: " + sId + " / " + numSavedSketches);
// var enablePrev = false;
// var enableNext = false;
currentSketchId = sId;
//clamp
if (currentSketchId > numSavedSketches) currentSketchId = numSavedSketches;
if (currentSketchId < 1) currentSketchId = 1;
//update textbox
//$("#txtSketch").val(currentSketchId);
updatePrevNextButtonState();
}
function updatePrevNextButtonStateOnClear() {
if (numSavedSketches > 0) enableButton(btnPrevious, prevDoodle);
disableButton(btnNext);
currentSketchId = numSavedSketches+1; //after the end of the list
disableButton(btnSave);
}
function updatePrevNextButtonState() {
//btnPrevious state
if (numSavedSketches==0 || currentSketchId < 2) {
disableButton(btnPrevious);
} else {
enableButton(btnPrevious, prevDoodle);
}
//btnNext state
if (numSavedSketches==0 || currentSketchId >= numSavedSketches) {
disableButton(btnNext);
} else {
enableButton(btnNext, nextDoodle);
}
}
function loadSketch(sketchId) {
$.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);
}
}
}
}).fail(function() {
console.log("loadSketch failed: ", response);
});
}
//btnSave.mouseup(saveSketch);
function saveSketch() {
svg = saveToSvg();
console.log("generated SVG [" + _points.length + " points, " + svg.length + " bytes]:\n" + svg);
$.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);
//}
}

39
js/utils.js Normal file
View File

@ -0,0 +1,39 @@
/*
* 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.
*/
// http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery
function getURLParameter(name) {
return decodeURI(
(new RegExp('[&?]'+name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1]
);
}
// returns true for all smartphones and tablets
function isMobileDevice() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Windows Mobile/i.test(navigator.userAgent);
}
// returns true for smartphones (Android will be a bit dodgy (tablet or phone, all depends on pixels vs devicePixelRatio...)
function isSmartphone() {
var returnBool = false;
if( /Android/i.test(navigator.userAgent) && window.devicePixelRatio > 1) {
var w = $(window).width() / window.devicePixelRatio;
console.log("Android device >> ratio'd width: " + w);
if (w < 480) {
returnBool = true;
}
} else {
returnBool = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini|Windows Mobile/i.test(navigator.userAgent)
}
return returnBool;
}
function distance(x1, y1, x2, y2) {
return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}

21
js/verticalShapes.js Normal file
View File

@ -0,0 +1,21 @@
var VERTICALSHAPE;
var verticalShapes = {
"NONE": 'none',
"DIVERGING": 'diverging',
"CONVERGING": 'converging',
"SINUS": 'sinus'
};
function setVerticalShape(s) {
VERTICALSHAPE = s;
redrawRenderedPreview();
}
function initVerticalShapes() {
resetVerticalShapes();
}
function resetVerticalShapes() {
setVerticalShape(verticalShapes.NONE);
}