“Contenteditable” SVG


The only natural multiline content editable element is the text area. Recently, css divisions can be made editable by setting a contenteditable property to true.
It would be nice if SVG containers were editable so text could be typed directly into a graphic element such as an ellipse.
Although this is not directly possible, a workaround can be done through the use of a foreign object.
Here is an example of some text that was typed into an ellipse:

Here is the code:

<!DOCTYPE html PUBLIC ‘-//W3C//DTD XHTML 1.0 Transitional//EN’ ‘http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd’&gt;
<html xmlns=’http://www.w3.org/1999/xhtml’&gt;
<head profile=’http://gmpg.org/xfn/11′&gt;
<title>Contenteditable SVG</title>
<style type=’text/css’>
body {margin-left:0;margin-right:0;font:normal normal 600 18px Arial;}
a{ text-decoration: }
:link { color: rgb(0, 0, 255) }
:visited {color :rgb(100, 0,100) }
:hover { }
:active { }
.svg1{position: absolute; width: 50%; height: 60%; left: 25%; top: 1%; }
.d1{position: absolute; width: 98%; height: 98%; left:1%; top:1%; font-size: 18px; }
</style>
</head>
<body>
<svg class=”svg1″ xmlns=”http://www.w3.org/2000/svg&#8221; version=”1.1″>
<ellipse cx=”50%” cy=”50%” rx=”42%” ry=”42%” style=”fill:#eefff5 ; stroke:black ; stroke-width:2″ />
<foreignObject x=”21%” y=”21%” width=”58%” height=”58%”>
<di id=”d1″ style=”position: absolute; width: 100%; height: 100%” contenteditable = “true”>
</di>
</foreignObject>
</svg>

document.getElementById(“d1”).focus();

</body>
</html>


A SVG container was created and an ellipse was added:
<ellipse cx=”50%” cy=”50%” rx=”42%” ry=”42%” style=”fill:#eefff5 ; stroke:black ; stroke-width:2″ />

A foreign object was placed on top of the ellipse and filled with a contenteditable layer:
<foreignObject x=”21%” y=”21%” width=”58%” height=”58%”>
<di id=”d1″ style=”position: absolute; width: 100%; height: 100%” contenteditable = “true”>
</di>
</foreignObject>

On loading the layer was given the focus:
document.getElementById(“d1”).focus();

This post just illustrated the basic concept. Future posts will eleborate.

A Mathematical Puzzle

This puzzle is a simple arithmetic problem in which two four digit numbers are added to give a five digit number. The challenge is that letters are substituted for the numbers.

Here is how a typical puzzle appears:

To be solvable one of the letter conversions is given.

Clicking new gives a new puzzle.

Here is the code:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head profile=”http://gmpg.org/xfn/11″&gt;
<title>Cypher Math</title>
<style>
body {margin-left:0;margin-right:0;font:normal normal normal 15px Arial;}
a{ text-decoration: }:link { color: rgb(s0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { }

</style>
</head>
<body>
<center><input type=”button” id=”b1″ name=”b1″ value=”new” onclick=’location.reload();’ /><br /><br /><input type=”text” id=”t1″ style=”position: relative; text-align: center; font: normal normal 600 18px Arial; border-width: 0″ name=”f1″ value=”” /><br /><br />
<textarea id=”ta1″ style=”position: relative; text-align: right; font: normal normal 800 24px Arial; border-width: 0″ name=”ta1″ rows=”12″ cols=”12″></textarea>
</center>

dig1 = new Array(); dig2 = new Array(); dig3 = new Array(); var ciph1 = [“*”,”*”,”*”,”*”]; var ciph2 = [“*”,”*”,”*”,”*”]; var ciph3 = [“*”,”*”,”*”,”*”,”*”]; var let; var let2; var let3; var let4; var let5; var let6; var let7; var let8; var let9; var let10;
var chose = Math.floor(4*Math.random() + 1);
let = Math.floor(26*Math.random() + 1) + 64;
ciph1[3] = String.fromCharCode(let); ciph1[0] = String.fromCharCode(let);

for (var i = 1; i

</body></html>


I started by creating a few addition problems that could be solved and went about developing an algorithm to make the conversion of the numbers to letters.

All the numbers must be between 1 and 9.

Each row of the addition consists of an array, eg; ciph1.

for (var i = 1; i < 20; i ++) {
let3 = Math.floor(26*Math.random() + 1) + 64;
if (let3 == let || let3 == let2) let3 = Math.floor(26*Math.random() + 1) + 64;
if (let3 != let && let3 != let2) {
ciph3[0] = String.fromCharCode(let3);
}
}

A letter key code is randomly chosen:
let3 = Math.floor(26*Math.random() + 1) + 64;

and if it is an unintended duplicate another one is chosen:
if (let3 == let || let3 == let2) let3 = Math.floor(26*Math.random() + 1) + 64;

This is done until the letter is unique.

The problem is displayed in a textarea:
document.getElementById(“ta1”).value = ciph1.toString().replace(new RegExp(‘,’, ‘g’),’ ‘) + “\n+ \n” + ciph2.toString().replace(new RegExp(‘,’, ‘g’),’ ‘) + “\n_____________\n\n” + ciph3.toString().replace(new RegExp(‘,’, ‘g’),’ ‘);

The given conversion is displayed in a text input:
if (chose == 2) document.getElementById(“t1″).value = ” ” + ciph2[1] + ” = 6″;

Clicking new reloads the page creating a new puzzle:
input type=”button” id=”b1″ name=”b1″ value=”new” onclick=’location.reload();’

HTML Painting – Mobile


This app adds mobile capability.

Here is the code:

<!DOCTYPE html PUBLIC ‘-//W3C//DTD XHTML 1.0 Transitional//EN’ ‘http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd’&gt;
<html xmlns=’http://www.w3.org/1999/xhtml’&gt;
<head profile=’http://gmpg.org/xfn/11′&gt;
<title>Draw</title>
<style type=’text/css’>
body {margin-left:0;margin-right:0;font:normal normal normal 12px Arial;}
a{ text-decoration: }
:link { color: rgb(0, 0, 255) }
:visited {color :rgb(100, 0,100) }
:hover { }
:active { }
#frame{position: absolute; left;0; top: 0;width: 100%; height: 100%; background: white}
#canvas, #canvas2, .d1{position: absolute; width: 99%; height: 89%; left: .5%; top: 50px; border: 1px solid}
#ta1{visibility: hidden}
#color{width: 80px; height: 25px; font:normal normal normal 12px Arial }
form{position: absolute; width: 100%; height: 100%; top: 0}
#ta1{position: absolute; width: 100%; height: 100%; left; 0}
#grd{position: absolute; width: 100%; height: 100%}
</style>
</head>
<body>
<di id=”frame”>
<svg id=”canvas” xmlns=”http://www.w3.org/2000/svg&#8221; version=”1.1″ width=”” height=””></svg></di>
<svg id=”canvas2″ xmlns=”http://www.w3.org/2000/svg&#8221; version=”1.1″ width=”” height=””></svg>
<form>
<input type=”text” id=”t1″ style=”width: 80px” name=”t1″ value=”brush width” onclick=’document.getElementById(“t1”).value = “” ;’/>
<input type=”text” id=”fc” style=”width: 80px” name=”fc” value=”fill color” onclick=’document.getElementById(“fc”).value = “” ;’/>
<input type=’color’ id=’color’ value= simple color />
<input type=”text” id=”trans” style=”width: 80px” name=”trans” value=”opacity”onclick=’document.getElementById(“trans”).value = “” ;’ />
<input type=”text” id=”bur” style=”width: 50px” name=”bur” value=”blur” onclick=’document.getElementById(“bur”).value = “” ;’ />
<input type=”text” id=”sw” style=”width: 80px” name=”sw” value=”stroke width” onclick=’document.getElementById(“sw”).value = “” ;’/>
<input type=’color’ id=’color2′ value= simple color />
<input id=”file” type=”file” style=”width: 90px” name=”files” value=”” onchange=’getFile();’ />
<input id=”check” type=”checkbox” style=”width:15px” name=”check” value=”” />By Width
<select id=”s1″ name=”s1″ >
<option >Brush</option>
<option >Circle</option>
<option >Square</option>
<option >Polygon</option>
<option >Reg Poly</option>
<option >Path</option>
</select>
<input type=”text” id=”ps” style=”width: 50px” name=”ps” value=”# sides” onclick=’document.getElementById(“ps”).value = “” ;’/>
<input id=”file2″ type=”file” style=”width: 90px” name=”file2″ value=”” onchange=’loadImage();’ />
<input type=”button” id=”fs” name=”fs” value=”Full Screen” onclick=’document.getElementById(“frame”).requestFullscreen();’ />
&nbsp&nbsp<button type=”button” id=”b1″ name=”b1″ value=” ” onclick=’save();’ >Save</button>
<input type=”text” id=”t2″ style=”width: 50px” name=”t2″ value=”” /> <input type=”text” id=”sm” style=”width: 50px” name=”sm” value=”points” /> <input type=”button” id=”ud” name=”ud” value=”undo” onclick = ‘udo();’ />
</form>

<di class =”d1″>
<svg id = “grd” xmlns=”http://www.w3.org/2000/svg”&gt;
<defs>
<pattern id=”smallGrid” width=”8″ height=”8″ patternUnits=”userSpaceOnUse”>
<path d=”M 8 0 L 0 0 0 8″ fill=”none” stroke=”gray” stroke-width=”0.5″/>
</pattern>
<pattern id=”grid” width=”80″ height=”80″ patternUnits=”userSpaceOnUse”>
<rect width=”80″ height=”80″ fill=”url(#smallGrid)”/>
<path d=”M 80 0 L 0 0 0 80″ fill=”none” stroke=”gray” stroke-width=”1″/>
</pattern>
</defs>
<rect width=”100%” height=”100%” fill=”url(#grid)” />
</svg>
</di>
<textarea id=”ta1″ name=”ta1″ ondblclick=’document.getElementById(“ta1”).style.visibility=”hidden”‘;></textarea>

var posX;var posY;var start; var stroke; var fill2; var fil = “”; var jump; var posoX = 0; var posoY = 0; var cnt = -1; var str1 = “”; var sz; var lft;
var r = 0; var g = 0; var b = 0; var x1 = 0; var y1 = 0; var wd = 0; var opac = 1; var strw = 0; var strokecolor; pt = new Array(); var pts = “”; var poly = false; var textToSave = “”; var oldURL = “svg.html”; var cntb = 0; var blr = “0”; var fil2 = “”; var loded = false;
var ang = 0; var px = new Array(); var py = new Array(); var l = 100; var px0 = 300; var py0 = 300; var pts2 = “”; var sids = 0; var sw = 0; var pth = false; px = new Array(); py = new Array(); var str2 = “”; var cntp = -1; var bck = “”;

var typ = document.getElementById(“s1”);

function loadImage() {
fil2 = document.getElementById(“file2”).value;
if (fil2.indexOf(“fake”) > -1) {
fil2 = fil2.substr(12, fil2.length – 12);
if (! loded) document.getElementById(“frame”).innerHTML = ‘‘ + document.getElementById(“frame”).innerHTML;
loded = true;
}
}

function udo() {
document.getElementById(“canvas”).innerHTML = bck;
}

function getFile() {
fil = document.getElementById(“file”).value;
if (fil.indexOf(“fake”) > -1) {
fil = fil.substr(12, fil.length – 12);
}
}

var IE = document.all?true:false;
if (!IE) document.captureEvents(Event.MOUSEMOVE)
document.onclick = getMouse2;
document.onmousemove = getMouseXY;

document.addEventListener(‘touchmove’, function(e) {
var touches = e.changedTouches;
posX = touches[0].pageX;
posY = touches[0].pageY;
mobl();
},false);

function getMouse2(e) {
if (IE) {
posX = event.clientX + document.body.scrollLeft;
posY = event.clientY + document.body.scrollTop – 50;
}
else {
posX = e.pageX;
posY = e.pageY – 50;
}
if(posY > 50) {

if (typ.value == “Circle” || typ.value == “Square” || typ.value == “Reg Poly”) cntb ++;
}

if (posY > 50 && document.getElementById(“bur”).value != “blur”) blr = document.getElementById(“bur”).value;

if (cntb % 2 == 1 ) {document.getElementById(“canvas”).innerHTML = ” + document.getElementById(“canvas”).innerHTML;
}

if (document.getElementById(“sw”).value != “stroke width”) strw = document.getElementById(“sw”).value;
strokecolor = document.getElementById(“color2”).value;
if (document.getElementById(“trans”).value != “opacity”) opac = parseFloat(document.getElementById(“trans”).value);
if (document.getElementById(“t1”).value != “brush width”) stroke = document.getElementById(“t1”).value;
var rd = document.getElementById(“color”).value.substr(1,2);
r = parseInt(rd, 16);
var gr = document.getElementById(“color”).value.substr(3,2);
g= parseInt(gr, 16);
var bl = document.getElementById(“color”).value.substr(5,2);
b = parseInt(bl, 16);
if (document.getElementById(“fc”).value == “fill color” ) {
fill2 = “rgba(” + r + “,” + g + “,” + b + “,” + opac + “)”;
} else {
fill2 = document.getElementById(“fc”).value;
}
if (document.getElementById(“check”).checked) jump = true;
if (! start && posY > 50) {
start = true;
if (jump) {
var dif = Math.sqrt((posX – posoX)^2 + (posY – posoY)^2);
if (dif > Math.sqrt(2)*Math.sqrt(parseInt(stroke))) {
posoX = posX;
posoY = posY;
if (fil != “”) {
sz = 4*parseInt(stroke);
bck = document.getElementById(“canvas”).innerHTML;
document.getElementById(“canvas”).innerHTML +=”;
document.getElementById(“t2”).value = document.getElementById(“canvas”).innerHTML;
}
// cnt = 0;
}
}
if (typ.value == “Circle” || typ.value == “Square” || typ.value == “Reg Poly” || typ.value == “Path”) {
poly = false;
cnt = -1;
}
if (typ.value == “Circle” || typ.value == “Square” || typ.value == “Reg Poly” || typ.value == “Polygon”) {
pth = false;
cntp = -1;
}

if (typ.value == “Reg Poly”) {
px0 = posX – 5 ;
py0 = posY;
sids = parseInt(document.getElementById(“ps”).value);
pts2 = “”;
l = parseInt(document.getElementById(“t1”).value);
ang = parseInt(180/sids);
for (var j = 1;j 0) {
px[j] =parseInt(dx);
py[j] = parseInt(dy);
}
pts2 = pts2 + px[j] + “,” + py[j] + ” ” ;
}
bck = document.getElementById(“canvas”).innerHTML;
document.getElementById(“canvas”).innerHTML +=”;
}

if (typ.value == “Circle”) {
bck = document.getElementById(“canvas”).innerHTML;
document.getElementById(“canvas”).innerHTML +=”;
}
if (typ.value == “Square”) {
x1 = posX – 5 – parseInt(stroke);
y1 = posY – parseInt(stroke);
wd = 2 * parseInt(stroke);
bck = document.getElementById(“canvas”).innerHTML;
document.getElementById(“canvas”).innerHTML +=”;
}

if (typ.value == “Path”) {
pth = true;
if (pth) cntp ++;
document.getElementById(“sm”).value = (cntp + 1).toString();
px[cntp] = posX – 5;
py[cntp] = posY;
if ( cntp == 0) {
str2 = ”;
}
if (cntp > 0) {
str2 = str2 + parseInt(px[cntp]).toString() + ‘,’ + parseInt(py[cntp]).toString() + ‘ ‘ ;
document.getElementById(“canvas2″).innerHTML +=”;
}
if (cntp > parseInt(document.getElementById(“ps”).value) -2) {
str2 = str2 + parseInt(px[cntp]).toString() + ‘,’ + parseInt(py[cntp]).toString() + ‘ ‘ + parseInt(px[cntp]).toString() + ‘,’ + parseInt(py[cntp]).toString() + ‘ ‘ + parseInt(px[cntp]).toString() + ‘,’ + parseInt(py[cntp]).toString() + ‘ ‘ + parseInt(px[0]).toString() + ‘,’ + parseInt(py[0]).toString() + ‘ ‘ + parseInt(px[0]).toString() + ‘,’ + parseInt(py[0]).toString() + ‘ Z” />’;
bck = document.getElementById(“canvas”).innerHTML;
document.getElementById(“canvas”).innerHTML += str2;
document.getElementById(“canvas2”).innerHTML = “”;
cntp = -1;
document.getElementById(“sm”).value = “points”;
for (var i = 0; i ‘;
if (poly && cnt == parseInt(document.getElementById(“ps”).value -1)) {
bck = document.getElementById(“canvas”).innerHTML;
document.getElementById(“canvas”).innerHTML +=”;
document.getElementById(“canvas2”).innerHTML = “”;
cnt = -1;
pts = “”;
}
start = false;

}
} else {
start = false;
jump = false;
}
}

function save() {
document.getElementById(“ta1”).value = ‘#canvas{position: absolute; width: 99%; height: 89%; left: .5%; top: 50px; border: 1px solid}’ + document.getElementById(“frame”).innerHTML + ”;
document.getElementById(“ta1”).style.visibility = “visible”;
textToSave = document.getElementById(“ta1″).value;
sve();
}

function sve() {
var textToSaveAsBlob = new Blob([textToSave], {type:”text/plain”});
var textToSaveAsURL = window.URL.createObjectURL(textToSaveAsBlob);
fileNameToSaveAs = prompt(“FileName to save as”,oldURL);
oldURL = fileNameToSaveAs;
var downloadLink = document.createElement(“a”);
downloadLink.download = fileNameToSaveAs;
downloadLink.innerHTML = “Download File”;
downloadLink.href = textToSaveAsURL;
downloadLink.onclick = destroyClickedElement;
downloadLink.style.display = “none”;
document.body.appendChild(downloadLink);
downloadLink.click();
}

function destroyClickedElement(event) {
document.body.removeChild(event.target);
}

function getMouseXY(e) {
if (IE) {
posX = event.clientX + document.body.scrollLeft;
posY = event.clientY + document.body.scrollTop – 50;
}
else {
posX = e.pageX;
posY = e.pageY – 50;
}
if (document.getElementById(“check”).checked) jump = true;
if (start && posY > 50) {
if (jump) {
var dif = Math.sqrt((posX – posoX)^2 + (posY – posoY)^2);
if (dif > Math.sqrt(2)*Math.sqrt(parseInt(stroke))) {
posoX = posX;
posoY = posY;
if (fil != “”) {
sz = 4*parseInt(stroke);
document.getElementById(“canvas”).innerHTML +=”;
document.getElementById(“t2”).value = document.getElementById(“canvas”).innerHTML;
}
// cnt = 0;
}
} else if (typ.value == “Reg Poly”) {
px0 = posX;
py0 = posY;
sids = parseInt(document.getElementById(“ps”).value);
pts2 = “”;
l = parseInt(document.getElementById(“t1”).value);
ang = parseInt(180/sids);
for (var j = 1;j 0) {
px[j] =parseInt(dx);
py[j] = parseInt(dy);
}
pts2 = pts2 + px[j] + “,” + py[j] + ” ” ;
}

document.getElementById(“canvas”).innerHTML +=”;

} else if (typ.value == “Circle”) {
document.getElementById(“canvas”).innerHTML +=”;
} else if (typ.value == “Square”) {
x1 = posX – 5 – parseInt(stroke);
y1 = posY – parseInt(stroke);
wd = 2 * parseInt(stroke);
document.getElementById(“canvas”).innerHTML +=”;
}
}
}

function mobl() {
if (document.getElementById(“check”).checked) jump = true;
if (start && posY > 50) {
if (jump) {
var dif = Math.sqrt((posX – posoX)^2 + (posY – posoY)^2);
if (dif > Math.sqrt(2)*Math.sqrt(parseInt(stroke))) {
posoX = posX;
posoY = posY;
if (fil != “”) {
sz = 4*parseInt(stroke);
document.getElementById(“canvas”).innerHTML +=”;
document.getElementById(“t2”).value = document.getElementById(“canvas”).innerHTML;
}
// cnt = 0;
}
} else if (typ.value == “Reg Poly”) {
px0 = posX;
py0 = posY;
sids = parseInt(document.getElementById(“ps”).value);
pts2 = “”;
l = parseInt(document.getElementById(“t1”).value);
ang = parseInt(180/sids);
for (var j = 1;j 0) {
px[j] =parseInt(dx);
py[j] = parseInt(dy);
}
pts2 = pts2 + px[j] + “,” + py[j] + ” ” ;
}

document.getElementById(“canvas”).innerHTML +=”;

} else if (typ.value == “Circle”) {
document.getElementById(“canvas”).innerHTML +=”;
} else if (typ.value == “Square”) {
x1 = posX – 5 – parseInt(stroke);
y1 = posY – parseInt(stroke);
wd = 2 * parseInt(stroke);
document.getElementById(“canvas”).innerHTML +=”;
}
}
}

</body>
</html>


A touchmove listener is added which gets the pointer coordinates and calls the function mobl():
document.addEventListener(‘touchmove’, function(e) {
var touches = e.changedTouches;
posX = touches[0].pageX;
posY = touches[0].pageY;
mobl();
},false);

The function mobl() basically duplicates the mousemove function:
function mobl() {
if (document.getElementById(“check”).checked) jump = true;
if (start && posY > 50) {
if (jump) {
var dif = Math.sqrt((posX – posoX)^2 + (posY – posoY)^2);
if (dif > Math.sqrt(2)*Math.sqrt(parseInt(stroke))) {
posoX = posX;
posoY = posY;
if (fil != “”) {
sz = 4*parseInt(stroke);
document.getElementById(“canvas”).innerHTML +='<image x=”‘ + posX + ‘” y=”‘ + posY + ‘” width=”‘ + sz + ‘” height=”‘ + sz + ‘” xlink:href=”‘ + fil + ‘” />’;
document.getElementById(“t2”).value = document.getElementById(“canvas”).innerHTML;
}
// cnt = 0;
}
} else if (typ.value == “Reg Poly”) {
px0 = posX;
py0 = posY;
sids = parseInt(document.getElementById(“ps”).value);
pts2 = “”;
l = parseInt(document.getElementById(“t1”).value);
ang = parseInt(180/sids);
for (var j = 1;j <=sids; j ++) {
var dx = px0 + l * (Math.sin( (j * 360/sids + ang) * (Math.PI/180)));
var dy = py0 + l * (Math.cos( (j * 360/sids + ang) * (Math.PI/180)));
if (j > 0) {
px[j] =parseInt(dx);
py[j] = parseInt(dy);
}
pts2 = pts2 + px[j] + “,” + py[j] + ” ” ;
}

document.getElementById(“canvas”).innerHTML +='<polygon points=”‘ + pts2 + ‘” style=”fill: ‘ + fill2 + ‘; filter: url(#shadow’ + cntb + ‘)” />’;

} else if (typ.value == “Circle”) {
document.getElementById(“canvas”).innerHTML +='<ellipse cx=”‘ + (posX – 5) + ‘” cy=”‘ + posY + ‘” rx=”‘ + stroke + ‘” ry=”‘ + stroke + ‘” style=”fill: ‘ + fill2 + ‘; filter: url(#shadow’ + cntb + ‘)” />’;
} else if (typ.value == “Square”) {
x1 = posX – 5 – parseInt(stroke);
y1 = posY – parseInt(stroke);
wd = 2 * parseInt(stroke);
document.getElementById(“canvas”).innerHTML +='<rect x=”‘ + x1 + ‘” y=”‘ + y1 + ‘” width=”‘ + wd + ‘” height=”‘ + wd + ‘” style=”fill: ‘ + fill2 + ‘; filter: url(#shadow’ + cntb + ‘)” />’;
}
}
}

HTML Jigsaw Update Mobile


This extends the previous app for use with mobile devices.

This is how it looks initially in full screen:

and after piecing together:

Here is the code:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head profile=”http://gmpg.org/xfn/11″&gt;
<title>Jigsaw</title>
<style>
body {margin-left:0;margin-right:0;font:normal normal normal 15px Arial;}
a{ text-decoration: }:link { color: rgb(0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { }

</style>
</head>
<body>
<di id = “frame” style=”position: absolute; left: 0; top: 0; height: 100%; width: 100%; background: white” >
<img src= “attachment3.jpg” style=”position: absolute; left: 10px; top: 10px; height: 180px” />
<img id=”piece1″ src= “images/piece1.gif” style=”position: absolute; left: 600px; top: 20px” /> <img id=”piece2″ src= “images/piece2.gif” style=”position: absolute; left: 600px; top: 150px” />
</di>

var posX;var posY; var mv = false; var el = “”;
var IE = document.all?true:false;
if (!IE) document.captureEvents(Event.MOUSEMOVE)
document.onclick = getMouse2;
document.onmousemove = getMouseXY;

document.addEventListener(‘touchmove’, function(e) {
var touches = e.changedTouches;
posX = touches[0].pageX;
posY = touches[0].pageY;
mobl();
},false);

function mobl() {
if (mv) {
document.getElementById(el).style.left = posX + “px”;
document.getElementById(el).style.top = posY + “px”;
}
}

function getMouse2(e) {
document.getElementById(“frame”).requestFullscreen();
if (IE) {
posX = event.clientX + document.body.scrollLeft;
posY = event.clientY + document.body.scrollTop;
} else {
posX = e.pageX;
posY = e.pageY;
}

if (mv) {
mv = false;
} else {
mv = true;
}

if (posY > 20 && posY 150 && posY
</body></html>


I will discuss only the changes.

The onclick listener is accepted by both mobile and non-mobile devices. onmousemove is accepted only by non-touchscreen devices while touchscreen devices accept the touchmove function. The comprehensive app therefore has all three functions.

The touchmove listener is as follows:
document.addEventListener(‘touchmove’, function(e) {
var touches = e.changedTouches;
posX = touches[0].pageX;
posY = touches[0].pageY;
mobl();
},false);

An array touches is created and used to get the pointer location:
posX = touches[0].pageX;
posY = touches[0].pageY;

A function mobl is then called:
function mobl() {
if (mv) {
document.getElementById(el).style.left = posX + “px”;
document.getElementById(el).style.top = posY + “px”;
}
}

As in the previous app, if the boolean mv is true, the chosen piece is moved to the position of the pointer.

A Prototype for a HTML Jigsaw Puzzle


This app is a prototype for a HTML jigsaw puzzle. As a prototype, it has only two pieces, but the concept is the same for any number of pieces.

This is how it looks initially in full screen:

and after piecing together:

Here is the code:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head profile=”http://gmpg.org/xfn/11″&gt;
<title>Jigsaw</title>
<style>
body {margin-left:0;margin-right:0;font:normal normal normal 15px Arial;}
a{ text-decoration: }:link { color: rgb(0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { }

</style>
</head>
<body>
<di id = “frame” style=”position: absolute; left: 0; top: 0; height: 100%; width: 100%; background: white” >
<img src= “attachment3.jpg” style=”position: absolute; left: 10px; top: 10px; height: 180px” />
<img id=”piece1″ src= “images/piece1.gif” style=”position: absolute; left: 600px; top: 20px” /> <img id=”piece2″ src= “images/piece2.gif” style=”position: absolute; left: 600px; top: 150px” />
</di>

var posX;var posY; var mv = false; var el = “”;
var IE = document.all?true:false;
if (!IE) document.captureEvents(Event.MOUSEMOVE)
document.onclick = getMouse2;
document.onmousemove = getMouseXY;

function getMouse2(e) {
document.getElementById(“frame”).requestFullscreen();
if (IE) {
posX = event.clientX + document.body.scrollLeft;
posY = event.clientY + document.body.scrollTop;
} else {
posX = e.pageX;
posY = e.pageY;
}

if (mv) {
mv = false;
} else {
mv = true;
}

if (posY > 20 && posY 150 && posY
</body></html>


The pieces were created with a graphics application. It is possible to use SVG clip-paths, but it was easier to make images.

I used GIMP to make a piece sized selection, copy it, paste as a new document, save as a transparent GIF, and convert the original selection to a path.
The path was then stroked so I could see how to make the next piece fit. Since I am no artist, the fit is not perfect, but the idea is demonstrated.
In the future I will try to make a complete jigsaw puzzle, at which time I will endeavor to make to make the fit perfect.

Two Event Listeners were set up, one to respond to a mouse click and one to a mouse move:
document.onclick = getMouse2;
document.onmousemove = getMouseXY;

Both set up the pointer position as a variable:
if (IE) {
posX = event.clientX + document.body.scrollLeft;
posY = event.clientY + document.body.scrollTop;
} else {
posX = e.pageX;
posY = e.pageY;
}

The click also had a toggle to allow the pointer to selectively choose a piece, allow it to be moved and then, on clicking again to be freed to select another piece:
if (mv) {
mv = false;
} else {
mv = true;
}

if (posY > 20 && posY < 140) el = “piece1”;
if (posY > 150 && posY < 270) el = “piece2”;
}

the variable el is created to hold the id of the piece to be moved.

If mv is true the move function function getMouseXY(e) then moves the id selected piece to its appropriate position, to be set by clicking again and turning mv false;

if (mv) {
document.getElementById(el).style.left = posX + “px”;
document.getElementById(el).style.top = posY + “px”;
}