Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1 @@
<iframe src='plugins/sketch/widget/sketch.html' style='width:100%;height:100%;'></iframe>
@@ -0,0 +1,68 @@
/*
Toying with the idea of compression, this file is not used, and is a work in progress.
*/

(function() {

var style = {
tool: "brush",
// compositing
globalAlpha: 1,
globalCompositeOperation: "source-over",
// colors and styles
strokeStyle: "#000000",
fillStyle: "#000000",
// shadows
shadowOffsetX: 0,
shadowOffsetY: 0,
shadowBlur: 0,
shadowColor: "rgba(0,0,0,0)",
// line caps/joins
lineWidth: 1,
lineCap: "butt",
lineJoin: "miter",
miterLimit: 10,
// text
font: "10px sans-serif",
textAlign: "start",
textBaseline: "alphabetic"
};

var styleOptions = {
tool: { "brush": 0, "eraser": 1, "text": 2 },
globalCompositeOperation: { "source-over": 0, "source-in": 1, "source-out": 2, "source-atop": 3, "destination-over": 4, "destination-in": 5, "destination-out": 6, "destination-atop": 7, "lighter": 8, "darker": 9, "copy": 10, "xor": 11 },
lineCap: { "butt": 0, "round": 1, "square": 2 },
lineJoin: { "round": 0, "bevel": 1, "miter": 2 },
textAlign: { "start": 0, "end": 1, "left": 2, "right": 3, "center": 4 },
textBaseline: { "top": 0, "hanging": 1, "middle": 2, "alphabetic": 3, "ideographic": 4, "bottom": 5 }
};

var styleShorthand = {};
(function() { // create mapping to shorthand code.
for (var i in styleOptions) {
var key = i.split(/(?=[A-Z])/);
for (var n = 0; n < key.length; n ++) key[n] = key[n][0].toLowerCase();
styleShorthand[key.join()] = i;
}
})();

sketch.encode = function() {
for (var p in sketch.path) {
//// Compress the path to SVG (+timeLapse).

//// Compress the style.
for (var i in sketch.style) {
// this makes "fillStyle" === "fs", and "textAlign" === "ta"
var key = styleShorthand[i];
/// this makes "start" === 0, and "hanging" === 1
var value = sketch.style[i];
if (styleOptions[i]) value = styleOptions[i][value];
}
}
};

sketch.decode = function() {

};

})();
@@ -0,0 +1,378 @@
/*
----------------------------------------------------
Sketch.js : 0.1 : 2012/09/01
----------------------------------------------------
https://github.com/mudcube/Sketch.js
*/

var Sketch = function(config) { "use strict";
var that = this;
// Utility for cloning objects.
var clone = function(obj) {
if (!obj || typeof(obj) !== 'object') return obj;
var temp = new obj.constructor();
for (var key in obj) {
if (!obj[key] || typeof(obj[key]) !== 'object') {
temp[key] = obj[key];
} else { // clone sub-object
temp[key] = clone(obj[key]);
}
}
return temp;
};
// Setting up <canvas> layers.
var layer = this.layer = {
0: document.createElement("canvas"), // Background bitmap layer.
1: document.createElement("canvas"), // Overlay drawing layer.
2: document.createElement("canvas") // Active drawing layer.
};
// Setting up <canvas> contexts.
var layer2d = this.layer2d = {
0: this.layer[0].getContext("2d"), // Background ctx.
1: this.layer[1].getContext("2d"), // Overlay drawing ctx.
2: this.layer[2].getContext("2d") // Active drawing ctx.
};
// Variables for fast access.
var innerWidth = window.innerWidth;
var innerHeight = window.innerHeight;
var layer0 = layer[0];
var layer1 = layer[1];
var layer2 = layer[2];
var ctx0 = layer2d[0];
var ctx1 = layer2d[1];
var ctx2 = layer2d[2];
// Style object.
this.zoom = 1;
this.style = {
tool: "brush",
globalAlpha: 0.5,
globalCompositeOperation: "source-over",
strokeStyle: "#FF0000",
lineWidth: 10,
lineCap: "round",
lineJoin: "round"
};
// Style caching object.
this.styleCache = undefined;
this.rendering = false;
this.path = [];
this.speed = 200; // fast-forward through the paths.
this.maxTimeLapse = 500; // maximum time to wait between draw calls (type of fast-forward).
///
this.init = function(config) {
if (!config) config = this;
this.zoom = config.zoom || 1.0;
this.element = config.element || document.body;
this.path = config.path || [];
for (var key in layer) {
this.element.appendChild(layer[key]);
}
///
Event.add(this.element, "mousedown", this.record);
};
//
this.destroy = function() {
this.path = [];
if (this.element.hasChildNodes()) {
while (this.element.childNodes.length >= 1) {
this.element.removeChild(this.element.firstChild);
}
}
///
Event.remove(this.element, "mousedown", this.record);
};
// Resize the <canvas> elements.
this.resize = function(width, height) {
innerWidth = width;
innerHeight = height;
// Adjust the size of the layers.
for (var key in layer) {
layer[key].width = innerWidth;
layer[key].height = innerHeight;
}
// Redraw the content.
that.redrawFast();
};
// Record the vector commands from mouse movements.
this.record = function(event) {
if (that.rendering) return;
var timer = new timeCapsule();
var dstEraser = that.style.globalCompositeOperation === "destination-out";
var dstDirect = false; // Draw on the layer (true), or the active layer (false).
var ctx = dstDirect ? ctx1 : ctx2;
var currentPath = [];
/// Capture mouse movements for drawing.
Event.proxy.drag({
event: event,
target: layer2,
listener: function (event, self) {
Event.cancel(event);
var coords = {};
coords.x = self.x * 1 / that.zoom;
coords.y = self.y * 1 / that.zoom;
if (self.state === "down") {
coords.beginPath = true;
for (var key in that.style) {
coords[key] = that.style[key];
ctx[key] = that.style[key];
}
}
// Record ms since last update.
coords.lapse = timer.getLapse();
// Push coords to current path.
currentPath.push(coords);
// Push coords to global path.
that.path.push(coords);
// Reset the composite operation.
ctx.globalCompositeOperation = "source-over";
//
if (!dstDirect) {
// Clear the path being actively drawn.
ctx.clearRect(0, 0, innerWidth, innerHeight);
// Setup for eraser mode.
if (dstEraser) {
layer1.style.display = "none";
ctx.save();
ctx.globalAlpha = 1;
ctx.drawImage(layer1, 0, 0);
ctx.restore();
ctx.globalCompositeOperation = "destination-out";
}
} else if (dstEraser) {
ctx.globalCompositeOperation = "destination-out";
}
// Draw the entire path.
ctx.save();
ctx.scale(that.zoom, that.zoom);
that.catmull({
path: currentPath,
ctx: ctx
});
ctx.restore();
// Record active to layer, and cleanup.
if (self.state === "up" && !dstDirect) {
if (dstEraser) {
layer1.style.display = "block";
ctx1.clearRect(0, 0, innerWidth, innerHeight);
}
ctx1.drawImage(layer2, 0, 0);
ctx.clearRect(0, 0, innerWidth, innerHeight);
}
}
});
};
// Redraw the vectors as quickly as possible.
this.redrawFast = function() {
// Clearing layers.
this.layerReset();
// Setting the properties.
var nid = -1;
var path = this.path;
var length = path.length;
var batches = [];
///
for (var n = 0; n < length; n ++) {
if (path[n].beginPath) nid ++;
if (!batches[nid]) batches[nid] = [];
batches[nid].push(path[n]);
}
// Drawing the batches.
ctx1.save();
ctx1.scale(that.zoom, that.zoom);
for (var n = 0; n < batches.length; n ++) {
this.setStyle(ctx1, batches[n][0]);
this.catmull({
path: batches[n],
ctx: ctx1
});
}
ctx1.restore();
//
this.layerRestore();
};
// Redraw the vectors animated as they were drawn.
this.redrawAnimate = function() {
// Clearing layers.
this.layerReset();
// Setting the properties.
var nid = 0;
var path = this.path;
var startId = 0;
var dstOut;
///
var animate = function() {
// Stoping rendering animation.
if (that.interval) clearInterval(that.interval);
// Grab the current path.
var coord = path[nid ++];
//
// Drawing is complete.
if (typeof(coord) === "undefined") {
ctx1.drawImage(layer2, 0, 0);
ctx2.clearRect(0, 0, innerWidth, innerHeight);
that.layerRestore();
return;
}
// Record to the background layer.
if (coord.beginPath) {
that.setStyle(ctx2, coord);
dstOut = ctx2.globalCompositeOperation === "destination-out";
startId = nid - 1;
}
// Loop through current section.
var currentPath = [];
for (var n = startId; n < nid; n ++) {
currentPath.push(path[n]);
}
//// Clear the path being actively drawn.
ctx2.globalCompositeOperation = "source-over";
ctx2.clearRect(0, 0, innerWidth, innerHeight);
// Setup for eraser mode.
if (dstOut) {
layer1.style.display = "none";
ctx2.save();
ctx2.globalAlpha = 1;
ctx2.drawImage(layer1, 0, 0);
ctx2.restore();
ctx2.globalCompositeOperation = "destination-out";
}
// Draw the entire path.
ctx2.save();
ctx2.scale(that.zoom, that.zoom);
that.catmull({
path: currentPath,
ctx: ctx2
});
ctx2.restore();
// Record active to layer, and cleanup.
if (!path[nid] || path[nid].beginPath) {
if (dstOut) {
layer1.style.display = "block";
ctx1.clearRect(0, 0, innerWidth, innerHeight);
}
ctx1.drawImage(layer2, 0, 0);
ctx2.clearRect(0, 0, innerWidth, innerHeight);
}
// Replay using timestamps.
var speed = coord.lapse * (1 / that.speed);
var timeout = Math.min(that.maxTimeLapse, speed);
that.interval = setInterval(animate, timeout);
};
// Start animation.
animate();
};
// Catmull-Rom spline.
this.catmull = function (config) {
var path = clone(config.path);
var tension = 1 - (config.tension || 0);
var ctx = config.ctx;
var length = path.length - 3;
path.splice(0, 0, path[0]);
path.push(path[path.length - 1]);
if (length <= 0) return;
for (var n = 0; n < length; n ++) {
var p1 = path[n];
var p2 = path[n + 1];
var p3 = path[n + 2];
var p4 = path[n + 3];
if (n == 0) {
ctx.beginPath();
ctx.moveTo(p2.x, p2.y);
}
ctx.bezierCurveTo(
p2.x + (tension * p3.x - tension * p1.x) / 6,
p2.y + (tension * p3.y - tension * p1.y) / 6,
p3.x + (tension * p2.x - tension * p4.x) / 6,
p3.y + (tension * p2.y - tension * p4.y) / 6,
p3.x, p3.y
);
}
ctx.stroke();
};
// Reset and Restore the settings on the <canvas>.
this.layerReset = function() {
if (this.interval) { // Style is cached.
clearInterval(this.interval);
} else if (this.rendering === false) { // Cache the style.
this.styleCache = clone(this.style);
}
layer1.style.display = "block";
ctx1.clearRect(0, 0, innerWidth, innerHeight);
ctx2.clearRect(0, 0, innerWidth, innerHeight);
this.rendering = true;
};
this.layerRestore = function() {
this.rendering = false;
if (typeof(this.styleCache) !== "undefined") {
this.style = clone(this.styleCache);
delete this.styleCache;
}
};
// Export to image/png.
this.toDataURL = function() {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.drawImage(layer0, 0, 0);
ctx.drawImage(layer1, 0, 0);
ctx.drawImage(layer2, 0, 0);
return canvas.toDataURL("image/png");
};
// Export to vector paths.
this.toString = function() {
return JSON.stringify(this.path);
};
// Clear the recording.
this.clearRecording = function() {
this.path = [];
this.layerReset();
this.layerRestore();
};
// Undo the last command.
this.undo = function() {
var length = this.path.length;
for (var n = length - 1; n >= 0; n --) {
var coord = this.path[n];
if (coord.beginPath) {
this.path.splice(n, length - n);
this.redrawFast();
break;
}
}
};
// Change the current tool.
this.setTool = function(tool) {
if ((this.style.tool = tool) === 'eraser') {
this.style.globalCompositeOperation = 'destination-out';
} else {
this.style.globalCompositeOperation = 'source-over';
}
};
// Change the current style of a layer.
this.setStyle = function(ctx, props) {
for (var key in props) {
if (typeof(ctx[key]) !== "undefined" && typeof(that.style[key]) !== "undefined") {
ctx[key] = props[key];
}
}
};
// Utility for keeping track of time.
var timeCapsule = function() {
var time = 0;
this.getLapse = function() {
if (time === 0) time = (new Date()).getTime();
var newTime = (new Date()).getTime();
var delay = newTime - time;
time = newTime;
return delay;
};
return this;
};
// Auto-boot.
if (typeof(config) === "object") {
this.init(config);
}
///
return this;
};

Large diffs are not rendered by default.