popup windows … much maligned … so if there is a way we can transfer to the less maligned …
iframe
… keeping the work within the one window, that would be good, yes? Happily, yes is the go, and with little bother too, with referencing code structure like …
function feedoff(intr, compduris, comptitleis) {
var iqw=0;
if (window.parent != window.self) {
if (window.parent.window.opener) {
//alert('vHere ' + ivid);
iqw=0;
while (parent.document.getElementById('i' + iqw)) {
if (parent.document.getElementById('i' + iqw).value.indexOf('cannotenda2') != -1) {
vidarrv[iqw]='cannotenda2';
}
iqw++;
}
//alert('Here ' + ivid);
window.parent.window.opener.nonytopen(ivid, compduris, comptitleis);
} else {
iqw=0;
while (parent.document.getElementById('i' + iqw)) {
if (parent.document.getElementById('i' + iqw).value.indexOf('cannotenda2') != -1) {
vidarrv[iqw]='cannotenda2';
}
iqw++;
}
//alert('gere ' + ivid);
parent.nonytopen(ivid, compduris, comptitleis);
}
//} else {
// alert('therE');
}
return intr;
}
function localended(avo) {
var iqw=0;
if (window.parent) {
if (parent.document.URL.indexOf('tbox=') != -1) {
if (window.parent.window.opener) {
iqw=0;
while (parent.document.getElementById('i' + iqw)) {
if (parent.document.getElementById('i' + iqw).value.indexOf('cannotenda2') != -1) {
vidarrv[iqw]='cannotenda2';
}
iqw++;
}
setTimeout(function() { parent.window.opener.document.getElementById('j' + parent.document.URL.split('tbox=')[1].split('&')[0]).value='' + Math.ceil(eval('' + contdurs[whichnonyt(eval(-1 + contstarts.length))])) + '.00'; parent.document.getElementById('mytopspan').innerHTML='You can close me now ... ' + parent.document.getElementById('mytopspan').innerHTML; parent.window.opener.focus(); parent.window.opener.backtobase(); parent.window.opener.focus(); duration=-1; aminytnon=false; player=altplayer; }, 1000);
… essentially unaffected by the pretty dramatic change of window usage configuration. Cute, huh?! But how is this made to happen? It’s really simple, really, as the second parameter of window.open can point to an iframe name attribute …
isolation interfacing as in our first designated phase 1 … is a doddle compared to when …
interfacing among a number of players in this (what pans out to be day one of) phase 2
… making it work in with the supervisor of YouTube API “inhouse” web application, and all it’s usage incarnations. We did not expect otherwise. but naturally hoped for the miracle of it all happening in a day.
Never mind … but what can we say about phase 2 we got “contained” today. It’s, to our mind …
YouTube API Caller Other Media Interfacing Tutorial
Today’s work is the result of a “generic push” by us to improve on attempts in the past to use our inhouse YouTube video playing interfacing suite of web applications to mix …
YouTube video media content … interspersed with …
non YouTube media content
… when we presented Spliced Audio/Video YouTube Shuffle Tutorial blog posting thread. We better like this “generic push” idea of adapting our inhouse YouTube API interfacing web application to process both types of media input categories and be handled just within it’s remit, if there are non YouTube media items, within a (newly nesting) …
table element … with …
left hand cell handling YouTube video media content presented via YouTube API’s iframe element approach … and the …
right hand cell handling non YouTube video media content presented via video or audio or img or iframe element depending on the data mimetype …
… and it is our inhouse YouTube API interfacing web application’s job to toggle between CSS display:none; and display:table-cell; for these two cells appropriately.
This work we see as a two part mini-project where …
today’s phase 1 work isolates that inhouse YouTube API interfacing web application and asks it to handle new hashtag based data arguments coming in to demonstrate it, in that isolation, works both for the new paradigm and any previous scenarios … and then …
after today we start phase 2 work interfacings, where we will rejoin the blog posting thread of yesterday’s Tabular Single Row Emoji Sharing Menu Tutorial and allow for the smarter inhouse YouTube API interfacing web application to be relatively seamless changing between YouTube and non YouTube media playing should a user enter a data URI, for example, in one of those textboxes to the right of the checkboxes
isolation interfacing as in our first designated phase 1 … is a doddle compared to when …
interfacing among a number of players in this (what pans out to be day one of) phase 2
… making it work in with the supervisor of YouTube API “inhouse” web application, and all it’s usage incarnations. We did not expect otherwise. but naturally hoped for the miracle of it all happening in a day.
Never mind … but what can we say about phase 2 we got “contained” today. It’s, to our mind …
YouTube API Caller Other Media Interfacing Tutorial
Today’s work is the result of a “generic push” by us to improve on attempts in the past to use our inhouse YouTube video playing interfacing suite of web applications to mix …
YouTube video media content … interspersed with …
non YouTube media content
… when we presented Spliced Audio/Video YouTube Shuffle Tutorial blog posting thread. We better like this “generic push” idea of adapting our inhouse YouTube API interfacing web application to process both types of media input categories and be handled just within it’s remit, if there are non YouTube media items, within a (newly nesting) …
table element … with …
left hand cell handling YouTube video media content presented via YouTube API’s iframe element approach … and the …
right hand cell handling non YouTube video media content presented via video or audio or img or iframe element depending on the data mimetype …
… and it is our inhouse YouTube API interfacing web application’s job to toggle between CSS display:none; and display:table-cell; for these two cells appropriately.
This work we see as a two part mini-project where …
today’s phase 1 work isolates that inhouse YouTube API interfacing web application and asks it to handle new hashtag based data arguments coming in to demonstrate it, in that isolation, works both for the new paradigm and any previous scenarios … and then …
after today we start phase 2 work interfacings, where we will rejoin the blog posting thread of yesterday’s Tabular Single Row Emoji Sharing Menu Tutorial and allow for the smarter inhouse YouTube API interfacing web application to be relatively seamless changing between YouTube and non YouTube media playing should a user enter a data URI, for example, in one of those textboxes to the right of the checkboxes
We had a day recently where we thought it useful to somehow point out to users if their Code Difference Report is not related to the most up to date one they could get, or for that matter if not at the beginning of the code development process.
And then a few days later we realized it would be related, and good, to supply some code file dates for reference, which now happens via a double click or with non-mobile user, just hovering over the Code Difference Report web page.
Primarily, what changed is encapsulated by this new PHP function …
<?php
function posthit($inhit) {
global $dtbizzo;
$outhit=$inhit;
$preout="";
$pregetme="";
$postgetme="";
$htis='';
if (strpos($inhit, 'http://www.rjmprogramming.com.au') !== false) {
$htis='http://';
} else if (strpos($inhit, 'HTTPS://www.rjmprogramming.com.au') !== false) {
$htis='HTTPS://';
}
if (strpos($inhit, '' . $htis . 'www.rjmprogramming.com.au') !== false) {
if (strpos($inhit, '_GETME') !== false) {
$pregetme=str_replace("" . $htis . "www.rjmprogramming.com.au",$_SERVER['DOCUMENT_ROOT'],explode("_GETME", $inhit)[0]);
} else {
$pregetme=rtrim(str_replace("" . $htis . "www.rjmprogramming.com.au",$_SERVER['DOCUMENT_ROOT'],explode("-GETME", $inhit)[0]),'-');
}
$maxlenfile="";
$minlenfile="";
$seclast="";
$thelast="";
foreach (glob($pregetme . "*GETME") as $flfilename) {
if ($thelast == "") {
$thelast=basename($flfilename);
} else {
$seclast=$thelast;
$thelast=basename($flfilename);
}
}
$thisoneinteresting=true;
$nextoneinteresting=false;
$midoneinterest=false;
foreach (glob($pregetme . "*GETME") as $flfilename) {
if ($dtbizzo == "") {
$dtbizzo=" document.body.title='Relevant filenames and dates (also via double click) are '; ";
}
if ($seclast == basename($flfilename)) {
$thisoneinteresting=true;
$nextoneinteresting=false;
$midoneinterest=false;
}
if (basename($flfilename) == basename($inhit)) {
$thisoneinteresting=true;
$nextoneinteresting=false;
$midoneinteresting=true;
}
if ($maxlenfile == "") {
$maxlenfile=$flfilename;
$minlenfile=$flfilename;
} else if (strlen($flfilename) > strlen($maxlenfile)) {
$maxlenfile=$flfilename;
} else if (strlen($flfilename) <= strlen($minlenfile)) {
if (strlen($flfilename) < strlen($minlenfile)) {
$minlenfile=$flfilename;
} else { //if (strpos($flfilename, '-GETME') !== false) {
if (file_exists(str_replace('_GETME','-GETME',$flfilename))) {
$minlenfile=str_replace('_GETME','-GETME',$flfilename);
} else {
$minlenfile=$flfilename;
}
}
}
if (strpos($dtbizzo, " ") === false && ($thisoneinteresting || $nextoneinteresting || $midoneinteresting)) {
$dtbizzo=str_replace(" ';", "' + String.fromCharCode(10) + '" . basename($flfilename) . " " . date ("F d Y H:i:s.", filemtime($flfilename)) . " ';",$dtbizzo);
if ($thisoneinteresting && !$nextoneinteresting && !$midoneinteresting) {
$thisoneinteresting=false;
$nextoneinteresting=true;
} else if ($thisoneinteresting && !$nextoneinteresting && $midoneinteresting) {
$thisoneinteresting=false;
$nextoneinteresting=false;
} else if (!$thisoneinteresting && !$nextoneinteresting && $midoneinteresting) {
$midoneinteresting=false;
} else if (!$thisoneinteresting && $nextoneinteresting && !$midoneinteresting) {
$nextoneinteresting=false;
}
}
}
if ($dtbizzo != "" && strpos($dtbizzo, " ") === false) { $dtbizzo.=' document.body.ondblclick=function(){ var huhpr=prompt(document.body.title,document.body.title); }; '; }
}
if ($minlenfile != "" && $maxlenfile != "") {
if (str_replace($_SERVER['DOCUMENT_ROOT'],"" . $htis . "www.rjmprogramming.com.au",$minlenfile) == $inhit && str_replace($_SERVER['DOCUMENT_ROOT'],"" . $htis . "www.rjmprogramming.com.au",$maxlenfile) == $inhit) {
$preout.=" corresponds to first (and last) difference report ... ";
} else if (str_replace($_SERVER['DOCUMENT_ROOT'],"" . $htis . "www.rjmprogramming.com.au",$minlenfile) == $inhit) {
$preout.=" is first relevant difference report and <a target=_blank title='Latest difference report' href='/PHP/Geographicals/diff.php?one=" . str_replace($_SERVER['DOCUMENT_ROOT'],"" . $htis . "www.rjmprogramming.com.au",$maxlenfile) . "'>currently the report would be</a> <font size=1>(but work on it may be not finalised)</font> ... ";
} else if (str_replace($_SERVER['DOCUMENT_ROOT'],"" . $htis . "www.rjmprogramming.com.au",$maxlenfile) == $inhit) {
$preout.=" <a target=_blank title='First difference report' href='/PHP/Geographicals/diff.php?one=" . str_replace($_SERVER['DOCUMENT_ROOT'],"" . $htis . "www.rjmprogramming.com.au",$minlenfile) . "'>is first relevant difference report</a> and this report corresponds to latest difference report ... ";
} else if ($minlenfile != "" && $maxlenfile != "") {
$preout.=" <a target=_blank title='First difference report' href='/PHP/Geographicals/diff.php?one=" . str_replace($_SERVER['DOCUMENT_ROOT'],"" . $htis . "www.rjmprogramming.com.au",$minlenfile) . "'>is first relevant difference report</a> and <a target=_blank title='Latest difference report' href='/PHP/Geographicals/diff.php?one=" . str_replace($_SERVER['DOCUMENT_ROOT'],"" . $htis . "www.rjmprogramming.com.au",$maxlenfile) . "'>currently the report would be</a> <font size=1>(but work on it may be not finalised)</font> ... ";
}
}
Code Difference AlmaLinux New Webserver Issue Tutorial
Software people involved in PHP programming will be aware that a lot of “what goes on under the hood” configuration wise happens regarding that PHP version’s php.ini configuration file. Sometimes you have direct access to changing the php.ini file. Need I say “be careful” if you make changes, and restart the Apache httpd service to implement? There’ll be situations you have no access to that php.ini file or rely on a shared hosting environment or prefer better experts to handle the changes cough, cough where you’ll forgo these changes to your web hoster’s expertise. Anyway, up until today, php.ini issues on our newer AlmaLinux webserver stopped it performing on that webserver, and had us pointing back to the old webserver IP address to get something working these last weeks.
That php.ini may have a directive …
disable_functions
… where PHP functions such as exec and shell_exec become more and more contentious over time regarding security concerns. Other PHP functions in that category might be file_get_contents (and we started using PHP fread a lot more for example …
… and though we could rearrange into a crontab/curl arrangement that would get around the need for exec and shell_exec calls within this project’s PHP … but as Lleyton and John would say … come on … and … you cannot be serious!
The new diff.php got changed as per this link to suit this new webserver, on it’s own terms.
Code Difference AlmaLinux HTML Issue Followup Tutorial
Web application solutions, looking at them the next day, can often throw up …
There’s more to it than that.
… ideas, especially if you’ve been beavering away at the one code source (section), and the overall solution might involve more than that. And it may be …
your own followup testing … or …
your own followup usage (somewhere else, that annoys) … or …
someone else’s observations
… which gets you to realize you’ve only addressed one part of several parts to an overall solution. This occurred regarding Code Difference AlmaLinux HTML Issue Tutorial from a couple of days ago, and us happening upon a link like the https://www.rjmprogramming.com.au/HTMLCSS/body_cavities.html-GETME (and we’re only worrying about .html and GETME style URLs here) of …
… is like one we’d use at this blog but want it to display code, and in this scenario we often display …
a code differences link … the problems of which we addressed in part one of the solution a few days ago …
a link like above that is meant to display HTML code …
as applicable, a link to the web application involved
So, as of a couple of days ago, that middle one would do something, for HTML code links, but not what we were expecting. But because we have that access to the WordPress blog TwentyTen theme header.php PHP codex code, we can amend as per (working off the structure of a previous modification you can read about at WordPress Table Cell Nests Code Element Overflow Issue Tutorial) …
<?php
function calendar_pass() {
var thisc='', thiscc='', thist='', jiicp=0, thisdate='', thistime='', nexttime='', thishour=0, nexthour=0, thisminute='', thissecond='00', thisurl='';
var h1cps=docgetclass('entry-title','*'); //document.getElementsByTagName('h2');
var tdzs=document.getElementsByTagName('td'), itdzs=0;
var cps=document.getElementsByTagName('a');
var cdes=document.getElementsByTagName('code');
var mfnd=false, washref='';
for (var ijcal=0; ijcal<cps.length; ijcal++) { // new calendar links background image
// Check for GETME links for .htm and no diff.php mention
if (('' + cps[ijcal].href).toLowerCase().indexOf('.htm') != -1 && ('' + cps[ijcal].href).indexOf('GETME') != -1 && ('' + cps[ijcal].href).indexOf('diff.php') == -1) {
washref=('' + cps[ijcal].href);
cps[ijcal].href='//www.rjmprogramming.com.au/PHP/Geographicals/diff.php?zero=' + encodeURIComponent(washref) + '#seehtmllook=n';
}
if (eval('' + ('' + cps[ijcal].href).split('/').length) == 8) {
if (eval('' + ('' + cps[ijcal].href).split('/')[4].length) == 4) {
mfnd=false;
for (itdzs=0; itdzs<tdzs.length; itdzs++) {
if (tdzs[itdzs].innerHTML.replace(String.fromCharCode(10),'').indexOf('<code') == 0 && navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i)) {
if (tdzs[itdzs].outerHTML.indexOf('-webkit-overflow-scrolling') == -1) {
if (1 == 1) {
tdzs[itdzs].innerHTML=tdzs[itdzs].innerHTML.replace('<code>','<code style="-webkit-overflow-scrolling:touch;overflow:scroll;">').replace('<code style="','<code style="-webkit-overflow-scrolling:touch;overflow:scroll;');
} else {
tdzs[itdzs].WebkitOverflowScrolling='touch';
tdzs[itdzs].Overflow='scroll';
}
}
}
cps[jiicp].innerHTML+=' <a id="oe' + h1cps[iicp].innerHTML.split(' id="d')[1].split('"')[0] + '" title="Change order of blog posts (now newest to oldest) to oldest through to newest (like a book)" target=_blank style="text-decoration:none;cursor:pointer;" onclick="hrrearrange(this);">🔀</a>';
… shows the content as an HTML webpage, whereas we’re used to seeing this display the HTML code contained within that file.
Our code difference reporting system worked that way, ideally. Other extensions like *.php* based ones act the same way between CentOS and AlmaLinux, but we’ve decided to live with this “new woooorrrrllldddd order”, and turn it, in a mild way, to our advantage, offering code difference report readers of *.html* code the chance now to …
see the content as the HTML text within … now that we intervene with PHP code such as …
It took us a long time, but we now feel we’re better across, writing web applications, and dealing with URLs, that …
not mentioning protocols http: nor https: specifically is preferable …
As time goes on, more and more we see the benefits of URLs that start with “/” (but not HTTP:// nor HTTPS:// absolute URL designations), especially when it comes to pointing at a “tool” (eg. external Javascript). It has
the benefits of …
is programmer controlled, so they can place the tool in Document Root folder (in the case of an Apache web server) … and, in so doing …
it’s irrelevant where the “parent” (calling) web application is placed … and …
mixed content issues are avoided by not using an absolute URL, though it kind of, is!
… both ideals above related to Mixed Content (ie. involving “competing protocols” within a webpage).
… and we suspect either of these two URLs might have caused this upper and lower HTML iframes issue up until today. How did we approach the remedy? We could have …
detected the Mixed Content potential of the incoming URL and in the PHP reissued the address bar call, effectively, via a header(‘Location: [newUrlFixedForNiMixedContent]’); style of recall … or, what we ended up doing, being (some readers may find the following “a little bit kludgy , this kludgy inside 🎵, am not one of those, who easily 🎶 kludgifies (at least in public)“) …
stayed on the same PHP execution call via …
<?php
if (isset($_GET['one'])) { // && !isset($_GET['two'])) {
if (strpos(('' .$_SERVER['SERVER_PORT']), '443') !== false && strpos(strtoupper($_GET['one']), 'HTTP') !== false && strpos(strtoupper($_GET['one']), 'HTTPS') === false) {
$_GET['one']='HTTPS' . substr($_GET['one'], 4);
} else if (strpos(('' .$_SERVER['SERVER_PORT']), '443') === false && strpos(strtoupper($_GET['one']), 'HTTPS') !== false) {
$_GET['one']='HTTP' . substr($_GET['one'], 5);
}
if (isset($_GET['two'])) {
if (strpos(('' .$_SERVER['SERVER_PORT']), '443') !== false && strpos(strtoupper($_GET['two']), 'HTTP') !== false && strpos(strtoupper($_GET['two']), 'HTTPS') === false) {
$_GET['two']='HTTPS' . substr($_GET['two'], 4);
} else if (strpos(('' .$_SERVER['SERVER_PORT']), '443') === false && strpos(strtoupper($_GET['two']), 'HTTPS') !== false) {
$_GET['two']='HTTP' . substr($_GET['two'], 5);
}
}
// more PHP
}
?>
… to not mix any of the apples with any of the pears!
Code Difference Highlighting User Interface Tutorial
Unless a piece of your web application functionality is categorized as “internal use only” you, as a programmer, will want to offer functionality that does not ask the user to remember some arcane URL (GET ? and &) arrangement at the address bar of a web browser. And so, onto yesterday’s Code Difference Highlighting Tutorial, talking about our inhouse PHP Code Difference Reporting functionality, we wanted to offer …
The PHP diff.php code got changed so that a user entered comma separated list will be scrutinised for whether it represents a single string to find, or if highlighting should happen for each list member in the comma separated list.
also useful, here, could be a highlighting functionality making use of the HTML mark element, that we gave a sneak peek to regarding, yesterday, with Ants Up a Wall Game Mobile Tutorial if you were one of those readers to click the …
It meant, in that scenario yesterday, when a single variable usage “tells a story” in the code, this code difference highlighting might be more effective at explaining the issues rather than showing the code in a code element (even with inhouse colour coding), because there is also the “before” and “after” scenarios there on the screen for the reader to contextualize. See the newly changed PHP diff.php code or try it yourself here.
the server side file and database and operating system smarts of the great serverside language PHP is … all while …
PHP writing out HTML (with its CSS and Javascript) has a web application able to access all that clientside intelligence
… and with this in mind, we allow for saved CSS styling user settings, as of today, with our Difference Report web application arrangements.
Don’t we need a database for this? Well, that is possible, and with serverside PHP, could be done, but we opt for clientside window.localStorage usage to …
Save user CSS styling settings
Recall user CSS styling settings
… so that a user might opt to “set and forget” their preferred set of …
New additional
Changed single line
New block of lines
Deleted lines
Changed multiple lines
… (CSS Selector) sensitive “categories” of Difference Report data type settings.
<?php
$style="<style> font { text-shadow: -1px 1px 1px #ff2d95; } </style>";
$legend="";
$mx="";
$onecommand=" function nocaret(invx) { var outvx=decodeURIComponent(invx); while (outvx.indexOf('<') > outvx.indexOf('>')) { outvx=outvx.replace('>' + outvx.split('>')[1].split('<')[0] + '<',''); } return encodeURIComponent(outvx); } function onb(event) { var othis=event.target, cih=''; if (('' + othis.id + ' ').substring(0,1) == 'f') { cih=('' + window.localStorage.getItem('diff_' + othis.id)).replace(/^undefined$/g,''.replace(/^null$/g,'')); if (('' + othis.innerHTML.replace(/\ \;/g,' ') + '~~').indexOf(' ~~') != -1) { if (cih == '') { window.localStorage.setItem('diff_' + othis.id, encodeURIComponent('14 >' + othis.innerText + '<')); } else { window.localStorage.removeItem('diff_' + othis.id); window.localStorage.setItem('diff_' + othis.id, nocaret(cih) + encodeURIComponent(' >' + othis.innerText + '<')); } } } } function blurize(othis) { if (1 == 2) { othis.onblur=function(event) { onb(event); }; } return othis; } function perhapsih(insg,ofo) { if (insg.indexOf('<') > insg.indexOf('<') && insg.indexOf('<') != -1) { ofo.innerHTML=insg.split('>')[1].split('>')[0]; ofo.setAttribute('data-ih', insg.split('>')[1].split('>')[0]); return insg.replace('>' + insg.split('>')[1].split('>')[0] + '<', ''); } } function givef(idn,cssis) { if (('' + document.getElementById('f' + idn).title).indexOf(' ' + decodeURIComponent(cssis) + ' ') == -1) { document.getElementById('f' + idn).title=document.getElementById('lspan').title + ' You have user CSS styling friendly one off setting of ' + decodeURIComponent(cssis) + ' for this category of Difference Reporting'; } } function getmaybe(foin,defis) { var mgs=document.URL.split(foin.id + '='); thatget=('' + window.localStorage.getItem('diff_' + foin.id)).replace(/^undefined$/g,'').replace(/^null$/g,''); if (thatget != '') { if (eval('' + mgs.length) == 1) { return decodeURIComponent(thatget); } else if (mgs[1].split('&')[0].split('#')[0] == '') { return decodeURIComponent(thatget); } } if (eval('' + mgs.length) > 1) { if (mgs[1].split('&')[0].split('#')[0] != '') { return decodeURIComponent(mgs[1].split('&')[0].split('#')[0]); } } return defis; } function getany() { var mgs=[],addget='',thisget=''; if (document.URL.replace('?','&').indexOf('&f') == -1 || 1 == 1) { for (var iig=0; iig<=6; iig++) { mgs=document.URL.split('f' + iig + '='); thisget=('' + window.localStorage.getItem('diff_f' + iig)).replace(/^undefined$/g,'').replace(/^null$/g,''); if (thisget != '') { document.getElementById('f' + iig).title=document.getElementById('lspan').title + ' You have user CSS styling friendly setting of ' + decodeURIComponent(thisget) + ' for this category of Difference Reporting'; } if (eval('' + mgs.length) > 1) { if (mgs[1].split('&')[0].split('#')[0] != '') { document.getElementById('f' + iig).title=document.getElementById('lspan').title + ' You have user CSS styling friendly setting of ' + decodeURIComponent(mgs[1].split('&')[0].split('#')[0]) + ' for this category of Difference Reporting'; } } if (document.URL.replace('?','&').indexOf('&f' + iig + '=') == -1) { addget+='&f' + iig + '=' + thisget; } } } if (addget != '') { location.href=(document.URL.split('#')[0] + addget).replace('.php&','.php?'); } } setTimeout(getany,2000); function removeany(newfo) { window.localStorage.removeItem('diff_' + newfo.id); } function addany(newishfo,newwhat) { removeany(newishfo); window.localStorage.setItem('diff_' + newishfo.id, newwhat); } function askabout(fo) { var defd='14', ccol='black', ccols=fo.outerHTML.split(' color=' + String.fromCharCode(34)), psizes=fo.outerHTML.split('px'); if (eval('' + ccols.length) > 1) { ccol=ccols[1].split(String.fromCharCode(34))[0]; } if (eval('' + psizes.length) > 1) { defd=psizes[0].split(':')[eval(-1 + psizes[0].split(':').length)].trim(); } var numis=prompt('How many px (ie. pixels) do you want for the font size of these ' + fo.innerHTML + ' parts of report? Optionally append after a space a colour that is not the default colour ' + ccol + ' for this category of difference report. Optionally append after a space any other styling you want ( eg. text-shadow: -1px 1px 1px #ff2d95; ). Append spaces to save for other Coding Difference Report sessions into the future. Prefix with minus ( ie. - ) to forget any remembered setting. An entry can be > followed by a new wording for this category followed by <', getmaybe(fo,defd)); if (numis != null) { if ((perhapsih(numis,fo) + 'x').trim().substring(0,1) == '-') { removeany(fo); numis=numis.replace('-',''); } if (('' + numis).trim() != '') { if (numis.replace(/\ $/g,'') != numis) { addany(fo,encodeURIComponent(numis.trim())); } location.href=(document.URL.split('#')[0] + '&' + fo.id + '=' + encodeURIComponent(numis.trim())).replace('.php&','.php?'); } } } ";
if (isset($_GET['f0']) || isset($_GET['f1']) || isset($_GET['f2']) || isset($_GET['f3']) || isset($_GET['f4']) || isset($_GET['f5']) || isset($_GET['f6'])) {
$onecommand.=" function sizefonts() { } setTimeout(sizefonts, 3000); ";
for ($ij=0; $ij<=6; $ij++) {
if (isset($_GET['f' . $ij])) {
$ihbit="";
$words=str_replace('+',' ',urldecode($_GET['f' . $ij]));
if (strpos($words, '<') !== false && strpos($words, '>') !== false) {
if (strpos($words, '<') > strpos($words, '>')) {
$ihbit=" document.getElementById('f" . $ij . "').innerHTML='" . str_replace("'", "' + String.fromCharCode(39) + '", explode('<',explode('>',$words)[1])[0]) . "'; ";
}
}
if (trim($words) != '') { $onecommand=str_replace("} ", " givef(" . $ij . ",'" . $_GET['f' . $ij] . "'); } ", $onecommand); }
$wordsa=explode(' ', trim($words));
if (sizeof($wordsa) > 1) {
$words=substr($words,(1 + strlen($wordsa[0])));
for ($ijj=1; $ijj<sizeof($wordsa); $ijj++) {
if (strpos($wordsa[$ijj], ':') === false && $ijj == 1) {
$words=trim(substr($words,(0 + strlen($wordsa[$ijj]))));
$style.='<style> .f' . $ij . " { font-color: " . trim($wordsa[$ijj]) . '; } </style>';
$onecommand=str_replace("} ", " document.getElementById('f" . $ij . "').color='' + '" . trim($wordsa[$ijj]) . "'; document.getElementById('f" . $ij . "').style.fontColor='' + '" . trim($wordsa[$ijj]) . "'; } ", $onecommand);
}
}
if (trim($words) != '') {
if (strpos($words, "{") !== false && strpos($words, "}") !== false) {
$style.='<style> ' . $words . ' </style>';
$onecommand=str_replace("} ", " document.getElementById('dstyle').innerHTML+='<style> ' + '" . $words . " </style>'; } ", $onecommand);
} else {
$style.='<style> .f' . $ij . " { " . $words . ' } </style>';
$onecommand=str_replace("} ", " document.getElementById('dstyle').innerHTML+='<style> .f" . $ij . " { ' + '" . $words . " } </style>'; } ", $onecommand);
}
}
}
$onecommand=str_replace("} ", $ihbit . " document.getElementById('f" . $ij . "').style.fontSize='' + '" . trim($wordsa[0]) . "px'; } ", $onecommand);
$style.='<style> .f' . $ij . " { font-size: " . trim($wordsa[0]) . 'px; } </style>';
}
}
}
?>
… to start making this happen (including being able to change our “inhouse category” names, if you like) in our changeddiff.php‘s more colourful Code Differences helper.
Yesterday’s Code Difference Privacy Tutorial represented too much of an echo chamber for our liking. Where possible, we prefer functionality that the users out there can tweak themselves.
In thinking about this, those 5 categories (involving 2 subcategories) …
New additional
Changed single line
New block of lines
Deleted lines
Changed multiple lines
… were what occurred to us could be the CSS Selector basis for us to improve the Code Difference reporting via CSS styling functionality.
Up to today the deployment of that CSS selector logic would have had to be more complex than necessary, but today’s …
giving new id and class attributes to the “legend” span id=lspan elements … and …
equivalent class attribute to report matching element data
… makes the deployment of CSS selector logic really easy, in PHP, as per …
<?php
$style="<style> font { text-shadow: -1px 1px 1px #ff2d95; } </style>";
$legend="";
$mx="";
$onecommand=" function askabout(fo) { var defd='14', ccol='black', ccols=fo.outerHTML.split(' color=' + String.fromCharCode(34)), psizes=fo.outerHTML.split('px'); if (eval('' + ccols.length) > 1) { ccol=ccols[1].split(String.fromCharCode(34))[0]; } if (eval('' + psizes.length) > 1) { defd=psizes[0].split(':')[eval(-1 + psizes[0].split(':').length)].trim(); } var numis=prompt('How many px (ie. pixels) do you want for the font size of these ' + fo.innerHTML + ' parts of report? Optionally append after a space a colour that is not the default colour ' + ccol + ' for this category of difference report. Optionally append after a space any other styling you want ( eg. text-shadow: -1px 1px 1px #ff2d95; )', defd); if (numis != null) { if (('' + numis).trim() != '') { location.href=(document.URL.split('#')[0] + '&' + fo.id + '=' + encodeURIComponent(numis.trim())).replace('.php&','.php?'); } } } ";
if (isset($_GET['f0']) || isset($_GET['f1']) || isset($_GET['f2']) || isset($_GET['f3']) || isset($_GET['f4']) || isset($_GET['f5']) || isset($_GET['f6'])) {
$onecommand.=" function sizefonts() { } setTimeout(sizefonts, 3000); ";
for ($ij=0; $ij<=6; $ij++) {
if (isset($_GET['f' . $ij])) {
$words=str_replace('+',' ',urldecode($_GET['f' . $ij]));
$wordsa=explode(' ', trim($words));
if (sizeof($wordsa) > 1) {
$words=substr($words,(1 + strlen($wordsa[0])));
for ($ijj=1; $ijj<sizeof($wordsa); $ijj++) {
if (strpos($wordsa[$ijj], ':') === false && $ijj == 1) {
$words=trim(substr($words,(0 + strlen($wordsa[$ijj]))));
$style.='<style> .f' . $ij . " { font-color: " . trim($wordsa[$ijj]) . '; } </style>';
$onecommand=str_replace("} ", " document.getElementById('f" . $ij . "').color='' + '" . trim($wordsa[$ijj]) . "'; document.getElementById('f" . $ij . "').style.fontColor='' + '" . trim($wordsa[$ijj]) . "'; } ", $onecommand);
}
}
if (trim($words) != '') {
if (strpos($words, "{") !== false && strpos($words, "}") !== false) {
$style.='<style> ' . $words . ' </style>';
$onecommand=str_replace("} ", " document.getElementById('dstyle').innerHTML+='<style> ' + '" . $words . " </style>'; } ", $onecommand);
} else {
$style.='<style> .f' . $ij . " { " . $words . ' } </style>';
$onecommand=str_replace("} ", " document.getElementById('dstyle').innerHTML+='<style> .f" . $ij . " { ' + '" . $words . " } </style>'; } ", $onecommand);
}
}
}
$onecommand=str_replace("} ", " document.getElementById('f" . $ij . "').style.fontSize='' + '" . trim($wordsa[0]) . "px'; } ", $onecommand);
$style.='<style> .f' . $ij . " { font-size: " . trim($wordsa[0]) . 'px; } </style>';
}
}
}
it was possible, but unlikely, for users to see other user generated reports, if they happened to be asking for reports at exactly the same time … because …
we had not catered for busy traffic here … but, today …
we cater, better, for busy online traffic … and at the same time …
improve the privacy of the reporting on an IP address basis
The downside, at least for us managing this, is that we do not want a build up of files belonging to difference reports long gone. We arrange it, then, that as soon as the report is created, a window.open scenario is coded for …
It’s coming up to a few years now, since we looked at the code differences reporting we offer the reader, as a way to scrutinize code changes, around here, when we presented Code Download Table Difference Functional Hover Tutorial. Well, we thought we might try some colour coding to perhaps lift the fog on the cryptic nature of Linux diff (difference) command based reports. They can be cryptic because they can feed into the automation feeding of the report into other Linux commands to facilitate ongoing editing endeavours, but we do not want to go into that here, at least today.
But on examining the reports we came up with the following difference report “categories” if you will …
New additional
Changed single line
New block of lines
Deleted lines
Changed multiple lines
… the header (of a block of interest) the dead give away, depending on the existence of “a” or “c” or “d” and/or “,” for a common sense reinterpretation by us not visiting “man diff” ourselves, yet, regarding this work.
Is it worth adding “onmouseover” event logic onto yesterday’s Code Download Table Difference Functional Linking Tutorial? You bet it is! Just because “onmouseover” has no relevance to mobile platforms, so, obversely, developing software with version control systems is irrelevant to mobile platforms.
… we figure. But this is of relevance to the programmer. Sometimes, rather than cater for all the platforms, settling on a subset (of those platforms) can be apt because …
one of mobile or non-mobile subsets of platforms is irrelevant to the scenario … as for today … or …
you try to reinvent the wheel on the pretext that you are waiting for a particular web browser or platform to allow the functionality in, into the future … you could be waiting a while, with the complexity of app arrangements going on around the net these days
Anyway, back to the “onmouseover” event on non-mobile platforms … it was the case that this event was a favourite for the conduit towards Ajax (client) functionality. And thinking on what we do today to nuance our Code Differences PHP web application, we were thinking …
What would Ajax (like to) do?
… and we decided Ajax would really like to …
populate a “div” style=display:inline-block; element adjacent to the functional detail to inform about … but this was not possible … so, instead, we …
populate a popup window near to the functional detail to inform about
… for a non-mobile “hover” (ie. “onmouseover”) event.
Along the way we add some more hashtag navigations and set up more colour coding to the output of (the optional) “functional links” Code Difference reporting.
So take a look at our changeddiff.php Code Differences helper applied to itself below …
“Report” button shows to its right …
function domrows() {
document.getElementById('dawrc').innerHTML='<input style=inline-block; type=button onclick=treportdo(); value=Report></input>';
var trsis=document.getElementsByTagName('tr');
for (var itrsis=0; itrsis<trsis.length; itrsis++) {
trsis[itrsis].onclick = function(e) { if (e.target.innerHTML != '') { var trs=document.getElementsByTagName('tr'); for (var itrs=0; itrs<trs.length; itrs++) { if (trs[itrs].outerHTML.indexOf(e.target.innerHTML) != -1) { trs[itrs].style.border='2px dotted red'; } } } };
}
}
… and table row onclick logic is dynamically applied to those “tr” elements
User clicks somewhere within rows they are interested in seeing be included in a report (which is a snippet of the whole Code Download Table, perhaps to do with a project of interest, or a learning topic of interest)
User optionally clicks the “Report” button …
function treportdo() {
var trsis=document.getElementsByTagName('tr');
webc='<html><head><script type="text/javascript"> function emailto(eto) { window.opener.parentemailto(eto); } function xemailto(eto) { if (eto.indexOf("@") != -1) { var zhr=new XMLHttpRequest(); var zform=new FormData(); zform.append("inline",""); zform.append("to",eto); zform.append("subj","Code Download Table part"); zform.append("body",document.getElementById("mytable").outerHTML); zhr.open("post", "//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php", true); zhr.send(zform); alert("Email sent to " + eto); } } </script></head><body><table id=mytable></table><br><br><br><input onblur=emailto(this.value); placeholder="Email to" type=email></input></body></html>';
for (var itrsis=0; itrsis<trsis.length; itrsis++) {
if (itrsis == 0) {
webc=webc.replace('</table>', trsis[itrsis].outerHTML + '</table>');
}
if (trsis[itrsis].outerHTML.indexOf('>') > trsis[itrsis].outerHTML.indexOf('border:')) {
if (trsis[itrsis].outerHTML.indexOf('dotted') > trsis[itrsis].outerHTML.indexOf('border:')) {
webc=webc.replace('</table>', trsis[itrsis].outerHTML + '</table>');
}
}
}
var woois=window.open('','_blank','top=20,left=20,width=600,height=600');
woois.document.write(webc);
}
… which causes a …
New popup window opens showing the relevant snippet of Code Download Table of interest to the user … including …
Textbox for an optional emailee entry that can be filled in … to …
Set off Ajax/FormData methodology means …
function parentemailto(eto) {
if (eto.indexOf("@") != -1) {
var zhr=new XMLHttpRequest();
var zform=new FormData();
zform.append("inline","");
zform.append("to",eto);
zform.append("subj","RJM Programming Code Download Table part");
zform.append("body", reltoabs('<table' + webc.split('</table>')[0].split('<table')[1] + '</table>'));
zhr.open("post", "//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php", true);
zhr.send(zform);
alert("Email sent to " + eto);
}
}
… to send off an Inline HTML Email report to the emailee … including …
Links of email can be clicked to get back to source code and other links back at the RJM Programming domain web server
… and … lo and behold … we saw a good use for the idea of …
download from “the net” to a Downloads folder on your computer or device … and more often than not …
you, the user, copies or renames this data to another location on your computer or device with command line or with operating system GUI
… and allowing for that second step above be programmatical with the most apt functionality that had ever passed our cotton pickin’ mind … our Code Download Table … wi’ all tho’ GETME’s!
But we don’t want to interfere too much with the Code Download Table “flow” here, so create up the top left 20 seconds worth of time (extendable by their actions) available to the user to create “download” attributes on all …
“a” links … with …
“href” attribute containing “GETME” …
but not “diff.php” … and …
“download” attribute (the attribute necessary to “download” rather than our default displaying of source code in a new webpage)
… plus no href attribute containing “?s=” either, for today’s purposes with a changedgetmelist.js external Javascript code file (that you can try out for yourself at this live run link) … via its new …
var dnprefix=decodeURIComponent(('' + localStorage.getItem('download_copy_to_folder')).replace(/^null$/g,'')); //.replace(/\+/g,' ').replace(/\\\\/g, '_').replace(/\//g, '_').replace(/\:/g, '_');
var delaymore=0;
var prefixask='<div id=firstask style="position:absolute;top:0px;left:0px;"> Download GETME? <input id=dpccb style=inline-block; type=checkbox onchange="dogetmes(document.getElementById(' + "'" + 'dpcis' + "'" + ').value);"></input> <input style=inline-block;width:300px; onclick="delaymore+=20000;" onblur="if (document.getElementById(' + "'" + 'dpccb' + "'" + ').checked) { dogetmes(document.getElementById(this.value); }" type=text id=dpcis placeholder="Optional Download Folder Later Copy to Place via Listener" value="' + dnprefix + '"></input></div>';
function dogetmes(dpprefix) {
delaymore+=20000;
var asis=document.getElementsByTagName('a');
if (dpprefix != dnprefix && 1 == 7) {
localStorage.setItem('download_copy_to_folder', dpprefix);
}
for (var iasis=0; iasis<asis.length; iasis++) {
if (asis[iasis].href.indexOf('diff.php') == -1 && asis[iasis].href.indexOf('?s=') == -1 && asis[iasis].href.indexOf('GETME') != -1) {
asis[iasis].download=dpprefix.replace(/\//g,'_').replace(/\\\\/g,'_').replace(/\:/g,'_') + asis[iasis].href.split('/')[eval(-1 + asis[iasis].href.split('/').length)];
}
}
}
function nomorepa() {
if (eval('' + delaymore) == 0) {
if (document.getElementById('firstask')) {
document.getElementById('firstask').innerHTML='';
}
} else {
setTimeout(nomorepa, eval('' + delaymore));
delaymore=0;
}
}
function lastdivpop() {
var wasih='';
if (document.getElementById('lastdiv')) {
if (document.getElementById('lastdiv').innerHTML == '') {
wasih=wasih;
setTimeout(lastdivpop, 3000);
} else if (document.getElementById('lastdiv').innerHTML.indexOf('firstask') == -1) {
wasih=document.getElementById('lastdiv').innerHTML;
document.getElementById('lastdiv').innerHTML=prefixask + wasih;
prefixask='';
setTimeout(nomorepa, 20000);
} else {
setTimeout(lastdivpop, 3000);
}
}
}
… and we’ve just “tweaked” (albeit, very importantly, in our books (… but the pamphlettes are still not playing ball)) to ensure no “file clobbering” takes place so that the Korn Shell now does …
suf=""
isuf=-1
while [ -f "${dpath}/${brest}${suf}" ]; do
((isuf=isuf+1))
suf="_${isuf}"
done
if [ ! -z "$suf" ]; then
echo "mv ${dpath}/${brest} ${dpath}/${brest}${suf} # `date`" >> download_to_place.out
mv ${dpath}/${brest} ${dpath}/${brest}${suf} >> download_to_place.out 2>> download_to_place.err
fi
But today is mainly about filling in the missing bits on the “server” side. This (need for a) “conduit” we referred to yesterday is because we accept no folder paths can be mentioned at the “server” end. Suppose, though, that the “non-pathed” filename we supply to an “a” link’s “download” attribute can be prefixed by a mildly mashed up version of that path we copy to from the Downloads folder of your “client” computer or device, as you perform a “download” via the clicking of an “a” link.
Well, at this blog we’d already started functionality to toggle the use or not of …
“a” links … with …
“href” attribute containing “GETME” …
but not “diff.php” … and …
“download” attribute (the attribute necessary to “download” rather than our default displaying of source code in a new webpage)
displaying of source code in a new webpage for GETME “a” links … versus …
use the changed PHPtoggle_download.php in conjunction with a changed good ‘ol TwentyTen Theme header.php as below …
<?php
if (outs == null) {
var dnprefix=decodeURIComponent(('' + localStorage.getItem('download_copy_to_folder')).replace(/^null$/g,'')).replace(/\+/g,' ').replace(/\\\\/g, '_').replace(/\//g, '_').replace(/\:/g, '_');
for (idmjk=0; idmjk<admjk.length; idmjk++) {
if (admjk[idmjk].href.indexOf('GETME') != -1 && admjk[idmjk].href.indexOf('diff.php') == -1) {
big = '----------------------GETME';
stuffs = newaspare.split('/');
if (dnprefix != '') {
admjk[idmjk].download = dnprefix + prestuffs[stuffs.length - 1];
} else {
admjk[idmjk].download = dnprefix + stuffs[stuffs.length - 1];
}
admjk[idmjk].title = "(Really download) " + admjk[idmjk].title + ' ... welcome to the long hover functionality that shows allows for a Download Mode for the blog that can be toggled';
admjk[idmjk].onmouseover = " getDownloadMode(); ";
admjk[idmjk].onmouseout = " yehBut(); ";
admjk[idmjk].ontouchstart = " getDownloadMode(); ";
admjk[idmjk].ontouchend = " yehBut(); ";
}
} else if (admjk[idmjk].href.indexOf('GETME') != -1 && origcafd < 0) { //!cafd) {
xp=admjk[idmjk].href.split("GETME");
prexp=xp[0].split("/");
postprexp=prexp[-1 + prexp.length].split(".");
extis = postprexp[-1 + postprexp.length].replace(/_/g,"").replace(/-/g,"").replace(/GETME/g,"");
outs="//www.rjmprogramming.com.au/getmelist.htm?topoff=150&tsp=" + (Math.floor(Math.random() * 1999900) + 100) + "#" + postprexp[0] + "." + postprexp[-1 + postprexp.length].replace(extis,"").replace(extis,"").replace(extis,"") + "GETME" + extis;
aorig=admjk[idmjk].innerHTML;
selbitis=allthecombos((admjk[idmjk].href + '=').split('=')[1].split('&')[0]);
admjk[idmjk].innerHTML=admjk[idmjk].innerHTML.replace(".","<span data-alt='" + outs + "' id='spn" + cafd + "' title=\" + Code Download Table\" onclick=\"if (cafd == cafd) { cafd=" + cafd + "; changeasfordownload(); } else { window.open('" + outs + "','_blank','top=100,left=100,width=500,height=500'); } return false; \"><select onchange=\" if (this.value.length > 0) { window.open(this.value,'_blank'); } return false; \" style='margin-bottom:0px;width:40px;' id='sel" + cafd + "'><option value=>⚫</option>" + selbitis + "</select></span>");
if (aorig == admjk[idmjk].innerHTML && admjk[idmjk].innerHTML.indexOf('er posts') == -1) admjk[idmjk].innerHTML=admjk[idmjk].innerHTML.replace(" ","<span data-alt='" + outs + "' id='spn" + cafd + "' title=\" + Code Download Table\" onclick=\"if (cafd == cafd) { cafd=" + cafd + "; changeasfordownload(); } else { window.open('" + outs + "','_blank','top=100,left=100,width=500,height=500'); } return false; \"><select onchange=\" if (this.value.length > 0) { window.open(this.value,'_blank'); } return false; \" style='margin-bottom:0px;width:40px;' id='sel" + cafd + "'><option value=>⚪</option>" + selbitis + "</select></span>");
cafd++;
} else if ((admjk[idmjk].innerHTML.indexOf('live run') != -1 || admjk[idmjk].title.toLowerCase().indexOf('click picture') != -1) && origcafd < 0) { //!cafd) {
outs="//www.rjmprogramming.com.au/slideshow.html#tuts";
admjk[idmjk].innerHTML=admjk[idmjk].innerHTML.replace(" ","<span data-alt='" + outs + "' id='spn" + cafd + "' title=\" + Cut to the Chase ... see the blog post list related to live runs and slideshows ... ie. the main point of the blog posting\" onclick=\"if (cafd == cafd) { cafd=" + cafd + "; changeasfordownload(); } else { window.open('" + outs + "','_blank','top=100,left=100,width=650,height=100'); } return false; \">✂</span>");
cafd++;
}
}
}
?>
… to, depending on whether the user specifies in the “All Posts” toggling’s Javascript prompt window presented, specifies a new comma separated “client folder of interest to copy to” place (stored in window.localStorage), will …
download with the GETME to the Downloads folder and copy off to the specified folder of interest (backing up as necessary) … versus …
the default download mode downloads to the Downloads folder without the GETME parts
See these changes in action below, contextualizing “server” and “client” codes in the full picture of assisted Downloads (copied on to a folder of the user’s interest) …
Downloading from “the net” (“server land”) to your computer or device (“client land”) is a big part of the online experience and the sharing of data over the world wide web. But have you ever wondered about the two step design of …
download from “the net” to a Downloads folder on your computer or device … and more often than not …
you, the user, copies or renames this data to another location on your computer or device with command line or with operating system GUI
… ? Why not allow the “server” side define where it can download to on the “client”? Well, that would be a security nightmare, allowing a highjacking of mission critical files on your computer or device. So, I get it, that is a “no no”. But could we have a controlled “arrangement” between …
… ? We think that sounds reasonable and so, today, we start our (two parts or more) mini-project (making step 2 above be considered to be programmatically handled, sometimes) designing a Korn Shell (“client” side) listener to suit our macOS “client” computer, executed as a background process via …
But what is the conduit, if the “server” web applications/pages cannot define a destination folder other than the macOS Downloads folder for the user involved? Well, that is where we need either …
Korn Shell interactive input (via read command) … or …
… to define a “client land” folder to copy to (from the user’s Download folder (receiving the downloaded data).
That first Korn Shell read command interactive input was interesting to us for a command backgrounded via the “&” command suffix. But if stdin and stdout are not mentioned in the command you can answer this interactive input and then the processing the Korn Shell performs proceeds in the background. Exactly what we were hoping for, but weren’t sure that this was the case!
The picture is filled in better tomorrow as we discuss the conduit in more detail tomorrow.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
YouTube API Caller Other Media Interfacing Tutorial
Today’s work is the result of a “generic push” by us to improve on attempts in the past to use our inhouse YouTube video playing interfacing suite of web applications to mix …
YouTube video media content … interspersed with …
non YouTube media content
… when we presented Spliced Audio/Video YouTube Shuffle Tutorial blog posting thread. We better like this “generic push” idea of adapting our inhouse YouTube API interfacing web application to process both types of media input categories and be handled just within it’s remit, if there are non YouTube media items, within a (newly nesting) …
table element … with …
left hand cell handling YouTube video media content presented via YouTube API’s iframe element approach … and the …
right hand cell handling non YouTube video media content presented via video or audio or img or iframe element depending on the data mimetype …
… and it is our inhouse YouTube API interfacing web application’s job to toggle between CSS display:none; and display:table-cell; for these two cells appropriately.
This work we see as a two part mini-project where …
today’s phase 1 work isolates that inhouse YouTube API interfacing web application and asks it to handle new hashtag based data arguments coming in to demonstrate it, in that isolation, works both for the new paradigm and any previous scenarios … and then …
after today we start phase 2 work interfacings, where we will rejoin the blog posting thread of yesterday’s Tabular Single Row Emoji Sharing Menu Tutorial and allow for the smarter inhouse YouTube API interfacing web application to be relatively seamless changing between YouTube and non YouTube media playing should a user enter a data URI, for example, in one of those textboxes to the right of the checkboxes
Tabular Single Row YouTube API Radio Play Textbox Tutorial
Apart from yesterday’s Tabular Single Row YouTube API Radio Play Tutorial‘s mainly HTML input type=checkbox means by which the user could control their YouTube video “Radio play” modes of use, there was one more aspect to user control we wanted to offer, that being to …
allow user to change a "Radio playlist" member in real time by changing the textboxes to the right of the individual checkboxes, primarily to enter in their own YouTube 11 character video identifier
… and have the software detect this event programmatically and update various aspects of that “Radio play” scenario.
This means, now, we can publish a more complete Javascript interventional snippet of code of relevance to yesterday’s and today’s work below …
if (parent.document.getElementById('shufflechk')) {
if (!parent.document.getElementById('shufflechk').checked) {
nextvidi++;
if (eval('' + viddurs.length) <= nextvidi) {
nextvidi=0;
}
} else {
nextvidi=Math.floor(Math.random() * eval('' + viddurs.length));
}
} else {
nextvidi++;
if (eval('' + viddurs.length) <= nextvidi) {
nextvidi=0;
}
}
}
}
setTimeout(playingvideo, onethous);
return '';
}
inplaying=true;
// rest of function playingvideo() as before ...
// end of function playingvideo()
}
Tabular Single Row YouTube API Radio Play Tutorial
Today, we’re back to the Tabular Single Row Media Gallery ideas regarding the web application of the recent Tabular Single Row Textarea Sharing Tutorial and we’re involving more “button innards” thinking, with respect to adding onto the pre-existing …
radio play with YouTube audio stream only non-mobile platform incarnation … involving textarea usage … with, today, after getting the YouTube API inhouse web application, better attuned to …
radio play with YouTube audio stream only mobile platform incarnation … and …
radio play with YouTube video play on non-mobile platform incarnation … and …
radio play with YouTube video play on mobile platform incarnation
… additional functionalities allowing for …
looping (though the total list) … default for “radio play” …
shuffling … non-default …
individual YouTube video checkbox user control can end a song before it’s finish
recognition of individual YouTube video checkbox user control as above (can mean an individual song can be looped or deliberately ignored)
saving the playlist for user recall on the same web browser
… functionalities within those three new incarnations. We could organize the first to use the YouTube API in that “vertical in the one table cell, with checkboxes” way, too, but see the textareas of different interest in an ongoing sense. We’ll see whether we offer that YouTube API approach in a future release?!
lots of use of textarea element placeholder attribute for explaining more complex data entry scenarios … and as of today …
a button element with the potential that it’s “innards” (ie. it’s innerHTML) is filled by a dropdown (ie. select element)
… it being our first time trying any HTML button element “innards” that were not always straight text. Is this the “comboBox” style of arrangement we used to see with desktop application GUIs and have long wished to simulate the usefulness of, online?!
Well, we’re not quite sure, but we think we’re going to look into it more into the future. So, what are we storing in this dropdown? We’re offering the user the chance … over to you code …
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+='Entries starting with _ can describe ... ' + String.fromCharCode(10) + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Comma separated audio stream only list of your own recallable YouTube IDs playlist' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Semicolon separated video list of your own recallable YouTube IDs playlist' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' One of these inhouse playlist names above which start with tube' + perusedlist + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' YouTube Playlist 34 character identifier' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Spotify Playlist 22 character identifier' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Sundry _email _sms _saverecall _http://youtube.com#example' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).ondblclick=function(event){ alert(event.target.title); };
… culminating in this new Javascript function …
function underscoresystem(inans, inota) {
var anchor=null, subjh='?subject=Hello';
var outans=inans;
var ytpllen=34; //eval('' + ('PLNtJQ2eJVoAMFocY7LOANvPH3cLyUonin').length);
var onevlen=11;
var sppllen=22;
var inhouse='';
var rperson='';
var mperson='';
var tperson='';
var jnota=inota;
if (outans.trim() != '') {
if (outans.toLowerCase().indexOf('mailto') == 0 || outans.toLowerCase().indexOf('email') == 0 || outans.toLowerCase().indexOf('sms') == 0 || outans.toLowerCase().indexOf('http') == 0 || outans.toLowerCase().indexOf('//') == 0 || outans.toLowerCase().indexOf('www.') == 0 || outans.toLowerCase().indexOf('save') == 0 || outans.toLowerCase().indexOf('recall') == 0) {
outans='_' + inans;
}
if (outans.replace(/^tube/g,'_').substring(0,1) == '_') {
subjh='?subject=' + encodeURIComponent('' + document.title + ' regarding ' + document.URL);
if (inota) {
if (document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target)) {
jnota=document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target);
}
}
if (('' + top.document.URL).indexOf('/swipe_media.htm') != -1) {
subjh='?subject=' + encodeURIComponent('' + document.title + ' regarding ' + document.URL);
if (('' + top.document.URL).indexOf('youtube=') != -1) {
if (('' + top.document.URL).indexOf('audioyoutube=') != -1 && ('' + top.document.URL).indexOf('isradio=') != -1) {
tperson='tuberadioapl_';
} else if (('' + top.document.URL).indexOf('youtube=') != -1 && ('' + top.document.URL).indexOf('isradio=') != -1) {
tperson='tuberadiovpl_';
} else if (('' + top.document.URL).indexOf('audioyoutube=') != -1 && ('' + top.document.URL).indexOf('isradio=') == -1) {
tperson='tubeapl_';
} else {
tperson='tubevpl_';
}
}
} else if ((outans + ' ').substring(0,4) == '____') {
tperson='tuberadioapl_';
outans=outans.replace(/^\_\_\_\_/g,'_');
} else if ((outans + ' ').substring(0,3) == '___') {
tperson='tuberadiovpl_';
outans=outans.replace(/^\_\_\_/g,'_');
} else if ((outans + ' ').substring(0,2) == '__') {
tperson='tubeapl_';
outans=outans.replace(/^\_\_/g,'_');
}
if (outans.indexOf('_recall') == 0 || outans.indexOf('_save') == 0) {
newlsval=tperson + ( ('' + Math.floor(Math.random() * 19897654)) + ('' + Math.floor(Math.random() * 19897654)) + ('' + Math.floor(Math.random() * 19897654)) + '000000000000000000000000000').substring(0,27);
if (perusedlist == '') { perusedlist=' ( and we found )'; }
perusedlist=perusedlist.replace(' )', ' ' + newlsval + ' )');
lastbize(newlsval, document.URL);
window.localStorage.setItem(newlsval, encodeURIComponent(document.URL));
jnota.title+=' ... for future recall reenter in web browser similar textarea playlist name ' + newlsval;
outans='';
if (inota) {
if (document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target)) {
jnota=document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target);
}
jnota.value='';
}
if (document.getElementById('recallable')) {
document.getElementById('recallable').innerHTML=newlsval;
document.getElementById('recallable').style.visibility='visible';
lastrecall=maybeselize(document.URL,lastrecall,newlsval);
}
newlsval='';
Yes, the placeholder was struggling with what we wanted to offer as functionality, so we’re putting more into the textarea title attribute. These options allow users to create their own recallable playlists or access existant YouTube or Spotify ones to keep the music rolling in changedswipe_media.htmlTabular Single Row Media Gallery helped out by ytaudioonly.jsexternal Javascript helper.
We do not expect many readers to have thought yesterday’s YouTube Audio Only Mobile Looping Tutorial that little bit different. It was long in the making, and difficult (at least for us), but a brief tutorial. Too brief for interested parties, and we intended it as a seminal discussion, but it’s meaning got subsumed, if you will, with the goings on of the blog posting thread last talked about with the day before’s Tabular Single Row Image Gallery Linking Tutorial.
You see, the YouTube audio stream only inhouse looping control is an essential thing we needed to tie down for today’s progress in this blog posting thread, that being the first incarnation of …
Radio Play
… sequenced modes of play we’re offering today, as a start, for …
non-mobile users
using audio stream only button option clicks
So what was (most) …
difficult (at least for us)
… about getting this first Radio Play idea happening? In a word …
Focus
You find yourself wanting to do lots of …
[element].click();
… and …
[element].blur();
… types of work, which work best with focus (and boy, did we need to “focus”?!) We’ve minimised this where we could, but what we highly recommend users of this functionality doing is …
be presented with the solo web browser full screen (initial) window with the radio emoji
click desired first song link when the song link emojis appear … then …
it’s best to make this window small and away from other web browser windows but not overlapping for the best chance of …
songs play in sequence and loop back through the list
background WordPress Blog random tutorial images … and link them via a modified 404.php creator’s …
<?php
if (strpos(('?' . $_SERVER['QUERY_STRING']), '?rand=') !== false) {
$huht=@file_get_contents('http://www.rjmprogramming.com.au/HTMLCSS/select_palette.php?putrandom=' . urlencode('?rand=' . $_GET['rand'] . '.' . $pnumis . $ptitle));
}
changedswipe_media.htmlTabular Single Row Media Gallery …
function checkmeout(evt, iscuttothechase) {
if (evt.target.outerHTML.split('>')[0].indexOf('url(') != -1) {
if (evt.target.outerHTML.split('>')[0].split('url(')[1].split(')')[0].indexOf('?rand=') != -1) {
if (iscuttothechase) {
document.getElementById('sparepal').src='/HTMLCSS/select_palette.php?getrandom=' + encodeURIComponent('RAND=' + evt.target.outerHTML.split('>')[0].split('url(')[1].split(')')[0].split('?rand=')[1].split("'")[0].split('"')[0].split(')')[0].split('"')[0]); // + ('&viJUNKa=' + evt.target.id).replace('td','div')
} else {
document.getElementById('sparepal').src='/HTMLCSS/select_palette.php?getrandom=' + encodeURIComponent('rand=' + evt.target.outerHTML.split('>')[0].split('url(')[1].split(')')[0].split('?rand=')[1].split("'")[0].split('"')[0].split(')')[0].split('"')[0]); // + ('&viJUNKa=' + evt.target.id).replace('td','div')
}
}
}
}
… web application, now accepting …
click on one of these WordPress Blog random tutorial images to open window to relevant WordPress Blog posting … or …
right click on one of these WordPress Blog random tutorial images to open window to relevant Cut to the Chase web application or presentation media regarding that blog posting above
… we’re sure is the reaction of some readers out there, to today’s “starting draft” for an idea we had. Today’s part of the jigsaw we want to explore may be “ho hum” and “so non-mobile”, but we’re hoping the thread appeals to more modern ideas as we go along.
So much for the apologies, and so let’s now look at the basis to this start …
media (so far just image) …
gallery … presented in …
table … HTML element … in …
single tr row
deliberately overflowing to the right of the screen view of the webpage … and so …
scrollable (ie. window scrollable) to … those right hand parts …
only “media loaded” …
as “scrolled to” or “stumbled upon”
So far the window scrollable detection goes (thanks to this useful link) …
window.onscroll = function (e) {
// called when the window is scrolled.
vspos4=document.body.scrollTop;
vspos3=document.body.scrollLeft;
var vstdn=Math.ceil(eval('' + vspos3) / eval('' + amin));
if (curtdn != vstdn) {
checkthen(vstdn);
}
}
… and the CSS produced “wording (ie. non-media) content” is produced down the bottom left of “inhouse WordPress Blog image cell background” ( possible because of background-size:contain ) we think is interesting …
Tabular Single Row YouTube API Radio Play Textbox Tutorial
Apart from yesterday’s Tabular Single Row YouTube API Radio Play Tutorial‘s mainly HTML input type=checkbox means by which the user could control their YouTube video “Radio play” modes of use, there was one more aspect to user control we wanted to offer, that being to …
allow user to change a "Radio playlist" member in real time by changing the textboxes to the right of the individual checkboxes, primarily to enter in their own YouTube 11 character video identifier
… and have the software detect this event programmatically and update various aspects of that “Radio play” scenario.
This means, now, we can publish a more complete Javascript interventional snippet of code of relevance to yesterday’s and today’s work below …
if (parent.document.getElementById('shufflechk')) {
if (!parent.document.getElementById('shufflechk').checked) {
nextvidi++;
if (eval('' + viddurs.length) <= nextvidi) {
nextvidi=0;
}
} else {
nextvidi=Math.floor(Math.random() * eval('' + viddurs.length));
}
} else {
nextvidi++;
if (eval('' + viddurs.length) <= nextvidi) {
nextvidi=0;
}
}
}
}
setTimeout(playingvideo, onethous);
return '';
}
inplaying=true;
// rest of function playingvideo() as before ...
// end of function playingvideo()
}
Tabular Single Row YouTube API Radio Play Tutorial
Today, we’re back to the Tabular Single Row Media Gallery ideas regarding the web application of the recent Tabular Single Row Textarea Sharing Tutorial and we’re involving more “button innards” thinking, with respect to adding onto the pre-existing …
radio play with YouTube audio stream only non-mobile platform incarnation … involving textarea usage … with, today, after getting the YouTube API inhouse web application, better attuned to …
radio play with YouTube audio stream only mobile platform incarnation … and …
radio play with YouTube video play on non-mobile platform incarnation … and …
radio play with YouTube video play on mobile platform incarnation
… additional functionalities allowing for …
looping (though the total list) … default for “radio play” …
shuffling … non-default …
individual YouTube video checkbox user control can end a song before it’s finish
recognition of individual YouTube video checkbox user control as above (can mean an individual song can be looped or deliberately ignored)
saving the playlist for user recall on the same web browser
… functionalities within those three new incarnations. We could organize the first to use the YouTube API in that “vertical in the one table cell, with checkboxes” way, too, but see the textareas of different interest in an ongoing sense. We’ll see whether we offer that YouTube API approach in a future release?!
lots of use of textarea element placeholder attribute for explaining more complex data entry scenarios … and as of today …
a button element with the potential that it’s “innards” (ie. it’s innerHTML) is filled by a dropdown (ie. select element)
… it being our first time trying any HTML button element “innards” that were not always straight text. Is this the “comboBox” style of arrangement we used to see with desktop application GUIs and have long wished to simulate the usefulness of, online?!
Well, we’re not quite sure, but we think we’re going to look into it more into the future. So, what are we storing in this dropdown? We’re offering the user the chance … over to you code …
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+='Entries starting with _ can describe ... ' + String.fromCharCode(10) + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Comma separated audio stream only list of your own recallable YouTube IDs playlist' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Semicolon separated video list of your own recallable YouTube IDs playlist' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' One of these inhouse playlist names above which start with tube' + perusedlist + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' YouTube Playlist 34 character identifier' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Spotify Playlist 22 character identifier' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Sundry _email _sms _saverecall _http://youtube.com#example' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).ondblclick=function(event){ alert(event.target.title); };
… culminating in this new Javascript function …
function underscoresystem(inans, inota) {
var anchor=null, subjh='?subject=Hello';
var outans=inans;
var ytpllen=34; //eval('' + ('PLNtJQ2eJVoAMFocY7LOANvPH3cLyUonin').length);
var onevlen=11;
var sppllen=22;
var inhouse='';
var rperson='';
var mperson='';
var tperson='';
var jnota=inota;
if (outans.trim() != '') {
if (outans.toLowerCase().indexOf('mailto') == 0 || outans.toLowerCase().indexOf('email') == 0 || outans.toLowerCase().indexOf('sms') == 0 || outans.toLowerCase().indexOf('http') == 0 || outans.toLowerCase().indexOf('//') == 0 || outans.toLowerCase().indexOf('www.') == 0 || outans.toLowerCase().indexOf('save') == 0 || outans.toLowerCase().indexOf('recall') == 0) {
outans='_' + inans;
}
if (outans.replace(/^tube/g,'_').substring(0,1) == '_') {
subjh='?subject=' + encodeURIComponent('' + document.title + ' regarding ' + document.URL);
if (inota) {
if (document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target)) {
jnota=document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target);
}
}
if (('' + top.document.URL).indexOf('/swipe_media.htm') != -1) {
subjh='?subject=' + encodeURIComponent('' + document.title + ' regarding ' + document.URL);
if (('' + top.document.URL).indexOf('youtube=') != -1) {
if (('' + top.document.URL).indexOf('audioyoutube=') != -1 && ('' + top.document.URL).indexOf('isradio=') != -1) {
tperson='tuberadioapl_';
} else if (('' + top.document.URL).indexOf('youtube=') != -1 && ('' + top.document.URL).indexOf('isradio=') != -1) {
tperson='tuberadiovpl_';
} else if (('' + top.document.URL).indexOf('audioyoutube=') != -1 && ('' + top.document.URL).indexOf('isradio=') == -1) {
tperson='tubeapl_';
} else {
tperson='tubevpl_';
}
}
} else if ((outans + ' ').substring(0,4) == '____') {
tperson='tuberadioapl_';
outans=outans.replace(/^\_\_\_\_/g,'_');
} else if ((outans + ' ').substring(0,3) == '___') {
tperson='tuberadiovpl_';
outans=outans.replace(/^\_\_\_/g,'_');
} else if ((outans + ' ').substring(0,2) == '__') {
tperson='tubeapl_';
outans=outans.replace(/^\_\_/g,'_');
}
if (outans.indexOf('_recall') == 0 || outans.indexOf('_save') == 0) {
newlsval=tperson + ( ('' + Math.floor(Math.random() * 19897654)) + ('' + Math.floor(Math.random() * 19897654)) + ('' + Math.floor(Math.random() * 19897654)) + '000000000000000000000000000').substring(0,27);
if (perusedlist == '') { perusedlist=' ( and we found )'; }
perusedlist=perusedlist.replace(' )', ' ' + newlsval + ' )');
lastbize(newlsval, document.URL);
window.localStorage.setItem(newlsval, encodeURIComponent(document.URL));
jnota.title+=' ... for future recall reenter in web browser similar textarea playlist name ' + newlsval;
outans='';
if (inota) {
if (document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target)) {
jnota=document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target);
}
jnota.value='';
}
if (document.getElementById('recallable')) {
document.getElementById('recallable').innerHTML=newlsval;
document.getElementById('recallable').style.visibility='visible';
lastrecall=maybeselize(document.URL,lastrecall,newlsval);
}
newlsval='';
Yes, the placeholder was struggling with what we wanted to offer as functionality, so we’re putting more into the textarea title attribute. These options allow users to create their own recallable playlists or access existant YouTube or Spotify ones to keep the music rolling in changedswipe_media.htmlTabular Single Row Media Gallery helped out by ytaudioonly.jsexternal Javascript helper.
We do not expect many readers to have thought yesterday’s YouTube Audio Only Mobile Looping Tutorial that little bit different. It was long in the making, and difficult (at least for us), but a brief tutorial. Too brief for interested parties, and we intended it as a seminal discussion, but it’s meaning got subsumed, if you will, with the goings on of the blog posting thread last talked about with the day before’s Tabular Single Row Image Gallery Linking Tutorial.
You see, the YouTube audio stream only inhouse looping control is an essential thing we needed to tie down for today’s progress in this blog posting thread, that being the first incarnation of …
Radio Play
… sequenced modes of play we’re offering today, as a start, for …
non-mobile users
using audio stream only button option clicks
So what was (most) …
difficult (at least for us)
… about getting this first Radio Play idea happening? In a word …
Focus
You find yourself wanting to do lots of …
[element].click();
… and …
[element].blur();
… types of work, which work best with focus (and boy, did we need to “focus”?!) We’ve minimised this where we could, but what we highly recommend users of this functionality doing is …
be presented with the solo web browser full screen (initial) window with the radio emoji
click desired first song link when the song link emojis appear … then …
it’s best to make this window small and away from other web browser windows but not overlapping for the best chance of …
songs play in sequence and loop back through the list
background WordPress Blog random tutorial images … and link them via a modified 404.php creator’s …
<?php
if (strpos(('?' . $_SERVER['QUERY_STRING']), '?rand=') !== false) {
$huht=@file_get_contents('http://www.rjmprogramming.com.au/HTMLCSS/select_palette.php?putrandom=' . urlencode('?rand=' . $_GET['rand'] . '.' . $pnumis . $ptitle));
}
changedswipe_media.htmlTabular Single Row Media Gallery …
function checkmeout(evt, iscuttothechase) {
if (evt.target.outerHTML.split('>')[0].indexOf('url(') != -1) {
if (evt.target.outerHTML.split('>')[0].split('url(')[1].split(')')[0].indexOf('?rand=') != -1) {
if (iscuttothechase) {
document.getElementById('sparepal').src='/HTMLCSS/select_palette.php?getrandom=' + encodeURIComponent('RAND=' + evt.target.outerHTML.split('>')[0].split('url(')[1].split(')')[0].split('?rand=')[1].split("'")[0].split('"')[0].split(')')[0].split('"')[0]); // + ('&viJUNKa=' + evt.target.id).replace('td','div')
} else {
document.getElementById('sparepal').src='/HTMLCSS/select_palette.php?getrandom=' + encodeURIComponent('rand=' + evt.target.outerHTML.split('>')[0].split('url(')[1].split(')')[0].split('?rand=')[1].split("'")[0].split('"')[0].split(')')[0].split('"')[0]); // + ('&viJUNKa=' + evt.target.id).replace('td','div')
}
}
}
}
… web application, now accepting …
click on one of these WordPress Blog random tutorial images to open window to relevant WordPress Blog posting … or …
right click on one of these WordPress Blog random tutorial images to open window to relevant Cut to the Chase web application or presentation media regarding that blog posting above
… we’re sure is the reaction of some readers out there, to today’s “starting draft” for an idea we had. Today’s part of the jigsaw we want to explore may be “ho hum” and “so non-mobile”, but we’re hoping the thread appeals to more modern ideas as we go along.
So much for the apologies, and so let’s now look at the basis to this start …
media (so far just image) …
gallery … presented in …
table … HTML element … in …
single tr row
deliberately overflowing to the right of the screen view of the webpage … and so …
scrollable (ie. window scrollable) to … those right hand parts …
only “media loaded” …
as “scrolled to” or “stumbled upon”
So far the window scrollable detection goes (thanks to this useful link) …
window.onscroll = function (e) {
// called when the window is scrolled.
vspos4=document.body.scrollTop;
vspos3=document.body.scrollLeft;
var vstdn=Math.ceil(eval('' + vspos3) / eval('' + amin));
if (curtdn != vstdn) {
checkthen(vstdn);
}
}
… and the CSS produced “wording (ie. non-media) content” is produced down the bottom left of “inhouse WordPress Blog image cell background” ( possible because of background-size:contain ) we think is interesting …
Tabular Single Row YouTube API Radio Play Tutorial
Today, we’re back to the Tabular Single Row Media Gallery ideas regarding the web application of the recent Tabular Single Row Textarea Sharing Tutorial and we’re involving more “button innards” thinking, with respect to adding onto the pre-existing …
radio play with YouTube audio stream only non-mobile platform incarnation … involving textarea usage … with, today, after getting the YouTube API inhouse web application, better attuned to …
radio play with YouTube audio stream only mobile platform incarnation … and …
radio play with YouTube video play on non-mobile platform incarnation … and …
radio play with YouTube video play on mobile platform incarnation
… additional functionalities allowing for …
looping (though the total list) … default for “radio play” …
shuffling … non-default …
individual YouTube video checkbox user control can end a song before it’s finish
recognition of individual YouTube video checkbox user control as above (can mean an individual song can be looped or deliberately ignored)
saving the playlist for user recall on the same web browser
… functionalities within those three new incarnations. We could organize the first to use the YouTube API in that “vertical in the one table cell, with checkboxes” way, too, but see the textareas of different interest in an ongoing sense. We’ll see whether we offer that YouTube API approach in a future release?!
lots of use of textarea element placeholder attribute for explaining more complex data entry scenarios … and as of today …
a button element with the potential that it’s “innards” (ie. it’s innerHTML) is filled by a dropdown (ie. select element)
… it being our first time trying any HTML button element “innards” that were not always straight text. Is this the “comboBox” style of arrangement we used to see with desktop application GUIs and have long wished to simulate the usefulness of, online?!
Well, we’re not quite sure, but we think we’re going to look into it more into the future. So, what are we storing in this dropdown? We’re offering the user the chance … over to you code …
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+='Entries starting with _ can describe ... ' + String.fromCharCode(10) + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Comma separated audio stream only list of your own recallable YouTube IDs playlist' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Semicolon separated video list of your own recallable YouTube IDs playlist' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' One of these inhouse playlist names above which start with tube' + perusedlist + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' YouTube Playlist 34 character identifier' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Spotify Playlist 22 character identifier' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).title+=' Sundry _email _sms _saverecall _http://youtube.com#example' + String.fromCharCode(10);
document.getElementById('inp' + potlist[jwq] + '_' + document.getElementById(potlist[jwq]).target).ondblclick=function(event){ alert(event.target.title); };
… culminating in this new Javascript function …
function underscoresystem(inans, inota) {
var anchor=null, subjh='?subject=Hello';
var outans=inans;
var ytpllen=34; //eval('' + ('PLNtJQ2eJVoAMFocY7LOANvPH3cLyUonin').length);
var onevlen=11;
var sppllen=22;
var inhouse='';
var rperson='';
var mperson='';
var tperson='';
var jnota=inota;
if (outans.trim() != '') {
if (outans.toLowerCase().indexOf('mailto') == 0 || outans.toLowerCase().indexOf('email') == 0 || outans.toLowerCase().indexOf('sms') == 0 || outans.toLowerCase().indexOf('http') == 0 || outans.toLowerCase().indexOf('//') == 0 || outans.toLowerCase().indexOf('www.') == 0 || outans.toLowerCase().indexOf('save') == 0 || outans.toLowerCase().indexOf('recall') == 0) {
outans='_' + inans;
}
if (outans.replace(/^tube/g,'_').substring(0,1) == '_') {
subjh='?subject=' + encodeURIComponent('' + document.title + ' regarding ' + document.URL);
if (inota) {
if (document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target)) {
jnota=document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target);
}
}
if (('' + top.document.URL).indexOf('/swipe_media.htm') != -1) {
subjh='?subject=' + encodeURIComponent('' + document.title + ' regarding ' + document.URL);
if (('' + top.document.URL).indexOf('youtube=') != -1) {
if (('' + top.document.URL).indexOf('audioyoutube=') != -1 && ('' + top.document.URL).indexOf('isradio=') != -1) {
tperson='tuberadioapl_';
} else if (('' + top.document.URL).indexOf('youtube=') != -1 && ('' + top.document.URL).indexOf('isradio=') != -1) {
tperson='tuberadiovpl_';
} else if (('' + top.document.URL).indexOf('audioyoutube=') != -1 && ('' + top.document.URL).indexOf('isradio=') == -1) {
tperson='tubeapl_';
} else {
tperson='tubevpl_';
}
}
} else if ((outans + ' ').substring(0,4) == '____') {
tperson='tuberadioapl_';
outans=outans.replace(/^\_\_\_\_/g,'_');
} else if ((outans + ' ').substring(0,3) == '___') {
tperson='tuberadiovpl_';
outans=outans.replace(/^\_\_\_/g,'_');
} else if ((outans + ' ').substring(0,2) == '__') {
tperson='tubeapl_';
outans=outans.replace(/^\_\_/g,'_');
}
if (outans.indexOf('_recall') == 0 || outans.indexOf('_save') == 0) {
newlsval=tperson + ( ('' + Math.floor(Math.random() * 19897654)) + ('' + Math.floor(Math.random() * 19897654)) + ('' + Math.floor(Math.random() * 19897654)) + '000000000000000000000000000').substring(0,27);
if (perusedlist == '') { perusedlist=' ( and we found )'; }
perusedlist=perusedlist.replace(' )', ' ' + newlsval + ' )');
lastbize(newlsval, document.URL);
window.localStorage.setItem(newlsval, encodeURIComponent(document.URL));
jnota.title+=' ... for future recall reenter in web browser similar textarea playlist name ' + newlsval;
outans='';
if (inota) {
if (document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target)) {
jnota=document.getElementById('inp' + inota.id + '_' + document.getElementById(inota.id).target);
}
jnota.value='';
}
if (document.getElementById('recallable')) {
document.getElementById('recallable').innerHTML=newlsval;
document.getElementById('recallable').style.visibility='visible';
lastrecall=maybeselize(document.URL,lastrecall,newlsval);
}
newlsval='';
Yes, the placeholder was struggling with what we wanted to offer as functionality, so we’re putting more into the textarea title attribute. These options allow users to create their own recallable playlists or access existant YouTube or Spotify ones to keep the music rolling in changedswipe_media.htmlTabular Single Row Media Gallery helped out by ytaudioonly.jsexternal Javascript helper.
We do not expect many readers to have thought yesterday’s YouTube Audio Only Mobile Looping Tutorial that little bit different. It was long in the making, and difficult (at least for us), but a brief tutorial. Too brief for interested parties, and we intended it as a seminal discussion, but it’s meaning got subsumed, if you will, with the goings on of the blog posting thread last talked about with the day before’s Tabular Single Row Image Gallery Linking Tutorial.
You see, the YouTube audio stream only inhouse looping control is an essential thing we needed to tie down for today’s progress in this blog posting thread, that being the first incarnation of …
Radio Play
… sequenced modes of play we’re offering today, as a start, for …
non-mobile users
using audio stream only button option clicks
So what was (most) …
difficult (at least for us)
… about getting this first Radio Play idea happening? In a word …
Focus
You find yourself wanting to do lots of …
[element].click();
… and …
[element].blur();
… types of work, which work best with focus (and boy, did we need to “focus”?!) We’ve minimised this where we could, but what we highly recommend users of this functionality doing is …
be presented with the solo web browser full screen (initial) window with the radio emoji
click desired first song link when the song link emojis appear … then …
it’s best to make this window small and away from other web browser windows but not overlapping for the best chance of …
songs play in sequence and loop back through the list
background WordPress Blog random tutorial images … and link them via a modified 404.php creator’s …
<?php
if (strpos(('?' . $_SERVER['QUERY_STRING']), '?rand=') !== false) {
$huht=@file_get_contents('http://www.rjmprogramming.com.au/HTMLCSS/select_palette.php?putrandom=' . urlencode('?rand=' . $_GET['rand'] . '.' . $pnumis . $ptitle));
}
changedswipe_media.htmlTabular Single Row Media Gallery …
function checkmeout(evt, iscuttothechase) {
if (evt.target.outerHTML.split('>')[0].indexOf('url(') != -1) {
if (evt.target.outerHTML.split('>')[0].split('url(')[1].split(')')[0].indexOf('?rand=') != -1) {
if (iscuttothechase) {
document.getElementById('sparepal').src='/HTMLCSS/select_palette.php?getrandom=' + encodeURIComponent('RAND=' + evt.target.outerHTML.split('>')[0].split('url(')[1].split(')')[0].split('?rand=')[1].split("'")[0].split('"')[0].split(')')[0].split('"')[0]); // + ('&viJUNKa=' + evt.target.id).replace('td','div')
} else {
document.getElementById('sparepal').src='/HTMLCSS/select_palette.php?getrandom=' + encodeURIComponent('rand=' + evt.target.outerHTML.split('>')[0].split('url(')[1].split(')')[0].split('?rand=')[1].split("'")[0].split('"')[0].split(')')[0].split('"')[0]); // + ('&viJUNKa=' + evt.target.id).replace('td','div')
}
}
}
}
… web application, now accepting …
click on one of these WordPress Blog random tutorial images to open window to relevant WordPress Blog posting … or …
right click on one of these WordPress Blog random tutorial images to open window to relevant Cut to the Chase web application or presentation media regarding that blog posting above
… we’re sure is the reaction of some readers out there, to today’s “starting draft” for an idea we had. Today’s part of the jigsaw we want to explore may be “ho hum” and “so non-mobile”, but we’re hoping the thread appeals to more modern ideas as we go along.
So much for the apologies, and so let’s now look at the basis to this start …
media (so far just image) …
gallery … presented in …
table … HTML element … in …
single tr row
deliberately overflowing to the right of the screen view of the webpage … and so …
scrollable (ie. window scrollable) to … those right hand parts …
only “media loaded” …
as “scrolled to” or “stumbled upon”
So far the window scrollable detection goes (thanks to this useful link) …
window.onscroll = function (e) {
// called when the window is scrolled.
vspos4=document.body.scrollTop;
vspos3=document.body.scrollLeft;
var vstdn=Math.ceil(eval('' + vspos3) / eval('' + amin));
if (curtdn != vstdn) {
checkthen(vstdn);
}
}
… and the CSS produced “wording (ie. non-media) content” is produced down the bottom left of “inhouse WordPress Blog image cell background” ( possible because of background-size:contain ) we think is interesting …
… combine, today, starting down, again, the road to “button innards” interest.
We thought the “how we got there” concept of embedding the form elements of data into “button innards” (ie. it’s innerHTML) in our new “button innards” web application interesting. Of course you don’t have to do this simple exercise of calculating time elapsed between two datetimes this way, but what we wanted to show was that with lots of things in web design, by now, there is more than one way to crack an egg …
Yesterday’s Making of Ffmpeg Video Overall Effects Tutorial‘s “first draft” feel of our inhouse Bookmark Export Filtering web application showed just that. Yes, there was quite a lot to improve on, the way we saw it being …
adding a (local system) file browsing means (called by our <iframe scrolling=no frameborder=0 id=cbi data-style=’border-top:1px solid black;border-bottom:1px solid black;border-left:2px solid yellow;border-right:1px solid yellow;’ style=’width:173px;height:228px;margin-top:-194px;’ src=’/HTMLCSS/client_browsing.htm?totype=html&d=189786754′></iframe> file browsing helper) …
function yesthreethree(restis) {
if (document.getElementById('bmkdata')) {
if (restis.indexOf('data:') == 0) {
if (restis.indexOf(';base64,') != -1) {
document.getElementById('bmkdata').value='' + window.atob(restis.split(';base64,')[1]);
analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
} else if (restis.indexOf(';utf8,') != -1) {
document.getElementById('bmkdata').value='' + (restis.split(';utf8,')[1]);
analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
}
} else {
document.getElementById('bmkdata').value=restis;
analyze(document.getElementById('bmkdata')); //document.getElementById('bmkdata').blur();
}
}
}
… by which to add Web Browser Bookmark Exporting Data File Content into that lower HTML textarea element (and unlock the two UL/LI and Table button elements) … as well as …
add a details/summary “reveal” means by which the user can show working actual HTML elements in addition to the HTML coding we’ve only be showing (with that “first draft”)
add Sharing functionality via either Email or SMS conduits
… and because the amounts of data can be large (and too big for a method=GET HTML form arrangement that clientside HTML/Javascript is capable of servicing) we were glad adding this Sharing functionality that yesterday we’d already started down the line of thinking that the …
lower HTML textarea content … would always be associated with …
hashtag … representation
… because with these methodologies the web server restrictions on (address bar) URL lengths (ie. error 414) melt away, at least for data lengths we’ve encountered with our testing so far, and that applies also to …
“mailto:” “a” URL links to Email a recipient … or …
“sms:” “a” URL links to SMS a recipient
… Sharing …
var mysubject='My Bookmarked Links ...', xlocationhref='';
if (('' + location.hash).indexOf('bmkdata=') != -1) { locationhash=location.hash; }
function doemail() {
if (document.getElementById('bmkdata').value.trim() != '') {
locationhash='#bmkdata=' + window.btoa(encodeURIComponent(document.getElementById('bmkdata').value));
mysubject='My Bookmarked Links ...';
if (document.getElementById('dtone').value.trim() != '' && document.getElementById('dttwo').value.trim() != '') {
mysubject='My Bookmarked Links from ' + document.getElementById('dtone').value.trim() + ' to ' + document.getElementById('dttwo').value.trim() + ' ... ';
} else if (document.getElementById('dtone').value.trim() != '') {
mysubject='My Bookmarked Links from ' + document.getElementById('dtone').value.trim() + ' ... ';
} else if (document.getElementById('dttwo').value.trim() != '') {
mysubject='My Bookmarked Links up until ' + document.getElementById('dttwo').value.trim() + ' ... ';
}
if (ulli && !utable) {
recipient=prompt('Please enter email address to send to? Will output UL/LI else if some blanks will output Table. Default email Subject is ' + mysubject + ' ... but if you want a different one append a hash (#) followed by your own Subject for the Email.', '');
if (recipient == null) { recipient=''; } else if (recipient.indexOf('@') == -1) { recipient=''; } else if (eval('' + recipient.indexOf('#')) > eval('' + recipient.indexOf('@'))) { mysubject=recipient.split('#')[1]; recipient=recipient.split('#')[0]; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='mailto:' + recipient.replace(/\ /g,'') + '?subject=' + encodeURIComponent(mysubject) + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
} else if (utable) {
recipient=prompt('Please enter email address to send to? Will output UL/LI else if some blanks will output Table. Default email Subject is ' + mysubject + ' ... but if you want a different one append a hash (#) followed by your own Subject for the Email.', '');
if (recipient == null) { recipient=''; } else if (recipient.indexOf('@') == -1) { recipient=''; } else if (eval('' + recipient.indexOf('#')) > eval('' + recipient.indexOf('@'))) { mysubject=recipient.split('#')[1]; recipient=recipient.split('#')[0]; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='mailto:' + recipient.replace(/\ /g,'') + '?subject=' + encodeURIComponent(mysubject) + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
}
}
}
function dosms() {
if (document.getElementById('bmkdata').value.trim() != '') {
locationhash='#bmkdata=' + window.btoa(encodeURIComponent(document.getElementById('bmkdata').value));
if (ulli && !utable) {
recipient=prompt('Please enter SMS number to send to? Will output UL/LI else if some blanks will output Table.', '');
if (recipient == null) { recipient=''; } else if (recipient.trim() != '' && recipient.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') != '') { recipient=''; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='sms:' + recipient.replace(/\ /g,'') + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
} else if (utable) {
recipient=prompt('Please enter SMS number to send to? Will output UL/LI else if some blanks will output Table.', '');
if (recipient == null) { recipient=''; } else if (recipient.trim() != '' && recipient.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') != '') { recipient=''; }
if (recipient.indexOf(' ') != -1) {
locationhref=document.URL.split('?')[0].split('#')[0] + '?table=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
} else {
locationhref=document.URL.split('?')[0].split('#')[0] + '?ulli=y&dtone=' + encodeURIComponent(document.getElementById('dtone').value) + '&dttwo=' + encodeURIComponent(document.getElementById('dttwo').value); // + '#bmkdata=' + encodeURIComponent(document.getElementById('bmkdata').value);
}
xlocationhref=locationhref;
if (recipient.trim() != '') {
if (1 == 1) {
processit();
} else {
document.getElementById('spareshare').href='sms:' + recipient.replace(/\ /g,'') + '&body=' + encodeURIComponent(locationhref + locationhash);
document.getElementById('spareshare').click();
recipient='';
}
}
}
}
}
function fillin() {
if (document.getElementById('rt') && document.getElementById('rd') && document.getElementById('rs')) {
if (document.getElementById('rd').innerHTML == document.getElementById('rs').outerHTML) {
document.getElementById('rd').innerHTML=document.getElementById('rs').outerHTML + document.getElementById('rt').value;
}
}
}
// Coming back from an email or SMS link click ...
… concepts, happily. Keeping it all “just clientside” means we can be talking meaningfully to a larger chunk of audience … if you’re listening, that is?!
… of links, am sure a lot of you would guess, were gleaned (within our Google Chrome web browser History (of that day)) Bookmarked (by us) webpages. We actually compiled via (the clunky and fairly slow) …
left half (of screen) with (Google Chrome) History showing, and right half with BBEdit Text Editor open at a new (ostensibly empty) file with loads of empty records available, and dragged URL records (identifiable, for us, via an asterisk, indicating a webpage was Bookmarked) from the left into the right to bring over the URLs (only) above, the rest constructed in a more manual way … and another way could have been …
use Google Chrome webpage showing Bookmark list and use its Export Bookmarks (no other choice but “All”) to a file that could be waded through to derive the list above … and then we thought of chance to code for …
hybrid “inhouse” clientside (ie. HTML/Javascript/CSS) web application …
… so far asking for the content of a web browser Export Bookmarks operation’s output file’s HTML (in the case of Google Chrome) content in an HTML textarea element (the user can fill in themselves), and filterable via two input type=datetime-local starting and ending datetime(s) of interest (the user can fill in, optionally)
… and we wish we could have got that hour and a half back, from yesterday, via swapping yesterday for today?!
Not all web browser brands have any Export Bookmarks functionality, like Google Chrome offers here on macOS. We’re going to be examining others, over time, and try to make our inhouse logic fit in with how that Bookmark data looks, on export, should we find any other Web Browser brands offering this functionality.
Today’s work (all on our macOS MAMP local Apache/PHP/MySQL web server environment) is only “toe dipping” in a “great big sea of possibilities” regarding the great ffmpeg‘s “filtering style abilities” further to yesterday’s Ffmpeg Video Subliminal Message Tutorial, but …
as you try out things you discover others …
can be a foot in the door researching this type of work yourself …
… we’re hoping. And it’s not as if ffmpeg was on it’s own here with the second concept below …
fade in and fade out video effect … inspired by Top 20 best commands for FFmpeg to try …
ffmpeg -i brush__turkey.m4v -vf "fade=in:0:5,fade=out:73:5" brushturkey_fadein_fadeout.m4v
colour balancing improvements regarding an underexposed video
… as the …
Gimp image editor’s abilities (matching a lot of what you read Photoshop can help with too) with Curves helped out … along with …
Python expertly designed to link Gimp and ffmpeg “whole of video” manipulations
… enacting …
selection of a video still image, ideally not containing the Brush Turkey …
we were inspired by a Brush Turkey which we videoed via an iPhone’s Camera app’s Video mode of capture option …
downloaded to this MacBook Air (via AirDrop … good for Apple sharing when the sizes get big … raw video 1:19s long)
And that’s where we are at here, thinking this would be a good opportunity to try creating a Subliminal Message video. But before we go on and “give the game away” you may want to see the result of our Subliminal Message insertion via ffmpeg theme to our work, in action, below. Unlock or give up to unlock the content below the video quiz below …
Congratulations! Yes, in between the “Raw Video” ffmpeg work and onto the other ffmpeg commands we renamed one slide to a *.jpeg equivalent and then set about replacing the *.jpg original slide with our Subliminal Message slide, as you can see here.
The work involves both of the HTML design “big concepts” we are keen on at this blog …
overlay … via CSS control of …
position:absolute;top:0px;left:0px;
opacity:1.0;
z-index:1;
reveal … lately, mostly, via HTML use of …
details
summary
… the latter being the “container” for our interactive input be able to control overlay items 2 and 3, which affect the “overlayed images” output display via the clicking of a new “Overlay Images” button. That button …
… one reason being that we do not want to install the wonderful ffmpeg (command line video creation tool) on the RJM Programming domain, but, in macOS, here with MAMP, we are quite happy to live with the Homebrew (Terminal application’s) install …
… setting up what could be “lost functionality”, but today, we come around to either adding a new …
Convert to Video
… dropdown option should all be ritchy ditch, and if not, often we will add new “Advice” dropdown options to remind the user what they’d need to arrange to get to a “Convert to Video” scenario.
Selecting “Convert to Video” sets up a hydrid “Internet/Intranet” feeling scenario where …
… be nested in PHP exec (via MAMP local web server incarnation of the inhouse Animated GIF Creator) as a way to create videos …
which can be downloaded within that public domain “parent” inhouse Animated GIF Creator session
Which begs the question …
How do we know when to offer the "Convert to Video" option on that dropdown?
This logic is centred around a few useful ideas (with this cross domain scenario ruling out Ajax and Iframe src= definitions, as useful ways to go) …
open the MAMP local web server “HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php” URLs in an iframe (with onload event logics) pointed at by a window.open second argument (effectively avoiding any loose useful or not popup windows hanging around) …
the most we can ask at the receiver is that window.opener is defined … and if so, just at that discovery …
an image called “amhere.jpg” is created via PHP GD functionality … and back at the “public domain” parent within the iframe onload logic (where with contentWindow or contentDocument do not expect a document.body or even a document) …
we attempt to “hotlink” that MAMP local web server image … as per …
var tryit='http://localhost:8888/PHP/animegif/amhere.jpg';
document.getElementById('ctvopt').value='advice';
document.getElementById('ctvopt').innerHTML='Advice on Convert to Video';
if (tryit != '') {
var im=new Image();
im.onload = function() {
document.getElementById('ctvopt').value='video';
console.log('this.height=' + eval('' + this.height));
if (eval('' + this.height) >= 20) {
document.getElementById('ctvopt').innerHTML='Convert to Video';
} else {
document.getElementById('ctvopt').innerHTML='Convert to Video (but ffmpeg not installed or in unexpected place)';
}
document.getElementById('imsel').title='All except Convert to Video, which needs ffmpeg installed, use ImageMagick';
};
… and check the code for the validity of any ImageMagick paths … and if not all these conditions, simulate the same and cobble it together in the code
… one reason being that we do not want to install the wonderful ffmpeg (command line video creation tool) on the RJM Programming domain, but, in macOS, here with MAMP, we are quite happy to live with the Homebrew (Terminal application’s) install …
brew install ffmpeg
… to open a whole new woooooorrrrlllllddd of video creation opportunities using this MAMP local web URL …
HTTP://localhost:8888/PHP/animegif/tutorial_to_animated_gif.php# Animated GIF Creator on MAMP local web server
… we tweaked out of “video/mp4” ffmpeg created videos thinking towards “video/webm” or “video.mov” ffmpeg created videos that suit the “few frames but spaced out video” we wanted to achieve for all practical purposes, that “video/mp4” playing too fast for us to see anything much but the first and last slide …
As a web application programmer we like buttons. There are “buttons” and there are buttons. Yes, there is an HTML button element, and it and the input type=button element render as that part of a webpage most recognizable to users the world over. You click or tap this button and something usually happens.
But we also enjoy “Emoji Buttons” for we graphically challenged programmers. Using an emoji text and graphic can make span elements or p elements or lots of other HTML elements that have an innerHTML property, be like a very succinct button like entity, also appreciated around here because it takes up so little webpage “real estate”.
And so onto yesterday’s Animated GIF Creator PDF Order Tutorial we set about, today, “revealing” any enduring animated GIF and/or PDF created during a previous session, using Emoji Buttons as the email/SMS sharing action buttons.
We say “revealing” because, like the way “Emoji Buttons” save webpage “real estate”, so can the use of the HTML5 details/summary element combination. It is in that summary “enduring” header part of that combination we can place some “Emoji Buttons”. You will (once you start creating and sharing animated GIFs and/or PDFs) see from our changedtutorial_to_animated_gif.php inhouse Animated GIF Creator (helped out by a changedemailhtml.php inhouse email creator helper serverside PHP web application) that we place three Emoji Buttons …
📟 SMS (Animated GIF or PDF) Limited relevance period (ie. no data URIs involved, so rely on an absolute URL whose content might change or start not to exist down the track) … via “a” “sms:” link click/tap
📧 Email (Animated GIF or PDF) Limited relevance period (ie. no data URIs involved, so rely on an absolute URL whose content might change or start not to exist down the track) … via “a” “mailto:” link click/tap
📧 Email (Animated GIF or PDF) Enduring relevance (ie. data URIs involved, so do not rely on any absolute URL whose content might change or start not to exist down the track) … via PHP mail function means of sending email with HTML attachment containing the relevant Animated GIF or PDF
Yes, “Emoji Buttons” can have their size controlled but not by the usual CSS width and height properties, but by the CSS font-size property. Here is the new Javascript function that those Emoji Button “onclick” logic points to …
<?php eho ”
function askes(isemail, isoab) {
var esask='', izhr=null, izform=null;
var isoa=document.getElementById(isoab.id.replace('b',''));
if (isemail) {
esask=prompt('Please enter email address to send this to', '');
if (esask != null) {
if (esask.indexOf('@') != -1) {
if (isoa.outerHTML.indexOf('<img') == 0) {
//alert('More Animated GIF to come');
izhr = new XMLHttpRequest();
izform=new FormData();
izform.append('to', esask.trim());
izform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . ".gif" . "');
izform.append('subj', 'My Latest Animated GIF via RJM Programming ... ');
izform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><img style=\"width:100%;height:900px;\" src=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . ".gif" . "\"></img></div></body>'));
izhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
izhr.send(izform);
} else if (isoa.outerHTML.indexOf('<a') != 0) {
//alert('More PDF to come');
izhr = new XMLHttpRequest();
izform=new FormData();
izform.append('to', esask.trim());
izform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . "_animegif.pdf" . "');
izform.append('subj', 'My Latest PDF via Animated GIF via RJM Programming ... ');
izform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "_\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><object style=\"width:100%;height:900px;\" type=\"application/pdf\" data=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . server_remote_addr() . "_animegif.pdf" . "\"></object></div></body>'));
izhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
izhr.send(izform);
} else {
isoa.href='mailto:' + esask.trim() + '?' + isoa.href.split('?')[1];
isoa.click();
}
}
}
} else {
esask=prompt('Please enter SMS number to send this to', '');
if (esask != null) {
if ((esask.trim() != '' && esask.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '')) {
isoa.href='sms:' + esask.trim() + '&' + isoa.href.split('&')[1];
isoa.click();
}
}
}
}
Order becomes an interesting subject to us today regarding the PDF conversions possible in amongst the Animated GIF creating of yesterday’s Animated GIF Creator PDF Share Tutorial.
That is because we may have some advanced users out there that want that possibility of differentiating the data of …
animated GIF … containing all features asked for, in totality … from the chance for the user to have any of the PDF conversion option sets below …
PDF … one of …
no PDF conversion
full PDF conversion … with the user choices regarding raw image content and title and watermarking and ImageMagick and GD modifiers
light PDF conversion … with the user choices regarding just raw image content (the only option out of these last three available before today’s work)
medium PDF conversion … with the user choices regarding just ImageMagick and GD modifiers
… perhaps as a “before and after” tool regarding presentations, maybe?
No dropdowns used here (enforcing an order), just the user’s order in which they choose to select the “PDF conversion” option from the ImageMagick dropdown (in relation to other selections) determining how and when this PDF conversion occurs in the workflow through to creating the animated GIF with our changedtutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application. Just remember to select PDF Conversion as early as possible to do that “light PDF conversion” option above and last thing if you are interested in “full PDF conversion” (where you can create PDF documents with the bells and whistles ImageMagick and GD modifiers can offer) and today’s tutorial picture is an example of “medium PDF conversion” (when we ordered our settings by first Grayscale, second PDF conversion and last title and watermarking options). Clicking or tapping the ImageMagick link can get the user to the Javascript popup window where they might define the email or SMS recipient for that PDF conversion data file download.
What sharing conduits do we code for? We always intended …
email … but as the day wore on trying to get the usage to work on mobile and non-mobile we decided to relinquish our wish to not have to create a user specific enduring (until that same browser type and user combination share) PDF document for the user request … mainly to get mobile email downloads to be friendly … and so this opened the door for …
SMS … to access that enduring PDF as a URL in the SMS message (that becomes a link for the recipient)
<?php echo ”
function emailhtmlit() {
var induri=ginduri;
var pemail='" . urldecode($_GET['outpdf']) . urldecode($_POST['outpdf']) . "';
if (pemail != null) {
if (pemail.indexOf('@') != -1 || (pemail.trim() != '' && pemail.trim().replace(/0/g,'').replace(/1/g,'').replace(/2/g,'').replace(/3/g,'').replace(/4/g,'').replace(/5/g,'').replace(/6/g,'').replace(/7/g,'').replace(/8/g,'').replace(/9/g,'') == '')) {
if (induri.trim() == '') {
if (induri == '') {
document.getElementById('pdfproposed').src='./animegif.pdf?rand=' + Math.floor(Math.random() * 19854654);
} else {
ginduri='found';
}
setTimeout(emailhtmlit, 15000);
} else {
ginduri='';
var zhr = new XMLHttpRequest();
var zform=new FormData();
if (pemail.indexOf('@') != -1) { zform.append('to', pemail); }
zform.append('inline', '" . dirname(__FILE__) . DIRECTORY_SEPARATOR . "animegif.pdf" . "');
zform.append('subj', 'My PDF version of Animated GIF via RJM Programming ... ');
zform.append('tdhuhta', ('<body><div title=\"" . explode('/animegif',$durlis)[0] . '/animegif/' . server_remote_addr() . "_\" style=\"overflow:auto;-webkit-overflow-scrolling:touch;height:100%;\"><object style=\"width:100%;height:900px;\" type=\"application/pdf\" data=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"><embed style=\"width:100%;height:900px;\" type=\"application/pdf\" src=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"></embed></object></div></body>'));
//zform.append('tdhuhta', ('<body><iframe srcdoc=\"" . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'animegif.pdf' . "\"></iframe></body>'));
zhr.open('post', '//www.rjmprogramming.com.au/HTMLCSS/emailhtml.php', true);
zhr.send(zform);
if (pemail.indexOf('@') == -1) {
var hrefp=document.getElementById('pdfsms').href.split('&body=')[0];
hrefp+=pemail.trim() + '&body=' + document.getElementById('pdfsms').href.split('&body=')[1];
document.getElementById('pdfsms').href=hrefp;
document.getElementById('pdfsms').click();
}
}
}
}
}
“; ?>
… and this called on our PHP email creator helper to better interface in its $_POST arguments reading section in our changedemailhtml.php inhouse email creator helper serverside PHP web application …
We have more “reliability work” and “email sharing work” to go after today’s start, but it primarily called on ImageMagick command line’s talent for a command like …
convert /tmp/imtmp0*.*[gGmMiI]* /tmp/imtmp000.pdf
… to “concatenate” into the one output PDF file (called “/tmp/imtmp000.pdf”) the slides, arranged by our code into that “/tmp/imtmp0*.*[gGmMiI]*” (file specification) arrangement above. Yes, we meant “convert” above, as “mogrify” (batch work) appears not to be able to perform this task.
Animated GIF Creator Slide Specific Application Tutorial
It’s all fine and good improving on the ImageMagick and GD and Exif functionality modifications like with yesterday’s Animated GIF Creator Exif Rotation Compensation Tutorial, but in reality, if you are going to start creating animated GIFs to explain a process, you are going to want to apply these “slide modifiers” on a slide by slide basis, rather than enforcing a “whole of animated GIF slide set” paradigm, as for the last few days worth of work.
And so, we decided to do what we often do, “sliding in” more functionality (chortle, chortle). We tend to want to …
start with hardcoded text (or element) … somewhere … today it happens to be in an HTML span element that once involved just …
<span id="smyim"></span>
… and used to get filled, Javascript DOM wise, when needed via …
document.getElementById('smyim').innerHTML='ImageMagick switches: '; // and yes, it remains that way even now, but read on ...
add intelligence (quite often that being onclick logic(s)) to that hardcoded element via …
<span id=smyim title=Application onclick=applyto(); style=cursor:pointer;text-decoration:underline;></span>
that serves the purpose, as the user clicks/taps it (alerted to that fact, perhaps, because we underline the element and add an appropriate cursor when hovering over it (plus a title)), of calling Javascript …
function applyto() {
var huhto=prompt('Apply ImageMagick and/or GD to which slides, in comma separated list, counting starting with 1? Defaults to applying to all slides. Comma delimit. Negatives mean all but. Ranges can be specified. For example ... 2,4-7,9', document.getElementById('appliedto').value);
if (huhto == null) { huhto=''; }
if (huhto.trim() == '') {
document.getElementById('appliedto').value='';
document.getElementById('smyim').title='Application';
} else {
document.getElementById('appliedto').value=huhto.trim();
document.getElementById('smyim').title=huhto.trim();
}
}
to glean an (often times out of the normal workflow of the web application) informational piece of data, interactively, from the user, via a Javascript prompt popup
So that’s the clientside of this work … “alerting the user to the existance of the functionality” you might say.
And then there’s “the application” of that nuanced user requirement. And that’s where the “inhouse ‘our’ prefix to wrapper function name paradigms” come in handy. We introduced “blanket” functionality thoughts via this approach, and so to “partially undo” that thinking, we make the “our” prefix conditional, as is available to us with our changedtutorial_to_animated_gif.php inhouse Animated GIF Creator serverside PHP web application (helped out by a changedexif_rotation_check.php inhouse Exif detector PHP helper), as per …
<?php
function ourcomplicated($inio, $iappl) {
$ideasl=explode(",", str_replace(' ','',$iappl));
$xour="our";
for ($iqa=0; $iqa<sizeof($ideasl); $iqa++) {
if (trim($ideasl[$iqa]) != '') {
$xour="";
if (('-' . $inio) == trim($ideasl[$iqa])) { return ""; }
$ideasr=explode("-", str_replace(' ','',trim($ideasl[$iqa])));
for ($iqb=0; $iqb<sizeof($ideasr); $iqb++) {
if (('' . $inio) == trim($ideasr[$iqb])) {
return "our";
}
if ($iqb == 1) {
if (trim($ideasr[1]) == "") { $ideasr[1]="99999999"; }
if ($inio >= $ideasr[0] && $inio <= $ideasr[1]) { return "our"; }
}
}
}
}
return $xour;
}
We use these “new abilities” better explaining “the abscence or otherwise of Exif checking”, and the implications of that in the animated GIF creator woooooorrrrrrlllllddd, contrasting the first two slides, showing one with “No Exif checking” (the bad old days) versus “With Exif checking” (the renaissance of liberated thinking in the South South East woooorrrrrlllldddd) in today’s animated GIF presentation.
Exchangeable image file format (officially Exif, according to JEIDA/JEITA/CIPA specifications) is a standard that specifies the formats for images, sound, and ancillary tags used by digital cameras (including smartphones), scanners and other systems handling image and sound files recorded by digital cameras.
But with some photos it’s there in the photo’s metadata information letting the future user know what orientation the camera of that mobile device was in as you took the photo. In order to help our animated GIF creator, the first slide image is scoured for Exif metadata and if found, a suitable rotation correction can be applied to the slides there and then. In order to scour for Exif metadata we needed to write a exif_rotation_check.php inhouse Exif detector PHP helper.
The PHP GD image library is so much more useful than for the “filters” interfaced to with yesterday’s Animated GIF Creator GD Filter Interfacing Tutorial. Under an “umbrella term” transformations, today we add interfacing to GD functionality …
Continuing on with the ImageMagick batch processing “mogrify” ideas of yesterday’s ImageMagick Batch Image Conversion Affine Transformation Tutorial and the “vignette” image editing ideas of Gimp Vignette Primer Tutorial, as below, today, we took some pet photos with an iPad’s Camera app and shared them off the Photos app via two Mail sharing option emails containing seven attachments each. Using an iPad, the JPEG “jpg” output files were too big for our inhouse Animated GIF Creator PHP web application to handle, and so to perform the …
animated GIF presentation, off these downloaded email photo attachments … and along the way …
quality adjusted them (yes, “mogrify” does not stuff JPG to JPG conversions, and we used “mogrify” -quality 20% switch here) … and …
rotate them 180 degrees (“mogrify” uses switches -affine -1,0,0,-1,0,0 -transform +repage here) … and …
… on the way to compiling into an animated GIF image, and we turned to ImageMagick again, using its affine transformation talents, along with its awesome vignetting talents …
… where the last two dropdown options will be similar, the last showing the input image into ImageMagick can be an animated GIF that is truely treated like an animated GIF.
The last time we talked about the miraculous, redolent and amazing image editor called Gimp am sure there was someone in a shower … it stands to reason … and one of those showerers, surely, would have been singing The Gimp Song … and if not … why not? … but we digress … anyway we had the Gimp Transparency Primer Tutorial as shown below go into some image transparency issues with Gimp.
In today’s tutorial we make use of a great tutorial (even so far as with direct quotes below) called Add a Vignette to a Photograph with GIMP (thanks) to try a photographic technique called vignetting on one of the photographs we added, recently, into the mix of those of the Custom Header Image mix at this blog … specifically the one of Nala, the dog, on the door ledge. Need to warn you here and now that if there was the time all over again, it would be better achieved that second time around, but this is not the point with learning, but rather getting some starting point with a great “product” like Gimp, and trying it yourself, once you have a method. It boils down to:
In the Layers dialog, click on your “Vg” layer to select it, and select Soft light from the “Mode” drop-down box
Right click on your “Vg” layer and go to Add Layer Mask. In the dialog that pops up, you want “Initialise Layer Mask to” set to “White (full opacity)”. Click “Add”
Below Opacity bar select Link icon next to Eye icon, which will already be showing
Use the freeform select tool (press F to bring this up) and draw a selection somewhere around the primary point of interest in your photo
Use your bucket tool (Shift+B) and click within the selection to fill it
Deselect your selection with Select->None
Go to Filters->Blur->Gaussian Blur. In the dialog that comes up, you want “Radius” set to a very large amount; a tenth of the longest edge of the photo is not too much
Click on your “Vg” layer to select it (if it isn’t already selected), and then slide the opacity slider towards the right until the effect is subtle enough. Our (ever so subtle) example of Nala, in the tutorial, used an opacity of about 66%
Click Export button in two windows (NB. this overwrites the image file, so if this is not desirable, export to a different image file name and/or type)
Here is a tutorial that adds to a previous Gimp Layers Primer Tutorial as shown below, and gives you more insight into the massive possibilities of using a sophisticated image editor and use layers with various amounts of transparency, especially suited to use with png image files.
Today’s tutorial where we construct a Birthday Card that needs tweaking for the words in front to be seen a bit more clearly, by making the image behind a bit more transparent, changes the transparency of a single image via:
Open Gimp graphical editor application
File->Open Layers … pick your image file
If Layers window not showing, make it show via Windows->Layers – Brushes
Below Opacity bar select Link icon next to Eye icon, which will already be showing
Change Opacity bar setting to a value of Transparency (100% is Opaque, 0% is Transparent) that suits … today we do 70%
File->Export
Click Export button in two windows (NB. this overwrites the image file, so if this is not desirable, export to a different image file name and/or type)
As with most Gimp ideas, jump in and give it a go, as you’ll find your own ways and means of using this great product … am pretty sure.
Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.
Here is a tutorial that gives you an insight into the massive possibilities of using a sophisticated image editor and use layers with various amounts of transparency, especially suited to use with png image files.
Transparency (or its obverse, opacity) can be used to have the one image achieve several “ends” (ie. purposes). Although it is a bit of a clumsy example in the tutorial, you can see that the technique can be used for artistic purposes … often called “Photoshopping” (named after the more famous, and also brilliant, rival product, Photoshop).
Lots of those classic “Photoshopping” techniques can be achieved in Gimp, and some other tutorials at this blog touch on that.
Am sure you can imagine what the concept of a layer is with regard to image manipulation. Within Gimp, for beginners not used to this concept, you find yourself underestimating and underplaying what can be achieved with the various layers of a multi-layered image. In simplistic terms each layer has the functionality in Gimp to be treated as a whole new image, and this is the best way to think of it when trying to achieve what you want to achieve with Gimp.
Link to Gimp “spiritual home” … here.
Link to Gimp forum … here.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.