├── .gitignore ├── .travis.yml ├── package.json ├── .editorconfig ├── .jshintrc ├── demos ├── index-widget-factory.requirejs.html ├── index-extend-skeleton.html ├── index-basic-plugin.html ├── index-namespace.html ├── index-highly-configurable.html ├── index-best-options.html ├── index-custom-events.html ├── index-prototypal-inheritance.html ├── index-widget-factory-mobile.html ├── index-widget-factory-plugin-boilerplate.html └── index-widget-factory-bridge.html ├── Gruntfile.js ├── scripts └── main.js ├── patterns ├── coffee │ ├── jquery.extend-skeleton.coffee │ ├── jquery.best.options.plugin-boilerplate.coffee │ ├── jquery.namespace.plugin-boilerplate.coffee │ ├── jquery.customevents.plugin-boilerplate.coffee │ ├── jquery.prototypal-inheritance.plugin-boilerplate.coffee │ ├── jquery.highly-configurable.plugin.boilerplate.coffee │ ├── jquery.basic.plugin-boilerplate.coffee │ ├── jquery.widget-factory.plugin-boilerplate.coffee │ ├── jquery.widget-factory.bridge.boilerplate.coffee │ ├── jquery.widget-factory.requirejs.boilerplate.coffee │ └── jquery.widget-factory.mobile-plugin.boilerplate.coffee ├── jquery.best.options.plugin-boilerplate.js ├── jquery.extend-skeleton.js ├── amd+commonjs │ ├── usage.html │ ├── pluginExtension.js │ └── pluginCore.js ├── jquery.customevents.plugin-boilerplate.js ├── jquery.namespace.plugin-boilerplate.js ├── jquery.prototypal-inheritance.plugin-boilerplate.js ├── jquery.highly-configurable.plugin.boilerplate.js ├── jquery.basic.plugin-boilerplate.js ├── jquery.widget-factory.bridge.boilerplate.js ├── jquery.widget-factory.plugin-boilerplate.js ├── jquery.widget-factory.requirejs.boilerplate.js └── jquery.widget-factory.mobile-plugin.boilerplate.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.8 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "grunt": "~0.4.1", 5 | "grunt-cli": "~0.1.7", 6 | "grunt-contrib-jshint": "~0.4.3" 7 | }, 8 | "scripts": { 9 | "test": "grunt travis --verbose" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = tab 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "boss": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "eqnull": true, 6 | "expr": true, 7 | "immed": true, 8 | "noarg": true, 9 | "onevar": true, 10 | "quotmark": "double", 11 | "smarttabs": true, 12 | "trailing": true, 13 | "unused": true, 14 | "node": true 15 | } 16 | -------------------------------------------------------------------------------- /demos/index-widget-factory.requirejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |'+msg+'
'); 38 | 39 | # Object.create support test, and fallback for browsers without it 40 | if typeof Object.create isnt "function" 41 | Object.create = (o) -> 42 | F = -> 43 | F:: = o 44 | new F() 45 | 46 | # Create a plugin based on a defined object 47 | $.plugin = (name, object) -> 48 | $.fn[name] = (options) -> 49 | @each -> 50 | $.data @, name, Object.create(object).init(options, @) unless $.data(@, name) 51 | return 52 | 53 | 54 | # Usage: 55 | # With myObject, we could now essentially do this: 56 | # $.plugin('myobj', myObject); 57 | 58 | # and at this point we could do the following 59 | # $('#elem').myobj({name: "John"}); 60 | # var inst = $('#elem').data('myobj'); 61 | # inst.myMethod('I am a method'); 62 | -------------------------------------------------------------------------------- /patterns/coffee/jquery.highly-configurable.plugin.boilerplate.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | * 'Highly configurable' mutable plugin boilerplate 3 | * Author: @markdalgleish 4 | * Further changes, comments: @addyosmani 5 | * Ported to CoffeeScript: @yckart 6 | * Licensed under the MIT license 7 | ### 8 | 9 | # Note that with this pattern, as per Alex Sexton's, the plugin logic 10 | # hasn't been nested in a jQuery plugin. Instead, we just use 11 | # jQuery for its instantiation. 12 | (($, window, document) -> 13 | 14 | # our plugin constructor 15 | Plugin = (elem, options) -> 16 | @elem = elem 17 | @$elem = $(elem) 18 | @options = options 19 | 20 | # This next line takes advantage of HTML5 data attributes 21 | # to support customization of the plugin on a per-element 22 | # basis. For example, 23 | # 24 | @metadata = @$elem.data("plugin-options") 25 | return 26 | 27 | 28 | # the plugin prototype 29 | Plugin:: = 30 | defaults: 31 | message: "Hello world!" 32 | 33 | init: -> 34 | # Introduce defaults that can be extended either 35 | # globally or using an object literal. 36 | @config = $.extend({}, @defaults, @options, @metadata) 37 | 38 | # Sample usage: 39 | # Set the message per instance: 40 | # $('#elem').plugin({ message: 'Goodbye World!'}); 41 | # or 42 | # var p = new Plugin(document.getElementById('elem'), 43 | # { message: 'Goodbye World!'}).init() 44 | # or, set the global default message: 45 | # Plugin.defaults.message = 'Goodbye World!' 46 | @sampleMethod() 47 | @ 48 | 49 | sampleMethod: -> 50 | # eg. show the currently configured message 51 | # console.log(this.config.message); 52 | 53 | 54 | Plugin.defaults = Plugin::defaults 55 | $.fn.plugin = (options) -> 56 | @each -> 57 | new Plugin(@, options).init() 58 | return 59 | return 60 | 61 | #optional: window.Plugin = Plugin; 62 | 63 | ) jQuery, window, document 64 | -------------------------------------------------------------------------------- /patterns/jquery.prototypal-inheritance.plugin-boilerplate.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery prototypal inheritance plugin boilerplate 3 | * Author: Alex Sexton, Scott Gonzalez 4 | * Further changes: @addyosmani 5 | * Licensed under the MIT license 6 | */ 7 | 8 | // myObject - an object representing a concept that you want 9 | // to model (e.g. a car) 10 | var myObject = { 11 | init: function( options, elem ) { 12 | // Mix in the passed-in options with the default options 13 | this.options = $.extend( {}, this.options, options ); 14 | 15 | // Save the element reference, both as a jQuery 16 | // reference and a normal reference 17 | this.elem = elem; 18 | this.$elem = $(elem); 19 | 20 | // Build the DOM's initial structure 21 | this._build(); 22 | 23 | // return this so that we can chain and use the bridge with less code. 24 | return this; 25 | }, 26 | options: { 27 | name: "No name" 28 | }, 29 | _build: function(){ 30 | //this.$elem.html(''+msg+'
'); 37 | } 38 | }; 39 | 40 | // Object.create support test, and fallback for browsers without it 41 | if ( typeof Object.create !== "function" ) { 42 | Object.create = function (o) { 43 | function F() {} 44 | F.prototype = o; 45 | return new F(); 46 | }; 47 | } 48 | 49 | // Create a plugin based on a defined object 50 | $.plugin = function( name, object ) { 51 | $.fn[name] = function( options ) { 52 | return this.each(function() { 53 | if ( ! $.data( this, name ) ) { 54 | $.data( this, name, Object.create(object).init( 55 | options, this ) ); 56 | } 57 | }); 58 | }; 59 | }; 60 | 61 | // Usage: 62 | // With myObject, we could now essentially do this: 63 | // $.plugin('myobj', myObject); 64 | 65 | // and at this point we could do the following 66 | // $('#elem').myobj({name: "John"}); 67 | // var inst = $('#elem').data('myobj'); 68 | // inst.myMethod('I am a method'); 69 | -------------------------------------------------------------------------------- /patterns/coffee/jquery.basic.plugin-boilerplate.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | * jQuery lightweight plugin boilerplate 3 | * Original author: @ajpiano 4 | * Further changes, comments: @addyosmani 5 | * Forked to CoffeeScript: @yckart 6 | ### 7 | 8 | (($, window, document) -> 9 | # window and document are passed through as local 10 | # variables rather than as globals, because this (slightly) 11 | # quickens the resolution process and can be more 12 | # efficiently minified (especially when both are 13 | # regularly referenced in your plugin). 14 | 15 | # The actual plugin constructor 16 | Plugin = (element, options) -> 17 | @element = element 18 | 19 | # jQuery has an extend method that merges the 20 | # contents of two or more objects, storing the 21 | # result in the first object. The first object 22 | # is generally empty because we don't want to alter 23 | # the default options for future instances of the plugin 24 | @options = $.extend({}, defaults, options) 25 | @_defaults = defaults 26 | @_name = pluginName 27 | @init() 28 | 29 | # Create the defaults once 30 | pluginName = "defaultPluginName" 31 | defaults = 32 | propertyName: "value" 33 | popperttyName: "value" 34 | 35 | Plugin:: = 36 | init: -> 37 | # Place initialization logic here 38 | # You already have access to the DOM element and 39 | # the options via the instance, e.g. this.element 40 | # and this.options 41 | # you can add more functions like the one below and 42 | # call them like so: this.yourOtherFunction(this.element, this.options). 43 | yourOtherFunction: (el, options) -> 44 | # some logic 45 | 46 | # A really lightweight plugin wrapper around the constructor, 47 | # preventing against multiple instantiations 48 | $.fn[pluginName] = (options) -> 49 | @each -> 50 | $.data @, "plugin_" + pluginName, new Plugin(@, options) unless $.data(@, "plugin_" + pluginName) 51 | 52 | # Prevents CoffeeScript to return a value from plugin wrapper. 53 | return 54 | ) jQuery, window, document 55 | -------------------------------------------------------------------------------- /patterns/jquery.highly-configurable.plugin.boilerplate.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 'Highly configurable' mutable plugin boilerplate 3 | * Author: @markdalgleish 4 | * Further changes, comments: @addyosmani 5 | * Licensed under the MIT license 6 | */ 7 | 8 | // Note that with this pattern, as per Alex Sexton's, the plugin logic 9 | // hasn't been nested in a jQuery plugin. Instead, we just use 10 | // jQuery for its instantiation. 11 | 12 | ;(function( $, window, document, undefined ){ 13 | 14 | // our plugin constructor 15 | var Plugin = function( elem, options ){ 16 | this.elem = elem; 17 | this.$elem = $(elem); 18 | this.options = options; 19 | 20 | // This next line takes advantage of HTML5 data attributes 21 | // to support customization of the plugin on a per-element 22 | // basis. For example, 23 | // 24 | this.metadata = this.$elem.data( "plugin-options" ); 25 | }; 26 | 27 | // the plugin prototype 28 | Plugin.prototype = { 29 | defaults: { 30 | message: "Hello world!" 31 | }, 32 | 33 | init: function() { 34 | // Introduce defaults that can be extended either 35 | // globally or using an object literal. 36 | this.config = $.extend({}, this.defaults, this.options, 37 | this.metadata); 38 | 39 | // Sample usage: 40 | // Set the message per instance: 41 | // $('#elem').plugin({ message: 'Goodbye World!'}); 42 | // or 43 | // var p = new Plugin(document.getElementById('elem'), 44 | // { message: 'Goodbye World!'}).init() 45 | // or, set the global default message: 46 | // Plugin.defaults.message = 'Goodbye World!' 47 | 48 | this.sampleMethod(); 49 | return this; 50 | }, 51 | 52 | sampleMethod: function() { 53 | // eg. show the currently configured message 54 | // console.log(this.config.message); 55 | } 56 | } 57 | 58 | Plugin.defaults = Plugin.prototype.defaults; 59 | 60 | $.fn.plugin = function(options) { 61 | return this.each(function() { 62 | new Plugin(this, options).init(); 63 | }); 64 | }; 65 | 66 | //optional: window.Plugin = Plugin; 67 | 68 | })( jQuery, window , document ); 69 | -------------------------------------------------------------------------------- /patterns/coffee/jquery.widget-factory.plugin-boilerplate.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | * jQuery UI Widget-factory plugin boilerplate (for 1.8/9+) 3 | * Author: @addyosmani 4 | * Further changes: @peolanha 5 | * Ported to CoffeeScript: @yckart 6 | * Licensed under the MIT license 7 | ### 8 | (($, window, document) -> 9 | 10 | # define your widget under a namespace of your choice 11 | # with additional parameters e.g. 12 | # $.widget( "namespace.widgetname", (optional) - an 13 | # existing widget prototype to inherit from, an object 14 | # literal to become the widget's prototype ); 15 | 16 | $.widget "namespace.widgetName", 17 | 18 | #Options to be used as defaults 19 | options: 20 | someValue: null 21 | 22 | 23 | #Setup widget (eg. element creation, apply theming 24 | # , bind events etc.) 25 | _create: -> 26 | # _create will automatically run the first time 27 | # this widget is called. Put the initial widget 28 | # setup code here, then you can access the element 29 | # on which the widget was called via this.element. 30 | # The options defined above can be accessed 31 | # via this.options this.element.addStuff(); 32 | 33 | # Destroy an instantiated plugin and clean up 34 | # modifications the widget has made to the DOM 35 | destroy: -> 36 | 37 | # this.element.removeStuff(); 38 | # For UI 1.8, destroy must be invoked from the 39 | # base widget 40 | $.Widget::destroy.call @ 41 | # For UI 1.9, define _destroy instead and don't 42 | # worry about 43 | # calling the base widget 44 | 45 | methodB: (event) -> 46 | #_trigger dispatches callbacks the plugin user 47 | # can subscribe to 48 | # signature: _trigger( "callbackName" , [eventObject], 49 | # [uiObject] ) 50 | # eg. this._trigger( "hover", e /*where e.type == 51 | # "mouseenter"*/, { hovered: $(e.target)}); 52 | console.log "methodB called" 53 | 54 | methodA: (event) -> 55 | @_trigger "dataChanged", event, 56 | key: "someValue" 57 | return 58 | 59 | 60 | # Respond to any changes the user makes to the 61 | # option method 62 | _setOption: (key, value) -> 63 | switch key 64 | when "someValue" 65 | #this.options.someValue = doSomethingWith( value ); 66 | else 67 | #this.options[ key ] = value; 68 | break 69 | 70 | 71 | # For UI 1.8, _setOption must be manually invoked 72 | # from the base widget 73 | $.Widget::_setOption.apply @, arguments_ 74 | # For UI 1.9 the _super method can be used instead 75 | # this._super( "_setOption", key, value ); 76 | return 77 | return 78 | ) jQuery, window, document 79 | -------------------------------------------------------------------------------- /patterns/coffee/jquery.widget-factory.bridge.boilerplate.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | * jQuery UI Widget factory "bridge" plugin boilerplate 3 | * Author: @erichynds 4 | * Further changes, additional comments: @addyosmani 5 | * Ported to CoffeeScript: @yckart 6 | * Licensed under the MIT license 7 | ### 8 | 9 | # a "widgetName" object constructor 10 | # required: this must accept two arguments, 11 | # options: an object of configuration options 12 | # element: the DOM element the instance was created on 13 | widgetName = (options, element) -> 14 | @name = "myWidgetName" 15 | @options = options 16 | @element = element 17 | @_init() 18 | return 19 | 20 | 21 | # the "widgetName" prototype 22 | widgetName:: = 23 | 24 | # _create will automatically run the first time this 25 | # widget is called 26 | _create: -> 27 | # creation code 28 | 29 | # required: initialization logic for the plugin goes into _init 30 | # This fires when your instance is first created and when 31 | # attempting to initialize the widget again (by the bridge) 32 | # after it has already been initialized. 33 | _init: -> 34 | # init code 35 | 36 | # required: objects to be used with the bridge must contain an 37 | # 'option'. Post-initialization, the logic for changing options 38 | # goes here. 39 | option: (key, value) -> 40 | 41 | # optional: get/change options post initialization 42 | # ignore if you don't require them. 43 | 44 | # signature: $('#foo').bar({ cool:false }); 45 | if $.isPlainObject(key) 46 | @options = $.extend(true, @options, key) 47 | 48 | # signature: $('#foo').option('cool'); - getter 49 | else if key and typeof value is "undefined" 50 | return @options[key] 51 | 52 | # signature: $('#foo').bar('option', 'baz', false); 53 | else 54 | @options[key] = value 55 | 56 | # required: option must return the current instance. 57 | # When re-initializing an instance on elements, option 58 | # is called first and is then chained to the _init method. 59 | @ 60 | 61 | 62 | # notice no underscore is used for public methods 63 | publicFunction: -> 64 | console.log "public function" 65 | 66 | 67 | # underscores are used for private methods 68 | _privateFunction: -> 69 | console.log "private function" 70 | 71 | # usage: 72 | 73 | # connect the widget obj to jQuery's API under the "foo" namespace 74 | # $.widget.bridge("foo", widgetName); 75 | 76 | # create an instance of the widget for use 77 | # var instance = $("#elem").foo({ 78 | # baz: true 79 | # }); 80 | 81 | # your widget instance exists in the elem's data 82 | # instance.data("foo").element; // => #elem element 83 | 84 | # bridge allows you to call public methods... 85 | # instance.foo("publicFunction"); // => "public method" 86 | 87 | # bridge prevents calls to internal methods 88 | # instance.foo("_privateFunction"); // => #elem element 89 | -------------------------------------------------------------------------------- /patterns/jquery.basic.plugin-boilerplate.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery lightweight plugin boilerplate 3 | * Original author: @ajpiano 4 | * Further changes, comments: @addyosmani 5 | * Licensed under the MIT license 6 | */ 7 | 8 | // the semi-colon before the function invocation is a safety 9 | // net against concatenated scripts and/or other plugins 10 | // that are not closed properly. 11 | ;(function ( $, window, document, undefined ) { 12 | 13 | // undefined is used here as the undefined global 14 | // variable in ECMAScript 3 and is mutable (i.e. it can 15 | // be changed by someone else). undefined isn't really 16 | // being passed in so we can ensure that its value is 17 | // truly undefined. In ES5, undefined can no longer be 18 | // modified. 19 | 20 | // window and document are passed through as local 21 | // variables rather than as globals, because this (slightly) 22 | // quickens the resolution process and can be more 23 | // efficiently minified (especially when both are 24 | // regularly referenced in your plugin). 25 | 26 | // Create the defaults once 27 | var pluginName = "defaultPluginName", 28 | defaults = { 29 | propertyName: "value" 30 | }; 31 | 32 | // The actual plugin constructor 33 | function Plugin( element, options ) { 34 | this.element = element; 35 | 36 | // jQuery has an extend method that merges the 37 | // contents of two or more objects, storing the 38 | // result in the first object. The first object 39 | // is generally empty because we don't want to alter 40 | // the default options for future instances of the plugin 41 | this.options = $.extend( {}, defaults, options) ; 42 | 43 | this._defaults = defaults; 44 | this._name = pluginName; 45 | 46 | this.init(); 47 | } 48 | 49 | Plugin.prototype = { 50 | 51 | init: function() { 52 | // Place initialization logic here 53 | // You already have access to the DOM element and 54 | // the options via the instance, e.g. this.element 55 | // and this.options 56 | // you can add more functions like the one below and 57 | // call them like so: this.yourOtherFunction(this.element, this.options). 58 | }, 59 | 60 | yourOtherFunction: function(el, options) { 61 | // some logic 62 | } 63 | }; 64 | 65 | // A really lightweight plugin wrapper around the constructor, 66 | // preventing against multiple instantiations 67 | $.fn[pluginName] = function ( options ) { 68 | return this.each(function () { 69 | if (!$.data(this, "plugin_" + pluginName)) { 70 | $.data(this, "plugin_" + pluginName, 71 | new Plugin( this, options )); 72 | } 73 | }); 74 | }; 75 | 76 | })( jQuery, window, document ); 77 | -------------------------------------------------------------------------------- /patterns/jquery.widget-factory.bridge.boilerplate.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Widget factory "bridge" plugin boilerplate 3 | * Author: @erichynds 4 | * Further changes, additional comments: @addyosmani 5 | * Licensed under the MIT license 6 | */ 7 | 8 | // a "widgetName" object constructor 9 | // required: this must accept two arguments, 10 | // options: an object of configuration options 11 | // element: the DOM element the instance was created on 12 | var widgetName = function( options, element ){ 13 | this.name = "myWidgetName"; 14 | this.options = options; 15 | this.element = element; 16 | this._init(); 17 | } 18 | 19 | // the "widgetName" prototype 20 | widgetName.prototype = { 21 | 22 | // _create will automatically run the first time this 23 | // widget is called 24 | _create: function(){ 25 | // creation code 26 | }, 27 | 28 | // required: initialization logic for the plugin goes into _init 29 | // This fires when your instance is first created and when 30 | // attempting to initialize the widget again (by the bridge) 31 | // after it has already been initialized. 32 | _init: function(){ 33 | // init code 34 | }, 35 | 36 | // required: objects to be used with the bridge must contain an 37 | // 'option'. Post-initialization, the logic for changing options 38 | // goes here. 39 | option: function( key, value ){ 40 | 41 | // optional: get/change options post initialization 42 | // ignore if you don't require them. 43 | 44 | // signature: $('#foo').bar({ cool:false }); 45 | if( $.isPlainObject( key ) ){ 46 | this.options = $.extend( true, this.options, key ); 47 | 48 | // signature: $('#foo').option('cool'); - getter 49 | } else if ( key && typeof value === "undefined" ){ 50 | return this.options[ key ]; 51 | 52 | // signature: $('#foo').bar('option', 'baz', false); 53 | } else { 54 | this.options[ key ] = value; 55 | } 56 | 57 | // required: option must return the current instance. 58 | // When re-initializing an instance on elements, option 59 | // is called first and is then chained to the _init method. 60 | return this; 61 | }, 62 | 63 | // notice no underscore is used for public methods 64 | publicFunction: function(){ 65 | console.log("public function"); 66 | }, 67 | 68 | // underscores are used for private methods 69 | _privateFunction: function(){ 70 | console.log("private function"); 71 | } 72 | }; 73 | 74 | // usage: 75 | 76 | // connect the widget obj to jQuery's API under the "foo" namespace 77 | // $.widget.bridge("foo", widgetName); 78 | 79 | // create an instance of the widget for use 80 | // var instance = $("#elem").foo({ 81 | // baz: true 82 | // }); 83 | 84 | // your widget instance exists in the elem's data 85 | // instance.data("foo").element; // => #elem element 86 | 87 | // bridge allows you to call public methods... 88 | // instance.foo("publicFunction"); // => "public method" 89 | 90 | // bridge prevents calls to internal methods 91 | // instance.foo("_privateFunction"); // => #elem element 92 | -------------------------------------------------------------------------------- /patterns/jquery.widget-factory.plugin-boilerplate.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Widget-factory plugin boilerplate (for 1.8/9+) 3 | * Author: @addyosmani 4 | * Further changes: @peolanha 5 | * Licensed under the MIT license 6 | */ 7 | 8 | ;(function ( $, window, document, undefined ) { 9 | 10 | // define your widget under a namespace of your choice 11 | // with additional parameters e.g. 12 | // $.widget( "namespace.widgetname", (optional) - an 13 | // existing widget prototype to inherit from, an object 14 | // literal to become the widget's prototype ); 15 | 16 | $.widget( "namespace.widgetName" , { 17 | 18 | //Options to be used as defaults 19 | options: { 20 | someValue: null 21 | }, 22 | 23 | //Setup widget (eg. element creation, apply theming 24 | // , bind events etc.) 25 | _create: function () { 26 | 27 | // _create will automatically run the first time 28 | // this widget is called. Put the initial widget 29 | // setup code here, then you can access the element 30 | // on which the widget was called via this.element. 31 | // The options defined above can be accessed 32 | // via this.options this.element.addStuff(); 33 | }, 34 | 35 | // Destroy an instantiated plugin and clean up 36 | // modifications the widget has made to the DOM 37 | destroy: function () { 38 | 39 | // this.element.removeStuff(); 40 | // For UI 1.8, destroy must be invoked from the 41 | // base widget 42 | $.Widget.prototype.destroy.call(this); 43 | // For UI 1.9, define _destroy instead and don't 44 | // worry about 45 | // calling the base widget 46 | }, 47 | 48 | methodB: function ( event ) { 49 | //_trigger dispatches callbacks the plugin user 50 | // can subscribe to 51 | // signature: _trigger( "callbackName" , [eventObject], 52 | // [uiObject] ) 53 | // eg. this._trigger( "hover", e /*where e.type == 54 | // "mouseenter"*/, { hovered: $(e.target)}); 55 | console.log("methodB called"); 56 | }, 57 | 58 | methodA: function ( event ) { 59 | this._trigger("dataChanged", event, { 60 | key: "someValue" 61 | }); 62 | }, 63 | 64 | // Respond to any changes the user makes to the 65 | // option method 66 | _setOption: function ( key, value ) { 67 | switch (key) { 68 | case "someValue": 69 | //this.options.someValue = doSomethingWith( value ); 70 | break; 71 | default: 72 | //this.options[ key ] = value; 73 | break; 74 | } 75 | 76 | // For UI 1.8, _setOption must be manually invoked 77 | // from the base widget 78 | $.Widget.prototype._setOption.apply( this, arguments ); 79 | // For UI 1.9 the _super method can be used instead 80 | // this._super( "_setOption", key, value ); 81 | } 82 | }); 83 | 84 | })( jQuery, window, document ); 85 | -------------------------------------------------------------------------------- /patterns/coffee/jquery.widget-factory.requirejs.boilerplate.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | * jQuery UI Widget + RequireJS module boilerplate (for 1.8/9+) 3 | * Authors: @jrburke, @addyosmani 4 | * Ported to CoffeeScript: @yckart 5 | * Licensed under the MIT license 6 | ### 7 | 8 | # Note from James: 9 | # 10 | # This assumes you are using the RequireJS+jQuery file, and 11 | # that the following files are all in the same directory: 12 | # 13 | # - require-jquery.js 14 | # - jquery-ui.custom.min.js (custom jQuery UI build with widget factory) 15 | # - templates/ 16 | # - asset.html 17 | # - ao.myWidget.js 18 | 19 | # Then you can construct the widget like so: 20 | 21 | #ao.myWidget.js file: 22 | #Uncomment this version for a sample using templates 23 | #define("ao.myWidget", ["jquery", "text!templates/asset.html", "jquery-ui.custom.min","jquery.tmpl"], function ($, assetHtml) { 24 | define "ao.myWidget", ["jquery", "jqueryui"], ($) -> 25 | 26 | # define your widget under a namespace of your choice 27 | # 'ao' is used here as a demonstration 28 | $.widget "ao.myWidget", 29 | 30 | # Options to be used as defaults 31 | options: {} 32 | 33 | # Set up widget (e.g. create element, apply theming, 34 | # bind events, etc.) 35 | _create: -> 36 | 37 | # _create will automatically run the first time 38 | # this widget is called. Put the initial widget 39 | # set-up code here, then you can access the element 40 | # on which the widget was called via this.element. 41 | # The options defined above can be accessed via 42 | # this.options 43 | 44 | #this.element.addStuff(); 45 | #this.element.addStuff(); 46 | #this.element.tmpl(assetHtml).appendTo(this.content); 47 | 48 | # Destroy an instantiated plugin and clean up modifications 49 | # that the widget has made to the DOM 50 | destroy: -> 51 | #t his.element.removeStuff(); 52 | # For UI 1.8, destroy must be invoked from the base 53 | # widget 54 | $.Widget::destroy.call @ 55 | # For UI 1.9, define _destroy instead and don't worry 56 | # about calling the base widget 57 | return 58 | 59 | methodB: (event) -> 60 | # _trigger dispatches callbacks the plugin user can 61 | # subscribe to 62 | #signature: _trigger( "callbackName" , [eventObject], 63 | # [uiObject] ) 64 | console.log "methodB called" 65 | 66 | methodA: (event) -> 67 | @_trigger "dataChanged", event, 68 | key: "someValue" 69 | 70 | 71 | 72 | #Respond to any changes the user makes to the option method 73 | _setOption: (key, value) -> 74 | switch key 75 | when "someValue" 76 | #this.options.someValue = doSomethingWith( value ); 77 | else 78 | #this.options[ key ] = value; 79 | break 80 | 81 | # For UI 1.8, _setOption must be manually invoked from 82 | # the base widget 83 | $.Widget::_setOption.apply @, arguments_ 84 | # For UI 1.9 the _super method can be used instead 85 | #this._super( "_setOption", key, value ); 86 | 87 | #somewhere assetHtml would be used for templating, depending 88 | # on your choice. 89 | 90 | return 91 | 92 | 93 | # If you are going to use the RequireJS optimizer to combine files 94 | # together, you can leave off the "ao.myWidget" argument to define: 95 | # define(["jquery", "text!templates/asset.html", "jquery-ui.custom.min"], … 96 | -------------------------------------------------------------------------------- /patterns/jquery.widget-factory.requirejs.boilerplate.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Widget + RequireJS module boilerplate (for 1.8/9+) 3 | * Authors: @jrburke, @addyosmani 4 | * Licensed under the MIT license 5 | */ 6 | 7 | // Note from James: 8 | // 9 | // This assumes you are using the RequireJS+jQuery file, and 10 | // that the following files are all in the same directory: 11 | // 12 | // - require-jquery.js 13 | // - jquery-ui.custom.min.js (custom jQuery UI build with widget factory) 14 | // - templates/ 15 | // - asset.html 16 | // - ao.myWidget.js 17 | 18 | // Then you can construct the widget like so: 19 | 20 | // ao.myWidget.js file: 21 | // Uncomment this version for a sample using templates 22 | // define(["jquery", "text!templates/asset.html", "jquery-ui.custom.min","jquery.tmpl"], function ($, assetHtml) { 23 | define(["jquery", "jqueryui"], function ($) { 24 | 25 | // define your widget under a namespace of your choice 26 | // 'ao' is used here as a demonstration 27 | $.widget( "ao.myWidget", { 28 | 29 | // Options to be used as defaults 30 | options: {}, 31 | 32 | // Set up widget (e.g. create element, apply theming, 33 | // bind events, etc.) 34 | _create: function () { 35 | 36 | // _create will automatically run the first time 37 | // this widget is called. Put the initial widget 38 | // set-up code here, then you can access the element 39 | // on which the widget was called via this.element. 40 | // The options defined above can be accessed via 41 | // this.options 42 | 43 | //this.element.addStuff(); 44 | //this.element.addStuff(); 45 | //this.element.tmpl(assetHtml).appendTo(this.content); 46 | }, 47 | 48 | // Destroy an instantiated plugin and clean up modifications 49 | // that the widget has made to the DOM 50 | destroy: function () { 51 | //t his.element.removeStuff(); 52 | // For UI 1.8, destroy must be invoked from the base 53 | // widget 54 | $.Widget.prototype.destroy.call( this ); 55 | // For UI 1.9, define _destroy instead and don't worry 56 | // about calling the base widget 57 | }, 58 | 59 | methodB: function ( event ) { 60 | // _trigger dispatches callbacks the plugin user can 61 | // subscribe to 62 | //signature: _trigger( "callbackName" , [eventObject], 63 | // [uiObject] ) 64 | console.log("methodB called"); 65 | }, 66 | 67 | methodA: function ( event ) { 68 | this._trigger("dataChanged", event, { 69 | key: "someValue" 70 | }); 71 | }, 72 | 73 | //Respond to any changes the user makes to the option method 74 | _setOption: function ( key, value ) { 75 | switch (key) { 76 | case "someValue": 77 | //this.options.someValue = doSomethingWith( value ); 78 | break; 79 | default: 80 | //this.options[ key ] = value; 81 | break; 82 | } 83 | 84 | // For UI 1.8, _setOption must be manually invoked from 85 | // the base widget 86 | $.Widget.prototype._setOption.apply( this, arguments ); 87 | // For UI 1.9 the _super method can be used instead 88 | //this._super( "_setOption", key, value ); 89 | } 90 | 91 | //somewhere assetHtml would be used for templating, depending 92 | // on your choice. 93 | }); 94 | }); 95 | 96 | // If you are going to use the RequireJS optimizer to combine files 97 | // together, you can leave off the "ao.myWidget" argument to define: 98 | // define(["jquery", "text!templates/asset.html", "jquery-ui.custom.min"], … 99 | -------------------------------------------------------------------------------- /patterns/coffee/jquery.widget-factory.mobile-plugin.boilerplate.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | * (jQuery mobile) jQuery UI Widget-factory plugin boilerplate (for 1.8/9+) 3 | * Author: @scottjehl 4 | * Further changes: @addyosmani 5 | * Ported to CoffeeScript: @yckart 6 | * Licensed under the MIT license 7 | ### 8 | (($, window, document) -> 9 | 10 | #define a widget under a namespace of your choice 11 | #here 'mobile' has been used in the first parameter 12 | $.widget "mobile.widgetName", $.mobile.widget, 13 | 14 | #Options to be used as defaults 15 | options: 16 | foo: true 17 | bar: false 18 | 19 | _create: -> 20 | # _create will automatically run the first time this 21 | # widget is called. Put the initial widget set-up code 22 | # here, then you can access the element on which 23 | # the widget was called via this.element 24 | # The options defined above can be accessed via 25 | # this.options 26 | 27 | #var m = this.element, 28 | #p = m.parents(":jqmData(role='page')"), 29 | #c = p.find(":jqmData(role='content')") 30 | 31 | # Private methods/props start with underscores 32 | _dosomething: -> 33 | 34 | # Public methods like these below can can be called 35 | # externally: 36 | # $("#myelem").foo( "enable", arguments ); 37 | 38 | enable: -> 39 | 40 | # Destroy an instantiated plugin and clean up modifications 41 | # the widget has made to the DOM 42 | destroy: -> 43 | #this.element.removeStuff(); 44 | # For UI 1.8, destroy must be invoked from the 45 | # base widget 46 | $.Widget::destroy.call @ 47 | return 48 | 49 | 50 | # For UI 1.9, define _destroy instead and don't 51 | # worry about calling the base widget 52 | methodB: (event) -> 53 | #_trigger dispatches callbacks the plugin user can 54 | # subscribe to 55 | #signature: _trigger( "callbackName" , [eventObject], 56 | # [uiObject] ) 57 | # eg. this._trigger( "hover", e /*where e.type == 58 | # "mouseenter"*/, { hovered: $(e.target)}); 59 | console.log "method B called" 60 | #this.methodA(); 61 | 62 | 63 | methodA: (event) -> 64 | @_trigger "dataChanged", event, 65 | key: "someValue" 66 | 67 | 68 | 69 | #Respond to any changes the user makes to the option method 70 | _setOption: (key, value) -> 71 | switch key 72 | when "someValue" 73 | # this is all optional 74 | @options.someValue = doSomethingWith(value) 75 | else 76 | # optional 77 | @options[key] = value 78 | break 79 | 80 | # For UI 1.8, _setOption must be manually invoked from 81 | # the base widget 82 | $.Widget::_setOption.apply @, arguments 83 | return 84 | # For UI 1.9 the _super method can be used instead 85 | # this._super( "_setOption", key, value ); 86 | return 87 | ) jQuery, window, document 88 | 89 | #usage: $("#myelem").foo( options ); 90 | 91 | # Some additional notes - delete this section before using the boilerplate. 92 | # 93 | # We can also self-init this widget whenever a new page in jQuery Mobile is created. jQuery Mobile's "page" plugin dispatches a "create" event when a jQuery Mobile page (found via data-role=page attr) is first initialized. 94 | # 95 | #We can listen for that event (called "pagecreate" ) and run our plugin automatically whenever a new page is created. 96 | # 97 | #$(document).bind("pagecreate", function (e) { 98 | # // In here, e.target refers to the page that was created 99 | # // (it's the target of the pagecreate event) 100 | # // So, we can simply find elements on this page that match a 101 | # // selector of our choosing, and call our plugin on them. 102 | # // Here's how we'd call our "foo" plugin on any element with a 103 | # // data-role attribute of "foo": 104 | # $(e.target).find("[data-role='foo']").foo(options); 105 | # 106 | # // Or, better yet, let's write the selector accounting for the configurable 107 | # // data-attribute namespace 108 | # $(e.target).find(":jqmData(role='foo')").foo(options); 109 | #}); 110 | # 111 | #That's it. Now you can simply reference the script containing your widget and pagecreate binding in a page running jQuery Mobile site, and it will automatically run like any other jQM plugin. 112 | -------------------------------------------------------------------------------- /patterns/jquery.widget-factory.mobile-plugin.boilerplate.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * (jQuery mobile) jQuery UI Widget-factory plugin boilerplate (for 1.8/9+) 3 | * Author: @scottjehl 4 | * Further changes: @addyosmani 5 | * Licensed under the MIT license 6 | */ 7 | 8 | ;(function ( $, window, document, undefined ) { 9 | 10 | //define a widget under a namespace of your choice 11 | //here 'mobile' has been used in the first parameter 12 | $.widget( "mobile.widgetName", $.mobile.widget, { 13 | 14 | //Options to be used as defaults 15 | options: { 16 | foo: true, 17 | bar: false 18 | }, 19 | 20 | _create: function() { 21 | // _create will automatically run the first time this 22 | // widget is called. Put the initial widget set-up code 23 | // here, then you can access the element on which 24 | // the widget was called via this.element 25 | // The options defined above can be accessed via 26 | // this.options 27 | 28 | //var m = this.element, 29 | //p = m.parents(":jqmData(role='page')"), 30 | //c = p.find(":jqmData(role='content')") 31 | }, 32 | 33 | // Private methods/props start with underscores 34 | _dosomething: function(){ }, 35 | 36 | // Public methods like these below can can be called 37 | // externally: 38 | // $("#myelem").foo( "enable", arguments ); 39 | 40 | enable: function() { }, 41 | 42 | // Destroy an instantiated plugin and clean up modifications 43 | // the widget has made to the DOM 44 | destroy: function () { 45 | //this.element.removeStuff(); 46 | // For UI 1.8, destroy must be invoked from the 47 | // base widget 48 | $.Widget.prototype.destroy.call(this); 49 | // For UI 1.9, define _destroy instead and don't 50 | // worry about calling the base widget 51 | }, 52 | 53 | methodB: function ( event ) { 54 | //_trigger dispatches callbacks the plugin user can 55 | // subscribe to 56 | //signature: _trigger( "callbackName" , [eventObject], 57 | // [uiObject] ) 58 | // eg. this._trigger( "hover", e /*where e.type == 59 | // "mouseenter"*/, { hovered: $(e.target)}); 60 | console.log("method B called"); 61 | //this.methodA(); 62 | }, 63 | 64 | methodA: function ( event ) { 65 | this._trigger("dataChanged", event, { 66 | key: "someValue" 67 | }); 68 | }, 69 | 70 | //Respond to any changes the user makes to the option method 71 | _setOption: function ( key, value ) { 72 | switch (key) { 73 | case "someValue": 74 | // this is all optional 75 | this.options.someValue = doSomethingWith( value ); 76 | break; 77 | default: 78 | // optional 79 | this.options[ key ] = value; 80 | break; 81 | } 82 | 83 | // For UI 1.8, _setOption must be manually invoked from 84 | // the base widget 85 | $.Widget.prototype._setOption.apply(this, arguments); 86 | // For UI 1.9 the _super method can be used instead 87 | // this._super( "_setOption", key, value ); 88 | } 89 | }); 90 | 91 | })( jQuery, window, document ); 92 | 93 | //usage: $("#myelem").foo( options ); 94 | 95 | /* Some additional notes - delete this section before using the boilerplate. 96 | 97 | We can also self-init this widget whenever a new page in jQuery Mobile is created. jQuery Mobile's "page" plugin dispatches a "create" event when a jQuery Mobile page (found via data-role=page attr) is first initialized. 98 | 99 | We can listen for that event (called "pagecreate" ) and run our plugin automatically whenever a new page is created. 100 | 101 | $(document).bind("pagecreate", function (e) { 102 | // In here, e.target refers to the page that was created 103 | // (it's the target of the pagecreate event) 104 | // So, we can simply find elements on this page that match a 105 | // selector of our choosing, and call our plugin on them. 106 | // Here's how we'd call our "foo" plugin on any element with a 107 | // data-role attribute of "foo": 108 | $(e.target).find("[data-role='foo']").foo(options); 109 | 110 | // Or, better yet, let's write the selector accounting for the configurable 111 | // data-attribute namespace 112 | $(e.target).find(":jqmData(role='foo')").foo(options); 113 | }); 114 | 115 | That's it. Now you can simply reference the script containing your widget and pagecreate binding in a page running jQuery Mobile site, and it will automatically run like any other jQM plugin. 116 | */ 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jQuery Plugin Patterns [](https://travis-ci.org/jquery-boilerplate/jquery-patterns) 2 | 3 | So, you've tried out [jQuery Boilerplate](https://github.com/jquery-boilerplate/jquery-boilerplate) or written a few of your own plugins before. They work to a degree and are readable, but you're interested in learning if there are better ways your plugin can be structured. 4 | 5 | Perhaps you're just looking for something that works better with the jQuery UI Widget factory, RequireJS or has built-in support for Publish/Subscribe. This repo hopes to provide a number of alternative boilerplates for kick-starting your jQuery plugin development catered towards the intermediate to advanced developer. 6 | 7 | This project won't seek to provide implementations for every possible pattern, but will attempt to cover popular patterns developers often use in the wild. 8 | 9 | ## Patterns 10 | 11 | - **[A lightweight start](https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.basic.plugin-boilerplate.js)**: perfect as a generic template for beginners 12 | and above, uses a basic defaults object, simple constructor for 13 | assigning the element to work with and extending options with 14 | defaults and a lightweight wrapper around the constructor to avoid 15 | issues with multiple instantiations 16 | - **[Widget factory](https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.widget-factory.plugin-boilerplate.js)**: for building complex, stateful plugins based on 17 | object-oriented principles. The majority of jQueryUI heavily relies 18 | on the widget factory as a base for components and this template 19 | covers almost all supported default methods including triggering 20 | events 21 | - **[Widget factory + RequireJS](https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.widget-factory.requirejs.boilerplate.js)**: for wrapping jQueryUI widgets inside 22 | RequireJS compatible modules. Also demonstrates very basic widget 23 | templating 24 | - **[Widget factory for jQuery mobile](https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.widget-factory.mobile-plugin.boilerplate.js)** - demonstrating best practices 25 | for building mobile widgets, includes many of the same concepts as 26 | the widget factory boilerplate, but also JQM specific usage 27 | advice/tips in the comments 28 | - **[Namespaced pattern](https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.namespace.plugin-boilerplate.js)**: to avoid collisions and improve code 29 | organization when working with components under another namespace 30 | - **[Best options](https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.best.options.plugin-boilerplate.js)**: globally/Per-call overridable options for 31 | greater option customization, based on Ben Almans [pluginization](http://benalman.com/talks/jquery-pluginization.html) talk 32 | - **[Custom events (Publish/Subscribe)](https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.customevents.plugin-boilerplate.js)**: for better application 33 | decoupling. Uses the Widget factory, but could be applied to the 34 | generic template 35 | - **[Extend pattern](https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.extend-skeleton.js)**: Extended options 36 | - **[Non Widget-factory widget](https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.simplewidget.plugin-boilerplate.js])**: if you wish to stay away from the 37 | widget factory. Uses Ben Alman’s simplewidget including coverage for 38 | creation, instantiation and other best practices that may be helpful 39 | - **[Prototypal inheritance pattern](https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.prototypal-inheritance.plugin-boilerplate.js)**: use a bridge to generate a 40 | plugin from an object (literal). Useful for code organization, 41 | readability, functionality heavily based around DOM element 42 | selection 43 | - **[Universal Module Definition pattern](https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/amd%2Bcommonjs/pluginCore.js)**: create AMD and CommonJS 44 | compatible plugin modules which are compatible with a number of 45 | different script loaders. You may also be interested in the [UMD](https://github.com/umdjs) project. 46 | 47 | 48 | ## Further reading 49 | 50 | More information about the patterns in this repo can be found in [Learning JavaScript Design Patterns](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#jquerypluginpatterns). 51 | 52 | ## Contributing 53 | 54 | If you have ideas for improvements that can be made to patterns currently in the repo, please feel free to create a new issue for discussion or send a pull request upstream. The same can be said about new patterns you wish to propose being added; for the sake of limiting confusion and complexity, I would ideally like to keep the number of overall patterns in the repo, but there are plans to separate these out into folders based on concerns. 55 | 56 | ## Team 57 | 58 | jQuery Patterns was made with love by these people and a bunch of awesome [contributors](https://github.com/jquery-boilerplate/jquery-patterns/graphs/contributors). 59 | 60 | [](http://addyosmani.com) | [](http://zenorocha.com) 61 | --- | --- | --- | --- | --- | --- | --- 62 | [Addy Osmani](http://addyosmani.com) | [Zeno Rocha](http://zenorocha.com) 63 | 64 | ## Credits 65 | 66 | Thanks to [@peol](http://github.com/peol), [@ajpiano](http://github.com/ajpiano), [@mathias](http://github.com/mathias), [@cowboy](http://github.com/cowboy), [@dougneiner](http://github.com/dougneiner) and others for their previous work (or tips) in this area. Some of this work is used as a basis for further improvements. 67 | 68 | --------------------------------------------------------------------------------