Author Archives: Tom Woodward

Weekly Web Harvest for 2017-11-12

  • Strange and Curious Wills of the Georgian Era in the Canterbury Court – Geri Walton

    WILLIAM SHACKELL, ESQ. – Governor of Plymouth – 12 October 1782
    “I desire that my body may be kept as long as it may not be offensive, and that one of my toes or fingers may be cut off to secure a certainty of my being dead, I also make this further request to my dear wife, that as she has been troubled with one old fool, she will not think of marrying a second.”

  • Interactive marginalia – Liza Daly – Medium

    marginalia and annotation considerations . . .

  • Character Building – Futility Closet

    A man gets well pummelled at a public school; is subject to every misery and every indignity which seventeen years of age can inflict upon nine and ten; has his eye nearly knocked out, and his clothes stolen and cut to pieces; and twenty years afterwards, when he is a chrysalis, and has forgotten the miseries of his grub state, is determined to act a manly part in life, and says, ‘I passed through all that myself, and I am determined my son shall pass through it as I have done;’ and away goes his bleating progeny to the tyranny and servitude of the long chamber or the large dormitory. It would surely be much more rational to say, ‘Because I have passed through it, I am determined my son shall not pass through it; because I was kicked for nothing, and cuffed for nothing, and fagged for everything, I will spare all these miseries to my child.’

  • Projection Mapping Central – Projection Mapping Central
  • Cards Against Humanity Saves America

    So we’ve purchased a plot of vacant land on the border and retained a law firm specializing in eminent domain to make it as time-consuming and expensive as possible for the wall to get built.

    On Day 1, all Cards Against Humanity Saves America recipients will get an illustrated map of the land, a certificate of our promise to fight the wall, some new cards, and a few other surprises.

  • Gendered Text Project
  • Game of Drones: Mexico’s Cartels Have a Deadly New Weapon

    The aerial drone in the rear cargo bay was armed and ready to be deployed. Sitting in an open plastic case beside an AK47 assault rifle and spare clips. The 3DR Solo Quadcopter carried a shrapnel-filled IED that was in turn rigged to detonate by remote control.

    It was the first time a weaponized Unmanned Aerial Vehicle (UAV) had been found in the hands of an organized crime group in Mexico.

    “[Such] explosives and weapons are for the exclusive use of the Army,” said State Attorney Carlos Zamarripa

Headless(-ish) WordPress Example

This is a bit of an odd one but possibly odd enough to be interesting.

The Scenario

We can’t run WordPress on the University servers but we’ve got a bunch of people who have content on WordPress and like that editing experience. They also want VCU domains which are much harder to get for off-site servers.

The Proposal

What if we take one of those sites and see what we can do with the REST API data in a headless1 HTML/JS/CSS only environment? I also wanted to keep it out of Vue or other larger javascript frameworks so it’d be less abstract to explain. At this point I am unsure that was a great choice but I can make a different choice next time.

More Details

The faculty member already had a WordPress site. It had a bunch of pages and had no posts. The faculty member also wanted the header image to change on various pages and for a sub-menu to be created on certain pages.

The Proof of Concept – Step One HTML Shell

I opted to sketch out the HTML portion loosely with Bootstrap 4. Initially, I’d gone to one of the HTML5Up templates but pulled back as it added a chunk more complexity than I needed initially. Plus, I knew Matt was going to handle taking the design to the next level so that took the pressure off me.

You can see the four main elements commented up below.

The menu piece was done manually for this but I think we generated it for the final (or if not, we could).

<!--THE MENU IS THE LARGEST PART OF THE HTML . . . WEIRD-->
<nav class="navbar navbar-toggleable-md navbar-inverse bg-inverse">
  <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
  <a class="navbar-brand" href="#">Navbar</a>

  <div class="collapse navbar-collapse" id="navbarsExampleDefault">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item active">
        <a class="nav-link" href="#home">Home <span class="sr-only">(current)</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#bio" id="bio-menu">Bio</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#join-us" id="join">Join Us</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#about">About</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#science">Science</a>
      </li>
    </ul>
  </div>
</nav>

<!--WHERE THE IMAGE GOES-->
<div class="container-fluid">
  <div class="row header" id="featured">
  </div>
</div>


<!--WHERE WE BUILD THE SECONDARY MENU WHEN NEEDED-->
<div class="secondary-menu">
  <div id="secondary-menu-parent"></div>
  <ul id="sub-menu-js"></ul>
</div>

<!--WHERE THE DATA GOES ON SHOW-->
<div class="container">
  <div class="row">
    <div class="col-md-8 main">
      <div id="theContent"></div>
    </div>
  </div>
</div>

<!--WHERE THE DATA GOES TO HIDE ON LOAD-->
<div id="hidden-data"></div>

The Javascript

The first portion is pretty standard fetch. I’m using the REST API route2 and asking for 90 pages. I can ask for up to 99 with the new(ish) rules without having to get into additional drama to expand that and I anticipate that 90 is plenty. You did have the capability of doing -1 like in wp_query in the older API scenario.3

//GET WORDPRESS JSON CONTENT
fetch(
  "https://rampages.us/kirkwarrenbrown/wp-json/wp/v2/pages?per_page=90&_embed"
)
  .then(function(response) {
  // Convert to JSON
  return response.json();
})
  .then(function(data) {
  for (i = 0; i < data.length; i++) {
    writePages(data[i]); //takes that JSON and writes it to our hidden HTML div
    if (data[i].slug == urlHash()) {
      var slug = urlHash(); //is there a slug? great set it
      setPage(slug); //set the page 
    }
  }
  if (urlHash() === "" || document.getElementById(slug) === null) {
    setPage("home"); //if not slug set the page to home
  }
  buildMenu(slug); //make the menu based on the slug
});

So that’s the big piece that gets the data and then runs it through our functions. Now lets break down some of the functions to see what they do.

The following function takes the JSON data and writes it into the hidden div (just a div with our good CSS-friend display:none). Once it’s there we can show it in other places really quickly because all the data is already loaded. Since we were already loading jQuery for Bootstrap stuff I went ahead and used it.

//writes the pages to a hidden div for later reference
function writePages(data) {
  var post = jQuery("#hidden-data").append(
    jQuery(
      '<div id="' +
      data.slug +
      '" class="post"' +
      getImage(data) +
      getParent(data) +
      getId(data) +
      '><h2 class="page-title">' +
      data.title.rendered +
      '</h2><div class="page-content">' +
      data.content.rendered +
      "</div></div>"
    )
  );
}

Our destination is the #hidden-data div and we’re just appending a div full of stuff for each page from the WordPress JSON. Key things to notice are the data attributes which make it easy to find the right data and pull the right pieces. There are a couple of smaller helper functions that do that formatting for me. I don’t know if it was worth chunking them out like this. I continue to struggle with how small my functions should be.4

function getId(data) {
  return ' data-id="' + data.id + '"';
}

OK, now we’ve got all the data in there but we’re also checking to see if there are any URL parameters (that #something in the URL) set that would decide what content should be actually showing. I’m doing that by looking for a URL hash that matches the page-slug generated from WordPress. I could have used the post ID or something else but slugs are unique values and they’re pretty human readable.

function urlHash() {
  if (window.location.hash) {
    var hash = window.location.hash.substring(1); //Puts hash in variable, and removes the # character
    //console.log(hash);
    return hash;
    // hash found
  } else {
    // No hash found
    return "about"; //no preferences? fine we'll set the page to about
  }
}

Once we’ve got a hash variable, set or assumed, we can then use it to pick our data and load it. That’s what this function does.

function setPage(slug) {
  var getPage = document.getElementById(slug).innerHTML;//get the HTML of our matching hidden post
  var getDestination = document.getElementById("theContent");//this is where it goes
  getDestination.innerHTML = getPage; //this puts the contents of getPage into our destination 

  //set header image
  var getImage = document.getElementById(slug).dataset.img;
  var getFeatured = document.getElementById("featured");
  getFeatured.style.backgroundImage = "url('" + getImage + "')";
}

I then realized we had to watch the URL in case their were changes. If I recall correctly, hitting the back button or something like that wasn’t being caught. Hard to remember at this point but I’m pretty sure this piece was useful.

//lets you do the back and forward properly --
$(window).on("hashchange", function() {
  setPage(urlHash());
  buildMenu(urlHash());
});

Now building the sub-menus . . . we needed to be able to do two things here which weren’t immediately obvious. One was to make a relationship between the page and sub-menu but also to relate the sub-menu pages to one another so the menu would stay there in all the scenarios. Thankfully the API had the parent ID in the JSON so we wrote it as a data attribute into the div. I really like data attributes.

function buildMenu(slug) {
  //get id etc
  var j = 0;
  var theId = parseInt(document.getElementById(slug).dataset.id);
  var secondMenu = document.getElementById("sub-menu-js");

  // var theParent = "";
  if (document.getElementById(slug).dataset.parent != 0) {
    var theParent = parseInt(document.getElementById(slug).dataset.parent); //get parent ID if exists
  }

  var posts = document.getElementsByClassName("post");
  document.getElementById("sub-menu-js").innerHTML = "";

  //default parent category in sub menu
  addParent(theId, theParent);

  for (i = 0; i < posts.length; i++) {
    //MENU BUILDING LOGIC
    if (
      theId == posts[i].dataset.parent ||
      theParent == posts[i].dataset.parent
    ) {
      var urlParam = posts[i].id;
      var menuName = posts[i].querySelector("h2").innerHTML;
      var a = document.createElement("a");
      var newItem = document.createElement("li");

      a.textContent = menuName;
      a.setAttribute("href", "#" + urlParam);
      newItem.appendChild(a);
      secondMenu.appendChild(newItem);
      j++;
    }
  }
  if (j === 0) {
    document.getElementById("sub-menu-js").innerHTML = "";
  }
}

Matt’s taken this all much further by doing things like sorting the menus based on the sort order options in WordPress pages and a chunk of other user interface and visual improvements.

In any case, it was a fun little experiment that might be useful to other people.

The whole non-pretty version is in the CodePen below and I’ll update this to link to the real one once we finalize things there.

See the Pen mindfulness – headless wp json display – hash version by Tom (@twwoodward) on CodePen.


1 I’m probably using this term appropriately but words tend to wander.

2 That’s, like, totally official terminology and stuff.

3 I also remember when Twitter was just 140 characters and they counted URL length against you. Snow hills! Both ways!

4 You’re right. It does sound like a personal problem.

YouTube Full Screen Player Page from Playlist

The request was that we do something interesting with the random screens we have scattered around our area. It seemed like it’d be easy to make a little full-page player for a YouTube playlist with some lightweight overlays to emphasize the connection with our department. Handling the videos displayed via a playlist makes it easy to add/remove content and the making the logo overlays part of the webpage let us keep things consistent across videos we didn’t control. Since we weren’t activating sound by default turning on captions by default was also a key need.

The codepen example is here.

The HTML is dead simple. We’ve got a wrapper div which lets us make it full screen, an ytplayer div for the videos, and two divs to do the logos.

 <div class="videoWrapper">
        	<div id="ytplayer"></div>
        	<div class="altlab-tv"></div>
        	<div class="ctle-tv">CTLE<div class="ctle-sub">Center for Teaching & Learning Excellence</div></div>
</div>

The javascript is pretty much a cut/paste from the YouTube API docs. There are a few changes to reference the playlist instead of a single video and some stuff around closed captions and that’s noted below.

 // Load the IFrame Player API code asynchronously.
			  var tag = document.createElement('script');
			  tag.src = "https://www.youtube.com/player_api";
			  var firstScriptTag = document.getElementsByTagName('script')[0];
			  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

			  var player;
			  function onYouTubePlayerAPIReady() {
			    player = new YT.Player('ytplayer', {
			      height: 'auto',
			      width: '100%',	
			          	   			
			      playerVars:{
			      	enablejsapi: 1,
              listType:'playlist',
			      	list: 'PLDr_4M2flXJlRLzruodOncrsz33omi3zp', //put playlist ID HERE <-----------------
			      	autoplay: 1,
			        controls: 0,
			        loop: 1,
			        cc_load_policy: 1,	//this works on explicitly set CC not auto-generated (I think) . . . 
			        cc_lang_pref: 'en',
			        iv_load_policy: 3,			     
			      }
			      
			    });			    
			  }

The main tweak here is to get the fullscreen video to behave. I use this pattern from CSS tricks. The other stuff just staples some divs over the lower corners to show the logos.

body {
  font-family: helvetica, sans-serif;
}
.videoWrapper {
			position: relative;
			padding-bottom: 56.25%; /* 16:9 */
			padding-top: 25px;
			height: 0;
		}
		.videoWrapper iframe {
			position: absolute;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
		}	

		.altlab-tv {
			background-image: url(http://rampages.us/extras/tv/imgs/logo.png);
			background-position: center;
			background-size: contain;
			background-repeat: no-repeat;
			width: 200px;
			height: 200px;
			position: absolute;
			z-index: 1000;
			display: block;
			bottom: 0;
			left: 0;
			margin: 30px;
			background-color: #f8b800;
		}

		.ctle-tv {
			font-size: 4rem;
			width: 160px;
			height: 170px;
			position: absolute;
			z-index: 1000;
			display: block;
			bottom: 0;
			right: 0;
			margin: 30px;
			background-color: #fff;
			padding: 20px;
			text-align: center;
			color: #000;
			background-color: #f8b800;
			font-weight: 600;
		}

		.ctle-sub {
			font-size: 1rem;
		}

Weekly Web Harvest for 2017-11-05

Weekly Web Harvest for 2017-10-29

Weekly Web Harvest for 2017-10-22

  • The Massively Friendly World of Competitive Giant Pumpkin Growing – Atlas Obscura

    “We’re to the point where we pass out seeds, hand out information, invite everybody to come to our meetings and tell them everything we know,” Jutras says. “If people want to do the work, I hope they do and grow a personal best … We have a saying: ‘You don’t grow these from the couch.’”

    —–is it when ideas get separated from additional work that people get secretive and weird?

    h/t Jon Becker

  • Daydream Labs: Accessibility in VR

    . So we’ve been exploring how spatial audio cues can be used for navigating and interacting with virtual environments.

  • This Light Bulb Confuses Smartphone Cameras To Protect | Co.Design

    Researchers have developed a new light that can protect locker rooms and stage performances from omnipresent smartphone surveillance.

    — world is going to get stranger and stranger as machines fight and cooperate with one another in ways we can’t see/hear (that whole umwelt thing)

  • -andre bergs-

    Protanopia is a digital comic for Ipad and Iphone. Created as an experiment into the possibilities of digital comics. Using elements from 3D and 2D animation in a realtime game engine, it creates an unique visual style, whilst still having a familiar feeling.

  • Amazon to end virtual textbook contract with UMass

    The university received its guaranteed commissions of $375,000, $465,000 and $610,000 in the first three years of the contract with Amazon, according to university spokesman Daniel Fitzgibbons. Commissions have not exceeded those guarantees, Fitzgibbons added, though he declined to speculate on why Amazon ended their deal with UMass Amherst.

  • Facebook Figured Out My Family Secrets, And It Won’t Tell Me How

    I sent the woman a Facebook message explaining the situation and asking if she was related to my biological grandfather.

    “Yes,” she wrote back.

    Rebecca Porter, we discovered, is my great aunt, by marriage. She is married to my biological grandfather’s brother; she met him 35 years ago, the year after I was born. Facebook knew my family tree better than I did

  • Math’s Beautiful Monsters – Issue 53: Monsters – Nautilus

    They should make sense in the same way that a physical object would.

Weekly Web Harvest for 2017-10-15

  • Suspect In Would-Be Airport Bombing Nabbed With Help From REI : The Two-Way : NPR

    There was no video footage this time. And again, the man paid cash. But, Anderson writes, “the individual … used an REI membership number when paying.”

    An REI membership entitles a customer to 10 percent back on purchases every year. In this case, it also gave the FBI a name — Michael Estes. After releasing a still photo from the surveillance footage, authorities found Estes in downtown Asheville. He was arrested on Oct. 7.

  • Worldly Wise – Futility Closet

    If work were good for you, the rich would leave none for the poor.

Weekly Web Harvest for 2017-10-08

  • youtube-dl/README.md at master · rg3/youtube-dl

    download youtube videos with many options from terminal

    h/t Wes Bos

  • Typography in ten minutes | Butterick’s Practical Typography

    : if you learn and fol­low these five ty­pog­ra­phy rules, you will be a bet­ter ty­pog­ra­pher than 95% of pro­fes­sional writ­ers and 70% of pro­fes­sional de­sign­ers. (The rest of this book will raise you to the 99th per­centile in both categories.)

  • Pollen: the book is a program

    Pollen is a publishing system that helps authors make functional and beautiful digital books.

  • The Bet I Made with Teachers All Around the United States Last Year – dy/dan

    My point in all of this is that math teachers have names for their numbers, much in the same way that ornithologists have names for their birds. And much in the same way that ornithologists haven’t given me a reason to care about the difference between a Woodlark and a Skylark, math teachers often fail to motivate the difference between rational numbers and integers and whole numbers and imaginary numbers and supernatural numbers.

    The difference is that ornithology isn’t a course that’s required for high school graduation and university enrollment and labor market participation. Kids aren’t forced to study ornithology for twelve years of their childhood.

    So I’m inviting us to ask ourselves: “Why did we invent these categories of numbers?” And if we agree that it was to more effectively communicate about numbers, we need to put students in a place where their communication suffers without those categories. If we can’t, then we should confess those categories are vanity.

  • Nolitory
  • Carol Black on Twitter: “THREAD How kids learn without your “feedback:””

Remove H1 Option from WordPress TinyMCE Editor

/**
 *  Remove the h1 tag from the WordPress editor.
 *
 *  @param   array  $settings  The array of editor settings
 *  @return  array             The modified edit settings
 */

function my_format_TinyMCE( $in ) {
        $in['block_formats'] = "Paragraph=p; Heading 2=h2; Heading 3=h3; Heading 4=h4; Heading 5=h5; Heading 6=h6;Preformatted=pre";
    return $in;
}
add_filter( 'tiny_mce_before_init', 'my_format_TinyMCE' );

In terms of basic accessibility guidelines each page should have only one H1 element. In WordPress that’s usually built into the template and is usually the title of the post or page.

I was thinking about that last night and wondered if we should just remove it as an option from the TinyMCE editor in WordPress. Removing options goes against my typical stance but I couldn’t think of any scenarios that demanded an H1 element but plenty of scenarios where offering it seemed like it’d create confusion.

A brief perusal of the WordPress Codex’s TinyMCE section led to the code above. I put it in our generic network-activated plugin for Rampages. Next steps could include integrating a filter in kses to remove/replace H1 elements with H2 elements but that’ll wait for now as it seems a chunk more aggressive.

Conditional Content Display in Gravity Forms Templates

I don’t know when the conditional shortcode was added or if it has always existed and I was just unaware . . . but the ability to use a shortcode wrapper to show/not show portions of content in blog posts created via Gravity Forms is all kinds of handy.

As a quick example, the following content would not show if the entry to the form field {What’s up?:1} was empty.

[gravityforms action="conditional" merge_tag="{What's up?:1}" condition="isnot" value=""]
<h2>{What's up?:1} - more words</h2>[/gravityforms]

In the past, if we didn’t have the wrapper and we had no response on the ‘What’s up?’ field we’d be left with the awkward ‘ – more words’ being there.

This is so very nice for optional content where you might want to add additional html structure — like a link to an attachment but *only* if the attachment exists. It solves so very many problems so easily.

You could also display different additional HTML elements based on the response to specific field entries. Like if a ranking on a scale of 1-5 is a 5 show a particular overly happy gif.

[gravityforms action="conditional" merge_tag="{How happy?:1}" condition="is" value="5"]
<img src="https://i.imgur.com/5tCHDqm.jpg" alt="a very happy turtle">
[/gravityforms]

Contact us

Academic Learning Transformation Lab - ALT Lab
1000 Floyd Ave, Suite 4102 | Richmond, Virginia 23284
altlab@vcu.edu | 804-827-5181

Last updated: September 26, 2017

Virginia Commonwealth University