3 * Cherries by @toddmotto, @cferdinandi, @adamfschwartz, @daniellmb.
5 * @todo: Use Cash or Underscore when jQuery is dropped by supported plugins.
8 /* global window, document, define, module */
9 (function (root, factory) {
13 // Inspired by https://github.com/addyosmani/memoize.js/blob/master/memoize.js
14 if (typeof define === 'function' && define.amd) {
15 // AMD. Register as an anonymous module.
18 else if (typeof exports === 'object') {
19 // Node. Does not work with strict CommonJS, but only CommonJS-like
20 // environments that support module.exports, like Node.
21 module.exports = factory();
24 // Browser globals (root is window).
25 root.dBlazy = factory();
27 })(this, function () {
32 * Object for public APIs where dBlazy stands for drupalBlazy.
39 * Check if the given element matches the selector.
41 * @name dBlazy.matches
43 * @param {Element} elem
44 * The current element.
45 * @param {String} selector
46 * Selector to match against (class, ID, data attribute, or tag).
49 * Returns true if found, else false.
51 * @see http://caniuse.com/#feat=matchesselector
52 * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
54 dBlazy.matches = function (elem, selector) {
55 // Element.matches() polyfill.
56 var p = Element.prototype;
60 p.mozMatchesSelector ||
61 p.msMatchesSelector ||
63 p.webkitMatchesSelector ||
65 var matches = (this.document || this.ownerDocument).querySelectorAll(s);
66 var i = matches.length;
67 while (--i >= 0 && matches.item(i) !== this) {
68 // Empty block to satisfy coder and eslint.
75 if (elem.matches(selector)) {
83 * Get the closest matching element up the DOM tree.
85 * Inspired by Chris Ferdinandi, http://github.com/cferdinandi/smooth-scroll.
87 * @name dBlazy.closest
89 * @param {Element} elem
91 * @param {String} selector
92 * Selector to match against (class, ID, data attribute, or tag).
94 * @return {Boolean|Element}
95 * Returns null if not match found.
97 * @see http://caniuse.com/#feat=element-closest
98 * @see http://caniuse.com/#feat=matchesselector
99 * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
101 dBlazy.closest = function (elem, selector) {
102 for (; elem && elem !== document; elem = elem.parentNode) {
103 if (dBlazy.matches(elem, selector)) {
112 * Returns a new object after merging two, or more objects.
114 * Inspired by @adamfschwartz, @zackbloom, http://youmightnotneedjquery.com.
116 * @name dBlazy.extend
118 * @param {Object} out
119 * The objects to merge together.
122 * Merged values of defaults and options.
124 dBlazy.extend = Object.assign || function (out) {
127 for (var i = 1, len = arguments.length; i < len; i++) {
132 for (var key in arguments[i]) {
133 if (arguments[i].hasOwnProperty(key)) {
134 out[key] = arguments[i][key];
143 * A simple forEach() implementation for Arrays, Objects and NodeLists.
145 * @name dBlazy.forEach
148 * @link https://github.com/toddmotto/foreach
150 * @param {Array|Object|NodeList} collection
151 * Collection of items to iterate.
152 * @param {Function} callback
153 * Callback function for each iteration.
154 * @param {Array|Object|NodeList} scope
155 * Object/NodeList/Array that forEach is iterating over (aka `this`).
157 dBlazy.forEach = function (collection, callback, scope) {
158 var proto = Object.prototype;
159 if (proto.toString.call(collection) === '[object Object]') {
160 for (var prop in collection) {
161 if (proto.hasOwnProperty.call(collection, prop)) {
162 callback.call(scope, collection[prop], prop, collection);
167 for (var i = 0, len = collection.length; i < len; i++) {
168 callback.call(scope, collection[i], i, collection);
174 * A simple hasClass wrapper.
176 * @name dBlazy.hasClass
178 * @param {Element} el
180 * @param {String} name
184 * True if of of the method is supported.
186 dBlazy.hasClass = function (el, name) {
188 return el.classList.contains(name);
191 return el.className.indexOf(name) !== -1;
196 * A simple wrapper for event delegation like jQuery.on().
198 * Inspired by http://stackoverflow.com/questions/30880757/
199 * javascript-equivalent-to-on.
203 * @param {Element} elm
204 * The parent HTML element.
205 * @param {String} eventName
206 * The event name to trigger.
207 * @param {String} childEl
208 * Child selector to match against (class, ID, data attribute, or tag).
209 * @param {Function} callback
210 * The callback function.
212 dBlazy.on = function (elm, eventName, childEl, callback) {
213 elm.addEventListener(eventName, function (event) {
214 var t = event.target;
215 while (t && t !== this) {
216 if (dBlazy.matches(t, childEl)) {
217 callback.call(t, event);
225 * Executes a function once.
229 * @author Daniel Lamb <dlamb.open.source@gmail.com>
230 * @link https://github.com/daniellmb/once.js
232 * @param {Function} fn
233 * The executed function.
236 * The function result.
238 dBlazy.once = function (fn) {
241 return function proxy() {
246 result = fn.apply(this, arguments);
247 // For garbage collection.
254 * A simple wrapper for JSON.parse() for string within data-* attributes.
258 * @param {String} str
259 * The string to convert into JSON object.
261 * @return {Object|Boolean}
262 * The JSON object, or false in case invalid.
264 dBlazy.parse = function (str) {
266 return JSON.parse(str);
274 * A simple wrapper to delay callback function on window resize.
276 * @name dBlazy.resize
278 * @link https://github.com/louisremi/jquery-smartresize
280 * @param {Function} c
281 * The callback function.
286 * The callback function.
288 dBlazy.resize = function (c, t) {
289 window.onresize = function () {
290 window.clearTimeout(t);
291 t = window.setTimeout(c, 200);
297 * A simple wrapper for triggering event like jQuery.trigger().
299 * @name dBlazy.trigger
301 * @param {Element} elm
303 * @param {String} eventName
304 * The event name to trigger.
305 * @param {Object} custom
306 * The optional object passed into a custom event.
307 * @param {String} type
308 * Default to MouseEvents, can be either:
309 * MouseEvents: click, mousedown, mouseup.
310 * HTMLEvents: focus, change, blur, select.
312 * @see https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
313 * @todo: See if any consistent way for both custom and native events.
315 dBlazy.trigger = function (elm, eventName, custom, type) {
317 custom = custom || {};
318 type = type || 'MouseEvents';
320 var addEvent = function (eventName, data) {
322 // @todo: Use Event constructor, pending as not supported by all IEs.
323 event = document.createEvent(data && typeof data === 'object' ? 'Event' : type);
324 event.initEvent(eventName, true, true, data);
329 // IE >= 9 compat, else SCRIPT445: Object doesn't support this action.
330 // https://msdn.microsoft.com/library/ff975299(v=vs.85).aspx
332 event = custom ? new CustomEvent(eventName, {detail: custom}) : addEvent(eventName);
335 event = addEvent(eventName, custom);
338 elm.dispatchEvent(event);