/** * Worf! * Nice Legs... for a human. * * @author Micky Hulse * @link http://mky.io * @docs https://github.com/mhulse/grunt-js-boiler * @copyright Copyright (c) 2014 Micky Hulse. * @license Released under the Apache License, Version 2.0. * @version 1.0.0 * @date 2014/08/29 */ /* jshint -W083, unused: vars */ //---------------------------------- // Notes to self: //console.profile('profile foo'); // ... code here ... //console.profileEnd('profile foo'); // ... or: // console.time('timing foo'); // ... code here ... // console.timeEnd('timing foo'); //---------------------------------- (function($, window, undefined) { /** * Function-level strict mode syntax. * * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode */ 'use strict'; //-------------------------------------------------------------------------- // // Local "globals": // //-------------------------------------------------------------------------- /** * Javascript console. * * @see http://www.paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ */ var console = window.console || { log : $.noop, warn : $.noop }; //---------------------------------- /** * The plugin namespace. */ var NS = 'worf'; //-------------------------------------------------------------------------- // // Defaults/settings: // //-------------------------------------------------------------------------- /** * Public defaults. * * @type { object } */ var defaults = { // Option defaults: classOn : NS + '_on', // Class applied when plugin initialized. foo : 'There is no honor', // A string that gets output to the console. bar : 'in attacking the weak.', // IBID. wrapper : 'div', // HTML element tag name use to wrap target element. // ... add more defaults here. // Callbacks: onInit : $.noop, // Callback on plugin initialization; "this" is the context of the current element. onAfterInit : $.noop, // Callback after plugin initialization; IBID. onBeforeWrapper : $.noop, // Callback before target element wrapped with `<div>`. onAfterWrapper : $.noop // Callback after target element wrapped with `<div>`. }; // defaults //-------------------------------------------------------------------------- // // Public methods: // //-------------------------------------------------------------------------- /** * Methods object. * * @type { object } */ var methods = { /** * Init constructor. * * @type { function } * @param { object } options Options object literal. * @this { object.jquery } * @return { object.jquery } Returns target object(s) for chaining purposes. */ init : function(options) { //---------------------------------- // Loop & return each this: //---------------------------------- return this.each(function() { //---------------------------------- // Declare, hoist and initialize: //---------------------------------- var $this = $(this); // Target object. var data = $this.data(NS); // Namespace instance data. var settings; // Settings object. //---------------------------------- // Data? //---------------------------------- if ( ! data) { //---------------------------------- // Initialize: //---------------------------------- settings = $.extend(true, {}, defaults, $.fn[NS].defaults, options, $this.data(NS + 'Options')); // Recursively merge defaults, options and HTML5 `data-` attribute options. //---------------------------------- // Namespaced instance data: //---------------------------------- $this.data(NS, { init : false, // Plugin initialization flag. settings : settings, // Merged plugin settings. target : $this // Target element plugin has been initialized on. }); //---------------------------------- // Easy access: //---------------------------------- data = $this.data(NS); } //---------------------------------- // Data initialization check: //---------------------------------- if ( ! data.init) { //---------------------------------- // Call main: //---------------------------------- _main.call($this, data); } else { //---------------------------------- // Ouch! //---------------------------------- console.warn('jQuery.%s thinks it\'s already initialized on %o.', NS, this); } }); }, // init /** * Removes plugin from element. * * @type { function } * @this { object.jquery } * @return { object.jquery } Returns target object(s) for chaining purposes. */ destroy : function() { //---------------------------------- // Loop & return each this: //---------------------------------- return this.each(function() { //---------------------------------- // Declare, hoist and initialize: //---------------------------------- var $this = $(this); var data = $this.data(NS); //---------------------------------- // Data? //---------------------------------- if (data) { //---------------------------------- // Wrapped `<div>`? //---------------------------------- if (data.target.parent().is(data.settings.wrapper)) { //---------------------------------- // Yup. Remove: //---------------------------------- data.target.unwrap(); } //---------------------------------- // Everything else: //---------------------------------- data.target // ... hot chaining action --> //---------------------------------- // Namespaced instance data: //---------------------------------- .removeData(NS) // --> // // // Remove other setups here. // // //---------------------------------- // Remove class: //---------------------------------- .removeClass(data.settings.classOn); // Destroyed! } }); } // destroy }; // methods //-------------------------------------------------------------------------- // // Private methods: // //-------------------------------------------------------------------------- /** * Called after plugin initialization. * * @private * @type { function } * @param { object } data Parent data object literal. * @this { object.jquery } */ var _main = function(data) { //---------------------------------- // Plugin initialization flag: //---------------------------------- data.init = true; //---------------------------------- // Callback: //---------------------------------- data.settings.onInit.call(data.target, data); //---------------------------------- // Target exists & contains text? //---------------------------------- if (data.target.length && ( ! _isEmpty(data.target))) { // Do stuff here ... For example, this: data.target.addClass(data.settings.classOn); // ... or this: console.log(data.settings.foo, data.settings.bar); // or even this: $.fn[NS].wrapper.call(data.target, data); } else { //---------------------------------- // Problemos: //---------------------------------- console.warn('jQuery.%s thinks there\'s a problem with your markup.', NS); } //---------------------------------- // Callback: //---------------------------------- data.settings.onAfterInit.call(data.target, data); }; // _main /** * Returns boolean true if element is empty. * * @param { object.jquery } $el * @return { boolean } */ function _isEmpty($el) { return ( ! $.trim($el.html())); } // isEmpty() //-------------------------------------------------------------------------- // // Method calling logic: // //-------------------------------------------------------------------------- /** * Boilerplate plugin logic. * * @constructor * @see http://learn.jquery.com/plugins/ * @type { function } * @param { string } method String method identifier. * @return { method } Calls plugin method with supplied params. */ $.fn[NS] = function(method) { if (methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if ((typeof method == 'object') || ( ! method)) { return methods.init.apply(this, arguments); } else { $.error('jQuery.%s thinks that %s doesn\'t exist', NS, method); } }; // $.fn[NS] //-------------------------------------------------------------------------- // // Public access to secondary functions: // @see http://www.learningjquery.com/2007/10/a-plugin-development-pattern/ // @see http://stackoverflow.com/questions/11286312/superfish-plugin-fn-extend-how-does-this-code-work // //-------------------------------------------------------------------------- /** * Wraps target with a `<div>`. * * Example (before instantiation): * * $.fn.worf.wrapper = function(data) { this.wrap('<section />'); }; * * @type { function } * @this { object.jquery } Target element. * @param { object } data Parent data object literal. * @return { object.jquery } Returns target object(s) for chaining purposes. */ $.fn[NS].wrapper = function(data) { //---------------------------------- // Callback: //---------------------------------- data.settings.onBeforeWrapper.call(data.target, data); //---------------------------------- // Wrap target with div: //---------------------------------- data.target.wrap('<' + data.settings.wrapper + ' />'); //---------------------------------- // Callback: //---------------------------------- data.settings.onAfterWrapper.call(data.target, data); }; // $.fn[NS].wrapper //-------------------------------------------------------------------------- // // Default settings: // //-------------------------------------------------------------------------- /** * Public defaults. * * Example (before instantiation): * * $.fn.worf.defaults.classOn = 'foo'; * * @see http://stackoverflow.com/questions/11306375/plugin-authoring-how-to-allow-myplugin-defaults-key-value * * @type { object } */ $.fn[NS].defaults = defaults; }(jQuery, window)); // Booyah!