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:
81
js/AddShapeDialog.js
Normal file
81
js/AddShapeDialog.js
Normal 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
139
js/Button.js
Normal 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
64
js/Class.js
Normal 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
342
js/Help.js
Normal 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
97
js/Keyboard.js
Normal 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
60
js/Message.js
Normal 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
34
js/Popup.js
Normal 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
357
js/Printer.js
Normal 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
84
js/PrinterPanel.js
Normal 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
117
js/Progressbar.js
Normal 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
778
js/SettingsWindow.js
Normal 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
183
js/Shape.js
Normal 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
140
js/Svg.js
Normal 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
146
js/Thermometer.js
Normal 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
262
js/UpdatePanel.js
Normal 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
132
js/WordArt.js
Normal 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
445
js/buttonbehaviors.js
Normal 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
490
js/canvasDrawing.js
Normal 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
18
js/d3dServerInterfacing.js
vendored
Normal 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
306
js/gcodeGenerating.js
Normal 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
69
js/init_layout.js
Normal 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
232
js/libs/FileSaver.js
Normal 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
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
72
js/libs/jquery-cookie.js
vendored
Normal 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
58
js/libs/jquery-coolfieldset.js
vendored
Normal 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
101
js/libs/jquery-fastclick.js
vendored
Normal 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
933
js/libs/jquery-joyride-2-1.js
vendored
Executable 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
184
js/main.js
Normal 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
357
js/previewRendering.js
Normal 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
70
js/sidebar.js
Normal 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
164
js/sketches.js
Normal 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
39
js/utils.js
Normal 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
21
js/verticalShapes.js
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user