Canvas Drag and Drop Positively Robust Tutorial

Canvas Drag and Drop Positively Robust Tutorial

Canvas Drag and Drop Positively Robust Tutorial

The recent Canvas Drag and Drop Even Less Ephemeral Tutorial progress allowed for …

  • animated GIF (and other incarnation) media file creation logic for the emailer … and today we add in …
  • animated GIF (and other incarnation) media file creation logic for the emailee … along with the ability to re-email on the same content

… thinking about both sides of a collaboration arrangement, taking the whole situation on from “even less ephemeral” to “positively robust” according to this dogma … tee hee!

Whatever (software) is the receiver of the email body’s link is what needs to change to add in 📹 (animated GIF) and 📧 (email) emoji button clicking based new Javascript logic here, that being the ever more and more changing helperer outerer htmlemail.html, it taking lots of leads from recent goings on …

New aspects to document.body onload event Javascript logic

newlh='<ht' + 'ml><he' + 'ad><title>Animated GIF presentation ...</title><scr' + 'ipt type=text/javascript> var pwhat=[], interested=true, curslide=0, slidecnt=' + eval(-1 + eval('' + lhdatas.length)) + ', delay=' + ('' + delay).replace('-','') + ', curdelay=eval(5000 + delay); function animate() { document.getElementById("topimg").style.backgroundImage="url(" + document.getElementById("animg" + curslide).src + ")"; if (typeof primeit == "function" && interested) { if (1 == 1) { primeit(document.getElementById("animg" + curslide).src); } } curslide++; if (curslide >= slidecnt) { curslide=0; interested=false; document.body.style.cursor="pointer"; } setTimeout(animate, curdelay); curdelay=delay; } </scr' + 'ipt></he' + 'ad><bo' + 'dy onload=animate();><div id=topimg style=width:100%;height:800px;background-repeat:no-repeat;background-size:contain;></div></bo' + 'dy></ht' + 'ml>';
New useful internal use only Javascript variables

var popupemailfunction=' function doagdisplay() { location.href="#ifconto"; } function dopopupemail() { var huhe=prompt("Please enter email address to share Animated GIF style presentation with. Append a hashtagged delay between slides in milliseconds that is not 4000 optionally.", ""); if (huhe == null) { huhe=""; } if (huhe.indexOf("@") != -1) { if (huhe.indexOf("#") != -1) { document.getElementById("aemail").href=document.getElementById("aemail").href.replace(encodeURIComponent("delay=4000"),encodeURIComponent("delay=" + huhe.split("#")[1])); huhe=huhe.split("#")[0]; } document.getElementById("aemail").href=document.getElementById("aemail").href.replace(":?", ":" + huhe + "?"); document.getElementById("aemail").click(); } } ';
var bodhead="<scr" + "ipt type=text/javascript> var documentURL=document.URL, agidis='slideshow', agiscnt=1, agconto=null; " + popupemailfunction + " function primeit(lastdrop) { if (7 == 99) { alert('' + lastdrop.length + ' ' + document.getElementById('aemail').href.length); } if (8 == 9) { if (lastdrop.indexOf('data:') > 0) { document.getElementById('aemail').href+=encodeURIComponent(encodeURIComponent('data:' + lastdrop.split('data:')[1].split(String.fromCharCode(39))[0].split(String.fromCharCode(34))[0].split(')')[0])); } else { document.getElementById('aemail').href+=encodeURIComponent(encodeURIComponent(lastdrop)); } } if (7 == 99) { alert('' + lastdrop.length + ' ' + document.getElementById('aemail').href.length); } if (agconto) { agconto.getElementById(agidis).focus(); agconto.getElementById(agidis).value=lastdrop; agconto.getElementById(agidis).blur(); agiscnt++; agidis='slideshow' + agiscnt; window.scrollTo(0,0); } } function agmaybe() { " + String.fromCharCode(10) + " iois=document.getElementById('ifconto'); " + String.fromCharCode(10) + " if (iois != null) { " + String.fromCharCode(10) + " if (iois.src.indexOf('tutorial_to_animated_gif.php') != -1) { " + String.fromCharCode(10) + " agconto = (iois.contentWindow || iois.contentDocument); " + String.fromCharCode(10) + " if (agconto != null) { " + String.fromCharCode(10) + " if (agconto.document) { agconto = agconto.document; } " + String.fromCharCode(10) + " agconto.getElementById('delay').value=(location.search.split('delay=')[1] ? decodeURIComponent(location.search.split('delay=')[1].split('&')[0].split('#')[0]) : '4000'); " + String.fromCharCode(10) + " agconto.getElementById('stitle').value='My Animation ...'; " + String.fromCharCode(10) + " agconto.getElementById('watermarkmode').value=' '; " + String.fromCharCode(10) + " var spans=agconto.getElementsByTagName('span'); " + String.fromCharCode(10) + " for (var jspans=0; jspans<spans.length; jspans++) { " + String.fromCharCode(10) + " if (spans[jspans].innerHTML=' Delay(s)' && spans[jspans].outerHTML.indexOf('do' + 'dt') != -1) { spans[jspans].click(); } " + String.fromCharCode(10) + " } " + String.fromCharCode(10) + " agconto.getElementById('slideshow').focus(); " + String.fromCharCode(10) + " window.scrollTo(0,0); " + String.fromCharCode(10) + " } " + String.fromCharCode(10) + " } " + String.fromCharCode(10) + " } " + String.fromCharCode(10) + " } " + String.fromCharCode(10) + " " + String.fromCharCode(10) + " function agcheck() { " + String.fromCharCode(10) + " if (document.getElementById('agshow')) { " + String.fromCharCode(10) + " if (document.getElementById('agshow').value == 'y') { " + String.fromCharCode(10) + " document.getElementById('agshow').value='Y'; " + String.fromCharCode(10) + " location.href='#ifconto'; " + String.fromCharCode(10) + " } " + String.fromCharCode(10) + " } " + String.fromCharCode(10) + " } " + String.fromCharCode(10) + " " + String.fromCharCode(10) + " setInterval(agcheck, 2000); " + String.fromCharCode(10) + " setTimeout(function(){ document.body.style.cursor='progress'; }, 4000); </scr" + "ipt>";
var bodtail="<input type=hidden value='' id='agshow'></input><input type=hidden value='' id='thegifnamegoeshere'></input><br><hr></hr><br><button title=\"Share via Email\" id=semail style=display:inline-block; onclick=\"dopopupemail();\">&#128231;</button> <button title=\"Create Animated GIF or other animation form\" id=sag style=display:inline-block; onclick=\"doagdisplay();\">&#128249;</button><a target=top id=aemail style=display:none; href=\"mailto:?subject=Animated%20GIF%20presentation&body=" + encodeURIComponent(document.URL) + "\">Email</a><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><iframe onload='setTimeout(agmaybe,3000);' id=ifconto name=ifconto src='/PHP/animegif/tutorial_to_animated_gif.php' style='width:100%;height:900px;display:BLOCK;'></iframe>";


if (eval('' + lhdatas.length) < 2) { bodhead=''; bodtail=''; }
Affect the outputted HTML and integration with inhouse Animated GIF Creator PHP web application

if (lh.indexOf('</head>') != -1 && lh.indexOf('<head') != -1) {
headis='<head' + lh.split('<head')[1].split('</head>')[0] + bodhead + '</head>';
//document.write(headis.replace(/https\:\/\//g,'//').replace(/http\:\/\//g,'//') + bodyis.replace(/https\:\/\//g,'//').replace(/http\:\/\//g,'//'));
if (bodyis.indexOf('</bo' + 'dy>') != -1) {
document.write(headis.replace(/https\:\/\//g,'//').replace(/http\:\/\//g,'//') + bodyis.replace(/https\:\/\//g,'//').replace(/http\:\/\//g,'//').split('</bo' + 'dy>')[0] + bodtail + '</bo' + bodyis.split('</bo')[1]);
} else {
document.write(headis.replace(/https\:\/\//g,'//').replace(/http\:\/\//g,'//') + bodyis.replace(/https\:\/\//g,'//').replace(/http\:\/\//g,'//') + bodtail);
}
} else if (bodyis != '<bo' + 'dy></bo' + 'dy>') {
if (bodyis.indexOf('</bo' + 'dy>') != -1) {
document.write(bodyis.replace(/https\:\/\//g,'//').replace(/http\:\/\//g,'//').split('</bo' + 'dy>')[0] + bodhead + '</bo' + bodyis.split('</bo')[1]);
} else {
document.write(bodyis.replace(/https\:\/\//g,'//').replace(/http\:\/\//g,'//') + bodhead);
}

}


Previous relevant Canvas Drag and Drop Even Less Ephemeral Tutorial is shown below.

Canvas Drag and Drop Even Less Ephemeral Tutorial

Canvas Drag and Drop Even Less Ephemeral Tutorial

In yesterday's Canvas Drag and Drop Less Ephemeral Tutorial ...

  • image slides ... could go into ...
  • data URI list ... could be hashtagged ... along with a ...
  • delay argument ... then, via user button click, sent via "a" "mailto:" link ...
  • email to emailee ... who on clicking the email body link ...
  • sees that Animated GIF presentation

... via a new piece of RJM Programming HTML and Javascript, but not as a saveable media file. Today, we allow for saveable media outputs by interfacing to our inhouse Animated GIF Creator PHP web application.

Below are the aspects to this integration ...

New static HTML

<input type=hidden value='' id='agshow'></input>
<input type=hidden value='' id='thegifnamegoeshere'></input> <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<iframe onload='setTimeout(agmaybe,3000);' id=ifconto name=ifconto src='/PHP/animegif/tutorial_to_animated_gif.php' style='width:100%;height:900px;display:BLOCK;'></iframe>
Tweaked static HTML

<button id=mydbut ontouchmove="dzoreopen=true;" oncontextmenu="dzoreopen=true;" ondblclick="dzoreopen=true;" onclick=" event.stopPropagation(); if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { drop(document.getElementById('target')); } else { document.getElementById('topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML='<span id=tarbut style=margin-top:' + document.getElementById('target').getAttribute('data-mt') + 'px;>' + dz + '</span>'; } ">Drop Zone for Canvas After Click</button>
New near document.body onload event code

function agmaybe() {
iois=document.getElementById('ifconto');
if (iois != null) {
//alert(13);
if (iois.src.indexOf('tutorial_to_animated_gif.php') != -1) {
agconto = (iois.contentWindow || iois.contentDocument);
//alert(23);
if (agconto != null) {
if (agconto.document) { agconto = agconto.document; }
//alert(33);
agconto.getElementById('delay').value='4000';
agconto.getElementById('stitle').value='My Animation ...';
agconto.getElementById('watermarkmode').value=' ';
var spans=agconto.getElementsByTagName('span');
for (var jspans=0; jspans
function agcheck() {
if (document.getElementById('agshow')) {
if (document.getElementById('agshow').value == 'y') {
document.getElementById('agshow').value='Y';
//document.getElementById('ifconto').style.display='block';
location.href='#ifconto';
}
}
}

setInterval(agcheck, 2000);
Popup window creation and renewal and featuring new Animated GIF creation 📹 emoji button

if (dzo == null) {
dzo=window.open('','_blank','top=200,left=' + eval(-300 + eval('' + screen.width)) + ',width=300,height=300');
if (popupemailfunction == '') {
popupemailfunction=' function doagdisplay() { window.opener.document.getElementById("agshow").value="y"; } function dopopupemail() { var huhe=prompt("Please enter email address to share Animated GIF style presentation with. Append a hashtagged delay between slides in milliseconds that is not 4000 optionally.", ""); if (huhe == null) { huhe=""; } if (huhe.indexOf("@") != -1) { if (huhe.indexOf("#") != -1) { document.getElementById("aemail").href=document.getElementById("aemail").href.replace(encodeURIComponent("delay=4000"),encodeURIComponent("delay=" + huhe.split("#")[1])); huhe=huhe.split("#")[0]; } document.getElementById("aemail").href=document.getElementById("aemail").href.replace(":?", ":" + huhe + "?"); document.getElementById("aemail").click(); } } ';
}
htmlis='<ht' + 'ml><he' + 'ad><title>Inhouse Animated GIF style presentation ...</title><scri' + 'pt type=text/javascript> var dataih="", oneround=false, curslides=0, thislag=0; ' + popupemailfunction + ' function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { if (!oneround) { oneround=true; document.getElementById("semail").style.display="inline-block"; document.getElementById("sag").style.display="inline-block"; } setTimeout(function() { dataih=window.opener.getdzfirst(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } else { setTimeout(function() { dataih=window.opener.getdznext(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div><br><button title="Share via Email" id=semail style=display:none; onclick="dopopupemail();">&#128231;</button> <button title="Create Animated GIF or Video" id=sag style=display:none; onclick="doagdisplay();">&#128249;</button><a target=top id=aemail style=display:none; href="mailto:?subject=Animated%20GIF%20presentation&body=' + encodeURIComponent('https://www.rjmprogramming.com.au/htmlemail.html?delay=4000#') + '">Email</a></bo' + 'dy></ht' + 'ml>';
dzo.document.write(htmlis);
dzo.document.title='Inhouse Animated GIF style presentation ...';
} else if (dzo.closed) {
if (dzoreopen) {
dzo=window.open('','_blank','top=200,left=' + eval(-300 + eval('' + screen.width)) + ',width=300,height=300');
htmlis='<ht' + 'ml><he' + 'ad><title>Inhouse Animated GIF style presentation ...</title><scri' + 'pt type=text/javascript> var dataih="", oneround=false, curslides=0, thislag=0; ' + popupemailfunction + ' function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { if (!oneround) { oneround=true; document.getElementById("semail").style.display="inline-block"; document.getElementById("sag").style.display="inline-block"; } setTimeout(function() { dataih=window.opener.getdzfirst(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } else { setTimeout(function() { dataih=window.opener.getdznext(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div><br><button title="Share via Email" id=semail style=display:none; onclick="dopopupemail();">📧</button> <button title="Create Animated GIF or Video" id=sag style=display:none; onclick="doagdisplay();">📹</button><a target=top id=aemail style=display:none; href="mailto:?subject=Animated%20GIF%20presentation&body=' + encodeURIComponent('https://www.rjmprogramming.com.au/htmlemail.html?delay=4000#') + '">Email</a></bo' + 'dy></ht' + 'ml>';
dzo.document.write(htmlis);
}
} else {
dzo.focus();
}

Javascript global variables of relevance

var lastdrop='', lastelem=null, wasmt='0px', popupemailfunction='', agconto=null, agidis='slideshow', agiscnt=1, iois=null, dzoreopen=false;
New event logic to allow for popup window reopening

document.getElementById('target').innerHTML=prefixingih + '<button ' + opsty + ' id=tarbut ontouchmove=\"dzoreopen=true;\" oncontextmenu=\"dzoreopen=true;\" onclick=\" event.stopPropagation(); if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { drop(document.getElementById(' + "'target'" + ')); } else { document.getElementById(' + "'topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML=''; if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { document.getElementById('starbut').style.zIndex='50'; } else { document.getElementById('starbut').style.marginTop='-50px'; document.getElementById('starbut').style.zIndex='-50'; } document.getElementById('starbut').innerHTML='" + dz + "'" + '; } \">' + dz + ' for Canvas After Click</button>';
Add a slide to Animated GIF creator

if (agconto) {
//alert('3 ' + agidis);
agconto.getElementById(agidis).focus();
agconto.getElementById(agidis).value=lastdrop;
//alert(56);
//iois.maybemore(lastdrop, 'slideshow' + agiscnt, agconto.getElementById(agidis));
agconto.getElementById(agidis).blur();
//alert(256);
agiscnt++;
//alert('2:' + agiscnt);
agidis='slideshow' + agiscnt;
//agconto.getElementById(agidis).focus();
window.scrollTo(0,0);
//setTimeout(function() { agconto.getElementById(agidis).focus(); }, 6000);
}

... in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application.


Previous relevant Canvas Drag and Drop Less Ephemeral Tutorial is shown below.

Canvas Drag and Drop Less Ephemeral Tutorial

Canvas Drag and Drop Less Ephemeral Tutorial

We're bucking the trend, today, "offering less" but "producing more", we're hoping, on top of the progress of yesterday's Canvas Drag and Drop Ephemeral Tutorial ....

  • adding totaking from an "internal use only" level of ephemeralness which only offered an individual user view of canvas based Animated GIF style slide creation work as an Animated GIF presentation in their individually presented popup window ... to ...
  • adding to above a new 📧 Email emoji sharing (and "caring") button within that popup window presentation ... that if clicked ...
    1. prompts the user as per ...

      var huhe=prompt("Please enter email address to share Animated GIF style presentation with. Append a hashtagged delay between slides in milliseconds that is not 4000 optionally.", "");
    2. allows an "a" "mailto:" link's "href" attribute reflect any hashtagged delay you specified ...

      if (huhe.indexOf("#") != -1) {
      document.getElementById("aemail").href=document.getElementById("aemail").href.replace(encodeURIComponent("delay=4000"),encodeURIComponent("delay=" + huhe.split("#")[1])); huhe=huhe.split("#")[0];
      }
    3. allows an "a" "mailto:" link's "href" attribute reflect any emailee you specified ...

      if (huhe.indexOf("@") != -1) {
      document.getElementById("aemail").href=document.getElementById("aemail").href.replace(":?", ":" + huhe + "?"); document.getElementById("aemail").click();
      }
    4. and if all ritchy ditch ...

      if (huhe.indexOf("@") != -1) {
      document.getElementById("aemail").click();
      }
    5. clicks the "a" "mailto:" link, passing control over to your default email client software to present you with an email body presentation webpage by which you can click a Send icon to send that largely hashtagged data email to an email recipient (ie. emailee)

... in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application, helped out by ever more changing helperer outerer htmlemail.html (just HTML and Javascript and hashtagging) of recent times, gradually replacing work that used to be a PHP thaing via emailhtml.php associated with RJM Programming domain's email server fed by PHP mail command based email messages, now more in favour of hashtagging methodologies using "a" "mailto:" links, where we are only talking "clientside" with the logic, and the data length restrictions are not as severe.

Today's animated GIF presentation, hence, is recreatable ...

Back to top

Did you know?

When it comes to email (and SMS for that matter) creation logic, occasionally, we find, you need to double encode ... we kid you not! We needed to today, as you see below ...


var popupemailfunction='';

if (popupemailfunction == '') {
popupemailfunction=' function dopopupemail() { var huhe=prompt("Please enter email address to share Animated GIF style presentation with. Append a hashtagged delay between slides in milliseconds that is not 4000 optionally.", ""); if (huhe == null) { huhe=""; } if (huhe.indexOf("@") != -1) { if (huhe.indexOf("#") != -1) { document.getElementById("aemail").href=document.getElementById("aemail").href.replace(encodeURIComponent("delay=4000"),encodeURIComponent("delay=" + huhe.split("#")[1])); huhe=huhe.split("#")[0]; } document.getElementById("aemail").href=document.getElementById("aemail").href.replace(":?", ":" + huhe + "?"); document.getElementById("aemail").click(); } } ';
}
htmlis='<ht' + 'ml><he' + 'ad><title>Inhouse Animated GIF style presentation ...</title><scri' + 'pt type=text/javascript> var dataih="", oneround=false, curslides=0, thislag=0; ' + popupemailfunction + ' function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { if (!oneround) { oneround=true; document.getElementById("semail").style.display="block"; } setTimeout(function() { dataih=window.opener.getdzfirst(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } else { setTimeout(function() { dataih=window.opener.getdznext(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div><br><button title="Share via Email" id=semail style=display:none; onclick="dopopupemail();">&#128231;</button><a target=top id=aemail style=display:none; href="mailto:?subject=Animated%20GIF%20presentation&body=' + encodeURIComponent('https://www.rjmprogramming.com.au/htmlemail.html?delay=4000#') + '">Email</a></bo' + 'dy></ht' + 'ml>';

The pattern of when (we need to do this) remains a bit mysterious to us, but if we ever nail down the pattern, am sure you'll hear about it! The good side to solving via "double encoding" means is that you can easily see where the wall needs to be repaired straight in front of you ... in real time?!


Previous relevant Canvas Drag and Drop Ephemeral Tutorial is shown below.

Canvas Drag and Drop Ephemeral Tutorial

Canvas Drag and Drop Ephemeral Tutorial

Onto yesterday's Canvas Drag and Drop Primer Tutorial's ...

  • proof of concept feel to being able to drag and drop via the canvas element ... today we have ...
  • proof of concept ideas regarding future sharing and collaboration aspects to this Drag and Drop where we start with our "ephemeral" inhouse popup window version of what we hope to offer, in future, for users to share with email recipients

And the other progress was another "drag" friend for the canvas element, allowing that textarea element (now with ondragstart="dragstart(event);" draggable=true attribution) down the bottom right also be "draggable" (to create the textarea wording on a white backgrounded slide "Drop Zone" result when dragged there) for our ...

  • "Drop Zone" for non-mobile ... and ...
  • "Click Zone" for mobile

... platforms. The drag and drop functionality related global variables now go ...


var dz='Drop Zone';
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
dz='Click Zone';
}
var dzslides=[], dzo=null, dzcnt=0;
var lastdrop='', lastelem=null, wasmt='0px';

... and the Drag and Drop logic now goes ...


function dragstart(ev) {
lastelem=ev.target;
if (('' + ev.target.outerHTML).indexOf('<textarea') == 0) {
ev.dataTransfer.setData("text/plain", ev.target.value);
} else {

ev.dataTransfer.setData("text/plain", document.getElementById('topcanvas').toDataURL());
}
}

function dragover(ev) {
ev.preventDefault();
}

function drop(ev) {
var prefixingih='', mtdone=false, opsty='';
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
if (('' + lastelem.outerHTML).indexOf('<textarea') == 0) {
lastdrop=lastelem.value;
while (lastdrop.indexOf(String.fromCharCode(10)) != -1) {
lastdrop=lastdrop.replace(String.fromCharCode(10),'<br>');
}
prefixingih='<span style=background-color:white;>' + lastdrop + '</span><br><br>';
opsty=' style="opacity:0.3;" ';
lastdrop=dzslides[0];
document.getElementById('target').innerHTML = dzslides[0];
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop='0px';
}
} else {

document.getElementById('target').innerHTML = document.getElementById('topcanvas').toDataURL();
lastdrop=document.getElementById('target').innerHTML;
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop=wasmt;
}
}
} else {
if (('' + lastelem.outerHTML).indexOf('<textarea') == 0) {
lastdrop=lastelem.value;
while (lastdrop.indexOf(String.fromCharCode(10)) != -1) {
lastdrop=lastdrop.replace(String.fromCharCode(10),'<br>');
}
prefixingih='<span style=background-color:white;>' + lastdrop + '</span><br><br>';
opsty=' style="opacity:0.3;" ';
lastdrop=dzslides[0];
document.getElementById('target').innerHTML = dzslides[0];
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop='0px';
}
} else {
ev.target.innerHTML = ev.dataTransfer.getData("text");
lastdrop=ev.target.innerHTML;
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop=wasmt;
}
}
}

document.getElementById('topcanvas').setAttribute('draggable','false');
document.getElementById('target').innerHTML=prefixingih + '<button ' + opsty + ' id=tarbut onclick=\" event.stopPropagation(); if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { drop(document.getElementById(' + "'target'" + ')); } else { document.getElementById(' + "'topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML=''; if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { document.getElementById('starbut').style.zIndex='50'; } else { document.getElementById('starbut').style.marginTop='-50px'; document.getElementById('starbut').style.zIndex='-50'; } document.getElementById('starbut').innerHTML='" + dz + "'" + '; } \">' + dz + ' for Canvas After Click</button>';
if (document.getElementById('tarbut') && !mtdone) {
mtdone=true;
if (prefixingih != '') {
document.getElementById('tarbut').style.marginTop='0px';
} else {
document.getElementById('tarbut').style.marginTop=wasmt;
}
}

document.getElementById('starbut').innerHTML='';
var rectis=document.getElementById('target').getBoundingClientRect();
if (document.getElementById('mydbut')) {
rectis=document.getElementById('mydbut').getBoundingClientRect();
} else if (document.getElementById('tarbut')) {
rectis=document.getElementById('tarbut').getBoundingClientRect();
}
document.getElementById('target').setAttribute('data-mt','' + eval(-30 + eval('' + rectis.width)));
document.getElementById('target').style.backgroundColor='white';
document.getElementById('target').style.height='' + rectis.width + 'px';
document.getElementById('target').style.backgroundImage='url(' + lastdrop + ')';
document.getElementById('target').style.backgroundSize='contain';
document.getElementById('target').style.backgroundRepeat='no-repeat';
if (document.getElementById('topdragstyle').innerHTML == '' && prefixingih == '') {
wasmt='' + eval(-30 + eval('' + rectis.width)) + 'px';
document.getElementById('tarbut').style.marginTop=wasmt;
//var rectistb=document.getElementById('tarbut').getBoundingClientRect();
//document.getElementById('topdragstyle').innerHTML='<style> #tarbut { position:absolute; left:' + rectistb.left + 'px; top:' + eval(eval(0 * eval(-30 + eval('' + rectis.width))) + eval('' + rectistb.top)) + 'px; margin-top:0px !important; } </style>';
}
if (document.getElementById('tarbut')) {
dzslides.push(document.getElementById('target').outerHTML.replace(document.getElementById('tarbut').outerHTML,''));
} else {
dzslides.push(document.getElementById('target').outerHTML);
}
if (eval('' + dzslides.length) > 2) {
var htmlis='';
if (dzo == null) {
dzo=window.open('','_blank','top=200,left=200,width=300,height=300');
htmlis='<ht' + 'ml><he' + 'ad><title>Inhouse Animated GIF style presentation ...</title><scri' + 'pt type=text/javascript> var curslides=0, thislag=0; function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdzfirst(); }, thislag); } else { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdznext(); }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div></bo' + 'dy></ht' + 'ml>';
dzo.document.write(htmlis);
dzo.document.title='Inhouse Animated GIF style presentation ...';
} //else if (dzo.closed) {
// dzo=window.open('','_blank','top=200,left=200,width=300,height=300');
// htmlis='<ht' + 'ml><he' + 'ad><scri' + 'pt type=text/javascript> var curslides=0, thislag=0; function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdzfirst(); }, thislag); } else { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdznext(); }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div></bo' + 'dy></ht' + 'ml>';
// dzo.document.write(htmlis);
//}
}

}

function getdzlen() {
return eval('' + dzslides.length);
}

function getdzfirst() {
var iwhich=0;
dzcnt=1;
if (iwhich == 0) {
//alert(document.getElementById('target').outerHTML);
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('url(' + document.getElementById('target').outerHTML.split('url(')[1].split(')')[0] + ')', 'url(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}

function getdznext() {
var iwhich=dzcnt;
dzcnt++;
if (iwhich == 0) {
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('url(' + document.getElementById('target').outerHTML.split('url(')[1].split(')')[0] + ')', 'url(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}

function getdz(iwhich) {
if (iwhich == 0) {
//alert(document.getElementById('target').outerHTML);
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('URL(' + document.getElementById('target').outerHTML.split('URL(')[1].split(')')[0] + ')', 'URL(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}


setTimeout(function() {
if (elem) {
if (eval('' + dzslides.length) == 0) {
dzslides.push(elem.toDataURL());
}
} else if (document.getElementById('topcanvas')) {
if (eval('' + dzslides.length) == 0) {
dzslides.push(document.getElementById('topcanvas').toDataURL());
}
}
}, 5000);

... in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application.


Previous relevant Canvas Drag and Drop Primer Tutorial is shown below.

Canvas Drag and Drop Primer Tutorial

Canvas Drag and Drop Primer Tutorial

It's time. It really is. It's time to revisit the "User of Signature Signature" web application using the HTML5 canvas element to create and share graphical data, that we last dealt with when we presented Canvas Sharing via Email Hashtagging Tutorial.

Why? Well, we want to combine ...

  • canvas ... graphical data ... to start with ... with ...
  • drag and drop methodologies

... to add to the functionality of our "User of Signature Signature" web application. Today is the "proof of concept" day because there are two conflicting issues for us ...

  • scribble functionality on a canvas element ... is at odds with ...
  • the draggable=true attribute usage crucial to getting Drag and Drop methodologies to kick in

You may have noticed with the "User of Signature Signature" web application it starts ready to scribble on the canvas straight away. And do you think we can resist that, as we test it?! No way!

That same steadiness to that canvas, as it is set up to resist "internal movement" also stops it being draggable alas ... depending how you look at it.

And so we thought about this and came up with ...

  1. do the usual this useful link suggests about setting up the canvas element for Drag and Drop ...

    <canvas ondragstart="dragstart(event);" draggable=truefalse title="Ready for you to create your canvas 🎨 content above any signature 💳 panel ..." id="topcanvas" width=850 height=600 style="background-color:white; position: absolute; top:0; left:0; border-bottom:5px solid yellow; border-right:5px solid blue;"></canvas>

    ... except start the draggable attribute with value false ... and team this with ...
  2. new Drop Zone HTML element helpers ...

    <div id="target" ondrop="drop(event);" ondragover="dragover(event);" data-mt="0" style="vertical-align:bottom;display:block;background-color:transparent;">
    <button id=mydbut onclick=" document.getElementById('topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML='<span id=tarbut style=margin-top:' + document.getElementById('target').getAttribute('data-mt') + 'px;>Drop Zone</span>'; ">Drop Zone for Canvas After Click</button>
    </div>
    <span id=starbut></span>

    ... and ...
  3. programmatically controlled HTML button element click logic ...

    var lastdrop='';

    function dragstart(ev) {
    ev.dataTransfer.setData("text/plain", document.getElementById('topcanvas').toDataURL());
    }

    function dragover(ev) {
    ev.preventDefault();
    }

    function drop(ev) {
    ev.target.innerHTML = ev.dataTransfer.getData("text");
    lastdrop=ev.target.innerHTML;
    document.getElementById('topcanvas').setAttribute('draggable','false');
    document.getElementById('target').innerHTML='<button id=tarbut onclick=\" document.getElementById(' + "'topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML=''; document.getElementById('starbut').style.marginTop='-50px'; document.getElementById('starbut').style.zIndex='-50'; document.getElementById('starbut').innerHTML='Drop Zone'" + '; \">Drop Zone for Canvas After Click</button>';
    document.getElementById('starbut').innerHTML='';
    var rectis=document.getElementById('target').getBoundingClientRect();
    if (document.getElementById('mydbut')) {
    rectis=document.getElementById('mydbut').getBoundingClientRect();
    } else if (document.getElementById('tarbut')) {
    rectis=document.getElementById('tarbut').getBoundingClientRect();
    }
    document.getElementById('target').setAttribute('data-mt','' + eval(-30 + eval('' + rectis.width)));
    document.getElementById('target').style.height='' + rectis.width + 'px';
    document.getElementById('target').style.backgroundImage='URL(' + lastdrop + ')';
    document.getElementById('target').style.backgroundSize='contain';
    document.getElementById('target').style.backgroundRepeat='no-repeat';
    if (document.getElementById('topdragstyle').innerHTML == '') {
    document.getElementById('tarbut').style.marginTop='' + eval(-30 + eval('' + rectis.width)) + 'px';
    //var rectistb=document.getElementById('tarbut').getBoundingClientRect();
    //document.getElementById('topdragstyle').innerHTML='<style> #tarbut { position:absolute; left:' + rectistb.left + 'px; top:' + eval(eval(0 * eval(-30 + eval('' + rectis.width))) + eval('' + rectistb.top)) + 'px; margin-top:0px !important; } </style>';
    }
    }

    ... to toggle between the modes of use

... in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application.

Like we say, "proof of concept" (and mobile platforms need more thinking, so see Stop Press below), today, ahead of "usefulness" to come!

Stop Press

We know (again, via this useful link) from simple canvas scenarios Drag and Drop can work for mobile, but we couldn't get it to work for our canvas used for scribbling and other annotation purposes. And so, in our further changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application we got some "onclick" event replacement code working for mobile platforms involving less work for the user. Some may see this as an enhancement, but personally, we're awfully fond of Drag and Drop even with the slightly greater bother. We just think it helps a user clarify in their minds what they are doing, and not necessarily blithely clicking away with impunity.


Previous relevant Canvas Sharing via Email Hashtagging Tutorial is shown below.

Canvas Sharing via Email Hashtagging Tutorial

Canvas Sharing via Email Hashtagging Tutorial

We're regular "tweakers" (for example, with Canvas via Image Web Share API Photo Postcard Tutorial) of what we call the ...

  • canvas ... based ...
  • signatures ... possible ...
  • scribble ... or ...
  • discrete click ... creating ...
  • annotation ... friendly ...

... "graphics sharing" web application "User of Signature Signature". And with us, more and more ...

  • decoupling our PHP mail sharing conduits ... in favour of ...
  • "a" link "mailto:" email (and sometimes "sms:" SMS) communication conduits ... now making use of ...
  • hashtagging ... parts to URLs used ...

... because this methodology has fewer data limits, it being a "clientside" only (for the main part) means of sharing data. Today, we're looking into this.

Happily, for this work, "the lynchpin" is some "signature_signature.js" external Javascript, which is all that needs tweaking to make these changes.

Previously, we'd relied, more, on "signature_signature.php" PHP, which oversaw our previous interfacing to the mail server up at the RJM Programming domain via the PHP mail function.

Now, the user sends their own emails with their own email client software, and emails contain one long body URL string where ...

  • canvas ... element's ...
  • [canvasElement].toDataURL('image/jpeg', 0.7) ... means by which a ...
  • data URI representation of the graphical data

... can be hashtagged into that long body URL string. If the email recipient clicks this link, our external Javascript ...

Reads such incoming data via location.hash ...

var azx=null, wowowo=null;
var imgbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('img=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('img=')[1].split('&')[0]).replace(/\ /g,'+') : '';
var passedsubject=location.search.split('subject=')[1] ? decodeURIComponent(location.search.split('subject=')[1].split('&')[0]) : '';
And loaded into canvas and new window ...

setTimeout(function(){
if (imgbih != '') {
//alert(imgbih);
if (top.document.getElementById('topcanvas')) {
var xanimg=document.createElement('img');
xanimg.onload=function() {
var tc=top.document.getElementById('topcanvas').getContext('2d');
tc.drawImage(xanimg, 0, 0);
};
xanimg.src=imgbih;
}
var wowowo=window.open('','_blank','top=100,left=100,width=800,height=800');
wowowo.document.write('<html><body style="background:URL(' + "'" + imgbih + "'" + ');background-repeat:no-repeat;"></body></html>');
imgbih='';
if (passedsubject != '') {
wowowo.document.title='' + passedsubject;
}
}
}, 5000);
Before submitting form method=POST to signature_signature.php check out form fields ...

if (('' + document.getElementById('emailto').value).indexOf('@') != -1 && ('' + document.getElementById('contentto').value).indexOf('data:image/jpeg;base64,') != -1) {
azx=document.createElement("a");
document.body.appendChild(azx);
azx.style = "display: none";
azx.href = 'mailto:' + document.getElementById('emailto').value + ('?subject=' + encodeURIComponent(document.getElementById('subject').value)).replace(/subject\=$/g,'subject=My%20Canvas%20Creation') + '&body=' + encodeURIComponent(top.document.URL.split('?')[0].split('#')[0] + ('?subject=' + encodeURIComponent(document.getElementById('subject').value)).replace(/subject\=$/g,'subject=My%20Canvas%20Creation') + '#img=' + encodeURIComponent(document.getElementById('contentto').value));
azx.click();
} else {

document.getElementById('myform').submit();
}

... to enact fairly simple changes for more stable sharing and collaboration arrangements via a tweaked external Javascript signature_signature.js code used by our signature use in canvas supervisory web application.


Previous relevant Canvas via Image Web Share API Photo Postcard Tutorial is shown below.

Canvas via Image Web Share API Photo Postcard Tutorial

Canvas via Image Web Share API Photo Postcard Tutorial

We discovered, further to yesterday's Canvas via Image Web Share API Personalization Tutorial, out and about, with an iPhone, that ...

Image Input Mode Image Sizing Modus Operandi
URL Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)
Browsing Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)

... of Images to Canvas No Clicking Form Signature Tutorial times was worth refocussing on, regarding the combination of ...

  • Browsing ... Contain (canvas) ... option above ... and ...
  • Take Photo ... mobile device option ... which, if followed through on, is more than likely to leave ...
  • On the right hand side of the canvas there will be white space suitable to scribble a message to complete your ...
  • Postcard ... which you can optionally ...
  • Share

... and if you are using the https: protocol with the Safari and Google Chrome web browser(s), we can help out more with that last step, being able to make a sharing button show the word "Postcard" ...


function checkonyellow() {
var ibuta=[], jbuta=0;
if (safariblurb != '') {
if (document.getElementById('shareurl')) {
if (document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
} else if (parent.document.getElementById('shareurl')) {
if (parent.document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=parent.document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
} else if (top.document.getElementById('shareurl')) {
if (top.document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=top.document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
}
}
}

... to make the procedure ...

Postcard via smart device Camera advice ...

1. Touch browsing Choose File button that has just appeared.
2. Choose to Take Photo.
3. Consider any zooming in or selfie modes of use in Photo mode.
4. Take photo with Camera app white button, choosing Use Photo or Retake, as required.
5. Scribble any optional messaging in white area to right, or use other discreet touch functionalities.
6. Optionally email off to recipient (as textbox below goes yellow, click email sender button to its left for easier usage).

... more obvious, via the changed external Javascript signature_signature.js code used by our signature use in canvas supervisory web application.


Previous relevant Canvas via Image Web Share API Personalization Tutorial is shown below.

Canvas via Image Web Share API Tutorial

Canvas via Image Web Share API Personalization Tutorial

Yesterday's Canvas via Imge Web Share API Tutorial's Web Share API (so far, on Safari and Google Chrome web browser(s)) image (via canvas) sharing functionality did not have enough privacy for the user, as far as we were concerned. We're not going in for some two factor authentication level of privacy here, and so we've opted for the use of the word "Personalization" in today's blog posting title.

What we've done here is incorporate into the image name (a form of) the IP address of the sender of the image, which the receiver is using (for that extra privacy) as they click the email link, that now involves (a form of) this IP address in its formation.

No surprises here, that we'd have to involve our changed PHP helper signature_signature.php "specialist signature PHP helper" more, getting this solution together.

And no surprises here, that we'd try to involve the web server's disk areas outside the scope of its (Apache) Document Root hierarchy. That step, alone, is important to force a curious user, to work their way through an inhouse PHP webpage URL of some sort, in order to extract that ...

  • image data in the form of a web server file ... to a ...
  • public domain webpage image in an HTML img tag where its src attribute is populated via that web server file's equivalent base64 data URI ...
    <?php

    echo "<html><body><img title='" . $thename . '' . "' src='data:image/webp;base64," . base64_encode(file_get_contents(DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . $thename . '')) . "'></img>" . $therest . "</body></html>";

    ?>

Am sure you are not surprised that today's external Javascript web_share_api_test.js Web Share API specialist needed to change ...


function tctowebp() {
var ourcanvc='';
var canvs=[];
if (window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0) {
canvs=document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
if (eval('' + canvs.length) == 0 && window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
}
if (eval('' + canvs.length) > 0) {
ourcanvc=canvs[0].toDataURL('image/webp');
if (lastcanvc != ' ') {
if (lastcanvc != ourcanvc) {
lastcanvc=ourcanvc;
canvc=ourcanvc;
czhr = new XMLHttpRequest();
czform = new FormData();
czform.append('webpit', canvc);
if (newidea.indexOf('canvas.webp') == -1) { // eg. MAMP ... http://localhost:8888/signature_signature.php?mycanvasimage=0000__1
czform.append('websecurepit', '' + myipadwsi);
}

czhr.onreadystatechange=cstateChanged;
czhr.open('post', './signature_signature.php', true);
czhr.send(czform);
} else {
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='#f9f9f9';
}
}
}
lastcanvc=ourcanvc;
}
return ourcanvc;
}

... as well, regarding its incorporation of Web Share API logic into our inhouse User of Signature canvas based web application, best tried, these days, in the Safari and Google Chrome web browser(s) using https: (secure) protocol?!


Previous relevant Canvas via Imge Web Share API Tutorial is shown below.

Canvas via Image Web Share API Tutorial

Canvas via Imge Web Share API Tutorial

We're on the lookout for ways to improve the sharing and collaboration functionality of yesterday's Images to Canvas No Clicking Form Signature Tutorial's "User of Signature" canvas using web application. We're calling on ..

  • Safari and Google Chrome web browser(s) ... for the moment, supporting the ...
  • Web Share API ... in order to ...
  • Fill out email (or other communication idea) attachment links or documents ... that ...
  • Prepare the message before you, the user, flesh out the message as you send it off to the recipient

... a more guaranteed way, these days, because there are more and more ways restrictions may stop you doing similar functionality with a mail server arrangement on your public domain website, where we use the Exim mail server, at the RJM Programming domain.

As far as data goes for this, we also call on our changed PHP helper signature_signature.php to create a webp image file for sharing ...

<?php

if (isset($_POST['webpit'])) {
file_put_contents($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'canvas.webp', base64_decode(explode(";base64,", str_replace(' ','+',urldecode($_POST['webpit'])))[1]));
exit;
}

?>

... as applicable. The modus operandi for this back at today's changed web_share_api_test.js external Javascript Web Share API specialist, uses Ajax methodologies ...


var canvc=' ', lastcanvc=' ', czhr=null, czform=null;

function cstateChanged() {
if (czhr.readyState == 4) {
if (czhr.status == 200) {
if (1 == 2) { alert('Did it'); }
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='yellow';
}
}
}
}

function tctowebp() {
var ourcanvc='';
var canvs=[];
if (window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0) {
canvs=document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
if (eval('' + canvs.length) == 0 && window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
}
if (eval('' + canvs.length) > 0) {
ourcanvc=canvs[0].toDataURL('image/webp');
if (lastcanvc != ' ') {
if (lastcanvc != ourcanvc) {
lastcanvc=ourcanvc;
canvc=ourcanvc;
czhr = new XMLHttpRequest();
czform = new FormData();
czform.append('webpit', canvc);
czhr.onreadystatechange=cstateChanged;
czhr.open('post', './signature_signature.php', true);
czhr.send(czform);
} else {
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='#f9f9f9';
}
}
}
lastcanvc=ourcanvc;
}
return ourcanvc;
}

... and set in motion via a couple of different parental calls such as ...


<script type='text/javascript' src='/web_share_api_test.js?canvasshare=as_necessary9876' defer></script>

... in ...

... for cuter Safari and Google Chrome web browser sharing and collaboration work.


Previous relevant Images to Canvas No Clicking Form Signature Tutorial is shown below.

Images to Canvas No Clicking Form Signature Tutorial

Images to Canvas No Clicking Form Signature Tutorial

Surprisingly, it's been more than a year since we changed anything to do with our ...

  • canvas based ...
  • images and annotation ... accepting ...
  • scribble (ie. can handle signatures) ... perhaps ...
  • form filling helping web application

... (when we presented Canvas Graphics Editing in Zoomed Webpage Tutorial) and we found that in order to add six more such Annotation option helpers into the mix ...

Image Input Mode Image Sizing Modus Operandi
URL Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)
Browsing Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)

... we only needed to change the external Javascript signature_signature.js code used by our signature use in canvas supervisory web application so that when you can not ask so much of the user regarding image scaling, and avoid any canvas clicking that way, offering these six new annotation functionality options.


Previous relevant Canvas Graphics Editing in Zoomed Webpage Tutorial is shown below.

Canvas Graphics Editing in Zoomed Webpage Tutorial

Canvas Graphics Editing in Zoomed Webpage Tutorial

Software integration can be useful for a number of reasons ...

  • code reuse
  • modularisation of functionality ... and in our case ...
  • if you like coding via "intervention points" you will enjoy coding for software integration improvements

As you might imagine it is easier in terms of the fact you can change "both ends" of the software integration, if it is "inhouse", and luckily, today, our software integration is "inhouse".

When we presented the recent Keyboard Based Cursor Canvas Content Copy Tutorial it focussed our attention on an improvement we could make to that user_of_signature_signature.htm canvas image editing web application. We wanted to offer the software integration with this web application to be able to successfully handle a webpage that has been zoomed to zoom factor different to 1.0 (or 100%).

We noticed before today's very simple changes to external Javascript signature_signature.js code, graphic positioning on the canvas was wrong when the webpage had a zoom factor that is not 1.

Applying the canvas co-ordinate references through "the lens of" Javascript function zmb below ...


var zzfac=location.search.split('zoom=')[1] ? decodeURIComponent(location.search.split('zoom=')[1].split('&')[0]) : "";
if (('' + location.hash).indexOf('zoom=') != -1) { zzfac=decodeURIComponent(('' + location.hash).split('zoom=')[1].split(';')[0]); }

function zmb(inco) {
if (zzfac != '') {
if (zzfac.indexOf('%') != -1) {
return eval(eval(eval('' + inco) / eval('' + zzfac.replace('%', ''))) * 100.0);
} else {
return eval(eval('' + inco) / eval('' + zzfac));
}
}
return inco;
}

... used in the following example code below ...


topelem.addEventListener('mousedown', function(event) {
if (topin) {
topin.value='- -' + isScribble + ',' + lastx + ',' + lasty + ',' + x + ',' + y;
}
if (isScribble == 1) {
threebackpmore=twobackpmore; twobackpmore=lastpmore; lastpmore=pmore; pmore=' pxam1 ';
isScribble=2;
if (event.pageX || event.pageY) {
ppx=zmb(event.pageX) - elemLeft;
ppy=zmb(event.pageY) - elemTop;
} else {
ppx=zmb(event.clientX) - elemLeft;
ppy=zmb(event.clientY) - elemTop;
}
if (pdgebi(topdcmcheck('dcm'))) {
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('text') == 0) {
//parent.document.title='here3 ' + tx + ',' + ty;
if (1 == 2 && tlx < 0 && tly < 0) {
tlx=ppx;
tly=ppy;
}
if (tx >= 0 && ty >= 0) {
prevtx=tx;
prevty=ty;
trotis=eval(((Math.atan2((ppy - prevty), (ppx - prevtx)) * 180.0 / Math.PI) + 360) % 360);
if (pdgebi('trotation')) {
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('textseq') == 0) {
jsrectbits[0]=prevtx;
jsrectbits[1]=prevty;
jsrectbits[2]=ppx;
jsrectbits[3]=ppy;
if (Math.abs(prevty - ppy) > Math.abs(prevtx - ppx)) {
trotis=eval(((Math.atan2((prevtx - ppx), (ppy - prevty)) * 180.0 / Math.PI) + 360) % 360);
}
}
parent.document.getElementById('trotation').value=Math.floor(trotis);
ppx=prevtx;
ppy=prevty;
prevtx=-1;
prevty=-1;
exceptwhen=endtrue(''); //true;
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('textseq') == -1) parent.document.getElementById(topdcmcheck('dcm')).value = 'Text';
}
//alert(trotis);
}
tx=ppx;
ty=ppy;
} else {
tx=ppx;
ty=ppy;
prevtx=-1;
prevty=-1;
}
} else {
tx=ppx;
ty=ppy;
prevtx=-1;
prevty=-1;
}
lastx=0; // new to scribble
lasty=0;
x=0;
y=0;
}
});

... we now have reasonable graphic positioning for the canvas when its webpage window has a non 1.0 zoom factor applied.


Previous relevant Keyboard Based Cursor Canvas Content Copy Tutorial is shown below.

Keyboard Based Cursor Canvas Content Copy Tutorial

Keyboard Based Cursor Canvas Content Copy Tutorial

You may have noticed with yesterday's Keyboard Based Cursor Share Content Copy Tutorial, crucial to the sharing code, was the use of the incredible HTML5 introduced canvas element, helped by that middleperson link to those public email and SMS sharing conduits, overseeing the great random Lorem Picsum background images and user created emoji and/or text initial annotations.

And this got us wondering whether there was a private (ie. inhouse) web application of the past that could further value add as another optional middleperson tool for (user) personalization (of the content) purposes. And yes, do you remember the canvas work involved in the featured web application of Emoji Borders and Backgrounds Canvas Annotation Tutorial?

It uses an interesting approach with the transference of data across from a canvas in one application to the canvas in that user_of_signature_signature.htm web application. Once our changed body_mouse_deepdive.html's "parent" web application has organized its canvas contents, it's just a matter of ...


var awo=null;
awo=window.open('./user_of_signature_signature.htm','_blank','top=100,left=100,width=800,height=800');

for the "child" canvas annotator to effectively "suck up" the canvas data into its canvas via ...


var cancont='';
var elemode=location.search.split('elemode=')[1] ? (location.search.split('elemode=')[1].split('&')[0]) : "canvas";

if (('' + window.opener).replace('null','') != '') {
var cans=window.opener.document.getElementsByTagName(elemode);
if (cans.length > 0) {
if (elemode == 'img') {
cancont=cans[0].src;
} else {
cancont=cans[0].toDataURL('image/png');
}
if (cancontw < 0 && canconth < 0) { cancontw=cans[0].width; canconth=cans[0].height; } } }

... possible because both "parent" and "child" exist on the same RJM Programming domain (web server).

Once there, another woooooorrrrrlllddd of image manipulations, via canvas, can await the user, perhaps ready for ongoing sharing possibilities with that "child".


Previous relevant Keyboard Based Cursor Share Content Copy Tutorial is shown below.

Keyboard Based Cursor Share Content Copy Tutorial

Keyboard Based Cursor Share Content Copy Tutorial

Yesterday's Keyboard Based Cursor Personalized Content Copy Tutorial has probably reached a point where some sharing mechanism is apt, for accountability and usefulness purposes, at the very least, regarding our recent web application allowing for foreground content on top of a random Lorem Picsum background image.

We've been helped out greatly by javascript - Can I take a screenshot from the clipboard? - Stack Overflow and html - How to render a blob on a canvas element? - Stack Overflow in the following new relevant Javascript code ...


function renderNoImage(canvas, blob) { // thanks to https://stackoverflow.com/questions/38004917/how-to-render-a-blob-on-a-canvas-element
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // This is important. If you are not using the blob, you should release it if you don't want to reuse it. It's good for memory.
ctx.drawImage(event.target, 0, 0)
img.style.display='none';
img.style.zIndex='-123';
};
img.src = URL.createObjectURL(blob);
}


function renderImage(canvas, blob) { // thanks to https://stackoverflow.com/questions/38004917/how-to-render-a-blob-on-a-canvas-element
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // This is important. If you are not using the blob, you should release it if you don't want to reuse it. It's good for memory.
ctx.drawImage(event.target, 0, 0)
};
img.src = URL.createObjectURL(blob);
}


addEventListener("paste", ev => { // thanks to https://stackoverflow.com/questions/55559432/can-i-take-a-screenshot-from-the-clipboard
for(const item of ev.clipboardData.items) { /// Clipboard may contain multiple elements of different type -- text, image, etc
if(item.type.startsWith("image/")) { /// We are only interested in clipboard data that is an image
if (emailee == '') {
emailee=prompt('Your clipboard has a useful image you could share the image with. Optionally, please enter an email address or SMS number to share with.');
if (emailee == null) { emailee=''; }
}
if (emailee.indexOf('@') != -1) {
document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="position:absolute;z-index:98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('aemail').href=document.getElementById('aemail').href.replace(':?', ':' + emailee + '?');
document.getElementById('aemail').click();
document.getElementById('mycanvas').innerHTML='';
} else if (emailee != '' && emailee.replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '') {
document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="position:absolute;z-index:98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('asms').href=document.getElementById('asms').href.replace(':&', ':' + emailee + '&');
document.getElementById('asms').click();
document.getElementById('mycanvas').innerHTML='';
} else {
//document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="display:none;position:absolute;z-index:-98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderNoImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('mycanvas').innerHTML='';
}
}
}
});

... which is explained to the user in the following way ...

Share with email or SMS happens via screenshots (macOS control-command-shift-3 or Windows Prnt-Scrn) followed by File menu Paste option causing chance to share after which File menu Undo can remove pasted part as required.

... in the changed "proof of concept" body_mouse_deepdive.html live run.


Previous relevant Keyboard Based Cursor Personalized Content Copy Tutorial is shown below.

Keyboard Based Cursor Personalized Content Copy Tutorial

Keyboard Based Cursor Personalized Content Copy Tutorial

On top of yesterday's Keyboard Based Cursor Content Copy Tutorial we wanted to offer the user the chance for them to tailor their foreground content on top of the Lorem Picsum background image.

The user can enter this via the keyboard because there is an HTML div contenteditable=true pallette to work with ...


<div id=mydiv onfocus='setTimeout(alte, 1000);' ondblclick="this.innerHTML='';" onclick='stamp(event);' title='Click and type and see the cursor change as you use Alt or Shift or Control that can be reset via Caps Lock with a click copying cursor into content ... RJM Programming ... May, 2022 ... thanks to https://picsum.photos/ Lorem Picsum You can change emojis if you know the \002602 form of UTF-16 (hex) entry (our example is umbrella and if you express it as \\002602 that leaves cursor just as your emoji). Double click existant foreground emoji element to clear all the foreground emoji elements.' spellcheck='false' contenteditable=true style='width:100vw;height:100%;color:transparent;text-color:transparent;' onkeypress=kpcursorlook(event); onkeydown=cursorlook(event); onmousemove=xycursorlook(event); ontouchmove=xycursorlook(event);></div>

It's "title" attribute now talks about two new pieces of functionality, those being ...

  • You can change emojis if you know the \002602 form of UTF-16 (hex) entry (our example is umbrella and if you express it as \\002602 that leaves cursor just as your emoji).
  • Double click existant foreground emoji element to clear all the foreground emoji elements.

The choice of keyboard input methodology also suits CSS "content" property definitions as one backslash followed by a hexidecimal value left padded with zeroes to a length of six. See us doing this below for an umbrella emoji (which could also be expressed in an HTML hexadecimal entity way &#x2602; ☂ the information for which you can find at Unicode Character 'UMBRELLA' (U+2602) we normally get to by entering "U+2602" at a web browser address bar, the knowledge for which we arrive at via entering "umbrella emojipedia" and moving down the top link's webpage to find the "U+2602") ...


<style> #myumbrella::after { content: '\002602'; } </style>

... that feeds into some new "onkeypress" keyboard event logic ...


function kpcursorlook(evt) {
var atofa=['a','b','c','d','e','f'];
var rgbit='';
if (evt.keyCode == 92) {
if (newu.length == 1) { gro=true; } else { gro=false; }
newu=("\\").substring(0,1);
} else if (evt.keyCode >= 48 && evt.keyCode <= 57) {
newu+=('' + eval(-48 + evt.keyCode));
} else if (evt.keyCode >= 65 && evt.keyCode <= 70) {
newu+=('' + atofa[eval(-65 + evt.keyCode)]);
} else if (evt.keyCode >= 97 && evt.keyCode <= 102) {
newu+=('' + atofa[eval(-97 + evt.keyCode)]);
}
if (newu.length == 7) {
if (lastcursor.indexOf(("\\\\").substring(0,2)) != -1) {
rgbit=lastcursor.split(("\\\\").substring(0,2))[1].substring(6);
lastcursor=lastcursor.split(("\\\\").substring(0,2))[0] + ("\\").substring(0,1) + newu + rgbit;
document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
} else if (lastcursor.indexOf(("\\").substring(0,1)) != -1) {
rgbit=lastcursor.split(("\\").substring(0,1))[1].substring(6);
lastcursor=lastcursor.split(("\\").substring(0,1))[0] + newu + rgbit;
if (gro) {
lastcursor=lastcursor.replace('>Shift/Alt/Ctrl', '>').replace('>Shift', '>').replace('>Alt', '>').replace('>Ctrl', '>').replace(" width='126'", " width='20'").replace(" width='66'", " width='20'").replace(" height='48'", " height='20'").replace('0 0 100 100', '0 0 20 20');
}
document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
}
}
// console.log(evt.keyCode);
}

... in the changed "proof of concept" body_mouse_deepdive.html live run.

Stop Press

Two more "title" attribute pieces of functionality are now ...

  • With complex emoji entries do not zero leftpad until your last simple entity eg. \\1F6A3\200D\2640\00FE0F could define a Woman Rowing complex emoji.
  • Optionally make emojis bigger after \ with + two time multipliers.

The choice of keyboard input methodology also suits CSS "content" property definitions as one backslash followed by "first off" sets of HTML entities they should not left zero pad in their entry, followed by a last HTML entity hexidecimal value left padded with zeroes to a length of six to finish up with. See us doing this below for a woman rowing emoji (which could also be expressed in an HTML hexadecimal entity way &#x1f6a3;&#x0200d;&#x02640;&#x0fe0f; the information for which you can find at 🚣‍♀️ Woman Rowing Boat Emoji we normally get to by entering "woman rowing emoji" at the web browser address bar) ...


<style> #myrowing::after { content: '\01f6a3\00200d\002640\00fe0f'; } </style>

... in the changed "proof of concept" body_mouse_deepdive.html live run.


Previous relevant Keyboard Based Cursor Content Copy Tutorial is shown below.

Keyboard Based Cursor Content Copy Tutorial

Keyboard Based Cursor Content Copy Tutorial

The "onkeydown" keyboard event can involve in its logic three mouse event (event object) property usages that caught our interest recently ...

... and we wanted to have "keyboard" meets "mouse" events, along with the brilliance of Lorem Picsum regarding background image randomosity and quality, working with HTML ...


<div id=mydiv onfocus='setTimeout(alte, 1000);' onclick='stamp(this);' title='Click and type and see the cursor change as you use Alt or Shift or Control that can be reset via Caps Lock with a click copying cursor into content ... RJM Programming ... May, 2022 ... thanks to https://picsum.photos/ Lorem Picsum' spellcheck='false' contenteditable=true style='width:100vw;height:100%;color:transparent;text-color:transparent;' data-onkeypress=xycursorlook(event); onkeydown=cursorlook(event); onmousemove=xycursorlook(event); ontouchmove=xycursorlook(event);></div>

... in a way we hadn't tried before that called on some inhouse cursor SVG creations (and so is, alas, just a non-mobile fully featured experience), in the sense that ...

  1. keyboard helps creating the "what" regarding content ...

    <script type='text/javascript'>
    var lastkeymodifier=''
    var lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function cursorlook(evt) {
    if (evt.altKey) {
    if (lastkeymodifier != 'alt') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,0,255,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Alt\\01f3d5</text></svg>\") 16 0, progress";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='alt';
    }
    } else if (evt.ctrlKey) {
    if (lastkeymodifier != 'ctrl') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Ctrl\\01f333</text></svg>\") 16 0, pointer";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='ctrl';
    }
    } else if (evt.shiftKey) {
    if (lastkeymodifier != 'shift') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(255,0,0,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Shift\\01f389</text></svg>\") 16 0, grab";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='shift';
    }
    } else {
    if (lastkeymodifier != '') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='';
    }
    }
    }
    </script>
  2. mouse "onmousemove" or "ontouchmove" helps with the "where" regarding content above ...

    <script type='text/javascript'>
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function xycursorlook(e) {
    e = e || window.event;
    e.preventDefault();

    if (e.touches) {
    if (e.touches[0].pageX) {
    pos3 = e.touches[0].pageX;
    pos4 = e.touches[0].pageY;
    } else {
    pos3 = e.touches[0].clientX;
    pos4 = e.touches[0].clientY;
    }
    //console.log('pos3=' + pos3 + ',pos4=' + pos4);
    } else if (e.clientX || e.clientY) {
    pos3 = e.clientX;
    pos4 = e.clientY;
    } else {
    pos3 = e.pageX;
    pos4 = e.pageY;
    }
    }
    </script>
  3. preparations for "onclick" way a cursor can be plonked into (real lasting) content ...

    <script type='text/javascript'>
    var lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function stamp(divo) {
    if (('' + pos3).indexOf('-') == -1) {
    if (allowable) {
    //if (allowed) {
    //allowed=false;
    //setTimeout(reseta, 2000);
    divo.innerHTML+='<div id=div' + subdiv + ' style="position:absolute;top:' + pos4 + 'px;left:' + pos3 + 'px;width:126px;height:48px;background-color:transparent;"></div><style> #div' + subdiv + ' { background:' + lastcursor.split(') ')[0] + ') no-repeat; } </style>';
    subdiv++;
    //console.log('lastpos4=' + lastpos4 + ' and pos4=' + pos4);
    //console.log('lastpos3=' + lastpos3 + ' and pos3=' + pos3);
    lastpos3=pos3;
    lastpos4=pos4;
    //}
    }
    }
    }
    </script>

... with the "proof of concept" body_mouse_deepdive.html live run.

If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.

Posted in Animation, eLearning, Event-Driven Programming, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a comment

Making of Canvas Drag and Drop Ephemeral Tutorial

Making of Canvas Drag and Drop Ephemeral Tutorial

Making of Canvas Drag and Drop Ephemeral Tutorial

Some recent tutorials called Canvas Drag and Drop Primer Tutorial and Canvas Drag and Drop Less Ephemeral Tutorial involved animated GIF presentations with slides created via the macOS Screenshot desktop application utility (ie. in /Applications/Utilities folder) we talked about with Screenshot macOS Utility Primer Tutorial previously. Three aspects of screenshotting, in order of difficulty, are of interest for matters beyond the ken of our usual command-control-shift-3 (99.99999% solution) …

  • drag and drop display
  • any display of cursor position
  • tailored cursor styling display

In the first drag and drop display case we’re interested in with those Ephemeral Tutorial presentations (as animated GIF slides) it’s the case that two people could achieve it, because one person runs out of fingers. We can simulate that second person being there via the use of the macOS Screenshot desktop application’s timed Capture modes of use, setting this off as a ten second delay, and being ready with the drag and drop at that correct time, as the software takes the appropriate screenshot.

Also useful to us was to check the Options menu’s Show Mouse Pointer option to satisfy that second option ideal screenshotting mode of operation above. We’ll leave you with two presentation animated GIF presentations enhanced by this macOS Screenshots magic!

… but it cannot help, itself, with a tailored cursor styling display, though (we’re not sure we weren’t dreaming, but) can assist in combination with command-control-shift-3 (as seen with Keyboard Based Cursor Image CSS Filter Tutorial) …


Previous relevant Screenshot macOS Utility Primer Tutorial is shown below.

Screenshot macOS Utility Primer Tutorial

Screenshot macOS Utility Primer Tutorial

We find using this macOS based MacBook Air (and ahead of that a Mac OS X based MacBook Pro), that the taking of screenshots is incredibly useful in helping explain goings on in I.T.. The Mac woooooorrrrrllllldddd has always been really good at this (and Windows PrtScrn key is hard to beat too), in a chronological sense for us, consisting of …

  1. years of Mac OS X based MacBook Pro use of Applications -> Utilities “Grab” desktop application to screenshot … great … but even better we cottoned onto …
  2. years of Mac OS X based MacBook Pro and macOS based MacBook Air use of “control-command-shift-3” to screenshot … and occasionally …
  3. Mac OS X or macOS Terminal application “screencapture” … and, today, back out to the GUI we want to show you …
  4. macOS based MacBook Air use of Applications -> Utilities “Screenshot” desktop application to screenshot

… and our verdict comparing old “Grab” to new “Screenshot”. “Screenshot” is quite definitive, as you’d expect, and can have you tailoring screen selection areas, and cute functionalities like “delayed takes” that make it worth a good look, and we’d say …

Every picturescreenshot is worth a thousand words!

If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.

Posted in Animation, eLearning, Operating System, Tutorials | Tagged , , , , , , , , , , | Leave a comment

Canvas Drag and Drop Even Less Ephemeral Tutorial

Canvas Drag and Drop Even Less Ephemeral Tutorial

Canvas Drag and Drop Even Less Ephemeral Tutorial

In yesterday’s Canvas Drag and Drop Less Ephemeral Tutorial

  • image slides … could go into …
  • data URI list … could be hashtagged … along with a …
  • delay argument … then, via user button click, sent via “a” “mailto:” link …
  • email to emailee … who on clicking the email body link …
  • sees that Animated GIF presentation

… via a new piece of RJM Programming HTML and Javascript, but not as a saveable media file. Today, we allow for saveable media outputs by interfacing to our inhouse Animated GIF Creator PHP web application.

Below are the aspects to this integration …

New static HTML

<input type=hidden value='' id='agshow'></input>
<input type=hidden value='' id='thegifnamegoeshere'></input> <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<iframe onload='setTimeout(agmaybe,3000);' id=ifconto name=ifconto src='/PHP/animegif/tutorial_to_animated_gif.php' style='width:100%;height:900px;display:BLOCK;'></iframe>
Tweaked static HTML

<button id=mydbut ontouchmove="dzoreopen=true;" oncontextmenu="dzoreopen=true;" ondblclick="dzoreopen=true;" onclick=" event.stopPropagation(); if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { drop(document.getElementById('target')); } else { document.getElementById('topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML='<span id=tarbut style=margin-top:' + document.getElementById('target').getAttribute('data-mt') + 'px;>' + dz + '</span>'; } ">Drop Zone for Canvas After Click</button>
New near document.body onload event code

function agmaybe() {
iois=document.getElementById('ifconto');
if (iois != null) {
//alert(13);
if (iois.src.indexOf('tutorial_to_animated_gif.php') != -1) {
agconto = (iois.contentWindow || iois.contentDocument);
//alert(23);
if (agconto != null) {
if (agconto.document) { agconto = agconto.document; }
//alert(33);
agconto.getElementById('delay').value='4000';
agconto.getElementById('stitle').value='My Animation ...';
agconto.getElementById('watermarkmode').value=' ';
var spans=agconto.getElementsByTagName('span');
for (var jspans=0; jspans
function agcheck() {
if (document.getElementById('agshow')) {
if (document.getElementById('agshow').value == 'y') {
document.getElementById('agshow').value='Y';
//document.getElementById('ifconto').style.display='block';
location.href='#ifconto';
}
}
}

setInterval(agcheck, 2000);
Popup window creation and renewal and featuring new Animated GIF creation 📹 emoji button

if (dzo == null) {
dzo=window.open('','_blank','top=200,left=' + eval(-300 + eval('' + screen.width)) + ',width=300,height=300');
if (popupemailfunction == '') {
popupemailfunction=' function doagdisplay() { window.opener.document.getElementById("agshow").value="y"; } function dopopupemail() { var huhe=prompt("Please enter email address to share Animated GIF style presentation with. Append a hashtagged delay between slides in milliseconds that is not 4000 optionally.", ""); if (huhe == null) { huhe=""; } if (huhe.indexOf("@") != -1) { if (huhe.indexOf("#") != -1) { document.getElementById("aemail").href=document.getElementById("aemail").href.replace(encodeURIComponent("delay=4000"),encodeURIComponent("delay=" + huhe.split("#")[1])); huhe=huhe.split("#")[0]; } document.getElementById("aemail").href=document.getElementById("aemail").href.replace(":?", ":" + huhe + "?"); document.getElementById("aemail").click(); } } ';
}
htmlis='<ht' + 'ml><he' + 'ad><title>Inhouse Animated GIF style presentation ...</title><scri' + 'pt type=text/javascript> var dataih="", oneround=false, curslides=0, thislag=0; ' + popupemailfunction + ' function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { if (!oneround) { oneround=true; document.getElementById("semail").style.display="inline-block"; document.getElementById("sag").style.display="inline-block"; } setTimeout(function() { dataih=window.opener.getdzfirst(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } else { setTimeout(function() { dataih=window.opener.getdznext(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div><br><button title="Share via Email" id=semail style=display:none; onclick="dopopupemail();">&#128231;</button> <button title="Create Animated GIF or Video" id=sag style=display:none; onclick="doagdisplay();">&#128249;</button><a target=top id=aemail style=display:none; href="mailto:?subject=Animated%20GIF%20presentation&body=' + encodeURIComponent('https://www.rjmprogramming.com.au/htmlemail.html?delay=4000#') + '">Email</a></bo' + 'dy></ht' + 'ml>';
dzo.document.write(htmlis);
dzo.document.title='Inhouse Animated GIF style presentation ...';
} else if (dzo.closed) {
if (dzoreopen) {
dzo=window.open('','_blank','top=200,left=' + eval(-300 + eval('' + screen.width)) + ',width=300,height=300');
htmlis='<ht' + 'ml><he' + 'ad><title>Inhouse Animated GIF style presentation ...</title><scri' + 'pt type=text/javascript> var dataih="", oneround=false, curslides=0, thislag=0; ' + popupemailfunction + ' function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { if (!oneround) { oneround=true; document.getElementById("semail").style.display="inline-block"; document.getElementById("sag").style.display="inline-block"; } setTimeout(function() { dataih=window.opener.getdzfirst(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } else { setTimeout(function() { dataih=window.opener.getdznext(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div><br><button title="Share via Email" id=semail style=display:none; onclick="dopopupemail();">📧</button> <button title="Create Animated GIF or Video" id=sag style=display:none; onclick="doagdisplay();">📹</button><a target=top id=aemail style=display:none; href="mailto:?subject=Animated%20GIF%20presentation&body=' + encodeURIComponent('https://www.rjmprogramming.com.au/htmlemail.html?delay=4000#') + '">Email</a></bo' + 'dy></ht' + 'ml>';
dzo.document.write(htmlis);
}
} else {
dzo.focus();
}

Javascript global variables of relevance

var lastdrop='', lastelem=null, wasmt='0px', popupemailfunction='', agconto=null, agidis='slideshow', agiscnt=1, iois=null, dzoreopen=false;
New event logic to allow for popup window reopening

document.getElementById('target').innerHTML=prefixingih + '<button ' + opsty + ' id=tarbut ontouchmove=\"dzoreopen=true;\" oncontextmenu=\"dzoreopen=true;\" onclick=\" event.stopPropagation(); if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { drop(document.getElementById(' + "'target'" + ')); } else { document.getElementById(' + "'topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML=''; if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { document.getElementById('starbut').style.zIndex='50'; } else { document.getElementById('starbut').style.marginTop='-50px'; document.getElementById('starbut').style.zIndex='-50'; } document.getElementById('starbut').innerHTML='" + dz + "'" + '; } \">' + dz + ' for Canvas After Click</button>';
Add a slide to Animated GIF creator

if (agconto) {
//alert('3 ' + agidis);
agconto.getElementById(agidis).focus();
agconto.getElementById(agidis).value=lastdrop;
//alert(56);
//iois.maybemore(lastdrop, 'slideshow' + agiscnt, agconto.getElementById(agidis));
agconto.getElementById(agidis).blur();
//alert(256);
agiscnt++;
//alert('2:' + agiscnt);
agidis='slideshow' + agiscnt;
//agconto.getElementById(agidis).focus();
window.scrollTo(0,0);
//setTimeout(function() { agconto.getElementById(agidis).focus(); }, 6000);
}

… in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application.


Previous relevant Canvas Drag and Drop Less Ephemeral Tutorial is shown below.

Canvas Drag and Drop Less Ephemeral Tutorial

Canvas Drag and Drop Less Ephemeral Tutorial

We’re bucking the trend, today, “offering less” but “producing more”, we’re hoping, on top of the progress of yesterday’s Canvas Drag and Drop Ephemeral Tutorial ….

  • adding totaking from an “internal use only” level of ephemeralness which only offered an individual user view of canvas based Animated GIF style slide creation work as an Animated GIF presentation in their individually presented popup window … to …
  • adding to above a new 📧 Email emoji sharing (and “caring”) button within that popup window presentation … that if clicked …
    1. prompts the user as per …

      var huhe=prompt("Please enter email address to share Animated GIF style presentation with. Append a hashtagged delay between slides in milliseconds that is not 4000 optionally.", "");
    2. allows an “a” “mailto:” link’s “href” attribute reflect any hashtagged delay you specified …

      if (huhe.indexOf("#") != -1) {
      document.getElementById("aemail").href=document.getElementById("aemail").href.replace(encodeURIComponent("delay=4000"),encodeURIComponent("delay=" + huhe.split("#")[1])); huhe=huhe.split("#")[0];
      }
    3. allows an “a” “mailto:” link’s “href” attribute reflect any emailee you specified …

      if (huhe.indexOf("@") != -1) {
      document.getElementById("aemail").href=document.getElementById("aemail").href.replace(":?", ":" + huhe + "?"); document.getElementById("aemail").click();
      }
    4. and if all ritchy ditch …

      if (huhe.indexOf("@") != -1) {
      document.getElementById("aemail").click();
      }
    5. clicks the “a” “mailto:” link, passing control over to your default email client software to present you with an email body presentation webpage by which you can click a Send icon to send that largely hashtagged data email to an email recipient (ie. emailee)

… in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application, helped out by ever more changing helperer outerer htmlemail.html (just HTML and Javascript and hashtagging) of recent times, gradually replacing work that used to be a PHP thaing via emailhtml.php associated with RJM Programming domain’s email server fed by PHP mail command based email messages, now more in favour of hashtagging methodologies using “a” “mailto:” links, where we are only talking “clientside” with the logic, and the data length restrictions are not as severe.

Today’s animated GIF presentation, hence, is recreatable

Back to top

Did you know?

When it comes to email (and SMS for that matter) creation logic, occasionally, we find, you need to double encode … we kid you not! We needed to today, as you see below …


var popupemailfunction='';

if (popupemailfunction == '') {
popupemailfunction=' function dopopupemail() { var huhe=prompt("Please enter email address to share Animated GIF style presentation with. Append a hashtagged delay between slides in milliseconds that is not 4000 optionally.", ""); if (huhe == null) { huhe=""; } if (huhe.indexOf("@") != -1) { if (huhe.indexOf("#") != -1) { document.getElementById("aemail").href=document.getElementById("aemail").href.replace(encodeURIComponent("delay=4000"),encodeURIComponent("delay=" + huhe.split("#")[1])); huhe=huhe.split("#")[0]; } document.getElementById("aemail").href=document.getElementById("aemail").href.replace(":?", ":" + huhe + "?"); document.getElementById("aemail").click(); } } ';
}
htmlis='<ht' + 'ml><he' + 'ad><title>Inhouse Animated GIF style presentation ...</title><scri' + 'pt type=text/javascript> var dataih="", oneround=false, curslides=0, thislag=0; ' + popupemailfunction + ' function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { if (!oneround) { oneround=true; document.getElementById("semail").style.display="block"; } setTimeout(function() { dataih=window.opener.getdzfirst(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } else { setTimeout(function() { dataih=window.opener.getdznext(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div><br><button title="Share via Email" id=semail style=display:none; onclick="dopopupemail();">&#128231;</button><a target=top id=aemail style=display:none; href="mailto:?subject=Animated%20GIF%20presentation&body=' + encodeURIComponent('https://www.rjmprogramming.com.au/htmlemail.html?delay=4000#') + '">Email</a></bo' + 'dy></ht' + 'ml>';

The pattern of when (we need to do this) remains a bit mysterious to us, but if we ever nail down the pattern, am sure you’ll hear about it! The good side to solving via “double encoding” means is that you can easily see where the wall needs to be repaired straight in front of you … in real time?!


Previous relevant Canvas Drag and Drop Ephemeral Tutorial is shown below.

Canvas Drag and Drop Ephemeral Tutorial

Canvas Drag and Drop Ephemeral Tutorial

Onto yesterday’s Canvas Drag and Drop Primer Tutorial‘s …

  • proof of concept feel to being able to drag and drop via the canvas element … today we have …
  • proof of concept ideas regarding future sharing and collaboration aspects to this Drag and Drop where we start with our “ephemeral” inhouse popup window version of what we hope to offer, in future, for users to share with email recipients

And the other progress was another “drag” friend for the canvas element, allowing that textarea element (now with ondragstart=”dragstart(event);” draggable=true attribution) down the bottom right also be “draggable” (to create the textarea wording on a white backgrounded slide “Drop Zone” result when dragged there) for our …

  • “Drop Zone” for non-mobile … and …
  • “Click Zone” for mobile

… platforms. The drag and drop functionality related global variables now go …


var dz='Drop Zone';
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
dz='Click Zone';
}
var dzslides=[], dzo=null, dzcnt=0;
var lastdrop='', lastelem=null, wasmt='0px';

… and the Drag and Drop logic now goes


function dragstart(ev) {
lastelem=ev.target;
if (('' + ev.target.outerHTML).indexOf('<textarea') == 0) {
ev.dataTransfer.setData("text/plain", ev.target.value);
} else {

ev.dataTransfer.setData("text/plain", document.getElementById('topcanvas').toDataURL());
}
}

function dragover(ev) {
ev.preventDefault();
}

function drop(ev) {
var prefixingih='', mtdone=false, opsty='';
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
if (('' + lastelem.outerHTML).indexOf('<textarea') == 0) {
lastdrop=lastelem.value;
while (lastdrop.indexOf(String.fromCharCode(10)) != -1) {
lastdrop=lastdrop.replace(String.fromCharCode(10),'<br>');
}
prefixingih='<span style=background-color:white;>' + lastdrop + '</span><br><br>';
opsty=' style="opacity:0.3;" ';
lastdrop=dzslides[0];
document.getElementById('target').innerHTML = dzslides[0];
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop='0px';
}
} else {

document.getElementById('target').innerHTML = document.getElementById('topcanvas').toDataURL();
lastdrop=document.getElementById('target').innerHTML;
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop=wasmt;
}
}
} else {
if (('' + lastelem.outerHTML).indexOf('<textarea') == 0) {
lastdrop=lastelem.value;
while (lastdrop.indexOf(String.fromCharCode(10)) != -1) {
lastdrop=lastdrop.replace(String.fromCharCode(10),'<br>');
}
prefixingih='<span style=background-color:white;>' + lastdrop + '</span><br><br>';
opsty=' style="opacity:0.3;" ';
lastdrop=dzslides[0];
document.getElementById('target').innerHTML = dzslides[0];
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop='0px';
}
} else {
ev.target.innerHTML = ev.dataTransfer.getData("text");
lastdrop=ev.target.innerHTML;
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop=wasmt;
}
}
}

document.getElementById('topcanvas').setAttribute('draggable','false');
document.getElementById('target').innerHTML=prefixingih + '<button ' + opsty + ' id=tarbut onclick=\" event.stopPropagation(); if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { drop(document.getElementById(' + "'target'" + ')); } else { document.getElementById(' + "'topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML=''; if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { document.getElementById('starbut').style.zIndex='50'; } else { document.getElementById('starbut').style.marginTop='-50px'; document.getElementById('starbut').style.zIndex='-50'; } document.getElementById('starbut').innerHTML='" + dz + "'" + '; } \">' + dz + ' for Canvas After Click</button>';
if (document.getElementById('tarbut') && !mtdone) {
mtdone=true;
if (prefixingih != '') {
document.getElementById('tarbut').style.marginTop='0px';
} else {
document.getElementById('tarbut').style.marginTop=wasmt;
}
}

document.getElementById('starbut').innerHTML='';
var rectis=document.getElementById('target').getBoundingClientRect();
if (document.getElementById('mydbut')) {
rectis=document.getElementById('mydbut').getBoundingClientRect();
} else if (document.getElementById('tarbut')) {
rectis=document.getElementById('tarbut').getBoundingClientRect();
}
document.getElementById('target').setAttribute('data-mt','' + eval(-30 + eval('' + rectis.width)));
document.getElementById('target').style.backgroundColor='white';
document.getElementById('target').style.height='' + rectis.width + 'px';
document.getElementById('target').style.backgroundImage='url(' + lastdrop + ')';
document.getElementById('target').style.backgroundSize='contain';
document.getElementById('target').style.backgroundRepeat='no-repeat';
if (document.getElementById('topdragstyle').innerHTML == '' && prefixingih == '') {
wasmt='' + eval(-30 + eval('' + rectis.width)) + 'px';
document.getElementById('tarbut').style.marginTop=wasmt;
//var rectistb=document.getElementById('tarbut').getBoundingClientRect();
//document.getElementById('topdragstyle').innerHTML='<style> #tarbut { position:absolute; left:' + rectistb.left + 'px; top:' + eval(eval(0 * eval(-30 + eval('' + rectis.width))) + eval('' + rectistb.top)) + 'px; margin-top:0px !important; } </style>';
}
if (document.getElementById('tarbut')) {
dzslides.push(document.getElementById('target').outerHTML.replace(document.getElementById('tarbut').outerHTML,''));
} else {
dzslides.push(document.getElementById('target').outerHTML);
}
if (eval('' + dzslides.length) > 2) {
var htmlis='';
if (dzo == null) {
dzo=window.open('','_blank','top=200,left=200,width=300,height=300');
htmlis='<ht' + 'ml><he' + 'ad><title>Inhouse Animated GIF style presentation ...</title><scri' + 'pt type=text/javascript> var curslides=0, thislag=0; function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdzfirst(); }, thislag); } else { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdznext(); }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div></bo' + 'dy></ht' + 'ml>';
dzo.document.write(htmlis);
dzo.document.title='Inhouse Animated GIF style presentation ...';
} //else if (dzo.closed) {
// dzo=window.open('','_blank','top=200,left=200,width=300,height=300');
// htmlis='<ht' + 'ml><he' + 'ad><scri' + 'pt type=text/javascript> var curslides=0, thislag=0; function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdzfirst(); }, thislag); } else { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdznext(); }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div></bo' + 'dy></ht' + 'ml>';
// dzo.document.write(htmlis);
//}
}

}

function getdzlen() {
return eval('' + dzslides.length);
}

function getdzfirst() {
var iwhich=0;
dzcnt=1;
if (iwhich == 0) {
//alert(document.getElementById('target').outerHTML);
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('url(' + document.getElementById('target').outerHTML.split('url(')[1].split(')')[0] + ')', 'url(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}

function getdznext() {
var iwhich=dzcnt;
dzcnt++;
if (iwhich == 0) {
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('url(' + document.getElementById('target').outerHTML.split('url(')[1].split(')')[0] + ')', 'url(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}

function getdz(iwhich) {
if (iwhich == 0) {
//alert(document.getElementById('target').outerHTML);
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('URL(' + document.getElementById('target').outerHTML.split('URL(')[1].split(')')[0] + ')', 'URL(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}


setTimeout(function() {
if (elem) {
if (eval('' + dzslides.length) == 0) {
dzslides.push(elem.toDataURL());
}
} else if (document.getElementById('topcanvas')) {
if (eval('' + dzslides.length) == 0) {
dzslides.push(document.getElementById('topcanvas').toDataURL());
}
}
}, 5000);

… in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application.


Previous relevant Canvas Drag and Drop Primer Tutorial is shown below.

Canvas Drag and Drop Primer Tutorial

Canvas Drag and Drop Primer Tutorial

It’s time. It really is. It’s time to revisit the “User of Signature Signature” web application using the HTML5 canvas element to create and share graphical data, that we last dealt with when we presented Canvas Sharing via Email Hashtagging Tutorial.

Why? Well, we want to combine …

  • canvas … graphical data … to start with … with …
  • drag and drop methodologies

… to add to the functionality of our “User of Signature Signature” web application. Today is the “proof of concept” day because there are two conflicting issues for us …

  • scribble functionality on a canvas element … is at odds with …
  • the draggable=true attribute usage crucial to getting Drag and Drop methodologies to kick in

You may have noticed with the “User of Signature Signature” web application it starts ready to scribble on the canvas straight away. And do you think we can resist that, as we test it?! No way!

That same steadiness to that canvas, as it is set up to resist “internal movement” also stops it being draggable alas … depending how you look at it.

And so we thought about this and came up with …

  1. do the usual this useful link suggests about setting up the canvas element for Drag and Drop …

    <canvas ondragstart="dragstart(event);" draggable=truefalse title="Ready for you to create your canvas 🎨 content above any signature 💳 panel ..." id="topcanvas" width=850 height=600 style="background-color:white; position: absolute; top:0; left:0; border-bottom:5px solid yellow; border-right:5px solid blue;"></canvas>

    … except start the draggable attribute with value false … and team this with …
  2. new Drop Zone HTML element helpers …

    <div id="target" ondrop="drop(event);" ondragover="dragover(event);" data-mt="0" style="vertical-align:bottom;display:block;background-color:transparent;">
    <button id=mydbut onclick=" document.getElementById('topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML='<span id=tarbut style=margin-top:' + document.getElementById('target').getAttribute('data-mt') + 'px;>Drop Zone</span>'; ">Drop Zone for Canvas After Click</button>
    </div>
    <span id=starbut></span>

    … and …
  3. programmatically controlled HTML button element click logic …

    var lastdrop='';

    function dragstart(ev) {
    ev.dataTransfer.setData("text/plain", document.getElementById('topcanvas').toDataURL());
    }

    function dragover(ev) {
    ev.preventDefault();
    }

    function drop(ev) {
    ev.target.innerHTML = ev.dataTransfer.getData("text");
    lastdrop=ev.target.innerHTML;
    document.getElementById('topcanvas').setAttribute('draggable','false');
    document.getElementById('target').innerHTML='<button id=tarbut onclick=\" document.getElementById(' + "'topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML=''; document.getElementById('starbut').style.marginTop='-50px'; document.getElementById('starbut').style.zIndex='-50'; document.getElementById('starbut').innerHTML='Drop Zone'" + '; \">Drop Zone for Canvas After Click</button>';
    document.getElementById('starbut').innerHTML='';
    var rectis=document.getElementById('target').getBoundingClientRect();
    if (document.getElementById('mydbut')) {
    rectis=document.getElementById('mydbut').getBoundingClientRect();
    } else if (document.getElementById('tarbut')) {
    rectis=document.getElementById('tarbut').getBoundingClientRect();
    }
    document.getElementById('target').setAttribute('data-mt','' + eval(-30 + eval('' + rectis.width)));
    document.getElementById('target').style.height='' + rectis.width + 'px';
    document.getElementById('target').style.backgroundImage='URL(' + lastdrop + ')';
    document.getElementById('target').style.backgroundSize='contain';
    document.getElementById('target').style.backgroundRepeat='no-repeat';
    if (document.getElementById('topdragstyle').innerHTML == '') {
    document.getElementById('tarbut').style.marginTop='' + eval(-30 + eval('' + rectis.width)) + 'px';
    //var rectistb=document.getElementById('tarbut').getBoundingClientRect();
    //document.getElementById('topdragstyle').innerHTML='<style> #tarbut { position:absolute; left:' + rectistb.left + 'px; top:' + eval(eval(0 * eval(-30 + eval('' + rectis.width))) + eval('' + rectistb.top)) + 'px; margin-top:0px !important; } </style>';
    }
    }

    … to toggle between the modes of use

… in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application.

Like we say, “proof of concept” (and mobile platforms need more thinking, so see Stop Press below), today, ahead of “usefulness” to come!

Stop Press

We know (again, via this useful link) from simple canvas scenarios Drag and Drop can work for mobile, but we couldn’t get it to work for our canvas used for scribbling and other annotation purposes. And so, in our further changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application we got some “onclick” event replacement code working for mobile platforms involving less work for the user. Some may see this as an enhancement, but personally, we’re awfully fond of Drag and Drop even with the slightly greater bother. We just think it helps a user clarify in their minds what they are doing, and not necessarily blithely clicking away with impunity.


Previous relevant Canvas Sharing via Email Hashtagging Tutorial is shown below.

Canvas Sharing via Email Hashtagging Tutorial

Canvas Sharing via Email Hashtagging Tutorial

We’re regular “tweakers” (for example, with Canvas via Image Web Share API Photo Postcard Tutorial) of what we call the …

  • canvas … based …
  • signatures … possible …
  • scribble … or …
  • discrete click … creating …
  • annotation … friendly …

… “graphics sharing” web application “User of Signature Signature”. And with us, more and more …

  • decoupling our PHP mail sharing conduits … in favour of …
  • “a” link “mailto:” email (and sometimes “sms:” SMS) communication conduits … now making use of …
  • hashtagging … parts to URLs used …

… because this methodology has fewer data limits, it being a “clientside” only (for the main part) means of sharing data. Today, we’re looking into this.

Happily, for this work, “the lynchpin” is some “signature_signature.js” external Javascript, which is all that needs tweaking to make these changes.

Previously, we’d relied, more, on “signature_signature.php” PHP, which oversaw our previous interfacing to the mail server up at the RJM Programming domain via the PHP mail function.

Now, the user sends their own emails with their own email client software, and emails contain one long body URL string where …

  • canvas … element’s …
  • [canvasElement].toDataURL(‘image/jpeg’, 0.7) … means by which a …
  • data URI representation of the graphical data

… can be hashtagged into that long body URL string. If the email recipient clicks this link, our external Javascript …

Reads such incoming data via location.hash

var azx=null, wowowo=null;
var imgbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('img=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('img=')[1].split('&')[0]).replace(/\ /g,'+') : '';
var passedsubject=location.search.split('subject=')[1] ? decodeURIComponent(location.search.split('subject=')[1].split('&')[0]) : '';
And loaded into canvas and new window …

setTimeout(function(){
if (imgbih != '') {
//alert(imgbih);
if (top.document.getElementById('topcanvas')) {
var xanimg=document.createElement('img');
xanimg.onload=function() {
var tc=top.document.getElementById('topcanvas').getContext('2d');
tc.drawImage(xanimg, 0, 0);
};
xanimg.src=imgbih;
}
var wowowo=window.open('','_blank','top=100,left=100,width=800,height=800');
wowowo.document.write('<html><body style="background:URL(' + "'" + imgbih + "'" + ');background-repeat:no-repeat;"></body></html>');
imgbih='';
if (passedsubject != '') {
wowowo.document.title='' + passedsubject;
}
}
}, 5000);
Before submitting form method=POST to signature_signature.php check out form fields

if (('' + document.getElementById('emailto').value).indexOf('@') != -1 && ('' + document.getElementById('contentto').value).indexOf('data:image/jpeg;base64,') != -1) {
azx=document.createElement("a");
document.body.appendChild(azx);
azx.style = "display: none";
azx.href = 'mailto:' + document.getElementById('emailto').value + ('?subject=' + encodeURIComponent(document.getElementById('subject').value)).replace(/subject\=$/g,'subject=My%20Canvas%20Creation') + '&body=' + encodeURIComponent(top.document.URL.split('?')[0].split('#')[0] + ('?subject=' + encodeURIComponent(document.getElementById('subject').value)).replace(/subject\=$/g,'subject=My%20Canvas%20Creation') + '#img=' + encodeURIComponent(document.getElementById('contentto').value));
azx.click();
} else {

document.getElementById('myform').submit();
}

… to enact fairly simple changes for more stable sharing and collaboration arrangements via a tweaked external Javascript signature_signature.js code used by our signature use in canvas supervisory web application.


Previous relevant Canvas via Image Web Share API Photo Postcard Tutorial is shown below.

Canvas via Image Web Share API Photo Postcard Tutorial

Canvas via Image Web Share API Photo Postcard Tutorial

We discovered, further to yesterday’s Canvas via Image Web Share API Personalization Tutorial, out and about, with an iPhone, that …

Image Input Mode Image Sizing Modus Operandi
URL Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)
Browsing Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)

… of Images to Canvas No Clicking Form Signature Tutorial times was worth refocussing on, regarding the combination of …

  • Browsing … Contain (canvas) … option above … and …
  • Take Photo … mobile device option … which, if followed through on, is more than likely to leave …
  • On the right hand side of the canvas there will be white space suitable to scribble a message to complete your …
  • Postcard … which you can optionally …
  • Share

… and if you are using the https: protocol with the Safari and Google Chrome web browser(s), we can help out more with that last step, being able to make a sharing button show the word “Postcard” …


function checkonyellow() {
var ibuta=[], jbuta=0;
if (safariblurb != '') {
if (document.getElementById('shareurl')) {
if (document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
} else if (parent.document.getElementById('shareurl')) {
if (parent.document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=parent.document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
} else if (top.document.getElementById('shareurl')) {
if (top.document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=top.document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
}
}
}

… to make the procedure …

Postcard via smart device Camera advice …

1. Touch browsing Choose File button that has just appeared.
2. Choose to Take Photo.
3. Consider any zooming in or selfie modes of use in Photo mode.
4. Take photo with Camera app white button, choosing Use Photo or Retake, as required.
5. Scribble any optional messaging in white area to right, or use other discreet touch functionalities.
6. Optionally email off to recipient (as textbox below goes yellow, click email sender button to its left for easier usage).

… more obvious, via the changed external Javascript signature_signature.js code used by our signature use in canvas supervisory web application.


Previous relevant Canvas via Image Web Share API Personalization Tutorial is shown below.

Canvas via Image Web Share API Tutorial

Canvas via Image Web Share API Personalization Tutorial

Yesterday’s Canvas via Imge Web Share API Tutorial‘s Web Share API (so far, on Safari and Google Chrome web browser(s)) image (via canvas) sharing functionality did not have enough privacy for the user, as far as we were concerned. We’re not going in for some two factor authentication level of privacy here, and so we’ve opted for the use of the word “Personalization” in today’s blog posting title.

What we’ve done here is incorporate into the image name (a form of) the IP address of the sender of the image, which the receiver is using (for that extra privacy) as they click the email link, that now involves (a form of) this IP address in its formation.

No surprises here, that we’d have to involve our changed PHP helper signature_signature.php “specialist signature PHP helper” more, getting this solution together.

And no surprises here, that we’d try to involve the web server’s disk areas outside the scope of its (Apache) Document Root hierarchy. That step, alone, is important to force a curious user, to work their way through an inhouse PHP webpage URL of some sort, in order to extract that …

  • image data in the form of a web server file … to a …
  • public domain webpage image in an HTML img tag where its src attribute is populated via that web server file’s equivalent base64 data URI …
    <?php

    echo "<html><body><img title='" . $thename . '' . "' src='data:image/webp;base64," . base64_encode(file_get_contents(DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . $thename . '')) . "'></img>" . $therest . "</body></html>";

    ?>

Am sure you are not surprised that today’s external Javascript web_share_api_test.js Web Share API specialist needed to change


function tctowebp() {
var ourcanvc='';
var canvs=[];
if (window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0) {
canvs=document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
if (eval('' + canvs.length) == 0 && window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
}
if (eval('' + canvs.length) > 0) {
ourcanvc=canvs[0].toDataURL('image/webp');
if (lastcanvc != ' ') {
if (lastcanvc != ourcanvc) {
lastcanvc=ourcanvc;
canvc=ourcanvc;
czhr = new XMLHttpRequest();
czform = new FormData();
czform.append('webpit', canvc);
if (newidea.indexOf('canvas.webp') == -1) { // eg. MAMP ... http://localhost:8888/signature_signature.php?mycanvasimage=0000__1
czform.append('websecurepit', '' + myipadwsi);
}

czhr.onreadystatechange=cstateChanged;
czhr.open('post', './signature_signature.php', true);
czhr.send(czform);
} else {
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='#f9f9f9';
}
}
}
lastcanvc=ourcanvc;
}
return ourcanvc;
}

… as well, regarding its incorporation of Web Share API logic into our inhouse User of Signature canvas based web application, best tried, these days, in the Safari and Google Chrome web browser(s) using https: (secure) protocol?!


Previous relevant Canvas via Imge Web Share API Tutorial is shown below.

Canvas via Image Web Share API Tutorial

Canvas via Imge Web Share API Tutorial

We’re on the lookout for ways to improve the sharing and collaboration functionality of yesterday’s Images to Canvas No Clicking Form Signature Tutorial‘s “User of Signature” canvas using web application. We’re calling on ..

  • Safari and Google Chrome web browser(s) … for the moment, supporting the …
  • Web Share API … in order to …
  • Fill out email (or other communication idea) attachment links or documents … that …
  • Prepare the message before you, the user, flesh out the message as you send it off to the recipient

… a more guaranteed way, these days, because there are more and more ways restrictions may stop you doing similar functionality with a mail server arrangement on your public domain website, where we use the Exim mail server, at the RJM Programming domain.

As far as data goes for this, we also call on our changed PHP helper signature_signature.php to create a webp image file for sharing …

<?php

if (isset($_POST['webpit'])) {
file_put_contents($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'canvas.webp', base64_decode(explode(";base64,", str_replace(' ','+',urldecode($_POST['webpit'])))[1]));
exit;
}

?>

… as applicable. The modus operandi for this back at today’s changed web_share_api_test.js external Javascript Web Share API specialist, uses Ajax methodologies …


var canvc=' ', lastcanvc=' ', czhr=null, czform=null;

function cstateChanged() {
if (czhr.readyState == 4) {
if (czhr.status == 200) {
if (1 == 2) { alert('Did it'); }
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='yellow';
}
}
}
}

function tctowebp() {
var ourcanvc='';
var canvs=[];
if (window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0) {
canvs=document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
if (eval('' + canvs.length) == 0 && window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
}
if (eval('' + canvs.length) > 0) {
ourcanvc=canvs[0].toDataURL('image/webp');
if (lastcanvc != ' ') {
if (lastcanvc != ourcanvc) {
lastcanvc=ourcanvc;
canvc=ourcanvc;
czhr = new XMLHttpRequest();
czform = new FormData();
czform.append('webpit', canvc);
czhr.onreadystatechange=cstateChanged;
czhr.open('post', './signature_signature.php', true);
czhr.send(czform);
} else {
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='#f9f9f9';
}
}
}
lastcanvc=ourcanvc;
}
return ourcanvc;
}

… and set in motion via a couple of different parental calls such as …


<script type='text/javascript' src='/web_share_api_test.js?canvasshare=as_necessary9876' defer></script>

… in …

… for cuter Safari and Google Chrome web browser sharing and collaboration work.


Previous relevant Images to Canvas No Clicking Form Signature Tutorial is shown below.

Images to Canvas No Clicking Form Signature Tutorial

Images to Canvas No Clicking Form Signature Tutorial

Surprisingly, it’s been more than a year since we changed anything to do with our …

  • canvas based …
  • images and annotation … accepting …
  • scribble (ie. can handle signatures) … perhaps …
  • form filling helping web application

… (when we presented Canvas Graphics Editing in Zoomed Webpage Tutorial) and we found that in order to add six more such Annotation option helpers into the mix …

Image Input Mode Image Sizing Modus Operandi
URL Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)
Browsing Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)

… we only needed to change the external Javascript signature_signature.js code used by our signature use in canvas supervisory web application so that when you can not ask so much of the user regarding image scaling, and avoid any canvas clicking that way, offering these six new annotation functionality options.


Previous relevant Canvas Graphics Editing in Zoomed Webpage Tutorial is shown below.

Canvas Graphics Editing in Zoomed Webpage Tutorial

Canvas Graphics Editing in Zoomed Webpage Tutorial

Software integration can be useful for a number of reasons …

  • code reuse
  • modularisation of functionality … and in our case …
  • if you like coding via “intervention points” you will enjoy coding for software integration improvements

As you might imagine it is easier in terms of the fact you can change “both ends” of the software integration, if it is “inhouse”, and luckily, today, our software integration is “inhouse”.

When we presented the recent Keyboard Based Cursor Canvas Content Copy Tutorial it focussed our attention on an improvement we could make to that user_of_signature_signature.htm canvas image editing web application. We wanted to offer the software integration with this web application to be able to successfully handle a webpage that has been zoomed to zoom factor different to 1.0 (or 100%).

We noticed before today’s very simple changes to external Javascript signature_signature.js code, graphic positioning on the canvas was wrong when the webpage had a zoom factor that is not 1.

Applying the canvas co-ordinate references through “the lens of” Javascript function zmb below …


var zzfac=location.search.split('zoom=')[1] ? decodeURIComponent(location.search.split('zoom=')[1].split('&')[0]) : "";
if (('' + location.hash).indexOf('zoom=') != -1) { zzfac=decodeURIComponent(('' + location.hash).split('zoom=')[1].split(';')[0]); }

function zmb(inco) {
if (zzfac != '') {
if (zzfac.indexOf('%') != -1) {
return eval(eval(eval('' + inco) / eval('' + zzfac.replace('%', ''))) * 100.0);
} else {
return eval(eval('' + inco) / eval('' + zzfac));
}
}
return inco;
}

used in the following example code below …


topelem.addEventListener('mousedown', function(event) {
if (topin) {
topin.value='- -' + isScribble + ',' + lastx + ',' + lasty + ',' + x + ',' + y;
}
if (isScribble == 1) {
threebackpmore=twobackpmore; twobackpmore=lastpmore; lastpmore=pmore; pmore=' pxam1 ';
isScribble=2;
if (event.pageX || event.pageY) {
ppx=zmb(event.pageX) - elemLeft;
ppy=zmb(event.pageY) - elemTop;
} else {
ppx=zmb(event.clientX) - elemLeft;
ppy=zmb(event.clientY) - elemTop;
}
if (pdgebi(topdcmcheck('dcm'))) {
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('text') == 0) {
//parent.document.title='here3 ' + tx + ',' + ty;
if (1 == 2 && tlx < 0 && tly < 0) {
tlx=ppx;
tly=ppy;
}
if (tx >= 0 && ty >= 0) {
prevtx=tx;
prevty=ty;
trotis=eval(((Math.atan2((ppy - prevty), (ppx - prevtx)) * 180.0 / Math.PI) + 360) % 360);
if (pdgebi('trotation')) {
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('textseq') == 0) {
jsrectbits[0]=prevtx;
jsrectbits[1]=prevty;
jsrectbits[2]=ppx;
jsrectbits[3]=ppy;
if (Math.abs(prevty - ppy) > Math.abs(prevtx - ppx)) {
trotis=eval(((Math.atan2((prevtx - ppx), (ppy - prevty)) * 180.0 / Math.PI) + 360) % 360);
}
}
parent.document.getElementById('trotation').value=Math.floor(trotis);
ppx=prevtx;
ppy=prevty;
prevtx=-1;
prevty=-1;
exceptwhen=endtrue(''); //true;
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('textseq') == -1) parent.document.getElementById(topdcmcheck('dcm')).value = 'Text';
}
//alert(trotis);
}
tx=ppx;
ty=ppy;
} else {
tx=ppx;
ty=ppy;
prevtx=-1;
prevty=-1;
}
} else {
tx=ppx;
ty=ppy;
prevtx=-1;
prevty=-1;
}
lastx=0; // new to scribble
lasty=0;
x=0;
y=0;
}
});

… we now have reasonable graphic positioning for the canvas when its webpage window has a non 1.0 zoom factor applied.


Previous relevant Keyboard Based Cursor Canvas Content Copy Tutorial is shown below.

Keyboard Based Cursor Canvas Content Copy Tutorial

Keyboard Based Cursor Canvas Content Copy Tutorial

You may have noticed with yesterday’s Keyboard Based Cursor Share Content Copy Tutorial, crucial to the sharing code, was the use of the incredible HTML5 introduced canvas element, helped by that middleperson link to those public email and SMS sharing conduits, overseeing the great random Lorem Picsum background images and user created emoji and/or text initial annotations.

And this got us wondering whether there was a private (ie. inhouse) web application of the past that could further value add as another optional middleperson tool for (user) personalization (of the content) purposes. And yes, do you remember the canvas work involved in the featured web application of Emoji Borders and Backgrounds Canvas Annotation Tutorial?

It uses an interesting approach with the transference of data across from a canvas in one application to the canvas in that user_of_signature_signature.htm web application. Once our changed body_mouse_deepdive.html‘s “parent” web application has organized its canvas contents, it’s just a matter of …


var awo=null;
awo=window.open('./user_of_signature_signature.htm','_blank','top=100,left=100,width=800,height=800');

for the “child” canvas annotator to effectively “suck up” the canvas data into its canvas via …


var cancont='';
var elemode=location.search.split('elemode=')[1] ? (location.search.split('elemode=')[1].split('&')[0]) : "canvas";

if (('' + window.opener).replace('null','') != '') {
var cans=window.opener.document.getElementsByTagName(elemode);
if (cans.length > 0) {
if (elemode == 'img') {
cancont=cans[0].src;
} else {
cancont=cans[0].toDataURL('image/png');
}
if (cancontw < 0 && canconth < 0) { cancontw=cans[0].width; canconth=cans[0].height; } } }

... possible because both "parent" and "child" exist on the same RJM Programming domain (web server).

Once there, another woooooorrrrrlllddd of image manipulations, via canvas, can await the user, perhaps ready for ongoing sharing possibilities with that "child".


Previous relevant Keyboard Based Cursor Share Content Copy Tutorial is shown below.

Keyboard Based Cursor Share Content Copy Tutorial

Keyboard Based Cursor Share Content Copy Tutorial

Yesterday's Keyboard Based Cursor Personalized Content Copy Tutorial has probably reached a point where some sharing mechanism is apt, for accountability and usefulness purposes, at the very least, regarding our recent web application allowing for foreground content on top of a random Lorem Picsum background image.

We've been helped out greatly by javascript - Can I take a screenshot from the clipboard? - Stack Overflow and html - How to render a blob on a canvas element? - Stack Overflow in the following new relevant Javascript code ...


function renderNoImage(canvas, blob) { // thanks to https://stackoverflow.com/questions/38004917/how-to-render-a-blob-on-a-canvas-element
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // This is important. If you are not using the blob, you should release it if you don't want to reuse it. It's good for memory.
ctx.drawImage(event.target, 0, 0)
img.style.display='none';
img.style.zIndex='-123';
};
img.src = URL.createObjectURL(blob);
}


function renderImage(canvas, blob) { // thanks to https://stackoverflow.com/questions/38004917/how-to-render-a-blob-on-a-canvas-element
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // This is important. If you are not using the blob, you should release it if you don't want to reuse it. It's good for memory.
ctx.drawImage(event.target, 0, 0)
};
img.src = URL.createObjectURL(blob);
}


addEventListener("paste", ev => { // thanks to https://stackoverflow.com/questions/55559432/can-i-take-a-screenshot-from-the-clipboard
for(const item of ev.clipboardData.items) { /// Clipboard may contain multiple elements of different type -- text, image, etc
if(item.type.startsWith("image/")) { /// We are only interested in clipboard data that is an image
if (emailee == '') {
emailee=prompt('Your clipboard has a useful image you could share the image with. Optionally, please enter an email address or SMS number to share with.');
if (emailee == null) { emailee=''; }
}
if (emailee.indexOf('@') != -1) {
document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="position:absolute;z-index:98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('aemail').href=document.getElementById('aemail').href.replace(':?', ':' + emailee + '?');
document.getElementById('aemail').click();
document.getElementById('mycanvas').innerHTML='';
} else if (emailee != '' && emailee.replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '') {
document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="position:absolute;z-index:98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('asms').href=document.getElementById('asms').href.replace(':&', ':' + emailee + '&');
document.getElementById('asms').click();
document.getElementById('mycanvas').innerHTML='';
} else {
//document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="display:none;position:absolute;z-index:-98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderNoImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('mycanvas').innerHTML='';
}
}
}
});

... which is explained to the user in the following way ...

Share with email or SMS happens via screenshots (macOS control-command-shift-3 or Windows Prnt-Scrn) followed by File menu Paste option causing chance to share after which File menu Undo can remove pasted part as required.

... in the changed "proof of concept" body_mouse_deepdive.html live run.


Previous relevant Keyboard Based Cursor Personalized Content Copy Tutorial is shown below.

Keyboard Based Cursor Personalized Content Copy Tutorial

Keyboard Based Cursor Personalized Content Copy Tutorial

On top of yesterday's Keyboard Based Cursor Content Copy Tutorial we wanted to offer the user the chance for them to tailor their foreground content on top of the Lorem Picsum background image.

The user can enter this via the keyboard because there is an HTML div contenteditable=true pallette to work with ...


<div id=mydiv onfocus='setTimeout(alte, 1000);' ondblclick="this.innerHTML='';" onclick='stamp(event);' title='Click and type and see the cursor change as you use Alt or Shift or Control that can be reset via Caps Lock with a click copying cursor into content ... RJM Programming ... May, 2022 ... thanks to https://picsum.photos/ Lorem Picsum You can change emojis if you know the \002602 form of UTF-16 (hex) entry (our example is umbrella and if you express it as \\002602 that leaves cursor just as your emoji). Double click existant foreground emoji element to clear all the foreground emoji elements.' spellcheck='false' contenteditable=true style='width:100vw;height:100%;color:transparent;text-color:transparent;' onkeypress=kpcursorlook(event); onkeydown=cursorlook(event); onmousemove=xycursorlook(event); ontouchmove=xycursorlook(event);></div>

It's "title" attribute now talks about two new pieces of functionality, those being ...

  • You can change emojis if you know the \002602 form of UTF-16 (hex) entry (our example is umbrella and if you express it as \\002602 that leaves cursor just as your emoji).
  • Double click existant foreground emoji element to clear all the foreground emoji elements.

The choice of keyboard input methodology also suits CSS "content" property definitions as one backslash followed by a hexidecimal value left padded with zeroes to a length of six. See us doing this below for an umbrella emoji (which could also be expressed in an HTML hexadecimal entity way &#x2602; ☂ the information for which you can find at Unicode Character 'UMBRELLA' (U+2602) we normally get to by entering "U+2602" at a web browser address bar, the knowledge for which we arrive at via entering "umbrella emojipedia" and moving down the top link's webpage to find the "U+2602") ...


<style> #myumbrella::after { content: '\002602'; } </style>

... that feeds into some new "onkeypress" keyboard event logic ...


function kpcursorlook(evt) {
var atofa=['a','b','c','d','e','f'];
var rgbit='';
if (evt.keyCode == 92) {
if (newu.length == 1) { gro=true; } else { gro=false; }
newu=("\\").substring(0,1);
} else if (evt.keyCode >= 48 && evt.keyCode <= 57) {
newu+=('' + eval(-48 + evt.keyCode));
} else if (evt.keyCode >= 65 && evt.keyCode <= 70) {
newu+=('' + atofa[eval(-65 + evt.keyCode)]);
} else if (evt.keyCode >= 97 && evt.keyCode <= 102) {
newu+=('' + atofa[eval(-97 + evt.keyCode)]);
}
if (newu.length == 7) {
if (lastcursor.indexOf(("\\\\").substring(0,2)) != -1) {
rgbit=lastcursor.split(("\\\\").substring(0,2))[1].substring(6);
lastcursor=lastcursor.split(("\\\\").substring(0,2))[0] + ("\\").substring(0,1) + newu + rgbit;
document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
} else if (lastcursor.indexOf(("\\").substring(0,1)) != -1) {
rgbit=lastcursor.split(("\\").substring(0,1))[1].substring(6);
lastcursor=lastcursor.split(("\\").substring(0,1))[0] + newu + rgbit;
if (gro) {
lastcursor=lastcursor.replace('>Shift/Alt/Ctrl', '>').replace('>Shift', '>').replace('>Alt', '>').replace('>Ctrl', '>').replace(" width='126'", " width='20'").replace(" width='66'", " width='20'").replace(" height='48'", " height='20'").replace('0 0 100 100', '0 0 20 20');
}
document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
}
}
// console.log(evt.keyCode);
}

... in the changed "proof of concept" body_mouse_deepdive.html live run.

Stop Press

Two more "title" attribute pieces of functionality are now ...

  • With complex emoji entries do not zero leftpad until your last simple entity eg. \\1F6A3\200D\2640\00FE0F could define a Woman Rowing complex emoji.
  • Optionally make emojis bigger after \ with + two time multipliers.

The choice of keyboard input methodology also suits CSS "content" property definitions as one backslash followed by "first off" sets of HTML entities they should not left zero pad in their entry, followed by a last HTML entity hexidecimal value left padded with zeroes to a length of six to finish up with. See us doing this below for a woman rowing emoji (which could also be expressed in an HTML hexadecimal entity way &#x1f6a3;&#x0200d;&#x02640;&#x0fe0f; the information for which you can find at 🚣‍♀️ Woman Rowing Boat Emoji we normally get to by entering "woman rowing emoji" at the web browser address bar) ...


<style> #myrowing::after { content: '\01f6a3\00200d\002640\00fe0f'; } </style>

... in the changed "proof of concept" body_mouse_deepdive.html live run.


Previous relevant Keyboard Based Cursor Content Copy Tutorial is shown below.

Keyboard Based Cursor Content Copy Tutorial

Keyboard Based Cursor Content Copy Tutorial

The "onkeydown" keyboard event can involve in its logic three mouse event (event object) property usages that caught our interest recently ...

... and we wanted to have "keyboard" meets "mouse" events, along with the brilliance of Lorem Picsum regarding background image randomosity and quality, working with HTML ...


<div id=mydiv onfocus='setTimeout(alte, 1000);' onclick='stamp(this);' title='Click and type and see the cursor change as you use Alt or Shift or Control that can be reset via Caps Lock with a click copying cursor into content ... RJM Programming ... May, 2022 ... thanks to https://picsum.photos/ Lorem Picsum' spellcheck='false' contenteditable=true style='width:100vw;height:100%;color:transparent;text-color:transparent;' data-onkeypress=xycursorlook(event); onkeydown=cursorlook(event); onmousemove=xycursorlook(event); ontouchmove=xycursorlook(event);></div>

... in a way we hadn't tried before that called on some inhouse cursor SVG creations (and so is, alas, just a non-mobile fully featured experience), in the sense that ...

  1. keyboard helps creating the "what" regarding content ...

    <script type='text/javascript'>
    var lastkeymodifier=''
    var lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function cursorlook(evt) {
    if (evt.altKey) {
    if (lastkeymodifier != 'alt') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,0,255,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Alt\\01f3d5</text></svg>\") 16 0, progress";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='alt';
    }
    } else if (evt.ctrlKey) {
    if (lastkeymodifier != 'ctrl') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Ctrl\\01f333</text></svg>\") 16 0, pointer";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='ctrl';
    }
    } else if (evt.shiftKey) {
    if (lastkeymodifier != 'shift') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(255,0,0,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Shift\\01f389</text></svg>\") 16 0, grab";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='shift';
    }
    } else {
    if (lastkeymodifier != '') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='';
    }
    }
    }
    </script>
  2. mouse "onmousemove" or "ontouchmove" helps with the "where" regarding content above ...

    <script type='text/javascript'>
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function xycursorlook(e) {
    e = e || window.event;
    e.preventDefault();

    if (e.touches) {
    if (e.touches[0].pageX) {
    pos3 = e.touches[0].pageX;
    pos4 = e.touches[0].pageY;
    } else {
    pos3 = e.touches[0].clientX;
    pos4 = e.touches[0].clientY;
    }
    //console.log('pos3=' + pos3 + ',pos4=' + pos4);
    } else if (e.clientX || e.clientY) {
    pos3 = e.clientX;
    pos4 = e.clientY;
    } else {
    pos3 = e.pageX;
    pos4 = e.pageY;
    }
    }
    </script>
  3. preparations for "onclick" way a cursor can be plonked into (real lasting) content ...

    <script type='text/javascript'>
    var lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function stamp(divo) {
    if (('' + pos3).indexOf('-') == -1) {
    if (allowable) {
    //if (allowed) {
    //allowed=false;
    //setTimeout(reseta, 2000);
    divo.innerHTML+='<div id=div' + subdiv + ' style="position:absolute;top:' + pos4 + 'px;left:' + pos3 + 'px;width:126px;height:48px;background-color:transparent;"></div><style> #div' + subdiv + ' { background:' + lastcursor.split(') ')[0] + ') no-repeat; } </style>';
    subdiv++;
    //console.log('lastpos4=' + lastpos4 + ' and pos4=' + pos4);
    //console.log('lastpos3=' + lastpos3 + ' and pos3=' + pos3);
    lastpos3=pos3;
    lastpos4=pos4;
    //}
    }
    }
    }
    </script>

... with the "proof of concept" body_mouse_deepdive.html live run.

If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.

Posted in Animation, eLearning, Event-Driven Programming, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a comment

Canvas Drag and Drop Less Ephemeral Tutorial

Canvas Drag and Drop Less Ephemeral Tutorial

Canvas Drag and Drop Less Ephemeral Tutorial

We’re bucking the trend, today, “offering less” but “producing more”, we’re hoping, on top of the progress of yesterday’s Canvas Drag and Drop Ephemeral Tutorial ….

  • adding totaking from an “internal use only” level of ephemeralness which only offered an individual user view of canvas based Animated GIF style slide creation work as an Animated GIF presentation in their individually presented popup window … to …
  • adding to above a new 📧 Email emoji sharing (and “caring”) button within that popup window presentation … that if clicked …
    1. prompts the user as per …

      var huhe=prompt("Please enter email address to share Animated GIF style presentation with. Append a hashtagged delay between slides in milliseconds that is not 4000 optionally.", "");
    2. allows an “a” “mailto:” link’s “href” attribute reflect any hashtagged delay you specified …

      if (huhe.indexOf("#") != -1) {
      document.getElementById("aemail").href=document.getElementById("aemail").href.replace(encodeURIComponent("delay=4000"),encodeURIComponent("delay=" + huhe.split("#")[1])); huhe=huhe.split("#")[0];
      }
    3. allows an “a” “mailto:” link’s “href” attribute reflect any emailee you specified …

      if (huhe.indexOf("@") != -1) {
      document.getElementById("aemail").href=document.getElementById("aemail").href.replace(":?", ":" + huhe + "?"); document.getElementById("aemail").click();
      }
    4. and if all ritchy ditch …

      if (huhe.indexOf("@") != -1) {
      document.getElementById("aemail").click();
      }
    5. clicks the “a” “mailto:” link, passing control over to your default email client software to present you with an email body presentation webpage by which you can click a Send icon to send that largely hashtagged data email to an email recipient (ie. emailee)

… in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application, helped out by ever more changing helperer outerer htmlemail.html (just HTML and Javascript and hashtagging) of recent times, gradually replacing work that used to be a PHP thaing via emailhtml.php associated with RJM Programming domain’s email server fed by PHP mail command based email messages, now more in favour of hashtagging methodologies using “a” “mailto:” links, where we are only talking “clientside” with the logic, and the data length restrictions are not as severe.

Today’s animated GIF presentation, hence, is recreatable

Back to top

Did you know?

When it comes to email (and SMS for that matter) creation logic, occasionally, we find, you need to double encode … we kid you not! We needed to today, as you see below …


var popupemailfunction='';

if (popupemailfunction == '') {
popupemailfunction=' function dopopupemail() { var huhe=prompt("Please enter email address to share Animated GIF style presentation with. Append a hashtagged delay between slides in milliseconds that is not 4000 optionally.", ""); if (huhe == null) { huhe=""; } if (huhe.indexOf("@") != -1) { if (huhe.indexOf("#") != -1) { document.getElementById("aemail").href=document.getElementById("aemail").href.replace(encodeURIComponent("delay=4000"),encodeURIComponent("delay=" + huhe.split("#")[1])); huhe=huhe.split("#")[0]; } document.getElementById("aemail").href=document.getElementById("aemail").href.replace(":?", ":" + huhe + "?"); document.getElementById("aemail").click(); } } ';
}
htmlis='<ht' + 'ml><he' + 'ad><title>Inhouse Animated GIF style presentation ...</title><scri' + 'pt type=text/javascript> var dataih="", oneround=false, curslides=0, thislag=0; ' + popupemailfunction + ' function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { if (!oneround) { oneround=true; document.getElementById("semail").style.display="block"; } setTimeout(function() { dataih=window.opener.getdzfirst(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } else { setTimeout(function() { dataih=window.opener.getdznext(); if (document.getElementById("aemail").href.indexOf(encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,"")))) == -1) { document.getElementById("aemail").href+=encodeURIComponent(encodeURIComponent(dataih.split("url(")[1].split(")")[0].replace(/\"\;/g,"").replace(/\"/g,""))); } document.getElementById("ptarget").innerHTML=dataih; }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div><br><button title="Share via Email" id=semail style=display:none; onclick="dopopupemail();">&#128231;</button><a target=top id=aemail style=display:none; href="mailto:?subject=Animated%20GIF%20presentation&body=' + encodeURIComponent('https://www.rjmprogramming.com.au/htmlemail.html?delay=4000#') + '">Email</a></bo' + 'dy></ht' + 'ml>';

The pattern of when (we need to do this) remains a bit mysterious to us, but if we ever nail down the pattern, am sure you’ll hear about it! The good side to solving via “double encoding” means is that you can easily see where the wall needs to be repaired straight in front of you … in real time?!


Previous relevant Canvas Drag and Drop Ephemeral Tutorial is shown below.

Canvas Drag and Drop Ephemeral Tutorial

Canvas Drag and Drop Ephemeral Tutorial

Onto yesterday’s Canvas Drag and Drop Primer Tutorial‘s …

  • proof of concept feel to being able to drag and drop via the canvas element … today we have …
  • proof of concept ideas regarding future sharing and collaboration aspects to this Drag and Drop where we start with our “ephemeral” inhouse popup window version of what we hope to offer, in future, for users to share with email recipients

And the other progress was another “drag” friend for the canvas element, allowing that textarea element (now with ondragstart=”dragstart(event);” draggable=true attribution) down the bottom right also be “draggable” (to create the textarea wording on a white backgrounded slide “Drop Zone” result when dragged there) for our …

  • “Drop Zone” for non-mobile … and …
  • “Click Zone” for mobile

… platforms. The drag and drop functionality related global variables now go …


var dz='Drop Zone';
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
dz='Click Zone';
}
var dzslides=[], dzo=null, dzcnt=0;
var lastdrop='', lastelem=null, wasmt='0px';

… and the Drag and Drop logic now goes


function dragstart(ev) {
lastelem=ev.target;
if (('' + ev.target.outerHTML).indexOf('<textarea') == 0) {
ev.dataTransfer.setData("text/plain", ev.target.value);
} else {

ev.dataTransfer.setData("text/plain", document.getElementById('topcanvas').toDataURL());
}
}

function dragover(ev) {
ev.preventDefault();
}

function drop(ev) {
var prefixingih='', mtdone=false, opsty='';
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
if (('' + lastelem.outerHTML).indexOf('<textarea') == 0) {
lastdrop=lastelem.value;
while (lastdrop.indexOf(String.fromCharCode(10)) != -1) {
lastdrop=lastdrop.replace(String.fromCharCode(10),'<br>');
}
prefixingih='<span style=background-color:white;>' + lastdrop + '</span><br><br>';
opsty=' style="opacity:0.3;" ';
lastdrop=dzslides[0];
document.getElementById('target').innerHTML = dzslides[0];
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop='0px';
}
} else {

document.getElementById('target').innerHTML = document.getElementById('topcanvas').toDataURL();
lastdrop=document.getElementById('target').innerHTML;
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop=wasmt;
}
}
} else {
if (('' + lastelem.outerHTML).indexOf('<textarea') == 0) {
lastdrop=lastelem.value;
while (lastdrop.indexOf(String.fromCharCode(10)) != -1) {
lastdrop=lastdrop.replace(String.fromCharCode(10),'<br>');
}
prefixingih='<span style=background-color:white;>' + lastdrop + '</span><br><br>';
opsty=' style="opacity:0.3;" ';
lastdrop=dzslides[0];
document.getElementById('target').innerHTML = dzslides[0];
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop='0px';
}
} else {
ev.target.innerHTML = ev.dataTransfer.getData("text");
lastdrop=ev.target.innerHTML;
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop=wasmt;
}
}
}

document.getElementById('topcanvas').setAttribute('draggable','false');
document.getElementById('target').innerHTML=prefixingih + '<button ' + opsty + ' id=tarbut onclick=\" event.stopPropagation(); if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { drop(document.getElementById(' + "'target'" + ')); } else { document.getElementById(' + "'topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML=''; if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { document.getElementById('starbut').style.zIndex='50'; } else { document.getElementById('starbut').style.marginTop='-50px'; document.getElementById('starbut').style.zIndex='-50'; } document.getElementById('starbut').innerHTML='" + dz + "'" + '; } \">' + dz + ' for Canvas After Click</button>';
if (document.getElementById('tarbut') && !mtdone) {
mtdone=true;
if (prefixingih != '') {
document.getElementById('tarbut').style.marginTop='0px';
} else {
document.getElementById('tarbut').style.marginTop=wasmt;
}
}

document.getElementById('starbut').innerHTML='';
var rectis=document.getElementById('target').getBoundingClientRect();
if (document.getElementById('mydbut')) {
rectis=document.getElementById('mydbut').getBoundingClientRect();
} else if (document.getElementById('tarbut')) {
rectis=document.getElementById('tarbut').getBoundingClientRect();
}
document.getElementById('target').setAttribute('data-mt','' + eval(-30 + eval('' + rectis.width)));
document.getElementById('target').style.backgroundColor='white';
document.getElementById('target').style.height='' + rectis.width + 'px';
document.getElementById('target').style.backgroundImage='url(' + lastdrop + ')';
document.getElementById('target').style.backgroundSize='contain';
document.getElementById('target').style.backgroundRepeat='no-repeat';
if (document.getElementById('topdragstyle').innerHTML == '' && prefixingih == '') {
wasmt='' + eval(-30 + eval('' + rectis.width)) + 'px';
document.getElementById('tarbut').style.marginTop=wasmt;
//var rectistb=document.getElementById('tarbut').getBoundingClientRect();
//document.getElementById('topdragstyle').innerHTML='<style> #tarbut { position:absolute; left:' + rectistb.left + 'px; top:' + eval(eval(0 * eval(-30 + eval('' + rectis.width))) + eval('' + rectistb.top)) + 'px; margin-top:0px !important; } </style>';
}
if (document.getElementById('tarbut')) {
dzslides.push(document.getElementById('target').outerHTML.replace(document.getElementById('tarbut').outerHTML,''));
} else {
dzslides.push(document.getElementById('target').outerHTML);
}
if (eval('' + dzslides.length) > 2) {
var htmlis='';
if (dzo == null) {
dzo=window.open('','_blank','top=200,left=200,width=300,height=300');
htmlis='<ht' + 'ml><he' + 'ad><title>Inhouse Animated GIF style presentation ...</title><scri' + 'pt type=text/javascript> var curslides=0, thislag=0; function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdzfirst(); }, thislag); } else { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdznext(); }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div></bo' + 'dy></ht' + 'ml>';
dzo.document.write(htmlis);
dzo.document.title='Inhouse Animated GIF style presentation ...';
} //else if (dzo.closed) {
// dzo=window.open('','_blank','top=200,left=200,width=300,height=300');
// htmlis='<ht' + 'ml><he' + 'ad><scri' + 'pt type=text/javascript> var curslides=0, thislag=0; function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdzfirst(); }, thislag); } else { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdznext(); }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div></bo' + 'dy></ht' + 'ml>';
// dzo.document.write(htmlis);
//}
}

}

function getdzlen() {
return eval('' + dzslides.length);
}

function getdzfirst() {
var iwhich=0;
dzcnt=1;
if (iwhich == 0) {
//alert(document.getElementById('target').outerHTML);
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('url(' + document.getElementById('target').outerHTML.split('url(')[1].split(')')[0] + ')', 'url(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}

function getdznext() {
var iwhich=dzcnt;
dzcnt++;
if (iwhich == 0) {
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('url(' + document.getElementById('target').outerHTML.split('url(')[1].split(')')[0] + ')', 'url(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}

function getdz(iwhich) {
if (iwhich == 0) {
//alert(document.getElementById('target').outerHTML);
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('URL(' + document.getElementById('target').outerHTML.split('URL(')[1].split(')')[0] + ')', 'URL(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}


setTimeout(function() {
if (elem) {
if (eval('' + dzslides.length) == 0) {
dzslides.push(elem.toDataURL());
}
} else if (document.getElementById('topcanvas')) {
if (eval('' + dzslides.length) == 0) {
dzslides.push(document.getElementById('topcanvas').toDataURL());
}
}
}, 5000);

… in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application.


Previous relevant Canvas Drag and Drop Primer Tutorial is shown below.

Canvas Drag and Drop Primer Tutorial

Canvas Drag and Drop Primer Tutorial

It’s time. It really is. It’s time to revisit the “User of Signature Signature” web application using the HTML5 canvas element to create and share graphical data, that we last dealt with when we presented Canvas Sharing via Email Hashtagging Tutorial.

Why? Well, we want to combine …

  • canvas … graphical data … to start with … with …
  • drag and drop methodologies

… to add to the functionality of our “User of Signature Signature” web application. Today is the “proof of concept” day because there are two conflicting issues for us …

  • scribble functionality on a canvas element … is at odds with …
  • the draggable=true attribute usage crucial to getting Drag and Drop methodologies to kick in

You may have noticed with the “User of Signature Signature” web application it starts ready to scribble on the canvas straight away. And do you think we can resist that, as we test it?! No way!

That same steadiness to that canvas, as it is set up to resist “internal movement” also stops it being draggable alas … depending how you look at it.

And so we thought about this and came up with …

  1. do the usual this useful link suggests about setting up the canvas element for Drag and Drop …

    <canvas ondragstart="dragstart(event);" draggable=truefalse title="Ready for you to create your canvas 🎨 content above any signature 💳 panel ..." id="topcanvas" width=850 height=600 style="background-color:white; position: absolute; top:0; left:0; border-bottom:5px solid yellow; border-right:5px solid blue;"></canvas>

    … except start the draggable attribute with value false … and team this with …
  2. new Drop Zone HTML element helpers …

    <div id="target" ondrop="drop(event);" ondragover="dragover(event);" data-mt="0" style="vertical-align:bottom;display:block;background-color:transparent;">
    <button id=mydbut onclick=" document.getElementById('topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML='<span id=tarbut style=margin-top:' + document.getElementById('target').getAttribute('data-mt') + 'px;>Drop Zone</span>'; ">Drop Zone for Canvas After Click</button>
    </div>
    <span id=starbut></span>

    … and …
  3. programmatically controlled HTML button element click logic …

    var lastdrop='';

    function dragstart(ev) {
    ev.dataTransfer.setData("text/plain", document.getElementById('topcanvas').toDataURL());
    }

    function dragover(ev) {
    ev.preventDefault();
    }

    function drop(ev) {
    ev.target.innerHTML = ev.dataTransfer.getData("text");
    lastdrop=ev.target.innerHTML;
    document.getElementById('topcanvas').setAttribute('draggable','false');
    document.getElementById('target').innerHTML='<button id=tarbut onclick=\" document.getElementById(' + "'topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML=''; document.getElementById('starbut').style.marginTop='-50px'; document.getElementById('starbut').style.zIndex='-50'; document.getElementById('starbut').innerHTML='Drop Zone'" + '; \">Drop Zone for Canvas After Click</button>';
    document.getElementById('starbut').innerHTML='';
    var rectis=document.getElementById('target').getBoundingClientRect();
    if (document.getElementById('mydbut')) {
    rectis=document.getElementById('mydbut').getBoundingClientRect();
    } else if (document.getElementById('tarbut')) {
    rectis=document.getElementById('tarbut').getBoundingClientRect();
    }
    document.getElementById('target').setAttribute('data-mt','' + eval(-30 + eval('' + rectis.width)));
    document.getElementById('target').style.height='' + rectis.width + 'px';
    document.getElementById('target').style.backgroundImage='URL(' + lastdrop + ')';
    document.getElementById('target').style.backgroundSize='contain';
    document.getElementById('target').style.backgroundRepeat='no-repeat';
    if (document.getElementById('topdragstyle').innerHTML == '') {
    document.getElementById('tarbut').style.marginTop='' + eval(-30 + eval('' + rectis.width)) + 'px';
    //var rectistb=document.getElementById('tarbut').getBoundingClientRect();
    //document.getElementById('topdragstyle').innerHTML='<style> #tarbut { position:absolute; left:' + rectistb.left + 'px; top:' + eval(eval(0 * eval(-30 + eval('' + rectis.width))) + eval('' + rectistb.top)) + 'px; margin-top:0px !important; } </style>';
    }
    }

    … to toggle between the modes of use

… in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application.

Like we say, “proof of concept” (and mobile platforms need more thinking, so see Stop Press below), today, ahead of “usefulness” to come!

Stop Press

We know (again, via this useful link) from simple canvas scenarios Drag and Drop can work for mobile, but we couldn’t get it to work for our canvas used for scribbling and other annotation purposes. And so, in our further changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application we got some “onclick” event replacement code working for mobile platforms involving less work for the user. Some may see this as an enhancement, but personally, we’re awfully fond of Drag and Drop even with the slightly greater bother. We just think it helps a user clarify in their minds what they are doing, and not necessarily blithely clicking away with impunity.


Previous relevant Canvas Sharing via Email Hashtagging Tutorial is shown below.

Canvas Sharing via Email Hashtagging Tutorial

Canvas Sharing via Email Hashtagging Tutorial

We’re regular “tweakers” (for example, with Canvas via Image Web Share API Photo Postcard Tutorial) of what we call the …

  • canvas … based …
  • signatures … possible …
  • scribble … or …
  • discrete click … creating …
  • annotation … friendly …

… “graphics sharing” web application “User of Signature Signature”. And with us, more and more …

  • decoupling our PHP mail sharing conduits … in favour of …
  • “a” link “mailto:” email (and sometimes “sms:” SMS) communication conduits … now making use of …
  • hashtagging … parts to URLs used …

… because this methodology has fewer data limits, it being a “clientside” only (for the main part) means of sharing data. Today, we’re looking into this.

Happily, for this work, “the lynchpin” is some “signature_signature.js” external Javascript, which is all that needs tweaking to make these changes.

Previously, we’d relied, more, on “signature_signature.php” PHP, which oversaw our previous interfacing to the mail server up at the RJM Programming domain via the PHP mail function.

Now, the user sends their own emails with their own email client software, and emails contain one long body URL string where …

  • canvas … element’s …
  • [canvasElement].toDataURL(‘image/jpeg’, 0.7) … means by which a …
  • data URI representation of the graphical data

… can be hashtagged into that long body URL string. If the email recipient clicks this link, our external Javascript …

Reads such incoming data via location.hash

var azx=null, wowowo=null;
var imgbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('img=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('img=')[1].split('&')[0]).replace(/\ /g,'+') : '';
var passedsubject=location.search.split('subject=')[1] ? decodeURIComponent(location.search.split('subject=')[1].split('&')[0]) : '';
And loaded into canvas and new window …

setTimeout(function(){
if (imgbih != '') {
//alert(imgbih);
if (top.document.getElementById('topcanvas')) {
var xanimg=document.createElement('img');
xanimg.onload=function() {
var tc=top.document.getElementById('topcanvas').getContext('2d');
tc.drawImage(xanimg, 0, 0);
};
xanimg.src=imgbih;
}
var wowowo=window.open('','_blank','top=100,left=100,width=800,height=800');
wowowo.document.write('<html><body style="background:URL(' + "'" + imgbih + "'" + ');background-repeat:no-repeat;"></body></html>');
imgbih='';
if (passedsubject != '') {
wowowo.document.title='' + passedsubject;
}
}
}, 5000);
Before submitting form method=POST to signature_signature.php check out form fields

if (('' + document.getElementById('emailto').value).indexOf('@') != -1 && ('' + document.getElementById('contentto').value).indexOf('data:image/jpeg;base64,') != -1) {
azx=document.createElement("a");
document.body.appendChild(azx);
azx.style = "display: none";
azx.href = 'mailto:' + document.getElementById('emailto').value + ('?subject=' + encodeURIComponent(document.getElementById('subject').value)).replace(/subject\=$/g,'subject=My%20Canvas%20Creation') + '&body=' + encodeURIComponent(top.document.URL.split('?')[0].split('#')[0] + ('?subject=' + encodeURIComponent(document.getElementById('subject').value)).replace(/subject\=$/g,'subject=My%20Canvas%20Creation') + '#img=' + encodeURIComponent(document.getElementById('contentto').value));
azx.click();
} else {

document.getElementById('myform').submit();
}

… to enact fairly simple changes for more stable sharing and collaboration arrangements via a tweaked external Javascript signature_signature.js code used by our signature use in canvas supervisory web application.


Previous relevant Canvas via Image Web Share API Photo Postcard Tutorial is shown below.

Canvas via Image Web Share API Photo Postcard Tutorial

Canvas via Image Web Share API Photo Postcard Tutorial

We discovered, further to yesterday’s Canvas via Image Web Share API Personalization Tutorial, out and about, with an iPhone, that …

Image Input Mode Image Sizing Modus Operandi
URL Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)
Browsing Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)

… of Images to Canvas No Clicking Form Signature Tutorial times was worth refocussing on, regarding the combination of …

  • Browsing … Contain (canvas) … option above … and …
  • Take Photo … mobile device option … which, if followed through on, is more than likely to leave …
  • On the right hand side of the canvas there will be white space suitable to scribble a message to complete your …
  • Postcard … which you can optionally …
  • Share

… and if you are using the https: protocol with the Safari and Google Chrome web browser(s), we can help out more with that last step, being able to make a sharing button show the word “Postcard” …


function checkonyellow() {
var ibuta=[], jbuta=0;
if (safariblurb != '') {
if (document.getElementById('shareurl')) {
if (document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
} else if (parent.document.getElementById('shareurl')) {
if (parent.document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=parent.document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
} else if (top.document.getElementById('shareurl')) {
if (top.document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=top.document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
}
}
}

… to make the procedure …

Postcard via smart device Camera advice …

1. Touch browsing Choose File button that has just appeared.
2. Choose to Take Photo.
3. Consider any zooming in or selfie modes of use in Photo mode.
4. Take photo with Camera app white button, choosing Use Photo or Retake, as required.
5. Scribble any optional messaging in white area to right, or use other discreet touch functionalities.
6. Optionally email off to recipient (as textbox below goes yellow, click email sender button to its left for easier usage).

… more obvious, via the changed external Javascript signature_signature.js code used by our signature use in canvas supervisory web application.


Previous relevant Canvas via Image Web Share API Personalization Tutorial is shown below.

Canvas via Image Web Share API Tutorial

Canvas via Image Web Share API Personalization Tutorial

Yesterday’s Canvas via Imge Web Share API Tutorial‘s Web Share API (so far, on Safari and Google Chrome web browser(s)) image (via canvas) sharing functionality did not have enough privacy for the user, as far as we were concerned. We’re not going in for some two factor authentication level of privacy here, and so we’ve opted for the use of the word “Personalization” in today’s blog posting title.

What we’ve done here is incorporate into the image name (a form of) the IP address of the sender of the image, which the receiver is using (for that extra privacy) as they click the email link, that now involves (a form of) this IP address in its formation.

No surprises here, that we’d have to involve our changed PHP helper signature_signature.php “specialist signature PHP helper” more, getting this solution together.

And no surprises here, that we’d try to involve the web server’s disk areas outside the scope of its (Apache) Document Root hierarchy. That step, alone, is important to force a curious user, to work their way through an inhouse PHP webpage URL of some sort, in order to extract that …

  • image data in the form of a web server file … to a …
  • public domain webpage image in an HTML img tag where its src attribute is populated via that web server file’s equivalent base64 data URI …
    <?php

    echo "<html><body><img title='" . $thename . '' . "' src='data:image/webp;base64," . base64_encode(file_get_contents(DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . $thename . '')) . "'></img>" . $therest . "</body></html>";

    ?>

Am sure you are not surprised that today’s external Javascript web_share_api_test.js Web Share API specialist needed to change


function tctowebp() {
var ourcanvc='';
var canvs=[];
if (window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0) {
canvs=document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
if (eval('' + canvs.length) == 0 && window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
}
if (eval('' + canvs.length) > 0) {
ourcanvc=canvs[0].toDataURL('image/webp');
if (lastcanvc != ' ') {
if (lastcanvc != ourcanvc) {
lastcanvc=ourcanvc;
canvc=ourcanvc;
czhr = new XMLHttpRequest();
czform = new FormData();
czform.append('webpit', canvc);
if (newidea.indexOf('canvas.webp') == -1) { // eg. MAMP ... http://localhost:8888/signature_signature.php?mycanvasimage=0000__1
czform.append('websecurepit', '' + myipadwsi);
}

czhr.onreadystatechange=cstateChanged;
czhr.open('post', './signature_signature.php', true);
czhr.send(czform);
} else {
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='#f9f9f9';
}
}
}
lastcanvc=ourcanvc;
}
return ourcanvc;
}

… as well, regarding its incorporation of Web Share API logic into our inhouse User of Signature canvas based web application, best tried, these days, in the Safari and Google Chrome web browser(s) using https: (secure) protocol?!


Previous relevant Canvas via Imge Web Share API Tutorial is shown below.

Canvas via Image Web Share API Tutorial

Canvas via Imge Web Share API Tutorial

We’re on the lookout for ways to improve the sharing and collaboration functionality of yesterday’s Images to Canvas No Clicking Form Signature Tutorial‘s “User of Signature” canvas using web application. We’re calling on ..

  • Safari and Google Chrome web browser(s) … for the moment, supporting the …
  • Web Share API … in order to …
  • Fill out email (or other communication idea) attachment links or documents … that …
  • Prepare the message before you, the user, flesh out the message as you send it off to the recipient

… a more guaranteed way, these days, because there are more and more ways restrictions may stop you doing similar functionality with a mail server arrangement on your public domain website, where we use the Exim mail server, at the RJM Programming domain.

As far as data goes for this, we also call on our changed PHP helper signature_signature.php to create a webp image file for sharing …

<?php

if (isset($_POST['webpit'])) {
file_put_contents($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'canvas.webp', base64_decode(explode(";base64,", str_replace(' ','+',urldecode($_POST['webpit'])))[1]));
exit;
}

?>

… as applicable. The modus operandi for this back at today’s changed web_share_api_test.js external Javascript Web Share API specialist, uses Ajax methodologies …


var canvc=' ', lastcanvc=' ', czhr=null, czform=null;

function cstateChanged() {
if (czhr.readyState == 4) {
if (czhr.status == 200) {
if (1 == 2) { alert('Did it'); }
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='yellow';
}
}
}
}

function tctowebp() {
var ourcanvc='';
var canvs=[];
if (window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0) {
canvs=document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
if (eval('' + canvs.length) == 0 && window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
}
if (eval('' + canvs.length) > 0) {
ourcanvc=canvs[0].toDataURL('image/webp');
if (lastcanvc != ' ') {
if (lastcanvc != ourcanvc) {
lastcanvc=ourcanvc;
canvc=ourcanvc;
czhr = new XMLHttpRequest();
czform = new FormData();
czform.append('webpit', canvc);
czhr.onreadystatechange=cstateChanged;
czhr.open('post', './signature_signature.php', true);
czhr.send(czform);
} else {
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='#f9f9f9';
}
}
}
lastcanvc=ourcanvc;
}
return ourcanvc;
}

… and set in motion via a couple of different parental calls such as …


<script type='text/javascript' src='/web_share_api_test.js?canvasshare=as_necessary9876' defer></script>

… in …

… for cuter Safari and Google Chrome web browser sharing and collaboration work.


Previous relevant Images to Canvas No Clicking Form Signature Tutorial is shown below.

Images to Canvas No Clicking Form Signature Tutorial

Images to Canvas No Clicking Form Signature Tutorial

Surprisingly, it’s been more than a year since we changed anything to do with our …

  • canvas based …
  • images and annotation … accepting …
  • scribble (ie. can handle signatures) … perhaps …
  • form filling helping web application

… (when we presented Canvas Graphics Editing in Zoomed Webpage Tutorial) and we found that in order to add six more such Annotation option helpers into the mix …

Image Input Mode Image Sizing Modus Operandi
URL Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)
Browsing Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)

… we only needed to change the external Javascript signature_signature.js code used by our signature use in canvas supervisory web application so that when you can not ask so much of the user regarding image scaling, and avoid any canvas clicking that way, offering these six new annotation functionality options.


Previous relevant Canvas Graphics Editing in Zoomed Webpage Tutorial is shown below.

Canvas Graphics Editing in Zoomed Webpage Tutorial

Canvas Graphics Editing in Zoomed Webpage Tutorial

Software integration can be useful for a number of reasons …

  • code reuse
  • modularisation of functionality … and in our case …
  • if you like coding via “intervention points” you will enjoy coding for software integration improvements

As you might imagine it is easier in terms of the fact you can change “both ends” of the software integration, if it is “inhouse”, and luckily, today, our software integration is “inhouse”.

When we presented the recent Keyboard Based Cursor Canvas Content Copy Tutorial it focussed our attention on an improvement we could make to that user_of_signature_signature.htm canvas image editing web application. We wanted to offer the software integration with this web application to be able to successfully handle a webpage that has been zoomed to zoom factor different to 1.0 (or 100%).

We noticed before today’s very simple changes to external Javascript signature_signature.js code, graphic positioning on the canvas was wrong when the webpage had a zoom factor that is not 1.

Applying the canvas co-ordinate references through “the lens of” Javascript function zmb below …


var zzfac=location.search.split('zoom=')[1] ? decodeURIComponent(location.search.split('zoom=')[1].split('&')[0]) : "";
if (('' + location.hash).indexOf('zoom=') != -1) { zzfac=decodeURIComponent(('' + location.hash).split('zoom=')[1].split(';')[0]); }

function zmb(inco) {
if (zzfac != '') {
if (zzfac.indexOf('%') != -1) {
return eval(eval(eval('' + inco) / eval('' + zzfac.replace('%', ''))) * 100.0);
} else {
return eval(eval('' + inco) / eval('' + zzfac));
}
}
return inco;
}

used in the following example code below …


topelem.addEventListener('mousedown', function(event) {
if (topin) {
topin.value='- -' + isScribble + ',' + lastx + ',' + lasty + ',' + x + ',' + y;
}
if (isScribble == 1) {
threebackpmore=twobackpmore; twobackpmore=lastpmore; lastpmore=pmore; pmore=' pxam1 ';
isScribble=2;
if (event.pageX || event.pageY) {
ppx=zmb(event.pageX) - elemLeft;
ppy=zmb(event.pageY) - elemTop;
} else {
ppx=zmb(event.clientX) - elemLeft;
ppy=zmb(event.clientY) - elemTop;
}
if (pdgebi(topdcmcheck('dcm'))) {
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('text') == 0) {
//parent.document.title='here3 ' + tx + ',' + ty;
if (1 == 2 && tlx < 0 && tly < 0) {
tlx=ppx;
tly=ppy;
}
if (tx >= 0 && ty >= 0) {
prevtx=tx;
prevty=ty;
trotis=eval(((Math.atan2((ppy - prevty), (ppx - prevtx)) * 180.0 / Math.PI) + 360) % 360);
if (pdgebi('trotation')) {
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('textseq') == 0) {
jsrectbits[0]=prevtx;
jsrectbits[1]=prevty;
jsrectbits[2]=ppx;
jsrectbits[3]=ppy;
if (Math.abs(prevty - ppy) > Math.abs(prevtx - ppx)) {
trotis=eval(((Math.atan2((prevtx - ppx), (ppy - prevty)) * 180.0 / Math.PI) + 360) % 360);
}
}
parent.document.getElementById('trotation').value=Math.floor(trotis);
ppx=prevtx;
ppy=prevty;
prevtx=-1;
prevty=-1;
exceptwhen=endtrue(''); //true;
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('textseq') == -1) parent.document.getElementById(topdcmcheck('dcm')).value = 'Text';
}
//alert(trotis);
}
tx=ppx;
ty=ppy;
} else {
tx=ppx;
ty=ppy;
prevtx=-1;
prevty=-1;
}
} else {
tx=ppx;
ty=ppy;
prevtx=-1;
prevty=-1;
}
lastx=0; // new to scribble
lasty=0;
x=0;
y=0;
}
});

… we now have reasonable graphic positioning for the canvas when its webpage window has a non 1.0 zoom factor applied.


Previous relevant Keyboard Based Cursor Canvas Content Copy Tutorial is shown below.

Keyboard Based Cursor Canvas Content Copy Tutorial

Keyboard Based Cursor Canvas Content Copy Tutorial

You may have noticed with yesterday’s Keyboard Based Cursor Share Content Copy Tutorial, crucial to the sharing code, was the use of the incredible HTML5 introduced canvas element, helped by that middleperson link to those public email and SMS sharing conduits, overseeing the great random Lorem Picsum background images and user created emoji and/or text initial annotations.

And this got us wondering whether there was a private (ie. inhouse) web application of the past that could further value add as another optional middleperson tool for (user) personalization (of the content) purposes. And yes, do you remember the canvas work involved in the featured web application of Emoji Borders and Backgrounds Canvas Annotation Tutorial?

It uses an interesting approach with the transference of data across from a canvas in one application to the canvas in that user_of_signature_signature.htm web application. Once our changed body_mouse_deepdive.html‘s “parent” web application has organized its canvas contents, it’s just a matter of …


var awo=null;
awo=window.open('./user_of_signature_signature.htm','_blank','top=100,left=100,width=800,height=800');

for the “child” canvas annotator to effectively “suck up” the canvas data into its canvas via …


var cancont='';
var elemode=location.search.split('elemode=')[1] ? (location.search.split('elemode=')[1].split('&')[0]) : "canvas";

if (('' + window.opener).replace('null','') != '') {
var cans=window.opener.document.getElementsByTagName(elemode);
if (cans.length > 0) {
if (elemode == 'img') {
cancont=cans[0].src;
} else {
cancont=cans[0].toDataURL('image/png');
}
if (cancontw < 0 && canconth < 0) { cancontw=cans[0].width; canconth=cans[0].height; } } }

... possible because both "parent" and "child" exist on the same RJM Programming domain (web server).

Once there, another woooooorrrrrlllddd of image manipulations, via canvas, can await the user, perhaps ready for ongoing sharing possibilities with that "child".


Previous relevant Keyboard Based Cursor Share Content Copy Tutorial is shown below.

Keyboard Based Cursor Share Content Copy Tutorial

Keyboard Based Cursor Share Content Copy Tutorial

Yesterday's Keyboard Based Cursor Personalized Content Copy Tutorial has probably reached a point where some sharing mechanism is apt, for accountability and usefulness purposes, at the very least, regarding our recent web application allowing for foreground content on top of a random Lorem Picsum background image.

We've been helped out greatly by javascript - Can I take a screenshot from the clipboard? - Stack Overflow and html - How to render a blob on a canvas element? - Stack Overflow in the following new relevant Javascript code ...


function renderNoImage(canvas, blob) { // thanks to https://stackoverflow.com/questions/38004917/how-to-render-a-blob-on-a-canvas-element
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // This is important. If you are not using the blob, you should release it if you don't want to reuse it. It's good for memory.
ctx.drawImage(event.target, 0, 0)
img.style.display='none';
img.style.zIndex='-123';
};
img.src = URL.createObjectURL(blob);
}


function renderImage(canvas, blob) { // thanks to https://stackoverflow.com/questions/38004917/how-to-render-a-blob-on-a-canvas-element
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // This is important. If you are not using the blob, you should release it if you don't want to reuse it. It's good for memory.
ctx.drawImage(event.target, 0, 0)
};
img.src = URL.createObjectURL(blob);
}


addEventListener("paste", ev => { // thanks to https://stackoverflow.com/questions/55559432/can-i-take-a-screenshot-from-the-clipboard
for(const item of ev.clipboardData.items) { /// Clipboard may contain multiple elements of different type -- text, image, etc
if(item.type.startsWith("image/")) { /// We are only interested in clipboard data that is an image
if (emailee == '') {
emailee=prompt('Your clipboard has a useful image you could share the image with. Optionally, please enter an email address or SMS number to share with.');
if (emailee == null) { emailee=''; }
}
if (emailee.indexOf('@') != -1) {
document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="position:absolute;z-index:98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('aemail').href=document.getElementById('aemail').href.replace(':?', ':' + emailee + '?');
document.getElementById('aemail').click();
document.getElementById('mycanvas').innerHTML='';
} else if (emailee != '' && emailee.replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '') {
document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="position:absolute;z-index:98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('asms').href=document.getElementById('asms').href.replace(':&', ':' + emailee + '&');
document.getElementById('asms').click();
document.getElementById('mycanvas').innerHTML='';
} else {
//document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="display:none;position:absolute;z-index:-98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderNoImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('mycanvas').innerHTML='';
}
}
}
});

... which is explained to the user in the following way ...

Share with email or SMS happens via screenshots (macOS control-command-shift-3 or Windows Prnt-Scrn) followed by File menu Paste option causing chance to share after which File menu Undo can remove pasted part as required.

... in the changed "proof of concept" body_mouse_deepdive.html live run.


Previous relevant Keyboard Based Cursor Personalized Content Copy Tutorial is shown below.

Keyboard Based Cursor Personalized Content Copy Tutorial

Keyboard Based Cursor Personalized Content Copy Tutorial

On top of yesterday's Keyboard Based Cursor Content Copy Tutorial we wanted to offer the user the chance for them to tailor their foreground content on top of the Lorem Picsum background image.

The user can enter this via the keyboard because there is an HTML div contenteditable=true pallette to work with ...


<div id=mydiv onfocus='setTimeout(alte, 1000);' ondblclick="this.innerHTML='';" onclick='stamp(event);' title='Click and type and see the cursor change as you use Alt or Shift or Control that can be reset via Caps Lock with a click copying cursor into content ... RJM Programming ... May, 2022 ... thanks to https://picsum.photos/ Lorem Picsum You can change emojis if you know the \002602 form of UTF-16 (hex) entry (our example is umbrella and if you express it as \\002602 that leaves cursor just as your emoji). Double click existant foreground emoji element to clear all the foreground emoji elements.' spellcheck='false' contenteditable=true style='width:100vw;height:100%;color:transparent;text-color:transparent;' onkeypress=kpcursorlook(event); onkeydown=cursorlook(event); onmousemove=xycursorlook(event); ontouchmove=xycursorlook(event);></div>

It's "title" attribute now talks about two new pieces of functionality, those being ...

  • You can change emojis if you know the \002602 form of UTF-16 (hex) entry (our example is umbrella and if you express it as \\002602 that leaves cursor just as your emoji).
  • Double click existant foreground emoji element to clear all the foreground emoji elements.

The choice of keyboard input methodology also suits CSS "content" property definitions as one backslash followed by a hexidecimal value left padded with zeroes to a length of six. See us doing this below for an umbrella emoji (which could also be expressed in an HTML hexadecimal entity way &#x2602; ☂ the information for which you can find at Unicode Character 'UMBRELLA' (U+2602) we normally get to by entering "U+2602" at a web browser address bar, the knowledge for which we arrive at via entering "umbrella emojipedia" and moving down the top link's webpage to find the "U+2602") ...


<style> #myumbrella::after { content: '\002602'; } </style>

... that feeds into some new "onkeypress" keyboard event logic ...


function kpcursorlook(evt) {
var atofa=['a','b','c','d','e','f'];
var rgbit='';
if (evt.keyCode == 92) {
if (newu.length == 1) { gro=true; } else { gro=false; }
newu=("\\").substring(0,1);
} else if (evt.keyCode >= 48 && evt.keyCode <= 57) {
newu+=('' + eval(-48 + evt.keyCode));
} else if (evt.keyCode >= 65 && evt.keyCode <= 70) {
newu+=('' + atofa[eval(-65 + evt.keyCode)]);
} else if (evt.keyCode >= 97 && evt.keyCode <= 102) {
newu+=('' + atofa[eval(-97 + evt.keyCode)]);
}
if (newu.length == 7) {
if (lastcursor.indexOf(("\\\\").substring(0,2)) != -1) {
rgbit=lastcursor.split(("\\\\").substring(0,2))[1].substring(6);
lastcursor=lastcursor.split(("\\\\").substring(0,2))[0] + ("\\").substring(0,1) + newu + rgbit;
document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
} else if (lastcursor.indexOf(("\\").substring(0,1)) != -1) {
rgbit=lastcursor.split(("\\").substring(0,1))[1].substring(6);
lastcursor=lastcursor.split(("\\").substring(0,1))[0] + newu + rgbit;
if (gro) {
lastcursor=lastcursor.replace('>Shift/Alt/Ctrl', '>').replace('>Shift', '>').replace('>Alt', '>').replace('>Ctrl', '>').replace(" width='126'", " width='20'").replace(" width='66'", " width='20'").replace(" height='48'", " height='20'").replace('0 0 100 100', '0 0 20 20');
}
document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
}
}
// console.log(evt.keyCode);
}

... in the changed "proof of concept" body_mouse_deepdive.html live run.

Stop Press

Two more "title" attribute pieces of functionality are now ...

  • With complex emoji entries do not zero leftpad until your last simple entity eg. \\1F6A3\200D\2640\00FE0F could define a Woman Rowing complex emoji.
  • Optionally make emojis bigger after \ with + two time multipliers.

The choice of keyboard input methodology also suits CSS "content" property definitions as one backslash followed by "first off" sets of HTML entities they should not left zero pad in their entry, followed by a last HTML entity hexidecimal value left padded with zeroes to a length of six to finish up with. See us doing this below for a woman rowing emoji (which could also be expressed in an HTML hexadecimal entity way &#x1f6a3;&#x0200d;&#x02640;&#x0fe0f; the information for which you can find at 🚣‍♀️ Woman Rowing Boat Emoji we normally get to by entering "woman rowing emoji" at the web browser address bar) ...


<style> #myrowing::after { content: '\01f6a3\00200d\002640\00fe0f'; } </style>

... in the changed "proof of concept" body_mouse_deepdive.html live run.


Previous relevant Keyboard Based Cursor Content Copy Tutorial is shown below.

Keyboard Based Cursor Content Copy Tutorial

Keyboard Based Cursor Content Copy Tutorial

The "onkeydown" keyboard event can involve in its logic three mouse event (event object) property usages that caught our interest recently ...

... and we wanted to have "keyboard" meets "mouse" events, along with the brilliance of Lorem Picsum regarding background image randomosity and quality, working with HTML ...


<div id=mydiv onfocus='setTimeout(alte, 1000);' onclick='stamp(this);' title='Click and type and see the cursor change as you use Alt or Shift or Control that can be reset via Caps Lock with a click copying cursor into content ... RJM Programming ... May, 2022 ... thanks to https://picsum.photos/ Lorem Picsum' spellcheck='false' contenteditable=true style='width:100vw;height:100%;color:transparent;text-color:transparent;' data-onkeypress=xycursorlook(event); onkeydown=cursorlook(event); onmousemove=xycursorlook(event); ontouchmove=xycursorlook(event);></div>

... in a way we hadn't tried before that called on some inhouse cursor SVG creations (and so is, alas, just a non-mobile fully featured experience), in the sense that ...

  1. keyboard helps creating the "what" regarding content ...

    <script type='text/javascript'>
    var lastkeymodifier=''
    var lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function cursorlook(evt) {
    if (evt.altKey) {
    if (lastkeymodifier != 'alt') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,0,255,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Alt\\01f3d5</text></svg>\") 16 0, progress";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='alt';
    }
    } else if (evt.ctrlKey) {
    if (lastkeymodifier != 'ctrl') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Ctrl\\01f333</text></svg>\") 16 0, pointer";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='ctrl';
    }
    } else if (evt.shiftKey) {
    if (lastkeymodifier != 'shift') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(255,0,0,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Shift\\01f389</text></svg>\") 16 0, grab";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='shift';
    }
    } else {
    if (lastkeymodifier != '') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='';
    }
    }
    }
    </script>
  2. mouse "onmousemove" or "ontouchmove" helps with the "where" regarding content above ...

    <script type='text/javascript'>
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function xycursorlook(e) {
    e = e || window.event;
    e.preventDefault();

    if (e.touches) {
    if (e.touches[0].pageX) {
    pos3 = e.touches[0].pageX;
    pos4 = e.touches[0].pageY;
    } else {
    pos3 = e.touches[0].clientX;
    pos4 = e.touches[0].clientY;
    }
    //console.log('pos3=' + pos3 + ',pos4=' + pos4);
    } else if (e.clientX || e.clientY) {
    pos3 = e.clientX;
    pos4 = e.clientY;
    } else {
    pos3 = e.pageX;
    pos4 = e.pageY;
    }
    }
    </script>
  3. preparations for "onclick" way a cursor can be plonked into (real lasting) content ...

    <script type='text/javascript'>
    var lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function stamp(divo) {
    if (('' + pos3).indexOf('-') == -1) {
    if (allowable) {
    //if (allowed) {
    //allowed=false;
    //setTimeout(reseta, 2000);
    divo.innerHTML+='<div id=div' + subdiv + ' style="position:absolute;top:' + pos4 + 'px;left:' + pos3 + 'px;width:126px;height:48px;background-color:transparent;"></div><style> #div' + subdiv + ' { background:' + lastcursor.split(') ')[0] + ') no-repeat; } </style>';
    subdiv++;
    //console.log('lastpos4=' + lastpos4 + ' and pos4=' + pos4);
    //console.log('lastpos3=' + lastpos3 + ' and pos3=' + pos3);
    lastpos3=pos3;
    lastpos4=pos4;
    //}
    }
    }
    }
    </script>

... with the "proof of concept" body_mouse_deepdive.html live run.

If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.

Posted in Animation, eLearning, Event-Driven Programming, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a comment

Canvas Drag and Drop Ephemeral Tutorial

Canvas Drag and Drop Ephemeral Tutorial

Canvas Drag and Drop Ephemeral Tutorial

Onto yesterday’s Canvas Drag and Drop Primer Tutorial‘s …

  • proof of concept feel to being able to drag and drop via the canvas element … today we have …
  • proof of concept ideas regarding future sharing and collaboration aspects to this Drag and Drop where we start with our “ephemeral” inhouse popup window version of what we hope to offer, in future, for users to share with email recipients

And the other progress was another “drag” friend for the canvas element, allowing that textarea element (now with ondragstart=”dragstart(event);” draggable=true attribution) down the bottom right also be “draggable” (to create the textarea wording on a white backgrounded slide “Drop Zone” result when dragged there) for our …

  • “Drop Zone” for non-mobile … and …
  • “Click Zone” for mobile

… platforms. The drag and drop functionality related global variables now go …


var dz='Drop Zone';
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
dz='Click Zone';
}
var dzslides=[], dzo=null, dzcnt=0;
var lastdrop='', lastelem=null, wasmt='0px';

… and the Drag and Drop logic now goes


function dragstart(ev) {
lastelem=ev.target;
if (('' + ev.target.outerHTML).indexOf('<textarea') == 0) {
ev.dataTransfer.setData("text/plain", ev.target.value);
} else {

ev.dataTransfer.setData("text/plain", document.getElementById('topcanvas').toDataURL());
}
}

function dragover(ev) {
ev.preventDefault();
}

function drop(ev) {
var prefixingih='', mtdone=false, opsty='';
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
if (('' + lastelem.outerHTML).indexOf('<textarea') == 0) {
lastdrop=lastelem.value;
while (lastdrop.indexOf(String.fromCharCode(10)) != -1) {
lastdrop=lastdrop.replace(String.fromCharCode(10),'<br>');
}
prefixingih='<span style=background-color:white;>' + lastdrop + '</span><br><br>';
opsty=' style="opacity:0.3;" ';
lastdrop=dzslides[0];
document.getElementById('target').innerHTML = dzslides[0];
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop='0px';
}
} else {

document.getElementById('target').innerHTML = document.getElementById('topcanvas').toDataURL();
lastdrop=document.getElementById('target').innerHTML;
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop=wasmt;
}
}
} else {
if (('' + lastelem.outerHTML).indexOf('<textarea') == 0) {
lastdrop=lastelem.value;
while (lastdrop.indexOf(String.fromCharCode(10)) != -1) {
lastdrop=lastdrop.replace(String.fromCharCode(10),'<br>');
}
prefixingih='<span style=background-color:white;>' + lastdrop + '</span><br><br>';
opsty=' style="opacity:0.3;" ';
lastdrop=dzslides[0];
document.getElementById('target').innerHTML = dzslides[0];
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop='0px';
}
} else {
ev.target.innerHTML = ev.dataTransfer.getData("text");
lastdrop=ev.target.innerHTML;
if (document.getElementById('tarbut')) {
mtdone=true;
document.getElementById('tarbut').style.marginTop=wasmt;
}
}
}

document.getElementById('topcanvas').setAttribute('draggable','false');
document.getElementById('target').innerHTML=prefixingih + '<button ' + opsty + ' id=tarbut onclick=\" event.stopPropagation(); if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { drop(document.getElementById(' + "'target'" + ')); } else { document.getElementById(' + "'topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML=''; if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) { document.getElementById('starbut').style.zIndex='50'; } else { document.getElementById('starbut').style.marginTop='-50px'; document.getElementById('starbut').style.zIndex='-50'; } document.getElementById('starbut').innerHTML='" + dz + "'" + '; } \">' + dz + ' for Canvas After Click</button>';
if (document.getElementById('tarbut') && !mtdone) {
mtdone=true;
if (prefixingih != '') {
document.getElementById('tarbut').style.marginTop='0px';
} else {
document.getElementById('tarbut').style.marginTop=wasmt;
}
}

document.getElementById('starbut').innerHTML='';
var rectis=document.getElementById('target').getBoundingClientRect();
if (document.getElementById('mydbut')) {
rectis=document.getElementById('mydbut').getBoundingClientRect();
} else if (document.getElementById('tarbut')) {
rectis=document.getElementById('tarbut').getBoundingClientRect();
}
document.getElementById('target').setAttribute('data-mt','' + eval(-30 + eval('' + rectis.width)));
document.getElementById('target').style.backgroundColor='white';
document.getElementById('target').style.height='' + rectis.width + 'px';
document.getElementById('target').style.backgroundImage='url(' + lastdrop + ')';
document.getElementById('target').style.backgroundSize='contain';
document.getElementById('target').style.backgroundRepeat='no-repeat';
if (document.getElementById('topdragstyle').innerHTML == '' && prefixingih == '') {
wasmt='' + eval(-30 + eval('' + rectis.width)) + 'px';
document.getElementById('tarbut').style.marginTop=wasmt;
//var rectistb=document.getElementById('tarbut').getBoundingClientRect();
//document.getElementById('topdragstyle').innerHTML='<style> #tarbut { position:absolute; left:' + rectistb.left + 'px; top:' + eval(eval(0 * eval(-30 + eval('' + rectis.width))) + eval('' + rectistb.top)) + 'px; margin-top:0px !important; } </style>';
}
if (document.getElementById('tarbut')) {
dzslides.push(document.getElementById('target').outerHTML.replace(document.getElementById('tarbut').outerHTML,''));
} else {
dzslides.push(document.getElementById('target').outerHTML);
}
if (eval('' + dzslides.length) > 2) {
var htmlis='';
if (dzo == null) {
dzo=window.open('','_blank','top=200,left=200,width=300,height=300');
htmlis='<ht' + 'ml><he' + 'ad><title>Inhouse Animated GIF style presentation ...</title><scri' + 'pt type=text/javascript> var curslides=0, thislag=0; function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdzfirst(); }, thislag); } else { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdznext(); }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div></bo' + 'dy></ht' + 'ml>';
dzo.document.write(htmlis);
dzo.document.title='Inhouse Animated GIF style presentation ...';
} //else if (dzo.closed) {
// dzo=window.open('','_blank','top=200,left=200,width=300,height=300');
// htmlis='<ht' + 'ml><he' + 'ad><scri' + 'pt type=text/javascript> var curslides=0, thislag=0; function shows() { thislag=1; curslides=window.opener.getdzlen(); for (var ii=0; ii<curslides; ii++) { if (ii == 0) { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdzfirst(); }, thislag); } else { setTimeout(function() { document.getElementById("ptarget").innerHTML=window.opener.getdznext(); }, thislag); } thislag+=4000; } setTimeout(shows, thislag); } setTimeout(shows,5000); </scr' + 'ipt></he' + 'ad><bo' + 'dy data-onload="setTimeout(shows,5000);"><div id=ptarget></div></bo' + 'dy></ht' + 'ml>';
// dzo.document.write(htmlis);
//}
}

}

function getdzlen() {
return eval('' + dzslides.length);
}

function getdzfirst() {
var iwhich=0;
dzcnt=1;
if (iwhich == 0) {
//alert(document.getElementById('target').outerHTML);
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('url(' + document.getElementById('target').outerHTML.split('url(')[1].split(')')[0] + ')', 'url(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}

function getdznext() {
var iwhich=dzcnt;
dzcnt++;
if (iwhich == 0) {
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('url(' + document.getElementById('target').outerHTML.split('url(')[1].split(')')[0] + ')', 'url(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}

function getdz(iwhich) {
if (iwhich == 0) {
//alert(document.getElementById('target').outerHTML);
var pone=document.getElementById('target').outerHTML.split('>')[0].replace('URL(' + document.getElementById('target').outerHTML.split('URL(')[1].split(')')[0] + ')', 'URL(' + dzslides[iwhich] + ')');
return pone + '></div>';
}
if (('' + dzslides[iwhich]) == 'undefined') { return ''; }
return dzslides[iwhich];
}


setTimeout(function() {
if (elem) {
if (eval('' + dzslides.length) == 0) {
dzslides.push(elem.toDataURL());
}
} else if (document.getElementById('topcanvas')) {
if (eval('' + dzslides.length) == 0) {
dzslides.push(document.getElementById('topcanvas').toDataURL());
}
}
}, 5000);

… in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application.


Previous relevant Canvas Drag and Drop Primer Tutorial is shown below.

Canvas Drag and Drop Primer Tutorial

Canvas Drag and Drop Primer Tutorial

It’s time. It really is. It’s time to revisit the “User of Signature Signature” web application using the HTML5 canvas element to create and share graphical data, that we last dealt with when we presented Canvas Sharing via Email Hashtagging Tutorial.

Why? Well, we want to combine …

  • canvas … graphical data … to start with … with …
  • drag and drop methodologies

… to add to the functionality of our “User of Signature Signature” web application. Today is the “proof of concept” day because there are two conflicting issues for us …

  • scribble functionality on a canvas element … is at odds with …
  • the draggable=true attribute usage crucial to getting Drag and Drop methodologies to kick in

You may have noticed with the “User of Signature Signature” web application it starts ready to scribble on the canvas straight away. And do you think we can resist that, as we test it?! No way!

That same steadiness to that canvas, as it is set up to resist “internal movement” also stops it being draggable alas … depending how you look at it.

And so we thought about this and came up with …

  1. do the usual this useful link suggests about setting up the canvas element for Drag and Drop …

    <canvas ondragstart="dragstart(event);" draggable=truefalse title="Ready for you to create your canvas 🎨 content above any signature 💳 panel ..." id="topcanvas" width=850 height=600 style="background-color:white; position: absolute; top:0; left:0; border-bottom:5px solid yellow; border-right:5px solid blue;"></canvas>

    … except start the draggable attribute with value false … and team this with …
  2. new Drop Zone HTML element helpers …

    <div id="target" ondrop="drop(event);" ondragover="dragover(event);" data-mt="0" style="vertical-align:bottom;display:block;background-color:transparent;">
    <button id=mydbut onclick=" document.getElementById('topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML='<span id=tarbut style=margin-top:' + document.getElementById('target').getAttribute('data-mt') + 'px;>Drop Zone</span>'; ">Drop Zone for Canvas After Click</button>
    </div>
    <span id=starbut></span>

    … and …
  3. programmatically controlled HTML button element click logic …

    var lastdrop='';

    function dragstart(ev) {
    ev.dataTransfer.setData("text/plain", document.getElementById('topcanvas').toDataURL());
    }

    function dragover(ev) {
    ev.preventDefault();
    }

    function drop(ev) {
    ev.target.innerHTML = ev.dataTransfer.getData("text");
    lastdrop=ev.target.innerHTML;
    document.getElementById('topcanvas').setAttribute('draggable','false');
    document.getElementById('target').innerHTML='<button id=tarbut onclick=\" document.getElementById(' + "'topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML=''; document.getElementById('starbut').style.marginTop='-50px'; document.getElementById('starbut').style.zIndex='-50'; document.getElementById('starbut').innerHTML='Drop Zone'" + '; \">Drop Zone for Canvas After Click</button>';
    document.getElementById('starbut').innerHTML='';
    var rectis=document.getElementById('target').getBoundingClientRect();
    if (document.getElementById('mydbut')) {
    rectis=document.getElementById('mydbut').getBoundingClientRect();
    } else if (document.getElementById('tarbut')) {
    rectis=document.getElementById('tarbut').getBoundingClientRect();
    }
    document.getElementById('target').setAttribute('data-mt','' + eval(-30 + eval('' + rectis.width)));
    document.getElementById('target').style.height='' + rectis.width + 'px';
    document.getElementById('target').style.backgroundImage='URL(' + lastdrop + ')';
    document.getElementById('target').style.backgroundSize='contain';
    document.getElementById('target').style.backgroundRepeat='no-repeat';
    if (document.getElementById('topdragstyle').innerHTML == '') {
    document.getElementById('tarbut').style.marginTop='' + eval(-30 + eval('' + rectis.width)) + 'px';
    //var rectistb=document.getElementById('tarbut').getBoundingClientRect();
    //document.getElementById('topdragstyle').innerHTML='<style> #tarbut { position:absolute; left:' + rectistb.left + 'px; top:' + eval(eval(0 * eval(-30 + eval('' + rectis.width))) + eval('' + rectistb.top)) + 'px; margin-top:0px !important; } </style>';
    }
    }

    … to toggle between the modes of use

… in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application.

Like we say, “proof of concept” (and mobile platforms need more thinking, so see Stop Press below), today, ahead of “usefulness” to come!

Stop Press

We know (again, via this useful link) from simple canvas scenarios Drag and Drop can work for mobile, but we couldn’t get it to work for our canvas used for scribbling and other annotation purposes. And so, in our further changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application we got some “onclick” event replacement code working for mobile platforms involving less work for the user. Some may see this as an enhancement, but personally, we’re awfully fond of Drag and Drop even with the slightly greater bother. We just think it helps a user clarify in their minds what they are doing, and not necessarily blithely clicking away with impunity.


Previous relevant Canvas Sharing via Email Hashtagging Tutorial is shown below.

Canvas Sharing via Email Hashtagging Tutorial

Canvas Sharing via Email Hashtagging Tutorial

We’re regular “tweakers” (for example, with Canvas via Image Web Share API Photo Postcard Tutorial) of what we call the …

  • canvas … based …
  • signatures … possible …
  • scribble … or …
  • discrete click … creating …
  • annotation … friendly …

… “graphics sharing” web application “User of Signature Signature”. And with us, more and more …

  • decoupling our PHP mail sharing conduits … in favour of …
  • “a” link “mailto:” email (and sometimes “sms:” SMS) communication conduits … now making use of …
  • hashtagging … parts to URLs used …

… because this methodology has fewer data limits, it being a “clientside” only (for the main part) means of sharing data. Today, we’re looking into this.

Happily, for this work, “the lynchpin” is some “signature_signature.js” external Javascript, which is all that needs tweaking to make these changes.

Previously, we’d relied, more, on “signature_signature.php” PHP, which oversaw our previous interfacing to the mail server up at the RJM Programming domain via the PHP mail function.

Now, the user sends their own emails with their own email client software, and emails contain one long body URL string where …

  • canvas … element’s …
  • [canvasElement].toDataURL(‘image/jpeg’, 0.7) … means by which a …
  • data URI representation of the graphical data

… can be hashtagged into that long body URL string. If the email recipient clicks this link, our external Javascript …

Reads such incoming data via location.hash

var azx=null, wowowo=null;
var imgbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('img=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('img=')[1].split('&')[0]).replace(/\ /g,'+') : '';
var passedsubject=location.search.split('subject=')[1] ? decodeURIComponent(location.search.split('subject=')[1].split('&')[0]) : '';
And loaded into canvas and new window …

setTimeout(function(){
if (imgbih != '') {
//alert(imgbih);
if (top.document.getElementById('topcanvas')) {
var xanimg=document.createElement('img');
xanimg.onload=function() {
var tc=top.document.getElementById('topcanvas').getContext('2d');
tc.drawImage(xanimg, 0, 0);
};
xanimg.src=imgbih;
}
var wowowo=window.open('','_blank','top=100,left=100,width=800,height=800');
wowowo.document.write('<html><body style="background:URL(' + "'" + imgbih + "'" + ');background-repeat:no-repeat;"></body></html>');
imgbih='';
if (passedsubject != '') {
wowowo.document.title='' + passedsubject;
}
}
}, 5000);
Before submitting form method=POST to signature_signature.php check out form fields

if (('' + document.getElementById('emailto').value).indexOf('@') != -1 && ('' + document.getElementById('contentto').value).indexOf('data:image/jpeg;base64,') != -1) {
azx=document.createElement("a");
document.body.appendChild(azx);
azx.style = "display: none";
azx.href = 'mailto:' + document.getElementById('emailto').value + ('?subject=' + encodeURIComponent(document.getElementById('subject').value)).replace(/subject\=$/g,'subject=My%20Canvas%20Creation') + '&body=' + encodeURIComponent(top.document.URL.split('?')[0].split('#')[0] + ('?subject=' + encodeURIComponent(document.getElementById('subject').value)).replace(/subject\=$/g,'subject=My%20Canvas%20Creation') + '#img=' + encodeURIComponent(document.getElementById('contentto').value));
azx.click();
} else {

document.getElementById('myform').submit();
}

… to enact fairly simple changes for more stable sharing and collaboration arrangements via a tweaked external Javascript signature_signature.js code used by our signature use in canvas supervisory web application.


Previous relevant Canvas via Image Web Share API Photo Postcard Tutorial is shown below.

Canvas via Image Web Share API Photo Postcard Tutorial

Canvas via Image Web Share API Photo Postcard Tutorial

We discovered, further to yesterday’s Canvas via Image Web Share API Personalization Tutorial, out and about, with an iPhone, that …

Image Input Mode Image Sizing Modus Operandi
URL Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)
Browsing Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)

… of Images to Canvas No Clicking Form Signature Tutorial times was worth refocussing on, regarding the combination of …

  • Browsing … Contain (canvas) … option above … and …
  • Take Photo … mobile device option … which, if followed through on, is more than likely to leave …
  • On the right hand side of the canvas there will be white space suitable to scribble a message to complete your …
  • Postcard … which you can optionally …
  • Share

… and if you are using the https: protocol with the Safari and Google Chrome web browser(s), we can help out more with that last step, being able to make a sharing button show the word “Postcard” …


function checkonyellow() {
var ibuta=[], jbuta=0;
if (safariblurb != '') {
if (document.getElementById('shareurl')) {
if (document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
} else if (parent.document.getElementById('shareurl')) {
if (parent.document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=parent.document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
} else if (top.document.getElementById('shareurl')) {
if (top.document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=top.document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
}
}
}

… to make the procedure …

Postcard via smart device Camera advice …

1. Touch browsing Choose File button that has just appeared.
2. Choose to Take Photo.
3. Consider any zooming in or selfie modes of use in Photo mode.
4. Take photo with Camera app white button, choosing Use Photo or Retake, as required.
5. Scribble any optional messaging in white area to right, or use other discreet touch functionalities.
6. Optionally email off to recipient (as textbox below goes yellow, click email sender button to its left for easier usage).

… more obvious, via the changed external Javascript signature_signature.js code used by our signature use in canvas supervisory web application.


Previous relevant Canvas via Image Web Share API Personalization Tutorial is shown below.

Canvas via Image Web Share API Tutorial

Canvas via Image Web Share API Personalization Tutorial

Yesterday’s Canvas via Imge Web Share API Tutorial‘s Web Share API (so far, on Safari and Google Chrome web browser(s)) image (via canvas) sharing functionality did not have enough privacy for the user, as far as we were concerned. We’re not going in for some two factor authentication level of privacy here, and so we’ve opted for the use of the word “Personalization” in today’s blog posting title.

What we’ve done here is incorporate into the image name (a form of) the IP address of the sender of the image, which the receiver is using (for that extra privacy) as they click the email link, that now involves (a form of) this IP address in its formation.

No surprises here, that we’d have to involve our changed PHP helper signature_signature.php “specialist signature PHP helper” more, getting this solution together.

And no surprises here, that we’d try to involve the web server’s disk areas outside the scope of its (Apache) Document Root hierarchy. That step, alone, is important to force a curious user, to work their way through an inhouse PHP webpage URL of some sort, in order to extract that …

  • image data in the form of a web server file … to a …
  • public domain webpage image in an HTML img tag where its src attribute is populated via that web server file’s equivalent base64 data URI …
    <?php

    echo "<html><body><img title='" . $thename . '' . "' src='data:image/webp;base64," . base64_encode(file_get_contents(DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . $thename . '')) . "'></img>" . $therest . "</body></html>";

    ?>

Am sure you are not surprised that today’s external Javascript web_share_api_test.js Web Share API specialist needed to change


function tctowebp() {
var ourcanvc='';
var canvs=[];
if (window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0) {
canvs=document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
if (eval('' + canvs.length) == 0 && window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
}
if (eval('' + canvs.length) > 0) {
ourcanvc=canvs[0].toDataURL('image/webp');
if (lastcanvc != ' ') {
if (lastcanvc != ourcanvc) {
lastcanvc=ourcanvc;
canvc=ourcanvc;
czhr = new XMLHttpRequest();
czform = new FormData();
czform.append('webpit', canvc);
if (newidea.indexOf('canvas.webp') == -1) { // eg. MAMP ... http://localhost:8888/signature_signature.php?mycanvasimage=0000__1
czform.append('websecurepit', '' + myipadwsi);
}

czhr.onreadystatechange=cstateChanged;
czhr.open('post', './signature_signature.php', true);
czhr.send(czform);
} else {
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='#f9f9f9';
}
}
}
lastcanvc=ourcanvc;
}
return ourcanvc;
}

… as well, regarding its incorporation of Web Share API logic into our inhouse User of Signature canvas based web application, best tried, these days, in the Safari and Google Chrome web browser(s) using https: (secure) protocol?!


Previous relevant Canvas via Imge Web Share API Tutorial is shown below.

Canvas via Image Web Share API Tutorial

Canvas via Imge Web Share API Tutorial

We’re on the lookout for ways to improve the sharing and collaboration functionality of yesterday’s Images to Canvas No Clicking Form Signature Tutorial‘s “User of Signature” canvas using web application. We’re calling on ..

  • Safari and Google Chrome web browser(s) … for the moment, supporting the …
  • Web Share API … in order to …
  • Fill out email (or other communication idea) attachment links or documents … that …
  • Prepare the message before you, the user, flesh out the message as you send it off to the recipient

… a more guaranteed way, these days, because there are more and more ways restrictions may stop you doing similar functionality with a mail server arrangement on your public domain website, where we use the Exim mail server, at the RJM Programming domain.

As far as data goes for this, we also call on our changed PHP helper signature_signature.php to create a webp image file for sharing …

<?php

if (isset($_POST['webpit'])) {
file_put_contents($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'canvas.webp', base64_decode(explode(";base64,", str_replace(' ','+',urldecode($_POST['webpit'])))[1]));
exit;
}

?>

… as applicable. The modus operandi for this back at today’s changed web_share_api_test.js external Javascript Web Share API specialist, uses Ajax methodologies …


var canvc=' ', lastcanvc=' ', czhr=null, czform=null;

function cstateChanged() {
if (czhr.readyState == 4) {
if (czhr.status == 200) {
if (1 == 2) { alert('Did it'); }
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='yellow';
}
}
}
}

function tctowebp() {
var ourcanvc='';
var canvs=[];
if (window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0) {
canvs=document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
if (eval('' + canvs.length) == 0 && window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
}
if (eval('' + canvs.length) > 0) {
ourcanvc=canvs[0].toDataURL('image/webp');
if (lastcanvc != ' ') {
if (lastcanvc != ourcanvc) {
lastcanvc=ourcanvc;
canvc=ourcanvc;
czhr = new XMLHttpRequest();
czform = new FormData();
czform.append('webpit', canvc);
czhr.onreadystatechange=cstateChanged;
czhr.open('post', './signature_signature.php', true);
czhr.send(czform);
} else {
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='#f9f9f9';
}
}
}
lastcanvc=ourcanvc;
}
return ourcanvc;
}

… and set in motion via a couple of different parental calls such as …


<script type='text/javascript' src='/web_share_api_test.js?canvasshare=as_necessary9876' defer></script>

… in …

… for cuter Safari and Google Chrome web browser sharing and collaboration work.


Previous relevant Images to Canvas No Clicking Form Signature Tutorial is shown below.

Images to Canvas No Clicking Form Signature Tutorial

Images to Canvas No Clicking Form Signature Tutorial

Surprisingly, it’s been more than a year since we changed anything to do with our …

  • canvas based …
  • images and annotation … accepting …
  • scribble (ie. can handle signatures) … perhaps …
  • form filling helping web application

… (when we presented Canvas Graphics Editing in Zoomed Webpage Tutorial) and we found that in order to add six more such Annotation option helpers into the mix …

Image Input Mode Image Sizing Modus Operandi
URL Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)
Browsing Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)

… we only needed to change the external Javascript signature_signature.js code used by our signature use in canvas supervisory web application so that when you can not ask so much of the user regarding image scaling, and avoid any canvas clicking that way, offering these six new annotation functionality options.


Previous relevant Canvas Graphics Editing in Zoomed Webpage Tutorial is shown below.

Canvas Graphics Editing in Zoomed Webpage Tutorial

Canvas Graphics Editing in Zoomed Webpage Tutorial

Software integration can be useful for a number of reasons …

  • code reuse
  • modularisation of functionality … and in our case …
  • if you like coding via “intervention points” you will enjoy coding for software integration improvements

As you might imagine it is easier in terms of the fact you can change “both ends” of the software integration, if it is “inhouse”, and luckily, today, our software integration is “inhouse”.

When we presented the recent Keyboard Based Cursor Canvas Content Copy Tutorial it focussed our attention on an improvement we could make to that user_of_signature_signature.htm canvas image editing web application. We wanted to offer the software integration with this web application to be able to successfully handle a webpage that has been zoomed to zoom factor different to 1.0 (or 100%).

We noticed before today’s very simple changes to external Javascript signature_signature.js code, graphic positioning on the canvas was wrong when the webpage had a zoom factor that is not 1.

Applying the canvas co-ordinate references through “the lens of” Javascript function zmb below …


var zzfac=location.search.split('zoom=')[1] ? decodeURIComponent(location.search.split('zoom=')[1].split('&')[0]) : "";
if (('' + location.hash).indexOf('zoom=') != -1) { zzfac=decodeURIComponent(('' + location.hash).split('zoom=')[1].split(';')[0]); }

function zmb(inco) {
if (zzfac != '') {
if (zzfac.indexOf('%') != -1) {
return eval(eval(eval('' + inco) / eval('' + zzfac.replace('%', ''))) * 100.0);
} else {
return eval(eval('' + inco) / eval('' + zzfac));
}
}
return inco;
}

used in the following example code below …


topelem.addEventListener('mousedown', function(event) {
if (topin) {
topin.value='- -' + isScribble + ',' + lastx + ',' + lasty + ',' + x + ',' + y;
}
if (isScribble == 1) {
threebackpmore=twobackpmore; twobackpmore=lastpmore; lastpmore=pmore; pmore=' pxam1 ';
isScribble=2;
if (event.pageX || event.pageY) {
ppx=zmb(event.pageX) - elemLeft;
ppy=zmb(event.pageY) - elemTop;
} else {
ppx=zmb(event.clientX) - elemLeft;
ppy=zmb(event.clientY) - elemTop;
}
if (pdgebi(topdcmcheck('dcm'))) {
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('text') == 0) {
//parent.document.title='here3 ' + tx + ',' + ty;
if (1 == 2 && tlx < 0 && tly < 0) {
tlx=ppx;
tly=ppy;
}
if (tx >= 0 && ty >= 0) {
prevtx=tx;
prevty=ty;
trotis=eval(((Math.atan2((ppy - prevty), (ppx - prevtx)) * 180.0 / Math.PI) + 360) % 360);
if (pdgebi('trotation')) {
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('textseq') == 0) {
jsrectbits[0]=prevtx;
jsrectbits[1]=prevty;
jsrectbits[2]=ppx;
jsrectbits[3]=ppy;
if (Math.abs(prevty - ppy) > Math.abs(prevtx - ppx)) {
trotis=eval(((Math.atan2((prevtx - ppx), (ppy - prevty)) * 180.0 / Math.PI) + 360) % 360);
}
}
parent.document.getElementById('trotation').value=Math.floor(trotis);
ppx=prevtx;
ppy=prevty;
prevtx=-1;
prevty=-1;
exceptwhen=endtrue(''); //true;
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('textseq') == -1) parent.document.getElementById(topdcmcheck('dcm')).value = 'Text';
}
//alert(trotis);
}
tx=ppx;
ty=ppy;
} else {
tx=ppx;
ty=ppy;
prevtx=-1;
prevty=-1;
}
} else {
tx=ppx;
ty=ppy;
prevtx=-1;
prevty=-1;
}
lastx=0; // new to scribble
lasty=0;
x=0;
y=0;
}
});

… we now have reasonable graphic positioning for the canvas when its webpage window has a non 1.0 zoom factor applied.


Previous relevant Keyboard Based Cursor Canvas Content Copy Tutorial is shown below.

Keyboard Based Cursor Canvas Content Copy Tutorial

Keyboard Based Cursor Canvas Content Copy Tutorial

You may have noticed with yesterday’s Keyboard Based Cursor Share Content Copy Tutorial, crucial to the sharing code, was the use of the incredible HTML5 introduced canvas element, helped by that middleperson link to those public email and SMS sharing conduits, overseeing the great random Lorem Picsum background images and user created emoji and/or text initial annotations.

And this got us wondering whether there was a private (ie. inhouse) web application of the past that could further value add as another optional middleperson tool for (user) personalization (of the content) purposes. And yes, do you remember the canvas work involved in the featured web application of Emoji Borders and Backgrounds Canvas Annotation Tutorial?

It uses an interesting approach with the transference of data across from a canvas in one application to the canvas in that user_of_signature_signature.htm web application. Once our changed body_mouse_deepdive.html‘s “parent” web application has organized its canvas contents, it’s just a matter of …


var awo=null;
awo=window.open('./user_of_signature_signature.htm','_blank','top=100,left=100,width=800,height=800');

for the “child” canvas annotator to effectively “suck up” the canvas data into its canvas via …


var cancont='';
var elemode=location.search.split('elemode=')[1] ? (location.search.split('elemode=')[1].split('&')[0]) : "canvas";

if (('' + window.opener).replace('null','') != '') {
var cans=window.opener.document.getElementsByTagName(elemode);
if (cans.length > 0) {
if (elemode == 'img') {
cancont=cans[0].src;
} else {
cancont=cans[0].toDataURL('image/png');
}
if (cancontw < 0 && canconth < 0) { cancontw=cans[0].width; canconth=cans[0].height; } } }

... possible because both "parent" and "child" exist on the same RJM Programming domain (web server).

Once there, another woooooorrrrrlllddd of image manipulations, via canvas, can await the user, perhaps ready for ongoing sharing possibilities with that "child".


Previous relevant Keyboard Based Cursor Share Content Copy Tutorial is shown below.

Keyboard Based Cursor Share Content Copy Tutorial

Keyboard Based Cursor Share Content Copy Tutorial

Yesterday's Keyboard Based Cursor Personalized Content Copy Tutorial has probably reached a point where some sharing mechanism is apt, for accountability and usefulness purposes, at the very least, regarding our recent web application allowing for foreground content on top of a random Lorem Picsum background image.

We've been helped out greatly by javascript - Can I take a screenshot from the clipboard? - Stack Overflow and html - How to render a blob on a canvas element? - Stack Overflow in the following new relevant Javascript code ...


function renderNoImage(canvas, blob) { // thanks to https://stackoverflow.com/questions/38004917/how-to-render-a-blob-on-a-canvas-element
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // This is important. If you are not using the blob, you should release it if you don't want to reuse it. It's good for memory.
ctx.drawImage(event.target, 0, 0)
img.style.display='none';
img.style.zIndex='-123';
};
img.src = URL.createObjectURL(blob);
}


function renderImage(canvas, blob) { // thanks to https://stackoverflow.com/questions/38004917/how-to-render-a-blob-on-a-canvas-element
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // This is important. If you are not using the blob, you should release it if you don't want to reuse it. It's good for memory.
ctx.drawImage(event.target, 0, 0)
};
img.src = URL.createObjectURL(blob);
}


addEventListener("paste", ev => { // thanks to https://stackoverflow.com/questions/55559432/can-i-take-a-screenshot-from-the-clipboard
for(const item of ev.clipboardData.items) { /// Clipboard may contain multiple elements of different type -- text, image, etc
if(item.type.startsWith("image/")) { /// We are only interested in clipboard data that is an image
if (emailee == '') {
emailee=prompt('Your clipboard has a useful image you could share the image with. Optionally, please enter an email address or SMS number to share with.');
if (emailee == null) { emailee=''; }
}
if (emailee.indexOf('@') != -1) {
document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="position:absolute;z-index:98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('aemail').href=document.getElementById('aemail').href.replace(':?', ':' + emailee + '?');
document.getElementById('aemail').click();
document.getElementById('mycanvas').innerHTML='';
} else if (emailee != '' && emailee.replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '') {
document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="position:absolute;z-index:98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('asms').href=document.getElementById('asms').href.replace(':&', ':' + emailee + '&');
document.getElementById('asms').click();
document.getElementById('mycanvas').innerHTML='';
} else {
//document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="display:none;position:absolute;z-index:-98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderNoImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('mycanvas').innerHTML='';
}
}
}
});

... which is explained to the user in the following way ...

Share with email or SMS happens via screenshots (macOS control-command-shift-3 or Windows Prnt-Scrn) followed by File menu Paste option causing chance to share after which File menu Undo can remove pasted part as required.

... in the changed "proof of concept" body_mouse_deepdive.html live run.


Previous relevant Keyboard Based Cursor Personalized Content Copy Tutorial is shown below.

Keyboard Based Cursor Personalized Content Copy Tutorial

Keyboard Based Cursor Personalized Content Copy Tutorial

On top of yesterday's Keyboard Based Cursor Content Copy Tutorial we wanted to offer the user the chance for them to tailor their foreground content on top of the Lorem Picsum background image.

The user can enter this via the keyboard because there is an HTML div contenteditable=true pallette to work with ...


<div id=mydiv onfocus='setTimeout(alte, 1000);' ondblclick="this.innerHTML='';" onclick='stamp(event);' title='Click and type and see the cursor change as you use Alt or Shift or Control that can be reset via Caps Lock with a click copying cursor into content ... RJM Programming ... May, 2022 ... thanks to https://picsum.photos/ Lorem Picsum You can change emojis if you know the \002602 form of UTF-16 (hex) entry (our example is umbrella and if you express it as \\002602 that leaves cursor just as your emoji). Double click existant foreground emoji element to clear all the foreground emoji elements.' spellcheck='false' contenteditable=true style='width:100vw;height:100%;color:transparent;text-color:transparent;' onkeypress=kpcursorlook(event); onkeydown=cursorlook(event); onmousemove=xycursorlook(event); ontouchmove=xycursorlook(event);></div>

It's "title" attribute now talks about two new pieces of functionality, those being ...

  • You can change emojis if you know the \002602 form of UTF-16 (hex) entry (our example is umbrella and if you express it as \\002602 that leaves cursor just as your emoji).
  • Double click existant foreground emoji element to clear all the foreground emoji elements.

The choice of keyboard input methodology also suits CSS "content" property definitions as one backslash followed by a hexidecimal value left padded with zeroes to a length of six. See us doing this below for an umbrella emoji (which could also be expressed in an HTML hexadecimal entity way &#x2602; ☂ the information for which you can find at Unicode Character 'UMBRELLA' (U+2602) we normally get to by entering "U+2602" at a web browser address bar, the knowledge for which we arrive at via entering "umbrella emojipedia" and moving down the top link's webpage to find the "U+2602") ...


<style> #myumbrella::after { content: '\002602'; } </style>

... that feeds into some new "onkeypress" keyboard event logic ...


function kpcursorlook(evt) {
var atofa=['a','b','c','d','e','f'];
var rgbit='';
if (evt.keyCode == 92) {
if (newu.length == 1) { gro=true; } else { gro=false; }
newu=("\\").substring(0,1);
} else if (evt.keyCode >= 48 && evt.keyCode <= 57) {
newu+=('' + eval(-48 + evt.keyCode));
} else if (evt.keyCode >= 65 && evt.keyCode <= 70) {
newu+=('' + atofa[eval(-65 + evt.keyCode)]);
} else if (evt.keyCode >= 97 && evt.keyCode <= 102) {
newu+=('' + atofa[eval(-97 + evt.keyCode)]);
}
if (newu.length == 7) {
if (lastcursor.indexOf(("\\\\").substring(0,2)) != -1) {
rgbit=lastcursor.split(("\\\\").substring(0,2))[1].substring(6);
lastcursor=lastcursor.split(("\\\\").substring(0,2))[0] + ("\\").substring(0,1) + newu + rgbit;
document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
} else if (lastcursor.indexOf(("\\").substring(0,1)) != -1) {
rgbit=lastcursor.split(("\\").substring(0,1))[1].substring(6);
lastcursor=lastcursor.split(("\\").substring(0,1))[0] + newu + rgbit;
if (gro) {
lastcursor=lastcursor.replace('>Shift/Alt/Ctrl', '>').replace('>Shift', '>').replace('>Alt', '>').replace('>Ctrl', '>').replace(" width='126'", " width='20'").replace(" width='66'", " width='20'").replace(" height='48'", " height='20'").replace('0 0 100 100', '0 0 20 20');
}
document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
}
}
// console.log(evt.keyCode);
}

... in the changed "proof of concept" body_mouse_deepdive.html live run.

Stop Press

Two more "title" attribute pieces of functionality are now ...

  • With complex emoji entries do not zero leftpad until your last simple entity eg. \\1F6A3\200D\2640\00FE0F could define a Woman Rowing complex emoji.
  • Optionally make emojis bigger after \ with + two time multipliers.

The choice of keyboard input methodology also suits CSS "content" property definitions as one backslash followed by "first off" sets of HTML entities they should not left zero pad in their entry, followed by a last HTML entity hexidecimal value left padded with zeroes to a length of six to finish up with. See us doing this below for a woman rowing emoji (which could also be expressed in an HTML hexadecimal entity way &#x1f6a3;&#x0200d;&#x02640;&#x0fe0f; the information for which you can find at 🚣‍♀️ Woman Rowing Boat Emoji we normally get to by entering "woman rowing emoji" at the web browser address bar) ...


<style> #myrowing::after { content: '\01f6a3\00200d\002640\00fe0f'; } </style>

... in the changed "proof of concept" body_mouse_deepdive.html live run.


Previous relevant Keyboard Based Cursor Content Copy Tutorial is shown below.

Keyboard Based Cursor Content Copy Tutorial

Keyboard Based Cursor Content Copy Tutorial

The "onkeydown" keyboard event can involve in its logic three mouse event (event object) property usages that caught our interest recently ...

... and we wanted to have "keyboard" meets "mouse" events, along with the brilliance of Lorem Picsum regarding background image randomosity and quality, working with HTML ...


<div id=mydiv onfocus='setTimeout(alte, 1000);' onclick='stamp(this);' title='Click and type and see the cursor change as you use Alt or Shift or Control that can be reset via Caps Lock with a click copying cursor into content ... RJM Programming ... May, 2022 ... thanks to https://picsum.photos/ Lorem Picsum' spellcheck='false' contenteditable=true style='width:100vw;height:100%;color:transparent;text-color:transparent;' data-onkeypress=xycursorlook(event); onkeydown=cursorlook(event); onmousemove=xycursorlook(event); ontouchmove=xycursorlook(event);></div>

... in a way we hadn't tried before that called on some inhouse cursor SVG creations (and so is, alas, just a non-mobile fully featured experience), in the sense that ...

  1. keyboard helps creating the "what" regarding content ...

    <script type='text/javascript'>
    var lastkeymodifier=''
    var lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function cursorlook(evt) {
    if (evt.altKey) {
    if (lastkeymodifier != 'alt') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,0,255,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Alt\\01f3d5</text></svg>\") 16 0, progress";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='alt';
    }
    } else if (evt.ctrlKey) {
    if (lastkeymodifier != 'ctrl') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Ctrl\\01f333</text></svg>\") 16 0, pointer";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='ctrl';
    }
    } else if (evt.shiftKey) {
    if (lastkeymodifier != 'shift') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(255,0,0,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Shift\\01f389</text></svg>\") 16 0, grab";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='shift';
    }
    } else {
    if (lastkeymodifier != '') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='';
    }
    }
    }
    </script>
  2. mouse "onmousemove" or "ontouchmove" helps with the "where" regarding content above ...

    <script type='text/javascript'>
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function xycursorlook(e) {
    e = e || window.event;
    e.preventDefault();

    if (e.touches) {
    if (e.touches[0].pageX) {
    pos3 = e.touches[0].pageX;
    pos4 = e.touches[0].pageY;
    } else {
    pos3 = e.touches[0].clientX;
    pos4 = e.touches[0].clientY;
    }
    //console.log('pos3=' + pos3 + ',pos4=' + pos4);
    } else if (e.clientX || e.clientY) {
    pos3 = e.clientX;
    pos4 = e.clientY;
    } else {
    pos3 = e.pageX;
    pos4 = e.pageY;
    }
    }
    </script>
  3. preparations for "onclick" way a cursor can be plonked into (real lasting) content ...

    <script type='text/javascript'>
    var lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function stamp(divo) {
    if (('' + pos3).indexOf('-') == -1) {
    if (allowable) {
    //if (allowed) {
    //allowed=false;
    //setTimeout(reseta, 2000);
    divo.innerHTML+='<div id=div' + subdiv + ' style="position:absolute;top:' + pos4 + 'px;left:' + pos3 + 'px;width:126px;height:48px;background-color:transparent;"></div><style> #div' + subdiv + ' { background:' + lastcursor.split(') ')[0] + ') no-repeat; } </style>';
    subdiv++;
    //console.log('lastpos4=' + lastpos4 + ' and pos4=' + pos4);
    //console.log('lastpos3=' + lastpos3 + ' and pos3=' + pos3);
    lastpos3=pos3;
    lastpos4=pos4;
    //}
    }
    }
    }
    </script>

... with the "proof of concept" body_mouse_deepdive.html live run.

If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.

Posted in Animation, eLearning, Event-Driven Programming, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a comment

Canvas Drag and Drop Primer Tutorial

Canvas Drag and Drop Primer Tutorial

Canvas Drag and Drop Primer Tutorial

It’s time. It really is. It’s time to revisit the “User of Signature Signature” web application using the HTML5 canvas element to create and share graphical data, that we last dealt with when we presented Canvas Sharing via Email Hashtagging Tutorial.

Why? Well, we want to combine …

  • canvas … graphical data … to start with … with …
  • drag and drop methodologies

… to add to the functionality of our “User of Signature Signature” web application. Today is the “proof of concept” day because there are two conflicting issues for us …

  • scribble functionality on a canvas element … is at odds with …
  • the draggable=true attribute usage crucial to getting Drag and Drop methodologies to kick in

You may have noticed with the “User of Signature Signature” web application it starts ready to scribble on the canvas straight away. And do you think we can resist that, as we test it?! No way!

That same steadiness to that canvas, as it is set up to resist “internal movement” also stops it being draggable alas … depending how you look at it.

And so we thought about this and came up with …

  1. do the usual this useful link suggests about setting up the canvas element for Drag and Drop …

    <canvas ondragstart="dragstart(event);" draggable=truefalse title="Ready for you to create your canvas 🎨 content above any signature 💳 panel ..." id="topcanvas" width=850 height=600 style="background-color:white; position: absolute; top:0; left:0; border-bottom:5px solid yellow; border-right:5px solid blue;"></canvas>

    … except start the draggable attribute with value false … and team this with …
  2. new Drop Zone HTML element helpers …

    <div id="target" ondrop="drop(event);" ondragover="dragover(event);" data-mt="0" style="vertical-align:bottom;display:block;background-color:transparent;">
    <button id=mydbut onclick=" document.getElementById('topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML='<span id=tarbut style=margin-top:' + document.getElementById('target').getAttribute('data-mt') + 'px;>Drop Zone</span>'; ">Drop Zone for Canvas After Click</button>
    </div>
    <span id=starbut></span>

    … and …
  3. programmatically controlled HTML button element click logic …

    var lastdrop='';

    function dragstart(ev) {
    ev.dataTransfer.setData("text/plain", document.getElementById('topcanvas').toDataURL());
    }

    function dragover(ev) {
    ev.preventDefault();
    }

    function drop(ev) {
    ev.target.innerHTML = ev.dataTransfer.getData("text");
    lastdrop=ev.target.innerHTML;
    document.getElementById('topcanvas').setAttribute('draggable','false');
    document.getElementById('target').innerHTML='<button id=tarbut onclick=\" document.getElementById(' + "'topcanvas').setAttribute('draggable','true'); document.getElementById('target').innerHTML=''; document.getElementById('starbut').style.marginTop='-50px'; document.getElementById('starbut').style.zIndex='-50'; document.getElementById('starbut').innerHTML='Drop Zone'" + '; \">Drop Zone for Canvas After Click</button>';
    document.getElementById('starbut').innerHTML='';
    var rectis=document.getElementById('target').getBoundingClientRect();
    if (document.getElementById('mydbut')) {
    rectis=document.getElementById('mydbut').getBoundingClientRect();
    } else if (document.getElementById('tarbut')) {
    rectis=document.getElementById('tarbut').getBoundingClientRect();
    }
    document.getElementById('target').setAttribute('data-mt','' + eval(-30 + eval('' + rectis.width)));
    document.getElementById('target').style.height='' + rectis.width + 'px';
    document.getElementById('target').style.backgroundImage='URL(' + lastdrop + ')';
    document.getElementById('target').style.backgroundSize='contain';
    document.getElementById('target').style.backgroundRepeat='no-repeat';
    if (document.getElementById('topdragstyle').innerHTML == '') {
    document.getElementById('tarbut').style.marginTop='' + eval(-30 + eval('' + rectis.width)) + 'px';
    //var rectistb=document.getElementById('tarbut').getBoundingClientRect();
    //document.getElementById('topdragstyle').innerHTML='<style> #tarbut { position:absolute; left:' + rectistb.left + 'px; top:' + eval(eval(0 * eval(-30 + eval('' + rectis.width))) + eval('' + rectistb.top)) + 'px; margin-top:0px !important; } </style>';
    }
    }

    … to toggle between the modes of use

… in the changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application.

Like we say, “proof of concept” (and mobile platforms need more thinking, so see Stop Press below), today, ahead of “usefulness” to come!

Stop Press

We know (again, via this useful link) from simple canvas scenarios Drag and Drop can work for mobile, but we couldn’t get it to work for our canvas used for scribbling and other annotation purposes. And so, in our further changed user_of_signature_signature.htm User of Signature Signature inhouse canvas graphic data web application we got some “onclick” event replacement code working for mobile platforms involving less work for the user. Some may see this as an enhancement, but personally, we’re awfully fond of Drag and Drop even with the slightly greater bother. We just think it helps a user clarify in their minds what they are doing, and not necessarily blithely clicking away with impunity.


Previous relevant Canvas Sharing via Email Hashtagging Tutorial is shown below.

Canvas Sharing via Email Hashtagging Tutorial

Canvas Sharing via Email Hashtagging Tutorial

We’re regular “tweakers” (for example, with Canvas via Image Web Share API Photo Postcard Tutorial) of what we call the …

  • canvas … based …
  • signatures … possible …
  • scribble … or …
  • discrete click … creating …
  • annotation … friendly …

… “graphics sharing” web application “User of Signature Signature”. And with us, more and more …

  • decoupling our PHP mail sharing conduits … in favour of …
  • “a” link “mailto:” email (and sometimes “sms:” SMS) communication conduits … now making use of …
  • hashtagging … parts to URLs used …

… because this methodology has fewer data limits, it being a “clientside” only (for the main part) means of sharing data. Today, we’re looking into this.

Happily, for this work, “the lynchpin” is some “signature_signature.js” external Javascript, which is all that needs tweaking to make these changes.

Previously, we’d relied, more, on “signature_signature.php” PHP, which oversaw our previous interfacing to the mail server up at the RJM Programming domain via the PHP mail function.

Now, the user sends their own emails with their own email client software, and emails contain one long body URL string where …

  • canvas … element’s …
  • [canvasElement].toDataURL(‘image/jpeg’, 0.7) … means by which a …
  • data URI representation of the graphical data

… can be hashtagged into that long body URL string. If the email recipient clicks this link, our external Javascript …

Reads such incoming data via location.hash

var azx=null, wowowo=null;
var imgbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('img=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('img=')[1].split('&')[0]).replace(/\ /g,'+') : '';
var passedsubject=location.search.split('subject=')[1] ? decodeURIComponent(location.search.split('subject=')[1].split('&')[0]) : '';
And loaded into canvas and new window …

setTimeout(function(){
if (imgbih != '') {
//alert(imgbih);
if (top.document.getElementById('topcanvas')) {
var xanimg=document.createElement('img');
xanimg.onload=function() {
var tc=top.document.getElementById('topcanvas').getContext('2d');
tc.drawImage(xanimg, 0, 0);
};
xanimg.src=imgbih;
}
var wowowo=window.open('','_blank','top=100,left=100,width=800,height=800');
wowowo.document.write('<html><body style="background:URL(' + "'" + imgbih + "'" + ');background-repeat:no-repeat;"></body></html>');
imgbih='';
if (passedsubject != '') {
wowowo.document.title='' + passedsubject;
}
}
}, 5000);
Before submitting form method=POST to signature_signature.php check out form fields

if (('' + document.getElementById('emailto').value).indexOf('@') != -1 && ('' + document.getElementById('contentto').value).indexOf('data:image/jpeg;base64,') != -1) {
azx=document.createElement("a");
document.body.appendChild(azx);
azx.style = "display: none";
azx.href = 'mailto:' + document.getElementById('emailto').value + ('?subject=' + encodeURIComponent(document.getElementById('subject').value)).replace(/subject\=$/g,'subject=My%20Canvas%20Creation') + '&body=' + encodeURIComponent(top.document.URL.split('?')[0].split('#')[0] + ('?subject=' + encodeURIComponent(document.getElementById('subject').value)).replace(/subject\=$/g,'subject=My%20Canvas%20Creation') + '#img=' + encodeURIComponent(document.getElementById('contentto').value));
azx.click();
} else {

document.getElementById('myform').submit();
}

… to enact fairly simple changes for more stable sharing and collaboration arrangements via a tweaked external Javascript signature_signature.js code used by our signature use in canvas supervisory web application.


Previous relevant Canvas via Image Web Share API Photo Postcard Tutorial is shown below.

Canvas via Image Web Share API Photo Postcard Tutorial

Canvas via Image Web Share API Photo Postcard Tutorial

We discovered, further to yesterday’s Canvas via Image Web Share API Personalization Tutorial, out and about, with an iPhone, that …

Image Input Mode Image Sizing Modus Operandi
URL Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)
Browsing Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)

… of Images to Canvas No Clicking Form Signature Tutorial times was worth refocussing on, regarding the combination of …

  • Browsing … Contain (canvas) … option above … and …
  • Take Photo … mobile device option … which, if followed through on, is more than likely to leave …
  • On the right hand side of the canvas there will be white space suitable to scribble a message to complete your …
  • Postcard … which you can optionally …
  • Share

… and if you are using the https: protocol with the Safari and Google Chrome web browser(s), we can help out more with that last step, being able to make a sharing button show the word “Postcard” …


function checkonyellow() {
var ibuta=[], jbuta=0;
if (safariblurb != '') {
if (document.getElementById('shareurl')) {
if (document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
} else if (parent.document.getElementById('shareurl')) {
if (parent.document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=parent.document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
} else if (top.document.getElementById('shareurl')) {
if (top.document.getElementById('shareurl').outerHTML.indexOf('yellow') != -1) {
ibuta=top.document.getElementsByTagName('input');
for (jbuta=0; jbuta<ibuta.length; jbuta++) {
if (('' + ibuta[jbuta].value) == 'Share URL Link') {
ibuta[jbuta].value='Postcard';
ibuta[jbuta].style.backgroundColor='cyan';
}
}
} else {
setTimeout(checkonyellow, 3000);
}
}
}
}

… to make the procedure …

Postcard via smart device Camera advice …

1. Touch browsing Choose File button that has just appeared.
2. Choose to Take Photo.
3. Consider any zooming in or selfie modes of use in Photo mode.
4. Take photo with Camera app white button, choosing Use Photo or Retake, as required.
5. Scribble any optional messaging in white area to right, or use other discreet touch functionalities.
6. Optionally email off to recipient (as textbox below goes yellow, click email sender button to its left for easier usage).

… more obvious, via the changed external Javascript signature_signature.js code used by our signature use in canvas supervisory web application.


Previous relevant Canvas via Image Web Share API Personalization Tutorial is shown below.

Canvas via Image Web Share API Tutorial

Canvas via Image Web Share API Personalization Tutorial

Yesterday’s Canvas via Imge Web Share API Tutorial‘s Web Share API (so far, on Safari and Google Chrome web browser(s)) image (via canvas) sharing functionality did not have enough privacy for the user, as far as we were concerned. We’re not going in for some two factor authentication level of privacy here, and so we’ve opted for the use of the word “Personalization” in today’s blog posting title.

What we’ve done here is incorporate into the image name (a form of) the IP address of the sender of the image, which the receiver is using (for that extra privacy) as they click the email link, that now involves (a form of) this IP address in its formation.

No surprises here, that we’d have to involve our changed PHP helper signature_signature.php “specialist signature PHP helper” more, getting this solution together.

And no surprises here, that we’d try to involve the web server’s disk areas outside the scope of its (Apache) Document Root hierarchy. That step, alone, is important to force a curious user, to work their way through an inhouse PHP webpage URL of some sort, in order to extract that …

  • image data in the form of a web server file … to a …
  • public domain webpage image in an HTML img tag where its src attribute is populated via that web server file’s equivalent base64 data URI …
    <?php

    echo "<html><body><img title='" . $thename . '' . "' src='data:image/webp;base64," . base64_encode(file_get_contents(DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . $thename . '')) . "'></img>" . $therest . "</body></html>";

    ?>

Am sure you are not surprised that today’s external Javascript web_share_api_test.js Web Share API specialist needed to change


function tctowebp() {
var ourcanvc='';
var canvs=[];
if (window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0) {
canvs=document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
if (eval('' + canvs.length) == 0 && window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
}
if (eval('' + canvs.length) > 0) {
ourcanvc=canvs[0].toDataURL('image/webp');
if (lastcanvc != ' ') {
if (lastcanvc != ourcanvc) {
lastcanvc=ourcanvc;
canvc=ourcanvc;
czhr = new XMLHttpRequest();
czform = new FormData();
czform.append('webpit', canvc);
if (newidea.indexOf('canvas.webp') == -1) { // eg. MAMP ... http://localhost:8888/signature_signature.php?mycanvasimage=0000__1
czform.append('websecurepit', '' + myipadwsi);
}

czhr.onreadystatechange=cstateChanged;
czhr.open('post', './signature_signature.php', true);
czhr.send(czform);
} else {
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='#f9f9f9';
}
}
}
lastcanvc=ourcanvc;
}
return ourcanvc;
}

… as well, regarding its incorporation of Web Share API logic into our inhouse User of Signature canvas based web application, best tried, these days, in the Safari and Google Chrome web browser(s) using https: (secure) protocol?!


Previous relevant Canvas via Imge Web Share API Tutorial is shown below.

Canvas via Image Web Share API Tutorial

Canvas via Imge Web Share API Tutorial

We’re on the lookout for ways to improve the sharing and collaboration functionality of yesterday’s Images to Canvas No Clicking Form Signature Tutorial‘s “User of Signature” canvas using web application. We’re calling on ..

  • Safari and Google Chrome web browser(s) … for the moment, supporting the …
  • Web Share API … in order to …
  • Fill out email (or other communication idea) attachment links or documents … that …
  • Prepare the message before you, the user, flesh out the message as you send it off to the recipient

… a more guaranteed way, these days, because there are more and more ways restrictions may stop you doing similar functionality with a mail server arrangement on your public domain website, where we use the Exim mail server, at the RJM Programming domain.

As far as data goes for this, we also call on our changed PHP helper signature_signature.php to create a webp image file for sharing …

<?php

if (isset($_POST['webpit'])) {
file_put_contents($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'canvas.webp', base64_decode(explode(";base64,", str_replace(' ','+',urldecode($_POST['webpit'])))[1]));
exit;
}

?>

… as applicable. The modus operandi for this back at today’s changed web_share_api_test.js external Javascript Web Share API specialist, uses Ajax methodologies …


var canvc=' ', lastcanvc=' ', czhr=null, czform=null;

function cstateChanged() {
if (czhr.readyState == 4) {
if (czhr.status == 200) {
if (1 == 2) { alert('Did it'); }
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='yellow';
}
}
}
}

function tctowebp() {
var ourcanvc='';
var canvs=[];
if (window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0) {
canvs=document.getElementsByTagName('canvas');
}
if (eval('' + canvs.length) == 0 && window.parent) {
canvs=parent.document.getElementsByTagName('canvas');
if (eval('' + canvs.length) == 0 && window.top) {
canvs=top.document.getElementsByTagName('canvas');
}
}
if (eval('' + canvs.length) > 0) {
ourcanvc=canvs[0].toDataURL('image/webp');
if (lastcanvc != ' ') {
if (lastcanvc != ourcanvc) {
lastcanvc=ourcanvc;
canvc=ourcanvc;
czhr = new XMLHttpRequest();
czform = new FormData();
czform.append('webpit', canvc);
czhr.onreadystatechange=cstateChanged;
czhr.open('post', './signature_signature.php', true);
czhr.send(czform);
} else {
if (document.getElementById('shareurl')) {
document.getElementById('shareurl').style.backgroundColor='#f9f9f9';
}
}
}
lastcanvc=ourcanvc;
}
return ourcanvc;
}

… and set in motion via a couple of different parental calls such as …


<script type='text/javascript' src='/web_share_api_test.js?canvasshare=as_necessary9876' defer></script>

… in …

… for cuter Safari and Google Chrome web browser sharing and collaboration work.


Previous relevant Images to Canvas No Clicking Form Signature Tutorial is shown below.

Images to Canvas No Clicking Form Signature Tutorial

Images to Canvas No Clicking Form Signature Tutorial

Surprisingly, it’s been more than a year since we changed anything to do with our …

  • canvas based …
  • images and annotation … accepting …
  • scribble (ie. can handle signatures) … perhaps …
  • form filling helping web application

… (when we presented Canvas Graphics Editing in Zoomed Webpage Tutorial) and we found that in order to add six more such Annotation option helpers into the mix …

Image Input Mode Image Sizing Modus Operandi
URL Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)
Browsing Actual (canvas resized to image)
Contain (canvas)
Cover (canvas)

… we only needed to change the external Javascript signature_signature.js code used by our signature use in canvas supervisory web application so that when you can not ask so much of the user regarding image scaling, and avoid any canvas clicking that way, offering these six new annotation functionality options.


Previous relevant Canvas Graphics Editing in Zoomed Webpage Tutorial is shown below.

Canvas Graphics Editing in Zoomed Webpage Tutorial

Canvas Graphics Editing in Zoomed Webpage Tutorial

Software integration can be useful for a number of reasons …

  • code reuse
  • modularisation of functionality … and in our case …
  • if you like coding via “intervention points” you will enjoy coding for software integration improvements

As you might imagine it is easier in terms of the fact you can change “both ends” of the software integration, if it is “inhouse”, and luckily, today, our software integration is “inhouse”.

When we presented the recent Keyboard Based Cursor Canvas Content Copy Tutorial it focussed our attention on an improvement we could make to that user_of_signature_signature.htm canvas image editing web application. We wanted to offer the software integration with this web application to be able to successfully handle a webpage that has been zoomed to zoom factor different to 1.0 (or 100%).

We noticed before today’s very simple changes to external Javascript signature_signature.js code, graphic positioning on the canvas was wrong when the webpage had a zoom factor that is not 1.

Applying the canvas co-ordinate references through “the lens of” Javascript function zmb below …


var zzfac=location.search.split('zoom=')[1] ? decodeURIComponent(location.search.split('zoom=')[1].split('&')[0]) : "";
if (('' + location.hash).indexOf('zoom=') != -1) { zzfac=decodeURIComponent(('' + location.hash).split('zoom=')[1].split(';')[0]); }

function zmb(inco) {
if (zzfac != '') {
if (zzfac.indexOf('%') != -1) {
return eval(eval(eval('' + inco) / eval('' + zzfac.replace('%', ''))) * 100.0);
} else {
return eval(eval('' + inco) / eval('' + zzfac));
}
}
return inco;
}

used in the following example code below …


topelem.addEventListener('mousedown', function(event) {
if (topin) {
topin.value='- -' + isScribble + ',' + lastx + ',' + lasty + ',' + x + ',' + y;
}
if (isScribble == 1) {
threebackpmore=twobackpmore; twobackpmore=lastpmore; lastpmore=pmore; pmore=' pxam1 ';
isScribble=2;
if (event.pageX || event.pageY) {
ppx=zmb(event.pageX) - elemLeft;
ppy=zmb(event.pageY) - elemTop;
} else {
ppx=zmb(event.clientX) - elemLeft;
ppy=zmb(event.clientY) - elemTop;
}
if (pdgebi(topdcmcheck('dcm'))) {
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('text') == 0) {
//parent.document.title='here3 ' + tx + ',' + ty;
if (1 == 2 && tlx < 0 && tly < 0) {
tlx=ppx;
tly=ppy;
}
if (tx >= 0 && ty >= 0) {
prevtx=tx;
prevty=ty;
trotis=eval(((Math.atan2((ppy - prevty), (ppx - prevtx)) * 180.0 / Math.PI) + 360) % 360);
if (pdgebi('trotation')) {
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('textseq') == 0) {
jsrectbits[0]=prevtx;
jsrectbits[1]=prevty;
jsrectbits[2]=ppx;
jsrectbits[3]=ppy;
if (Math.abs(prevty - ppy) > Math.abs(prevtx - ppx)) {
trotis=eval(((Math.atan2((prevtx - ppx), (ppy - prevty)) * 180.0 / Math.PI) + 360) % 360);
}
}
parent.document.getElementById('trotation').value=Math.floor(trotis);
ppx=prevtx;
ppy=prevty;
prevtx=-1;
prevty=-1;
exceptwhen=endtrue(''); //true;
if (parent.document.getElementById(topdcmcheck('dcm')).value.indexOf('textseq') == -1) parent.document.getElementById(topdcmcheck('dcm')).value = 'Text';
}
//alert(trotis);
}
tx=ppx;
ty=ppy;
} else {
tx=ppx;
ty=ppy;
prevtx=-1;
prevty=-1;
}
} else {
tx=ppx;
ty=ppy;
prevtx=-1;
prevty=-1;
}
lastx=0; // new to scribble
lasty=0;
x=0;
y=0;
}
});

… we now have reasonable graphic positioning for the canvas when its webpage window has a non 1.0 zoom factor applied.


Previous relevant Keyboard Based Cursor Canvas Content Copy Tutorial is shown below.

Keyboard Based Cursor Canvas Content Copy Tutorial

Keyboard Based Cursor Canvas Content Copy Tutorial

You may have noticed with yesterday’s Keyboard Based Cursor Share Content Copy Tutorial, crucial to the sharing code, was the use of the incredible HTML5 introduced canvas element, helped by that middleperson link to those public email and SMS sharing conduits, overseeing the great random Lorem Picsum background images and user created emoji and/or text initial annotations.

And this got us wondering whether there was a private (ie. inhouse) web application of the past that could further value add as another optional middleperson tool for (user) personalization (of the content) purposes. And yes, do you remember the canvas work involved in the featured web application of Emoji Borders and Backgrounds Canvas Annotation Tutorial?

It uses an interesting approach with the transference of data across from a canvas in one application to the canvas in that user_of_signature_signature.htm web application. Once our changed body_mouse_deepdive.html‘s “parent” web application has organized its canvas contents, it’s just a matter of …


var awo=null;
awo=window.open('./user_of_signature_signature.htm','_blank','top=100,left=100,width=800,height=800');

for the “child” canvas annotator to effectively “suck up” the canvas data into its canvas via …


var cancont='';
var elemode=location.search.split('elemode=')[1] ? (location.search.split('elemode=')[1].split('&')[0]) : "canvas";

if (('' + window.opener).replace('null','') != '') {
var cans=window.opener.document.getElementsByTagName(elemode);
if (cans.length > 0) {
if (elemode == 'img') {
cancont=cans[0].src;
} else {
cancont=cans[0].toDataURL('image/png');
}
if (cancontw < 0 && canconth < 0) { cancontw=cans[0].width; canconth=cans[0].height; } } }

... possible because both "parent" and "child" exist on the same RJM Programming domain (web server).

Once there, another woooooorrrrrlllddd of image manipulations, via canvas, can await the user, perhaps ready for ongoing sharing possibilities with that "child".


Previous relevant Keyboard Based Cursor Share Content Copy Tutorial is shown below.

Keyboard Based Cursor Share Content Copy Tutorial

Keyboard Based Cursor Share Content Copy Tutorial

Yesterday's Keyboard Based Cursor Personalized Content Copy Tutorial has probably reached a point where some sharing mechanism is apt, for accountability and usefulness purposes, at the very least, regarding our recent web application allowing for foreground content on top of a random Lorem Picsum background image.

We've been helped out greatly by javascript - Can I take a screenshot from the clipboard? - Stack Overflow and html - How to render a blob on a canvas element? - Stack Overflow in the following new relevant Javascript code ...


function renderNoImage(canvas, blob) { // thanks to https://stackoverflow.com/questions/38004917/how-to-render-a-blob-on-a-canvas-element
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // This is important. If you are not using the blob, you should release it if you don't want to reuse it. It's good for memory.
ctx.drawImage(event.target, 0, 0)
img.style.display='none';
img.style.zIndex='-123';
};
img.src = URL.createObjectURL(blob);
}


function renderImage(canvas, blob) { // thanks to https://stackoverflow.com/questions/38004917/how-to-render-a-blob-on-a-canvas-element
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // This is important. If you are not using the blob, you should release it if you don't want to reuse it. It's good for memory.
ctx.drawImage(event.target, 0, 0)
};
img.src = URL.createObjectURL(blob);
}


addEventListener("paste", ev => { // thanks to https://stackoverflow.com/questions/55559432/can-i-take-a-screenshot-from-the-clipboard
for(const item of ev.clipboardData.items) { /// Clipboard may contain multiple elements of different type -- text, image, etc
if(item.type.startsWith("image/")) { /// We are only interested in clipboard data that is an image
if (emailee == '') {
emailee=prompt('Your clipboard has a useful image you could share the image with. Optionally, please enter an email address or SMS number to share with.');
if (emailee == null) { emailee=''; }
}
if (emailee.indexOf('@') != -1) {
document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="position:absolute;z-index:98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('aemail').href=document.getElementById('aemail').href.replace(':?', ':' + emailee + '?');
document.getElementById('aemail').click();
document.getElementById('mycanvas').innerHTML='';
} else if (emailee != '' && emailee.replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '') {
document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="position:absolute;z-index:98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('asms').href=document.getElementById('asms').href.replace(':&', ':' + emailee + '&');
document.getElementById('asms').click();
document.getElementById('mycanvas').innerHTML='';
} else {
//document.getElementById('mydiv').style.overflow='scroll';
document.getElementById('mycanvas').innerHTML='<br><canvas style="display:none;position:absolute;z-index:-98;top:0px;left:0px;" id=thecanvas width=' + screen.width + ' height=' + screen.height + '></canvas>';
elem=document.getElementById('thecanvas');
context=elem.getContext('2d');
renderNoImage(elem, item.getAsFile()); //context.drawImage(item.getAsFile(), 0, 0);
document.getElementById('mycanvas').innerHTML='';
}
}
}
});

... which is explained to the user in the following way ...

Share with email or SMS happens via screenshots (macOS control-command-shift-3 or Windows Prnt-Scrn) followed by File menu Paste option causing chance to share after which File menu Undo can remove pasted part as required.

... in the changed "proof of concept" body_mouse_deepdive.html live run.


Previous relevant Keyboard Based Cursor Personalized Content Copy Tutorial is shown below.

Keyboard Based Cursor Personalized Content Copy Tutorial

Keyboard Based Cursor Personalized Content Copy Tutorial

On top of yesterday's Keyboard Based Cursor Content Copy Tutorial we wanted to offer the user the chance for them to tailor their foreground content on top of the Lorem Picsum background image.

The user can enter this via the keyboard because there is an HTML div contenteditable=true pallette to work with ...


<div id=mydiv onfocus='setTimeout(alte, 1000);' ondblclick="this.innerHTML='';" onclick='stamp(event);' title='Click and type and see the cursor change as you use Alt or Shift or Control that can be reset via Caps Lock with a click copying cursor into content ... RJM Programming ... May, 2022 ... thanks to https://picsum.photos/ Lorem Picsum You can change emojis if you know the \002602 form of UTF-16 (hex) entry (our example is umbrella and if you express it as \\002602 that leaves cursor just as your emoji). Double click existant foreground emoji element to clear all the foreground emoji elements.' spellcheck='false' contenteditable=true style='width:100vw;height:100%;color:transparent;text-color:transparent;' onkeypress=kpcursorlook(event); onkeydown=cursorlook(event); onmousemove=xycursorlook(event); ontouchmove=xycursorlook(event);></div>

It's "title" attribute now talks about two new pieces of functionality, those being ...

  • You can change emojis if you know the \002602 form of UTF-16 (hex) entry (our example is umbrella and if you express it as \\002602 that leaves cursor just as your emoji).
  • Double click existant foreground emoji element to clear all the foreground emoji elements.

The choice of keyboard input methodology also suits CSS "content" property definitions as one backslash followed by a hexidecimal value left padded with zeroes to a length of six. See us doing this below for an umbrella emoji (which could also be expressed in an HTML hexadecimal entity way &#x2602; ☂ the information for which you can find at Unicode Character 'UMBRELLA' (U+2602) we normally get to by entering "U+2602" at a web browser address bar, the knowledge for which we arrive at via entering "umbrella emojipedia" and moving down the top link's webpage to find the "U+2602") ...


<style> #myumbrella::after { content: '\002602'; } </style>

... that feeds into some new "onkeypress" keyboard event logic ...


function kpcursorlook(evt) {
var atofa=['a','b','c','d','e','f'];
var rgbit='';
if (evt.keyCode == 92) {
if (newu.length == 1) { gro=true; } else { gro=false; }
newu=("\\").substring(0,1);
} else if (evt.keyCode >= 48 && evt.keyCode <= 57) {
newu+=('' + eval(-48 + evt.keyCode));
} else if (evt.keyCode >= 65 && evt.keyCode <= 70) {
newu+=('' + atofa[eval(-65 + evt.keyCode)]);
} else if (evt.keyCode >= 97 && evt.keyCode <= 102) {
newu+=('' + atofa[eval(-97 + evt.keyCode)]);
}
if (newu.length == 7) {
if (lastcursor.indexOf(("\\\\").substring(0,2)) != -1) {
rgbit=lastcursor.split(("\\\\").substring(0,2))[1].substring(6);
lastcursor=lastcursor.split(("\\\\").substring(0,2))[0] + ("\\").substring(0,1) + newu + rgbit;
document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
} else if (lastcursor.indexOf(("\\").substring(0,1)) != -1) {
rgbit=lastcursor.split(("\\").substring(0,1))[1].substring(6);
lastcursor=lastcursor.split(("\\").substring(0,1))[0] + newu + rgbit;
if (gro) {
lastcursor=lastcursor.replace('>Shift/Alt/Ctrl', '>').replace('>Shift', '>').replace('>Alt', '>').replace('>Ctrl', '>').replace(" width='126'", " width='20'").replace(" width='66'", " width='20'").replace(" height='48'", " height='20'").replace('0 0 100 100', '0 0 20 20');
}
document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
}
}
// console.log(evt.keyCode);
}

... in the changed "proof of concept" body_mouse_deepdive.html live run.

Stop Press

Two more "title" attribute pieces of functionality are now ...

  • With complex emoji entries do not zero leftpad until your last simple entity eg. \\1F6A3\200D\2640\00FE0F could define a Woman Rowing complex emoji.
  • Optionally make emojis bigger after \ with + two time multipliers.

The choice of keyboard input methodology also suits CSS "content" property definitions as one backslash followed by "first off" sets of HTML entities they should not left zero pad in their entry, followed by a last HTML entity hexidecimal value left padded with zeroes to a length of six to finish up with. See us doing this below for a woman rowing emoji (which could also be expressed in an HTML hexadecimal entity way &#x1f6a3;&#x0200d;&#x02640;&#x0fe0f; the information for which you can find at 🚣‍♀️ Woman Rowing Boat Emoji we normally get to by entering "woman rowing emoji" at the web browser address bar) ...


<style> #myrowing::after { content: '\01f6a3\00200d\002640\00fe0f'; } </style>

... in the changed "proof of concept" body_mouse_deepdive.html live run.


Previous relevant Keyboard Based Cursor Content Copy Tutorial is shown below.

Keyboard Based Cursor Content Copy Tutorial

Keyboard Based Cursor Content Copy Tutorial

The "onkeydown" keyboard event can involve in its logic three mouse event (event object) property usages that caught our interest recently ...

... and we wanted to have "keyboard" meets "mouse" events, along with the brilliance of Lorem Picsum regarding background image randomosity and quality, working with HTML ...


<div id=mydiv onfocus='setTimeout(alte, 1000);' onclick='stamp(this);' title='Click and type and see the cursor change as you use Alt or Shift or Control that can be reset via Caps Lock with a click copying cursor into content ... RJM Programming ... May, 2022 ... thanks to https://picsum.photos/ Lorem Picsum' spellcheck='false' contenteditable=true style='width:100vw;height:100%;color:transparent;text-color:transparent;' data-onkeypress=xycursorlook(event); onkeydown=cursorlook(event); onmousemove=xycursorlook(event); ontouchmove=xycursorlook(event);></div>

... in a way we hadn't tried before that called on some inhouse cursor SVG creations (and so is, alas, just a non-mobile fully featured experience), in the sense that ...

  1. keyboard helps creating the "what" regarding content ...

    <script type='text/javascript'>
    var lastkeymodifier=''
    var lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function cursorlook(evt) {
    if (evt.altKey) {
    if (lastkeymodifier != 'alt') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,0,255,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Alt\\01f3d5</text></svg>\") 16 0, progress";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='alt';
    }
    } else if (evt.ctrlKey) {
    if (lastkeymodifier != 'ctrl') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Ctrl\\01f333</text></svg>\") 16 0, pointer";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='ctrl';
    }
    } else if (evt.shiftKey) {
    if (lastkeymodifier != 'shift') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='66' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(255,0,0,0.3);fill:black;font-family:Verdana;font-size:17px;'><text y='80%'>Shift\\01f389</text></svg>\") 16 0, grab";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='shift';
    }
    } else {
    if (lastkeymodifier != '') {
    lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    document.head.innerHTML+='<style> html { cursor: ' + lastcursor + '; } </style>';
    lastkeymodifier='';
    }
    }
    }
    </script>
  2. mouse "onmousemove" or "ontouchmove" helps with the "where" regarding content above ...

    <script type='text/javascript'>
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function xycursorlook(e) {
    e = e || window.event;
    e.preventDefault();

    if (e.touches) {
    if (e.touches[0].pageX) {
    pos3 = e.touches[0].pageX;
    pos4 = e.touches[0].pageY;
    } else {
    pos3 = e.touches[0].clientX;
    pos4 = e.touches[0].clientY;
    }
    //console.log('pos3=' + pos3 + ',pos4=' + pos4);
    } else if (e.clientX || e.clientY) {
    pos3 = e.clientX;
    pos4 = e.clientY;
    } else {
    pos3 = e.pageX;
    pos4 = e.pageY;
    }
    }
    </script>
  3. preparations for "onclick" way a cursor can be plonked into (real lasting) content ...

    <script type='text/javascript'>
    var lastcursor="Url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='126' height='48' viewport='0 0 100 100' style='border-radius:15px;background-color:rgba(0,255,0,0.3);fill:black;font-family:Verdana;font-size:16px;'><text y='80%'>Shift/Alt/Ctrl\\002753</text></svg>\") 16 0, crosshair";
    var pos3=-1, pos4=-1;
    var subdiv=1;
    var lastpos3=-2, lastpos4=-1;
    var allowable=false;

    function stamp(divo) {
    if (('' + pos3).indexOf('-') == -1) {
    if (allowable) {
    //if (allowed) {
    //allowed=false;
    //setTimeout(reseta, 2000);
    divo.innerHTML+='<div id=div' + subdiv + ' style="position:absolute;top:' + pos4 + 'px;left:' + pos3 + 'px;width:126px;height:48px;background-color:transparent;"></div><style> #div' + subdiv + ' { background:' + lastcursor.split(') ')[0] + ') no-repeat; } </style>';
    subdiv++;
    //console.log('lastpos4=' + lastpos4 + ' and pos4=' + pos4);
    //console.log('lastpos3=' + lastpos3 + ' and pos3=' + pos3);
    lastpos3=pos3;
    lastpos4=pos4;
    //}
    }
    }
    }
    </script>

... with the "proof of concept" body_mouse_deepdive.html live run.

If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.

Posted in eLearning, Event-Driven Programming, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , | Leave a comment

GeoJson World Coastlines Drag and Drop Makeover Tutorial

GeoJson World Coastlines Drag and Drop Makeover Tutorial

GeoJson World Coastlines Drag and Drop Makeover Tutorial

We’ve got a mild makeover happening with the Drag and Drop functionality related to GeoJson World Coastlines Drag and Drop Tutorial today.

At first …

  • we wanted the first popup window content be aligned to the top and left … and then …
  • we thought it would be good to also, in “Drag and Drop land”, relevant countries nearby to the user’s drop point TimeZone Places be showing below that (and it panned out the best way to show this, for us, was via an iframe pointing at another incarnation of the tz_places.php webpage, because it could have GET arguments iso, iso2, iso3 etcetera to point at ISO 2 letter country codes, which we made more readily available (via a new data-cc global data attribute applied to the select option subelements presented) for the changed external Javascript countries.js we decided should get into the mix via a new Javascript function …

    function tzagain(inhtml) {
    var outhtml=inhtml, dccs=[], getarg='?', theone=1;
    if (inhtml.indexOf('left:0px;') != -1 && inhtml.indexOf(' data-cc=') != -1) {
    dccs=inhtml.split(' data-cc=');
    for (var ii=1; ii<dccs.length; ii++) {
    if (getarg == '?') {
    getarg+='iso=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2);
    theone++;
    } else if (getarg.indexOf('=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2)) == -1) {
    getarg+='&iso' + theone + '=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2);
    theone++;
    }
    }
    if (getarg != '?') {
    if (inhtml.indexOf('</bo' + 'ody>') != -1) {
    outhtml=inhtml.split('</bo' + 'dy>')[0] + '<iframe src="./tz_places.php' + getarg + '" style="position:absolute;left:0px;top:120px;width:100%;height:900px;"></iframe></body></html>';
    } else {
    outhtml=inhtml + '<iframe src="./tz_places.php' + getarg + '" style="position:absolute;left:0px;top:120px;width:100%;height:900px;"></iframe>';
    }
    }
    }
    return outhtml;
    }

    )

… used by that changed TimeZone Places tz_places.php as exemplified below

<?php

echo "<!doctype html><html><head><scr" . "ipt type='text/javascript' src='/HTMLCSS/countries.js?rand=" . rand(0,16756453) . "'></scr" . "ipt><scr" . "ipt type='text/javascript'> function lookoutfortop() { if (parent.document.getElementById('getmell')) { parent.document.getElementById('nearest').innerHTML=\"" . $pd . $retmore . "\"; } else if (top.document.getElementById('nearest')) { if (top.document.getElementById('getmell')) { top.document.getElementById('nearest').innerHTML=\"" . $pd . $retmore . "\"; } else { top.document.getElementById('nearest').innerHTML=\"" . $pd . $ret . "\"; } top.window.scrollTo(0,0); } else { if (top.document.getElementById('getmell')) { document.body.innerHTML=tzagain(\"" . $pd . $retmore . "\"); } else { document.body.innerHTML=tzagain(\"" . $pd . str_replace(':70px;',':0px;',str_replace(':350px;',':0px;',$ret)) . "\"); } } if (('' + top.document.URL).indexOf('/regions.') != -1) { if (document.getElementById('snearest')) { document.getElementById('snearest').style.left='0px'; } } } </scr" . "ipt></head><body onload=' lookoutfortop();'" . $backfullstyle . ">" . $pd . $ret . "</body></html>"; //$ret;

?>

… that superfluous looking ?rand=blah measure being pretty useful really regarding getting around the cache keeping old external Javascript in its mind after changes.

Curiously, the grandparent regions.php starting off all this needed no changing! We hope you like these tweaks!


Previous relevant GeoJson World Coastlines Drag and Drop Tutorial is shown below.

GeoJson World Coastlines Drag and Drop Tutorial

GeoJson World Coastlines Drag and Drop Tutorial

The primary aim of today’s work, onto yesterday’s GeoJson World Drag and Drop on iPad Tutorial iPad integration is to …

  • add similar drag and drop logic into our World Coastlines GeoJson web application … and along the way, also for the World Countries web application …
  • hold off involving the (pretty kludgy looking) vertical scrollbar of our drag and drop pin’s underlying HTML iframe …

    <iframe scrolling=no frameborder=0 name=iftr id=iftr style=display:none; srcdoc="<body style=background-color:transparent;><p id=mg title='Wikipedia country page below via drag and drop to world map' draggable='true'>📍</p><br><br><div id=myh1></div><script type='text/javascript' src='./countries.js?rand=321156747657' defer></script></body>" data-src=></iframe>

    … until the first drag operation starting, calls on …

    parent.document.getElementById('iftr').scrolling='yes';

    … proving a Javascript DOM control of the “scrolling” attribute works (as we weren’t sure, having never done this before)

It’s worth beavering away at a guinea pig web application until (just about complete) satisfaction (for now) before having a parallel set of code changes happening simultaneously, we’ve always found.

So, what happened in “external Javascript land”? No need for a “regions.js” here, as parent.document.URL can be scrutinised in that “external Javascript land” to discover which web application is the parent, and act accordingly. So changed were our changed external Javascript countries.js in …


Previous relevant GeoJson World Drag and Drop on iPad Tutorial is shown below.

GeoJson World Drag and Drop on iPad Tutorial

GeoJson World Drag and Drop on iPad Tutorial

Yesterday’s GeoJson World Drag and Drop Google Tutorial had us at a very interesting position, one that we cannot recall ever happening before, that being …

  • our GeoJson World Countries web application Drag and Drop logic worked on an iPhone … but …
  • our GeoJson World Countries web application Drag and Drop logic did not work on an iPad

They’re both iOS! And usually the smaller iPhone has the problem and the larger iPad is okay when there is an odd scenario happening. So, what gives? Well, the odd thing is, it was just rearrangements of code and iframe srcdoc usage


<iframe frameborder=0 name=iftr id=iftr style=display:none; srcdoc="<body style=background-color:transparent;><p id=mg title='Wikipedia country page below via drag and drop to world map' draggable='true'>📍</p><br><br><div id=myh1></div><script type='text/javascript' src='./countries.js?rand=321156747657' defer></script></body>" data-src=></iframe>

… that ended up helping us fix the issues. Figure this, on iPad our emoji pin could not be made visible down the bottom left of iPad screen but could be made to work in the title elements section?! Of course, we might have been having a bad day, but in our defence, even debugging in Safari via …

  • iPad Safari web browser invocation …
  • Apple white lead from iPad to MacBook Air …
  • MacBook Air Safari web browser Develop menu dropdown got us to debugging functionality

showed nothing untoward, and neither did, on the iPad’s Chrome browser …


chrome://inspect

… debugging techniques.

Changed were our changed external Javascript countries.js in our changed countries.php GeoJson World Countries PHP web application in a new window. If you have any clues yourself, the comments are there?!


Previous relevant GeoJson World Drag and Drop Google Tutorial is shown below.

GeoJson World Drag and Drop Google Tutorial

GeoJson World Drag and Drop Google Tutorial

We often turn to what we like to call …

  • “long hover” (ie. on non-mobile, wait for a long while after the onmouseover event initiation to see whether the user is still hovering) … and today, a bit like that, is the new, for us …
  • “long drag” (the user waits a long time between the drag initialization and the drop event)

… and because we found “dawdling” on a drag and drop fairly unnatural, we think this “long drag” idea “has legs”, in that it works well as a deliberate act made by a user, knowing at the end they benefit from their actions. For us, with our GeoJSON Map web application, yesterday, with GeoJson World Drag and Drop Pin Tutorial, the drag and drop led to …

  • Wikipedia country information webpage … and today, we allow a “long drag” get you to …
  • Google Maps drop position information webpage … if the “long drag” is for 10 or more seconds …
  • Google Earth drop position information webpage … if the “very long drag” is for 20 or more seconds

… arranged for via our changed external Javascript countries.js in our changed countries.php GeoJson World Countries PHP web application in a new window.


Previous relevant GeoJson World Drag and Drop Pin Tutorial is shown below.

GeoJson World Drag and Drop Pin Tutorial

GeoJson World Drag and Drop Pin Tutorial

Before today, with the GeoJson Countries web application from GeoJson World Colour Wheel Wikipedia Integration Tutorial we’ve mainly used …

  • onclick event logic … and today, we start to also include …
  • drag and drop event logic (like, but nuanced as explained below, the experimental drag and drop ideas included in the recent Planet Moon Game Tutorial) … the nuanced differences involving …
    1. the drag part of the events occurs in an iframe (populated via small amount of srcdoc HTML and Javascript) able to reference its originator via window.parent …
    2. drop part of the events occurs in that originator parent and so several Javascript function called by the child reside in the parent … and …
    3. the child “drag” event controller uses the new external Javascript countries.js

      // countries.js
      // July, 2023
      // RJM Programming
      // Help out countries.html and countries.php
      var pos3=0, pos4=0, tdid='', poligono, punto, coone='', prectis;

      // var poligono = [[2,9],[8,6],[12,10],[15,2],[10,4],[5,1]];
      // var punto = [6, 5];

      function pointInPolygon(polygon, point) { // thanks to https://community.appinventor.mit.edu/t/geofence-check-if-a-point-is-inside-a-polygon-javascript-map/57091
      var odd = false;
      for (var i = 0, j = polygon.length - 1; i < polygon.length; i++) {
      if (((polygon[i][1] > point[1]) !== (polygon[j][1] > point[1]))
      && (point[0] < ((polygon[j][0] - polygon[i][0]) * (point[1] - polygon[i][1]) / (polygon[j][1] - polygon[i][1]) + polygon[i][0]))) {
      odd = !odd;
      }
      j = i;
      }
      return odd;
      }

      function andlaterstill() {
      if (9 == 6) { // temporary
      if (tdid != '') {
      document.getElementById(tdid).innerHTML=document.getElementById(tdid).innerHTML.substring(0,1);
      } else if (document.getElementById('mytable').innerHTML.indexOf(clonedatatwo) != '') {
      document.getElementById('myh1').innerHTML=document.getElementById('myh1').innerHTML.split('</table>')[0] + '</table>';
      }
      if (document.getElementsByTagName('div')[0].innerHTML.indexOf(clonedatatwo) != -1) {
      document.getElementsByTagName('div')[0].innerHTML=document.getElementsByTagName('div')[0].innerHTML.replace(clonedatatwo,'');
      } else if (document.getElementsByTagName('div')[0].innerHTML.indexOf(clonedatatwo.replace('dragging','')) != -1) {
      document.getElementsByTagName('div')[0].innerHTML=document.getElementsByTagName('div')[0].innerHTML.replace(clonedatatwo.replace('dragging',''),'');
      } else if (document.body.innerHTML.split('<table')[0].indexOf(clonedatatwo.replace('dragging','')) != -1) {
      document.body.innerHTML=document.body.innerHTML.replace(document.body.innerHTML.split('<table')[0], document.body.innerHTML.split('<table')[0].replace(clonedatatwo.replace('dragging',''),''));
      } else if (document.body.innerHTML.split('<table')[0].indexOf(clonedatatwo) != -1) {
      document.body.innerHTML=document.body.innerHTML.replace(document.body.innerHTML.split('<table')[0], document.body.innerHTML.split('<table')[0].replace(clonedatatwo,''));
      }
      }
      tdid='';
      }

      function getprectis() {
      if (window.opener) {
      if (window.opener.document.getElementsByTagName('body')[0]) {
      return window.opener.document.body.getBoundingClientRect();
      } else if (window.parent) {
      if (parent.document.getElementsByTagName('body')[0]) {
      return parent.document.body.getBoundingClientRect();
      }
      }
      } else if (window.parent) {
      if (parent.document.getElementsByTagName('body')[0]) {
      return parent.document.body.getBoundingClientRect();
      }
      }
      return null;
      }

      function wod(ididea) {
      if (window.opener) {
      if (window.opener.document.getElementsByTagName(ididea)[0]) {
      return window.opener.document.getElementsByTagName(ididea)[0];
      } else if (window.parent) {
      if (parent.document.getElementsByTagName(ididea)[0]) {
      return parent.document.getElementsByTagName(ididea)[0];
      }
      }
      } else if (window.parent) {
      if (parent.document.getElementsByTagName(ididea)[0]) {
      return parent.document.getElementsByTagName(ididea)[0];
      }
      }
      return null;
      }

      function ccit() {
      var divs, esot=[], bodyois=null;
      if (window.opener) {
      if (window.opener.document.getElementsByTagName('body')[0]) {
      bodyois=window.opener.document.getElementsByTagName('body')[0];
      divs=window.opener.document.getElementsByTagName('div');
      } else if (window.parent) {
      if (parent.document.getElementsByTagName('body')[0]) {
      bodyois=parent.document.getElementsByTagName('body')[0];
      divs=parent.document.getElementsByTagName('div');
      }
      }
      } else if (window.parent) {
      if (parent.document.getElementsByTagName('body')[0]) {
      bodyois=parent.document.getElementsByTagName('body')[0];
      divs=parent.document.getElementsByTagName('div');
      }
      }

      for (var ii=0; ii<divs.length; ii++) {
      if (divs[ii].outerHTML.split('>')[0].indexOf(' title=' + String.fromCharCode(34) + coone) != -1) {
      divs[ii].innerHTML=divs[ii].innerHTML.replace('z-index:24;', 'z-index:123;').replace('lime;', 'rgba(200,200,200,0.3);');
      divs[ii].name='d' + tdid.replace(/\ /g, '_');
      }
      }
      }

      function andqlater() {
      //alert('HeRe');
      tdid='';
      var ppig='[]', coo='', coos=[], ip=0;
      var squares; //=window.opener.document.getElementsByTagName('area');

      if (window.opener) {
      if (window.opener.document.getElementsByTagName('body')[0]) {
      squares=window.opener.document.getElementsByTagName('area');
      } else if (window.parent) {
      if (parent.document.getElementsByTagName('body')[0]) {
      squares=parent.document.getElementsByTagName('area');
      }
      }
      } else if (window.parent) {
      if (parent.document.getElementsByTagName('body')[0]) {
      squares=parent.document.getElementsByTagName('area');
      }
      }

      for (var ii=1; ii<=squares.length; ii++) {
      ppig='[]';
      coos=squares[eval(-1 + ii)].coords.replace(/\ /g,',').split(',');
      ppig='[[' + coos[0] + ',' + coos[1] + ']]';
      for (ip=2; ip<coos.length; ip+=2) {
      ppig=ppig.replace(']]', '],[' + coos[ip] + ',' + coos[eval(1 + ip)] + ']]');
      poligono=eval(ppig);
      //alert('' + punto);
      if (pointInPolygon(poligono, punto)) {
      tdid='' + squares[eval(-1 + ii)].getAttribute('data-cname');
      coone=('' + squares[eval(-1 + ii)].coords).split(' ')[0];
      setTimeout(ccit, 2000);
      console.log('tdid=' + tdid + ' and coone=' + coone);
      if (window.opener) {
      if (window.opener.document.getElementsByTagName('body')[0]) {
      window.focus();
      document.getElementById('myh1').innerHTML='<iframe style="width:100%;height:800px;" src="//wikipedia.org/wiki/' + encodeURIComponent(tdid.replace(/\ /g,'_')) + '"></iframe>';
      } else if (window.parent) {
      if (parent.document.getElementsByTagName('body')[0]) {
      document.getElementById('myh1').innerHTML='<iframe style="width:100%;height:800px;" src="//wikipedia.org/wiki/' + encodeURIComponent(tdid.replace(/\ /g,'_')) + '"></iframe>';
      window.open('//wikipedia.org/wiki/' + encodeURIComponent(tdid.replace(/\ /g,'_')),'_blank','top=180,left=' + eval(-350 + screen.width) + ',width=350,height=600');
      }
      }
      } else if (window.parent) {
      if (parent.document.getElementsByTagName('body')[0]) {
      document.getElementById('myh1').innerHTML='<iframe style="width:100%;height:800px;" src="//wikipedia.org/wiki/' + encodeURIComponent(tdid.replace(/\ /g,'_')) + '"></iframe>';
      window.open('//wikipedia.org/wiki/' + encodeURIComponent(tdid.replace(/\ /g,'_')),'_blank','top=180,left=' + eval(-350 + screen.width) + ',width=350,height=600');
      }
      }
      return tdid;
      }
      }
      }
      console.log('tdid=' + tdid);
      //setTimeout(andlaterstill, 100);
      return '';
      }

      window.addEventListener("DOMContentLoaded", () => {
      const source = document.querySelector("#mg");
      console.log('source.id=' + source.id);
      source.addEventListener("dragstart", (ev) => {
      console.log("dragStart");
      // Change the source element's background color
      // to show that drag has started
      ev.currentTarget.classList.add("dragging");
      // Clear the drag data cache (for all formats/types)
      ev.dataTransfer.clearData();
      // Set the drag's format and data.
      // Use the event target's id for the data
      ev.dataTransfer.setData("text/plain", ev.target.id);
      //ev.dataTransfer.setData("text/html", ev.target.outerHTML);
      });
      source.addEventListener("dragend", (ev) =>
      ev.target.classList.remove("dragging")
      );

      const target = wod('body'); //window.opener.document.getElementsByTagName('body')[0];
      target.id='usemap';
      console.log('target.id=' + target.id);
      target.addEventListener("dragover", (ev) => {
      console.log("dragOver");
      ev.preventDefault();
      });
      target.addEventListener("drop", (ev) => {
      console.log("Drop");
      ev.preventDefault();
      // Get the data, which is the id of the source element
      const data = ev.dataTransfer.getData("text");
      const source = document.getElementById(data);

      var prectis=getprectis();

      console.log('' + ev.target.id);
      if (!document.getElementById('callback')) {
      if (('' + ev.target.id).substring(0,2) == 'im') {
      score+=eval(ev.target.innerHTML.substring(0,1));
      }
      } else if (1 == 1) { //document.getElementById('callback')) {
      secs++;
      if (('' + ev.target.getAttribute('data-answer')) == ('' + document.getElementById('mg').getAttribute('data-answer'))) {
      score++;
      document.getElementById('score').innerHTML='Score: ' + score + '/' + secs + '';
      } else {
      document.getElementById('score').innerHTML='Score: ' + score + '/' + secs + '';
      alert('Correct answer was ' + document.getElementById('mg').getAttribute('data-answer'));
      }
      location.href=document.getElementById('callback').value + '?score=' + score + '&secs=' + secs;
      }
      if (1 == 2) {
      //clonedatatwo=document.getElementById('source').outerHTML;
      //document.getElementById('mytable').innerHTML=document.getElementById('mytable').innerHTML.replace(clonedatatwo, '');
      //ev.target.innerHTML=ev.target.innerHTML.substring(0,1) + clonedatatwo;
      } else if ((9 == 9 || ('' + ev.target.id).substring(0,2) == 'im')) {
      //clonedatatwo=document.getElementById('source').outerHTML;
      // // andqlater();
      //document.getElementById('mytable').innerHTML=document.getElementById('mytable').innerHTML.replace(clonedatatwo, '');
      //ev.target.innerHTML=ev.target.innerHTML.substring(0,1) + clonedatatwo;
      //document.getElementById('myh1').appendChild(source);
      //if (('' + ev.target.id).substring(0,2) != 'im') {
      //document.getElementById('myh1').insertAdjacentHTML('beforeend', clonedatatwo);
      //} else {
      //ev.target.insertAdjacentHTML('beforeend', clonedatatwo);
      //}
      } else {
      ev.target.appendChild(source);
      }

      prectis=getprectis();

      if (ev.touches) {
      if (ev.touches[0].pageX) {
      pos3 = ev.touches[0].pageX;
      pos4 = ev.touches[0].pageY;
      } else {
      pos3 = ev.touches[0].clientX;
      pos4 = ev.touches[0].clientY;
      }
      console.log('pos3 = ' + pos3 + ',pos4 = ' + pos4);
      } else if (ev.clientX || ev.clientY) {
      pos3 = ev.clientX;
      pos4 = ev.clientY;
      console.log('pos3 = ' + pos3 + ' ,pos4 = ' + pos4);
      } else {
      pos3 = ev.pageX;
      pos4 = ev.pageY;
      console.log('pos3 = ' + pos3 + ', pos4 = ' + pos4);
      }
      //alert('[' + pos3 + ',' + pos4 + ']');
      var wop=0;
      if (window.opener) {
      if (window.opener.document.getElementsByTagName('body')[0]) {
      window.opener.derivethislong(pos3);
      window.opener.derivethislat(pos4);
      wop=window.opener.getzoom();
      } else if (window.parent) {
      if (parent.document.getElementsByTagName('body')[0]) {
      parent.derivethislong(pos3);
      parent.derivethislat(pos4);
      wop=parent.getzoom();
      }
      }
      } else if (window.parent) {
      if (parent.document.getElementsByTagName('body')[0]) {
      parent.derivethislong(pos3);
      parent.derivethislat(pos4);
      wop=parent.getzoom();
      }
      }

      //pos3+=eval(wop * prectis.left);
      //pos4+=eval(wop * prectis.top);
      var xwop='1';


      console.log('prectis.left/top=' + prectis.left + '/' + prectis.top);


      //punto=eval('[' + eval(eval('' + pos3) / eval('' + wop)) + ',' + eval(eval('' + pos4) / eval('' + wop)) + ']');

      punto=eval('[' + eval(eval(-prectis.left * eval('' + wop) + eval('' + pos3)) / eval('' + wop)) + ',' + eval(eval(-prectis.top * eval('' + wop) + eval('' + pos4)) / eval('' + wop)) + ']');

      console.log(punto);
      andqlater();

      });

      //const reset = document.querySelector("#reset");
      //reset.addEventListener("click", () => document.location.reload());

      });

      function dragorig() {
      if (document.getElementById('mg')) {
      document.getElementById('mg').draggable='true';
      } else {
      setTimeout(dragorig, 5000);
      }
      }

      // setTimeout(dragorig, 5000);

… to open Wikipedia Country information webpages of dropped into user drags (on a world map), in our changed countries.php GeoJson World Countries PHP web application in a new window.


Previous relevant GeoJson World Colour Wheel Wikipedia Integration Tutorial is shown below.

GeoJson World Colour Wheel Wikipedia Integration Tutorial

GeoJson World Colour Wheel Wikipedia Integration Tutorial

Further to yesterday’s GeoJson World Coastline Quiz Deployments Tutorial, with our two …

… web applications, today we want to deepen the integration with Wikipedia on two fronts …

  • GeoJson World Coastline optional quiz Wikipedia webpage navigation offerings, achieved via a Javascript override initiative …

    const confirm = (cblurb) => {
    if (cblurb.indexOf('Answer was ') != -1 && cblurb.indexOf('. ') != -1) {
    var resp=prompt(cblurb.replace(cblurb.split('Answer was ')[1].split('. ')[0] + '. ', cblurb.split('Answer was ')[1].split('. ')[0] + ' (answer W to see more information on Wikipedia). '), '');
    if (resp != null) {
    if (resp.toLowerCase() == 'w') {
    window.open('//en.wikipedia.org/wiki/' + cblurb.split('Answer was ')[1].split('. ')[0].replace(/\ /g,'_'), '_blank', 'top=70,left=70,width=650,height=650');
    return true;
    }
    return true;
    } else {
    return false;
    }
    }
    return window.confirm(cblurb);
    };
  • GeoJson World Countries and Coastline changed map.php Map Chart new onclick menu links to our Colour Wheel web application, and its Wikipedia images and TimeZone information

… in our changed regions.php GeoJson World Coastlines PHP web application in a new window, or you can also try below.


Previous relevant GeoJson World Coastline Quiz Deployments Tutorial is shown below.

GeoJson World Coastline Quiz Deployments Tutorial

GeoJson World Coastline Quiz Deployments Tutorial

The deployment follow up to yesterday’s GeoJson World Coastline Function Noun Naming Tutorial seems to concern mobile platform use of the new …

… where heap memory concerns related to the global variables memory used in our GeoJson World Coastlines webpage could cause mobile platform usage reloads of the web application, reminiscent of the external Javascript concerns we had back at GeoJson World Countries SVG Overlay Safari Error Tutorial.

There, as for here, mobile usage got better by swapping global variable usage for HTML content static PHP …

<?php echo ”

<select id=spops style=display:none;>" . $sih . "</select>
<select id=srivs style=display:none;>" . $rih . "</select>

“; ?>

… via …

<?php

$sih='';
$rih='';

function oururlencode($instuff) {
$outstuff='';
$dotbits=explode('.', $instuff);
if (sizeof($dotbits) > 1) {
$outstuff=$instuff;
for ($ii=1; $ii<sizeof($dotbits); $ii++) {
if (strlen( explode(',', explode(' ', $dotbits[$ii])[0])[0] ) > 3) {
$outstuff=str_replace('.' . explode(',', explode(' ', $dotbits[$ii])[0])[0], '.' . substr(explode(',', explode(' ', $dotbits[$ii])[0])[0],0,3), $outstuff);
}
}
return urlencode($outstuff);
}
return urlencode($instuff);
}

if (file_exists('population.geojson')) {
$rbits=explode(']}}', file_get_contents('./population.geojson'));
for ($irr=1; $irr<(-1 + sizeof($rbits)); $irr++) {
$hdrbit=explode('{"type":"Feature', $rbits[-1 + $irr])[-1 + sizeof(explode('{"type":"Feature', $rbits[-1 + $irr]))];
$hbits=explode(',"NAME":"', $hdrbit);
if (sizeof($hbits) > 1) {
if (strpos($sih, ' id=' . str_replace('+','%20',urlencode(explode('"', $hbits[1])[0])) . ' ') === false) {
$sih.='<option id=' . str_replace('+','%20',urlencode(explode('"', $hbits[1])[0])) . ' value=' . str_replace('+','%20', str_replace('[','', str_replace(']','', str_replace('],[',',' ,explode(']}', explode(':[',$hdrbit)[1])[0])))) . '></option>';
}
}
}
}

if (file_exists('rivers.geojson')) {
$rbits=explode(']]}}', file_get_contents('./rivers.geojson'));
for ($irr=1; $irr<(-1 + sizeof($rbits)); $irr++) {
$hdrbit=explode('{"type":"Feature', $rbits[-1 + $irr])[-1 + sizeof(explode('{"type":"Feature', $rbits[-1 + $irr]))];
$hbits=explode(',"name":"', $hdrbit);
if (sizeof($hbits) > 1) {
if (strpos($sih, 'id=river' . str_replace('+','%20',urlencode(explode('"', $hbits[1])[0])) . '>') === false) {
$rih.='<option value=' . str_replace('+','%20', oururlencode(str_replace('[','', str_replace(']','', str_replace('],[', ',', str_replace(']],[[', ' ', explode(']]}', explode(':[[',$hdrbit)[1])[0])))))) . ' id=river' . str_replace('+','%20',urlencode(explode('"', $hbits[1])[0])) . '></option>';
} else {
$rih=str_replace(' id=river' . str_replace('+','%20', oururlencode(explode('"', $hbits[1])[0])) . '>', str_replace('+','%20', urlencode(str_replace('[','', str_replace(']','', str_replace('],[', ',', str_replace(']],[[', ' ', explode(']]}', explode(':[[',$hdrbit)[1])[0])))))) . ' id=river' . str_replace('+','%20',urlencode(explode('"', $hbits[1])[0])) . '>', $rih);
}
}
}
}

?>

… code usage (ie. the HTML file ends up bigger, to help) in our changed regions.php GeoJson World Coastlines PHP web application in a new window, or you can also try below.


Previous relevant GeoJson World Coastline Function Noun Naming Tutorial is shown below.

GeoJson World Coastline Function Noun Naming Tutorial

GeoJson World Coastline Function Noun Naming Tutorial

We’re working on an extension to yesterday’s GeoJson World Coastline Rivers Quiz Tutorial‘s Rivers Quiz functionality within our GeoJson World Coastlines web application, and have …

  • settled on an approach … but …
  • not yet finished on deployment issues

… but it is this approach we wanted to talk about today.

Our approach borrows from Object Oriented Programming (OOP) the idea that …

  • just as with OOP thinking class names are like nouns and the methods within that class are like verbs … we, with our approach
  • help readability of our non-OOP functional code by including those nouns and verbs, as well as ideas like use of plurals to indicate array involvement, with our Javascript function naming

… we can best illustrate to you via showing you new functions and variables and modified code to show you this approach in code …


var rivers='', arivers=[], iguess=-1, isofar=' ', jscore=0, jgoes=0, elema=null, contexta=null, rectisleft=0, rectistop=0, isokto=true;
var populations='', apopulations=[], jguess=-1, jsofar=' ', both=false, jlastn='';
var idone=false;


function populaterivers() {
var found=-1, ifnd=0;
var rbits=rivers.split(']]}}');
var murraytotal=0.0;
rivers='';
var lastn='', hdrbit='', hbits=[];
for (var irr=1; irr<(-1 + rbits.length); irr++) {
hdrbit=rbits[-1 + irr].split('{' + String.fromCharCode(34) + 'type' + String.fromCharCode(34) + ':' + String.fromCharCode(34) + 'Feature')[eval(-1 + rbits[-1 + irr].split('{' + String.fromCharCode(34) + 'type' + String.fromCharCode(34) + ':' + String.fromCharCode(34) + 'Feature').length)] + '';
//alert(hdrbit);
hbits=hdrbit.split(',' + String.fromCharCode(34) + 'name' + String.fromCharCode(34) + ':' + String.fromCharCode(34));
if (hbits.length > 1) {
if ((!navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i) || eval('' + arivers.length) <= 200)) {
if (lastn != hbits[1].split(String.fromCharCode(34))[0]) {
lastn=hbits[1].split(String.fromCharCode(34))[0];
found=-1;
if (arivers.length > 0) {
for (ifnd=0; ifnd<arivers.length; ifnd++) {
if (('' + arivers[ifnd]).indexOf(':') != -1 && found == -1) {
if (('' + arivers[ifnd]).split(':')[0] == lastn) { found=ifnd; }
}
}
}
if (found >= 0) {
if (lastn == 'Murray') {
console.log(hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,''));
}
if (!navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
arivers[found]+=' ' + lessit(hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,''));
}
} else {
if (lastn == 'Murray') {
console.log(lastn + ':' + hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,''));
}
arivers.push(lastn + ':' + lessit(hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,'')));
}
} else if (1 == 1 || !navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
if (lastn == 'Murray') {
console.log(hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,''));
}
arivers[-1 + arivers.length]+=' ' + lessit(hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,''));
}
}
}
}
rbits=[];
lastn='';
//if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
// alert(arivers.length);
//}
//return;
//plotariver(getariver());
if (!idone) { idone=true; getariver(); }
}


function lessit(maybe) {
var altmaybe='', altmaybed='';
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
var acsvs=maybe.split(',');
for (var ic=0; ic<acsvs.length; ic++) {
altmaybe+=altmaybed + (acsvs[ic] + '.xyz').substring(0,eval(3 + (acsvs[ic] + '.xyz').indexOf('.'))).split('.xyz')[0];
altmaybed=',';
}
acsvs=[];
return altmaybe;
}
return maybe;
}



function populatepopulations() {
var found=-1, ifnd=0;
var rbits=populations.split(']}}');
populations='';
var jlastn='', hdrbit='', hbits=[];
for (var irr=1; irr<(-1 + rbits.length); irr++) {
hdrbit=rbits[-1 + irr].split('{' + String.fromCharCode(34) + 'type' + String.fromCharCode(34) + ':' + String.fromCharCode(34) + 'Feature')[eval(-1 + rbits[-1 + irr].split('{' + String.fromCharCode(34) + 'type' + String.fromCharCode(34) + ':' + String.fromCharCode(34) + 'Feature').length)] + '';
//alert(hdrbit);
hbits=hdrbit.split(',' + String.fromCharCode(34) + 'NAME' + String.fromCharCode(34) + ':' + String.fromCharCode(34));
if (hbits.length > 1) {
if (jlastn != hbits[1].split(String.fromCharCode(34))[0]) {
jlastn=hbits[1].split(String.fromCharCode(34))[0];
found=-1;
if (apopulations.length > 0) {
for (ifnd=0; ifnd<apopulations.length; ifnd++) {
if (('' + apopulations[ifnd]).indexOf(':') != -1 && found == -1) {
if (('' + apopulations[ifnd]).split(':')[0] == jlastn) { found=ifnd; }
}
}
}
if (found >= 0) {
if (jlastn == 'Sydney') { console.log(hdrbit.split(':[')[1].replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,'')); }
apopulations[found]+=' ' + lessit(hdrbit.split(':[')[1].replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,''));
} else {
if (jlastn == 'Sydney') { console.log(jlastn + ':' + hdrbit.split(':[')[1].replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,'')); }
apopulations.push(jlastn + ':' + lessit(hdrbit.split(':[')[1].replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,'')));
}
} else {
if (jlastn == 'Sydney') { console.log(hdrbit.split(':[')[1].replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,'')); }
apopulations[-1 + apopulations.length]+=' ' + lessit(hdrbit.split(':[')[1].replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,''));
}
}
}
rbits=[];
jlastn='';
//return;
//plotariver(getariver());
}



function getariver() {
//for (var ih=0; ih<arivers.length; ih++) {
// if (arivers[ih].split(':')[0] == 'Murray') { return ih; }
//}
if (!idone) { document.getElementById('ifrivers').src='./rivers.geojson'; } else if (arivers.length > 0) {
iguess=Math.floor(Math.random() * arivers.length);
if (isofar.indexOf(',' + iguess + ',') != -1) {
while (isofar.indexOf(',' + iguess + ',') != -1) {
iguess=Math.floor(Math.random() * arivers.length);
}
}
isofar+=',' + iguess + ',';
lastn=arivers[iguess].split(':')[0];
plotariver(iguess);
setTimeout(askariver, 8000);
return iguess;
}
}


function askariver() {
var another=false;
var midbit='';
var origboth=both;
var thing='river';
if (!both) { midbit='Append spaces to also answer a question regarding the Populations Quiz, or P to just do Populations Quiz.'; } else { thing='population'; }
var retthis=prompt('What is the name of this new blue river plotted on the world map? ' + midbit + ' Enter ? to get given more time looking at (longitude,latitude) = (' + arivers[iguess].split(':')[1].split(',')[0] + ',' + arivers[iguess].split(':')[1].split(',')[1] + ')', '');
if (retthis == null) {
both=false;
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (retthis.toLowerCase().trim() == 'p') {
both=false;
isokto=true;
getapopulation();
return '';
} else if (retthis.trim() == '?') {
if (retthis.trim() != retthis && !origboth) { both=true; }
setTimeout(askariver, 8000);
return '';
} else if (retthis.trim() == '') {
if (retthis != '' && !origboth) { thing='population'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
if (retthis != '' && !origboth) { isokto=true; both=true; getapopulation(); }
} else if (lastn.toLowerCase().indexOf(retthis.toLowerCase()) != -1 && lastn.toLowerCase() == retthis.toLowerCase() && retthis.trim().length >= 1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='population'; }
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (lastn.toLowerCase().indexOf(retthis.toLowerCase()) == -1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='population'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (lastn.toLowerCase() == retthis.toLowerCase()) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='population'; }
jgoes++;
jscore++;
another=confirm('Well done! Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (retthis.trim().length >= 1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='population'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else {
if (retthis.trim() != retthis && !origboth) { both=true; thing='population'; }
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
}
if (both && !origboth) { isitok=true; getapopulation(); return ''; } else if (both) { return ''; }
if (another) { if (both) { getariver(); getapopulation(); } else { getariver(); } } else { contexta.clearRect(0,0,360,180); }
return '';
}



function plotariver(which) {
if (both) { isokto=true; }
if (isokto) { contexta.clearRect(0,0,360,180); }
if (both) { isokto=false; }
lastn=arivers[which].split(':')[0];
var rest=arivers[which].split(':')[1];
var consolelog='';
console.log(rest);
var restlonglat=[]; //rest.split(',');
var therest=rest.split(' ');
//alert(eval(180.0 + eval('' + restlonglat[0])) + ',' + eval(90.0 - eval('' + restlonglat[1])));
for (var jrrr=0; jrrr<therest.length; jrrr++) {
restlonglat=therest[jrrr].split(',');
for (var irrr=2; irrr<restlonglat.length; irrr+=2) {
if (eval(1 + irrr) < eval('' + restlonglat.length)) {
if (irrr == 2) {
contexta.strokeStyle = '#0000ff';
contexta.lineWidth = 1;
contexta.beginPath();
//console.log('context.moveTo(' + eval(180.0 + eval('' + restlonglat[0])) + ',' + eval(90.0 - eval('' + restlonglat[1])) + ');');
contexta.moveTo(eval(180.0 + eval('' + restlonglat[0])), eval(90.0 - eval('' + restlonglat[1])));
}
//alert(eval(180.0 + eval('' + restlonglat[irrr])) + ',' + eval(90.0 - eval('' + restlonglat[1 + irrr])));
consolelog=('context.lineTo(' + eval(180.0 + eval('' + restlonglat[eval(0 + eval('' + irrr))])) + ',' + eval(90.0 - eval('' + restlonglat[eval(1 + eval('' + irrr))])) + ');');
if (consolelog.indexOf('NaN') == -1) {
contexta.lineTo(eval(180.0 + eval('' + restlonglat[eval(0 + eval('' + irrr))])), eval(90.0 - eval('' + restlonglat[eval(1 + eval('' + irrr))])));
}
//if (eval(irrr + 1) >= eval('' + restlonglat.length)) { context.stroke(); }
}
}
contexta.stroke();
}
}


function getthejson(iois) {
if (iois.src.indexOf('.geojson') != -1) {
var aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
if (iois.src.indexOf('rivers.geojson') != -1) {
rivers='' + aconto.body.innerHTML;
setTimeout(populaterivers, 500);
iois.src='./population.geojson';
} else if (iois.src.indexOf('population.geojson') != -1) { // && !navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
populations='' + aconto.body.innerHTML;
setTimeout(populatepopulations, 500);
}

}
}
}
}

function getapopulation() {
//for (var ih=0; ih<apopulations.length; ih++) {
// if (apopulations[ih].split(':')[0] == 'Murray') { return ih; }
//}
jguess=Math.floor(Math.random() * apopulations.length);
if (jsofar.indexOf(',' + jguess + ',') != -1) {
while (jsofar.indexOf(',' + jguess + ',') != -1) {
jguess=Math.floor(Math.random() * apopulations.length);
}
}
jsofar+=',' + jguess + ',';
jlastn=apopulations[jguess].split(':')[0];
plotapopulation(jguess);
setTimeout(askapopulation, 9000);
return jguess;
}


function askapopulation() {
var another=false;
var origboth=both;
var midbit='';
var thing='population';
if (!both) { midbit='Append spaces to also answer a question regarding the Rivers Quiz, or R to just do Rivers Quiz.'; } else { thing='river'; }
var retthis=prompt('What is the name of this new red population area plotted on the world map? ' + midbit + ' Enter ? to get given more time looking at (longitude,latitude) = (' + apopulations[jguess].split(':')[1].split(',')[0] + ',' + apopulations[jguess].split(':')[1].split(',')[1] + ')', '');
if (retthis == null) {
both=false;
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (retthis.toLowerCase().trim() == 'r') {
both=false;
isokto=true;
getariver();
return '';
} else if (retthis.trim() == '?') {
if (retthis.trim() != retthis && !origboth) { both=true; }
setTimeout(askapopulation, 9000);
return '';
} else if (retthis.trim() == '') {
if (retthis != '' && !origboth) { thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
if (retthis != '' && !origboth) { isokto=true; both=true; getariver(); }
} else if (jlastn.toLowerCase().indexOf(retthis.toLowerCase()) != -1 && jlastn.toLowerCase() == retthis.toLowerCase() && retthis.trim().length >= 1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (jlastn.toLowerCase().indexOf(retthis.toLowerCase()) == -1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (jlastn.toLowerCase() == retthis.toLowerCase()) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Well done! Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (retthis.trim().length >= 1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
}
if (both) { contexta.clearRect(0,0,360,180); }
if (both && !origboth) { isitok=true; getariver(); return ''; }
if (another) { if (both) { getariver(); getapopulation(); } else { getapopulation(); } } else { contexta.clearRect(0,0,360,180); }
return '';
}


function plotapopulation(which) {
if (isokto) { contexta.clearRect(0,0,360,180); }
//if (both) { isokto=true; }
jlastn=apopulations[which].split(':')[0];
var rest=apopulations[which].split(':')[1];
var restlonglat=[]; //rest.split(',');
restlonglat=rest.split(',');
if (eval('' + restlonglat.length) >= 2) {
contexta.fillStyle = 'red';
contexta.fillRect(eval(180.0 + eval('' + restlonglat[0])), eval(90.0 - eval('' + restlonglat[1])),1,1);
contexta.fill();
}
}

… in our changed regions.php GeoJson World Coastlines PHP web application in a new window, or you can also try below.


Previous relevant GeoJson World Coastline Rivers Quiz Tutorial is shown below.

GeoJson World Coastline Rivers Quiz Tutorial

GeoJson World Coastline Rivers Quiz Tutorial

It’s time to turn our attention away from GeoJson World Countries, as talked about with yesterday’s GeoJson World Countries Plotted Ports Tutorial, and back to GeoJson World Coastline ideas. Why? We want to add a …


Rivers Quiz

… via the (generously provided) HTTP://geojson.xyz rivers lake centerlines GeoJSON data we download and then uploaded to become rivers.geojson data file. Now we were wondering out of …

  • use the URL to this GeoJSON file as the “src” attribute of an HTML iframe …

    <iframe id=ifrivers onload=getthejson(this); style=display:none; src=./rivers.geojson></iframe>

    … element (and then access the content via the onload event …

    var rivers='', arivers=[], iguess=-1, isofar=' ', jscore=0, jgoes=0, elema=null, contexta=null, rectisleft=0, rectistop=0;

    function getthejson(iois) {
    var aconto = (iois.contentWindow || iois.contentDocument);
    if (aconto != null) {
    if (aconto.document) { aconto = aconto.document; }
    if (aconto.body != null) {
    rivers='' + aconto.body.innerHTML;
    setTimeout(populaterivers, 500);
    }
    }
    }

    … function) would suffice, or if we would end up using …
  • Ajax call

… to access this data, and were a bit surprised the former method was all fine. Of course there are snazzy inbuilt Javascript hierarchical calls you can make to process the data, but we find, with GeoJSON data, in the client realm (where we’re keen to stay with today’s work (though PHP serverside can, of course, be purloined to do all this work, should you have that available)), of Javascript, we just need very basic string functions …

  • split (versus PHP explode)
  • index (versus PHP strpos)
  • substring (versus PHP substr)
  • replace (versus PHP str_replace)
  • push (versus PHP array_push)

… to get by processing …


var rivers='', arivers=[], iguess=-1, isofar=' ', jscore=0, jgoes=0, elema=null, contexta=null, rectisleft=0, rectistop=0;

function populaterivers() {
var found=-1, ifnd=0;
var rbits=rivers.split(']]}}');
var lastn='', hdrbit='', hbits=[];
for (var irr=1; irr<(-1 + rbits.length); irr++) {
hdrbit=rbits[-1 + irr].split('{' + String.fromCharCode(34) + 'type' + String.fromCharCode(34) + ':' + String.fromCharCode(34) + 'Feature')[eval(-1 + rbits[-1 + irr].split('{' + String.fromCharCode(34) + 'type' + String.fromCharCode(34) + ':' + String.fromCharCode(34) + 'Feature').length)] + '';
hbits=hdrbit.split(',' + String.fromCharCode(34) + 'name' + String.fromCharCode(34) + ':' + String.fromCharCode(34));
if (hbits.length > 1) {
if (lastn != hbits[1].split(String.fromCharCode(34))[0]) {
lastn=hbits[1].split(String.fromCharCode(34))[0];
found=-1;
if (arivers.length > 0) {
for (ifnd=0; ifnd<arivers.length; ifnd++) {
if (('' + arivers[ifnd]).indexOf(':') != -1 && found == -1) {
if (('' + arivers[ifnd]).split(':')[0] == lastn) { found=ifnd; }
}
}
}
if (found >= 0) {
//if (lastn == 'Murray') { console.log(hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,'')); }
arivers[found]+=' ' + hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,'');
} else {
//if (lastn == 'Murray') { console.log(lastn + ':' + hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,'')); }
arivers.push(lastn + ':' + hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,''));
}
} else {
//if (lastn == 'Murray') { console.log(hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,'')); }
arivers[-1 + arivers.length]+=' ' + hdrbit.split(':[[')[1].replace(/\]\]\,\[\[/g,' ').replace(/\]\,\[/g,',').replace(/\[/g,'').replace(/\]/g,'');
}
}
}
lastn='';
}

… the (ever so) consistent GeoJSON data!

The progress with GeoJson World Countries helped too. We knew to add another HTML canvas layer as per

  • document.body (now with the new onmousemove=airportplot(event); event logic) lowest level …
  • overlayed by HTML canvas element plotted with world country linework … now including …
  • overlayed by HTML canvas element dedicated to nearest airport plotting …

    <canvas id=myacanvas height='180' width='360' style='background-color:transparent;z-index:55;display:inline-block;position:absolute;top:0px;left:0px;'></canvas>
  • overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
  • HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay

… and to, at document.body onload logic …


// ... and extended document.body onload event logic has added, up near its top ...
elema = document.getElementById('myacanvas');
contexta = elema.getContext('2d');

… and supplement with another HTML sub “emoji button” ❓ ( &#10067; ) type element …


<sub style=cursor:pointer; onclick=getariver(); title='River Quiz'>&#10067;</sub>

… allowing for …


function getariver() {
//for (var ih=0; ih<arivers.length; ih++) {
// if (arivers[ih].split(':')[0] == 'Murray') { return ih; }
//}
iguess=Math.floor(Math.random() * arivers.length);
if (isofar.indexOf(',' + iguess + ',') != -1) {
while (isofar.indexOf(',' + iguess + ',') != -1) {
iguess=Math.floor(Math.random() * arivers.length);
}
}
isofar+=',' + iguess + ',';
lastn=arivers[iguess].split(':')[0];
plotariver(iguess);
setTimeout(askariver, 8000);
return iguess;
}

function askariver() {
var another=false;
var retthis=prompt('What is the name of this new blue river plotted on the world map? Enter ? to get given more time looking at (longitude,latitude) = (' + arivers[iguess].split(':')[1].split(',')[0] + ',' + arivers[iguess].split(':')[1].split(',')[1] + ')', '');
if (retthis == null) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (retthis.trim() == '?') {
setTimeout(askariver, 8000);
return '';
} else if (retthis.trim() == '') {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (lastn.toLowerCase().indexOf(retthis.toLowerCase()) == -1) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (lastn.toLowerCase() == retthis.toLowerCase()) {
jgoes++;
jscore++;
another=confirm('Well done! Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (retthis.trim().length >= 1) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else {
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
}
if (another) { getariver(); } else { contexta.clearRect(0,0,360,180); }
return '';
}

function plotariver(which) {
contexta.clearRect(0,0,360,180);
lastn=arivers[which].split(':')[0];
var rest=arivers[which].split(':')[1];
console.log(rest);
var restlonglat=[]; //rest.split(',');
var therest=rest.split(' ');
//alert(eval(180.0 + eval('' + restlonglat[0])) + ',' + eval(90.0 - eval('' + restlonglat[1])));
for (var jrrr=0; jrrr<therest.length; jrrr++) {
restlonglat=therest[jrrr].split(',');
for (var irrr=2; irrr<restlonglat.length; irrr+=2) {
if (irrr == 2) {
contexta.strokeStyle = '#0000ff';
contexta.lineWidth = 1;
contexta.beginPath();
console.log('context.moveTo(' + eval(180.0 + eval('' + restlonglat[0])) + ',' + eval(90.0 - eval('' + restlonglat[1])) + ');');
contexta.moveTo(eval(180.0 + eval('' + restlonglat[0])), eval(90.0 - eval('' + restlonglat[1])));
}
//alert(eval(180.0 + eval('' + restlonglat[irrr])) + ',' + eval(90.0 - eval('' + restlonglat[1 + irrr])));
console.log('context.lineTo(' + eval(180.0 + eval('' + restlonglat[eval(0 + eval('' + irrr))])) + ',' + eval(90.0 - eval('' + restlonglat[eval(1 + eval('' + irrr))])) + ');');
contexta.lineTo(eval(180.0 + eval('' + restlonglat[eval(0 + eval('' + irrr))])), eval(90.0 - eval('' + restlonglat[eval(1 + eval('' + irrr))])));
//if (eval(irrr + 1) >= eval('' + restlonglat.length)) { context.stroke(); }
}
contexta.stroke();
}
}

… to work the Rivers Quiz. Finally, though, for all good practicalities we also need those zoom logics out of GeoJson World Countries logic, via “emoji button” 🔎 ( &#128270; ) …


<a style=cursor:pointer; title='Zoom In' onclick='event.stopPropagation(); event.preventDefault(); zoomin();'>&#128270;</a>

… calling on meta name=viewport tag for mobile …


<meta id='myviewport' name='viewport' content='width=device-width, initial-scale=1, minimum-scale=0.1, maximum-scale=8, user-scalable=yes' >

… as well as the zooming Javascript …


var jzoom=1.0, izoom=location.search.split('zoom=')[1] ? eval(decodeURIComponent(location.search.split('zoom=')[1].split('&')[0])) : 1.0;

function zoomin() {
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
jzoom*=2;
document.getElementById('myviewport').setAttribute('content', 'width=device-width, initial-scale=' + eval(jzoom * izoom) + ', minimum-scale=0.1, maximum-scale=8, user-scalable=yes');
window.scrollTo(0,0); //izoom*=2;
} else {
//if (1 == 1) {
// alert('Please use your web browser View Menu options Zoom In or Zoom Out');
//} else {
document.body.style.zoom=('' + eval(izoom * 200.0) + '%');
izoom*=2;
//location.href=document.URL.split('?')[0].split('#')[0] + '?zoom=' + encodeURIComponent('' + eval(2 * izoom));
//}
}
}

So feel free to try the new Rivers Quiz in our changed regions.php GeoJson World Coastlines PHP web application in a new window, or you can also try below.


Previous relevant GeoJson World Countries Plotted Ports Tutorial is shown below.

GeoJson World Countries Plotted Ports Tutorial

GeoJson World Countries Plotted Ports Tutorial

There are a few interesting aspects to today’s extension of functionality onto yesterday’s GeoJson World Countries Plotted Airports Tutorial‘s GeoJson World Countries web application …

  • where to modularise … we think “data collection” commonality is a good reason, and so we make these changes to intair.php
  • making an (“animated emoji”) button dual purpose on top of originally being a single purpose button …

    <sub title='Show Nearby Airports' onclick='doair=how(true,this); twothousand*=2; this.title=this.title.substring(0,4) + String.fromCharCode(105) + String.fromCharCode(110) + String.fromCharCode(103) + this.title.replace(this.title.split(String.fromCharCode(32))[0] + String.fromCharCode(32), String.fromCharCode(32));' data-type=9992 style=cursor:pointer; id=portsub>&#9992;</sub>

    … working with the intairsuffix global variable that could add a new GET argument where both the “port” label in &port=[value] and that [value] can affect behaviour from the intair.php PHP helper tool above …

    var intairsuffix='', zhra=null, zhrb=null, kklat=0, kklong=0, doair=false, vsll=[-999.0], answered=true;

    function how(atr, isub) {
    var curgd=isub.getAttribute('data-type');
    if (('' + curgd) == '9992') { // airport
    if (intairsuffix != '') {
    intairsuffix='&port=air';
    isub.title='Showing Nearby Airports and Ports';
    document.getElementById('title').value='Nearby Timezone Places and Airports and Ports';
    }
    } else if (('' + curgd) == '128674') { // port
    if (intairsuffix == '') {
    if (doair) {
    intairsuffix='&port=air';
    isub.title='Showing Nearby Airports and Ports';
    document.getElementById('title').value='Nearby Timezone Places and Airports and Ports';
    } else {
    intairsuffix='&port=y';
    isub.title='Showing Nearby Ports';
    document.getElementById('title').value='Nearby Timezone Places and Ports';
    }
    }
    }
    return true;
    }

    function feedhow() {
    var isub=document.getElementById('portsub');
    var curgd=isub.getAttribute('data-type');
    if (('' + curgd) == '9992') { // airport
    isub.innerHTML='&#128674;';
    isub.setAttribute('data-type', '128674');
    } else if (('' + curgd) == '128674') { // port
    isub.innerHTML='&#9992;';
    isub.setAttribute('data-type', '9992');
    }
    }

  • Ajax asynchronous usage for second half of a synchronous previous usage

    var intairsuffix='', zhra=null, zhrb=null, kklat=0, kklong=0, doair=false, vsll=[-999.0], answered=true;

    function stateChangedb() {
    if (zhrb.readyState == 4) {
    if (zhrb.status == 200) {
    var rectis=document.body.getBoundingClientRect();
    var topllong=-180.0;
    var topllat=90.0;
    var onepixelequals=izoom;
    var bts=zhrb.response.split('.src + ' + String.fromCharCode(39));
    if (eval('' + bts.length) > 1) {
    for (var ijh=1; ijh<bts.length; ijh++) {
    var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
    eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
    if (!inarray(vs,vsll)) {
    vsll.push(vs);
    drawaac(
    eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
    eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
    );
    }
    }
    }
    }
    }
    }


    function stateChangeda() {
    if (zhra.readyState == 4) {
    if (zhra.status == 200) {
    var rectis=document.body.getBoundingClientRect();
    var topllong=-180.0;
    var topllat=90.0;
    var onepixelequals=izoom;
    var bts=zhra.response.split('.src + ' + String.fromCharCode(39));
    if (eval('' + bts.length) > 1) {
    for (var ijh=1; ijh<bts.length; ijh++) {
    var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
    eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
    if (!inarray(vs,vsll)) {
    vsll.push(vs);
    drawaac(
    eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
    eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
    );
    }
    }
    }
    if (intairsuffix.indexOf('&port=air') != -1) {
    zhrb = new XMLHttpRequest();
    zhrb.onreadystatechange=stateChangedb;
    zhrb.open('get', '/HTMLCSS/intair.php?num=6&lat=' + kklat + '&long=' + kklong + '&port=y', true);
    zhrb.send(null);
    }

    answered=true;
    }
    }
    }

    function naira(klat, klong) {
    if (answered && doair) {
    answered=false;
    zhra = new XMLHttpRequest();
    zhra.onreadystatechange=stateChangeda;
    console.log('/HTMLCSS/intair.php?num=6&lat=' + klat + '&long=' + klong + intairsuffix);
    if (intairsuffix.indexOf('&port=air') != -1) {
    kklat=klat;
    kklong=klong;
    zhra.open('get', '/HTMLCSS/intair.php?num=6&lat=' + klat + '&long=' + klong, true);
    } else {

    zhra.open('get', '/HTMLCSS/intair.php?num=6&lat=' + klat + '&long=' + klong + intairsuffix, true);
    }
    zhra.send(null);
    }
    }

    … keeps a fastish synchronous call (that we enforce via that answered global variable) but truely invokes an asynchronous arrangement extracting Nearby Ports data to plot, as applicable

in adding

So far, with yesterday’s GeoJson World Countries Google Directions Trip Tutorial‘s GeoJson World Countries points of interest include …

… in our changed countries.php web application you can also try below.


Previous relevant GeoJson World Countries Plotted Airports Tutorial is shown below.

GeoJson World Countries Plotted Airports Tutorial

GeoJson World Countries Plotted Airports Tutorial

Introducing the Map Chart recognition of nearby Airports with yesterday’s GeoJson World Countries Nearest Airports Tutorial‘s progress on our latest GeoJson World Countries PHP web application, it set us to seeing …

  • the combination of Google Directions‘s talents allowing you to reposition on the fly … and …
  • the onmousemove event, at least for our non-mobile users

… could mean that if we pre-plot airports on the world map, given that the user has clicked the ✈ ( &#9992; ) “Show an Interest in Airports” emoji button, as a non-mobile user hovers over the world map, this pre-plotting might help trip planners with their travel options, should air travel be part of their interest, in the same way it is an option up at Google Directions in our changed countries.php web application you can also try below. The overlay scenario now reads

  • document.body (now with the new onmousemove=airportplot(event); event logic) lowest level …
  • overlayed by HTML canvas element plotted with world country linework … now including …
  • overlayed by HTML canvas element dedicated to nearest airport plotting …

    <canvas id=myacanvas height='180' width='360' style='background-color:transparent;z-index:55;display:inline-block;position:absolute;top:0px;left:0px;'></canvas>
  • overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
  • HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay

… worked by new (sometimes Ajax) Javascript code …


var zhra=null, elema=null, contexta=null, answered=true, vsll=[-999.0], doair=false; // clicking ✈ sets doair=true;

function drawaac(centerX, centerY) {
contexta.beginPath();
contexta.globalAlpha = 0.9;
if (1 == 1) {
contexta.fillStyle = 'black';
contexta.fillRect(centerX / izoom, centerY / izoom,1,1);
contexta.fill();
} else {
contexta.arc(centerX / izoom, centerY / izoom, 1, 0, 2 * Math.PI, false);
contexta.fillStyle = 'black';
contexta.fill();
contexta.lineWidth = 5;
contexta.lineWidth = 0.00001;
contexta.strokeStyle = '#003300';
contexta.stroke();
}
return true;
}

function inarray(needle, haystack) { // thanks to https://stackoverflow.com/questions/784012/javascript-equivalent-of-phps-in-array
var length = haystack.length;
for (var i = 0; i < length; i++) {
if (haystack[i] == needle) return true;
}
return false;
}

function stateChangeda() {
if (zhra.readyState == 4) {
if (zhra.status == 200) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
var onepixelequals=izoom;
var bts=zhra.response.split('.src + ' + String.fromCharCode(39));
if (eval('' + bts.length) > 1) {
for (var ijh=1; ijh<bts.length; ijh++) {
var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
if (!inarray(vs,vsll)) {
vsll.push(vs);
drawaac(
eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
);
}
}
}
answered=true;
}
}
}

function naira(klat, klong) {
if (answered && doair) {
answered=false;
zhra = new XMLHttpRequest();
zhra.onreadystatechange=stateChangeda;
zhra.open('get', '/HTMLCSS/intair.php?num=6&lat=' + klat + '&long=' + klong, true);
zhra.send(null);
}
}

function airportplot(e) {
if (answered) {
var rectis=null; //document.body.getBoundingClientRect();
var blat=0, blong=0;
var topllong=-180.0;
var topllat=90.0;
onepixelequals=eval(0.0 + eval(1.0 * izoom));
e = e || window.event;
e.preventDefault();
if (e.touches) {
if (e.touches[0].pageX) {
naira(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY)); //if (drawac(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY))) e = e; }
} else {
rectis=document.body.getBoundingClientRect();
naira(eval(-rectis.left + e.touches[0].clientX), eval(-rectis.top + e.touches[0].clientY)); //if (drawac(eval(-rectis.left + e.touches[0].clientX), eval(-rectis.top + e.touches[0].clientY))) { e = e; }
}
} else if (e.pageX || e.pageY) {
blat=eval(eval(eval(topllat * onepixelequals - eval(-rectistop + e.pageY) * 1)) / onepixelequals);
blong=eval(eval(eval(topllong * onepixelequals + eval(-rectisleft + e.pageX) * 1)) / onepixelequals);
if ((blat >= -90.0 && blat <= 90.0) && (blong >= -180.0 && blong <= 180.0)) {
naira(blat, blong); //if (drawac(eval(-rectisleft + e.pageX), eval(-rectistop + e.pageY))) { e = e; }
}
} else {
rectis=document.body.getBoundingClientRect();
naira(eval(-rectis.left + e.clientX), eval(-rectis.top + e.clientY)); //if (drawac(eval(-rectis.left + e.clientX), eval(-rectis.top + e.clientY))) { e = e; }
}
}
}

// ... and extended document.body onload event logic has added, up near its top ...
elema = document.getElementById('myacanvas');
contexta = elema.getContext('2d');


Previous relevant GeoJson World Countries Nearest Airports Tutorial is shown below.

GeoJson World Countries Nearest Airports Tutorial

GeoJson World Countries Nearest Airports Tutorial

So far, with yesterday’s GeoJson World Countries Google Directions Trip Tutorial‘s GeoJson World Countries points of interest include …

… being as we’re into trip planning now!


var airportstuff='', doair=false, zhr=null;

function stateChanged() {
if (zhr.readyState == 4) {
if (zhr.status == 200) {
var bts=zhr.response.split('.src + ' + String.fromCharCode(39));
if (eval('' + bts.length) > 1) {
for (var ijh=1; ijh function windowopen(p1, p2, p3) {
if (p1.length > 800) {
document.getElementById('iddata').value=(p1.split('&data=')[1].split('&')[0].split('#')[0]).replace(/\&20\;/g,' ');
document.getElementById('smapif').click();
return null;
}
return window.open(p1, p2, p3);
}

function apm(mu) {
if (airportstuff != '' && doair) {
return (mu + airportstuff).replace('&', encodeURIComponent(' and Airports') + '&');
}
return mu;
}

function nair(klat, klong) {
airportstuff='';
zhr = new XMLHttpRequest();
zhr.onreadystatechange=stateChanged;
zhr.open('get', '/HTMLCSS/intair.php?num=4&lat=' + klat + '&long=' + klong, true);
zhr.send(null);
}


function alats(inlat) {
if (inlat == 0 && lastlats.length == 0) { inlat=inlat; } else { lastlats.push(inlat); }
return inlat;
}

function alongs(inlong) {
if (inlong == 0 && lastlongs.length == 0) { inlong=inlong; } else { lastlongs.push(inlong); }
setTimeout(prenair, 200); //nair(thislat, inlong);
return inlong;
}

function prenair() {
nair(thislat, thislong);
}

… featuring Ajax Javascript code.

Try this out with our changed countries.php web application you can also try below.


Previous relevant GeoJson World Countries Google Directions Trip Tutorial is shown below.

GeoJson World Countries Google Directions Trip Tutorial

GeoJson World Countries Google Directions Trip Tutorial

Adding to the “zoom” progress of yesterday’s GeoJson World Countries Zoom Tutorial, today, we wanted to …

  • add interfacing functionality to the excellent Google Directions part of Google Maps, perhaps to help with Trip planning, or even just to associate a Placename with a latitude and longitude as clicked by the user, via the very simple URL arrangement …
    https://www.google.com/maps/dir/[decimalLatitudeDegrees]/[decimalLongitudeDegrees]
    … helped out by new Javascript functions …

    var lastlats=[], lastlongs=[], lastlat=-99.0, lastlong=-99.0, thislat=0.0, thislong=0.0;

    function preface(inblurb) {
    var extras='';
    var outblurb=inblurb;
    if (Math.abs(eval('' + lastlat)) > 0.0 || Math.abs(eval('' + lastlong)) > 0.0) {
    if (Math.abs(eval('' + lastlat)) <= 90.0 && Math.abs(eval('' + lastlong)) <= 180.0) { extras=' Add G for Google Directions between (' + lastlat + ',' + lastlong + ') to (' + thislat + ',' + thislong + ') and spaces (also more trip legs) to hashtag navigate to Google Charts later. '; } } return extras + outblurb; }
    function alats(inlat) {
    if (inlat == 0 && lastlats.length == 0) { inlat=inlat; } else { lastlats.push(inlat); }
    return inlat;
    }

    function alongs(inlong) {
    if (inlong == 0 && lastlongs.length == 0) { inlong=inlong; } else { lastlongs.push(inlong); }
    return inlong;
    }

    … and …
  • add some hashtag navigation (fairly self explanatory emoji button ) ideas now that our GeoJson world map can be zoomed in on

… in our changed countries.php web application you can also try below.


Previous relevant GeoJson World Countries Zoom Tutorial is shown below.

GeoJson World Countries Zoom Tutorial

GeoJson World Countries Zoom Tutorial

Onto yesterday’s GeoJson World Countries TimeZone Times Tutorial GeoJson World Countries web application’s capabilities we want to add zooming, that doesn’t rely on web browser functionality (which continues to work). With that in mind we create a new emoji ( &#128270; ) 🔎 link, with this onclick event code …


var jzoom=1.0, izoom=location.search.split('zoom=')[1] ? eval(decodeURIComponent(location.search.split('zoom=')[1].split('&')[0])) : 1.0;

function zoomin() {
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
jzoom*=2;
document.getElementById('myviewport').setAttribute('content', 'width=device-width, initial-scale=' + eval(jzoom * izoom) + ', minimum-scale=0.1, maximum-scale=8, user-scalable=yes');
window.scrollTo(0,0);
} else {
document.body.style.zoom=('' + eval(izoom * 200.0) + '%');
izoom*=2;
}
}

… to multiply the webpage zoom factor in a programmatical way. To acheive this, we have a two way approach (as you might have surmised from above) …

  • for mobile, the logic is easier by introducing a new meta name=viewport …

    <meta id="myviewport" name="viewport" content="width=device-width, initial-scale=1, minimum-scale=0.1, maximum-scale=8, user-scalable=yes" >

    … tag … while …
  • for non-mobile we needed to realize that event.pageX and event.pageY co-ordinates grow in proportion to the zoom factor, and that better latitude and longitude determining lines of code would go

    function canvasclick(e) {
    var rectis=document.body.getBoundingClientRect();
    var topllong=-180.0;
    var topllat=90.0;
    onepixelequals=eval(0.0 + eval(1.0 * izoom));
    //document.title='canvasclick';
    e = e || window.event;
    e.preventDefault();
    if (e.touches) {
    if (e.touches[0].pageX) {
    //lastl='Longitude,Latitude coordinates are ' + eval(topllong + eval(-rectis.left + e.touches[0].pageX) * onepixelequals) + ',' + eval(topllat - eval(-rectis.top + e.touches[0].pageY) * onepixelequals);
    if (drawc(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY))) {
    thislat=eval(topllat - eval(-rectistop + e.touches[0].pageY) * onepixelequals);
    thislong=eval(topllong + eval(-rectisleft + e.touches[0].pageX) * onepixelequals);
    //console.log('rectistop=' + rectistop + ' and rectisleft=' + rectisleft + ' and rectisy=' + rectisy + ' and thislat=' + thislat);
    document.getElementById('nearestif').src='/PHP/tz_places.php?place=&latitude=' + encodeURIComponent('' + eval(topllat - eval(-rectistop + e.touches[0].pageY) * onepixelequals)) + '&longitude=' + encodeURIComponent('' + eval(topllong + eval(-rectisleft + e.touches[0].pageX) * onepixelequals)) + '&ntztontz=y';
    }
    } else {

    //lastl='Longitude,Latitude coordinates are ' + eval(topllong + eval(-rectis.left + e.touches[0].clientX) * onepixelequals) + ',' + eval(topllat - eval(-rectis.top + e.touches[0].clientY) * onepixelequals);
    if (drawc(eval(-rectis.left + e.touches[0].clientX), eval(-rectis.top + e.touches[0].clientY))) {
    thislat=eval(topllat - eval(-rectis.top + e.touches[0].clientY) * onepixelequals);
    thislong=eval(topllong + eval(-rectis.left + e.touches[0].clientX) * onepixelequals);
    //console.log('rectis.top=' + rectis.top + ' and rectis.left=' + rectis.left + ' and rectis.y=' + rectis.y + ' anD thislat=' + thislat);
    document.getElementById('nearestif').src='/PHP/tz_places.php?place=&latitude=' + encodeURIComponent('' + eval(topllat - eval(-rectis.top + e.touches[0].clientY) * onepixelequals)) + '&longitude=' + encodeURIComponent('' + eval(topllong + eval(-rectis.left + e.touches[0].clientX) * onepixelequals)) + '&ntztontz=y';
    }
    }
    } else if (e.pageX || e.pageY) {
    //lastl='Longitude,Latitude coordinates are ' + eval(topllong + e.clientX * onepixelequals) + ',' + eval(topllat - e.clientY * onepixelequals);
    //alert('02: ' + e.screenX + ' ' + e.pageX + ' ' + e.screenY + ' ' + e.pageY + ' ' + thislong + ' ' + thislat);
    if (drawc(eval(-rectisleft + e.pageX), eval(-rectistop + e.pageY))) {
    //thislat=eval(topllat - eval(-rectistop + e.pageY) * onepixelequals);
    //thislong=eval(topllong + eval(-rectisleft + e.pageX) * onepixelequals);
    thislat=eval(eval(eval(topllat * onepixelequals - eval(-rectistop + e.pageY) * 1)) / onepixelequals);
    thislong=eval(eval(eval(topllong * onepixelequals + eval(-rectisleft + e.pageX) * 1)) / onepixelequals);

    //alert('22: ' + e.screenX + ' ' + e.pageX + ' ' + e.screenY + ' ' + e.pageY + ' ' + thislong + ' ' + thislat);
    //console.log('rectistop=' + rectistop + ' and rectisleft=' + rectisleft + ' and rectisy=' + rectisy + ' aNd thislat=' + thislat + ' and e.pageY=' + e.pageY + ' and new plus idea=' + eval(topllat - eval(rectistop + e.pageY) * onepixelequals));
    //document.getElementById('nearestif').src='/PHP/tz_places.php?place=&latitude=' + encodeURIComponent('' + eval(topllat - eval(-rectistop + e.pageY) * onepixelequals)) + '&longitude=' + encodeURIComponent('' + eval(topllong + eval(-rectisleft + e.pageX) * onepixelequals)) + '&ntztontz=y';
    document.getElementById('nearestif').src='/PHP/tz_places.php?place=&latitude=' + encodeURIComponent('' + thislat) + '&longitude=' + encodeURIComponent('' + thislong) + '&ntztontz=y';
    }
    } else {
    //lastl='Longitude,Latitude coordinates are ' + eval(topllong + eval(-rectis.left + e.pageX) * onepixelequals) + ',' + eval(topllat - eval(-rectis.top + e.pageY) * onepixelequals);
    if (drawc(eval(-rectis.left + e.clientX), eval(-rectis.top + e.clientY))) {
    thislat=eval(topllat - eval(-rectis.top + e.clientY) * onepixelequals);
    thislong=eval(topllong + eval(-rectis.left + e.clientX) * onepixelequals);
    //console.log('rectis.top=' + rectis.top + ' and rectis.left=' + rectis.left + ' and rectis.y=' + rectis.y + ' And thislat=' + thislat);
    document.getElementById('nearestif').src='/PHP/tz_places.php?place=&latitude=' + encodeURIComponent('' + eval(topllat - eval(-rectis.top + e.clientY) * onepixelequals)) + '&longitude=' + encodeURIComponent('' + eval(topllong + eval(-rectis.left + e.clientX) * onepixelequals)) + '&ntztontz=y';
    }
    }
    }

… in our changed countries.php web application you can also try below.


Previous relevant GeoJson World Countries TimeZone Times Tutorial is shown below.

GeoJson World Countries TimeZone Times Tutorial

GeoJson World Countries TimeZone Times Tutorial

Some readers might be aware of our “theory regarding adverbs” and “web applications” on the net …

  • the most catered for adverb relates to the “where of life” … and the second banana is …
  • the “when of life”

… and, further to yesterday’s GeoJson World Countries SVG Overlay Safari Error Tutorial‘s emphasis on the “where of life”, today we add in a bit of the “when of life”, something right down the line of the remit of TimeZone talents.

Seriously though, a lot of us dream of the rest of the world on a world map, and wonder what time it is in other parts of the world. Phone call to relatives? A reminder SMS call? Email a game collaboration? It could all be part of life’s rich tapestry!

The expresion of this, for us, today, improving the communications with our current GeoJsom World Countries web application, take the form of emoji clocks from the 12 hour clock example forms such as …

  • 1 o’clock is &#128336; 🕐
  • 2 o’clock is &#128337; 🕑
  • 12 o’clock is &#128347; 🕛
  • 2:30 is &#128349; 🕝
  • 11:30 is &#128358; 🕦
  • 12:30 is &#128359; 🕧

… to show in “prompt” and “confirm” popup windows, as well as Map Chart maps … via new Javascript functions …


function clockit(hoursoffgmt) {
// 1 o'clock is 🕐
// 2 o'clock is 🕑
// 12 o'clock is 🕛
// 2:30 is 🕝
// 11:30 is 🕦
// 12:30 is 🕧
var along='Saturday,Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday';
var gmtdatetimeis='' + new Date().toUTCString();
var daybit='';
var ampm=' am';
if (gmtdatetimeis.indexOf('day') != -1) {
ampm+=', ' + gmtdatetimeis.split('day')[0].split(' ')[eval(-1 + gmtdatetimeis.split('day')[0].split(' ').length)] + 'day';
} else if (gmtdatetimeis.indexOf(',') != -1) {
ampm+=', ' + (gmtdatetimeis.split(',')[0].split(' ')[eval(-1 + gmtdatetimeis.split(',')[0].split(' ').length)] + '').replace(/^Sat$/g,'Saturday').replace(/^Sun$/g,'Sunday').replace(/^Mon$/g,'Monday').replace(/^Tue$/g,'Tuesday').replace(/^Wed$/g,'Wednesday').replace(/^Thu$/g,'Thursday').replace(/^Fri$/g,'Friday');
}
if (ampm.length > 3) { daybit=ampm.substring(5); }
var minis=eval('' + gmtdatetimeis.split(':')[1]);
var hris=eval(('' + gmtdatetimeis.split(':')[0]).split(' ')[eval(-1 + ('' + gmtdatetimeis.split(':')[0]).split(' ').length)]);
if (('' + hoursoffgmt).indexOf('-') != -1 && eval(eval('' + hoursoffgmt.replace('+','')) + hris) < 0.0) {
ampm=ampm.replace('am', 'pm');
if (daybit != '') { ampm=ampm.replace(daybit, along.split(',' + daybit)[0].split(',')[eval(-1 + along.split(',' + daybit)[0].split(',').length)]); }
} else if (('' + hoursoffgmt).indexOf('-') == -1 && eval(eval('' + hoursoffgmt.replace('+','')) + hris) >= 24) {
if (daybit != '') { ampm=ampm.replace(daybit, along.split(daybit + ',')[1].split(',')[0]); }
} else if (('' + hoursoffgmt).indexOf('-') == -1 && eval(eval('' + hoursoffgmt.replace('+','')) + hris) >= 12) {
ampm=ampm.replace('am', 'pm');
}
hris+=eval(eval(24 + eval('' + hoursoffgmt.replace('+','').split('.')[0])) % 24);
if (('' + hoursoffgmt.replace('+','')).indexOf('.5') != -1) {
if (('' + hoursoffgmt.replace('+','')).indexOf('-') != -1 && minis >= 30) {
minis-=30;
} else if (('' + hoursoffgmt.replace('+','')).indexOf('-') != -1) {
hris--;
if (hris < 24) { hris=23; ampm=ampm.replace('am', 'pm'); if (daybit != '') { ampm=ampm.replace(daybit, along.split(',' + daybit)[0].split(',')[eval(-1 + along.split(',' + daybit)[0].split(',').length)]); } }
minis+=30;
} else if (('' + hoursoffgmt.replace('+','')).indexOf('-') == -1 && minis < 30) {
minis+=30;
} else if (('' + hoursoffgmt.replace('+','')).indexOf('-') != -1) {
hris++;
if (hris > 24) { hris=24; ampm=ampm.replace('pm', 'am'); if (daybit != '') { ampm=ampm.replace(daybit, along.split(daybit + ',')[1].split(',')[0]); } }
minis-=30;
}
} else if (('' + hoursoffgmt.replace('+','')).indexOf('.25') != -1) {
if (('' + hoursoffgmt.replace('+','')).indexOf('-') != -1 && minis >= 15) {
minis-=15;
} else if (('' + hoursoffgmt.replace('+','')).indexOf('-') != -1) {
hris--;
if (hris < 24) { hris=23; ampm=ampm.replace('am', 'pm'); if (daybit != '') { ampm=ampm.replace(daybit, along.split(',' + daybit)[0].split(',')[eval(-1 + along.split(',' + daybit)[0].split(',').length)]); } }
minis+=15;
} else if (('' + hoursoffgmt.replace('+','')).indexOf('-') == -1 && minis < 45) {
minis+=15;
} else if (('' + hoursoffgmt.replace('+','')).indexOf('-') != -1) {
hris++;
if (hris > 24) { hris=24; ampm=ampm.replace('pm', 'am'); if (daybit != '') { ampm=ampm.replace(daybit, along.split(daybit + ',')[1].split(',')[0]); } }
minis-=15;
}
} else if (('' + hoursoffgmt.replace('+','')).indexOf('.75') != -1) {
if (('' + hoursoffgmt.replace('+','')).indexOf('-') != -1 && minis >= 45) {
minis-=45;
} else if (('' + hoursoffgmt.replace('+','')).indexOf('-') != -1) {
hris--;
if (hris < 24) { hris=23; ampm=ampm.replace('am', 'pm'); if (daybit != '') { ampm=ampm.replace(daybit, along.split(',' + daybit)[0].split(',')[eval(-1 + along.split(',' + daybit)[0].split(',').length)]); } }
minis+=45;
} else if (('' + hoursoffgmt.replace('+','')).indexOf('-') == -1 && minis < 15) {
minis+=45;
} else if (('' + hoursoffgmt.replace('+','')).indexOf('-') != -1) {
hris++;
if (hris > 24) { hris=24; ampm=ampm.replace('pm', 'am'); if (daybit != '') { ampm=ampm.replace(daybit, along.split(daybit + ',')[1].split(',')[0]); } }
minis-=45;
}
}
//alert('' + hoursoffgmt + ' ' + hris + ' ' + minis + ' ' + gmtdatetimeis);


if (minis > 45 && eval(hris % 12) == 0) {
return ' ' + String.fromCodePoint(128336) + ampm;
} else if (eval(hris % 12) == 0 && minis <= 15) {
return ' ' + String.fromCodePoint(128347) + ampm;
} else if (eval(hris % 12) == 0) {
return ' ' + String.fromCodePoint(128359) + ampm;
} else if (minis >= 45) {
return ' ' + String.fromCodePoint(eval(eval(hris % 12) + 128336)) + ampm;
} else if (minis <= 15) {
return ' ' + String.fromCodePoint(eval(eval(hris % 12) + 128335)) + ampm;
} else {
return ' ' + String.fromCodePoint(eval(eval(hris % 12) + 128347)) + ampm;
}
return '';
}

function emfilter(inih) {
var bcxs=inih.split(' &#');
var bitis='', ib=0;
var outih=inih;
for (ib=1; ib<bcxs.length; ib++) {
bitis=' &#' + bcxs[ib].split(' ')[0];
outih=outih.replace(bitis, ' ' + eval('String.fromCodePoint(' + bitis.substring(3).replace(/\;\&\#/g,',').replace(/\;/g,'') + ')'));
}
bcxs=outih.split('(');
for (ib=1; ib<bcxs.length; ib++) {
if (bcxs[ib].split(')')[0].indexOf(',') == -1 && bcxs[ib].split(')')[0].indexOf(' ') == -1 && (bcxs[ib].substring(0,1) == '-' || bcxs[ib].substring(0,1) == '+' || (bcxs[ib].substring(0,1) >= '0' && bcxs[ib].substring(0,1) <= '9'))) {
outih=outih.replace('(' + bcxs[ib].split(')')[0] + ')', '(' + bcxs[ib].split(')')[0] + clockit(bcxs[ib].split(')')[0]) + ')');
}
}
return outih;
}

Also, in these same places we add in Time Place country ISO-2 character code based emoji flags, adding to information and colour pizazz in the informational parts to the workings of our changed countries.php web application you can also try below.


Previous relevant GeoJson World Countries SVG Overlay Safari Error Tutorial is shown below.

GeoJson World Countries SVG Overlay Safari Error Tutorial

GeoJson World Countries SVG Overlay Safari Error Tutorial

Mostly on smaller devices (such as an iPhone), but elsewhere as well, but less drastically, this morning, using our current Display GeoJson Countries web application we would get the Safari web browser error …

A problem repeatedly occurred with https://www.rjmprogramming.com.au/HTMLCSS/countries.php

… with web application work up to yesterday’s GeoJson World Countries SVG Overlay Colour Infill Tutorial.

On discovering our first solution theory of turning yesterday’s mapsvg.js external Javascript work into an async piece of work made no difference to this situation, we surmised that the huge amount of content held in the Javascript (ie. client side) global variable appendtoinnerHTML was causing memory issues. We couldn’t shift much to do with the overall amount of “data” needing to be handled, in order to implement country SVG colour infilling, but we could shift the data from being …

  • client side (external) Javascript held … to, instead, (have that data) be (determined on the) …
  • server side PHP filling in the contents of our (relevant) HTML div id=svgd ahead of the document.body onload event timing …
    <?php

    $icnt=0;

    function apptohtmstuff($coordsare, $origc) {
    global $icnt;
    $minl=-1;
    $mint=-1;
    $maxl=-1;
    $maxt=-1;
    $zysare=explode(',', $coordsare);
    $svgcis='';
    for ($ij=0; $ij<sizeof($zysare); $ij+=2) {
    if ($minl < 0) {
    $minl=$zysare[$ij];
    $maxl=$zysare[$ij];
    $mint=$zysare[1 + $ij];
    $maxt=$zysare[1 + $ij];
    } else {
    if ($zysare[$ij] < $minl) { $minl=$zysare[$ij]; }
    if ($zysare[$ij] > $maxl) { $maxl=$zysare[$ij]; }
    if ($zysare[1 + $ij] < $mint) { $mint=$zysare[1 + $ij]; }
    if ($zysare[1 + $ij] > $maxt) { $maxt=$zysare[1 + $ij]; }
    }
    }

    for ($ij=0; $ij<sizeof($zysare); $ij+=2) {
    if ($svgcis == '') {
    $svgcis='' . ($zysare[$ij] - $minl) . ',' . ($zysare[1 + $ij] - $mint);
    } else {
    $svgcis.=' ' . ($zysare[$ij] - $minl) . ',' . ($zysare[1 + $ij] - $mint);
    }
    }

    $icnt++;
    return '<div title="' . $coordsare . '" id="dsa' . (-1 + $icnt) . '" style="position:absolute;left:' . $minl . 'px;top:' . $mint . 'px;width:' . ($maxl - $minl) . 'px;height:' . ($maxt - $mint) . ';display:BLOCK;z-index:24;"><svg height="100%" width="100%"><polygon points="' . $svgcis . '" style="fill:lime;stroke:purple;stroke-width:1"></polygon></svg></div>';
    }

    ?>
    … meaning the web application’s underlying HTML size increased at the expense of no need for external Javascript involvement any more

… in our changed countries.php web application you can also try below.

We’re not exactly sure why, but …

  • this improved web application stability …

    …everywhere … and an idea we’d ditched yesterday of …
  • idea to pre-colour “land” parts of the world GeoJson map green (ahead of the document.body onload event) also came good (after causing problems yesterday)

… meaning now, “overlay” wise, we could say

  • document.body lowest level …
  • overlayed by HTML canvas element plotted with world country linework …
  • overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
  • HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay

… and we (reckon we’ve) improved the colour coding user experience at the same time.

We noticed that tweaks in the changed …


var lastflagged='', appendtoinnerHTML='', waitplease=false; // used to make sure "area" element onclick code precedes any document.body onmousedown or ontouchdown code

function checkdsa(ath) {
waitplease=true;
var wasih='';
var athcoords=('' + ath.coords); //.substring(0, Math.floor(eval(0.1 * ('' + ath.coords).length)));
if (document.getElementById('svgd').innerHTML.indexOf(('' + athcoords)) != -1) {
wasih='<div title=' + String.fromCharCode(34) + '' + ath.coords + document.getElementById('svgd').innerHTML.split(ath.coords)[1].split('<div ')[0];
if (lastflagged == '') {
if (wasih.indexOf(' id=' + String.fromCharCode(34)) != -1) { lastflagged=wasih.split(' id=' + String.fromCharCode(34))[1].split(String.fromCharCode(34))[0]; } else { lastflagged=''; }
} else {
document.getElementById(lastflagged).innerHTML=document.getElementById(lastflagged).innerHTML.replace(/rgba\(255\,87\,51\,0.5\)/g, 'rgba(200,200,200,0.3)');
if (wasih.indexOf(' id=' + String.fromCharCode(34)) != -1) { lastflagged=wasih.split(' id=' + String.fromCharCode(34))[1].split(String.fromCharCode(34))[0]; } else { lastflagged=''; }
}
if (lastflagged != '') {
document.getElementById(lastflagged).innerHTML=document.getElementById(lastflagged).innerHTML.replace('z-index:24;', 'z-index:123;').replace('lime;', 'rgba(255,87,51,0.5);');
document.getElementById(lastflagged).title=ath.title;
} else {
document.getElementById('svgd').innerHTML=document.getElementById('svgd').innerHTML.replace(wasih, '<div title=' + String.fromCharCode(34) + '' + ath.title + wasih.split(ath.coords)[1].split('<div ')[0].replace('none;', 'block;z-index:123;').replace('lime;', 'rgba(255,87,51,0.5);'));
}
} else if (('' + appendtoinnerHTML).indexOf(('' + athcoords)) != -1) {
if (document.getElementById('svgd').innerHTML.indexOf(' title=' + String.fromCharCode(34) + '' + ath.title + '' + String.fromCharCode(34) + '') != -1) {
document.getElementById('svgd').innerHTML=document.getElementById('svgd').innerHTML.replace(/rgba\(255\,87\,51\,0.5\)/g, 'rgba(200,200,200,0.3)');
document.getElementById('svgd').innerHTML+='<div title=' + String.fromCharCode(34) + '' + ath.title + appendtoinnerHTML.split(ath.coords)[1].split('<div ')[0].replace('none;', 'block;z-index:123;').replace(/\ id\=/g, ' data-id=').replace('lime;', 'rgba(255,87,51,0.5);');
} else {
document.getElementById('svgd').innerHTML=document.getElementById('svgd').innerHTML.replace(/rgba\(255\,87\,51\,0.5\)/g, 'rgba(200,200,200,0.3)');
document.getElementById('svgd').innerHTML+='<div title=' + String.fromCharCode(34) + '' + ath.title + appendtoinnerHTML.split(ath.coords)[1].split('<div ')[0].replace('none;', 'block;z-index:123;').replace('lime;', 'rgba(255,87,51,0.5);');
}
}
waitplease=false;
return '';
}

… also lessened the burden on the client side by only asking any Javascript DOM command operations act on single HTML element at a time, not a whole swathe of hosted ones, in any operation.


Previous relevant GeoJson World Countries Geo Chart Tutorial is shown below.

GeoJson World Countries Geo Chart Tutorial

GeoJson World Countries Geo Chart Tutorial

There are many advantages regarding today’s task to …

Integrate Google Chart Geo Chart

… as an additional “drill down” map option, functionality adding onto the work of yesterday’s GeoJson World Countries Tutorial.

What deducible data item needs to be determined for these Geo Charts to work? We need a way to deduce ISO-2 character country codes from the ISO-3 character codes existing in the GeoJson “countries.geojson” data from yesterday’s work. We happened upon the extremely generous mapping data webpage to help with these ISO-2 character deductions

<?php

$geojsonis=file_get_contents('countries.geojson');
$parts=explode("]]", $geojsonis);
$globalattr="";
$mappings=file_get_contents('http://gist.github.com/tadast/8827699'); // thanks
if (strpos($mappings, '>IN</td>') !== false) {
$mappings.='<td>IN</td><td>KAS</td><td>EH</td><td>SAH</td><td>SO</td><td>SOL</td><td>SS</td><td>SDS</td><td>XK</td><td>KOS</td><td>CY</td><td>CYN</td></body>';
}


for ($i=0; $i<(-1 + sizeof($parts)); $i++) {
if (strpos($parts[$i], '"sr_sov_a3":"') !== false) {
$globalattr=' data-iso3="' . explode('"', explode('"sr_sov_a3":"', $parts[$i])[1])[0] . '"';
if (strpos($mappings, '>' . explode('"', explode('"sr_sov_a3":"', $parts[$i])[1])[0] . '</td>') !== false) {
$iparts=explode('>' . explode('"', explode('"sr_sov_a3":"', $parts[$i])[1])[0] . '</td>', $mappings);
$globalattr.=' data-iso2="' . explode('>', explode('</td>', $iparts[0])[-2 + sizeof(explode('</td>', $iparts[0]))])[-1 + sizeof(explode('>', explode('</td>', $iparts[0])[-2 + sizeof(explode('</td>', $iparts[0]))]))] . '"';
} else if (substr(explode('"', explode('"sr_sov_a3":"', $parts[$i])[1])[0],-1,1) >= '0' && substr(explode('"', explode('"sr_sov_a3":"', $parts[$i])[1])[0],-1,1) <= '9') {
$globalattr.=' data-iso2="' . substr(explode('"', explode('"sr_sov_a3":"', $parts[$i])[1])[0],0,2) . '"';
}
}
if (strpos($parts[$i], '"sr_subunit":"') !== false) {
$globalattr.=' data-cname="' . explode('"', explode('"sr_subunit":"', $parts[$i])[1])[0] . '"';
}
$coords=str_replace('[','',str_replace(']','',explode('[[' , $parts[$i])[-1 + sizeof(explode('[[' , $parts[$i]))]));
$newcoords=$coords;
if (!$dolatlong || 1 == 1) {
$lls=explode(",", $coords);
$newcoords="";
for ($j=0; $j<sizeof($lls); $j+=2) {
$lls[$j]=($lls[$j] + $longoff) * $factor;
$lls[$j + 1]=($lls[$j + 1] + $latoff) * $factor;
if ($newcoords != "") { $newcoords.=","; $htmlis=str_replace("}", " context.lineTo(" . $lls[$j] . ',' . ((180.0 * $factor) - $lls[$j + 1]) . "); \n}", $htmlis); } else { $htmlis=str_replace("}", " context.stroke(); \n context.beginPath(); \n context.moveTo(" . $lls[$j] . ',' . ((180.0 * $factor) - $lls[$j + 1]) . "); \n}", $htmlis); }
$newcoords.='' . $lls[$j] . ',' . ((180.0 * $factor) - $lls[$j + 1]);
}
}
$abit.="<area" . $globalattr . " onclick=\"ouralert(this.getAttribute('data-cname'),this.getAttribute('data-iso3'),this.getAttribute('data-iso2'),'Longitude,Latitude coordinates are " . $coords . "');\" shape='poly' coords='" . $newcoords . "'></area>\n";
//echo "<area type='poly' coords='" . $newcoords . "'></area>\n";
}

?>

… in our image map area elements PHP creation code above. As you can see, extra “intelligence”, moving forward, is contained in area element global data attributes.

Which leaves us with why any of this helps functionality within our changed countries.php web application you can also try below?

  • Geo Chart can zoom into a country view …
  • Geo Chart can involve emoji (🏠 &127968;) or image (SVG) circle based symbology for the “User Clicked Place” and nearby TimeZone places respectively …
  • all these symbols can be clicked to open popup windows containing TimeZone Place Wikipedia webpages of relevance …
  • an emoji national flag (eg. Zambia “ZA” could be used to derive &#127487;&#127462; 🇿🇦 flag emoji) derived from those ISO-2 character codes can supplement the GeoJson (more ISO-3 character based) names presented in the underlying data, in the Geo Chart title …
  • contextualizing the accompanying Map Chart … and …
  • vice versa regarding hovering over symbology (which works on Map Chart, but not Geo Chart) …
  • within the Map Chart iframe a “Geo” link can glean a “zoomed out” world Geo Chart view of your TimeZone places

And feel free to (re-)try our tweaked “to make thinner the coastline linework” regions.php GeoJson PHP web application in a new window, or you can also try below.


Previous relevant GeoJson World Countries Tutorial is shown below.

GeoJson World Countries Tutorial

GeoJson World Countries Tutorial

Know your GeoJson! Yes, pretty obviously, any two GeoJson datasets might display the same in that “map plotting” sense, but one might have different and/or more “intelligence” than the other. Often, an XML has more “intelligence” than equivalent HTML (barring the use of global data attributes, that is), as today’s Corollacorollary.

And so, moving on from yesterday’s GeoJson World Coastline TimeZone Tutorial, today, we present a new GeoJson admin 0 countries we download, and upload as “countries.geojson” data set, again via the excellent HTTP://geojson.xyz/ repository, thanks.

From it, we do get a different JSON additional “intelligence”


{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"scalerank":1,"labelrank":1,"sr_sov_a3":"ZWE","sr_adm0_a3":"ZWE","sr_gu_a3":"ZWE","sr_su_a3":"ZWE","sr_subunit":"Zimbabwe","featureclass":"Admin-0 scale ranks"},"geometry":{"type":"Polygon","coordinates":[[[31.287890625000017,-22.40205078125001],[31.197265625,-22.34492187500001],[31.07343750000001,-22.30781250000001],[30.916113281250006,-22.29072265625001],[30.71162109375001,-22.2978515625],[30.46015625000001,-22.32900390625001],[30.1904296875,-22.291113281250006],[29.90234375,-22.184179687500006],[29.6630859375,-22.146289062500003],[29.37744140625,-22.19277343750001],[29.36484375,-22.193945312500006],[29.315234375000017,-22.15771484375],[29.237207031250023,-22.07949218750001],[29.106835937500023,-22.065722656250003],[29.07148437500001,-22.047460937500006],[29.042382812500023,-22.018359375],[29.023339843750023,-21.98125],[29.01582031250001,-21.93994140625],[29.03730468750001,-21.811328125],[29.02558593750001,-21.796875],[28.99072265625,-21.78144531250001],[28.919335937500023,-21.766015625],[28.74775390625001,-21.707617187500006],[28.532031250000017,-21.65126953125001],[28.181640625,-21.58935546875],[28.04560546875001,-21.573046875],[28.014062500000023,-21.55419921875],[27.974609375,-21.50673828125001],[27.90742187500001,-21.35908203125001],[27.844140625000023,-21.261523437500003],[27.693457031250006,-21.11103515625001],[27.66943359375,-21.064257812500003],[27.67695312500001,-20.94482421875],[27.688085937500006,-20.84833984375001],[27.70429687500001,-20.76640625],[27.69697265625001,-20.689746093750003],[27.69482421875,-20.59453125],[27.699609375000023,-20.53066406250001],[27.679296875,-20.503027343750006],[27.624609375,-20.48359375000001],[27.46894531250001,-20.47480468750001],[27.28076171875,-20.47871093750001],[27.27460937500001,-20.3818359375],[27.256738281250023,-20.23203125],[27.221484375000017,-20.145800781250003],[27.17822265625,-20.10097656250001],[27.091796875,-20.05419921875],[26.91669921875001,-19.99013671875001],[26.67822265625,-19.89277343750001],[26.474609375,-19.748632812500006],[26.241015625000017,-19.5693359375],[26.168066406250006,-19.53828125000001],[26.081933593750023,-19.369921875],[25.95068359375,-19.08173828125001],[25.95917968750001,-18.985644531250003],[25.939355468750023,-18.93867187500001],[25.811914062500023,-18.79707031250001],[25.78369140625,-18.72353515625001],[25.76123046875,-18.64921875],[25.55830078125001,-18.441796875],[25.4892578125,-18.35126953125001],[25.43671875000001,-18.234960937500006],[25.384375,-18.14199218750001],[25.340234375000023,-18.1044921875],[25.28242187500001,-18.04121093750001],[25.242285156250006,-17.969042968750003],[25.224023437500023,-17.91523437500001],[25.239062500000017,-17.843066406250003],[25.2587890625,-17.793554687500006],[25.451757812500006,-17.84511718750001],[25.55712890625,-17.84951171875001],[25.6396484375,-17.82412109375001],[25.741601562500023,-17.858203125],[25.86328125,-17.951953125],[25.995898437500017,-17.969824218750006],[26.139550781250023,-17.91171875],[26.333398437500023,-17.929296875],[26.577539062500023,-18.022558593750006],[26.779882812500006,-18.04150390625],[27.020800781250017,-17.95839843750001],[27.235742187500023,-17.728320312500003],[27.437890625000023,-17.51191406250001],[27.63671875,-17.262109375],[27.75654296875001,-17.060351562500003],[27.932226562500006,-16.89619140625001],[28.16376953125001,-16.76972656250001],[28.399804687500023,-16.66279296875001],[28.760644531250023,-16.53193359375001],[28.760546875000017,-16.53212890625001],[28.83271484375001,-16.424121093750003],[28.856738281250017,-16.30615234375],[28.856738281250017,-16.14228515625001],[28.875585937500006,-16.0361328125],[28.9130859375,-15.98779296875],[28.973046875000023,-15.950097656250009],[29.050585937500017,-15.901171875],[29.287890625000017,-15.776464843750006],[29.4873046875,-15.69677734375],[29.729589843750006,-15.644628906250006],[29.994921875000017,-15.64404296875],[30.25068359375001,-15.643457031250009],[30.39609375,-15.64306640625],[30.39814453125001,-15.80078125],[30.409375,-15.978222656250011],[30.437792968750017,-15.995312500000011],[30.630175781250017,-15.999218750000011],[30.938769531250017,-16.01171875],[31.236230468750023,-16.02363281250001],[31.426171875000023,-16.15234375],[31.48984375,-16.1796875],[31.687597656250006,-16.214160156250003],[31.939843750000023,-16.428808593750006],[32.243261718750006,-16.44873046875],[32.45195312500002,-16.515722656250006],[32.635839843750006,-16.589453125],[32.741796875,-16.67763671875001],[32.81025390625001,-16.69765625],[32.902929687500006,-16.704199218750006],[32.94804687500002,-16.71230468750001],[32.93789062500002,-16.775976562500006],[32.87626953125002,-16.88359375],[32.884375,-17.03779296875001],[32.969335937500006,-17.2515625],[32.98076171875002,-17.4375],[32.9546875,-17.765429687500003],[32.95556640625,-18.08291015625001],[32.96464843750002,-18.1962890625],[32.978515625,-18.271484375],[32.99638671875002,-18.312597656250006],[32.99306640625002,-18.35957031250001],[32.942480468750006,-18.49267578125],[32.90166015625002,-18.632910156250006],[32.90029296875002,-18.6890625],[32.88457031250002,-18.728515625],[32.8544921875,-18.763671875],[32.72197265625002,-18.828417968750003],[32.69921875,-18.868457031250003],[32.69970703125,-18.94091796875],[32.71650390625001,-19.00185546875001],[32.766210937500006,-19.02431640625001],[32.826171875,-19.05878906250001],[32.84980468750001,-19.10439453125001],[32.85,-19.152441406250006],[32.83095703125002,-19.24140625000001],[32.77763671875002,-19.388769531250006],[32.83076171875001,-19.558203125],[32.89042968750002,-19.668066406250006],[32.97265625,-19.79541015625],[33.00673828125002,-19.873828125],[33.0048828125,-19.93017578125],[32.992773437500006,-19.98486328125],[32.86962890625,-20.2171875],[32.780859375,-20.36152343750001],[32.67255859375001,-20.51611328125],[32.529296875,-20.613085937500003],[32.49238281250001,-20.659765625],[32.477636718750006,-20.712988281250006],[32.48281250000002,-20.82890625],[32.476171875,-20.95009765625001],[32.353613281250006,-21.136523437500003],[32.429785156250006,-21.29707031250001],[32.41240234375002,-21.311816406250003],[32.37109375,-21.33486328125001],[32.19472656250002,-21.515429687500003],[32.01630859375001,-21.698046875],[31.88593750000001,-21.83154296875],[31.737695312500023,-21.9833984375],[31.57148437500001,-22.15351562500001],[31.429492187500017,-22.298828125],[31.287890625000017,-22.40205078125001]]]}}

… and “mapping look” because country borders are added in, and some JSON attributes we can call on to allow for …

  • the similar Google Chart Map Chart maps introduced yesterday into the user interaction mix … as well as …
  • Wikipedia, thanks, webpage lookup possibilities calling on those new GeoJson property attributes presented within the dataset above

We enable this, as we just add in some Javascript popup window type equals “prompt” to the existant “confirm” and “alert” logic of yesterday.

Feel free to try the how we got there regions.php GeoJson PHP World Countries web application in a new window, or below …


Previous relevant GeoJson World Coastline TimeZone Tutorial is shown below.

GeoJson World Coastline TimeZone Tutorial

GeoJson World Coastline TimeZone Tutorial

Luckily for programmers all over, the organization of TimeZones has had an International flavour in its development and maintenance. As such, given the “purely coastline” GeoJson data involved in our fledgling PHP web application of yesterday’s GeoJson World Coastline Primer Tutorial a useful arrangement for improvement involves …

  • document.body onclick event co-ordinates … able to be converted to …
  • longitude, latitude (easily, only because of our simplistic map projection, of course) … onfed to …
  • our inhouse “TimeZone Places” web application PHP helper

… can have us helping out your curious web “clicking” user with the 3 nearest TimeZone places, as a reference as to where they are “clicking” in the world.

Feel free to try our improved regions.php GeoJson PHP web application in a new window, or below …

Stop Press

Our efforts in making our new tweaked regions.php GeoJson PHP web application more mobile platform user friendly reminds us about a salutary issue regarding touch gestures …

  • in a discrete click methodology of interest, you could adopt a non-mobile “onmousedown” logic set that does not get interfered with by a mobile “ontouchdown” logic set (perhaps leaving “onclick” event, which both non-mobile and mobile both recognise, for another event logic role) … and …
  • neither will interrupt the mobile gestures associated with swiping and pinching, which refer to the events “ontouchstart” and “ontouchend” at either end of their lifespan


Previous relevant GeoJson World Coastline Primer Tutorial is shown below.

GeoJson World Coastline Primer Tutorial

GeoJson World Coastline Primer Tutorial

We’re keen to further explore the possibilities of GeoJson public data sets, further to yesterday’s Swift Playgrounds on macOS Map Emoji Tutorial.

And then we remembered some of the methodologies we used with Responsive Web Design Landing Page Image Map Tutorial, talking about image maps

And so, we arrive at a long planned for tilt at Image Map functionality that we often turn to Mobilefish.Com and its excellent Image Map Creation to help us out … but not today?! Why not? We have a funny set of needs, they being …

  • our Image Map’s image will have a variable set of width x height dimensions …
  • our Image Map’s image will be transparent
  • our Image Map needs to have a hole left aside inside it where the functionality that originally existed (and pointed to WordPress Blog content like you are reading), is still working

… the first two ideas of which, along with …

… in our “early days” “proof of concept” regions.php GeoJson PHP web application.

If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.

Posted in eLearning, Event-Driven Programming, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a comment

Find English Word Game Collaboration Tutorial

Find English Word Game Collaboration Tutorial

Find English Word Game Collaboration Tutorial

Collaboration, regarding games, is synonymous with “level playing field”, and that is a principle upheld with today’s introduction of sharing and collaboration functionality into our Find the Word game, and further to yesterday’s Find English Mobile Clicked Word Ends Game Tutorial.

As such, we need to pick “common denominators”, so to speak, and that is, as of recent times, to encourage the use of …

  • tabular mode of use … when sharing via email or SMS … rather than …
  • textarea selectionchange event mode of use

… in the sense that we don’t know the arrangements for an email or SMS recipient as to whether they’ll be reading the communication on a mobile or non-mobile platform.

Once a communication is sent, the letters presented will match, and to get the competitive juices flowing, we add a timer aspect to the scoring. Much of the change relates to catering for email and SMS hashtagging arrangements …


var mode=(location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('mode=')[1] ? (decodeURIComponent((location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('mode=')[1].split('&')[0])) : '';
var tabularize=(location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('table=')[1] ? (decodeURIComponent((location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('table=')[1].split('&')[0])) : 'Null';
var minwordlength=(location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('min=')[1] ? Math.min(3,eval(decodeURIComponent((location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('min=')[1].split('&')[0]))) : 3;
var maxwordlength=(location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('max=')[1] ? eval(decodeURIComponent((location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('max=')[1].split('&')[0])) : 15;
var numlettersacross=(location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('across=')[1] ? eval(decodeURIComponent((location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('across=')[1].split('&')[0])) : (tabularize == 'Null' ? 50 : 80);
var numlettersdown=(location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('down=')[1] ? eval(decodeURIComponent((location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('down=')[1].split('&')[0])) : 40;
var fontpixels=(location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('pixels=')[1] ? eval(decodeURIComponent((location.search + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'').replace(/^\#/g,'&')).split('pixels=')[1].split('&')[0])) : 13;

var firstdate=new Date();
var seconddate=new Date();
var updatehowlong=false, jhis='';

if (('' + window.localStorage.getItem('findwordcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('findwordcollaboratoremailee');
}
if (('' + window.localStorage.getItem('findwordcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('findwordcollaboratorsmsno');
}


function doemail(inidea) {
////alert(3);
var zfrom='youllneverfindthis', zto='youllneverfindthis';
var azx=top.document.getElementById('xae' + 'mail');
//alert(33);
if (!azx) { azx=top.document.createElement("a"); }
//alert(3333);
//if (1 == 1) {
//document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
//}
//(334);
azx.style = "display: none";
//alert(2334);
azx.target = "_top";
//alert(6334);
azx.id = 'xae' + 'mail';
//if (1 == 6 && bihbig.indexOf('</he' + 'ad>') != -1 || bihbig.indexOf('<b' + 'o' + 'dy') != -1 || bihbig.indexOf('<h' + '1') == 0) {
//azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h2')[0].innerHTML.split('&')[0].split('<')[0].replace(/\`/g,'').replace(/^Events\ in\ /g, 'Events in Month ').substring(0)) + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(bihbig)); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
//} else {
var newurl=document.URL.split('?')[0].split('#')[0] + '?mode=' + encodeURIComponent(mode.trim()) + '&across=' + numlettersacross + '&down=' + numlettersdown + '&fontpixels=' + fontpixels + '&wordlenmin=' + minwordlength + '&wordlenmax=' + maxwordlength + '&table=' + tabularize + '#' + encodeURIComponent(jhis.replace(/\<br\>/g,''));
if (!oops && tabularize == 'Null' && !document.getElementById('tablemy')) {
zfrom=encodeURIComponent('Highlighting');
zto=encodeURIComponent('Click Start/End');
newurl=document.URL.split('?')[0].split('#')[0] + '?mode=' + encodeURIComponent(mode.trim()) + '&across=' + numlettersacross + '&down=' + numlettersdown + '&fontpixels=' + fontpixels + '&wordlenmin=' + minwordlength + '&wordlenmax=' + maxwordlength + '&table=y#' + encodeURIComponent(jhis.replace(/\<br\>/g,''));
}
if (sparewes) {
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(sparewes.document.getElementsByTagName('h1')[0].innerText).replace(zfrom,zto) + '&body=' + encodeURIComponent(newurl); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + sparewes.document.body.innerHTML.split('<h2')[1]));
} else {
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h1')[0].innerText).replace(zfrom,zto) + '&body=' + encodeURIComponent(newurl); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
}
//}
azx.click();
if (!oops && tabularize == 'Null' && !document.getElementById('tablemy')) {
location.href=newurl;
}
if (!updatehowlong) {
updatehowlong=true;
setInterval(upscore, 5000);
}
return '';
}

function dosms() {
if (('' + window.localStorage.getItem('findwordcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('findwordcollaboratoremailee');
}
if (('' + window.localStorage.getItem('findwordcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('findwordcollaboratorsmsno');
}
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (1 == 1 || eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '?mode=' + encodeURIComponent(mode.trim()) + '&across=' + numlettersacross + '&down=' + numlettersdown + '&fontpixels=' + fontpixels + '&wordlenmin=' + minwordlength + '&wordlenmax=' + maxwordlength + '&table=' + tabularize + '#' + encodeURIComponent(jhis.replace(/\<br\>/g,'')))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('findwordcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('findwordcollaboratoremailee');
}
window.localStorage.setItem('findwordcollaboratoremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('findwordcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('findwordcollaboratorsmsno');
}
window.localStorage.setItem('findwordcollaboratorsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
var newurl=document.URL.split('?')[0].split('#')[0] + '?mode=' + encodeURIComponent(mode.trim()) + '&across=' + numlettersacross + '&down=' + numlettersdown + '&fontpixels=' + fontpixels + '&wordlenmin=' + minwordlength + '&wordlenmax=' + maxwordlength + '&table=' + tabularize + '#' + encodeURIComponent(jhis.replace(/\<br\>/g,''));
if (!oops && tabularize == 'Null' && !document.getElementById('tablemy')) {
newurl=document.URL.split('?')[0].split('#')[0] + '?mode=' + encodeURIComponent(mode.trim()) + '&across=' + numlettersacross + '&down=' + numlettersdown + '&fontpixels=' + fontpixels + '&wordlenmin=' + minwordlength + '&wordlenmax=' + maxwordlength + '&table=y#' + encodeURIComponent(jhis.replace(/\<br\>/g,''));
}
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(newurl.replace('#','&').replace('?','#')); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
if (!oops && tabularize == 'Null' && !document.getElementById('tablemy')) {
location.href=newurl;
}
if (!updatehowlong) {
updatehowlong=true;
setInterval(upscore, 5000);
}
}
return '';
}

function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('findwordcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('findwordcollaboratoremailee');
}
window.localStorage.setItem('findwordcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}

function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('findwordcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('findwordcollaboratorsmsno');
}
window.localStorage.setItem('findwordcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}

… with that “midair feeling” approach to the “a” “mailto:” and/or “sms:” links used via the 📧 (email) and/or 📟 (SMS) emoji buttons now available in the changed textarea_find_words.html Find the Words game.


Previous relevant Find English Mobile Clicked Word Ends Game Tutorial is shown below.

Find English Mobile Clicked Word Ends Game Tutorial

Find English Mobile Clicked Word Ends Game Tutorial

We tried making the mobile platforms work with the selectionchange event of yesterday’s Find English Highlighted Words Game Selection Tutorial‘s Find the Word game web application, but we decided, today …

  1. the default game operation for mobile platforms should be a new …
    • overlayed
    • table … based …
    • cell … onclick event based …
    • modus operandi … for each end of the user chosen words (as user clicked)

    … means by which to play the game … as well as …

  2. the default game operation for non-mobile platforms remains the old textarea selectionchange event methodology … but either above …
  3. defaults can be overridden by a new address bar URL argument

We made the selectionchange event range.start and range.end form the suffix of those “cell” element “IDs” above within the following document.body onload event function


var tabularize=location.search.split('table=')[1] ? (decodeURIComponent(location.search.split('table=')[1].split('&')[0])) : 'Null';

function pickletters() {
var valis='', randval=eval(Math.floor(Math.random() * 26) % 26), ihis='', lvalis='';
for (var ir=1; ir<=numlettersdown; ir++) {
lvalis='';
for (var ic=1; ic<=numlettersacross; ic++) {
valis+=String.fromCharCode(eval(randval + 'A'.charCodeAt(0)));
lvalis+=String.fromCharCode(eval(randval + 'A'.charCodeAt(0)));
if (eval(linesarr.length) < ir) {
linesarr.push(valis.slice(-1));
} else {
linesarr[eval(-1 + ir)]+=valis.slice(-1);
}
if (eval(colsarr.length) < ic) {
colsarr.push(valis.slice(-1));
} else {
colsarr[eval(-1 + ic)]+=valis.slice(-1);
}
ihis+=String.fromCharCode(eval(randval + 'A'.charCodeAt(0)));
randval=eval(Math.floor(Math.random() * 26) % 26);
}
origcomplines.push(lvalis);
lvalis='';
if (ir < 50) {
valis+=String.fromCharCode(10);
ihis+='<br>';
}
}
//alert(valis);
document.getElementById('myta').value=valis;
eol=document.getElementById('myta').value.substring(numlettersacross).split('A')[0].split('B')[0].split('C')[0].split('D')[0].split('E')[0].split('F')[0].split('G')[0].split('H')[0].split('I')[0].split('J')[0].split('K')[0].split('L')[0].split('M')[0].split('N')[0].split('O')[0].split('P')[0].split('Q')[0].split('R')[0].split('S')[0].split('T')[0].split('U')[0].split('V')[0].split('W')[0].split('X')[0].split('Y')[0].split('Z')[0];
oureol=eol;
var lris='ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
for (var ii=0; ii<lris.length; ii++) {
letteremojis.push(orflag(lris[ii]));
blankemojis.push(' '); // ' ';
loweremojis.push(lris[ii].toLowerCase()); // ' ';
upperemojis.push(lris[ii].toUpperCase()); // ' ';
}
firstval=document.getElementById('myta').value;
document.getElementById('mya').href=document.URL.split('#')[0];
if ((tabularize + 'n').toLowerCase().substring(0,1) == 'y' || (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i) && tabularize == 'Null')) {
document.getElementById('myh1').innerHTML=document.getElementById('myh1').innerHTML.replace('Highlighting', 'Click Start/End')
var rectta=document.getElementById('myta').getBoundingClientRect();
var trline='', ninis=0, icis=0, jicis=0, lettersare=[], tableouter=document.getElementsByTagName('textarea')[0].outerHTML.replace(' style="', ' style="position:absolute;z-index:92;opacity:1.0;background-color:rgba(0,255,0,0.6);top:0px;left:0px;margin:0 0 0 0;padding:0 0 0 0;').replace('myta', 'tablemy').replace(/textarea/g,'table');
for (icis=0; icis<origcomplines.length; icis++) {
lettersare=origcomplines[icis].split('');
trline='<tr></tr>';
for (jicis=0; jicis<lettersare.length; jicis++) {
trline=trline.replace('</tr>', '<td id=td' + ninis + ' onclick=tdanalyze(this);>' + lettersare[jicis] + '</td></tr>');
ninis++;
}
for (jicis=0; jicis<eol.length; jicis++) {
ninis++;
}
tableouter=tableouter.replace('</table>', trline + '</table>');
}
document.body.style.backgroundColor='white';
document.getElementById('myta').style.visibility='hidden';
if (!navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('tdright').style.textAlign='right';
}
document.getElementById('dtable').innerHTML=tableouter;
if (document.getElementById('mybut')) { document.getElementById('mybut').style.display='none'; }
tableih=document.getElementById('dtable').innerHTML;
}

if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
if (!document.getElementById('tablemy')) { ioff=10000; }
if (!document.getElementById('tablemy')) { clickthere=false; }
}
}

And that way we hived off most of the selectionchange event logic into a global variable sensitive new Javascript function hansel called by both methodologies above …


var tog=['lightgreen', 'olive'];

function hansel() {
var joff=rstart, koff=eval('' + eol.length);
lesssel=thesel;
var huheol=eol;
if (huheol == '') { huheol=String.fromCharCode(10); }
while (lesssel.indexOf(huheol) != -1) {
lesssel=lesssel.replace(huheol, '');
}
complines=(thesel + '~').replace(huheol + '~', '').replace('~','').split(huheol);
//diagl=eval('' + complines.length);
//diagl+=eval(eval(-1 + eval('' + complines.length)) * 80);
//document.title='' + eval('' + lesssel.length) + ' vs ' + eval(eval(eval(-1 + eval('' + complines.length)) * 80) + eval(2 - eval('' + complines.length)));
if (sofar.indexOf(';' + thesel + ';') != -1) {
document.title=document.title.replace('Score:','Score+'); //'Score :' + document.getElementById('score').innerHTML.split('Score:')[1] + ' ... Repeat selection ...';
} else if (sofar.indexOf(';' + thesel + ';') == -1 && eval('' + complines.length) >= minwordlength && eval('' + complines.length) <= maxwordlength && eval('' + lesssel.length) == eval(eval(eval('' + complines.length) + eval(eval(-1 + eval('' + complines.length)) * numlettersacross)))) {
// 3 goes with 163 (2x80 + 3) or 159 (2x80 - 1)
// 4 goes with 244 (3x80 + 4) or 238 (3x80 - 2)
// 5 goes with 325 (4x80 + 5) or 317 (4x80 - 3)
icols=0;
twowordstocheck=[lesssel.substring(icols).toUpperCase().substring(0,1), lesssel.substring(icols).toUpperCase().substring(0,1)];
tworesults=['',''];
//eol=complines[1].substring(numlettersacross);
xnumlettersacross=eval(eval('' + eol.length) + eval('' + numlettersacross));
altsel=thesel.substring(0,1).toLowerCase() + complines[0].substring(1).split(eol)[0] + eol;
//rstart=eval('' + range.start);
//rend=eval('' + range.end);
indxofinterest=eval(rstart % eval(1 * eval(eval('' + eol.length) + eval('' + numlettersacross))));
indxofinterest++;
oureol=eol;
for (ilines=1; ilines<eval('' + complines.length); ilines++) {
if (eval(1 + ilines) >= eval('' + complines.length)) { oureol=''; }
icols+=eval(1 + numlettersacross);
joff+=eval(1 + numlettersacross);
if (document.getElementById('td' + eval(joff + koff))) {
if (document.getElementById('td' + eval(joff + koff)).outerHTML.indexOf('pink') == -1) { lastpinks.push('td' + eval(joff + koff)); }
document.getElementById('td' + eval(joff + koff)).style.backgroundColor='pink';
koff+=eval('' + eol.length);
}
if (indxofinterest > 0) {
altsel+=complines[ilines].substring(0,indxofinterest).toUpperCase() + complines[ilines].substring(indxofinterest).substring(0,1).toLowerCase() + complines[ilines].substring(eval(1 + indxofinterest)).toUpperCase() + oureol;
} else {
altsel+=complines[ilines].substring(0,1).toLowerCase() + complines[ilines].substring(1).toUpperCase() + oureol;
}
indxofinterest++;
twowordstocheck[0]+=lesssel.substring(icols).toUpperCase().substring(0,1);
//alert('twowordstocheck_so_far=' + twowordstocheck[0] + ' ilines=' + ilines + ' rstart % xnumlettersacross+=' + eval(rstart % xnumlettersacross) + ' rstart % numlettersacross=' + eval(rstart % numlettersacross) + ' rstart=' + rstart + ' rend=' + rend + ' complines[ilines]=' + complines[ilines] + ' altsel_so_far=' + altsel);
twowordstocheck[1]=lesssel.substring(icols).toUpperCase().substring(0,1) + twowordstocheck[1];
}
altsel+=thesel.substring(rend);
//alert('twowordstocheck_so_far=' + twowordstocheck[0] + ' ilines=' + ilines + ' rstart % xnumlettersacross+=' + eval(rstart % xnumlettersacross) + ' rstart % numlettersacross=' + eval(rstart % numlettersacross) + ' rstart=' + rstart + ' rend=' + rend + ' complines[ilines]=' + complines[ilines] + ' altsel_so_far=' + altsel);
document.title='Left Diagonal words ' + twowordstocheck[0] + ' and ' + twowordstocheck[1] + ' being checked ...';
documenttitle=' ' + document.title;
document.getElementById('results').style.backgroundColor='pink';
document.getElementById('results').innerHTML=documenttitle.replace(' words ', ' words <br> ').replace(' being checked', ' <br> being checked');
document.getElementById('myta').style.backgroundColor='rgb(230,230,230)';
if (!document.getElementById('tablemy')) {
if (document.getElementById('mybut')) { document.getElementById('mybut').disabled=false; document.getElementById('mybut').style.display='block'; }
}
sofar+=thesel + ';';
//document.getElementById('myta').style.cursor='progress';
if (!showsel) { setTimeout(preshowthesel, eval(ioff + 4000)); setTimeout(showthesel, eval(ioff + 6000)); }
//rstart=eval('' + range.start);
//rend=eval('' + range.end);
isg1=true;
ish=false;
isd=false;
isg0=false;
showsel=true;
} else if (sofar.indexOf(';' + thesel + ';') == -1 && eval('' + complines.length) >= minwordlength && eval('' + complines.length) <= maxwordlength && eval('' + lesssel.length) == eval(eval(eval(-1 + eval('' + complines.length)) * numlettersacross) + eval(2 - eval('' + complines.length)))) {
// 3 goes with 163 (2x80 + 3) or 159 (2x80 - 1)
// 4 goes with 244 (3x80 + 4) or 238 (3x80 - 2)
// 5 goes with 325 (4x80 + 5) or 317 (4x80 - 3)
icols=0;
twowordstocheck=[lesssel.substring(icols).toUpperCase().substring(0,1), lesssel.substring(icols).toUpperCase().substring(0,1)];
tworesults=['',''];
sofar+=thesel + ';';
//eol=complines[1].substring(numlettersacross);
xnumlettersacross=eval(eval('' + eol.length) + eval('' + numlettersacross));
altsel=thesel.substring(0,1).toLowerCase() + complines[0].substring(1).split(eol)[0] + eol;
//rstart=eval('' + range.start);
//rend=eval('' + range.end);
indxofinterest=eval(rstart % eval(1 * eval(eval('' + eol.length) + eval('' + numlettersacross))));
indxofinterest--;
oureol=eol;
for (ilines=1; ilines<eval('' + complines.length); ilines++) {
if (eval(1 + ilines) >= eval('' + complines.length)) { oureol=''; }
icols+=eval(-1 + numlettersacross);
joff+=eval(-1 + numlettersacross);
if (document.getElementById('td' + eval(joff + koff))) {
if (document.getElementById('td' + eval(joff + koff)).outerHTML.indexOf('pink') == -1) { lastpinks.push('td' + eval(joff + koff)); }
document.getElementById('td' + eval(joff + koff)).style.backgroundColor='pink';
koff+=eval('' + eol.length);
}
if (indxofinterest > 0) {
altsel+=complines[ilines].substring(0,indxofinterest).toUpperCase() + complines[ilines].substring(indxofinterest).substring(0,1).toLowerCase() + complines[ilines].substring(eval(1 + indxofinterest)).toUpperCase() + oureol;
} else {
altsel+=complines[ilines].substring(0,1).toLowerCase() + complines[ilines].substring(1).toUpperCase() + oureol;
}
indxofinterest--;
twowordstocheck[0]+=lesssel.substring(icols).toUpperCase().substring(0,1);
//alert('Twowordstocheck_so_far=' + twowordstocheck[0] + ' ilines=' + ilines + ' rstart % xnumlettersacross+=' + eval(rstart % xnumlettersacross) + ' rstart % numlettersacross=' + eval(rstart % numlettersacross) + ' rstart=' + rstart + ' rend=' + rend + ' complines[ilines]=' + complines[ilines] + ' altsel_so_far=' + altsel);
twowordstocheck[1]=lesssel.substring(icols).toUpperCase().substring(0,1) + twowordstocheck[1];
}
altsel+=thesel.substring(rend);
document.title='Right Diagonal words ' + twowordstocheck[0] + ' and ' + twowordstocheck[1] + ' being checked ...';
documenttitle=' ' + document.title;
document.getElementById('results').style.backgroundColor='pink';
document.getElementById('results').innerHTML=documenttitle.replace(' words ', ' words <br> ').replace(' being checked', ' <br> being checked');
document.getElementById('myta').style.backgroundColor='rgb(245,245,245)';
if (!document.getElementById('tablemy')) {
if (document.getElementById('mybut')) { document.getElementById('mybut').disabled=false; document.getElementById('mybut').style.display='block'; }
}
//document.getElementById('myta').style.cursor='progress';
if (!showsel) { setTimeout(preshowthesel, eval(ioff + 4000)); setTimeout(showthesel, eval(ioff + 6000)); }
//rstart=eval('' + range.start);
//rend=eval('' + range.end);
isg0=true;
ish=false;
isd=false;
isg1=false;
showsel=true;
} else if (sofar.indexOf(';' + thesel + ';') == -1 && eval('' + complines.length) >= minwordlength && eval('' + complines.length) <= maxwordlength && eval(-1 + eval('' + lesssel.length)) == eval(eval(eval(-1 + eval('' + complines.length)) * numlettersacross) + eval(0 * eval('' + complines.length)))) {
// 6 goes with 401
// 4 goes with 241
icols=0;
twowordstocheck=[lesssel.substring(icols).toUpperCase().substring(0,1), lesssel.substring(icols).toUpperCase().substring(0,1)];
tworesults=['',''];
sofar+=thesel + ';';
//eol=complines[1].substring(numlettersacross);
xnumlettersacross=eval(eval('' + eol.length) + eval('' + numlettersacross));
altsel=thesel.substring(0,1).toLowerCase() + complines[0].substring(1).split(eol)[0] + eol;
//rstart=eval('' + range.start);
//rend=eval('' + range.end);
indxofinterest=eval(rstart % eval(1 * eval(eval('' + eol.length) + eval('' + numlettersacross))));
oureol=eol;
for (ilines=1; ilines<eval('' + complines.length); ilines++) {
icols+=numlettersacross;
joff+=numlettersacross;
if (document.getElementById('td' + eval(joff + koff))) {
if (document.getElementById('td' + eval(joff + koff)).outerHTML.indexOf('pink') == -1) { lastpinks.push('td' + eval(joff + koff)); }
document.getElementById('td' + eval(joff + koff)).style.backgroundColor='pink';
koff+=eval('' + eol.length);
}
if (eval(1 + ilines) >= eval('' + complines.length)) { oureol=''; }
if (indxofinterest > 0) {
altsel+=complines[ilines].substring(0,indxofinterest).toUpperCase() + complines[ilines].substring(indxofinterest).substring(0,1).toLowerCase() + complines[ilines].substring(eval(1 + indxofinterest)).toUpperCase() + oureol;
} else {
altsel+=complines[ilines].substring(0,1).toLowerCase() + complines[ilines].substring(1).toUpperCase() + oureol;
}
twowordstocheck[0]+=lesssel.substring(icols).toUpperCase().substring(0,1);
//alert('TWowordstocheck_so_far=' + twowordstocheck[0] + ' indxofinterest=' + indxofinterest + ' ilines=' + ilines + ' rstart % xnumlettersacross+=' + eval(rstart % xnumlettersacross) + ' rstart % numlettersacross=' + eval(rstart % numlettersacross) + ' rstart=' + rstart + ' rend=' + rend + ' complines[ilines]=' + complines[ilines] + ' altsel_so_far=' + altsel);
twowordstocheck[1]=lesssel.substring(icols).toUpperCase().substring(0,1) + twowordstocheck[1];
}
altsel+=thesel.substring(rend);
//alert('TWowordstocheck_so_far=' + twowordstocheck[0] + ' indxofinterest=' + indxofinterest + ' ilines=' + ilines + ' rstart % xnumlettersacross+=' + eval(rstart % xnumlettersacross) + ' rstart % numlettersacross=' + eval(rstart % numlettersacross) + ' rstart=' + rstart + ' rend=' + rend + ' complines[ilines]=' + complines[ilines] + ' altsel_so_far=' + altsel);
document.title='Down words ' + twowordstocheck[0] + ' and ' + twowordstocheck[1] + ' being checked ...';
documenttitle=' ' + document.title;
document.getElementById('results').style.backgroundColor='pink';
document.getElementById('results').innerHTML=documenttitle.replace(' words ', ' words <br> ').replace(' being checked', ' <br> being checked');
document.getElementById('myta').style.backgroundColor='rgb(230,230,230)';
if (!document.getElementById('tablemy')) {
if (document.getElementById('mybut')) { document.getElementById('mybut').disabled=false; document.getElementById('mybut').style.display='block'; }
}
//document.getElementById('myta').style.cursor='progress';
if (!showsel) { setTimeout(preshowthesel, eval(ioff + 4000)); setTimeout(showthesel, eval(ioff + 6000)); }
//rstart=eval('' + range.start);
//rend=eval('' + range.end);
isd=true;
ish=false;
isg1=false;
isg0=false;
showsel=true;
} else if (sofar.indexOf(';' + thesel + ';') == -1 && thesel.indexOf(String.fromCharCode(10)) == -1 && eval('' + thesel.length) >= minwordlength && eval('' + thesel.length) <= maxwordlength) {
//alert('Horizontal word ' + eval('' + thesel.length) + ' ... ' + thesel);
twowordstocheck=[lesssel.toUpperCase(), lesssel.split('').reverse().join('').toUpperCase()];
tworesults=['',''];
sofar+=thesel + ';';
document.title='Horizontal words ' + twowordstocheck[0] + ' and ' + twowordstocheck[1] + ' being checked ...';
documenttitle=' ' + document.title;
document.getElementById('results').style.backgroundColor='pink';
document.getElementById('results').innerHTML=documenttitle.replace(' words ', ' words <br> ').replace(' being checked', ' <br> being checked');
document.getElementById('myta').style.backgroundColor='rgb(230,230,230)';
if (document.getElementById('tablemy')) {
for (var iicols=rstart; iicols<=rend; iicols++) {
if (document.getElementById('td' + iicols)) {
if (document.getElementById('td' + iicols).outerHTML.indexOf('pink') == -1) { lastpinks.push('td' + iicols); }
document.getElementById('td' + iicols).style.backgroundColor='pink';
}
}
}
if (!document.getElementById('tablemy')) {
if (document.getElementById('mybut')) { document.getElementById('mybut').disabled=false; document.getElementById('mybut').style.display='block'; }
}
//document.getElementById('myta').style.cursor='progress';
if (!showsel) { setTimeout(preshowthesel, eval(ioff + 4000)); setTimeout(showthesel, eval(ioff + 6000)); }
showsel=true;
//rstart=eval('' + range.start);
//rend=eval('' + range.end);
ish=true;
isg1=false;
isd=false;
isg0=false;
} else {
document.title='Score:' + document.getElementById('score').innerHTML.split('Score:')[1] + ' ... Find the Words Highlighting Game - RJM Programming - March, 2024';
}
}

function handleSelection() { // thanks to https://stackoverflow.com/questions/46651479/reacting-to-selection-changes-in-an-html-textarea
const activeElement = document.activeElement;

// Make sure this is your textarea
if (activeElement && activeElement.outerHTML.indexOf('<textarea') == 0) {
const range = {
start: activeElement.selectionStart,
end: activeElement.selectionEnd
};
// Do something with your range
isvalid=true;
if (!document.getElementById('tablemy') && navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
for (iiis=0; iiis<origcomplines.length; iiis++) {
if (thesel.indexOf(origcomplines[iiis]) == 0) {
isvalid=false;
}
}
}
if (isvalid && eval('' + range.end) > eval('' + range.start)) {
thesel=(activeElement.value.substring(range.start).substring(0, eval(eval('' + range.end) - eval('' + range.start) )));
rstart=eval('' + range.start);
rend=eval('' + range.end);
hansel();
}
}
}

document.addEventListener('selectionchange', handleSelection);

… in the changed textarea_find_words.html Find the Words game.


Previous relevant Find English Highlighted Words Game Selection Tutorial is shown below.

Find English Highlighted Words Game Selection Tutorial

Find English Highlighted Words Game Selection Tutorial

Thinking about yesterday’s Find English Highlighted Words Game Primer Tutorial‘s first draft of a Find the Word game web application, we found it easy to pinpoint where to improve it …

  • better informing the user when a textarea selection event detection caused an analysis of the word forwards and backwards to be analyzed … and flagging this better for them …

    function preshowthesel() {
    if (showsel) {
    if (rend >= 0 && rstart >= 0) {
    if (!clickthere) {
    setTimeout(preshowthesel, 5000);
    return '';
    }
    if (isd || isg0 || isg1) {
    if (altsel.trim() != '' && thesel != '') {
    //alert('here ' + document.getElementById('myta').value.indexOf(thesel) + ' ... ' + altsel)
    document.getElementById('myta').setAttribute('data-alt', encodeURIComponent(document.getElementById('myta').value.replace(thesel.substring(0,eval('' + altsel.length)), hlem(altsel))));
    document.getElementById('myta').value=ovlem(document.getElementById('myta').value.replace(thesel.substring(0,eval('' + altsel.length)), lem(altsel)));
    } else if (altsel.trim() != '' && lastsel != '') {
    //alert('hEre ' + document.getElementById('myta').value.indexOf(lastsel) + ' ... ' + altsel)
    document.getElementById('myta').setAttribute('data-alt', encodeURIComponent(document.getElementById('myta').value.replace(lastsel.substring(0,eval('' + altsel.length)), lem(altsel))));
    document.getElementById('myta').value=ovlem(document.getElementById('myta').value.replace(lastsel.substring(0,eval('' + altsel.length)), lem(altsel)));
    } // else {
    //alert('Why? ' + thesel);
    //}
    } else if (ish) {
    if (eval('' + rstart) > 0) {
    document.getElementById('myta').setAttribute('data-alt', encodeURIComponent(document.getElementById('myta').value.substring(0,rstart).toUpperCase() + hlem(document.getElementById('myta').value.substring(rstart).substring(0, eval(eval('' + rend) - eval('' + rstart) )).toLowerCase()) + document.getElementById('myta').value.substring(rend).toUpperCase()));
    document.getElementById('myta').value=togglelem(document.getElementById('myta').value.substring(0,rstart).toUpperCase() + hlem(document.getElementById('myta').value.substring(rstart).substring(0, eval(eval('' + rend) - eval('' + rstart) )).toLowerCase()) + document.getElementById('myta').value.substring(rend).toUpperCase());
    } else {
    document.getElementById('myta').setAttribute('data-alt', encodeURIComponent(hlem(document.getElementById('myta').value.substring(rstart).substring(0, eval(eval('' + rend) - eval('' + rstart) )).toLowerCase()) + document.getElementById('myta').value.substring(rend).toUpperCase()));
    document.getElementById('myta').value=togglelem(hlem(document.getElementById('myta').value.substring(rstart).substring(0, eval(eval('' + rend) - eval('' + rstart) )).toLowerCase()) + document.getElementById('myta').value.substring(rend).toUpperCase());
    }
    }
    }
    altsel=' ';
    }
    }

    … as your starting point …
  • parameterizations via ? and & address bar URL arguments … pretty self explanatory via …

    var minwordlength=location.search.split('min=')[1] ? Math.min(3,eval(decodeURIComponent(location.search.split('min=')[1].split('&')[0]))) : 3;
    var maxwordlength=location.search.split('max=')[1] ? eval(decodeURIComponent(location.search.split('max=')[1].split('&')[0])) : 15;
    var numlettersacross=location.search.split('across=')[1] ? eval(decodeURIComponent(location.search.split('across=')[1].split('&')[0])) : 76;
    var numlettersdown=location.search.split('down=')[1] ? eval(decodeURIComponent(location.search.split('down=')[1].split('&')[0])) : 40;
    var fontpixels=location.search.split('pixels=')[1] ? eval(decodeURIComponent(location.search.split('pixels=')[1].split('&')[0])) : 13;
  • regional indicator based emoji numerical display helpers for down and diagonal user guesses … in an overlayed textarea element … using …

    var letteremojis=[], blankemojis=[], loweremojis=[], upperemojis=[];

    var lris='ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
    for (var ii=0; ii<lris.length; ii++) {
    letteremojis.push(orflag(lris[ii]));
    blankemojis.push(' '); // ' ';
    loweremojis.push(lris[ii].toLowerCase()); // ' ';
    upperemojis.push(lris[ii].toUpperCase()); // ' ';
    }

    function orflag(thiscc) {
    var lri='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    var dri=['127462','127463','127464','127465','127466','127467','127468','127469','127470','127471','127472','127473','127474','127475','127476','127477','127478','127479','127480','127481','127482','127483','127484','127485','127486','127487'];
    var ccsuff='', ccchar=' ', cde='';
    for (var iccsuff=0; iccsuff<thiscc.length; iccsuff++) {
    ccchar=thiscc.substring(iccsuff, eval(1 + eval('' + iccsuff))).toUpperCase();
    ccsuff+=String.fromCodePoint(dri[eval('' + lri.indexOf(ccchar))]); //'&#' + dri[eval('' + lri.indexOf(ccchar))] + ';';
    cde='.';
    }
    return ccsuff;
    }

    … as an ascii letter to emoji mapping mechanism … or …
  • toggle the display text case for horizontal user guesses … in an overlayed textarea element … using …

    function togglelem(insg) {
    var outsg='';
    var inarr=insg.split('');
    for (var jin=0; jin<insg.length; jin++) {
    if (insg.substring(jin).substring(0,1) == insg.substring(jin).substring(0,1).toLowerCase() && insg.substring(jin).substring(0,1) != insg.substring(jin).substring(0,1).toUpperCase()) {
    outsg+=insg.substring(jin).substring(0,1).toUpperCase();
    } else if (insg.substring(jin).substring(0,1) != insg.substring(jin).substring(0,1).toLowerCase() && insg.substring(jin).substring(0,1) == insg.substring(jin).substring(0,1).toUpperCase()) {
    outsg+=insg.substring(jin).substring(0,1).toLowerCase();
    } else {
    outsg+=insg.substring(jin).substring(0,1)
    }
    }
    return outsg;
    }
  • add a very simple text CSS styling embellisher …
    <style>

    textarea {
    text-shadow: -1px 1px 1px #952dff;
    }

    </style>
  • use the webpage title (on web browser tab) to allow a synopsis without resorting to right hand information, perhaps … via …

    document.title='Score:' + document.getElementById('score').innerHTML.split('Score:')[1] + ' ... Find the Words Highlighting Game - RJM Programming - March, 2024';

… in the changed textarea_find_words.html Find the Words game.


Previous relevant Find English Highlighted Words Game Primer Tutorial is shown below.

Find English Highlighted Words Game Primer Tutorial

Find English Highlighted Words Game Primer Tutorial

Yesterday’s Event Calendar Collaboration Textarea Highlight Linking Tutorial gave us an idea for an English Word Game based on the use of the “/usr/share/dict/words” inbuilt, in our case, English, dictionary you get to look up for free on macOS and Linux web server systems.

This highlighting within a sea of letters presented in a textarea was the framework for users to play a game highlighting …

  • horizontally
  • vertically
  • diagonally

… words in left to right or top to bottom order, or reversed, to score in our “Find the Words” game presented today.

You might question the design here. Highlighting within a textarea element is easy to understand for the horizontal option above, but how does it work for the other two? Well, the textarea selection start position relative to the selection end position can differentiate between the last two options above, as well as for none of the above. And we hope to help with some better display smarts with future releases.

Take a look at the crucial selectionchange event logic for today’s Find the Word game …


function handleSelection() { // thanks to https://stackoverflow.com/questions/46651479/reacting-to-selection-changes-in-an-html-textarea
const activeElement = document.activeElement;

// Make sure this is your textarea
if (activeElement && activeElement.outerHTML.indexOf('<textarea') == 0) {
const range = {
start: activeElement.selectionStart,
end: activeElement.selectionEnd
};
// Do something with your range
if (eval('' + range.end) > eval('' + range.start)) {
thesel=(activeElement.value.substring(range.start).substring(0, eval(eval('' + range.end) - eval('' + range.start) )));
lesssel=thesel;
while (lesssel.indexOf(String.fromCharCode(10)) != -1) {
lesssel=lesssel.replace(String.fromCharCode(10), '');
}
complines=thesel.split(String.fromCharCode(10));
//diagl=eval('' + complines.length);
//diagl+=eval(eval(-1 + eval('' + complines.length)) * 80);
//document.title='' + eval('' + lesssel.length) + ' vs ' + eval(eval(eval(-1 + eval('' + complines.length)) * 80) + eval(2 - eval('' + complines.length)));
if (sofar.indexOf(';' + thesel + ';') != -1) {
document.title='Repeat selection ...';
} else if (sofar.indexOf(';' + thesel + ';') == -1 && eval('' + complines.length) >= 3 && eval('' + lesssel.length) == eval(eval(eval('' + complines.length) + eval(eval(-1 + eval('' + complines.length)) * 80)))) {
// 3 goes with 163 (2x80 + 3) or 159 (2x80 - 1)
// 4 goes with 244 (3x80 + 4) or 238 (3x80 - 2)
// 5 goes with 325 (4x80 + 5) or 317 (4x80 - 3)
icols=0;
twowordstocheck=[lesssel.substring(icols).substring(0,1), lesssel.substring(icols).substring(0,1)];
tworesults=['',''];
for (ilines=1; ilines<eval('' + complines.length); ilines++) {
icols+=81;
twowordstocheck[0]+=lesssel.substring(icols).substring(0,1);
twowordstocheck[1]=lesssel.substring(icols).substring(0,1) + twowordstocheck[1];
}
document.title='Left Diagonal words ' + twowordstocheck[0] + ' and ' + twowordstocheck[1] + ' being checked ...';
documenttitle=' ' + document.title;
document.getElementById('results').innerHTML=documenttitle;
sofar+=thesel + ';';
//document.getElementById('myta').style.cursor='progress';
if (!showsel) { setTimeout(showthesel, 5000); }
showsel=true;
} else if (sofar.indexOf(';' + thesel + ';') == -1 && eval('' + complines.length) >= 3 && eval('' + lesssel.length) == eval(eval(eval(-1 + eval('' + complines.length)) * 80) + eval(2 - eval('' + complines.length)))) {
// 3 goes with 163 (2x80 + 3) or 159 (2x80 - 1)
// 4 goes with 244 (3x80 + 4) or 238 (3x80 - 2)
// 5 goes with 325 (4x80 + 5) or 317 (4x80 - 3)
icols=0;
twowordstocheck=[lesssel.substring(icols).substring(0,1), lesssel.substring(icols).substring(0,1)];
tworesults=['',''];
sofar+=thesel + ';';
for (ilines=1; ilines<eval('' + complines.length); ilines++) {
icols+=79;
twowordstocheck[0]+=lesssel.substring(icols).substring(0,1);
twowordstocheck[1]=lesssel.substring(icols).substring(0,1) + twowordstocheck[1];
}
document.title='Right Diagonal words ' + twowordstocheck[0] + ' and ' + twowordstocheck[1] + ' being checked ...';
documenttitle=' ' + document.title;
document.getElementById('results').innerHTML=documenttitle;
//document.getElementById('myta').style.cursor='progress';
if (!showsel) { setTimeout(showthesel, 5000); }
showsel=true;
} else if (sofar.indexOf(';' + thesel + ';') == -1 && eval('' + complines.length) >= 3 && eval(-1 + eval('' + lesssel.length)) == eval(eval(eval(-1 + eval('' + complines.length)) * 80) + eval(0 * eval('' + complines.length)))) {
// 6 goes with 401
// 4 goes with 241
icols=0;
twowordstocheck=[lesssel.substring(icols).substring(0,1), lesssel.substring(icols).substring(0,1)];
tworesults=['',''];
sofar+=thesel + ';';
for (ilines=1; ilines<eval('' + complines.length); ilines++) {
icols+=80;
twowordstocheck[0]+=lesssel.substring(icols).substring(0,1);
twowordstocheck[1]=lesssel.substring(icols).substring(0,1) + twowordstocheck[1];
}
document.title='Down words ' + twowordstocheck[0] + ' and ' + twowordstocheck[1] + ' being checked ...';
documenttitle=' ' + document.title;
document.getElementById('results').innerHTML=documenttitle;
//document.getElementById('myta').style.cursor='progress';
if (!showsel) { setTimeout(showthesel, 5000); }
showsel=true;
} else if (sofar.indexOf(';' + thesel + ';') == -1 && thesel.indexOf(String.fromCharCode(10)) == -1 && eval('' + thesel.length) >= 3) {
//alert('Horizontal word ' + eval('' + thesel.length) + ' ... ' + thesel);
twowordstocheck=[lesssel, lesssel.split('').reverse().join('')];
tworesults=['',''];
sofar+=thesel + ';';
document.title='Horizontal words ' + twowordstocheck[0] + ' and ' + twowordstocheck[1] + ' being checked ...';
documenttitle=' ' + document.title;
document.getElementById('results').innerHTML=documenttitle;
//document.getElementById('myta').style.cursor='progress';
if (!showsel) { setTimeout(showthesel, 5000); }
showsel=true;
} else {
document.title='Find the Words Highlighting Game - RJM Programming - March, 2024';
}
}
}
}

document.addEventListener('selectionchange', handleSelection);

… in our first draft Find the Words game you can try below …


Previous relevant Event Calendar Collaboration Textarea Highlight Linking Tutorial is shown below.

Event Calendar Collaboration Textarea Highlight Linking Tutorial

Event Calendar Collaboration Textarea Highlight Linking Tutorial

For the first time we can remember, with our Events in Month web application of yesterday’s Event Calendar Collaboration Remembering Recipient Tutorial

  • we’re channelling how in emails “word strings” starting with “http” become links … and so …
  • in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available

Here is the relevant Javascript we ended up with, for this …


var thesel='', showsel=false, lastsel='';

function showthesel() {
var uwords=[], iuw=1, ils=0;
if (showsel) {
showsel=false;
if (lastsel != thesel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
lastsel=thesel;
thesel='';
}
if (lastsel != '' && thesel == '') {
uwords=lastsel.toLowerCase().split('http');
ils=eval(4 + eval('' + uwords[0].length));
for (iuw=1; iuw<uwords.length; iuw++) {
window.open('//' + lastsel.substring(ils).split('//')[1].split(' ')[0].split(String.fromCharCode(10))[0].split(String.fromCharCode(13))[0], '_blank', 'top=' + eval(10 * iuw + 50) + ',left=' + eval(10 * iuw + 50) + ',width=600,height=600');
ils+=eval(4 + eval('' + uwords[iuw].length));
}
}
}
}

function handleSelection() { // thanks to https://stackoverflow.com/questions/46651479/reacting-to-selection-changes-in-an-html-textarea
const activeElement = document.activeElement;

// Make sure this is your textarea
if (activeElement && activeElement.outerHTML.indexOf('<textarea') == 0) {
const range = {
start: activeElement.selectionStart,
end: activeElement.selectionEnd
};
// Do something with your range
if (eval('' + range.end) > eval('' + range.start)) {
thesel=(activeElement.value.substring(range.start).substring(0, eval(eval('' + range.end) - eval('' + range.start) )));
if (!showsel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
showsel=true;
setTimeout(showthesel, 5000);
}
}
}
}

document.addEventListener('selectionchange', handleSelection);

Also, today, we have a few CSS tweaks in the changed events_in_month.htm parent of the Events in Month web application. Feel free to try it for yourself.

Stop Press

Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!


Previous relevant Event Calendar Collaboration Remembering Recipient Tutorial is shown below.

Event Calendar Collaboration Remembering Recipient Tutorial

Event Calendar Collaboration Remembering Recipient Tutorial

When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …

  • When it comes to procedures …
  • Aaaaaarrrrgggghhhh
  • Nuances count … regarding …
  • Errrrrrr
  • Saving time …
  • Saving frustration

… resulting in the “easy to remember” acronym WANESS … we rest our cases!

What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.

Global variables initialization …

var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …

if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …

function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}

function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic

function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}

//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}

return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}

var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}

Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changed events_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!


Previous relevant Event Calendar Collaboration Tutorial is shown below.

Event Calendar Collaboration Tutorial

Event Calendar Collaboration Tutorial

The Event Calendar web application project, of Event Calendar PHP Bookmark Tutorial, from last year is worth a revisit, the reason being …

  • it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
  • each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
  • hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs

… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!

Timing became really important with this integration of …

  • email
  • SMS

… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …

  1. Month
  2. Year

… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …

Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons

<h1>Events in Month &nbsp;&nbsp;<button style=display:none; title='Share via Email' id=semail onclick="doemail('');">&#128231;</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>&#128223;</button> </h1>
New static HTML div element placed to the bottom of the body element …

<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …

var bigbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1].split('&')[0]) : '';
if (bigbih != '' && bigbih.indexOf('xae' + 'mail') == -1) {
bigbih+='<a target=_blank id=xae' + 'mail style=display:none; href="mailto:"></a>';
}
if (bigbih != '' && bigbih.indexOf('xas' + 'ms') == -1) {
bigbih+='<a target=_blank id=xas' + 'ms style=display:none; href="sms:"></a>';
}
var viatext='', smallbih='', wes=null, sparewes=null;
On or around document.body onload event we can analyze any such location.hash hashtagging data …

if (bigbih.indexOf('</he' + 'ad>') != -1 || bigbih.indexOf('<b' + 'o' + 'dy') != -1 || bigbih.indexOf('<h' + '1') == 0) {
//bigbih=bigbih.replace('" id="eventcalendar"', 'margin-top:50px;" id="eventcalendar"');
viatext=' (via contact at ' + ('' + new Date()) + ')';
if (bigbih.indexOf('<h' + '1') == 0) {
smallbih=bigbih;
} else {
smallbih=bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].replace(bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].split('>')[0] + '>', '');
}
wes=windowopen('./events_in_month.htm','_blank','top=40,left=40,width=800,height=800');
//alert(bigbih);
if (wes) {
setTimeout(function(){
wes.document.write(bigbih);
}, 3000);
//document.body.innerHTML=bigbih;
setTimeout(function(){
wes.document.getElementById('semail').style.display='inline-block';
wes.document.getElementById('ssms').style.display='inline-block';
wes.document.getElementsByTagName('h1')[0].style.display='none';
wes.document.getElementsByTagName('h3')[0].style.display='none';
//wes.setviatext(viatext);
//wes.setphpthere(' ');
//wes.togglewords();
}, 5000);
}
}
A window.open wrapper windowopen Javascript function …

function windowopen(poneis, ptwois, pthreeis) {
document.getElementById('divas').innerHTML='<iframe name=ovif id=ovif style="position:absolute;top:140px;left:0px;width:100%;height:100%;z-index:99;" src="' + poneis + '" onload=wopencheck(this);></iframe>';
//document.getElementsByTagName('h1')[0].style.opacity='0.0';
//document.getElementsByTagName('h3')[0].style.opacity='0.0';
sparewes=window.open(poneis.split('.htm')[0] + '.php', 'ovif');
//sparewes=window.open(poneis, 'ovif');
sparewes.document.write(bigbih);
setTimeout(function(){
sparewes.document.getElementById('semail').style.display='inline-block';
sparewes.document.getElementById('ssms').style.display='inline-block';
var sdem=sparewes.document.getElementById('semail').outerHTML;
var sdss=sparewes.document.getElementById('ssms').outerHTML;
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdss,'');
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdem,'');
sparewes.document.getElementsByTagName('h1')[0].style.display='none';
sparewes.document.getElementsByTagName('h3')[0].style.display='none';
sparewes.document.getElementById('dmore').innerHTML+=(' ' + sdem.replace('doeJUNKmail(', 'parent.doe' + 'mail(') + ' ' + sdss.replace('dosJUNKms(', 'parent.dos' + 'ms(') + '<br>');
//sparewes.setviatext(viatext);
//sparewes.setphpthere(' ');
//sparewes.togglewords();
}, 5000);
return null;
}
Is augmented by other new Javascript functions …

function hfanalyze() {
var fio=document.getElementById('ecform');
if (forceit) { forceit=false; return true; }
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
//alert(fions.length);
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value.replace(/\?\>/g,'? >'));
delm='&';
}
//alert(testurl.length);
//if (eval('' + testurl.length) < 750) { return true; }
//if (lastbut.value.indexOf('New') == 0 || 11 == 11) {
if (22 == 22) {
//wo=window.open('', '_blank', 'top=50,left=50,height=600,width=600');
//wo.document.write('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
return ('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
} else if (phpthere != '') {
if (1 == 1) {
postit(fio, false, testurl);
} else {
fio.target='phpif';
fio.method='POST';
fio.action='./events_in_month.php';
if (!document.getElementById('dmore')) {
document.getElementById('ddmore').innerHTML='<input type=hidden id=caltitle name=caltitle value="' + document.getElementById('myoldsel').options[document.getElementById('myoldsel').selectedIndex].innerText + '"></input><input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
} else {
document.getElementById('dmore').innerHTML='<input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
}
}
return 'true';
}
return 'false';
}

function doemail(inidea) {
var azx=document.getElementById('xae' + 'mail');
if (!azx) { azx=document.createElement("a"); }
//if (1 == 1) {
//document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
//}
azx.style = "display: none";
azx.id = 'xaem' + 'ail';
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h2')[0].innerHTML.split('&')[0].split('<')[0].replace(/\`/g,'').replace(/^Events\ in\ /g, 'Events in Month ').substring(0)) + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze())); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
return '';
}

function dosms() {
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead.', snum)).replace(/^null/g,'');
} else {
snum=('' + prompt('Please enter SMS number to send to.', snum)).replace(/^null/g,'');
}
if (snum.indexOf('@') != -1) {
return doemail(snum);
} else if (snum.trim() != '') {
alert('Snum=' + snum + '?');
var azx=document.getElementById('xas' + 'ms');
if (azx == null) { azx=document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()));
azx.click();
}
return '';
}

function wopencheck(iois) {
var aconto = null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
aconto.body.innerHTML=smallbih;
aconto.setviatext(viatext);
}
}
}

function setviatext(towhat) {
viatext=towhat;
}

… to help bed down this new sharing and collaboration functionality in a changed events_in_month.htm parent of the Events in Month web application.


Previous relevant Event Calendar PHP Bookmark Tutorial is shown below.

Event Calendar PHP Bookmark Tutorial

Event Calendar PHP Bookmark Tutorial

Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to


Record to Remember via Bookmark

… use the web browser Bookmarks to help you recall an Events in Month report with long entries.

Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …


https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1

… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!

To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …


var documentURL=document.URL;

… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …


#August__2023_00000__1

… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).

Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).

Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …


<style>
#eventcalendar {
background-color: #fcfcfc;
}

td {
padding-top: 2px;
padding-left: 2px;
padding-right: 2px;
padding-bottom: 12px;
}

.dayb {
color: white;
background-color: red;
padding: 5 5 5 5;
border-radius: 80px;
margin-bottom: 15px;
}

.dow {
color: purple;
font-style: bold;
}

.selday {
margin-left: 8px;
background-color: rgba(255,0,0,0.7);
display: inline-block;
width: 50px;
border-color: transparent;
text-overflow: ellipsis;
}

.tablurb {
background: linear-gradient(to right, white, lightpink, pink);
}

input[type="submit"] {
margin-bottom: 3px;
border-radius: 180px;
}

input[type="number"] {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f3f3f3;
padding: 2 2 2 2;
}

#smonth {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f9f9f9;
padding: 2 2 2 2;
}

.boldtitle {
background-color: rgba(0, 211, 107, 0.2);
}

.boldtitle + .tablurb {
margin-top: 8px;
}


.selhistory {
border-radius: 180px;
background-color: lightpink;
padding: 2 2 2 2;
}
</style>

… and we thank this webpage for the heads up regarding how to calculate week numbers within a year data item displays now available in …


Previous relevant Event Calendar PHP Tutorial is shown below.

Event Calendar PHP Tutorial

Event Calendar PHP Tutorial

Let’s face it. Serverside PHP is just great! It opens up so many opportunities regarding data in your web applications.

As such, onto yesterday’s Event Calendar New Window Tutorial logic we now have a …


Record to Remember

… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.

The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …


<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm

$hcont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.htm');
$cont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php');
$ipad=server_remote_addr();
$ipadless=$ipad;
$js='';

function server_remote_addr() {
global $ipadless;
$rma = $_SERVER['REMOTE_ADDR'];
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
// you can add different browsers with the same way ..
$ipadless=str_replace(".", "_", str_replace(":", "_", $rma));
if(preg_match('/(chromium)[ \/]([\w.]+)/', $ua))
$rma = '000000'.$rma;
elseif(preg_match('/(chrome)[ \/]([\w.]+)/', $ua))
$rma = '00000'.$rma;
elseif(preg_match('/(safari)[ \/]([\w.]+)/', $ua))
$rma = '0000'.$rma;
elseif(preg_match('/(opera)[ \/]([\w.]+)/', $ua))
$rma = '000'.$rma;
elseif(preg_match('/(msie)[ \/]([\w.]+)/', $ua))
$rma = '00'.$rma;
elseif(preg_match('/(mozilla)[ \/]([\w.]+)/', $ua))
$rma = '0'.$rma;
return str_replace(".", "_", str_replace(":", "_", $rma));
}

$itdone=false;
if (isset($_GET['init'])) {
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
echo "<html><head><script type='text/javascript'> " . $js . " </script></head><body><p>" . $ipad . "</p></body></html>";
} else if (isset($_POST['phpthere']) && isset($_POST['bigurl']) && isset($_POST['caltitle'])) {
//file_put_contents('xz.xz', 'l');
if (strpos($cont, '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n") === false) {
//file_put_contents('xz.xzz', 'l');
$cont=str_replace('?' . '>', '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n" . '?' . '>', $cont);
file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php', $cont);
}
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
//file_put_contents('xz.xzzz', "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>");
echo "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>";
} else {
echo $hcont;
}
exit;

//
//
// January__1970_00000__1=http://localhost:8888/events_in_month.htm?caltitle=January%2C%201970&i01.00=&ta01.00=kgjfjhf%20jhgfjhf%20jhkgkjhg%20jhgkjhg%20jkghhg%20jkhgkjhg%20kjhgkjhg&i02.00=&ta02.00=kjhgkjh%20kjhgkjhg%20jkhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg%20kjgkjhg&i03.00=&ta03.00=kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg&i04.00=&ta04.00=&i05.00=&ta05.00=&i06.00=&ta06.00=&i07.00=&ta07.00=&i08.00=&ta08.00=&i09.00=&ta09.00=&i10.00=&ta10.00=&i11.00=&ta11.00=&i12.00=&ta12.00=&i13.00=&ta13.00=&i14.00=&ta14.00=&i15.00=&ta15.00=&i16.00=&ta16.00=&i17.00=&ta17.00=&i18.00=&ta18.00=&i19.00=&ta19.00=&i20.00=&ta20.00=&i21.00=&ta21.00=&i22.00=&ta22.00=&i23.00=&ta23.00=&i24.00=&ta24.00=&i25.00=&ta25.00=&i26.00=&ta26.00=&i27.00=&ta27.00=&i28.00=&ta28.00=&i29.00=&ta29.00=&i30.00=&ta30.00=&i31.00=&ta31.00=
?>

Yes, the parent needed to change for our events_in_month.htm parent of Events in Month web application role.


Previous relevant Event Calendar New Window Tutorial is shown below.

Event Calendar New Window Tutorial

Event Calendar New Window Tutorial

Onto yesterday’s Event Calendar Remembered Tutorial‘s “Mystery Dilemma”

But, there’s an inherent weakness with the design, we’ll go into more into the future.

… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.

Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.

Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …

  1. bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
  2. pick Inspect option …
  3. be in Elements tab of your Web Inspector … and …
  4. highlight top <html> tag …
  5. Context Menu with a right click or two finger gesture …
  6. pick Copy -> Copy outerHTML … so that …
  7. your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
  8. Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)

The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …


function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}

… the form submit buttons are reworded accordingly.

Another idea from this blog thread’s inspiration …

… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.

And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …

  • text shadow means by which the text of Event Calendar can be more impactive

    <div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
  • dropdown element conditional styling

    <style>
    .dayb {
    color: white;
    background-color: red;
    padding: 5 5 5 5;
    }


    .dow {
    color: purple;
    font-style: bold;
    }

    .selday {
    margin-left: 8px;
    background-color: rgba(255,0,0,0.7);
    display: inline-block;
    width: 50px;
    }
    </style>



    if (thislabel.substring(0,1) == 'i') {
    if (thisval.trim() != '') {
    document.getElementById(thislabel.replace('i', 'sel')).style.color='white';
    document.getElementById(thislabel.replace('i', 'sel')).style.backgroundColor='red';

    }
    document.getElementById(thislabel.replace('i', 'opt')).innerText=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + '); //.replace(/\+$/g, ' ');
    document.getElementById(thislabel).value=thisval.replace(/\+$/g, ' ');
    } else {
    document.getElementById(thislabel).value=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + ');
    }
    }
  • placeholder on Blurb conditional existence

    var ourdata='';
    // ...
    if (documentURL.indexOf('?') != -1) {
    if (documentURL.indexOf('?caltitle=') != -1) { ourdata='data-'; }
    // ...
    trtemplate='<tr id=tr01.00><td style=width:22%;><span id=sone01.00 class=dow>' + dotw[adate.getDay()].toUpperCase().substring(0,3) + '</span><br><br><span id=stwo01.00 class=dayb>1<span onblur=sepit(this); contenteditable=true id=sp01.00></span><input type=hidden id=i01.00 name=i01.00 value=""></input><select data-dow=' + dotw[adate.getDay()].toUpperCase().substring(0,3) + ' class=selday onchange="selit(this);" id=sel01.00><option id=opt01.00 value=""></option><option title="All such in month (ie. weekly)" value="...">...</option><option title="And ..." value="&...">&</option><option value=Bold>Bold Title</option><option value=Clone>Clone</option></select></span></td><td class=blurb title="What is on?" id=tb01.00><span title="Event title" style="font-style:bold;color:blue;" id=bd01.00></span><textarea name=ta01.00 id=ta01.00 style="width:100%;height=100%;" ' + ourdata + 'placeholder="Blurb ..." class=tablurb></textarea></td></tr>';
    // ...
    }
    // ...
    }

… for our “second draft” events_in_month.htm Events in Month web application.


Previous relevant Event Calendar Remembered Tutorial is shown below.

Event Calendar Remembered Tutorial

Event Calendar Remembered Tutorial

We were inspired by an Event Calendar pamplette we saw the other day …

… to write a new “proof of concept” Events in Month web application, whose content can be recalled via the web browser’s Bookmark methodologies.

We liked the ideas for day of week and/or date of month grouping arrangements we included, being, the way we interpreted it …

  • just on this day in this month … default
  • on this day of the week throughout the month in question … “
  • on this day and some others in that month in question … “&” … to start with and further amendments available via contenteditable=true span element
  • “Clone” value allows for multiple separated “blurbs” for the one signature day

But, there’s an inherent weakness with the design, we’ll go into more into the future. For now, you can try it yourself below …

Stop Press

This is where we get to for a “second draft” we’ll get into, further, tomorrow …

If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.


If this was interesting you may be interested in this too.

Posted in Ajax, eLearning, Event-Driven Programming, Games, Tutorials | Tagged , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a comment