Important note: Webshim is not compatible with upcoming jQuery 3.x and I do not plan to work on a new major version. I will still fix critical bugs in the future though.

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")

Download

Webshim depends on jQuery.

Introduction

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

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.

Webshim is also more than a polyfill, it has become a UI component and widget library. Webshim enables a developer to also enhance HTML5 capable browsers with more highly customizable, extensible and flexible UI components and widgets. Its capability based loading system enables webshim to only load those files, which are needed for the specific browser/device and only when it is needed (deferred loading), to reduce especially the initial network payload.

Installation

Take the 'js-webshim' folder and add it to your project. (You will find a minified and a dev folder there. Put the whole 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-webshim/minified/polyfiller.js"></script>
  
<script>
  //webshim.setOptions('basePath', '/js-webshim/minified/shims/');

  //request the features you need:
  webshim.polyfill('es5 mediaelement forms');
  
  $(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.) webshim.polyfill( "canvas geolocation" );

Available Features

setOptions should always be called before the polyfill method.

The available options for webshim.setOptions :


// enhanceAuto will be false for devices smaller than 720px (i.e. Smartphones or for devices smaller than 1024px and with touchevents (i.e.: Tablets)
webshim.setOptions('enhanceAuto', !(matchMedia('(max-device-width: 720px)').matches || matchMedia('(max-device-width: 1024px)').matches && Modernizr.touchevents) );

webshim.setOptions({
	'forms-ext': {
		replaceUI: 'auto'
	},
	'mediaelement', {
		replaceUI: 'auto'
	}
});

//webshims will implement those features in all browsers/devices
// but will only enhance capable browsers on desktop with custom styleable mediaelement controls and form widgets
webshim.polyfill('forms forms-ext mediaelement');

setOptions can also take a single options parameter:


webshim.setOptions({
  extendNative: true
});

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


webshim.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
  }
});

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 webshim.ready and pass the feature name(s) and a callback function:


webshim.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':


webshim.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 or HTML5shiv 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
webshim.setOptions('waitReady', false);
// call webshim.polyfill() before domready
webshim.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)


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

Abstractions

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

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

webshim.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 webshim.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
}

webshim.defineProperties (object, props)

webshim.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 forms:

Implemented/fixed in forms-ext:

forms Options

A typical configuration would look like this:


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

webshim.polyfill('forms');

forms-ext Options

A typical configuration would look like this:


//configure forms features
webshim.setOptions("forms", {
	lazyCustomMessages: true,
	replaceValidationUI: true,
	customDatalist: "auto",
	list: {
		"filter": "^"
	}
});

//configure forms-ext features
webshim.setOptions("forms-ext", {
	replaceUI: "auto",
	types: "date range number",
	date: {
		startView: 2,
		openOnFocus: true,
		classes: "show-week"
	},
	number: {
		calculateWidth: false
	},
	range: {
		classes: "show-activevaluetooltip"
	}
});

//load forms and forms-ext features
webshim.polyfill('forms forms-ext');

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/FlashCanvas Pro:


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

//start polyfilling forms
webshim.polyfill('forms');

Webshim will implement a bridge between the native/polyfilled drawImage canvas API and the polyfilled mediaelement API. To get this work in IE8 the 'flashpro' option type for the canvas feature has to be used.

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>
	webshim.setOptions('mediaelement', {replaceUI: 'auto'});
	webshim.polyfill('mediaelement');
</script>

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

Webshim will implement a bridge between the native/polyfilled canvas.drawImage API and the polyfilled mediaelement API (flash).


<script>
	webshim.setOptions({
		canvas: {
			type: 'flashpro'
		},
		mediaelement: {
			replaceUI: 'auto'
		}
	});

	webshim.polyfill('mediaelement canvas');
</script>
<script>
	$(function(){
		var context, $video;
		function initCanvas() {
			var $canvas = document.getElementsByTagName("canvas")[0];
			$video = $("video");
			context = $canvas.getContext("2d");
			$video.on("timeupdate", paintFrame);
		}

		function paintFrame(e) {
			context.drawImage($video[0], 0, 0);

			//timeupdate is dispatched every 250ms, let's paint the frame more often,
			//but only if a real timeupdate was dispatched
			if(e){
				setTimeout(paintFrame, 65);
				setTimeout(paintFrame, 130);
				setTimeout(paintFrame, 195);
			}
		}

		initCanvas();
	});
</script>

<div class="mediaplayer ratio-16-9">
	<video controls preload="none" poster="sintel-trailer.jpg">
		<source src="sintel-trailer.mp4" type="video/mp4" />
	</video>
</div>
<canvas style="border: 1px solid black;" height="280" width="500"></canvas>


Here you can find more information about customizing controls and extending the mediaplayer's behavior.

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


webshim.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


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

Constraints of the details polyfill

FileReader/FormData/CORS

The filereader feature enables accessing and reading a file from an input[type="file"]. Additionally it can be processed over AJAX to a server. Due to its obtrusive nature an input has to have an additional class with the name ws-filereader:

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

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" multiple="" accept="image/*" />

//load the filereader
webshim.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);
  });

});

To get the file transferred to a server the FormData constructor in conjunction with AJAX can be used:


$('form[data-action].ajax-form').on('submit', function(){
	//FormData is similar to $.serializeArray, but can handle type="file" in AJAX
	var data = new FormData(this);
	$.ajax({
		url: $form.data('action'),
		success: function(data){
			//success
		},
		error: function(){
			//error
		},
		data: data,
		processData: false,
		contentType: false,
		type: 'POST'
	});

	e.preventDefault();
});

For the code above to fully work it is important, that everything is either in the same origin or a crossdomain.xml is on the server. A featured example including php code can be found in the filereader directory.

In case webshim is not served from the same domain (for exmaple using a CDN) a crossdomain.xml like the following should be reachable on your server root:


<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
	<allow-access-from domain="*" secure="false" />
</cross-domain-policy>

In case you don't have full access to your server's root to do so, you can host Moxie.min.swf (Flash) on your server and tell webshim to load them from there:


webshim.setOptions('filereader', {
	swfpath: '/assests/Moxie.min.swf'
});

The filereader feature is also lazy loaded by webhsim's form feature as soon as an element matching the input.ws-filereader selector is used. Additionally the forms features enables custom styleable [type=file] elements if it finds a wrapper with the class ws-custom-file:


<script>
	//filereader is lazyLoaded no need to call feature
	webshim.polyfill('forms');
</script>
<div class="ws-custom-file">
	<!-- add ws-filereader class to all shimable type="file" inputs -->
	<input type="file" class="ws-filereader" id="user-photo" multiple="" accept="image/*" />

	<!-- button and .ws-file-value for custom filepicker UI -->
	<button type="button">Browse</button>
	<span class="ws-file-value">...</span>
</div>

The filereader feature is based on code from https://github.com/moxiecode/moxie.

matchMedia

The matchMedia feature is a polyfill of matchMedia. It includes also a polyfill for matchMedia('(max-width: 480px').addListener.

This feature is based on code from https://github.com/paulirish/matchMedia.js.

usermedia

The usermedia feature is a polyfill of navigator.getUserMedia. It includes also a polyfill for URL and the srcObj property.

A demo with code example of getUserMedia polyfill.

url

The url feature is a polyfill of WHATWG URL Spec. It also includes the searchParams object.

The URL object has the following properties:

Usage


var link = new URL('http://afarkas.github.io/webshim/demos/index.html?param1=value1');
link.protocol; // returns 'http:'
link.searchParams.get('param1'); // returns 'value1'
link.searchParams.append('param2', 'value2');
link.href // returns 'http://afarkas.github.io/webshim/demos/index.html?param1=value1&param2=value2'

This feature is based on code from https://github.com/inexorabletash/polyfill/blob/master/url.js.

Promise

The promise feature is a polyfill of ES6 Promises. For instructions on using promises and details of the API, read JavaScript Promises at HTML5Rocks.

This feature is based on code from https://github.com/paulmillr/es6-shim.

Responsive Images

A strict polyfill of the picture element draft specification from the Responsive Images Community Group, including the srcset and sizes attributes.


webshim.polyfill('picture'):

This feature is based on code from https://github.com/aFarkas/respimage/. Complete documentation is available there.

sticky (position: sticky)

The sticky feature is a polyfill for the CSS position value sticky (position sticky demo).

Descriptive/Markup Usage

Simply add the class ws-sticky to all elements, which should become sticky and define either a top or bottom value:


<section>
	<header class="ws-sticky">
		<!-- ... -->
	</header>
	<!-- ... -->

	<footer class="ws-sticky">
		<!-- ... -->
	</footer>
</section>

Sticky and responsive webdesign. The sticky position can be also used in conjunction with mediaqueries. Simply add a data-stickymedia attribute with your mediaquery to your element:


<section>
	<header class="ws-sticky" data-stickymedia="(max-width: 480px)">
		<!-- ... -->
	</header>
	<!-- ... -->
</section>

JS Usage

Often you don't want to add the class 'ws-sticky' directly into your HTML, in this case you can use the JS-API. Simply trigger the event 'wssticky' on all elements, which should become sticky:


$('table.long-table > thead')
	.addClass('ws-sticky')
	.trigger('wssticky')
;

Example with mediaqueries:


$('table.long-table > thead')
	.addClass('ws-sticky')
	.data('stickymedia', '(max-width: 480px)')
	.trigger('wssticky')
;

The stickyness of an element, can also be removed or updated with the JS API:


//use destroysticky to remove sticky
$('dl.list-view > dt')
	.removeClass('ws-sticky')
	.trigger('destroysticky')
;

//use updatesticky to remove sticky
$('dl.list-view > dt').trigger('updatesticky');

Webshim sticky implementation supports sticky in conjunction with either top or bottom properties and on most elements including table headers (thead) and table footer (tfoot). It also implements "local" sticky areas, where the parent element of the 'sticky element' has set the overflow value to 'auto/scroll'.

Known restrictions:

Webshim's opinionated enhancement cross browser development strategy

Webshim has evolved form a general polyfill library to a sophisticated UI component / UI widget library which makes it easy to create scalable, responsive and highly adaptable websites and webapps.

While webshim polyfills older browsers and automatically fixes a lot of bugs, it enables the developer to also enhance even modern browsers with highly customizable widgets (datepicker, form validation, slider, autosuggest, custom stylable controls for video/audio).

Webshim is opinionated, that a developer should always solve a problem the HTML5 way.

For example most datepickers have a direct configuration option to disable certain days to be picked from the control. This is not possible with current implementations of input[type="date"], but it is possible to use the HTML 5 form validation API to constrain the user input to certain days. Therefore webshim offers a way to constrain the user input and those constraints are used to decide whether certain days should be disabled in the pickercontrol.

This way a developer can switch between polyfilled/enhanced and native widgets. While the appearance might be different the base functionality will work in all browsers. But if a certain feature is barley possible with HTML5 or a developer needs full control in all browsers/devices, he can always switch to a enhance all strategy.

Webshim automatically scales your enhancements

Webshim might in total load a bunch of files to implement or enhance features. This might look crazy at first glance. But due to the fact that webshim uses conditionally and deferred loading techniques, it turns out, that these techniques help to build large and fast responding websites and webapps. Webshim often initially loads only few (often only one file) and small files depending on the capabilities of the device and the requested features and then delays loading a bunch of other files either as soon as they are needed or after onload also depending on the capabilities of the device.

Webshim's mobile strategy

Often a developer might choose not to "enhance" a widget on smartphones and use built-in UI widgets, to create fast responding websites. In case a developer switches to Webshim's custom UI widgets, he gets fully responsive, font-size scalable and touch-optimized widgets.

Tip: Enlarge touch target size of UI widgets by using the font-size scalability:


/* enlarge mediacontrols by factor 1.5 from 16px to 24px */
.touchevents .mediaplayer {
	font-size: 24px;
}

/* enlarge rangeslider, datepicker by factor ~1.5 from 13px to 19px */
.touchevents .ws-popover,
.touchevents .ws-range {
	font-size: 19px;
}


@media (pointer:coarse), (-moz-touch-enabled), (-webkit-touch-enabled), (touch-enabled) {
	.mediaplayer {
		font-size: 24px;
	}

	.ws-popover,
	.ws-range {
		font-size: 19px;
	}
}

Webshim and accessibility

All UI components of webshim are fully accessible and conform to WCAG 2.0 (Level A and AA) and are therefore also Section 508-compliant. Webshim not only adds some WAI-ARIA roles, but also conforms to the ARIA Best Practices and tests all widgets with real screenreaders.

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

While most scripts only use one JS file to include the whole 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.

Configuring the path manually

//configure path manually
webshim.setOptions('basePath', '/yourFolderTo/shims/');
//polyfill
webshim.polyfill('forms mediaelement');

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
		+-- 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
	|-- 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

Customizing styles and UI

Webshim offers a lot of different config options to change the behavior of widgets and controls. The pre-defined styles loaded by webshim are just an offer. A developer is appealed to enhance/change those styles. Even all animation are done with CSS and can be changed.

Webshim pre-defined selectors are sometime overqualified to minimize conflicts with existing page styles.

There are two different main strategies to customize webshim widget's styles. In case you only want to make small changes, you can simply override the styles with your own modification. But in case you either want to have full control or need to make more changes, it is wise to set loadStyles to false and write you own styles.

In case you are setting loadStyles to false you can either start from scratch or grab/copy the default styles from webshim included in the dev folder.


webshim.setOptions('loadStyles', false);

Some widget's also use inline styles to calculate the position or the dimension of the widget. This can be turned off depending on the widget settings. (Pro Tip: This can also improve performance, especially in old IE8 or some mobile browsers.):


webshim.setOptions({
	loadStyles: false,
	'forms-ext': {
		widgets: {
			calculateWidth: false
		}
	},
	track: {
		positionDisplay: false
	}
});

In case you have made some nice changes and want to share those with the community, feel free to add those to the themes-directory and do start a pr.

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:


webshim.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:


webshim.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 webshim.register :


webshim.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.


webshim.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
webshim.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
webshim.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
  webshim.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
  webshim.defineNodenamesProperty('audio video', 'HAVE_CURRENT_DATA', {
    prop: {
      value: 2
    }
  });
  
  // Implements a new method called play on audio, video
  webshim.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
  webshim.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
  });
});