├── .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 | jQuery Plugin Patterns 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demos/index-extend-skeleton.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extend Skeleton 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demos/index-basic-plugin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Basic Plugin 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demos/index-namespace.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Namespace 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demos/index-highly-configurable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Highly Configurable 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.initConfig({ 4 | 5 | // Lint definitions 6 | jshint: { 7 | all: ["Gruntfile.js", "patterns/**/*.js", "scripts/**/*.js"], 8 | options: { 9 | jshintrc: ".jshintrc" 10 | } 11 | } 12 | 13 | }); 14 | 15 | grunt.loadNpmTasks("grunt-contrib-jshint"); 16 | 17 | grunt.registerTask("default", ["jshint"]); 18 | grunt.registerTask("travis", ["jshint"]); 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /demos/index-best-options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Best options 5 | 6 | 7 | 8 | 9 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /scripts/main.js: -------------------------------------------------------------------------------- 1 | requirejs.config({ 2 | paths: { 3 | "jquery": "http://code.jquery.com/jquery-1.8.3.min", 4 | "jqueryui": "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min", 5 | "boilerplate": "../patterns/jquery.widget-factory.requirejs.boilerplate" 6 | } 7 | }); 8 | 9 | require(["jquery", "jqueryui", "boilerplate"], function ($, ui, boilerplate) { 10 | $(function () { 11 | var instance = ($("body").myWidget()); 12 | console.log(instance); 13 | 14 | instance.myWidget("methodB"); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /demos/index-custom-events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Custom events 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demos/index-prototypal-inheritance.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Prototypal Inheritance 5 | 6 | 7 | 8 | 9 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /demos/index-widget-factory-mobile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Widget Factory Mobile 5 | 6 | 7 | 8 | 9 | 10 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /demos/index-widget-factory-plugin-boilerplate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Widget Factory Plugin Boilerplate 5 | 6 | 7 | 8 | 9 | 10 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/coffee/jquery.extend-skeleton.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | * jQuery extend-based plugin boilerplate 3 | * Author: @oscargodson 4 | * Further changes: @timmywil, @addyosmani 5 | * Ported to CoffeeScript: @yckart 6 | * Licensed under the MIT license 7 | ### 8 | 9 | #As you'll notice below, we're making use of $.fn.extend to create our plugin rather 10 | #than opting for $.fn.pluginname. This type of structure may be useful if you need 11 | #to add a relatively large number of methods to your plugin. There are however alternatives 12 | #to this that may be better suited, including Alex Sexton's prototypal inheritence pattern 13 | #which is also included in this repo. 14 | 15 | (($) -> 16 | $.fn.extend pluginname: (options) -> 17 | @defaultOptions = {} 18 | settings = $.extend({}, @defaultOptions, options) 19 | @each -> 20 | $this = $(@) 21 | 22 | # Prevents CoffeeScript to return a value from plugin wrapper. 23 | return 24 | ) jQuery 25 | -------------------------------------------------------------------------------- /patterns/jquery.best.options.plugin-boilerplate.js: -------------------------------------------------------------------------------- 1 | /* [URL] */ 2 | ;(function(defaults, $, window, document, undefined) { 3 | 4 | 'use strict'; 5 | 6 | $.extend({ 7 | // Function to change the default properties of the plugin 8 | // Usage: 9 | // jQuery.pluginSetup({property:'Custom value'}); 10 | pluginSetup : function(options) { 11 | 12 | return $.extend(defaults, options); 13 | } 14 | }).fn.extend({ 15 | // Usage: 16 | // jQuery(selector).pluginName({property:'value'}); 17 | pluginName : function(options) { 18 | 19 | options = $.extend({}, defaults, options); 20 | 21 | return $(this).each(function() { 22 | 23 | // Plugin logic 24 | // Calling the function: 25 | // jQuery(selector).pluginName(options); 26 | }); 27 | }, 28 | otherMethod : function() { 29 | 30 | // Some logic 31 | // Calling the function: 32 | // jQuery(selector).otherMethod(options); 33 | } 34 | }); 35 | })({ 36 | property : "value", 37 | otherProperty : "value" 38 | }, jQuery, window, document); 39 | -------------------------------------------------------------------------------- /demos/index-widget-factory-bridge.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Widget Factory Bridge 5 | 6 | 7 | 8 | 9 | 10 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /patterns/jquery.extend-skeleton.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery extend-based plugin boilerplate 3 | * Author: @oscargodson 4 | * Further changes: @timmywil, @addyosmani 5 | * Licensed under the MIT license 6 | */ 7 | 8 | /* 9 | As you'll notice below, we're making use of $.fn.extend to create our plugin rather 10 | than opting for $.fn.pluginname. This type of structure may be useful if you need 11 | to add a relatively large number of methods to your plugin. There are however alternatives 12 | to this that may be better suited, including Alex Sexton's prototypal inheritence pattern 13 | which is also included in this repo. 14 | */ 15 | 16 | 17 | //the semi colon before function invocation is a safety net against concatenated 18 | //scripts and/or other plugins which may not be closed properly. 19 | ;(function($){ 20 | $.fn.extend({ 21 | pluginName: function( options ) { 22 | 23 | this.defaultOptions = {}; 24 | 25 | var settings = $.extend({}, this.defaultOptions, options); 26 | 27 | return this.each(function() { 28 | 29 | var $this = $(this); 30 | 31 | }); 32 | 33 | } 34 | 35 | }); 36 | 37 | })(jQuery); 38 | -------------------------------------------------------------------------------- /patterns/amd+commonjs/usage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 32 | -------------------------------------------------------------------------------- /patterns/coffee/jquery.best.options.plugin-boilerplate.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | * jQuery 'best options' plugin boilerplate 3 | * Author: @cowboy 4 | * Further changes: @addyosmani 5 | * Forked to CoffeeScript: @yckart 6 | * Licensed under the MIT license 7 | ### 8 | (($, window, document) -> 9 | $.fn.pluginName = (options) -> 10 | 11 | # Here's a best practice for overriding 'defaults' 12 | # with specified options. Note how, rather than a 13 | # regular defaults object being passed as the second 14 | # parameter, we instead refer to $.fn.pluginName.options 15 | # explicitly, merging it with the options passed directly 16 | # to the plugin. This allows us to override options both 17 | # globally and on a per-call level. 18 | 19 | options = $.extend({}, $.fn.pluginName.options, options) 20 | @each -> 21 | elem = $(@) 22 | # Some logic 23 | 24 | 25 | # Globally overriding options 26 | # Here are our publicly accessible default plugin options 27 | # that are available in case the user doesn't pass in all 28 | # of the values expected. The user is given a default 29 | # experience but can also override the values as necessary. 30 | # eg. $fn.pluginName.key ='otherval'; 31 | 32 | $.fn.pluginName.options = 33 | key: "value" 34 | myMethod: (elem, param) -> 35 | 36 | # Prevents CoffeeScript to return a value from plugin wrapper. 37 | return 38 | ) jQuery, window, document 39 | -------------------------------------------------------------------------------- /patterns/coffee/jquery.namespace.plugin-boilerplate.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | * jQuery namespaced 'Starter' plugin boilerplate 3 | * Author: @dougneiner 4 | * Further changes: @addyosmani 5 | * Ported to CoffeeScript: @yckart 6 | * Licensed under the MIT license 7 | ### 8 | (($) -> 9 | $.myNamespace = {} unless $.myNamespace 10 | $.myNamespace.myPluginName = (el, myFunctionParam, options) -> 11 | # To avoid scope issues, use 'base' instead of 'this' 12 | # to reference this class from internal events and functions. 13 | base = @ 14 | 15 | # Access to jQuery and DOM versions of element 16 | base.$el = $(el) 17 | base.el = el 18 | 19 | # Add a reverse reference to the DOM object 20 | base.$el.data "myNamespace.myPluginName", base 21 | 22 | base.init = -> 23 | base.myFunctionParam = myFunctionParam 24 | base.options = $.extend({}, $.myNamespace.myPluginName.defaultOptions, options) 25 | 26 | # Put your initialization code here 27 | 28 | 29 | # Sample Function, Uncomment to use 30 | # base.functionName = function( paramaters ){ 31 | # 32 | # }; 33 | # Run initializer 34 | base.init() 35 | 36 | $.myNamespace.myPluginName.defaultOptions = myDefaultValue: "" 37 | 38 | $.fn.mynamespace_myPluginName = (myFunctionParam, options) -> 39 | @each -> 40 | new $.myNamespace.myPluginName(@, myFunctionParam, options) 41 | return 42 | return 43 | 44 | ) jQuery 45 | -------------------------------------------------------------------------------- /patterns/coffee/jquery.customevents.plugin-boilerplate.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | * jQuery custom-events plugin boilerplate 3 | * Author: DevPatch 4 | * Further changes: @addyosmani 5 | * Ported to CoffeeScript: @yckart 6 | * Licensed under the MIT license 7 | ### 8 | 9 | # In this pattern, we use jQuery's custom events to add 10 | # pub/sub (publish/subscribe) capabilities to widgets. 11 | # Each widget would publish certain events and subscribe 12 | # to others. This approach effectively helps to decouple 13 | # the widgets and enables them to function independently. 14 | (($, window, document) -> 15 | $.widget "ao.eventStatus", 16 | options: { 17 | 18 | } 19 | _create: -> 20 | self = @ 21 | 22 | #self.element.addClass( "my-widget" ); 23 | 24 | #subscribe to 'myEventStart' 25 | self.element.bind "myEventStart", (e) -> 26 | console.log "event start" 27 | 28 | 29 | #subscribe to 'myEventEnd' 30 | self.element.bind "myEventEnd", (e) -> 31 | console.log "event end" 32 | return 33 | 34 | 35 | #unsubscribe to 'myEventStart' 36 | #self.element.unbind( "myEventStart", function(e){ 37 | #console.log("unsubscribed to this event"); 38 | #}); 39 | destroy: -> 40 | $.Widget::destroy.apply @, arguments_ 41 | return 42 | 43 | return 44 | ) jQuery, window, document 45 | 46 | #Publishing event notifications 47 | #usage: 48 | # $(".my-widget").trigger "myEventStart" 49 | # $(".my-widget").trigger "myEventEnd" 50 | -------------------------------------------------------------------------------- /patterns/jquery.customevents.plugin-boilerplate.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery custom-events plugin boilerplate 3 | * Author: DevPatch 4 | * Further changes: @addyosmani 5 | * Licensed under the MIT license 6 | */ 7 | 8 | // In this pattern, we use jQuery's custom events to add 9 | // pub/sub (publish/subscribe) capabilities to widgets. 10 | // Each widget would publish certain events and subscribe 11 | // to others. This approach effectively helps to decouple 12 | // the widgets and enables them to function independently. 13 | 14 | ;(function ( $, window, document, undefined ) { 15 | $.widget("ao.eventStatus", { 16 | options: { 17 | 18 | }, 19 | 20 | _create : function() { 21 | var self = this; 22 | 23 | //self.element.addClass( "my-widget" ); 24 | 25 | //subscribe to 'myEventStart' 26 | self.element.bind( "myEventStart", function( e ) { 27 | console.log("event start"); 28 | }); 29 | 30 | //subscribe to 'myEventEnd' 31 | self.element.bind( "myEventEnd", function( e ) { 32 | console.log("event end"); 33 | }); 34 | 35 | //unsubscribe to 'myEventStart' 36 | //self.element.unbind( "myEventStart", function(e){ 37 | ///console.log("unsubscribed to this event"); 38 | //}); 39 | }, 40 | 41 | destroy: function(){ 42 | $.Widget.prototype.destroy.apply( this, arguments ); 43 | }, 44 | }); 45 | })( jQuery, window , document ); 46 | 47 | //Publishing event notifications 48 | //usage: 49 | // $(".my-widget").trigger("myEventStart"); 50 | // $(".my-widget").trigger("myEventEnd"); 51 | -------------------------------------------------------------------------------- /patterns/amd+commonjs/pluginExtension.js: -------------------------------------------------------------------------------- 1 | // Extension to module core 2 | 3 | (function ( name, definition ) { 4 | var theModule = definition(), 5 | hasDefine = typeof define === "function", 6 | hasExports = typeof module !== "undefined" && module.exports; 7 | 8 | if ( hasDefine ) { // AMD Module 9 | define(theModule); 10 | } else if ( hasExports ) { // Node.js Module 11 | module.exports = theModule; 12 | } else { // Assign to common namespaces or simply the global object (window) 13 | 14 | // account for for flat-file/global module extensions 15 | var obj = null; 16 | var namespaces = name.split("."); 17 | var scope = (this.jQuery || this.ender || this.$ || this); 18 | for (var i = 0; i < namespaces.length; i++) { 19 | var packageName = namespaces[i]; 20 | if (obj && i == namespaces.length - 1) { 21 | obj[packageName] = theModule; 22 | } else if (typeof scope[packageName] === "undefined") { 23 | scope[packageName] = {}; 24 | } 25 | obj = scope[packageName]; 26 | } 27 | 28 | } 29 | })("core.plugin", function () { 30 | 31 | // Define your module here and return the public API. 32 | // This code could be easily adapted with the core to 33 | // allow for methods that overwrite and extend core functionality 34 | // in order to expand the highlight method to do more if you wish. 35 | return { 36 | setGreen: function ( el ) { 37 | highlight(el, "green"); 38 | }, 39 | setRed: function ( el ) { 40 | highlight(el, errorColor); 41 | } 42 | }; 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /patterns/jquery.namespace.plugin-boilerplate.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery namespaced 'Starter' plugin boilerplate 3 | * Author: @dougneiner 4 | * Further changes: @addyosmani 5 | * Licensed under the MIT license 6 | */ 7 | 8 | ;(function ( $ ) { 9 | if (!$.myNamespace) { 10 | $.myNamespace = {}; 11 | } 12 | 13 | $.myNamespace.myPluginName = function ( el, myFunctionParam, options ) { 14 | // To avoid scope issues, use 'base' instead of 'this' 15 | // to reference this class from internal events and functions. 16 | var base = this; 17 | 18 | // Access to jQuery and DOM versions of element 19 | base.$el = $(el); 20 | base.el = el; 21 | 22 | // Add a reverse reference to the DOM object 23 | base.$el.data( "myNamespace.myPluginName" , base ); 24 | 25 | base.init = function () { 26 | base.myFunctionParam = myFunctionParam; 27 | 28 | base.options = $.extend({}, 29 | $.myNamespace.myPluginName.defaultOptions, options); 30 | 31 | // Put your initialization code here 32 | }; 33 | 34 | // Sample Function, Uncomment to use 35 | // base.functionName = function( paramaters ){ 36 | // 37 | // }; 38 | // Run initializer 39 | base.init(); 40 | }; 41 | 42 | $.myNamespace.myPluginName.defaultOptions = { 43 | myDefaultValue: "" 44 | }; 45 | 46 | $.fn.mynamespace_myPluginName = function 47 | ( myFunctionParam, options ) { 48 | return this.each(function () { 49 | (new $.myNamespace.myPluginName(this, 50 | myFunctionParam, options)); 51 | }); 52 | }; 53 | 54 | })( jQuery ); 55 | -------------------------------------------------------------------------------- /patterns/amd+commonjs/pluginCore.js: -------------------------------------------------------------------------------- 1 | // Module/Plugin core 2 | // Note: the wrapper code you see around the module is what enables 3 | // us to support multiple module formats and specifications by 4 | // mapping the arguments defined to what a specific format expects 5 | // to be present. Our actual module functionality is defined lower 6 | // down, where a named module and exports are demonstrated. 7 | // 8 | // Note that dependencies can just as easily be declared if required 9 | // and should work as demonstrated earlier with the AMD module examples. 10 | 11 | (function ( name, definition ){ 12 | var theModule = definition(), 13 | // this is considered "safe": 14 | hasDefine = typeof define === "function" && define.amd, 15 | // hasDefine = typeof define === "function", 16 | hasExports = typeof module !== "undefined" && module.exports; 17 | 18 | if ( hasDefine ){ // AMD Module 19 | define(theModule); 20 | } else if ( hasExports ) { // Node.js Module 21 | module.exports = theModule; 22 | } else { // Assign to common namespaces or simply the global object (window) 23 | (this.jQuery || this.ender || this.$ || this)[name] = theModule; 24 | } 25 | })( "core", function () { 26 | var module = this; 27 | module.plugins = []; 28 | module.highlightColor = "yellow"; 29 | module.errorColor = "red"; 30 | 31 | // define the core module here and return the public API 32 | 33 | // This is the highlight method used by the core highlightAll() 34 | // method and all of the plugins highlighting elements different 35 | // colors 36 | module.highlight = function(el,strColor){ 37 | if(this.jQuery){ 38 | jQuery(el).css("background", strColor); 39 | } 40 | } 41 | return { 42 | highlightAll:function(){ 43 | module.highlight("div", module.highlightColor); 44 | } 45 | }; 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /patterns/coffee/jquery.prototypal-inheritance.plugin-boilerplate.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | * jQuery prototypal inheritance plugin boilerplate 3 | * Author: Alex Sexton, Scott Gonzalez 4 | * Further changes: @addyosmani 5 | * Ported to CoffeeScript: @yckart 6 | * Licensed under the MIT license 7 | ### 8 | 9 | # myObject - an object representing a concept that you want 10 | # to model (e.g. a car) 11 | myObject = 12 | init: (options, elem) -> 13 | # Mix in the passed-in options with the default options 14 | @options = $.extend({}, @options, options) 15 | 16 | # Save the element reference, both as a jQuery 17 | # reference and a normal reference 18 | @elem = elem 19 | @$elem = $(elem) 20 | 21 | # Build the DOM's initial structure 22 | @_build() 23 | 24 | # return this so that we can chain and use the bridge with less code. 25 | @ 26 | 27 | options: 28 | name: "No name" 29 | 30 | _build: -> 31 | #this.$elem.html('

'+this.options.name+'

'); 32 | 33 | myMethod: (msg) -> 34 | # You have direct access to the associated and cached 35 | # jQuery element 36 | console.log "myMethod triggered" 37 | # this.$elem.append('

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

'+this.options.name+'

'); 31 | }, 32 | myMethod: function( msg ){ 33 | // You have direct access to the associated and cached 34 | // jQuery element 35 | console.log("myMethod triggered"); 36 | // this.$elem.append('

'+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 [![Build Status](https://secure.travis-ci.org/jquery-boilerplate/jquery-patterns.svg?branch=master)](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 | [![Addy Osmani](http://gravatar.com/avatar/96270e4c3e5e9806cf7245475c00b275?s=70)](http://addyosmani.com) | [![Zeno Rocha](http://gravatar.com/avatar/e190023b66e2b8aa73a842b106920c93?s=70)](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 | --------------------------------------------------------------------------------