… group of access methodologies to our web server here at rjmprogramming.com.au domain. And, believe me, they are all used extensively around here, for many and various purposes.
We had ssh sorted pretty easily for the two scenarios, because it’s use only involved IP addresses, the two, for us, that are concerned, but with sftp, at least for us, we find three scenarios of concern show at the end of this posting part, the last of which we’ll show you the creation of a connection mechanism to in configuring FileZilla, with today’s animated GIF tutorial picture presentation, where no remembering of passwords was needed, and you’ll see us demonstrating …
FileZilla (top left icon) SiteManager -> Duplicate …
… and …
FileZilla SiteManager -> Rename …
… talents
! Huh?!
How so?!
Well, we’re glad you got that proxy to ask?! OpenSSH is used a lot, and for us …
… with web server access arrangements, and these passwords end up being the crux of access controlled by keychains and OpenSSH protected files which also get transferred, in all probability during a migration phase where web server contents is transferred from one IP address to the new web server IP addresses. For us, we relied on our web hoster, the great Crazy Domains to do this migration, and it all went so smoothly, thanks, but if you have those system operator skills you can do this yourself, if you decide to roll this way, or that way …
Anyway, we’re here today nearing the time for the actual DNS changeover, mulling over the question …
But what if we have forgotten something?!
We’re pretty sure there’ll be a bit of time where access back to the CentOS web server will be available to us, and we’d like an sftp access method for that purpose, corresponding to the last of the three FileZilla modes of connection below …
Ongoing sftp connection to whatever IP address web server corresponds to rjmprogramming.com.au
Before DNS changeover getting to proposed AlmaLinux web server
After DNS changeover getting back to CentOS web server (just in case)
amend wp-config.php to add in code statement … thanks to this great advice …
define('RELOCATE',true);
… meaning URLs such as https://[newIPaddressForRJMProgrammingIntoTheFuture]/ITblog just address data and software on [newIPaddressForRJMProgrammingIntoTheFuture] proposed new AlmaLinux9 web server (rather than ducking off to https://www.rjmprogramming.com.au/ITblog/ or https://[oldIPaddressForRJMProgrammingFromTheNearFuturePast]/ITblog/)
in WordPress admin section General Settings make sure both WordPress Address (URL) and Site Address (URL) get mapped to … https://[newIPaddressForRJMProgrammingIntoTheFuture]/ITblog
crontab and curl “MySql Watchdog” basis … we also, around here, use …
crontab and curl “Landing Page Daily Updates Regarding WordPress Blog Posting Content”
… so that going to the RJM Programming Landing Page can glean the latest WordPress Blog content.
During our “pre AlmaLinux” “pre DNS changeover” period, we’ve been doing two lots of WordPress Post “CMS style” textarea blog posting sessions for each daily new blog posting. We’ve got a WordPress Blog with permalinks set to a variant of the blog posting title, and between the two versions …
that part of a URL to get to this content remains the same (phew!) but the POST_ID becomes unreliable as a guaranteed thaing … the least worriesome aspect, against …
the Landing Page dropdowns for new, but soon to be the DNS IP address need to be populated via WordPress Blog MySql based data, via a tutorial_options.php piece of inhouse PHP which needs to change in a few ways …
references to what used to be “mysql_” style PHP calls now need to be transferred to equivalent “mysqli_” ones (and thanks to php – Replacement for deprecated function mysql_connect – Stack Overflow here, regarding great advice) where, as well as the i change, for all but …
$link = mysqli_connect($hostname, $username, $password);
mysqli_num_rows($res)
mysqli_fetch_row($res)
mysqli_close($link)
… references, for our PHP code, which used mysqli_select_db($link, $dbname) and mysqli_query($link,$qry) and mysqli_errno($link) … we needed that extra first argument of the mysqli connection value … changes that reminded us, a tad, of how to go from a CSS Selector to a Javascript DOM by getting rid of _ and just to the right of where that was, capitalize that character, thinking
rethink any $_GET[] or $_POST[] arrangements into $argc (and $argv[1] argument) based logic …
<?php
$index = "index";
if (isset($argc)) {
if ($argc >= 2) {
if ($argv[1] != "mobile") {
$index = $argv[1];
}
}
} else if (isset($_GET['index'])) {
$index = $_GET['index'];
} else if (isset($_POST['index'])) {
$index = $_POST['index'];
}
?>
… so that …
AlmaLinux
CentOS
cd ~rjmprogr/public_html/PHP; php tutorial_options.php
Yes, we’re migrating again (as we were talking about with Apache/PHP/MySql Web Hosting Website Migration ssh Tutorial), though as you read this blog posting, if hot off the press here on 19/7/2024, we’ll not have transferred the DNS over yet, which we’ll outline the reasons for into the future. But, today, we’re excited to outline some steps after …
… it was, mainly, regarding our MySql Watchdog ( korn shell scripting in the Linux Apache/PHP/MySql web server /etc/init.d scripts called via crontab ) that concerned us most. Why (Nala getting distracted, ever since MySql Down Thinking Followup Tutorial … or …)?
Well, understandably our Crazy Domains web hosts were kind enough to …
take on the migration … thanks … and not, just blithely …
make any “old scripting” assumptions ( ie. we came across the new web server’s /etc/init.d folder as empty (and /etc/rc.d/init.d folder, for your AlmaLinux Apache2 daemon process folder arrangements not empty, but missing our own tailored scripting (and that scripting and our crontabbing used /etc/init.d mentions) … so …) … especially any to do with anything even resembling …
crontab … nor, heaven forbid …
curl … (the use of which, up to now, we’ve only ever combined with argument URLs that mentioned rjmprogramming.com.au rather than any numerical IP address … hence, our concerns) … scripting being helped out by crontab … that had any personal tailoring ( ie. a crontab -l
# showed default AlamaLinux arrangements only
This can be very powerful stuff, and you could, during a period before the DNS changeover, if you had a matching crontab situation happening, having two watchdogs on two different IP addresses (only one of which is pointed to proper DNS rjmprogramming.com.au arrangements trying to pick up a MySql service, and though only one would get through picking up the true rjmprogramming.com.au MySql service, the other would, at the very least, be doing superfluous activity because of the way we scripted it. We scripted for …
one DNS arrangement for the one IP address related to rjmprogramming.com.au … rather than …
one DNS arrangement for the one IP address related to rjmprogramming.com.au and another IP address being readied for a future DNS rearrangement that it will map to … and after that …
one DNS arrangement where that new IP address can newly become the DNS for rjmprogramming.com.au and another obsolete (but still having a working crontab happening) IP address awaits a decommission
That is the power, and also the worry, regarding how curl and crontab can be such useful Linux web server tools!
For number 3 scenario, perhaps we can be quick decommissioning, but we imagine we’ll want to have another test period after the DNS changeover. We’ll see. Anyway, on the road towards solutions we’re pretty sure a first step is …
before thinking of starting up any “inhouse scripting componentry” crontab schedulings on the new IP address web server …
populate our empty /etc/init.d … with …
matching script content and permissions and ownership as is (and then, was) happening on the current (but soon to be decommissioned) IP address web server
This transfer of scripts is not suitable for our FileZilla sftp transferring arrangements, and we do not feel like tackling any big shake up of arrangements here.
But, we ended up with a zipping and unzipping methodology that went like …
pick a folder that is hidden to the public (for both IP addresses) but that your Linux administration user (in our case “root”) can get to and our FileZilla web server user (in our case “rjmprogr”) can plonk to and from, for us, that being (notice, regarding Apache, no “public_html” mention) … /home/rjmprogr
for current, but soon to be decommissioned IP address … ssh login … cd /etc/init.d … zip /home/rjmprogr/etcinitd.zip *
use relevant FileZilla on current, but soon to be decommissioned IP address setup to copy this back to your local operating system, ours being macOS …
use relevant FileZilla on new, but soon to be the DNS IP address setup to copy this local operating system etcinitd.zip over to /home/rjmprogr
for new, but soon to be the DNS IP address … ssh login … cd /etc/init.d … unzip /home/rjmprogr/etcinitd.zip *
if happy ( via … ls -l /etc/init.d/* … checking and comparing between IP addresses ), for current, but soon to be decommissioned IP address … ssh login … rm -f /home/rjmprogr/etcinitd.zip # or do this via appropriate FileZilla session
if happy ( via … ls -l /etc/init.d/* … checking and comparing between IP addresses ), for new, but soon to be the DNS IP address … ssh login … rm -f /home/rjmprogr/etcinitd.zip # or do this via appropriate FileZilla session
… that we were really happy with.
For a small number of script files, though, an animated GIF presentation we’ve provided (and don’t you just hate it when you only get the knack of it near the end of the job … sheeesh!) you might want to use …
open two macOS Terminal windows side by side (and not overlapping) …
left one has ssh login to current, but soon to be decommissioned IP address … cd /etc/init.d
right one has ssh login to new, but soon to be the DNS IP address … cd /etc/init.d
for each ( mainly *.*sh for us ) (korn shell usually for us) script of interest (we’ll call thesc.ksh below) …
# no return key press below …
left % cat thesc.ksh
# you might want to use Terminal app’s Edit -> Copy the highlighted “thesc.ksh” to help (via Terminal app’s Edit -> Paste) with …
right % vi thesc.ksh
at left, as necessary, press Return key
at left highlight from the bottom ( probably “exit” ) up to the top ( probably “#!/bin/ksh” ) and use Terminal app’s Edit -> Copy
at right, as necessary, press Return key
at right, if content already there, use appropriate <escape>999dd command to clear to nothing then …
at right, press “i” key (for Insert) …
at right, use Terminal app’s Edit -> Paste
at right, maybe press <escape>w (to have an interim look) ahead of …
at right, maybe press <escape>wq!
attend to any ownership (research chown and chgrp) or file permission (research chmod) mismatches you would tweak to via ls -l /etc/init.d/* … checking and comparing between IP addresses
and consider only leaving “inhouse scripting” in /etc/init.d on new, but soon to be the DNS IP address here, for now (and into the future rewrite scripting for /etc/rc.d/init.d reuse)
When we discussed Linux Watchdog Primer Tutorial in that very generic fashion below, we were being that … generic. Today we turn to specifics in that regard. On the rjmprogramming.com.au domain we use a …
(software) watchdog which checks for the health of the MySql Service, and if not healthy, restore it to health … which is all fine and good from the point of view of the domain rjmprogramming.com.au … but depending on what the user was doing we could …
code for intervention within the MySql using piece of software, and writing out the database error to the webpage, and present alternative navigation
… on the understanding with that latter thought, that we put into play today for a WordPress 4.1.1 blog, we realized we needed to intervene in …
… where the redirection to /slideshow.html effectively “Cuts to the Chase” for the gist of that blog posting you never got to, as MySql is currently down.
Notice how specific the actions can be when you write code “dependent” on another software component, as distinct from the “watchdog” approach we often want to be “independent” in its thinking.
Guess this begs the question? How did we work out where to intervene? We just got to folders on the rjmprogramming.com.au web server with the WordPress (Codex) software and went (showing you the one that yielded the nicest result for us) …
pwd // and we are at [DocumentRoot]/ITblog
cd wp-includes
fgrep 'Error establishing a database connection' *.php
Do you have a computer operational problem? What the … what is that humanoid on about? Well, is there a computer operation you have to do routinely, to fix an ongoing problem, that relies on your personal intervention that, one day, you’d like to not have to worry about, or better still … you usually intervene, but would like a backup approach should you get sick and can’t do it … here’s where you need Fido a watchdog
… am not recommending a Golden Retriever, unless you want your process licked rather than attended to? (had you forgotten this was a question, CedricLinux Nala)? …
oh, moi?)
“
Why is bellybutton fluff blue? But we digress, or at Buckingham Palace one digresses.
Right, back at watchdogs, there are schools of thought …
The process is down for too long and we need to do something about it, because customers are leaving
If you need a watchdog to save your bacon, then (clearly you’re not with it, because the dog’s eaten the bacon and) there is something else fundamentally wrong or something you do not understand, which is what you really should resolve, either way
My view is that, if the underlying process would take years to understand or if it is written in legacy code, I’d go more with the former idea, especially as there is delight in creating a really good watchdog (training one? not so easy) … it can be really hard to do … for this reason cannot give code here really, because there is no “out of the box” that is a responsible approach to advise … you have to study the issue and cut it into its components, unit test the solutions to the components of your watchdog solution, and retest with the interactions of those components. However, this is a coding enthusiast’s view, and is a bit short-sighted, perhaps. In any case, what will save the day is that this decision will probably be made for you by an operations expert, if you work in a large organization.
Some other watchdog considerations should be …
Is the attempt to automate the solution that the watchdog will provide technically possible … may not be?!
We can resolve it with personal intervention … can the watchdog simulate each step of the human intervention? If so, go for that approach if possible.
Be very careful of approaches that involve mouse clicks, as they are quite often relative to too many other environmental issues … try to restrict the watchdog solution to command line/scripting/keyboard ideas … on Windows, AutoHotKey is an excellent recording program of interest (would recommend just using it for keyboard recordings, if using it for a watchdog … by the way, tomorrow (tomorrow arrived today) we do a tutorial showing you how to create an AutoHotKey terminate-and-stay-resident program on Windows).
Have we identified the real intervention points? If not, you might succeed some of the time, but not all the time, and you may cause damage on those times when you have made some assumptions, with your incomplete understanding.
Here is an example. You have an overnight batch process run, and it falls over at a certain point, and you get paged at 3 or 4 (it’s bound to be AM). It has been tentatively decided you might want to create a watchdog … what are some considerations …
What do log files tell me? Find out.
Is it a single thing that is missing that would resolve the problem once and for all? If yes, well, you know not to deviate from this one thing … ignore ideas below.
Of the few problems, is it worth proceeding with the watchdog idea, because the number of separate issues can often cause a factor of ten more complication issue points, and maybe you should stick with human intervention.
Break the watchdog problem into these problem issues as a separate unit-testable piece of scripting code (or whatever your watchdog solution entails) … test each for success … retest for their interaction with each other (ie. that they don’t interfere with each other).
The title of this tutorial mentions Linux but generic thinking like above covers other operating system thought patterns, but there are some Linux (or Unix) tools that are great Linux commands that we should point out … thanks to Hscripts.com and CyberCiti.biz for this …
crontab ( eg. */5 * * * * Username /path/to/command # where /path/to/command gets run by Username every five minutes … arranged via crobtab -e ) … for Windows, equivalent would be Task Schedular in Windows Primer Tutorial
And here are some of the practicalities of a watchdog …
Where does it run? If at more than one place, consider each place separately. If it runs on more than one computer, then clearly this is important. Does the directory and file permissions allow the watchdog to run, but do not allow other users to misuse it? (Please say yes here.) A generic thing about crontab or nohup (or Windows start) scripting arrangements are that you should not assume the environments of these processes is the same as your current running command line process … you should write as if you have just logged on and have done nothing … so just about the first decision of the script is to “cd” itself to the proper place where it was designed to run.
Which user(s) (on whichever computer(s)) can run it?
When does it run? If the solution is uncomplicated enough, maybe you can use a pre-emptive approach. For example, you know the problem is to do with a file missing when a non-critical process fails but later on that file is looked for, then why not pre-emptively get the watchdog to create that file (with default data) ahead of the process run crash point time.
As part of the question above, does one subprocess need to end before another starts? If yes, you need to intervene in such a way that that process architecture remains, and you need to work out an independent way for your independent watchdog to step in, at the correct time, and take over the same task, as required. But if it gets to this, don’t you understand the underlying process well enough to have a crack at doing the “real” solution (for all time)? Have a think, now, and keep checking in on the issue?
You need to log the workings of the watchdog both for information and for further research which might help in achieving a “real” solution (without the watchdog) further down the track.
So why was this posting called a tutorial? Well, there’s some homework. You see there are these Daleks, and we sort of need to know when they’re going to invade Earth again, and Dr Who is not always available, so, was wondering, if it wouldn’t be too inconvenient … if you wouldn’t mind writing that watchdog to detect a Dalek invasion and shoo them off … 6 hours … okay?
“
Woofsky!
“
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.
We want to be able to control the way a Google Charts Geo Chart can be nested within an HTML div element, for instance. We started the day wanting to be able to make …
a Google Charts Geo Chart be a background image to a div element … alas, on this first draft we couldn’t get there (but will continue with the research here) … whereas we succeeded …
adding the Google Charts Geo Chart interfacer’s resultant SVG data as the innerHTML (ie. content) …
<?php echo ”
function newbackin() {
if (dmyxhr.readyState == 4) {
if (dmyxhr.status == 200) {
if (dmyxhr.responseText) {
var m_t='image/jpeg';
var h_t='179';
var w_t='320';
var dbits = dmyxhr.responseText.split('\"height\": ');
if (dbits.length > 1) {
h_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"mime_type\": \"');
if (dbits.length > 1) {
m_t=dbits[1].split('\"')[0];
}
dbits = dmyxhr.responseText.split('\"width\": ');
if (dbits.length > 1) {
w_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"data\":');
dbits = dmyxhr.responseText.split('\"data\":');
if (dbits.length > 1) {
// replace all '_' with '/' and all '-' with '+' thanks to https://stackoverflow.com/questions/757675/website-screenshots
dgsbi='<img alt=\"Blog Posting Image\" style=\"width:' + w_t + 'px;height:' + h_t + 'px;\" width=' + w_t + ' height=' + h_t + ' src=\"data:' + m_t + ';base64,' + dbits[1].split('\"')[1].split('\"')[0].replace(/\_/g,'/').replace(/\-/g,'+') + '\"></img>';
//alert('dgsbi=' + dgsbi);
}
}
}
}
}
function ajaxit(urlin) {
if (urlin.length > 0) {
aurl=urlin;
if (window.XMLHttpRequest) {
dmyxhr = new window.XMLHttpRequest;
}
else {
try {
dmyxhr = new ActiveXObject('Msxml2.XMLHTTP');
} catch (othermicrosoft) {
try {
dmyxhr = new ActiveXObject('Microsoft.XMLHTTP');
} catch (failed) {
dmyxhr = false;
}
}
}
var xurl = 'https://www.googleapis.com/pagespeedonline/v1/runPagespeed?url=' + encodeURIComponent(urlin) + '&screenshot=true';
if (dmyxhr) {
dmyxhr.onreadystatechange = newbackin;
dmyxhr.open('GET', xurl, true);
dmyxhr.send(null);
}
}
}
function wbtoa(instris) {
var outstris=instris;
while (outstris.indexOf(String.fromCharCode(10)) != -1) {
outstris=outstris.replace(String.fromCharCode(10),'');
}
var xzs=prompt(outstris, outstris);
return outstris.replace(/\\\"/g, \"'\");
}
In that Nearest Places form part of the TimeZone places webpage, we noticed that even when shaping to enter a TimeZone Placename in the first textbox we made no attempt to fill in …
latitude
longitude
… when we have the information to do so, and even if we’re misunderstanding a placename designation, that should not stop us from trying, because those two numerical fields above can be corrected, and the form resubmitted, in these scenarios.
Apart from making a PHP derived Javascript variable be made available to the code, we “wrap” …
GeoJson World Countries Drag and Drop Makeover Nuance Tutorial
Nuance alert! We’re not sure if you noticed, but if you tried out the Drag and Drop functionality in the World Countries web application of yesterday’s GeoJson World Countries Drag and Drop Makeover Tutorial you may have noticed …
for a country with lots of TimeZone places, like Brazil, you could get a decent Google Chart Geo Chart map up … but if you were to click the “Map?” link down the bottom of that iframe …
it would fail to show a Google Chart Map Chart for that country’s TimeZone places
This fix, believe it or not, is interesting, perhaps only in an “internal use only” sense, we grant you. But our solution got us delving even more into hashtagging data, so that the solution we came up with was a hybrid whereby …
stayed with PHP $_GET[] (ie. address bar ? and & argumented) data (versus using PHP $_POST[] methodologies) … but …
where it came to the &data=[most of the data] part, other than its first character, we hashtagged the rest
… so that the logic flows as per usual, but we intervene at places and flesh it out via location.hash (client side only) means. We won’t bore you too much with all the places of intervention except the receiving map.php’s “easiest to get” intervention …
In addition to the Wikipedia information, at the very least, presented following a successful drag and drop operation, from today, we also start presenting a new HTML iframe element containing …
Nearest TimeZone places along with Google Charts for each unique country involved
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-ccglobal 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;
}
… 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!
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 …
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
“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
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 …
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 …
drop part of the events occurs in that originator parent and so several Javascript function called by the child reside in the parent … and …
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');
}
}
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');
}
}
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);
… 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 …
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 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();
}
}
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 …
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” ❓ ( ❓ ) type element …
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 '';
}
… 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” 🔎 ( 🔎 ) …
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>✈</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='🚢';
isub.setAttribute('data-type', '128674');
} else if (('' + curgd) == '128674') { // port
isub.innerHTML='✈';
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;
}
}
}
… 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
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 ✈ ( ✈ ) “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 changedcountries.phpweb 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 …
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');
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;
}
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 ( 🔎 ) 🔎 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;
… 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 {
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 🕐 🕐
2 o’clock is 🕑 🕑
12 o’clock is 🕛 🕛
2:30 is 🕝 🕝
11:30 is 🕦 🕦
12:30 is 🕧 🕧
… to show in “prompt” and “confirm” popup windows, as well as Map Chart maps … via new Javascript functions …
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 changedcountries.phpweb application you can also try below.
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]; }
}
}
…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
… 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.
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 …
… 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.
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 🇿🇦 🇿🇦 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
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.
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 …
… 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.
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
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
We want to be able to control the way a Google Charts Geo Chart can be nested within an HTML div element, for instance. We started the day wanting to be able to make …
a Google Charts Geo Chart be a background image to a div element … alas, on this first draft we couldn’t get there (but will continue with the research here) … whereas we succeeded …
adding the Google Charts Geo Chart interfacer’s resultant SVG data as the innerHTML (ie. content) …
<?php echo ”
function newbackin() {
if (dmyxhr.readyState == 4) {
if (dmyxhr.status == 200) {
if (dmyxhr.responseText) {
var m_t='image/jpeg';
var h_t='179';
var w_t='320';
var dbits = dmyxhr.responseText.split('\"height\": ');
if (dbits.length > 1) {
h_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"mime_type\": \"');
if (dbits.length > 1) {
m_t=dbits[1].split('\"')[0];
}
dbits = dmyxhr.responseText.split('\"width\": ');
if (dbits.length > 1) {
w_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"data\":');
dbits = dmyxhr.responseText.split('\"data\":');
if (dbits.length > 1) {
// replace all '_' with '/' and all '-' with '+' thanks to https://stackoverflow.com/questions/757675/website-screenshots
dgsbi='<img alt=\"Blog Posting Image\" style=\"width:' + w_t + 'px;height:' + h_t + 'px;\" width=' + w_t + ' height=' + h_t + ' src=\"data:' + m_t + ';base64,' + dbits[1].split('\"')[1].split('\"')[0].replace(/\_/g,'/').replace(/\-/g,'+') + '\"></img>';
//alert('dgsbi=' + dgsbi);
}
}
}
}
}
function ajaxit(urlin) {
if (urlin.length > 0) {
aurl=urlin;
if (window.XMLHttpRequest) {
dmyxhr = new window.XMLHttpRequest;
}
else {
try {
dmyxhr = new ActiveXObject('Msxml2.XMLHTTP');
} catch (othermicrosoft) {
try {
dmyxhr = new ActiveXObject('Microsoft.XMLHTTP');
} catch (failed) {
dmyxhr = false;
}
}
}
var xurl = 'https://www.googleapis.com/pagespeedonline/v1/runPagespeed?url=' + encodeURIComponent(urlin) + '&screenshot=true';
if (dmyxhr) {
dmyxhr.onreadystatechange = newbackin;
dmyxhr.open('GET', xurl, true);
dmyxhr.send(null);
}
}
}
function wbtoa(instris) {
var outstris=instris;
while (outstris.indexOf(String.fromCharCode(10)) != -1) {
outstris=outstris.replace(String.fromCharCode(10),'');
}
var xzs=prompt(outstris, outstris);
return outstris.replace(/\\\"/g, \"'\");
}
In that Nearest Places form part of the TimeZone places webpage, we noticed that even when shaping to enter a TimeZone Placename in the first textbox we made no attempt to fill in …
latitude
longitude
… when we have the information to do so, and even if we’re misunderstanding a placename designation, that should not stop us from trying, because those two numerical fields above can be corrected, and the form resubmitted, in these scenarios.
Apart from making a PHP derived Javascript variable be made available to the code, we “wrap” …
GeoJson World Countries Drag and Drop Makeover Nuance Tutorial
Nuance alert! We’re not sure if you noticed, but if you tried out the Drag and Drop functionality in the World Countries web application of yesterday’s GeoJson World Countries Drag and Drop Makeover Tutorial you may have noticed …
for a country with lots of TimeZone places, like Brazil, you could get a decent Google Chart Geo Chart map up … but if you were to click the “Map?” link down the bottom of that iframe …
it would fail to show a Google Chart Map Chart for that country’s TimeZone places
This fix, believe it or not, is interesting, perhaps only in an “internal use only” sense, we grant you. But our solution got us delving even more into hashtagging data, so that the solution we came up with was a hybrid whereby …
stayed with PHP $_GET[] (ie. address bar ? and & argumented) data (versus using PHP $_POST[] methodologies) … but …
where it came to the &data=[most of the data] part, other than its first character, we hashtagged the rest
… so that the logic flows as per usual, but we intervene at places and flesh it out via location.hash (client side only) means. We won’t bore you too much with all the places of intervention except the receiving map.php’s “easiest to get” intervention …
In addition to the Wikipedia information, at the very least, presented following a successful drag and drop operation, from today, we also start presenting a new HTML iframe element containing …
Nearest TimeZone places along with Google Charts for each unique country involved
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-ccglobal 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;
}
… 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!
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 …
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
“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
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 …
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 …
drop part of the events occurs in that originator parent and so several Javascript function called by the child reside in the parent … and …
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');
}
}
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');
}
}
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);
… 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 …
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 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();
}
}
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 …
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” ❓ ( ❓ ) type element …
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 '';
}
… 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” 🔎 ( 🔎 ) …
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>✈</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='🚢';
isub.setAttribute('data-type', '128674');
} else if (('' + curgd) == '128674') { // port
isub.innerHTML='✈';
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;
}
}
}
… 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
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 ✈ ( ✈ ) “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 changedcountries.phpweb 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 …
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');
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;
}
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 ( 🔎 ) 🔎 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;
… 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 {
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 🕐 🕐
2 o’clock is 🕑 🕑
12 o’clock is 🕛 🕛
2:30 is 🕝 🕝
11:30 is 🕦 🕦
12:30 is 🕧 🕧
… to show in “prompt” and “confirm” popup windows, as well as Map Chart maps … via new Javascript functions …
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 changedcountries.phpweb application you can also try below.
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]; }
}
}
…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
… 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.
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 …
… 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.
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 🇿🇦 🇿🇦 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
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.
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 …
… 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.
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
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
… so from that … let’s see … we get The Four TopsPipsP’s (in relation to lemon trees … tee hee).
Well, to make Moon Angles and Astronomy Helper web applications work on AlmaLinux we needed to attend to a couple of install matters …
install the Python package manager Pip via (the AlmaLinux PHP dnf package manager) …
dnf install python3-pip.noarch
install the Python module, helping out with Astronomy matters, called PyEphem via …
sudo pip install pyephem
… to be in a position to progress, and then add a check for existence of /usr/bin/python (for AlmaLinux) in preference to /usr/local/bin/python3.3 (for CentOS) as the final measure needed to start getting results back to any PHP overseers …
PHP/Javascript/HTML and Python PyEphem Moon Angle Tutorial
A long time ago now we presented PHP/Javascript/HTML Moon Angle Tutorial but now we feel is a good time for it to get a makeover. Why is that? Well, we have the relatively new discovery of the very useful Python PyEphem module to thank for that.
As far as the new version goes we now offer a Moon Angle “time of transit (and relative to now)” calculation, which we think will be of more use to users.
The Moon Angle at Transit calculations again involve the Sublunary Point as the position on Earth directly above which is Moon is located. Add in the Python PyEphem work and some more terminology that may interest you could involve …
The Moon Angle web application live run, which we welcome you to try, has the underlying PHP moon_angle_now_at.php that supervises the Python exemplied above, and which featured these changes to make this happen.
Here is a tutorial that calculates Noon Moon Angles from Earth via the entry of Latitude and Longitude. The calculation of noon moon angles uses the concept of Sublunary Point, which is explained in the quote from Wikipedia below.
The sublunary point (Latin sub-lunar, under the moon ‘, from Latin) is the one place on earth where the moon in exactly the zenith is. He is the point where the line connecting the centers of the Earth and Moon intersects the Earth’s surface. The point is a common auxiliary point to observe the Moon in the celestial mechanics and astronomical phenomenology , and in particular the theory of the tides , as well as the theory of eclipses .
The calculation of the point corresponding to the determination of the geocentric coordinates of the Moon, it has the same longitude and latitude , as the moon astronomical length and width has – both are denoted by Ο and Ξ².
The Javascript embellishments in this tutorial mainly revolve around:
Here is a link to a live run. (The way it changed on 1/12/2013 to have a dropdown of placenames as extra functionality will be explained in a tutorial called PHP/Javascript/HTML Geographical Placename Integration Tutorial on 3/12/2013.)
Here is a link to some downloadable PHP programming source code which you may want to rename to moon_angle_now_at.php
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.
Today we start down the road adjusting our inhouse Daylight Saving Time PHP web application from the previous PHP (starting with a 5) invocation at Daylight Saving Time Iframe Sandbox Tutorial for an AlmaLinux PHP (starting with an 8) friendly one.
Mainly here, we are decoupling our absolute URL logic hardcoding of rjmprogramming.com.au with that hybrid absolute meets relative means by which we can call our Google Chart inhouse Map Chart interfacer, for example, via …
/PHP/Map/map.php
… rather than …
https://www.rjmprogramming.com.au/PHP/Map/map.php
… suiting this “DNS changeover period” as well as ongoing “when AlmaLinux becomes the DNS rjmprogramming.com.au incarnation period” equally. It might even suit your local web server testing if you play your cards right (ie. keeping the $_SERVER[‘REQUEST_URI’] folder structure), sometimes making use of $_SERVER[‘SERVER_NAME’] along the way, in changes we made to daylight_saving_time.htmllive run (old way) or live run (new way) (helped out by a moderately changeddaylight_saving_time.php).
… URL up at the web browser address bar. This can happen when combining HTML iframe “srcdoc” attribute usage (possibly in combination with “src” attribute usage). The research we undertook into this issue got us to …
… got us to a moderately changed to daylight_saving_time.php adding a “sandbox” attribute to the “mapif” HTML iframe we use with both “srcdoc” and “src” navigational attributes as per …
Also to improve, am sure you’d agree, is to change those large scale Geo Charts we could present when the user picks TimeZone Places all within the one continent, from being navigated to in a popup window.open window, to being sent in an HTML “src” attribute iframe element (for short URLs) and to HTML “srcdoc” attribute iframe element via Ajax/FormDate methodologies (for longer URLs (ie. those too long for web address bar URLs)).
There is a global Google Charts capability to accept the tooltip content here as being in HTML form, as an alternative to (the default) text form.
Why bother with HTML tooltips? Well, it is the way to lessen the width (at the expense of height) of the Geo Chart tooltip displays, which we’re housing in a relatively small (but scrollable) HTML iframe element. Rather than ask for more width from the HTML iframe, let’s see whether we can make these HTML tooltips happen. For this, we need to meet the issue from two sides, those being …
the bulk of the task lies with getting our interface to Google Chart Geo Chart to be able to accept HTML as input with the “data” item … by involving the option …
var options = {
tooltip: {isHtml: true},
legend: 'none'
};
… and then adding an additional Geo Chart data column accepting that data as a <p> paragraph </p> element as per …
data.addColumn({'type': 'string', 'role': 'tooltip', 'p': {'html': true}});
… then a data item snippet like …
The rain<p>in Spain<p>falls mainly<p>on the plain.
the sender of the message needs to massage the “data” item it sends as per …
zform.append('data', newdatabit.replace(/\+/g,' ').replace(/\ in\ /g, ',"<br>in ').replace(/\ on\ /g, '<br>on ').replace(/\~\,/g, '"~,'));
Along the way, as you may notice with the differences link just above, we also added code whereby the user does not always have to (perhaps reach “above the fold”) to instigate this Geo Chart (with clock emojis) functionality by adding more navigational “smarts” within those “fixed” elements by allowing the user to click on the emoji clock (now effectively button) elements within the Android “toast” “fixed” ones, as an alternative way to make this happen. Users in the know will save on “scroll time”, perhaps, this way.
we continue on adding emoji country flags into HTML select (dropdown) element innerHTML properties (ie. what you see) …and today while in the emoji mood …
we allow the user to choose to display the map data temporarily in the form of a Google ChartGeo Chart with emoji clock pointers, because the user chose to do so at a new dropdown element
The onmouseover and ontouchstart events appending to the datasets that make up these Google Charts can get quite verbose, you might know from practice. So our plan is to …
continue using the same HTML iframe hoster of the Geo Charts that the Map Charts use …
and when Map Charts require it because the GET URL lengths would become too big, we work it that an HTML form action=[MapChartURL] target=[theIframeName] method=POST makes it happen … the one additional codeline in that logic going (where variable mysrc contains that accumulated URL) …
if (mysrc.length < 800) {
document.getElementById('mapif').style.display='block';
document.getElementById('mapif').src=mysrc;
} else {
document.getElementById('mapif').title=mysrc;
document.getElementById('mapsubmit').click();
}
… the title property not held to those same restrictions to length as GET URL (address bar) navigations … and that is used below …
the Geo Chart logic is based around the global var thgm=false; initialized variable and this initialized HTML select (dropdown) element …
<select id=sthgm onchange=" if (this.value.length == 1) { thgm=false; } else { thgm=true; } if (thgm) { maptogeohere(); } "><option value=" ">Map Chart</option><option value=" ">Geo Chart (with clocks)</option></select>
… calling as user designated (and take a look out for the Ajax/FormData/IframeSrcdoc alternative approach to the navigation) …
var zhr=null, zform=null;
CSS property position: absolute; with heaps of praise as your friend getting overlay functionalities happening … but today a useful property for us is …
CSS property position: fixed;
What is the essential difference between position: absolute; and position: fixed;? Well, position: absolute; can be teamed with a …
CSS property left: [intoWebPageX]px;
CSS property top: [intoWebPageY]px;
… whereas position: fixed; can be teamed with a …
CSS property left: [fromScreenLeftX]px;
CSS property top: [fromScreenTopY]px;
… meaning that if you have a web page where scrolling is involved to clickable links “below the fold”, but you want to keep some aspects of the webpage constantly “above the fold” you may benefit from position: fixed; thinking. We have two bits to today’s Daylight Saving web application’s display that we want to apply this position: fixed; thinking to …
the HTML “Timezone Places Map” iframe … and, new to today’s work …
temporary HTML div (informational dialog box) reminding us of Android “toast” (to inform the user of a TimeZone place’s country name)
In both cases above we do not start out with any static HTML mentioning “fixed” anything? Do you know why? Some of you will have guessed correctly …
we wait for relative positioning to happen naturally …
and when we are interested, and the relevant HTML element exists …
we call out [thatElement].getBoundingClientRect().left (equates to CSS property left: [fromScreenLeftX]px;) and [thatElement].getBoundingClientRect().top (equates to CSS property top: [fromScreenTopY]px;) … in the case of the HTML iframe above … as per …
… give or take small pixel offsets, as good places to then freeze them on the screen henceforth. Or by coding (like for the Android “toast” looking temporary dialog boxes) …
… in an apt position the user will have decided by their (non-mobile only, alas, but see) hover (onmouseover event (logic created on the fly)) of one of the TimeZone place “a” links (flanked by the static emoji and dynamic country flag emoji)) …
asis[iasis].onmouseover=function(event) {
if (document.getElementById('result')) { document.getElementById('result').title='Timezone and Latitude and Longitude and Trip Planner functionalities (times as of screen creation)'; }
var jindex=eval('' + event.target.getAttribute('data-title'));
var thisx=0, thisy=0;
if (event.touches) {
thisx=event.changedTouches[0].clientX;
thisy=event.changedTouches[0].clientY;
} else if (event.clientX) {
thisx=event.clientX;
thisy=event.clientY;
} else {
thisx=event.pageX;
thisy=event.pageY;
}
if (('' + document.getElementById('custom-alert-1').style.position).toLowerCase().indexOf('absolute') != -1) {
document.getElementById('custom-alert-1').style.position='fixed';
document.getElementById('custom-alert-1').style.left='' + eval(40 + eval('' + thisx)) + 'px';
if (('' + window.scrollY) != '') {
document.getElementById('custom-alert-1').style.top='' + eval(-60 + eval('' + thisy) + Math.floor(eval(('' + window.scrollY).replace('px','') + '.0'))) + 'px';
} else {
document.getElementById('custom-alert-1').style.top='' + eval(-60 + eval('' + thisy)) + 'px';
}
}
inctyis=' in ' + beforectys[jindex];
var tzd = new Date(new Date().toLocaleString("en-US", {timeZone: event.target.title.split('~')[1].split('~')[0].split(' ')[0]}));
// 1 o'clock is 🕐
// 2 o'clock is 🕑
// 12 o'clock is 🕛
// 2:30 is 🕝
// 11:30 is 🕦
// 12:30 is 🕧
var bestemojicodepoint=0;
var hris=eval(('' + tzd).split(' GMT')[0].split(' ')[eval(-1 + ('' + tzd).split(' GMT')[0].split(' ').length)].split(':')[0]);
var spprefix='';
var spsuffix='';
if (hris >= 12) { hris-=12; spprefix="<span style='filter: invert(1); mix-blend-mode: difference;'>"; spsuffix="</span>"; }
var minis=eval(('' + tzd).split(' GMT')[0].split(' ')[eval(-1 + ('' + tzd).split(' GMT')[0].split(' ').length)].split(':')[1]);
var gmthris=hris;
var gmtminis=minis;
if (minis > 45 && hris == 12) {
bestemojicodepoint=128336;
} else if (minis >= 45) {
bestemojicodepoint=128336;
bestemojicodepoint+=hris;
} else if (minis <= 15) {
bestemojicodepoint=128335;
bestemojicodepoint+=hris;
} else if (minis > 15 && minis < 45 && eval(hris % 12) == 0) {
bestemojicodepoint=128359;
} else {
bestemojicodepoint=128347;
bestemojicodepoint+=hris;
}
document.getElementById('custom-alert-1').innerHTML=event.target.title.split('~')[1].split('~')[0] + ' <br>where time is <br>' + ('' + tzd).split(' GMT')[0] + ' ' + spprefix + String.fromCodePoint(bestemojicodepoint) + spsuffix;
document.getElementById('custom-alert-1').style.visibility='visible';
if (!within_hiding_fourfivehundred) {
within_hiding_fourfivehundred=true;
setTimeout(andt, hiding_fourfivehundred);
}
inctyis='';
if (eval('' + thisx) > eval(eval('' + beforerects[jindex].left) + eval('' + beforerects[jindex].width))) {
isok=2;
maxok=2;
event.target.click();
if (1 == 2) {
alert(beforectys[jindex] + ' will show on map to right');
}
}
};
// and then for mobile platform …
asis[iasis].ontouchstart=asis[iasis].onmouseover;
… a lot of what else we’d like to explain encapsulated in colour coded code above, that is placed in “the bigger picture” before where we add those dynamically placed emoji country flags.
asis[iasis] represents a member of an array of “a” links that could “nest into …
var asis=document.getElementsByTagName('a');
for (var iasis=0; iasis<asis.length; iasis++) {
// member of an array of "a" link logic goes here
}
user’s event object gets passed in, the most interesting data member being “event.target” that is the “a” HTML element object that got hovered over
var jindex=eval(” + event.target.getAttribute(‘data-title’)); represents the index into the saved array of [element].getBoundingCliuentRect() result sets for each “a” link that contains a “/” (yes, those are TimeZone Place “a” elements) … helped out by the preceeding …
// part of member of an array of "a" link logic ... where "tcic" is a two letter ISO country code ...
asis[iasis].style.textAlign='left'; // we want it left aligned because we can then tell whether the user hovered over the "yet to be placed" emoji country flags ... via ... if (eval('' + thisx) > eval(eval('' + beforerects[jindex].left) + eval('' + beforerects[jindex].width))) { alert('Yes, they did hover over the emoji country flag'); }
beforeids=asis[iasis].id;
beforerects.push(asis[iasis].getBoundingClientRect()); // relevant to previous comment
if (selceqih.indexOf('"' + tcic + '">') != -1) {
ctynameis=selceqih.split('"' + tcic + '">')[1].split('<')[0].replace(/\"/g, '`').replace(/\'/g, '`');
beforectys.push(ctynameis);
asis[iasis].setAttribute('data-title', '' + eval(-1 + beforerects.length)); // allow var jindex=eval('' + event.target.getAttribute('data-title')); above, work
// more member of an array of "a" link logic here
}
// end of part of member of an array of "a" link logic
var tzd = new Date(new Date().toLocaleString(“en-US”, {timeZone: event.target.title.split(‘~’)[1].split(‘~’)[0].split(‘ ‘)[0]})); gets the datetime at relevant TimeZone place (of “a” link) now
code to work out nearest quarter hour clock emoji we can append to Android “toast” dialog box, along with that timestamp wording, based on some PHP code we developed when we presented TimeZone Country Places Emoji Tutorial
code to create and set up the later self destruction of the Android “toast” like dialog box …
code above … and this …
document.getElementById('custom-alert-1').innerHTML=event.target.title.split('~')[1].split('~')[0] + ' <br>where time is <br>' + ('' + tzd).split(' GMT')[0] + ' ' + spprefix + String.fromCodePoint(bestemojicodepoint) + spsuffix;
document.getElementById('custom-alert-1').style.visibility='visible';
if (!within_hiding_fourfivehundred) {
within_hiding_fourfivehundred=true;
setTimeout(andt, hiding_fourfivehundred);
}
self-destroyed by …
function andt() {
within_hiding_fourfivehundred=false;
document.getElementById('custom-alert-1').innerHTML="";
document.getElementById('custom-alert-1').style.visibility='hidden';
}
event.target.click(); as nested in if (eval(” + thisx) > eval(eval(” + beforerects[jindex].left) + eval(” + beforerects[jindex].width))) { alert(‘Yes, they did hover over the emoji country flag’); } “the user hovered over the emoji country flag” then we join in the old “onclick” logic of the “a” link that can populate the HTML iframe with a Google ChartsMap Chart
then, as an idea to allow for mobile platform functionality we tried …
asis[iasis].ontouchstart=asis[iasis].onmouseover;
… placed after the big onmouseover event logic definition above, and (the use of) it more or less worked on mobile
var proposedstyle='';
for (var iasis=0; iasis<asis.length; iasis++) {
if (('' + asis[iasis].id).indexOf('/') != -1) {
if (seleih.indexOf(asis[iasis].id) != -1) {
tcic=seleih.split(asis[iasis].id)[1].split(',')[3];
if (tcic.length == 2) {
proposedstyle='<style> a[title*="' + asis[iasis].id + '"]:after { content: "' + orflag(tcic) + '" } </style>';
if (document.getElementById('dstyle').innerHTML.indexOf(proposedstyle.split('{')[0]) == -1) {
document.getElementById('dstyle').innerHTML+=proposedstyle;
}
}
}
}
}
… placed after the onmouseover and ontouchstart event logics above, can be a means by which some Javascript code can differentiate the position of a hover, and act accordingly in a Javascript DOM event logic environment.
Daylight Saving Time Emoji Country Flag TimeZone Tutorial
Today’s solution to a “smallish” display non-mission-critical issue regarding a web application (adding some “emoji country flags” to augmenting some pre-existant static CSS emoji arrangements embellishing a list of TimeZones in the web application of SVG Network Digital and Analogue Clock Local Time Tutorial) is being published because it combines a lot of interesting Javascript and CSS concepts we’d like to share, some of those being …
the content assisted by a new static string (via an invisible static HTML select element …
<select id="sele" style="display:none;"><option value="Africa/Abidjan" data-geo="5.31666,-4.03334,GMT,CI,+0">Africa/Abidjan</option><option value="Africa/Accra" data-geo="5.55,-0.21667,GMT,GH,+0">Africa/Accra</option><option value="Africa/Addis_Ababa" data-geo="9.03333,38.7,EAT,ET,+3">Africa/Addis_Ababa</option> ... </select>
iframe (as target of an HTML form element submission event) onload event … our “user on call” “intervention point” (=”function postipetc()”) (always an important integration consideration) … logic (as its last clause)
… that “at call” call (by us) saving bandwidth versus less speedy during execution “trade-off” decision we made on this project (because of new array above, there got to be this two-pronged approach choice available, forsaking a traversal of this new array to pre-populate the HTML div element (initially statically set to …
<div id="dstyle" style="display:none;"></div>
… mentioned above)).
Another reason for this decision, as well, for us, is due to our deep admiration for …
Javascript "adding on the fly" CSS styling into the webpage HTML
For us, it seems to always work, even for “after the HTML creation”, it seems to us, as in today’s work. We’re not going to test it, but perhaps the :after selector CSS is what’s helping out here, but anyway,it works.
Let’s let you know that the new Javascript to create the content consists of …
function orflag(thiscc) { // in is ISO 2 character code and return is String.fromCodePoint() derived emoji country flag CSS content
var ccchar="", ccsuff="";
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"];
for (var iccsuff=0; iccsuff<thiscc.length; iccsuff++) {
ccchar=thiscc.substring(iccsuff, eval(1 + eval('' + iccsuff))).toUpperCase();
ccsuff+=String.fromCodePoint(eval('' + dri[eval('' + lri.indexOf(ccchar))])); //'' + dri[eval('' + lri.indexOf(ccchar))] + ';';
}
return ccsuff;
}
function postipetc() { // called off the iframe onload event which represents a "user on call" intervention
var proposedstyle='', tcic='';
var asis=document.getElementsByTagName('a');
for (var iasis=0; iasis<asis.length; iasis++) {
if (('' + asis[iasis].id).indexOf('/') != -1) {
if (seleih.indexOf(asis[iasis].id) != -1) {
tcic=seleih.split(asis[iasis].id)[1].split(',')[3];
if (tcic.length == 2) {
proposedstyle='<style> a[title*="' + asis[iasis].id + '"]:after { content: "' + orflag(tcic) + '" } </style>';
if (document.getElementById('dstyle').innerHTML.indexOf(proposedstyle.split('{')[0]) == -1) {
document.getElementById('dstyle').innerHTML+=proposedstyle;
}
}
}
}
}
}
local time … and today we remedy that, as well as …
improve a loading logic weakness that could result in our SVG object element returning null … and …
add an onhover Country Name piece of display data
Leaving out the fairly straightforward last change above, let’s go into more detail regarding those other improvements.
local time …
the issue here being that a “local time” is awkward, but possible (thanks to this useful link) in serverside PHP …
<?php
if (strpos(('' . $_SERVER['QUERY_STRING']), "localtime") !== false) {
echo "<html><body><div id=mydiv></div><script type='text/javascript'>
var asuff='" . $midbit . $csuff . "';
var adate = new Date();
var dow=['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
var his=eval('' + adate.getHours());
var mis=eval('' + adate.getMinutes());
var sis=eval('' + adate.getSeconds());
var ssuff='';
if (('' + adate).indexOf(' GMT') != -1) { ssuff=' GMT' + ('' + adate).split(' GMT')[1]; }
if (1 == 1) {
document.getElementById('mydiv').innerHTML=dow[eval('' + adate.getDay())] + ' ' + ('0' + his).slice(-2) + ':' + ('0' + mis).slice(-2) + ':' + ('0' + sis).split('.')[0].slice(-2) + ' ' + ('0' + adate.getDate()).slice(-2) + ' ' + ('0' + eval(1 + eval('' + adate.getMonth()))).slice(-2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + ('' + adate.getFullYear()) + ' ' + ssuff + asuff;
} else {
document.write(dow[eval('' + adate.getDay())] + ' ' + ('0' + his).slice(-2) + ':' + ('0' + mis).slice(-2) + ':' + ('0' + sis).split('.')[0].slice(-2) + ' ' + ('0' + adate.getDate()).slice(-2) + ' ' + ('0' + eval(1 + eval('' + adate.getMonth()))).slice(-2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + ('' + adate.getFullYear()) + ' ' + ssuff + asuff);
}
</script></body></html>";
}
?>
… but patently more suited to the Javascript available to us with the “HTML supervisor” client facing web application component as per …
var loct='';
var adate = new Date();
var dow=['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
var his=eval('' + adate.getHours());
var mis=eval('' + adate.getMinutes());
var sis=eval('' + adate.getSeconds());
var ssuff='';
if (('' + adate).indexOf(' GMT') != -1) { ssuff=' GMT' + ('' + adate).split(' GMT')[1]; }
var curts=dow[eval('' + adate.getDay())] + ' ' + ('0' + his).slice(-2) + ':' + ('0' + mis).slice(-2) + ':' + ('0' + sis).split('.')[0].slice(-2) + ' ' + ('0' + adate.getDate()).slice(-2) + ' ' + ('0' + eval(1 + eval('' + adate.getMonth()))).slice(-2).replace('01','Jan').replace('02','Feb').replace('03','Mar').replace('04','Apr').replace('05','May').replace('06','Jun').replace('07','Jul').replace('08','Aug').replace('09','Sep').replace('10','Oct').replace('11','Nov').replace('12','Dec') + ' ' + ('' + adate.getFullYear()) + ' ' + ssuff;
var loct='';
if (tz.indexOf('localtime') != -1) { loct=curts; }
… and, as such, to continue the process every second to update our clock, we set in play a navigational technique we’d never put into play before that we can remember, that being …
// Rather than reloading content in an HTML iframe element via [iFrameElementObject].src=[URLtoUseAsContentForTheIframe]; ... for SVG object ID=myclock we ...
function getmelt(iois) {
if (iois != null) {
var aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
if (tz.indexOf('localtime') != -1) {
if (aconto.body.innerHTML.indexOf('</div>') != -1) {
loct=aconto.body.innerHTML.split('</div>')[0].split('>')[eval(-1 + aconto.body.innerHTML.split('</div>')[0].split('>').length)];
} else {
loct=aconto.body.innerHTML;
}
if (uprefix.indexOf('&both=') > 0) {
uprefix='&both=' + uprefix.split('&both=')[1];
} else if (uprefix.indexOf('&analogue=') > 0) {
uprefix='&analogue=' + uprefix.split('&analogue=')[1];
} else if (uprefix.indexOf('&y=') > 0) {
uprefix='&y=' + uprefix.split('&y=')[1];
} document.getElementById('myclock').data="svg_clock.php?timezone=" + encodeURIComponent(tz + loct) + uprefix;
setTimeout(morelt, 1000);
}
}
}
}
}
function morelt() {
document.getElementById('loces').src='cldate.php?localtime=' + Math.floor(Math.random() * 198765433);
}
// ... in an onload event function for an HTML iframe element, presenting "enough of a flag" to tell the other web application components we are wanting local time
… the downside being that the SVG tends to flash a bit (with activity)
improve a loading logic weakness that could result in our SVG object element returning null …
we actually had flawed code up until today not doing its job, but today the “HTML Supervisor”‘s “setTimeout(checkmyobject,4000);” checker of existence of the SVG object is better for …
function checkmyobject() {
if (!document.getElementById('myclock')) { window.location.reload(true);
} else {
var t=document.querySelector("#myclock");
var htmlDocument=t.contentDocument;
if (('' + htmlDocument).replace(/\<p\>/g,'').replace(/\<\/p\>/g,'').replace(String.fromCharCode(10),'').replace('null','') == '') { window.location.reload(true); }
}
}
… in its approach to, for the first time we can remember doing, refreshing the webpage programmatically via “window.location.reload(true);” to eventually get such failed initially loaded SVG object data to succeed
SVG Network Digital and Analogue Clock Daylight Saving Tutorial
We were pondering how best to “value add” onto the web application of our recent SVG Network Digital and Analogue Clock Tutorial when we remembered that the “star codeline” of that project’s “PHP (Digital Clock) content helper” was …
<?php
$thedate=date('l H:i:s d M Y e');
?>
… and we leave this to be …
<?php
$thedate=date('l H:i:s d M Y e I~') . $emflag;
?>
… after today’s two streams of work …
flag Daylight Saving clock times having noticed researching PHP date function’s “date ( string $format [, int $timestamp = time() ] ) : string” first format parameter the interesting switch …
format character
Description
Example returned values
I (capital i)
Whether or not the date is in daylight saving time
… as a means by which its compadre web application components can be helped out recognizing Daylight Saving clock (time) scenarios
add some Internationalization improvement by adding some emoji flag improvements getting to this project’s “PHP (Digital Clock) content helper” as that $emflag variable as per …
<?php
$emflag="";
if (isset($_GET['emflag'])) {
$emflag=" " . urldecode($_GET['emflag']);
}
?>
… and instigated back at the HTML supervisor as per …
var froms='youllneverfindthis';
var tos='';
var prevfromefs='youllneverfindthis';
var fromefs='youllneverfindthis';
var toefs='';
var isotwois='';
var flagbit='';
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 ourtzlist="<option value=\"Africa/Abidjan\" data-geo=\"5.31666,-4.03334,GMT,CI,+0\">Africa/Abidjan</option><option value=\"Africa/Accra\" data-geo=\"5.55,-0.21667,GMT,GH,+0\">Africa/Accra</option><option value=\"Africa/Addis_Ababa\" data-geo=\"9.03333,38.7,EAT,ET,+3\">Africa/Addis_Ababa</option><option value=\"Africa/Algiers\" data-geo=\"36.78333,3.05,CET,DZ,+1\">Africa/Algiers</option><option value=\"Africa/Asmara\" data-geo=\"15.33333,38.88333,EAT,ER,+3\">Africa/Asmara</option><option value=\"Africa/Bamako\" data-geo=\"12.65,-8,GMT,ML,+0\">Africa/Bamako</option>"]; // etcetera etcetera etcetera
var parts;
var tz=location.search.split('timezone=')[1] ? decodeURIComponent(location.search.split('timezone=')[1].split('&')[0]).replace(/\+/g,' ') : '';
if (tz != '') { froms=location.search.split('timezone=')[1].split('&')[0]; parts=ourtzlist.split(decodeURIComponent(froms)); if (parts.length > 1) { isotwois=parts[1].split(',')[3]; if (isotwois.replace(/\?/g,'').length == 2) { if (document.URL.indexOf('?') != -1) { fromefs=encodeURIComponent(orccflag(isotwois.toUpperCase())); flagbit='&emflag=' + fromefs; } else { fromefs=encodeURIComponent(orccflag(isotwois.toUpperCase())); flagbit='?emflag=' + fromefs; } } } }
var uprefix=('?' + (document.URL + flagbit + '?#').split('#')[0].split('?')[1]).replace('?','&').replace('timezone=','prtimezoneev=').replace('emflag=','premflagev=').replace(froms,tos).replace(fromefs,toefs).replace('prtimezoneev=&','').replace('premflagev=&','');
Another approach to a Digital Clock web application is off and running today, reminiscent of some of the concepts in Digital Clock Styling for Daylight Saving Tutorial. Today’s difference though is a significant one involving …
… and we’d like to thank The PHP Anthology (Volume II: Applications) by Harry Fuecks (ISBN: 0-9579218-4-5) for the inspiration of methodologies used. We are encouraged to move on from this “proof of concept” beginning because we are enthusiastic to build on the very simple SVG text element beginnings with more advanced work in future blogs.
Feel free to try it yourself here or look below at (initially) a GMT digital clock …
Digital Clock Styling for Daylight Saving Tutorial
The Daylight Saving Time web application we talked about with Daylight Saving Time TimeZone Emoji Tutorial starts out showing a digital clock displaying the time at various Timezone Places around the world. It occurred to us, this morning, looking over at the alarm clock (also digital) that it would be interesting to recreate that look, at least a bit. But what immediately seemed appealing was not any photography or media based work, but the really simple CSS idea to make use of …
HTML table elements containing …
two tr row elements per numeral (or digit) of time to define … each row containing a …
single td cell with the content (ie. non-breaking space) … but working to define the numbers via the correct combinations of use of …
… reminding me of how incredible it is that just a few lines can be used by artists to have a viewer know exactly what they were depicting. More with faces, mind you, but even so, is an interesting part of how our brains work.
The code changes just involve a new array in the Javascript as per …
The last time we added a change to our Daylight Saving TimeZone Information web application was during our Other Side of the World Google Chart Tutorial work, and then, so many things were going on with interfacing and integration we didn’t really revisit the Daylight Saving TimeZone Information web application for itself, as it is so good to do every now and then to …
incorporate new things you’ve learnt
check on functionality that has fallen by the wayside (or, sin of all sins, never got to the wayside)
Our recent perusals around the “wooooooooorrrrllldd” of CSS3 reminded us of the power of CSS Selectors, especially the ones that came in with CSS3. The “two out of three” that we use today are described on today’s tutorial picture as well as we’d want to do here … so here goes …
Featuring
CSS3 Selectors for element attributes
^= (starts with)
*= (contains)
$= (ends with … not used)
Thanks to
When you try to memorize syntax do you look for “linkages”? Yes, you can find anything on the net. But seriously, this CSS syntax can be remembered because it uses the characters of a lot of RegEx wildcarding systems. Another kind of “linkage” we saw between this CSS(3) Selector functionality, and where it would be useful, was that we’d noted that TimeZone identifiers have a name, and generally they take a form …
Region/Placename
… and that Region (plus or minus the “/“) felt to us like a good candidate for functionality enhancement via the CSS(3) Selectors above. You can judge for yourself way below, or by clicking today’s tutorial picture.
Three other “popular” items around here, featuring, today, are …
emojis (some of which were multiple and so we wish to rethank Iemoji (multiple HTML entity information) and FileFormat Information (normal HTML entity information) and Emojipedia (finding them) for these emoji lookups, as well as the CSS syntax help from this webpage (needed, because, unbelievable to us, we’d only ever done this with Javascript, in the past)), and their role to help internationalization of your web applications, and to add a bit of colour, and cuteness
HTML element alt attribute when a data busy HTML select element option tag (the tag that we get most satisfaction from, making busy … it has to be said) is already using …
title
value
text (or innerHTML)
… and you come along later not wanting to clobber any good logic looking for another “data place” to populate with your new “data item” (of interest) … well, that could be the “oft neglected except perhaps with the HTML img tag” alt attribute
Apart from that alt logic above the changes we made to daylight_saving_time.html amounted to changes adding in new CSS(3) Selectors (that just happen … the joy of CSS, we guess) …
Google Charts provides great chart tools for various purposes. We already use the Map Chart heavily with our “Other Side of the World” recent web application we last talked about below with Other Side of the World Continents and Countries Tutorial. This is no surprise given the geolocation and positioning and mapping aspects to the project. But also apt is the …
Geo Chart which displays nominated countries and/or regions on a world map … and we also thought it would be good to call on a …
Bar Chart can display Great Circle distances between places the user identifies during an execution of the “Other Side of the World” web application session
Over time you get a feeling for what Google Chart suits what scenario of data arrangements. Perhaps, here it would be instructional to read Google’s miscellaneous examples if you are wondering what is possible with this great software suite. We interface to many of these charts here at this blog and to read through these you could visit this link.
Today, as far as this “feel” goes, we’d like to admire how the Google Chart “way” of being housed in an HTML div element leads itself to scaling ready for smaller format devices, so long as you, the coder above, “play the game” so to speak, trying to specify percentage values for HTML element width properties where possible. This came to play with today’s Google Chart Bar Chart “fitting in” with right column table cell (td) widths with today’s “Other Side of the World” web application.
These two code files were involved in these changes …
Other Side of the World Continents and Countries Tutorial
Today, as with WordPress 4.1.1’s Other Side of the World Continents and Countries Tutorial, we came across a scenario where there was a tiny advantage being Australian, in that our “Continent Name” equals our “Country Name” … which I suppose you could argue for Antarctica, maybe?!
We were amused by this at school, and now have the first real world “application” of this in that today’s task, much harder than we thought it would be, was, with our “Other Side of the World” web application (we last talked about with Other Side of the World Timezone Tutorial as shown below) to link the two bigger data sets up the top of the list of three datasets going on with our application …
The Weather Underground tends to talk about Placenames and their Country Names whereas the PHP TimeZones tend to link Placenames with Continent (or region) Names … and the missing link between these two talking to each other a good percentage of the time involves the PHP’s getLocation() functionality we’ve talked about before as a method for the DateTimeZone class, along with some 2 character ISO Country code information you can read more about at //stackoverflow.com/questions/17842003/php-intl-country-code-2-chars-to-country-name.
Making this link has lots of benefits …
our “closest to” lists now have more data to call on (supplementing the last of those datasets above) … and …
we can now allow for searches by Country on that top HTML select (dropdown) element …
we can fill in more specified places now that we can add in a Country so often and often reach a point with the Weather Underground scrutiny where there is only one returned value, at which point, some days ago, we added logic to say just go and display this on the Map Chart and the rest straight away to save some time
people tend to think of a Country when they think of a place, at least on dry land, that is.
An unusual code threesome were involved in these changes …
… that last one getting a new calling method just for this purpose of …
take a placename
take its inputted Continent (or region) Name … and …
have the supervisory codes send to tz_places.php a $_GET[] call for the purpose of …
using getLocation() functionality find a 2 character ISO Country Code … and …
link this to a full Country Name via various arrays we have in the PHP … and finish off by …
sign off the PHP (in an HTML iframe element) by changing relevant fields in top.document or parent.document DOM HTML elements via a simple HTML body onload event method of making those changes and exiting the called PHP … allowing for …
supervisors pick up their usual logic flows once we have a Placename,CountryName textbox scenario going
Of course “Australia” the continent name being the same as “Australia” the country name saves so much time I’ll think I’ll have that cup of coffee now. See ya!
We’ve got a lot of “where” functionality mixed in with “when” functionality with our “Other Side of the World” web application. To us, the easiest way to link “where” and “when” in the world is the concept of Earth’s timezones. We spent a lot of time looking into this with the blog thread ending with TimeZone Country Places Data Tutorial and then it went through another makeover when we tackled its integration into the Weather Underground API inspired blog postings culminating in Weather API via Iframe jQuery Ajax AutoComplete Tutorial
Reading down that first blog posting thread to its “Primer Tutorial” …
… you can see how good PHP is, natively, with timezones, and how approachable all this is for all PHP programmers … exciting stuff!
And there’s another series of blog postings culminating in HTML/PHP Timezone Feed Function Keys Tutorial involved in this same realm of thinking, that we use today (as with WordPress 4.1.1’s Other Side of the World Timezone Tutorial), along with the … you guessed it … Client Pre-emptive Iframe technique approaches to making use of what it helps with. Integrating “what it helps with” is quite interesting, and quite “Ajax”y in its ways. We call its supervisory web application into a new HTML iframe element of our “Other Side of the World” web application and pluck out only its initial “Digital Clock” HTML table display, and transfer that HTML table and its modified CSS styling over into a new HTML div element within the “Other Side of the World” web application, initially style “display:none;” but capable of being called into play via the user selecting an “Hour Time of the Day” value of interest, that causes the iframeddaylight_saving_time.html (changed this way).
What the user sees/does to use this new functionality looks like …
they see the “Hour of the Day” dropdown
they select an “Hour of the Day” value of interest … in today’s tutorial picture, here in AEST Australia (around about 8pm) we chose “07am”
the “Digital Clock” flashes random current times of day from around the “TimeZone Places World” and when it lobs onto the first matching time between 07am and 08am (exclusive) …
that matching “TimeZone Place” is copied into the “Other Side of the World” Place textbox … and …
the “Weather Underground” (autocomplete) database is scrutinized for a match, and if so …
the Map Chart shows the matched place as the base position along with its “Other Side of the World” counterpart, allowing for Google Chart click/touch (its “select”) event logic to allow for other functionalities that include a Weather link and TimeZone information (with a current time of day) … along with the …
usual “packdrill” for TimeZone Places iframe table cell and YouTube API Embedded iframe table cell in the (now) bottom row of the “Other Side of the World” (big) table element
The code for these TimeZone integration changes, building on yesterday’s Other Side of the World Map Chart Styling Tutorial, remains as just HTML (apart from the supervised PHP for the Google Chart interface) that you could call other_side_of_the_world.htm (changed in this minor way for our HTML supervisor of additional TimeZone functionalities today) if you like, or want to try out (or try out for a specific (argument) location, such as Tokyo try out), again. The Client Pre-emptive Iframe reading and use of PHP continues today, making great use of its TimeZone code talents.
Other Side of the World Map Chart Styling Tutorial
You’ll have noticed with our “Other Side of the World” web application of recent days … or maybe not, if you were dozing off … how much use we make of the Google ChartsMap Chart functionality. Can’t do without it … thanks, Google.
Now if you go to read about the Google Chart Map Chart you may end up at this very interesting webpage, and even if it was some time back you were last there, it is always worth checking back in every now and then for information on changes, or to reread this functionality rich software suite documentation. We only touch the “tip of the iceberg” with the way we interface to Google Charts, but today we want to take some small steps to improving on that. Even taking small steps, when dealing with a third-party product so rich in possibilities, we try to be generic and be able to offer more to those adventurous users out there, both laptop and mobile (at least iPad only at this stage) users.
So we have set up a way for iPad users using the mobile app to be able to save their comma prefixed …
localized Google Chart Map Chart styling ideas from here when they interactively enter Map Chart criteria … and for …
all users of our “Other Side of the World” web application get an additional non-default Google Chart Map Chart “Styled Map” tab (in addition to their default “Map” and “Satellite” tabs), and some non-default Google Chart Map Chart icons … thanks to Icons-Land here
The new icons for that latter scenario also feature a different icon showing after a “pin” icon is selected (ie. the Google Chart click/touch “select” event is called into play).
The code for these Map Chart changes remains as just HTML (apart from the supervised PHP for the Google Chart interface) that you could call other_side_of_the_world.htm (changed in this minor way for our HTML supervisor of Google Chart Map Chart purposes today) if you like, or want to try out (or try out for a specific (argument) location, such as Beijing try out), again. The major changes related to in the Google Chart Map Chart web application PHP you could call map.php, and which changed in this way, starting down our long path towards more Map Chart styling possibilities.
Our “Other Side of the World” web application we’ve been developing lately has made extensive use of the HTML iframe element, mainly as a “reader” of data in that Client Pre-emptive Iframe technique way. But the HTML iframe element is probably better known for its data integration and display talents, and it is these that we call upon today, to (software) integrate two other existing sources of data so that, display-wise we have four table (td) cells in play now those being the original …
top left cell where the user interacts to show latitude and longitudes and placenames of interest
top right cell showing these “latitude and longitudes and placenames of interest” paired with their “Other Side of the World” counterparts within a Google ChartsMap Chart (software) integration … and, as of today we add into the equation …
bottom left cell where depending on whether we’ve derived our “latitude and longitudes and placenames of interest” from …
bottom right cell where the user’s “latitude and longitudes and placenames of interest”‘s “Other Side of the World” interfaces to the useful webpage (thanks) HTML select (dropdown) element results in an HTML iframe element hosting a YouTube Embedded Video in Iframe API RegEx Tutorial ‘s YouTube (embedded) video integrator
We now think the use of all this can have you hopping around the world discovering lots of geographical based, video based and timezone based information about lots of places around the world, lots of which you may never have known much about.
We’ve software integrated today, as well as integrated “where” web application thoughts with “when” web application thoughts.
Another feature of today’s changes involves the geolocation features of the Javascript …
The code for this remains as just HTML (apart from the supervised PHP for the Google Chart interface) that you could call other_side_of_the_world.htm (changed in this way for our HTML iframe (software) integration purposes today) if you like, or want to try out (or try out for a specific (argument) location, such as Alice Springs try out), again. It also required small changes to …
The onblur event in web programming is a very important event regarding interactive keyboard entries made by the user. We base new functionality, today, with our “Other Side of the World” web application, by allowing a user who enters in their own “place” information, can have that information filtered through the same “autocomplete” database provided by the wonderful Weather Underground and its great API service.
When we presented the last Weather Underground related blog posting we even used this functionality also interfacing to the onkeyup keyboard event, making it look up the database after just a few characters typed into the associated HTML input type=text text box, but today we lessen the interaction, presuming the user knows a location of interest and will only want information after tabbing out of this text box … hence the onblur event, only, logic interface to new functionality that creates an additional HTML select (dropdown) element of use to populate those same …
So that’s the idea, but making it happen involved some tweaking of the parts to the Weather Underground blog posting Weather API via Iframe jQuery Ajax AutoComplete Tutorial from some time back, the changes for which we’ll explain later.
Again we call on Client Pre-emptive Iframe techniques for this, telling us that you can just keep on adding HTML iframe elements to make this technique happen for several different sources of information, as necessary.
The code for this is just HTML (apart from the supervised PHP for the Google Chart interface) that you could call other_side_of_the_world.htm (changed in this way for our onblur event purposes today) if you like, or want to try out (or try out for a specific (argument) location, such as Darwin try out), again. It also required small changes to …
A fair while ago now we were in the midst of writing a Geographicals Suite of web applications that, given Latitude and Longitude pairs you could calculate things like …
Sun Angle at noon
Moon Angle at noon
Coriolis Effect
Distance between Geographical Locations
Weather at Geographical Location
… and that we eventually added some “placename” capabilities to all this, introduced with PHP/Javascript/HTML Sun Angle Tutorial, which harnessed all this useful goodwill of this useful webpage (thanks) publishing some placename geographicals data.
placename
country … linked to …
latitude
longitude
… and that we “channel” today, via our beloved Client Pre-emptive Iframe techniques, so that we reuse PHP, rather than having to create new PHP, as an aid to the modularization for added “placename” functionality to our “Other Side of the World” web application we started presenting yesterday with Other Side of the World Primer Tutorial as shown below.
Another thing we are trialling today is a concept (that admittedly seems to need more work in Firefox) of an HTML select (dropdown) element having an onclick event (after an onchange event that changes that select element value to a non-nothing value) having a logic whereby that select element value is used to repopulate the …
placename (, country) (Great Circle Distance in km away)
latitude
longitude
… HTML input type=text and type=number fields automatically. In the normal case of events in non-Firefox web browsers an onchange event change of value to a non-nothing value just causes that select element value to be one of the places shown on the Google ChartsMap Chart that we display.
The default is to show five of the nearest placenames in the derived list, but a “+” button can increase that number of “nearest”s as required.
The code for this is just HTML (apart from the supervised PHP for the Google Chart interface) that you could call other_side_of_the_world.htm (changed in this way for our purposes today) if you like, or want to try out, again perhaps?
Today we’ve written a first draft of an “Other Side of the World” web application using a Google Chart Map Chart embedded into an HTML iframe element to show the user …
the position of the place that they enter in for their latitude and longitude … as well as …
“the other side of the world” to the position of the place that they enter in for their latitude and longitude, calculated by imagining you take a trip from your original location through the middle of the Earth and straight through onto the other location
Is this our “sister” location? Am not sure. But somebody was telling “Porkies” to me as a child where we were told “China” as being on the other side.
The code for this is just HTML (apart from the supervised PHP for the Google Chart interface) that you could call other_side_of_the_world.html if you like, or want to try out.
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.
Have you been waiting for more “heads up” regarding issues we’ll have moving from a PHP (of a version starting with a 5) to a PHP (of a version starting with an 8)? Join the crowd?! But so far, it hasn’t been that bad. That is, until we started into looking at the PHP TimeZone functionalities. Going back to a previous time of referencing this, at the recent Google Charts Geo Chart Region Picker Conditional Positioning Tutorial, associative array based codelines like …
Google Charts Geo Chart Region Picker Alignment Tutorial
In our inhouse Region Picker we like the way the right hand webpage side involved some useful …
position: sticky;
… positioning, useful within the realms of what is going on on the right hand side.
But now we’re wanting some horizontal integration, linking commonalities of left and right regarding the country of interest, as a user’s eyes scan across the webpage, at pivotal times.
… all feature the great [element].scrollIntoView() way to programmatically scroll, rather than use (the sometimes ignored) window.scrollTo([left],[top]); or location.href=’#[elementID]’; approaches. It has the advantage of not altering location.hash (if you see that as a plus). We’d rather not add complexity, so, yes, we prefer the [aCountryElement].scrollIntoView(); call approach, at those pivotal times. This accounts for the left hand side of the webpage agreeing to the right hand side’s vertical scroll position. But what about the other way around? There, we started applying a CSS padding-top property to relevant right hand side elements (via DOM [element].style.paddingTop=[pixelsToExtendDown]px;), at pivotal points in the programming flow.
And on double clicking a right hand side button to start creating those background Geo Chart SVG based “images” we turn on a progress cursor, at least for non-mobile platforms, because the user needs patience here, in the changedregions_via_countries.htmlRegion Picker.
background-image:url(‘data:image/svg+xml;utf8, blahde blah ‘); nor background-image:url(‘data:image/svg+xml;base64, blahde blah ‘); ideas were not working for us … but today, we started to try …
overlay HTML div position:absolute; opacity:0.5; z-index:-4; …
We can’t remember a “foreground overlay” scenario so resembling a “background image feeling” end result, the transparent colour introduced into the Google ChartsGeo ChartSVG being crucial to help make this all work.
We want to be able to control the way a Google Charts Geo Chart can be nested within an HTML div element, for instance. We started the day wanting to be able to make …
a Google Charts Geo Chart be a background image to a div element … alas, on this first draft we couldn’t get there (but will continue with the research here) … whereas we succeeded …
adding the Google Charts Geo Chart interfacer’s resultant SVG data as the innerHTML (ie. content) …
<?php echo ”
function newbackin() {
if (dmyxhr.readyState == 4) {
if (dmyxhr.status == 200) {
if (dmyxhr.responseText) {
var m_t='image/jpeg';
var h_t='179';
var w_t='320';
var dbits = dmyxhr.responseText.split('\"height\": ');
if (dbits.length > 1) {
h_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"mime_type\": \"');
if (dbits.length > 1) {
m_t=dbits[1].split('\"')[0];
}
dbits = dmyxhr.responseText.split('\"width\": ');
if (dbits.length > 1) {
w_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"data\":');
dbits = dmyxhr.responseText.split('\"data\":');
if (dbits.length > 1) {
// replace all '_' with '/' and all '-' with '+' thanks to https://stackoverflow.com/questions/757675/website-screenshots
dgsbi='<img alt=\"Blog Posting Image\" style=\"width:' + w_t + 'px;height:' + h_t + 'px;\" width=' + w_t + ' height=' + h_t + ' src=\"data:' + m_t + ';base64,' + dbits[1].split('\"')[1].split('\"')[0].replace(/\_/g,'/').replace(/\-/g,'+') + '\"></img>';
//alert('dgsbi=' + dgsbi);
}
}
}
}
}
function ajaxit(urlin) {
if (urlin.length > 0) {
aurl=urlin;
if (window.XMLHttpRequest) {
dmyxhr = new window.XMLHttpRequest;
}
else {
try {
dmyxhr = new ActiveXObject('Msxml2.XMLHTTP');
} catch (othermicrosoft) {
try {
dmyxhr = new ActiveXObject('Microsoft.XMLHTTP');
} catch (failed) {
dmyxhr = false;
}
}
}
var xurl = 'https://www.googleapis.com/pagespeedonline/v1/runPagespeed?url=' + encodeURIComponent(urlin) + '&screenshot=true';
if (dmyxhr) {
dmyxhr.onreadystatechange = newbackin;
dmyxhr.open('GET', xurl, true);
dmyxhr.send(null);
}
}
}
function wbtoa(instris) {
var outstris=instris;
while (outstris.indexOf(String.fromCharCode(10)) != -1) {
outstris=outstris.replace(String.fromCharCode(10),'');
}
var xzs=prompt(outstris, outstris);
return outstris.replace(/\\\"/g, \"'\");
}
In that Nearest Places form part of the TimeZone places webpage, we noticed that even when shaping to enter a TimeZone Placename in the first textbox we made no attempt to fill in …
latitude
longitude
… when we have the information to do so, and even if we’re misunderstanding a placename designation, that should not stop us from trying, because those two numerical fields above can be corrected, and the form resubmitted, in these scenarios.
Apart from making a PHP derived Javascript variable be made available to the code, we “wrap” …
GeoJson World Countries Drag and Drop Makeover Nuance Tutorial
Nuance alert! We’re not sure if you noticed, but if you tried out the Drag and Drop functionality in the World Countries web application of yesterday’s GeoJson World Countries Drag and Drop Makeover Tutorial you may have noticed …
for a country with lots of TimeZone places, like Brazil, you could get a decent Google Chart Geo Chart map up … but if you were to click the “Map?” link down the bottom of that iframe …
it would fail to show a Google Chart Map Chart for that country’s TimeZone places
This fix, believe it or not, is interesting, perhaps only in an “internal use only” sense, we grant you. But our solution got us delving even more into hashtagging data, so that the solution we came up with was a hybrid whereby …
stayed with PHP $_GET[] (ie. address bar ? and & argumented) data (versus using PHP $_POST[] methodologies) … but …
where it came to the &data=[most of the data] part, other than its first character, we hashtagged the rest
… so that the logic flows as per usual, but we intervene at places and flesh it out via location.hash (client side only) means. We won’t bore you too much with all the places of intervention except the receiving map.php’s “easiest to get” intervention …
In addition to the Wikipedia information, at the very least, presented following a successful drag and drop operation, from today, we also start presenting a new HTML iframe element containing …
Nearest TimeZone places along with Google Charts for each unique country involved
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-ccglobal 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;
}
… 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!
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 …
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
“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
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 …
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 …
drop part of the events occurs in that originator parent and so several Javascript function called by the child reside in the parent … and …
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');
}
}
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');
}
}
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);
… 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 …
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 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();
}
}
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 …
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” ❓ ( ❓ ) type element …
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 '';
}
… 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” 🔎 ( 🔎 ) …
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>✈</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='🚢';
isub.setAttribute('data-type', '128674');
} else if (('' + curgd) == '128674') { // port
isub.innerHTML='✈';
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;
}
}
}
… 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
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 ✈ ( ✈ ) “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 changedcountries.phpweb 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 …
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');
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;
}
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 ( 🔎 ) 🔎 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;
… 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 {
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 🕐 🕐
2 o’clock is 🕑 🕑
12 o’clock is 🕛 🕛
2:30 is 🕝 🕝
11:30 is 🕦 🕦
12:30 is 🕧 🕧
… to show in “prompt” and “confirm” popup windows, as well as Map Chart maps … via new Javascript functions …
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 changedcountries.phpweb application you can also try below.
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]; }
}
}
…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
… 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.
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 …
… 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.
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 🇿🇦 🇿🇦 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
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.
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 …
… 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.
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
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
… into your life … the bigger the better! Wonder at all those zeroes at the end? Carve a long factorial into a piece of soap? They say the Lord’s Prayer was written on a pinhead … what are you waiting for?!
Today we have an unusual linkage of tutorials, where we link our tutorial here, today, called “PHP Bcmath Primer Tutorial” with one from some time back (and I.T. historically speaking, a really long way back) called Lisp Primer Tutorial as shown below.
The link is the way the languages handle some arithmetic on very large numbers, and today we present a web application that is like a calculator for some of those very large number calculations. Am not certain Lisp does the work this way, but the “bcmath” module in PHP, that we use for the purpose here today, uses “strings” to achieve the affect, so that I don’t believe there are any times during the calculations, necessarily, with the CPU register, that there are any great huge numbers being handled, but piece by piece, the CPU register is helping piece together that string that represents the answer we are after, not using any scientific notation in the process.
We throw into the mix a bit of fun and games … just imagine you are on Central (train) Station in Sydney, or loads of other train stations in the world where it is pretty obvious the station announcements are pieced together bits of audio, and today we make use of the wonderful Google Translate functionality in order for you to, optionally (and not always … no go with Firefox for instance), hear the results in those great mellifluous tones (in English, as default).
Hope you really give some curly questions out there to the live run. Here is the PHP source code using the “bcmath” module you could call got_big_numbers.php
Have you heard of Lisp, the programming language? Do you remember how incredible Hewlett Packard calculators were back in the 1970’s (we did as Land Surveyors … they were the bee’s knees, especially to program for those Land Surveying calculations). And do you (older users) remember the excitement regarding reverse Polish notation? Well, Lisp, the second oldest high-level language (after Fortran) uses this reverse Polish notation and thinking.
Am no expert with Lisp, as you’ll see, and we’ll defer to code available on the net and contributed by others, but have to mention its incredible mathematical scope, for instance, with its functionality for huge numbers (integers). You will notice with languages like C++ it is not at all easy doing a calculation like the factorial of 1000 … or 1000! … or 1000 x 999 x 998 x —> x 2 x 1 … but Lisp does it with considerable ease. As a matter of interest, as far as this type of big integer in Java, you may want to check on the BigO data type.
Today’s tutorial introduces you to Lisp we install using LispWorks Personal on a Mac laptop, and execute a Hello World and some large factorial calculations. Lisp, being the old language it is, is a very functional language, and although global variables are allowed, the Lisp “way” is not to resort to such approaches.
Below are some links regarding Lisp you may want to peruse:
As we intimated earlier, using PHP gettext is a good framework to use regarding writing your own serverside language translation functionality, but the “gruntwork” is there ahead of you maintaining those [domain].po (Poedit) files. And so, improving on yesterday’s AlmaLinux PHP Gettext Your Own Translation Tutorial …
Why don’t we back up an “insufficient data” or “unknown” translation ask from a user, now that they can enter their own words/phrases, by presenting a popup window Google Translate window as a backup?
Why don’t we set up a system to follow up on moderations being attended to and accepted by administrative overseers, by adding those words/phrases accepted …
<?php
$lastfile="";
if (file_exists('/tmp/get_text_moderation.txt')) {
$lines=explode("\n", file_get_contents('/tmp/get_text_moderation.txt'));
for ($ilines=0; $ilines<sizeof($lines); $ilines++) {
if (strpos($lines[$ilines], DIRECTORY_SEPARATOR . 'LC_MESSAGES' . DIRECTORY_SEPARATOR) !== false) {
$lastfile=$lines[$ilines];
} else if (strpos($lines[$ilines], ' msgid ') !== false && strpos($lines[$ilines], '_await') !== false) {
if (strpos(file_get_contents($lastfile), " msgid " . explode('_await', explode(' msgid ', $lines[$ilines])[1])[0] . '"' ) !== false) {
$wordopt.="<option value='" . explode('_await', explode(' msgid "', $lines[$ilines])[1])[0] . "'>" . explode('_await', explode(' msgid "', $lines[$ilines])[1])[0] . "</option>" . "\n";
}
}
}
}
?>
… through to the Words/Phrases dropdown, in the web application?
user supplying their own English to “Their Newly Introduced Locale” word/phrase translation
… is fine and good in principle, but needs moderation, and that requires a deal of thinking. But then we thought, as far as moderation goes, why don’t we …
add to the [domain].po relevant [domain].po msgid entry a suffix that goes …
… allowing the creator of the translation see their translation echoed back to them, but other people see no translation should they ask for the same translation conditions. That means if someone is not using the web application properly, only they, or people looking over their shoulder, see their own translation ideas happening ahead of a moderator stripping out from the msgid string any suffix like above if that moderator determines it is a genuine translation data item, in which case from then on all users see that translation suggestion come into “live play”.
Do not think of gettext as some miracle panacea for a language translation issue. It is just a good framework to use, but you still have to do the bulk of the “hard yards” …
creating [domain].po (Poedit) input files where msgid strings have translate from strings and msgstr strings have translate to strings relevant to your language locale (eg. de_DE for German) …
convert these to [domain].mo via …
msgfmt [domain].po -o [domain].mo
for each relevant locale … you can check for relevance of via …
locale -a
Yesterday’s Yahoo YQL Web Service JSON Thesaurus Tutorial as shown below, is a good candidate for “gettext” internationalization, and we show the files necessary to oversee yesterday’s HTML and Javascript with a layer of PHP to achieve it. We test this on our local MAMP local web server, where “gettext” is enabled (unfortunately, this is not the case with the rjmprogramming.com.au domain, so today, we only have source code rather than an additional live run … for that you can watch a video playing of a MAMP session using it here).
Now this additional internationalization concerns the …
language of the wording on the screen, and in error messages and titles
starting of an execution in the same language as last used … we use PHP methods, but you could also use Cookies for this idea
… the languages of the YQL web service of the data emanating from its links to the altervista thesaurus web service database with Yahoo YQL website … thanks. For the altervista thesaurus database usage it is best to request a key for your web service queries. You fill in a word (or two, or three) of interest to search for like-minded words.
Leave aside lots of time and/or manperson power for the translation work. Hope this helps you out.
Language teachers will sometimes resort to an immersion method, in the classroom, where the only language allowed to be spoken is the language being learnt, often the second language for the student who is being taught. ESL teachers face this decision sometimes. We read an interesting article on the subject here, and decided to use our Pros and Cons web application to create our own view of the importance of the excellent points talked about in that article, as you can see with our report …
Then there are degrees of “immersion”, as well, with questions such as whether you allow the use of paper (or electronic) dictionaries and translators. Think there is a lot of agreement that the best language learning happens with some degree of context and use of realia to help the student envisage the situations in which that vocabulary might be encountered. However, with written work or independent study, and even in the classroom a (web application) tool to help the learner with a difficult word could help, and so we’ve extended the functionality of the previous Yahoo YQL Web Service JSON Thesaurus Tutorial as shown below, by offering the chance for the native language be used to translate the word, and offer a list of thesaurus word lists of the language they are learning out of the list of languages below …
Read more about this below as well, but this tutorial uses a YQL web service into the data emanating from its links to the altervista thesaurus web service database with Yahoo YQL website … thanks. For the altervista thesaurus database usage it is best to request a key for your web service queries. You fill in a word (or two, or three) of interest to search for like-minded words.
We found useful, and thank, for this extra functionality, the tremendous MyMemory API resource.
Here is a tutorial that might be re-introducing you to the Yahoo Web Services called YQL, building on previous ones here at this blog. The name is the way it is because it simplified the API aspects of its functionality for the developer to concentrate on SQL, and I’m really supportive of this concept. You don’t have to output in JSON, as other data forms like XML are acceptable. Let’s see what Wikipedia says about YQL below.
Yahoo! Query Language (YQL) Gumtree Advertisementusing Android Phone Photos Tutorialis an SQL-like query language created by Yahoo! as part of their Developer Network. YQL is designed to retrieve and manipulate data from APIs through a single Web interface, thus allowing mashups that enable developers to create their own applications.[1]
Initially launched in October 2008 with access to Yahoo APIs,[2] February 2009 saw the addition of open data tables from third parties such as Google Reader, the Guardian, and The New York Times.[3] Some of these APIs still require an API key to access them. On April 29th of 2009, Yahoo introduced the capability to execute the tables of data built through YQL using JavaScript run on the company’s servers for free.[3]
So this tutorial uses a YQL web service into the data emanating from its links to the altervista thesaurus web service database with Yahoo YQL website … thanks. For the altervista thesaurus database usage it is best to request a key for your web service queries. You fill in a word (or two, or three) of interest to search for like-minded words.
And what is a thesaurus? Read what Wikipedia says here but, basically, it can be thought of as a repository of a language’s list of words of a similar meaning to the word of your interest. It is a great tool for people learning a language foreign to them, especially regarding their vocabulary.
The two big PHP functions of use (as distinct from an Ajax approach, requiring no PHP … read on) for this are:
json_decode … when there is a data structure of any complexity, this function is highly recommended, but for today’s tutorial we do not use it, and take the opportunity to show you a solution using Ajax that requires no PHP … link to some downloadable Ajax inspired HTML programming source code which you may want to rename to Ajax_yql_thesaurus.html which changes from the HTML supervising PHP approach as per Ajax_yql_thesaurus.html (and then there is an Ajax live run)
Good links for information regarding this tutorial (thanks) are:
Another tool you should have in your armoury for jobs like this is the online JSON validator here. A generic JSON approach to issues could be:
Type the URL you were given into a web browser address bar and have a look at it
Type the URL you were given into http://jsonlint.com/ and have it validated
Understand in your own mind what would be different about 1. to make it suitable
Incorporate findings of 3. into massaging of data between file_get_contents and json_decode
Here is a link to some downloadable HTML programming source code which you may want to rename to yql_thesaurus.html which calls some downloadable PHP programming source code which you may want to rename to yql_thesaurus.php (and then there is a PHP live run).
Stay tuned for an interesting blog posting tomorrow which combines the new thesaurus functionality of today’s contribution, along with the functionality “smarts” of yesterday’s PHP/CSS Sentence Auxiliary Verb Game Tutorial.
Stop Press
As of 14th May 2015 a blog posting called HTML/Javascript Homonyms Game Tutorial inspired a “tweak” to the arrangement above such that a call of the web application above such as Sense of Humour will cause the display of a lookup during the “onload” event, 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.