Permalink
Browse files

Enhancer: Add ability to instantiate multiple widgets per element

This also make many speed and organization improvements to the base
enhancer as well as exposing several helper methods
  • Loading branch information...
arschmitz committed May 8, 2015
1 parent e1e1a62 commit bc68371539be4c17caad4e2c4a4b24fa26088d99
View
@@ -22,11 +22,13 @@
'data.js',
'animationComplete.js',
'widgets/enhancer.js',
'degradeInputs.js',
'widgets/enhancer.backcompat.js',
'widgets/enhancer.widgetCrawler.js',
'widgets/page.js',
'widgets/page.dialog.js',
'widgets/loader.js',
'degradeInputs.js',
'events/navigate.js',
'navigation/path.js',
'navigation/base.js',
@@ -18,17 +18,16 @@
// AMD. Register as an anonymous module.
define( [
"jquery",
"jquery-ui/widget",
"widgets/enhancer" ], factory );
"widgets/enhancer",
"widgets/enhancer.widgetCrawler" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
if ( $.mobileBackcompat !== false ) {
var originalGenerator = $.fn.enhance.initGenerator,
filter = function( elements ) {
var filter = function( elements ) {
elements = elements.not( $.mobile.keepNative );
if ( $.mobile.ignoreContentEnabled ) {
@@ -40,14 +39,16 @@ if ( $.mobileBackcompat !== false ) {
}
return elements;
},
generator = function( prototype, ns ) {
generator = function( prototype ) {
return prototype.initSelector ||
$[ prototype.namespace ][ prototype.widgetName ].prototype.initSelector ||
originalGenerator( prototype, ns );
$[ prototype.namespace ][ prototype.widgetName ].prototype.initSelector || false;
};
$.fn.enhance._filter = filter;
$.fn.enhance.initGenerator = generator;
$.enhance._filter = filter;
$.enhance.defaultProp = function() {
return "data-" + $.mobile.ns + "role";
};
$.enhance.initGenerator = generator;
}
} );
View
@@ -17,99 +17,162 @@
// AMD. Register as an anonymous module.
define( [
"jquery",
"jquery-ui/widget" ], factory );
"jquery" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
var plugin = {
enhance: function() {
var installed = false;
// Loop over and execute any hooks that exist
for ( var i = 0; i < $.fn.enhance.hooks.length; i++ ) {
$.fn.enhance.hooks[ i ].apply( this, arguments );
}
$.fn.extend( {
enhance: function() {
return $.enhance.enhance( this );
},
enhanceWithin: function() {
this.children().enhance();
return this;
},
enhanceOptions: function() {
return $.enhance.getOptions( this );
},
enhanceRoles: function() {
return $.enhance.getRoles( this );
}
} );
$.enhance = $.enhance || {};
$.extend( $.enhance, {
// Call the default enhancer function
$.fn.enhance.defaultFunction.apply( this, arguments );
enhance: function( elem ) {
var i,
enhanceables = elem.find( "[" + $.enhance.defaultProp() + "]" ).addBack();
return this;
if ( $.enhance._filter ) {
enhanceables = $.enhance._filter( enhanceables );
}
// Check if the widget factory exists and if it
// does make sure the options extension is installed
$.enhance._installWidget();
// Loop over and execute any hooks that exist
for ( i = 0; i < $.enhance.hooks.length; i++ ) {
$.enhance.hooks[ i ].call( elem, enhanceables );
}
// Call the default enhancer function
$.enhance.defaultFunction.call( elem, enhanceables );
return elem;
},
getNamespace = function() {
return $.fn.enhance.ns || $.mobile.ns || "";
};
// Generate the init selector to be used by a widget
plugin.enhance.initGenerator = function( prototype, ns ) {
return "[data-" + ns + "role='" + prototype.widgetName + "']";
};
// Check if the enhancer has already been defined if it has copy its hooks if not
// define an empty array
plugin.enhance.hooks = ( $.fn.enhance && $.fn.enhance.hooks ) ? $.fn.enhance.hooks : [];
plugin.enhance._filter = $.fn.enhance ? $.fn.enhance._filter || false : false;
// Default function
plugin.enhance.defaultFunction = function() {
var that = this.addBack();
// Enhance widgets
function crawlChildren( _childConstructors ) {
$.each( _childConstructors, function( index, constructor ) {
var prototype = constructor.prototype,
found = that.find(
plugin.enhance.initGenerator( prototype, getNamespace() )
);
if ( plugin.enhance._filter ) {
found = plugin.enhance._filter( found );
}
found[ prototype.widgetName ]();
if ( constructor._childConstructors && constructor._childConstructors.length > 0 ) {
crawlChildren( constructor._childConstructors );
// Check if the enhancer has already been defined if it has copy its hooks if not
// define an empty array
hooks: $.enhance.hooks || [],
_filter: $.enhance._filter || false,
defaultProp: $.enhance.defaultProp || function() { return "data-ui-role"; },
defaultFunction: function( enhancables ) {
enhancables.each( function() {
var i,
roles = $( this ).enhanceRoles();
for ( i = 0; i < roles.length; i++ ) {
if ( $.fn[ roles[ i ] ] ) {
$( this )[ roles[ i ] ]();
}
}
} );
}
crawlChildren( $.Widget._childConstructors );
};
// This is for backcompat remove in 1.6
plugin.enhanceWithin = function() {
return this.children().enhance();
};
$.extend( $.fn, plugin );
$.extend( $.Widget.prototype, {
_getCreateOptions: function() {
var option, value,
// Get all data at once avoid multiple lookups http://jsperf.com/jqm-data-bulk
data = this.element.data(),
options = {},
ns = getNamespace().replace( "-", "" );
// Translate data-attributes to options
for ( option in this.options ) {
value = data[ ns + (
!ns ?
option :
option.charAt( 0 ).toUpperCase() + option.slice( 1 )
) ];
if ( value !== undefined ) {
options[ option ] = value;
}
},
cache: true,
roleCache: {},
getRoles: function( element ) {
if ( !element.length ) {
return [];
}
var role,
// Look for cached roles
roles = $.enhance.roleCache[ !!element[ 0 ].id ? element[ 0 ].id : undefined ];
// We already have done this return the roles
if ( roles ) {
return roles;
}
// This is our first time get the attribute and parse it
role = element.attr( $.enhance.defaultProp() );
roles = role ? role.match( /\S+/g ) : [];
// Caches the array of roles for next time
$.enhance.roleCache[ element[ 0 ].id ] = roles;
// Return the roles
return roles;
},
optionCache: {},
getOptions: function( element ) {
var options = $.enhance.optionCache[ !!element[ 0 ].id ? element[ 0 ].id : undefined ],
ns;
// Been there done that return what we already found
if ( !!options ) {
return options;
}
// This is the first time lets compile the options object
options = {};
ns = ( $.mobile.ns || "ui-" ).replace( "-", "" );
$.each( $( element ).data(), function( option, value ) {
option = option.replace( ns, "" );
option = option.charAt( 0 ).toLowerCase() + option.slice( 1 );
options[ option ] = value;
} );
// Cache the options for next time
$.enhance.optionCache[ element[ 0 ].id ] = options;
// Return the options
return options;
},
_installWidget: function() {
if ( $.Widget && !installed ) {
$.extend( $.Widget.prototype, {
_getCreateOptions: function( options ) {
var option, value,
dataOptions = this.element.enhanceOptions();
options = options || {};
// Translate data-attributes to options
for ( option in this.options ) {
value = dataOptions[ option ];
if ( value !== undefined ) {
options[ option ] = value;
}
}
return options;
}
} );
installed = true;
}
}
} );
return plugin;
$.enhance._installWidget();
return $.enhance;
} );
@@ -0,0 +1,63 @@
/*!
* jQuery Mobile Enhancer @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Enhancer Widget Crawler
//>>group: Widgets
//>>description: Adds support for custom initSlectors on widget prototypes
//>>docs: http://api.jquerymobile.com/enhancer/
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [
"jquery",
"../core",
"widgets/enhancer" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
var widgetCrawler = function( elements, _childConstructors ) {
$.each( _childConstructors, function( index, constructor ) {
var prototype = constructor.prototype,
plugin = $.enhance,
selector = plugin.initGenerator( prototype ),
found;
if( !selector ) {
return;
}
found = elements.find( selector );
if ( plugin._filter ) {
found = plugin._filter( found );
}
found[ prototype.widgetName ]();
if ( constructor._childConstructors && constructor._childConstructors.length > 0 ) {
widgetCrawler( elements, constructor._childConstructors );
}
} );
},
widgetHook = function() {
if ( !$.enhance.initGenerator || !$.Widget ) {
return;
}
// Enhance widgets with custom initSelectors
widgetCrawler( this.addBack(), $.Widget._childConstructors );
};
return $.enhance.hooks.push( widgetHook );
} );
View
@@ -24,7 +24,8 @@
"../widget",
"../core",
"widgets/enhancer",
"widgets/enhancer.backcompat" ], factory );
"widgets/enhancer.backcompat",
"widgets/enhancer.widgetCrawler" ], factory );
} else {
// Browser globals
Oops, something went wrong.

0 comments on commit bc68371

Please sign in to comment.