The CSS “border” emphasis of the previous Emoji Borders and Backgrounds Canvas Annotation Tutorial has concepts, we see online, confusing people, and myself, regarding what the CSS “outline” feature is capable of (or not capable of). Here is W3school‘s pertinent paragraph on this …
Note: Outline differs from borders! The outline is drawn outside the element’s border, and may overlap other content. Also, the outline is NOT a part of the element’s dimensions; the element’s total width and height is not affected by the width of the outline.
The CSS “outline” concept is not the panacea to as many web design requirements as the “border” concept is. For instance, even seeing within the purview of making our “proof of concept” web application …
no linear gradients can be worked in the “outline” realm
no such properties as “outline-top” or “outline-left” or “outline-bottom” or “outline-right” exist within the “outline” realm
no such property as “outline-radius”
no outline images in the “outline” realm
If we were to summarize, the CSS “outline” concept’s talents can lie with …
being around an HTML element (and not part of it … perhaps in web design, useful as “a place of rest” (including non-mobile on hover element title displays) for users?!)
decorative colour around an HTML element (perhaps to get the user’s attention to hone in on that element, or perhaps a dynamic conduit towards “thickening” an existant HTML element border (especially as Javascript DOM such as [element].style.outline=’2px solid green’; // is available to programmers))
can form a replacement nesting HTML element background colour means
in it’s simplest CSS syntax ( eg: outline: 2px solid green; ) can be easily remembered next to the simplest syntax of border (probably very familiar to you) CSS syntax ( eg: border: 2px solid green; ) … just differently located on the webpage
… contents in synch as far as looks go. That ignores the reason CANVAS (element) was invented when HTML5 came out. It was invented to not only contain image graphical data, but be able to amend that data. So we also allow it with our web application from today on, integrating with our inhouse “User of Signature” web application last talked about at Local Fonts Revisited Middle Tutorial, to amend (initially image via [canvas].toDataURL(‘image/png’) via SVG streams of data which may include emoji borders and backgrounds) graphical data.
We integrate via a same domain window.open scenario. Before you all start groaning, consider the advantages of a …
… albeit non-mobile window.open (ie. popup window) call. Not only are you …
(on non-mobile) seeing two windows on top of each other … but …
the “window.opener” (acting like “parent” does for an “iframe” arrangement) reference can have you, via Javascript DOM, share the data of the popup window canvas element with that of the “window.opener” (window) canvas element
… and then, we arrange for a “reset” (to ignore these annotation changes), via the “middle-element” IMG element, should the “Undo” functionality of our HTML changeduser_of_signature_signature.html‘s User of Signature Signature (standalone) live run not be enough flexibility to the functionality, which has a new document.body onload event triggering of Javascript function cancontlook below …
var cancont='';
var cancontw=-1, canconth=-1;
if (('' + window.opener).replace('null','') != '') {
var cans=window.opener.document.getElementsByTagName('canvas');
if (cans.length > 0) { cancont=cans[0].toDataURL('image/png'); cancontw=cans[0].width; canconth=cans[0].height; }
}
function cancontlook() {
if (cancont != '') { // called via SVG - IMG - CANVAS
celem=document.getElementById('topcanvas');
ccontext = celem.getContext("2d");
celem.width=cancontw;
celem.height=canconth;
cimg=new Image;
cimg.onload = function(){
ccontext.drawImage(cimg,0,0);
};
cimg.src=cancont;
if (document.getElementById('toptd')) {
document.getElementById('toptd').style.left='' + cancontw + 'px';
var recttoptd=document.getElementById('toptd').getBoundingClientRect();
document.getElementById('topcbutton').innerHTML='⬅';
document.getElementById('topcbutton').style.position='fixed';
document.getElementById('topcbutton').style.top='' + recttoptd.top + 'px';
//document.getElementById('topcbutton').style.left='' + recttoptd.left + 'px';
document.getElementById('topcbutton').style.left='' + eval(0.98 * eval('' + screen.width)) + 'px';
document.getElementById('topcbutton').style.zIndex='89';
document.getElementById('topcbutton').title='Back to SVG - IMG - CANVAS web application caller';
//document.getElementById('topcbutton').onclick=function() { window.opener.focus(); };
}
if (document.getElementById('toppixellate')) {
document.getElementById('toppixellate').style.top='' + canconth + 'px';
}
if (('' + location.hash).replace(/^\#/g,'') != '') {
document.getElementById('topcstyle').innerHTML='
function iysh() {
if (document.getElementById('topcbutton').innerHTML.indexOf(' ') != -1) {
document.getElementById('topcbutton').innerHTML=document.getElementById('topcbutton').innerHTML.replace(' ','');
alert('Please do this manually');
}
}
function woff() {
document.getElementById('topcbutton').innerHTML+=' ';
setTimeout(iysh, 2000);
window.opener.focus();
}
… that might help you “look back at yourself from outside” as we use it to interface to Google PageSpeed Insights webpage screenshot functionality (which can look at a designated URL and effectively screenshot it for you) … thanks.
We used to restrict our curl usage thinking to “only ?” first argument use too, arguing (with ourselves) that the Linux and Unix and macOS and Mac OS X “&” character command line meaning of “process in the background” would be a constraint. Of course it isn’t, because … anyone, anyone? Yes, Henry, you should use a “curl” command line command, using double quotes, of the form …
… and the “&”‘s are understood to be URL “GET” argument delimiters, and not a “process in the background” designator.
And here’s the thing exemplified above. The alternative file_get_contents PHP function cannot do that https: protocol type of URL, yet Google PageSpeed Insights webpage screenshot functionality demands such URLs … hence the “curl” interest.
$extis='.xxx';
if (strpos(('' . $_SERVER['QUERY_STRING']), 'howpagespeedseesme=') !== false) {
if (file_exists('/tmp/')) {
if (file_exists('/tmp/pscs.xxx')) {
unlink('/tmp/pscs.xxx');
}
}
exec($psbig);
while (!file_exists('/tmp/pscs' . $extis)) {
sleep(4);
}
while (filesize('/tmp/pscs' . $extis) == 0) {
sleep(4);
}
$scx=file_get_contents('/tmp/pscs' . $extis);
$scxs=explode('"screenshot":', $scx);
if (sizeof($scxs) > 1) {
$postscxs=explode('"data": ', $scxs[1]);
if (sizeof($postscxs) > 1) {
echo '<html><body><h2>How Google <a target=_blank title="Google PageSpeed Insights" href="https://pagespeed.web.dev/">PageSpeed Insights</a> Screenshots This ...</h2><img src=' . substr($postscxs[1],0,1) . explode(substr($postscxs[1],0,1), substr($postscxs[1],1))[0] . substr($postscxs[1],0,1) . '></img></body></html>';
} else {
echo '<html><body><h2>How Google <a target=_blank title="Google PageSpeed Insights" href="https://pagespeed.web.dev/">PageSpeed Insights</a> Screenshots This ...</h2><p>Sorry, resource exhausted. Could try again later.</p></body></html>';
}
} else {
echo '<html><body><h2>How Google <a target=_blank title="Google PageSpeed Insights" href="https://pagespeed.web.dev/">PageSpeed Insights</a> Screenshots This</h2><p>Sorry, resource exhausted. Could try again later.</p></body></html>';
}
exit;
}
?>
… working with the new HTML div <div id=readyforiframe></div> element, along with <span title=’Google PageSpeed Insights webpage screenshot view of this web browser address URL’ onclick=’psit();’> 📸 </span> 📸 “emoji button” in the HTML static code … with associated Javascript function …
Emoji Borders and Backgrounds Web Browser Tutorial
Yesterday’s Emoji Borders and Backgrounds Shape Sharing Tutorial had us sharing via email or SMS our “Emoji Borders and Backgrounds” creations via inhouse organized arrangements. But many people are used to just right-clicking (for Windows) and two finger gesturing (for macOS and Mac OS X) on …
SVG
IMG
CANVAS
… graphical elements, which can be made to “look” the same, and sort of “suck it and see” as far as menu options presented, helping them out sharing graphical information. In many cases, users don’t care what form of element the image comes contained in. But, if you involve “background-image” CSS along the way to achieve a (consistent) “look”, as we had done before today, (some of) the output of those menu options could be lacking (because they look for “foreground data”), and so we do away with the need for a “background-image” …
<?php
function ctoilater() {
img.src=canvas.toDataURL('data/png');
}
An example of how having web browser (right-clicking (for Windows) and two finger gesturing (for macOS and Mac OS X)) menu options be useful, consider an image menu option such as “Copy image” be used by the user. The user can compose their own (email client (or get to email client via all uppercase web application “to” email “address” definition)) email then, and just Edit -> Paste the results, as an image, into the email body, ahead of sending off that email.
Emoji Borders and Backgrounds Shape Sharing Tutorial
The code “shape shifting” of recent times morphs into “shape sharing” together in amongst the parent users of our PHP tool “Emoji Borders and Backgrounds” web application featuring in yesterday’s Emoji Borders and Backgrounds Shape Numerics Tutorial.
We found the “SVG+XML” mimetype data not accepted by Gmail for an Inline HTML Email, so we have coded for the Downloadable HTML Attachment “way to go”.
<?php
function emailit() {
var fbit='';
var tois=prompt('Please enter an email address to email to (all uppercase uses email client rather than PHP mail and all numerical sends an SMS).','');
if (tois == null) { tois=''; }
if (tois.trim() != '') {
if (tois.indexOf('@') != -1) {
if (tois.toUpperCase() == tois) {
document.getElementById('divemail').innerHTML='<a id=aemail style=display:none; href=mailto:' + tois + '?subject=My%20Emoji%20Border%20and%20Background%20Creation&body=' + encodeURIComponent(document.URL) + '>Email</a>';
//fbit=('' + document.getElementById('aemail').href).split('?')[1];
//document.getElementById('aemail').href='mailto:' + tois + '?' + fbit;
document.getElementById('aemail').click();
} else {
var zhr = new XMLHttpRequest();
var zform=new FormData();
if (1 == 2) { zform.append('inline', ''); }
zform.append('to', tois);
zform.append('subj', 'My Emoji Borders and Background Creation ... at [date] ...');
//alert(encodeURIComponent('<html><body><img src=\"<?php echo $bemb; ?>\"></img></body></html>'));
zform.append('tdhuhta', encodeURIComponent('<html>' + '<?php echo $headstyle; ?>' + '<body><img src="<?php echo $bemb; ?>"></img></body></html>'));
zhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
zhr.send(zform);
alert('Email sent to ' + tois);
}
} else if (tois.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,'') == '') {
document.getElementById('divsms').innerHTML='<a id=asms style=display:none; href=sms:' + tois + '&body=' + encodeURIComponent(document.URL) + '>SMS</a>';
//document.getElementById('asms').href='sms:' + tois + '&body=' + encodeURIComponent(document.URL);
document.getElementById('asms').click();
}
}
}
Emoji Borders and Backgrounds Shape Numerics Tutorial
If you are familiar with the geometry of shapes, it is full of numbers, with the implications as to how the shape is represented and displayed, that those numbers can affect. And so, onto yesterday’s Emoji Borders and Backgrounds Shapes Tutorial‘s start with …
SVG ellipse element functionality today … and for all these, as well as the …
SVG text element we start allowing “shape numerics” as being a user definable thaing
These “shape numerics” can help the user control (that SVG element’s) …
position
size
opacity
font size … (for text elements) on top of that non-numeric control yesterday’s work gave us, being (that SVG element’s fill) …
colour
This is all handled, still, within that “image” argument definition, and we allow a decimal (ie. “.”) delimiter be the way a user separates their integer values (where an opacity value becomes “0.” + [integerValue]).
Onto yesterday’s Emoji Borders and Backgrounds Text Justification Tutorial we add some consideration of SVG shape creations into the mix of the Emoji Borders and Backgrounds work, starting just with the PHP tool, at this stage, and just considering …
… to start off with, and feeding off yesterday’s …
width=[designatedPxWidth] and height=[designatedPxHeight] … and/or …
image=[someImageUrl] (width and height)
… breakdown as to how a user can define a box of interest, as a great “line in the sand” for us to work around with this new “Shapes functionality”. It’s an interfacing job, for tomorrow, getting parents to work with this “image” argument based way a user can define these shapes, including a “noimage” option that uses an image’s width and height (to help out with the shape definition) but does not display that image.
Along the way we also mildly rearrange PHP code for inline_svg.php‘s live run so as to allow for more than one user text to be added via justification prefixing methodologies.
Please enter comma separated HTML decimal entity(s) for your Emoji Border to slideshow. Can use an Emoji Menu entry (eg. control-command-space for macOS or Mac OS X, logo key + . (period) for Windows, control+space for iOS, top left + for Android keyboard). Optionally append &[text|image]css=[CSSstyle] to change styling. Optionally justify some text via &[ul|uc|ur|ml|[mc]|mr|bl|bc|br]text=[someText%0AsecondLine] to change styling. Eg. 🏖&css=opacity:0.9;&textcss=font-size:20px;&ultext=Hello%0AThere
Please enter comma separated HTML decimal entity(s) for your Emoji Border to slideshow. Can use an Emoji Menu entry (eg. control-command-space for macOS or Mac OS X, logo key + . (period) for Windows, control+space for iOS, top left + for Android keyboard). Optionally append &[text|image]css=[CSSstyle] to change styling. Optionally justify some text via &[ul|uc|ur|ml|[mc]|mr|bl|bc|br]text=[someText%0AsecondLine] to change styling. Eg. 🏖&iurl=four.jpg&css=opacity:0.9;font-size:20px;&textcss=text-shadow: -2px 2px 3px #ff2d95;&ultext=Hello%0AThere
Day before yesterday’s (third parent (to our PHP tool) “cab off the rank”) SVG -> IMG -> CANVAS changed PHPcanvas_svg.phplive run now prompts …
Please enter comma separated HTML decimal entity(s) for your Emoji Border to graphics. Can use an Emoji Menu entry (eg. control-command-space for macOS or Mac OS X, logo key + . (period) for Windows, control+space for iOS, top left + for Android keyboard). Optionally append &iurl=[newImageURL] to change base image. Optionally append &[text|image]css=[CSSstyle] to change styling. Optionally justify some text via &[ul|uc|ur|ml|[mc]|mr|bl|bc|br]text=[someText%0AsecondLine] to change styling. Eg. 🏖&iurl=four.jpg&css=opacity:0.9;font-size:20px;&textcss=text-shadow: -2px 2px 3px #ff2d95;&ultext=Hello%0AThere
… via a new “text” argument arranged for the PHP and in readiness for some more aesthetic improvements in the display side of things with our emoji borders and backgrounds
Well, what gives? Eh, wot’s up ‘guv? Yes, indeed … all we can say is …
We got sidetracted.
… and applying spell checker to get around an expression that amuses us …
We got sidetracked.
So, “being ‘guv” … eh, wot … today we’re having a posting catch up with all the parents and PHP tool child allowing for some CSS styling to be a possibility with the web applications around the theme of “Emoji Borders and Backgrounds”, onto yesterday’s Emoji Borders and Backgrounds in Canvas Tutorial.
And so wotwhat’s the go with styling in SVG “land”? Is SVG classy? It’s not that much different to HTML and CSS, in that “style” elements are allowed, and SVG allows you to define a “class” attribute for SVG elements, so that’s kind of classy … eh, wot, ‘guv?! Thanks to this great link for the heads up on this.
… to add the means by which the SVG’s optional “style” elements be populated, leaving it up to the various parents to inform the users of the possibilities here.
Please enter comma separated HTML decimal entity(s) for your Emoji Border to slideshow. Can use an Emoji Menu entry (eg. control-command-space for macOS or Mac OS X, logo key + . (period) for Windows, control+space for iOS, top left + for Android keyboard). Optionally append &[text|image]css=[CSSstyle] to change styling. Eg. 🏖&css=opacity:0.9;&textcss=font-size:20px;
Please enter comma separated HTML decimal entity(s) for your Emoji Border to slideshow. Can use an Emoji Menu entry (eg. control-command-space for macOS or Mac OS X, logo key + . (period) for Windows, control+space for iOS, top left + for Android keyboard). Optionally append &[text|image]css=[CSSstyle] to change styling. Eg. 🏖&iurl=four.jpg&css=opacity:0.9;font-size:20px;&textcss=text-shadow: -2px 2px 3px #ff2d95;
Yesterday’s (third parent (to our PHP tool) “cab off the rank”) SVG -> IMG -> CANVAS changed PHPcanvas_svg.phplive run now prompts …
Please enter comma separated HTML decimal entity(s) for your Emoji Border to graphics. Can use an Emoji Menu entry (eg. control-command-space for macOS or Mac OS X, logo key + . (period) for Windows, control+space for iOS, top left + for Android keyboard). Optionally append &iurl=[newImageURL] to change base image. Optionally append &[text|image]css=[CSSstyle] to change styling. Eg. 🏖&iurl=four.jpg&css=opacity:0.9;font-size:20px;&textcss=text-shadow: -2px 2px 3px #ff2d95;
Can we start with SVG+XML data and get to populate CANVAS data via IMG data via data URIs? Well, not everything works, but enough works to have a lot of what you’d want to do “in this neck of the woods” be possible.
<?php
var svg = document.querySelector('svg');
var img = document.querySelector('img');
var canvas = document.querySelector('canvas');
// get svg data
var xml = new XMLSerializer().serializeToString(svg);
//alert(xml);
// make it base64
var svg64 = ''; //btoa(xml);
var b64Start = ''; //'data:image/svg+xml;base64,';
Well, it’s our “second cab off the rank” regarding a parent idea to oversee our current PHP tool idea for Emoji Borders and Backgrounds.
Anything new? Well, yes, quite a big concept too. With our PHP tool, today, we are allowing it, for a PHP parent (today’s changedimagegridmap.php‘s live run), to fill in a …
It meant a division of two “threads” of modus operandi (possible by “overriding” the PHP echo function into an inhouse “ourecho” (wrapper style of) function), now, for our changedinline_svg.php PHP tool’s PHP tool live run …
“Inhouse Slideshow” functionality can not avoid the serverside PHP (ie. the changedindex.php PHP clone “original” and clone) in all its aspects, and so we still need …
for your more ephemeral “clientside only” tweaks we’ve started, today, with inhouse_slideshow.js to use Javascript junction “overrides” (of PHP Javascript ones above) are possible as a “single external Javascript code file” maintenance ongoing possibility for “Inhouse Slideshow” functionality …
// inhouse_slideshow.js
// RJM Programming
// January, 2022
// Help out with ephemeral aspects to Inhouse Slideshow PHPs
function changeto(tvis) {
if (tvis.toLowerCase().indexOf('exif') == 0) {
if (document.URL.indexOf('?') == -1) {
location.href=posturl(document.URL.split('#')[0] + '?exif=y');
} else {
location.href=posturl(document.URL.split('#')[0].replace(/pdf\=/g, 'pdTf=') + '&exif=y');
}
} else if (tvis.toLowerCase().indexOf('pdf') == 0) {
if (document.URL.indexOf('?') == -1) {
location.href=posturl(document.URL.split('#')[0] + '?pdf=y');
} else {
location.href=posturl(document.URL.split('#')[0].replace(/exif\=/g, 'exTif=') + '&pdf=y');
}
} else if (tvis.toLowerCase().indexOf('emoji') == 0) {
//var borderis=prompt('Please enter comma separated HTML decimal entity(s) for your Emoji Border to slideshow. Can use an Emoji Menu entry.', '');
borderblurb='Please enter comma separated HTML decimal entity(s) for your Emoji Border to slideshow. Can use an Emoji Menu entry (eg. control-command-space for macOS or Mac OS X, logo key + . (period) for Windows, control=space for iOS, top left + for Android keyboard). ';
var borderis=prompt(borderblurb, '');
var emojiy='';
if (borderis == null) { borderis=''; } else { borderis=postbb(borderis); }
if (borderis != '') { emojiy='emoji=' + encodeURIComponent(borderis); }
if (emojiy != '') {
if (document.URL.indexOf('?') == -1) {
location.href=posturl(document.URL.split('#')[0] + '?' + emojiy);
} else {
location.href=posturl(document.URL.split('#')[0].replace(/emoji\=/g, 'emojTi=') + '&' + emojiy);
}
}
} else {
if (document.URL.indexOf('exif=') != -1 || document.URL.indexOf('pdf=') != -1) {
location.href=posturl(document.URL.split('#')[0].replace(/exif\=/g, 'exTif=').replace(/pdf\=/g, 'pdTf='));
} else {
location.href=posturl(document.URL.split('#')[0] + '&eHuhxif=y');
}
}
}
… including that one change to Javascript prompt popup window wording as an example of an ephemeral “clientside only” tweak … becomes a possibility (which is optional … ie. the PHP won’t fail if such an external Javascript does not exist, as those of you actively trying our guinea pig Inhouse Slideshow yesterday might attest to?!)
… the functionalities that would be “cool” at the HTML/Javascript/CSS parent side, and then work out how to implement similar functionality, as possible, in SVG, and if possible, we should be able to pass arguments over to the “PHP tool” to eventually code it to be standalone to help other “parenting arrangements”.
… today’s “theme” concerns the first of those other “parenting arrangements” mentioned above, our PHP for Inhouse Slideshow presentation display options …
… the new “Emoji Border Slideshow” display option above calling into play our inline_svg.php PHP tool’s live run (“Are we there yet, as a tool, I mean … Mum … Dad … anyone?”) web application.
And in so doing, to use an Emoji Menu methodology of selecting your emoji border decoration is “right up your street, Inhouse Slideshow supervisor”.
… into the mix. Why? Well, perhaps, first off, you should read some history of this “Inhouse Slideshow” project over time at Inhouse Slideshow Temporary Unzip Tutorial and then see that this “external Javascript lark” could be better for a way to “future proof” the venture, with some improvements being changeable through one (into your chosen Document Root folder, wherever that may be (that is what the leading “/” infers)) external Javascript codefile rather than a myriad number of identical PHP codefiles.
Emoji Borders and Backgrounds via Emoji Menu Tutorial
On this macOS MacBook Air we’re used to …
control-command-space
… bringing up an Emoji Menu from which an emoji can be selected and placed wherever the cursor had been placed before that menu display. That could be …
on an address bar where the cursor is placed
in a Javascript prompt window that is focussed
… both scenarios (amongst many others, no doubt) of which we will be interested with our current Emoji Borders and Background web application project. Today’s work develops our PHP tool ahead of its supervisory web application, and so we are interested in the first scenario above, as far as testing it goes with our changed underlyinginline_svg.php PHP tool live run web application.
The argument “text” is already catered for from yesterday but the nature of the data this $_GET[‘text’] might contain had not catered for …
unicode data (via Emoji Menus) … but rather had catered for …
Javascript call of String.fromCodePoint([comma separated list of HTML Decimal Entity(s)]);
… only, as of yesterday’s Emoji Borders and Backgrounds Image Text PHP SVG Tutorial. As you would probably concur, yesterday’s setup was quite user unfriendly, as so many of us now are used to those Emoji Menus being used to fill in your emoji data requirements.
The main gist of the PHP code changes goes …
<?php
$innards='';
$preinnards='';
function ingp($m) { // thanks to https://pretagteam.com/question/how-do-i-convert-unicode-special-characters-to-html-entities
global $innards, $preinnards;
if ($innards != '') {
$entity = preg_replace_callback('/[\x{80}-\x{10FFFF}]/u', function ($m) {
$char = current($m);
$utf = iconv('UTF-8', 'UCS-4', $char);
return sprintf("%s;", ltrim(strtoupper(bin2hex($utf)), "0"));
}, $innards);
$preinnards=htmlentities($entity);
$innards='';
}
}
// And then later we change ...
if (isset($_GET['insvg'])) {
$innards=str_replace('+','',urldecode($_GET['insvg']));
if (trim(str_replace(',','',str_replace('0','',str_replace('1','',str_replace('2','',str_replace('3','',str_replace('4','',str_replace('5','',str_replace('6','',str_replace('7','',str_replace('8','',str_replace('9','',$innards)))))))))))) != '') {
ingp($innards);
}
if ($preinnards == '') {
$preinnards='&#' . str_replace(',',';&#',$innards) . ';';
}
// rest of if block code uses $preinnards as SVG text innerHTML ... but UTF-16 Surrogate pair scenarios can fail
}
emoji border … now with content control via comma separated list of emoji HTML decimal Entity(s) … argumented to PHP tool
emoji background … now with content control via comma separated list of emoji HTML decimal Entity(s) … argumented to PHP tool
centralized text (in foreground) … now with wording control via HTML span contenteditable=true elements … not yet argumented to PHP tool
<?php
if (isset($_GET['text'])) {
$txt=str_replace("\n","<br>", str_replace(' ',' ',str_replace('+', ' ', urldecode($_GET['text']))));
$minus30=($w / 2.0) - strlen(explode('<br>', $txt)[0]) * 3.1;
$top30=($h / 2.0) + 25 - (sizeof(explode('<br>', $txt)) * 8);
$lines=explode('<br>', $txt);
for ($il=0; $il<sizeof($lines); $il++) {
$textcontent.='<text x="' . $minus30 . '" y="' . $top30 . '">' . $lines[$il] . '</text>';
$top30+=15;
}
} else if (isset($_POST['text'])) {
$txt=str_replace("\n","<br>", str_replace(' ',' ',str_replace('+', ' ', urldecode($_POST['text']))));
$minus30=($w / 2.0) - strlen(explode('<br>', $txt)[0]) * 3.1;
$top30=($h / 2.0) + 25 - (sizeof(explode('<br>', $txt)) * 8);
$lines=explode('<br>', $txt);
for ($il=0; $il<sizeof($lines); $il++) {
$textcontent.='<text x="' . $minus30 . '" y="' . $top30 . '">' . $lines[$il] . '</text>';
$top30+=15;
}
}
?>
centralized image (in foreground) … including some width and height control … argumented to PHP tool
… via a new “text” argument arranged for the PHP and in readiness for some more aesthetic improvements in the display side of things with our emoji borders and backgrounds changed underlyinginline_svg.php PHP tool live run web application (or in iframe).
… partnership during testing. Our final aim is to make the “underlying PHP” a proper standalone tool, but “that’s a ways off”, as they say. Why this extra level of complication? It’s easier to …
see
test
debug (via web browser web inspectors)
… the functionalities that would be “cool” at the HTML/Javascript/CSS parent side, and then work out how to implement similar functionality, as possible, in SVG, and if possible, we should be able to pass arguments over to the “PHP tool” to eventually code it to be standalone to help other “parenting arrangements”.
And so, today, we “get functional” the combination of parental control through to PHP SVG child display …
Optionally enter an image URL ( as needs be, suffix by &width=[preferredWidth]&height=[preferredHeight] and/or &background=y ) and/or # prefixed comma separated list of emoji HTML decimal Entity(s) ( eg. #127462,127465 ) ? Cancel if it is wording you want to change to the left there.
emoji border … now with content control via comma separated list of emoji HTML decimal Entity(s) … argumented to PHP tool
emoji background … now with content control via comma separated list of emoji HTML decimal Entity(s) … argumented to PHP tool
centralized text (in foreground) … now with wording control via HTML span contenteditable=true elements … not yet argumented to PHP tool
centralized image (in foreground) … including some width and height control … argumented to PHP tool
function ask(sois) {
var huhi=prompt('Optionally enter an image URL ( as needs be, suffix by &width=[preferredWidth]&height=[preferredHeight] and/or &background=y ) and/or # prefixed comma separated list of emoji HTML decimal Entity(s) ( eg. #127462,127465 ) ? Cancel if it is wording you want to change to the left there.','');
if (huhi == null) { huhi=''; }
if (huhi.trim() != '') {
if (document.URL.indexOf('rjmprogramming.com.au') == -1 && sois.title.indexOf('rjmprogramming.com.au') != -1) {
sois.title=document.URL.substring(0,8) + document.URL.substring(8).split('/')[0] + sois.title.split('rjmprogramming.com.au')[1];
}
if (huhi.indexOf('#') != -1) {
var huhis=huhi.split('#')[1];
for (var ihuhis=0; ihuhis<huhis.length; ihuhis++) {
if (eval('' + ihuhis) < eval('' + huhis.length)) {
if (huhis.substring(ihuhis).substring(0,1).replace('0','').replace('1','').replace('2','').replace('3','').replace('4','').replace('5','').replace('6','').replace('7','').replace('8','').replace('9','').replace(',','') != '') {
huhi=huhi.replace('#' + huhis.split(huhis.substring(ihuhis).substring(0,1))[0], '');
huhis=huhis.split(huhis.substring(ihuhis).substring(0,1))[0];
}
}
}
if (huhis == '') {
if (sois.title.indexOf('?insvg=') != -1) {
sois.title=sois.title.replace('?insvg=' + sois.title.split('?insvg=')[1].split('&')[0].split('#')[0],'');
} else if (sois.title.indexOf('&insvg=') != -1) {
sois.title=sois.title.replace('&insvg=' + sois.title.split('&insvg=')[1].split('&')[0].split('#')[0],'');
}
} else if (sois.title.indexOf('?insvg=') != -1) {
sois.title=sois.title.replace('?insvg=' + sois.title.split('?insvg=')[1].split('&')[0].split('#')[0],'?insvg=' + encodeURIComponent(huhis));
} else if (sois.title.indexOf('&insvg=') != -1) {
sois.title=sois.title.replace('&insvg=' + sois.title.split('&insvg=')[1].split('&')[0].split('#')[0],'&insvg=' + encodeURIComponent(huhis));
} else if (sois.title.indexOf('?') != -1) {
sois.title=sois.title.replace('?','?insvg=' + encodeURIComponent(huhis) + '&');
} else {
sois.title=sois.title.split('#')[0] + '?insvg=' + encodeURIComponent(huhis);
}
}
if (huhi.split('&')[0].split('#')[0].trim() != '') {
if (sois.title.indexOf('?image=') != -1) {
sois.title=sois.title.replace('?image=' + sois.title.split('?image=')[1].split('&')[0].split('#')[0],'');
} else if (sois.title.indexOf('&image=') != -1) {
sois.title=sois.title.replace('&image=' + sois.title.split('&image=')[1].split('&')[0].split('#')[0],'');
}
sois.title+='&image=' + encodeURIComponent(huhi.split('&')[0]) + huhi.replace(huhi.split('&')[0],'');
} else if (sois.title.indexOf('?image=') != -1) {
sois.title=sois.title.replace('?image=' + sois.title.split('?image=')[1].split('&')[0].split('#')[0],'');
} else if (sois.title.indexOf('&image=') != -1) {
sois.title=sois.title.replace('&image=' + sois.title.split('&image=')[1].split('&')[0].split('#')[0],'');
}
document.getElementById(sois.id.replace('span','if')).src=sois.title; //+='&image=' + encodeURIComponent(huhi.split('&')[0]) + huhi.replace(huhi.split('&')[0],'');
}
}
function snapshot(sio) {
lastih=sio.innerHTML;
lastio=-1;
for (var iuh=0; iuh<cds.length; iuh++) {
if (lastih == cds[iuh]) { lastio=iuh; } else if (sio.innerHTML.indexOf(' of ') != -1) { if (lastih.split(' of ')[0] == cds[iuh].split(' of ')[0]) { lastio=iuh; } }
}
}
It’s all fine and good creating the Emoji Borders and Backgrounds of yesterday’s Emoji Borders and Backgrounds Primer Tutorial, but it’s the context of their use that we are starting to take an interest in with today’s …
“making use of” “proof of concept” emoji_border_background.htmllive run link for you to contextualize and make use of those table’s iframe contents from yesterday …
… as we figure out, perhaps, some better “tool like generics” here. We’ll see (we hope)!
But, so far, how would we assess the approach, here? Well, what we had to do today reminded us a lot of what we do here, often, at this blog, when we talk about “overlay” webpage design approaches. CSS position:absolute and opacity and Javascript [element].getBoundingClientRect() all made guest appearances!
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 …
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 …
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 …
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 …
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 …
Spotlight is macOS’s built-in, system-wide search engine. Activated by pressing Command + Spacebar, it lets you instantly find files, launch apps, perform math calculations, convert currency, check weather, and run quick system commands without leaving your current window.
… or, for the purposes of today’s tutorial’s purposes, is the bosom buddy, on macOS, of …
Keyboard Shortcuts
… because, after that initial …
Command + Spacebar
… keyboard … shortcut … to get to the Spotlight desktop application, just about everything is possible to get to, to many degrees, without relying on any clicks anywhere, as the previous Windows and macOS Hanging Useful Keyboard Shortcuts Tutorial set the scene for, too.
So, why the interest?
Well, we got into a spot of bother where it became very useful to use (just keyboard) …
Command + Spacebar
Bluetooth
Return key
Arrow down to Bluetooth File Exchange
Return key
Arrow down to (in our case) M720 Triathlon
Return key
… one day. Huh?!
Let me explain. We’ve had clicking problems with this MacBook Air’s trackpad to do with it’s clicking capabilities only (ie. swipe gestures still worked), and, as we gave a bit of the background to with Bluetooth Mouse Show Desktop Primer Tutorial the way we worked around this was to buy and install a bluetooth mouse from Logitech called the M720 Triathlon. All good for more than a year now. But one day we turned Bluetooth Off then rebooted the MacBook Air. Ouch?! Sure, we could log in to the desktop, but doing much else easily relied on …
a fully functional trackpad … or, what could of worked if we’d arranged it …
an installed USB lead based mouse … or even, perhaps, we’re not sure …
plugging in some USB lead based mouse that is plug and play
… none of which were available to me. But good ol’ Spotlight and Keyboard Shortcuts were (ie. to reconnect the bluetooth mouse)! Yayyyyyy!
Can hear some of you asking …
So, what happens when the M720 Triathlon runs out of battery? Isn’t it the same panic?
Well, it happened last week, and we were wondering too! A message comes up on the macOS screen to say the M720 Triathlon is low on battery power, and we just took that message at it’s word, changing the battery, and carried on.
Can hear some of you asking …
So, do the M720 Triathlon and what’s left of the trackpad’s functionality get in each other’s way?
No, they can either or both work at whatever they are capable of.
… it gave us that very annoying “hanging experience” to the extent that our MacBook Air rebooted itself. And so, given this, we hereby apologize if a previous version of the Dots and Boxes game has caused you any grief.
But the fix to our dots_and_boxes_game.htmlDots and Boxes game was a “one number different” fix, and rather than short change our readers … heaven forbid … we’re going to focus, today, more on what keyboard shortcuts on macOS or Windows might help you out in the bad scenario described above …
For Mac, press Option + Command + Esc to open the Force Quit window, or hold Option + Command + Shift + Esc for three seconds to force quit the active app directly. For Windows, press Alt + F4 to close the active app, or Ctrl + Shift + Esc to open Task Manager to kill processes.
Mac Force Quit Shortcuts:
Force Quit Dialog: Option + Command + Esc
Force Quit Focused App: Option + Command + Shift + Esc (hold for 3 seconds)
Standard Quit: Command + Q
Windows Force Quit Shortcuts:
Close Active App: Alt + F4
Open Task Manager (to terminate app): Ctrl + Shift + Esc
Alternative Task Manager: Ctrl + Alt + Delete then select Task Manager
Linux/Other:
Xkill: Ctrl + Alt + Esc (often used to click and destroy a window)
These may or may not help depending on the severity of the memory issue you are facing, in which case …
For Windows, use Win + X then U then R for a fast restart. Alternatively, Alt + F4 on the desktop opens a shutdown menu. For Mac, use Control + Command + Power Button to force a restart. These methods allow full system control without a mouse.
Windows Restart Shortcuts
Power User Menu (Fastest): Press Win + X, then U, then R.
Desktop Shutdown: Press Alt + F4 while on the desktop to open the shutdown dialog, then select “Restart”.
Ctrl+Alt+Del: Press Ctrl + Alt + Del, use arrow keys to reach the power icon, press Enter, and select “Restart”.
Run Command: Press Win + R, type shutdown -r -t 0, and hit Enter.
macOS Restart Shortcuts
Force Restart: Control + Command + Power button (or Touch ID button).
Standard Restart: Control + Option + Command + Power button (closes apps, then restarts).
Linux/Other
Alt+SysRq+B: Forcibly reboots the system immediately.
We hope, one day, but not too soon, you find this useful?!
Yes, that’s it for us today, simply because the “morphed into” is a lot like “the end result”, our new Dots and Boxes Game. After all, both are …
online games
on a board
with span cells
containing onclick logic
ideally for 2 or more players
And as for the onclick logic Javascript logic? We can’t remember relying on the outerHTML (ie. the entire HTML element expressed as HTML) of an HTML (span) element for the (onclick event) logic basis, as per (the plug in code different to (and the same as) yesterday’s code) …
if (document.getElementById('names')) {
if (document.getElementById('names').value == '' && document.getElementById('names').placeholder.indexOf(',') != -1) {
document.getElementById('names').value=document.getElementById('names').placeholder;
//alert(1);
document.getElementById('numsel').value='' + document.getElementById('names').value.split(',').length;
//alert(document.getElementById('numsel').value);
nparrange(document.getElementById('numsel'));
//alert(11);
document.getElementById('smore').innerHTML=notify('Your go ' + anames[curplayer]);
}
}
console.log('1');
if (spano.innerHTML.toLowerCase() == 's' || spano.innerHTML.toLowerCase() == 'o') { return true; }
console.log('2');
var rtis=spano.getBoundingClientRect();
console.log('3');
e = e || window.event;
e.preventDefault();
if (e.touches) {
if (e.touches[0].pageX) {
xx = e.touches[0].pageX;
yy = e.touches[0].pageY;
} else {
xx = e.touches[0].clientX;
yy = e.touches[0].clientY;
}
//console.log('pos3=' + pos3 + ',pos4=' + pos4);
} else if (e.clientX || e.clientY) {
xx = e.clientX;
yy = e.clientY;
} else {
xx = e.pageX;
yy = e.pageY;
}
var topm=eval(yy - rtis.top);
var bottomm=eval(rtis.bottom - yy);
var leftm=eval(xx - rtis.left);
var rightm=eval(rtis.right - xx);
var sbl=eval('' + noc(spano.outerHTML).split('border-').length);
var sbc=eval('' + spano.outerHTML.split('background-color').length);
Even though it took too long to get to yesterday’s Word Find Game Viewport Tutorial‘s Word Find Game’s level of satisfaction, for us, it was always going to be worth it, because when you overengineer it can mean cloning off that into another useful web application is a “paring down” exercise, more than anything, and this is infinitely easier than “a reinvention of the wheel” scenario, especially where it comes to “cross platform” game usage “user experience” issues.
The fun bit of such an approach, in thinking about what we end up with today is the thought that a …
Word Find Game … could be cloned or morphed into a …
SOS Game
… we remember from our days at high school, in less than a day. The memory of the SOS Game did not extend to the detail, and so we thank How to Play – SOS for reminders about the status of diagonality, while up versus down and forwards versus backwards concerns were a bit “per se” regarding “SOS”!
It was the confluence of one “onclick” type event to cover, regarding span elements set out in a grid initialized to a non-breaking space ( ie. ) …
fill in contents as “S”
fill in contents as “O”
… that set us to determining for the span “cell” click, was it positioned …
in the top half means “S” … or …
in the bottom half means “O”
? This had us converging on changes to the “clicks” mode onclick event Javascript logic …
function didclick(e, spano) {
if (document.getElementById('names')) {
if (document.getElementById('names').value == '' && document.getElementById('names').placeholder.indexOf(',') != -1) {
document.getElementById('names').value=document.getElementById('names').placeholder;
//alert(1);
document.getElementById('numsel').value='' + document.getElementById('names').value.split(',').length;
//alert(document.getElementById('numsel').value);
nparrange(document.getElementById('numsel'));
//alert(11);
document.getElementById('smore').innerHTML=notify('Your go ' + anames[curplayer]);
}
}
console.log('1');
if (spano.innerHTML.toLowerCase() == 's' || spano.innerHTML.toLowerCase() == 'o') { return true; }
console.log('2');
var rtis=spano.getBoundingClientRect();
console.log('3');
e = e || window.event;
e.preventDefault();
if (e.touches) {
if (e.touches[0].pageX) {
xx = e.touches[0].pageX;
yy = e.touches[0].pageY;
} else {
xx = e.touches[0].clientX;
yy = e.touches[0].clientY;
}
//console.log('pos3=' + pos3 + ',pos4=' + pos4);
} else if (e.clientX || e.clientY) {
xx = e.clientX;
yy = e.clientY;
} else {
xx = e.pageX;
yy = e.pageY;
}
var thislet=' ';
var locsubscore=0;
var thisi=eval(spano.id.replace('span',''));
if (eval(yy - rtis.top) <= eval(rtis.bottom - yy)) { // s
thislet='S';
spano.innerHTML=thislet;
spano.style.color='rgb(128,0,128)'; //'#000000';
} else {
thislet='O';
spano.innerHTML=thislet;
spano.style.color='#0000ff';
}
sbs=[];
if (eval(thisi % 100) >= 2) { // forwards we are at end
if ((sb(document.getElementById('span' + eval(-2 + thisi))).innerHTML + sb(document.getElementById('span' + eval(-1 + thisi))).innerHTML + sb(document.getElementById('span' + eval(thisi))).innerHTML) == 'SOS') {
sbord();
locsubscore++;
}
}
sbs=[];
if (eval(thisi % 100) >= 1 && eval(thisi % 100) <= 98) { // forwards we are in the middle
if ((sb(document.getElementById('span' + eval(-1 + thisi))).innerHTML + sb(document.getElementById('span' + eval(thisi))).innerHTML + sb(document.getElementById('span' + eval(1 + thisi))).innerHTML) == 'SOS') {
sbord();
locsubscore++;
}
}
sbs=[];
if (eval(thisi % 100) <= 97) { // forwards we are at start
if ((sb(document.getElementById('span' + eval(thisi))).innerHTML + sb(document.getElementById('span' + eval(1 + thisi))).innerHTML + sb(document.getElementById('span' + eval(2 + thisi))).innerHTML) == 'SOS') {
sbord();
locsubscore++;
}
}
if (updownallowed) {
sbs=[];
if (eval(thisi / 100) >= 2) { // down we are at end
if ((sb(document.getElementById('span' + eval(-200 + thisi))).innerHTML + sb(document.getElementById('span' + eval(-100 + thisi))).innerHTML + sb(document.getElementById('span' + eval(thisi))).innerHTML) == 'SOS') {
sbord();
locsubscore++;
}
}
sbs=[];
if (eval(thisi / 100) >= 1 && eval(thisi / 100) <= 18) { // down we are in the middle
if ((sb(document.getElementById('span' + eval(-100 + thisi))).innerHTML + sb(document.getElementById('span' + eval(thisi))).innerHTML + sb(document.getElementById('span' + eval(100 + thisi))).innerHTML) == 'SOS') {
sbord();
locsubscore++;
}
}
sbs=[];
if (eval(thisi / 100) <= 17) { // down we are at start
if ((sb(document.getElementById('span' + eval(thisi))).innerHTML + sb(document.getElementById('span' + eval(100 + thisi))).innerHTML + sb(document.getElementById('span' + eval(200 + thisi))).innerHTML) == 'SOS') {
sbord();
locsubscore++;
}
}
}
CSS styling keeps getting better for webpages, with extended functionality, as proven when CSS3 came to being. Two styling features we find quite impactive are …
CSS animation … and …
CSS linear gradients
… we use, respectively, regarding …
“selection” textarea and “click” div of span elements … helping create a colour coded border “glow” 3D effect …
<style>
Yesterday’s Word Find Game Personalization Tutorial, and the Word Find Game from versions before all had an unwieldy relationship with mobile platforms. The …
selection, via Selection API, using HTML textarea, suits non-mobile well … but we think today’s newly coded for …
click, via HTML span element onclick logics, work better on mobile platforms
… and so we’ve coded for a new dropdown to offer both modus operandi as options, with new “click” modus operandi Javascript …
Yesterday’s Word Find Game Modes of Use Tutorial gave us the means by which we could have multiple players in our “Finding the Word” game, as a number. But many users prefer “names” to “numbers”. And so …
we start asking for an optional (comma separated) user name list … that if used …
starts looking for Notifications API functionality to keep the players informed about proceedings … and …
if underlying Navigator APInavigator.platform points at an underlying macOS operating system, also offer the user the chance to turn notification wording to audio commentary, as well
… modes of use that the user(s) can control. And so, with this in mind, to improve on yesterday’s Word Find Game Underlay Tutorial we’ve set out providing ways the users can control these two concepts via two new dropdowns …
… along with a span element showing the current player number all within that topmost h1 element, and helped out by two new dropdown (ie. select element) onchange event Javascript logics …
var numplayers=1;
var curplayer=0;
var ascores=[0], agoes=[0];
var backwardsallowed=true;
var updownallowed=true;
var diagonalsallowed=true;
var zdebye='';
var zzdebye='';
var lastzdebye='';
var goutstris='';
var delayis=0;
var sofar=';';
var score=0, goes=0, subscore=0;
… but there is essentially only the difference in the control of “z-index” (and maybe “opacity”) property different about the concepts, and to deploy to improve on for the start we made with yesterday’s Word Find Game Primer Tutorial.
“Underlay” usefulness, to us, goes like this …
what you “underlay” … for us within the new CSS styling …
<style>
textarea {
font-family: Courier New;
font-size: 20px;
opacity: 0.9;
z-index: 9;
padding: 0 0 0 0;
margin: 0 0 0 0;
}
It’s lucky for us that there are so many good resources out there to help, one link of real interest, for us, being this excellent link, thanks, getting us to use this new Javascript function …
Here’s another day of testing a Javascript API today, which has that “desktop feel”, that being the Clipboard API …
The Clipboard API provides the ability to respond to clipboard commands (cut, copy, and paste) as well as to asynchronously read from and write to the system clipboard.
… our clipboard_api_test.htmlproof of concept version offering “text clipboard data” functionality from the text based clipboard into an HTML textarea element via …
append
prepend
at cursor
… modes of use, you can also try below (or get a sneakpeak at tomorrow’s ideas) …
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
Around here the hashtag means of constructing URLs, especially regarding “a” email mailto: and SMS sms: URL links, has saved the day manys the time, given our emphasis away from mail server based communication logics.
And today, it is used internally, in URLs, as well. That is because the titles for the seven songs can go beyond the ? and & get argument URL length limits for this blog’s domain Apache/PHP/MySql web server.
In this project, we’ve had the good luck to always organize the shaping of the Song Title data arguments to the end of the ? and & get argument parts of the URLs, wherever they be used. As such, the simple arrangement to not be haunted by URL length restrictions just via a …
var urlofinterest=urlofinterest.replace('&thetlist=', '#thetlist'); // hence ... doh!
… type of Javascript code arrangement. Cute, huh?!
first you have to store such a listing URL into window.localStorage …
if (('' + window.localStorage.getItem('song_list_' + decodeURIComponent(document.URL.split('thelist=')[1].split('&')[0].split('#')[0]))).replace(/^undefined/g,'').replace(/^null/g,'') == '') {
window.localStorage.setItem('song_list_' + decodeURIComponent(document.URL.split('thelist=')[1].split('&')[0].split('#')[0]), newerproposedurl.replace('?', '?subject=' + encodeURIComponent(document.getElementById('mys1').innerText) + '&'));
} else {
window.localStorage.removeItem('song_list_' + decodeURIComponent(document.URL.split('thelist=')[1].split('&')[0].split('#')[0]));
window.localStorage.setItem('song_list_' + decodeURIComponent(document.URL.split('thelist=')[1].split('&')[0].split('#')[0]), newerproposedurl.replace('?', '?subject=' + encodeURIComponent(document.getElementById('mys1').innerText) + '&'));
}
… and then …
you should recall these stored settings so that they can be reused …
function ourblurb(inurlidea) {
return String.fromCharCode(10) + decodeURIComponent(('' + inurlidea + '#thetlist=').split('thetlist=')[1].split('&')[0].split('#')[0]).replace(/\|/g,' ... then ... ' + String.fromCharCode(10));
}
function thenagain(inurlidea) {
var outurlidea=inurlidea, isokayhere=true, iuyx=0;
//alert(inurlidea + ' becomes ' + ('' + inurlidea + '#order=').split('order=')[1].trim());
if (inurlidea.indexOf('thelist=') != -1 && eval('' + ('' + inurlidea + '#order=').split('order=')[1].split('#')[0].trim().split(',').length) == 7) {
//alert(inurlidea + ' Becomes ' + ('' + inurlidea + '#order=').split('order=')[1].split('#')[0] + ' of length ' + eval('' + ('' + inurlidea + '#order=').split('order=')[1].split('#')[0].length));
if (eval('' + ('' + inurlidea + '#order=').split('order=')[1].split('#')[0].length) == 13) {
//alert(inurlidea + ' becomeS ' + ('' + inurlidea + '#order=').split('order=')[1].split('#')[0].trim());
var theseurlsare=('' + inurlidea + '#thelist=').split('thelist=')[1].split('&')[0].split('#')[0];
var newurlsare=theseurlsare;
var theurlsare=decodeURIComponent(theseurlsare.split('&')[0].split('#')[0]).split(',');
var thesetsare=('' + inurlidea + '#thetlist=').split('thetlist=')[1].split('&')[0].split('#')[0];
var newtsare=thesetsare;
var thetsare=decodeURIComponent(thesetsare.split('&')[0].split('#')[0]).split('|');
var thenumsarehere=('' + inurlidea + '#order=').split('order=')[1].split('#')[0].trim().split(',');
var minone=-1;
if ((',' + ('' + inurlidea + '#order=').split('order=')[1].trim() + ',').indexOf(',0,') != -1) {
minone=0;
}
//alert(inurlidea + ' becOmeS ' + ('' + inurlidea + '#order=').split('order=')[1].split('#')[0].trim());
You’d think we’d be done with ablutions at this blog, but noooooo wayyyy … Luna says … there’s all manner of molecular interest out there …
It’s not just H and the 2 and the O … it’s the art of the roleroll.
… or so says “The Lu” anyway!
We wanted our recent Shower Songs web application be the application of choice for URLs at RJM Programming involving our Shower Songs web application YouTube API “radio play” web application.
In a “remembering and recall” scenario …
first you have to store such a listing URL into window.localStorage …
… and then …
you should recall these stored settings so that they can be reused …
our inhouse Shower Songs project using YouTube API means to “Radio Play” some, let’s say, “theatrical pursuits by the water”
A couple of times now, we’ve used it as company in the bath via an iPhone, preparing the list ahead of ever having to tap another anything, to hear some songs in the sequence the user establishes ahead of time … ie. the purpose of this web application.
However, we made some bad colour choices, especially regarding the mobile red button overlayed by black text containing the song title. It was hard to see. It is easier now, that text being yellow.
While there, we decided on a pale green background colour to apply to the textarea user interactive input and progress displaying conduit, when it’s relevant YouTube video is playing (in audio stream only mode), and another principle of our Shower Songs logic is that you should only see one pale green textarea for any great period of time … ie. the songs play in sequence, and looping around in continuous play, like on the radio.
Believe it or not, the actual top level hierarchy Shower Songs window needs no changes for this, but …