Friday, 16 December 2011

using cite in blockquote

The blockquote tag has had a cite property for quite a while.

Unfortunately this is not used.
An improvement is to append it at the end of the blockquote by using the ::after selector:

blockquote[cite]::after {
 content: "source: " attr(cite);
 display: block;
 border-top: solid 1px grey;
 margin-top: 1em;
 text-align: center;
}

And we get something like so:
If the cite attribute is present, it must be a valid URL
potentially surrounded by spaces.

Unfortunately this generated text cannot be selected in Firefox, it is a long-standing bug.
It can be selected in Opera.
It cannot be selected in Chrome.

... IE9? Doesn't even generate the content.

Thursday, 15 December 2011

HTML5 + JS = validation of input file type and size

With HTML5's File API one can now access the files in an input field. Readonly, of course - security oblige.

So besides using the accept attribute to improve usability in standard-friendly browsers (grumble grumble), one can now go another step further and help the user with some client-side validation.

This feature is present in updated browsers, with the unsurprising exception of IE.


<input type="file" multiple accept="application/pdf" name="documents">
<input type="file"          accept="image/*" name="photo">
<input type="file" multiple accept="text/csv" name="logs">

By using the .files property of the <input type="file"> one can iterate over the uploaded files for the input (see the multiple attribute) and do some validation.
The File has a name and a lastModified date, but it inherits from Blob and thus also has a size and a type.
One could probably use the type instead of the name, to better validate the uploaded file against the input's accept attribute, but we'll leave that for another time.

Let's do some client-side validation:

// HERE BE DRAGONS:
// - Untested, typos and bugs may exist;
// - Does address "foo/*";
// - str.endsWith() is an extension of string that is not normally present otherwise - implement or change the code;
// - Using JQuery is not necessary, this code uses it for simplicity;
// - Fus ro DAH!
$(document).ready(function{
  $("input[accept]").change(function({
    var accept = $(this).attr("accept");
    var MAXFILESIZE = 4000000; // Load this from somewhere. (bytes)
    var extension;
    if( accept === "application/pdf" ) {
      extension = ".pdf";
    } else if( accept === "text/csv") {
      extension = ".csv";
    } else /*if( accept.startsWith("image/")) {
      extension = "." + accept.substring(7);
      if(extension === ".*") {
        extension = "";
      }
    } else*/ {
      return true;
    }
    
    var files = $(this).files;
    if(!files) {
      // WHY U NO HTML5??
      var val = §(this).val();
      if( val && !val.endsWith(extension) ) {
        $(this).val("");
        return false;
      }
    } else {
      for(int i=0; i < files.length; i++) {
        var file = files[i];
        if(!file.name.endsWith(extension)
          && !confirm(
            "File extension is not " + extension + ": " + file.name
            + " \nUpload file anyway?"
            )
          || file.size > MAXFILESIZE
          && confirm(
            "File is bigger than " + (MAXFILESIZE/1024)
            + "KiB and will fail to upload: " + file.name
            + " \nCancel submission?"
            )
          ) {
        }
      }
    }
  });
});

As a last note, never trust user input means one must always do server-side validation.
But also doing it on the client side means better usability and a more pleasant experience - and serving the users is our purpose... most of the times, at least. :)