More rendering issues today, adding onto yesterday’s PDF Slideshow and Form Creation Poll Spreadsheet Tutorial progress. We want to be able to render forms well in PDF eventually, and so we’re using the great start Rick van Buuren and Clรฉment Lavoillotte‘s excellent HTML table rendering via Fpdf code represents, and extending it. Today, we start down the road to trying to render the HTML …
input element
. So what’s the big deal here? Well, to say you have a mini project to render the HTML input element is not that straightforward (it not being in the Rick van Buuren and Clรฉment Lavoillotte Fpdf open source code we found). Consider the many and varied type attributes for an input element. Consider the zillions of combinations of outerHTML manifestations of element attributes to cater for. With this latter consideration, like with other types of HTML elements we try to render via Fpdf, it is a bigger ask than we can present here to cater for everything. We’ll just try to deal with two attributes …
- type
- value
… but even considering just these two HTML attributes, quite a bit of variation of procedure happens when coding it, a couple to exemplify being …
- input type=”color” value corresponds to a “Fpdf-speak” bgcolor attribute concern, not a content concern, that it mostly is for other input types …
- input type=”submit” and type=”button” content would be good presented as an overlay to a “Fpdf-speak” cell, whereas other types will want to look more like an underlay
A job like this, that we are just starting down (the road of doing), and will tweak well into the future, helps you appreciate the “art” of web browser renderers (ie. programmers) out there in the “www” (woooooooooooooooprrrrrrrrllllllld wide web).
Again, to see this, try the changed form_creator.php‘s live run parts where PDF is rendered via Fpdf where the input data contains input element HTML.
Previous relevant PDF Slideshow and Form Creation Poll Spreadsheet Tutorial is shown below.
Meanwhile, back at the polls, after yesterday’s PDF Slideshow and Form Creation Helper Canvas Tutorial, we wanted to add functionality to allow for sensible overview of captured Poll data.
With this, though, to reinvent the wheel is a huge ask, and a bit of a silly ask, given the number of “out of the box” spreadsheet offerings out there. Use PHP and you can hook up …
- web application produced (mimetype text/csv) csv (comma separated values) data … via …
- (local) download … and onto, optionally …
- default spreadsheet application opened with this csv data
… and once all this happens, a good spreadsheet application will offer you ways to compare and tabulate and show in a variety of chart forms, the contents of this “rows and columns” data as a whole or as subsets of the whole.
How does PHP arrange it? One new non-submit button calling on the Javascript (client) onclick event logic …
function collateit() {
window.open(document.URL.split('?')[0].split('#')[0] + '?collate=' + Math.floor(Math.random() * 18765432), '_blank', 'top=80,left=80,width=500,height=500');
}
How does PHP create the csv data? One new “if” PHP code snippet as per …
<?php
if (isset($_GET['collate'])) {
$bits=explode('/' . '/ ?', $wasphpcode);
$csvis="";
for ($ij=1; $ij<sizeof($bits); $ij++) {
$arec=explode("\n", $bits[$ij])[0];
if (strpos($arec, "&ipollid=") !== false) {
$plid=explode("&", explode("&ipollid=", $arec)[1])[0];
$arec=str_replace("&ipollid=" . $plid, "", $arec);
if ($csvis == "") {
$csvis='"Poller Number","Timestamp","Favourite anything","Favourite integer","Favourite colour","Favourite file","Favourite email","Favourite url","Favourite date","Favourite time","Favourite month"' . "\n";
}
if (strpos($arec, "×tamp=") !== false) {
$ts=explode("&", explode("×tamp=", $arec)[1])[0];
// Initialising a DateTime
date_default_timezone_set('GMT');
//$datetime = gmdate("M d Y H:i:s", mktime(0, 0, 0, 1, 1, 1970)); //new DateTime('2019-09-30');
$xdatetime = new DateTime('1970-01-01 00:00:00');
// DateInterval object is taken as the
// parameter of the add() function
// Here lots of seconds added
$dis=(($ts - ($ts % (24 * 60 * 60))) / (24 * 60 * 60));
$hrs=floor(($ts % (24 * 60 * 60)) / (60 * 60));
$mins=floor(($ts % (60 * 60)) / 60);
$secs=floor(($ts % 60));
$xdatetime->add(new DateInterval('P' . $dis . 'D'));
$xdatetime->add(new DateInterval('PT' . $hrs . 'H'));
$xdatetime->add(new DateInterval('PT' . $mins . 'M'));
$xdatetime->add(new DateInterval('PT' . $secs . 'S'));
$arec=str_replace("×tamp=" . $ts, "", $arec);
$brec='"' . $plid . '","' . $xdatetime->format('Y-m-d H:i:s') . " GMT" . '","' . str_replace("+"," ",urldecode(explode("&", explode("fa=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fn=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fc=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("ff=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fe=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fu=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fd=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("ft=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fm=", $arec)[1])[0])) . '"' . "\n";
} else {
$brec='"' . $plid . '","","' . str_replace("+"," ",urldecode(explode("&", explode("fa=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fn=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fc=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("ff=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fe=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fu=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fd=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("ft=", $arec)[1])[0])) . '","' . str_replace("+"," ",urldecode(explode("&", explode("fm=", $arec)[1])[0])) . '"' . "\n";
}
$csvis.=$brec;
}
}
if ($csvis != "") {
header('Content-type: text/csv');
header('Content-disposition: attachment;filename=form_creator.csv');
echo $csvis;
exit;
}
}
?>
To see this, try the changed form_creator.php‘s live run.
Previous relevant PDF Slideshow and Form Creation Helper Canvas Tutorial is shown below.
Today we venture back to other rendering thoughts regarding our PDF Slideshow and Poll Referrer Integration Tutorial blog post thread’s existant use of …
- PDF rendering …in conjunction to …
- HTML rendering (of input HTML code) … and today we add …
- Canvas rendering … onto to Image rendering (for many web browsers)
… as that graphical rendering choice. Graphical (rendering) choices open you up to HTML img elements or background elements,and with many, onto animation possibilities. No surprise, really, to us, as we often choose between PDF or an image or a set of images or animated GIF or video as the source data for our blog presentation ideas.
This project keeps going into different areas and in different directions because the possibilities are numerous, but please do not despair nor be bewildered. It’s the possibilities that count, not the look. The look of things can be nuanced near the end, as required. Components can be pared down and/or simplified, but we have decided this is all for later. We still have mode conceptual ideas to explore with all this work you can try with the changed form_creator.php‘s live run.
How, in that code above, was Canvas rendering achieved, in broad brush terms?
- use of multiple form submit buttons …
if (isset($_POST['both']) || isset($_GET['both'])) {
$canvh="<html><head><scr" . "ipt type='text/javascript'> var elem=null,context=null,x=[],y=[],w=[],h=[],aimg=[],iimg=0; " . $sleepcode . " function di(iois) { if (iois.src != '') { context.drawImage(ielem,x[iimg],y[iimg],w[iimg],h[iimg]); iimg++; if (aimg.length > iimg) { iois.src=aimg[iimg] + '?xx=' + iimg; } } } function docanvas() { elem=document.getElementById('canvas'); ielem=document.getElementById('him'); context=elem.getContext('2d'); context.font='8px Arial'; } </scr" . "ipt></head><body onload=\" window.open('form_creator.pdf','_blank','left=50,top=50,width=695,height=942'); docanvas(); \"><canvas style='border:1px solid red;width:595px;width:842px;' width=" . (595 / 2.54) . " height=" . (842 / 2.54) . " id=canvas></canvas><br><img onload=di(this); id=him style=display:none; src=></img></body></html>";
for ($jj=0; $jj<sizeof($pdf->canvascmds); $jj++) {
$canvh=str_replace("} <"," " . $pdf->canvascmds[$jj] . " \n} <", $canvh);
}
$pdfcont=$pdf->Output('form_creator.pdf','F');
echo $canvh;
} else if (isset($_POST['justc']) || isset($_GET['justc'])) {
$canvh="<html><head><scr" . "ipt type='text/javascript'> var elem=null,context=null,x=[],y=[],w=[],h=[],aimg=[],iimg=0; " . $sleepcode . " function di(iois) { if (iois.src != '') { context.drawImage(ielem,x[iimg],y[iimg],w[iimg],h[iimg]); iimg++; if (aimg.length > iimg) { iois.src=aimg[iimg] + '?xx=' + iimg; } } } function docanvas() { elem=document.getElementById('canvas'); ielem=document.getElementById('him'); context=elem.getContext('2d'); context.font='8px Arial'; } </scr" . "ipt></head><body onload=\" docanvas(); \"><canvas style='border:1px solid red;width:595px;width:842px;' width=" . (595 / 2.54) . " height=" . (842 / 2.54) . " id=canvas></canvas><br><img onload=di(this); id=him style=display:none; src=></img></body></html>";
for ($jj=0; $jj<sizeof($pdf->canvascmds); $jj++) {
$canvh=str_replace("} <"," " . $pdf->canvascmds[$jj] . " \n} <", $canvh);
}
echo $canvh;
} else {
header('Content-type: application/pdf');
$pdf->Output();
}
… remember HTML Form Multiple Submit Buttons Primer Tutorial? - intersperse the brilliance of Rick van Buuren and Clรฉment Lavoillotte‘s excellent HTML table rendering via Fpdf code with HTML5 canvas code (for that alternative rendering) … proving again, it is a lot of a battle, especially with event driven programming, to know when and/or where to intervene, for most effect
That interspersing is collected into Javascript command arrays (that $pdf->canvascmds above), which help piece together HTML for a whole new webpage (with canvas) HTML codeset.
You can also see this play out at WordPress 4.1.1’s PDF Slideshow and Form Creation Helper Canvas Tutorial.
Previous relevant PDF Slideshow and Poll Referrer Integration Tutorial is shown below.
A data storage means for our latest Online Email Polling web application functionality was sorted out with yesterday’s PDF Slideshow and Poll Form Creation Storage Tutorial and today we undertake …
- software integration of an initial email (inline HTML) execution with a means by which …
- continued user (via their email address) polling possibilities (and reporting) via online web browser access usage
… that “continued user (via their email address) polling possibilities” courtesy of a changed and integrated oninvalid.html (with the changed form_creator.php‘s live run).
As you would surmise, to (software) integrate you need messaging conduits that let one software component let other integrated software components know something about their environment via these “conduit ideas”. Our “conduit idea” today is …
Javascript (client) | PHP (server) |
---|---|
document.referrer | $_SERVER[‘HTTP_REFERER’] |
… and it is with today’s “Javascript (client)” aspects (but remember Inline Email HTML disallows Javascript, and hence any of that oninvalid event logic available back at web browser incarnations) of the oninvalid.html work where we add changes to its scripting content as per …
<html>
<head>
<script type='text/javascript'>
// other client Javascript logic here
function getref() {
for (var ii=0; ii<evalas.length; ii++) {
eval(evalas[ii]);
}
var youremail='';
if (('' + document.referrer).indexOf('creator.php') != -1) {
if (('' + document.referrer).indexOf('?pollerquery=') != -1) {
youremail=" for Your Email [<a title='Email to yourself via default email client application' href='mailto:" + decodeURIComponent(('' + document.referrer).split('?pollerquery=')[1].split('&')[0]) + "'>" + decodeURIComponent(('' + document.referrer).split('?pollerquery=')[1].split('&')[0]) + '</a>] ';
}
document.getElementById('myh1').innerHTML='Favourites Polling';
document.getElementById('ibut').value='Poll Me';
document.getElementById('myform').action=('' + document.referrer).split('?')[0].split('#')[0];
if (document.URL.indexOf('timestamp=') != -1) {
document.getElementById('dref').innerHTML=location.search.split('timestamp=')[1] ? '<a target=_blank title="RJM Programming Favourites Polling" href="' + document.getElementById('myform').action + '">Favourites Polling</a>' + youremail + ' on ' + todatetime(location.search.split('timestamp=')[1].split('&')[0]) + ' ... <br><br>' : '';
if (evalas.length > 0) {
document.getElementById('ibut').value='Poll Me Again';
}
document.getElementById('dref').style.backgroundColor='yellow';
document.getElementById('dref').style.border='5px solid pink';
document.getElementById('dref').style.margin='5px 5px 5px 5px';
}
}
}
setTimeout(dorep, 2000);
</script>
</head>
<body onload='getref();'>
// body HTML goes here
</body>
</html>
… the (software) integration via document.referrer (knowledge) turning oninvalid.html into a dual purpose web application (component), able to “talk and integrate or collaborate” with the supervisory form_creator.php “Online Email Polling” capable web application.
The other aspect to “conduit talk” is how the user meets this software capability. The user clicks the Polling button and adds a “?” to their email address to have the web application quiz itself as to whether that email address has ever been polled before, and this (software) integration gets called into play.
Previous relevant PDF Slideshow and Poll Form Creation Storage Tutorial is shown below.
The means by which we can approach “Online Email Polling” was achieved with yesterday’s PDF Slideshow and Poll Form Creation Inline Email Tutorial, but it stopped short of being accountable. It was only of relevance to the user executing the web application in that incarnation.
But what about storing the findings data somewhere for …
- recall (to the user who owns the Favourites Data) … and … further down the track …
- collate several user sets of data into a statistical report of some kind
? Well, we start down that road today? Do we use a database to do this? Not exactly. Do we use cookies or storage of that ilk? Not exactly? Well, what then? We use the PHP itself to store user findings. We have this as a choice because there is an element of data hiding to the use of PHP as the basis of your web application, and we’re making use of that fact today.
Was (before an rmetcalfe15@gmail.com polling) … |
---|
Then (after an rmetcalfe15@gmail.com polling) … |
And so yet again, we have form_creator.php‘s live run for you to try some more accountable Poll Email (Inline) HTML ideas.
Previous relevant PDF Slideshow and Poll Form Creation Inline Email Tutorial is shown below.
Another functionality dovetailing with the recent PDF Slideshow and Form Creation Helper Inline Email Tutorial‘s use of Email Inline HTML (mimetype text/html), we figure, is …
Online Polling via Email
… being that “one step easier than downloading” method to try to garner someone’s opinion online. Looks wise, it just involves one more button to our form, and behind the scenes, one hidden HTML textarea element. Leave this too open-ended, and you’ll end up with a lot of spam answers, alas. So we turned our mind to deconstructing HTML from the recent HTML Oninvalid Event Form Validation Primer Tutorial, paring it back and rearranging code that was in the head section to be in the body section. Not too hard, really.
What we did learn, doing this, was how to push the Exim Mail Server functionality up at the rjmprogramming.com.au web server deliver an individual email message in the queue ahead of the 30 build up Exim waits for, in that queue, before delivering emails, in the normal case of events. You can see this …
# cd /var/log
# # Do the work in the browser to set up and send the email ... then ...
# tail exim_mainlog | grep 'rmetcalfe15@gmail.com'
# # copy the last record's third word (after two timestamp related words) into {buffer}
# exim -M [paste {buffer} here]
#
… play out at the bottom of today’s tutorial picture.
So yet again, we have form_creator.php‘s live run for you to try some Poll Email (Inline) HTML ideas.
Previous relevant PDF Slideshow and Form Creation Helper Inline Email Tutorial is shown below.
Up to now, here at RJM Programming with this blog, we’ve resisted talking much about …
- emails containing HTML Inline (ie. those with a mime type of text/html) … in favour of (talking more about) …
- emails containing HTML Attachments (ie. have a mime type of text/plain with the body of the email and mime type application/octet for attachments)
… and yet, the former has the advantage that you can get an online task done without a download operation, necessarily. So why the bias to more online work (does not compute)? Well, it is to do with the restrictions the email client applications put on the types of data accepted with HTML Inline text/html data, that is not as stringent as that imposed on HTML Attachments that would have to be downloaded onto your local system to have any impact and so is one step removed from security issues (but still a consideration, we hope you’d agree). It depends a lot on the email client involved, but suspects to not accepting (or stripping) your HTML data are …
- no Javascript in document.head nor document.body (with onload event logic and other inline event logics, even)
- sometimes no document.head will be accepted (eg. Gmail)
… and this makes what is useful as Email (Inline) HTML as a very small subset of HTML(/CSS/Javascript) functionality (pretty much just HTML design and inline CSS styling). Nevertheless, for today’s work, after the leads of yesterday’s PDF Slideshow and Form Creation Helper Preview Tutorial, especially its revisit to mapping relative URLs to absolute URLs (ie. ones starting with “http:” or “https:”) …
var url=document.URL.split('#')[0].split('?')[0];
function relative_to_absolute(inth) {
var huhs, posthuhs, ii, m, delimis=\" \";
var uubits=url.split(\"?\");
if (url == \"\") return inth;
if (uubits[0].indexOf(\"/\") != -1) {
var uuubits=uubits[0].split(\"/\");
if (uuubits[eval(uuubits.length - 1)] != \"\") {
if (uuubits[eval(uuubits.length - 1)].indexOf(\".\") != -1) {
uubits[0] = uubits[0].replace(\"/\" + uuubits[eval(uuubits.length - 1)], \"/\");
} else {
uubits[0] += \"/\";
}
}
}
var uudirname=uubits[0];
var outth = inth;
var ideas = new Array(\" href='\", ' href=\"', \" Href='\", ' Href=\"', \" HREF='\", ' HREF=\"', \" href=\", \" Href=\", \" HREF=\", \" src='\", ' src=\"', \" Src='\", ' Src=\"', \" SRC='\", ' SRC=\"', \" src=\", \" Src=\", \" SRC=\");
if (uudirname != \"\" && url != \"\") {
for (m=0; m<ideas.length; m++) {
huhs = inth.split(ideas[m]);
if (huhs.length > 1) {
for (ii=eval(huhs.length - 1); ii>=1; ii--) {
delimis=ideas[m].substring(eval(ideas[m].length - 1), eval(ideas[m].length));
if (delimis == \"=\") delimis=\" \";
posthuhs=huhs[ii].trim().replace(\">\",\" \").replace(/mailto:/g, \"http:\").replace(/javascript:none;/g, \"http:\").split(delimis);
if (posthuhs[0].length > 0) {
if (posthuhs[0].substring(0,1) == \"#\" || posthuhs[0].substring(0,4).toLowerCase().replace(/file/g, \"http\").toLowerCase() == \"http\") {
outth = outth;
} else if (posthuhs[0].substring(0,1) != \"/\" && posthuhs[0].substring(0,1) != \"'\" && posthuhs[0].substring(0,1) != '\"') {
while (outth.indexOf(posthuhs[0]) != -1) {
outth=outth.replace(posthuhs[0], \"youwill-never-ever-findthis\");
}
outth=outth.replace(/youwill-never-ever-findthis/g, uudirname + posthuhs[0]).replace(/\/\.\//g, '/');
} else if (posthuhs[0].substring(1,2) != \"/\" && posthuhs[0].substring(0,1) != \"'\" && posthuhs[0].substring(0,1) != '\"') {
while (outth.indexOf(posthuhs[0]) != -1) {
outth=outth.replace(posthuhs[0], \"youwill-never-ever-findthis\");
}
outth=outth.replace(/youwill-never-ever-findthis/g, uudirname.substring(0, eval(uudirname.length - 1)) + posthuhs[0]).replace(/\/\.\//g, '/');
}
}
}
}
}
}
return outth.replace(/\/\.\//g, '/');
}
Why is this mapping a good idea? Well …
- user interaction wise, it is great to offer a user the chance to use either relative (for brevity) or absolute (for cross domain) URLs … and yet …
- email (and a web browser address bar and curl, as two other examples) are “starting from scratch” places regarding URLs and to link to other places online, need absolute URLs to succeed
Yet again, we have form_creator.php‘s live run for you to try some Email (Inline) HTML. It calls the changed emailhtml.php to facilitate the Email Inline HTML body.
Previous relevant PDF Slideshow and Form Creation Helper Preview Tutorial is shown below.
Yesterday’s PDF Slideshow and Form Creation Helper Primer Tutorial suited those used to uploading data, a lot of which will involve those users knowing a bit about HTML coding, in relation to that presentation of it within an HTML textarea element. We’re still asking you to know HTML coding as we add onto yesterday’s …
- HTML (or Text) view (in textarea) … with, today …
- Visual view (in overlayed new window, and available to the user via a new light blue non-form-submit button)
… mimicking what a lot of Content Management Systems (CMS) provide for their Content Managers, that being a way HTML input data, that can be previewed by that user in a “Visual view” showing an HTML webpage rendering of that HTML coding. This can reassure users that what they are (HTML) coding will produce a look in keeping with what they were expecting.
In our WordPress blog CMS they do this for the Content Writer with two tabs called “Text” (ie. the HTML coding) and “Visual” (ie. the HTML as rendered by the web browser involved). That is cute, and easy to use, but we see the “Visual” part as optional, and don’t really wish to prioritise its use. As well as that, it is a stepping stone to another improvement we wish to make, soon.
Again, we have form_creator.php‘s live run for you to try.
Did you know?
The HTML textarea element is truly amazing, and the performing of today’s work has clarified (some more … have been letting it wash over for years!) how it works in my mind.
There are two parts to the “content” of a textarea element we want to concentrate on. They are its “innerHTML” (what it looks like) and its “value” (what gets passed with an HTML form as its data).
We often, here at RJM Programming, start a textarea’s existence with HTML of a textarea with a blank “value” but a filled in “innerHTML”, and that filling in can be HTML code with all the caretted < and > data we get used to as Web Content managers. Immediately, on that textarea element’s onload, that textarea will be given a “value”.
But if you make a change to that textarea data, or even if you don’t, and go back to see the relationship …
- “value” is full of the caretted < and > characters … but …
- “innerHTML” is full of corresponding < and > HTML entities
… and as explainers of HTML (in online blogs and online HTML articles) around the world will know, that use of < and > within <code></code> elements for instance, saves the web browser by thinking it needs to interpret the carets of HTML code, and, instead, as we the explainers would prefer too, just display the caret characters as displayed text.
This is a topic, and it feels alive, and undertold, “The Wonders of the HTML Textarea Element”, that you, dear reader, may want to individually look into yourself, by writing your own HTML to see this textarea behaviour yourself. And we haven’t even touched on, here, possibilities regarding setting a textarea to “readonly” true? And how the web browsers often allow for resizing it so easily? All fascinating! We suspect it was fascinating to the web browser and mobile platform programmers as well, and they came up with a very powerful and useful tool for lots of online content writers out there.
Previous relevant PDF Slideshow and Form Creation Helper Primer Tutorial is shown below.
Our recent work involving the great Fpdf creator of PDF files when we presented Ajax FormData Object No Body PHP PDF Tutorial has got us starting on a new PDF (PHP) web application we are starting out thinking will help with …
- online forms (probably via thinking in terms of Fpdf open source programmers like Rick van Buuren and Clรฉment Lavoillotte‘s excellent HTML table rendering ideas) via HTML table intermediate user interactions … and …
- slideshows
… but we will not be surprised if the project branches out into other ideas. We’ll see over time.
We hope you come along for the trip starting with a bit of a proof of concept form_creator.php‘s live run for you to try, where we allow you to enter (and be able to change) some HTML table code (if that’s what you end up with?!) in a pink HTML textarea element, and that will become PDF should you click the underlying HTML form’s yellow submit button.
Hope to see you for tomorrow’s PDF writing developments 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.