2013-11-10 10:41:35 +00:00
|
|
|
/**
|
|
|
|
* WHITEBOPHIR
|
|
|
|
*********************************************************
|
|
|
|
* @licstart The following is the entire license notice for the
|
|
|
|
* JavaScript code in this page.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013 Ophir LOJKINE
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* The JavaScript code in this page is free software: you can
|
|
|
|
* redistribute it and/or modify it under the terms of the GNU
|
|
|
|
* General Public License (GNU GPL) as published by the Free Software
|
|
|
|
* Foundation, either version 3 of the License, or (at your option)
|
|
|
|
* any later version. The code is distributed WITHOUT ANY WARRANTY;
|
|
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
|
|
|
|
*
|
|
|
|
* As additional permission under GNU GPL version 3 section 7, you
|
|
|
|
* may distribute non-source (e.g., minimized or compacted) forms of
|
|
|
|
* that code without the copy of the GNU GPL normally required by
|
|
|
|
* section 4, provided you include this license notice and a URL
|
|
|
|
* through which recipients can access the Corresponding Source.
|
|
|
|
*
|
|
|
|
* @licend
|
|
|
|
*/
|
|
|
|
|
2013-09-27 10:39:47 +00:00
|
|
|
var Tools = {};
|
2013-10-05 00:07:48 +00:00
|
|
|
|
2013-10-06 22:43:00 +00:00
|
|
|
Tools.board = document.getElementById("board");
|
2013-09-27 10:39:47 +00:00
|
|
|
Tools.svg = document.getElementById("canvas");
|
2013-12-09 22:08:59 +00:00
|
|
|
Tools.socket = io.connect('', {
|
|
|
|
"reconnection delay" : 1, //Make the xhr connections as fast as possible
|
|
|
|
});
|
2013-10-05 00:07:48 +00:00
|
|
|
Tools.curTool = null;
|
2014-02-22 19:49:14 +00:00
|
|
|
Tools.boardName = (function(){
|
|
|
|
var path = window.location.pathname.split("/");
|
|
|
|
return path[path.length-1];
|
|
|
|
})();
|
2013-10-05 00:07:48 +00:00
|
|
|
|
2013-12-07 18:05:43 +00:00
|
|
|
//Get the board as soon as the page is loaded
|
2014-02-22 19:49:14 +00:00
|
|
|
Tools.socket.emit("getboard", Tools.boardName);
|
2013-12-07 18:05:43 +00:00
|
|
|
|
2013-10-03 14:24:45 +00:00
|
|
|
Tools.HTML = {
|
|
|
|
template : new Minitpl("#tools > .tool"),
|
2013-12-14 12:45:55 +00:00
|
|
|
addTool : function(toolName, toolIcon) {
|
2013-10-03 14:24:45 +00:00
|
|
|
var callback = function () {
|
|
|
|
Tools.change(toolName);
|
|
|
|
};
|
2013-10-13 10:52:01 +00:00
|
|
|
return this.template.add(function (elem) {
|
2013-10-03 14:24:45 +00:00
|
|
|
elem.addEventListener("click", callback);
|
|
|
|
elem.id = "toolID-"+toolName;
|
2013-12-14 12:45:55 +00:00
|
|
|
elem.getElementsByClassName("tool-name")[0].textContent = toolName;
|
|
|
|
elem.getElementsByClassName("tool-icon")[0].className += " " + toolIcon;
|
2013-10-03 14:24:45 +00:00
|
|
|
}
|
2013-10-13 10:52:01 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
changeTool : function(oldToolName, newToolName) {
|
|
|
|
var oldTool = document.getElementById("toolID-"+oldToolName);
|
|
|
|
var newTool = document.getElementById("toolID-"+newToolName);
|
|
|
|
if (oldTool) oldTool.classList.remove("curTool");
|
|
|
|
if (newTool) newTool.classList.add("curTool");
|
2013-10-06 22:43:00 +00:00
|
|
|
},
|
|
|
|
addStylesheet : function(href) {
|
|
|
|
//Adds a css stylesheet to the html or svg document
|
|
|
|
var link = document.createElement("link");
|
|
|
|
link.href = href;
|
|
|
|
link.rel = "stylesheet";
|
|
|
|
link.type = "text/css";
|
|
|
|
document.head.appendChild(link);
|
2013-10-03 14:24:45 +00:00
|
|
|
}
|
2013-10-06 22:43:00 +00:00
|
|
|
};
|
|
|
|
|
2013-09-27 10:39:47 +00:00
|
|
|
Tools.list = {}; // An array of all known tools. {"toolName" : {toolObject}}
|
|
|
|
|
|
|
|
Tools.add = function (newTool) {
|
|
|
|
if (newTool.name in Tools.list) {
|
|
|
|
console.log("Tools.add: The tool '"+newTool.name+"' is already" +
|
|
|
|
"in the list. Updating it...");
|
|
|
|
}
|
2013-10-05 00:12:51 +00:00
|
|
|
|
|
|
|
//Format the new tool correctly
|
|
|
|
Tools.applyHooks(Tools.toolHooks, newTool);
|
|
|
|
|
2013-09-27 10:39:47 +00:00
|
|
|
//Add the tool to the list
|
|
|
|
Tools.list[newTool.name] = newTool;
|
2013-10-03 14:24:45 +00:00
|
|
|
|
2013-10-06 22:43:00 +00:00
|
|
|
if (newTool.stylesheet) {
|
|
|
|
Tools.HTML.addStylesheet(newTool.stylesheet);
|
|
|
|
}
|
|
|
|
|
2013-10-03 14:24:45 +00:00
|
|
|
//Add the tool to the GUI
|
2013-12-14 12:45:55 +00:00
|
|
|
Tools.HTML.addTool(newTool.name, newTool.icon);
|
2013-12-07 18:05:43 +00:00
|
|
|
|
|
|
|
//There may be pending messages for the tool
|
|
|
|
var pending = Tools.pendingMessages[newTool.name];
|
|
|
|
if (pending) {
|
|
|
|
console.log("Drawing pending messages for '%s'.", newTool.name);
|
|
|
|
var msg;
|
|
|
|
while (msg = pending.shift()) {
|
|
|
|
//Transmit the message to the tool (precising that it comes from the network)
|
|
|
|
newTool.draw(msg, false);
|
|
|
|
}
|
|
|
|
}
|
2013-12-03 17:07:02 +00:00
|
|
|
};
|
2013-09-27 10:39:47 +00:00
|
|
|
|
|
|
|
Tools.change = function (toolName){
|
2013-10-03 14:24:45 +00:00
|
|
|
if (! (toolName in Tools.list)) {
|
|
|
|
throw "Trying to select a tool that has never been added!";
|
|
|
|
}
|
2013-10-13 10:52:01 +00:00
|
|
|
|
2013-11-10 16:21:05 +00:00
|
|
|
var newtool = Tools.list[toolName];
|
|
|
|
|
2013-10-13 10:52:01 +00:00
|
|
|
//Update the GUI
|
|
|
|
var curToolName = (Tools.curTool) ? Tools.curTool.name : "";
|
2013-12-08 20:46:42 +00:00
|
|
|
try {
|
|
|
|
Tools.HTML.changeTool(curToolName, toolName);
|
|
|
|
} catch (e) {
|
|
|
|
console.error("Unable to update the GUI with the new tool. "+e);
|
|
|
|
}
|
2013-11-10 16:21:05 +00:00
|
|
|
Tools.svg.style.cursor = newtool.mouseCursor || "auto";
|
2013-10-04 19:33:57 +00:00
|
|
|
|
2013-09-27 10:39:47 +00:00
|
|
|
//There is not necessarily already a curTool
|
2013-10-05 00:07:48 +00:00
|
|
|
if (Tools.curTool !== null) {
|
2013-10-03 14:24:45 +00:00
|
|
|
//It's useless to do anything if the new tool is already selected
|
2013-10-06 10:16:45 +00:00
|
|
|
if (newtool === Tools.curTool) return;
|
2013-10-03 14:24:45 +00:00
|
|
|
|
2013-09-27 10:39:47 +00:00
|
|
|
//Remove the old event listeners
|
2013-10-04 19:33:57 +00:00
|
|
|
for (var event in Tools.curTool.compiledListeners) {
|
|
|
|
var listener = Tools.curTool.compiledListeners[event];
|
2013-10-06 22:43:00 +00:00
|
|
|
Tools.board.removeEventListener(event, listener);
|
2013-08-27 17:43:49 +00:00
|
|
|
}
|
2013-10-22 18:22:47 +00:00
|
|
|
|
2013-10-05 00:02:59 +00:00
|
|
|
//Call the callbacks of the old tool
|
|
|
|
Tools.curTool.onquit(newtool);
|
2013-09-27 10:39:47 +00:00
|
|
|
}
|
2013-10-03 14:24:45 +00:00
|
|
|
|
2013-09-27 10:39:47 +00:00
|
|
|
//Add the new event listeners
|
2013-10-04 19:33:57 +00:00
|
|
|
for (var event in newtool.compiledListeners) {
|
|
|
|
var listener = newtool.compiledListeners[event];
|
2013-10-06 22:43:00 +00:00
|
|
|
Tools.board.addEventListener(event, listener);
|
2013-09-27 10:39:47 +00:00
|
|
|
}
|
2013-10-04 19:33:57 +00:00
|
|
|
|
2013-10-05 00:02:59 +00:00
|
|
|
//Call the start callback of the new tool
|
2013-10-05 00:07:48 +00:00
|
|
|
newtool.onstart(Tools.curTool);
|
2013-10-03 14:24:45 +00:00
|
|
|
Tools.curTool = newtool;
|
2013-10-06 22:43:00 +00:00
|
|
|
};
|
2013-09-27 10:39:47 +00:00
|
|
|
|
2013-10-06 22:43:00 +00:00
|
|
|
Tools.send = function(data, toolName){
|
2013-12-03 17:07:02 +00:00
|
|
|
toolName = toolName || Tools.curTool.name;
|
2014-02-22 19:49:14 +00:00
|
|
|
var d = data;
|
|
|
|
d.tool = toolName;
|
|
|
|
Tools.applyHooks(Tools.messageHooks, d);
|
|
|
|
var message = {
|
|
|
|
"board" : Tools.boardName,
|
|
|
|
"data" : d
|
|
|
|
}
|
2013-10-04 19:33:57 +00:00
|
|
|
Tools.socket.emit('broadcast', message);
|
2013-10-06 22:43:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Tools.drawAndSend = function (data) {
|
|
|
|
Tools.curTool.draw(data, true);
|
|
|
|
Tools.send(data);
|
|
|
|
};
|
2013-09-27 10:39:47 +00:00
|
|
|
|
2013-12-07 18:05:43 +00:00
|
|
|
//Object containing the messages that have been received before the corresponding tool
|
|
|
|
//is loaded. keys : the name of the tool, values : array of messages for this tool
|
|
|
|
Tools.pendingMessages = {};
|
|
|
|
|
|
|
|
//Receive draw instructions from the server
|
2013-09-27 10:39:47 +00:00
|
|
|
Tools.socket.on("broadcast", function (message){
|
2013-10-04 19:33:57 +00:00
|
|
|
//Check if the message is in the expected format
|
2013-12-07 03:46:31 +00:00
|
|
|
if (message.tool) {
|
2013-10-04 19:33:57 +00:00
|
|
|
var tool = Tools.list[message.tool];
|
2013-12-07 18:05:43 +00:00
|
|
|
if (tool) {
|
2013-12-09 22:08:59 +00:00
|
|
|
Tools.applyHooks(Tools.messageHooks, message);
|
2013-12-07 18:05:43 +00:00
|
|
|
tool.draw(message, false); //draw the received data
|
|
|
|
if (message._children) {
|
|
|
|
for (var i=0; i<message._children.length; i++) {
|
2013-12-09 22:08:59 +00:00
|
|
|
//Apply hooks on children too
|
|
|
|
Tools.applyHooks(Tools.messageHooks, message._children[i]);
|
2013-12-07 18:05:43 +00:00
|
|
|
tool.draw(message._children[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//We received a message destinated to a tool that we don't have
|
|
|
|
//So we add it to the pending messages
|
|
|
|
if (Tools.pendingMessages[message.tool] === undefined) {
|
|
|
|
Tools.pendingMessages[message.tool] = [];
|
2013-12-07 03:46:31 +00:00
|
|
|
}
|
2013-12-07 18:05:43 +00:00
|
|
|
Tools.pendingMessages[message.tool].push(message);
|
2013-12-07 03:46:31 +00:00
|
|
|
}
|
2013-10-04 19:33:57 +00:00
|
|
|
} else {
|
2013-12-07 18:05:43 +00:00
|
|
|
console.error("Received a badly formatted message (no tool). ", message);
|
2013-09-27 10:39:47 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-10-04 19:33:57 +00:00
|
|
|
//List of hook functions that will be applied to messages before sending or drawing them
|
|
|
|
Tools.messageHooks = [
|
|
|
|
function resizeCanvas (m) {
|
2013-12-07 03:46:31 +00:00
|
|
|
//Enlarge the canvas is something is drawn near its border
|
|
|
|
if (m.x && m.y) {
|
|
|
|
var svg = Tools.svg, x=m.x, y=m.y;
|
2013-11-11 13:11:38 +00:00
|
|
|
if (x > svg.width.baseVal.value - 1000) {
|
|
|
|
svg.width.baseVal.value = x + 2000;
|
2013-10-04 19:33:57 +00:00
|
|
|
}
|
2013-11-11 12:21:08 +00:00
|
|
|
if (y > svg.height.baseVal.value - 500) {
|
|
|
|
svg.height.baseVal.value = y + 2000;
|
2013-08-27 17:43:49 +00:00
|
|
|
}
|
2013-10-04 19:33:57 +00:00
|
|
|
}
|
2013-08-27 17:43:49 +00:00
|
|
|
}
|
2013-10-04 19:33:57 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
//List of hook functions that will be applied to tools before adding them
|
|
|
|
Tools.toolHooks = [
|
|
|
|
function checkToolAttributes(tool) {
|
|
|
|
if (typeof(tool.name)!=="string") throw "A tool must have a name";
|
|
|
|
if (typeof(tool.listeners)!=="object") {
|
|
|
|
tool.listeners = {};
|
|
|
|
}
|
|
|
|
if (typeof(tool.onstart)!=="function") {
|
2013-12-03 17:07:02 +00:00
|
|
|
tool.onstart = function(){};
|
2013-10-04 19:33:57 +00:00
|
|
|
}
|
|
|
|
if (typeof(tool.onquit)!=="function") {
|
2013-12-03 17:07:02 +00:00
|
|
|
tool.onquit = function(){};
|
2013-10-04 19:33:57 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
function compileListeners (tool) {
|
|
|
|
//compile listeners into compiledListeners
|
|
|
|
var listeners = tool.listeners;
|
2013-12-03 17:07:02 +00:00
|
|
|
|
|
|
|
//A tool may provide precompiled listeners
|
2013-10-04 19:33:57 +00:00
|
|
|
var compiled = tool.compiledListeners || {};
|
|
|
|
tool.compiledListeners = compiled;
|
|
|
|
|
|
|
|
function compile (listener) { //closure
|
2013-10-05 10:34:36 +00:00
|
|
|
return (function listen (evt){
|
2013-11-12 22:45:54 +00:00
|
|
|
var x = evt.pageX,
|
|
|
|
y = evt.pageY;
|
|
|
|
return listener(x,y,evt,false);
|
|
|
|
});
|
2013-10-04 19:33:57 +00:00
|
|
|
}
|
2013-08-27 17:43:49 +00:00
|
|
|
|
2013-11-12 22:45:54 +00:00
|
|
|
function compileTouch (listener) { //closure
|
|
|
|
return (function touchListen (evt) {
|
|
|
|
//Currently, we don't handle multitouch
|
|
|
|
if (evt.changedTouches.length === 1) {
|
|
|
|
//evt.preventDefault();
|
|
|
|
var touch = evt.changedTouches[0];
|
|
|
|
var x = touch.pageX,
|
|
|
|
y = touch.pageY;
|
|
|
|
return listener(x,y,evt,true);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (listeners.press) {
|
|
|
|
compiled["mousedown"] = compile(listeners.press);
|
|
|
|
compiled["touchstart"] = compileTouch(listeners.press);
|
|
|
|
}
|
|
|
|
if (listeners.move) {
|
|
|
|
compiled["mousemove"] = compile(listeners.move);
|
|
|
|
compiled["touchmove"] = compileTouch(listeners.move);
|
|
|
|
}
|
2013-10-04 19:33:57 +00:00
|
|
|
if (listeners.release) {
|
2013-11-12 22:45:54 +00:00
|
|
|
var release = compile(listeners.release),
|
|
|
|
releaseTouch = compileTouch(listeners.release);
|
|
|
|
compiled["mouseup"] = release;
|
|
|
|
compiled["mouseleave"] = release;
|
|
|
|
compiled["touchleave"] = releaseTouch;
|
|
|
|
compiled["touchend"] = releaseTouch;
|
|
|
|
compiled["touchcancel"] = releaseTouch;
|
2013-10-04 19:33:57 +00:00
|
|
|
}
|
2013-08-27 17:43:49 +00:00
|
|
|
}
|
2013-10-04 19:33:57 +00:00
|
|
|
];
|
2013-08-27 17:43:49 +00:00
|
|
|
|
2013-10-04 19:33:57 +00:00
|
|
|
Tools.applyHooks = function(hooks, object) {
|
|
|
|
//Apply every hooks on the object
|
|
|
|
hooks.forEach(function(hook) {
|
|
|
|
hook(object);
|
|
|
|
});
|
2013-10-06 10:16:45 +00:00
|
|
|
};
|
|
|
|
|
2013-08-27 17:43:49 +00:00
|
|
|
|
2013-10-06 10:16:45 +00:00
|
|
|
// Utility functions
|
2013-08-27 17:43:49 +00:00
|
|
|
|
2013-10-04 19:33:57 +00:00
|
|
|
Tools.generateUID = function (prefix, suffix) {
|
2013-11-23 23:49:05 +00:00
|
|
|
var uid = Date.now().toString(36); //Create the uids in chronological order
|
|
|
|
uid += (Math.round(Math.random()*36)).toString(36); //Add a random character at the end
|
|
|
|
if (prefix) uid = prefix + uid;
|
|
|
|
if (suffix) uid = uid + suffix;
|
|
|
|
return uid;
|
2013-10-06 10:16:45 +00:00
|
|
|
};
|
|
|
|
|
2013-10-06 22:43:00 +00:00
|
|
|
Tools.createSVGElement = function (name) {
|
2013-10-06 10:16:45 +00:00
|
|
|
return document.createElementNS(Tools.svg.namespaceURI, name);
|
|
|
|
};
|
|
|
|
|
2013-10-06 22:43:00 +00:00
|
|
|
Tools.positionElement = function (elem, x, y) {
|
|
|
|
elem.style.top = y+"px";
|
|
|
|
elem.style.left = x+"px";
|
|
|
|
};
|
|
|
|
|
2013-10-06 10:16:45 +00:00
|
|
|
(function color (){
|
|
|
|
var chooser = document.getElementById("chooseColor");
|
|
|
|
Tools.getColor = function(){
|
2013-11-20 21:51:32 +00:00
|
|
|
return chooser.value;
|
2013-10-06 10:16:45 +00:00
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function size (){
|
|
|
|
var chooser = document.getElementById("chooseSize");
|
|
|
|
|
|
|
|
function update (){
|
|
|
|
if (chooser.value<1 || chooser.value > 50) {
|
|
|
|
chooser.value=3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
update();
|
|
|
|
|
|
|
|
chooser.onchange = update;
|
|
|
|
Tools.getSize = function(){
|
|
|
|
return chooser.value;
|
|
|
|
};
|
|
|
|
})();
|
2013-10-04 19:33:57 +00:00
|
|
|
|
2013-10-06 10:16:45 +00:00
|
|
|
//Scale the canvas on load
|
2013-10-04 19:33:57 +00:00
|
|
|
Tools.svg.width.baseVal.value = document.body.clientWidth;
|
|
|
|
Tools.svg.height.baseVal.value = document.body.clientHeight;
|
|
|
|
|
2013-10-06 22:43:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
(function menu () {
|
|
|
|
var menu = document.getElementById("menu");
|
|
|
|
tog = document.getElementById("toggleMenu");
|
|
|
|
|
|
|
|
tog.onclick = function(e){
|
|
|
|
menu.classList.toggle("closed");
|
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
2013-11-21 22:27:34 +00:00
|
|
|
/*********** Polyfills ***********/
|
2013-12-03 17:10:17 +00:00
|
|
|
if (!window.performance || !window.performance.now) {
|
2013-10-28 21:52:21 +00:00
|
|
|
window.performance = {
|
|
|
|
"now" : Date.now
|
|
|
|
}
|
|
|
|
}
|
2013-11-21 22:27:34 +00:00
|
|
|
if (!Math.hypot) {
|
|
|
|
Math.hypot = function (x,y) {
|
|
|
|
//The true Math.hypot accepts any number of parameters
|
|
|
|
return Math.sqrt(x*x+y*y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-28 21:52:21 +00:00
|
|
|
|
2013-10-04 19:33:57 +00:00
|
|
|
/**
|
|
|
|
What does a "tool" object look like?
|
|
|
|
newtool = {
|
|
|
|
"name" : "SuperTool",
|
|
|
|
"listeners" : {
|
|
|
|
"press" : function(x,y,evt){...},
|
|
|
|
"move" : function(x,y,evt){...},
|
|
|
|
"release" : function(x,y,evt){...},
|
|
|
|
},
|
2013-10-06 22:43:00 +00:00
|
|
|
"draw" : function(data, isLocal){
|
2013-10-04 19:33:57 +00:00
|
|
|
//Print the data on Tools.svg
|
|
|
|
},
|
|
|
|
"onstart" : function(oldTool){...},
|
|
|
|
"onquit" : function(newTool){...},
|
2013-10-06 22:43:00 +00:00
|
|
|
"stylesheet" : "style.css",
|
2013-10-04 19:33:57 +00:00
|
|
|
}
|
|
|
|
*/
|