1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-09-11 23:09:42 +00:00
TiddlyWiki5/core/modules/new_widgets/edit-bitmap.js
Jeremy Ruston 9e3618bdcf Rename the 'title' attributes of various widgets to 'tiddler'
The change is to avoid confusion with the HTML 'title' attribute. The
name 'tiddler' better emphasises the purpose of the attribute, too.
2013-10-30 13:36:44 +00:00

314 lines
9.2 KiB
JavaScript

/*\
title: $:/core/modules/new_widgets/edit-bitmap.js
type: application/javascript
module-type: new_widget
Edit-bitmap widget
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
// Default image sizes
var DEFAULT_IMAGE_WIDTH = 300,
DEFAULT_IMAGE_HEIGHT = 185;
var Widget = require("$:/core/modules/new_widgets/widget.js").widget;
var EditBitmapWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
EditBitmapWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
EditBitmapWidget.prototype.render = function(parent,nextSibling) {
var self = this;
// Save the parent dom node
this.parentDomNode = parent;
// Compute our attributes
this.computeAttributes();
// Execute our logic
this.execute();
// Create our element
this.canvasDomNode = $tw.utils.domMaker("canvas",{
document: this.document,
"class":"tw-edit-bitmapeditor",
eventListeners: [{
name: "touchstart", handlerObject: this, handlerMethod: "handleTouchStartEvent"
},{
name: "touchmove", handlerObject: this, handlerMethod: "handleTouchMoveEvent"
},{
name: "touchend", handlerObject: this, handlerMethod: "handleTouchEndEvent"
},{
name: "mousedown", handlerObject: this, handlerMethod: "handleMouseDownEvent"
},{
name: "mousemove", handlerObject: this, handlerMethod: "handleMouseMoveEvent"
},{
name: "mouseup", handlerObject: this, handlerMethod: "handleMouseUpEvent"
}]
});
this.widthDomNode = $tw.utils.domMaker("input",{
document: this.document,
"class":"tw-edit-bitmapeditor-width",
eventListeners: [{
name: "change", handlerObject: this, handlerMethod: "handleWidthChangeEvent"
}]
});
this.heightDomNode = $tw.utils.domMaker("input",{
document: this.document,
"class":"tw-edit-bitmapeditor-height",
eventListeners: [{
name: "change", handlerObject: this, handlerMethod: "handleHeightChangeEvent"
}]
});
// Insert the elements into the DOM
parent.insertBefore(this.canvasDomNode,nextSibling);
parent.insertBefore(this.widthDomNode,nextSibling);
parent.insertBefore(this.heightDomNode,nextSibling);
this.domNodes.push(this.canvasDomNode,this.widthDomNode,this.heightDomNode);
// Load the image into the canvas
this.loadCanvas();
};
/*
Compute the internal state of the widget
*/
EditBitmapWidget.prototype.execute = function() {
// Get our parameters
this.editTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
this.editClass = this.getAttribute("class");
};
/*
Note that the bitmap editor intentionally doesn't try to refresh itself because it would be confusing to have the image changing spontaneously while editting it
*/
EditBitmapWidget.prototype.refresh = function(changedTiddlers) {
return false;
};
/*
Remove any DOM nodes created by this widget or its children
*/
EditBitmapWidget.prototype.removeChildDomNodes = function() {
$tw.utils.each(this.domNodes,function(domNode) {
domNode.parentNode.removeChild(domNode);
});
this.domNodes = [];
};
EditBitmapWidget.prototype.loadCanvas = function() {
var tiddler = this.wiki.getTiddler(this.editTitle),
currImage = new Image();
// Set up event handlers for loading the image
var self = this;
currImage.onload = function() {
// Copy the image to the on-screen canvas
self.initCanvas(self.canvasDomNode,currImage.width,currImage.height,currImage);
// And also copy the current bitmap to the off-screen canvas
self.currCanvas = self.document.createElement("canvas");
self.initCanvas(self.currCanvas,currImage.width,currImage.height,currImage);
// Set the width and height input boxes
self.updateSize();
};
currImage.onerror = function() {
// Set the on-screen canvas size and clear it
self.initCanvas(self.canvasDomNode,DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT);
// Set the off-screen canvas size and clear it
self.currCanvas = self.document.createElement("canvas");
self.initCanvas(self.currCanvas,DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT);
// Set the width and height input boxes
self.updateSize();
}
// Get the current bitmap into an image object
currImage.src = "data:" + tiddler.fields.type + ";base64," + tiddler.fields.text;
};
EditBitmapWidget.prototype.initCanvas = function(canvas,width,height,image) {
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
if(image) {
ctx.drawImage(image,0,0);
} else {
ctx.fillStyle = "#fff";
ctx.fillRect(0,0,canvas.width,canvas.height);
}
}
/*
** Update the input boxes with the actual size of the canvas
*/
EditBitmapWidget.prototype.updateSize = function() {
this.widthDomNode.value = this.currCanvas.width;
this.heightDomNode.value = this.currCanvas.height;
};
/*
** Change the size of the canvas, preserving the current image
*/
EditBitmapWidget.prototype.changeCanvasSize = function(newWidth,newHeight) {
// Create and size a new canvas
var newCanvas = this.document.createElement("canvas");
this.initCanvas(newCanvas,newWidth,newHeight);
// Copy the old image
var ctx = newCanvas.getContext("2d");
ctx.drawImage(this.currCanvas,0,0);
// Set the new canvas as the current one
this.currCanvas = newCanvas;
// Set the size of the onscreen canvas
this.canvasDomNode.width = newWidth;
this.canvasDomNode.height = newHeight;
// Paint the onscreen canvas with the offscreen canvas
ctx = this.canvasDomNode.getContext("2d");
ctx.drawImage(this.currCanvas,0,0);
};
EditBitmapWidget.prototype.handleWidthChangeEvent = function(event) {
// Get the new width
var newWidth = parseInt(this.widthDomNode.value,10);
// Update if necessary
if(newWidth > 0 && newWidth !== this.currCanvas.width) {
this.changeCanvasSize(newWidth,this.currCanvas.height);
}
// Update the input controls
this.updateSize();
};
EditBitmapWidget.prototype.handleHeightChangeEvent = function(event) {
// Get the new width
var newHeight = parseInt(this.heightDomNode.value,10);
// Update if necessary
if(newHeight > 0 && newHeight !== this.currCanvas.height) {
this.changeCanvasSize(this.currCanvas.width,newHeight);
}
// Update the input controls
this.updateSize();
};
EditBitmapWidget.prototype.handleTouchStartEvent = function(event) {
this.brushDown = true;
this.strokeStart(event.touches[0].clientX,event.touches[0].clientY);
event.preventDefault();
event.stopPropagation();
return false;
};
EditBitmapWidget.prototype.handleTouchMoveEvent = function(event) {
if(this.brushDown) {
this.strokeMove(event.touches[0].clientX,event.touches[0].clientY);
}
event.preventDefault();
event.stopPropagation();
return false;
};
EditBitmapWidget.prototype.handleTouchEndEvent = function(event) {
if(this.brushDown) {
this.brushDown = false;
this.strokeEnd();
}
event.preventDefault();
event.stopPropagation();
return false;
};
EditBitmapWidget.prototype.handleMouseDownEvent = function(event) {
this.strokeStart(event.clientX,event.clientY);
this.brushDown = true;
event.preventDefault();
event.stopPropagation();
return false;
};
EditBitmapWidget.prototype.handleMouseMoveEvent = function(event) {
if(this.brushDown) {
this.strokeMove(event.clientX,event.clientY);
event.preventDefault();
event.stopPropagation();
return false;
}
return true;
};
EditBitmapWidget.prototype.handleMouseUpEvent = function(event) {
if(this.brushDown) {
this.brushDown = false;
this.strokeEnd();
event.preventDefault();
event.stopPropagation();
return false;
}
return true;
};
EditBitmapWidget.prototype.adjustCoordinates = function(x,y) {
var canvasRect = this.canvasDomNode.getBoundingClientRect(),
scale = this.canvasDomNode.width/canvasRect.width;
return {x: (x - canvasRect.left) * scale, y: (y - canvasRect.top) * scale};
};
EditBitmapWidget.prototype.strokeStart = function(x,y) {
// Start off a new stroke
this.stroke = [this.adjustCoordinates(x,y)];
};
EditBitmapWidget.prototype.strokeMove = function(x,y) {
var ctx = this.canvasDomNode.getContext("2d"),
t;
// Add the new position to the end of the stroke
this.stroke.push(this.adjustCoordinates(x,y));
// Redraw the previous image
ctx.drawImage(this.currCanvas,0,0);
// Render the stroke
ctx.strokeStyle = "#ff0";
ctx.lineWidth = 3;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.beginPath();
ctx.moveTo(this.stroke[0].x,this.stroke[0].y);
for(t=1; t<this.stroke.length-1; t++) {
var s1 = this.stroke[t],
s2 = this.stroke[t-1],
tx = (s1.x + s2.x)/2,
ty = (s1.y + s2.y)/2;
ctx.quadraticCurveTo(s2.x,s2.y,tx,ty);
}
ctx.stroke();
};
EditBitmapWidget.prototype.strokeEnd = function() {
// Copy the bitmap to the off-screen canvas
var ctx = this.currCanvas.getContext("2d");
ctx.drawImage(this.canvasDomNode,0,0);
// Save the image into the tiddler
this.saveChanges();
};
EditBitmapWidget.prototype.saveChanges = function() {
var tiddler = this.wiki.getTiddler(this.editTitle);
if(tiddler) {
// data URIs look like "data:<type>;base64,<text>"
var dataURL = this.canvasDomNode.toDataURL(tiddler.fields.type,1.0),
posColon = dataURL.indexOf(":"),
posSemiColon = dataURL.indexOf(";"),
posComma = dataURL.indexOf(","),
type = dataURL.substring(posColon+1,posSemiColon),
text = dataURL.substring(posComma+1);
var update = {type: type, text: text};
this.wiki.addTiddler(new $tw.Tiddler(tiddler,update));
}
};
exports["edit-bitmap"] = EditBitmapWidget;
})();