0

Here are the facts:

  • I manage a large number of Git repositories.
  • They use Travis CI for builds.
  • I built a small web page at http://status.scijava.org/ to keep track of the list of components.
  • One of the table columns displays the relevant Travis CI build badge for each component.
  • I currently use sorttable.js to make the table columns easily sortable. This is helpful for understanding the current status of the project.

It would be very nice if I could sort the Build column by Travis CI build status. Unfortunately, the build status is loaded only from the image content of the <img> tags, so sorttable.js has nothing to go on in the HTML itself.

So I ask the HTML and javascript gurus!

How can I make this table sortable by Travis CI build status?

A couple of soft requirements:

  • The site is hosted on GitHub Pages, and I'd like to keep it that way.
  • The solution should be easy on the Travis servers. Of course I want to be nice, and use the fewest possible number and intensity of API calls.
ctrueden
  • 6,751
  • 3
  • 37
  • 69
  • 2
    Load the image with your script, convert the bytes to a base64 encoded string, add this as the source of the image using the [data uri scheme](https://en.wikipedia.org/wiki/Data_URI_scheme) and also as a custom sort key for sorttable. – Andreas Apr 28 '17 at 18:45
  • 1
    I don't understand why someone downvoted this question. Is it not clear enough technically? Could someone please enlighten me? – ctrueden Apr 30 '17 at 20:50

1 Answers1

1

Here is the process which worked for me. With minimal adaptation, this scheme can be used to make any <img> elements sortable on the client side via javascript such as sorttable.js.

  1. Based on Andreas's suggestion (thanks Andreas!), I wrote some javascript which paints each <img> onto its own canvas, then extracts the base64 data via canvas.toDataURL().
  2. I hashed the contents of toDataURL() to create a custom sort key for sorttable.js, and assigned it to the sorttable_customkey attribute of the containing <td>. For my purposes, taking the first 64 characters of the data was sufficient, but you could of course write any hash function you like for this, depending on your image pixels.
  3. To avoid the error Tainted canvases may not be exported, I copied each <img> src to a new Image and set crossOrigin=anonymous, as suggested by shady sherif on a different SO post.

Here is the JavaScript code of my working solution:

function imageData(img) {
  var canvas = document.createElement("canvas");
  canvas.width = img.width;
  canvas.height = img.height;

  // HACK: Avoid 'Tainted canvases may not be exported' error.
  // See: https://stackoverflow.com/a/27260385/1207769
  var anonImg = new Image();
  anonImg.setAttribute('crossOrigin', 'anonymous');
  anonImg.src = img.src;

  canvas.getContext("2d").drawImage(anonImg, 0, 0);
  return canvas.toDataURL();
}

function hashCode(s) {
  // NB: Using the first 64 characters is good enough here, and much faster.
  return s.substring(0, 64);
}

// See: https://stackoverflow.com/q/43686686/1207769
function makeBadgesSortable() {
  var tds = document.body.getElementsByClassName("badge");
  for (var i=0; i<tds.length; i++) {
    var imgs = tds[i].getElementsByTagName("img");
    if (imgs.length < 1) continue;
    tds[i].setAttribute("sorttable_customkey", hashCode(imageData(imgs[0])));
  }
}

And then on the HTML side, add class="badge" to each <td> in the table containing a Travis <img> badge, as well as onload="makeBadgesSortable()" to the <body> element.

Community
  • 1
  • 1
ctrueden
  • 6,751
  • 3
  • 37
  • 69