Walking Trip …

Walking Trip

Walking Trip

Offenbach's Suite ... Warts 'n All

Offenbach's Suite ... Warts 'n All

 📅  

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

Posted in Photography, Trips | Tagged , , | 34 Comments

ComboBox Idea Datalist Tutorial

ComboBox Idea Datalist Tutorial

ComboBox Idea Datalist Tutorial

Onto yesterday’s ComboBox Idea Primer Tutorial‘s …

Button/Select

Input/Datalist ( … today,
as a second Combobox idea
we add …)

<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 id=defopt value=''>Optionally enter a value ...</option><option value=1>1</option></select></button>

<input oninput="if (1 == 11) { maggage(this); } else { setTimeout(premassage,200); }" id=myinput list="comboboxlist" value="">
<datalist id="comboboxlist">
<option value="List item 1">
<option value="List item 2">
<option value="List item 3">
<option value="List item 4">
</datalist>
Pros Cons
Streamlined look
Reselects obvious
Bit klunky
Reselects awkward

… 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 …

  1. select … or …
  2. datalist

… 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.

Feel free to try the changed second draft ComboBox ideas further explorations …


Previous relevant ComboBox Idea Primer Tutorial is shown below.

ComboBox Idea Primer Tutorial

ComboBox Idea Primer Tutorial

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>

… in our revisit to the concept, today, with a first draft ComboBox ideas exploratory effort.


Previous relevant Button Innards Primer Tutorial is shown below.

Button Innards Primer Tutorial

Button Innards Primer Tutorial

Yesterday’s …

… 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 …


Previous relevant Bookmark Exporting Filtering Sharing Tutorial is shown below.

Bookmark Exporting Filtering Sharing Tutorial

Bookmark Exporting Filtering Sharing Tutorial

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 ...


if (document.URL.indexOf('?') == -1 && ('' + location.hash).indexOf('bmkdata=') != -1) {
setTimeout(function(){
document.getElementById('resulting').innerHTML=decodeURIComponent(('' + location.hash).split('bmkdata=')[1]).replace('><summary', ' open><summary').replace('<details title=', '<details data-title=');
fillin();
}, 5000);
}

… 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?!

So, again, feel free to try our “second draft” dated_bookmarks.html Dated Bookmark Export web application, yourself, perhaps below.


Previous relevant Making of Ffmpeg Video Overall Effects Tutorial is shown below.

Making of Ffmpeg Video Overall Effects Tutorial

Making of Ffmpeg Video Overall Effects Tutorial

At the end of yesterday’s Ffmpeg Video Overall Effects Tutorial there was an Unordered List …

… 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?!

Feel free to try our “first draft” proof of concept Dated Bookmark Export web application, yourself.

Did you know?

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.


Previous relevant Ffmpeg Video Overall Effects Tutorial is shown below.

Ffmpeg Video Overall Effects Tutorial

Ffmpeg Video Overall Effects Tutorial

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 …

  1. 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
  2. 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 …

  1. selection of a video still image, ideally not containing the Brush Turkey …
  2. open image in Gimp
  3. “Aoife”‘esque Colours -> Curves…
  4. opens Adjust Colour Curves subwindow (initially a straight line)
  5. but dragging it to become a curve in a way that brightens the underexposed image to improve … we then …
  6. click + button to Save presets to a name …
  7. controlled by left pointing infilled triangle icon click that Manages Presets … and we ended up with …
  8. bush_-_turkey.presets


  9. and then we discovered this Converts GIMP colour curve to something the FFmpeg can read in the curve filter excellent project, the extraction of the Python there allowing us to go …

    python3 ../gimp_curves_ffmpeg.py

    … to output brush.out

  10. which formed most of the final curves — ffmpeg examples inspired ffmpeg command …

    ffmpeg -y -i brush__turkey.m4v -filter_complex "
    [0:v]
    curves=
    master='0.0/0 0.01171875/0.033751864777061671 0.0234375/0.067435644319434537 0.03515625/0.10098325339242978 0.046875/0.1343266067613586 0.05859375/0.16739761919153215 0.0703125/0.20012820544826168 0.08203125/0.23245028029685835 0.09375/0.26429575850263332 0.10546875/0.29559655483089792 0.1171875/0.32628458404696314 0.12890625/0.35629176091614018 0.140625/0.38555000020374036 0.15234375/0.41399121667507488 0.1640625/0.44154732509545486 0.17578125/0.46815024023019147 0.1875/0.49373187684459596 0.19921875/0.51822414970397945 0.2109375/0.54155897357365324 0.22265625/0.56366826321892849 0.234375/0.58448393340511628 0.24609375/0.6039378988975278 0.2578125/0.62196207446147445 0.26953125/0.63848837486226717 0.28125/0.65344871486521738 0.29296875/0.66677500923563593 0.3046875/0.67964838149075235 0.31640625/0.6910493179152648 0.328125/0.70214399210941336 0.33984375/0.71293769240972005 0.3515625/0.72343570715270744 0.36328125/0.73364332467489723 0.375/0.74356583331281156 0.38671875/0.75320852140297312 0.3984375/0.76257667728190348 0.41015625/0.77167558928612512 0.421875/0.78051054575216006 0.43359375/0.78908683501653032 0.4453125/0.7974097454157586 0.45703125/0.80548456528636625 0.46875/0.81331658296487608 0.48046875/0.82091108678780988 0.4921875/0.82827336509169003 0.50390625/0.83540870621303842 0.515625/0.84232239848837742 0.52734375/0.84901973025422939 0.5390625/0.85550598984711601 0.55078125/0.86178646560355965 0.5625/0.86786644586008233 0.57421875/0.87375121895320629 0.5859375/0.87944607321945378 0.59765625/0.88495629699534695 0.609375/0.89028717861740791 0.62109375/0.89544400642215893 0.6328125/0.90043206874612158 0.64453125/0.90525665392581867 0.65625/0.9099230502977721 0.66796875/0.91443654619850423 0.6796875/0.91880242996453687 0.69140625/0.92302598993239249 0.703125/0.92711251443859277 0.71484375/0.93106729181966053 0.7265625/0.93489561041211733 0.73828125/0.93860275855248565 0.75/0.94219402457728751 0.76171875/0.94567469682304517 0.7734375/0.94905006362628086 0.78515625/0.95232541332351639 0.796875/0.95550603425127423 0.80859375/0.9585972147460764 0.8203125/0.96160424314444504 0.83203125/0.96453240778290228 0.84375/0.96738699699797048 0.85546875/0.97017329912617156 0.8671875/0.97289660250402765 0.87890625/0.97556219546806111 0.890625/0.97817536635479407 0.90234375/0.98074140350074845 0.9140625/0.98326559524244661 0.92578125/0.98575322991641046 0.9375/0.98820959585916268 0.94921875/0.99063998140722487 0.9609375/0.99304967489711937 0.97265625/0.99544396466536833 0.984375/0.9978281390484941 0.99609375/1":red="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1":green="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1":blue="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1'
    [v]" -map '[v]' -an brushturkey_better_colour.m4v

… was crucial to our discoveries today

  1. fade in and fade out video effect …
  2. colour balancing improvements regarding an underexposed video …

Further reading for you here, could involve …


Previous relevant Ffmpeg Video Subliminal Message Tutorial is shown below.

Ffmpeg Video Subliminal Message Tutorial

Ffmpeg Video Subliminal Message Tutorial

Revisiting the brilliant ffmpeg, as discussed at the recent Ffmpeg Install and Public Face Tutorial

  • the other day we were reading the excellent Fastest way to extract frames using ffmpeg? and noted it down for a future discussion … and then today …
  • 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 …

What is the secret word?
Raw video … to it’s slides …
ffmpeg -i ../IMG_2784.MOV -r 1/1 %03d.jpg
ffmpeg -i %03d.jpg brush_turkey.m4v
Hard guess?
ffmpeg -r 8 -i %03d.jpg brush-turkey.m4v
Easier guess?
ffmpeg -r 6 -i %03d.jpg brush__turkey.m4v


Previous relevant Ffmpeg Install and Public Face Tutorial is shown below.

Ffmpeg Install and Public Face Tutorial

Ffmpeg Install and Public Face Tutorial

Our recent web server VPS migration can not only be …

  • a migration of functionality (eg. this WordPress blog) … as well as …
  • an opportunity to open up new vistas of serverside functionality

… such as today’s ffmpeg install, which is only feasible because of the …

  • additional diskspace … chance on new web server allows for …
  • chance to install ffmpeg …

    dnf config-manager --set-enabled powertools
    dnf install --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-$(rpm -E %rhel).noarch.rpm -y
    dnf install --nogpgcheck https://mirrors.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-$(rpm -E %rhel).noarch.rpm -y
    dnf install ffmpeg

    … thanks to this great link, along with ImageMagick …

… on this new “public face” (even more public when DNS is applied later).

Codewise this involved …

… further to recent work touching these ideas with Animated GIF Creator Overlayed Images Top Left Tutorial.

See some of this public install of ffmpeg here.


Previous relevant Animated GIF Creator Overlayed Images Top Left Tutorial is shown below.

Animated GIF Creator Overlayed Images Top Left Tutorial

Animated GIF Creator Overlayed Images Top Left Tutorial

To add to the recent Animated GIF Creator Overlayed Images Tutorial‘s user interaction logic, we add a way to control those two “overlay” CSS properties …

  • left
  • top

… but more in the sense that we allow CSS …

  • margin-eft
  • margin-top

… come into the picture, after a dalliance with CSS calc thoughts.

This allows for offsetted images be part of the “overlay mix” that you can retry this at our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator.


Previous relevant Animated GIF Creator Overlayed Images Tutorial is shown below.

Animated GIF Creator Overlayed Images Tutorial

Animated GIF Creator Overlayed Images Tutorial

We wanted to add an “Overlayed Images” functionality component to the “Animated GIF Creator” of Animated GIF Creator Video Intranet Tutorial, today.

The work involves both of the HTML design “big concepts” we are keen on at this blog …

  • overlay … via CSS control of
    1. position:absolute;top:0px;left:0px;
    2. opacity:1.0;
    3. z-index:1;
  • reveal … lately, mostly, via HTML use of …
    1. details
    2. 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 …


<input disabled onclick="overlaythem();" id="overlayit" type="button" title='Overlay Images' value='Overlay Images'></input>

… starts off as disabled … and works with new HTML div elements …


<div id=overlayedj><h1>&nbsp;</h1><br></div><div id=overlayedi></div>

… until at least two images are defined, at which time the button becomes enabled …


function onfit(ithis) {
if (ithis.value == '' && ('' + ithis.placeholder).indexOf(']') != -1) {
ithis.value=('' + ithis.placeholder).split('[')[1].split(']')[0];
}
ithis.type='number';
}

function overlayds(inm, newop, newzi) {
var outdets='';
var divrect=null;
var poslt=' data-style=\"position:absolute;left:px;top:px;\" ';
var reposit='';
if (!document.getElementById('detsum' + ('' + inm))) {
if (document.getElementById('overlayedi').innerHTML == '') {
document.getElementById('overlayedi').title='Overlayed Images Below ... Click for It in New Window ...';
document.getElementById('overlayedj').innerHTML='<h1>Overlayed Images Below ...</h1><br>';
divrect=document.getElementById('overlayedi').getBoundingClientRect();
poslt=' data-style=\"position:absolute;left:' + ('' + divrect.left).split('.')[0].trim() + 'px;top:' + ('' + eval(200 + eval('' + ('' + divrect.top).split('.')[0]))).trim() + 'px;\" ';
} else {
poslt=' data-style=\"' + document.getElementById('detsum1').getAttribute('data-style') + '\" ';
}
outdets='&nbsp;&nbsp;<details ' + poslt + ' id=\"detsum' + ('' + inm) + '\" style=\" display:inline-block; \" open><summary id=\"sumdet' + ('' + inm) + '\">Overlay</summary><input onchange=overlayds(' + inm + ',this.value,String.fromCharCode(32)); onblur=overlayds(' + inm + ',this.value,String.fromCharCode(32)); type=text onfocus=onfit(this); id=\"opacity' + ('' + inm) + '\" style=\" display:inline-block; \" placeholder=\"Opacity [1.00]\" value=\"\" step=0.01 min=0.00 max=1.00></input><br><input onfocus=onfit(this); onchange=overlayds(' + inm + ',String.fromCharCode(32),this.value); onblur=overlayds(' + inm + ',String.fromCharCode(32),this.value); type=text id=\"zindex' + ('' + inm) + '\" style=\" display:inline-block; \" placeholder=\"Z-Index [1]\" value=\"\" min==999 max=999 step=1></input><br></details>';
reposit=poslt.replace(' data-',' ');
reposit=reposit.replace('\"', '\"opacity:1.0;z-index:1;');
if (document.getElementById('overlayedi').innerHTML == '') {
document.getElementById('overlayedi').innerHTML='<img id=\"imgdet' + ('' + inm) + '\" src=\"' + document.getElementById('slideshow' + ('' + inm).replace(/^1$/g,'')).value + '\" ' + reposit + '></img>';
document.getElementById('overlayedi').style.width=('' + document.getElementById('imgdet' + ('' + inm)).width).replace('px','') + 'px';
document.getElementById('overlayedi').style.height=('' + document.getElementById('imgdet' + ('' + inm)).height).replace('px','') + 'px';
document.getElementById('overlayedi').onclick = function(){ var woo=window.open('','_blank','top=100,left=100,width=' + document.getElementById('overlayedi').style.width.replace('px','') + ',height=' + document.getElementById('overlayedi').style.height.replace('px','')); woo.document.write('<html><head><title>Overlayed Images Below ...</title></head><body>' + document.getElementById('overlayedi').outerHTML.replace(/left\:[\ ]*/g,'left:0.0').replace(/top\:[\ ]*/g,'top:0.0') + '</body></html>'); woo.document.title='Overlayed Images Below ...'; };
} else {
document.getElementById('overlayedi').innerHTML+='<img id=\"imgdet' + ('' + inm) + '\" src=\"' + document.getElementById('slideshow' + ('' + inm).replace(/^1$/g,'')).value + '\" ' + reposit + '></img>';
}
} else {
document.getElementById('detsum' + ('' + inm)).style.display='inline-block';
if (newop != ' ') {
if (newop.trim() == '') {
document.getElementById('imgdet' + inm).style.opacity='1.0';
} else {
document.getElementById('imgdet' + inm).style.opacity='' + newop;
}
}
if (newz != ' ') {
if (newz.trim() == '') {
document.getElementById('imgdet' + inm).style.zIndex='1';
} else {
document.getElementById('imgdet' + inm).style.zIndex='' + newzi;
}
}
}
return outdets;
}

function overlaythem() {
var suffidea=1;
while (document.getElementById('slideshow' + ('' + suffidea).replace(/^1$/g, '')).value != '') {
document.getElementById('ours' + ('' + suffidea)).innerHTML+=overlayds(suffidea,'','');
suffidea++;
}
}

You can try this at our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator.


Previous relevant Animated GIF Creator Video Intranet Tutorial is shown below.

Animated GIF Creator Video Intranet Tutorial

Animated GIF Creator Video Intranet Tutorial

With the recent Animated GIF Creator Video Revisit Tutorial (preceding Animated GIF Creator Video Share Tutorial) we wrote …

… 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 …

  • the user is using our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator containing this new “Convert to Video” option off the “ImageMagick” dropdown …
  • the user defines some image slide data … eg. browsing to create data-URIs (but careful not to do too many) …
  • the user selects that “Convert to Video” option …
  • a window.open popup calls URL like …
    HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php?video=1687644
    … in a new window …
  • those data-URIs are converted into /tmp/jmtmp0*.[imageExt] files … ready for …
  • ffmpeg commands like …

    ffmpeg -framerate 0.1 -i /tmp/jmtmp%03d.jpeg -c:v libvpx-vp9 -pix_fmt yuva420p -lossless 1 -c:a copy video.webm; ffmpeg -i video.webm video.mov 2>> video.bad

    … 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) …

  1. 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) …
  2. the most we can ask at the receiver is that window.opener is defined … and if so, just at that discovery …
  3. 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) …
  4. 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';
    };


    im.src=tryit;
    tryit='';
    }


Previous relevant Animated GIF Creator Video Revisit Tutorial is shown below.

Animated GIF Creator Video Revisit Tutorial

Animated GIF Creator Video Revisit Tutorial

We hope you realize that our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator (helped out by a changed emailhtml.php inhouse email creator helper serverside PHP web application) can be accessed …

  1. in that public RJM Programming mode of use above … but even better can be …
  2. download relevant code to macOS MAMP local web server places off /Applications/MAMP/htdocs/ $_SERVER[‘DOCUMENT_ROOT’] places …

    … 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

And so, onto yesterday’s Animated GIF Creator PDF Last Reveal Tutorial we decided to revisit video creation parts of our inhouse Animated GIF Creator on this MacBook Air. We needed to reinstall ffmpeg, and we show that in today’s animated GIF tutorial picture and also created the “video.mov” video below, in the process …

… 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 …

<?php

$videofr=" -r " . (1000 / $delay) . " ";
$videofr=" -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS ";
$videomime="video/mp4";
$videoext=".mp4";

$videofr=" -framerate 0.1 ";
$videoext=".webm";
$videomime="video/webm";
$videomidbit="-c:v libvpx-vp9 -pix_fmt yuva420p -lossless 1 -c:a copy";
//$videocmd=$videopath . "ffmpeg -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS " . " -i " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "%03d.jpg " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mp4 2> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
//$svideocmd="ffmpeg -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS " . " -i " . "%03d.jpg " . "video.mp4";
$videocmd=$videopath . "ffmpeg " . $videofr . " -i " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "%03d.jpg " . $videomidbit . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " 2> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$svideocmd="ffmpeg " . $videofr . " -i " . "%03d.jpg " . $videomidbit . " " . "video" . $videoext;
$videocmd.="; " . $videopath . "ffmpeg -i " . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mov 2>> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$svideocmd.="; " . "ffmpeg -i " . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mov 2>> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$videohtml="<video title='" . $svideocmd . "' controls id=ivideo type='" . $videomime . "'><source src='video.mov'></source><source src='video" . $videoext . "'></source></video>";
$videosuffix="";

?>


Previous relevant Animated GIF Creator PDF Last Reveal Tutorial is shown below.

Animated GIF Creator PDF Last Reveal Tutorial

Animated GIF Creator PDF Last Reveal Tutorial

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 changed tutorial_to_animated_gif.php inhouse Animated GIF Creator (helped out by a changed emailhtml.php inhouse email creator helper serverside PHP web application) that we place three Emoji Buttons …

  1. 📟 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
  2. 📧 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
  3. 📧 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();
}
}
}
}

“; ?>

Cute, huh?!


Previous relevant Animated GIF Creator PDF Order Tutorial is shown below.

Animated GIF Creator PDF Order Tutorial

Animated GIF Creator PDF Order Tutorial

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 …
    1. no PDF conversion
    2. full PDF conversion … with the user choices regarding raw image content and title and watermarking and ImageMagick and GD modifiers
    3. 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)
    4. 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 changed tutorial_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.


Previous relevant Animated GIF Creator PDF Share Tutorial is shown below.

Animated GIF Creator PDF Share Tutorial

Animated GIF Creator PDF Share Tutorial

To share yesterday’s Animated GIF Creator PDF Conversion Tutorial PDF data formatted image slides (and then onto an animated GIF) would be a step forward, wouldn’t you say?

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)

… the email methodology used being that “midair feeling” Ajax/FormData approach in our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application …

<?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 changed emailhtml.php inhouse email creator helper serverside PHP web application …

<?php

$psacv="";
$okayin=true;
$subd="";
$subptitle='';
$subdata="";
$subfile="";
$getpostfound=false;
<br>
foreach( $_POST as $name=>$val ) {
if ($val != "") {
// blah blah blah //file_put_contents("jnv." . $name, "jnv.inv00 " . strlen($phpcont));
if ($name == "tdhuhta" && $subfile != "" && $subdata != "" && strpos(str_replace("+"," ",urldecode($val)), $subfile) !== false) {
if (strpos(str_replace("+"," ",urldecode($val)), ' srcdoc="') !== false || strpos(str_replace("+"," ",urldecode($val)), ' data="') !== false) {
if (strpos(str_replace("+"," ",urldecode($val)), ' title="') !== false) { //file_put_contents('haaa.aaa',explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1]);
if (!file_exists('../PHP/animegif/' . explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1] . 'animegif.pdf')) {
if (file_exists('../PHP/animegif/animegif.pdf')) {
copy('../PHP/animegif/animegif.pdf', '../PHP/animegif/' . explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1] . 'animegif.pdf');
}
}
$psacv=explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0] . 'animegif.pdf';
$subptitle='<a target=_blank href="' . explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0] . 'animegif.pdf">Mobile whole PDF</a><br>';
}
$vrest=explode('"', explode(' srcdoc="',str_replace("+"," ",urldecode($val)))[1])[0];
$val=urlencode("<body>" . $subptitle . "<iframe style='width:100%;height:95vh;overflow:auto;-webkit-overflow-scrolling:touch;' srcdoc='<div style=width:92%;height:95vh;overflow:auto;-webkit-overflow-scrolling:touch;><object id=myobj style=width:95%;height:95vh; type=application/pdf data=\"" . $subdata . "\"></object></div>'></iframe></body>");
}
// blah blah blah
if ($name == "inline") {
if (strpos(str_replace("+"," ",urldecode($val)), 'animegif.pdf') !== false && isset($_POST['tdhuhta'])) {
if (file_exists(str_replace("+"," ",urldecode($val)))) {
$subfile=str_replace("+"," ",urldecode($val));
$subdata='data:application/pdf;base64,' . base64_encode(file_get_contents(str_replace("+"," ",urldecode($val))));
$val="";
$name="x";
$okayin=false;
}
}
// blah blah blah
}
}

?>

Creating the PDFs got helped out in “reliability terms” via the introduced animegif.php inhouse Wait Around Longer serverside PHP web application.


Previous relevant Animated GIF Creator PDF Conversion Tutorial is shown below.

Animated GIF Creator PDF Conversion Tutorial

Animated GIF Creator PDF Conversion Tutorial

Up to yesterday’s Animated GIF Creator Slide Specific Application Tutorial the recent progress had us …

  • knowing the good places to intervene between the original slide image data being read in and the animated GIF slide output … and …
  • involvement of ImageMagick … so, today, these two help us down the path to …
  • adding PDF as an output format possibility, in addition to the animated GIF and other ideas floated as output formats

… with our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator 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.

We hope you stay the course!


Previous relevant Animated GIF Creator Slide Specific Application Tutorial is shown below.

Animated GIF Creator Slide Specific Application Tutorial

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 changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application (helped out by a changed exif_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;
}


?>

teamed with

<?php

$ours="our";
for ($io=1; $io$ours=ourcomplicated($io, $iappliedto);
// blah blah blah
if (strpos($aphoto[0], ";base64,") !== false) {
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromstring(base64_decode('" . explode(";base64,",$aphoto[0])[1] . "'));", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachonestring));
} else if (strpos(strtolower($aphoto[0]), "//") !== false) {
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromstring(@file_get_contents('" . $prefix . $aphoto[0] . "'));", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".jp") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
//file_put_contents('qpqp.qpqp', $eachone . "\n\n" . $atext . "\n\n" . str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromjpeg('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
//file_put_contents('qpqpqp.qpqpqp', $gifphp);
} else if (strpos(strtolower($aphoto[0]), ".png") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefrompng('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".gif") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromgif('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".bmp") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefrombmp('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
}
// blah blah blah
}

?>

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.

Get the picture?!


Previous relevant Animated GIF Creator Exif Rotation Compensation Tutorial is shown below.

Animated GIF Creator Exif Rotation Compensation Tutorial

Animated GIF Creator Exif Rotation Compensation Tutorial

The recent couple of days of work like yesterday’s Animated GIF Creator GD Transformations Interfacing Tutorial have given us “rotation functionality” tools to counteract the unusual effects Exif …

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.

… can have converting mobile device camera created images into animated GIFs via our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.

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.


Previous relevant Animated GIF Creator GD Transformations Interfacing Tutorial is shown below.

Animated GIF Creator GD Transformations Interfacing Tutorial

Animated GIF Creator GD Transformations Interfacing Tutorial

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 …

… into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application. We found it more user friendly to tailor the asking of numerical parameters for these GD calls work via …

<?php

$gfcds='';
$gdfs=[];
if (strpos(($imvgn . $imafn . $imqty . $imgds), '|') !== false) {
$gdfs=explode(';', trim(explode('|', $imvgn . $imafn . $imqty . $imgds)[1]));
for ($itf=0; $itf<sizeof($gdfs); $itf++) {
if (trim($gdfs[$itf]) != '') {
if (trim($gdfs[$itf]) == strtolower(trim($gdfs[$itf]))) {
$gfcds.=chr(10) . " imagefilter(" . "$" . "oimo, IMG_FILTER_" . strtoupper(trim($gdfs[$itf])) . "); " . chr(10);
} else if (trim($gdfs[$itf]) == strtoupper(trim($gdfs[$itf]))) {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo, IMG_" . str_replace(" ","_",strtoupper(trim( str_replace(explode(' ',$gdfs[$itf])[0] . ' ','',$gdfs[$itf]) ))) . "); " . chr(10);
} else if (strpos(strtoupper(trim($gdfs[$itf])), "SCALE") !== false) {
$gfcds.=chr(10) . " $" . "oimo=image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . str_replace(" ","_",(trim( str_replace(explode(',',explode(" ",(trim($gdfs[$itf])))[0])[0],'',$gdfs[$itf]) ))) . "); " . chr(10);
} else if (strpos(strtoupper(trim($gdfs[$itf])), "COPY") === false) {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . str_replace(" ","_",(trim( str_replace(explode(',',explode(" ",(trim($gdfs[$itf])))[0])[0],'',$gdfs[$itf]) ))) . "); " . chr(10);
} else {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . "," . "$" . "oimo" . explode("," . "$" . "oimo", $gdfs[$itf])[1] . "); " . chr(10);
}
}
}
}

?>

… to work with modified PHP writes PHP of the ilk …

<?php echo ”

function ourimagecreatefromstring(\$inidis) {
global \$imvgn, \$imafn, \$imqty, \$imgds, \$gfcds;
if ((\$imvgn . \$imafn . \$imqty . \$imgds) != '' && strpos(\$inidis, ';base64,') !== false) {
\$extis=str_replace('jpeg','jpg',explode('/', explode(';base64,', \$inidis)[0])[1]);
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, base64_encode(explode(';base64,', \$inidis)[1]));
\$fzis=filesize(\$sofarout);
if (trim(explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0]) != '') {
exec('" . $immogpath . "mogrify ' . explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0] . ' /tmp/imtmp.' . \$extis);
}
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false ||
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
\$oimo=null;
if (strtolower(\$extis) == 'png') {
\$oimo=imagecreatefrompng('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'bmp') {
\$oimo=imagecreatefrombmp('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'gif') {
\$oimo=imagecreatefromgif('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
\$oimo=imagecreatefromjpeg('/tmp/imtmp.' . \$extis); " . $gfcds . "
}
if (\$oimo) { return \$oimo; }
} else if ((\$imvgn . \$imafn . \$imqty . \$imgds) != '') {
\$extis=str_replace('jpeg','jpg','" . $inextis . "');
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
if (strpos(strtolower((\$imvgn . \$imafn . \$imqty . \$imgds)), '-format ') === false) { \$imvgn.=' -format " . str_replace('jpg','jpeg',$inextis) . " '; }
if (trim(explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0]) != '') {
exec('" . $immogpath . "mogrify ' . explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0] . ' /tmp/imtmp.' . \$extis);
}
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
\$oimo=null;
if (strtolower(\$extis) == 'png') {
\$oimo=imagecreatefrompng('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'bmp') {
\$oimo=imagecreatefrombmp('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'gif') {
\$oimo=imagecreatefromgif('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
\$oimo=imagecreatefromjpeg('/tmp/imtmp.' . \$extis); " . $gfcds . "
}
if (\$oimo) { return \$oimo; }
}
return imagecreatefromstring(\$inidis);
}

“; “?>

… and to get there clientwise, in the Javascript …


function gdadd(oselo) {
var pari=1, thispari='0';
if (oselo.value != '') {
//alert(oselo.value);
var oselovalue=oselo.value.replace(',' + '$' + 'oimo','~' + '$' + 'oimo').split(',')[0];
//alert(oselovalue);
var oseloval=oselo.value;
if (document.getElementById('imgds')) { if (document.getElementById('imgds').value == '') { document.getElementById('imgds').value='|'; } else { document.getElementById('imgds').value+=';'; } }
oseloval=oseloval.replace(',' + '$' + 'oimo','~' + '$' + 'oimo');
oseloval=oseloval.replace(oseloval.split(',')[0], '');
while (oseloval.indexOf(',') != -1) {
//alert(oseloval);
if ((oseloval + ' ').substring(1).split(',')[0].trim() != '') {
if ((' ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' ').toLowerCase().indexOf(' colour ') != -1) {
thispari=prompt('Enter' + (' ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' ').toLowerCase().replace(' colour ', ' red,green,blue colour ') + 'parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
if (thispari == null) {
thispari='0';
} else if (thispari.trim() == '') {
thispari='0';
} else if (thispari.indexOf(',') == -1) {
oselovalue+=',' + thispari + '';
} else {
oselovalue+=',imagecolorallocate(' + '$' + 'oimo,' + thispari + ')';
}
} else {
thispari=prompt('Enter ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
if (thispari == null) {
thispari='0';
} else {
oselovalue+=',' + thispari;
}
}
oseloval=oseloval.replace(',' + (oseloval + ' ').substring(1).split(',')[0].trim(),'').trim();
} else {
thispari=prompt('Enter numerical parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
oseloval=oseloval.replace(',','');
if (thispari == null) {
thispari='0';
} else {
oselovalue+=',' + thispari;
}
}
//oseloval=oseloval.replace(oseloval.split(',')[0] + ',', '');
//oseloval=oseloval.replace(',','');
pari++;
}
if (document.getElementById('gdopt')) {
document.getElementById('gdopt').innerHTML+='  ' + oselovalue.replace('~',',');
}
if (document.getElementById('imgds')) {
document.getElementById('imgds').value+=oselovalue.replace('~',',');
//document.getElementById('imgds').style.display='inline-block';
//document.getElementById('imgds').style.width='300px';
if (document.getElementById('smyim').innerHTML == '') { document.getElementById('smyim').innerHTML='ImageMagick switches: '; }
} else if (document.getElementById('myim')) {
document.getElementById('myim').innerHTML='<span is=smyim>ImageMagick switches: </span><input id=imqty name=imqty type=text style=display:inline-block; value=\"\"></input><input id=imgds name=imgds type=hidden style=display:inline-block; value=\"|' + oselovalue.replace('~',',') + '\"></input>';
document.getElementById('imqty').style.width='300px';
}
oselo.value='';
}
}


Previous relevant Animated GIF Creator GD Filter Interfacing Tutorial is shown below.

Animated GIF Creator GD Filter Interfacing Tutorial

Animated GIF Creator GD Filter Interfacing Tutorial

We are into image modification on the way to creating “compiled” animated GIF images, at the moment. Yesterday’s Animated GIF Creator ImageMagick Interfacing Tutorial got us interfacing (or integrating) …

  • ImageMagick batch processing “mogrify” ideas … and, today, it is the turn of …
  • GD image filters …

… into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.


Previous relevant Animated GIF Creator ImageMagick Interfacing Tutorial is shown below.

Animated GIF Creator ImageMagick Interfacing Tutorial

Animated GIF Creator ImageMagick Interfacing Tutorial

Yes, you guessed it! On top of yesterday’s ImageMagick Vignette Primer Tutorial, today we want to interface the ImageMagick batch processing “mogrify” ideas into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.

“Interfacing” (or integrating) into code often has you asking the question …


Where do we intervene to place our logic?

We’ve decided to “wrap” our PHP GD calls …

… into inhouse “our” prefixed function versions as per (PHP writing out PHP) …

<?php echo ”

\$imvgn='" . $imvgn . "'; /" . "/ vignette factor
\$imafn='" . $imafn . "'; /" . "/ affine transformation
\$imqty='" . $imqty . "'; /" . "/ image conversion and/or quality RE jpeg


function ourimagecreatefromstring(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '' && strpos(\$inidis, ';base64,') !== false) {
\$extis=str_replace('jpeg','jpg',explode('/', explode(';base64,', \$inidis)[0])[1]);
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, base64_encode(explode(';base64,', \$inidis)[1]));
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false ||
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
} else if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis=str_replace('jpeg','jpg','" . $inextis . "');
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
if (strpos(strtolower((\$imvgn . \$imafn . \$imqty)), '-format ') === false) { \$imvgn.=' -format " . str_replace('jpg','jpeg',$inextis) . " '; }
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromstring(\$inidis);
}

function ourimagecreatefromjpeg(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='jpg';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromjpeg(\$inidis);
}

function ourimagecreatefrompng(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='png';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefrompng(\$inidis);
}

function ourimagecreatefromgif(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='gif';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromgif(\$inidis);
}

function ourimagecreatefrombmp(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='bmp';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefrombmp(\$inidis);
}

“; ?>

… and change existant calls into (equivalent inhouse) “our” function calls as above.


Previous relevant ImageMagick Vignette Primer Tutorial is shown below.

ImageMagick Vignette Primer Tutorial

ImageMagick Vignette Primer Tutorial

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 …
  • vignette 50% (“mogrify” uses switch -vignette 50 here)

… 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.

By the way, should you be interested in photo orientation off a mobile device camera, have a read of PHP Exif Image Information Revisit Tutorial.

Nala and Luna and Charlie bid you a fond farewell!


Previous relevant Gimp Vignette Primer Tutorial is shown below.

Gimp Vignette Primer Tutorial

Gimp Vignette Primer Tutorial

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:

  1. Open Gimp graphical editor application
  2. File->Open … pick your image file
  3. Layer->New Layer (we’ll call “Vg”) … pick Foreground Colour
  4. In the Layers dialog, click on your “Vg” layer to select it, and select Soft light from the “Mode” drop-down box
  5. 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”
  6. Below Opacity bar select Link icon next to Eye icon, which will already be showing
  7. 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
  8. Use your bucket tool (Shift+B) and click within the selection to fill it
  9. Deselect your selection with Select->None
  10. 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
  11. 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%
  12. 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 an image comparison link.

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.


Previous relevant Gimp Transparency Primer Tutorial is shown below.

Gimp Transparency Primer Tutorial

Gimp Transparency Primer Tutorial

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:

  1. Open Gimp graphical editor application
  2. File->Open Layers … pick your image file
  3. If Layers window not showing, make it show via Windows->Layers – Brushes
  4. Below Opacity bar select Link icon next to Eye icon, which will already be showing
  5. Change Opacity bar setting to a value of Transparency (100% is Opaque, 0% is Transparent) that suits … today we do 70%
  6. File->Export
  7. 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.


Previous relevant Gimp Layers Primer Tutorial is shown below.

Gimp Layers Primer Tutorial

Gimp Layers Primer Tutorial

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.

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

ComboBox Idea Primer Tutorial

ComboBox Idea Primer Tutorial

ComboBox Idea Primer Tutorial

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>

… in our revisit to the concept, today, with a first draft ComboBox ideas exploratory effort.


Previous relevant Button Innards Primer Tutorial is shown below.

Button Innards Primer Tutorial

Button Innards Primer Tutorial

Yesterday’s …

… 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 …


Previous relevant Bookmark Exporting Filtering Sharing Tutorial is shown below.

Bookmark Exporting Filtering Sharing Tutorial

Bookmark Exporting Filtering Sharing Tutorial

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 ...


if (document.URL.indexOf('?') == -1 && ('' + location.hash).indexOf('bmkdata=') != -1) {
setTimeout(function(){
document.getElementById('resulting').innerHTML=decodeURIComponent(('' + location.hash).split('bmkdata=')[1]).replace('><summary', ' open><summary').replace('<details title=', '<details data-title=');
fillin();
}, 5000);
}

… 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?!

So, again, feel free to try our “second draft” dated_bookmarks.html Dated Bookmark Export web application, yourself, perhaps below.


Previous relevant Making of Ffmpeg Video Overall Effects Tutorial is shown below.

Making of Ffmpeg Video Overall Effects Tutorial

Making of Ffmpeg Video Overall Effects Tutorial

At the end of yesterday’s Ffmpeg Video Overall Effects Tutorial there was an Unordered List …

… 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?!

Feel free to try our “first draft” proof of concept Dated Bookmark Export web application, yourself.

Did you know?

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.


Previous relevant Ffmpeg Video Overall Effects Tutorial is shown below.

Ffmpeg Video Overall Effects Tutorial

Ffmpeg Video Overall Effects Tutorial

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 …

  1. 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
  2. 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 …

  1. selection of a video still image, ideally not containing the Brush Turkey …
  2. open image in Gimp
  3. “Aoife”‘esque Colours -> Curves…
  4. opens Adjust Colour Curves subwindow (initially a straight line)
  5. but dragging it to become a curve in a way that brightens the underexposed image to improve … we then …
  6. click + button to Save presets to a name …
  7. controlled by left pointing infilled triangle icon click that Manages Presets … and we ended up with …
  8. bush_-_turkey.presets


  9. and then we discovered this Converts GIMP colour curve to something the FFmpeg can read in the curve filter excellent project, the extraction of the Python there allowing us to go …

    python3 ../gimp_curves_ffmpeg.py

    … to output brush.out

  10. which formed most of the final curves — ffmpeg examples inspired ffmpeg command …

    ffmpeg -y -i brush__turkey.m4v -filter_complex "
    [0:v]
    curves=
    master='0.0/0 0.01171875/0.033751864777061671 0.0234375/0.067435644319434537 0.03515625/0.10098325339242978 0.046875/0.1343266067613586 0.05859375/0.16739761919153215 0.0703125/0.20012820544826168 0.08203125/0.23245028029685835 0.09375/0.26429575850263332 0.10546875/0.29559655483089792 0.1171875/0.32628458404696314 0.12890625/0.35629176091614018 0.140625/0.38555000020374036 0.15234375/0.41399121667507488 0.1640625/0.44154732509545486 0.17578125/0.46815024023019147 0.1875/0.49373187684459596 0.19921875/0.51822414970397945 0.2109375/0.54155897357365324 0.22265625/0.56366826321892849 0.234375/0.58448393340511628 0.24609375/0.6039378988975278 0.2578125/0.62196207446147445 0.26953125/0.63848837486226717 0.28125/0.65344871486521738 0.29296875/0.66677500923563593 0.3046875/0.67964838149075235 0.31640625/0.6910493179152648 0.328125/0.70214399210941336 0.33984375/0.71293769240972005 0.3515625/0.72343570715270744 0.36328125/0.73364332467489723 0.375/0.74356583331281156 0.38671875/0.75320852140297312 0.3984375/0.76257667728190348 0.41015625/0.77167558928612512 0.421875/0.78051054575216006 0.43359375/0.78908683501653032 0.4453125/0.7974097454157586 0.45703125/0.80548456528636625 0.46875/0.81331658296487608 0.48046875/0.82091108678780988 0.4921875/0.82827336509169003 0.50390625/0.83540870621303842 0.515625/0.84232239848837742 0.52734375/0.84901973025422939 0.5390625/0.85550598984711601 0.55078125/0.86178646560355965 0.5625/0.86786644586008233 0.57421875/0.87375121895320629 0.5859375/0.87944607321945378 0.59765625/0.88495629699534695 0.609375/0.89028717861740791 0.62109375/0.89544400642215893 0.6328125/0.90043206874612158 0.64453125/0.90525665392581867 0.65625/0.9099230502977721 0.66796875/0.91443654619850423 0.6796875/0.91880242996453687 0.69140625/0.92302598993239249 0.703125/0.92711251443859277 0.71484375/0.93106729181966053 0.7265625/0.93489561041211733 0.73828125/0.93860275855248565 0.75/0.94219402457728751 0.76171875/0.94567469682304517 0.7734375/0.94905006362628086 0.78515625/0.95232541332351639 0.796875/0.95550603425127423 0.80859375/0.9585972147460764 0.8203125/0.96160424314444504 0.83203125/0.96453240778290228 0.84375/0.96738699699797048 0.85546875/0.97017329912617156 0.8671875/0.97289660250402765 0.87890625/0.97556219546806111 0.890625/0.97817536635479407 0.90234375/0.98074140350074845 0.9140625/0.98326559524244661 0.92578125/0.98575322991641046 0.9375/0.98820959585916268 0.94921875/0.99063998140722487 0.9609375/0.99304967489711937 0.97265625/0.99544396466536833 0.984375/0.9978281390484941 0.99609375/1":red="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1":green="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1":blue="0.0/0 0.01171875/0.011764705882352943 0.0234375/0.023529411764705879 0.03515625/0.035294117647058816 0.046875/0.047058823529411764 0.05859375/0.058823529411764712 0.0703125/0.07058823529411766 0.08203125/0.082352941176470587 0.09375/0.094117647058823528 0.10546875/0.10588235294117647 0.1171875/0.11764705882352938 0.12890625/0.12941176470588237 0.140625/0.14117647058823532 0.15234375/0.15294117647058825 0.1640625/0.1647058823529412 0.17578125/0.17647058823529413 0.1875/0.18823529411764706 0.19921875/0.20000000000000007 0.2109375/0.21176470588235291 0.22265625/0.22352941176470589 0.234375/0.23529411764705882 0.24609375/0.24705882352941175 0.2578125/0.25882352941176473 0.26953125/0.27058823529411768 0.28125/0.28235294117647064 0.29296875/0.29411764705882348 0.3046875/0.30588235294117649 0.31640625/0.31764705882352934 0.328125/0.3294117647058824 0.33984375/0.3411764705882353 0.3515625/0.35294117647058826 0.36328125/0.3647058823529411 0.375/0.37647058823529411 0.38671875/0.38823529411764712 0.3984375/0.39999999999999997 0.41015625/0.41176470588235292 0.421875/0.42352941176470582 0.43359375/0.43529411764705883 0.4453125/0.44705882352941184 0.45703125/0.45882352941176474 0.46875/0.47058823529411764 0.48046875/0.48235294117647054 0.4921875/0.49411764705882355 0.50390625/0.50588235294117645 0.515625/0.51764705882352946 0.52734375/0.52941176470588236 0.5390625/0.54117647058823526 0.55078125/0.55294117647058827 0.5625/0.56470588235294117 0.57421875/0.57647058823529407 0.5859375/0.58823529411764708 0.59765625/0.59999999999999998 0.609375/0.61176470588235299 0.62109375/0.62352941176470589 0.6328125/0.63529411764705879 0.64453125/0.6470588235294118 0.65625/0.65882352941176481 0.66796875/0.6705882352941176 0.6796875/0.68235294117647061 0.69140625/0.69411764705882351 0.703125/0.70588235294117663 0.71484375/0.71764705882352942 0.7265625/0.7294117647058822 0.73828125/0.74117647058823533 0.75/0.75294117647058822 0.76171875/0.76470588235294112 0.7734375/0.77647058823529425 0.78515625/0.78823529411764703 0.796875/0.80000000000000004 0.80859375/0.81176470588235294 0.8203125/0.82352941176470584 0.83203125/0.83529411764705874 0.84375/0.84705882352941175 0.85546875/0.85882352941176465 0.8671875/0.87058823529411766 0.87890625/0.88235294117647056 0.890625/0.89411764705882357 0.90234375/0.90588235294117636 0.9140625/0.91764705882352937 0.92578125/0.92941176470588238 0.9375/0.94117647058823528 0.94921875/0.95294117647058818 0.9609375/0.96470588235294119 0.97265625/0.97647058823529409 0.984375/0.98823529411764721 0.99609375/1'
    [v]" -map '[v]' -an brushturkey_better_colour.m4v

… was crucial to our discoveries today

  1. fade in and fade out video effect …
  2. colour balancing improvements regarding an underexposed video …

Further reading for you here, could involve …


Previous relevant Ffmpeg Video Subliminal Message Tutorial is shown below.

Ffmpeg Video Subliminal Message Tutorial

Ffmpeg Video Subliminal Message Tutorial

Revisiting the brilliant ffmpeg, as discussed at the recent Ffmpeg Install and Public Face Tutorial

  • the other day we were reading the excellent Fastest way to extract frames using ffmpeg? and noted it down for a future discussion … and then today …
  • 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 …

What is the secret word?
Raw video … to it’s slides …
ffmpeg -i ../IMG_2784.MOV -r 1/1 %03d.jpg
ffmpeg -i %03d.jpg brush_turkey.m4v
Hard guess?
ffmpeg -r 8 -i %03d.jpg brush-turkey.m4v
Easier guess?
ffmpeg -r 6 -i %03d.jpg brush__turkey.m4v


Previous relevant Ffmpeg Install and Public Face Tutorial is shown below.

Ffmpeg Install and Public Face Tutorial

Ffmpeg Install and Public Face Tutorial

Our recent web server VPS migration can not only be …

  • a migration of functionality (eg. this WordPress blog) … as well as …
  • an opportunity to open up new vistas of serverside functionality

… such as today’s ffmpeg install, which is only feasible because of the …

  • additional diskspace … chance on new web server allows for …
  • chance to install ffmpeg …

    dnf config-manager --set-enabled powertools
    dnf install --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-$(rpm -E %rhel).noarch.rpm -y
    dnf install --nogpgcheck https://mirrors.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-$(rpm -E %rhel).noarch.rpm -y
    dnf install ffmpeg

    … thanks to this great link, along with ImageMagick …

… on this new “public face” (even more public when DNS is applied later).

Codewise this involved …

… further to recent work touching these ideas with Animated GIF Creator Overlayed Images Top Left Tutorial.

See some of this public install of ffmpeg here.


Previous relevant Animated GIF Creator Overlayed Images Top Left Tutorial is shown below.

Animated GIF Creator Overlayed Images Top Left Tutorial

Animated GIF Creator Overlayed Images Top Left Tutorial

To add to the recent Animated GIF Creator Overlayed Images Tutorial‘s user interaction logic, we add a way to control those two “overlay” CSS properties …

  • left
  • top

… but more in the sense that we allow CSS …

  • margin-eft
  • margin-top

… come into the picture, after a dalliance with CSS calc thoughts.

This allows for offsetted images be part of the “overlay mix” that you can retry this at our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator.


Previous relevant Animated GIF Creator Overlayed Images Tutorial is shown below.

Animated GIF Creator Overlayed Images Tutorial

Animated GIF Creator Overlayed Images Tutorial

We wanted to add an “Overlayed Images” functionality component to the “Animated GIF Creator” of Animated GIF Creator Video Intranet Tutorial, today.

The work involves both of the HTML design “big concepts” we are keen on at this blog …

  • overlay … via CSS control of
    1. position:absolute;top:0px;left:0px;
    2. opacity:1.0;
    3. z-index:1;
  • reveal … lately, mostly, via HTML use of …
    1. details
    2. 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 …


<input disabled onclick="overlaythem();" id="overlayit" type="button" title='Overlay Images' value='Overlay Images'></input>

… starts off as disabled … and works with new HTML div elements …


<div id=overlayedj><h1>&nbsp;</h1><br></div><div id=overlayedi></div>

… until at least two images are defined, at which time the button becomes enabled …


function onfit(ithis) {
if (ithis.value == '' && ('' + ithis.placeholder).indexOf(']') != -1) {
ithis.value=('' + ithis.placeholder).split('[')[1].split(']')[0];
}
ithis.type='number';
}

function overlayds(inm, newop, newzi) {
var outdets='';
var divrect=null;
var poslt=' data-style=\"position:absolute;left:px;top:px;\" ';
var reposit='';
if (!document.getElementById('detsum' + ('' + inm))) {
if (document.getElementById('overlayedi').innerHTML == '') {
document.getElementById('overlayedi').title='Overlayed Images Below ... Click for It in New Window ...';
document.getElementById('overlayedj').innerHTML='<h1>Overlayed Images Below ...</h1><br>';
divrect=document.getElementById('overlayedi').getBoundingClientRect();
poslt=' data-style=\"position:absolute;left:' + ('' + divrect.left).split('.')[0].trim() + 'px;top:' + ('' + eval(200 + eval('' + ('' + divrect.top).split('.')[0]))).trim() + 'px;\" ';
} else {
poslt=' data-style=\"' + document.getElementById('detsum1').getAttribute('data-style') + '\" ';
}
outdets='&nbsp;&nbsp;<details ' + poslt + ' id=\"detsum' + ('' + inm) + '\" style=\" display:inline-block; \" open><summary id=\"sumdet' + ('' + inm) + '\">Overlay</summary><input onchange=overlayds(' + inm + ',this.value,String.fromCharCode(32)); onblur=overlayds(' + inm + ',this.value,String.fromCharCode(32)); type=text onfocus=onfit(this); id=\"opacity' + ('' + inm) + '\" style=\" display:inline-block; \" placeholder=\"Opacity [1.00]\" value=\"\" step=0.01 min=0.00 max=1.00></input><br><input onfocus=onfit(this); onchange=overlayds(' + inm + ',String.fromCharCode(32),this.value); onblur=overlayds(' + inm + ',String.fromCharCode(32),this.value); type=text id=\"zindex' + ('' + inm) + '\" style=\" display:inline-block; \" placeholder=\"Z-Index [1]\" value=\"\" min==999 max=999 step=1></input><br></details>';
reposit=poslt.replace(' data-',' ');
reposit=reposit.replace('\"', '\"opacity:1.0;z-index:1;');
if (document.getElementById('overlayedi').innerHTML == '') {
document.getElementById('overlayedi').innerHTML='<img id=\"imgdet' + ('' + inm) + '\" src=\"' + document.getElementById('slideshow' + ('' + inm).replace(/^1$/g,'')).value + '\" ' + reposit + '></img>';
document.getElementById('overlayedi').style.width=('' + document.getElementById('imgdet' + ('' + inm)).width).replace('px','') + 'px';
document.getElementById('overlayedi').style.height=('' + document.getElementById('imgdet' + ('' + inm)).height).replace('px','') + 'px';
document.getElementById('overlayedi').onclick = function(){ var woo=window.open('','_blank','top=100,left=100,width=' + document.getElementById('overlayedi').style.width.replace('px','') + ',height=' + document.getElementById('overlayedi').style.height.replace('px','')); woo.document.write('<html><head><title>Overlayed Images Below ...</title></head><body>' + document.getElementById('overlayedi').outerHTML.replace(/left\:[\ ]*/g,'left:0.0').replace(/top\:[\ ]*/g,'top:0.0') + '</body></html>'); woo.document.title='Overlayed Images Below ...'; };
} else {
document.getElementById('overlayedi').innerHTML+='<img id=\"imgdet' + ('' + inm) + '\" src=\"' + document.getElementById('slideshow' + ('' + inm).replace(/^1$/g,'')).value + '\" ' + reposit + '></img>';
}
} else {
document.getElementById('detsum' + ('' + inm)).style.display='inline-block';
if (newop != ' ') {
if (newop.trim() == '') {
document.getElementById('imgdet' + inm).style.opacity='1.0';
} else {
document.getElementById('imgdet' + inm).style.opacity='' + newop;
}
}
if (newz != ' ') {
if (newz.trim() == '') {
document.getElementById('imgdet' + inm).style.zIndex='1';
} else {
document.getElementById('imgdet' + inm).style.zIndex='' + newzi;
}
}
}
return outdets;
}

function overlaythem() {
var suffidea=1;
while (document.getElementById('slideshow' + ('' + suffidea).replace(/^1$/g, '')).value != '') {
document.getElementById('ours' + ('' + suffidea)).innerHTML+=overlayds(suffidea,'','');
suffidea++;
}
}

You can try this at our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator.


Previous relevant Animated GIF Creator Video Intranet Tutorial is shown below.

Animated GIF Creator Video Intranet Tutorial

Animated GIF Creator Video Intranet Tutorial

With the recent Animated GIF Creator Video Revisit Tutorial (preceding Animated GIF Creator Video Share Tutorial) we wrote …

… 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 …

  • the user is using our changed tutorial_to_animated_gif.php inhouse public domain Animated GIF Creator containing this new “Convert to Video” option off the “ImageMagick” dropdown …
  • the user defines some image slide data … eg. browsing to create data-URIs (but careful not to do too many) …
  • the user selects that “Convert to Video” option …
  • a window.open popup calls URL like …
    HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php?video=1687644
    … in a new window …
  • those data-URIs are converted into /tmp/jmtmp0*.[imageExt] files … ready for …
  • ffmpeg commands like …

    ffmpeg -framerate 0.1 -i /tmp/jmtmp%03d.jpeg -c:v libvpx-vp9 -pix_fmt yuva420p -lossless 1 -c:a copy video.webm; ffmpeg -i video.webm video.mov 2>> video.bad

    … 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) …

  1. 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) …
  2. the most we can ask at the receiver is that window.opener is defined … and if so, just at that discovery …
  3. 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) …
  4. 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';
    };


    im.src=tryit;
    tryit='';
    }


Previous relevant Animated GIF Creator Video Revisit Tutorial is shown below.

Animated GIF Creator Video Revisit Tutorial

Animated GIF Creator Video Revisit Tutorial

We hope you realize that our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator (helped out by a changed emailhtml.php inhouse email creator helper serverside PHP web application) can be accessed …

  1. in that public RJM Programming mode of use above … but even better can be …
  2. download relevant code to macOS MAMP local web server places off /Applications/MAMP/htdocs/ $_SERVER[‘DOCUMENT_ROOT’] places …

    … 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

And so, onto yesterday’s Animated GIF Creator PDF Last Reveal Tutorial we decided to revisit video creation parts of our inhouse Animated GIF Creator on this MacBook Air. We needed to reinstall ffmpeg, and we show that in today’s animated GIF tutorial picture and also created the “video.mov” video below, in the process …

… 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 …

<?php

$videofr=" -r " . (1000 / $delay) . " ";
$videofr=" -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS ";
$videomime="video/mp4";
$videoext=".mp4";

$videofr=" -framerate 0.1 ";
$videoext=".webm";
$videomime="video/webm";
$videomidbit="-c:v libvpx-vp9 -pix_fmt yuva420p -lossless 1 -c:a copy";
//$videocmd=$videopath . "ffmpeg -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS " . " -i " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "%03d.jpg " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mp4 2> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
//$svideocmd="ffmpeg -r " . (1000 / $delay) . " -filter:v setpts=" . ($delay / 1000) . "*PTS " . " -i " . "%03d.jpg " . "video.mp4";
$videocmd=$videopath . "ffmpeg " . $videofr . " -i " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "%03d.jpg " . $videomidbit . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " 2> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$svideocmd="ffmpeg " . $videofr . " -i " . "%03d.jpg " . $videomidbit . " " . "video" . $videoext;
$videocmd.="; " . $videopath . "ffmpeg -i " . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mov 2>> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$svideocmd.="; " . "ffmpeg -i " . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video" . $videoext . " " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.mov 2>> " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "video.bad";
$videohtml="<video title='" . $svideocmd . "' controls id=ivideo type='" . $videomime . "'><source src='video.mov'></source><source src='video" . $videoext . "'></source></video>";
$videosuffix="";

?>


Previous relevant Animated GIF Creator PDF Last Reveal Tutorial is shown below.

Animated GIF Creator PDF Last Reveal Tutorial

Animated GIF Creator PDF Last Reveal Tutorial

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 changed tutorial_to_animated_gif.php inhouse Animated GIF Creator (helped out by a changed emailhtml.php inhouse email creator helper serverside PHP web application) that we place three Emoji Buttons …

  1. 📟 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
  2. 📧 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
  3. 📧 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();
}
}
}
}

“; ?>

Cute, huh?!


Previous relevant Animated GIF Creator PDF Order Tutorial is shown below.

Animated GIF Creator PDF Order Tutorial

Animated GIF Creator PDF Order Tutorial

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 …
    1. no PDF conversion
    2. full PDF conversion … with the user choices regarding raw image content and title and watermarking and ImageMagick and GD modifiers
    3. 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)
    4. 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 changed tutorial_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.


Previous relevant Animated GIF Creator PDF Share Tutorial is shown below.

Animated GIF Creator PDF Share Tutorial

Animated GIF Creator PDF Share Tutorial

To share yesterday’s Animated GIF Creator PDF Conversion Tutorial PDF data formatted image slides (and then onto an animated GIF) would be a step forward, wouldn’t you say?

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)

… the email methodology used being that “midair feeling” Ajax/FormData approach in our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application …

<?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 changed emailhtml.php inhouse email creator helper serverside PHP web application …

<?php

$psacv="";
$okayin=true;
$subd="";
$subptitle='';
$subdata="";
$subfile="";
$getpostfound=false;
<br>
foreach( $_POST as $name=>$val ) {
if ($val != "") {
// blah blah blah //file_put_contents("jnv." . $name, "jnv.inv00 " . strlen($phpcont));
if ($name == "tdhuhta" && $subfile != "" && $subdata != "" && strpos(str_replace("+"," ",urldecode($val)), $subfile) !== false) {
if (strpos(str_replace("+"," ",urldecode($val)), ' srcdoc="') !== false || strpos(str_replace("+"," ",urldecode($val)), ' data="') !== false) {
if (strpos(str_replace("+"," ",urldecode($val)), ' title="') !== false) { //file_put_contents('haaa.aaa',explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1]);
if (!file_exists('../PHP/animegif/' . explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1] . 'animegif.pdf')) {
if (file_exists('../PHP/animegif/animegif.pdf')) {
copy('../PHP/animegif/animegif.pdf', '../PHP/animegif/' . explode('/animegif/', explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0])[1] . 'animegif.pdf');
}
}
$psacv=explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0] . 'animegif.pdf';
$subptitle='<a target=_blank href="' . explode('"', explode(' title="', str_replace("+"," ",urldecode($val)))[1])[0] . 'animegif.pdf">Mobile whole PDF</a><br>';
}
$vrest=explode('"', explode(' srcdoc="',str_replace("+"," ",urldecode($val)))[1])[0];
$val=urlencode("<body>" . $subptitle . "<iframe style='width:100%;height:95vh;overflow:auto;-webkit-overflow-scrolling:touch;' srcdoc='<div style=width:92%;height:95vh;overflow:auto;-webkit-overflow-scrolling:touch;><object id=myobj style=width:95%;height:95vh; type=application/pdf data=\"" . $subdata . "\"></object></div>'></iframe></body>");
}
// blah blah blah
if ($name == "inline") {
if (strpos(str_replace("+"," ",urldecode($val)), 'animegif.pdf') !== false && isset($_POST['tdhuhta'])) {
if (file_exists(str_replace("+"," ",urldecode($val)))) {
$subfile=str_replace("+"," ",urldecode($val));
$subdata='data:application/pdf;base64,' . base64_encode(file_get_contents(str_replace("+"," ",urldecode($val))));
$val="";
$name="x";
$okayin=false;
}
}
// blah blah blah
}
}

?>

Creating the PDFs got helped out in “reliability terms” via the introduced animegif.php inhouse Wait Around Longer serverside PHP web application.


Previous relevant Animated GIF Creator PDF Conversion Tutorial is shown below.

Animated GIF Creator PDF Conversion Tutorial

Animated GIF Creator PDF Conversion Tutorial

Up to yesterday’s Animated GIF Creator Slide Specific Application Tutorial the recent progress had us …

  • knowing the good places to intervene between the original slide image data being read in and the animated GIF slide output … and …
  • involvement of ImageMagick … so, today, these two help us down the path to …
  • adding PDF as an output format possibility, in addition to the animated GIF and other ideas floated as output formats

… with our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator 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.

We hope you stay the course!


Previous relevant Animated GIF Creator Slide Specific Application Tutorial is shown below.

Animated GIF Creator Slide Specific Application Tutorial

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 changed tutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application (helped out by a changed exif_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;
}


?>

teamed with

<?php

$ours="our";
for ($io=1; $io$ours=ourcomplicated($io, $iappliedto);
// blah blah blah
if (strpos($aphoto[0], ";base64,") !== false) {
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromstring(base64_decode('" . explode(";base64,",$aphoto[0])[1] . "'));", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachonestring));
} else if (strpos(strtolower($aphoto[0]), "//") !== false) {
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromstring(@file_get_contents('" . $prefix . $aphoto[0] . "'));", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".jp") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
//file_put_contents('qpqp.qpqp', $eachone . "\n\n" . $atext . "\n\n" . str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromjpeg('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
//file_put_contents('qpqpqp.qpqpqp', $gifphp);
} else if (strpos(strtolower($aphoto[0]), ".png") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefrompng('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".gif") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefromgif('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
} else if (strpos(strtolower($aphoto[0]), ".bmp") !== false) {
if (strpos($prefix, "../../") !== false && !file_exists($prefix . $aphoto[0]) && file_exists(str_replace("../../","../",$prefix) . $aphoto[0])) {
$prefix=str_replace("../../","../",$prefix);
}
$gifphp.=str_replace("imagecreatefromjpeg('source01.jpg');", "" . $ours . "imagecreatefrombmp('" . $prefix . $aphoto[0] . "');", str_replace("\$text='';", "\$text=\"" . str_replace("+"," ",urldecode($atext)) . "\";", $eachone));
}
// blah blah blah
}

?>

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.

Get the picture?!


Previous relevant Animated GIF Creator Exif Rotation Compensation Tutorial is shown below.

Animated GIF Creator Exif Rotation Compensation Tutorial

Animated GIF Creator Exif Rotation Compensation Tutorial

The recent couple of days of work like yesterday’s Animated GIF Creator GD Transformations Interfacing Tutorial have given us “rotation functionality” tools to counteract the unusual effects Exif …

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.

… can have converting mobile device camera created images into animated GIFs via our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.

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.


Previous relevant Animated GIF Creator GD Transformations Interfacing Tutorial is shown below.

Animated GIF Creator GD Transformations Interfacing Tutorial

Animated GIF Creator GD Transformations Interfacing Tutorial

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 …

… into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application. We found it more user friendly to tailor the asking of numerical parameters for these GD calls work via …

<?php

$gfcds='';
$gdfs=[];
if (strpos(($imvgn . $imafn . $imqty . $imgds), '|') !== false) {
$gdfs=explode(';', trim(explode('|', $imvgn . $imafn . $imqty . $imgds)[1]));
for ($itf=0; $itf<sizeof($gdfs); $itf++) {
if (trim($gdfs[$itf]) != '') {
if (trim($gdfs[$itf]) == strtolower(trim($gdfs[$itf]))) {
$gfcds.=chr(10) . " imagefilter(" . "$" . "oimo, IMG_FILTER_" . strtoupper(trim($gdfs[$itf])) . "); " . chr(10);
} else if (trim($gdfs[$itf]) == strtoupper(trim($gdfs[$itf]))) {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo, IMG_" . str_replace(" ","_",strtoupper(trim( str_replace(explode(' ',$gdfs[$itf])[0] . ' ','',$gdfs[$itf]) ))) . "); " . chr(10);
} else if (strpos(strtoupper(trim($gdfs[$itf])), "SCALE") !== false) {
$gfcds.=chr(10) . " $" . "oimo=image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . str_replace(" ","_",(trim( str_replace(explode(',',explode(" ",(trim($gdfs[$itf])))[0])[0],'',$gdfs[$itf]) ))) . "); " . chr(10);
} else if (strpos(strtoupper(trim($gdfs[$itf])), "COPY") === false) {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . str_replace(" ","_",(trim( str_replace(explode(',',explode(" ",(trim($gdfs[$itf])))[0])[0],'',$gdfs[$itf]) ))) . "); " . chr(10);
} else {
$gfcds.=chr(10) . " image" . explode(',',explode(" ",strtolower(trim($gdfs[$itf])))[0])[0] . "(" . "$" . "oimo" . "," . "$" . "oimo" . explode("," . "$" . "oimo", $gdfs[$itf])[1] . "); " . chr(10);
}
}
}
}

?>

… to work with modified PHP writes PHP of the ilk …

<?php echo ”

function ourimagecreatefromstring(\$inidis) {
global \$imvgn, \$imafn, \$imqty, \$imgds, \$gfcds;
if ((\$imvgn . \$imafn . \$imqty . \$imgds) != '' && strpos(\$inidis, ';base64,') !== false) {
\$extis=str_replace('jpeg','jpg',explode('/', explode(';base64,', \$inidis)[0])[1]);
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, base64_encode(explode(';base64,', \$inidis)[1]));
\$fzis=filesize(\$sofarout);
if (trim(explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0]) != '') {
exec('" . $immogpath . "mogrify ' . explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0] . ' /tmp/imtmp.' . \$extis);
}
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false ||
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
\$oimo=null;
if (strtolower(\$extis) == 'png') {
\$oimo=imagecreatefrompng('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'bmp') {
\$oimo=imagecreatefrombmp('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'gif') {
\$oimo=imagecreatefromgif('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
\$oimo=imagecreatefromjpeg('/tmp/imtmp.' . \$extis); " . $gfcds . "
}
if (\$oimo) { return \$oimo; }
} else if ((\$imvgn . \$imafn . \$imqty . \$imgds) != '') {
\$extis=str_replace('jpeg','jpg','" . $inextis . "');
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
if (strpos(strtolower((\$imvgn . \$imafn . \$imqty . \$imgds)), '-format ') === false) { \$imvgn.=' -format " . str_replace('jpg','jpeg',$inextis) . " '; }
if (trim(explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0]) != '') {
exec('" . $immogpath . "mogrify ' . explode('|', \$imvgn . \$imafn . \$imqty . \$imgds)[0] . ' /tmp/imtmp.' . \$extis);
}
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
\$oimo=null;
if (strtolower(\$extis) == 'png') {
\$oimo=imagecreatefrompng('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'bmp') {
\$oimo=imagecreatefrombmp('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strtolower(\$extis) == 'gif') {
\$oimo=imagecreatefromgif('/tmp/imtmp.' . \$extis); " . $gfcds . "
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
\$oimo=imagecreatefromjpeg('/tmp/imtmp.' . \$extis); " . $gfcds . "
}
if (\$oimo) { return \$oimo; }
}
return imagecreatefromstring(\$inidis);
}

“; “?>

… and to get there clientwise, in the Javascript …


function gdadd(oselo) {
var pari=1, thispari='0';
if (oselo.value != '') {
//alert(oselo.value);
var oselovalue=oselo.value.replace(',' + '$' + 'oimo','~' + '$' + 'oimo').split(',')[0];
//alert(oselovalue);
var oseloval=oselo.value;
if (document.getElementById('imgds')) { if (document.getElementById('imgds').value == '') { document.getElementById('imgds').value='|'; } else { document.getElementById('imgds').value+=';'; } }
oseloval=oseloval.replace(',' + '$' + 'oimo','~' + '$' + 'oimo');
oseloval=oseloval.replace(oseloval.split(',')[0], '');
while (oseloval.indexOf(',') != -1) {
//alert(oseloval);
if ((oseloval + ' ').substring(1).split(',')[0].trim() != '') {
if ((' ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' ').toLowerCase().indexOf(' colour ') != -1) {
thispari=prompt('Enter' + (' ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' ').toLowerCase().replace(' colour ', ' red,green,blue colour ') + 'parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
if (thispari == null) {
thispari='0';
} else if (thispari.trim() == '') {
thispari='0';
} else if (thispari.indexOf(',') == -1) {
oselovalue+=',' + thispari + '';
} else {
oselovalue+=',imagecolorallocate(' + '$' + 'oimo,' + thispari + ')';
}
} else {
thispari=prompt('Enter ' + (oseloval + ' ').substring(1).split(',')[0].trim() + ' parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
if (thispari == null) {
thispari='0';
} else {
oselovalue+=',' + thispari;
}
}
oseloval=oseloval.replace(',' + (oseloval + ' ').substring(1).split(',')[0].trim(),'').trim();
} else {
thispari=prompt('Enter numerical parameter for GD ' + oselo.value.split(',')[0] + ' ' + pari + ' of ' + eval(-1 + oselo.value.replace(',' + '$' + 'oimo','').split(',').length) + '?', '0');
oseloval=oseloval.replace(',','');
if (thispari == null) {
thispari='0';
} else {
oselovalue+=',' + thispari;
}
}
//oseloval=oseloval.replace(oseloval.split(',')[0] + ',', '');
//oseloval=oseloval.replace(',','');
pari++;
}
if (document.getElementById('gdopt')) {
document.getElementById('gdopt').innerHTML+='  ' + oselovalue.replace('~',',');
}
if (document.getElementById('imgds')) {
document.getElementById('imgds').value+=oselovalue.replace('~',',');
//document.getElementById('imgds').style.display='inline-block';
//document.getElementById('imgds').style.width='300px';
if (document.getElementById('smyim').innerHTML == '') { document.getElementById('smyim').innerHTML='ImageMagick switches: '; }
} else if (document.getElementById('myim')) {
document.getElementById('myim').innerHTML='<span is=smyim>ImageMagick switches: </span><input id=imqty name=imqty type=text style=display:inline-block; value=\"\"></input><input id=imgds name=imgds type=hidden style=display:inline-block; value=\"|' + oselovalue.replace('~',',') + '\"></input>';
document.getElementById('imqty').style.width='300px';
}
oselo.value='';
}
}


Previous relevant Animated GIF Creator GD Filter Interfacing Tutorial is shown below.

Animated GIF Creator GD Filter Interfacing Tutorial

Animated GIF Creator GD Filter Interfacing Tutorial

We are into image modification on the way to creating “compiled” animated GIF images, at the moment. Yesterday’s Animated GIF Creator ImageMagick Interfacing Tutorial got us interfacing (or integrating) …

  • ImageMagick batch processing “mogrify” ideas … and, today, it is the turn of …
  • GD image filters …

… into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.


Previous relevant Animated GIF Creator ImageMagick Interfacing Tutorial is shown below.

Animated GIF Creator ImageMagick Interfacing Tutorial

Animated GIF Creator ImageMagick Interfacing Tutorial

Yes, you guessed it! On top of yesterday’s ImageMagick Vignette Primer Tutorial, today we want to interface the ImageMagick batch processing “mogrify” ideas into our changed tutorial_to_animated_gif.php inhouse Animated GIF Creator PHP web application.

“Interfacing” (or integrating) into code often has you asking the question …


Where do we intervene to place our logic?

We’ve decided to “wrap” our PHP GD calls …

… into inhouse “our” prefixed function versions as per (PHP writing out PHP) …

<?php echo ”

\$imvgn='" . $imvgn . "'; /" . "/ vignette factor
\$imafn='" . $imafn . "'; /" . "/ affine transformation
\$imqty='" . $imqty . "'; /" . "/ image conversion and/or quality RE jpeg


function ourimagecreatefromstring(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '' && strpos(\$inidis, ';base64,') !== false) {
\$extis=str_replace('jpeg','jpg',explode('/', explode(';base64,', \$inidis)[0])[1]);
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, base64_encode(explode(';base64,', \$inidis)[1]));
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false ||
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
} else if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis=str_replace('jpeg','jpg','" . $inextis . "');
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
if (strpos(strtolower((\$imvgn . \$imafn . \$imqty)), '-format ') === false) { \$imvgn.=' -format " . str_replace('jpg','jpeg',$inextis) . " '; }
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromstring(\$inidis);
}

function ourimagecreatefromjpeg(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='jpg';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromjpeg(\$inidis);
}

function ourimagecreatefrompng(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='png';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefrompng(\$inidis);
}

function ourimagecreatefromgif(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='gif';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefromgif(\$inidis);
}

function ourimagecreatefrombmp(\$inidis) {
global \$imvgn, \$imafn, \$imqty;
if ((\$imvgn . \$imafn . \$imqty) != '') {
\$extis='bmp';
\$inextis=\$extis;
\$sofarout='/tmp/imtmp.' . \$extis;
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
unlink(\$imfilename);
}
file_put_contents(\$sofarout, \$inidis);
\$fzis=filesize(\$sofarout);
exec('" . $immogpath . "mogrify ' . \$imvgn . \$imafn . \$imqty . ' /tmp/imtmp.' . \$extis);
foreach (glob('/tmp/imtmp.*') as \$imfilename) {
if (filesize(\$imfilename) != \$fzis) { // strpos(\$imfilename, '.' . \$extis) === false) {
\$sofarout=\$imfilename;
\$extis=explode('.', \$imfilename)[1];
}
}
if (strtolower(\$extis) == 'png') {
return imagecreatefrompng('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'bmp') {
return imagecreatefrombmp('/tmp/imtmp.' . \$extis);
} else if (strtolower(\$extis) == 'gif') {
return imagecreatefromgif('/tmp/imtmp.' . \$extis);
} else if (strpos(strtolower(\$extis), 'jp') !== false) {
return imagecreatefromjpeg('/tmp/imtmp.' . \$extis);
}
}
return imagecreatefrombmp(\$inidis);
}

“; ?>

… and change existant calls into (equivalent inhouse) “our” function calls as above.


Previous relevant ImageMagick Vignette Primer Tutorial is shown below.

ImageMagick Vignette Primer Tutorial

ImageMagick Vignette Primer Tutorial

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 …
  • vignette 50% (“mogrify” uses switch -vignette 50 here)

… 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.

By the way, should you be interested in photo orientation off a mobile device camera, have a read of PHP Exif Image Information Revisit Tutorial.

Nala and Luna and Charlie bid you a fond farewell!


Previous relevant Gimp Vignette Primer Tutorial is shown below.

Gimp Vignette Primer Tutorial

Gimp Vignette Primer Tutorial

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:

  1. Open Gimp graphical editor application
  2. File->Open … pick your image file
  3. Layer->New Layer (we’ll call “Vg”) … pick Foreground Colour
  4. In the Layers dialog, click on your “Vg” layer to select it, and select Soft light from the “Mode” drop-down box
  5. 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”
  6. Below Opacity bar select Link icon next to Eye icon, which will already be showing
  7. 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
  8. Use your bucket tool (Shift+B) and click within the selection to fill it
  9. Deselect your selection with Select->None
  10. 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
  11. 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%
  12. 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 an image comparison link.

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.


Previous relevant Gimp Transparency Primer Tutorial is shown below.

Gimp Transparency Primer Tutorial

Gimp Transparency Primer Tutorial

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:

  1. Open Gimp graphical editor application
  2. File->Open Layers … pick your image file
  3. If Layers window not showing, make it show via Windows->Layers – Brushes
  4. Below Opacity bar select Link icon next to Eye icon, which will already be showing
  5. Change Opacity bar setting to a value of Transparency (100% is Opaque, 0% is Transparent) that suits … today we do 70%
  6. File->Export
  7. 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.


Previous relevant Gimp Layers Primer Tutorial is shown below.

Gimp Layers Primer Tutorial

Gimp Layers Primer Tutorial

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.

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

Editable Calendar PHP Tutorial

Editable Calendar PHP Tutorial

Editable Calendar PHP Tutorial

Up until yesterday’s Editable Calendar Collaboration Tutorial the roles for PHP were …

  • help bring non default yellow span element text over to any Google Translate language translation …
  • help reduce URL size for SMS links … otherwise …
  • if called under other conditions pass content through to the HTML webpage

… and today, just before “otherwise” above, we want to add …

Offer calendar timestamp equivalents that are not the Gregorian calendar, out of Julian, French, Jewish

… which PHP Calendar Functions can help with, via HTML iframe src attribute calling URLs like …


https://www.rjmprogramming.com.au/calendar_contenteditable.php?docals=y&monththe=8&yearthe=2026&datethe=1

involving, in the changed calendar_contenteditable.php helping PHP …


<?php
// calendar_contenteditable.php
// RJM Programming - September, 2026
// Help out calendar_contenteditable.html
error_reporting(E_ALL);

function extended_jdtofrench($juliandate) { // thanks to https://www.php.net/manual/en/function.jdtofrench.php
if ($juliandate > 2380945) {
// jdtofrench () only accepts dates until september 1806
$gregorian_date = jdtogregorian ($juliandate);
$temp = explode ('/', $gregorian_date);
$year = $temp[2];
$juliandate = gregoriantojd ($temp[0], $temp[1], 1805);
$republican_date = jdtofrench ($juliandate);
$republican_date = explode ('/', $republican_date);
$diff = $year - 1805;
$republican_date[2] = $republican_date[2] + $diff;
} else {
$republican_date = jdtofrench($juliandate);
}
return $republican_date[1] . '/' . (1 + $republican_date[0]) . '/' . $republican_date[2];
}

function mdytodmy($inv) {
$things=explode('/', $inv);
if (sizeof($things) == 3) {
return '' . $things[1] . '/' . (1 + $things[0]) . '/' . $things[2];
}
return $inv;
}

function mdytodmyj($inv) {
$things=explode('/', $inv);
if (sizeof($things) == 3) {
return '' . $things[1] . '/' . (0 + $things[0]) . '/' . $things[2];
}
return $inv;
}

if (isset($_GET['docals']) && isset($_GET['monththe']) && isset($_GET['yearthe'])) {
//data-gregorian='' data-julian='' data-jewish='' data-french=''
$onl='';
$jewishtsh='';
$frenchts='';
$gregdate='1';
$gregmonth='' . $_GET['monththe'];
$gregyear='' . $_GET['yearthe'];
if (isset($_GET['datethe'])) {
$gregdate='' . $_GET['datethe'];
}
$jdnum=gregoriantojd($gregmonth, $gregdate, $gregyear);
$gregoriants='' . $gregmonth . '/' . $gregdate . '/' . $gregyear;
$frenchts=extended_jdtofrench(($jdnum + 2));
$jewishts=jdtojewish(($jdnum + 1));
$juliants=jdtojulian($jdnum);
$jewishtsz=jdtojewish(($jdnum + 1), true);
$jewishtsh=iconv('WINDOWS-1255', 'UTF-8', $jewishtsz);
$onl.=" var ispans=0, spans=parent.document.getElementsByTagName('span'); ";
$onl.=" parent.document.getElementById('calcap').setAttribute('data-gregorian', '" . mdytodmy($gregoriants) . "'); spans[1].setAttribute('data-gregorian', '" . mdytodmy($gregoriants) . "'); ";
$onl.=" parent.document.getElementById('calcap').setAttribute('data-julian', '" . mdytodmy($juliants) . "'); spans[1].setAttribute('data-julian', '" . mdytodmy($juliants) . "'); ";
$onl.=" parent.document.getElementById('calcap').setAttribute('data-french', '" . $frenchts . "'); spans[1].setAttribute('data-french', '" . $frenchts . "'); ";
$onl.=" parent.document.getElementById('calcap').setAttribute('data-jewish', '" . mdytodmyj($jewishts) . "'); spans[1].setAttribute('data-jewish', '" . mdytodmyj($jewishts) . "'); ";
$onl.=" parent.document.getElementById('calcap').setAttribute('data-jewishy', '" . $jewishtsh . "'); spans[1].setAttribute('data-jewishy', '" . $jewishtsh . "'); ";


if (5 == 5) {
for ($ioff=1; $ioff<=31; $ioff++) {
$jdnum++;
$gregoriants='' . $gregmonth . '/' . (1 + $ioff) . '/' . $gregyear;
$frenchts=extended_jdtofrench(($jdnum + 2));
$jewishts=jdtojewish(($jdnum + 1));
$juliants=jdtojulian($jdnum);
$jewishtsz=jdtojewish(($jdnum + 1), true);
$jewishtsh=iconv('WINDOWS-1255', 'UTF-8', $jewishtsz);
$onl.=" if (eval('' + spans.length) > " . ($ioff + 1) . ") { ";
$onl.=" spans[" . ($ioff + 1) . "].setAttribute('data-gregorian', '" . mdytodmy($gregoriants) . "'); ";
$onl.=" spans[" . ($ioff + 1) . "].setAttribute('data-julian', '" . mdytodmy($juliants) . "'); ";
$onl.=" spans[" . ($ioff + 1) . "].setAttribute('data-french', '" . $frenchts . "'); ";
$onl.=" spans[" . ($ioff + 1) . "].setAttribute('data-jewish', '" . mdytodmyj($jewishts) . "'); ";
$onl.=" spans[" . ($ioff + 1) . "].setAttribute('data-jewishy', '" . $jewishtsh . "'); ";
$onl.=" } ";
}
}

echo "<html><head><meta charset='UTF-8'/></head><body onload=\"" . $onl . "\"></body></html>";
exit;
} else
if (isset($_POST['tihis'])) {
if (isset($_POST['ticpc'])) {
if (strpos(str_replace('+',' ',urldecode($_POST['ticpc'])) . '~$#', '.cpc~$#') !== false && strpos('~$#' . str_replace('+',' ',urldecode($_POST['ticpc'])), '~$#tmp' . DIRECTORY_SEPARATOR) !== false) {
//file_put_contents('zx.zx9','' . str_replace('+',' ',urldecode($_POST['ticpc'])));
file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . str_replace('+',' ',urldecode($_POST['ticpc'])), str_replace('+',' ',urldecode($_POST['tihis'])));
}
} else {
file_put_contents('tihis.txt', str_replace('+',' ',urldecode($_POST['tihis'])));
}
exit;
} else if (isset($_GET['tihis'])) {
if (strpos(str_replace('+',' ',urldecode($_GET['tihis'])) . '~$#', '.cpc~$#') !== false && strpos('~$#' . str_replace('+',' ',urldecode($_GET['tihis'])), '~$#tmp' . DIRECTORY_SEPARATOR) !== false) {
if (file_exists(str_replace('+',' ',urldecode($_GET['tihis'])))) {
$newih=file_get_contents(str_replace('+',' ',urldecode($_GET['tihis'])));
if (trim($newih) == $newih) {
file_put_contents(str_replace('+',' ',urldecode($_GET['tihis'])), file_get_contents(str_replace('+',' ',urldecode($_GET['tihis']))) . ' ' );
} else if (3 == 4) {
unlink(str_replace('+',' ',urldecode($_GET['tihis'])));
}
if (strpos($newih, '<cap' . 'tion ') !== false) {
if (str_replace('?','',explode('tihis=', $_SERVER['QUERY_STRING'])[0]) != '') {
$usualis=file_get_contents('http://www.rjmprogramming.com.au/calendar_contenteditable.html?' . str_replace('?','',explode('tihis=', $_SERVER['QUERY_STRING'])[0]));
} else if (strpos($newih, '<cap' . 'tion data-durl="') !== false) {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . basename(explode('"',explode('<cap' . 'tion data-durl="', $newih)[1])[0]));
} else {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'calendar_contenteditable.html');
}
$bittochange='<cap' . 'tion ' . explode('</table>', explode('<cap' . 'tion ', $usualis)[-1 + sizeof(explode('<cap' . 'tion ', $usualis))])[0];
echo str_replace($bittochange, $newih, $usualis);
exit;
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
} else if (file_exists('tihis.txt')) {
$newih=file_get_contents('tihis.txt');
unlink('tihis.txt');
if (strpos($newih, '<cap' . 'tion ') !== false) {
if (strpos($newih, '<cap' . 'tion data-durl="') !== false) {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . basename(explode('"',explode('<cap' . 'tion data-durl="', $newih)[1])[0]));
} else {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'calendar_contenteditable.html');
}
$bittochange='<cap' . 'tion ' . explode('</table>', explode('<cap' . 'tion ', $usualis)[-1 + sizeof(explode('<cap' . 'tion ', $usualis))])[0];
echo str_replace($bittochange, $newih, $usualis);
exit;
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
?>

… 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 + ' ';
}
}
}

… by the changed tenth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Collaboration Tutorial is shown below.

Editable Calendar Collaboration Tutorial

Editable Calendar Collaboration Tutorial

Yesterday’s Editable Calendar Cross Domain Sharing Tutorial had us …

  • 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 …


function doemail(inidea) {
var newurl=document.URL.split('//')[0] + '//' + 'www.rjmprogramming.com.au/calendar_contenteditable.html' + '?_x_tr_tl=' + document.getElementById('language').value + '&month=' + themonth + '&theyear=' + theyear + '#' + encodeURIComponent('<h' + '1' + (('' + document.body.innerHTML).split(('<h' + '1'))[1]).split(' id="lastdiv"></div>')[0] + ' id="lastdiv"></div>');
var zfrom='youllneverfindthis', zto='youllneverfindthis';
var azx=top.document.getElementById('xae' + 'mail');
if (!azx) { azx=top.document.createElement("a"); }
top.document.body.appendChild(azx);
azx.style = "display: none";
azx.target = "_top";
azx.id = 'xae' + 'mail';
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h1')[0].innerHTML.split('<select')[0].split('</font>')[0].split('>')[eval(-1 + document.getElementsByTagName('h1')[0].innerHTML.split('<select')[0].split('</font>')[0].split('>').length)].replace(/\ \;/g,' ')) + '%20' + encodeURIComponent(document.getElementsByTagName('h1')[0].innerHTML.split('</select>')[1].split('</font>')[0].split('>')[eval(-1 + document.getElementsByTagName('h1')[0].innerHTML.split('</select>')[1].split('</font>')[0].split('>').length)].split('<')[0].replace(/\ \;/g,' ') + ' ... ') + '%20' + encodeURIComponent(document.getElementById('calcap').innerText) + '&body=' + encodeURIComponent(newurl); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
return '';
}

function dosms() {
var newurl=document.URL.split('//')[0] + '//' + 'www.rjmprogramming.com.au/calendar_contenteditable.html' + '?_x_tr_tl=' + document.getElementById('language').value + '&month=' + themonth + '&theyear=' + theyear + '#' + encodeURIComponent('<h' + '1' + (('' + document.body.innerHTML).split(('<h' + '1'))[1]).split(' id="lastdiv"></div>')[0] + ' id="lastdiv"></div>');
if (1 == 1) {
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('findeditablecalendaremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('findeditablecalendaremailee');
}
window.localStorage.setItem('findeditablecalendaremailee', 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() == '') {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('findeditablecalendarsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('findeditablecalendarsmsno');
}
window.localStorage.setItem('findeditablecalendarsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
top.document.body.appendChild(azx);
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(newurl.replace('#','&').replace('?','#')); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
}
return '';
}

… we’ll see). Calendars can give that date and time context to collaboration between users, and so, we invite you to try this with the changed ninth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Cross Domain Sharing Tutorial is shown below.

Editable Calendar Cross Domain Sharing Tutorial

Editable Calendar Cross Domain Sharing Tutorial

Unless you are new to the party you’ll know that with yesterday’s Editable Calendar Styling Tutorial‘s Editable Calendar project …


Google Translate

… 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 …

  1. Google Translate‘s domain “child” window … and …
  2. RJM Programming‘s domain “parent” window

… and so the communications between parent and child (regarding any Saved calendar data a user may want to apply and remember), in this scenario, are restricted, but asking Under what conditions can window.opener.parentfunc() type of call work when the domains are different? reminded us of the talents of window.postMessage to help here, thanks. It got us to …

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;

if (('' + event.data).indexOf(encodeURIComponent('<h' + '1')) != -1) {
if (document.getElementById('language').outerHTML.indexOf(' data-lcode="') != -1 && document.getElementById('language').outerHTML.indexOf(' data-lcode=""') == -1) {
thislang=document.getElementById('language').getAttribute('data-lcode');
}
document.body.innerHTML='<h' + '1' + decodeURIComponent(('' + event.data).split(encodeURIComponent('<h' + '1'))[1]).split(' id="lastdiv"></div>')[0] + ' id="lastdiv"></div>';
if (thislang.trim() != '') { document.getElementById('language').value=thislang; document.getElementById('language').setAttribute('data-lcode', thislang); }
inplay=true;
lastinplayc=document.body.innerHTML + ' ';
fromfor=true;
setTimeout(lsit, 2000);
}
});
Be broadcasting at the Google Translate domain document.URL scenario “child” window …

if (window.opener) {
if (document.URL.indexOf('&iam=') != -1) {
window.opener.postMessage(encodeURIComponent(document.body.innerHTML), decodeURIComponent(document.URL.split('&iam=')[1].split('&')[0].split('#')[0]));
} else {
window.opener.postMessage(encodeURIComponent(document.body.innerHTML), 'https://rjmprogramming.com.au');
}
}

… for our Editable Calendar project.

Also, today, we introduce Month, Year “breadcrumb” caption element …

CSS caption element SVG background image based arrows … thanks to this great advice

#calcap { // thanks to https://www.google.com/search?q=background+arrow+background+svg+images+at+left+and+right+of+an+element&rlz=1C5OZZY_en&oq=background+arrow+background+svg+images+at+left+and+right+of+an+element&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCTM1ODAyajBqN6gCALACAA&sourceid=chrome&source=chrome.ob&ie=UTF-8
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); background-color: rgb(240,240,240); border-radius: 8px;
background-image:
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>'),
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>');
background-position: left center, right center;
background-repeat: no-repeat;
}
Javascript caption element ondblclick event logic (based on cursor positioning)

function incit(e) {
var x=-999, y=-999;
var rectis=e.target.getBoundingClientRect();
if (e.touches) {
if (e.touches[0].pageX) {
x = e.touches[0].pageX;
y = e.touches[0].pageY;
} else {
x = e.touches[0].clientX;
y = e.touches[0].clientY;
}
} else if (e.clientX || e.clientY) {
x = e.clientX;
y = e.clientY;
} else {
x = e.pageX;
x = e.pageY;
}
if (x >= 0 && y >= 0 && document.getElementById('calcap').getAttribute('data-thism') != '') {
var curm=eval('' + document.getElementById('calcap').getAttribute('data-thism'));
var cury=eval('' + document.getElementById('calcap').getAttribute('data-thisy'));
if (x <= eval(eval('' + rectis.left) + eval(eval('' + rectis.width) / 2))) {
themonth=eval(-1 + curm);
if (themonth < 0) {
themonth=11;
theyear=eval(-1 + cury);
}
e.target.innerHTML='' + mths[themonth] + ', ' + theyear;
calcapv='';
newmy(e.target);
} else {
themonth=eval(1 + curm);
if (themonth > 11) {
themonth=0;
theyear=eval(1 + cury);
}
e.target.innerHTML='' + mths[themonth] + ', ' + theyear;
calcapv='';
newmy(e.target);
}
}
}

… in the changed eighth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Styling Tutorial is shown below.

Editable Calendar Styling Tutorial

Editable Calendar Styling Tutorial

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 …


<style>
td {
height: 15%;
vertical-align: top;
padding: 2 2 2 2;
}

td {
border-radius: 8px;
background-color: rgb(252,252,252);
}

.pad {
border-color: transparent;
background-color: transparent;
}

td span {
background: linear-gradient(to bottom, rgba(170,170,170,0.3) 30%, yellow 30%);
font-weight: bold;
}

#calcap {
font-weight: bold;
text-shadow: -1px 1px 1px #952dff;
}

#bsave {
background-color: orange;
}

#calcap {
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); background-color: rgb(240,240,240); border-radius: 8px;
}

th {
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); color: blue; background-color: rgb(240,240,240); border-radius: 8px;
}

button {
border-radius: 8px;
}

select {
border-radius: 8px;
}

table {
border-radius: 6px;
background-color: transparent;
}

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.

Yet again, feel free to try this changed, better styled seventh draft Editable Calendar you can try below.


Previous relevant Editable Calendar Save and Recall Tutorial is shown below.

Editable Calendar Save and Recall Tutorial

Editable Calendar Save and Recall Tutorial

Regarding the recent Editable Calendar Get Arguments Tutorial it set up …

  • Month, Year as a dynamic user editable concept … worthy of …
  • coding to allow for the Saving and Recall of user calendar data according to their Month, Year value …

… in window.localStorage data repository particular to the web browser and device they are using.

To deploy this, we’ve decided on creating a scenario with …

  • a new dropdown (ie. select) element containing Month, Year Saved value choices … nested …

    <button id=bsave translate=yes onclick=lsit(); style=display:none;>Save <select onchange=recallsm(this); id=savedd style=display:none;><option translate=yes value=''>Optionally recall a saved Editable Calendar month ...</option></select></button>

    …within …
  • a new button element that a user clicks/taps to Save the current calendar data regarding the displayed Month, Year into window.localStorage data repository …

    function recallsm(osel) {
    if (osel.value.trim() != '') {
    if (osel.value.substring(0,1) == '-') {
    oselvalue=osel.value;
    setTimeout(function(){
    try {
    window.localStorage.removeItem('editable_calendar_' + oselvalue.substring(1).replace(',','_').replace('.','_'));
    } catch(jghfjhg) { }
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }, 2000);
    } else {
    themonth=eval(-1 + eval('' + osel.value.replace(',','.').split('.')[0]));
    theyear=eval('' + eval('' + osel.value.replace(',','.').split('.')[1]));
    usuff='&year=' + theyear + '&month=' + themonth;
    now = new Date(eval('' + theyear), eval('' + themonth), 1);
    document.body.innerHTML=decodeURIComponent(('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)));
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }
    }
    }

    function lscheck() {
    if (origdd == '') { origdd=document.getElementById('savedd').innerHTML; }
    Object.keys(window.localStorage).forEach(function(key){
    if (('' + key).indexOf('editable_calendar_') == 0) {
    //alert(('' + key));
    //alert(('' + key).split('editable_calendar_')[1]);
    //alert(document.getElementById('savedd').innerHTML);
    if (document.getElementById('savedd').innerHTML.indexOf('>' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>') == -1) {
    //alert('2:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').innerHTML+='<option value="' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    document.getElementById('savedd').innerHTML+='<option value="-' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">Forget ' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    //alert('3:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').style.display='inline-block';
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    });
    }

    function lsit() {
    if (inplay && lastinplayc != '') {
    var curval=('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)).replace(/^undefined/g,'').replace(/^null/g,'');
    if (curval != '') {
    if (decodeURIComponent(curval) != lastinplayc) {
    window.localStorage.removeItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear);
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    }
    } else {
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    }
    }
    }

    function inpcheck() {
    if (inplay) {
    if (document.body.innerHTML != lastinplayc) {
    lastinplayc=document.body.innerHTML.trim();
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    }

… in the changed sixth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Get Arguments Tutorial is shown below.

Editable Calendar Get Arguments Tutorial

Editable Calendar Get Arguments Tutorial

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);
}

Yes, the first cab off the rank, tipping our toes into thoughts using this, building on yesterday’s Editable Calendar English User Entry Translation Tutorial, is to …

  • take what used to be a pretty static …

    Month, Year

    … HTML caption element … and …

  • apply the global attribute …

    contenteditable=true

    … 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='';

    function ChangeLcodeNow(){
    var val = document.getElementById('language'), dd=null, pprefix='';
    docURL=documentURL.split('rjmprogramming.com.au/')[eval(-1 + documentURL.split('rjmprogramming.com.au/').length)];
    if (docURL.indexOf('.translate.goog/') != -1) { docURL=docURL.split('.translate.goog/')[1]; }
    if (changesmade) { docURL=docURL.replace('.html','.php').replace('.htm','.php'); ajaxit(); }
    if (documentURL.indexOf('&_x_tr_tl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_blank', 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    documentURL=documentURL.replace('&_x_tr_tl=', '&xtrtl=');
    } else if (documentURL.indexOf('&xtrtl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_top');
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    }
    }

    … and through to our own RJM Programming code

… the one in today’s tutorial picture featuring …


https://www-rjmprogramming-com-au.translate.goog/calendar_contenteditable?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=zh-CN&year=2026&month=1

Again, feel free to try this in the changed fifth draft Editable Calendar you can try below, helped out by a tweaked calendar_contenteditable.php helping PHP newish player.


Previous relevant Editable Calendar English User Entry Translation Tutorial is shown below.

Editable Calendar English User Entry Translation Tutorial

Editable Calendar English User Entry Translation Tutorial

For today’s work, we were thinking of using window.sessionStorage logic, but because of the advice of Can a window.open Google Translate popup window access the sessionStorage written by the originating window from another domain? warning us off, that what we regard as an “ever so cute” solution is not a goer, alas! So, what else is going to help regarding the dilemma …

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 …
<?php

if (file_exists('tihis.txt')) {
$newih=file_get_contents('tihis.txt');
unlink('tihis.txt');
if (strpos($newih, '<cap' . 'tion ') !== false) {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'calendar_contenteditable.html');
$bittochange='<cap' . 'tion ' . explode('</table>', explode('<cap' . 'tion ', $usualis)[-1 + sizeof(explode('<cap' . 'tion ', $usualis))])[0];
echo str_replace($bittochange, $newih, $usualis);
exit;
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
}

?>
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 ''; }

Feel free to try this in the changed fourth draft Editable Calendar you can try below, helped out by a new calendar_contenteditable.php helping PHP new player.


Previous relevant Editable Calendar Intl Tutorial is shown below.

Editable Calendar Intl Tutorial

Editable Calendar Intl Tutorial

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 …
  • JavaScript Intl Object Reference ideas

… that can help with language translations, and we make available to the user via a toggling “-” (to “+”) HTML element modus operandi.

Especially, as we need more work improving the Google Translate interfacing, trying this changed third draft Editable Calendar you can try below, is definitely worth considering.


Previous relevant Editable Calendar Google Translate Tutorial is shown below.

Editable Calendar Google Translate Tutorial

Editable Calendar Google Translate Tutorial

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 …


Google Translate

… 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 changed second draft Editable 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.


Previous relevant Editable Calendar Primer Tutorial is shown below.

Editable Calendar Primer Tutorial

Editable Calendar Primer Tutorial

Today’s new Editable Calendar themed inhouse web application combines …

Today’s steps of progress in this project …

  1. establish the current month and year’s “month showing” calendar into a webpage … and …
  2. within that calendar date cell we place a span element with contenteditable=true … allowing …
  3. user to add content into the calendar

… in this first draft “proof of new to us concept” Editable Calendar you can try below. We hope you tune in tomorrow for more progress!


Previous relevant Event Calendar Collaboration Textarea Highlight Linking Tutorial is shown below.

Event Calendar Collaboration Textarea Highlight Linking Tutorial

Event Calendar Collaboration Textarea Highlight Linking Tutorial

For the first time we can remember, with our Events in Month web application of yesterday’s Event Calendar Collaboration Remembering Recipient Tutorial

  • we’re channelling how in emails “word strings” starting with “http” become links … and so …
  • in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available

Here is the relevant Javascript we ended up with, for this …


var thesel='', showsel=false, lastsel='';

function showthesel() {
var uwords=[], iuw=1, ils=0;
if (showsel) {
showsel=false;
if (lastsel != thesel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
lastsel=thesel;
thesel='';
}
if (lastsel != '' && thesel == '') {
uwords=lastsel.toLowerCase().split('http');
ils=eval(4 + eval('' + uwords[0].length));
for (iuw=1; iuw<uwords.length; iuw++) {
window.open('//' + lastsel.substring(ils).split('//')[1].split(' ')[0].split(String.fromCharCode(10))[0].split(String.fromCharCode(13))[0], '_blank', 'top=' + eval(10 * iuw + 50) + ',left=' + eval(10 * iuw + 50) + ',width=600,height=600');
ils+=eval(4 + eval('' + uwords[iuw].length));
}
}
}
}

function handleSelection() { // thanks to https://stackoverflow.com/questions/46651479/reacting-to-selection-changes-in-an-html-textarea
const activeElement = document.activeElement;

// Make sure this is your textarea
if (activeElement && activeElement.outerHTML.indexOf('<textarea') == 0) {
const range = {
start: activeElement.selectionStart,
end: activeElement.selectionEnd
};
// Do something with your range
if (eval('' + range.end) > eval('' + range.start)) {
thesel=(activeElement.value.substring(range.start).substring(0, eval(eval('' + range.end) - eval('' + range.start) )));
if (!showsel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
showsel=true;
setTimeout(showthesel, 5000);
}
}
}
}

document.addEventListener('selectionchange', handleSelection);

Also, today, we have a few CSS tweaks in the changed events_in_month.htm parent of the Events in Month web application. Feel free to try it for yourself.

Stop Press

Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!


Previous relevant Event Calendar Collaboration Remembering Recipient Tutorial is shown below.

Event Calendar Collaboration Remembering Recipient Tutorial

Event Calendar Collaboration Remembering Recipient Tutorial

When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …

  • When it comes to procedures …
  • Aaaaaarrrrgggghhhh
  • Nuances count … regarding …
  • Errrrrrr
  • Saving time …
  • Saving frustration

… resulting in the “easy to remember” acronym WANESS … we rest our cases!

What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.

Global variables initialization …

var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …

if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …

function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}

function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic

function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}

//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}

return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}

var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}

Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changed events_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!


Previous relevant Event Calendar Collaboration Tutorial is shown below.

Event Calendar Collaboration Tutorial

Event Calendar Collaboration Tutorial

The Event Calendar web application project, of Event Calendar PHP Bookmark Tutorial, from last year is worth a revisit, the reason being …

  • it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
  • each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
  • hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs

… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!

Timing became really important with this integration of …

  • email
  • SMS

… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …

  1. Month
  2. Year

… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …

Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons

<h1>Events in Month &nbsp;&nbsp;<button style=display:none; title='Share via Email' id=semail onclick="doemail('');">&#128231;</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>&#128223;</button> </h1>
New static HTML div element placed to the bottom of the body element …

<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …

var bigbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1].split('&')[0]) : '';
if (bigbih != '' && bigbih.indexOf('xae' + 'mail') == -1) {
bigbih+='<a target=_blank id=xae' + 'mail style=display:none; href="mailto:"></a>';
}
if (bigbih != '' && bigbih.indexOf('xas' + 'ms') == -1) {
bigbih+='<a target=_blank id=xas' + 'ms style=display:none; href="sms:"></a>';
}
var viatext='', smallbih='', wes=null, sparewes=null;
On or around document.body onload event we can analyze any such location.hash hashtagging data …

if (bigbih.indexOf('</he' + 'ad>') != -1 || bigbih.indexOf('<b' + 'o' + 'dy') != -1 || bigbih.indexOf('<h' + '1') == 0) {
//bigbih=bigbih.replace('" id="eventcalendar"', 'margin-top:50px;" id="eventcalendar"');
viatext=' (via contact at ' + ('' + new Date()) + ')';
if (bigbih.indexOf('<h' + '1') == 0) {
smallbih=bigbih;
} else {
smallbih=bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].replace(bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].split('>')[0] + '>', '');
}
wes=windowopen('./events_in_month.htm','_blank','top=40,left=40,width=800,height=800');
//alert(bigbih);
if (wes) {
setTimeout(function(){
wes.document.write(bigbih);
}, 3000);
//document.body.innerHTML=bigbih;
setTimeout(function(){
wes.document.getElementById('semail').style.display='inline-block';
wes.document.getElementById('ssms').style.display='inline-block';
wes.document.getElementsByTagName('h1')[0].style.display='none';
wes.document.getElementsByTagName('h3')[0].style.display='none';
//wes.setviatext(viatext);
//wes.setphpthere(' ');
//wes.togglewords();
}, 5000);
}
}
A window.open wrapper windowopen Javascript function …

function windowopen(poneis, ptwois, pthreeis) {
document.getElementById('divas').innerHTML='<iframe name=ovif id=ovif style="position:absolute;top:140px;left:0px;width:100%;height:100%;z-index:99;" src="' + poneis + '" onload=wopencheck(this);></iframe>';
//document.getElementsByTagName('h1')[0].style.opacity='0.0';
//document.getElementsByTagName('h3')[0].style.opacity='0.0';
sparewes=window.open(poneis.split('.htm')[0] + '.php', 'ovif');
//sparewes=window.open(poneis, 'ovif');
sparewes.document.write(bigbih);
setTimeout(function(){
sparewes.document.getElementById('semail').style.display='inline-block';
sparewes.document.getElementById('ssms').style.display='inline-block';
var sdem=sparewes.document.getElementById('semail').outerHTML;
var sdss=sparewes.document.getElementById('ssms').outerHTML;
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdss,'');
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdem,'');
sparewes.document.getElementsByTagName('h1')[0].style.display='none';
sparewes.document.getElementsByTagName('h3')[0].style.display='none';
sparewes.document.getElementById('dmore').innerHTML+=(' ' + sdem.replace('doeJUNKmail(', 'parent.doe' + 'mail(') + ' ' + sdss.replace('dosJUNKms(', 'parent.dos' + 'ms(') + '<br>');
//sparewes.setviatext(viatext);
//sparewes.setphpthere(' ');
//sparewes.togglewords();
}, 5000);
return null;
}
Is augmented by other new Javascript functions …

function hfanalyze() {
var fio=document.getElementById('ecform');
if (forceit) { forceit=false; return true; }
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
//alert(fions.length);
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value.replace(/\?\>/g,'? >'));
delm='&';
}
//alert(testurl.length);
//if (eval('' + testurl.length) < 750) { return true; }
//if (lastbut.value.indexOf('New') == 0 || 11 == 11) {
if (22 == 22) {
//wo=window.open('', '_blank', 'top=50,left=50,height=600,width=600');
//wo.document.write('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
return ('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
} else if (phpthere != '') {
if (1 == 1) {
postit(fio, false, testurl);
} else {
fio.target='phpif';
fio.method='POST';
fio.action='./events_in_month.php';
if (!document.getElementById('dmore')) {
document.getElementById('ddmore').innerHTML='<input type=hidden id=caltitle name=caltitle value="' + document.getElementById('myoldsel').options[document.getElementById('myoldsel').selectedIndex].innerText + '"></input><input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
} else {
document.getElementById('dmore').innerHTML='<input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
}
}
return 'true';
}
return 'false';
}

function doemail(inidea) {
var azx=document.getElementById('xae' + 'mail');
if (!azx) { azx=document.createElement("a"); }
//if (1 == 1) {
//document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
//}
azx.style = "display: none";
azx.id = 'xaem' + 'ail';
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h2')[0].innerHTML.split('&')[0].split('<')[0].replace(/\`/g,'').replace(/^Events\ in\ /g, 'Events in Month ').substring(0)) + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze())); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
return '';
}

function dosms() {
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead.', snum)).replace(/^null/g,'');
} else {
snum=('' + prompt('Please enter SMS number to send to.', snum)).replace(/^null/g,'');
}
if (snum.indexOf('@') != -1) {
return doemail(snum);
} else if (snum.trim() != '') {
alert('Snum=' + snum + '?');
var azx=document.getElementById('xas' + 'ms');
if (azx == null) { azx=document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()));
azx.click();
}
return '';
}

function wopencheck(iois) {
var aconto = null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
aconto.body.innerHTML=smallbih;
aconto.setviatext(viatext);
}
}
}

function setviatext(towhat) {
viatext=towhat;
}

… to help bed down this new sharing and collaboration functionality in a changed events_in_month.htm parent of the Events in Month web application.


Previous relevant Event Calendar PHP Bookmark Tutorial is shown below.

Event Calendar PHP Bookmark Tutorial

Event Calendar PHP Bookmark Tutorial

Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to


Record to Remember via Bookmark

… use the web browser Bookmarks to help you recall an Events in Month report with long entries.

Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …


https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1

… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!

To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …


var documentURL=document.URL;

… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …


#August__2023_00000__1

… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).

Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).

Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …


<style>
#eventcalendar {
background-color: #fcfcfc;
}

td {
padding-top: 2px;
padding-left: 2px;
padding-right: 2px;
padding-bottom: 12px;
}

.dayb {
color: white;
background-color: red;
padding: 5 5 5 5;
border-radius: 80px;
margin-bottom: 15px;
}

.dow {
color: purple;
font-style: bold;
}

.selday {
margin-left: 8px;
background-color: rgba(255,0,0,0.7);
display: inline-block;
width: 50px;
border-color: transparent;
text-overflow: ellipsis;
}

.tablurb {
background: linear-gradient(to right, white, lightpink, pink);
}

input[type="submit"] {
margin-bottom: 3px;
border-radius: 180px;
}

input[type="number"] {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f3f3f3;
padding: 2 2 2 2;
}

#smonth {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f9f9f9;
padding: 2 2 2 2;
}

.boldtitle {
background-color: rgba(0, 211, 107, 0.2);
}

.boldtitle + .tablurb {
margin-top: 8px;
}


.selhistory {
border-radius: 180px;
background-color: lightpink;
padding: 2 2 2 2;
}
</style>

… and we thank this webpage for the heads up regarding how to calculate week numbers within a year data item displays now available in …


Previous relevant Event Calendar PHP Tutorial is shown below.

Event Calendar PHP Tutorial

Event Calendar PHP Tutorial

Let’s face it. Serverside PHP is just great! It opens up so many opportunities regarding data in your web applications.

As such, onto yesterday’s Event Calendar New Window Tutorial logic we now have a …


Record to Remember

… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.

The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …


<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm

$hcont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.htm');
$cont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php');
$ipad=server_remote_addr();
$ipadless=$ipad;
$js='';

function server_remote_addr() {
global $ipadless;
$rma = $_SERVER['REMOTE_ADDR'];
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
// you can add different browsers with the same way ..
$ipadless=str_replace(".", "_", str_replace(":", "_", $rma));
if(preg_match('/(chromium)[ \/]([\w.]+)/', $ua))
$rma = '000000'.$rma;
elseif(preg_match('/(chrome)[ \/]([\w.]+)/', $ua))
$rma = '00000'.$rma;
elseif(preg_match('/(safari)[ \/]([\w.]+)/', $ua))
$rma = '0000'.$rma;
elseif(preg_match('/(opera)[ \/]([\w.]+)/', $ua))
$rma = '000'.$rma;
elseif(preg_match('/(msie)[ \/]([\w.]+)/', $ua))
$rma = '00'.$rma;
elseif(preg_match('/(mozilla)[ \/]([\w.]+)/', $ua))
$rma = '0'.$rma;
return str_replace(".", "_", str_replace(":", "_", $rma));
}

$itdone=false;
if (isset($_GET['init'])) {
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
echo "<html><head><script type='text/javascript'> " . $js . " </script></head><body><p>" . $ipad . "</p></body></html>";
} else if (isset($_POST['phpthere']) && isset($_POST['bigurl']) && isset($_POST['caltitle'])) {
//file_put_contents('xz.xz', 'l');
if (strpos($cont, '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n") === false) {
//file_put_contents('xz.xzz', 'l');
$cont=str_replace('?' . '>', '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n" . '?' . '>', $cont);
file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php', $cont);
}
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
//file_put_contents('xz.xzzz', "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>");
echo "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>";
} else {
echo $hcont;
}
exit;

//
//
// January__1970_00000__1=http://localhost:8888/events_in_month.htm?caltitle=January%2C%201970&i01.00=&ta01.00=kgjfjhf%20jhgfjhf%20jhkgkjhg%20jhgkjhg%20jkghhg%20jkhgkjhg%20kjhgkjhg&i02.00=&ta02.00=kjhgkjh%20kjhgkjhg%20jkhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg%20kjgkjhg&i03.00=&ta03.00=kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg&i04.00=&ta04.00=&i05.00=&ta05.00=&i06.00=&ta06.00=&i07.00=&ta07.00=&i08.00=&ta08.00=&i09.00=&ta09.00=&i10.00=&ta10.00=&i11.00=&ta11.00=&i12.00=&ta12.00=&i13.00=&ta13.00=&i14.00=&ta14.00=&i15.00=&ta15.00=&i16.00=&ta16.00=&i17.00=&ta17.00=&i18.00=&ta18.00=&i19.00=&ta19.00=&i20.00=&ta20.00=&i21.00=&ta21.00=&i22.00=&ta22.00=&i23.00=&ta23.00=&i24.00=&ta24.00=&i25.00=&ta25.00=&i26.00=&ta26.00=&i27.00=&ta27.00=&i28.00=&ta28.00=&i29.00=&ta29.00=&i30.00=&ta30.00=&i31.00=&ta31.00=
?>

Yes, the parent needed to change for our events_in_month.htm parent of Events in Month web application role.


Previous relevant Event Calendar New Window Tutorial is shown below.

Event Calendar New Window Tutorial

Event Calendar New Window Tutorial

Onto yesterday’s Event Calendar Remembered Tutorial‘s “Mystery Dilemma”

But, there’s an inherent weakness with the design, we’ll go into more into the future.

… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.

Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.

Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …

  1. bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
  2. pick Inspect option …
  3. be in Elements tab of your Web Inspector … and …
  4. highlight top <html> tag …
  5. Context Menu with a right click or two finger gesture …
  6. pick Copy -> Copy outerHTML … so that …
  7. your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
  8. Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)

The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …


function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}

… the form submit buttons are reworded accordingly.

Another idea from this blog thread’s inspiration …

… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.

And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …

  • text shadow means by which the text of Event Calendar can be more impactive

    <div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
  • dropdown element conditional styling

    <style>
    .dayb {
    color: white;
    background-color: red;
    padding: 5 5 5 5;
    }


    .dow {
    color: purple;
    font-style: bold;
    }

    .selday {
    margin-left: 8px;
    background-color: rgba(255,0,0,0.7);
    display: inline-block;
    width: 50px;
    }
    </style>



    if (thislabel.substring(0,1) == 'i') {
    if (thisval.trim() != '') {
    document.getElementById(thislabel.replace('i', 'sel')).style.color='white';
    document.getElementById(thislabel.replace('i', 'sel')).style.backgroundColor='red';

    }
    document.getElementById(thislabel.replace('i', 'opt')).innerText=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + '); //.replace(/\+$/g, ' ');
    document.getElementById(thislabel).value=thisval.replace(/\+$/g, ' ');
    } else {
    document.getElementById(thislabel).value=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + ');
    }
    }
  • placeholder on Blurb conditional existence

    var ourdata='';
    // ...
    if (documentURL.indexOf('?') != -1) {
    if (documentURL.indexOf('?caltitle=') != -1) { ourdata='data-'; }
    // ...
    trtemplate='<tr id=tr01.00><td style=width:22%;><span id=sone01.00 class=dow>' + dotw[adate.getDay()].toUpperCase().substring(0,3) + '</span><br><br><span id=stwo01.00 class=dayb>1<span onblur=sepit(this); contenteditable=true id=sp01.00></span><input type=hidden id=i01.00 name=i01.00 value=""></input><select data-dow=' + dotw[adate.getDay()].toUpperCase().substring(0,3) + ' class=selday onchange="selit(this);" id=sel01.00><option id=opt01.00 value=""></option><option title="All such in month (ie. weekly)" value="...">...</option><option title="And ..." value="&...">&</option><option value=Bold>Bold Title</option><option value=Clone>Clone</option></select></span></td><td class=blurb title="What is on?" id=tb01.00><span title="Event title" style="font-style:bold;color:blue;" id=bd01.00></span><textarea name=ta01.00 id=ta01.00 style="width:100%;height=100%;" ' + ourdata + 'placeholder="Blurb ..." class=tablurb></textarea></td></tr>';
    // ...
    }
    // ...
    }

… for our “second draft” events_in_month.htm Events in Month web application.


Previous relevant Event Calendar Remembered Tutorial is shown below.

Event Calendar Remembered Tutorial

Event Calendar Remembered Tutorial

We were inspired by an Event Calendar pamplette we saw the other day …

… to write a new “proof of concept” Events in Month web application, whose content can be recalled via the web browser’s Bookmark methodologies.

We liked the ideas for day of week and/or date of month grouping arrangements we included, being, the way we interpreted it …

  • just on this day in this month … default
  • on this day of the week throughout the month in question … “
  • on this day and some others in that month in question … “&” … to start with and further amendments available via contenteditable=true span element
  • “Clone” value allows for multiple separated “blurbs” for the one signature day

But, there’s an inherent weakness with the design, we’ll go into more into the future. For now, you can try it yourself below …

Stop Press

This is where we get to for a “second draft” we’ll get into, further, tomorrow …

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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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

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

PHP macOS say Supervising Google Translate Sharing Tutorial

PHP macOS say Supervising Google Translate Sharing Tutorial

PHP macOS say Supervising Google Translate Sharing Tutorial

Today’s integration of the Javascript clientside window.postMessage into …

… but this time the RJM Programming‘s domain window called by the Google Translate “child” window is a modified PHP index.php Landing Page

<?php echo ”

if (window.opener) {
if (document.URL.indexOf('translate.goog') == -1 && document.URL.indexOf('text=') != -1 && document.URL.indexOf('_x_tr_sl=') != -1 && document.URL.indexOf('_x_tr_hl=') != -1) {
valvalue='//www-rjmprogramming-com-au.translate.goog/index.php?' + document.URL.split('?')[1];
location.href=valvalue; //wwoo=window.open(valvalue, '_top');
} else if (document.URL.indexOf('translate.goog') != -1 && document.URL.indexOf('text=') != -1 && document.URL.indexOf('_x_tr_sl=') != -1 && document.URL.indexOf('_x_tr_hl=') != -1) {
//document.getElementById('mywords').style.display='block';
setTimeout(function(){
if (document.getElementById('mywords').innerText.trim() != '' && document.getElementById('mywords').innerText != decodeURIComponent(document.URL.split('text=')[1].split('&')[0].split('#')[0])) {
if (document.URL.indexOf('&iam=') != -1) {
window.opener.postMessage(encodeURIComponent(document.getElementById('mywords').innerText), decodeURIComponent(document.URL.split('&iam=')[1].split('&')[0].split('#')[0]));
} else {
window.opener.postMessage(encodeURIComponent(document.getElementById('mywords').innerText), 'https://rjmprogramming.com.au');
}
window.localStorage.setItem('last_gt_trans', encodeURIComponent(document.getElementById('mywords').innerText)); // alert('1:' + document.getElementById('mywords').innerText);
} else {
setTimeout(anotherlook, 1000);
}
}, 1000);
} else if (('' + parent.document.URL).indexOf('translate.goog') != -1 && ('' + parent.document.URL).indexOf('text=') != -1 && ('' + parent.document.URL).indexOf('_x_tr_sl=') != -1 && ('' + parent.document.URL).indexOf('_x_tr_hl=') != -1) {
//document.getElementById('mywords').style.display='block';
setTimeout(function(){
if (document.getElementById('mywords').innerText.trim() != '' && document.getElementById('mywords').innerText != decodeURIComponent(('' + parent.document.URL).split('text=')[1].split('&')[0].split('#')[0])) {
if (('' + parent.document.URL).indexOf('&iam=') != -1) {
window.opener.postMessage(encodeURIComponent(document.getElementById('mywords').innerText), decodeURIComponent(('' + parent.document.URL).split('&iam=')[1].split('&')[0].split('#')[0]));
} else {
window.opener.postMessage(encodeURIComponent(document.getElementById('mywords').innerText), 'https://rjmprogramming.com.au');
}
window.localStorage.setItem('last_gt_trans', encodeURIComponent(document.getElementById('mywords').innerText)); // alert('2:' + document.getElementById('mywords').innerText);
} else {
setTimeout(anotherlook, 1000);
}
}, 1000);
}
}


“; ?>

… “broadcasting of window.postMessage” to marry with today’s changed PHP macos_say_record.php‘s listening …

<?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 …

  1. in a web browser link to our inhouse Voiceover Ideas as the “parent” window
  2. if not already selected select Google Translate in top “mode of use” dropdown
  3. in the Language Code dropdown select a language such as Hindi
  4. this populates the Words textarea with some phrasing macOS say command associates with a Voice speaking that selected language
  5. 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
  6. click/tap Hear These Words button
  7. 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 …
  8. is able to communicate back to the “parent” window via window.postMessage …
  9. 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 …
  10. 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 PHP index.php Landing Page detect the call and isolate the content to just be the wording up for translation, and then in today’s changed PHP macos_say_record.php Voiceover 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.


Previous relevant PHP macOS say Supervising a Retrained Siri Nuance Tutorial is shown below.

PHP macOS say Supervisor Modes of Use Tutorial

PHP macOS say Supervising a Retrained Siri Nuance Tutorial

It’s Nuance Day!!!!

Complacency alert!!! Sometimes nuances take longer to resolve than the 90%ers.

We found the Siri retraining procedures we tried to help out with, on a first day’s worth of effort, regarding PHP macOS say Supervising a Retrained Siri Tutorial, were lacking …

  • order, when starting half way though the list
  • 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 …


<div style=display:inline-block; id=spaudio>
<audio title='Repeat hearing of previous Siri speech.' onclick='if (oopsa) { trywav(this); }' controls id=mysiriaudio style=display:none;>
<source onerror='oopsa=true; ' id=mysirisource type=audio/aiff src=/lastaudiosiri.aiff></source>
<source onerror='oopsb=true; ' id=mysirisourcetwo type=audio/wav src=/lastaudiosiri.wav></source>
</audio>
</div>

… and then form method=POSTing to thyself to invoke …

<?php

// ffmpeg work via PHP exec conduit to command line ...
$ffmpegcommand="";
$ffmpegselect="";
$ffmpegform="";
if (file_exists("/usr/local/bin/ffmpeg")) {
$ffmpegcommand="/usr/local/bin/ffmpeg ";
} else if (file_exists("/usr/bin/ffmpeg")) {
$ffmpegcommand="/usr/bin/ffmpeg ";
} else {
shell_exec("ffmpeg -codecs > " . dirname(__FILE__) . DIRECTORY_SEPARATOR . "ffmhuhpeg.huh");
if (file_exists(dirname(__FILE__) . DIRECTORY_SEPARATOR . "ffmhuhpeg.huh")) {
$retblurb=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . "ffmhuhpeg.huh");
unlink(dirname(__FILE__) . DIRECTORY_SEPARATOR . "ffmhuhpeg.huh");
if (strpos($retblurb, "odecs") !== false) {
$ffmpegcommand="ffmpeg ";
}
} else if (file_exists("/usr/local/bin/ffmpeg")) {
$ffmpegcommand="/usr/local/bin/ffmpeg ";
} else if (file_exists("/usr/bin/ffmpeg")) {
$ffmpegcommand="/usr/bin/ffmpeg ";
}
}

if (isset($_POST['sirisay'])) {
passthru("say" . $sayprefix . " " . str_replace("\n",",,,,,,",$thewords) . " > " . dirname(__FILE__) . "/sayout.out" . " 2> " . dirname(__FILE__) . "/sayerr.err");
if (file_exists($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . "lastaudiosiri.wav")) {
unlink($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . "lastaudiosiri.wav");
}
exec("say" . $sayprefix . " -o " . $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . "lastaudiosiri.aiff" . " " . str_replace("\n",",,,,,,",$thewords) . " > " . dirname(__FILE__) . "/sayout.out" . " 2> " . dirname(__FILE__) . "/sayerr.err ; " . $ffmpegcommand . " -i " . $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . "lastaudiosiri.aiff" . " " . $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . "lastaudiosiri.wav");
if (file_exists($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . "lastaudiosiri.wav")) {
unlink($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . "lastaudiosiri.aiff");
echo "<html><body onload=\"if (parent.document.getElementById('mysirisourcetwo')) { parent.document.getElementById('mysirisourcetwo').type='audio/wav'; parent.document.getElementById('mysirisourcetwo').src=parent.document.getElementById('mysirisourcetwo').src.replace('.aiff','.wav'); }\"></body></html>";
}
} else {
passthru("say" . $sayprefix . " " . str_replace("\n",",,,,,,",$thewords) . " > " . dirname(__FILE__) . "/sayout.out" . " 2> " . dirname(__FILE__) . "/sayerr.err");
}

?>

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++;
}
}
}

Nuance is just so nuancey!!


Previous relevant PHP macOS say Supervising a Retrained Siri Tutorial is shown below.

PHP macOS say Supervisor Modes of Use Tutorial

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 …

  1. if you have an iOS device to be the Siri conduit …
  2. 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 …
  3. MAMP … Apache/PHP/MySql local web server … and have downloaded the latest (at time of writing) …
  4. http://localhost:8888/macos_say_record.php into that MAMP‘s macOS (default port) Document Root folder
  5. 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” …

  1. Siri, how’s the weather?
  2. Hey Siri, send a message.
  3. Siri, set a timer for three minutes.
  4. Hey Siri, get directions home.
  5. Siri, play some music.

… and then all being well, what can then be the case is …

… but please be aware …

  • Siri may forget your voice as a result … versus …
  • Siri can recognize up to 2 voices and one of these could be your “out and about” voice

… regarding our changed inhouse macos_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.


Previous relevant PHP macOS say Supervisor Modes of Use Tutorial is shown below.

PHP macOS say Supervisor Modes of Use Tutorial

PHP macOS say Supervisor Modes of Use Tutorial

After yesterday’s Making of Siri on iPhone Plays Spotify Song via macOS say Tutorial, today we turn back to the PHP code that started our foray into “Siri meets say” thought patterns.

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 ' . "'" . '); }"';
    }


    ?>
    … working with …
    <?php echo ”

    <input type=text name=audioname id=audioname value='audiocapture." . $bestext . "'" . $audioft . "></input>

    “; ?>

… to improve the user experience for those curious macOS (perhaps MAMP) local web server users of our changed inhouse macos_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.


Previous relevant Making of Siri on iPhone Plays Spotify Song via macOS say Tutorial is shown below.

Making of Siri on iPhone Plays Spotify Song via macOS say Tutorial

Making of Siri on iPhone Plays Spotify Song via macOS say Tutorial

Making yesterday’s Siri on iPhone Plays Spotify Song via macOS say Tutorial‘s video involved three devices …

  1. this MacBook Air (using “say” commands) … and …
  2. an iPhone with Siri and Spotify … but, also …
  3. a Windows 10 laptop helped contextualize … via …

… use of …

  • 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

… some steps of which you can see with today’s animated GIF tutorial picture.


Previous relevant Siri on iPhone Plays Spotify Song via macOS say Tutorial is shown below.

Siri on iPhone Plays Spotify Song via macOS say Tutorial

Siri on iPhone Plays Spotify Song via macOS say Tutorial

Yesterday’s Siri on iPad Plays Apple Music Song Tutorial represented to us, regarding the …

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 …

iOS music playing (Apple Music or Spotify etcetera) … and Siri … along with … 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 inhouse macos_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

… as a hardware style of thinking solution. Anyway, take a look at today’s iPhone Siri meets macOS “say” control of iPhone Spotify learning curve hereabouts …


Previous relevant Siri on iPad Plays Apple Music Song Tutorial is shown below.

Siri on iPad Plays Apple Music Song Tutorial

Siri on iPad Plays Apple Music Song Tutorial

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 …

Hey Siri, Play Brahms

… but as you can see from today’s tutorial animated GIF picture (also showing Siri’s setup) we had no Brahms in our Apple Music collection, and so tried …

Hey Siri, Play U2

… with more success!


Previous relevant Siri on iPad Mail Dictate Tutorial is shown below.

Siri on iPad Mail Dictate Tutorial

Siri on iPad Mail Dictate Tutorial

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.


Previous relevant Apple iOS Siri Audio Commentary Tutorial is shown below.

Apple iOS Siri Audio Commentary Tutorial

Apple iOS Siri Audio Commentary Tutorial (click/touch to open and then hover, click/touch will have audio commentary)

Today we wanted to show you an idea inspired by our own previous Apple iOS Siri Primer Tutorial that combines …

… especially as Siri so much relates to audio and “voice recognition”.

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;

function omo(ois) {
if (lastomo != ("" + ois.title)) {
if (ois.alt.indexOf('.') != -1) {
prebits=ois.alt.split(',');
for (i=1; i<=prebits.length; i++) {
bits=prebits[i - 1].split('.');
document.getElementById('iaudio' + i).type="audio/" + bits[1].replace('mp3','mpeg');
document.getElementById('iaudio' + i).src=prebits[i - 1];
try {
document.getElementById('iaudio' + i).play();
} catch (e) {
document.title=('problem');
}
}
}
}
lastomo="" + ois.title;
}

function oc(ois) {
if (lastoc != ("" + ois.title)) {
if (ois.alt.indexOf('.') != -1) {
prebits=ois.alt.split(',');
for (i=1; i<=prebits.length; i++) {
bits=prebits[i - 1].split('.');
document.getElementById('iaudio' + i).type="audio/" + bits[1].replace('mp3','mpeg');
document.getElementById('iaudio' + i).src=prebits[i - 1];
try {
document.getElementById('iaudio' + i).play();
} catch (e) {
document.title=('problem');
}
}
}
}
lastoc="" + ois.title;
}

</script>

You’ll see a reminder of the first CSS plan where we have …


<style>
.screenshot_1::before {
content: url('slide1.m4a');
}
</style>

… because you never know what the future holds?!

Please try our live run and/or peruse or download the HTML and Javascript and “not now” CSS you could call siri_setup_with_audio.html


Previous relevant Apple iOS Siri Primer Tutorial is shown below.

Apple iOS Siri Primer Tutorial

Apple iOS Siri Primer Tutorial

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.

Let’s see how Apple describes Siri this way …

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 …


Settings -> General -> Siri

… relates to “voice recognition” … and which we feature some of the steps involved, with today’s tutorial picture.


Previous relevant Windows 10 Cortana Primer Tutorial is shown below.

Windows 10 Cortana Primer Tutorial

Windows 10 Cortana Primer Tutorial

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 …

  1. have the urge to run for the nearest cupboard before being caught talking into a computer (microphone) … and who …
  2. 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).

Did you know?

Sadly, some days ago saw the passing of Frank Dickens, the creator of the Bristow cartoon series, forever etched on my brain regarding The Great Tea Trolley Disaster of ’67. R.I.P.


Previous relevant WebEx Presentation with Microphone Tutorial is shown below.

WebEx Presentation with Microphone Tutorial

WebEx Presentation with Microphone 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 …

  1. click on System Preferences off the Apple menu
  2. click the Sound option
  3. click the Output tab
  4. pick USB audio CODEC
  5. if you intend using speakers or headphones connected off this microphone from its 1/8″ (3.5mm) Headphone Jack, click the Input tab
  6. pick USB audio CODEC

Being heard never seemed so easy!


Previous relevant WebEx Prerecording Primer Tutorial is shown below.

WebEx Prerecording Primer Tutorial

WebEx Prerecording Primer Tutorial

We’ve been trying out WebEx (by Cisco) prerecording as a video conferencing idea as an alternative to …

… 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.

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

Editable Calendar Collaboration Tutorial

Editable Calendar Collaboration Tutorial

Editable Calendar Collaboration Tutorial

Yesterday’s Editable Calendar Cross Domain Sharing Tutorial had us …

  • 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 …


function doemail(inidea) {
var newurl=document.URL.split('//')[0] + '//' + 'www.rjmprogramming.com.au/calendar_contenteditable.html' + '?_x_tr_tl=' + document.getElementById('language').value + '&month=' + themonth + '&theyear=' + theyear + '#' + encodeURIComponent('<h' + '1' + (('' + document.body.innerHTML).split(('<h' + '1'))[1]).split(' id="lastdiv"></div>')[0] + ' id="lastdiv"></div>');
var zfrom='youllneverfindthis', zto='youllneverfindthis';
var azx=top.document.getElementById('xae' + 'mail');
if (!azx) { azx=top.document.createElement("a"); }
top.document.body.appendChild(azx);
azx.style = "display: none";
azx.target = "_top";
azx.id = 'xae' + 'mail';
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h1')[0].innerHTML.split('<select')[0].split('</font>')[0].split('>')[eval(-1 + document.getElementsByTagName('h1')[0].innerHTML.split('<select')[0].split('</font>')[0].split('>').length)].replace(/\ \;/g,' ')) + '%20' + encodeURIComponent(document.getElementsByTagName('h1')[0].innerHTML.split('</select>')[1].split('</font>')[0].split('>')[eval(-1 + document.getElementsByTagName('h1')[0].innerHTML.split('</select>')[1].split('</font>')[0].split('>').length)].split('<')[0].replace(/\ \;/g,' ') + ' ... ') + '%20' + encodeURIComponent(document.getElementById('calcap').innerText) + '&body=' + encodeURIComponent(newurl); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
return '';
}

function dosms() {
var newurl=document.URL.split('//')[0] + '//' + 'www.rjmprogramming.com.au/calendar_contenteditable.html' + '?_x_tr_tl=' + document.getElementById('language').value + '&month=' + themonth + '&theyear=' + theyear + '#' + encodeURIComponent('<h' + '1' + (('' + document.body.innerHTML).split(('<h' + '1'))[1]).split(' id="lastdiv"></div>')[0] + ' id="lastdiv"></div>');
if (1 == 1) {
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('findeditablecalendaremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('findeditablecalendaremailee');
}
window.localStorage.setItem('findeditablecalendaremailee', 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() == '') {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('findeditablecalendarsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('findeditablecalendarsmsno');
}
window.localStorage.setItem('findeditablecalendarsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
top.document.body.appendChild(azx);
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(newurl.replace('#','&').replace('?','#')); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
}
return '';
}

… we’ll see). Calendars can give that date and time context to collaboration between users, and so, we invite you to try this with the changed ninth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Cross Domain Sharing Tutorial is shown below.

Editable Calendar Cross Domain Sharing Tutorial

Editable Calendar Cross Domain Sharing Tutorial

Unless you are new to the party you’ll know that with yesterday’s Editable Calendar Styling Tutorial‘s Editable Calendar project …


Google Translate

… 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 …

  1. Google Translate‘s domain “child” window … and …
  2. RJM Programming‘s domain “parent” window

… and so the communications between parent and child (regarding any Saved calendar data a user may want to apply and remember), in this scenario, are restricted, but asking Under what conditions can window.opener.parentfunc() type of call work when the domains are different? reminded us of the talents of window.postMessage to help here, thanks. It got us to …

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;

if (('' + event.data).indexOf(encodeURIComponent('<h' + '1')) != -1) {
if (document.getElementById('language').outerHTML.indexOf(' data-lcode="') != -1 && document.getElementById('language').outerHTML.indexOf(' data-lcode=""') == -1) {
thislang=document.getElementById('language').getAttribute('data-lcode');
}
document.body.innerHTML='<h' + '1' + decodeURIComponent(('' + event.data).split(encodeURIComponent('<h' + '1'))[1]).split(' id="lastdiv"></div>')[0] + ' id="lastdiv"></div>';
if (thislang.trim() != '') { document.getElementById('language').value=thislang; document.getElementById('language').setAttribute('data-lcode', thislang); }
inplay=true;
lastinplayc=document.body.innerHTML + ' ';
fromfor=true;
setTimeout(lsit, 2000);
}
});
Be broadcasting at the Google Translate domain document.URL scenario “child” window …

if (window.opener) {
if (document.URL.indexOf('&iam=') != -1) {
window.opener.postMessage(encodeURIComponent(document.body.innerHTML), decodeURIComponent(document.URL.split('&iam=')[1].split('&')[0].split('#')[0]));
} else {
window.opener.postMessage(encodeURIComponent(document.body.innerHTML), 'https://rjmprogramming.com.au');
}
}

… for our Editable Calendar project.

Also, today, we introduce Month, Year “breadcrumb” caption element …

CSS caption element SVG background image based arrows … thanks to this great advice

#calcap { // thanks to https://www.google.com/search?q=background+arrow+background+svg+images+at+left+and+right+of+an+element&rlz=1C5OZZY_en&oq=background+arrow+background+svg+images+at+left+and+right+of+an+element&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCTM1ODAyajBqN6gCALACAA&sourceid=chrome&source=chrome.ob&ie=UTF-8
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); background-color: rgb(240,240,240); border-radius: 8px;
background-image:
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>'),
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>');
background-position: left center, right center;
background-repeat: no-repeat;
}
Javascript caption element ondblclick event logic (based on cursor positioning)

function incit(e) {
var x=-999, y=-999;
var rectis=e.target.getBoundingClientRect();
if (e.touches) {
if (e.touches[0].pageX) {
x = e.touches[0].pageX;
y = e.touches[0].pageY;
} else {
x = e.touches[0].clientX;
y = e.touches[0].clientY;
}
} else if (e.clientX || e.clientY) {
x = e.clientX;
y = e.clientY;
} else {
x = e.pageX;
x = e.pageY;
}
if (x >= 0 && y >= 0 && document.getElementById('calcap').getAttribute('data-thism') != '') {
var curm=eval('' + document.getElementById('calcap').getAttribute('data-thism'));
var cury=eval('' + document.getElementById('calcap').getAttribute('data-thisy'));
if (x <= eval(eval('' + rectis.left) + eval(eval('' + rectis.width) / 2))) {
themonth=eval(-1 + curm);
if (themonth < 0) {
themonth=11;
theyear=eval(-1 + cury);
}
e.target.innerHTML='' + mths[themonth] + ', ' + theyear;
calcapv='';
newmy(e.target);
} else {
themonth=eval(1 + curm);
if (themonth > 11) {
themonth=0;
theyear=eval(1 + cury);
}
e.target.innerHTML='' + mths[themonth] + ', ' + theyear;
calcapv='';
newmy(e.target);
}
}
}

… in the changed eighth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Styling Tutorial is shown below.

Editable Calendar Styling Tutorial

Editable Calendar Styling Tutorial

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 …


<style>
td {
height: 15%;
vertical-align: top;
padding: 2 2 2 2;
}

td {
border-radius: 8px;
background-color: rgb(252,252,252);
}

.pad {
border-color: transparent;
background-color: transparent;
}

td span {
background: linear-gradient(to bottom, rgba(170,170,170,0.3) 30%, yellow 30%);
font-weight: bold;
}

#calcap {
font-weight: bold;
text-shadow: -1px 1px 1px #952dff;
}

#bsave {
background-color: orange;
}

#calcap {
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); background-color: rgb(240,240,240); border-radius: 8px;
}

th {
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); color: blue; background-color: rgb(240,240,240); border-radius: 8px;
}

button {
border-radius: 8px;
}

select {
border-radius: 8px;
}

table {
border-radius: 6px;
background-color: transparent;
}

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.

Yet again, feel free to try this changed, better styled seventh draft Editable Calendar you can try below.


Previous relevant Editable Calendar Save and Recall Tutorial is shown below.

Editable Calendar Save and Recall Tutorial

Editable Calendar Save and Recall Tutorial

Regarding the recent Editable Calendar Get Arguments Tutorial it set up …

  • Month, Year as a dynamic user editable concept … worthy of …
  • coding to allow for the Saving and Recall of user calendar data according to their Month, Year value …

… in window.localStorage data repository particular to the web browser and device they are using.

To deploy this, we’ve decided on creating a scenario with …

  • a new dropdown (ie. select) element containing Month, Year Saved value choices … nested …

    <button id=bsave translate=yes onclick=lsit(); style=display:none;>Save <select onchange=recallsm(this); id=savedd style=display:none;><option translate=yes value=''>Optionally recall a saved Editable Calendar month ...</option></select></button>

    …within …
  • a new button element that a user clicks/taps to Save the current calendar data regarding the displayed Month, Year into window.localStorage data repository …

    function recallsm(osel) {
    if (osel.value.trim() != '') {
    if (osel.value.substring(0,1) == '-') {
    oselvalue=osel.value;
    setTimeout(function(){
    try {
    window.localStorage.removeItem('editable_calendar_' + oselvalue.substring(1).replace(',','_').replace('.','_'));
    } catch(jghfjhg) { }
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }, 2000);
    } else {
    themonth=eval(-1 + eval('' + osel.value.replace(',','.').split('.')[0]));
    theyear=eval('' + eval('' + osel.value.replace(',','.').split('.')[1]));
    usuff='&year=' + theyear + '&month=' + themonth;
    now = new Date(eval('' + theyear), eval('' + themonth), 1);
    document.body.innerHTML=decodeURIComponent(('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)));
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }
    }
    }

    function lscheck() {
    if (origdd == '') { origdd=document.getElementById('savedd').innerHTML; }
    Object.keys(window.localStorage).forEach(function(key){
    if (('' + key).indexOf('editable_calendar_') == 0) {
    //alert(('' + key));
    //alert(('' + key).split('editable_calendar_')[1]);
    //alert(document.getElementById('savedd').innerHTML);
    if (document.getElementById('savedd').innerHTML.indexOf('>' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>') == -1) {
    //alert('2:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').innerHTML+='<option value="' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    document.getElementById('savedd').innerHTML+='<option value="-' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">Forget ' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    //alert('3:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').style.display='inline-block';
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    });
    }

    function lsit() {
    if (inplay && lastinplayc != '') {
    var curval=('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)).replace(/^undefined/g,'').replace(/^null/g,'');
    if (curval != '') {
    if (decodeURIComponent(curval) != lastinplayc) {
    window.localStorage.removeItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear);
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    }
    } else {
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    }
    }
    }

    function inpcheck() {
    if (inplay) {
    if (document.body.innerHTML != lastinplayc) {
    lastinplayc=document.body.innerHTML.trim();
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    }

… in the changed sixth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Get Arguments Tutorial is shown below.

Editable Calendar Get Arguments Tutorial

Editable Calendar Get Arguments Tutorial

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);
}

Yes, the first cab off the rank, tipping our toes into thoughts using this, building on yesterday’s Editable Calendar English User Entry Translation Tutorial, is to …

  • take what used to be a pretty static …

    Month, Year

    … HTML caption element … and …

  • apply the global attribute …

    contenteditable=true

    … 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='';

    function ChangeLcodeNow(){
    var val = document.getElementById('language'), dd=null, pprefix='';
    docURL=documentURL.split('rjmprogramming.com.au/')[eval(-1 + documentURL.split('rjmprogramming.com.au/').length)];
    if (docURL.indexOf('.translate.goog/') != -1) { docURL=docURL.split('.translate.goog/')[1]; }
    if (changesmade) { docURL=docURL.replace('.html','.php').replace('.htm','.php'); ajaxit(); }
    if (documentURL.indexOf('&_x_tr_tl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_blank', 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    documentURL=documentURL.replace('&_x_tr_tl=', '&xtrtl=');
    } else if (documentURL.indexOf('&xtrtl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_top');
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    }
    }

    … and through to our own RJM Programming code

… the one in today’s tutorial picture featuring …


https://www-rjmprogramming-com-au.translate.goog/calendar_contenteditable?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=zh-CN&year=2026&month=1

Again, feel free to try this in the changed fifth draft Editable Calendar you can try below, helped out by a tweaked calendar_contenteditable.php helping PHP newish player.


Previous relevant Editable Calendar English User Entry Translation Tutorial is shown below.

Editable Calendar English User Entry Translation Tutorial

Editable Calendar English User Entry Translation Tutorial

For today’s work, we were thinking of using window.sessionStorage logic, but because of the advice of Can a window.open Google Translate popup window access the sessionStorage written by the originating window from another domain? warning us off, that what we regard as an “ever so cute” solution is not a goer, alas! So, what else is going to help regarding the dilemma …

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 …
<?php

if (file_exists('tihis.txt')) {
$newih=file_get_contents('tihis.txt');
unlink('tihis.txt');
if (strpos($newih, '<cap' . 'tion ') !== false) {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'calendar_contenteditable.html');
$bittochange='<cap' . 'tion ' . explode('</table>', explode('<cap' . 'tion ', $usualis)[-1 + sizeof(explode('<cap' . 'tion ', $usualis))])[0];
echo str_replace($bittochange, $newih, $usualis);
exit;
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
}

?>
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 ''; }

Feel free to try this in the changed fourth draft Editable Calendar you can try below, helped out by a new calendar_contenteditable.php helping PHP new player.


Previous relevant Editable Calendar Intl Tutorial is shown below.

Editable Calendar Intl Tutorial

Editable Calendar Intl Tutorial

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 …
  • JavaScript Intl Object Reference ideas

… that can help with language translations, and we make available to the user via a toggling “-” (to “+”) HTML element modus operandi.

Especially, as we need more work improving the Google Translate interfacing, trying this changed third draft Editable Calendar you can try below, is definitely worth considering.


Previous relevant Editable Calendar Google Translate Tutorial is shown below.

Editable Calendar Google Translate Tutorial

Editable Calendar Google Translate Tutorial

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 …


Google Translate

… 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 changed second draft Editable 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.


Previous relevant Editable Calendar Primer Tutorial is shown below.

Editable Calendar Primer Tutorial

Editable Calendar Primer Tutorial

Today’s new Editable Calendar themed inhouse web application combines …

Today’s steps of progress in this project …

  1. establish the current month and year’s “month showing” calendar into a webpage … and …
  2. within that calendar date cell we place a span element with contenteditable=true … allowing …
  3. user to add content into the calendar

… in this first draft “proof of new to us concept” Editable Calendar you can try below. We hope you tune in tomorrow for more progress!


Previous relevant Event Calendar Collaboration Textarea Highlight Linking Tutorial is shown below.

Event Calendar Collaboration Textarea Highlight Linking Tutorial

Event Calendar Collaboration Textarea Highlight Linking Tutorial

For the first time we can remember, with our Events in Month web application of yesterday’s Event Calendar Collaboration Remembering Recipient Tutorial

  • we’re channelling how in emails “word strings” starting with “http” become links … and so …
  • in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available

Here is the relevant Javascript we ended up with, for this …


var thesel='', showsel=false, lastsel='';

function showthesel() {
var uwords=[], iuw=1, ils=0;
if (showsel) {
showsel=false;
if (lastsel != thesel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
lastsel=thesel;
thesel='';
}
if (lastsel != '' && thesel == '') {
uwords=lastsel.toLowerCase().split('http');
ils=eval(4 + eval('' + uwords[0].length));
for (iuw=1; iuw<uwords.length; iuw++) {
window.open('//' + lastsel.substring(ils).split('//')[1].split(' ')[0].split(String.fromCharCode(10))[0].split(String.fromCharCode(13))[0], '_blank', 'top=' + eval(10 * iuw + 50) + ',left=' + eval(10 * iuw + 50) + ',width=600,height=600');
ils+=eval(4 + eval('' + uwords[iuw].length));
}
}
}
}

function handleSelection() { // thanks to https://stackoverflow.com/questions/46651479/reacting-to-selection-changes-in-an-html-textarea
const activeElement = document.activeElement;

// Make sure this is your textarea
if (activeElement && activeElement.outerHTML.indexOf('<textarea') == 0) {
const range = {
start: activeElement.selectionStart,
end: activeElement.selectionEnd
};
// Do something with your range
if (eval('' + range.end) > eval('' + range.start)) {
thesel=(activeElement.value.substring(range.start).substring(0, eval(eval('' + range.end) - eval('' + range.start) )));
if (!showsel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
showsel=true;
setTimeout(showthesel, 5000);
}
}
}
}

document.addEventListener('selectionchange', handleSelection);

Also, today, we have a few CSS tweaks in the changed events_in_month.htm parent of the Events in Month web application. Feel free to try it for yourself.

Stop Press

Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!


Previous relevant Event Calendar Collaboration Remembering Recipient Tutorial is shown below.

Event Calendar Collaboration Remembering Recipient Tutorial

Event Calendar Collaboration Remembering Recipient Tutorial

When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …

  • When it comes to procedures …
  • Aaaaaarrrrgggghhhh
  • Nuances count … regarding …
  • Errrrrrr
  • Saving time …
  • Saving frustration

… resulting in the “easy to remember” acronym WANESS … we rest our cases!

What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.

Global variables initialization …

var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …

if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …

function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}

function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic

function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}

//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}

return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}

var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}

Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changed events_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!


Previous relevant Event Calendar Collaboration Tutorial is shown below.

Event Calendar Collaboration Tutorial

Event Calendar Collaboration Tutorial

The Event Calendar web application project, of Event Calendar PHP Bookmark Tutorial, from last year is worth a revisit, the reason being …

  • it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
  • each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
  • hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs

… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!

Timing became really important with this integration of …

  • email
  • SMS

… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …

  1. Month
  2. Year

… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …

Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons

<h1>Events in Month &nbsp;&nbsp;<button style=display:none; title='Share via Email' id=semail onclick="doemail('');">&#128231;</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>&#128223;</button> </h1>
New static HTML div element placed to the bottom of the body element …

<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …

var bigbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1].split('&')[0]) : '';
if (bigbih != '' && bigbih.indexOf('xae' + 'mail') == -1) {
bigbih+='<a target=_blank id=xae' + 'mail style=display:none; href="mailto:"></a>';
}
if (bigbih != '' && bigbih.indexOf('xas' + 'ms') == -1) {
bigbih+='<a target=_blank id=xas' + 'ms style=display:none; href="sms:"></a>';
}
var viatext='', smallbih='', wes=null, sparewes=null;
On or around document.body onload event we can analyze any such location.hash hashtagging data …

if (bigbih.indexOf('</he' + 'ad>') != -1 || bigbih.indexOf('<b' + 'o' + 'dy') != -1 || bigbih.indexOf('<h' + '1') == 0) {
//bigbih=bigbih.replace('" id="eventcalendar"', 'margin-top:50px;" id="eventcalendar"');
viatext=' (via contact at ' + ('' + new Date()) + ')';
if (bigbih.indexOf('<h' + '1') == 0) {
smallbih=bigbih;
} else {
smallbih=bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].replace(bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].split('>')[0] + '>', '');
}
wes=windowopen('./events_in_month.htm','_blank','top=40,left=40,width=800,height=800');
//alert(bigbih);
if (wes) {
setTimeout(function(){
wes.document.write(bigbih);
}, 3000);
//document.body.innerHTML=bigbih;
setTimeout(function(){
wes.document.getElementById('semail').style.display='inline-block';
wes.document.getElementById('ssms').style.display='inline-block';
wes.document.getElementsByTagName('h1')[0].style.display='none';
wes.document.getElementsByTagName('h3')[0].style.display='none';
//wes.setviatext(viatext);
//wes.setphpthere(' ');
//wes.togglewords();
}, 5000);
}
}
A window.open wrapper windowopen Javascript function …

function windowopen(poneis, ptwois, pthreeis) {
document.getElementById('divas').innerHTML='<iframe name=ovif id=ovif style="position:absolute;top:140px;left:0px;width:100%;height:100%;z-index:99;" src="' + poneis + '" onload=wopencheck(this);></iframe>';
//document.getElementsByTagName('h1')[0].style.opacity='0.0';
//document.getElementsByTagName('h3')[0].style.opacity='0.0';
sparewes=window.open(poneis.split('.htm')[0] + '.php', 'ovif');
//sparewes=window.open(poneis, 'ovif');
sparewes.document.write(bigbih);
setTimeout(function(){
sparewes.document.getElementById('semail').style.display='inline-block';
sparewes.document.getElementById('ssms').style.display='inline-block';
var sdem=sparewes.document.getElementById('semail').outerHTML;
var sdss=sparewes.document.getElementById('ssms').outerHTML;
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdss,'');
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdem,'');
sparewes.document.getElementsByTagName('h1')[0].style.display='none';
sparewes.document.getElementsByTagName('h3')[0].style.display='none';
sparewes.document.getElementById('dmore').innerHTML+=(' ' + sdem.replace('doeJUNKmail(', 'parent.doe' + 'mail(') + ' ' + sdss.replace('dosJUNKms(', 'parent.dos' + 'ms(') + '<br>');
//sparewes.setviatext(viatext);
//sparewes.setphpthere(' ');
//sparewes.togglewords();
}, 5000);
return null;
}
Is augmented by other new Javascript functions …

function hfanalyze() {
var fio=document.getElementById('ecform');
if (forceit) { forceit=false; return true; }
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
//alert(fions.length);
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value.replace(/\?\>/g,'? >'));
delm='&';
}
//alert(testurl.length);
//if (eval('' + testurl.length) < 750) { return true; }
//if (lastbut.value.indexOf('New') == 0 || 11 == 11) {
if (22 == 22) {
//wo=window.open('', '_blank', 'top=50,left=50,height=600,width=600');
//wo.document.write('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
return ('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
} else if (phpthere != '') {
if (1 == 1) {
postit(fio, false, testurl);
} else {
fio.target='phpif';
fio.method='POST';
fio.action='./events_in_month.php';
if (!document.getElementById('dmore')) {
document.getElementById('ddmore').innerHTML='<input type=hidden id=caltitle name=caltitle value="' + document.getElementById('myoldsel').options[document.getElementById('myoldsel').selectedIndex].innerText + '"></input><input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
} else {
document.getElementById('dmore').innerHTML='<input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
}
}
return 'true';
}
return 'false';
}

function doemail(inidea) {
var azx=document.getElementById('xae' + 'mail');
if (!azx) { azx=document.createElement("a"); }
//if (1 == 1) {
//document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
//}
azx.style = "display: none";
azx.id = 'xaem' + 'ail';
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h2')[0].innerHTML.split('&')[0].split('<')[0].replace(/\`/g,'').replace(/^Events\ in\ /g, 'Events in Month ').substring(0)) + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze())); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
return '';
}

function dosms() {
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead.', snum)).replace(/^null/g,'');
} else {
snum=('' + prompt('Please enter SMS number to send to.', snum)).replace(/^null/g,'');
}
if (snum.indexOf('@') != -1) {
return doemail(snum);
} else if (snum.trim() != '') {
alert('Snum=' + snum + '?');
var azx=document.getElementById('xas' + 'ms');
if (azx == null) { azx=document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()));
azx.click();
}
return '';
}

function wopencheck(iois) {
var aconto = null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
aconto.body.innerHTML=smallbih;
aconto.setviatext(viatext);
}
}
}

function setviatext(towhat) {
viatext=towhat;
}

… to help bed down this new sharing and collaboration functionality in a changed events_in_month.htm parent of the Events in Month web application.


Previous relevant Event Calendar PHP Bookmark Tutorial is shown below.

Event Calendar PHP Bookmark Tutorial

Event Calendar PHP Bookmark Tutorial

Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to


Record to Remember via Bookmark

… use the web browser Bookmarks to help you recall an Events in Month report with long entries.

Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …


https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1

… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!

To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …


var documentURL=document.URL;

… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …


#August__2023_00000__1

… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).

Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).

Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …


<style>
#eventcalendar {
background-color: #fcfcfc;
}

td {
padding-top: 2px;
padding-left: 2px;
padding-right: 2px;
padding-bottom: 12px;
}

.dayb {
color: white;
background-color: red;
padding: 5 5 5 5;
border-radius: 80px;
margin-bottom: 15px;
}

.dow {
color: purple;
font-style: bold;
}

.selday {
margin-left: 8px;
background-color: rgba(255,0,0,0.7);
display: inline-block;
width: 50px;
border-color: transparent;
text-overflow: ellipsis;
}

.tablurb {
background: linear-gradient(to right, white, lightpink, pink);
}

input[type="submit"] {
margin-bottom: 3px;
border-radius: 180px;
}

input[type="number"] {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f3f3f3;
padding: 2 2 2 2;
}

#smonth {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f9f9f9;
padding: 2 2 2 2;
}

.boldtitle {
background-color: rgba(0, 211, 107, 0.2);
}

.boldtitle + .tablurb {
margin-top: 8px;
}


.selhistory {
border-radius: 180px;
background-color: lightpink;
padding: 2 2 2 2;
}
</style>

… and we thank this webpage for the heads up regarding how to calculate week numbers within a year data item displays now available in …


Previous relevant Event Calendar PHP Tutorial is shown below.

Event Calendar PHP Tutorial

Event Calendar PHP Tutorial

Let’s face it. Serverside PHP is just great! It opens up so many opportunities regarding data in your web applications.

As such, onto yesterday’s Event Calendar New Window Tutorial logic we now have a …


Record to Remember

… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.

The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …


<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm

$hcont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.htm');
$cont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php');
$ipad=server_remote_addr();
$ipadless=$ipad;
$js='';

function server_remote_addr() {
global $ipadless;
$rma = $_SERVER['REMOTE_ADDR'];
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
// you can add different browsers with the same way ..
$ipadless=str_replace(".", "_", str_replace(":", "_", $rma));
if(preg_match('/(chromium)[ \/]([\w.]+)/', $ua))
$rma = '000000'.$rma;
elseif(preg_match('/(chrome)[ \/]([\w.]+)/', $ua))
$rma = '00000'.$rma;
elseif(preg_match('/(safari)[ \/]([\w.]+)/', $ua))
$rma = '0000'.$rma;
elseif(preg_match('/(opera)[ \/]([\w.]+)/', $ua))
$rma = '000'.$rma;
elseif(preg_match('/(msie)[ \/]([\w.]+)/', $ua))
$rma = '00'.$rma;
elseif(preg_match('/(mozilla)[ \/]([\w.]+)/', $ua))
$rma = '0'.$rma;
return str_replace(".", "_", str_replace(":", "_", $rma));
}

$itdone=false;
if (isset($_GET['init'])) {
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
echo "<html><head><script type='text/javascript'> " . $js . " </script></head><body><p>" . $ipad . "</p></body></html>";
} else if (isset($_POST['phpthere']) && isset($_POST['bigurl']) && isset($_POST['caltitle'])) {
//file_put_contents('xz.xz', 'l');
if (strpos($cont, '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n") === false) {
//file_put_contents('xz.xzz', 'l');
$cont=str_replace('?' . '>', '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n" . '?' . '>', $cont);
file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php', $cont);
}
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
//file_put_contents('xz.xzzz', "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>");
echo "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>";
} else {
echo $hcont;
}
exit;

//
//
// January__1970_00000__1=http://localhost:8888/events_in_month.htm?caltitle=January%2C%201970&i01.00=&ta01.00=kgjfjhf%20jhgfjhf%20jhkgkjhg%20jhgkjhg%20jkghhg%20jkhgkjhg%20kjhgkjhg&i02.00=&ta02.00=kjhgkjh%20kjhgkjhg%20jkhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg%20kjgkjhg&i03.00=&ta03.00=kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg&i04.00=&ta04.00=&i05.00=&ta05.00=&i06.00=&ta06.00=&i07.00=&ta07.00=&i08.00=&ta08.00=&i09.00=&ta09.00=&i10.00=&ta10.00=&i11.00=&ta11.00=&i12.00=&ta12.00=&i13.00=&ta13.00=&i14.00=&ta14.00=&i15.00=&ta15.00=&i16.00=&ta16.00=&i17.00=&ta17.00=&i18.00=&ta18.00=&i19.00=&ta19.00=&i20.00=&ta20.00=&i21.00=&ta21.00=&i22.00=&ta22.00=&i23.00=&ta23.00=&i24.00=&ta24.00=&i25.00=&ta25.00=&i26.00=&ta26.00=&i27.00=&ta27.00=&i28.00=&ta28.00=&i29.00=&ta29.00=&i30.00=&ta30.00=&i31.00=&ta31.00=
?>

Yes, the parent needed to change for our events_in_month.htm parent of Events in Month web application role.


Previous relevant Event Calendar New Window Tutorial is shown below.

Event Calendar New Window Tutorial

Event Calendar New Window Tutorial

Onto yesterday’s Event Calendar Remembered Tutorial‘s “Mystery Dilemma”

But, there’s an inherent weakness with the design, we’ll go into more into the future.

… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.

Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.

Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …

  1. bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
  2. pick Inspect option …
  3. be in Elements tab of your Web Inspector … and …
  4. highlight top <html> tag …
  5. Context Menu with a right click or two finger gesture …
  6. pick Copy -> Copy outerHTML … so that …
  7. your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
  8. Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)

The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …


function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}

… the form submit buttons are reworded accordingly.

Another idea from this blog thread’s inspiration …

… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.

And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …

  • text shadow means by which the text of Event Calendar can be more impactive

    <div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
  • dropdown element conditional styling

    <style>
    .dayb {
    color: white;
    background-color: red;
    padding: 5 5 5 5;
    }


    .dow {
    color: purple;
    font-style: bold;
    }

    .selday {
    margin-left: 8px;
    background-color: rgba(255,0,0,0.7);
    display: inline-block;
    width: 50px;
    }
    </style>



    if (thislabel.substring(0,1) == 'i') {
    if (thisval.trim() != '') {
    document.getElementById(thislabel.replace('i', 'sel')).style.color='white';
    document.getElementById(thislabel.replace('i', 'sel')).style.backgroundColor='red';

    }
    document.getElementById(thislabel.replace('i', 'opt')).innerText=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + '); //.replace(/\+$/g, ' ');
    document.getElementById(thislabel).value=thisval.replace(/\+$/g, ' ');
    } else {
    document.getElementById(thislabel).value=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + ');
    }
    }
  • placeholder on Blurb conditional existence

    var ourdata='';
    // ...
    if (documentURL.indexOf('?') != -1) {
    if (documentURL.indexOf('?caltitle=') != -1) { ourdata='data-'; }
    // ...
    trtemplate='<tr id=tr01.00><td style=width:22%;><span id=sone01.00 class=dow>' + dotw[adate.getDay()].toUpperCase().substring(0,3) + '</span><br><br><span id=stwo01.00 class=dayb>1<span onblur=sepit(this); contenteditable=true id=sp01.00></span><input type=hidden id=i01.00 name=i01.00 value=""></input><select data-dow=' + dotw[adate.getDay()].toUpperCase().substring(0,3) + ' class=selday onchange="selit(this);" id=sel01.00><option id=opt01.00 value=""></option><option title="All such in month (ie. weekly)" value="...">...</option><option title="And ..." value="&...">&</option><option value=Bold>Bold Title</option><option value=Clone>Clone</option></select></span></td><td class=blurb title="What is on?" id=tb01.00><span title="Event title" style="font-style:bold;color:blue;" id=bd01.00></span><textarea name=ta01.00 id=ta01.00 style="width:100%;height=100%;" ' + ourdata + 'placeholder="Blurb ..." class=tablurb></textarea></td></tr>';
    // ...
    }
    // ...
    }

… for our “second draft” events_in_month.htm Events in Month web application.


Previous relevant Event Calendar Remembered Tutorial is shown below.

Event Calendar Remembered Tutorial

Event Calendar Remembered Tutorial

We were inspired by an Event Calendar pamplette we saw the other day …

… to write a new “proof of concept” Events in Month web application, whose content can be recalled via the web browser’s Bookmark methodologies.

We liked the ideas for day of week and/or date of month grouping arrangements we included, being, the way we interpreted it …

  • just on this day in this month … default
  • on this day of the week throughout the month in question … “
  • on this day and some others in that month in question … “&” … to start with and further amendments available via contenteditable=true span element
  • “Clone” value allows for multiple separated “blurbs” for the one signature day

But, there’s an inherent weakness with the design, we’ll go into more into the future. For now, you can try it yourself below …

Stop Press

This is where we get to for a “second draft” we’ll get into, further, tomorrow …

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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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

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

Editable Calendar Cross Domain Sharing Tutorial

Editable Calendar Cross Domain Sharing Tutorial

Editable Calendar Cross Domain Sharing Tutorial

Unless you are new to the party you’ll know that with yesterday’s Editable Calendar Styling Tutorial‘s Editable Calendar project …


Google Translate

… 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 …

  1. Google Translate‘s domain “child” window … and …
  2. RJM Programming‘s domain “parent” window

… and so the communications between parent and child (regarding any Saved calendar data a user may want to apply and remember), in this scenario, are restricted, but asking Under what conditions can window.opener.parentfunc() type of call work when the domains are different? reminded us of the talents of window.postMessage to help here, thanks. It got us to …

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;

if (('' + event.data).indexOf(encodeURIComponent('<h' + '1')) != -1) {
if (document.getElementById('language').outerHTML.indexOf(' data-lcode="') != -1 && document.getElementById('language').outerHTML.indexOf(' data-lcode=""') == -1) {
thislang=document.getElementById('language').getAttribute('data-lcode');
}
document.body.innerHTML='<h' + '1' + decodeURIComponent(('' + event.data).split(encodeURIComponent('<h' + '1'))[1]).split(' id="lastdiv"></div>')[0] + ' id="lastdiv"></div>';
if (thislang.trim() != '') { document.getElementById('language').value=thislang; document.getElementById('language').setAttribute('data-lcode', thislang); }
inplay=true;
lastinplayc=document.body.innerHTML + ' ';
fromfor=true;
setTimeout(lsit, 2000);
}
});
Be broadcasting at the Google Translate domain document.URL scenario “child” window …

if (window.opener) {
if (document.URL.indexOf('&iam=') != -1) {
window.opener.postMessage(encodeURIComponent(document.body.innerHTML), decodeURIComponent(document.URL.split('&iam=')[1].split('&')[0].split('#')[0]));
} else {
window.opener.postMessage(encodeURIComponent(document.body.innerHTML), 'https://rjmprogramming.com.au');
}
}

… for our Editable Calendar project.

Also, today, we introduce Month, Year “breadcrumb” caption element …

CSS caption element SVG background image based arrows … thanks to this great advice

#calcap { // thanks to https://www.google.com/search?q=background+arrow+background+svg+images+at+left+and+right+of+an+element&rlz=1C5OZZY_en&oq=background+arrow+background+svg+images+at+left+and+right+of+an+element&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCTM1ODAyajBqN6gCALACAA&sourceid=chrome&source=chrome.ob&ie=UTF-8
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); background-color: rgb(240,240,240); border-radius: 8px;
background-image:
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>'),
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>');
background-position: left center, right center;
background-repeat: no-repeat;
}
Javascript caption element ondblclick event logic (based on cursor positioning)

function incit(e) {
var x=-999, y=-999;
var rectis=e.target.getBoundingClientRect();
if (e.touches) {
if (e.touches[0].pageX) {
x = e.touches[0].pageX;
y = e.touches[0].pageY;
} else {
x = e.touches[0].clientX;
y = e.touches[0].clientY;
}
} else if (e.clientX || e.clientY) {
x = e.clientX;
y = e.clientY;
} else {
x = e.pageX;
x = e.pageY;
}
if (x >= 0 && y >= 0 && document.getElementById('calcap').getAttribute('data-thism') != '') {
var curm=eval('' + document.getElementById('calcap').getAttribute('data-thism'));
var cury=eval('' + document.getElementById('calcap').getAttribute('data-thisy'));
if (x <= eval(eval('' + rectis.left) + eval(eval('' + rectis.width) / 2))) {
themonth=eval(-1 + curm);
if (themonth < 0) {
themonth=11;
theyear=eval(-1 + cury);
}
e.target.innerHTML='' + mths[themonth] + ', ' + theyear;
calcapv='';
newmy(e.target);
} else {
themonth=eval(1 + curm);
if (themonth > 11) {
themonth=0;
theyear=eval(1 + cury);
}
e.target.innerHTML='' + mths[themonth] + ', ' + theyear;
calcapv='';
newmy(e.target);
}
}
}

… in the changed eighth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Styling Tutorial is shown below.

Editable Calendar Styling Tutorial

Editable Calendar Styling Tutorial

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 …


<style>
td {
height: 15%;
vertical-align: top;
padding: 2 2 2 2;
}

td {
border-radius: 8px;
background-color: rgb(252,252,252);
}

.pad {
border-color: transparent;
background-color: transparent;
}

td span {
background: linear-gradient(to bottom, rgba(170,170,170,0.3) 30%, yellow 30%);
font-weight: bold;
}

#calcap {
font-weight: bold;
text-shadow: -1px 1px 1px #952dff;
}

#bsave {
background-color: orange;
}

#calcap {
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); background-color: rgb(240,240,240); border-radius: 8px;
}

th {
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); color: blue; background-color: rgb(240,240,240); border-radius: 8px;
}

button {
border-radius: 8px;
}

select {
border-radius: 8px;
}

table {
border-radius: 6px;
background-color: transparent;
}

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.

Yet again, feel free to try this changed, better styled seventh draft Editable Calendar you can try below.


Previous relevant Editable Calendar Save and Recall Tutorial is shown below.

Editable Calendar Save and Recall Tutorial

Editable Calendar Save and Recall Tutorial

Regarding the recent Editable Calendar Get Arguments Tutorial it set up …

  • Month, Year as a dynamic user editable concept … worthy of …
  • coding to allow for the Saving and Recall of user calendar data according to their Month, Year value …

… in window.localStorage data repository particular to the web browser and device they are using.

To deploy this, we’ve decided on creating a scenario with …

  • a new dropdown (ie. select) element containing Month, Year Saved value choices … nested …

    <button id=bsave translate=yes onclick=lsit(); style=display:none;>Save <select onchange=recallsm(this); id=savedd style=display:none;><option translate=yes value=''>Optionally recall a saved Editable Calendar month ...</option></select></button>

    …within …
  • a new button element that a user clicks/taps to Save the current calendar data regarding the displayed Month, Year into window.localStorage data repository …

    function recallsm(osel) {
    if (osel.value.trim() != '') {
    if (osel.value.substring(0,1) == '-') {
    oselvalue=osel.value;
    setTimeout(function(){
    try {
    window.localStorage.removeItem('editable_calendar_' + oselvalue.substring(1).replace(',','_').replace('.','_'));
    } catch(jghfjhg) { }
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }, 2000);
    } else {
    themonth=eval(-1 + eval('' + osel.value.replace(',','.').split('.')[0]));
    theyear=eval('' + eval('' + osel.value.replace(',','.').split('.')[1]));
    usuff='&year=' + theyear + '&month=' + themonth;
    now = new Date(eval('' + theyear), eval('' + themonth), 1);
    document.body.innerHTML=decodeURIComponent(('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)));
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }
    }
    }

    function lscheck() {
    if (origdd == '') { origdd=document.getElementById('savedd').innerHTML; }
    Object.keys(window.localStorage).forEach(function(key){
    if (('' + key).indexOf('editable_calendar_') == 0) {
    //alert(('' + key));
    //alert(('' + key).split('editable_calendar_')[1]);
    //alert(document.getElementById('savedd').innerHTML);
    if (document.getElementById('savedd').innerHTML.indexOf('>' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>') == -1) {
    //alert('2:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').innerHTML+='<option value="' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    document.getElementById('savedd').innerHTML+='<option value="-' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">Forget ' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    //alert('3:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').style.display='inline-block';
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    });
    }

    function lsit() {
    if (inplay && lastinplayc != '') {
    var curval=('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)).replace(/^undefined/g,'').replace(/^null/g,'');
    if (curval != '') {
    if (decodeURIComponent(curval) != lastinplayc) {
    window.localStorage.removeItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear);
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    }
    } else {
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    }
    }
    }

    function inpcheck() {
    if (inplay) {
    if (document.body.innerHTML != lastinplayc) {
    lastinplayc=document.body.innerHTML.trim();
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    }

… in the changed sixth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Get Arguments Tutorial is shown below.

Editable Calendar Get Arguments Tutorial

Editable Calendar Get Arguments Tutorial

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);
}

Yes, the first cab off the rank, tipping our toes into thoughts using this, building on yesterday’s Editable Calendar English User Entry Translation Tutorial, is to …

  • take what used to be a pretty static …

    Month, Year

    … HTML caption element … and …

  • apply the global attribute …

    contenteditable=true

    … 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='';

    function ChangeLcodeNow(){
    var val = document.getElementById('language'), dd=null, pprefix='';
    docURL=documentURL.split('rjmprogramming.com.au/')[eval(-1 + documentURL.split('rjmprogramming.com.au/').length)];
    if (docURL.indexOf('.translate.goog/') != -1) { docURL=docURL.split('.translate.goog/')[1]; }
    if (changesmade) { docURL=docURL.replace('.html','.php').replace('.htm','.php'); ajaxit(); }
    if (documentURL.indexOf('&_x_tr_tl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_blank', 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    documentURL=documentURL.replace('&_x_tr_tl=', '&xtrtl=');
    } else if (documentURL.indexOf('&xtrtl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_top');
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    }
    }

    … and through to our own RJM Programming code

… the one in today’s tutorial picture featuring …


https://www-rjmprogramming-com-au.translate.goog/calendar_contenteditable?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=zh-CN&year=2026&month=1

Again, feel free to try this in the changed fifth draft Editable Calendar you can try below, helped out by a tweaked calendar_contenteditable.php helping PHP newish player.


Previous relevant Editable Calendar English User Entry Translation Tutorial is shown below.

Editable Calendar English User Entry Translation Tutorial

Editable Calendar English User Entry Translation Tutorial

For today’s work, we were thinking of using window.sessionStorage logic, but because of the advice of Can a window.open Google Translate popup window access the sessionStorage written by the originating window from another domain? warning us off, that what we regard as an “ever so cute” solution is not a goer, alas! So, what else is going to help regarding the dilemma …

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 …
<?php

if (file_exists('tihis.txt')) {
$newih=file_get_contents('tihis.txt');
unlink('tihis.txt');
if (strpos($newih, '<cap' . 'tion ') !== false) {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'calendar_contenteditable.html');
$bittochange='<cap' . 'tion ' . explode('</table>', explode('<cap' . 'tion ', $usualis)[-1 + sizeof(explode('<cap' . 'tion ', $usualis))])[0];
echo str_replace($bittochange, $newih, $usualis);
exit;
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
}

?>
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 ''; }

Feel free to try this in the changed fourth draft Editable Calendar you can try below, helped out by a new calendar_contenteditable.php helping PHP new player.


Previous relevant Editable Calendar Intl Tutorial is shown below.

Editable Calendar Intl Tutorial

Editable Calendar Intl Tutorial

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 …
  • JavaScript Intl Object Reference ideas

… that can help with language translations, and we make available to the user via a toggling “-” (to “+”) HTML element modus operandi.

Especially, as we need more work improving the Google Translate interfacing, trying this changed third draft Editable Calendar you can try below, is definitely worth considering.


Previous relevant Editable Calendar Google Translate Tutorial is shown below.

Editable Calendar Google Translate Tutorial

Editable Calendar Google Translate Tutorial

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 …


Google Translate

… 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 changed second draft Editable 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.


Previous relevant Editable Calendar Primer Tutorial is shown below.

Editable Calendar Primer Tutorial

Editable Calendar Primer Tutorial

Today’s new Editable Calendar themed inhouse web application combines …

Today’s steps of progress in this project …

  1. establish the current month and year’s “month showing” calendar into a webpage … and …
  2. within that calendar date cell we place a span element with contenteditable=true … allowing …
  3. user to add content into the calendar

… in this first draft “proof of new to us concept” Editable Calendar you can try below. We hope you tune in tomorrow for more progress!


Previous relevant Event Calendar Collaboration Textarea Highlight Linking Tutorial is shown below.

Event Calendar Collaboration Textarea Highlight Linking Tutorial

Event Calendar Collaboration Textarea Highlight Linking Tutorial

For the first time we can remember, with our Events in Month web application of yesterday’s Event Calendar Collaboration Remembering Recipient Tutorial

  • we’re channelling how in emails “word strings” starting with “http” become links … and so …
  • in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available

Here is the relevant Javascript we ended up with, for this …


var thesel='', showsel=false, lastsel='';

function showthesel() {
var uwords=[], iuw=1, ils=0;
if (showsel) {
showsel=false;
if (lastsel != thesel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
lastsel=thesel;
thesel='';
}
if (lastsel != '' && thesel == '') {
uwords=lastsel.toLowerCase().split('http');
ils=eval(4 + eval('' + uwords[0].length));
for (iuw=1; iuw<uwords.length; iuw++) {
window.open('//' + lastsel.substring(ils).split('//')[1].split(' ')[0].split(String.fromCharCode(10))[0].split(String.fromCharCode(13))[0], '_blank', 'top=' + eval(10 * iuw + 50) + ',left=' + eval(10 * iuw + 50) + ',width=600,height=600');
ils+=eval(4 + eval('' + uwords[iuw].length));
}
}
}
}

function handleSelection() { // thanks to https://stackoverflow.com/questions/46651479/reacting-to-selection-changes-in-an-html-textarea
const activeElement = document.activeElement;

// Make sure this is your textarea
if (activeElement && activeElement.outerHTML.indexOf('<textarea') == 0) {
const range = {
start: activeElement.selectionStart,
end: activeElement.selectionEnd
};
// Do something with your range
if (eval('' + range.end) > eval('' + range.start)) {
thesel=(activeElement.value.substring(range.start).substring(0, eval(eval('' + range.end) - eval('' + range.start) )));
if (!showsel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
showsel=true;
setTimeout(showthesel, 5000);
}
}
}
}

document.addEventListener('selectionchange', handleSelection);

Also, today, we have a few CSS tweaks in the changed events_in_month.htm parent of the Events in Month web application. Feel free to try it for yourself.

Stop Press

Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!


Previous relevant Event Calendar Collaboration Remembering Recipient Tutorial is shown below.

Event Calendar Collaboration Remembering Recipient Tutorial

Event Calendar Collaboration Remembering Recipient Tutorial

When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …

  • When it comes to procedures …
  • Aaaaaarrrrgggghhhh
  • Nuances count … regarding …
  • Errrrrrr
  • Saving time …
  • Saving frustration

… resulting in the “easy to remember” acronym WANESS … we rest our cases!

What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.

Global variables initialization …

var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …

if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …

function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}

function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic

function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}

//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}

return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}

var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}

Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changed events_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!


Previous relevant Event Calendar Collaboration Tutorial is shown below.

Event Calendar Collaboration Tutorial

Event Calendar Collaboration Tutorial

The Event Calendar web application project, of Event Calendar PHP Bookmark Tutorial, from last year is worth a revisit, the reason being …

  • it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
  • each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
  • hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs

… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!

Timing became really important with this integration of …

  • email
  • SMS

… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …

  1. Month
  2. Year

… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …

Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons

<h1>Events in Month &nbsp;&nbsp;<button style=display:none; title='Share via Email' id=semail onclick="doemail('');">&#128231;</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>&#128223;</button> </h1>
New static HTML div element placed to the bottom of the body element …

<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …

var bigbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1].split('&')[0]) : '';
if (bigbih != '' && bigbih.indexOf('xae' + 'mail') == -1) {
bigbih+='<a target=_blank id=xae' + 'mail style=display:none; href="mailto:"></a>';
}
if (bigbih != '' && bigbih.indexOf('xas' + 'ms') == -1) {
bigbih+='<a target=_blank id=xas' + 'ms style=display:none; href="sms:"></a>';
}
var viatext='', smallbih='', wes=null, sparewes=null;
On or around document.body onload event we can analyze any such location.hash hashtagging data …

if (bigbih.indexOf('</he' + 'ad>') != -1 || bigbih.indexOf('<b' + 'o' + 'dy') != -1 || bigbih.indexOf('<h' + '1') == 0) {
//bigbih=bigbih.replace('" id="eventcalendar"', 'margin-top:50px;" id="eventcalendar"');
viatext=' (via contact at ' + ('' + new Date()) + ')';
if (bigbih.indexOf('<h' + '1') == 0) {
smallbih=bigbih;
} else {
smallbih=bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].replace(bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].split('>')[0] + '>', '');
}
wes=windowopen('./events_in_month.htm','_blank','top=40,left=40,width=800,height=800');
//alert(bigbih);
if (wes) {
setTimeout(function(){
wes.document.write(bigbih);
}, 3000);
//document.body.innerHTML=bigbih;
setTimeout(function(){
wes.document.getElementById('semail').style.display='inline-block';
wes.document.getElementById('ssms').style.display='inline-block';
wes.document.getElementsByTagName('h1')[0].style.display='none';
wes.document.getElementsByTagName('h3')[0].style.display='none';
//wes.setviatext(viatext);
//wes.setphpthere(' ');
//wes.togglewords();
}, 5000);
}
}
A window.open wrapper windowopen Javascript function …

function windowopen(poneis, ptwois, pthreeis) {
document.getElementById('divas').innerHTML='<iframe name=ovif id=ovif style="position:absolute;top:140px;left:0px;width:100%;height:100%;z-index:99;" src="' + poneis + '" onload=wopencheck(this);></iframe>';
//document.getElementsByTagName('h1')[0].style.opacity='0.0';
//document.getElementsByTagName('h3')[0].style.opacity='0.0';
sparewes=window.open(poneis.split('.htm')[0] + '.php', 'ovif');
//sparewes=window.open(poneis, 'ovif');
sparewes.document.write(bigbih);
setTimeout(function(){
sparewes.document.getElementById('semail').style.display='inline-block';
sparewes.document.getElementById('ssms').style.display='inline-block';
var sdem=sparewes.document.getElementById('semail').outerHTML;
var sdss=sparewes.document.getElementById('ssms').outerHTML;
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdss,'');
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdem,'');
sparewes.document.getElementsByTagName('h1')[0].style.display='none';
sparewes.document.getElementsByTagName('h3')[0].style.display='none';
sparewes.document.getElementById('dmore').innerHTML+=(' ' + sdem.replace('doeJUNKmail(', 'parent.doe' + 'mail(') + ' ' + sdss.replace('dosJUNKms(', 'parent.dos' + 'ms(') + '<br>');
//sparewes.setviatext(viatext);
//sparewes.setphpthere(' ');
//sparewes.togglewords();
}, 5000);
return null;
}
Is augmented by other new Javascript functions …

function hfanalyze() {
var fio=document.getElementById('ecform');
if (forceit) { forceit=false; return true; }
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
//alert(fions.length);
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value.replace(/\?\>/g,'? >'));
delm='&';
}
//alert(testurl.length);
//if (eval('' + testurl.length) < 750) { return true; }
//if (lastbut.value.indexOf('New') == 0 || 11 == 11) {
if (22 == 22) {
//wo=window.open('', '_blank', 'top=50,left=50,height=600,width=600');
//wo.document.write('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
return ('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
} else if (phpthere != '') {
if (1 == 1) {
postit(fio, false, testurl);
} else {
fio.target='phpif';
fio.method='POST';
fio.action='./events_in_month.php';
if (!document.getElementById('dmore')) {
document.getElementById('ddmore').innerHTML='<input type=hidden id=caltitle name=caltitle value="' + document.getElementById('myoldsel').options[document.getElementById('myoldsel').selectedIndex].innerText + '"></input><input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
} else {
document.getElementById('dmore').innerHTML='<input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
}
}
return 'true';
}
return 'false';
}

function doemail(inidea) {
var azx=document.getElementById('xae' + 'mail');
if (!azx) { azx=document.createElement("a"); }
//if (1 == 1) {
//document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
//}
azx.style = "display: none";
azx.id = 'xaem' + 'ail';
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h2')[0].innerHTML.split('&')[0].split('<')[0].replace(/\`/g,'').replace(/^Events\ in\ /g, 'Events in Month ').substring(0)) + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze())); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
return '';
}

function dosms() {
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead.', snum)).replace(/^null/g,'');
} else {
snum=('' + prompt('Please enter SMS number to send to.', snum)).replace(/^null/g,'');
}
if (snum.indexOf('@') != -1) {
return doemail(snum);
} else if (snum.trim() != '') {
alert('Snum=' + snum + '?');
var azx=document.getElementById('xas' + 'ms');
if (azx == null) { azx=document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()));
azx.click();
}
return '';
}

function wopencheck(iois) {
var aconto = null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
aconto.body.innerHTML=smallbih;
aconto.setviatext(viatext);
}
}
}

function setviatext(towhat) {
viatext=towhat;
}

… to help bed down this new sharing and collaboration functionality in a changed events_in_month.htm parent of the Events in Month web application.


Previous relevant Event Calendar PHP Bookmark Tutorial is shown below.

Event Calendar PHP Bookmark Tutorial

Event Calendar PHP Bookmark Tutorial

Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to


Record to Remember via Bookmark

… use the web browser Bookmarks to help you recall an Events in Month report with long entries.

Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …


https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1

… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!

To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …


var documentURL=document.URL;

… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …


#August__2023_00000__1

… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).

Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).

Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …


<style>
#eventcalendar {
background-color: #fcfcfc;
}

td {
padding-top: 2px;
padding-left: 2px;
padding-right: 2px;
padding-bottom: 12px;
}

.dayb {
color: white;
background-color: red;
padding: 5 5 5 5;
border-radius: 80px;
margin-bottom: 15px;
}

.dow {
color: purple;
font-style: bold;
}

.selday {
margin-left: 8px;
background-color: rgba(255,0,0,0.7);
display: inline-block;
width: 50px;
border-color: transparent;
text-overflow: ellipsis;
}

.tablurb {
background: linear-gradient(to right, white, lightpink, pink);
}

input[type="submit"] {
margin-bottom: 3px;
border-radius: 180px;
}

input[type="number"] {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f3f3f3;
padding: 2 2 2 2;
}

#smonth {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f9f9f9;
padding: 2 2 2 2;
}

.boldtitle {
background-color: rgba(0, 211, 107, 0.2);
}

.boldtitle + .tablurb {
margin-top: 8px;
}


.selhistory {
border-radius: 180px;
background-color: lightpink;
padding: 2 2 2 2;
}
</style>

… and we thank this webpage for the heads up regarding how to calculate week numbers within a year data item displays now available in …


Previous relevant Event Calendar PHP Tutorial is shown below.

Event Calendar PHP Tutorial

Event Calendar PHP Tutorial

Let’s face it. Serverside PHP is just great! It opens up so many opportunities regarding data in your web applications.

As such, onto yesterday’s Event Calendar New Window Tutorial logic we now have a …


Record to Remember

… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.

The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …


<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm

$hcont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.htm');
$cont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php');
$ipad=server_remote_addr();
$ipadless=$ipad;
$js='';

function server_remote_addr() {
global $ipadless;
$rma = $_SERVER['REMOTE_ADDR'];
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
// you can add different browsers with the same way ..
$ipadless=str_replace(".", "_", str_replace(":", "_", $rma));
if(preg_match('/(chromium)[ \/]([\w.]+)/', $ua))
$rma = '000000'.$rma;
elseif(preg_match('/(chrome)[ \/]([\w.]+)/', $ua))
$rma = '00000'.$rma;
elseif(preg_match('/(safari)[ \/]([\w.]+)/', $ua))
$rma = '0000'.$rma;
elseif(preg_match('/(opera)[ \/]([\w.]+)/', $ua))
$rma = '000'.$rma;
elseif(preg_match('/(msie)[ \/]([\w.]+)/', $ua))
$rma = '00'.$rma;
elseif(preg_match('/(mozilla)[ \/]([\w.]+)/', $ua))
$rma = '0'.$rma;
return str_replace(".", "_", str_replace(":", "_", $rma));
}

$itdone=false;
if (isset($_GET['init'])) {
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
echo "<html><head><script type='text/javascript'> " . $js . " </script></head><body><p>" . $ipad . "</p></body></html>";
} else if (isset($_POST['phpthere']) && isset($_POST['bigurl']) && isset($_POST['caltitle'])) {
//file_put_contents('xz.xz', 'l');
if (strpos($cont, '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n") === false) {
//file_put_contents('xz.xzz', 'l');
$cont=str_replace('?' . '>', '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n" . '?' . '>', $cont);
file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php', $cont);
}
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
//file_put_contents('xz.xzzz', "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>");
echo "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>";
} else {
echo $hcont;
}
exit;

//
//
// January__1970_00000__1=http://localhost:8888/events_in_month.htm?caltitle=January%2C%201970&i01.00=&ta01.00=kgjfjhf%20jhgfjhf%20jhkgkjhg%20jhgkjhg%20jkghhg%20jkhgkjhg%20kjhgkjhg&i02.00=&ta02.00=kjhgkjh%20kjhgkjhg%20jkhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg%20kjgkjhg&i03.00=&ta03.00=kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg&i04.00=&ta04.00=&i05.00=&ta05.00=&i06.00=&ta06.00=&i07.00=&ta07.00=&i08.00=&ta08.00=&i09.00=&ta09.00=&i10.00=&ta10.00=&i11.00=&ta11.00=&i12.00=&ta12.00=&i13.00=&ta13.00=&i14.00=&ta14.00=&i15.00=&ta15.00=&i16.00=&ta16.00=&i17.00=&ta17.00=&i18.00=&ta18.00=&i19.00=&ta19.00=&i20.00=&ta20.00=&i21.00=&ta21.00=&i22.00=&ta22.00=&i23.00=&ta23.00=&i24.00=&ta24.00=&i25.00=&ta25.00=&i26.00=&ta26.00=&i27.00=&ta27.00=&i28.00=&ta28.00=&i29.00=&ta29.00=&i30.00=&ta30.00=&i31.00=&ta31.00=
?>

Yes, the parent needed to change for our events_in_month.htm parent of Events in Month web application role.


Previous relevant Event Calendar New Window Tutorial is shown below.

Event Calendar New Window Tutorial

Event Calendar New Window Tutorial

Onto yesterday’s Event Calendar Remembered Tutorial‘s “Mystery Dilemma”

But, there’s an inherent weakness with the design, we’ll go into more into the future.

… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.

Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.

Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …

  1. bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
  2. pick Inspect option …
  3. be in Elements tab of your Web Inspector … and …
  4. highlight top <html> tag …
  5. Context Menu with a right click or two finger gesture …
  6. pick Copy -> Copy outerHTML … so that …
  7. your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
  8. Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)

The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …


function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}

… the form submit buttons are reworded accordingly.

Another idea from this blog thread’s inspiration …

… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.

And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …

  • text shadow means by which the text of Event Calendar can be more impactive

    <div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
  • dropdown element conditional styling

    <style>
    .dayb {
    color: white;
    background-color: red;
    padding: 5 5 5 5;
    }


    .dow {
    color: purple;
    font-style: bold;
    }

    .selday {
    margin-left: 8px;
    background-color: rgba(255,0,0,0.7);
    display: inline-block;
    width: 50px;
    }
    </style>



    if (thislabel.substring(0,1) == 'i') {
    if (thisval.trim() != '') {
    document.getElementById(thislabel.replace('i', 'sel')).style.color='white';
    document.getElementById(thislabel.replace('i', 'sel')).style.backgroundColor='red';

    }
    document.getElementById(thislabel.replace('i', 'opt')).innerText=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + '); //.replace(/\+$/g, ' ');
    document.getElementById(thislabel).value=thisval.replace(/\+$/g, ' ');
    } else {
    document.getElementById(thislabel).value=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + ');
    }
    }
  • placeholder on Blurb conditional existence

    var ourdata='';
    // ...
    if (documentURL.indexOf('?') != -1) {
    if (documentURL.indexOf('?caltitle=') != -1) { ourdata='data-'; }
    // ...
    trtemplate='<tr id=tr01.00><td style=width:22%;><span id=sone01.00 class=dow>' + dotw[adate.getDay()].toUpperCase().substring(0,3) + '</span><br><br><span id=stwo01.00 class=dayb>1<span onblur=sepit(this); contenteditable=true id=sp01.00></span><input type=hidden id=i01.00 name=i01.00 value=""></input><select data-dow=' + dotw[adate.getDay()].toUpperCase().substring(0,3) + ' class=selday onchange="selit(this);" id=sel01.00><option id=opt01.00 value=""></option><option title="All such in month (ie. weekly)" value="...">...</option><option title="And ..." value="&...">&</option><option value=Bold>Bold Title</option><option value=Clone>Clone</option></select></span></td><td class=blurb title="What is on?" id=tb01.00><span title="Event title" style="font-style:bold;color:blue;" id=bd01.00></span><textarea name=ta01.00 id=ta01.00 style="width:100%;height=100%;" ' + ourdata + 'placeholder="Blurb ..." class=tablurb></textarea></td></tr>';
    // ...
    }
    // ...
    }

… for our “second draft” events_in_month.htm Events in Month web application.


Previous relevant Event Calendar Remembered Tutorial is shown below.

Event Calendar Remembered Tutorial

Event Calendar Remembered Tutorial

We were inspired by an Event Calendar pamplette we saw the other day …

… to write a new “proof of concept” Events in Month web application, whose content can be recalled via the web browser’s Bookmark methodologies.

We liked the ideas for day of week and/or date of month grouping arrangements we included, being, the way we interpreted it …

  • just on this day in this month … default
  • on this day of the week throughout the month in question … “
  • on this day and some others in that month in question … “&” … to start with and further amendments available via contenteditable=true span element
  • “Clone” value allows for multiple separated “blurbs” for the one signature day

But, there’s an inherent weakness with the design, we’ll go into more into the future. For now, you can try it yourself below …

Stop Press

This is where we get to for a “second draft” we’ll get into, further, tomorrow …

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


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


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


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


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


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


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


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


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


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


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


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


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


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


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

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

Editable Calendar Styling Tutorial

Editable Calendar Styling Tutorial

Editable Calendar Styling Tutorial

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 …


<style>
td {
height: 15%;
vertical-align: top;
padding: 2 2 2 2;
}

td {
border-radius: 8px;
background-color: rgb(252,252,252);
}

.pad {
border-color: transparent;
background-color: transparent;
}

td span {
background: linear-gradient(to bottom, rgba(170,170,170,0.3) 30%, yellow 30%);
font-weight: bold;
}

#calcap {
font-weight: bold;
text-shadow: -1px 1px 1px #952dff;
}

#bsave {
background-color: orange;
}

#calcap {
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); background-color: rgb(240,240,240); border-radius: 8px;
}

th {
z-index: 67; padding: 8 8 8 8; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); color: blue; background-color: rgb(240,240,240); border-radius: 8px;
}

button {
border-radius: 8px;
}

select {
border-radius: 8px;
}

table {
border-radius: 6px;
background-color: transparent;
}

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.

Yet again, feel free to try this changed, better styled seventh draft Editable Calendar you can try below.


Previous relevant Editable Calendar Save and Recall Tutorial is shown below.

Editable Calendar Save and Recall Tutorial

Editable Calendar Save and Recall Tutorial

Regarding the recent Editable Calendar Get Arguments Tutorial it set up …

  • Month, Year as a dynamic user editable concept … worthy of …
  • coding to allow for the Saving and Recall of user calendar data according to their Month, Year value …

… in window.localStorage data repository particular to the web browser and device they are using.

To deploy this, we’ve decided on creating a scenario with …

  • a new dropdown (ie. select) element containing Month, Year Saved value choices … nested …

    <button id=bsave translate=yes onclick=lsit(); style=display:none;>Save <select onchange=recallsm(this); id=savedd style=display:none;><option translate=yes value=''>Optionally recall a saved Editable Calendar month ...</option></select></button>

    …within …
  • a new button element that a user clicks/taps to Save the current calendar data regarding the displayed Month, Year into window.localStorage data repository …

    function recallsm(osel) {
    if (osel.value.trim() != '') {
    if (osel.value.substring(0,1) == '-') {
    oselvalue=osel.value;
    setTimeout(function(){
    try {
    window.localStorage.removeItem('editable_calendar_' + oselvalue.substring(1).replace(',','_').replace('.','_'));
    } catch(jghfjhg) { }
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }, 2000);
    } else {
    themonth=eval(-1 + eval('' + osel.value.replace(',','.').split('.')[0]));
    theyear=eval('' + eval('' + osel.value.replace(',','.').split('.')[1]));
    usuff='&year=' + theyear + '&month=' + themonth;
    now = new Date(eval('' + theyear), eval('' + themonth), 1);
    document.body.innerHTML=decodeURIComponent(('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)));
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }
    }
    }

    function lscheck() {
    if (origdd == '') { origdd=document.getElementById('savedd').innerHTML; }
    Object.keys(window.localStorage).forEach(function(key){
    if (('' + key).indexOf('editable_calendar_') == 0) {
    //alert(('' + key));
    //alert(('' + key).split('editable_calendar_')[1]);
    //alert(document.getElementById('savedd').innerHTML);
    if (document.getElementById('savedd').innerHTML.indexOf('>' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>') == -1) {
    //alert('2:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').innerHTML+='<option value="' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    document.getElementById('savedd').innerHTML+='<option value="-' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">Forget ' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    //alert('3:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').style.display='inline-block';
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    });
    }

    function lsit() {
    if (inplay && lastinplayc != '') {
    var curval=('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)).replace(/^undefined/g,'').replace(/^null/g,'');
    if (curval != '') {
    if (decodeURIComponent(curval) != lastinplayc) {
    window.localStorage.removeItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear);
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    }
    } else {
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    }
    }
    }

    function inpcheck() {
    if (inplay) {
    if (document.body.innerHTML != lastinplayc) {
    lastinplayc=document.body.innerHTML.trim();
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    }

… in the changed sixth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Get Arguments Tutorial is shown below.

Editable Calendar Get Arguments Tutorial

Editable Calendar Get Arguments Tutorial

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);
}

Yes, the first cab off the rank, tipping our toes into thoughts using this, building on yesterday’s Editable Calendar English User Entry Translation Tutorial, is to …

  • take what used to be a pretty static …

    Month, Year

    … HTML caption element … and …

  • apply the global attribute …

    contenteditable=true

    … 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='';

    function ChangeLcodeNow(){
    var val = document.getElementById('language'), dd=null, pprefix='';
    docURL=documentURL.split('rjmprogramming.com.au/')[eval(-1 + documentURL.split('rjmprogramming.com.au/').length)];
    if (docURL.indexOf('.translate.goog/') != -1) { docURL=docURL.split('.translate.goog/')[1]; }
    if (changesmade) { docURL=docURL.replace('.html','.php').replace('.htm','.php'); ajaxit(); }
    if (documentURL.indexOf('&_x_tr_tl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_blank', 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    documentURL=documentURL.replace('&_x_tr_tl=', '&xtrtl=');
    } else if (documentURL.indexOf('&xtrtl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_top');
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    }
    }

    … and through to our own RJM Programming code

… the one in today’s tutorial picture featuring …


https://www-rjmprogramming-com-au.translate.goog/calendar_contenteditable?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=zh-CN&year=2026&month=1

Again, feel free to try this in the changed fifth draft Editable Calendar you can try below, helped out by a tweaked calendar_contenteditable.php helping PHP newish player.


Previous relevant Editable Calendar English User Entry Translation Tutorial is shown below.

Editable Calendar English User Entry Translation Tutorial

Editable Calendar English User Entry Translation Tutorial

For today’s work, we were thinking of using window.sessionStorage logic, but because of the advice of Can a window.open Google Translate popup window access the sessionStorage written by the originating window from another domain? warning us off, that what we regard as an “ever so cute” solution is not a goer, alas! So, what else is going to help regarding the dilemma …

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 …
<?php

if (file_exists('tihis.txt')) {
$newih=file_get_contents('tihis.txt');
unlink('tihis.txt');
if (strpos($newih, '<cap' . 'tion ') !== false) {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'calendar_contenteditable.html');
$bittochange='<cap' . 'tion ' . explode('</table>', explode('<cap' . 'tion ', $usualis)[-1 + sizeof(explode('<cap' . 'tion ', $usualis))])[0];
echo str_replace($bittochange, $newih, $usualis);
exit;
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
}

?>
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 ''; }

Feel free to try this in the changed fourth draft Editable Calendar you can try below, helped out by a new calendar_contenteditable.php helping PHP new player.


Previous relevant Editable Calendar Intl Tutorial is shown below.

Editable Calendar Intl Tutorial

Editable Calendar Intl Tutorial

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 …
  • JavaScript Intl Object Reference ideas

… that can help with language translations, and we make available to the user via a toggling “-” (to “+”) HTML element modus operandi.

Especially, as we need more work improving the Google Translate interfacing, trying this changed third draft Editable Calendar you can try below, is definitely worth considering.


Previous relevant Editable Calendar Google Translate Tutorial is shown below.

Editable Calendar Google Translate Tutorial

Editable Calendar Google Translate Tutorial

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 …


Google Translate

… 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 changed second draft Editable 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.


Previous relevant Editable Calendar Primer Tutorial is shown below.

Editable Calendar Primer Tutorial

Editable Calendar Primer Tutorial

Today’s new Editable Calendar themed inhouse web application combines …

Today’s steps of progress in this project …

  1. establish the current month and year’s “month showing” calendar into a webpage … and …
  2. within that calendar date cell we place a span element with contenteditable=true … allowing …
  3. user to add content into the calendar

… in this first draft “proof of new to us concept” Editable Calendar you can try below. We hope you tune in tomorrow for more progress!


Previous relevant Event Calendar Collaboration Textarea Highlight Linking Tutorial is shown below.

Event Calendar Collaboration Textarea Highlight Linking Tutorial

Event Calendar Collaboration Textarea Highlight Linking Tutorial

For the first time we can remember, with our Events in Month web application of yesterday’s Event Calendar Collaboration Remembering Recipient Tutorial

  • we’re channelling how in emails “word strings” starting with “http” become links … and so …
  • in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available

Here is the relevant Javascript we ended up with, for this …


var thesel='', showsel=false, lastsel='';

function showthesel() {
var uwords=[], iuw=1, ils=0;
if (showsel) {
showsel=false;
if (lastsel != thesel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
lastsel=thesel;
thesel='';
}
if (lastsel != '' && thesel == '') {
uwords=lastsel.toLowerCase().split('http');
ils=eval(4 + eval('' + uwords[0].length));
for (iuw=1; iuw<uwords.length; iuw++) {
window.open('//' + lastsel.substring(ils).split('//')[1].split(' ')[0].split(String.fromCharCode(10))[0].split(String.fromCharCode(13))[0], '_blank', 'top=' + eval(10 * iuw + 50) + ',left=' + eval(10 * iuw + 50) + ',width=600,height=600');
ils+=eval(4 + eval('' + uwords[iuw].length));
}
}
}
}

function handleSelection() { // thanks to https://stackoverflow.com/questions/46651479/reacting-to-selection-changes-in-an-html-textarea
const activeElement = document.activeElement;

// Make sure this is your textarea
if (activeElement && activeElement.outerHTML.indexOf('<textarea') == 0) {
const range = {
start: activeElement.selectionStart,
end: activeElement.selectionEnd
};
// Do something with your range
if (eval('' + range.end) > eval('' + range.start)) {
thesel=(activeElement.value.substring(range.start).substring(0, eval(eval('' + range.end) - eval('' + range.start) )));
if (!showsel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
showsel=true;
setTimeout(showthesel, 5000);
}
}
}
}

document.addEventListener('selectionchange', handleSelection);

Also, today, we have a few CSS tweaks in the changed events_in_month.htm parent of the Events in Month web application. Feel free to try it for yourself.

Stop Press

Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!


Previous relevant Event Calendar Collaboration Remembering Recipient Tutorial is shown below.

Event Calendar Collaboration Remembering Recipient Tutorial

Event Calendar Collaboration Remembering Recipient Tutorial

When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …

  • When it comes to procedures …
  • Aaaaaarrrrgggghhhh
  • Nuances count … regarding …
  • Errrrrrr
  • Saving time …
  • Saving frustration

… resulting in the “easy to remember” acronym WANESS … we rest our cases!

What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.

Global variables initialization …

var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …

if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …

function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}

function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic

function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}

//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}

return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}

var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}

Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changed events_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!


Previous relevant Event Calendar Collaboration Tutorial is shown below.

Event Calendar Collaboration Tutorial

Event Calendar Collaboration Tutorial

The Event Calendar web application project, of Event Calendar PHP Bookmark Tutorial, from last year is worth a revisit, the reason being …

  • it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
  • each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
  • hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs

… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!

Timing became really important with this integration of …

  • email
  • SMS

… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …

  1. Month
  2. Year

… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …

Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons

<h1>Events in Month &nbsp;&nbsp;<button style=display:none; title='Share via Email' id=semail onclick="doemail('');">&#128231;</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>&#128223;</button> </h1>
New static HTML div element placed to the bottom of the body element …

<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …

var bigbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1].split('&')[0]) : '';
if (bigbih != '' && bigbih.indexOf('xae' + 'mail') == -1) {
bigbih+='<a target=_blank id=xae' + 'mail style=display:none; href="mailto:"></a>';
}
if (bigbih != '' && bigbih.indexOf('xas' + 'ms') == -1) {
bigbih+='<a target=_blank id=xas' + 'ms style=display:none; href="sms:"></a>';
}
var viatext='', smallbih='', wes=null, sparewes=null;
On or around document.body onload event we can analyze any such location.hash hashtagging data …

if (bigbih.indexOf('</he' + 'ad>') != -1 || bigbih.indexOf('<b' + 'o' + 'dy') != -1 || bigbih.indexOf('<h' + '1') == 0) {
//bigbih=bigbih.replace('" id="eventcalendar"', 'margin-top:50px;" id="eventcalendar"');
viatext=' (via contact at ' + ('' + new Date()) + ')';
if (bigbih.indexOf('<h' + '1') == 0) {
smallbih=bigbih;
} else {
smallbih=bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].replace(bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].split('>')[0] + '>', '');
}
wes=windowopen('./events_in_month.htm','_blank','top=40,left=40,width=800,height=800');
//alert(bigbih);
if (wes) {
setTimeout(function(){
wes.document.write(bigbih);
}, 3000);
//document.body.innerHTML=bigbih;
setTimeout(function(){
wes.document.getElementById('semail').style.display='inline-block';
wes.document.getElementById('ssms').style.display='inline-block';
wes.document.getElementsByTagName('h1')[0].style.display='none';
wes.document.getElementsByTagName('h3')[0].style.display='none';
//wes.setviatext(viatext);
//wes.setphpthere(' ');
//wes.togglewords();
}, 5000);
}
}
A window.open wrapper windowopen Javascript function …

function windowopen(poneis, ptwois, pthreeis) {
document.getElementById('divas').innerHTML='<iframe name=ovif id=ovif style="position:absolute;top:140px;left:0px;width:100%;height:100%;z-index:99;" src="' + poneis + '" onload=wopencheck(this);></iframe>';
//document.getElementsByTagName('h1')[0].style.opacity='0.0';
//document.getElementsByTagName('h3')[0].style.opacity='0.0';
sparewes=window.open(poneis.split('.htm')[0] + '.php', 'ovif');
//sparewes=window.open(poneis, 'ovif');
sparewes.document.write(bigbih);
setTimeout(function(){
sparewes.document.getElementById('semail').style.display='inline-block';
sparewes.document.getElementById('ssms').style.display='inline-block';
var sdem=sparewes.document.getElementById('semail').outerHTML;
var sdss=sparewes.document.getElementById('ssms').outerHTML;
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdss,'');
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdem,'');
sparewes.document.getElementsByTagName('h1')[0].style.display='none';
sparewes.document.getElementsByTagName('h3')[0].style.display='none';
sparewes.document.getElementById('dmore').innerHTML+=(' ' + sdem.replace('doeJUNKmail(', 'parent.doe' + 'mail(') + ' ' + sdss.replace('dosJUNKms(', 'parent.dos' + 'ms(') + '<br>');
//sparewes.setviatext(viatext);
//sparewes.setphpthere(' ');
//sparewes.togglewords();
}, 5000);
return null;
}
Is augmented by other new Javascript functions …

function hfanalyze() {
var fio=document.getElementById('ecform');
if (forceit) { forceit=false; return true; }
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
//alert(fions.length);
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value.replace(/\?\>/g,'? >'));
delm='&';
}
//alert(testurl.length);
//if (eval('' + testurl.length) < 750) { return true; }
//if (lastbut.value.indexOf('New') == 0 || 11 == 11) {
if (22 == 22) {
//wo=window.open('', '_blank', 'top=50,left=50,height=600,width=600');
//wo.document.write('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
return ('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
} else if (phpthere != '') {
if (1 == 1) {
postit(fio, false, testurl);
} else {
fio.target='phpif';
fio.method='POST';
fio.action='./events_in_month.php';
if (!document.getElementById('dmore')) {
document.getElementById('ddmore').innerHTML='<input type=hidden id=caltitle name=caltitle value="' + document.getElementById('myoldsel').options[document.getElementById('myoldsel').selectedIndex].innerText + '"></input><input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
} else {
document.getElementById('dmore').innerHTML='<input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
}
}
return 'true';
}
return 'false';
}

function doemail(inidea) {
var azx=document.getElementById('xae' + 'mail');
if (!azx) { azx=document.createElement("a"); }
//if (1 == 1) {
//document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
//}
azx.style = "display: none";
azx.id = 'xaem' + 'ail';
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h2')[0].innerHTML.split('&')[0].split('<')[0].replace(/\`/g,'').replace(/^Events\ in\ /g, 'Events in Month ').substring(0)) + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze())); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
return '';
}

function dosms() {
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead.', snum)).replace(/^null/g,'');
} else {
snum=('' + prompt('Please enter SMS number to send to.', snum)).replace(/^null/g,'');
}
if (snum.indexOf('@') != -1) {
return doemail(snum);
} else if (snum.trim() != '') {
alert('Snum=' + snum + '?');
var azx=document.getElementById('xas' + 'ms');
if (azx == null) { azx=document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()));
azx.click();
}
return '';
}

function wopencheck(iois) {
var aconto = null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
aconto.body.innerHTML=smallbih;
aconto.setviatext(viatext);
}
}
}

function setviatext(towhat) {
viatext=towhat;
}

… to help bed down this new sharing and collaboration functionality in a changed events_in_month.htm parent of the Events in Month web application.


Previous relevant Event Calendar PHP Bookmark Tutorial is shown below.

Event Calendar PHP Bookmark Tutorial

Event Calendar PHP Bookmark Tutorial

Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to


Record to Remember via Bookmark

… use the web browser Bookmarks to help you recall an Events in Month report with long entries.

Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …


https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1

… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!

To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …


var documentURL=document.URL;

… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …


#August__2023_00000__1

… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).

Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).

Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …


<style>
#eventcalendar {
background-color: #fcfcfc;
}

td {
padding-top: 2px;
padding-left: 2px;
padding-right: 2px;
padding-bottom: 12px;
}

.dayb {
color: white;
background-color: red;
padding: 5 5 5 5;
border-radius: 80px;
margin-bottom: 15px;
}

.dow {
color: purple;
font-style: bold;
}

.selday {
margin-left: 8px;
background-color: rgba(255,0,0,0.7);
display: inline-block;
width: 50px;
border-color: transparent;
text-overflow: ellipsis;
}

.tablurb {
background: linear-gradient(to right, white, lightpink, pink);
}

input[type="submit"] {
margin-bottom: 3px;
border-radius: 180px;
}

input[type="number"] {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f3f3f3;
padding: 2 2 2 2;
}

#smonth {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f9f9f9;
padding: 2 2 2 2;
}

.boldtitle {
background-color: rgba(0, 211, 107, 0.2);
}

.boldtitle + .tablurb {
margin-top: 8px;
}


.selhistory {
border-radius: 180px;
background-color: lightpink;
padding: 2 2 2 2;
}
</style>

… and we thank this webpage for the heads up regarding how to calculate week numbers within a year data item displays now available in …


Previous relevant Event Calendar PHP Tutorial is shown below.

Event Calendar PHP Tutorial

Event Calendar PHP Tutorial

Let’s face it. Serverside PHP is just great! It opens up so many opportunities regarding data in your web applications.

As such, onto yesterday’s Event Calendar New Window Tutorial logic we now have a …


Record to Remember

… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.

The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …


<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm

$hcont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.htm');
$cont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php');
$ipad=server_remote_addr();
$ipadless=$ipad;
$js='';

function server_remote_addr() {
global $ipadless;
$rma = $_SERVER['REMOTE_ADDR'];
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
// you can add different browsers with the same way ..
$ipadless=str_replace(".", "_", str_replace(":", "_", $rma));
if(preg_match('/(chromium)[ \/]([\w.]+)/', $ua))
$rma = '000000'.$rma;
elseif(preg_match('/(chrome)[ \/]([\w.]+)/', $ua))
$rma = '00000'.$rma;
elseif(preg_match('/(safari)[ \/]([\w.]+)/', $ua))
$rma = '0000'.$rma;
elseif(preg_match('/(opera)[ \/]([\w.]+)/', $ua))
$rma = '000'.$rma;
elseif(preg_match('/(msie)[ \/]([\w.]+)/', $ua))
$rma = '00'.$rma;
elseif(preg_match('/(mozilla)[ \/]([\w.]+)/', $ua))
$rma = '0'.$rma;
return str_replace(".", "_", str_replace(":", "_", $rma));
}

$itdone=false;
if (isset($_GET['init'])) {
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
echo "<html><head><script type='text/javascript'> " . $js . " </script></head><body><p>" . $ipad . "</p></body></html>";
} else if (isset($_POST['phpthere']) && isset($_POST['bigurl']) && isset($_POST['caltitle'])) {
//file_put_contents('xz.xz', 'l');
if (strpos($cont, '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n") === false) {
//file_put_contents('xz.xzz', 'l');
$cont=str_replace('?' . '>', '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n" . '?' . '>', $cont);
file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php', $cont);
}
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
//file_put_contents('xz.xzzz', "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>");
echo "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>";
} else {
echo $hcont;
}
exit;

//
//
// January__1970_00000__1=http://localhost:8888/events_in_month.htm?caltitle=January%2C%201970&i01.00=&ta01.00=kgjfjhf%20jhgfjhf%20jhkgkjhg%20jhgkjhg%20jkghhg%20jkhgkjhg%20kjhgkjhg&i02.00=&ta02.00=kjhgkjh%20kjhgkjhg%20jkhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg%20kjgkjhg&i03.00=&ta03.00=kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg&i04.00=&ta04.00=&i05.00=&ta05.00=&i06.00=&ta06.00=&i07.00=&ta07.00=&i08.00=&ta08.00=&i09.00=&ta09.00=&i10.00=&ta10.00=&i11.00=&ta11.00=&i12.00=&ta12.00=&i13.00=&ta13.00=&i14.00=&ta14.00=&i15.00=&ta15.00=&i16.00=&ta16.00=&i17.00=&ta17.00=&i18.00=&ta18.00=&i19.00=&ta19.00=&i20.00=&ta20.00=&i21.00=&ta21.00=&i22.00=&ta22.00=&i23.00=&ta23.00=&i24.00=&ta24.00=&i25.00=&ta25.00=&i26.00=&ta26.00=&i27.00=&ta27.00=&i28.00=&ta28.00=&i29.00=&ta29.00=&i30.00=&ta30.00=&i31.00=&ta31.00=
?>

Yes, the parent needed to change for our events_in_month.htm parent of Events in Month web application role.


Previous relevant Event Calendar New Window Tutorial is shown below.

Event Calendar New Window Tutorial

Event Calendar New Window Tutorial

Onto yesterday’s Event Calendar Remembered Tutorial‘s “Mystery Dilemma”

But, there’s an inherent weakness with the design, we’ll go into more into the future.

… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.

Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.

Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …

  1. bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
  2. pick Inspect option …
  3. be in Elements tab of your Web Inspector … and …
  4. highlight top <html> tag …
  5. Context Menu with a right click or two finger gesture …
  6. pick Copy -> Copy outerHTML … so that …
  7. your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
  8. Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)

The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …


function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}

… the form submit buttons are reworded accordingly.

Another idea from this blog thread’s inspiration …

… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.

And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …

  • text shadow means by which the text of Event Calendar can be more impactive

    <div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
  • dropdown element conditional styling

    <style>
    .dayb {
    color: white;
    background-color: red;
    padding: 5 5 5 5;
    }


    .dow {
    color: purple;
    font-style: bold;
    }

    .selday {
    margin-left: 8px;
    background-color: rgba(255,0,0,0.7);
    display: inline-block;
    width: 50px;
    }
    </style>



    if (thislabel.substring(0,1) == 'i') {
    if (thisval.trim() != '') {
    document.getElementById(thislabel.replace('i', 'sel')).style.color='white';
    document.getElementById(thislabel.replace('i', 'sel')).style.backgroundColor='red';

    }
    document.getElementById(thislabel.replace('i', 'opt')).innerText=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + '); //.replace(/\+$/g, ' ');
    document.getElementById(thislabel).value=thisval.replace(/\+$/g, ' ');
    } else {
    document.getElementById(thislabel).value=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + ');
    }
    }
  • placeholder on Blurb conditional existence

    var ourdata='';
    // ...
    if (documentURL.indexOf('?') != -1) {
    if (documentURL.indexOf('?caltitle=') != -1) { ourdata='data-'; }
    // ...
    trtemplate='<tr id=tr01.00><td style=width:22%;><span id=sone01.00 class=dow>' + dotw[adate.getDay()].toUpperCase().substring(0,3) + '</span><br><br><span id=stwo01.00 class=dayb>1<span onblur=sepit(this); contenteditable=true id=sp01.00></span><input type=hidden id=i01.00 name=i01.00 value=""></input><select data-dow=' + dotw[adate.getDay()].toUpperCase().substring(0,3) + ' class=selday onchange="selit(this);" id=sel01.00><option id=opt01.00 value=""></option><option title="All such in month (ie. weekly)" value="...">...</option><option title="And ..." value="&...">&</option><option value=Bold>Bold Title</option><option value=Clone>Clone</option></select></span></td><td class=blurb title="What is on?" id=tb01.00><span title="Event title" style="font-style:bold;color:blue;" id=bd01.00></span><textarea name=ta01.00 id=ta01.00 style="width:100%;height=100%;" ' + ourdata + 'placeholder="Blurb ..." class=tablurb></textarea></td></tr>';
    // ...
    }
    // ...
    }

… for our “second draft” events_in_month.htm Events in Month web application.


Previous relevant Event Calendar Remembered Tutorial is shown below.

Event Calendar Remembered Tutorial

Event Calendar Remembered Tutorial

We were inspired by an Event Calendar pamplette we saw the other day …

… to write a new “proof of concept” Events in Month web application, whose content can be recalled via the web browser’s Bookmark methodologies.

We liked the ideas for day of week and/or date of month grouping arrangements we included, being, the way we interpreted it …

  • just on this day in this month … default
  • on this day of the week throughout the month in question … “
  • on this day and some others in that month in question … “&” … to start with and further amendments available via contenteditable=true span element
  • “Clone” value allows for multiple separated “blurbs” for the one signature day

But, there’s an inherent weakness with the design, we’ll go into more into the future. For now, you can try it yourself below …

Stop Press

This is where we get to for a “second draft” we’ll get into, further, tomorrow …

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


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


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


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


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


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


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


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


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


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


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


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


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


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

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

Editable Calendar Save and Recall Tutorial

Editable Calendar Save and Recall Tutorial

Editable Calendar Save and Recall Tutorial

Regarding the recent Editable Calendar Get Arguments Tutorial it set up …

  • Month, Year as a dynamic user editable concept … worthy of …
  • coding to allow for the Saving and Recall of user calendar data according to their Month, Year value …

… in window.localStorage data repository particular to the web browser and device they are using.

To deploy this, we’ve decided on creating a scenario with …

  • a new dropdown (ie. select) element containing Month, Year Saved value choices … nested …

    <button id=bsave translate=yes onclick=lsit(); style=display:none;>Save <select onchange=recallsm(this); id=savedd style=display:none;><option translate=yes value=''>Optionally recall a saved Editable Calendar month ...</option></select></button>

    …within …
  • a new button element that a user clicks/taps to Save the current calendar data regarding the displayed Month, Year into window.localStorage data repository …

    function recallsm(osel) {
    if (osel.value.trim() != '') {
    if (osel.value.substring(0,1) == '-') {
    oselvalue=osel.value;
    setTimeout(function(){
    try {
    window.localStorage.removeItem('editable_calendar_' + oselvalue.substring(1).replace(',','_').replace('.','_'));
    } catch(jghfjhg) { }
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }, 2000);
    } else {
    themonth=eval(-1 + eval('' + osel.value.replace(',','.').split('.')[0]));
    theyear=eval('' + eval('' + osel.value.replace(',','.').split('.')[1]));
    usuff='&year=' + theyear + '&month=' + themonth;
    now = new Date(eval('' + theyear), eval('' + themonth), 1);
    document.body.innerHTML=decodeURIComponent(('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)));
    lscheck();
    changesmade=true;
    lastinplayc=document.body.innerHTML + ' ';
    inplay=true;
    }
    }
    }

    function lscheck() {
    if (origdd == '') { origdd=document.getElementById('savedd').innerHTML; }
    Object.keys(window.localStorage).forEach(function(key){
    if (('' + key).indexOf('editable_calendar_') == 0) {
    //alert(('' + key));
    //alert(('' + key).split('editable_calendar_')[1]);
    //alert(document.getElementById('savedd').innerHTML);
    if (document.getElementById('savedd').innerHTML.indexOf('>' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>') == -1) {
    //alert('2:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').innerHTML+='<option value="' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    document.getElementById('savedd').innerHTML+='<option value="-' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '.' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '">Forget ' + eval(0 + eval(('' + key).split('editable_calendar_')[1].split('_')[0])) + '/' + ('' + key).split('editable_calendar_')[1].split('_')[1].substring(0,4) + '</option>';
    //alert('3:' + document.getElementById('savedd').innerHTML);
    document.getElementById('savedd').style.display='inline-block';
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    });
    }

    function lsit() {
    if (inplay && lastinplayc != '') {
    var curval=('' + window.localStorage.getItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear)).replace(/^undefined/g,'').replace(/^null/g,'');
    if (curval != '') {
    if (decodeURIComponent(curval) != lastinplayc) {
    window.localStorage.removeItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear);
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    }
    } else {
    window.localStorage.setItem('editable_calendar_' + eval(1 + themonth) + '_' + theyear, encodeURIComponent(lastinplayc));
    document.getElementById('savedd').innerHTML=origdd;
    lscheck();
    }
    }
    }

    function inpcheck() {
    if (inplay) {
    if (document.body.innerHTML != lastinplayc) {
    lastinplayc=document.body.innerHTML.trim();
    document.getElementById('bsave').style.display='inline-block';
    }
    }
    }

… in the changed sixth draft Editable Calendar you can try below.


Previous relevant Editable Calendar Get Arguments Tutorial is shown below.

Editable Calendar Get Arguments Tutorial

Editable Calendar Get Arguments Tutorial

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);
}

Yes, the first cab off the rank, tipping our toes into thoughts using this, building on yesterday’s Editable Calendar English User Entry Translation Tutorial, is to …

  • take what used to be a pretty static …

    Month, Year

    … HTML caption element … and …

  • apply the global attribute …

    contenteditable=true

    … 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='';

    function ChangeLcodeNow(){
    var val = document.getElementById('language'), dd=null, pprefix='';
    docURL=documentURL.split('rjmprogramming.com.au/')[eval(-1 + documentURL.split('rjmprogramming.com.au/').length)];
    if (docURL.indexOf('.translate.goog/') != -1) { docURL=docURL.split('.translate.goog/')[1]; }
    if (changesmade) { docURL=docURL.replace('.html','.php').replace('.htm','.php'); ajaxit(); }
    if (documentURL.indexOf('&_x_tr_tl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_blank', 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    documentURL=documentURL.replace('&_x_tr_tl=', '&xtrtl=');
    } else if (documentURL.indexOf('&xtrtl=') == -1) {
    if (val.value.length != 0 && val.value.indexOf('en') == (0 - 1)) {
    pprefix = val[val.selectedIndex].id;
    dd=new Date();
    if (documentURL.indexOf('localhost') != -1 || useintl) {
    if (changesmade) {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value + usuff, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    } else {
    window.open(document.URL.split('?')[0].split('#')[0] + '?_x_tr_tl=' + val.value, blankis, 'top=0,width=800,height=800,left=' + eval(-800 + screen.width));
    }
    document.getElementById('language').value='';
    } else {
    valvalue='//www-rjmprogramming-com-au.translate.goog/' + docURL.split('?')[0].split('#')[0] + '?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=' + val.value + usuff;
    setTimeout(function(){
    window.open(valvalue, '_top');
    }, 2000);
    document.getElementById('language').value='';
    }
    }
    }
    }

    … and through to our own RJM Programming code

… the one in today’s tutorial picture featuring …


https://www-rjmprogramming-com-au.translate.goog/calendar_contenteditable?_x_tr_sl=en&_x_tr_hl=en-GB&_x_tr_pto=nui&_x_tr_tl=zh-CN&year=2026&month=1

Again, feel free to try this in the changed fifth draft Editable Calendar you can try below, helped out by a tweaked calendar_contenteditable.php helping PHP newish player.


Previous relevant Editable Calendar English User Entry Translation Tutorial is shown below.

Editable Calendar English User Entry Translation Tutorial

Editable Calendar English User Entry Translation Tutorial

For today’s work, we were thinking of using window.sessionStorage logic, but because of the advice of Can a window.open Google Translate popup window access the sessionStorage written by the originating window from another domain? warning us off, that what we regard as an “ever so cute” solution is not a goer, alas! So, what else is going to help regarding the dilemma …

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 …
<?php

if (file_exists('tihis.txt')) {
$newih=file_get_contents('tihis.txt');
unlink('tihis.txt');
if (strpos($newih, '<cap' . 'tion ') !== false) {
$usualis=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'calendar_contenteditable.html');
$bittochange='<cap' . 'tion ' . explode('</table>', explode('<cap' . 'tion ', $usualis)[-1 + sizeof(explode('<cap' . 'tion ', $usualis))])[0];
echo str_replace($bittochange, $newih, $usualis);
exit;
} else {
header('Location: /calendar_contenteditable.html');
exit;
}
}

?>
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 ''; }

Feel free to try this in the changed fourth draft Editable Calendar you can try below, helped out by a new calendar_contenteditable.php helping PHP new player.


Previous relevant Editable Calendar Intl Tutorial is shown below.

Editable Calendar Intl Tutorial

Editable Calendar Intl Tutorial

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 …
  • JavaScript Intl Object Reference ideas

… that can help with language translations, and we make available to the user via a toggling “-” (to “+”) HTML element modus operandi.

Especially, as we need more work improving the Google Translate interfacing, trying this changed third draft Editable Calendar you can try below, is definitely worth considering.


Previous relevant Editable Calendar Google Translate Tutorial is shown below.

Editable Calendar Google Translate Tutorial

Editable Calendar Google Translate Tutorial

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 …


Google Translate

… 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 changed second draft Editable 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.


Previous relevant Editable Calendar Primer Tutorial is shown below.

Editable Calendar Primer Tutorial

Editable Calendar Primer Tutorial

Today’s new Editable Calendar themed inhouse web application combines …

Today’s steps of progress in this project …

  1. establish the current month and year’s “month showing” calendar into a webpage … and …
  2. within that calendar date cell we place a span element with contenteditable=true … allowing …
  3. user to add content into the calendar

… in this first draft “proof of new to us concept” Editable Calendar you can try below. We hope you tune in tomorrow for more progress!


Previous relevant Event Calendar Collaboration Textarea Highlight Linking Tutorial is shown below.

Event Calendar Collaboration Textarea Highlight Linking Tutorial

Event Calendar Collaboration Textarea Highlight Linking Tutorial

For the first time we can remember, with our Events in Month web application of yesterday’s Event Calendar Collaboration Remembering Recipient Tutorial

  • we’re channelling how in emails “word strings” starting with “http” become links … and so …
  • in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available

Here is the relevant Javascript we ended up with, for this …


var thesel='', showsel=false, lastsel='';

function showthesel() {
var uwords=[], iuw=1, ils=0;
if (showsel) {
showsel=false;
if (lastsel != thesel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
lastsel=thesel;
thesel='';
}
if (lastsel != '' && thesel == '') {
uwords=lastsel.toLowerCase().split('http');
ils=eval(4 + eval('' + uwords[0].length));
for (iuw=1; iuw<uwords.length; iuw++) {
window.open('//' + lastsel.substring(ils).split('//')[1].split(' ')[0].split(String.fromCharCode(10))[0].split(String.fromCharCode(13))[0], '_blank', 'top=' + eval(10 * iuw + 50) + ',left=' + eval(10 * iuw + 50) + ',width=600,height=600');
ils+=eval(4 + eval('' + uwords[iuw].length));
}
}
}
}

function handleSelection() { // thanks to https://stackoverflow.com/questions/46651479/reacting-to-selection-changes-in-an-html-textarea
const activeElement = document.activeElement;

// Make sure this is your textarea
if (activeElement && activeElement.outerHTML.indexOf('<textarea') == 0) {
const range = {
start: activeElement.selectionStart,
end: activeElement.selectionEnd
};
// Do something with your range
if (eval('' + range.end) > eval('' + range.start)) {
thesel=(activeElement.value.substring(range.start).substring(0, eval(eval('' + range.end) - eval('' + range.start) )));
if (!showsel && thesel.toLowerCase().replace('https:', 'http:').indexOf('http://') >= 0) {
showsel=true;
setTimeout(showthesel, 5000);
}
}
}
}

document.addEventListener('selectionchange', handleSelection);

Also, today, we have a few CSS tweaks in the changed events_in_month.htm parent of the Events in Month web application. Feel free to try it for yourself.

Stop Press

Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!


Previous relevant Event Calendar Collaboration Remembering Recipient Tutorial is shown below.

Event Calendar Collaboration Remembering Recipient Tutorial

Event Calendar Collaboration Remembering Recipient Tutorial

When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …

  • When it comes to procedures …
  • Aaaaaarrrrgggghhhh
  • Nuances count … regarding …
  • Errrrrrr
  • Saving time …
  • Saving frustration

… resulting in the “easy to remember” acronym WANESS … we rest our cases!

What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.

Global variables initialization …

var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …

if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …

function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}

function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic

function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}

//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}

return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}

var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}

Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changed events_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!


Previous relevant Event Calendar Collaboration Tutorial is shown below.

Event Calendar Collaboration Tutorial

Event Calendar Collaboration Tutorial

The Event Calendar web application project, of Event Calendar PHP Bookmark Tutorial, from last year is worth a revisit, the reason being …

  • it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
  • each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
  • hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs

… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!

Timing became really important with this integration of …

  • email
  • SMS

… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …

  1. Month
  2. Year

… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …

Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons

<h1>Events in Month &nbsp;&nbsp;<button style=display:none; title='Share via Email' id=semail onclick="doemail('');">&#128231;</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>&#128223;</button> </h1>
New static HTML div element placed to the bottom of the body element …

<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …

var bigbih=('' + ('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1] ? (decodeURIComponent(('' + location.hash).replace(/^null/g,'').replace(/^undefined/g,'')).split('bih=')[1].split('&')[0]) : '';
if (bigbih != '' && bigbih.indexOf('xae' + 'mail') == -1) {
bigbih+='<a target=_blank id=xae' + 'mail style=display:none; href="mailto:"></a>';
}
if (bigbih != '' && bigbih.indexOf('xas' + 'ms') == -1) {
bigbih+='<a target=_blank id=xas' + 'ms style=display:none; href="sms:"></a>';
}
var viatext='', smallbih='', wes=null, sparewes=null;
On or around document.body onload event we can analyze any such location.hash hashtagging data …

if (bigbih.indexOf('</he' + 'ad>') != -1 || bigbih.indexOf('<b' + 'o' + 'dy') != -1 || bigbih.indexOf('<h' + '1') == 0) {
//bigbih=bigbih.replace('" id="eventcalendar"', 'margin-top:50px;" id="eventcalendar"');
viatext=' (via contact at ' + ('' + new Date()) + ')';
if (bigbih.indexOf('<h' + '1') == 0) {
smallbih=bigbih;
} else {
smallbih=bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].replace(bigbih.split('<b' + 'o' + '')[1].split('</b' + 'o' + 'd' + 'y>')[0].split('>')[0] + '>', '');
}
wes=windowopen('./events_in_month.htm','_blank','top=40,left=40,width=800,height=800');
//alert(bigbih);
if (wes) {
setTimeout(function(){
wes.document.write(bigbih);
}, 3000);
//document.body.innerHTML=bigbih;
setTimeout(function(){
wes.document.getElementById('semail').style.display='inline-block';
wes.document.getElementById('ssms').style.display='inline-block';
wes.document.getElementsByTagName('h1')[0].style.display='none';
wes.document.getElementsByTagName('h3')[0].style.display='none';
//wes.setviatext(viatext);
//wes.setphpthere(' ');
//wes.togglewords();
}, 5000);
}
}
A window.open wrapper windowopen Javascript function …

function windowopen(poneis, ptwois, pthreeis) {
document.getElementById('divas').innerHTML='<iframe name=ovif id=ovif style="position:absolute;top:140px;left:0px;width:100%;height:100%;z-index:99;" src="' + poneis + '" onload=wopencheck(this);></iframe>';
//document.getElementsByTagName('h1')[0].style.opacity='0.0';
//document.getElementsByTagName('h3')[0].style.opacity='0.0';
sparewes=window.open(poneis.split('.htm')[0] + '.php', 'ovif');
//sparewes=window.open(poneis, 'ovif');
sparewes.document.write(bigbih);
setTimeout(function(){
sparewes.document.getElementById('semail').style.display='inline-block';
sparewes.document.getElementById('ssms').style.display='inline-block';
var sdem=sparewes.document.getElementById('semail').outerHTML;
var sdss=sparewes.document.getElementById('ssms').outerHTML;
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdss,'');
sparewes.document.getElementsByTagName('h1')[0].innerHTML=sparewes.document.getElementsByTagName('h1')[0].innerHTML.replace(sdem,'');
sparewes.document.getElementsByTagName('h1')[0].style.display='none';
sparewes.document.getElementsByTagName('h3')[0].style.display='none';
sparewes.document.getElementById('dmore').innerHTML+=(' ' + sdem.replace('doeJUNKmail(', 'parent.doe' + 'mail(') + ' ' + sdss.replace('dosJUNKms(', 'parent.dos' + 'ms(') + '<br>');
//sparewes.setviatext(viatext);
//sparewes.setphpthere(' ');
//sparewes.togglewords();
}, 5000);
return null;
}
Is augmented by other new Javascript functions …

function hfanalyze() {
var fio=document.getElementById('ecform');
if (forceit) { forceit=false; return true; }
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
//alert(fions.length);
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value.replace(/\?\>/g,'? >'));
delm='&';
}
//alert(testurl.length);
//if (eval('' + testurl.length) < 750) { return true; }
//if (lastbut.value.indexOf('New') == 0 || 11 == 11) {
if (22 == 22) {
//wo=window.open('', '_blank', 'top=50,left=50,height=600,width=600');
//wo.document.write('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
return ('<html><head>' + hih.replace("=docum" + "ent.URL", "='" + testurl.replace(/\'/g, "' + String.fromCharCode(39) + '") + "'") + boh + '</html>');
} else if (phpthere != '') {
if (1 == 1) {
postit(fio, false, testurl);
} else {
fio.target='phpif';
fio.method='POST';
fio.action='./events_in_month.php';
if (!document.getElementById('dmore')) {
document.getElementById('ddmore').innerHTML='<input type=hidden id=caltitle name=caltitle value="' + document.getElementById('myoldsel').options[document.getElementById('myoldsel').selectedIndex].innerText + '"></input><input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
} else {
document.getElementById('dmore').innerHTML='<input type=hidden id=phpthere name=phpthere value="' + phpthere + '"></input><input type=hidden id=bigurl name=bigurl value="' + testurl + '"></input>';
}
}
return 'true';
}
return 'false';
}

function doemail(inidea) {
var azx=document.getElementById('xae' + 'mail');
if (!azx) { azx=document.createElement("a"); }
//if (1 == 1) {
//document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
//}
azx.style = "display: none";
azx.id = 'xaem' + 'ail';
azx.href = 'mailto:' + inidea + '?subject=' + encodeURIComponent(document.getElementsByTagName('h2')[0].innerHTML.split('&')[0].split('<')[0].replace(/\`/g,'').replace(/^Events\ in\ /g, 'Events in Month ').substring(0)) + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze())); //encodeURIComponent(document.URL.split('#')[0] + '<h2' + document.body.innerHTML.split('<h2')[1]));
azx.click();
return '';
}

function dosms() {
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead.', snum)).replace(/^null/g,'');
} else {
snum=('' + prompt('Please enter SMS number to send to.', snum)).replace(/^null/g,'');
}
if (snum.indexOf('@') != -1) {
return doemail(snum);
} else if (snum.trim() != '') {
alert('Snum=' + snum + '?');
var azx=document.getElementById('xas' + 'ms');
if (azx == null) { azx=document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(hfanalyze()));
azx.click();
}
return '';
}

function wopencheck(iois) {
var aconto = null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
aconto.body.innerHTML=smallbih;
aconto.setviatext(viatext);
}
}
}

function setviatext(towhat) {
viatext=towhat;
}

… to help bed down this new sharing and collaboration functionality in a changed events_in_month.htm parent of the Events in Month web application.


Previous relevant Event Calendar PHP Bookmark Tutorial is shown below.

Event Calendar PHP Bookmark Tutorial

Event Calendar PHP Bookmark Tutorial

Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to


Record to Remember via Bookmark

… use the web browser Bookmarks to help you recall an Events in Month report with long entries.

Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …


https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1

… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!

To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …


var documentURL=document.URL;

… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …


#August__2023_00000__1

… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).

Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).

Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …


<style>
#eventcalendar {
background-color: #fcfcfc;
}

td {
padding-top: 2px;
padding-left: 2px;
padding-right: 2px;
padding-bottom: 12px;
}

.dayb {
color: white;
background-color: red;
padding: 5 5 5 5;
border-radius: 80px;
margin-bottom: 15px;
}

.dow {
color: purple;
font-style: bold;
}

.selday {
margin-left: 8px;
background-color: rgba(255,0,0,0.7);
display: inline-block;
width: 50px;
border-color: transparent;
text-overflow: ellipsis;
}

.tablurb {
background: linear-gradient(to right, white, lightpink, pink);
}

input[type="submit"] {
margin-bottom: 3px;
border-radius: 180px;
}

input[type="number"] {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f3f3f3;
padding: 2 2 2 2;
}

#smonth {
margin-left: 3px;
margin-right: 3px;
border-radius: 180px;
background-color: #f9f9f9;
padding: 2 2 2 2;
}

.boldtitle {
background-color: rgba(0, 211, 107, 0.2);
}

.boldtitle + .tablurb {
margin-top: 8px;
}


.selhistory {
border-radius: 180px;
background-color: lightpink;
padding: 2 2 2 2;
}
</style>

… and we thank this webpage for the heads up regarding how to calculate week numbers within a year data item displays now available in …


Previous relevant Event Calendar PHP Tutorial is shown below.

Event Calendar PHP Tutorial

Event Calendar PHP Tutorial

Let’s face it. Serverside PHP is just great! It opens up so many opportunities regarding data in your web applications.

As such, onto yesterday’s Event Calendar New Window Tutorial logic we now have a …


Record to Remember

… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.

The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …


<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm

$hcont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.htm');
$cont=file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php');
$ipad=server_remote_addr();
$ipadless=$ipad;
$js='';

function server_remote_addr() {
global $ipadless;
$rma = $_SERVER['REMOTE_ADDR'];
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
// you can add different browsers with the same way ..
$ipadless=str_replace(".", "_", str_replace(":", "_", $rma));
if(preg_match('/(chromium)[ \/]([\w.]+)/', $ua))
$rma = '000000'.$rma;
elseif(preg_match('/(chrome)[ \/]([\w.]+)/', $ua))
$rma = '00000'.$rma;
elseif(preg_match('/(safari)[ \/]([\w.]+)/', $ua))
$rma = '0000'.$rma;
elseif(preg_match('/(opera)[ \/]([\w.]+)/', $ua))
$rma = '000'.$rma;
elseif(preg_match('/(msie)[ \/]([\w.]+)/', $ua))
$rma = '00'.$rma;
elseif(preg_match('/(mozilla)[ \/]([\w.]+)/', $ua))
$rma = '0'.$rma;
return str_replace(".", "_", str_replace(":", "_", $rma));
}

$itdone=false;
if (isset($_GET['init'])) {
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
echo "<html><head><script type='text/javascript'> " . $js . " </script></head><body><p>" . $ipad . "</p></body></html>";
} else if (isset($_POST['phpthere']) && isset($_POST['bigurl']) && isset($_POST['caltitle'])) {
//file_put_contents('xz.xz', 'l');
if (strpos($cont, '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n") === false) {
//file_put_contents('xz.xzz', 'l');
$cont=str_replace('?' . '>', '// ' . str_replace(' ','_',str_replace(',','_',str_replace('+',' ',urldecode($_POST['caltitle'])))) . '_' . str_replace('+',' ',urldecode($_POST['phpthere'])) . '=' . $_POST['bigurl'] . "\n" . '?' . '>', $cont);
file_put_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'events_in_month.php', $cont);
}
if (strpos($cont, '_' . $ipad . '=') !== false) {
$things=explode('_' . $ipad . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
if (strpos($cont, '0' . $ipadless . '=') !== false) {
$things=explode('0' . $ipadless . '=', $cont);
for ($it=1; $it<sizeof($things); $it++) {
if ($it == 1 && !$itdone) {
$itdone=true;
$js.="\n parent.document.getElementById('myoldsel').style.display='inline-block'; \n";
}
$js.="\n parent.document.getElementById('myoldsel').innerHTML+='<option value=\"" . explode("\n", $things[$it])[0] . "\">" . explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[0] . ', ' . explode('_', explode('__', explode(' ' . ' ' . ' ', $things[-1 + $it])[-1 + sizeof(explode(' ' . ' ' . ' ', $things[-1 + $it]))])[1])[0] . "</option>'; \n";
}
}
//file_put_contents('xz.xzzz', "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>");
echo "<html><head><script type='text/javascript'> " . $js . "\n parent.checkif(parent.document.getElementById('phpif')); \n" . " </script></head><body><p>" . str_replace(' ','%20',str_replace('+',' ',urldecode($_POST['bigurl']))) . "</p></body></html>";
} else {
echo $hcont;
}
exit;

//
//
// January__1970_00000__1=http://localhost:8888/events_in_month.htm?caltitle=January%2C%201970&i01.00=&ta01.00=kgjfjhf%20jhgfjhf%20jhkgkjhg%20jhgkjhg%20jkghhg%20jkhgkjhg%20kjhgkjhg&i02.00=&ta02.00=kjhgkjh%20kjhgkjhg%20jkhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg%20kjgkjhg&i03.00=&ta03.00=kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20kjhgkjhg%20jhkgkjhg&i04.00=&ta04.00=&i05.00=&ta05.00=&i06.00=&ta06.00=&i07.00=&ta07.00=&i08.00=&ta08.00=&i09.00=&ta09.00=&i10.00=&ta10.00=&i11.00=&ta11.00=&i12.00=&ta12.00=&i13.00=&ta13.00=&i14.00=&ta14.00=&i15.00=&ta15.00=&i16.00=&ta16.00=&i17.00=&ta17.00=&i18.00=&ta18.00=&i19.00=&ta19.00=&i20.00=&ta20.00=&i21.00=&ta21.00=&i22.00=&ta22.00=&i23.00=&ta23.00=&i24.00=&ta24.00=&i25.00=&ta25.00=&i26.00=&ta26.00=&i27.00=&ta27.00=&i28.00=&ta28.00=&i29.00=&ta29.00=&i30.00=&ta30.00=&i31.00=&ta31.00=
?>

Yes, the parent needed to change for our events_in_month.htm parent of Events in Month web application role.


Previous relevant Event Calendar New Window Tutorial is shown below.

Event Calendar New Window Tutorial

Event Calendar New Window Tutorial

Onto yesterday’s Event Calendar Remembered Tutorial‘s “Mystery Dilemma”

But, there’s an inherent weakness with the design, we’ll go into more into the future.

… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.

Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.

Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …

  1. bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
  2. pick Inspect option …
  3. be in Elements tab of your Web Inspector … and …
  4. highlight top <html> tag …
  5. Context Menu with a right click or two finger gesture …
  6. pick Copy -> Copy outerHTML … so that …
  7. your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
  8. Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)

The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …


function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}

… the form submit buttons are reworded accordingly.

Another idea from this blog thread’s inspiration …

… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.

And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …

  • text shadow means by which the text of Event Calendar can be more impactive

    <div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
  • dropdown element conditional styling

    <style>
    .dayb {
    color: white;
    background-color: red;
    padding: 5 5 5 5;
    }


    .dow {
    color: purple;
    font-style: bold;
    }

    .selday {
    margin-left: 8px;
    background-color: rgba(255,0,0,0.7);
    display: inline-block;
    width: 50px;
    }
    </style>



    if (thislabel.substring(0,1) == 'i') {
    if (thisval.trim() != '') {
    document.getElementById(thislabel.replace('i', 'sel')).style.color='white';
    document.getElementById(thislabel.replace('i', 'sel')).style.backgroundColor='red';

    }
    document.getElementById(thislabel.replace('i', 'opt')).innerText=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + '); //.replace(/\+$/g, ' ');
    document.getElementById(thislabel).value=thisval.replace(/\+$/g, ' ');
    } else {
    document.getElementById(thislabel).value=thisval.replace(/\+/g, ' ').replace(/\ \ \ /g, ' + ');
    }
    }
  • placeholder on Blurb conditional existence

    var ourdata='';
    // ...
    if (documentURL.indexOf('?') != -1) {
    if (documentURL.indexOf('?caltitle=') != -1) { ourdata='data-'; }
    // ...
    trtemplate='<tr id=tr01.00><td style=width:22%;><span id=sone01.00 class=dow>' + dotw[adate.getDay()].toUpperCase().substring(0,3) + '</span><br><br><span id=stwo01.00 class=dayb>1<span onblur=sepit(this); contenteditable=true id=sp01.00></span><input type=hidden id=i01.00 name=i01.00 value=""></input><select data-dow=' + dotw[adate.getDay()].toUpperCase().substring(0,3) + ' class=selday onchange="selit(this);" id=sel01.00><option id=opt01.00 value=""></option><option title="All such in month (ie. weekly)" value="...">...</option><option title="And ..." value="&...">&</option><option value=Bold>Bold Title</option><option value=Clone>Clone</option></select></span></td><td class=blurb title="What is on?" id=tb01.00><span title="Event title" style="font-style:bold;color:blue;" id=bd01.00></span><textarea name=ta01.00 id=ta01.00 style="width:100%;height=100%;" ' + ourdata + 'placeholder="Blurb ..." class=tablurb></textarea></td></tr>';
    // ...
    }
    // ...
    }

… for our “second draft” events_in_month.htm Events in Month web application.


Previous relevant Event Calendar Remembered Tutorial is shown below.

Event Calendar Remembered Tutorial

Event Calendar Remembered Tutorial

We were inspired by an Event Calendar pamplette we saw the other day …

… to write a new “proof of concept” Events in Month web application, whose content can be recalled via the web browser’s Bookmark methodologies.

We liked the ideas for day of week and/or date of month grouping arrangements we included, being, the way we interpreted it …

  • just on this day in this month … default
  • on this day of the week throughout the month in question … “
  • on this day and some others in that month in question … “&” … to start with and further amendments available via contenteditable=true span element
  • “Clone” value allows for multiple separated “blurbs” for the one signature day

But, there’s an inherent weakness with the design, we’ll go into more into the future. For now, you can try it yourself below …

Stop Press

This is where we get to for a “second draft” we’ll get into, further, tomorrow …

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


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


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


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


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


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


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


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


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


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


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


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


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

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