How to Generate Random Quotes in JavaScript

The Promenade theme that I’ve been writing about in my last few posts comes with the default text “PROMENADE WORDPRESS THEME BY AUDIOTHEME” in its footer. Most site owners customise this; for example Marianne Faithfull has set her text to “CONTACT | ACKNOWLEDGEMENTS | THEME BY AUDIOTHEME”. The teacher who I was using Promenade for wanted custom text too, but of a special kind, namely quotations about her subject that changed every few seconds. In this post and the next, I’ll explain how I did that for her.

All web browsers can run programs in a language called JavaScript. This can do a lot of things, such as checking data typed into forms and then submitting it automatically. What’s of interest today is that JavaScript can also “edit” the page in your browser, doing things such as inserting new pieces of HTML, hiding and then revealing existing pieces, and changing their style and position. These effects can be fancy indeed, as in Dan Motzenbecker’s OriDomi, a library which enables you to fold regions on the page as if they were paper.
One of the images on the OriDomi home page, folded like an accordion
One of the images on the OriDomi home page, folded like an accordion

Given the knowledge that JavaScript can hide and reveal bits of HTML, the idea is simple. I put all the quotations into the footer one after the other. I then wrote something in JavaScript that generated a random number, revealed that quotation and hid all the others, and then repeated indefinitely. Here is a demonstration, on a stand-alone web page rather than in WordPress.

Now I’ll show the programming. Here’s the web page, with some of the quotations omitted to save space.

<html> 
<head> 
<title>Show Random Quotes</title> 
<link rel=stylesheet 
href="random_quotes.css" type="text/css"> 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> 
<script src="show_random_quotes.js"></script> 
</head>

<body>

<h1>Show Random Quotes</h1>

<p>

<span class=quote-container>
<span class=quote-body>Dancing is a perpendicular expression of a horizontal desire.</span> 
<span class=quote-author>George Bernard Shaw</span>
</span>

<span class=quote-container>
<span class=quote-body>Dancing is a wonderful training for girls, it's the first way you learn to guess what a man is going to do before he does it.</span> 
<span class=quote-author>Christopher Morley</span>
</span>

<span class=quote-container>
<span class=quote-body>Through dancing many maidens have been unmaidened, whereby I may say it is the storehouse and nursery of bastardy.</span> 
<span class=quote-author>John Northbrooke</span>
</span>

....

<span class=quote-container>
<span class=quote-body>Somebody just gave me a shower radio. Thanks a lot. Do you really want music in the shower? I guess there's no better place to dance than a slick surface next to a glass door.</span> 
<span class=quote-author>Jerry Seinfeld</span>
</span>

</p>

<script>
$( function () {
     showRandomQuotes();
   }
 )
</script>

</body>
</html>

There are several important parts to the above. The first is a link to a stylesheet, which I’ll show below.

The second part is a link to the JavaScript quote generator, and another to a library called jQuery which makes it easier to create effects such as fades, and to write code that is compatible across browsers. You will notice I’ve linked to a copy at Google. I could have made a download and linked to that, but as Google say in “Google Hosted Libraries”, “The Google Hosted Libraries is a stable, reliable, high-speed, globally available content distribution network for the most popular, open-source JavaScript libraries”. For a demo like this one, that was just less trouble.

The third part is the quotations themselves. Each consists of one <span> element delimiting the body of the quotation (e.g. “Dancing is a perpendicular expression of a horizontal desire.”), and one delimiting the author’s name (e.g. “George Bernard Shaw”). Both are wrapped inside another <span>. This controls visibility: when that’s turned off, the entire quotation becomes invisible.

And the fourth part, near the end, is this:

$( function () {
     showRandomQuotes();
   }
 )
This invokes the quotation generator, but in a way that you might dub “load safe”. It’s jQuery notation for calling showRandomQuotes() in a way that ensures the page has completely loaded first, so that all the HTML that the function needs is safely in place.

Having seen the page, let’s look at its stylesheet, random_quotes.css . Its first rule, the one for .quote-container, is the only one that matters to the quote generator. It turns display of all the quotations off, leaving it to JavaScript to choose first one and then another and turn them back on. The final two rules just style the body of the quotation and the author’s name. Actually, the one that styles the body does nothing, but I’ve left it in in case I want to add to it later.

/* random_quotes.css */

/*
This file works with show_random_quotes.js 
and random_quotes.html . It styles the quotes 
and their containers as described in the 
comments below.
*/


.quote-container
  { display: none;
  }
/*
quote-container is the class for the outer 
 enclosing each quote. This initialises
it to not display. The JavaScript will override
this by fading randomly chosen quotes in and 
out.
*/


.quote-body
  { 
  }
/*
The body of the quote. Does nothing so far.
*/


.quote-author
  { font-style: italic;
  }
/*
Italicises the author's name in each quote.
*/

Finally, here is the JavaScript code. As my comments say, the important part is the function showRandomQuotes.

/* show_random_quotes.js */


/*
This file works with the stylesheet random_quotes.css
and the demo page random_quotes.html . This HTML
is a sequence of <span> elements, each containing a
quotation split into body and author. Initially, all 
the quotes are invisible, as set by the stylesheet. 
The function showRandomQuotes() below chooses a random 
<span>, fades it in, and then after enough delay for 
the user to read it, fades it out and repeats. 
*/


/* Returns a random integer between min (inclusive) and 
   max (inclusive).
*/
function getRandomInt( min, max ) 
{
  return Math.floor( Math.random() * (max - min + 1) )  
         + min;
   // Using Math.round() will give a non-uniform distribution,
   // according to the author.
   // From 
   // http://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range 
}


/* Phases randomly chosen s in and out as described
   in the comment at the top.
*/
function showRandomQuotes() 
{
  // Adapted from 
  // http://stackoverflow.com/questions/12065273/fade-in-out-text-loop-jquery

  var quotes = jQuery( ".quote-container" );
  // All the s.

  var no_of_quotes = quotes.length;

  function showNextQuote() 
  {
    var fade_in_and_out_delay = 2000;

    var reading_time = 5000;
    // Time a quote remains visible. This is long
    // enough to read the longest quote on the
    // website.

    var quote_index = getRandomInt( 0, no_of_quotes-1 );
    // Which <span> to fade in next. This index
    // is zero-based.

    quotes.eq( quote_index ).fadeIn( fade_in_and_out_delay )
        .delay( reading_time )
        .fadeOut( fade_in_and_out_delay, showNextQuote );
    // Fade the quote in, leave it for a bit, fade 
    // it out, and repeat.


  }

  showNextQuote();
  // Show the first quote.

}

In my next post, I’ll describe how I made this work within a WordPress footer.

How to Run PHP under WordPress with Justyn’s Magic Includer

Were you to call a gardener to come round and tidy the garden, you’d give them instructions such as “mow the lawn, dig out weed clumps, then use my long-handled shears to trim grass overhanging the edges”. These seem clear, but they’ve omitted something crucial. Where should the gardener find the mower, the trowel, and the shears? In their van; in your shed; or somewhere undefined amongst bits of DIY kit in the garage? I faced a similar situation when writing the script described in “How to Bulk-add Gigs in an AudioTheme Theme”. I solved it with Justyn’s Magic Includer, a.k.a. The Ultimate Include/Require Script for WordPress by Justyn Hornor.

The analogue to tools in a program is “functions”: self-contained pieces of code that perform an action or return data, possibly the result of a calculation. In my script, these include classes(), which returns the names, times, days and venues for the teacher’s classes; terms(), which returns the three parts of each year when these classes take place; is_in_term(), which works out whether any given date falls within a specified term; and add_class(), which adds all the classes of a given type to the gig listing. These are tools I’ve made.

But they depend on other people’s tools. My function add_class() wouldn’t work unless it could call the functions written by AudioTheme for inserting gigs. And because gigs are represented as posts, those wouldn’t work unless they could call the functions written by WordPress’s authors for handling posts. So when I’m running my programs, I need them to know that AudioTheme’s functions are in the shed over there, whereas the WordPress functions are buried at the back of my garage behind that pile of motorbike spares. This is what Justyn’s Magic Includer does. It enables code to run and have access to all the WordPress functions, even though I’m running it at a terminal rather than by clicking WordPress buttons or menus.

To use it, download his script or copy the one below. Then just load it into PHP before the other code you want to run.

<?php
/* Justyn's Magic Includer */
/* Slightly modified by Jocelyn Ireson-Paine */

/*
This is a script I use for loading the 
WordPress files. It's from "The Ultimate 
Include/Require Script for WordPress", by 
Justyn Hornor,
www.sitepoint.com/the-ultimate-includerequire-script-for-wordpress/ 

I have changed the formatting to the style I
prefer. At the end of the file, there was 
some code for generating a table of admin
users, as a test that the script is working.
I've hived that off into a separate function,
justyn_test(). You can call that as a test
after including the script.

The following comments are Justyn's. Assumes 
you are using the root folder of your server 
for your WordPress install. If not, just add 
the sub-folder where for WordPress you are using.

For example, add the sub-folder to the $location 
variable:
$location = $_SERVER['DOCUMENT_ROOT'] . '/your-sub-folder';
*/

$location = '/home/jenny/www/www.danceanddare.co.uk';

include( $location . '/wp-config.php' );
include( $location . '/wp-load.php' );
include( $location . '/wp-includes/pluggable.php' );

global $wpdb;

if ( !isset( $wpdb ) ) {
  include( $location . '/wp-config.php' );
  include( $location . '/wp-includes/wp-db.php' );
}


/* A function to test the above include 
   statements are working by listing all 
   the admins and their email addresses in 
   a table.
*/
function justyn_test()
{
  $args = array( 'role' => 'Administrator'
               , 'fields' => 'all_with_meta'
               );

  $query = get_users( $args ); 
  // Use the get_users call.

  echo '<table cellpadding="0" cellspacing="0" border="1">' . "\n";
  echo '<tr><thead><th>Nice Name</th><th>Email</th></thead></tr>' . "\n";
  // Start the table.

  foreach ( $query as $query ) {
    echo '<tr><td>' . $query->user_nicename . '</td><td><a href="mailto:' . $query->user_email . '">' . $query->user_email . '</a></td></tr>' . "\n";
  } 
  // For each of the admins, make a table row.

  echo "</table>\n"; 
  // Close the table.
}

?>

Here’s a demo. I ran it interactively, but my interactive PHP turns out to be the so-called “Interactive Mode” rather than “Interactive Shell”. This is explained at “Interactive shell ¶” on php.net . It means that even though I’m running the code from the terminal, PHP doesn’t execute the commands until I type an end-of-file. So below, I include Justyn’s Magic Includer and then run his test. This generates a table of the admin users and their email addresses, but doesn’t do so until I type end-of-file, shown as ^D.

jenny@onza:~/www/www.danceanddare.co.uk$ php -a
Interactive mode enabled

php > include('justyn.php');
php > justyn_test();
php > ^D<table cellpadding="0" cellspacing="0" border="1">
<tr><thead><th>Nice Name</th><th>Email</th></thead></tr>
<tr><td>Jocelyn</td><td><a href="mailto:jocelyn@supermail.com">jocelyn@supermail.com</a></td></tr>
<tr><td>Jenny</td><td><a href="mailto:jenny@supermail.com">mailto:jenny@supermail.com</a></td></tr>
</table>
jenny@onza:~/www/www.danceanddare.co.uk$ 

And here’s the table as rendered by your browser:

Nice NameEmail
Jocelynmailto:jocelyn@supermail.com
Jennymailto:jenny@supermail.com

How to Bulk-add Gigs in an AudioTheme Theme

In “A Promenade of Promenades”, I wrote about using AudioTheme’s Promenade theme to give a clear minimalist visual design to a Summertown teacher’s WordPress website. This teacher gives regular weekly classes, and asked me to list them on the site, all the way up to mid-2018.

Promenade, as I also mentioned last time, was designed for musicians, and has a feature for displaying lists of gigs on the home page. It can be customised to use the word “class” instead of “gig”, so making the list suit the vocation is no problem. However, to add a gig, you have to do much the same as when writing a blog post. You need to type the name of the gig as a title. For the band Manowar who I found use Promenade, this could be something like “The Final Battle”; for my teacher, it’s more likely to be “Music and Movement”. You also have to select the date from a calendar, the time from a menu, and the venue from a list of locations known to Google Maps that I’ve previously set up. For the classes she wanted me to add, this would have amounted to about 350 title-typings and date-time-venue selections. Ouch. So this was definitely worth trying to automate.

I got in touch with Anna DiTommaso and Brady Vercher from AudioTheme Support. Both were extremely helpful, and I want to thank them here, because their advice on various customisations saved me several days of trial and error. Brady is one of AudioTheme’s developers, and helped me understand how gigs are stored. With his help, I was able to write a PHP script for bulk-adding classes, and I’m reproducing it here in case it’s useful to anyone else. I ran it from the command-line using Justyn’s Magic Includer, which I’ll write about in a later post.

One final point is that with musical gigs, it’s often not important to know when they end. Everybody’s too drunk, stoned or stunned to care. But my customer did want this shown. I discovered that the easiest way to do it was to standardise on writing the start time and the end time in the gig-description field, as e.g. “9-10am” or “5:50-6:30pm”. I then copied the files audiotheme/single-gig.php and audiotheme/parts/gig-meta.php into my child theme, and edited them so that the description field was always visible.

<?php


/* add_classes.php */


/*
This script adds all Jenny's classes
to her Promenade-themed site, between the 
terms returned by function terms(). 
It defines add_classes() to do so. 
Use delete_all_classes() to delete them 
all if there are problems.
*/


/* Returns an array of class structures.
   Each element represents one kind of class,
   held weekly during term. The fields are
   for my convenience in writing the
   information only: they'll need converting
   to what AudioTheme expects, which I do
   in function add_class().
*/
function classes()
{
  $class1 = array( 'title' => 'Music and Movement'
                 , 'venue' => 'Little Acorns, Headington'
                 , 'descr' => '4-5pm'
                 , 'day'   => 'Thursday'
                 , 'time'  => '4pm'
                 );

  $class2 = array( 'title' => 'Listen, Move, Dance'
                 , 'venue' => 'Revolution Dance Studio, Cowley'
                 , 'descr' => '7-8pm'
                 , 'day'   => 'Thursday'
                 , 'time'  => '7pm'
                 );

  $class3 = array( 'title' => 'Rhythm and Rhyme'
                 , 'venue' => 'Yarnton Church Hall'
                 , 'descr' => '6-7pm'
                 , 'day'   => 'Friday'
                 , 'time'  => '6pm'
                 );

  return array( $class1, $class2, $class3 );
}


/* Returns an array of pairs. Each
   pair is an array representing a term,
   its first element being the start date
   and its second the end date. As with
   classes(), these fields are for me,
   and mean nothing to AudioTheme.
*/
function terms()
{
  return array( array( new DateTime('2016-Oct-01')
                     , new DateTime('2016-Dec-17') 
                     )
              , array( new DateTime('2017-Jan-07')
                     , new DateTime('2017-Mar-23') 
                     )
              , array( new DateTime('2017-Apr-20')
                     , new DateTime('2017-Jul-06') 
                     )
              , array( new DateTime('2017-Oct-01')
                     , new DateTime('2017-Dec-17') 
                     )
              , array( new DateTime('2018-Jan-07')
                     , new DateTime('2018-Mar-24') 
                     )
              , array( new DateTime('2018-Apr-20')
                     , new DateTime('2018-Jul-06') 
                     )
              );
}


/* Adds all classes.
*/
function add_classes()
{
  foreach ( classes() as $class )
    add_class( $class );
}


/* Adds one class. Classes are assumed to start
   on the first relevant day of October 2016.
*/
function add_class( $class )
{
  $start_string = 'First ' . $class['day'] . ' of Oct 2016';
  $start_date_time  = new DateTime( $start_string );
  $interval    = new DateInterval( 'P1W' );

  $recurrences = 250;
  $dates = new DatePeriod( $start_date_time, $interval, $recurrences );

  // The above code sets up $dates as a structure
  // representing 250 successive weekly time points. 
  // 250 may actually be slightly too many, but it 
  // will be filtered by the final term anyway.

  // Now loop over these timepoints, and for each one,
  // get the info about the class and add it as a gig.

  foreach ( $dates as $date ) {
    if ( is_in_terms( $date, terms() ) ) {
      echo $class['title'] . ' ' . 
           $class['venue'] . ' ' . 
           $class['descr'] . ' ' .
           $date->format( 'D d M Y' ) . "\n";

      add_gig( $class['title']
            , sanitize_title( $class['title'] )
              // This is the WordPress slug.
            , $date->format( 'D d M Y' )
            , $class['time'] 
            , $class['descr'] 
            , $class['venue'] 
            );
    }
  }
}


/* Returns true if $date falls within one
   of the terms.
*/ 
function is_in_terms( $date, $terms )
{
  if ( sizeof( $terms ) == 0 )
    return false;
  else if ( is_in_term( $date, $terms[0] ) )
    return true;
  else 
    return is_in_terms( $date, array_slice( $terms, 1 ) );
}


/* Returns true if $date is within $term.
   $term is an array representing a pair of
   dates as above.
*/ 
function is_in_term( $date, $term )
{
  return is_between_dates( $date, $term[0], $term[1] );
}


/* Returns true if $date is between $start
   and $end.
*/
function is_between_dates( DateTime $date
                         , DateTime $start
                         , DateTime $end
                         ) 
{
  return $date >= $start && $date <= $end;
}


/* Adds a gig with the relevant fields. This is where
   I interface with AudioTheme.
   $title is the main title, e.g. Music and Movement. 
   $name is a slug, e.g. music-and-movement. 
   $date is a date in any reasonable format, e.g. 
   '22 Jan 2017'. 
   $time is the same for time, e.g. '2pm'. 
   $description should be of the form '9-10am' or 
   '2:30-3:30pm'. All classes last for an hour,
   and this enables us to let site visitors see the
   end time.
   $venue is one of the venue names. I have made
   sure that these are all known to Google Maps,
   so that they display properly in the single-gig
   listing. 
*/
function add_gig( $title, 
                  $name,
                  $date,
                  $time,
                  $description, 
                  $venue 
                )
{
  // This code is based on source code shown me by
  // Brady Vercher at AudioTheme Support. He said 
  // that gigs are stored as a custom post type (CPT), 
  // with the various properties saved as post meta, 
  // so I could probably just loop through my classes 
  // and use wp_insert_post() and add_post_meta() to 
  // insert them. He also said he uses a library called 
  // Posts 2 Posts to store the relationships between 
  // gigs and venues, so those would need to be accounted 
  // for separately. However, I don't think I've had to 
  // worry about that. My code works anyway, if I get 
  // venue IDs as below and pass those to 
  // set_audiotheme_gig_venue_id().

  $venue_post = get_page_by_title( $venue, OBJECT, 'audiotheme_venue' );
  $venue_id = $venue_post->ID;

  $args = array(
    'post_type'      => 'audiotheme_gig',
    'post_author'    => jenny_id(),
    'post_content'   => '',
    'post_title'     => $title,
    'post_excerpt'   => $description,
    'post_status'    => 'publish',
    'comment_status' => 'closed',
    'ping_status'    => 'closed',
    'post_password'  => '',
    'post_name'      => $name,
    'to_ping'        => '',
    'pinged'         => '',
    'post_parent'    => 0
  );
                  
  $post_id = wp_insert_post( $args );
  
  set_audiotheme_gig_venue_id( $post_id, $venue_id );

  $dt = date_parse( $date . ' ' . $time );
  $datetime = sprintf( '%d-%s-%s %s:%s:%s',
                       $dt['year'],
                       zeroise( $dt['month'], 2 ),
                       zeroise( $dt['day'], 2 ),
                       zeroise( $dt['hour'], 2 ),
                       zeroise( $dt['minute'], 2 ),
                       zeroise( $dt['second'], 2 )
                     );
  update_post_meta( $post_id, '_audiotheme_gig_datetime', $datetime );
  //
  // I don't know how much of the date reformatting
  // is necessary. I'm just imitating AudioTheme's code.

  $t = date_parse( $time );
  $time = sprintf( '%s:%s:%s',
                   zeroise( $t['hour'], 2 ),
                   zeroise( $t['minute'], 2 ),
		   zeroise( $t['second'], 2 ) 
                 );
  update_post_meta( $post_id, '_audiotheme_gig_time', $time );
}


/* Gets the user ID for the site's owner,
   Jenny. I use this for the 'post_author' 
   argument in $args in add_gig(), so that 
   classes appear with a sensible author. 
*/
function jenny_id()
{
  $jenny = get_user_by( 'login', 'Jenny' );
  $jenny_id = $jenny->id;
}


/* Deletes all the classes. This function
   isn't called above, but I use it if
   I've mis-added so many classes that
   deleting them manually isn't feasible.
*/
function delete_all_classes()
{
  $args = array( 'post_type' => 'audiotheme_gig'
               , 'posts_per_page' => -1
               , 'post_status' => 'any'
               , 'fields' => 'ids'
               );

  $gigs = get_posts( $args );
  //
  // Each element of $gigs is an ID.
  // That's because of the 
  //   'fields' => 'ids'
  // above.

  foreach( $gigs as $gig_id )
    wp_delete_post( $gig_id, true );
}

?>

A Promenade of Promenades

I’ve been using the Promenade theme from AudioTheme. A “theme” is a piece of software that determines the visual design of a website built with WordPress or some other content-management tool. One of my customers wanted Promenade for her site because of its uncluttered minimalist design, so we bought a copy from AudioTheme and experimented.

Like AudioTheme’s other themes, Promenade is designed for musicians. So one of its features is the ability to add a list of gigs. This can be displayed on your home page. If you click on one of these gigs, you get details of it alone, including a Google Maps display showing the venue. My customer is actually not a musician but a teacher who gives regular classes around Oxford. These can be listed via the gig display, and Promenade can be customised so that it heads the list with the phrase “Upcoming Classes” rather than its default of “Upcoming Gigs”.

I wanted to see what other sites using the theme looked like, and how they’d customised it. Poking around Google, I discovered Themetix, which advertises itself as a search engine for themes. And indeed, I was able to search for Promenade sites, many of which did have the distinctive Promenade style. But there were some sites whose screenshots on Themetix looked like Promenade, but which turned out to be quite different when visited. Jacob Whitesides, for instance. So I don’t know how Themetix works, but it’s probably not by inspecting sites at the time I search.

Below, I’ve set up a gallery of sites that definitely are using Promenade. I know, because I checked the page source. There’s a wide variety of musical styles, from The Cedar Lake Seven Men’s Gospel Choir to Manowar‘s “Hail And Kill MMXIV”. Apparently, Manowar got into the Guinness Book of World Records in 1984 for delivering the loudest performance, a record they have since broken.

There are the German site Die Sterne, and the Polish site Masters. There are two non-musical sites: Pastor Bob Thune from Coram Deo, who advertises sermons and other gospel-centered resources; and Toni Mascaró, who promotes lectures about his passion for Creativity, Innovation and Entrepreneurship. There’s In Cahoots, which I originally read as Inca Hoots, and there’s Marianne Faithfull. If Promenade is good enough for her, it ought to be good enough for anyone.

Some of the sites use Promenade pretty much as it’s sold, while others seem to have done a lot of customisation. Anna DiTomasso has changed background colour, MisterGsongs has changed the colours in his main menu, and Jackie Greene is using the typewriter-like font Trixie. Die Sterne and Jackie Greene have set up a child theme, which enables them to set up their own styles without interfering with those supplied by Promenade. If you don’t do that, you risk losing your changes whenever the theme’s authors update it. Actually, I am using a child theme in this blog, although not of Promenade. I did this so that I could style the “Promenade of Promenades” gallery below.

Introducing Myself to the Oxford Research Software Developers’ Network

The RSDN is an association of software developers at Oxford University. I went along to their meeting on October 5th and introduced myself and some of the projects I’d worked on. Here’s a page about these. It covers the same material, more or less, as on my home page, but in more detail. The things I discussed were: