Dynamic Timer Web Browser Popup Hashtag Navigation Tutorial

Dynamic Timer Web Browser Popup Hashtag Navigation Tutorial

Dynamic Timer Web Browser Popup Hashtag Navigation Tutorial

The recent Dynamic Timer Web Browser LIFO Tutorial, though continuing the decoupling of the “Reminders in Session” web application from the need to involve popup windows, the fact is, if you are using a non-mobile platform and these reminders are crucial, the best way to notice them in that same web browser, while doing other work in other tabs, is via popup windows. With this in mind, it would be good to improve those popup window hashtag navigation possibilities.

There is little doubt you will have seen the proposed hashtag navigation on many a website with numerous data sets to make available to the user (ie. be able to navigate to, and perhaps focus on, by taking the focus off others, as well) …

<?php

var popuphashtag=' <a onclick="curf=1; setTimeout(justthis,1000);" href="#popup1" title="Top">&lt;&lt;</a> <a onclick="if (curf > 1) { curf--; } setTimeout(justthis,1000);" href="#popup1" title="Prev">&lt;</a> <a onclick="if (curf < lastf) { curf++; } setTimeout(justthis,1000);" href="#popup1" title="Next">&gt;</a> <a onclick="curf=lastf; setTimeout(justthis,1000);" href="#popup1" title="Bottom">&gt;&gt;</a> ';
// looks like  << < > >> 

?>

… and am sure you’ve seen this before on webpages all over the net.

It’s those “a” link “onclick” new logics, though, that also open focussed to “details/summary” elements and close non-focussed ones, a nuance not always “part of the mix” of such hashtag navigating, but we feel it adds to the clarity for the user, presumably expecting something definite having clicked a link like that. This programmatic opening and closing of “details/summary” elements does not take away from the opportunity for the user to do their own opening and closing themselves between user scrolling and user clicking and user popup window resizing.

<?php // writes Javascript …

function crbr(inst) {
var brsuff='', prevc='', svalp='', svals='';
var sval=inst + ' ... ' + (new Date());
if (scriptstuff == '') {
scriptstuff='<scr' + 'ipt type="text/javascript"> var curf=1, lastf=1, lastht=-1, pii="popup", idts=0, dts=[], lastih="", thisih=""; ';
scriptstuff+=' function justthis() { if (lastht != curf || 1 == 1) { lastht=curf; location.href="#popup" + curf; spechit(curf); } } ';
scriptstuff+=String.fromCharCode(10);
scriptstuff+=' function spechit(iw) { var iii=1; thisih=""; pii="popup" + iii, dts=document.getElementsByTagName("details"); idts=0; ';
scriptstuff+=' while (document.getElementById(pii)) { if (iw == iii) { dts[idts].open=true; idts++; } else { dts[idts].open=false; idts++; } lastf=iii; thisih=pii; iii++; pii="popup" + iii; } ';
scriptstuff+=' } ';
scriptstuff+=String.fromCharCode(10);

scriptstuff+=' function lhit() { var iii=1; thisih=""; pii="popup" + iii; ';
scriptstuff+=' while (document.getElementById(pii)) { lastf=iii; thisih=pii; iii++; pii="popup" + iii; } ';
scriptstuff+=' pii="popup" + eval(-1 + iii); ';
scriptstuff+=' if (thisih.length > 0 && thisih != lastih) { lastih=thisih; dts=document.getElementsByTagName("details"); ';
scriptstuff+=' for (idts=0; idts<dts.length; idts++) { if (dts[idts].id != thisih) { dts[idts].open=false; } else { curf=eval(1 + idts); } } location.href="#" + thisih; } } setInterval(lhit,1000); </sc' + 'ript>';
scriptstuff+=String.fromCharCode(10);
scriptstuff+='<sty' + 'le> ';
scriptstuff+=' details { width:100%; text-align:center; } ';
scriptstuff+=' summary { ';
scriptstuff+=' background-image: -o-linear-gradient(left top, rgba(205, 205, 205, 1) 0%, rgba(255, 255, 0, 0.2) 50%); ';
scriptstuff+=' background-image: -moz-linear-gradient(left top, rgba(205, 205, 205, 1) 0%, rgba(255, 255, 0, 0.2) 50%); ';
scriptstuff+=' background-image: -webkit-linear-gradient(left top, rgba(205, 205, 205, 1) 0%, rgba(255, 255, 0, 0.2) 50%); ';
scriptstuff+=' background-image: -ms-linear-gradient(left top, rgba(205, 205, 205, 1) 0%, rgba(255, 255, 0, 0.2) 50%); ';
scriptstuff+=' background-image: linear-gradient(to left top, rgba(205, 205, 205, 1) 0%, rgba(255, 255, 0, 0.2) 50%); ';
scriptstuff+='</sty' + 'le> ';
scriptstuff+=String.fromCharCode(10);
}
dsp='<details id=popup' + pppid + ' open><summary>&#128718; ' + (new Date()) + popuphashtag + '</summary>';
dss='</details>';
pppid++;
//
// Rest of crbr logic follows
//
return inst;
}

?>

Again, see this more comprehensive popup hashtag navigation possibilties with the changed HTML/Javascript/CSS stparam.html live run link.


Previous relevant Dynamic Timer Web Browser LIFO Tutorial is shown below.

Dynamic Timer Web Browser LIFO Tutorial

Dynamic Timer Web Browser LIFO Tutorial

Thinking about yesterday’s Dynamic Timer Web Browser Voiceover Tutorial in our notes we wrote …

stack rather than heap (or whatever) with ordering and get rid of ” … [date]” because in summary tag
hashtag navigate via script in popup
as with popup, open most recent and close other reminder details/summary combos on the arrival of a new reminder
add (linear gradient) color to summary tags
semicolon ; is dumb idea as delimiter in logic … better is ` … make this backward compatible for old ; ones
hashtag navigations

… as pretty self explanatory improvements.

Within the main page of our Reminders in Session web application we’re progressively further decoupling it from the popup window dependence. In both, we give a LIFO (last in, first out) feel to the whole thing, on the understanding that the last reminder is the one we want to appear “above the fold” in both parent (ie. main) web pages and any popup windows, though …

  • in main webpage the LIFO happens by physically arranging the details/summary hosted HTML data of latest reminder appears first … while …
  • in popup webpage the LIFO happens by virtue of hashtag navigation to latest details/summary hosted HTML data of interest

… and common to both, at any given time, outside user interaction, the latest details/summary is “open” (ie. showing) while any others are programmatically closed.

In order to help out users who hate scrolling and prefer hashtag navigation we now supply new links on the relevant summary tag …


<details id=dmymsr style='width:100%;height:600px;'><summary id=smymsr>Reminder Voiceover <span title='Animated Emoji' style='opacity: 0.4; font-size: 32px;'>&#127909;</span><span style='margin-left: -32px; opacity: 0.4; font-size: 32px;'>&#128483;</span> Ideas via <a href=#myh1 title=Top>Reminders in Session</a><a onclick=htrtop(); href=#cbi title=Iframe>.</a></summary></details>

… with associated Javascript onclick logic …



var htrect=null;

function posthtrtop() {
window.scrollTo(0, eval(('' + htrect.top).split('.')[0]));
}

function htrtop() {
if (htrect) {
htrect=document.getElementById('divmymsr').getBoundingClientRect();
if (1 == 1) {
setTimeout(posthtrtop, 2000);
} else {
console.log(htrect);
//alert(htrect.top);
window.scrollTo(0, eval(('' + htrect.top).split('.')[0]));
//location.href='#cbi';
}
}
}

htrect=document.getElementById('divmymsr').getBoundingClientRect(); // at document.body onload logic

… to help get from the Voiceover Ideas back up to the parent and vice versa, perhaps without scrolling required.

As HTML (programmatically or as created by the user) in the textarea is allowed, the previous choice of inhouse Standing Order semicolon (inhouse) delimitation is a bit dumb, so we’ve kept backward compatibility, but moved to the backtick (ie. `) as a better (inhouse) delimitation thought.

See this with the changed HTML/Javascript/CSS stparam.html live run link.


Previous relevant Dynamic Timer Web Browser Voiceover Tutorial is shown below.

Dynamic Timer Web Browser Voiceover Tutorial

Dynamic Timer Web Browser Voiceover Tutorial

We wanted to come at the recent Reminders web application of the recent Dynamic Timer Web Browser Reminder Media Tutorial for a few “improvement angles” in the changed HTML/Javascript/CSS stparam.html live run link today …

  • add “abscissa” logic to the “integer” requirement of the Standing Order recall functionality to allow for programmatical repeats of the Javascript prompt first (and only) piece of interactive entry (per dropdown selection) …

    var althastobedec=0;
    // working out althastobedec value goes here
    var irepeats=0, ansis='', latedate.setTime(latedate.getTime() + eval(eval('' + althastobedec) * 1000));
    ansis=prompt('Can hang around until next unique time at ' + latedate + ' if you leave answer as supplied, else Cancel. The number after decimal point could be number of repeat entries.', '' + althastobedec + '.0');
    if (ansis == null) { ansis=''; }
    if (ansis.trim() == '') { document.getElementById('rwording').value=''; }
    if (ansis.indexOf('.') != -1) { repeats=eval('0' + ansis.split('.')[1]); ansis=ansis.split('.')[0]; }
    for (irepeats=0; irepeats<=repeats; irepeats++) { // project Standing Order to next available date logic goes here }
  • our favourite "reveal" functionality pairing of details/summary is used to keep a track of your reminders down the bottom of the parent webpage ...

    // HTML
    <div id=divmore></div>
    // Javascript
    document.getElementById('divmore').innerHTML+="<details id=details" + detailscount + " style='width:100%;'><summary>&#128718; " + (new Date()) + "</summary>" + sval + "</details><br>";
  • integrate the "Voiceover Ideas" (last talked about with Voiceover Ideas Google Translate Tutorial) to enhance the audio element possibilities that came with yesterday's Dynamic Timer Web Browser Reminder Media Tutorial ...

    // HTML
    <div id=divmymsr style='display:none;width:100%;height:600px;'>
    <details id=dmymsr style='width:100%;height:600px;'><summary id=smymsr>Reminder Voiceover <span title='Animated Emoji' style='opacity: 0.4; font-size: 32px;'>&#127909;</span><span style='margin-left: -32px; opacity: 0.4; font-size: 32px;'>&#128483;</span> Ideas</summary></details>
    </div>
    // Onload Javascript
    var amsg = location.search.split('msg=')[1] ? decodeURIComponent(location.search.split('msg=')[1].split('&')[0]) : '';
    var fgo=true;

    if (amsg == '' && fgo) {
    document.getElementById('divmymsr').style.display='block';
    document.getElementById('dmymsr').innerHTML+="<iframe onload=checkrest(this); style='width:100%;height:600px;' src=/macos_say_record.php id=mymsr></iframe>";
    throbbingspans();
    }

    fgo=false;
    // Helper PHP
    the changed PHP macos_say_record.php Voiceover helper live run

Should you be that lucky user who has downloaded the source code above to a macOS local web server system, then you can benefit from the "mighty" Text to English Speech via Mac OS X's command line say command used by PHP via exec to make audio files (under the auspices of a MAMP local Apache/PHP/MySql web server that might support a URL such as HTTP://localhost:8888/stparam.html calling on HTTP://localhost:8888/macos_say_record.php), perhaps.


Previous relevant Dynamic Timer Web Browser Reminder Media Tutorial is shown below.

Dynamic Timer Web Browser Reminder Media Tutorial

Dynamic Timer Web Browser Reminder Media Tutorial

An "onions ofthe 4th dimension" improvement to yesterday's Dynamic Timer Web Browser Reminder Standing Order Date Tutorial could be ...

  • make the content of the reminders a more functional and useful consideration ... by ...
  • as well as text/html content entered in by the user add helperer outerer media browsing for ...
    1. image
    2. audio
    3. video

... mime types via the File API that came along with HTML5. But rather than reinventing that "File API logic wheel" we'll do what macos_say_record.php did and use client_browsing.htm's File API logic (that you can read about at Voiceover Ideas Google Translate Tutorial) in a child iframe scenario, tailoring our parent to suit those same interfacing considerations. Then we can add a listener in our parent application waiting for those interfacings to be populated with media data, to include in the textarea reminder content element.

Try the changed HTML/Javascript/CSS stparam.html live run link to see what we mean.


Previous relevant Dynamic Timer Web Browser Reminder Standing Order Date Tutorial is shown below.

Dynamic Timer Web Browser Reminder Standing Order Date Tutorial

Dynamic Timer Web Browser Reminder Standing Order Date Tutorial

The Standing Orders of yesterday's Dynamic Timer Web Browser Reminder Standing Order Tutorial were based on ...

  • times relative to other times ... but am wondering whether it tweaked with you that ...
  • to define a very large "seconds to wait" (integer) value could involve days other than the day on which the web application is used ... meaning that ...
  • Standing Orders can be made more powerful as an idea by making use of this "loophole"

... and so with Standing Order cases that would normally display (in a Javascript alert window) ...

Sorry, we only look forward into today.

... can be an opportunity for large improvements in "forward planning" with users who will keep the web application's window open for days at a time, and want the same reminders to be delivered at regular times each day. So now, you might see, on selecting a Standing Order dropdown option "Tue Dec 29 2020 11:53:26 GMT 1100 (AEDT)" (in a Javascript prompt window) ...

Can hang around until next unique time at Wed Dec 30 2020 11:53:19 GMT+1100 (AEDT) if you leave answer as supplied, else Cancel

59344

... and in okaying that "59344" answer, the next time you might see something like ...

Can hang around until next unique time at Thu Dec 31 2020 11:53:19 GMT+1100 (AEDT) if you leave answer as supplied, else Cancel

145658

... etcetera etcetera etcetera ... making your Standing Order span dates, perhaps.

Along the way with the changed HTML/Javascript/CSS stparam.html live run link we've also decoupled the web application from the success (or not) of the popup window, imbuing enough "smarts" in the parent window web application to tell the whole story without the popup window (though it ... would be nice ... yes ... with them). We've also aesthetically improved the whole look of the "Reminders in Web Browser Session" web application somewhat.


Previous relevant Dynamic Timer Web Browser Reminder Standing Order Tutorial is shown below.

Dynamic Timer Web Browser Reminder Standing Order Tutorial

Dynamic Timer Web Browser Reminder Standing Order Tutorial

An improvement onto yesterday's Dynamic Timer Web Browser Session Tutorial could be a way to reduce the amount of interactive entry required, in the case of a repeated set of reminders, in other words a "Standing Order of Reminders".

We're going to involve window.localStorage to store these optional Standing Orders, which ten, on re-execution, will be available via a dropdown list of standing orders as applicable.

To create a Standing Order from your cumulatively created set of reminders we'll present a button dynamically, that is optional for the user to click to save those accumulated reminders into a "Standing Order of Reminders", as per ...

HTML

<h1><select onchange=soit(this); style='margin-left:12px;margin-right:10px;display:none;' id=sorems></select><input onclick=putso(); id=iorems style='margin-left:12px;margin-right:10px;display:none;' type=button value='Make a Standing Order the '></input>Reminders <select style='display:none;' id=selrems></select> in Session</h1>
Javascript

var options=[];
var thesedates=[];
var thesewords=[];
var theseseconds=[];

function soit(sois) {
var remsare=[], wrems=[];
if (sois.value != '') {
remsare=sois.value.split(';;');
for (var ire=0; ire<remsare.length; ire++) {
document.getElementById('ih').value=remsare[ire].substring(0,2);
document.getElementById('im').value=remsare[ire].substring(2).substring(0,2);
document.getElementById('is').value=remsare[ire].substring(4).substring(0,2);
wrems=remsare[ire].split(';');
document.getElementById('rwording').value=remsare[ire].substring(eval(8 + wrems[1].length));
document.getElementById('go').click();
}
sois.value='';
}
}

function putso() {
var innards='', innardsd='';
if (window.localStorage) {
if (options.length > 0) {
options.sort();
for (var iu=0; iu<options.length; iu++) {
innards+=innardsd + options[iu];
innardsd=';;';
}
localStorage.setItem('reminderso' + nextson, encodeURIComponent('<option value="' + innards.replace(/\"/g,'\"') + '">' + (new Date()) + '</option>'));
}
}
}

function getso() {
var retso='', retson=1, thisso=' ', preretso='';
if (window.localStorage) {
while (thisso != '') {
thisso='';
if (localStorage.getItem('reminderso' + retson)) {
thisso=decodeURIComponent(localStorage.getItem('reminderso' + retson).replace(/^null$/g,'').replace(/^undefined$/g,'')).replace(/\+/g,' ');
}
if (thisso != '') {
retso+=thisso;
preretso='<option value="">Standing Order</option>';
}
retson++;
nextson++;
}
}
return preretso + retso;
}

function createReminder(what, when) {
var other='', whenvalue='', latestdate=new Date();
if (('' + when.id).indexOf('number') == -1 && hastobedec != '') {
if (what != '') {
document.title=midprefix + 'Waiting ' + hastobedec + ' (up to ' + hastobedec + ' remaining) seconds from ' + (new Date()) + ' ... ' + documenttitle;
when=document.getElementById('rnumber');
other=hastobedec;
timeaway=eval('' + other);
origtimeaway=timeaway;
dd1=new Date();
document.getElementById('nums').innerHTML='';
document.getElementById('myh3').innerHTML=document.getElementById('myh3').innerHTML.split(' ... ')[0];
}
} else if (('' + whenvalue).indexOf('-') == -1) {
whenvalue=when.value;
document.title=midprefix + 'Waiting ' + whenvalue + ' (up to ' + whenvalue + ' remaining) seconds from ' + (new Date()) + ' ... ' + documenttitle;
timeaway=eval('' + whenvalue);
origtimeaway=timeaway;
dd1=new Date();
document.getElementById('nums').innerHTML='';
document.getElementById('myh3').innerHTML=document.getElementById('myh3').innerHTML.split(' ... ')[0];
}
if (('' + when.id).indexOf('number') != -1 && what != '') {
myfuncs.push(myfunc);
thesewords.push(what);
latestdate.setTime(latestdate.getTime() + eval(eval('' + timeaway) * 1000))
thesedates.push(latestdate);

if (other != '') {
theseseconds.push(-1);
document.getElementById('iorems').style.display='inline-block';
options.push(("0" + latestdate.getHours()).slice(-2) + ("0" + eval(0 + latestdate.getMinutes())).slice(-2) + ("0" + latestdate.getSeconds()).slice(-2) + ";-1;" + what);

setTimeout(myfuncs[eval(-1 + myfuncs.length)], eval(1000 * eval('' + other)), what);
} else if (('' + whenvalue).indexOf('-') == -1) {
theseseconds.push(eval('' + whenvalue));
document.getElementById('iorems').style.display='inline-block';
options.push(("0" + latestdate.getHours()).slice(-2) + ("0" + eval(0 + latestdate.getMinutes())).slice(-2) + ("0" + latestdate.getSeconds()).slice(-2) + eval('' + whenvalue) + ";" + what);

setTimeout(myfuncs[eval(-1 + myfuncs.length)], eval(1000 * eval('' + whenvalue)), what);
}
}
hastobe='';
hastobedec='';
}

function onl() {
document.getElementById('ih').value=('0' + h).slice(-2);
document.getElementById('im').value=('0' + m).slice(-2);
document.getElementById('is').value=('0' + s).slice(-2);
if (amsg == '') {
document.getElementById('sorems').innerHTML=getso();
}
if (document.getElementById('sorems').innerHTML != '') {
document.getElementById('sorems').style.display='inline-block';
}

}

Try the changed HTML/Javascript/CSS stparam.html live run link for the possibility of standing order reminders tailored for personal use per web browser "brand", and device.


Previous relevant Dynamic Timer Web Browser Session Tutorial is shown below.

Dynamic Timer Primer Tutorial

Dynamic Timer Web Browser Session Tutorial

We're revisiting the dynamic timer via good ol' Javascript setTimeout we presented with Dynamic Timer Primer Tutorial by starting again, thanks to ideas from Pass parameter to setTimeout callback function (thanks) and trying to let it live for a web browser session where the dynamic timer originator window is still an open window (but not necessarily in focus), and we ask of that web browser that it allows window.open popup windows be allowable. In other words, our next ...

  • Dynamic Timer ...
  • Javascript (ie. client) incarnation ... can be used to ....
  • build up a stack of reminders ... in the two modes of user entry ...
    1. absolute DateTime ... or ...
    2. relative seconds to wait

    ... ahead of, perhaps ...

  • working with other windows within that web browser session that if still in focus as a reminder becomes due gets a reminder popup window being placed on top

Again, we base the "dynamic timer" logic on Pass parameter to setTimeout callback function as per ...


var myfuncs=[];
var amsg = location.search.split('msg=')[1] ? decodeURIComponent(location.search.split('msg=')[1].split('&')[0]) : '';
var woises=[];
var documenttitle=document.title;
var dd1, dd2;
var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
var s = d.getSeconds();
var hastobe='', hastobedec='';
var timeaway=0, origtimeaway=0;
var myfunc = function(x){xx=x;preprewois()};
var x = "test";
var xx="";
var firstgo=true;
var wois=null;
if (amsg == '') {
window.onbeforeunload = function() {
if (wois) {
if (!wois.closed) {
wois.close();
}
}
return false;
};
wois=window.open(document.URL.split('#')[0].split('?')[0] + '?msg=' + encodeURIComponent(' '),'_blank','top=250,left=800,width=500,height=500');
}

function createReminder(what, when) {
var other='', whenvalue='';
if (('' + when.id).indexOf('number') == -1 && hastobedec != '') {
if (what != '') {
document.title='Waiting ' + hastobedec + ' (up to ' + hastobedec + ' remaining) seconds from ' + (new Date()) + ' ... ' + documenttitle;
when=document.getElementById('rnumber');
other=hastobedec;
timeaway=eval('' + other);
origtimeaway=timeaway;
dd1=new Date();
document.getElementById('nums').innerHTML='';
document.getElementById('myh3').innerHTML=document.getElementById('myh3').innerHTML.split(' ... ')[0];
}
} else if (('' + whenvalue).indexOf('-') == -1) {
whenvalue=when.value;
document.title='Waiting ' + whenvalue + ' (up to ' + whenvalue + ' remaining) seconds from ' + (new Date()) + ' ... ' + documenttitle;
timeaway=eval('' + whenvalue);
origtimeaway=timeaway;
dd1=new Date();
document.getElementById('nums').innerHTML='';
document.getElementById('myh3').innerHTML=document.getElementById('myh3').innerHTML.split(' ... ')[0];
}
if (('' + when.id).indexOf('number') != -1 && what != '') {
myfuncs.push(myfunc);
if (other != '') {
setTimeout(myfuncs[eval(-1 + myfuncs.length)], eval(1000 * eval('' + other)), what);
} else if (('' + whenvalue).indexOf('-') == -1) {
setTimeout(myfuncs[eval(-1 + myfuncs.length)], eval(1000 * eval('' + whenvalue)), what);
}
}
hastobe='';
hastobedec='';
}

... as some "starter code" for you to expand out on based on today's HTML/Javascript/CSS stparam.html live run link that by the end of the day's work started catering for those web browser's not working for window.focus() so that we allow ...

  • parent webpage's tab to get a bell 🛎 emoji first character you might notice away doing work in other web browser tabs ... and for ...
  • multiple reminders (not necessarily dynamically viewed) be, also (because they exist in full in the popup child window should you persevere), in a new dropdown in the parent webpage window
  • latest reminder glows in the parent window

... as you can see below. Happy reminding!

Dynamic Timer Primer Tutorial

Did you know?

You may, on examining the code above, be intrigued by our use of absolute date (in Javascript client land) differences. Why use this more resource intensive approach? Well, in the case of a user wandering off to other web browser tabs to do other work, the setTimeout (and I think setInterval) second parameter "thousandth of a second integer number" representation of length of time to wait is honoured for a while and then at irregular intervals, it looks like to us. But if you expect this, even our places in the parent webpage and in its tab title where a number of seconds left to go (ie. a countdown number in seconds) is shown to the user, we can resort to absolute date (in Javascript client land) differences to be sure of the numbers here. You as the user can see where absolute date (in Javascript client land) differences pulls the count back into line jumping a bit from the incremental count that occurs when your parent window is always in "focus". When a window is not in "focus" it is referred to as in "blur", just like for HTML input type=text element textboxes.


Previous relevant Dynamic Timer Primer Tutorial is shown below.

Dynamic Timer Primer Tutorial

Dynamic Timer Primer Tutorial

As we imtimated when we presented Details Summary Button Delayed Onclick Tutorial we're back "to infill", today. Yes, our favourite client-side timer, setTimeout can be dynamic with respect to ...

  • what it does
  • when it does it

... and today, starting slowly here, we have what_and_when.html, an HTML and Javascript proof of concept web application that you can try for yourself to perform ...

  • what: your tailored Javascript
  • when: via either "seconds away" or a "set hh:mm:ss" local time

What "powers" the dynamism? Good ol' Javascript eval as per statements like ...


eval('setTimeout(function() { ' + document.getElementById('jsis').value + String.fromCharCode(10) + ' document.getElementById("isecs").value="-1"; preonl(1); }, ' + eval(eval('' + document.getElementById('isecs').value) * 1000) + ')');


Previous relevant Details Summary Button Delayed Onclick Tutorial is shown below.

Details Summary Button Delayed Onclick Tutorial

Details Summary Button Delayed Onclick Tutorial

You might have noticed with yesterday's Details Summary Button Onclick Primer Tutorial's ....

  • details or summary element onclick logic that it relied on an assumption that ...
  • the onclick event occurs after a details element attribute "open" is finalized ... and though ...
  • we "feel" that this might be true ... yet, we would rather ...
  • not chance it, and make it that the onclick event occurs "delayed" after any details element attribute "open" is finalized ... using ...
  • that ever useful setTimeout timer function

... but with more nuance than our usual Javascript ...


function calledBySetTimeout_nothingPassed() {
console.log('called by setTimeout');
}

setTimeout(calledBySetTimeout_nothingPassed, 3000); // delayed by 3 seconds

... way of doing things, that we seem to use in every second project. Today, with great advice from this very useful link we jump quite a few nuances to more complexity than we had in mind for some future "infilling" blog postings, but the ideas being so cute, we want to drop you users unfamiliar with the joys of the "setTimeout" timer into some quite deeeeeeeeeeep water ... with ... called ...


function makeDelayedHandler(f, time){ // Delay execution of event handler function "f" by "time" ms, thanks to https://stackoverflow.com/questions/3048005/document-onclick-settimeout-function-javascript-help
return function(e){
var ev = e || window.event;
setTimeout(function(){
f(ev);
}, time);
};
}

function checkds(e) {
var ois = (e && e.target) || (event && event.srcElement);
var dpp='';
var dld='';
var oga='';
try {
dpp=ois.getAttribute('data-preid').replace(/^null$/g, '').replace(/^undefined$/g, '');
dld=ois.getAttribute('data-lastid').replace(/^null$/g, '').replace(/^undefined$/g, '');
} catch(ewq) {
}
if (ois.parentElement && ois.outerHTML.toLowerCase().indexOf('<summary') == 0) {
oga=('' + ois.getAttribute('data-pardet')).replace(/^null$/g, '').replace(/^undefined$/g, '').replace(/^false$/g, '');
if (oga != '') {
ois=document.getElementById(oga);
oga=ois.open; //('' + ois.getAttribute('open')).replace(/^null$/g, '').replace(/^undefined$/g, '').replace(/^false$/g, '');
try {
if (dpp.trim() == '' && dld.trim() == '') {
dpp=ois.getAttribute('data-preid').replace(/^null$/g, '').replace(/^undefined$/g, '');
dld=ois.getAttribute('data-lastid').replace(/^null$/g, '').replace(/^undefined$/g, '');
}
} catch(ewqtwo) {
}
} else {
oga=ois.parentElement.open; //('' + ois.parentElement.getAttribute('open')).replace(/^null$/g, '').replace(/^undefined$/g, '').replace(/^false$/g, '');
ois=ois.parentElement;
}
if (dpp.trim() == '' && dld.trim() == '') {
dpp=ois.getAttribute('data-preid').replace(/^null$/g, '').replace(/^undefined$/g, '');
dld=ois.getAttribute('data-lastid').replace(/^null$/g, '').replace(/^undefined$/g, '');
}
} else {
oga=ois.open; //('' + ois.getAttribute('open')).replace(/^null$/g, '').replace(/^undefined$/g, '').replace(/^false$/g, '');
}
if (oga) {
location.href='#' + dpp; //ois.getAttribute('data-preid');
location.href='#' + dld; //ois.getAttribute('data-lastid');
}
}

... via changed calling logic as per ...


var initiallynone=false;


function lookfordsa() {
var lastdet1='', lastdet2='', lastdetid='';
var ebefore='', fbefore='';
var nodetails=true;
var lasteis=null, lastid='';
var indselems=document.getElementsByTagName('details');
var hashtop='top';
var bihis='';
if (eval('' + indselems.length) > 0) {
var inelems=document.getElementsByTagName('*');
nodetails=false;
if (initiallynone) { initiallynone=false; setTimeout(lookfordsa, 3000); return; }
bihis=document.body.innerHTML;


for (var iplj=0; iplj<inelems.length; iplj++) {
if (('' + inelems[iplj].outerHTML).toLowerCase().replace(/^null$/g, '').replace(/^undefined$/g, '').indexOf('<br') == 0) {
lastid=lastid;
} else if (('' + inelems[iplj].outerHTML).toLowerCase().replace(/^null$/g, '').replace(/^undefined$/g, '').indexOf('<summary') == 0) {
nodetails=false;
if (lastdetid != '') {
inelems[iplj].setAttribute('data-pardet', lastdetid);
lastdetid='';
}

if (lasteis && lastid != '') {
if (('' + inelems[iplj].onclick).replace(/^null$/g, '').replace(/^undefined$/g, '') == '') {
inelems[iplj].setAttribute('data-preid', '' + hashtop.replace('#',''));
inelems[iplj].setAttribute('data-lastid', '' + lastid);
if (1 == 1) {
inelems[iplj].onclick = makeDelayedHandler(checkds, 1000);
} else {

inelems[iplj].onclick = function(evt) { location.href='#' + evt.target.getAttribute('data-preid'); location.href='#' + evt.target.getAttribute('data-lastid'); }
}
console.log('summary:' + lastid);
}
}
lasteis=null;
lastid='';
} else if (('' + inelems[iplj].outerHTML).toLowerCase().replace(/^null$/g, '').replace(/^undefined$/g, '').indexOf('<details') == 0) {
nodetails=false;
if (lasteis && lastid != '') {
if (('' + inelems[iplj].onclick).replace(/^null$/g, '').replace(/^undefined$/g, '') == '') {
lastdet1=hashtop.replace('#','');
inelems[iplj].setAttribute('data-preid', '' + hashtop.replace('#',''));
lastdet2=lastid;
inelems[iplj].setAttribute('data-lastid', '' + lastid);
//document.getElementById('myh1').innerHTML+=('details:' + lastid);
if (1 == 1) {
inelems[iplj].onclick = makeDelayedHandler(checkds, 1000);
} else {

inelems[iplj].onclick = function(evt) { if (('' + evt.target.getAttribute('open')).replace(/^null$/g, '').replace(/^undefined$/g, '').replace(/^false$/g, '') != '') { location.href='#' + evt.target.getAttribute('data-preid'); location.href='#' + evt.target.getAttribute('data-lastid'); } }
}
//inelems[iplj].onclick = function(evt) { location.href='#' + evt.target.getAttribute('data-preid'); location.href='#' + evt.target.getAttribute('data-lastid'); }
lastdetid=('' + inelems[iplj].id);
lasteis=null;
lastid='';

}
} else {
if (bihis.indexOf(('' + inelems[iplj].outerHTML).replace(/^null$/g, '').replace(/^undefined$/g, '')) != -1) {
ebefore=bihis.split(('' + inelems[iplj].outerHTML).replace(/^null$/g, '').replace(/^undefined$/g, ''))[0].replace(/\&nbsp\;/g,'');
if ((ebefore.trim() + '~').indexOf('</a>~') != -1) {
fbefore='<a ' + ebefore.split('<a ')[eval(-1 + ebefore.split('<a ').length)];
if (fbefore.indexOf(' id="') != -1) {
lastid=fbefore.split(' id="')[1].split('"')[0];
lasteis=document.getElementById(lastid);
console.log(lastid);
console.log(lasteis.outerHTML);
console.log(lasteis.href);
if (('' + lasteis.href).indexOf('#') >= 0) {
hashtop='#' + lasteis.href.split('#')[1];
//alert('4:' + hashtop);
}

if (('' + inelems[iplj].onclick).replace(/^null$/g, '').replace(/^undefined$/g, '') == '') {
lastdet1=hashtop.replace('#','');
inelems[iplj].setAttribute('data-preid', '' + hashtop.replace('#',''));
lastdet2=lastid;
inelems[iplj].setAttribute('data-lastid', '' + lastid);
//document.getElementById('myh1').innerHTML+=('details:' + lastid);
if (1 == 1) {
inelems[iplj].onclick = makeDelayedHandler(checkds, 1000);
} else {

inelems[iplj].onclick = function(evt) { if (('' + evt.target.getAttribute('open')).replace(/^null$/g, '').replace(/^undefined$/g, '').replace(/^false$/g, '') != '') { location.href='#' + evt.target.getAttribute('data-preid'); location.href='#' + evt.target.getAttribute('data-lastid'); } }
}
lastdetid=('' + inelems[iplj].id);
lasteis=null;
lastid='';

}
}
//alert('Not Here ');
}
}
}
} else {
if (('' + inelems[iplj].type).toLowerCase() == 'a') {
if (('' + inelems[iplj].id) != '') {
if (('' + inelems[iplj].href).indexOf('#') >= 0) {
hashtop='#' + inelems[iplj].href.split('#')[1];
//alert('7:' + hashtop);
lasteis=inelems[iplj];
lastid=inelems[iplj].id;
} else {
lasteis=null;
lastid='';
}
}
} else if (('' + inelems[iplj].outerHTML).replace(/^null$/g, '').replace(/^undefined$/g, '').replace(/\&nbsp\;/g, '').trim() != '') {
//if (lasteis) { alert('lastid=' + lastid + ' and this.outerHTML=' + inelems[iplj].outerHTML); }
lasteis=null;
lastid='';
}
}
}
}
if (nodetails) { initiallynone=true; setTimeout(lookfordsa, 3000); } else if (initiallynone) { initiallynone=false; setTimeout(lookfordsa, 3000); }
}

setTimeout(lookfordsa, 3000);

... involved in the changed external Javascript details_hash.js that you can test with our inhouse chat web application.


Previous relevant Details Summary Button Onclick Primer Tutorial is shown below.

Details Summary Button Onclick Primer Tutorial

Details Summary Button Onclick Primer Tutorial

We've written an external Javascript details_hash.js that ...

  • adds button like qualities (ie. onclick logic) to details/summary element combinations that are ...
  • preceeded by an "a" tag with # hashtag navigation "href" attribute ... and a ...

  • real "id" attribute (that, in turn, can be hashtag navigated to) ... in which case ...
  • dynamic "onclick" logic is added (to details or summary), if none defined already, to hashtag navigate to a good view of this details/summary content (to top of screen, but allowing for "Back to Chat" hashtag navigation, already there, as well)

... uses logic we had yesterday's Chat No Sockets Chrome Hear It Tutorial web architecture to work from, with testing and which you can try yourself via today's changed php_listener.php's chat web application.

You'll see in that changed code above, the biggest changes being the call of this external Javascript, as per (within document.head) ...

<?php echo "

<style>
b:hover { border: 1px solid red; }
a[href=\"#myh1\"] { font-size: 32px; }
summary { background-color:#f0f0f0; border: 1px solid olive; }

</style>
<script type='text/javascript' src='HTTP://www.rjmprogramming.com.au/PHP/details_hash.js?x=" . rand(0,23456) . "' defer></script>

"; ?>


Previous relevant Chat No Sockets Chrome Hear It Tutorial is shown below.

Chat No Sockets Chrome Hear It Tutorial

Chat No Sockets Chrome Hear It Tutorial

Back at the inhouse Chat web application today we combine ...

... to get helped out by the excellent Google Translate's translation and Text to Speech capabilities, to add a "hear it" layer of functionality to this Chat web application.

As well, we establish ...

  • bold text styling of the latest chat line from a chat collaborator ... that has ...
  • HTML ...

    <b onclick="gtit(this);" onmouseover="gtit(this);">Chat Line</b>

    ... combining with ...
  • Javascript ...

    var gtw=null, gtwurl='';

    function windowopen(gtu, gtb, gtx) {
    if (gtu == gtwurl) {
    if (gtw) {
    if (gtw.closed) {
    return window.open(gtu, gtb, gtx);
    }
    } else {
    return window.open(gtu, gtb, gtx);
    }
    } else {
    gtwurl=gtu;
    if (gtw) {
    if (!gtw.closed) {
    gtw.close();
    }
    }
    return window.open(gtu, gtb, gtx);
    }
    return gtw;
    }

    function gtit(ihis) {
    var instuff=(ihis.innerHTML.split('<')[0]);
    gtw=windowopen('https://translate.google.com/#view=home&op=translate&sl=' + top.document.getElementById('ootw').value.substring(0,2).replace(top.document.getElementById('ootw').value.substring(2),'auto') + '&tl=' + top.document.getElementById('ootw').value.substring(2) + '&text=' + encodeURIComponent(instuff.split('<br>')[0]), '_blank', 'left=100,top=100,width=500,height=500');
    }
  • for the non-Microsoft web browsers we establish our favourite "reveal" stylers, the details/summary element combination that can programmatically "scrunch up" and allow user interactive flexibility via ...
    <?php echo "

    function detsum(viaoid, thisoid, ems) {
    var eparts=ems.split('.'), ipre=0;
    var eprefix=\"<p title='Animated Emoji' style='opacity: 0.4; font-size: 32px;'>&#\" + eparts[0] + \";</p>\";
    eprefix=\"&#\" + eparts[0] + \";\";
    var esuffix='';
    if (navigator.appName == 'Microsoft Internet Explorer' || (navigator.appName == 'Netscape' && navigator.appVersion.indexOf('Edge') > -1)) {
    return '';
    }
    var noif=thisoid.replace('if','');
    if (viaoid.indexOf(noif) != -1) {
    if (eparts.length > 1) {
    eprefix=\"<span title='Animated Emoji' style='opacity: 0.4; font-size: 32px;'>&#\" + eparts[0] + \";</span>\";
    for (ipre=1; ipre<eparts.length; ipre++) {
    esuffix+=\"<span title='Animated Emoji' style='opacity: 0.4; font-size: 32px;'>&#\" + eparts[ipre] + \";</span>\";
    }
    }
    eprefix=eprefix.replace('</p>','</span>').replace('<p','<span');
    return '  <details id=det' + noif + ' style=display:inline-block;width:95%; open><summary style=display:inline;font-size:32px;>' + eprefix + esuffix + '</summary>';
    } else {
    if (eparts.length > 1) {
    eprefix=\"<span title='Animated Emoji' style='opacity: 0.4; font-size: 32px;'>&#\" + eparts[0] + \";</span>\";
    for (ipre=1; ipre<eparts.length; ipre++) {
    esuffix+=\"<span title='Animated Emoji' style='opacity: 0.4; font-size: 32px;'>&#\" + eparts[ipre] + \";</span>\";
    }
    }
    eprefix=eprefix.replace('</p>','</span>').replace('<p','<span');
    return '  <details id=det' + noif + ' style=display:inline-block;width:95%;><summary style=display:inline;font-size:32px;>' + eprefix + esuffix + '</summary>';
    }
    }


    function sumdet(viaoid, thisoid) {
    if (navigator.appName == 'Microsoft Internet Explorer' || (navigator.appName == 'Netscape' && navigator.appVersion.indexOf('Edge') > -1)) {
    return '';
    }
    return '</details>';
    }

    function ddivfbit(ioi) {
    var detlist=document.getElementsByTagName('details'), idetlist=1;
    if (document.getElementById('dfeedback').innerHTML == '') {
    document.getElementById('dfeedback').innerHTML=\"<a id=ifdict href=#myh1 style=vertical-align:top; title=Top>^</a>\" + detsum(ioi.id,'ifdict','128483') + \"<br><iframe id=idict style='width:100%;height:800px;' src='https://www.rjmprogramming.com.au/PHP/speech_supervisor.php?topchat=" . $enduring . "'></iframe>\" + sumdet(ioi.id,'ifdict') + \"<br><a id=ifimg href=#myh1 style=vertical-align:top; title=Top>^</a>\" + detsum(ioi.id,'ifimg','128444.127912') + \"<br><iframe id=iimg style='width:100%;height:800px;' src='../HTMLCSS/feedback.htm?sid=" . $enduring . "'></iframe>\" + sumdet(ioi.id,'ifimg') + \"<br><a id=ifav href=#myh1 style=vertical-align:top; title=Top>^</a>\" + detsum(ioi.id,'ifav','128452.128250') + \"<br><iframe id=iav style='width:100%;height:800px;' src='../macos_say_record.php?topchat=" . $enduring . "'></iframe>\" + sumdet(ioi.id,'ifav') + \"\";
    } else if (ioi.id.indexOf('dict') != -1 && document.getElementById('idict').src.indexOf('topchat=') == -1) {
    if (document.getElementById('detdict')) { document.getElementById('detdict').setAttribute('open',true); for (idetlist=1; idetlist<detlist.length; idetlist++) { detlist[idetlist].removeAttribute('open'); } }
    document.getElementById('idict').src=document.getElementById('idict').src.split('?')[0].split('#')[0] + '?topchat=" . $enduring . "';
    } else if (detlist.length > 0) {
    for (idetlist=0; idetlist<detlist.length; idetlist++) {
    if (detlist[idetlist].id.indexOf(ioi.id.replace('if','').replace('aa','')) != -1) {
    detlist[idetlist].setAttribute('open',true);
    } else {
    detlist[idetlist].removeAttribute('open');
    }
    }
    }
    }


    function divfbit(ioi) {
    var detlist=document.getElementsByTagName('details'), idetlist=1;
    if (document.getElementById('dfeedback').innerHTML == '') {
    document.getElementById('dfeedback').innerHTML=\"<a id=ifdict href=#myh1 style=vertical-align:top; title=Top>^</a>\" + detsum(ioi.id,'ifdict','128483') + \"<br><iframe id=idict style='width:100%;height:800px;' src='https://www.rjmprogramming.com.au/PHP/speech_supervisor.php'></iframe>\" + sumdet(ioi.id,'ifdict') + \"<br><a id=ifimg href=#myh1 style=vertical-align:top; title=Top>^</a>\" + detsum(ioi.id,'ifimg','128444.127912') + \"<br><iframe id=iimg style='width:100%;height:800px;' src='../HTMLCSS/feedback.htm?sid=" . $enduring . "'></iframe>\" + sumdet(ioi.id,'ifimg') + \"<br><a id=ifav href=#myh1 style=vertical-align:top; title=Top>^</a>\" + detsum(ioi.id,'ifav','128452.128250') + \"<br><iframe id=iav style='width:100%;height:800px;' src='../macos_say_record.php?topchat=" . $enduring . "'></iframe>\" + sumdet(ioi.id,'ifav') + \"\";
    } else if (ioi.id.indexOf('dict') != -1 && document.getElementById('idict').src.indexOf('topchat=') != -1) {
    if (document.getElementById('detdict')) { document.getElementById('detdict').setAttribute('open',true); for (idetlist=1; idetlist<detlist.length; idetlist++) { detlist[idetlist].removeAttribute('open'); } }
    document.getElementById('idict').src=document.getElementById('idict').src.split('?')[0].split('#')[0];
    } else if (detlist.length > 0) {
    for (idetlist=0; idetlist<detlist.length; idetlist++) {
    if (detlist[idetlist].id.indexOf(ioi.id.replace('if','').replace('aa','')) != -1) {
    detlist[idetlist].setAttribute('open',true);
    } else {
    detlist[idetlist].removeAttribute('open');
    }
    }
    }
    }

    "; ?>

Today's changed php_listener.php's chat web application is worth your while (re)trying, we reckon.


Previous relevant Chat No Sockets Chrome Hands Free Tutorial is shown below.

Chat No Sockets Chrome Hands Free Tutorial

Chat No Sockets Chrome Hands Free Tutorial

Continuing on with yesterday's Chat No Sockets Dictation Tutorial start to our ...

Google Chrome "Hands Free" Chat dream

... we've progressed a little via ...

  • localStorage recall of last email or SMS into respective textbox "placeholder" attributes (ie. not all the way to "value" attribute yet) ... ready for ...
  • new "email" or "sms" Dictation word logic to try to focus to respective (Email "iemail" or SMS "isms") textbox ... triggering ..,.
  • respective (Email "iemail" or SMS "isms") textbox "placeholder" attribute sets "value" attribute to that localStorage derived setting ...
  • dictated "email invite" will send an email invitation via PHP mail rather than the extra interactions of using an "a" "mailto:" email client application link (as you can see with today's tutorial picture)

Of course there's more "hands free" (logic) to go, but consider "Hey Siri" or other ways to get to the webpage's web application, and the dream is gradually becoming a reality.

Today's changed php_listener.php's chat web application now also interfaces to a changed speech_supervisor.php PHP code for "Dictation" functionalities.


Previous relevant Chat No Sockets Dictation Tutorial is shown below.

Chat No Sockets Dictation Tutorial

Chat No Sockets Dictation Tutorial

We've got a couple of concepts onto yesterday's Chat No Sockets Media Tutorial progress, those being ...

  • allow, at least for Google Chrome web browsing, "Dictation" Speech to Text functionality thanks to Google Speech to Text functionality we last would have referenced with Looks Nice Nearby Speech to Text Game Video Tutorial ... and associated with an overall aim for "hands free" (as well) ...
  • on some browsers we've succeeded playing a Beeoop sound when your Chat collaborator "chat line" has arrived, and we'd like to thank this excellent link for the methodology used ... Javascript ...

    var a=null;
    if (!navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
    a=new AudioContext(); // browsers limit the number of concurrent audio contexts, so you better re-use'em
    }

    function beep(vol, freq, duration) { // thanks to https://odino.org/emit-a-beeping-sound-with-javascript/
    if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
    navigator.vibrate(200);
    } else if (a) {
    v=a.createOscillator();
    u=a.createGain();
    v.connect(u);
    v.frequency.value=freq;
    v.type="square";
    u.connect(a.destination);
    u.gain.value=vol*0.01;
    v.start(a.currentTime);
    v.stop(a.currentTime+duration*0.001);
    }
    }

    function mosay(instuff) {
    document.getElementById('bboop').click(); //beep(999, 220, 300);
    return instuff;
    }

    ... to work with HTML ...

    <button style='display:none;' id=bboop onclick='beep(999, 220, 300);'>Boop</button>

Today's changed php_listener.php's chat web application now also interfaces to a changed speech_supervisor.php PHP code for "Dictation" functionalities.


Previous relevant Chat No Sockets Media Tutorial is shown below.

Chat No Sockets Media Tutorial

Chat No Sockets Media Tutorial

Adding to yesterday's Chat No Sockets Imagery Tutorial ...

  • image functionality ... there's more to "media" in the online wooooorrrrrlllllldddd than just images, and so today ... we add the possibility for ...
  • audio
  • video

... sharing capabilities with our inhouse Chat web application. We need to turn to a "helper" that uses the HTML5 File API, and the recent one we almost immediately thought of (after some small room ruminations) that recent "Voiceover" ideas web application (which became a helper, in turn, to the "Haiku" creator web application (connected to the knee bone)).

An awkward single "a" link seemed a bit forlorn in view of these Chat data functionality extensions, and so we constructed two Animated Emoji Button "a"/"span" sets utilizing the Javascript "throbbingspans()" function as per ...


var tgsps=[], tgspsop=[], newres='', preurl='', tgspsopwhat=[];
// Eg. of html <a style='text-decoration:underline;cursor:pointer;' onclick=\"divfbit(); location.href='#ifimg'; \" title='Image Canvas'><span title='Animated Emoji' style='opacity: 0.4; font-size: 32px;'>&#128444;</span><span style='margin-left: -32px; opacity: 0.4; font-size: 32px;'>&#127912;</span></a>

function throbbingspans() {
var isps, jsps;
if (tgsps.length == 0) {
var sps=document.getElementsByTagName('span');
for (isps=0; isps<sps.length; isps++) {
if (('' + sps[isps].style.opacity) != '') {
if (eval('' + sps[isps].style.opacity) < 1.0) {
tgsps.push(sps[isps]);
tgspsop.push(eval('' + sps[isps].style.opacity));
tgspsopwhat.push(eval('0.10'));
}
}
}
}
if (tgsps.length != 0) {
for (jsps=0; jsps<tgsps.length; jsps+=2) {
if (tgspsop[jsps] > 0.12 && tgspsop[jsps] < 0.88 && tgspsop[1 + jsps] > 0.12 && tgspsop[1 + jsps] < 0.88) { // && tgspsop[jsps] >= tgspsop[1 + jsps]) {
tgspsop[jsps]+=tgspsopwhat[jsps];
tgspsop[1 + jsps]-=tgspsopwhat[1 + jsps];
tgsps[jsps].style.opacity='' + tgspsop[jsps];
tgsps[1 + jsps].style.opacity='' + tgspsop[1 + jsps];
} else if (tgspsop[jsps] > 0.12 && tgspsop[jsps] < 0.88 && tgspsop[1 + jsps] > 0.12 && tgspsop[1 + jsps] < 0.88) { // && tgspsop[jsps] <= tgspsop[1 + jsps]) {
tgspsop[jsps]-=tgspsopwhat[jsps];
tgspsop[1 + jsps]+=tgspsopwhat[1 + jsps];
tgsps[jsps].style.opacity='' + tgspsop[jsps];
tgsps[1 + jsps].style.opacity='' + tgspsop[1 + jsps];
} else if (tgspsop[jsps] > 0.88) {
tgspsop[jsps]-=0.1;
tgspsop[1 + jsps]+=0.1;
tgsps[jsps].style.opacity='' + tgspsop[jsps];
tgsps[1 + jsps].style.opacity='' + tgspsop[1 + jsps];
tgspsopwhat[jsps]=-tgspsopwhat[jsps];
tgspsopwhat[1 + jsps]=-tgspsopwhat[1 + jsps];
} else if (tgspsop[1 + jsps] > 0.88) {
tgspsop[jsps]+=0.1;
tgspsop[1 + jsps]-=0.1;
tgsps[jsps].style.opacity='' + tgspsop[jsps];
tgsps[1 + jsps].style.opacity='' + tgspsop[1 + jsps];
tgspsopwhat[jsps]=-tgspsopwhat[jsps];
tgspsopwhat[1 + jsps]=-tgspsopwhat[1 + jsps];
} else if (tgspsop[1 + jsps] < 0.12) {
tgspsop[jsps]-=0.1;
tgspsop[1 + jsps]+=0.1;
tgsps[jsps].style.opacity='' + tgspsop[jsps];
tgsps[1 + jsps].style.opacity='' + tgspsop[1 + jsps];
tgspsopwhat[jsps]=-tgspsopwhat[jsps];
tgspsopwhat[1 + jsps]=-tgspsopwhat[1 + jsps];
} else if (tgspsop[jsps] < 0.12) {
tgspsop[jsps]+=0.1;
tgspsop[1 + jsps]-=0.1;
tgsps[jsps].style.opacity='' + tgspsop[jsps];
tgsps[1 + jsps].style.opacity='' + tgspsop[1 + jsps];
tgspsopwhat[jsps]=-tgspsopwhat[jsps];
tgspsopwhat[1 + jsps]=-tgspsopwhat[1 + jsps];
}
}
setTimeout(throbbingspans, 900);
}
}

Today's changed php_listener.php's chat web application now also interfaces to a changed macos_say_record.php PHP code for "Voiceover" audio and video creation functionalities.


Previous relevant Chat No Sockets Imagery Tutorial is shown below.

Chat No Sockets Imagery Tutorial

Chat No Sockets Imagery Tutorial

SMS moved on many years ago from a text based content system onto one that these days allows media sharing, as well, so we should allow for this too, adding onto the functionality of yesterday's Chat No Sockets SMS Invitation Tutorial.

We wanted to do this by interfacing to an inhouse web application that allows for canvas graphical data creation, and then onto the Chat invitee as imagery (via the [canvas].toDataURL method). For this we decided to interface to the inhouse "Feedback" web application.

At regular intervals we call the "Feedback" web application, flagging it to regularly check for changed canvas data conditions, in which case our parent "Chat" web application table cell like ...


<td id=thi style='background-size:contain;background-repeat:no-repeat;background-color:white;'></td>

... is given a background image (later passed onto your Chat collaborator) via Javascript DOM (that is the onload event logic of a child iframe to the parent Chat (PHP) web application) such as ...


function zcheckitagain(iois) {
if (iois != null) {
var aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
if (aconto.body.innerHTML != '') {
if (aconto.body.innerHTML.indexOf('"da' + 'ta:') != -1) {
parent.otherimgdatauri='data:' + aconto.body.innerHTML.split('"da' + 'ta:')[1].split('"')[0];
parent.document.getElementById('thi').style.backgroundImage='URL(' + parent.otherimgdatauri + ')';
} else if (aconto.body.innerHTML.indexOf("'da" + 'ta:') != -1) {
parent.otherimgdatauri='data:' + aconto.body.innerHTML.split("'da" + 'ta:')[1].split("'")[0];
parent.document.getElementById('thi').style.backgroundImage='URL(' + parent.otherimgdatauri + ')';
}
}
}
}
}
}

The changed php_listener.php's chat web application "fourth draft" interfacing to the canvas functionality of the "Feedback" web application, and helped out by the changed external Javascript world.js code.


Previous relevant Chat No Sockets SMS Invitation Tutorial is shown below.

Chat No Sockets SMS Invitation Tutorial

Chat No Sockets SMS Invitation Tutorial

Yesterday's Chat No Sockets Session Tutorial gives us an opportunity to become more "granular" with our examination of nuances to ...

  • the web application's "surfing the web" look and aesthetics (first look) and usage practicalities (involving button disabling/enabling at appropriate places in the PHP code (writing Javascript logic)) ... and ... a bit gobsmacking to us ...
  • the need to place a two step logic "tidy up" of obsolete files when dealing with SMS Invitations (to Chat)

... the latter being that we discovered that ...

  • between the point an inviter opens their SMS Messaging app with a populated message that is the URL our Chat web application wants the inviter to send ... and ...
  • that inviter typing the carriage return character that "renders" that SMS that is sent to the invitee ... but ...
  • before the invitee even sees the SMS

... that URL "render" causes an (unexpected to us) real visit of our web server code, and we need to just let the "look of" the "resultant SMS webpage" through at this point, but leave "the implications" for the next time this "SMS webpage" is asked for when the invitee clicks/taps the SMS link they receive from the inviter. The timing of all this is controlled in the logic by the existence of a (what used to be exclusively) "chat_*.line" file, but we now need (to allow for "chat_*.lin2" then "chat_*.line") as per ...

On clicking the "Invite" button, having filled out the "SMS number" field (rather than the Email one)
function butlogic() {
if (document.getElementById('isms').value.trim() != '') {
document.getElementById('jchild').src=document.URL.split('#')[0].split('?')[0] + '?sid=" . $enduring . "&address=' + encodeURIComponent(document.getElementById('isms').value + '#') + '&ipaddress=' + encodeURIComponent('" . $sra . "') + '&ichat=' + encodeURIComponent(document.getElementById('ichat').value);
var wasasms=document.getElementById('asms').href;
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('asms').href=(document.getElementById('asms').href.replace(':&',':' + document.getElementById('isms').value + '&') + encodeURIComponent(document.URL.split('#')[0].split('?')[0]) + encodeURIComponent('?address=' + dummyencodeURIComponent(encodeURIComponent(document.getElementById('isms').value)) + '#'));
} else {
document.getElementById('asms').href=(document.getElementById('asms').href.replace(':&',':' + document.getElementById('isms').value + '&') + encodeURIComponent(document.URL.split('#')[0].split('?')[0]) + encodeURIComponent('?address=' + encodeURIComponent(encodeURIComponent(document.getElementById('isms').value)) + '#'));
}
document.getElementById('asms').click();
document.getElementById('asms').href=wasasms;
} else if (document.getElementById('iemail').value.indexOf('@') != -1) {
document.getElementById('jchild').src=document.URL.split('#')[0].split('?')[0] + '?sid=" . $enduring . "&address=' + encodeURIComponent(document.getElementById('iemail').value) + '&ipaddress=' + encodeURIComponent('" . $sra . "') + '&ichat=' + encodeURIComponent(document.getElementById('ichat').value);
document.getElementById('aemail').href=(document.getElementById('aemail').href.replace(':?',':' + document.getElementById('iemail').value + '?') + encodeURIComponent(document.URL.split('#')[0].split('?')[0]) + encodeURIComponent('?address=' + encodeURIComponent(document.getElementById('iemail').value)));
document.getElementById('aemail').click();
document.getElementById('aemail').href=wasaemail;
}
}
Where the child iframe call above lobs onto in order to create an interim file
} else if (isset($_GET['address']) && isset($_GET['ipaddress'])) {
if (strpos(str_replace("@","",str_replace("+"," ",urldecode($_GET['address']))), "#") !== false) {
file_put_contents("chat_" . explode("#", str_replace("@","",str_replace("+"," ",urldecode($_GET['address']))))[0] . "__" . str_replace("+"," ",urldecode($_GET['ipaddress'])) . ".RLS", str_replace("+"," ",urldecode($_GET['ichat'])));
} else {

file_put_contents("chat_" . explode("#", str_replace("@","",str_replace("+"," ",urldecode($_GET['address']))))[0] . "__" . str_replace("+"," ",urldecode($_GET['ipaddress'])) . ".rls", str_replace("+"," ",urldecode($_GET['ichat'])));
}
exit;
Where the "command line" usage part does its bit to appropriately rename those interim files
if ($argv) { // command line ...
$cfindings="";
$goes=0;
$howmanygoes="-1";
$par=getenv("TERM"); // thanks to https://stackoverflow.com/questions/3214935/can-a-bash-script-tell-if-its-being-run-via-cron
if ("$par" == "" || "$par" == "dummy") { // via cron
$cfindings1=exec("crontab -l | grep 'php_listener' | grep -v 'grep' | cut -d ' ' -f 1");
$cfindings2=exec("crontab -l | grep 'php_listener' | grep -v 'grep' | cut -d ' ' -f 2");
$cfindings3=exec("crontab -l | grep 'php_listener' | grep -v 'grep' | cut -d ' ' -f 3");
$cfindings4=exec("crontab -l | grep 'php_listener' | grep -v 'grep' | cut -d ' ' -f 4");
$cfindings5=exec("crontab -l | grep 'php_listener' | grep -v 'grep' | cut -d ' ' -f 5");
if (strpos($cfindings1, "*/") !== false) {
$howmanygoes=12 * explode("*/", $cfindings1)[1];
} else if (strpos($cfindings2, "*/") !== false) {
$howmanygoes=720 * explode("*/", $cfindings2)[1];
} else if (strpos($cfindings3, "*/") !== false) {
$howmanygoes=17280 * explode("*/", $cfindings3)[1];
}
}
while (1) {
foreach (glob(dirname(__FILE__) . "/chat_*.rls") as $ourfilename) {
rename($ourfilename, explode(".rls", $ourfilename)[0] . ".line");
}
foreach (glob(dirname(__FILE__) . "/chat_*.RLS") as $ourfilename) {
rename($ourfilename, explode(".RLS", $ourfilename)[0] . ".lin2");
}

if ($howmanygoes >= 0) { $goes++; if ($goes >= $howmanygoes) { exit; } }
sleep(5);
}
exit;
}
Where the "command line" usage part's renamed file is found by the web application and the two step deletion is needed
} else { // here from email or SMS link
$sra="";
$cont="";
foreach (glob("chat_" . str_replace("@","",str_replace("+"," ",urldecode($_GET['address']))) . "__*.lin*") as $ourfilename) {
$sra=explode(".",explode("chat_" . str_replace("@","",str_replace("+"," ",urldecode($_GET['address']))) . "__", $ourfilename)[1])[0];
$scont=file_get_contents($ourfilename);
$cont=$scont . "<br>";
if (trim($scont) != "") { $scont=' ... starting with "' . file_get_contents($ourfilename) . '"'; }
if (strpos($ourfilename, ".lin2") !== false) {
rename($ourfilename, explode(".lin2", $ourfilename)[0] . ".line");
$datait=" data-";
} else {

unlink($ourfilename);
}
}
echo "<!doctype html>
// etcetera etcetera etcetera
<body " . $datait . "onload='onlis();'>
// etcetera etcetera etcetera
";

So yet again, feel free to try the changed (including "session" logic) php_listener.php's chat web application "third draft".


Previous relevant Chat No Sockets Session Tutorial is shown below.

Chat No Sockets Session Tutorial

Chat No Sockets Session Tutorial

Back from the "command line" PHP usage of yesterday's Chat No Sockets Cron Tutorial that day's thinking about how to improve the "surfing the net" parts of the Chat web application we're developing got us starting to involve PHP ...


Sessions

... and can't they just be really useful as the identifying methodology to hone in on a webpage session of interest, and exclude all irrelevant others ...

<?php

session_start();
$enduring='' . session_id();
if (isset($_GET['sid'])) {
$enduring=str_replace("+"," ",urldecode($_GET['sid']));
} else if (isset($_POST['sid'])) {
$enduring=str_replace("+"," ",urldecode($_POST['sid']));
}
$dbit=' data-oe="" ';


function server_remote_addr() {
global $enduring;
$rma = $_SERVER['REMOTE_ADDR'];
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
// you can add different browsers with the same way ..
if(preg_match('/(opr)[ \/]([\w.]+)/', $ua))
$rma = '000'.$rma;
elseif(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(".", "x", str_replace(":", "x", $rma . $enduring));
}

?>

... the only nuance being that iframe children get their session IDs mapped to that of the parent via that "sid" argument above.

And so today's work represents a "shoring up" day for the basis for a Chat, and a relief that this "better honing" also meant that we no longer needed to tweak those pesky $_SERVER['HTTP_USER_AGENT'] combinations (a dark art indeed).

So again, feel free to try the changed php_listener.php's chat web application "third draft".


Previous relevant Chat No Sockets Cron Tutorial is shown below.

Chat No Sockets Cron Tutorial

Chat No Sockets Cron Tutorial

Yesterday's Chat No Sockets Primer Tutorial had a "command line" PHP usage component, we wonder whether you noticed? We intend to keep this arrangement for our "no sockets" Chat web application. It will not function as that "Chat" without the command line part of the "equation" being activated. You might think of it as the "traffic cop" of the web application.

As far as that "command line" PHP usage goes ...

  • Isn't it great to have the one code source for all this?
  • How are we going to manage this command line usage, out of ...
    1. interactive in an interactive command line session
    2. set off a background process run of it via the "&" suffix
    3. crontab it (on our Linux web server)

    ?

Suggestion 1 is kludgy, over the top and awkward to arrange for any long period of time, though useful if non-continuous "process coverage" is the go.

Suggestion 2 and 3 are great for "continuous process coverage" (we privately think of as "jigsaw coverage"), but in our books (and the pamphlettes have scarpered it to Pamphlette Island ... which they must intend on "founding"?!) crontab is a better option to take for at least two reasons ...

  • crontab resurrects itself on a system reboot
  • crontab is self documenting (an important advantage regarding command line processing usage)

... but if we are to use crontab "jigsaw coverage", to avoid "jigsaw overlap" we're going to have to change ... regarding ...

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ minute (0 - 59)
   β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€ hour (0 - 23)
   β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€ day of month (1 - 31)
   β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€ month (1 - 12)
   β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€ day of week (0 - 6 => Sunday - Saturday, or
   β”‚ β”‚ β”‚ β”‚ β”‚ 1 - 7 => Monday - Sunday)
   ↓ ↓ ↓ ↓ ↓
   * * * * * command to be executed

... thanks to https://stackoverflow.com/questions/18919151/crontab-day-of-the-week-syntax for above crontab syntax ... as per ( our assumption being that the user will use one of the "every ? units" syntax such as
*/6 * * * * php this_php_happens_every_six_minutes.php
) ...

To <--- <--- From

if ($argv) { // command line ...
$cfindings="";
$goes=0;
$howmanygoes="-1";
$par=getenv("TERM"); // thanks to https://stackoverflow.com/questions/3214935/can-a-bash-script-tell-if-its-being-run-via-cron
if ("$par" == "$TERM") { // via cron
$cfindings1=exec("crontab -l | grep 'php_listener' | grep -v 'grep' | cut -d ' ' -f 1");
$cfindings2=exec("crontab -l | grep 'php_listener' | grep -v 'grep' | cut -d ' ' -f 2");
$cfindings3=exec("crontab -l | grep 'php_listener' | grep -v 'grep' | cut -d ' ' -f 3");
$cfindings4=exec("crontab -l | grep 'php_listener' | grep -v 'grep' | cut -d ' ' -f 4");
$cfindings5=exec("crontab -l | grep 'php_listener' | grep -v 'grep' | cut -d ' ' -f 5");
if (strpos($cfindings1, "*/") !== false) {
$howmanygoes=12 * explode("*/", $cfindings1)[1];
} else if (strpos($cfindings2, "*/") !== false) {
$howmanygoes=720 * explode("*/", $cfindings2)[1];
} else if (strpos($cfindings3, "*/") !== false) {
$howmanygoes=17280 * explode("*/", $cfindings3)[1];
}
}
while (1) {
foreach (glob("chat_*.rls") as $ourfilename) {
rename($ourfilename, explode(".rls", $ourfilename)[0] . ".line");
}
if ($howmanygoes >= 0) { $goes++; if ($goes >= $howmanygoes) { exit; } }
sleep(5);
}
exit;
}

if ($argv) { // command line ...
while (1) {
foreach (glob("chat_*.rls") as $ourfilename) {
rename($ourfilename, explode(".rls", $ourfilename)[0] . ".line");
}
sleep(5);
}
exit;
}

Again, feel free to try the changed php_listener.php's chat web application "second draft".


Previous relevant Chat No Sockets Primer Tutorial is shown below.

Chat No Sockets Primer Tutorial

Chat No Sockets Primer Tutorial

We're starting on a new PHP web application project. We've got a "first draft" of a chat web application that does not use sockets, but rather ...

  1. invites somebody else (via "Invite" button) via email or SMS
  2. that "somebody else" opens the web application via email or SMS links
  3. from then on keep the chat going via "Send" buttons in the two chat incarnations

It needs more work, that is for sure, but perhaps you want to see php_listener.php's chat web application "first draft".

As with good learning between two parties, each listens, takes it in, and replies, as required.

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.

This entry was posted in eLearning, Event-Driven Programming, Tutorials and tagged , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>