… and decided, as a Javascript ploy to use, with either idea, if an option value differs from it’s associated text (ie. it’s innerHTML or innerText) we’d represent a dropdown or datalist selected button or input element representation as …
innerHTML (value)
… which becomes more interesting, we’re hoping, by today’s additional functionality allowing a user to enter a URL (when the ? link appears) whose content is scoured for either …
… first such element to glean content (especially as the ComboBox concept is all to do with “data”) to adopt for either guise of ComboBox. With the Input/Datalist one you maybe select a datalist option, but to have another go, you may have to change the input value to nothing for a datalist to be offered again. The Button/Select is more our hybrid not having this idea in place.
We’ve long been interested in what used to be a cute feature of GUI widgetry desktop web applications (before any mobile anything) called “ComboBox” as talked about at Button Innards Primer Tutorial. To us, what a “ComboBox”‘s essence is …
Combination of interactively keyboard entry and/or a dropdown means of data definition
… and, like with that tutorial linked to above, it is an HTML button element we start with …
<button onclick="document.getElementById('ssave').focus();" id=bsave><span style='background-color:white;min-width:40px;padding:2 2 2 2;' onclick="event.stopPropagation();" id=ssave onblur=lsit(this); contenteditable="true"></span><select onclick="event.stopPropagation();" style='margin-left:100px;' contenteditable=yes onchange=recallsm(this); id=savedd><option value=''>Optionally enter a value ...</option><option value=1>1</option></select></button>
… combine, today, starting down, again, the road to “button innards” interest.
We thought the “how we got there” concept of embedding the form elements of data into “button innards” (ie. it’s innerHTML) in our new “button innards” web application interesting. Of course you don’t have to do this simple exercise of calculating time elapsed between two datetimes this way, but what we wanted to show was that with lots of things in web design, by now, there is more than one way to crack an egg …
Yesterday’s Making of Ffmpeg Video Overall Effects Tutorial‘s “first draft” feel of our inhouse Bookmark Export Filtering web application showed just that. Yes, there was quite a lot to improve on, the way we saw it being …
adding a (local system) file browsing means (called by our <iframe scrolling=no frameborder=0 id=cbi data-style=’border-top:1px solid black;border-bottom:1px solid black;border-left:2px solid yellow;border-right:1px solid yellow;’ style=’width:173px;height:228px;margin-top:-194px;’ src=’/HTMLCSS/client_browsing.htm?totype=html&d=189786754′></iframe> file browsing helper) …
function yesthreethree(restis) {
if (document.getElementById('bmkdata')) {
if (restis.indexOf('data:') == 0) {
if (restis.indexOf(';base64,') != -1) {
document.getElementById('bmkdata').value='' + window.atob(restis.split(';base64,')[1]);
analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
} else if (restis.indexOf(';utf8,') != -1) {
document.getElementById('bmkdata').value='' + (restis.split(';utf8,')[1]);
analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
}
} else {
document.getElementById('bmkdata').value=restis;
analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
}
}
}
… by which to add Web Browser Bookmark Exporting Data File Content into that lower HTML textarea element (and unlock the two UL/LI and Table button elements) … as well as …
add a details/summary “reveal” means by which the user can show working actual HTML elements in addition to the HTML coding we’ve only be showing (with that “first draft”)
add Sharing functionality via either Email or SMS conduits
… and because the amounts of data can be large (and too big for a method=GET HTML form arrangement that clientside HTML/Javascript is capable of servicing) we were glad adding this Sharing functionality that yesterday we’d already started down the line of thinking that the …
lower HTML textarea content … would always be associated with …
hashtag … representation
… because with these methodologies the web server restrictions on (address bar) URL lengths (ie. error 414) melt away, at least for data lengths we’ve encountered with our testing so far, and that applies also to …
“mailto:” “a” URL links to Email a recipient … or …
“sms:” “a” URL links to SMS a recipient
… Sharing …
var mysubject='My Bookmarked Links ...', xlocationhref='';
if (('' + location.hash).indexOf('bmkdata=') != -1) { locationhash=location.hash; }
function doemail() {
if (document.getElementById('bmkdata').value.trim() != '') {
locationhash='#bmkdata=' + window.btoa(encodeURIComponent(document.getElementById('bmkdata').value));
mysubject='My Bookmarked Links ...';
if (document.getElementById('dtone').value.trim() != '' && document.getElementById('dttwo').value.trim() != '') {
mysubject='My Bookmarked Links from ' + document.getElementById('dtone').value.trim() + ' to ' + document.getElementById('dttwo').value.trim() + ' ... ';
} else if (document.getElementById('dtone').value.trim() != '') {
mysubject='My Bookmarked Links from ' + document.getElementById('dtone').value.trim() + ' ... ';
} else if (document.getElementById('dttwo').value.trim() != '') {
mysubject='My Bookmarked Links up until ' + document.getElementById('dttwo').value.trim() + ' ... ';
}
if (ulli && !utable) {
recipient=prompt('Please enter email address to send to? Will output UL/LI else if some blanks will output Table. Default email Subject is ' + mysubject + ' ... but if you want a different one append a hash (#) followed by your own Subject for the Email.', '');
if (recipient == null) { recipient=''; } else if (recipient.indexOf('@') == -1) { recipient=''; } else if (eval('' + recipient.indexOf('#')) > eval('' + recipient.indexOf('@'))) { mysubject=recipient.split('#')[1]; recipient=recipient.split('#')[0]; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='mailto:' + recipient.replace(/\ /g,'') + '?subject=' + encodeURIComponent(mysubject) + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
} else if (utable) {
recipient=prompt('Please enter email address to send to? Will output UL/LI else if some blanks will output Table. Default email Subject is ' + mysubject + ' ... but if you want a different one append a hash (#) followed by your own Subject for the Email.', '');
if (recipient == null) { recipient=''; } else if (recipient.indexOf('@') == -1) { recipient=''; } else if (eval('' + recipient.indexOf('#')) > eval('' + recipient.indexOf('@'))) { mysubject=recipient.split('#')[1]; recipient=recipient.split('#')[0]; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='mailto:' + recipient.replace(/\ /g,'') + '?subject=' + encodeURIComponent(mysubject) + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
}
}
}
function dosms() {
if (document.getElementById('bmkdata').value.trim() != '') {
locationhash='#bmkdata=' + window.btoa(encodeURIComponent(document.getElementById('bmkdata').value));
if (ulli && !utable) {
recipient=prompt('Please enter SMS number to send to? Will output UL/LI else if some blanks will output Table.', '');
if (recipient == null) { recipient=''; } else if (recipient.trim() != '' && recipient.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') != '') { recipient=''; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='sms:' + recipient.replace(/\ /g,'') + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
} else if (utable) {
recipient=prompt('Please enter SMS number to send to? Will output UL/LI else if some blanks will output Table.', '');
if (recipient == null) { recipient=''; } else if (recipient.trim() != '' && recipient.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') != '') { recipient=''; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='sms:' + recipient.replace(/\ /g,'') + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
}
}
}
function fillin() {
if (document.getElementById('rt') && document.getElementById('rd') && document.getElementById('rs')) {
if (document.getElementById('rd').innerHTML == document.getElementById('rs').outerHTML) {
document.getElementById('rd').innerHTML=document.getElementById('rs').outerHTML + document.getElementById('rt').value;
}
}
}
// Coming back from an email or SMS link click ...
… concepts, happily. Keeping it all “just clientside” means we can be talking meaningfully to a larger chunk of audience … if you’re listening, that is?!
… of links, am sure a lot of you would guess, were gleaned (within our Google Chrome web browser History (of that day)) Bookmarked (by us) webpages. We actually compiled via (the clunky and fairly slow) …
left half (of screen) with (Google Chrome) History showing, and right half with BBEdit Text Editor open at a new (ostensibly empty) file with loads of empty records available, and dragged URL records (identifiable, for us, via an asterisk, indicating a webpage was Bookmarked) from the left into the right to bring over the URLs (only) above, the rest constructed in a more manual way … and another way could have been …
use Google Chrome webpage showing Bookmark list and use its Export Bookmarks (no other choice but “All”) to a file that could be waded through to derive the list above … and then we thought of chance to code for …
hybrid “inhouse” clientside (ie. HTML/Javascript/CSS) web application …
… so far asking for the content of a web browser Export Bookmarks operation’s output file’s HTML (in the case of Google Chrome) content in an HTML textarea element (the user can fill in themselves), and filterable via two input type=datetime-local starting and ending datetime(s) of interest (the user can fill in, optionally)
… and we wish we could have got that hour and a half back, from yesterday, via swapping yesterday for today?!
Not all web browser brands have any Export Bookmarks functionality, like Google Chrome offers here on macOS. We’re going to be examining others, over time, and try to make our inhouse logic fit in with how that Bookmark data looks, on export, should we find any other Web Browser brands offering this functionality.
Today’s work (all on our macOS MAMP local Apache/PHP/MySQL web server environment) is only “toe dipping” in a “great big sea of possibilities” regarding the great ffmpeg‘s “filtering style abilities” further to yesterday’s Ffmpeg Video Subliminal Message Tutorial, but …
as you try out things you discover others …
can be a foot in the door researching this type of work yourself …
… we’re hoping. And it’s not as if ffmpeg was on it’s own here with the second concept below …
fade in and fade out video effect … inspired by Top 20 best commands for FFmpeg to try …
ffmpeg -i brush__turkey.m4v -vf "fade=in:0:5,fade=out:73:5" brushturkey_fadein_fadeout.m4v
colour balancing improvements regarding an underexposed video
… as the …
Gimp image editor’s abilities (matching a lot of what you read Photoshop can help with too) with Curves helped out … along with …
Python expertly designed to link Gimp and ffmpeg “whole of video” manipulations
… enacting …
selection of a video still image, ideally not containing the Brush Turkey …
we were inspired by a Brush Turkey which we videoed via an iPhone’s Camera app’s Video mode of capture option …
downloaded to this MacBook Air (via AirDrop … good for Apple sharing when the sizes get big … raw video 1:19s long)
And that’s where we are at here, thinking this would be a good opportunity to try creating a Subliminal Message video. But before we go on and “give the game away” you may want to see the result of our Subliminal Message insertion via ffmpeg theme to our work, in action, below. Unlock or give up to unlock the content below the video quiz below …
Congratulations! Yes, in between the “Raw Video” ffmpeg work and onto the other ffmpeg commands we renamed one slide to a *.jpeg equivalent and then set about replacing the *.jpg original slide with our Subliminal Message slide, as you can see here.
The work involves both of the HTML design “big concepts” we are keen on at this blog …
overlay … via CSS control of …
position:absolute;top:0px;left:0px;
opacity:1.0;
z-index:1;
reveal … lately, mostly, via HTML use of …
details
summary
… the latter being the “container” for our interactive input be able to control overlay items 2 and 3, which affect the “overlayed images” output display via the clicking of a new “Overlay Images” button. That button …
… one reason being that we do not want to install the wonderful ffmpeg (command line video creation tool) on the RJM Programming domain, but, in macOS, here with MAMP, we are quite happy to live with the Homebrew (Terminal application’s) install …
… setting up what could be “lost functionality”, but today, we come around to either adding a new …
Convert to Video
… dropdown option should all be ritchy ditch, and if not, often we will add new “Advice” dropdown options to remind the user what they’d need to arrange to get to a “Convert to Video” scenario.
Selecting “Convert to Video” sets up a hydrid “Internet/Intranet” feeling scenario where …
… be nested in PHP exec (via MAMP local web server incarnation of the inhouse Animated GIF Creator) as a way to create videos …
which can be downloaded within that public domain “parent” inhouse Animated GIF Creator session
Which begs the question …
How do we know when to offer the "Convert to Video" option on that dropdown?
This logic is centred around a few useful ideas (with this cross domain scenario ruling out Ajax and Iframe src= definitions, as useful ways to go) …
open the MAMP local web server “HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php” URLs in an iframe (with onload event logics) pointed at by a window.open second argument (effectively avoiding any loose useful or not popup windows hanging around) …
the most we can ask at the receiver is that window.opener is defined … and if so, just at that discovery …
an image called “amhere.jpg” is created via PHP GD functionality … and back at the “public domain” parent within the iframe onload logic (where with contentWindow or contentDocument do not expect a document.body or even a document) …
we attempt to “hotlink” that MAMP local web server image … as per …
var tryit='http://localhost:8888/PHP/animegif/amhere.jpg';
document.getElementById('ctvopt').value='advice';
document.getElementById('ctvopt').innerHTML='Advice on Convert to Video';
if (tryit != '') {
var im=new Image();
im.onload = function() {
document.getElementById('ctvopt').value='video';
console.log('this.height=' + eval('' + this.height));
if (eval('' + this.height) >= 20) {
document.getElementById('ctvopt').innerHTML='Convert to Video';
} else {
document.getElementById('ctvopt').innerHTML='Convert to Video (but ffmpeg not installed or in unexpected place)';
}
document.getElementById('imsel').title='All except Convert to Video, which needs ffmpeg installed, use ImageMagick';
};
… and check the code for the validity of any ImageMagick paths … and if not all these conditions, simulate the same and cobble it together in the code
… one reason being that we do not want to install the wonderful ffmpeg (command line video creation tool) on the RJM Programming domain, but, in macOS, here with MAMP, we are quite happy to live with the Homebrew (Terminal application’s) install …
brew install ffmpeg
… to open a whole new woooooorrrrlllllddd of video creation opportunities using this MAMP local web URL …
HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php# Animated GIF Creator on MAMP local web server
… we tweaked out of “video/mp4” ffmpeg created videos thinking towards “video/webm” or “video.mov” ffmpeg created videos that suit the “few frames but spaced out video” we wanted to achieve for all practical purposes, that “video/mp4” playing too fast for us to see anything much but the first and last slide …
As a web application programmer we like buttons. There are “buttons” and there are buttons. Yes, there is an HTML button element, and it and the input type=button element render as that part of a webpage most recognizable to users the world over. You click or tap this button and something usually happens.
But we also enjoy “Emoji Buttons” for we graphically challenged programmers. Using an emoji text and graphic can make span elements or p elements or lots of other HTML elements that have an innerHTML property, be like a very succinct button like entity, also appreciated around here because it takes up so little webpage “real estate”.
And so onto yesterday’s Animated GIF Creator PDF Order Tutorial we set about, today, “revealing” any enduring animated GIF and/or PDF created during a previous session, using Emoji Buttons as the email/SMS sharing action buttons.
We say “revealing” because, like the way “Emoji Buttons” save webpage “real estate”, so can the use of the HTML5 details/summary element combination. It is in that summary “enduring” header part of that combination we can place some “Emoji Buttons”. You will (once you start creating and sharing animated GIFs and/or PDFs) see from our changedtutorial_to_animated_gif.php inhouse Animated GIF Creator (helped out by a changedemailhtml.php inhouse email creator helper serverside PHP web application) that we place three Emoji Buttons …
📟 SMS (Animated GIF or PDF) Limited relevance period (ie. no data URIs involved, so rely on an absolute URL whose content might change or start not to exist down the track) … via “a” “sms:” link click/tap
📧 Email (Animated GIF or PDF) Limited relevance period (ie. no data URIs involved, so rely on an absolute URL whose content might change or start not to exist down the track) … via “a” “mailto:” link click/tap
📧 Email (Animated GIF or PDF) Enduring relevance (ie. data URIs involved, so do not rely on any absolute URL whose content might change or start not to exist down the track) … via PHP mail function means of sending email with HTML attachment containing the relevant Animated GIF or PDF
Yes, “Emoji Buttons” can have their size controlled but not by the usual CSS width and height properties, but by the CSS font-size property. Here is the new Javascript function that those Emoji Button “onclick” logic points to …
<?php eho ”
function askes(isemail, isoab) {
var esask='', izhr=null, izform=null;
var isoa=document.getElementById(isoab.id.replace('b',''));
if (isemail) {
esask=prompt('Please enter email address to send this to', '');
if (esask != null) {
if (esask.indexOf('@') != -1) {
if (isoa.outerHTML.indexOf('<img') == 0) {
//alert('More Animated GIF to come');
izhr = new XMLHttpRequest();
izform=new FormData();
izform.append('to', esask.trim());
izform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . ".gif" . "');
izform.append('subj', 'My Latest Animated GIF via RJM Programming ... ');
izform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><img style=\"width:100%;height:900px;\" src=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . ".gif" . "\"></img></div></body>'));
izhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
izhr.send(izform);
} else if (isoa.outerHTML.indexOf('<a') != 0) {
//alert('More PDF to come');
izhr = new XMLHttpRequest();
izform=new FormData();
izform.append('to', esask.trim());
izform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . "_animegif.pdf" . "');
izform.append('subj', 'My Latest PDF via Animated GIF via RJM Programming ... ');
izform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "_\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><object style=\"width:100%;height:900px;\" type=\"application/pdf\" data=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . "_animegif.pdf" . "\"></object></div></body>'));
izhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
izhr.send(izform);
} else {
isoa.href='mailto:' + esask.trim() + '?' + isoa.href.split('?')[1];
isoa.click();
}
}
}
} else {
esask=prompt('Please enter SMS number to send this to', '');
if (esask != null) {
if ((esask.trim() != '' && esask.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '')) {
isoa.href='sms:' + esask.trim() + '&' + isoa.href.split('&')[1];
isoa.click();
}
}
}
}
Order becomes an interesting subject to us today regarding the PDF conversions possible in amongst the Animated GIF creating of yesterday’s Animated GIF Creator PDF Share Tutorial.
That is because we may have some advanced users out there that want that possibility of differentiating the data of …
animated GIF … containing all features asked for, in totality … from the chance for the user to have any of the PDF conversion option sets below …
PDF … one of …
no PDF conversion
full PDF conversion … with the user choices regarding raw image content and title and watermarking and ImageMagick and GD modifiers
light PDF conversion … with the user choices regarding just raw image content (the only option out of these last three available before today’s work)
medium PDF conversion … with the user choices regarding just ImageMagick and GD modifiers
… perhaps as a “before and after” tool regarding presentations, maybe?
No dropdowns used here (enforcing an order), just the user’s order in which they choose to select the “PDF conversion” option from the ImageMagick dropdown (in relation to other selections) determining how and when this PDF conversion occurs in the workflow through to creating the animated GIF with our changedtutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application. Just remember to select PDF Conversion as early as possible to do that “light PDF conversion” option above and last thing if you are interested in “full PDF conversion” (where you can create PDF documents with the bells and whistles ImageMagick and GD modifiers can offer) and today’s tutorial picture is an example of “medium PDF conversion” (when we ordered our settings by first Grayscale, second PDF conversion and last title and watermarking options). Clicking or tapping the ImageMagick link can get the user to the Javascript popup window where they might define the email or SMS recipient for that PDF conversion data file download.
What sharing conduits do we code for? We always intended …
email … but as the day wore on trying to get the usage to work on mobile and non-mobile we decided to relinquish our wish to not have to create a user specific enduring (until that same browser type and user combination share) PDF document for the user request … mainly to get mobile email downloads to be friendly … and so this opened the door for …
SMS … to access that enduring PDF as a URL in the SMS message (that becomes a link for the recipient)
<?php echo ”
function emailhtmlit() {
var induri=ginduri;
var pemail='" . urldecode($_GET['outpdf']) . urldecode($_POST['outpdf']) . "';
if (pemail != null) {
if (pemail.indexOf('@') != -1 || (pemail.trim() != '' && pemail.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '')) {
if (induri.trim() == '') {
if (induri == '') {
document.getElementById('pdfproposed').src='./animegif.pdf?rand=' + Math.floor(Math.random() * 19854654);
} else {
ginduri='found';
}
setTimeout(emailhtmlit, 15000);
} else {
ginduri='';
var zhr = new XMLHttpRequest();
var zform=new FormData();
if (pemail.indexOf('@') != -1) { zform.append('to', pemail); }
zform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . "animegif.pdf" . "');
zform.append('subj', 'My PDF version of Animated GIF via RJM Programming ... ');
zform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "_\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><object style=\"width:100%;height:900px;\" type=\"application/pdf\" data=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"><embed style=\"width:100%;height:900px;\" type=\"application/pdf\" src=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"></embed></object></div></body>'));
//zform.append('tdhuhta', ('<body><iframe srcdoc=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"></iframe></body>'));
zhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
zhr.send(zform);
if (pemail.indexOf('@') == -1) {
var hrefp=document.getElementById('pdfsms').href.split('&body=')[0];
hrefp+=pemail.trim() + '&body=' + document.getElementById('pdfsms').href.split('&body=')[1];
document.getElementById('pdfsms').href=hrefp;
document.getElementById('pdfsms').click();
}
}
}
}
}
“; ?>
… and this called on our PHP email creator helper to better interface in its $_POST arguments reading section in our changedemailhtml.php inhouse email creator helper serverside PHP web application …
We have more “reliability work” and “email sharing work” to go after today’s start, but it primarily called on ImageMagick command line’s talent for a command like …
convert /tmp/imtmp0*.*[gGmMiI]* /tmp/imtmp000.pdf
… to “concatenate” into the one output PDF file (called “/tmp/imtmp000.pdf”) the slides, arranged by our code into that “/tmp/imtmp0*.*[gGmMiI]*” (file specification) arrangement above. Yes, we meant “convert” above, as “mogrify” (batch work) appears not to be able to perform this task.
Animated GIF Creator Slide Specific Application Tutorial
It’s all fine and good improving on the ImageMagick and GD and Exif functionality modifications like with yesterday’s Animated GIF Creator Exif Rotation Compensation Tutorial, but in reality, if you are going to start creating animated GIFs to explain a process, you are going to want to apply these “slide modifiers” on a slide by slide basis, rather than enforcing a “whole of animated GIF slide set” paradigm, as for the last few days worth of work.
And so, we decided to do what we often do, “sliding in” more functionality (chortle, chortle). We tend to want to …
start with hardcoded text (or element) … somewhere … today it happens to be in an HTML span element that once involved just …
<span id="smyim"></span>
… and used to get filled, Javascript DOM wise, when needed via …
document.getElementById('smyim').innerHTML='ImageMagick switches: '; // and yes, it remains that way even now, but read on ...
add intelligence (quite often that being onclick logic(s)) to that hardcoded element via …
<span id=smyim title=Application onclick=applyto(); style=cursor:pointer;text-decoration:underline;></span>
that serves the purpose, as the user clicks/taps it (alerted to that fact, perhaps, because we underline the element and add an appropriate cursor when hovering over it (plus a title)), of calling Javascript …
function applyto() {
var huhto=prompt('Apply ImageMagick and/or GD to which slides, in comma separated list, counting starting with 1? Defaults to applying to all slides. Comma delimit. Negatives mean all but. Ranges can be specified. For example ... 2,4-7,9', document.getElementById('appliedto').value);
if (huhto == null) { huhto=''; }
if (huhto.trim() == '') {
document.getElementById('appliedto').value='';
document.getElementById('smyim').title='Application';
} else {
document.getElementById('appliedto').value=huhto.trim();
document.getElementById('smyim').title=huhto.trim();
}
}
to glean an (often times out of the normal workflow of the web application) informational piece of data, interactively, from the user, via a Javascript prompt popup
So that’s the clientside of this work … “alerting the user to the existance of the functionality” you might say.
And then there’s “the application” of that nuanced user requirement. And that’s where the “inhouse ‘our’ prefix to wrapper function name paradigms” come in handy. We introduced “blanket” functionality thoughts via this approach, and so to “partially undo” that thinking, we make the “our” prefix conditional, as is available to us with our changedtutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application (helped out by a changedexif_rotation_check.php inhouse Exif detector PHP helper), as per …
<?php
function ourcomplicated($inio, $iappl) {
$ideasl=explode(",", str_replace(' ','',$iappl));
$xour="our";
for ($iqa=0; $iqa<sizeof($ideasl); $iqa++) {
if (trim($ideasl[$iqa]) != '') {
$xour="";
if (('-' . $inio) == trim($ideasl[$iqa])) { return ""; }
$ideasr=explode("-", str_replace(' ','',trim($ideasl[$iqa])));
for ($iqb=0; $iqb<sizeof($ideasr); $iqb++) {
if (('' . $inio) == trim($ideasr[$iqb])) {
return "our";
}
if ($iqb == 1) {
if (trim($ideasr[1]) == "") { $ideasr[1]="99999999"; }
if ($inio >= $ideasr[0] && $inio <= $ideasr[1]) { return "our"; }
}
}
}
}
return $xour;
}
We use these “new abilities” better explaining “the abscence or otherwise of Exif checking”, and the implications of that in the animated GIF creator woooooorrrrrrlllllddd, contrasting the first two slides, showing one with “No Exif checking” (the bad old days) versus “With Exif checking” (the renaissance of liberated thinking in the South South East woooorrrrrlllldddd) in today’s animated GIF presentation.
Exchangeable image file format (officially Exif, according to JEIDA/JEITA/CIPA specifications) is a standard that specifies the formats for images, sound, and ancillary tags used by digital cameras (including smartphones), scanners and other systems handling image and sound files recorded by digital cameras.
But with some photos it’s there in the photo’s metadata information letting the future user know what orientation the camera of that mobile device was in as you took the photo. In order to help our animated GIF creator, the first slide image is scoured for Exif metadata and if found, a suitable rotation correction can be applied to the slides there and then. In order to scour for Exif metadata we needed to write a exif_rotation_check.php inhouse Exif detector PHP helper.
The PHP GD image library is so much more useful than for the “filters” interfaced to with yesterday’s Animated GIF Creator GD Filter Interfacing Tutorial. Under an “umbrella term” transformations, today we add interfacing to GD functionality …
Continuing on with the ImageMagick batch processing “mogrify” ideas of yesterday’s ImageMagick Batch Image Conversion Affine Transformation Tutorial and the “vignette” image editing ideas of Gimp Vignette Primer Tutorial, as below, today, we took some pet photos with an iPad’s Camera app and shared them off the Photos app via two Mail sharing option emails containing seven attachments each. Using an iPad, the JPEG “jpg” output files were too big for our inhouse Animated GIF Creator PHP web application to handle, and so to perform the …
animated GIF presentation, off these downloaded email photo attachments … and along the way …
quality adjusted them (yes, “mogrify” does not stuff JPG to JPG conversions, and we used “mogrify” -quality 20% switch here) … and …
rotate them 180 degrees (“mogrify” uses switches -affine -1,0,0,-1,0,0 -transform +repage here) … and …
… on the way to compiling into an animated GIF image, and we turned to ImageMagick again, using its affine transformation talents, along with its awesome vignetting talents …
… where the last two dropdown options will be similar, the last showing the input image into ImageMagick can be an animated GIF that is truely treated like an animated GIF.
The last time we talked about the miraculous, redolent and amazing image editor called Gimp am sure there was someone in a shower … it stands to reason … and one of those showerers, surely, would have been singing The Gimp Song … and if not … why not? … but we digress … anyway we had the Gimp Transparency Primer Tutorial as shown below go into some image transparency issues with Gimp.
In today’s tutorial we make use of a great tutorial (even so far as with direct quotes below) called Add a Vignette to a Photograph with GIMP (thanks) to try a photographic technique called vignetting on one of the photographs we added, recently, into the mix of those of the Custom Header Image mix at this blog … specifically the one of Nala, the dog, on the door ledge. Need to warn you here and now that if there was the time all over again, it would be better achieved that second time around, but this is not the point with learning, but rather getting some starting point with a great “product” like Gimp, and trying it yourself, once you have a method. It boils down to:
In the Layers dialog, click on your “Vg” layer to select it, and select Soft light from the “Mode” drop-down box
Right click on your “Vg” layer and go to Add Layer Mask. In the dialog that pops up, you want “Initialise Layer Mask to” set to “White (full opacity)”. Click “Add”
Below Opacity bar select Link icon next to Eye icon, which will already be showing
Use the freeform select tool (press F to bring this up) and draw a selection somewhere around the primary point of interest in your photo
Use your bucket tool (Shift+B) and click within the selection to fill it
Deselect your selection with Select->None
Go to Filters->Blur->Gaussian Blur. In the dialog that comes up, you want “Radius” set to a very large amount; a tenth of the longest edge of the photo is not too much
Click on your “Vg” layer to select it (if it isn’t already selected), and then slide the opacity slider towards the right until the effect is subtle enough. Our (ever so subtle) example of Nala, in the tutorial, used an opacity of about 66%
Click Export button in two windows (NB. this overwrites the image file, so if this is not desirable, export to a different image file name and/or type)
Here is a tutorial that adds to a previous Gimp Layers Primer Tutorial as shown below, and gives you more insight into the massive possibilities of using a sophisticated image editor and use layers with various amounts of transparency, especially suited to use with png image files.
Today’s tutorial where we construct a Birthday Card that needs tweaking for the words in front to be seen a bit more clearly, by making the image behind a bit more transparent, changes the transparency of a single image via:
Open Gimp graphical editor application
File->Open Layers … pick your image file
If Layers window not showing, make it show via Windows->Layers – Brushes
Below Opacity bar select Link icon next to Eye icon, which will already be showing
Change Opacity bar setting to a value of Transparency (100% is Opaque, 0% is Transparent) that suits … today we do 70%
File->Export
Click Export button in two windows (NB. this overwrites the image file, so if this is not desirable, export to a different image file name and/or type)
As with most Gimp ideas, jump in and give it a go, as you’ll find your own ways and means of using this great product … am pretty sure.
Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.
Here is a tutorial that gives you an insight into the massive possibilities of using a sophisticated image editor and use layers with various amounts of transparency, especially suited to use with png image files.
Transparency (or its obverse, opacity) can be used to have the one image achieve several “ends” (ie. purposes). Although it is a bit of a clumsy example in the tutorial, you can see that the technique can be used for artistic purposes … often called “Photoshopping” (named after the more famous, and also brilliant, rival product, Photoshop).
Lots of those classic “Photoshopping” techniques can be achieved in Gimp, and some other tutorials at this blog touch on that.
Am sure you can imagine what the concept of a layer is with regard to image manipulation. Within Gimp, for beginners not used to this concept, you find yourself underestimating and underplaying what can be achieved with the various layers of a multi-layered image. In simplistic terms each layer has the functionality in Gimp to be treated as a whole new image, and this is the best way to think of it when trying to achieve what you want to achieve with Gimp.
Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
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’ve long been interested in what used to be a cute feature of GUI widgetry desktop web applications (before any mobile anything) called “ComboBox” as talked about at Button Innards Primer Tutorial. To us, what a “ComboBox”‘s essence is …
Combination of interactively keyboard entry and/or a dropdown means of data definition
… and, like with that tutorial linked to above, it is an HTML button element we start with …
<button onclick="document.getElementById('ssave').focus();" id=bsave><span style='background-color:white;min-width:40px;padding:2 2 2 2;' onclick="event.stopPropagation();" id=ssave onblur=lsit(this); contenteditable="true"></span><select onclick="event.stopPropagation();" style='margin-left:100px;' contenteditable=yes onchange=recallsm(this); id=savedd><option value=''>Optionally enter a value ...</option><option value=1>1</option></select></button>
… combine, today, starting down, again, the road to “button innards” interest.
We thought the “how we got there” concept of embedding the form elements of data into “button innards” (ie. it’s innerHTML) in our new “button innards” web application interesting. Of course you don’t have to do this simple exercise of calculating time elapsed between two datetimes this way, but what we wanted to show was that with lots of things in web design, by now, there is more than one way to crack an egg …
Yesterday’s Making of Ffmpeg Video Overall Effects Tutorial‘s “first draft” feel of our inhouse Bookmark Export Filtering web application showed just that. Yes, there was quite a lot to improve on, the way we saw it being …
adding a (local system) file browsing means (called by our <iframe scrolling=no frameborder=0 id=cbi data-style=’border-top:1px solid black;border-bottom:1px solid black;border-left:2px solid yellow;border-right:1px solid yellow;’ style=’width:173px;height:228px;margin-top:-194px;’ src=’/HTMLCSS/client_browsing.htm?totype=html&d=189786754′></iframe> file browsing helper) …
function yesthreethree(restis) {
if (document.getElementById('bmkdata')) {
if (restis.indexOf('data:') == 0) {
if (restis.indexOf(';base64,') != -1) {
document.getElementById('bmkdata').value='' + window.atob(restis.split(';base64,')[1]);
analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
} else if (restis.indexOf(';utf8,') != -1) {
document.getElementById('bmkdata').value='' + (restis.split(';utf8,')[1]);
analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
}
} else {
document.getElementById('bmkdata').value=restis;
analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
}
}
}
… by which to add Web Browser Bookmark Exporting Data File Content into that lower HTML textarea element (and unlock the two UL/LI and Table button elements) … as well as …
add a details/summary “reveal” means by which the user can show working actual HTML elements in addition to the HTML coding we’ve only be showing (with that “first draft”)
add Sharing functionality via either Email or SMS conduits
… and because the amounts of data can be large (and too big for a method=GET HTML form arrangement that clientside HTML/Javascript is capable of servicing) we were glad adding this Sharing functionality that yesterday we’d already started down the line of thinking that the …
lower HTML textarea content … would always be associated with …
hashtag … representation
… because with these methodologies the web server restrictions on (address bar) URL lengths (ie. error 414) melt away, at least for data lengths we’ve encountered with our testing so far, and that applies also to …
“mailto:” “a” URL links to Email a recipient … or …
“sms:” “a” URL links to SMS a recipient
… Sharing …
var mysubject='My Bookmarked Links ...', xlocationhref='';
if (('' + location.hash).indexOf('bmkdata=') != -1) { locationhash=location.hash; }
function doemail() {
if (document.getElementById('bmkdata').value.trim() != '') {
locationhash='#bmkdata=' + window.btoa(encodeURIComponent(document.getElementById('bmkdata').value));
mysubject='My Bookmarked Links ...';
if (document.getElementById('dtone').value.trim() != '' && document.getElementById('dttwo').value.trim() != '') {
mysubject='My Bookmarked Links from ' + document.getElementById('dtone').value.trim() + ' to ' + document.getElementById('dttwo').value.trim() + ' ... ';
} else if (document.getElementById('dtone').value.trim() != '') {
mysubject='My Bookmarked Links from ' + document.getElementById('dtone').value.trim() + ' ... ';
} else if (document.getElementById('dttwo').value.trim() != '') {
mysubject='My Bookmarked Links up until ' + document.getElementById('dttwo').value.trim() + ' ... ';
}
if (ulli && !utable) {
recipient=prompt('Please enter email address to send to? Will output UL/LI else if some blanks will output Table. Default email Subject is ' + mysubject + ' ... but if you want a different one append a hash (#) followed by your own Subject for the Email.', '');
if (recipient == null) { recipient=''; } else if (recipient.indexOf('@') == -1) { recipient=''; } else if (eval('' + recipient.indexOf('#')) > eval('' + recipient.indexOf('@'))) { mysubject=recipient.split('#')[1]; recipient=recipient.split('#')[0]; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='mailto:' + recipient.replace(/\ /g,'') + '?subject=' + encodeURIComponent(mysubject) + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
} else if (utable) {
recipient=prompt('Please enter email address to send to? Will output UL/LI else if some blanks will output Table. Default email Subject is ' + mysubject + ' ... but if you want a different one append a hash (#) followed by your own Subject for the Email.', '');
if (recipient == null) { recipient=''; } else if (recipient.indexOf('@') == -1) { recipient=''; } else if (eval('' + recipient.indexOf('#')) > eval('' + recipient.indexOf('@'))) { mysubject=recipient.split('#')[1]; recipient=recipient.split('#')[0]; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='mailto:' + recipient.replace(/\ /g,'') + '?subject=' + encodeURIComponent(mysubject) + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
}
}
}
function dosms() {
if (document.getElementById('bmkdata').value.trim() != '') {
locationhash='#bmkdata=' + window.btoa(encodeURIComponent(document.getElementById('bmkdata').value));
if (ulli && !utable) {
recipient=prompt('Please enter SMS number to send to? Will output UL/LI else if some blanks will output Table.', '');
if (recipient == null) { recipient=''; } else if (recipient.trim() != '' && recipient.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') != '') { recipient=''; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='sms:' + recipient.replace(/\ /g,'') + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
} else if (utable) {
recipient=prompt('Please enter SMS number to send to? Will output UL/LI else if some blanks will output Table.', '');
if (recipient == null) { recipient=''; } else if (recipient.trim() != '' && recipient.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') != '') { recipient=''; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='sms:' + recipient.replace(/\ /g,'') + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
}
}
}
function fillin() {
if (document.getElementById('rt') && document.getElementById('rd') && document.getElementById('rs')) {
if (document.getElementById('rd').innerHTML == document.getElementById('rs').outerHTML) {
document.getElementById('rd').innerHTML=document.getElementById('rs').outerHTML + document.getElementById('rt').value;
}
}
}
// Coming back from an email or SMS link click ...
… concepts, happily. Keeping it all “just clientside” means we can be talking meaningfully to a larger chunk of audience … if you’re listening, that is?!
… of links, am sure a lot of you would guess, were gleaned (within our Google Chrome web browser History (of that day)) Bookmarked (by us) webpages. We actually compiled via (the clunky and fairly slow) …
left half (of screen) with (Google Chrome) History showing, and right half with BBEdit Text Editor open at a new (ostensibly empty) file with loads of empty records available, and dragged URL records (identifiable, for us, via an asterisk, indicating a webpage was Bookmarked) from the left into the right to bring over the URLs (only) above, the rest constructed in a more manual way … and another way could have been …
use Google Chrome webpage showing Bookmark list and use its Export Bookmarks (no other choice but “All”) to a file that could be waded through to derive the list above … and then we thought of chance to code for …
hybrid “inhouse” clientside (ie. HTML/Javascript/CSS) web application …
… so far asking for the content of a web browser Export Bookmarks operation’s output file’s HTML (in the case of Google Chrome) content in an HTML textarea element (the user can fill in themselves), and filterable via two input type=datetime-local starting and ending datetime(s) of interest (the user can fill in, optionally)
… and we wish we could have got that hour and a half back, from yesterday, via swapping yesterday for today?!
Not all web browser brands have any Export Bookmarks functionality, like Google Chrome offers here on macOS. We’re going to be examining others, over time, and try to make our inhouse logic fit in with how that Bookmark data looks, on export, should we find any other Web Browser brands offering this functionality.
Today’s work (all on our macOS MAMP local Apache/PHP/MySQL web server environment) is only “toe dipping” in a “great big sea of possibilities” regarding the great ffmpeg‘s “filtering style abilities” further to yesterday’s Ffmpeg Video Subliminal Message Tutorial, but …
as you try out things you discover others …
can be a foot in the door researching this type of work yourself …
… we’re hoping. And it’s not as if ffmpeg was on it’s own here with the second concept below …
fade in and fade out video effect … inspired by Top 20 best commands for FFmpeg to try …
ffmpeg -i brush__turkey.m4v -vf "fade=in:0:5,fade=out:73:5" brushturkey_fadein_fadeout.m4v
colour balancing improvements regarding an underexposed video
… as the …
Gimp image editor’s abilities (matching a lot of what you read Photoshop can help with too) with Curves helped out … along with …
Python expertly designed to link Gimp and ffmpeg “whole of video” manipulations
… enacting …
selection of a video still image, ideally not containing the Brush Turkey …
we were inspired by a Brush Turkey which we videoed via an iPhone’s Camera app’s Video mode of capture option …
downloaded to this MacBook Air (via AirDrop … good for Apple sharing when the sizes get big … raw video 1:19s long)
And that’s where we are at here, thinking this would be a good opportunity to try creating a Subliminal Message video. But before we go on and “give the game away” you may want to see the result of our Subliminal Message insertion via ffmpeg theme to our work, in action, below. Unlock or give up to unlock the content below the video quiz below …
Congratulations! Yes, in between the “Raw Video” ffmpeg work and onto the other ffmpeg commands we renamed one slide to a *.jpeg equivalent and then set about replacing the *.jpg original slide with our Subliminal Message slide, as you can see here.
The work involves both of the HTML design “big concepts” we are keen on at this blog …
overlay … via CSS control of …
position:absolute;top:0px;left:0px;
opacity:1.0;
z-index:1;
reveal … lately, mostly, via HTML use of …
details
summary
… the latter being the “container” for our interactive input be able to control overlay items 2 and 3, which affect the “overlayed images” output display via the clicking of a new “Overlay Images” button. That button …
… one reason being that we do not want to install the wonderful ffmpeg (command line video creation tool) on the RJM Programming domain, but, in macOS, here with MAMP, we are quite happy to live with the Homebrew (Terminal application’s) install …
… setting up what could be “lost functionality”, but today, we come around to either adding a new …
Convert to Video
… dropdown option should all be ritchy ditch, and if not, often we will add new “Advice” dropdown options to remind the user what they’d need to arrange to get to a “Convert to Video” scenario.
Selecting “Convert to Video” sets up a hydrid “Internet/Intranet” feeling scenario where …
… be nested in PHP exec (via MAMP local web server incarnation of the inhouse Animated GIF Creator) as a way to create videos …
which can be downloaded within that public domain “parent” inhouse Animated GIF Creator session
Which begs the question …
How do we know when to offer the "Convert to Video" option on that dropdown?
This logic is centred around a few useful ideas (with this cross domain scenario ruling out Ajax and Iframe src= definitions, as useful ways to go) …
open the MAMP local web server “HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php” URLs in an iframe (with onload event logics) pointed at by a window.open second argument (effectively avoiding any loose useful or not popup windows hanging around) …
the most we can ask at the receiver is that window.opener is defined … and if so, just at that discovery …
an image called “amhere.jpg” is created via PHP GD functionality … and back at the “public domain” parent within the iframe onload logic (where with contentWindow or contentDocument do not expect a document.body or even a document) …
we attempt to “hotlink” that MAMP local web server image … as per …
var tryit='http://localhost:8888/PHP/animegif/amhere.jpg';
document.getElementById('ctvopt').value='advice';
document.getElementById('ctvopt').innerHTML='Advice on Convert to Video';
if (tryit != '') {
var im=new Image();
im.onload = function() {
document.getElementById('ctvopt').value='video';
console.log('this.height=' + eval('' + this.height));
if (eval('' + this.height) >= 20) {
document.getElementById('ctvopt').innerHTML='Convert to Video';
} else {
document.getElementById('ctvopt').innerHTML='Convert to Video (but ffmpeg not installed or in unexpected place)';
}
document.getElementById('imsel').title='All except Convert to Video, which needs ffmpeg installed, use ImageMagick';
};
… and check the code for the validity of any ImageMagick paths … and if not all these conditions, simulate the same and cobble it together in the code
… one reason being that we do not want to install the wonderful ffmpeg (command line video creation tool) on the RJM Programming domain, but, in macOS, here with MAMP, we are quite happy to live with the Homebrew (Terminal application’s) install …
brew install ffmpeg
… to open a whole new woooooorrrrlllllddd of video creation opportunities using this MAMP local web URL …
HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php# Animated GIF Creator on MAMP local web server
… we tweaked out of “video/mp4” ffmpeg created videos thinking towards “video/webm” or “video.mov” ffmpeg created videos that suit the “few frames but spaced out video” we wanted to achieve for all practical purposes, that “video/mp4” playing too fast for us to see anything much but the first and last slide …
As a web application programmer we like buttons. There are “buttons” and there are buttons. Yes, there is an HTML button element, and it and the input type=button element render as that part of a webpage most recognizable to users the world over. You click or tap this button and something usually happens.
But we also enjoy “Emoji Buttons” for we graphically challenged programmers. Using an emoji text and graphic can make span elements or p elements or lots of other HTML elements that have an innerHTML property, be like a very succinct button like entity, also appreciated around here because it takes up so little webpage “real estate”.
And so onto yesterday’s Animated GIF Creator PDF Order Tutorial we set about, today, “revealing” any enduring animated GIF and/or PDF created during a previous session, using Emoji Buttons as the email/SMS sharing action buttons.
We say “revealing” because, like the way “Emoji Buttons” save webpage “real estate”, so can the use of the HTML5 details/summary element combination. It is in that summary “enduring” header part of that combination we can place some “Emoji Buttons”. You will (once you start creating and sharing animated GIFs and/or PDFs) see from our changedtutorial_to_animated_gif.php inhouse Animated GIF Creator (helped out by a changedemailhtml.php inhouse email creator helper serverside PHP web application) that we place three Emoji Buttons …
📟 SMS (Animated GIF or PDF) Limited relevance period (ie. no data URIs involved, so rely on an absolute URL whose content might change or start not to exist down the track) … via “a” “sms:” link click/tap
📧 Email (Animated GIF or PDF) Limited relevance period (ie. no data URIs involved, so rely on an absolute URL whose content might change or start not to exist down the track) … via “a” “mailto:” link click/tap
📧 Email (Animated GIF or PDF) Enduring relevance (ie. data URIs involved, so do not rely on any absolute URL whose content might change or start not to exist down the track) … via PHP mail function means of sending email with HTML attachment containing the relevant Animated GIF or PDF
Yes, “Emoji Buttons” can have their size controlled but not by the usual CSS width and height properties, but by the CSS font-size property. Here is the new Javascript function that those Emoji Button “onclick” logic points to …
<?php eho ”
function askes(isemail, isoab) {
var esask='', izhr=null, izform=null;
var isoa=document.getElementById(isoab.id.replace('b',''));
if (isemail) {
esask=prompt('Please enter email address to send this to', '');
if (esask != null) {
if (esask.indexOf('@') != -1) {
if (isoa.outerHTML.indexOf('<img') == 0) {
//alert('More Animated GIF to come');
izhr = new XMLHttpRequest();
izform=new FormData();
izform.append('to', esask.trim());
izform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . ".gif" . "');
izform.append('subj', 'My Latest Animated GIF via RJM Programming ... ');
izform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><img style=\"width:100%;height:900px;\" src=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . ".gif" . "\"></img></div></body>'));
izhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
izhr.send(izform);
} else if (isoa.outerHTML.indexOf('<a') != 0) {
//alert('More PDF to come');
izhr = new XMLHttpRequest();
izform=new FormData();
izform.append('to', esask.trim());
izform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . "_animegif.pdf" . "');
izform.append('subj', 'My Latest PDF via Animated GIF via RJM Programming ... ');
izform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "_\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><object style=\"width:100%;height:900px;\" type=\"application/pdf\" data=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . "_animegif.pdf" . "\"></object></div></body>'));
izhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
izhr.send(izform);
} else {
isoa.href='mailto:' + esask.trim() + '?' + isoa.href.split('?')[1];
isoa.click();
}
}
}
} else {
esask=prompt('Please enter SMS number to send this to', '');
if (esask != null) {
if ((esask.trim() != '' && esask.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '')) {
isoa.href='sms:' + esask.trim() + '&' + isoa.href.split('&')[1];
isoa.click();
}
}
}
}
Order becomes an interesting subject to us today regarding the PDF conversions possible in amongst the Animated GIF creating of yesterday’s Animated GIF Creator PDF Share Tutorial.
That is because we may have some advanced users out there that want that possibility of differentiating the data of …
animated GIF … containing all features asked for, in totality … from the chance for the user to have any of the PDF conversion option sets below …
PDF … one of …
no PDF conversion
full PDF conversion … with the user choices regarding raw image content and title and watermarking and ImageMagick and GD modifiers
light PDF conversion … with the user choices regarding just raw image content (the only option out of these last three available before today’s work)
medium PDF conversion … with the user choices regarding just ImageMagick and GD modifiers
… perhaps as a “before and after” tool regarding presentations, maybe?
No dropdowns used here (enforcing an order), just the user’s order in which they choose to select the “PDF conversion” option from the ImageMagick dropdown (in relation to other selections) determining how and when this PDF conversion occurs in the workflow through to creating the animated GIF with our changedtutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application. Just remember to select PDF Conversion as early as possible to do that “light PDF conversion” option above and last thing if you are interested in “full PDF conversion” (where you can create PDF documents with the bells and whistles ImageMagick and GD modifiers can offer) and today’s tutorial picture is an example of “medium PDF conversion” (when we ordered our settings by first Grayscale, second PDF conversion and last title and watermarking options). Clicking or tapping the ImageMagick link can get the user to the Javascript popup window where they might define the email or SMS recipient for that PDF conversion data file download.
What sharing conduits do we code for? We always intended …
email … but as the day wore on trying to get the usage to work on mobile and non-mobile we decided to relinquish our wish to not have to create a user specific enduring (until that same browser type and user combination share) PDF document for the user request … mainly to get mobile email downloads to be friendly … and so this opened the door for …
SMS … to access that enduring PDF as a URL in the SMS message (that becomes a link for the recipient)
<?php echo ”
function emailhtmlit() {
var induri=ginduri;
var pemail='" . urldecode($_GET['outpdf']) . urldecode($_POST['outpdf']) . "';
if (pemail != null) {
if (pemail.indexOf('@') != -1 || (pemail.trim() != '' && pemail.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '')) {
if (induri.trim() == '') {
if (induri == '') {
document.getElementById('pdfproposed').src='./animegif.pdf?rand=' + Math.floor(Math.random() * 19854654);
} else {
ginduri='found';
}
setTimeout(emailhtmlit, 15000);
} else {
ginduri='';
var zhr = new XMLHttpRequest();
var zform=new FormData();
if (pemail.indexOf('@') != -1) { zform.append('to', pemail); }
zform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . "animegif.pdf" . "');
zform.append('subj', 'My PDF version of Animated GIF via RJM Programming ... ');
zform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "_\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><object style=\"width:100%;height:900px;\" type=\"application/pdf\" data=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"><embed style=\"width:100%;height:900px;\" type=\"application/pdf\" src=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"></embed></object></div></body>'));
//zform.append('tdhuhta', ('<body><iframe srcdoc=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"></iframe></body>'));
zhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
zhr.send(zform);
if (pemail.indexOf('@') == -1) {
var hrefp=document.getElementById('pdfsms').href.split('&body=')[0];
hrefp+=pemail.trim() + '&body=' + document.getElementById('pdfsms').href.split('&body=')[1];
document.getElementById('pdfsms').href=hrefp;
document.getElementById('pdfsms').click();
}
}
}
}
}
“; ?>
… and this called on our PHP email creator helper to better interface in its $_POST arguments reading section in our changedemailhtml.php inhouse email creator helper serverside PHP web application …
We have more “reliability work” and “email sharing work” to go after today’s start, but it primarily called on ImageMagick command line’s talent for a command like …
convert /tmp/imtmp0*.*[gGmMiI]* /tmp/imtmp000.pdf
… to “concatenate” into the one output PDF file (called “/tmp/imtmp000.pdf”) the slides, arranged by our code into that “/tmp/imtmp0*.*[gGmMiI]*” (file specification) arrangement above. Yes, we meant “convert” above, as “mogrify” (batch work) appears not to be able to perform this task.
Animated GIF Creator Slide Specific Application Tutorial
It’s all fine and good improving on the ImageMagick and GD and Exif functionality modifications like with yesterday’s Animated GIF Creator Exif Rotation Compensation Tutorial, but in reality, if you are going to start creating animated GIFs to explain a process, you are going to want to apply these “slide modifiers” on a slide by slide basis, rather than enforcing a “whole of animated GIF slide set” paradigm, as for the last few days worth of work.
And so, we decided to do what we often do, “sliding in” more functionality (chortle, chortle). We tend to want to …
start with hardcoded text (or element) … somewhere … today it happens to be in an HTML span element that once involved just …
<span id="smyim"></span>
… and used to get filled, Javascript DOM wise, when needed via …
document.getElementById('smyim').innerHTML='ImageMagick switches: '; // and yes, it remains that way even now, but read on ...
add intelligence (quite often that being onclick logic(s)) to that hardcoded element via …
<span id=smyim title=Application onclick=applyto(); style=cursor:pointer;text-decoration:underline;></span>
that serves the purpose, as the user clicks/taps it (alerted to that fact, perhaps, because we underline the element and add an appropriate cursor when hovering over it (plus a title)), of calling Javascript …
function applyto() {
var huhto=prompt('Apply ImageMagick and/or GD to which slides, in comma separated list, counting starting with 1? Defaults to applying to all slides. Comma delimit. Negatives mean all but. Ranges can be specified. For example ... 2,4-7,9', document.getElementById('appliedto').value);
if (huhto == null) { huhto=''; }
if (huhto.trim() == '') {
document.getElementById('appliedto').value='';
document.getElementById('smyim').title='Application';
} else {
document.getElementById('appliedto').value=huhto.trim();
document.getElementById('smyim').title=huhto.trim();
}
}
to glean an (often times out of the normal workflow of the web application) informational piece of data, interactively, from the user, via a Javascript prompt popup
So that’s the clientside of this work … “alerting the user to the existance of the functionality” you might say.
And then there’s “the application” of that nuanced user requirement. And that’s where the “inhouse ‘our’ prefix to wrapper function name paradigms” come in handy. We introduced “blanket” functionality thoughts via this approach, and so to “partially undo” that thinking, we make the “our” prefix conditional, as is available to us with our changedtutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application (helped out by a changedexif_rotation_check.php inhouse Exif detector PHP helper), as per …
<?php
function ourcomplicated($inio, $iappl) {
$ideasl=explode(",", str_replace(' ','',$iappl));
$xour="our";
for ($iqa=0; $iqa<sizeof($ideasl); $iqa++) {
if (trim($ideasl[$iqa]) != '') {
$xour="";
if (('-' . $inio) == trim($ideasl[$iqa])) { return ""; }
$ideasr=explode("-", str_replace(' ','',trim($ideasl[$iqa])));
for ($iqb=0; $iqb<sizeof($ideasr); $iqb++) {
if (('' . $inio) == trim($ideasr[$iqb])) {
return "our";
}
if ($iqb == 1) {
if (trim($ideasr[1]) == "") { $ideasr[1]="99999999"; }
if ($inio >= $ideasr[0] && $inio <= $ideasr[1]) { return "our"; }
}
}
}
}
return $xour;
}
We use these “new abilities” better explaining “the abscence or otherwise of Exif checking”, and the implications of that in the animated GIF creator woooooorrrrrrlllllddd, contrasting the first two slides, showing one with “No Exif checking” (the bad old days) versus “With Exif checking” (the renaissance of liberated thinking in the South South East woooorrrrrlllldddd) in today’s animated GIF presentation.
Exchangeable image file format (officially Exif, according to JEIDA/JEITA/CIPA specifications) is a standard that specifies the formats for images, sound, and ancillary tags used by digital cameras (including smartphones), scanners and other systems handling image and sound files recorded by digital cameras.
But with some photos it’s there in the photo’s metadata information letting the future user know what orientation the camera of that mobile device was in as you took the photo. In order to help our animated GIF creator, the first slide image is scoured for Exif metadata and if found, a suitable rotation correction can be applied to the slides there and then. In order to scour for Exif metadata we needed to write a exif_rotation_check.php inhouse Exif detector PHP helper.
The PHP GD image library is so much more useful than for the “filters” interfaced to with yesterday’s Animated GIF Creator GD Filter Interfacing Tutorial. Under an “umbrella term” transformations, today we add interfacing to GD functionality …
Continuing on with the ImageMagick batch processing “mogrify” ideas of yesterday’s ImageMagick Batch Image Conversion Affine Transformation Tutorial and the “vignette” image editing ideas of Gimp Vignette Primer Tutorial, as below, today, we took some pet photos with an iPad’s Camera app and shared them off the Photos app via two Mail sharing option emails containing seven attachments each. Using an iPad, the JPEG “jpg” output files were too big for our inhouse Animated GIF Creator PHP web application to handle, and so to perform the …
animated GIF presentation, off these downloaded email photo attachments … and along the way …
quality adjusted them (yes, “mogrify” does not stuff JPG to JPG conversions, and we used “mogrify” -quality 20% switch here) … and …
rotate them 180 degrees (“mogrify” uses switches -affine -1,0,0,-1,0,0 -transform +repage here) … and …
… on the way to compiling into an animated GIF image, and we turned to ImageMagick again, using its affine transformation talents, along with its awesome vignetting talents …
… where the last two dropdown options will be similar, the last showing the input image into ImageMagick can be an animated GIF that is truely treated like an animated GIF.
The last time we talked about the miraculous, redolent and amazing image editor called Gimp am sure there was someone in a shower … it stands to reason … and one of those showerers, surely, would have been singing The Gimp Song … and if not … why not? … but we digress … anyway we had the Gimp Transparency Primer Tutorial as shown below go into some image transparency issues with Gimp.
In today’s tutorial we make use of a great tutorial (even so far as with direct quotes below) called Add a Vignette to a Photograph with GIMP (thanks) to try a photographic technique called vignetting on one of the photographs we added, recently, into the mix of those of the Custom Header Image mix at this blog … specifically the one of Nala, the dog, on the door ledge. Need to warn you here and now that if there was the time all over again, it would be better achieved that second time around, but this is not the point with learning, but rather getting some starting point with a great “product” like Gimp, and trying it yourself, once you have a method. It boils down to:
In the Layers dialog, click on your “Vg” layer to select it, and select Soft light from the “Mode” drop-down box
Right click on your “Vg” layer and go to Add Layer Mask. In the dialog that pops up, you want “Initialise Layer Mask to” set to “White (full opacity)”. Click “Add”
Below Opacity bar select Link icon next to Eye icon, which will already be showing
Use the freeform select tool (press F to bring this up) and draw a selection somewhere around the primary point of interest in your photo
Use your bucket tool (Shift+B) and click within the selection to fill it
Deselect your selection with Select->None
Go to Filters->Blur->Gaussian Blur. In the dialog that comes up, you want “Radius” set to a very large amount; a tenth of the longest edge of the photo is not too much
Click on your “Vg” layer to select it (if it isn’t already selected), and then slide the opacity slider towards the right until the effect is subtle enough. Our (ever so subtle) example of Nala, in the tutorial, used an opacity of about 66%
Click Export button in two windows (NB. this overwrites the image file, so if this is not desirable, export to a different image file name and/or type)
Here is a tutorial that adds to a previous Gimp Layers Primer Tutorial as shown below, and gives you more insight into the massive possibilities of using a sophisticated image editor and use layers with various amounts of transparency, especially suited to use with png image files.
Today’s tutorial where we construct a Birthday Card that needs tweaking for the words in front to be seen a bit more clearly, by making the image behind a bit more transparent, changes the transparency of a single image via:
Open Gimp graphical editor application
File->Open Layers … pick your image file
If Layers window not showing, make it show via Windows->Layers – Brushes
Below Opacity bar select Link icon next to Eye icon, which will already be showing
Change Opacity bar setting to a value of Transparency (100% is Opaque, 0% is Transparent) that suits … today we do 70%
File->Export
Click Export button in two windows (NB. this overwrites the image file, so if this is not desirable, export to a different image file name and/or type)
As with most Gimp ideas, jump in and give it a go, as you’ll find your own ways and means of using this great product … am pretty sure.
Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.
Here is a tutorial that gives you an insight into the massive possibilities of using a sophisticated image editor and use layers with various amounts of transparency, especially suited to use with png image files.
Transparency (or its obverse, opacity) can be used to have the one image achieve several “ends” (ie. purposes). Although it is a bit of a clumsy example in the tutorial, you can see that the technique can be used for artistic purposes … often called “Photoshopping” (named after the more famous, and also brilliant, rival product, Photoshop).
Lots of those classic “Photoshopping” techniques can be achieved in Gimp, and some other tutorials at this blog touch on that.
Am sure you can imagine what the concept of a layer is with regard to image manipulation. Within Gimp, for beginners not used to this concept, you find yourself underestimating and underplaying what can be achieved with the various layers of a multi-layered image. In simplistic terms each layer has the functionality in Gimp to be treated as a whole new image, and this is the best way to think of it when trying to achieve what you want to achieve with Gimp.
Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
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.
… called and applicable via yellow span element ondblclick event logic …
function othercals(ospan) {
var doall=false, spanos=[ospan], relv=false;
if (ospan.outerHTML.indexOf(' data-julian=') != -1) {
var thep=prompt('Can put other calendar type information after first three dots, as required regarding ' + ospan.outerHTML + ' if you enter a suitable data- prefixing answer? Append answer by ++ to apply to all the yellow span elements.', '');
if (thep == null) { thep=''; } else { thep=thep.toLowerCase(); }
if ((thep + '~').indexOf('++~') != -1) { doall=true; thep=thep.replace(/\+\+$/g,''); spanos=document.getElementsByTagName('span'); }
for (var ii=0; ii<spanos.length; ii++) {
if (spanos[ii].outerHTML.indexOf(' data-' + thep.replace(/data\-/g,'') + '="') != -1) {
relv=true;
spanos[ii].innerHTML=spanos[ii].innerHTML.replace('...', '... ' + spanos[ii].outerHTML.split(' data-' + thep.replace(/data\-/g,'') + '="')[1].split('"')[0] + ' ');
spanos[ii].title=spanos[ii].title.replace(' other calendar types', ' other calendar types, such as your ' + thep.replace(/data\-/g,'').substring(0,1).toUpperCase() + thep.replace(/data\-/g,'').substring(1).toLowerCase().replace(/y$/g,''));
}
}
if (relv) {
changesmade=true;
inplay=true;
lastinplayc=document.body.innerHTML + ' ';
}
}
}
sharing … and caring and we all know sharing can lead to …
collaboration
… and today we’re encouraging users to join in with others sharing editable calendars via email (and though the hashtagging goes beyond SMS lengths we’re including that as an option too, and we may find a way down the track that the lengths of data can be reduced to suit an SMS message …
… is our go to regarding the internationalization and language translation functionality. We get to Google Translate, where permitted (up to you, and whether you have popup blockers happening with the web browser you are using), via a popup window, which means, in that scenario, there are two distinct domains in play …
Be listening out at the RJM Programming domain document.URL scenario “parent” window …
window.addEventListener("message", (event) => { // thanks to https://www.google.com/search?q=Undef+what+conditions+can+window.opener.parentfunc()+type+of+call+work+when+the+domains+are+different%3F&rlz=1C5OZZY_en&oq=Undef+what+conditions+can+window.opener.parentfunc()+type+of+call+work+when+the+domains+are+different%3F&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCTI2NzQwajBqN6gCALACAA&sourceid=chrome&source=chrome.ob&ie=UTF-8
var thislang='';
// Always verify the origin of the sender for security
if (event.origin !== "https://www-rjmprogramming-com-au.translate.goog") return;
Today is Styling Day regarding our current Editable Calendar project we last talked about with yesterday’s Editable Calendar Save and Recall Tutorial. We cannot recall ever arranging the body element background exactly as we did (probably because we only had the top and the bottom of the screen for context, the middle being the calendar) in the CSS below …
body {
background-image: linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0.8)),url(https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSAMB0R0_H50ZU4AW0t51IqB0WwOyF1BEmSPy3MMxisNythxHs69b4lcx8&s=10);
background-size: contain;
background-repeat: repeat;
}
</style>
… where some judiciously placed background-color:transparent helps this out with transparency ideas. We find applying border-radius a good way to “soften” the look, and “text-shadow” as a way to allow for a 3D look to innerHTML contenteditable=true types of elements.
Get arguments are those ? or & delimited parts to URLs you sometimes see up at the address bar of web browsers. We’d be lost without them. And, though Google Translate uses it’s own set arrangement of get arguments for this “static URL” language translation mode of use we’ve been using in this Editable Calendar recent project, it does not mean that Google Translate will stop you also using any of your own “personalized” get argument arrangements as well, that it just ends up “sending through to the keeper” …
if (document.URL.indexOf('month=') != -1 && document.URL.indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + document.URL.split('year=')[1].split('&')[0].split('#')[0]), eval('' + document.URL.split('month=')[1].split('&')[0].split('#')[0]), 1);
} else if (('' + parent.document.URL).indexOf('month=') != -1 && ('' + parent.document.URL).indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + ('' + parent.document.URL).split('year=')[1].split('&')[0].split('#')[0]), eval('' + ('' + parent.document.URL).split('month=')[1].split('&')[0].split('#')[0]), 1);
}
… to it, making it editable and open to more personalization regarding your Editable Calendar, on the way to what we hope ends up being a more useful web application tool … while …
that HTML caption element’s onblur event …
var now = new Date(), firstDayIndex=0, daysThisMonth=0;
var origih=''; // is filled with innards of the original calendar table element at document.body onload
var calcapv=''; // is updated with HTML caption element innards before any user interaction
var usuff=''; // personalized get arguments go here
var changesmade=false; // goes to true when user blurb onkeypress happens within calendar tbody span elements or if the Month, Year changes
function newmy(ocap) {
if (ocap.innerHTML != calcapv) {
if (ocap.innerHTML.indexOf(',') != -1) {
if (mths.indexOf(ocap.innerHTML.split(',')[0].trim()) != -1 && eval('' + ocap.innerHTML.split(',')[1].trim().length) == 4) {
//changesmade=true;
usuff='&year=' + ocap.innerHTML.split(',')[1].trim() + '&month=' + mths.indexOf(ocap.innerHTML.split(',')[0].trim());
now = new Date(eval('' + ocap.innerHTML.split(',')[1].trim()), eval('' + mths.indexOf(ocap.innerHTML.split(',')[0].trim())), 1);
document.getElementById('wp-calendar').innerHTML=origih;
thismonth();
}
}
}
}
… can be used to store our “personalized get arguments” …
ready to be appended to the (web browser address bar) URL reaching Google Translate …
var valvalue='';
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out …
… we referenced when presenting Editable Calendar Google Translate Tutorial before the recent Editable Calendar Intl Tutorial? Today’s work plonks them back in by involving PHP, as our server side language that can write to and read from ascii text files stored on our RJM Programming domain web server. By, when needed, getting Google Translate to, when required (ie. we’ve decided this is when the keyboard onkeypress happens), reference a .php based RJM Programming URL, leaves that opportunity to …
Shape to write the calendar table innards to an ascii web server text file via Ajax … client side …
function ajaxit() {
var zhr = new XMLHttpRequest();
var zform = new FormData();
zform.append('tihis', document.getElementById('wp-calendar').innerHTML);
zhr.open('post', '/' + docURL.split('?')[0].split('#')[0].replace('.html','.php').replace('.htm','.php'), true);
zhr.send(zform);
}
Write the calendar table innards to an ascii web server text file via PHP … server side …
<?php
if (isset($_POST['tihis'])) {
file_put_contents('tihis.txt', str_replace('+',' ',urldecode($_POST['tihis'])));
exit;
}
?>
As needed, very soon after, as Google Translate is called with a .php based URL, substitute this saved table innards into a read from default representation of the HTML via PHP … server side …
Leaving any document.body onload event logics out (in first codeline of such onload event Javascript function), in such a scenario, leaving it all to Google Translate’s talents … client side …
if (('' + parent.document.URL).indexOf('.php') != -1) { return ''; }
Because the theme of our current Editable Calendar web application revolves around time stamps and day names and month names regarding yesterday’s Editable Calendar Google Translate Tutorial that means, regarding language internationalization ideas …
as well as yesterday’s Google Translate ideas … also available is …
Yesterday’s Editable Calendar Primer Tutorial‘s start to our Editable Calendar project was a very anglo-centric exercise. Today, we want to use the great …
… to help translate our inhouse burgeoning Editable Calendar skeletal contents to some non English languages.
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out in this changedsecond draftEditable Calendar you can try below, because we’re using the Google Translate “static URL” approach to the translations. But we will examine, down the track, if we can do better?! Again, stay tuned for more progression and internationalization over the days on this Editable Calendar project.
we’re channelling how in emails “word strings” starting with “http” become links … and so …
in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available
Here is the relevant Javascript we ended up with, for this …
Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!
When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …
When it comes to procedures …
Aaaaaarrrrgggghhhh …
Nuances count … regarding …
Errrrrrr …
Saving time …
Saving frustration
… resulting in the “easy to remember” acronym WANESS … we rest our cases!
What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.
Global variables initialization …
var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …
if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …
function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}
function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic …
function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}
Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changedevents_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!
it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs
… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!
Timing became really important with this integration of …
email
SMS
… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …
Month
Year
… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …
Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons …
<h1>Events in Month <button style=display:none; title='Share via Email' id=semail onclick="doemail('');">📧</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>📟</button> </h1>
New static HTML div element placed to the bottom of the body element …
<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …
Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to …
Record to Remember via Bookmark
… use the web browser Bookmarks to help you recall an Events in Month report with long entries.
Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …
… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!
To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …
var documentURL=document.URL;
… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …
#August__2023_00000__1
… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).
Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).
Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …
… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.
The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …
<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm
But, there’s an inherent weakness with the design, we’ll go into more into the future.
… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.
Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.
Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …
bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
pick Inspect option …
be in Elements tab of your Web Inspector … and …
highlight top <html> tag …
Context Menu with a right click or two finger gesture …
pick Copy -> Copy outerHTML … so that …
your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)
The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …
function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}
… the form submit buttons are reworded accordingly.
Another idea from this blog thread’s inspiration …
… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.
And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …
text shadow means by which the text of Event Calendar can be more impactive …
<div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
PHP macOS say Supervising Google Translate Sharing Tutorial
Today’s integration of the Javascript clientside window.postMessage into …
our inhouse PHP macOS say Supervisor, or Voiceover Ideas, talked about at PHP macOS say Supervising a Retrained Siri Nuance Tutorial, regarding it’s Google Translate dropdown option (used as a Voiceover Idea both with the macOS PHP MAMP environment and the live RJM Programming version) … benefits from the recent …
… cross domain concepts, which is used, here, as of today, in the usage scenario we’ll outline later, whereby English to Non-English language translations become a possibility into the “functionality mix”
<?php echo ”
window.addEventListener(\"message\", (event) => { // thanks to https://www.google.com/search?q=Undef+what+conditions+can+window.opener.parentfunc()+type+of+call+work+when+the+domains+are+different%3F&rlz=1C5OZZY_en&oq=Undef+what+conditions+can+window.opener.parentfunc()+type+of+call+work+when+the+domains+are+different%3F&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCTI2NzQwajBqN6gCALACAA&sourceid=chrome&source=chrome.ob&ie=UTF-8
// Always verify the origin of the sender for security
if (event.origin !== \"https://www-rjmprogramming-com-au.translate.goog\") return;
lasttrans=decodeURIComponent(decodeURIComponent(event.data));
document.getElementById('thewords').title=document.getElementById('thewords').value;
document.getElementById('thewords').value=decodeURIComponent(decodeURIComponent(event.data));
});
“; ?>
… for a usage scenario like …
in a web browser link to our inhouse Voiceover Ideas as the “parent” window
if not already selected select Google Translate in top “mode of use” dropdown
in the Language Code dropdown select a language such as Hindi
this populates the Words textarea with some phrasing macOS say command associates with a Voice speaking that selected language
now here, perhaps you would not think to do this, but we’re saying a user might override those Hindi default words with some English words the user enters
click/tap Hear These Words button
as of today, in this type of scenario, as well as one Google Translate popup window appearing, under it, in this scenario another Google Translate of RJM Programming Landing Page with Tailored Arguments “child” window is created, and which …
is able to communicate back to the “parent” window via window.postMessage …
so as to make it that the displayed content of the Words textarea can become the Hindi translation of the English while the on hover title attribute can become the originating English … and …
in this scenario a Words textarea ondblclick (for a double click/tap) toggles the roles above
Stop Press
Reading back to ourselves that list above, what irked as a programmer, was the non-generic feel of the use of the word “English”. After all, Google Translate can detect the language, but for us to use this “auto” coding idea we needed to, in a changed PHPindex.phpLanding Page detect the call and isolate the content to just be the wording up for translation, and then in today’s changed PHPmacos_say_record.phpVoiceover Ideas ensure the Google Translate “auto” coding “detect language” can be harnessed. All this means some natively English speaking user could still pick a language like Hindi in that dropdown and then type in a French word such as “Bonjour” into the Words textarea element then click/tap the Hear These Words button to start involving the language French into the mix of language translation information that follows.
confusing … in the way first one we’re asking you not to click the button but others we do
you may want to replay the audio from a previous Siri command
first say invocation a bit slow (on the uptake)
That third one is the most interesting. You see, macOS say likes to output any audio output asked of it to an audio/aiff format, which browsers like Google Chrome will not play. To overcome this, we start with …
Yes, ideally we’d like you, on your macOS system, to have installed the amazing, the stupendous, the good ol’ ffmpeg command line tool, as an “optional” here.
And then, back at the “client side ranch” …
function getthenextone() {
if (retridx > 0) {
if (document.getElementById('mysiriaudio')) {
document.getElementById('mysiriaudio').style.display='inline-block';
if (document.getElementById('mysirisource').src.indexOf('?') == -1) {
document.getElementById('mysirisource').src+='?rand=' + Math.floor(Math.random() * 167564);
} else {
document.getElementById('mysirisource').src+=('' + Math.floor(Math.random() * 9))
}
if (document.getElementById('mysirisourcetwo').src.indexOf('?') == -1) {
document.getElementById('mysirisourcetwo').src+='?rand=' + Math.floor(Math.random() * 167564);
} else {
document.getElementById('mysirisourcetwo').src+=('' + Math.floor(Math.random() * 9))
}
if (document.getElementById('spaudio')) {
document.getElementById('spaudio').innerHTML=document.getElementById('spaudio').innerHTML;
}
}
if (retridx < eval('' + retrainings.length)) {
document.getElementById('saysub').disabled=false;
document.getElementById('saysub').style.cursor='pointer';
document.getElementById('saysub').value='Hear These Words ... ' + retrainings[retridx] + ' (when you click/tap)';
document.getElementById('thewords').value=retrainings[retridx];
retridx++;
}
}
}
PHP macOS say Supervising a Retrained Siri Tutorial
Here Siri, it’s time for a retraining session.
Hey Bud, I only respond to Hey.
Sorry, Bud … but as the provider of your treats … smirk … Hey Siri, it’s time for a retraining session.
Why retrain Siri? Well, let me ask (not you Siri) …
Do you cringe at the sound of your own voice? Even in a cupboard? Especially in a cupboard?!
… and then …
And yet, you can see the advantages of Siri if it did not involve the sound of your own voice? Even in a cupboard? Especially in a cupboard?!
Yes? Well …
if you have an iOS device to be the Siri conduit …
if you have a macOS device (and so access to the great say Text to Speech command via PHP exec command), such as the MacBook Air, here, today … installed with …
MAMP … Apache/PHP/MySql local web server … and have downloaded the latest (at time of writing) …
http://localhost:8888/macos_say_record.php into that MAMP‘s macOS (default port) Document Root folder
http://localhost:8888/HTMLCSS/client_browsing.htm into that MAMP’s macOS (default port) Document Root folder’s HTMLCSS subfolder
… phew!!! Have we got news for you?!!!
Yes, it might be worthwhile for people still reading to force a retraining of Siri in favour of a macOS Voice of Choice.
Assuming you’re ready for Siri retraining class (further to PHP macOS say Supervisor Modes of Use Tutorial below), and you have cancelled your own voice settings and being asked by the Siri setup, below are what we as that ever so fluid a concept were asked to “Say” “Hey” …
It seems a bit strange to be talking about macOS and command line “say” commands, and not, on top of its …
usual “surfing the net” via the web browser address bar mode of use … to, today, add …
a “command line” mode of use … and …
the manufactured feeling “curl” mode of use
… to its talents so that “say” on the macOS command line can have equivalencies with …
<?php
if (isset($argv)) { // command line
$commandsofar="say ";
$numargs = sizeof($argv);
if ($numargs > 1) {
if (PHP_OS == "Darwin") {
for ($ii=1; $ii<$numargs; $ii++) {
if ($ii == 1 && strtolower($argv[$ii]) == 'say') {
$commandsofar="say ";
} else {
$commandsofar.=' ' . $argv[$ii];
}
}
passthru($commandsofar);
exit;
}
} else {
if (PHP_OS == "Darwin") {
passthru("info say");
exit;
}
}
} else if (isset($_GET['curlit'])) { // curl
$commandsofar="say ";
if (PHP_OS == "Darwin") {
passthru(str_replace("say say ","say ","say " . str_replace('+',' ',urldecode($_GET['curlit']))));
exit;
}
}
?>
a “command line” mode of use … where “say blah-blah-blah” on a command line can be equivalent to, say, “php ./macos_say_record.php blah-blah-blah” …
the manufactured feeling “curl” mode of use asks of the user a way to use a command like “curl ‘HTTP://localhost:8888/macos_say_record.php?curlit=blah-blah-blah‘” (where blah-blah-blah = encodeURIComponent(blah-blah-blah))
We also increased “say” functionality by not forcing …
the default output, for “say” use, of creating an audio file … and, as of today, adding that, as an alternative …
if the audio file is set to blank by the user the “say” operating system command is performed behind the scenes …
<?php
if (str_replace("+"," ",urldecode($_GET['audioname'])) == "") { passthru("say" . $sayprefix . " " . str_replace("\n",",,,,,,",$thewords) . " > " . dirname(__FILE__) . "/sayout.out" . " 2> " . dirname(__FILE__) . "/sayerr.err");
exit;
} else {
exec("say" . $sayprefix . " -o " . dirname(__FILE__) . "/" . str_replace("+"," ",urldecode($_GET['audioname'])) . " " . str_replace("\n",",,,,,,",$thewords) . " > " . dirname(__FILE__) . "/sayout.out" . " 2> " . dirname(__FILE__) . "/sayerr.err");
}
?>
… getting us to moderate that “audiofile” textbox functionality this way …
<?php
$audioft="";
if (PHP_OS == "Darwin") {
$audioft=' title="This made to be blank and the topmost say dropdown choice will cause the text to audio to play through your speakers." onblur="if (this.value.trim().length == 0 && document.getElementById(' . "'" . 'saymode' . "'" . ').value.length == 0) { document.getElementById(' . "'" . 'saysub' . "'" . ').value=document.getElementById(' . "'" . 'saysub' . "'" . ').value.replace(' . "'" . 'Record ' . "'" . ',' . "'" . 'Say ' . "'" . '); } else if (this.value.trim().length != 0 && document.getElementById(' . "'" . 'saymode' . "'" . ').value.length == 0) { document.getElementById(' . "'" . 'saysub' . "'" . ').value=document.getElementById(' . "'" . 'saysub' . "'" . ').value.replace(' . "'" . 'Say ' . "'" . ',' . "'" . 'Record ' . "'" . '); }"';
}
Windows 10 Camera app … photographing the slide contents of yesterday’s video presentation
Windows 10 Photos app … turning the slides above into yesterday’s video presentation … along with its Share option Mail to email off and sftp into place
Don’t be concerned, it will not harm you
It’s only me pursuing somethin’ I’m not sure of
Across my dreams with nets of wonder
I chase the bright elusive butterfly of love
… ilk of things, a creeping up on our interest in a programmable link between …
macOS say (via crontab or at (macOS command scheduling functionality), eventually) control
And our verdict, after today’s iPhone Siri setup (which gets you to “say”, modelling …
say “Hey Siri”
say “Hey Siri, send a message”
say “Hey Siri, how’s the weather today”
say “Hey Siri, set a timer for three minutes”
say “Hey Siri, play some music”
… sentences above to configure the relevant voice) along with a macOS command line “say” arrangement, is … da, da da da da da, da, da da we could go on with the whole lion bit, but we’ll spare you … yes, we can get “say” command on macOS work a …
Hey Siri, Play U2
say “Hey Siri, Play U2”
… type of Siri dictation command onto (our) iPhone’s (default Spotify) music app to play a song. It’s just that … first wooooorrrrlllldddd problem alert … the “say” command was not that flash at …
Hey Siri, Stop
say “Hey Siri, Stop”
… to stop the music, as well as our own … yoo hoo … voice is. And so, our recommendation is to setup Siri for multiple voices. How? Hard to believe, but it seems, in iPhone’s Settings -> Siri & Search screen, turning the top green (Listen for “Hey Siri”) switch in the settings to off and then back on is the go?! The resultant procedure is where you can “train” Siri to recognize a macOS “say” voice, as well as your own voice, or a bunch of other “Siri Voice” (menu) choices offered “out of the box” with the iPhone iOS installed. It may be that you should explore macOS “say” -v [VoiceName] choices, as well, here.
How does this work sit with macOS crontab “say” command thinking that goes with our changed inhousemacos_say_record.php (we recommend you download to macOS MAMP local web server and startup via HTTP://localhost:8888/macos_say_record.php web address URL) MacOS Text to Audio online PHP supervisor talked about with MacOS Text to Audio Scheduling Tutorial? Well, without reliable “stop” … first wooooorrrrlllldddd problem or not … we are going to continue research, but we’re not sure that you will be totally spared from your own voice interventions for stopping (of music) purposes … or perhaps get yourselves an Apple HomePod mini …
We’re resetting the iOS Siri app mentioned in Siri on iPad Mail Dictate Tutorial on another iPad today. We wanted to set it up (via Settings -> Siri & Search) to play songs on Apple Music with spoken words (directed to Siri) such as …
Hope you are not like me and feel like going into a cupboard to use a microphone on a mobile device. If this is not you, and you have a half recent version of iOS on an iPad or iPhone, and you have Siri available as an app, then today’s tutorial may be of interest.
Siri can be like a personal assistant, and the way we best think of it as such, is as a tool to help dictate an email, using the Mail app on our iPad. To get to that, though, if you’ve never used Siri before, you should consult this Apple webpage, or our previous Apple iOS Siri Audio Commentary Tutorial to get started. Once you’re setup for Siri in this way it is very easy to start using Siri when composing emails.
Open the Mail app
Fill in the To field via the keyboard … Siri can’t do this very well … but while you are at the keyboard, what’s the third button from the left on the bottom row? Yes, the “microphone” button gets you to Siri functionality … cute, huh?!
Guess we don’t need to say, but will, that this microphone button becomes useful as soon as you tap in a “keyboard” anything … Siri can be there to assist … personally!
But a word of caution here, is to check about homonyms like today’s “in all innocence” …
The rain in Spain falls mainly on the plane
… Siri faux pas. This is a small concern compared to how much it can do that is correct. Hands free dictation can be very useful for those users on the go, or those kinaesthetic thinkers, perhaps.
On the iPad latest iOS, which we’ll have to talk about soon, there are other good suggestions for Siri use to add to …
Mail (app) … those being …
Safari
Photos
Camera
Settings … and
Numerous Third Party and homegrown apps that access the keyboard, and so, Siri “microphone”
Hope this is food for thought, and we’ll leave you with today’s PDF presentation of a Mail app session using Siri to help build up the body section email content, here.
Now that idea’s first, second and everything aside from ‘HTML audio elements that allow for an audio commentary of the 9 “subimages”‘ of the third parts we’ve been consistent on, but the idea to do this in the first place was secondly inspired by reading this interesting link referring to the CSS selectors :before and :after use of the content property, especially where it says …
url(url) Sets the content to be some kind of media (an image, a sound, a video, etc.)
… not quite believing it could be true … yes, an image, but “a sound, a video“? No way! Well, alas, as of this date, yes … no way … yet. Then seeing this CSS approach wasn’t working … lo and behold … this link explains more … thanks.
But this setback is not the end of the world, and the other backup plan of …
HTML area elements of the HTML map element will be given onmouseover and onclick event logic added to it while using the Mobilefish online map creator … and we’ll massage the resultant HTML to put this logic in as well as the HTML audio element “shells” as well as …
Define audio file comma separated lists in the HTML area elements’ alt attributes
… involves this Javascript DOM code to make the audio commentary be controlled by a hover (but not on mobile devices) and/or click/touch of a “subimage” of interest from the main image …
<script type='text/javascript'>
var lastomo='';
var lastoc='';
var e, prebits, bits, i;
After the tutorial two days back, with Windows 10 and its Cortana voice recognition “personal assistant” (that we tested out with Windows 10 Cortana Primer Tutorial below) you’d be wondering if Apple has an equivalent, and yes, for iOS, which we are going to show today on an iPad, and there are “kind of” ways for Mac OS X El Capitan you can read about here.
Siri lets you use your voice to send messages, schedule meetings, place phone calls and more. Ask Siri to do things just by talking the way you talk. Siri understands what you say, knows what you mean and even talks back. Siri is so easy to use and does so much, you’ll keep finding more and more ways to use it.
This being an iPad, and this being iOS, we had no issues with microphones setting up Siri. The microphone is well integrated into iPad normal usage, and the main job for today’s iPad Siri setup that you get to via …
In the area of robotics and artificial intelligence, perhaps the best known concept to we “mere mortals” is “voice recognition”. Perhaps because research into it goes back to 1932, before the Second World War … and 66 years before the “Worm Farm Incident of Simmons Street Disaster” … but we digress … and no … “I’m not ready to open up about this at this delicate stage of my life, yet, Brad.”.
Voice recognition has come a long way from those earliest endeavours when the speech recognition relied on training software for an individual voice. This became apparent to me trying out Cortana in Windows 10. Once working, it didn’t seem to matter who in our house asked the same question of Cortana, the speech recognition software recognized and translated the speech into the same text for all of us. Actually, Microsoft describes Cortana this way …
Cortana is your clever new personal assistant.
Cortana will help you find things on your PC, manage your calendar, track packages, find files, chat with you, and tell jokes. The more you use Cortana, the more personalized your experience will be.
To get started, type a question in the search box on the taskbar. Or select the microphone icon and talk to Cortana. (Typing works for all types of PCs, but you need a mic to talk.)
… and I see what they mean by this, because you can work Cortana without the voice recognition part, if you like, or if you have the urge to run for the nearest cupboard before being caught talking into a computer (microphone). Perhaps Cortana should have a special “Darkroom Edition” for people who …
have the urge to run for the nearest cupboard before being caught talking into a computer (microphone) … and who …
have a hobby developing and printing photographs
Anyway, we agree with Microsoft that Cortana is clever, and it’s nice for us to find another use for the microphone (brand called MXL Tempo) we used with WebEx work we talked about with WebEx Presentation with Microphone Tutorial below.
There is not much to setting up Cortana, except, perhaps, for the microphone bit, which we’ll talk more about later. Cortana’s “personal assistant” and interface down next to the Windows icon at the bottom left of the screen guides you well through what you have to do.
We got stuck a bit, regarding setting up the microphone, with a cycle of it presenting this voice recognition test always resulting in a message wondering whether we had the microphone set to “mute on”, which wasn’t the case. But what was the case, and remedied this problem was to use a USB 2 port rather than a USB 3 port … in case this happens to you.
Other than that, Cortana is pretty cute, and could be a good enough reason on its own to upgrade to Windows 10 from Windows 7, Windows 8.1, Windows Phone 8.1 or Windows 9 operating systems before Friday, 29th July 2016 which is the cut off day for free upgrades. Our experience of the upgrade was talked about at Windows 10 Upgrade Primer Tutorial (and backups were discussed at Windows File History Backup Primer Tutorial).
We’ve been doing some more WebEx (by Cisco) lately, and realised, at least with using a MacBook Pro laptop, we needed to invest in a microphone, to be heard, as the inbuilt microphone systems were not up to it.
We opted for a USB connected microphone brand called MXL Tempo, sold here in Australia, and have found it to be good, especially mounted on the stand provided … well, no complaints, anyway?! Where it has a 1/8″ (3.5mm) Headphone Jack we plugged in our own speakers, though you could use headphones here as well.
Of course we’ll also be constructing a garage, and buying a guitar, and calling on “tree fellers”karaoke backing track of Peter, Paul and Mary to complete the picture of this week’s project … getting the new microphone to make breakfast in the morning before you even knew you needed breakfast get me on The Voices.
In the WebEx “Audio Connection” menu via “Call Using Computer” option have both input and output audio be handled by “USB audio CODEC” (if they are options … if not, there is a hardware (perhaps configuration) problem with your audio and microphone connection) as you can see at today’s tutorial picture. So long as you succeed and have the USB connected, the audio connection will default to this arrangement for the next time. Cute, huh?!
To make it permanent that the MXL Tempo microphone arrangement is the default device for recordings …
click on System Preferences off the Apple menu
click the Sound option
click the Output tab
pick USB audio CODEC
if you intend using speakers or headphones connected off this microphone from its 1/8″ (3.5mm) Headphone Jack, click the Input tab
… regarding video conferencing products we’ve tried at this blog.
Have to say, WebEx is great, even with respect to the “wide eyed and bushy tailed” reaction “this little black duck” has to all these networky communicaty ideas on the net (at least we spelt “net” correctly).
Have to thank my wife, Maree, for her expertise and the facilities her company, Thomson Reuters, supplies for the serving of WebEx recordings … thanks everyone. Have been assured they are periodically deleted, and my lame impersonations of the old “ducks on the wall” can rest in peace shortly.
And so, we have a slideshow starting with a WebEx email link to join a meeting, and we pan down the email to show you other WebEx functionalities, such as adding a Calendar reference to the meeting time, and though we haven’t shown you detail here, rest assured it handles timezone scenarios very well, unless you lie about living in Antarctica, that is … sorry, scientists in Antarctica reading this blog posting … all 237 of you.
During this “earlier than today exploration of WebEx” session the necessary software installs just happened for this MacBook Pro Mac OS X laptop as if we were shelling peas … it’s always good to have some handy when installing any software. So we won’t show you this unless we deem it essential at a later date. You can perhaps do as I did, and ask a real WebEx user invite you to a meeting, to set yourself up. In fact, today’s session meeting creation time you may notice is well in the past from that earlier introductory learning session Maree and I had, and you can bring back up that old email, and resurrect that meeting again and again, if you like … am not sure if there is an expiry date on this too, like with server stored WebEx prerecordings.
So also rest assured, WebEx handles …
video via webcam on your device
audio via microphone on your device (“Use Computer”) or via a phone line
the synchronization of the two above
mobile devices
Did you know?
A .ics extension file, as you can see being used as an email attachment file extension in is, as explained in this link‘s sublink …
ICS is a global format for calendar files widely being utilized by various calendar and email programs including Google Calendar, Apple iCal, and Microsoft Outlook. These files enable users to share and publish information directly from their calendars over email or via uploading it to the world wide web.
… as helping interface meetings to online calendar appointments. Cute, huh?!
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.
sharing … and caring and we all know sharing can lead to …
collaboration
… and today we’re encouraging users to join in with others sharing editable calendars via email (and though the hashtagging goes beyond SMS lengths we’re including that as an option too, and we may find a way down the track that the lengths of data can be reduced to suit an SMS message …
… is our go to regarding the internationalization and language translation functionality. We get to Google Translate, where permitted (up to you, and whether you have popup blockers happening with the web browser you are using), via a popup window, which means, in that scenario, there are two distinct domains in play …
Be listening out at the RJM Programming domain document.URL scenario “parent” window …
window.addEventListener("message", (event) => { // thanks to https://www.google.com/search?q=Undef+what+conditions+can+window.opener.parentfunc()+type+of+call+work+when+the+domains+are+different%3F&rlz=1C5OZZY_en&oq=Undef+what+conditions+can+window.opener.parentfunc()+type+of+call+work+when+the+domains+are+different%3F&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCTI2NzQwajBqN6gCALACAA&sourceid=chrome&source=chrome.ob&ie=UTF-8
var thislang='';
// Always verify the origin of the sender for security
if (event.origin !== "https://www-rjmprogramming-com-au.translate.goog") return;
Today is Styling Day regarding our current Editable Calendar project we last talked about with yesterday’s Editable Calendar Save and Recall Tutorial. We cannot recall ever arranging the body element background exactly as we did (probably because we only had the top and the bottom of the screen for context, the middle being the calendar) in the CSS below …
body {
background-image: linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0.8)),url(https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSAMB0R0_H50ZU4AW0t51IqB0WwOyF1BEmSPy3MMxisNythxHs69b4lcx8&s=10);
background-size: contain;
background-repeat: repeat;
}
</style>
… where some judiciously placed background-color:transparent helps this out with transparency ideas. We find applying border-radius a good way to “soften” the look, and “text-shadow” as a way to allow for a 3D look to innerHTML contenteditable=true types of elements.
Get arguments are those ? or & delimited parts to URLs you sometimes see up at the address bar of web browsers. We’d be lost without them. And, though Google Translate uses it’s own set arrangement of get arguments for this “static URL” language translation mode of use we’ve been using in this Editable Calendar recent project, it does not mean that Google Translate will stop you also using any of your own “personalized” get argument arrangements as well, that it just ends up “sending through to the keeper” …
if (document.URL.indexOf('month=') != -1 && document.URL.indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + document.URL.split('year=')[1].split('&')[0].split('#')[0]), eval('' + document.URL.split('month=')[1].split('&')[0].split('#')[0]), 1);
} else if (('' + parent.document.URL).indexOf('month=') != -1 && ('' + parent.document.URL).indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + ('' + parent.document.URL).split('year=')[1].split('&')[0].split('#')[0]), eval('' + ('' + parent.document.URL).split('month=')[1].split('&')[0].split('#')[0]), 1);
}
… to it, making it editable and open to more personalization regarding your Editable Calendar, on the way to what we hope ends up being a more useful web application tool … while …
that HTML caption element’s onblur event …
var now = new Date(), firstDayIndex=0, daysThisMonth=0;
var origih=''; // is filled with innards of the original calendar table element at document.body onload
var calcapv=''; // is updated with HTML caption element innards before any user interaction
var usuff=''; // personalized get arguments go here
var changesmade=false; // goes to true when user blurb onkeypress happens within calendar tbody span elements or if the Month, Year changes
function newmy(ocap) {
if (ocap.innerHTML != calcapv) {
if (ocap.innerHTML.indexOf(',') != -1) {
if (mths.indexOf(ocap.innerHTML.split(',')[0].trim()) != -1 && eval('' + ocap.innerHTML.split(',')[1].trim().length) == 4) {
//changesmade=true;
usuff='&year=' + ocap.innerHTML.split(',')[1].trim() + '&month=' + mths.indexOf(ocap.innerHTML.split(',')[0].trim());
now = new Date(eval('' + ocap.innerHTML.split(',')[1].trim()), eval('' + mths.indexOf(ocap.innerHTML.split(',')[0].trim())), 1);
document.getElementById('wp-calendar').innerHTML=origih;
thismonth();
}
}
}
}
… can be used to store our “personalized get arguments” …
ready to be appended to the (web browser address bar) URL reaching Google Translate …
var valvalue='';
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out …
… we referenced when presenting Editable Calendar Google Translate Tutorial before the recent Editable Calendar Intl Tutorial? Today’s work plonks them back in by involving PHP, as our server side language that can write to and read from ascii text files stored on our RJM Programming domain web server. By, when needed, getting Google Translate to, when required (ie. we’ve decided this is when the keyboard onkeypress happens), reference a .php based RJM Programming URL, leaves that opportunity to …
Shape to write the calendar table innards to an ascii web server text file via Ajax … client side …
function ajaxit() {
var zhr = new XMLHttpRequest();
var zform = new FormData();
zform.append('tihis', document.getElementById('wp-calendar').innerHTML);
zhr.open('post', '/' + docURL.split('?')[0].split('#')[0].replace('.html','.php').replace('.htm','.php'), true);
zhr.send(zform);
}
Write the calendar table innards to an ascii web server text file via PHP … server side …
<?php
if (isset($_POST['tihis'])) {
file_put_contents('tihis.txt', str_replace('+',' ',urldecode($_POST['tihis'])));
exit;
}
?>
As needed, very soon after, as Google Translate is called with a .php based URL, substitute this saved table innards into a read from default representation of the HTML via PHP … server side …
Leaving any document.body onload event logics out (in first codeline of such onload event Javascript function), in such a scenario, leaving it all to Google Translate’s talents … client side …
if (('' + parent.document.URL).indexOf('.php') != -1) { return ''; }
Because the theme of our current Editable Calendar web application revolves around time stamps and day names and month names regarding yesterday’s Editable Calendar Google Translate Tutorial that means, regarding language internationalization ideas …
as well as yesterday’s Google Translate ideas … also available is …
Yesterday’s Editable Calendar Primer Tutorial‘s start to our Editable Calendar project was a very anglo-centric exercise. Today, we want to use the great …
… to help translate our inhouse burgeoning Editable Calendar skeletal contents to some non English languages.
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out in this changedsecond draftEditable Calendar you can try below, because we’re using the Google Translate “static URL” approach to the translations. But we will examine, down the track, if we can do better?! Again, stay tuned for more progression and internationalization over the days on this Editable Calendar project.
we’re channelling how in emails “word strings” starting with “http” become links … and so …
in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available
Here is the relevant Javascript we ended up with, for this …
Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!
When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …
When it comes to procedures …
Aaaaaarrrrgggghhhh …
Nuances count … regarding …
Errrrrrr …
Saving time …
Saving frustration
… resulting in the “easy to remember” acronym WANESS … we rest our cases!
What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.
Global variables initialization …
var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …
if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …
function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}
function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic …
function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}
Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changedevents_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!
it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs
… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!
Timing became really important with this integration of …
email
SMS
… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …
Month
Year
… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …
Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons …
<h1>Events in Month <button style=display:none; title='Share via Email' id=semail onclick="doemail('');">📧</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>📟</button> </h1>
New static HTML div element placed to the bottom of the body element …
<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …
Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to …
Record to Remember via Bookmark
… use the web browser Bookmarks to help you recall an Events in Month report with long entries.
Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …
… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!
To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …
var documentURL=document.URL;
… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …
#August__2023_00000__1
… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).
Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).
Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …
… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.
The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …
<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm
But, there’s an inherent weakness with the design, we’ll go into more into the future.
… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.
Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.
Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …
bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
pick Inspect option …
be in Elements tab of your Web Inspector … and …
highlight top <html> tag …
Context Menu with a right click or two finger gesture …
pick Copy -> Copy outerHTML … so that …
your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)
The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …
function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}
… the form submit buttons are reworded accordingly.
Another idea from this blog thread’s inspiration …
… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.
And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …
text shadow means by which the text of Event Calendar can be more impactive …
<div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
… is our go to regarding the internationalization and language translation functionality. We get to Google Translate, where permitted (up to you, and whether you have popup blockers happening with the web browser you are using), via a popup window, which means, in that scenario, there are two distinct domains in play …
Be listening out at the RJM Programming domain document.URL scenario “parent” window …
window.addEventListener("message", (event) => { // thanks to https://www.google.com/search?q=Undef+what+conditions+can+window.opener.parentfunc()+type+of+call+work+when+the+domains+are+different%3F&rlz=1C5OZZY_en&oq=Undef+what+conditions+can+window.opener.parentfunc()+type+of+call+work+when+the+domains+are+different%3F&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCTI2NzQwajBqN6gCALACAA&sourceid=chrome&source=chrome.ob&ie=UTF-8
var thislang='';
// Always verify the origin of the sender for security
if (event.origin !== "https://www-rjmprogramming-com-au.translate.goog") return;
Today is Styling Day regarding our current Editable Calendar project we last talked about with yesterday’s Editable Calendar Save and Recall Tutorial. We cannot recall ever arranging the body element background exactly as we did (probably because we only had the top and the bottom of the screen for context, the middle being the calendar) in the CSS below …
body {
background-image: linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0.8)),url(https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSAMB0R0_H50ZU4AW0t51IqB0WwOyF1BEmSPy3MMxisNythxHs69b4lcx8&s=10);
background-size: contain;
background-repeat: repeat;
}
</style>
… where some judiciously placed background-color:transparent helps this out with transparency ideas. We find applying border-radius a good way to “soften” the look, and “text-shadow” as a way to allow for a 3D look to innerHTML contenteditable=true types of elements.
Get arguments are those ? or & delimited parts to URLs you sometimes see up at the address bar of web browsers. We’d be lost without them. And, though Google Translate uses it’s own set arrangement of get arguments for this “static URL” language translation mode of use we’ve been using in this Editable Calendar recent project, it does not mean that Google Translate will stop you also using any of your own “personalized” get argument arrangements as well, that it just ends up “sending through to the keeper” …
if (document.URL.indexOf('month=') != -1 && document.URL.indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + document.URL.split('year=')[1].split('&')[0].split('#')[0]), eval('' + document.URL.split('month=')[1].split('&')[0].split('#')[0]), 1);
} else if (('' + parent.document.URL).indexOf('month=') != -1 && ('' + parent.document.URL).indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + ('' + parent.document.URL).split('year=')[1].split('&')[0].split('#')[0]), eval('' + ('' + parent.document.URL).split('month=')[1].split('&')[0].split('#')[0]), 1);
}
… to it, making it editable and open to more personalization regarding your Editable Calendar, on the way to what we hope ends up being a more useful web application tool … while …
that HTML caption element’s onblur event …
var now = new Date(), firstDayIndex=0, daysThisMonth=0;
var origih=''; // is filled with innards of the original calendar table element at document.body onload
var calcapv=''; // is updated with HTML caption element innards before any user interaction
var usuff=''; // personalized get arguments go here
var changesmade=false; // goes to true when user blurb onkeypress happens within calendar tbody span elements or if the Month, Year changes
function newmy(ocap) {
if (ocap.innerHTML != calcapv) {
if (ocap.innerHTML.indexOf(',') != -1) {
if (mths.indexOf(ocap.innerHTML.split(',')[0].trim()) != -1 && eval('' + ocap.innerHTML.split(',')[1].trim().length) == 4) {
//changesmade=true;
usuff='&year=' + ocap.innerHTML.split(',')[1].trim() + '&month=' + mths.indexOf(ocap.innerHTML.split(',')[0].trim());
now = new Date(eval('' + ocap.innerHTML.split(',')[1].trim()), eval('' + mths.indexOf(ocap.innerHTML.split(',')[0].trim())), 1);
document.getElementById('wp-calendar').innerHTML=origih;
thismonth();
}
}
}
}
… can be used to store our “personalized get arguments” …
ready to be appended to the (web browser address bar) URL reaching Google Translate …
var valvalue='';
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out …
… we referenced when presenting Editable Calendar Google Translate Tutorial before the recent Editable Calendar Intl Tutorial? Today’s work plonks them back in by involving PHP, as our server side language that can write to and read from ascii text files stored on our RJM Programming domain web server. By, when needed, getting Google Translate to, when required (ie. we’ve decided this is when the keyboard onkeypress happens), reference a .php based RJM Programming URL, leaves that opportunity to …
Shape to write the calendar table innards to an ascii web server text file via Ajax … client side …
function ajaxit() {
var zhr = new XMLHttpRequest();
var zform = new FormData();
zform.append('tihis', document.getElementById('wp-calendar').innerHTML);
zhr.open('post', '/' + docURL.split('?')[0].split('#')[0].replace('.html','.php').replace('.htm','.php'), true);
zhr.send(zform);
}
Write the calendar table innards to an ascii web server text file via PHP … server side …
<?php
if (isset($_POST['tihis'])) {
file_put_contents('tihis.txt', str_replace('+',' ',urldecode($_POST['tihis'])));
exit;
}
?>
As needed, very soon after, as Google Translate is called with a .php based URL, substitute this saved table innards into a read from default representation of the HTML via PHP … server side …
Leaving any document.body onload event logics out (in first codeline of such onload event Javascript function), in such a scenario, leaving it all to Google Translate’s talents … client side …
if (('' + parent.document.URL).indexOf('.php') != -1) { return ''; }
Because the theme of our current Editable Calendar web application revolves around time stamps and day names and month names regarding yesterday’s Editable Calendar Google Translate Tutorial that means, regarding language internationalization ideas …
as well as yesterday’s Google Translate ideas … also available is …
Yesterday’s Editable Calendar Primer Tutorial‘s start to our Editable Calendar project was a very anglo-centric exercise. Today, we want to use the great …
… to help translate our inhouse burgeoning Editable Calendar skeletal contents to some non English languages.
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out in this changedsecond draftEditable Calendar you can try below, because we’re using the Google Translate “static URL” approach to the translations. But we will examine, down the track, if we can do better?! Again, stay tuned for more progression and internationalization over the days on this Editable Calendar project.
we’re channelling how in emails “word strings” starting with “http” become links … and so …
in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available
Here is the relevant Javascript we ended up with, for this …
Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!
When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …
When it comes to procedures …
Aaaaaarrrrgggghhhh …
Nuances count … regarding …
Errrrrrr …
Saving time …
Saving frustration
… resulting in the “easy to remember” acronym WANESS … we rest our cases!
What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.
Global variables initialization …
var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …
if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …
function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}
function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic …
function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}
Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changedevents_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!
it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs
… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!
Timing became really important with this integration of …
email
SMS
… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …
Month
Year
… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …
Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons …
<h1>Events in Month <button style=display:none; title='Share via Email' id=semail onclick="doemail('');">📧</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>📟</button> </h1>
New static HTML div element placed to the bottom of the body element …
<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …
Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to …
Record to Remember via Bookmark
… use the web browser Bookmarks to help you recall an Events in Month report with long entries.
Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …
… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!
To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …
var documentURL=document.URL;
… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …
#August__2023_00000__1
… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).
Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).
Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …
… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.
The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …
<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm
But, there’s an inherent weakness with the design, we’ll go into more into the future.
… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.
Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.
Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …
bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
pick Inspect option …
be in Elements tab of your Web Inspector … and …
highlight top <html> tag …
Context Menu with a right click or two finger gesture …
pick Copy -> Copy outerHTML … so that …
your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)
The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …
function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}
… the form submit buttons are reworded accordingly.
Another idea from this blog thread’s inspiration …
… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.
And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …
text shadow means by which the text of Event Calendar can be more impactive …
<div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
Today is Styling Day regarding our current Editable Calendar project we last talked about with yesterday’s Editable Calendar Save and Recall Tutorial. We cannot recall ever arranging the body element background exactly as we did (probably because we only had the top and the bottom of the screen for context, the middle being the calendar) in the CSS below …
body {
background-image: linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0.8)),url(https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSAMB0R0_H50ZU4AW0t51IqB0WwOyF1BEmSPy3MMxisNythxHs69b4lcx8&s=10);
background-size: contain;
background-repeat: repeat;
}
</style>
… where some judiciously placed background-color:transparent helps this out with transparency ideas. We find applying border-radius a good way to “soften” the look, and “text-shadow” as a way to allow for a 3D look to innerHTML contenteditable=true types of elements.
Get arguments are those ? or & delimited parts to URLs you sometimes see up at the address bar of web browsers. We’d be lost without them. And, though Google Translate uses it’s own set arrangement of get arguments for this “static URL” language translation mode of use we’ve been using in this Editable Calendar recent project, it does not mean that Google Translate will stop you also using any of your own “personalized” get argument arrangements as well, that it just ends up “sending through to the keeper” …
if (document.URL.indexOf('month=') != -1 && document.URL.indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + document.URL.split('year=')[1].split('&')[0].split('#')[0]), eval('' + document.URL.split('month=')[1].split('&')[0].split('#')[0]), 1);
} else if (('' + parent.document.URL).indexOf('month=') != -1 && ('' + parent.document.URL).indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + ('' + parent.document.URL).split('year=')[1].split('&')[0].split('#')[0]), eval('' + ('' + parent.document.URL).split('month=')[1].split('&')[0].split('#')[0]), 1);
}
… to it, making it editable and open to more personalization regarding your Editable Calendar, on the way to what we hope ends up being a more useful web application tool … while …
that HTML caption element’s onblur event …
var now = new Date(), firstDayIndex=0, daysThisMonth=0;
var origih=''; // is filled with innards of the original calendar table element at document.body onload
var calcapv=''; // is updated with HTML caption element innards before any user interaction
var usuff=''; // personalized get arguments go here
var changesmade=false; // goes to true when user blurb onkeypress happens within calendar tbody span elements or if the Month, Year changes
function newmy(ocap) {
if (ocap.innerHTML != calcapv) {
if (ocap.innerHTML.indexOf(',') != -1) {
if (mths.indexOf(ocap.innerHTML.split(',')[0].trim()) != -1 && eval('' + ocap.innerHTML.split(',')[1].trim().length) == 4) {
//changesmade=true;
usuff='&year=' + ocap.innerHTML.split(',')[1].trim() + '&month=' + mths.indexOf(ocap.innerHTML.split(',')[0].trim());
now = new Date(eval('' + ocap.innerHTML.split(',')[1].trim()), eval('' + mths.indexOf(ocap.innerHTML.split(',')[0].trim())), 1);
document.getElementById('wp-calendar').innerHTML=origih;
thismonth();
}
}
}
}
… can be used to store our “personalized get arguments” …
ready to be appended to the (web browser address bar) URL reaching Google Translate …
var valvalue='';
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out …
… we referenced when presenting Editable Calendar Google Translate Tutorial before the recent Editable Calendar Intl Tutorial? Today’s work plonks them back in by involving PHP, as our server side language that can write to and read from ascii text files stored on our RJM Programming domain web server. By, when needed, getting Google Translate to, when required (ie. we’ve decided this is when the keyboard onkeypress happens), reference a .php based RJM Programming URL, leaves that opportunity to …
Shape to write the calendar table innards to an ascii web server text file via Ajax … client side …
function ajaxit() {
var zhr = new XMLHttpRequest();
var zform = new FormData();
zform.append('tihis', document.getElementById('wp-calendar').innerHTML);
zhr.open('post', '/' + docURL.split('?')[0].split('#')[0].replace('.html','.php').replace('.htm','.php'), true);
zhr.send(zform);
}
Write the calendar table innards to an ascii web server text file via PHP … server side …
<?php
if (isset($_POST['tihis'])) {
file_put_contents('tihis.txt', str_replace('+',' ',urldecode($_POST['tihis'])));
exit;
}
?>
As needed, very soon after, as Google Translate is called with a .php based URL, substitute this saved table innards into a read from default representation of the HTML via PHP … server side …
Leaving any document.body onload event logics out (in first codeline of such onload event Javascript function), in such a scenario, leaving it all to Google Translate’s talents … client side …
if (('' + parent.document.URL).indexOf('.php') != -1) { return ''; }
Because the theme of our current Editable Calendar web application revolves around time stamps and day names and month names regarding yesterday’s Editable Calendar Google Translate Tutorial that means, regarding language internationalization ideas …
as well as yesterday’s Google Translate ideas … also available is …
Yesterday’s Editable Calendar Primer Tutorial‘s start to our Editable Calendar project was a very anglo-centric exercise. Today, we want to use the great …
… to help translate our inhouse burgeoning Editable Calendar skeletal contents to some non English languages.
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out in this changedsecond draftEditable Calendar you can try below, because we’re using the Google Translate “static URL” approach to the translations. But we will examine, down the track, if we can do better?! Again, stay tuned for more progression and internationalization over the days on this Editable Calendar project.
we’re channelling how in emails “word strings” starting with “http” become links … and so …
in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available
Here is the relevant Javascript we ended up with, for this …
Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!
When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …
When it comes to procedures …
Aaaaaarrrrgggghhhh …
Nuances count … regarding …
Errrrrrr …
Saving time …
Saving frustration
… resulting in the “easy to remember” acronym WANESS … we rest our cases!
What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.
Global variables initialization …
var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …
if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …
function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}
function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic …
function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}
Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changedevents_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!
it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs
… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!
Timing became really important with this integration of …
email
SMS
… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …
Month
Year
… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …
Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons …
<h1>Events in Month <button style=display:none; title='Share via Email' id=semail onclick="doemail('');">📧</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>📟</button> </h1>
New static HTML div element placed to the bottom of the body element …
<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …
Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to …
Record to Remember via Bookmark
… use the web browser Bookmarks to help you recall an Events in Month report with long entries.
Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …
… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!
To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …
var documentURL=document.URL;
… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …
#August__2023_00000__1
… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).
Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).
Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …
… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.
The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …
<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm
But, there’s an inherent weakness with the design, we’ll go into more into the future.
… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.
Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.
Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …
bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
pick Inspect option …
be in Elements tab of your Web Inspector … and …
highlight top <html> tag …
Context Menu with a right click or two finger gesture …
pick Copy -> Copy outerHTML … so that …
your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)
The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …
function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}
… the form submit buttons are reworded accordingly.
Another idea from this blog thread’s inspiration …
… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.
And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …
text shadow means by which the text of Event Calendar can be more impactive …
<div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
Get arguments are those ? or & delimited parts to URLs you sometimes see up at the address bar of web browsers. We’d be lost without them. And, though Google Translate uses it’s own set arrangement of get arguments for this “static URL” language translation mode of use we’ve been using in this Editable Calendar recent project, it does not mean that Google Translate will stop you also using any of your own “personalized” get argument arrangements as well, that it just ends up “sending through to the keeper” …
if (document.URL.indexOf('month=') != -1 && document.URL.indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + document.URL.split('year=')[1].split('&')[0].split('#')[0]), eval('' + document.URL.split('month=')[1].split('&')[0].split('#')[0]), 1);
} else if (('' + parent.document.URL).indexOf('month=') != -1 && ('' + parent.document.URL).indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + ('' + parent.document.URL).split('year=')[1].split('&')[0].split('#')[0]), eval('' + ('' + parent.document.URL).split('month=')[1].split('&')[0].split('#')[0]), 1);
}
… to it, making it editable and open to more personalization regarding your Editable Calendar, on the way to what we hope ends up being a more useful web application tool … while …
that HTML caption element’s onblur event …
var now = new Date(), firstDayIndex=0, daysThisMonth=0;
var origih=''; // is filled with innards of the original calendar table element at document.body onload
var calcapv=''; // is updated with HTML caption element innards before any user interaction
var usuff=''; // personalized get arguments go here
var changesmade=false; // goes to true when user blurb onkeypress happens within calendar tbody span elements or if the Month, Year changes
function newmy(ocap) {
if (ocap.innerHTML != calcapv) {
if (ocap.innerHTML.indexOf(',') != -1) {
if (mths.indexOf(ocap.innerHTML.split(',')[0].trim()) != -1 && eval('' + ocap.innerHTML.split(',')[1].trim().length) == 4) {
//changesmade=true;
usuff='&year=' + ocap.innerHTML.split(',')[1].trim() + '&month=' + mths.indexOf(ocap.innerHTML.split(',')[0].trim());
now = new Date(eval('' + ocap.innerHTML.split(',')[1].trim()), eval('' + mths.indexOf(ocap.innerHTML.split(',')[0].trim())), 1);
document.getElementById('wp-calendar').innerHTML=origih;
thismonth();
}
}
}
}
… can be used to store our “personalized get arguments” …
ready to be appended to the (web browser address bar) URL reaching Google Translate …
var valvalue='';
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out …
… we referenced when presenting Editable Calendar Google Translate Tutorial before the recent Editable Calendar Intl Tutorial? Today’s work plonks them back in by involving PHP, as our server side language that can write to and read from ascii text files stored on our RJM Programming domain web server. By, when needed, getting Google Translate to, when required (ie. we’ve decided this is when the keyboard onkeypress happens), reference a .php based RJM Programming URL, leaves that opportunity to …
Shape to write the calendar table innards to an ascii web server text file via Ajax … client side …
function ajaxit() {
var zhr = new XMLHttpRequest();
var zform = new FormData();
zform.append('tihis', document.getElementById('wp-calendar').innerHTML);
zhr.open('post', '/' + docURL.split('?')[0].split('#')[0].replace('.html','.php').replace('.htm','.php'), true);
zhr.send(zform);
}
Write the calendar table innards to an ascii web server text file via PHP … server side …
<?php
if (isset($_POST['tihis'])) {
file_put_contents('tihis.txt', str_replace('+',' ',urldecode($_POST['tihis'])));
exit;
}
?>
As needed, very soon after, as Google Translate is called with a .php based URL, substitute this saved table innards into a read from default representation of the HTML via PHP … server side …
Leaving any document.body onload event logics out (in first codeline of such onload event Javascript function), in such a scenario, leaving it all to Google Translate’s talents … client side …
if (('' + parent.document.URL).indexOf('.php') != -1) { return ''; }
Because the theme of our current Editable Calendar web application revolves around time stamps and day names and month names regarding yesterday’s Editable Calendar Google Translate Tutorial that means, regarding language internationalization ideas …
as well as yesterday’s Google Translate ideas … also available is …
Yesterday’s Editable Calendar Primer Tutorial‘s start to our Editable Calendar project was a very anglo-centric exercise. Today, we want to use the great …
… to help translate our inhouse burgeoning Editable Calendar skeletal contents to some non English languages.
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out in this changedsecond draftEditable Calendar you can try below, because we’re using the Google Translate “static URL” approach to the translations. But we will examine, down the track, if we can do better?! Again, stay tuned for more progression and internationalization over the days on this Editable Calendar project.
we’re channelling how in emails “word strings” starting with “http” become links … and so …
in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available
Here is the relevant Javascript we ended up with, for this …
Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!
When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …
When it comes to procedures …
Aaaaaarrrrgggghhhh …
Nuances count … regarding …
Errrrrrr …
Saving time …
Saving frustration
… resulting in the “easy to remember” acronym WANESS … we rest our cases!
What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.
Global variables initialization …
var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …
if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …
function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}
function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic …
function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}
Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changedevents_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!
it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs
… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!
Timing became really important with this integration of …
email
SMS
… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …
Month
Year
… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …
Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons …
<h1>Events in Month <button style=display:none; title='Share via Email' id=semail onclick="doemail('');">📧</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>📟</button> </h1>
New static HTML div element placed to the bottom of the body element …
<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …
Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to …
Record to Remember via Bookmark
… use the web browser Bookmarks to help you recall an Events in Month report with long entries.
Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …
… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!
To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …
var documentURL=document.URL;
… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …
#August__2023_00000__1
… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).
Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).
Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …
… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.
The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …
<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm
But, there’s an inherent weakness with the design, we’ll go into more into the future.
… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.
Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.
Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …
bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
pick Inspect option …
be in Elements tab of your Web Inspector … and …
highlight top <html> tag …
Context Menu with a right click or two finger gesture …
pick Copy -> Copy outerHTML … so that …
your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)
The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …
function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}
… the form submit buttons are reworded accordingly.
Another idea from this blog thread’s inspiration …
… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.
And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …
text shadow means by which the text of Event Calendar can be more impactive …
<div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>