pathanimator/inbrowser/ts/BrowserHandler.ts

263 lines
7.7 KiB
TypeScript

/// <reference path="PAImage.ts" />
/// <reference path="PathAnimator.ts" />
/**
* Functionality to handle browser io
*/
class BrowserHandler {
/**
* Drop area for file uploads
*/
private dropArea: HTMLElement;
/**
* Main instace of application
*/
private instance: PathAnimator;
/**
* Indicator flag if the drop area is highlighted
*/
private isHighlighted: boolean;
/**
* Algrothm selection dropdown element
*/
private algorithmElem: HTMLSelectElement;
/**
* Constructor
*/
constructor(instance: PathAnimator) {
this.instance = instance;
this.isHighlighted = false;
this.dropArea = document.getElementById('drop-area');
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
this.dropArea.addEventListener(eventName, this.preventDefaults, false);
});
this.dropArea.addEventListener('dragover', this.dragOver.bind(this), false)
this.dropArea.addEventListener('drop', this.dragDrop.bind(this), false)
this.algorithmElem = <HTMLSelectElement>document.getElementById("alsel");
this.algorithmElem.addEventListener("change", this.algorithmChanged.bind(this));
this.algorithmChanged();
document.getElementById("fileElem").addEventListener("change", this.handleSourceFile.bind(this));
document.getElementById("run").addEventListener("click", this.run.bind(this));
document.getElementById("save").addEventListener("click", this.save.bind(this));
}
/**
* Prevent default action for given event
* @param e incoming event
*/
preventDefaults (e: Event) {
e.preventDefault()
e.stopPropagation()
}
/**
* Event when something is dragged over out drag/drop area
*/
dragOver() {
if (!this.isHighlighted) {
this.dropArea.classList.add("highlight");
this.isHighlighted = true;
}
}
/**
* Something was droppen on our drag/drop area
* @param e Incoming event
*/
dragDrop(e: DragEvent) {
let dt = e.dataTransfer
let files = dt.files
this.setSourceFile(files);
if (this.isHighlighted) {
this.dropArea.classList.remove("highlight");
this.isHighlighted = false;
}
}
/**
* A file was selected via upload file button
* @param e: Incoming event
*/
handleSourceFile(e: Event) {
var fs = <HTMLInputElement>e.target;
this.setSourceFile(fs.files);
}
/**
* Set the source file and trigger processing with given file list. Only the first file will be used
* @param fileList the file list generated by the browser on drop or file selection
*/
setSourceFile(fileList: FileList) {
// we only support one image
if (fileList.length > 0) {
var file = fileList.item(0);
this.processFile(file);
}
}
/**
* Process (load) given file, calls fileLoaded when read
* @param file file to process
*/
processFile(file: File) {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.addEventListener("loadend", this.fileLoaded.bind(this));
}
/**
* The file loading from processFile has been finished.
* fileParsed will be called after image was rendered
* @param e progress event (normally file loaded)
*/
fileLoaded(e:ProgressEvent) {
if (e.type === "loadend") { // sanity check
var reader = <FileReader>e.target;
let img = <HTMLImageElement>document.createElement('img');
img.src = <string>reader.result;
img.addEventListener("load", this.fileParsed.bind(this));
}
}
/**
* The image data from file is ready.
* The canvas is filled with the image and the pixel data is extracted
* and set for the main processing instance.
* @param e incoming event
*/
fileParsed(e: Event) {
var img = <HTMLImageElement>e.target;
const canvas = <HTMLCanvasElement>document.getElementById("inputcanvas");
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const rgba = ctx.getImageData(0, 0, img.width, img.height).data;
var image = new PAImage(img.width, img.height, 4);
image.setPixels(rgba);
this.instance.setInputImage(image);
document.getElementById("run").classList.remove("hidden");
}
/**
* The algrothm select dropdown was changed.
* Show and hide algrothm specific inputs
*/
algorithmChanged() {
var div = document.getElementById("algorithm").getElementsByTagName("div");
for (var i = 0; i < div.length; ++i) {
if (div[i].hasAttribute("id")) {
if (div[i].getAttribute("id") === this.algorithmElem.value) {
div[i].classList.remove("hidden");
} else {
div[i].classList.add("hidden");
}
}
}
}
/**
* The run button was pressed.
* The algorithm specific setting are collected and an async timeout call is triggered to run the PathAnimator run function
* @param e incoming Event
*/
run(e: Event) {
e.preventDefault();
e.stopPropagation();
// collect algorithm param
var config = {
algorithm: this.algorithmElem.value
}
var div = document.getElementById(this.algorithmElem.value);
var fields = div.getElementsByTagName("input");
for (var i = 0; i < fields.length; ++i) {
config[fields[i].getAttribute("name")] = fields[i].value;
}
document.getElementById("output").classList.add("running");
document.getElementById("run").classList.add("hidden");
document.getElementById("save").classList.add("hidden");
window.setTimeout(this.instance.run.bind(this.instance, config), 10);
}
/**
* Clear logging output
*/
clearOutput() {
document.getElementById("output").innerHTML = "";
}
/**
* Append message to logging output
* @param data string to display
*/
appendOutput(data: string) {
var p = document.createElement("p");
p.innerText = data;
document.getElementById("output").append(p);
}
/**
* Replace old logging output by new data
* @param data new string to display
*/
replaceOutput(data: string) {
this.clearOutput();
this.appendOutput(data);
}
/**
* Processing has completed
* Show buttons and hide running animation
*/
completed() {
document.getElementById("output").classList.remove("running");
document.getElementById("run").classList.remove("hidden");
document.getElementById("save").classList.remove("hidden");
}
/**
* Show output of the algorithm
* The Canvas is filled with the output of the algorithm.
* Preprocessing is needed, the algorithm exports RGB data, the canvas wants RGBA.
* @param image image to show
*/
showOutput(image: PAImage) {
var output = <HTMLCanvasElement>document.getElementById("outputcanvas");
output.classList.remove("hidden");
var width = image.getWidth();
var height = image.getHeight();
output.width = width;
output.height = height;
var end = width* height * 3;
const ctx = output.getContext('2d');
var outCanvas = ctx.getImageData(0,0, width, height);
const outCanvasPixel = outCanvas.data;
var imagePixel = image.getPixels8();
var offsetCanvas = 0;
var offsetImage = 0;
for (var i = 0; i < end; ++i) {
outCanvasPixel[offsetCanvas++] = imagePixel[offsetImage++];
outCanvasPixel[offsetCanvas++] = imagePixel[offsetImage++];
outCanvasPixel[offsetCanvas++] = imagePixel[offsetImage++];
outCanvasPixel[offsetCanvas++] = 255;
}
ctx.putImageData(outCanvas, 0, 0);
}
/**
* User has clicked the save button
* Trigger download
* @param e incoming Event
*/
save(e: Event) {
var output = <HTMLCanvasElement>document.getElementById("outputcanvas");
var dataURL = output.toDataURL("image/png");
var a = document.createElement('a');
a.href = dataURL;
a.download = "output.png";
document.body.appendChild(a);
a.click();
}
}