Webshim is a polyfill library that enables you to reliably use HTML5 features across browsers, even if native support is lacking.

The project is hosted on GitHub, and is available for use under the MIT software license.

You can report bugs and discuss features on the GitHub issues page.

Downloads & Dependencies

(Right-click, and use "Save As")
Version 1.12.5

Webshim depends on jQuery and Modernizr.

Introduction

There are many appealing HTML5 features such as form validation, geolocation, and FileReader which ease the burden of writing rich web applications.

Using these features responsibly requires supporting the browsers and versions that the audience is likely to use, without breaking usability. There are two common ways to support these new features. Abstractions declare a library or piece of code that invents a new API which you can use to access the feature. jQuery is a good example; It defines an API that provides access to features that are buggy or incomplete, by coming up with it's own format. $.ajax is an abstraction over xmlhttprequest which is a standard that has varying support. The other solution is called polyfilling, which fills in the gaps in a lacking browser rather than than designing a new API.

Webshim is a polyfill solution. It adds these features in a way that is transparent to the end user and developer. It enables coding against the browser standards rather than learning and maintaining abstractions. It results in less code that is simpler and easier to maintain.

Installation

Take the 'js-webshim' folder and add it to your project. (You will find a minified and a dev folder there. Put the hole folder, not only the polyfiller.js file into your project!) Here is an example showing how to include dependencies and load webshims:


<script src="js/jquery.js"></script>
<script src="js/modernizr-custom.js"></script>
<script src="js-webshim/minified/polyfiller.js"></script>
  
<script>
  //webshims.setOptions('basePath', '/js-webshim/minified/shims/');
  
  webshims.polyfill();
  
  $(function(){
    // use all implemented API-features on DOM-ready
  });
</script>

Configuration

The polyfill method accepts an optional whitespace-separated feature list. Call it as soon as possible (before DOM ready.) webshims.polyfill( "canvas geolocation" );

Available Features

setOptions should always be called before the polyfill method.

The available options for webshims.setOptions :


webshims.setOptions('extendNative', true);

setOptions can also take a single options parameter:


webshims.setOptions({
  extendNative: true
});

Feature specific options are nested options with the feature name as their key.


webshims.setOptions({
  // configure generally option
  extendNative: true,

  // configure canvas-shim
  canvas: {
    type: 'flash' // use flashcanvas instead of excanvas as polyfill
  },

  // configure forms-shim
  forms: { 
    lazyCustomMessages: true // implement customValidationMessages
  }
});

The polyfill-options can also be changed by using declarative markup, using a data-polyfill-cfg attribute with a valid JSON string on a script-element:


<script src="some.js" data-polyfill='{
"basePath": "pathToYour/shims-folder/",
"canvas": {
  "type": "flash"
}
}'>

</script>

Fire When Ready

Not every feature is ready immediately in all browsers; some shims might take time to load before you can use them. You can use one of jQuery's ready methods to delay working with elements until the DOM and any necessary shims are ready.


$(function(){
  // work with DOM + all implemented features
});

$(document).ready(function(){
  // work with DOM + all features
});

If you want to use a feature as soon as possible or you have set the waitReady option to false , you can use webshims.ready and pass the feature name(s) and a callback function:


webshims.ready('geolocation es5', function(){
  // work with geolocation and es5
});

Note that this callback function may be called before the DOM is ready. If you want to use a feature after DOM-Ready, simply pass the string 'DOM':


webshims.ready('DOM canvas', function(){
  // work with canvas in the document
});

DOM Abstractions

Due to the fact that we cannot extend accessors of elements in all browsers, we always use jQuery as an extension-wrapper.

HTML5 shiv and innershiv

IE8- isn't capable of rendering HTML5 elements. Webshims lib in combination with Modernizr automatically fixes this issue and adds WAI-ARIA landmarks to enable accessibility of HTML5.

Dynamically adding HTML5 markup

Additionally it implements the following manipulation methods to allow dynamic creation of HTML5 elements: .updatePolyfill() , .htmlPolyfill() , .appendPolyfill() , .prependPolyfill() , .afterPolyfill() , .beforePolyfill() .replaceWithPolyfill() , .appendPolyfillTo() , .prependPolyfillTo() , .insertPolyfillAfter() , .insertPolyfillBefore() , .replacePolyfillAll()


var html = '<section><form action="#">' +
           '<input type="text" placeholder="name" name="test" required/>' +
           '<input type="date" required="required" name="test2" />' +
           '<input type="submit" />' +
           '</form></section>';
$('#Shiv-dynamic-html5-test').htmlPolyfill(html);
 

Using webshims' updatePolyfill method with Backbone/jQMobile/Knockout

If a plugin, script or framework is used, which uses a "normal" JS/jQuery manipulation method instead of the corresponding webshims enhanced manipulation method to generate new HTML5 content (i.e.: .html() instead of .htmlPolyfill() ), the method .updatePolyfill() can be used to polyfill the dynamic content:


$('#my-dynamic-container').load('ajax/test.html', function(){
    $(this).updatePolyfill();
});

jQuery mobile example


//set waitReady to false
webshims.setOptions('waitReady', false);
// call webshims.polyfill() before domready
webshims.polyfill();

// bind to the pageinit event after domready...
$(document).on('pageinit', function(e){
    // ...and call updatePolyfill on the changed element container
    $(e.target).updatePolyfill();
});

For jQuery Mobile see also webshims and jQ mobile

ES5

The es5 feature uses the ES5 shim by Kris Kowal (all object methods, which can't be used cross-browser are removed.).

The script implements the following methods:

Note: All methods added to the prototype object are visible inside of a for in loop, while native implementations aren't enumerable. (use hasOwnProperty)


webshims.ready('es5', function(){
    [1, 2, 3].forEach(function(){
        // do stuff
    });
});

Abstractions

Webshims Lib adds the following methods: webshims.objectCreate , webshims.defineProperty , webshims.defineProperties , webshims.getOwnPropertyDescriptor and corresponding support flags: Modernizr.objectAccessor (true in all modern Browsers including IE9, getters and setters can be used on all objects) and Modernizr.advancedObjectProperties (true in FF4, IE9, Chrome 10..., the full ES5 specification of defineProperty is implemented (including writeable, enumerable and configurable).

webshims.objectCreate(proto [, propertiesObject, options ])

webshims.objectCreate works very similar to Object.create

If propertiesObject is defined, the method goes through this object and sets writeable, enumerable and configurable to true, if the corresponding property is undefined.

After this, it will pass proto and propertiesObject to Object.create (if defined) or will use Crockfords begetObject -Method on the proto -object and then calls webshims.defineProperties with the returned object and the propertiesObject .

If you pass the options parameter and the Object already has a Options-property. The options-property on the object will be deeply extended otherwise an new options-property will be created.

If the object has a method called _create , it will call this method with options as first argument.

After this, the created object will be returned.


var carProto = {
    options: {
        foo: 'bar',
        baz: 'boom'
    },
    wheels: 4, 
    drive: function(){
        this.isDriving = true;
    }
};

var myCar = Object.create(carProto, {
    _create: {
        value: function(){
            this.drive();
        }
    },
    jumps: {
        value: function(){
            //implements jumping
        }
    },
    {baz: 'jo'}
);

// myCar will look like this:

{
    // own property:
    options: { 
        foo: 'bar',
        baz: 'jo'
    },
    
    // prototype:
    wheels: 4, 
    
    // prototype
    drive: function(){
        this.isDriving = true;
    },
    
    // own property:
    _create: function(){
        this.drive();
    },
    
    // own property:
    jumps: function(){
        //implements jumping
    },
    
    // own property:
    isDriving: true
}

webshims.defineProperties (object, props)

webshims.defineProperties works similar to Object.defineProperties

It will go through the props properties and will set writeable, enumerable and configurable to true, if they are undefined.

After this either Object.defineProperties will be invoked or legacy code is used.

Forms

The forms (constraint validation without number, date etc.) and forms-ext (number, date, range etc.) features of the webshim lib are implementing support for the constraint validation API, some input widgets and the placeholder-attribute.

The forms feature also implements an easy and cross browser way to control the validation UI (error message and styling the validation bubble).

Implemented/fixed in form:

Implemented/fixed in form-ext:

forms Options

A typical configuration would look like this:


webshims.setOptions("forms", {
	lazyCustomMessages: true,
	replaceValidationUI: true,
	customDatalist: "auto",
	list: {
		"filter": "^"
	}
});

forms-ext Options

Extensions

Canvas

the canvas feature implements one jQuery method .getContext() .


var ctx = $('#my-canvas').getContext('2d');
ctx.clearRect(10, 10, 100, 100);
ctx.fillStyle = "rgb(200,0,0)";  
ctx.fillRect(10, 10, 55, 50);  
 
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";  
ctx.fillRect(30, 30, 55, 50);

Webshim Lib can implement excanvas or FlashCanvas (default)/FlashCanvas Pro:


// always run configuration before calling webshims.polyfill();
window.FlashCanvasOptions = {
	disableContextMenu: true
};
webshims.setOptions('canvas', {
	type: 'flashpro' // excanvas | flash | flashpro
});

Options for the canvas feature

Media

Audio, video and source

The mediaelement feature implements the audio, video and source elements including their API and enables playing mp4, mp3, flv, fla etc. media files and playing rtmp streams in incapable browsers.

Implemented/fixed elements, attributes, properties, methods, events

Options for the mediaelement feature

Playing HLS/RTMP Streams


<video poster="poster.jpg" controls="">
	<!-- Mac OS / iOS HLS Streaming -->
	<source src="http://server.com/path/file.m3u8" type="application/x-mpegURL" />
	<!-- rtmp streaming: using the data-server attribute -->
	<source data-server="rtmp://server.com/path/" src="file.mp4" type="video/rtmp" />
	<!-- rtmp streaming: using an identifier (i.e.: mp4:) -->
	<source src="rtmp://server.com/path/identifier:file.mp4" type="video/rtmp" />
</video>

Loading youtube videos with different Qualities

Video quality can be suggested by using the vq parameter. Possible values are: small, medium, large, hd720, hd1080, highres


<video poster="poster.jpg" controls="" src="http://www.youtube.com/watch?v=siOHh0uzcuY&vq=large">
</video>

Using customstyleable controls


<style>
	/* add basic styles */
	.mediaplayer {
		position: relative;
		height: 0;
		width: 100%;
		padding-bottom: 56.25%; /* 16/9 */
	}

	.mediaplayer video,
	.mediaplayer .polyfill-video {
		position: absolute;
		top: 0;
		left: 0;
		height: 100%;
		width: 100%;
	}
</style>

<script>
	webshims.setOptions('mediaelement', {replaceUI: 'auto'});
	webshims.polyfill('mediaelement');
</script>

<div class="mediaplayer">
	<video poster="poster.jpg" src="http://www.youtube.com/watch?v=siOHh0uzcuY&vq=large">
	</video>
</div>

Examples/Demos

Track Element Support

The track feature implements the track element width a WebVTT parser and the corresponding DOM- and JS-APIs.

Implemented/fixed elements, attributes, properties, methods, events

Options for the track feature

Examples

Geolocation

The geolocation -feature implements the navigator.geolocation API. The following methods are available:

The shim uses the geolocation information provided by http://freegeoip.net and/or googles API-Loader

Options for geolocation


webshims.setOptions('geolocation', {
    confirmText: '{location} wants to know your position. It is Ok to press Ok.'
});

navigator.geolocation.getCurrentPosition(function(pos){
    alert("Thx, you are @ latitude: "+ pos.coords.latitude +"/longitude: " + pos.coords.longitude);
});

Details & Summary

The HTML5 details element is an interactive element. If the open attribute is set the details are shown, if not the details are hidden.

click to toggle visibility

Here are the details of this element:

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore mag

Dynamically creating a details element (always use htmlPolyfill, afterPolyfill, beforePolyfill etc.):


$('details:first').afterPolyfill('<details open="open"><p>Here is some text</p></details>');

The openness of a details element can be also scripted. Simply change the open IDL-attribute:


// set the open attribute of the first details element ...
// ... to its opposite value
$('details:first').prop('open', !$('details').prop('open') );

Options for the details feature


webshims.setOptions('details', {animate: true});

Constraints of the details polyfill

FileReader

An example of how to read a file as a base64 string from a form control:


<!-- add ws-filereader class to all shimable type="file" inputs -->
<input type="file" class="ws-filereader" id="user-photo" />

webshims.polyfill('filereader');

//on DOM ready filereader is shimmed
$(function(){

  $('#user-photo').on('change', function (evt) {
    var reader, file;
    reader = new FileReader();
    reader.onload = function (evt) {
      var fileData = evt.target.result;
      // fileData is the base64 encoded image
    };

    //use $.prop to access files property
    file = $(this).prop('files')[0];
    reader.readAsDataURL(file);
  });

});

Options for the FileReader feature

Element specific options

This feature is based on code from https://github.com/Jahdrien/FileReader.

Typical project setup and JS optimizations (requirejs, concatination of JS files)

While most scripts only use one JS file to include the hole behavior, Webshims is using the polyfiller.js file, a polyfiller named AMD module, as an entry point and loads conditionally other resources. This means webshims needs to know, where those resources are.

The code inside of the polyfiller.js automatically detects the path to the script it is run from and assumes, that the shims folder is in the same directory. This means, you have to make sure, that either the shims folder is placed parallel to the code of the polyfiller.js or to configure the path using the basePath option.

Optimizing into the same folder

The following example, shows a very simple setup where the shims folder is parallel to the normal polyfiller.js file and the optimized js file (i.e.: app.min.js):


+-- projectfolder
	|-- index.html
	+-- scripts
		+-- vendor
			|-- jquery.js
			|-- modernizr.custom.js
		+-- shims
		|-- main.js
		|-- require.js
		|-- app.min.js (optimized code, which also includes the polyfiller.js code)
		|-- polyfiller.js

Optimizing into a different folder

A better way is to output the concatenated and compressed code into a different directory for deployment. For a webshims project this means beside an optimization task (concat and minify) often another simple task to copy the shims folder parallel to the optimized script package:


|-- projectfolder
	|-- index.html
	+-- bower-components
		|-- jquery
		+-- js-webshim
			+-- dev
				|-- polyfiller.js
				|-- shims
			|-- minified
	+-- scripts
		+-- vendor
			|-- modernizr.custom.js
	|-- main.js (requires polyfiller.js from the **dev** folder)
	|-- require.js
	+-- optimized-scripts (folder with optimized scripts)
		|-- app.min.js (optimized code, which also includes the polyfiller.js code)
		|-- shims (copied shims folder from the **minified** folder)

Customizing Webshim

Modernizr

Webshims lib does not need every Modernizr test and supports creating a custom Modernizr build.

Modernizr detections used by webshim

Writing a New Polyfill

A Pollyfill is always split into at least two parts. First the test and loader definition, and then the implementation.

Assume there is a new method magicMethod on the window object that we want to polyfill if the browser doesn't provide it. An implementation would look like this:


webshims.addPolyfill('magicMethod', {
  test: function(){
    return ('magicMethod' in window); 
  }
});
// loads magicMethod.js from shim-folder if test returns false

Create a file called magicMethod with your polyfill code and place it in the shim folder:


window.magicMethod = function(){
  // your polyfill implementation
};

If your implementation has dependencies on other polyfills/modules, you can add a dependencies property to make sure they're loaded too:


webshims.addPolyfill('magicMethod', {
  test: function(){
    return ('magicMethod' in window); 
  },
  d: ['es5'] // d = dependencies
});
// load magicMethod.js and its dependencies if test returns false

If your shim has dependencies, you have to register your implementation code with webshims.register :


webshims.register('magicMethod', function($, webshims, window, document, undefined, options){
  // now you can use es5-feature feature  
  window.magicMethod = function(){
    // your polyfill implementation
  };
});

In case of a DOM extension, webshims lib comes with some useful extension-methods.


webshims.addPolyfill('mediaelement', {
  test: function(){
    return ('canPlayType' in document.createElement('video'));  
  },
  d: ['dom-support'],
  methodNames: ['play'] // pause, load
});
// load mediaelement.js and DOM extension features from shim folder
// if test returns false and always create a jQuery plugin called play,
// which tries to invoke native or polyfilled play

// listen to 'play' events in the capturing phase
// can use event delegation with jQuery's bind method
webshims.capturingEvents(['play']);

Now put a mediaelement.js inside of your shim folder and start implementing some features. The dom-support feature of Webshims lib comes with some very useful implementation helpers. Here are two of them:


// wait till the DOM-Extension feature is loaded
webshims.register('mediaelement', function($, webshims, window, document, undefined, options){
  // webshims refers to jQuery.webshims in this function
  
  // Implements a new IDL property on audio, video called currentTime
  webshims.defineNodeNamesProperty('audio, video', 'currentTime', {
    prop: {
      get: function(){
        // your getter implementation ("this" refers to the element)
      },
      set: function(value){
        // your setter implementation ("this" refers to the element)
      }
    }
  });
  
  // Implements a new constant property called HAVE_CURRENT_DATA on audio, video
  webshims.defineNodenamesProperty('audio video', 'HAVE_CURRENT_DATA', {
    prop: {
      value: 2
    }
  });
  
  // Implements a new method called play on audio, video
  webshims.defineNodeNamesProperty('audio video', 'play', {
    prop: {
      value: function(){
        // your play method implementation
        // this refers to the element
      }
    }
  });
  
  // Implements a new boolean IDL property called controls on audio, video
  // which is reflected by a content attribute
  webshims.defineNodeNamesBooleanProperty('audio video', 'controls', {
    // set works more like an onSet:
    // you don't have to set the value anymore on the DOM node, only handle true/false values
    set: function(value){
      if(value){
        // show controls for elem
      } else {
        // hide controls for elem
      }
    },
    initAttr: true
  });
});