quirematrix
Version:
An easy to use, dependency-less software to visualize in the browser the layout of quires, using a very intuitive notation system
358 lines (312 loc) • 9.15 kB
JavaScript
(function() {
/**
* [description]
* @param {string|object} canvasId Id of canvas element where to draw the matrix or jQuery object of the element
*/
var Matrix = function(canvasId){
var version = "1.2.0";
var canvas = (typeof canvasId === 'string' ? document.getElementById(canvasId) : canvasId[0]),
ctx = canvas.getContext("2d"),
height = canvas.height,
width = canvas.width,
step = 20,
rows = 1,
values_map = {
'0': '1',
'1': '0',
'0*': '1*',
'1*': '0*',
'0+': '1+',
'1+': '0+',
'1x': '1x',
'2': '0',
'2*': '0',
'3': '0',
'3*': '0',
'4': '0',
'4*': '0',
'0+*': '1+*',
'1+*': '0+*',
};
ctx.lineWidth = 1.5;
/**
* Draws a single line but does not render
*/
function single(){
ctx.moveTo(0, height-(rows*step));
ctx.lineTo((width/2), height-((rows-1)*step));
}
/**
* Draws a single line renders it
*/
function addOne(){
ctx.beginPath();
single();
ctx.stroke();
}
/**
* Draws input: 0
* left: nothing
* right: single
*/
function addZero(){
ctx.beginPath();
ctx.moveTo((width/2), height-((rows-1)*step));
ctx.lineTo(width, height-(step*rows));
ctx.stroke();
}
/**
* Draws input: 0+
* left: stub
* right: single
*/
function addZeroPlus(dash){
ctx.beginPath();
if ( dash ) ctx.setLineDash([5]);
ctx.moveTo(width, height-(rows*step));
ctx.lineTo((width/2), height-((rows-1)*step));
ctx.lineTo((width/2)-(width/10), height-(rows*step)+step*0.75);
ctx.stroke();
if ( dash ) ctx.setLineDash([]);
}
/**
* Draws input: 0*
* left: single dashed
* right: single
*/
function addZeroDash(){
ctx.beginPath();
ctx.setLineDash([5]);
ctx.moveTo(0, height-(rows*step));
ctx.lineTo((width/2), height-((rows-1)*step));
ctx.stroke();
ctx.setLineDash([]);
ctx.beginPath();
ctx.moveTo((width/2), height-((rows-1)*step));
ctx.lineTo(width, height-(step*rows));
ctx.stroke();
}
/**
* Draws input: 1+
* left: single
* right: stub
*/
function addOnePlus(dash){
ctx.beginPath();
if ( dash ) ctx.setLineDash([5]);
single();
ctx.lineTo((width/2)+(width/10), height-(rows*step)+step*0.75);
ctx.stroke();
if ( dash ) ctx.setLineDash([]);
}
/**
* Draws input: 1x
* left: single
* right: single
*/
function addOnePer(){
ctx.beginPath();
single();
ctx.lineTo(width, height-(step*rows));
ctx.stroke();
}
/**
* Draws input: 1x*
* left: single dashed
* right: single dashed
*/
function addOnePerDash(){
ctx.beginPath();
ctx.setLineDash([5]);
single();
ctx.lineTo(width, height-(step*rows));
ctx.stroke();
ctx.setLineDash([]);
}
/**
* Draws input: 1*
* left: single
* right: single dashed
*/
function addOneDash(){
ctx.beginPath();
ctx.moveTo(0, height-(rows*step));
ctx.lineTo((width/2), height-((rows-1)*step));
ctx.stroke();
ctx.beginPath();
ctx.setLineDash([5]);
ctx.moveTo((width/2), height-((rows-1)*step));
ctx.lineTo(width, height-(step*rows));
ctx.stroke();
ctx.setLineDash([]);
}
/**
* Draws input: 2 and 2*
* left: 2 singles (dashed)
* right: -
*/
function addTwo(dash){
ctx.beginPath();
if (dash){
ctx.setLineDash([5]);
}
single();
ctx.lineTo(0, height-(rows*step)-(step/2));
ctx.stroke();
ctx.setLineDash([]);
}
/**
* Draws input: 3 and 3*
* left: 3 singles (dashed)
* right: -
*/
function addThree(dash){
ctx.beginPath();
if (dash){
ctx.setLineDash([5]);
}
single();
ctx.lineTo(0, height-(rows*step)-(step/3));
ctx.stroke();
ctx.moveTo((width/2), height-((rows-1)*step));
ctx.lineTo(0, height-(rows*step)-step*2/3);
ctx.stroke();
ctx.setLineDash([]);
}
/**
* Draws input: 4 and 4*
* left: 4 singles (dashed)
* right: -
*/
function addFour(dash){
ctx.beginPath();
if (dash){
ctx.setLineDash([5]);
}
single();
ctx.lineTo(0, height-(rows*step)-(step/4));
ctx.stroke();
ctx.moveTo((width/2), height-((rows-1)*step));
ctx.lineTo(0, height-(rows*step)-step*2/4);
ctx.stroke();
ctx.moveTo((width/2), height-((rows-1)*step));
ctx.lineTo(0, height-(rows*step)-step*3/4);
ctx.stroke();
ctx.setLineDash([]);
}
/**
* Utility function to add listener to element
* From: https://stackoverflow.com/a/24173176/586449
* @param {object} elem element to observe
* @param {string} eventType event to listen to
* @param {function} handler callback to run
*/
function addEventHandler(elem, eventType, handler) {
if (elem.addEventListener){
elem.addEventListener (eventType, handler, false);
} else if (elem.attachEvent) {
elem.attachEvent ('on' + eventType, handler);
}
}
/**
* Draws a row on canvas
* @param {string} type Input value describing row
*/
this.addRow = function(type){
if (type === '1') addOne();
else if (type === '1+') addOnePlus();
else if (type === '1x') addOnePer();
else if (type === '1x*') addOnePerDash();
else if (type === '1*') addOneDash();
else if (type === '0') addZero();
else if (type === '0*') addZeroDash();
else if (type === '0+') addZeroPlus();
else if (type === '2') addTwo();
else if (type === '2*') addTwo(true);
else if (type === '3') addThree();
else if (type === '3*') addThree(true);
else if (type === '4') addFour();
else if (type === '4*') addFour(true);
else if (type === '0+*') addZeroPlus(true);
else if (type === '1+*') addOnePlus(true);
rows++;
};
/**
* Clears canvas
*/
this.clear = function(){
rows = 1;
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
};
/**
* Parses a string and outputs martix on canvas and second-half result in second half container
* @param {string} val String to be parsed
* @param {string|object} secondHalfId Id of element or jQuery object where to put the seconf half reconstruction
*/
this.parseString = function(val, secondHalfId){
var $this = this,
secondHalf = secondHalfId ? (typeof secondHalfId === 'string' ? document.getElementById(secondHalfId) : secondHalfId[0]) : false,
tot = 0,
a = [];
// clear canvas
$this.clear();
if (!val || val === '') return '';
var val_arr = val.replace(/\/$/, '').split('/');
if (val_arr.length > 3) {
height = 50 + (val_arr.length*step);
canvas.setAttribute('height', height);
}
// Draw on canvas
val_arr.forEach(function(e){
if (e){
if (e !== '1x*' && e !== '1+*') {
tot += parseInt(e.replace('*', '').replace('x', ''));
}
$this.addRow(e);
}
});
// Format second half text
var val_arr_rev = val_arr.reverse();
val_arr_rev.forEach(function(e, i, a){
if (typeof values_map[e] !== 'undefined'){
a[i] = values_map[e];
}
if (e && a[i] !== '1x*'){
var nr = a[i] ? parseInt(a[i].replace('*', '').replace('x', '')) : 0;
tot += (isNaN(nr) ? 0 : nr);
}
});
var res = {
text: val_arr_rev.join('/'),
tot: tot*2
};
if (secondHalf) {
secondHalf.innerHTML = '';
secondHalf.innerHTML = '<code><strong>' + res.text + '</strong></code><br>Total: <strong>' + res.tot + '</strong> pages';
} else {
console.log(res);
}
return res;
};
/**
* Observes input, parses its value on each input and writes results
* to canvas and second half element, if available, otherwise logs it.
* @param {string} inputId Input element to observe
* @param {string} secondHalfId Id of element where to put the seconf half reconstruction
*/
this.observeInput = function(inputId, secondHalfId){
var input = ( typeof inputId === 'string' ? document.getElementById(inputId) : inputId[0]),
$this = this;
$this.parseString(input.value, secondHalfId);
addEventHandler(input, 'input', function(){
$this.parseString(input.value, secondHalfId);
});
};
};
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = Matrix;
} else {
window.Matrix = Matrix;
}
})();