Thursday, June 3rd, 2010

Border Image Generator Tool; Using local file APIs

Category: CSS

>Kevin Decker has upgraded his border-image generator tool. The major update is the ability to not have to host an image, but also use local ones. The tool itself is useful, but the post is very interesting as we get to listen in to the implementation process of Kevin as he got the feature working on bleeding edge browser APIs (e.g. Data URIs, the File API and the Web Storage API):

Display

The problem of displaying the image was the easiest to solve as all of the targeted browsers support data URIs as part of the border-image property. This allowed the application to generate URLs like the following

  1. border-width: 46px;
  2. border-image: url("data:image/png;base64,iVBORw0KGgoAAA....") 46 stretch;

Reading

Reading

With the actual display issue solvled, there was still the non-trivial issue of actually loading the content. My initial investigations involved Flash, which provides FileReference.load API which allowing for this functionality, but under the version I was testing on this API is only usable if invoked in response to user input. Being a HTML guy and lacking functional knowledge of the Flash development process I quickly ruled this technique out.

Continuing my research I came across an article on MDC that covered this exact use case, using the draft File API specification. This worked like a charm, even exposing the ability to read the image contents directly into a data URI.

The API is very clean for use cases such as this:

javascript
< view plain text >
  1. function loadFile(file) {
  2.     var reader = new FileReader();
  3.     reader.onload = function(event) {
  4.          updateImageURI(file.name, reader.result);
  5.      };
  6.      reader.readAsDataURL(file);
  7. }

Where the file variable above is a File object retrieved from a <input type="file">

or the dataTransfer object passed to the drop html event.

javascript
< view plain text >
  1. $(&quot;body&quot;).bind(&quot;dragenter dragover&quot;, function(event) {
  2.             // We have to cancel these events or we will not recieve the drop event
  3.             event.preventDefault();
  4.             event.stopPropagation();
  5.         });
  6.         $(&quot;body&quot;).bind(&quot;drop&quot;, function(event) {
  7.             event.preventDefault();
  8.             event.stopPropagation();
  9.             var dataTransfer = event.originalEvent.dataTransfer,
  10.                 file = dataTransfer.files[0];
  11.  
  12.             loadFile(file);
  13.         });
  14.         $(&quot;#localImage&quot;).bind(&quot;change&quot;, function(event) {
  15.             var file = this.files[0];
  16.  
  17.             loadFile(file);
  18.         });

This unfortunately is not without it’s issues. The File API is very much bleeding edge at this point and support is somewhat limited. As of this writing Firefox is the only browser which features this in production (Version 3.6). Support landed in the WebKit trunk literally earlier this month and can be used in their nightlies, but so far has not made it into any production releases.

The site is currently designed to progressively enhance as it detects support for the File API, so no need to worry about being left out of the site as a whole if you are not on one of these browsers yet.

History

After loading the content using the File API, the original implementation utilized the same #hash storage method for the data URIs, but this proved to be problematic as these strings can become quite large and interacting with these URLs was unwieldily. Needing another data store and being forced to maintain a cache of all local images due to the security model employed by the File API, we were left the options of storing all data in Javascript space or using the new Web Storage APIs implemented as part of HTML5.

Examining both options it seemed the the best course was to utilize the sessionStorage object when available and fail over to the javascript data model when not.

javascript
< view plain text >
  1. // Check for browser support of session storage and that it is accessible
  2.     // This may be inaccessible under certain contexts such as file://
  3.     function supportsSessionStorage() {
  4.         try {
  5.             return !!window.sessionStorage;
  6.         } catch (err) {
  7.             return false;
  8.         }
  9.     }
  10.     var localDataBinding = (function() {
  11.         if (supportsSessionStorage()) {
  12.             // If they support FileReader they really should support storage... but who knows (With the exception of file://)
  13.             return {
  14.                 storeImage: function(name, data) {
  15.                     var entryId = (parseInt(sessionStorage[&quot;imageList-count&quot;])||0)+1;
  16.                     sessionStorage[&quot;imageList-count&quot;] = entryId;
  17.                     sessionStorage[&quot;imageList-src-&quot; + entryId] = data;
  18.                     sessionStorage[&quot;imageList-display-&quot; + entryId] = name;
  19.                     return entryId;
  20.                 },
  21.                 getImage: function(entryId) {
  22.                     return { src: sessionStorage[&quot;imageList-src-&quot; + entryId], displayName: sessionStorage[&quot;imageList-display-&quot; + entryId] };
  23.                 }
  24.             };
  25.         } else {
  26.             // Fail over to plain js structures, meaing that refresh, etc will cause failures.
  27.             var cache = [];
  28.             return {
  29.                 storeImage: function(name, data) {
  30.                     cache.push({ src: data, displayName: name });
  31.                     return cache.length-1;
  32.                 },
  33.                 getImage: function(entryId) {
  34.                     return cache[entryId];
  35.                 }
  36.             };
  37.         }
  38.     })();

Give the post a look through, and check out the tool itself to get those nice borders :)

Related Content:

2 Comments »

Comments feed TrackBack URI

If your looking for a good File API that works across many browsers, http://www.tiddlywiki.com/ . Its code pretty much the best manual you can find on how to do cross-browser compatible local file access, including Safari, Internet Explorer and non-bleedingedge Firefox version.

Comment by hansschmucker — June 3, 2010

Internet Explorer 9 Preview has the full support of HTML5 and CSS3
http://img.cnbeta.com/newsimg/100603/08593701153887240.png

Comment by hackwaly — June 3, 2010

Leave a comment

You must be logged in to post a comment.