out and about (using mobile data but not WiFi, perhaps Bluetooth off, volume reasonable)
not being charged
… scenarios. We were at first amazed but got back to “WiFi Land” doing research about our use of an iPhone’s Control Center screen (we know of, as the one with the Torch) to avoid having to effectively be touching the iPhone screen to keep it awake semi regularly (that “awake” state the ideal state for our Shower Songs “radio play” executions). We succeeded on a car trip with this but back at “WiFi Land” our research …
… filled with “No” led us, for a couple of days, to thinking this must have been a figment of our imagination. But, today, out and about, this time not in a car, but under those conditions above, it happened again. The use of the iPhone’s Control Center screen helped not have to keep the iPhone awake at semi-regular intervals. Perhaps our Shower Songs YouTube API methodologies (not recognized as “Media” in many car Bluetooth systems, for instance) do not exactly fit in with your typical scenario all the references above are saying “No” to? Who knows? But below is our (tweaked via experience) advice regarding our inhouse Shower Songs “radio play” out and about on a mobile device …
on a well charged (ahead of time) mobile device (such as an iPhone with iOS, that all the technical terminology below references) out and about (using mobile data but not WiFi) and not being charged
close unnecessary apps you have open that are not the web browser you are going to use to …
if you are in a car we often find it best turning Bluetooth off and use the iPhone speakers (with a decent volume applied)
open our inhouse Shower Songs web application in a mobile web browser application
arrange the playlist with it’s emergent red buttons you can tap as they arrive if you are impatient (perhaps leaving some unselected)
once satisfied with your ordering and music is playing …
in our iPhone’s case, swipe down and left from top right, to reveal that iPhone’s Control Center display (with the Torch!)
to loop and continuously play with much less need to “tap to wake” (but if you do, just unswipe the Control Center to tweak the Shower Songs music play settings, as required, or if the connection drops (hence the “perhaps leaving some unselected” advice above))
… be able to open a new popup window to the right which is a YouTube temporary playlist play scenario webpage, as well as remember this, and recreate this, for the next time
We break with our normal transmission, because we read something …
… Luuunnnaa?! Sarcasm does not look good on you (though lilac green’s not bad). And wipe that sardonic grin off your breakfast bowl food rearrangements you’ve just made?!
A feather to our boa constrictor … Luuunnnaa?! … get off the keyboard now … don’t you have some bone to pick, or that book about Dogma (tee hee) to read?!w
… on that Shower Songs YouTube embedded iframe inhouse playlist concept, further to Shower Songs Playlist Subset Revisit Tutorial. Some people, like Luna today, may want to bypass our Shower Songs just audio paradigm to dynamically create a temporary YouTube playlist and play video and audio together in a playlist in sequential order.
Anyway, we tweaked stuff to start the ball rolling making this be a possibility, as well as involving YouTube video ID PtR4cWb4HNg in the list to spark this concept into action. And later we’ll explain maybe why PtR4cWb4HNg should lead the comma separated list.
Radio Play via comma separate YouTube 11 character IDs or search ideas for an inhouse playlist (if any in a comma separated list is PtR4cWb4HNg temporary YouTube playlist audio and video play can be attempted) … 7 such uses Shower Songs … egs. Tragedy,Boogie Wonderland,Down Among the Dead Men flash … random … Tragedy ,Imagine*3,Breezin … ordered 5 … Tragedy ,Pina Colada Song,Fast Car … audio … Tragedy ,Galveston,Fix You … audio ordered,
Did You Know?
If at the time of launching (and deploying) into making one of these temporary YouTube playlists and you are logged into your own YouTube channel, more possibilities present, and today’s tutorial animated GIF shows a bit of this (though we found out about the virtue of putting PtR4cWb4HNg first later, alas).
The playlist gets played, and on non-mobile it may start itself. For each track you can use the Save button down near the bottom to save to a new (where we are recommending PtR4cWb4HNg to go first) or pre-existing public or private playlist. Okay, so you have this new YouTube playlist.
… in which case a brief error message may happen and it may start playing (on non-mobile) that YouTube playlist at track 2 … hence my advice regarding PtR4cWb4HNg … or …
A few attempts we made failed to “curb the enthusiasm” of the parent window left behind by the new window.open window of these Playlist Subsets the user defines, until we did something we can not recall ever doing before, namely …
var tdsarea=document.getElementsByTagName('td');
for (var iopj=eval(-1 + tdsarea.length); iopj>=0; iopj--) {
tdsarea[iopj].innerHTML=tdsarea[iopj].innerText;
}
… to not only be “curbing it’s enthusiasm to play” but also making that parent window end up being a “display snapshot only” of what had been going on before the user set up more than one “L=loop” looping textarea functionalities that defines a playlist subset scenario where that new window takes on the role of “enthusiastic play”.
add to an “individual song looping subset” with another “individual looping song” … and it also serves another “arcane usage” to our mind, we explain below …
out and about using a laptop lassooed to a mobile (or just the mobile itself) using mobile data (rather than Wi-Fi) this web browser Shower Songs web application depends on a connection as a song starts and if you have run out of never before tapped buttons to choose from (and the web application has lost it’s connection) it can become an impotent web application, sometimes, so that to add to the web application’s possibilities of button tapping, and often reawakening it, the better, we figure
… work did not function for as many scenarios as we would like. This problem requires Javascript web browser clientside debugging via a Web Browser web inspector “Developer” style of debugging (ie. deep and meaningful).
When it gets to the “deep and meaningful” we mean “comparing” console tab “good versus bad” types of reporting, which becomes untenable if too much is already being reported in our favourite “reporting tool conduit” for us …
console.warn([some messaging text]);
… you can read more about at YouTube API Caller Phase Two Console Warning Tutorial. Over the three window hierarchies, over quite some time, we’ve filled console.warn web inspector tab with so much information it is untenable for what we have to do debugging the issues above.
And so, today, we’re showing a “conditional approach” to controlling “console.warn” logging via “grandchild level” substitution of all “console.warn(” for “consolewarn(” and then the new Javascript function …
function consolewarn(inms) {
if (window.self != window.parent) {
if (('' + top.document.URL).indexOf('consolewarnoptinall') != -1 || ('' + top.document.URL).indexOf('consolewarnoptingrandchild') != -1) {
console.warn(inms);
}
} else if (('' + document.URL).indexOf('consolewarnoptoutall') == -1) {
console.warn(inms);
}
}
… and at the “parent level” substitution of all “console.warn(” for “consolewarn(” and then the new Javascript function …
function consolewarn(inms) {
if (window.self != window.parent) {
if (('' + top.document.URL).indexOf('consolewarnoptinall') != -1 || ('' + top.document.URL).indexOf('consolewarnoptinchild') != -1) {
console.warn(inms);
}
} else if (('' + document.URL).indexOf('consolewarnoptoutall') == -1) {
console.warn(inms);
}
}
… as an address bar controlling URL via, for example …
… means by which we can better hone in on the debugging issues as they arrive, so that self contained relevance fits on the one screen, and the Clear Console icon becomes an additional friend to our work, within …
YouTube API iOS Shower Song Subset Looping Tutorial
You’d think we’d be done with ablutions at this blog, but ingen måte … Luna (aka Måne) says … there’s all manner of molekylær interesse out there in and out of the fjords …
It’s not just H and the 2 and the O … it’s the art of the rollerulle.
… or so says “The Lu” (aka “Toalettet”) anyway!
Today’s Shower Songs tweak involves it’s “individual song looping” thinking. It occurred to us, that it’s use by a user might be what they want, it becomes a bit inflexible should they either …
want to “move on” to a new song … via …
add to an “individual song looping subset” with another “individual looping song” … and it also serves another “arcane usage” to our mind, we explain below …
out and about using a laptop lassooed to a mobile (or just the mobile itself) using mobile data (rather than Wi-Fi) this web browser Shower Songs web application depends on a connection as a song starts and if you have run out of never before tapped buttons to choose from (and the web application has lost it’s connection) it can become an impotent web application, sometimes, so that to add to the web applications’s possibilities of button tapping, and often reawakening it, the better, we figure
At least, on “mobile” iOS (which we have access to) can any “command line” feel of playlists ending up at our Shower Songs web application be arranged?
Pretty pithy, huh?! Before we launch into the great iOS Shortcuts app (we’ve talked about before at Shortcuts iOS App Email Arrival Sounds Tutorial) helping here, let’s outline some other simpler ideas you might want to try yourself in the DIY range of ideas …
Add to Home Screen … with a tailored (Playlist) Title … this being our preferred option to future proof the remembering … but also available is …
Add Bookmark … or …
Add Favourite
Make your web browser’s opening Home Page be a URL of interest (like one of these playlists) within that web browser app’s Settings area
as well as the Shower Songs (as well as Tabular Single Row Media Gallery) web application (with Playlists of 7) can have a dropdown be populated ready to choose and replay that Playlist (that can be email or SMS shared with a recipient)
Okay, all that being said, the great Shortcuts iOS app can serve two purposes a bit “above and beyond this” …
you can DIY build an iOS feeling and “hybrid looking” amalgum of iOS and “old days” dialog box “desktop feeling” personalized application(s) …
the act of constructing any one is sure to inspire other iOS automation ideas you might have
We’re sure lots of you more aux fait with the great iOS Shortcuts app can do better than the “bit kludgy” effort we came up with, and that is the joy of programming, huh?! It can come down to …
what you think is the most efficient …
you feel you can handle … and …
end up with a better automated solution … but …
it’s not “all about efficiency” always … it’s also learning about new methodologies you might have to spend more time discovering the ins and outs about
As a case in point, it will be hard to stop yours truly just (iOS mobile) opening the relevant Radio Play web application in a web browser, and going from there (with Bookmarks and Favourites), but there you go?! In this case, perhaps yes, but you should see all the other Messaging and automation possibilities that a peruse of the iOS Shortcuts app can open up to you!
Todays’ animated GIF presentation shows our endeavours “warts and all” (latching onto, intervention wise, when a user taps the Music mobile app icon) for your perusal.
Can a list of seven non-YouTube-ID comma separated values (ie. YouTube song search basis strings) be entered and still end up at the Shower Songs web application?
Well, yes, that’s possible if an intervention point can be arranged. After all, the command line prompt goes …
Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist … 7 for Shower Songs … egs. Tragedy,Boogie Wonderland,Down Among the Dead Men … random … Tragedy ,Imagine*3,Breezin … ordered 5 … Tragedy ,Pina Colada Son,Fast Car … audio … Tragedy ,Galveston,Fix You … audio ordered, and go.
… begging the question “What happens if we enter, at the command line …”
play
Tragedy ,Pina Colada Song,Fast Car,Breezin,Galveston,Fix You,Imagine
… and that’s where “our fun” (chortle, chortle) began (yeh, right … after a Goose Chase with it being the seventh one) because that seventh YouTube video ID (involved with YouTube “Imagine” search (and something about this rings a bell)) comes back with …
This content can’t be played on your mobile browser. Get the YouTube app to start watching.
Ouch?! What to do? Looking optimistically on this, it’s good to discover it now (yeh, right).
macOS (building onthe changedplay.sh shell script) …
#!/bin/bash
rest=""
if [ -z "$1" ]; then
echo "Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... 7 such uses Shower Songs ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men flash ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Song,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go."
read ans
else
preans="$1"
ans="$1 $2 $3 $4 $5 $6 $7 $8 $9"
while [ ! -z "$9" ]; do
shift
rest="${rest} $9"
done
ans="$ans${rest}"
fi
if [ ! -z "$ans" ]; then
commas_only="${ans//[^,]}"
if [ "${#preans}" == "83" ]; then
if [ "${#commas_only}" == "6" ]; then
open "https://www.rjmprogramming.com.au/$1"
else
open "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html $ans"
fi
else
open "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html $ans"
fi
fi
exit
Windows (building onthe changedplay.bat script) …
@echo off
rem play.bat
rem RJM Programming
rem November, 2025
set passed=
set rest=
set seven=0
if trick==trick%1 (
set /p ans=Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... 7 for Shower Songs ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Son,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go.
set ansplus="%ans% "
if "%ansplus:~12,1%"=="," (
if "%ansplus:~24,1%"=="," (
if "%ansplus:~36,1%"=="," (
if "%ansplus:~48,1%"=="," (
if "%ansplus:~60,1%"=="," (
if "%ansplus:~72,1%"=="," (
if "%ansplus:~84,1%"==" " (
set seven=7
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/shower_songs.html?thelist=%ans%"
goto :end
) else (
set seven=6
)
) else (
set seven=5
)
) else (
set seven=4
)
) else (
set seven=3
)
) else (
set seven=2
)
) else (
set seven=1
)
) else (
set seven=0
)
) else (
set ans="%1"
set ansplus="%1,%2,%3,%4,%5,%6,%7 %8 "
if "%ansplus:~12,1%"=="," (
if "%ansplus:~24,1%"=="," (
if "%ansplus:~36,1%"=="," (
if "%ansplus:~48,1%"=="," (
if "%ansplus:~60,1%"=="," (
if "%ansplus:~72,1%"=="," (
if trick==trick%8 (
set seven=7
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/shower_songs.html?thelist=%1,%2,%3,%4,%5,%6,%7"
goto :end
) else (
set seven=6
)
) else (
set seven=5
)
) else (
set seven=4
)
) else (
set seven=3
)
) else (
set seven=2
)
) else (
set seven=1
)
) else (
set seven=0
)
set passed=yes
if trick==trick%2 goto :loop
set ans="%ans%,%2"
if trick==trick%3 goto :loop
set ans="%ans%,%3"
if trick==trick%4 goto :loop
set ans="%ans%,%4"
if trick==trick%5 goto :loop
set ans="%ans%,%5"
if trick==trick%6 goto :loop
set ans="%ans%,%6"
if trick==trick%7 goto :loop
set ans="%ans%,%7"
if trick==trick%8 goto :loop
set ans="%ans%,%8"
if trick==trick%9 goto :loop
set ans="%ans%,%9"
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans:"=%"
:loop
if "%9"=="" (
@echo off
) else (
shift
set rest="%rest% %9"
goto :loop
)
set ans="%ans%%rest%"
set ans=%ans:"=%
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
if trick==trick%passed% (
set ans=%ans:"=%
if "%ans:,=%"=="%ans%" (
echo "Nothing to do."
) else (
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
)
rem exit
:end
… and involving, if you are on macOS or Windows implementing accordingly …
YouTube API Caller Radio Play Doctype Meta Tutorial
It took a session on this MacBook Air’s macOS Firefox web browser trying the Tabular Single Row Media Gallery web application to emphasise the importance of more consistency checking on …
doctype HTML tagdeclaration …
<!DOCTYPE html>
… and …
meta HTML display tagcharset attribute …
<meta charset="UTF-8">
… existence in web applications we create, to the point that not having the second one defined was stopping proper webpage display on Firefox. Was this a portent of what is to come soon on more web browsers?!
Let’s look at the purpose of these two HTML tagthingos …
What’s the scripting here? They are called batch files with extension *.bat we’re calling play.bat (going back to the DOS days of the ’70’s again). With *.bat you just need to plonk your play.bat anywhere within the Windows local system path, and the word (now command) “play” will do the rest anywhere in that user’s command line environment. And the batch file has …
@echo off
rem play.bat
rem RJM Programming
rem November, 2025
set passed=
set rest=
if trick==trick%1 (
set /p ans=Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Son,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go.
) else (
set ans="%1"
set passed=yes
if trick==trick%2 goto :loop
set ans="%ans%,%2"
if trick==trick%3 goto :loop
set ans="%ans%,%3"
if trick==trick%4 goto :loop
set ans="%ans%,%4"
if trick==trick%5 goto :loop
set ans="%ans%,%5"
if trick==trick%6 goto :loop
set ans="%ans%,%6"
if trick==trick%7 goto :loop
set ans="%ans%,%7"
if trick==trick%8 goto :loop
set ans="%ans%,%8"
if trick==trick%9 goto :loop
set ans="%ans%,%9"
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans:"=%"
:loop
if "%9"=="" (
@echo off
) else (
shift
set rest="%rest%,%9"
goto :loop
)
set ans="%ans%%rest%"
set ans=%ans:"=%
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
if trick==trick%passed% (
set ans=%ans:"=%
if "%ans:,=%"=="%ans%" (
echo "Nothing to do."
) else (
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
)
rem exit
:end
… as your starting gambit, here, where the equivalent of macOS’s “open” is “explorer.exe” on Windows (ie. the File Explorer) … almost as cute!
YouTube API Caller Radio Play Playlist Local System Tutorial
The ..
command line
… of a laptop or notebook or personal computer can be an environment to relax from the GUI aspects of your modern operating systems and think about …
processes
batch driven tasks
task simplifications
… and in the category of that last one, on our local macOS operating system we envisaged a day we could slip back to the “quiet world” of the “command line” and type a command like …
play Tragedy,Imagine,Down Among the Dead Men PAN -
… and get somewhere playing those songs through a web browser accessing the YouTube API and our interfacings to it in webpage software form, playing in a continuous Radio Play style.
You see, with all the distractions, only having to remember …
play
… no matter where we were in “command line” woooorrrrllllddd seemed the ideal, and yes, it’s possible. However, we think explaining …
bash /Applications/MAMP/htdocs/play.ksh Tragedy,Imagine,Down Among the Dead Men PAN -
… is a necessary dividing line between two parts to the discussion. So what’s bash? It’s the shell we associate as your macOS default shell (we get to via the “out of the box” macOS desktop application called Terminal) … well, it used to be … anyway, the first line of (what started as play.ksh then play.sh and then eventually, just, play … as a filename that is) …
#!/bin/bash
rest=""
if [ -z "$1" ]; then
echo "Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Song,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go."
read ans
else
ans="$1 $2 $3 $4 $5 $6 $7 $8 $9"
while [ ! -z "$9" ]; do
shift
rest="${rest} $9"
done
ans="$ans${rest}"
fi
if [ ! -z "$ans" ]; then
open "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html $ans"
fi
exit
… tells the local system use the bash shell to interpret this what you might like to think of as an executable script (which we went chmod 777 play regarding to make it so).
Okay, so we wrote this script in /Applications/MAMP/htdocs/ and that …
bash /Applications/MAMP/htdocs/play Tragedy,Imagine,Down Among the Dead Men PAN -
… could swing it at this stage, or even being in /Applications/MAMP/htdocs/ and going …
./play Tragedy,Imagine,Down Among the Dead Men PAN -
… but (even in /Applications/MAMP/htdocs/) …
play Tragedy,Imagine,Down Among the Dead Men PAN -
… is most likely to reply something like …
zsh: no such command
As the programmer, perhaps you could leave it at that with your “insider knowledge”, or if “play” as a command already meant something (a test that would be good to do before any such proposed work), otherwise … do you really think you’ll remember next Wednesday? So …
enter into the wooooorrrrllllddd of aliases (thanks to this great advice, thanks) … eg.
alias play="/Applications/MAMP/htdocs/play"
placed into …
~/.bashrc
… via (in our case) …
vi ~/.bashrc
then …
source ~/.bashrc
… needs to be included into the login procedure of …
~/.profile
…via (in our case) …
vi ~/.profile
to make …
play
… as the interactively asking alternative to the argumentative example of …
play Tragedy,Imagine,Down Among the Dead Men PAN -
… mean something … and anywhere
… all of this possible because of the brilliantly useful macOS (not Linux nor unix) …
… opener of web browsers from the command line blowing of our tiny little rather still naive mind … mind you, VAX/VMS “phone” command from the ’70’s might have even been better?! And then there were those punch cards … in the hopper … and if you got a character wrong … back to the end of the queue.
By the way, this brought into play for us what your default web browser is. Ours is Safari yet we develop more on Google Chrome. Safari, for us, does more Popup Blocking, and it is a thing to look out for should a window not open when expected (and these command line methodologies don’t get past you still having to unmute and/or click/tap once before the Radio Play mode of play kicks off). To undo the Popup Block there will probably be a means to do this to the right of the web browser address bar.
But there is a point. Today represents an improvement on yesterday’s arrangements for dynamic playlist creation regarding our Tabular Single Row Media Gallery web application via the playlist emoji button 🔀 click/tap or on the web browser command line. With yesterday’s scenario, had you entered as your internal playlist comma separated string …
Born to Run,Born to Run,Born to Run,Born to Run
… without the …
Burp …
… that is you’d have ended up with the top find off a YouTube search repeated 4 times. Is this ideal? We asked Nala and Luna but, unfortunately they were busy, so they left me with the decision, and we came up with …
That’s kind of silly.
For these few reasons …
variety
album style Radio Play listening possibilities
perhaps getting around that …
This video requires payment to watch YouTube
… issue we talked about before … because you could uncheck it’s checkbox if the error appears
giving non-top search items a go
… we decided to “mix it up” regarding entries like …
Born to Run,Born to Run,Born to Run,Born to Run
… as well as the simpler …
Born to Run*4
… so that repeats don’t cause users indigestion … chortle, chortle.
Here’s the Javascript turning Born to Run*4 into Born to Run,Born to Run,Born to Run,Born to Run …
YouTube API Caller Radio Play Playlist Songname Search List Address Bar Tutorial
Don’t know if you are like me, but especially when on this MacBook Air we’re, forever, using a web browser address bar to type in, what we’ve set up as, a Google search engine webpage query. Rather than even going to Google webpage first, that is!
Most people don’t want to know about arcane web application argument arrangements … though we’re very fond of the 5 minute ones?!
What’s wrong with some of you? Who would not relish the thought of …
&or?andgo=y # will click the Radio button after all above (unless notgo is mentioned in the list somewhere), as relevant, with the programmatically determined derived YouTube ID list via YouTube search(es)
💬🦆 Come On!
… when, after a bit of thought and tweaks to our RJM Programming domain’s Apache Document Root residing 404.shtml webpage Javascript …
if (document.URL.indexOf('rjmprogramming.com.au/HTMLCSS/swipe_media.htm') != -1) {
if (decodeURIComponent(document.URL).indexOf(',') != -1) {
if (decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'')).toLowerCase().indexOf('notgo') != -1) {
location.href='//www.rjmprogramming.com.au/HTMLCSS/swipe_media.html?thelist=' + encodeURIComponent(decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace('notgo','').replace('NOTGO','').replace('Notgo','')));
} else {
location.href='//www.rjmprogramming.com.au/HTMLCSS/swipe_media.html?andgo=y&thelist=' + encodeURIComponent(decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'')));
exit;
}
} else if (decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'')).toLowerCase().indexOf('ask') != -1) {
location.href='//www.rjmprogramming.com.au/HTMLCSS/swipe_media.html?ask=y';
exit;
}
}
… now all you need to add after that link above, on the web browser address bar is …
[commaSeparatedList_decodeURIComponent_ized]
… ie. just type a comma separated YouTube song search basis link after the link wording above
YouTube API Caller Radio Play Playlist Songname Search List Dropdown Tutorial
Around here, regarding web application work, we really like dropdown (ie. HTML select element) work because a lot of information can be …
stored
displayed (often taking up minimal webpage space)
it’s option subelements can have an event life of their own, or be part of the select element event whole picture
dropdown multiple selection mode (as with today’s work) can …
still support your single choice scenario
we’ve coded today’s dropdown to support, as applicable, ordered single click choices … versus …
can allow for control key (sets of choices not necessarily adjacent) and shift key (blocks of choices)
can respond (as with today’s dropdown) to preset selections established at creation via dropdown (ie. select) innerHTML option subelement selected attribute setting
dropdown events onmousedown and ontouchdown (as for today’s dropdown) and onclick can be harnessed ahead of an onchange event … and in today’s case can help unset any preset option subelement selected attribute setting(s)
Just so flexible! Subelement option CSS styling can be less friendly, but even so, often background colouring is possible, and we do a bit of that with the work from yesterday’s YouTube API Caller Radio Play Playlist Songname Search List Tutorial and today’s better “shored up” work you can see a bit of below (where your starting point might be that we “wrap” this particular Javascript prompt window through the Javascript overprompt function below) …
var outplist='', kaconto=null, kacontoarr=[], rconeis=155, gconeis=255, bconeis=255;
function karlook(iois) {
var aconto=null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
if (outplist.indexOf(',') != -1) {
kaconto=aconto;
setTimeout(function(){ kaconto.getElementById('ajaxs').style.cursor='progress'; }, 3000);
setTimeout(postkarlook, 7000);
} else if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
kaconto=aconto;
setTimeout(mpostkarlook, 7000);
}
}
}
}
function karlookagain(iois,idxn) {
var aconto=null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
if (eval('' + kacontoarr.length) > eval('' + idxn)) {
kacontoarr[eval('' + idxn)]=aconto;
} else {
kacontoarr.push(aconto);
}
}
}
}
function mpostkarlook() {
if (kaconto.getElementById('ajaxs')) {
var kbuts=kaconto.getElementsByTagName('input');
for (var lbuts=0; lbuts<kbuts.length; lbuts++) {
if (('' + kbuts[lbuts].type) == 'button') {
if (('' + kbuts[lbuts].value).indexOf('Radio') == 0) {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', String.fromCharCode(10) + 'Radio');
}
}
}
} else {
setTimeout(mpostkarlook, 5000);
}
}
function postkarlook() {
var selectedones='', oktogo=true, hihi=0;
if (kaconto.getElementById('ajaxs')) {
//alert(kaconto.getElementById('ajaxs').innerHTML);
for (hihi=0; hihi<kacontoarr.length; hihi++) {
if (!kacontoarr[hihi]) {
oktogo=false;
//alert('hihi=' + hihi + ' NOT okay');
} else if (!kacontoarr[hihi].getElementById('ajaxs')) {
oktogo=false;
//alert('hihi=' + hihi + ' not okay');
}
}
if (!oktogo) {
setTimeout(postkarlook, 5000);
} else if (kaconto.getElementById('ajaxs').innerHTML.indexOf('... and then ...') == -1) {
kaconto.getElementById('ajaxs').style.cursor='pointer';
var otherps=baeisf();
if (otherps != '' || navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
var kbuts=kaconto.getElementsByTagName('input');
for (var lbuts=0; lbuts<kbuts.length; lbuts++) {
if (('' + kbuts[lbuts].type) == 'button') {
if (('' + kbuts[lbuts].value).indexOf('Radio') == 0) {
var wasvl=kbuts[lbuts].value;
if (otherps == ' ') {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', 'Ordered ' + String.fromCharCode(10) + 'Audio ' + String.fromCharCode(10) + 'Radio');
} else if (otherps == ' ') {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', 'Audio ' + String.fromCharCode(10) + 'Radio');
} else if (otherps == ' ') {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', 'Ordered ' + String.fromCharCode(10) + 'Radio');
} else {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', String.fromCharCode(10) + 'Radio');
}
}
}
}
}
selectedones=(kaconto.getElementById('ajaxs').innerHTML.split(kaconto.getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>')[1].split('</option>')[0] + '</option>').replace('>', ' class=uptop selected>');
for (hihi=0; hihi<kacontoarr.length; hihi++) {
if (rconeis == gconeis && rconeis == bconeis) {
bconeis-=7;
if (bconeis < 0) { bconeis+=255; }
} else if (rconeis == gconeis) {
gconeis-=7;
if (gconeis < 0) { gconeis+=255; }
} else {
rconeis-=7;
if (rconeis < 0) { rconeis+=255; }
}
selectedones+=((kacontoarr[hihi].getElementById('ajaxs').innerHTML.split(kacontoarr[hihi].getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>')[1].split('</option>')[0] + '</option>').replace('>', ' class=uptop selected>')).replace(/option\ onclick\=/g, 'option style=background-color:rgb(' + rconeis + ',' + gconeis + ',' + bconeis + '); onclick=');
kaconto.getElementById('ajaxs').innerHTML+='<option value="">... and then ...</option>';
kaconto.getElementById('ajaxs').innerHTML+=kacontoarr[hihi].getElementById('ajaxs').innerHTML.split(kacontoarr[hihi].getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>')[1].replace(/option\ onclick\=/g, 'option style=background-color:rgb(' + rconeis + ',' + gconeis + ',' + bconeis + '); onclick=');
//alert(kacontoarr[hihi].getElementById('ajaxs').innerHTML);
}
//alert(selectedones);
kaconto.getElementById('ajaxs').innerHTML=kaconto.getElementById('ajaxs').innerHTML.replace(kaconto.getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>', kaconto.getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>' + selectedones + (selectedones != '' ? '<option id=optclk value="">____ Selection above available (resettable via click here) as you click/tap button at right ____</option>' : ''));
if (selectedones != '') {
kaconto.getElementById('ajaxs').onmousedown=function(event){ if (event.target.parentNode.outerHTML.split('>')[0].indexOf(' data-flagged=') == -1) { event.target.parentNode.setAttribute('data-flagged','y'); event.target.parentNode.innerHTML=event.target.parentNode.innerHTML.replace(/\ selected/g, ' ').replace('____ Selection above available (resettable via click here) as you click/tap button at right ____','____ Please select choices in usual multiple choice dropdown ways ____'); } };
kaconto.getElementById('ajaxs').ontouchdown=function(event){ if (event.target.parentNode.outerHTML.split('>')[0].indexOf(' data-flagged=') == -1) { event.target.parentNode.setAttribute('data-flagged','y'); event.target.parentNode.innerHTML=event.target.parentNode.innerHTML.replace(/\ selected/g, ' ').replace('____ Selection above available (resettable via click here) as you click/tap button at right ____','____ Please select choices in usual multiple choice dropdown ways ____'); } };
}
}
}
}
function baeisf() {
var outarrp=outplist.split(',');
if (eval('' + outarrp.length) > 1) {
return outarrp[0].replace(outarrp[0].trim(), '');
}
return '';
}
And so, today …
we add onto the default random ordering mode of Radio Play via YouTube search comma separated list, the further choices ordered or audio or audio ordered, as per the relevant Javascript prompt blurb that is now going …
Enter YouTube 11 character code video (comma separated) or audio stream only of video (semicolon) list to Radio Play (and add ! to start in shuffle mode and/or # for the order you enter and/or & for whole list) … or a YouTube Search String basis for playlist via a Radio📻? button to activate, later. Comma separateds can be search ideas … egs. Tragedy,Boogie Wonderland,Down Among the Dead Men … random … Tragedy ,Imagine,Breezin … ordered … Tragedy ,Pina Colada Song,Fast Car … audio … Tragedy ,Galveston,Angel of the Morning … audio ordered. Double click on this cell for playlist redefinitions.
started allowing for dropdown events onmousedown and ontouchdown be able to reset the preset choices we programmatically select for the user should they be happy (but our motivation for this event work was the potential to run into a YouTube video that gives the error …
Get arguments are those ? or & delimited parts to URLs you sometimes see up at the address bar of web browsers. We’d be lost without them. And, though Google Translate uses it’s own set arrangement of get arguments for this “static URL” language translation mode of use we’ve been using in this Editable Calendar recent project, it does not mean that Google Translate will stop you also using any of your own “personalized” get argument arrangements as well, that it just ends up “sending through to the keeper” …
if (document.URL.indexOf('month=') != -1 && document.URL.indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + document.URL.split('year=')[1].split('&')[0].split('#')[0]), eval('' + document.URL.split('month=')[1].split('&')[0].split('#')[0]), 1);
} else if (('' + parent.document.URL).indexOf('month=') != -1 && ('' + parent.document.URL).indexOf('year=') != -1 && firstlook) {
now = new Date(eval('' + ('' + parent.document.URL).split('year=')[1].split('&')[0].split('#')[0]), eval('' + ('' + parent.document.URL).split('month=')[1].split('&')[0].split('#')[0]), 1);
}
… to it, making it editable and open to more personalization regarding your Editable Calendar, on the way to what we hope ends up being a more useful web application tool … while …
that HTML caption element’s onblur event …
var now = new Date(), firstDayIndex=0, daysThisMonth=0;
var origih=''; // is filled with innards of the original calendar table element at document.body onload
var calcapv=''; // is updated with HTML caption element innards before any user interaction
var usuff=''; // personalized get arguments go here
var changesmade=false; // goes to true when user blurb onkeypress happens within calendar tbody span elements or if the Month, Year changes
function newmy(ocap) {
if (ocap.innerHTML != calcapv) {
if (ocap.innerHTML.indexOf(',') != -1) {
if (mths.indexOf(ocap.innerHTML.split(',')[0].trim()) != -1 && eval('' + ocap.innerHTML.split(',')[1].trim().length) == 4) {
//changesmade=true;
usuff='&year=' + ocap.innerHTML.split(',')[1].trim() + '&month=' + mths.indexOf(ocap.innerHTML.split(',')[0].trim());
now = new Date(eval('' + ocap.innerHTML.split(',')[1].trim()), eval('' + mths.indexOf(ocap.innerHTML.split(',')[0].trim())), 1);
document.getElementById('wp-calendar').innerHTML=origih;
thismonth();
}
}
}
}
… can be used to store our “personalized get arguments” …
ready to be appended to the (web browser address bar) URL reaching Google Translate …
var valvalue='';
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out …
… we referenced when presenting Editable Calendar Google Translate Tutorial before the recent Editable Calendar Intl Tutorial? Today’s work plonks them back in by involving PHP, as our server side language that can write to and read from ascii text files stored on our RJM Programming domain web server. By, when needed, getting Google Translate to, when required (ie. we’ve decided this is when the keyboard onkeypress happens), reference a .php based RJM Programming URL, leaves that opportunity to …
Shape to write the calendar table innards to an ascii web server text file via Ajax … client side …
function ajaxit() {
var zhr = new XMLHttpRequest();
var zform = new FormData();
zform.append('tihis', document.getElementById('wp-calendar').innerHTML);
zhr.open('post', '/' + docURL.split('?')[0].split('#')[0].replace('.html','.php').replace('.htm','.php'), true);
zhr.send(zform);
}
Write the calendar table innards to an ascii web server text file via PHP … server side …
<?php
if (isset($_POST['tihis'])) {
file_put_contents('tihis.txt', str_replace('+',' ',urldecode($_POST['tihis'])));
exit;
}
?>
As needed, very soon after, as Google Translate is called with a .php based URL, substitute this saved table innards into a read from default representation of the HTML via PHP … server side …
Leaving any document.body onload event logics out (in first codeline of such onload event Javascript function), in such a scenario, leaving it all to Google Translate’s talents … client side …
if (('' + parent.document.URL).indexOf('.php') != -1) { return ''; }
Because the theme of our current Editable Calendar web application revolves around time stamps and day names and month names regarding yesterday’s Editable Calendar Google Translate Tutorial that means, regarding language internationalization ideas …
as well as yesterday’s Google Translate ideas … also available is …
Yesterday’s Editable Calendar Primer Tutorial‘s start to our Editable Calendar project was a very anglo-centric exercise. Today, we want to use the great …
… to help translate our inhouse burgeoning Editable Calendar skeletal contents to some non English languages.
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out in this changedsecond draftEditable Calendar you can try below, because we’re using the Google Translate “static URL” approach to the translations. But we will examine, down the track, if we can do better?! Again, stay tuned for more progression and internationalization over the days on this Editable Calendar project.
we’re channelling how in emails “word strings” starting with “http” become links … and so …
in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available
Here is the relevant Javascript we ended up with, for this …
Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!
When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …
When it comes to procedures …
Aaaaaarrrrgggghhhh …
Nuances count … regarding …
Errrrrrr …
Saving time …
Saving frustration
… resulting in the “easy to remember” acronym WANESS … we rest our cases!
What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.
Global variables initialization …
var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …
if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …
function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}
function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic …
function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}
Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changedevents_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!
it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs
… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!
Timing became really important with this integration of …
email
SMS
… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …
Month
Year
… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …
Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons …
<h1>Events in Month <button style=display:none; title='Share via Email' id=semail onclick="doemail('');">📧</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>📟</button> </h1>
New static HTML div element placed to the bottom of the body element …
<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …
Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to …
Record to Remember via Bookmark
… use the web browser Bookmarks to help you recall an Events in Month report with long entries.
Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …
… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!
To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …
var documentURL=document.URL;
… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …
#August__2023_00000__1
… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).
Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).
Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …
… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.
The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …
<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm
But, there’s an inherent weakness with the design, we’ll go into more into the future.
… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.
Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.
Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …
bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
pick Inspect option …
be in Elements tab of your Web Inspector … and …
highlight top <html> tag …
Context Menu with a right click or two finger gesture …
pick Copy -> Copy outerHTML … so that …
your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)
The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …
function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}
… the form submit buttons are reworded accordingly.
Another idea from this blog thread’s inspiration …
… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.
And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …
text shadow means by which the text of Event Calendar can be more impactive …
<div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out …
… we referenced when presenting Editable Calendar Google Translate Tutorial before the recent Editable Calendar Intl Tutorial? Today’s work plonks them back in by involving PHP, as our server side language that can write to and read from ascii text files stored on our RJM Programming domain web server. By, when needed, getting Google Translate to, when required (ie. we’ve decided this is when the keyboard onkeypress happens), reference a .php based RJM Programming URL, leaves that opportunity to …
Shape to write the calendar table innards to an ascii web server text file via Ajax … client side …
function ajaxit() {
var zhr = new XMLHttpRequest();
var zform = new FormData();
zform.append('tihis', document.getElementById('wp-calendar').innerHTML);
zhr.open('post', '/' + docURL.split('?')[0].split('#')[0].replace('.html','.php').replace('.htm','.php'), true);
zhr.send(zform);
}
Write the calendar table innards to an ascii web server text file via PHP … server side …
<?php
if (isset($_POST['tihis'])) {
file_put_contents('tihis.txt', str_replace('+',' ',urldecode($_POST['tihis'])));
exit;
}
?>
As needed, very soon after, as Google Translate is called with a .php based URL, substitute this saved table innards into a read from default representation of the HTML via PHP … server side …
Leaving any document.body onload event logics out (in first codeline of such onload event Javascript function), in such a scenario, leaving it all to Google Translate’s talents … client side …
if (('' + parent.document.URL).indexOf('.php') != -1) { return ''; }
Because the theme of our current Editable Calendar web application revolves around time stamps and day names and month names regarding yesterday’s Editable Calendar Google Translate Tutorial that means, regarding language internationalization ideas …
as well as yesterday’s Google Translate ideas … also available is …
Yesterday’s Editable Calendar Primer Tutorial‘s start to our Editable Calendar project was a very anglo-centric exercise. Today, we want to use the great …
… to help translate our inhouse burgeoning Editable Calendar skeletal contents to some non English languages.
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out in this changedsecond draftEditable Calendar you can try below, because we’re using the Google Translate “static URL” approach to the translations. But we will examine, down the track, if we can do better?! Again, stay tuned for more progression and internationalization over the days on this Editable Calendar project.
we’re channelling how in emails “word strings” starting with “http” become links … and so …
in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available
Here is the relevant Javascript we ended up with, for this …
Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!
When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …
When it comes to procedures …
Aaaaaarrrrgggghhhh …
Nuances count … regarding …
Errrrrrr …
Saving time …
Saving frustration
… resulting in the “easy to remember” acronym WANESS … we rest our cases!
What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.
Global variables initialization …
var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …
if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …
function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}
function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic …
function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}
Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changedevents_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!
it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs
… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!
Timing became really important with this integration of …
email
SMS
… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …
Month
Year
… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …
Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons …
<h1>Events in Month <button style=display:none; title='Share via Email' id=semail onclick="doemail('');">📧</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>📟</button> </h1>
New static HTML div element placed to the bottom of the body element …
<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …
Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to …
Record to Remember via Bookmark
… use the web browser Bookmarks to help you recall an Events in Month report with long entries.
Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …
… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!
To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …
var documentURL=document.URL;
… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …
#August__2023_00000__1
… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).
Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).
Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …
… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.
The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …
<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm
But, there’s an inherent weakness with the design, we’ll go into more into the future.
… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.
Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.
Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …
bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
pick Inspect option …
be in Elements tab of your Web Inspector … and …
highlight top <html> tag …
Context Menu with a right click or two finger gesture …
pick Copy -> Copy outerHTML … so that …
your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)
The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …
function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}
… the form submit buttons are reworded accordingly.
Another idea from this blog thread’s inspiration …
… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.
And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …
text shadow means by which the text of Event Calendar can be more impactive …
<div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
… be able to open a new popup window to the right which is a YouTube temporary playlist play scenario webpage, as well as remember this, and recreate this, for the next time
We break with our normal transmission, because we read something …
… Luuunnnaa?! Sarcasm does not look good on you (though lilac green’s not bad). And wipe that sardonic grin off your breakfast bowl food rearrangements you’ve just made?!
A feather to our boa constrictor … Luuunnnaa?! … get off the keyboard now … don’t you have some bone to pick, or that book about Dogma (tee hee) to read?!w
… on that Shower Songs YouTube embedded iframe inhouse playlist concept, further to Shower Songs Playlist Subset Revisit Tutorial. Some people, like Luna today, may want to bypass our Shower Songs just audio paradigm to dynamically create a temporary YouTube playlist and play video and audio together in a playlist in sequential order.
Anyway, we tweaked stuff to start the ball rolling making this be a possibility, as well as involving YouTube video ID PtR4cWb4HNg in the list to spark this concept into action. And later we’ll explain maybe why PtR4cWb4HNg should lead the comma separated list.
Radio Play via comma separate YouTube 11 character IDs or search ideas for an inhouse playlist (if any in a comma separated list is PtR4cWb4HNg temporary YouTube playlist audio and video play can be attempted) … 7 such uses Shower Songs … egs. Tragedy,Boogie Wonderland,Down Among the Dead Men flash … random … Tragedy ,Imagine*3,Breezin … ordered 5 … Tragedy ,Pina Colada Song,Fast Car … audio … Tragedy ,Galveston,Fix You … audio ordered,
Did You Know?
If at the time of launching (and deploying) into making one of these temporary YouTube playlists and you are logged into your own YouTube channel, more possibilities present, and today’s tutorial animated GIF shows a bit of this (though we found out about the virtue of putting PtR4cWb4HNg first later, alas).
The playlist gets played, and on non-mobile it may start itself. For each track you can use the Save button down near the bottom to save to a new (where we are recommending PtR4cWb4HNg to go first) or pre-existing public or private playlist. Okay, so you have this new YouTube playlist.
… in which case a brief error message may happen and it may start playing (on non-mobile) that YouTube playlist at track 2 … hence my advice regarding PtR4cWb4HNg … or …
A few attempts we made failed to “curb the enthusiasm” of the parent window left behind by the new window.open window of these Playlist Subsets the user defines, until we did something we can not recall ever doing before, namely …
var tdsarea=document.getElementsByTagName('td');
for (var iopj=eval(-1 + tdsarea.length); iopj>=0; iopj--) {
tdsarea[iopj].innerHTML=tdsarea[iopj].innerText;
}
… to not only be “curbing it’s enthusiasm to play” but also making that parent window end up being a “display snapshot only” of what had been going on before the user set up more than one “L=loop” looping textarea functionalities that defines a playlist subset scenario where that new window takes on the role of “enthusiastic play”.
add to an “individual song looping subset” with another “individual looping song” … and it also serves another “arcane usage” to our mind, we explain below …
out and about using a laptop lassooed to a mobile (or just the mobile itself) using mobile data (rather than Wi-Fi) this web browser Shower Songs web application depends on a connection as a song starts and if you have run out of never before tapped buttons to choose from (and the web application has lost it’s connection) it can become an impotent web application, sometimes, so that to add to the web application’s possibilities of button tapping, and often reawakening it, the better, we figure
… work did not function for as many scenarios as we would like. This problem requires Javascript web browser clientside debugging via a Web Browser web inspector “Developer” style of debugging (ie. deep and meaningful).
When it gets to the “deep and meaningful” we mean “comparing” console tab “good versus bad” types of reporting, which becomes untenable if too much is already being reported in our favourite “reporting tool conduit” for us …
console.warn([some messaging text]);
… you can read more about at YouTube API Caller Phase Two Console Warning Tutorial. Over the three window hierarchies, over quite some time, we’ve filled console.warn web inspector tab with so much information it is untenable for what we have to do debugging the issues above.
And so, today, we’re showing a “conditional approach” to controlling “console.warn” logging via “grandchild level” substitution of all “console.warn(” for “consolewarn(” and then the new Javascript function …
function consolewarn(inms) {
if (window.self != window.parent) {
if (('' + top.document.URL).indexOf('consolewarnoptinall') != -1 || ('' + top.document.URL).indexOf('consolewarnoptingrandchild') != -1) {
console.warn(inms);
}
} else if (('' + document.URL).indexOf('consolewarnoptoutall') == -1) {
console.warn(inms);
}
}
… and at the “parent level” substitution of all “console.warn(” for “consolewarn(” and then the new Javascript function …
function consolewarn(inms) {
if (window.self != window.parent) {
if (('' + top.document.URL).indexOf('consolewarnoptinall') != -1 || ('' + top.document.URL).indexOf('consolewarnoptinchild') != -1) {
console.warn(inms);
}
} else if (('' + document.URL).indexOf('consolewarnoptoutall') == -1) {
console.warn(inms);
}
}
… as an address bar controlling URL via, for example …
… means by which we can better hone in on the debugging issues as they arrive, so that self contained relevance fits on the one screen, and the Clear Console icon becomes an additional friend to our work, within …
YouTube API iOS Shower Song Subset Looping Tutorial
You’d think we’d be done with ablutions at this blog, but ingen måte … Luna (aka Måne) says … there’s all manner of molekylær interesse out there in and out of the fjords …
It’s not just H and the 2 and the O … it’s the art of the rollerulle.
… or so says “The Lu” (aka “Toalettet”) anyway!
Today’s Shower Songs tweak involves it’s “individual song looping” thinking. It occurred to us, that it’s use by a user might be what they want, it becomes a bit inflexible should they either …
want to “move on” to a new song … via …
add to an “individual song looping subset” with another “individual looping song” … and it also serves another “arcane usage” to our mind, we explain below …
out and about using a laptop lassooed to a mobile (or just the mobile itself) using mobile data (rather than Wi-Fi) this web browser Shower Songs web application depends on a connection as a song starts and if you have run out of never before tapped buttons to choose from (and the web application has lost it’s connection) it can become an impotent web application, sometimes, so that to add to the web applications’s possibilities of button tapping, and often reawakening it, the better, we figure
At least, on “mobile” iOS (which we have access to) can any “command line” feel of playlists ending up at our Shower Songs web application be arranged?
Pretty pithy, huh?! Before we launch into the great iOS Shortcuts app (we’ve talked about before at Shortcuts iOS App Email Arrival Sounds Tutorial) helping here, let’s outline some other simpler ideas you might want to try yourself in the DIY range of ideas …
Add to Home Screen … with a tailored (Playlist) Title … this being our preferred option to future proof the remembering … but also available is …
Add Bookmark … or …
Add Favourite
Make your web browser’s opening Home Page be a URL of interest (like one of these playlists) within that web browser app’s Settings area
as well as the Shower Songs (as well as Tabular Single Row Media Gallery) web application (with Playlists of 7) can have a dropdown be populated ready to choose and replay that Playlist (that can be email or SMS shared with a recipient)
Okay, all that being said, the great Shortcuts iOS app can serve two purposes a bit “above and beyond this” …
you can DIY build an iOS feeling and “hybrid looking” amalgum of iOS and “old days” dialog box “desktop feeling” personalized application(s) …
the act of constructing any one is sure to inspire other iOS automation ideas you might have
We’re sure lots of you more aux fait with the great iOS Shortcuts app can do better than the “bit kludgy” effort we came up with, and that is the joy of programming, huh?! It can come down to …
what you think is the most efficient …
you feel you can handle … and …
end up with a better automated solution … but …
it’s not “all about efficiency” always … it’s also learning about new methodologies you might have to spend more time discovering the ins and outs about
As a case in point, it will be hard to stop yours truly just (iOS mobile) opening the relevant Radio Play web application in a web browser, and going from there (with Bookmarks and Favourites), but there you go?! In this case, perhaps yes, but you should see all the other Messaging and automation possibilities that a peruse of the iOS Shortcuts app can open up to you!
Todays’ animated GIF presentation shows our endeavours “warts and all” (latching onto, intervention wise, when a user taps the Music mobile app icon) for your perusal.
Can a list of seven non-YouTube-ID comma separated values (ie. YouTube song search basis strings) be entered and still end up at the Shower Songs web application?
Well, yes, that’s possible if an intervention point can be arranged. After all, the command line prompt goes …
Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist … 7 for Shower Songs … egs. Tragedy,Boogie Wonderland,Down Among the Dead Men … random … Tragedy ,Imagine*3,Breezin … ordered 5 … Tragedy ,Pina Colada Son,Fast Car … audio … Tragedy ,Galveston,Fix You … audio ordered, and go.
… begging the question “What happens if we enter, at the command line …”
play
Tragedy ,Pina Colada Song,Fast Car,Breezin,Galveston,Fix You,Imagine
… and that’s where “our fun” (chortle, chortle) began (yeh, right … after a Goose Chase with it being the seventh one) because that seventh YouTube video ID (involved with YouTube “Imagine” search (and something about this rings a bell)) comes back with …
This content can’t be played on your mobile browser. Get the YouTube app to start watching.
Ouch?! What to do? Looking optimistically on this, it’s good to discover it now (yeh, right).
macOS (building onthe changedplay.sh shell script) …
#!/bin/bash
rest=""
if [ -z "$1" ]; then
echo "Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... 7 such uses Shower Songs ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men flash ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Song,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go."
read ans
else
preans="$1"
ans="$1 $2 $3 $4 $5 $6 $7 $8 $9"
while [ ! -z "$9" ]; do
shift
rest="${rest} $9"
done
ans="$ans${rest}"
fi
if [ ! -z "$ans" ]; then
commas_only="${ans//[^,]}"
if [ "${#preans}" == "83" ]; then
if [ "${#commas_only}" == "6" ]; then
open "https://www.rjmprogramming.com.au/$1"
else
open "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html $ans"
fi
else
open "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html $ans"
fi
fi
exit
Windows (building onthe changedplay.bat script) …
@echo off
rem play.bat
rem RJM Programming
rem November, 2025
set passed=
set rest=
set seven=0
if trick==trick%1 (
set /p ans=Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... 7 for Shower Songs ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Son,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go.
set ansplus="%ans% "
if "%ansplus:~12,1%"=="," (
if "%ansplus:~24,1%"=="," (
if "%ansplus:~36,1%"=="," (
if "%ansplus:~48,1%"=="," (
if "%ansplus:~60,1%"=="," (
if "%ansplus:~72,1%"=="," (
if "%ansplus:~84,1%"==" " (
set seven=7
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/shower_songs.html?thelist=%ans%"
goto :end
) else (
set seven=6
)
) else (
set seven=5
)
) else (
set seven=4
)
) else (
set seven=3
)
) else (
set seven=2
)
) else (
set seven=1
)
) else (
set seven=0
)
) else (
set ans="%1"
set ansplus="%1,%2,%3,%4,%5,%6,%7 %8 "
if "%ansplus:~12,1%"=="," (
if "%ansplus:~24,1%"=="," (
if "%ansplus:~36,1%"=="," (
if "%ansplus:~48,1%"=="," (
if "%ansplus:~60,1%"=="," (
if "%ansplus:~72,1%"=="," (
if trick==trick%8 (
set seven=7
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/shower_songs.html?thelist=%1,%2,%3,%4,%5,%6,%7"
goto :end
) else (
set seven=6
)
) else (
set seven=5
)
) else (
set seven=4
)
) else (
set seven=3
)
) else (
set seven=2
)
) else (
set seven=1
)
) else (
set seven=0
)
set passed=yes
if trick==trick%2 goto :loop
set ans="%ans%,%2"
if trick==trick%3 goto :loop
set ans="%ans%,%3"
if trick==trick%4 goto :loop
set ans="%ans%,%4"
if trick==trick%5 goto :loop
set ans="%ans%,%5"
if trick==trick%6 goto :loop
set ans="%ans%,%6"
if trick==trick%7 goto :loop
set ans="%ans%,%7"
if trick==trick%8 goto :loop
set ans="%ans%,%8"
if trick==trick%9 goto :loop
set ans="%ans%,%9"
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans:"=%"
:loop
if "%9"=="" (
@echo off
) else (
shift
set rest="%rest% %9"
goto :loop
)
set ans="%ans%%rest%"
set ans=%ans:"=%
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
if trick==trick%passed% (
set ans=%ans:"=%
if "%ans:,=%"=="%ans%" (
echo "Nothing to do."
) else (
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
)
rem exit
:end
… and involving, if you are on macOS or Windows implementing accordingly …
YouTube API Caller Radio Play Doctype Meta Tutorial
It took a session on this MacBook Air’s macOS Firefox web browser trying the Tabular Single Row Media Gallery web application to emphasise the importance of more consistency checking on …
doctype HTML tagdeclaration …
<!DOCTYPE html>
… and …
meta HTML display tagcharset attribute …
<meta charset="UTF-8">
… existence in web applications we create, to the point that not having the second one defined was stopping proper webpage display on Firefox. Was this a portent of what is to come soon on more web browsers?!
Let’s look at the purpose of these two HTML tagthingos …
What’s the scripting here? They are called batch files with extension *.bat we’re calling play.bat (going back to the DOS days of the ’70’s again). With *.bat you just need to plonk your play.bat anywhere within the Windows local system path, and the word (now command) “play” will do the rest anywhere in that user’s command line environment. And the batch file has …
@echo off
rem play.bat
rem RJM Programming
rem November, 2025
set passed=
set rest=
if trick==trick%1 (
set /p ans=Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Son,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go.
) else (
set ans="%1"
set passed=yes
if trick==trick%2 goto :loop
set ans="%ans%,%2"
if trick==trick%3 goto :loop
set ans="%ans%,%3"
if trick==trick%4 goto :loop
set ans="%ans%,%4"
if trick==trick%5 goto :loop
set ans="%ans%,%5"
if trick==trick%6 goto :loop
set ans="%ans%,%6"
if trick==trick%7 goto :loop
set ans="%ans%,%7"
if trick==trick%8 goto :loop
set ans="%ans%,%8"
if trick==trick%9 goto :loop
set ans="%ans%,%9"
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans:"=%"
:loop
if "%9"=="" (
@echo off
) else (
shift
set rest="%rest%,%9"
goto :loop
)
set ans="%ans%%rest%"
set ans=%ans:"=%
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
if trick==trick%passed% (
set ans=%ans:"=%
if "%ans:,=%"=="%ans%" (
echo "Nothing to do."
) else (
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
)
rem exit
:end
… as your starting gambit, here, where the equivalent of macOS’s “open” is “explorer.exe” on Windows (ie. the File Explorer) … almost as cute!
YouTube API Caller Radio Play Playlist Local System Tutorial
The ..
command line
… of a laptop or notebook or personal computer can be an environment to relax from the GUI aspects of your modern operating systems and think about …
processes
batch driven tasks
task simplifications
… and in the category of that last one, on our local macOS operating system we envisaged a day we could slip back to the “quiet world” of the “command line” and type a command like …
play Tragedy,Imagine,Down Among the Dead Men PAN -
… and get somewhere playing those songs through a web browser accessing the YouTube API and our interfacings to it in webpage software form, playing in a continuous Radio Play style.
You see, with all the distractions, only having to remember …
play
… no matter where we were in “command line” woooorrrrllllddd seemed the ideal, and yes, it’s possible. However, we think explaining …
bash /Applications/MAMP/htdocs/play.ksh Tragedy,Imagine,Down Among the Dead Men PAN -
… is a necessary dividing line between two parts to the discussion. So what’s bash? It’s the shell we associate as your macOS default shell (we get to via the “out of the box” macOS desktop application called Terminal) … well, it used to be … anyway, the first line of (what started as play.ksh then play.sh and then eventually, just, play … as a filename that is) …
#!/bin/bash
rest=""
if [ -z "$1" ]; then
echo "Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Song,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go."
read ans
else
ans="$1 $2 $3 $4 $5 $6 $7 $8 $9"
while [ ! -z "$9" ]; do
shift
rest="${rest} $9"
done
ans="$ans${rest}"
fi
if [ ! -z "$ans" ]; then
open "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html $ans"
fi
exit
… tells the local system use the bash shell to interpret this what you might like to think of as an executable script (which we went chmod 777 play regarding to make it so).
Okay, so we wrote this script in /Applications/MAMP/htdocs/ and that …
bash /Applications/MAMP/htdocs/play Tragedy,Imagine,Down Among the Dead Men PAN -
… could swing it at this stage, or even being in /Applications/MAMP/htdocs/ and going …
./play Tragedy,Imagine,Down Among the Dead Men PAN -
… but (even in /Applications/MAMP/htdocs/) …
play Tragedy,Imagine,Down Among the Dead Men PAN -
… is most likely to reply something like …
zsh: no such command
As the programmer, perhaps you could leave it at that with your “insider knowledge”, or if “play” as a command already meant something (a test that would be good to do before any such proposed work), otherwise … do you really think you’ll remember next Wednesday? So …
enter into the wooooorrrrllllddd of aliases (thanks to this great advice, thanks) … eg.
alias play="/Applications/MAMP/htdocs/play"
placed into …
~/.bashrc
… via (in our case) …
vi ~/.bashrc
then …
source ~/.bashrc
… needs to be included into the login procedure of …
~/.profile
…via (in our case) …
vi ~/.profile
to make …
play
… as the interactively asking alternative to the argumentative example of …
play Tragedy,Imagine,Down Among the Dead Men PAN -
… mean something … and anywhere
… all of this possible because of the brilliantly useful macOS (not Linux nor unix) …
… opener of web browsers from the command line blowing of our tiny little rather still naive mind … mind you, VAX/VMS “phone” command from the ’70’s might have even been better?! And then there were those punch cards … in the hopper … and if you got a character wrong … back to the end of the queue.
By the way, this brought into play for us what your default web browser is. Ours is Safari yet we develop more on Google Chrome. Safari, for us, does more Popup Blocking, and it is a thing to look out for should a window not open when expected (and these command line methodologies don’t get past you still having to unmute and/or click/tap once before the Radio Play mode of play kicks off). To undo the Popup Block there will probably be a means to do this to the right of the web browser address bar.
But there is a point. Today represents an improvement on yesterday’s arrangements for dynamic playlist creation regarding our Tabular Single Row Media Gallery web application via the playlist emoji button 🔀 click/tap or on the web browser command line. With yesterday’s scenario, had you entered as your internal playlist comma separated string …
Born to Run,Born to Run,Born to Run,Born to Run
… without the …
Burp …
… that is you’d have ended up with the top find off a YouTube search repeated 4 times. Is this ideal? We asked Nala and Luna but, unfortunately they were busy, so they left me with the decision, and we came up with …
That’s kind of silly.
For these few reasons …
variety
album style Radio Play listening possibilities
perhaps getting around that …
This video requires payment to watch YouTube
… issue we talked about before … because you could uncheck it’s checkbox if the error appears
giving non-top search items a go
… we decided to “mix it up” regarding entries like …
Born to Run,Born to Run,Born to Run,Born to Run
… as well as the simpler …
Born to Run*4
… so that repeats don’t cause users indigestion … chortle, chortle.
Here’s the Javascript turning Born to Run*4 into Born to Run,Born to Run,Born to Run,Born to Run …
YouTube API Caller Radio Play Playlist Songname Search List Address Bar Tutorial
Don’t know if you are like me, but especially when on this MacBook Air we’re, forever, using a web browser address bar to type in, what we’ve set up as, a Google search engine webpage query. Rather than even going to Google webpage first, that is!
Most people don’t want to know about arcane web application argument arrangements … though we’re very fond of the 5 minute ones?!
What’s wrong with some of you? Who would not relish the thought of …
&or?andgo=y # will click the Radio button after all above (unless notgo is mentioned in the list somewhere), as relevant, with the programmatically determined derived YouTube ID list via YouTube search(es)
💬🦆 Come On!
… when, after a bit of thought and tweaks to our RJM Programming domain’s Apache Document Root residing 404.shtml webpage Javascript …
if (document.URL.indexOf('rjmprogramming.com.au/HTMLCSS/swipe_media.htm') != -1) {
if (decodeURIComponent(document.URL).indexOf(',') != -1) {
if (decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'')).toLowerCase().indexOf('notgo') != -1) {
location.href='//www.rjmprogramming.com.au/HTMLCSS/swipe_media.html?thelist=' + encodeURIComponent(decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace('notgo','').replace('NOTGO','').replace('Notgo','')));
} else {
location.href='//www.rjmprogramming.com.au/HTMLCSS/swipe_media.html?andgo=y&thelist=' + encodeURIComponent(decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'')));
exit;
}
} else if (decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'')).toLowerCase().indexOf('ask') != -1) {
location.href='//www.rjmprogramming.com.au/HTMLCSS/swipe_media.html?ask=y';
exit;
}
}
… now all you need to add after that link above, on the web browser address bar is …
[commaSeparatedList_decodeURIComponent_ized]
… ie. just type a comma separated YouTube song search basis link after the link wording above
YouTube API Caller Radio Play Playlist Songname Search List Dropdown Tutorial
Around here, regarding web application work, we really like dropdown (ie. HTML select element) work because a lot of information can be …
stored
displayed (often taking up minimal webpage space)
it’s option subelements can have an event life of their own, or be part of the select element event whole picture
dropdown multiple selection mode (as with today’s work) can …
still support your single choice scenario
we’ve coded today’s dropdown to support, as applicable, ordered single click choices … versus …
can allow for control key (sets of choices not necessarily adjacent) and shift key (blocks of choices)
can respond (as with today’s dropdown) to preset selections established at creation via dropdown (ie. select) innerHTML option subelement selected attribute setting
dropdown events onmousedown and ontouchdown (as for today’s dropdown) and onclick can be harnessed ahead of an onchange event … and in today’s case can help unset any preset option subelement selected attribute setting(s)
Just so flexible! Subelement option CSS styling can be less friendly, but even so, often background colouring is possible, and we do a bit of that with the work from yesterday’s YouTube API Caller Radio Play Playlist Songname Search List Tutorial and today’s better “shored up” work you can see a bit of below (where your starting point might be that we “wrap” this particular Javascript prompt window through the Javascript overprompt function below) …
var outplist='', kaconto=null, kacontoarr=[], rconeis=155, gconeis=255, bconeis=255;
function karlook(iois) {
var aconto=null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
if (outplist.indexOf(',') != -1) {
kaconto=aconto;
setTimeout(function(){ kaconto.getElementById('ajaxs').style.cursor='progress'; }, 3000);
setTimeout(postkarlook, 7000);
} else if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
kaconto=aconto;
setTimeout(mpostkarlook, 7000);
}
}
}
}
function karlookagain(iois,idxn) {
var aconto=null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
if (eval('' + kacontoarr.length) > eval('' + idxn)) {
kacontoarr[eval('' + idxn)]=aconto;
} else {
kacontoarr.push(aconto);
}
}
}
}
function mpostkarlook() {
if (kaconto.getElementById('ajaxs')) {
var kbuts=kaconto.getElementsByTagName('input');
for (var lbuts=0; lbuts<kbuts.length; lbuts++) {
if (('' + kbuts[lbuts].type) == 'button') {
if (('' + kbuts[lbuts].value).indexOf('Radio') == 0) {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', String.fromCharCode(10) + 'Radio');
}
}
}
} else {
setTimeout(mpostkarlook, 5000);
}
}
function postkarlook() {
var selectedones='', oktogo=true, hihi=0;
if (kaconto.getElementById('ajaxs')) {
//alert(kaconto.getElementById('ajaxs').innerHTML);
for (hihi=0; hihi<kacontoarr.length; hihi++) {
if (!kacontoarr[hihi]) {
oktogo=false;
//alert('hihi=' + hihi + ' NOT okay');
} else if (!kacontoarr[hihi].getElementById('ajaxs')) {
oktogo=false;
//alert('hihi=' + hihi + ' not okay');
}
}
if (!oktogo) {
setTimeout(postkarlook, 5000);
} else if (kaconto.getElementById('ajaxs').innerHTML.indexOf('... and then ...') == -1) {
kaconto.getElementById('ajaxs').style.cursor='pointer';
var otherps=baeisf();
if (otherps != '' || navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
var kbuts=kaconto.getElementsByTagName('input');
for (var lbuts=0; lbuts<kbuts.length; lbuts++) {
if (('' + kbuts[lbuts].type) == 'button') {
if (('' + kbuts[lbuts].value).indexOf('Radio') == 0) {
var wasvl=kbuts[lbuts].value;
if (otherps == ' ') {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', 'Ordered ' + String.fromCharCode(10) + 'Audio ' + String.fromCharCode(10) + 'Radio');
} else if (otherps == ' ') {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', 'Audio ' + String.fromCharCode(10) + 'Radio');
} else if (otherps == ' ') {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', 'Ordered ' + String.fromCharCode(10) + 'Radio');
} else {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', String.fromCharCode(10) + 'Radio');
}
}
}
}
}
selectedones=(kaconto.getElementById('ajaxs').innerHTML.split(kaconto.getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>')[1].split('</option>')[0] + '</option>').replace('>', ' class=uptop selected>');
for (hihi=0; hihi<kacontoarr.length; hihi++) {
if (rconeis == gconeis && rconeis == bconeis) {
bconeis-=7;
if (bconeis < 0) { bconeis+=255; }
} else if (rconeis == gconeis) {
gconeis-=7;
if (gconeis < 0) { gconeis+=255; }
} else {
rconeis-=7;
if (rconeis < 0) { rconeis+=255; }
}
selectedones+=((kacontoarr[hihi].getElementById('ajaxs').innerHTML.split(kacontoarr[hihi].getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>')[1].split('</option>')[0] + '</option>').replace('>', ' class=uptop selected>')).replace(/option\ onclick\=/g, 'option style=background-color:rgb(' + rconeis + ',' + gconeis + ',' + bconeis + '); onclick=');
kaconto.getElementById('ajaxs').innerHTML+='<option value="">... and then ...</option>';
kaconto.getElementById('ajaxs').innerHTML+=kacontoarr[hihi].getElementById('ajaxs').innerHTML.split(kacontoarr[hihi].getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>')[1].replace(/option\ onclick\=/g, 'option style=background-color:rgb(' + rconeis + ',' + gconeis + ',' + bconeis + '); onclick=');
//alert(kacontoarr[hihi].getElementById('ajaxs').innerHTML);
}
//alert(selectedones);
kaconto.getElementById('ajaxs').innerHTML=kaconto.getElementById('ajaxs').innerHTML.replace(kaconto.getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>', kaconto.getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>' + selectedones + (selectedones != '' ? '<option id=optclk value="">____ Selection above available (resettable via click here) as you click/tap button at right ____</option>' : ''));
if (selectedones != '') {
kaconto.getElementById('ajaxs').onmousedown=function(event){ if (event.target.parentNode.outerHTML.split('>')[0].indexOf(' data-flagged=') == -1) { event.target.parentNode.setAttribute('data-flagged','y'); event.target.parentNode.innerHTML=event.target.parentNode.innerHTML.replace(/\ selected/g, ' ').replace('____ Selection above available (resettable via click here) as you click/tap button at right ____','____ Please select choices in usual multiple choice dropdown ways ____'); } };
kaconto.getElementById('ajaxs').ontouchdown=function(event){ if (event.target.parentNode.outerHTML.split('>')[0].indexOf(' data-flagged=') == -1) { event.target.parentNode.setAttribute('data-flagged','y'); event.target.parentNode.innerHTML=event.target.parentNode.innerHTML.replace(/\ selected/g, ' ').replace('____ Selection above available (resettable via click here) as you click/tap button at right ____','____ Please select choices in usual multiple choice dropdown ways ____'); } };
}
}
}
}
function baeisf() {
var outarrp=outplist.split(',');
if (eval('' + outarrp.length) > 1) {
return outarrp[0].replace(outarrp[0].trim(), '');
}
return '';
}
And so, today …
we add onto the default random ordering mode of Radio Play via YouTube search comma separated list, the further choices ordered or audio or audio ordered, as per the relevant Javascript prompt blurb that is now going …
Enter YouTube 11 character code video (comma separated) or audio stream only of video (semicolon) list to Radio Play (and add ! to start in shuffle mode and/or # for the order you enter and/or & for whole list) … or a YouTube Search String basis for playlist via a Radio📻? button to activate, later. Comma separateds can be search ideas … egs. Tragedy,Boogie Wonderland,Down Among the Dead Men … random … Tragedy ,Imagine,Breezin … ordered … Tragedy ,Pina Colada Song,Fast Car … audio … Tragedy ,Galveston,Angel of the Morning … audio ordered. Double click on this cell for playlist redefinitions.
started allowing for dropdown events onmousedown and ontouchdown be able to reset the preset choices we programmatically select for the user should they be happy (but our motivation for this event work was the potential to run into a YouTube video that gives the error …
We break with our normal transmission, because we read something …
… Luuunnnaa?! Sarcasm does not look good on you (though lilac green’s not bad). And wipe that sardonic grin off your breakfast bowl food rearrangements you’ve just made?!
A feather to our boa constrictor … Luuunnnaa?! … get off the keyboard now … don’t you have some bone to pick, or that book about Dogma (tee hee) to read?!w
… on that Shower Songs YouTube embedded iframe inhouse playlist concept, further to Shower Songs Playlist Subset Revisit Tutorial. Some people, like Luna today, may want to bypass our Shower Songs just audio paradigm to dynamically create a temporary YouTube playlist and play video and audio together in a playlist in sequential order.
Anyway, we tweaked stuff to start the ball rolling making this be a possibility, as well as involving YouTube video ID PtR4cWb4HNg in the list to spark this concept into action. And later we’ll explain maybe why PtR4cWb4HNg should lead the comma separated list.
Radio Play via comma separate YouTube 11 character IDs or search ideas for an inhouse playlist (if any in a comma separated list is PtR4cWb4HNg temporary YouTube playlist audio and video play can be attempted) … 7 such uses Shower Songs … egs. Tragedy,Boogie Wonderland,Down Among the Dead Men flash … random … Tragedy ,Imagine*3,Breezin … ordered 5 … Tragedy ,Pina Colada Song,Fast Car … audio … Tragedy ,Galveston,Fix You … audio ordered,
Did You Know?
If at the time of launching (and deploying) into making one of these temporary YouTube playlists and you are logged into your own YouTube channel, more possibilities present, and today’s tutorial animated GIF shows a bit of this (though we found out about the virtue of putting PtR4cWb4HNg first later, alas).
The playlist gets played, and on non-mobile it may start itself. For each track you can use the Save button down near the bottom to save to a new (where we are recommending PtR4cWb4HNg to go first) or pre-existing public or private playlist. Okay, so you have this new YouTube playlist.
… in which case a brief error message may happen and it may start playing (on non-mobile) that YouTube playlist at track 2 … hence my advice regarding PtR4cWb4HNg … or …
A few attempts we made failed to “curb the enthusiasm” of the parent window left behind by the new window.open window of these Playlist Subsets the user defines, until we did something we can not recall ever doing before, namely …
var tdsarea=document.getElementsByTagName('td');
for (var iopj=eval(-1 + tdsarea.length); iopj>=0; iopj--) {
tdsarea[iopj].innerHTML=tdsarea[iopj].innerText;
}
… to not only be “curbing it’s enthusiasm to play” but also making that parent window end up being a “display snapshot only” of what had been going on before the user set up more than one “L=loop” looping textarea functionalities that defines a playlist subset scenario where that new window takes on the role of “enthusiastic play”.
add to an “individual song looping subset” with another “individual looping song” … and it also serves another “arcane usage” to our mind, we explain below …
out and about using a laptop lassooed to a mobile (or just the mobile itself) using mobile data (rather than Wi-Fi) this web browser Shower Songs web application depends on a connection as a song starts and if you have run out of never before tapped buttons to choose from (and the web application has lost it’s connection) it can become an impotent web application, sometimes, so that to add to the web application’s possibilities of button tapping, and often reawakening it, the better, we figure
… work did not function for as many scenarios as we would like. This problem requires Javascript web browser clientside debugging via a Web Browser web inspector “Developer” style of debugging (ie. deep and meaningful).
When it gets to the “deep and meaningful” we mean “comparing” console tab “good versus bad” types of reporting, which becomes untenable if too much is already being reported in our favourite “reporting tool conduit” for us …
console.warn([some messaging text]);
… you can read more about at YouTube API Caller Phase Two Console Warning Tutorial. Over the three window hierarchies, over quite some time, we’ve filled console.warn web inspector tab with so much information it is untenable for what we have to do debugging the issues above.
And so, today, we’re showing a “conditional approach” to controlling “console.warn” logging via “grandchild level” substitution of all “console.warn(” for “consolewarn(” and then the new Javascript function …
function consolewarn(inms) {
if (window.self != window.parent) {
if (('' + top.document.URL).indexOf('consolewarnoptinall') != -1 || ('' + top.document.URL).indexOf('consolewarnoptingrandchild') != -1) {
console.warn(inms);
}
} else if (('' + document.URL).indexOf('consolewarnoptoutall') == -1) {
console.warn(inms);
}
}
… and at the “parent level” substitution of all “console.warn(” for “consolewarn(” and then the new Javascript function …
function consolewarn(inms) {
if (window.self != window.parent) {
if (('' + top.document.URL).indexOf('consolewarnoptinall') != -1 || ('' + top.document.URL).indexOf('consolewarnoptinchild') != -1) {
console.warn(inms);
}
} else if (('' + document.URL).indexOf('consolewarnoptoutall') == -1) {
console.warn(inms);
}
}
… as an address bar controlling URL via, for example …
… means by which we can better hone in on the debugging issues as they arrive, so that self contained relevance fits on the one screen, and the Clear Console icon becomes an additional friend to our work, within …
YouTube API iOS Shower Song Subset Looping Tutorial
You’d think we’d be done with ablutions at this blog, but ingen måte … Luna (aka Måne) says … there’s all manner of molekylær interesse out there in and out of the fjords …
It’s not just H and the 2 and the O … it’s the art of the rollerulle.
… or so says “The Lu” (aka “Toalettet”) anyway!
Today’s Shower Songs tweak involves it’s “individual song looping” thinking. It occurred to us, that it’s use by a user might be what they want, it becomes a bit inflexible should they either …
want to “move on” to a new song … via …
add to an “individual song looping subset” with another “individual looping song” … and it also serves another “arcane usage” to our mind, we explain below …
out and about using a laptop lassooed to a mobile (or just the mobile itself) using mobile data (rather than Wi-Fi) this web browser Shower Songs web application depends on a connection as a song starts and if you have run out of never before tapped buttons to choose from (and the web application has lost it’s connection) it can become an impotent web application, sometimes, so that to add to the web applications’s possibilities of button tapping, and often reawakening it, the better, we figure
At least, on “mobile” iOS (which we have access to) can any “command line” feel of playlists ending up at our Shower Songs web application be arranged?
Pretty pithy, huh?! Before we launch into the great iOS Shortcuts app (we’ve talked about before at Shortcuts iOS App Email Arrival Sounds Tutorial) helping here, let’s outline some other simpler ideas you might want to try yourself in the DIY range of ideas …
Add to Home Screen … with a tailored (Playlist) Title … this being our preferred option to future proof the remembering … but also available is …
Add Bookmark … or …
Add Favourite
Make your web browser’s opening Home Page be a URL of interest (like one of these playlists) within that web browser app’s Settings area
as well as the Shower Songs (as well as Tabular Single Row Media Gallery) web application (with Playlists of 7) can have a dropdown be populated ready to choose and replay that Playlist (that can be email or SMS shared with a recipient)
Okay, all that being said, the great Shortcuts iOS app can serve two purposes a bit “above and beyond this” …
you can DIY build an iOS feeling and “hybrid looking” amalgum of iOS and “old days” dialog box “desktop feeling” personalized application(s) …
the act of constructing any one is sure to inspire other iOS automation ideas you might have
We’re sure lots of you more aux fait with the great iOS Shortcuts app can do better than the “bit kludgy” effort we came up with, and that is the joy of programming, huh?! It can come down to …
what you think is the most efficient …
you feel you can handle … and …
end up with a better automated solution … but …
it’s not “all about efficiency” always … it’s also learning about new methodologies you might have to spend more time discovering the ins and outs about
As a case in point, it will be hard to stop yours truly just (iOS mobile) opening the relevant Radio Play web application in a web browser, and going from there (with Bookmarks and Favourites), but there you go?! In this case, perhaps yes, but you should see all the other Messaging and automation possibilities that a peruse of the iOS Shortcuts app can open up to you!
Todays’ animated GIF presentation shows our endeavours “warts and all” (latching onto, intervention wise, when a user taps the Music mobile app icon) for your perusal.
Can a list of seven non-YouTube-ID comma separated values (ie. YouTube song search basis strings) be entered and still end up at the Shower Songs web application?
Well, yes, that’s possible if an intervention point can be arranged. After all, the command line prompt goes …
Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist … 7 for Shower Songs … egs. Tragedy,Boogie Wonderland,Down Among the Dead Men … random … Tragedy ,Imagine*3,Breezin … ordered 5 … Tragedy ,Pina Colada Son,Fast Car … audio … Tragedy ,Galveston,Fix You … audio ordered, and go.
… begging the question “What happens if we enter, at the command line …”
play
Tragedy ,Pina Colada Song,Fast Car,Breezin,Galveston,Fix You,Imagine
… and that’s where “our fun” (chortle, chortle) began (yeh, right … after a Goose Chase with it being the seventh one) because that seventh YouTube video ID (involved with YouTube “Imagine” search (and something about this rings a bell)) comes back with …
This content can’t be played on your mobile browser. Get the YouTube app to start watching.
Ouch?! What to do? Looking optimistically on this, it’s good to discover it now (yeh, right).
macOS (building onthe changedplay.sh shell script) …
#!/bin/bash
rest=""
if [ -z "$1" ]; then
echo "Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... 7 such uses Shower Songs ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men flash ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Song,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go."
read ans
else
preans="$1"
ans="$1 $2 $3 $4 $5 $6 $7 $8 $9"
while [ ! -z "$9" ]; do
shift
rest="${rest} $9"
done
ans="$ans${rest}"
fi
if [ ! -z "$ans" ]; then
commas_only="${ans//[^,]}"
if [ "${#preans}" == "83" ]; then
if [ "${#commas_only}" == "6" ]; then
open "https://www.rjmprogramming.com.au/$1"
else
open "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html $ans"
fi
else
open "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html $ans"
fi
fi
exit
Windows (building onthe changedplay.bat script) …
@echo off
rem play.bat
rem RJM Programming
rem November, 2025
set passed=
set rest=
set seven=0
if trick==trick%1 (
set /p ans=Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... 7 for Shower Songs ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Son,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go.
set ansplus="%ans% "
if "%ansplus:~12,1%"=="," (
if "%ansplus:~24,1%"=="," (
if "%ansplus:~36,1%"=="," (
if "%ansplus:~48,1%"=="," (
if "%ansplus:~60,1%"=="," (
if "%ansplus:~72,1%"=="," (
if "%ansplus:~84,1%"==" " (
set seven=7
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/shower_songs.html?thelist=%ans%"
goto :end
) else (
set seven=6
)
) else (
set seven=5
)
) else (
set seven=4
)
) else (
set seven=3
)
) else (
set seven=2
)
) else (
set seven=1
)
) else (
set seven=0
)
) else (
set ans="%1"
set ansplus="%1,%2,%3,%4,%5,%6,%7 %8 "
if "%ansplus:~12,1%"=="," (
if "%ansplus:~24,1%"=="," (
if "%ansplus:~36,1%"=="," (
if "%ansplus:~48,1%"=="," (
if "%ansplus:~60,1%"=="," (
if "%ansplus:~72,1%"=="," (
if trick==trick%8 (
set seven=7
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/shower_songs.html?thelist=%1,%2,%3,%4,%5,%6,%7"
goto :end
) else (
set seven=6
)
) else (
set seven=5
)
) else (
set seven=4
)
) else (
set seven=3
)
) else (
set seven=2
)
) else (
set seven=1
)
) else (
set seven=0
)
set passed=yes
if trick==trick%2 goto :loop
set ans="%ans%,%2"
if trick==trick%3 goto :loop
set ans="%ans%,%3"
if trick==trick%4 goto :loop
set ans="%ans%,%4"
if trick==trick%5 goto :loop
set ans="%ans%,%5"
if trick==trick%6 goto :loop
set ans="%ans%,%6"
if trick==trick%7 goto :loop
set ans="%ans%,%7"
if trick==trick%8 goto :loop
set ans="%ans%,%8"
if trick==trick%9 goto :loop
set ans="%ans%,%9"
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans:"=%"
:loop
if "%9"=="" (
@echo off
) else (
shift
set rest="%rest% %9"
goto :loop
)
set ans="%ans%%rest%"
set ans=%ans:"=%
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
if trick==trick%passed% (
set ans=%ans:"=%
if "%ans:,=%"=="%ans%" (
echo "Nothing to do."
) else (
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
)
rem exit
:end
… and involving, if you are on macOS or Windows implementing accordingly …
YouTube API Caller Radio Play Doctype Meta Tutorial
It took a session on this MacBook Air’s macOS Firefox web browser trying the Tabular Single Row Media Gallery web application to emphasise the importance of more consistency checking on …
doctype HTML tagdeclaration …
<!DOCTYPE html>
… and …
meta HTML display tagcharset attribute …
<meta charset="UTF-8">
… existence in web applications we create, to the point that not having the second one defined was stopping proper webpage display on Firefox. Was this a portent of what is to come soon on more web browsers?!
Let’s look at the purpose of these two HTML tagthingos …
What’s the scripting here? They are called batch files with extension *.bat we’re calling play.bat (going back to the DOS days of the ’70’s again). With *.bat you just need to plonk your play.bat anywhere within the Windows local system path, and the word (now command) “play” will do the rest anywhere in that user’s command line environment. And the batch file has …
@echo off
rem play.bat
rem RJM Programming
rem November, 2025
set passed=
set rest=
if trick==trick%1 (
set /p ans=Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Son,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go.
) else (
set ans="%1"
set passed=yes
if trick==trick%2 goto :loop
set ans="%ans%,%2"
if trick==trick%3 goto :loop
set ans="%ans%,%3"
if trick==trick%4 goto :loop
set ans="%ans%,%4"
if trick==trick%5 goto :loop
set ans="%ans%,%5"
if trick==trick%6 goto :loop
set ans="%ans%,%6"
if trick==trick%7 goto :loop
set ans="%ans%,%7"
if trick==trick%8 goto :loop
set ans="%ans%,%8"
if trick==trick%9 goto :loop
set ans="%ans%,%9"
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans:"=%"
:loop
if "%9"=="" (
@echo off
) else (
shift
set rest="%rest%,%9"
goto :loop
)
set ans="%ans%%rest%"
set ans=%ans:"=%
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
if trick==trick%passed% (
set ans=%ans:"=%
if "%ans:,=%"=="%ans%" (
echo "Nothing to do."
) else (
explorer.exe "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html %ans%"
)
)
rem exit
:end
… as your starting gambit, here, where the equivalent of macOS’s “open” is “explorer.exe” on Windows (ie. the File Explorer) … almost as cute!
YouTube API Caller Radio Play Playlist Local System Tutorial
The ..
command line
… of a laptop or notebook or personal computer can be an environment to relax from the GUI aspects of your modern operating systems and think about …
processes
batch driven tasks
task simplifications
… and in the category of that last one, on our local macOS operating system we envisaged a day we could slip back to the “quiet world” of the “command line” and type a command like …
play Tragedy,Imagine,Down Among the Dead Men PAN -
… and get somewhere playing those songs through a web browser accessing the YouTube API and our interfacings to it in webpage software form, playing in a continuous Radio Play style.
You see, with all the distractions, only having to remember …
play
… no matter where we were in “command line” woooorrrrllllddd seemed the ideal, and yes, it’s possible. However, we think explaining …
bash /Applications/MAMP/htdocs/play.ksh Tragedy,Imagine,Down Among the Dead Men PAN -
… is a necessary dividing line between two parts to the discussion. So what’s bash? It’s the shell we associate as your macOS default shell (we get to via the “out of the box” macOS desktop application called Terminal) … well, it used to be … anyway, the first line of (what started as play.ksh then play.sh and then eventually, just, play … as a filename that is) …
#!/bin/bash
rest=""
if [ -z "$1" ]; then
echo "Radio Play via comma separate YouTube 11 character IDs or search ideas for a playlist ... egs. Tragedy,Boogie Wonderland,Down Among the Dead Men ... random ... Tragedy ,Imagine*3,Breezin ... ordered 5 ... Tragedy ,Pina Colada Song,Fast Car ... audio ... Tragedy ,Galveston,Fix You ... audio ordered, and go."
read ans
else
ans="$1 $2 $3 $4 $5 $6 $7 $8 $9"
while [ ! -z "$9" ]; do
shift
rest="${rest} $9"
done
ans="$ans${rest}"
fi
if [ ! -z "$ans" ]; then
open "https://www.rjmprogramming.com.au/HTMLCSS/swipe_media.html $ans"
fi
exit
… tells the local system use the bash shell to interpret this what you might like to think of as an executable script (which we went chmod 777 play regarding to make it so).
Okay, so we wrote this script in /Applications/MAMP/htdocs/ and that …
bash /Applications/MAMP/htdocs/play Tragedy,Imagine,Down Among the Dead Men PAN -
… could swing it at this stage, or even being in /Applications/MAMP/htdocs/ and going …
./play Tragedy,Imagine,Down Among the Dead Men PAN -
… but (even in /Applications/MAMP/htdocs/) …
play Tragedy,Imagine,Down Among the Dead Men PAN -
… is most likely to reply something like …
zsh: no such command
As the programmer, perhaps you could leave it at that with your “insider knowledge”, or if “play” as a command already meant something (a test that would be good to do before any such proposed work), otherwise … do you really think you’ll remember next Wednesday? So …
enter into the wooooorrrrllllddd of aliases (thanks to this great advice, thanks) … eg.
alias play="/Applications/MAMP/htdocs/play"
placed into …
~/.bashrc
… via (in our case) …
vi ~/.bashrc
then …
source ~/.bashrc
… needs to be included into the login procedure of …
~/.profile
…via (in our case) …
vi ~/.profile
to make …
play
… as the interactively asking alternative to the argumentative example of …
play Tragedy,Imagine,Down Among the Dead Men PAN -
… mean something … and anywhere
… all of this possible because of the brilliantly useful macOS (not Linux nor unix) …
… opener of web browsers from the command line blowing of our tiny little rather still naive mind … mind you, VAX/VMS “phone” command from the ’70’s might have even been better?! And then there were those punch cards … in the hopper … and if you got a character wrong … back to the end of the queue.
By the way, this brought into play for us what your default web browser is. Ours is Safari yet we develop more on Google Chrome. Safari, for us, does more Popup Blocking, and it is a thing to look out for should a window not open when expected (and these command line methodologies don’t get past you still having to unmute and/or click/tap once before the Radio Play mode of play kicks off). To undo the Popup Block there will probably be a means to do this to the right of the web browser address bar.
But there is a point. Today represents an improvement on yesterday’s arrangements for dynamic playlist creation regarding our Tabular Single Row Media Gallery web application via the playlist emoji button 🔀 click/tap or on the web browser command line. With yesterday’s scenario, had you entered as your internal playlist comma separated string …
Born to Run,Born to Run,Born to Run,Born to Run
… without the …
Burp …
… that is you’d have ended up with the top find off a YouTube search repeated 4 times. Is this ideal? We asked Nala and Luna but, unfortunately they were busy, so they left me with the decision, and we came up with …
That’s kind of silly.
For these few reasons …
variety
album style Radio Play listening possibilities
perhaps getting around that …
This video requires payment to watch YouTube
… issue we talked about before … because you could uncheck it’s checkbox if the error appears
giving non-top search items a go
… we decided to “mix it up” regarding entries like …
Born to Run,Born to Run,Born to Run,Born to Run
… as well as the simpler …
Born to Run*4
… so that repeats don’t cause users indigestion … chortle, chortle.
Here’s the Javascript turning Born to Run*4 into Born to Run,Born to Run,Born to Run,Born to Run …
YouTube API Caller Radio Play Playlist Songname Search List Address Bar Tutorial
Don’t know if you are like me, but especially when on this MacBook Air we’re, forever, using a web browser address bar to type in, what we’ve set up as, a Google search engine webpage query. Rather than even going to Google webpage first, that is!
Most people don’t want to know about arcane web application argument arrangements … though we’re very fond of the 5 minute ones?!
What’s wrong with some of you? Who would not relish the thought of …
&or?andgo=y # will click the Radio button after all above (unless notgo is mentioned in the list somewhere), as relevant, with the programmatically determined derived YouTube ID list via YouTube search(es)
💬🦆 Come On!
… when, after a bit of thought and tweaks to our RJM Programming domain’s Apache Document Root residing 404.shtml webpage Javascript …
if (document.URL.indexOf('rjmprogramming.com.au/HTMLCSS/swipe_media.htm') != -1) {
if (decodeURIComponent(document.URL).indexOf(',') != -1) {
if (decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'')).toLowerCase().indexOf('notgo') != -1) {
location.href='//www.rjmprogramming.com.au/HTMLCSS/swipe_media.html?thelist=' + encodeURIComponent(decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace('notgo','').replace('NOTGO','').replace('Notgo','')));
} else {
location.href='//www.rjmprogramming.com.au/HTMLCSS/swipe_media.html?andgo=y&thelist=' + encodeURIComponent(decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'').replace(/^\%20/g,'')));
exit;
}
} else if (decodeURIComponent(document.URL.split('rjmprogramming.com.au/HTMLCSS/swipe_media.htm')[1].replace(/^l/g,'')).toLowerCase().indexOf('ask') != -1) {
location.href='//www.rjmprogramming.com.au/HTMLCSS/swipe_media.html?ask=y';
exit;
}
}
… now all you need to add after that link above, on the web browser address bar is …
[commaSeparatedList_decodeURIComponent_ized]
… ie. just type a comma separated YouTube song search basis link after the link wording above
YouTube API Caller Radio Play Playlist Songname Search List Dropdown Tutorial
Around here, regarding web application work, we really like dropdown (ie. HTML select element) work because a lot of information can be …
stored
displayed (often taking up minimal webpage space)
it’s option subelements can have an event life of their own, or be part of the select element event whole picture
dropdown multiple selection mode (as with today’s work) can …
still support your single choice scenario
we’ve coded today’s dropdown to support, as applicable, ordered single click choices … versus …
can allow for control key (sets of choices not necessarily adjacent) and shift key (blocks of choices)
can respond (as with today’s dropdown) to preset selections established at creation via dropdown (ie. select) innerHTML option subelement selected attribute setting
dropdown events onmousedown and ontouchdown (as for today’s dropdown) and onclick can be harnessed ahead of an onchange event … and in today’s case can help unset any preset option subelement selected attribute setting(s)
Just so flexible! Subelement option CSS styling can be less friendly, but even so, often background colouring is possible, and we do a bit of that with the work from yesterday’s YouTube API Caller Radio Play Playlist Songname Search List Tutorial and today’s better “shored up” work you can see a bit of below (where your starting point might be that we “wrap” this particular Javascript prompt window through the Javascript overprompt function below) …
var outplist='', kaconto=null, kacontoarr=[], rconeis=155, gconeis=255, bconeis=255;
function karlook(iois) {
var aconto=null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
if (outplist.indexOf(',') != -1) {
kaconto=aconto;
setTimeout(function(){ kaconto.getElementById('ajaxs').style.cursor='progress'; }, 3000);
setTimeout(postkarlook, 7000);
} else if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
kaconto=aconto;
setTimeout(mpostkarlook, 7000);
}
}
}
}
function karlookagain(iois,idxn) {
var aconto=null;
aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
if (eval('' + kacontoarr.length) > eval('' + idxn)) {
kacontoarr[eval('' + idxn)]=aconto;
} else {
kacontoarr.push(aconto);
}
}
}
}
function mpostkarlook() {
if (kaconto.getElementById('ajaxs')) {
var kbuts=kaconto.getElementsByTagName('input');
for (var lbuts=0; lbuts<kbuts.length; lbuts++) {
if (('' + kbuts[lbuts].type) == 'button') {
if (('' + kbuts[lbuts].value).indexOf('Radio') == 0) {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', String.fromCharCode(10) + 'Radio');
}
}
}
} else {
setTimeout(mpostkarlook, 5000);
}
}
function postkarlook() {
var selectedones='', oktogo=true, hihi=0;
if (kaconto.getElementById('ajaxs')) {
//alert(kaconto.getElementById('ajaxs').innerHTML);
for (hihi=0; hihi<kacontoarr.length; hihi++) {
if (!kacontoarr[hihi]) {
oktogo=false;
//alert('hihi=' + hihi + ' NOT okay');
} else if (!kacontoarr[hihi].getElementById('ajaxs')) {
oktogo=false;
//alert('hihi=' + hihi + ' not okay');
}
}
if (!oktogo) {
setTimeout(postkarlook, 5000);
} else if (kaconto.getElementById('ajaxs').innerHTML.indexOf('... and then ...') == -1) {
kaconto.getElementById('ajaxs').style.cursor='pointer';
var otherps=baeisf();
if (otherps != '' || navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
var kbuts=kaconto.getElementsByTagName('input');
for (var lbuts=0; lbuts<kbuts.length; lbuts++) {
if (('' + kbuts[lbuts].type) == 'button') {
if (('' + kbuts[lbuts].value).indexOf('Radio') == 0) {
var wasvl=kbuts[lbuts].value;
if (otherps == ' ') {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', 'Ordered ' + String.fromCharCode(10) + 'Audio ' + String.fromCharCode(10) + 'Radio');
} else if (otherps == ' ') {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', 'Audio ' + String.fromCharCode(10) + 'Radio');
} else if (otherps == ' ') {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', 'Ordered ' + String.fromCharCode(10) + 'Radio');
} else {
kbuts[lbuts].value=kbuts[lbuts].value.replace('Radio', String.fromCharCode(10) + 'Radio');
}
}
}
}
}
selectedones=(kaconto.getElementById('ajaxs').innerHTML.split(kaconto.getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>')[1].split('</option>')[0] + '</option>').replace('>', ' class=uptop selected>');
for (hihi=0; hihi<kacontoarr.length; hihi++) {
if (rconeis == gconeis && rconeis == bconeis) {
bconeis-=7;
if (bconeis < 0) { bconeis+=255; }
} else if (rconeis == gconeis) {
gconeis-=7;
if (gconeis < 0) { gconeis+=255; }
} else {
rconeis-=7;
if (rconeis < 0) { rconeis+=255; }
}
selectedones+=((kacontoarr[hihi].getElementById('ajaxs').innerHTML.split(kacontoarr[hihi].getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>')[1].split('</option>')[0] + '</option>').replace('>', ' class=uptop selected>')).replace(/option\ onclick\=/g, 'option style=background-color:rgb(' + rconeis + ',' + gconeis + ',' + bconeis + '); onclick=');
kaconto.getElementById('ajaxs').innerHTML+='<option value="">... and then ...</option>';
kaconto.getElementById('ajaxs').innerHTML+=kacontoarr[hihi].getElementById('ajaxs').innerHTML.split(kacontoarr[hihi].getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>')[1].replace(/option\ onclick\=/g, 'option style=background-color:rgb(' + rconeis + ',' + gconeis + ',' + bconeis + '); onclick=');
//alert(kacontoarr[hihi].getElementById('ajaxs').innerHTML);
}
//alert(selectedones);
kaconto.getElementById('ajaxs').innerHTML=kaconto.getElementById('ajaxs').innerHTML.replace(kaconto.getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>', kaconto.getElementById('ajaxs').innerHTML.split('</option>')[0] + '</option>' + selectedones + (selectedones != '' ? '<option id=optclk value="">____ Selection above available (resettable via click here) as you click/tap button at right ____</option>' : ''));
if (selectedones != '') {
kaconto.getElementById('ajaxs').onmousedown=function(event){ if (event.target.parentNode.outerHTML.split('>')[0].indexOf(' data-flagged=') == -1) { event.target.parentNode.setAttribute('data-flagged','y'); event.target.parentNode.innerHTML=event.target.parentNode.innerHTML.replace(/\ selected/g, ' ').replace('____ Selection above available (resettable via click here) as you click/tap button at right ____','____ Please select choices in usual multiple choice dropdown ways ____'); } };
kaconto.getElementById('ajaxs').ontouchdown=function(event){ if (event.target.parentNode.outerHTML.split('>')[0].indexOf(' data-flagged=') == -1) { event.target.parentNode.setAttribute('data-flagged','y'); event.target.parentNode.innerHTML=event.target.parentNode.innerHTML.replace(/\ selected/g, ' ').replace('____ Selection above available (resettable via click here) as you click/tap button at right ____','____ Please select choices in usual multiple choice dropdown ways ____'); } };
}
}
}
}
function baeisf() {
var outarrp=outplist.split(',');
if (eval('' + outarrp.length) > 1) {
return outarrp[0].replace(outarrp[0].trim(), '');
}
return '';
}
And so, today …
we add onto the default random ordering mode of Radio Play via YouTube search comma separated list, the further choices ordered or audio or audio ordered, as per the relevant Javascript prompt blurb that is now going …
Enter YouTube 11 character code video (comma separated) or audio stream only of video (semicolon) list to Radio Play (and add ! to start in shuffle mode and/or # for the order you enter and/or & for whole list) … or a YouTube Search String basis for playlist via a Radio📻? button to activate, later. Comma separateds can be search ideas … egs. Tragedy,Boogie Wonderland,Down Among the Dead Men … random … Tragedy ,Imagine,Breezin … ordered … Tragedy ,Pina Colada Song,Fast Car … audio … Tragedy ,Galveston,Angel of the Morning … audio ordered. Double click on this cell for playlist redefinitions.
started allowing for dropdown events onmousedown and ontouchdown be able to reset the preset choices we programmatically select for the user should they be happy (but our motivation for this event work was the potential to run into a YouTube video that gives the error …
Because the theme of our current Editable Calendar web application revolves around time stamps and day names and month names regarding yesterday’s Editable Calendar Google Translate Tutorial that means, regarding language internationalization ideas …
as well as yesterday’s Google Translate ideas … also available is …
Yesterday’s Editable Calendar Primer Tutorial‘s start to our Editable Calendar project was a very anglo-centric exercise. Today, we want to use the great …
… to help translate our inhouse burgeoning Editable Calendar skeletal contents to some non English languages.
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out in this changedsecond draftEditable Calendar you can try below, because we’re using the Google Translate “static URL” approach to the translations. But we will examine, down the track, if we can do better?! Again, stay tuned for more progression and internationalization over the days on this Editable Calendar project.
we’re channelling how in emails “word strings” starting with “http” become links … and so …
in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available
Here is the relevant Javascript we ended up with, for this …
Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!
When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …
When it comes to procedures …
Aaaaaarrrrgggghhhh …
Nuances count … regarding …
Errrrrrr …
Saving time …
Saving frustration
… resulting in the “easy to remember” acronym WANESS … we rest our cases!
What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.
Global variables initialization …
var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …
if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …
function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}
function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic …
function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}
Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changedevents_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!
it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs
… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!
Timing became really important with this integration of …
email
SMS
… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …
Month
Year
… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …
Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons …
<h1>Events in Month <button style=display:none; title='Share via Email' id=semail onclick="doemail('');">📧</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>📟</button> </h1>
New static HTML div element placed to the bottom of the body element …
<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …
Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to …
Record to Remember via Bookmark
… use the web browser Bookmarks to help you recall an Events in Month report with long entries.
Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …
… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!
To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …
var documentURL=document.URL;
… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …
#August__2023_00000__1
… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).
Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).
Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …
… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.
The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …
<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm
But, there’s an inherent weakness with the design, we’ll go into more into the future.
… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.
Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.
Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …
bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
pick Inspect option …
be in Elements tab of your Web Inspector … and …
highlight top <html> tag …
Context Menu with a right click or two finger gesture …
pick Copy -> Copy outerHTML … so that …
your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)
The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …
function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}
… the form submit buttons are reworded accordingly.
Another idea from this blog thread’s inspiration …
… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.
And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …
text shadow means by which the text of Event Calendar can be more impactive …
<div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
Yesterday’s Editable Calendar Primer Tutorial‘s start to our Editable Calendar project was a very anglo-centric exercise. Today, we want to use the great …
… to help translate our inhouse burgeoning Editable Calendar skeletal contents to some non English languages.
Why the skeletal here? Well, your own entries made before you select a dropdown non English language choice are stripped out in this changedsecond draftEditable Calendar you can try below, because we’re using the Google Translate “static URL” approach to the translations. But we will examine, down the track, if we can do better?! Again, stay tuned for more progression and internationalization over the days on this Editable Calendar project.
we’re channelling how in emails “word strings” starting with “http” become links … and so …
in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available
Here is the relevant Javascript we ended up with, for this …
Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!
When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …
When it comes to procedures …
Aaaaaarrrrgggghhhh …
Nuances count … regarding …
Errrrrrr …
Saving time …
Saving frustration
… resulting in the “easy to remember” acronym WANESS … we rest our cases!
What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.
Global variables initialization …
var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …
if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …
function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}
function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic …
function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}
Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changedevents_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!
it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs
… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!
Timing became really important with this integration of …
email
SMS
… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …
Month
Year
… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …
Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons …
<h1>Events in Month <button style=display:none; title='Share via Email' id=semail onclick="doemail('');">📧</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>📟</button> </h1>
New static HTML div element placed to the bottom of the body element …
<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …
Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to …
Record to Remember via Bookmark
… use the web browser Bookmarks to help you recall an Events in Month report with long entries.
Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …
… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!
To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …
var documentURL=document.URL;
… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …
#August__2023_00000__1
… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).
Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).
Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …
… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.
The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …
<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm
But, there’s an inherent weakness with the design, we’ll go into more into the future.
… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.
Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.
Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …
bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
pick Inspect option …
be in Elements tab of your Web Inspector … and …
highlight top <html> tag …
Context Menu with a right click or two finger gesture …
pick Copy -> Copy outerHTML … so that …
your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)
The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …
function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}
… the form submit buttons are reworded accordingly.
Another idea from this blog thread’s inspiration …
… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.
And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …
text shadow means by which the text of Event Calendar can be more impactive …
<div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>
we’re channelling how in emails “word strings” starting with “http” become links … and so …
in our event in month “blurb” textarea elements we allow user highlighted text containing such “http” “word strings” be opened in popup windows showing the content of those URLs, as available
Here is the relevant Javascript we ended up with, for this …
Just briefly, delved into the wooooorrrrllllldddd of days of the week before 1582 debate … you had to be there … and in the words of Lleyton … Come on!!!! And we came out the other side. Good times! Anyway, thanks for all the interesting discussion, which we recommend you start at with this gem. Anyway, we now allow dates after 1582 (after this was previously set at 1970) in all respects and dates between 0000 and 1581 only via tweaks to the address bar URL you do yourself and we just hide the day of the week string. Otherwise … this could be needed?!
When doing our inhouse testing for Event Calendar Collaboration Tutorial the other day, it got us “peeved”, shall we say. We wanted a mechanism, with those “a” link “mailto:” emailing arrangements, of not having to fill out the email address in the “To:” field each time. First world problem alert! Still and all …
When it comes to procedures …
Aaaaaarrrrgggghhhh …
Nuances count … regarding …
Errrrrrr …
Saving time …
Saving frustration
… resulting in the “easy to remember” acronym WANESS … we rest our cases!
What can help here? Well, it is a personalization … so … anyone, anyone? Yes, Lou, back from holidays, a month late … yes, well, okay … we can see you’ve put some thought into this … HTTP Cookies could be used. Yes, Shwetank, you’ve had your hand up some time now. We agree, Web Storage window.localStorage allows for more data, so we’ll go with that, though either style of approach would work here.
Global variables initialization …
var defemail='', defsms='';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
Within document.body onload event logic …
if (document.getElementById('semail') && document.getElementById('ssms')) {
document.getElementById('semail').oncontextmenu=function(){ emailaskit(); };
document.getElementById('semail').ontouchmove=function(){ emailaskit(); };
document.getElementById('ssms').oncontextmenu=function(){ smsaskit(); };
document.getElementById('ssms').ontouchmove=function(){ smsaskit(); };
if (navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
document.getElementById('semail').title+=' Move gesture here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Move gesture here can ask for new default remembered SMS number for collaboration purposes';
} else {
document.getElementById('semail').title+=' Right click here can ask for new default remembered email address for collaboration purposes';
document.getElementById('ssms').title+=' Right click here can ask for new default remembered SMS number for collaboration purposes';
}
}
Calling these two Javascript function helpers …
function emailaskit() {
if (defemail == defemail.trim()) {
defemail=defemail.trim() + ' ';
var emailm=prompt('Enter default email address.', defemail.trim());
if (emailm == null) { emailm=''; }
if (emailm.trim() != '' && emailm.trim().indexOf('@') != -1) {
defemail=emailm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', emailm.trim());
}
}
defemail=defemail.trim() + ' ';
}
function smsaskit() {
if (defsms == defsms.trim()) {
defsms=defsms.trim() + ' ';
var smsm=prompt('Enter default SMS number.', defsms.trim());
if (smsm == null) { smsm=''; }
if (smsm.trim() != '' && smsm.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
defsms=smsm.trim() + ' ';
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', smsm.trim());
}
}
defsms=defsms.trim() + ' ';
}
And allow control, as well, at the SMS prompt window logic …
function dosms() {
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defemail=window.localStorage.getItem('eventcollaboratoremailee');
}
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
defsms=window.localStorage.removeItem('eventcollaboratorsmsno');
}
//alert('' + eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length));
if (eval('' + ('sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()))).length) >= 18000) {
snum=('' + prompt('Please enter SMS number to send to. We suspect message is too long for an SMS and if you enter an email address here instead, we will try to send an email instead. Append space(s) to remember, whatever you enter, for next time.', snum)).replace(/^null/g, (defemail.indexOf('@') != -1 ? defemail : (defsms != '' ? defsms : '')) );
} else {
snum=('' + prompt('Please enter SMS number to send to. Append space(s) to remember for next time.', snum)).replace(/^null/g, (defsms != '' ? defsms : (defemail.indexOf('@') != -1 ? defemail : '')));
}
if (snum == null) { snum=''; }
if (snum.indexOf('@') != -1) {
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratoremailee')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratoremailee');
}
window.localStorage.setItem('eventcollaboratoremailee', snum);
defemail=snum;
}
return doemail(snum);
} else if (snum.trim() != '' && snum.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'').trim() == '') {
//alert('Snum=' + snum + '?');
if (snum.trim() != snum) {
snum=snum.trim();
if (('' + window.localStorage.getItem('eventcollaboratorsmsno')).replace(/^null/g,'').replace(/^undefined/g,'') != '') {
window.localStorage.removeItem('eventcollaboratorsmsno');
}
window.localStorage.setItem('eventcollaboratorsmsno', snum);
defsms=snum;
}
var azx=top.document.getElementById('xas' + 'ms');
if (azx == null) { azx=top.document.createElement("a"); }
//if (1 == 1) {
// document.getElementById('divas').appendChild(azx);
//} else {
top.document.body.appendChild(azx);
// }
azx.id = 'xas' + 'ms';
azx.target = "_top";
azx.style = "display: none";
azx.href = 'sms:' + snum + '&body=' + encodeURIComponent(document.URL.split('?')[0].split('#')[0] + '#bih=' + encodeURIComponent(parent.hfanalyze()));
azx.click();
}
return '';
}
Also, today, we battled the logic to allow the textarea element onblur events also open the door to emailing and SMS “work in progress”. Believe it or not, this timing change was quite difficult in the changedevents_in_month.htm parent of the Events in Month web application. Never a dull moment in web application I.T. we’d say!
it did not have a fully fleshed out collaboration or sharing set of functionalities … at the time, probably, because …
each Monthly calendar filled out, and wanting to be saved, could involve much more than the 850 characters available to previous “mailto:” or “sms:” communication conduits, back to when we first tackled this project … but now …
hashtagging parts to the “mailto:” URLs (and maybe sometimes the “sms:” URL) might cover the data length needs
… so that this Event Calendar could be a web application at the center of a collaboration network of people working on that event organization. Much more useful, we figure!
Timing became really important with this integration of …
email
SMS
… communication conduit possibilities. With its design we have to wait until the point where the user is filling in textarea elements regarding a designated …
Month
Year
… of relevance, before we allow the email 📧 and SMS 📟 emoji buttons be shown. In the code …
Amended HTML h1 element static HTML to add, later shown, perhaps, email 📧 and SMS 📟 emoji buttons …
<h1>Events in Month <button style=display:none; title='Share via Email' id=semail onclick="doemail('');">📧</button> <button style=display:none; title='Share via SMS' id=ssms onclick=dosms();>📟</button> </h1>
New static HTML div element placed to the bottom of the body element …
<div id=divas></div>
New Javascript global variables (picking up hashtagging parts of the address bar URL, perhaps linked off an email or SMS link clicked) …
Client meets server today, allowing the PHP data storage talents in yesterday’s Event Calendar PHP Tutorial‘s work to meet a “clientside” way to …
Record to Remember via Bookmark
… use the web browser Bookmarks to help you recall an Events in Month report with long entries.
Involving a Bookmark still needs those “get” ? and & arguments, and we allow the PHP to lookup for us the data underscoring an address bar URL such as …
… that mapping being possible, now, making use of a pseudo hashtag arrangement … your Clayton‘s hashtag, if you will!
To start to use a hashtag suits, as your hashtag navigation only makes sense in the “clientside” woooorrrrrllllddd, and even there, really using one only tries to position a webpage where an element with an ID matching the hashtag sits (and we are never going to have this ID element in our work), and in the meantime we’ve passed across from the child PHP to the parent HTML a valuable piece of information helping the link to the Bookmark system be possible. The Javascript codeline (you may have noticed) …
var documentURL=document.URL;
… as stupid and simple looking as it is, is crucial for us. We get child webpage parts (like our PHP) to change document.URL to a far too long in normal circumstances address bar URL the rest of the code feeds off. It’s just that now, that far too long in normal circumstances address bar URL has a #[hashtag] appended such as …
#August__2023_00000__1
… uniquifying a Month, Year Events in Month identifier part with a version of the user’s IP address (so that they only see what is relevant to them).
Address bar URLs such as “https://www.rjmprogramming.com.au/HTMLCSS/events_in_month.php?exactlabel=August__2023_00000__1” are Bookmarkable, and so we allow for similar outcomes with less concern about how much data is being “recorded” and recallable (via that web browser’s Bookmark system).
Also, today, a lot of CSS tweaks, so that the CSS styling now looks like …
… form submit button (toggling) value to start involving PHP with those longer datasets of Event in Month descriptions, in our most recent project. What do we use as the data conduit? No, not a database. No, not a serverside flat file. No, not clientside window.localStorage nor window.sessionStorage nor HTTP Cookies. We store long Event in Month description data in our new events_in_month.php PHP itself. And this same PHP can populate options in a new dropdown element in the parent to facilitate the recalling of any relevant “Record to Remember” recordings.
The PHP is kind of short, so we will show it below, including one MAMP example of that “self storage” …
<?php
// events_in_month.php
// RJM Programming
// August, 2023
// Help out events_in_month.htm
But, there’s an inherent weakness with the design, we’ll go into more into the future.
… well … it’s a perennial for us, regarding how if you stick with clientside thinking, only, web applications, when the amount of data to remember starts adding up, the “get arguments approach” ( ie. use ? and & arguments at the address bar ) is restricted by length restrictions regarding URL lengths.
Rather than jump to PHP serverside ideas just yet, we wanted to show some more ideas, staying with “clientside only thinking”, today, as well as improving the UX (user experience) and small steps forward regarding styling.
Okay then, regarding the idea to remember an Event in Month form, when there is a lot of data, and staying “just clientside”, we’ve coded for a “New Window” idea, albeit not as memorable as the default “Remember in Bookmark” possible for your smaller datasets. However, in saying that, we found that within this new window, with our Google Chrome web browser, we could …
bring up Context Menu with a right click or two finger gesture within the popup window webpage content …
pick Inspect option …
be in Elements tab of your Web Inspector … and …
highlight top <html> tag …
Context Menu with a right click or two finger gesture …
pick Copy -> Copy outerHTML … so that …
your Event Calendar for your Events in Month choice is in a text buffer … ready for you to …
Paste into a Text Editor that Events in Month webpage to store (perhaps in a MAMP local Apache/PHP/mySql web server environment, where you can further look at and develop your own ideas)
The user is told when the switch to “New Window” compromise becomes active, decided upon by reconstructing the proposed address bar URL regularly and when too long …
function formanalyze() {
var fio=document.getElementsByTagName('form')[0];
var delm='?';
var fioih=fio.innerHTML;
var fions=fioih.split(' name="');
var testurl=documentURL.split('?')[0].split('#')[0];
for (var ijk=1; ijk<fions.length; ijk++) {
testurl+=delm + fions[ijk].split('"')[0] + '=' + encodeURIComponent(document.getElementById(fions[ijk].split('"')[0]).value);
delm='&';
}
setTimeout(formanalyze, 3000);
if (eval('' + testurl.length) >= 750) {
if (document.getElementById('remember')) { document.getElementById('remember').value='New window'; }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value='New window'; }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value='New window'; }
} else {
if (document.getElementById('remember')) { document.getElementById('remember').value=document.getElementById('remember').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('rememberme')) { document.getElementById('rememberme').value=document.getElementById('rememberme').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
if (document.getElementById('remembermoi')) { document.getElementById('remembermoi').value=document.getElementById('remembermoi').value.replace(/^Remember$/g, 'Remember via Bookmark'); }
}
return eval('' + testurl.length);
}
… the form submit buttons are reworded accordingly.
Another idea from this blog thread’s inspiration …
… we’ve now addressed in today’s “second draft” is allowing for an optional bold Event Day Blurb Title, available to the user via a new dropdown “Bold Title” option.
And, how can we do more with colour, to help the right bits stand out, and be consistent? We thought …
text shadow means by which the text of Event Calendar can be more impactive …
<div style="text-shadow:-1px 1px 1px #ff2d95;" id=eventcalendar></div>