Forked from cferdinandi/umd-script-boilerplate.js
Last active
October 29, 2020 15:54
-
-
Save alex-wdmg/86e761e86e57b4b833c26f50ab8d0924 to your computer and use it in GitHub Desktop.
A simple boilerplate for UMD JS modules.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (root, factory) { | |
if (typeof define === 'function' && define.amd) | |
define([], factory(root)); | |
else if (typeof exports === 'object') | |
module.exports = factory(root); | |
else | |
root.myPlugin = factory(root); | |
})(typeof global !== "undefined" ? global : this.window || this.global, function (root) { | |
'use strict'; | |
// | |
// Variables | |
// | |
let myPlugin = {}; // Object for public APIs | |
let settings, eventTimeout; | |
// Default settings | |
let defaults = { | |
someVar: 123, | |
initClass: 'js-myplugin', | |
callbackBefore: function () {}, | |
callbackAfter: function () {} | |
}; | |
// | |
// Methods | |
// | |
/** | |
* A simple forEach() implementation for Arrays, Objects and NodeLists | |
* @private | |
* @param {Array|Object|NodeList} collection Collection of items to iterate | |
* @param {Function} callback Callback function for each iteration | |
* @param {Array|Object|NodeList} scope Object/NodeList/Array that forEach is iterating over (aka `this`) | |
*/ | |
let forEach = function (collection, callback, scope) { | |
if (Object.prototype.toString.call(collection) === '[object Object]') { | |
for (let prop in collection) { | |
if (Object.prototype.hasOwnProperty.call(collection, prop)) { | |
callback.call(scope, collection[prop], prop, collection); | |
} | |
} | |
} else { | |
for (let i = 0, len = collection.length; i < len; i++) { | |
callback.call(scope, collection[i], i, collection); | |
} | |
} | |
}; | |
/** | |
* Check for plugin support features | |
* @returns {boolean} | |
*/ | |
let supports = function () { | |
if (!!document.querySelector && !!root.addEventListener) | |
return false; | |
return true; | |
}; | |
/** | |
* Merge defaults with user options | |
* @private | |
* @param {Object} defaults Default settings | |
* @param {Object} options User options | |
* @returns {Object} Merged values of defaults and options | |
*/ | |
let extend = function (defaults, options) { | |
let extended = {}; | |
forEach(defaults, function (value, prop) { | |
extended[prop] = defaults[prop]; | |
}); | |
forEach(options, function (value, prop) { | |
extended[prop] = options[prop]; | |
}); | |
return extended; | |
}; | |
/** | |
* Remove whitespace from a string | |
* @private | |
* @param {String} string | |
* @returns {String} | |
*/ | |
let trim = function (string) { | |
return string.replace(/^\s+|\s+$/g, ''); | |
}; | |
/** | |
* Convert data-options attribute into an object of key/value pairs | |
* @private | |
* @param {String} options Link-specific options as a data attribute string | |
* @returns {Object} | |
*/ | |
let getDataOptions = function (options) { | |
if (!options || !(typeof JSON === 'object' && typeof JSON.parse === 'function')) | |
return {}; | |
else | |
return JSON.parse(options); | |
}; | |
/** | |
* Destroy the current initialization. | |
* @public | |
*/ | |
myPlugin.destroy = function () { | |
// If plugin isn't already initialized, stop | |
if (!settings) | |
return; | |
// Remove init class for conditional CSS | |
document.documentElement.classList.remove(settings.initClass); | |
// @todo Undo any other init functions... | |
// Remove event listeners | |
document.removeEventListener('click', someEventHandler, false); | |
// Reset variables | |
settings = null; | |
eventTimeout = null; | |
}; | |
/** | |
* On window scroll and resize, only run events at a rate of 15fps for better performance | |
* @private | |
* @param {Function} eventTimeout Timeout function | |
* @param {Object} settings | |
*/ | |
let eventThrottler = function () { | |
if (!eventTimeout) { | |
eventTimeout = setTimeout(function() { | |
eventTimeout = null; | |
//actualMethod(settings); | |
}, 69); | |
} | |
}; | |
// @todo Do something... | |
/** | |
* Get the closest matching element up the DOM tree | |
* @param {Element} elem Starting element | |
* @param {String} selector Selector to match against (class, ID, or data attribute) | |
* @return {Boolean|Element} Returns false if not match found | |
*/ | |
let getClosest = function (elem, selector) { | |
let firstChar = selector.charAt(0); | |
for (; elem && elem !== document; elem = elem.parentNode) { | |
switch (firstChar) { | |
case '.': | |
if (elem.classList.contains(selector.substr(1))) | |
return elem; | |
break; | |
case '#': | |
if (elem.id === selector.substr(1)) | |
return elem; | |
break; | |
case '[': | |
if (elem.hasAttribute(selector.substr(1, selector.length - 2))) | |
return elem; | |
break; | |
default: | |
return false; | |
} | |
} | |
}; | |
/** | |
* Some Handle Event | |
* @private | |
*/ | |
let someEventHandler = function (event) { | |
let toggle = event.target; | |
let closest = getClosest(toggle, '[data-some-selector]'); | |
if (closest) { | |
// run some methods | |
} | |
}; | |
/** | |
* Initialize Plugin | |
* @public | |
* @param {Object} options User settings | |
*/ | |
myPlugin = function (options) { | |
// Features test | |
if (!supports) | |
return; | |
else | |
myPlugin.destroy(); // Destroy any existing initializations | |
// Merge options with defaults | |
settings = extend(defaults, options || {}); | |
// Add class to HTML element to activate conditional CSS | |
document.documentElement.classList.add( settings.initClass ); | |
// @todo Do something... | |
// Listen for events | |
document.addEventListener('click', someEventHandler, false); | |
}; | |
// | |
// Public APIs | |
// | |
return myPlugin; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment