Skip to content

Instantly share code, notes, and snippets.

@cowboy
Created June 22, 2010 19:22
Show Gist options
  • Save cowboy/448926 to your computer and use it in GitHub Desktop.
Save cowboy/448926 to your computer and use it in GitHub Desktop.
Widget schema idea
/*
Say you have widgets in the page, and each one has its own option defaults.
You embed these widgets in the page, but as your site grows, you may want
to change the defaults in a way that affects only new widgets, leaving the
existing widgets unmodified. How do you manage these "schema" updates?
Sample HTML:
<div class="widget" data-schema="1" data-type="that_widget" data-params="bar=5&amp;baz=6" style="width:200px;height:300px">
Default content!
</div>
Sample JS:
// On DOM ready, init any widgets that haven't already been initialized.
$(function(){
$('.widget').sampleWidget();
});
*/
(function($){
var widget_name = 'sampleWidget',
default_type = 'this_widget',
// Schema revisions (1-based).
schemas = [
{
_default: {
_init: init_widget,
width: 300,
height: 250,
params {
foo: 1,
bar: 2,
baz: function(options){
return options.params.foo || window.foo || 'foo!';
}
}
},
this_widget: {
width: 160,
height: 600
},
that_widget: {
params: {
foo: 3
}
}
}
// Future schema revisions go here!
];
$.fn[ widget_name ] = function( opts ){
return this.each(function(){
var widget = $(this),
data,
matches,
style,
// Get schema rev and type from from data- attributes.
rev = widget.attr('data-schema') || 1,
type = widget.attr('data-type') || default_type,
defaults = [],
options = {
params: $.deparam( widget.attr('data-params') || '' ) // Yum, BBQ!
};
// Init element data if not already initialized.
widget.data( sampleWidget, data = widget.data( sampleWidget ) || {} );
// Don't re-initialize an already-initializied video player!
if ( data.ready ) {
return;
}
// For this widget, we only want to merge schemas array items from
// 0 <= N < rev.
$.each( schemas, function(i,v){
defaults[i] = v;
return i < rev - 1;
});
// Shallow merge defaults from end to beginning, overriding older defaults
// with newer defaults.
defaults = $.extend.apply( null, [ {} ].concat( defaults ) );
// Override default width / height with any inline style width / height.
style = widget.attr( 'style' ) || '';
if ( matches = style.match( /(?:^|\s|;)width\s*:\s*(\d+)px/i ) ) {
options.width = widget.width() || matches[ 1 ];
}
if ( matches = style.match( /(?:^|\s|;)height\s*:\s*(\d+)px/i ) ) {
options.height = widget.height() || matches[ 1 ];
}
// Deep-recurse into options object, executing any functions (except for
// the _init function).
(function revive( obj ) {
var k, v;
for ( k in obj ) {
v = obj[ k ];
if ( Object.prototype.toString.call( v ) === '[object Object]' ) {
revive( v );
} else if ( $.isFunction( v ) && k !== '_init' ){
obj[ k ] = v( options );
}
}
})( options );
// Merge explicit data-options + width + height -> type-specific defaults
// -> global (_default) defaults.
options = $.extend( true, {}, defaults._default, defaults[ type ], options, opts );
// If an init function is specified, run it now. If init function doesn't
// return false, assume success and set flag in widget data to prevent
// re-initialization.
if ( $.isFunction( options._init ) ) {
data.ready = options._init.call( widget, options ) !== false;
}
});
};
// Sample "default" widget init method.
function init_widget( options ) {
this.html( 'hello world' );
};
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment