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 * Or use https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/
115 * Global_Objects/Object/assign.
117 * @name dBlazy.extend
119 * Inspired by @adamfschwartz, @zackbloom, http://youmightnotneedjquery.com.
121 * @param {Object} out
122 * The objects to merge together.
125 * Merged values of defaults and options.
127 dBlazy.extend = Object.assign || function (out) {
130 for (var i = 1, len = arguments.length; i < len; i++) {
135 for (var key in arguments[i]) {
136 if (arguments[i].hasOwnProperty(key)) {
137 out[key] = arguments[i][key];
146 * A simple forEach() implementation for Arrays, Objects and NodeLists.
148 * @name dBlazy.forEach
151 * @link https://github.com/toddmotto/foreach
153 * @param {Array|Object|NodeList} collection
154 * Collection of items to iterate.
155 * @param {Function} callback
156 * Callback function for each iteration.
157 * @param {Array|Object|NodeList} scope
158 * Object/NodeList/Array that forEach is iterating over (aka `this`).
160 dBlazy.forEach = function (collection, callback, scope) {
161 var proto = Object.prototype;
162 if (proto.toString.call(collection) === '[object Object]') {
163 for (var prop in collection) {
164 if (proto.hasOwnProperty.call(collection, prop)) {
165 callback.call(scope, collection[prop], prop, collection);
170 for (var i = 0, len = collection.length; i < len; i++) {
171 callback.call(scope, collection[i], i, collection);
177 * A simple wrapper for event delegation like jQuery.on().
179 * Inspired by http://stackoverflow.com/questions/30880757/
180 * javascript-equivalent-to-on.
184 * @param {Element} elm
185 * The parent HTML element.
186 * @param {String} eventName
187 * The event name to trigger.
188 * @param {String} childEl
189 * Child selector to match against (class, ID, data attribute, or tag).
190 * @param {Function} callback
191 * The callback function.
193 dBlazy.on = function (elm, eventName, childEl, callback) {
194 elm.addEventListener(eventName, function (event) {
195 var t = event.target;
196 while (t && t !== this) {
197 if (dBlazy.matches(t, childEl)) {
198 callback.call(t, event);
206 * Executes a function once.
210 * @author Daniel Lamb <dlamb.open.source@gmail.com>
211 * @link https://github.com/daniellmb/once.js
213 * @param {Function} fn
214 * The executed function.
217 * The function result.
219 dBlazy.once = function (fn) {
222 return function proxy() {
227 result = fn.apply(this, arguments);
228 // For garbage collection.
235 * A simple wrapper for JSON.parse() for string within data-* attributes.
239 * @param {String} str
240 * The string to convert into JSON object.
242 * @return {Object|Boolean}
243 * The JSON object, or false in case invalid.
245 dBlazy.parse = function (str) {
247 return JSON.parse(str);
255 * A simple wrapper to delay callback function on window resize.
257 * @name dBlazy.resize
259 * @link https://github.com/louisremi/jquery-smartresize
261 * @param {Function} c
262 * The callback function.
267 * The callback function.
269 dBlazy.resize = function (c, t) {
270 window.onresize = function () {
271 window.clearTimeout(t);
272 t = window.setTimeout(c, 200);
278 * A simple wrapper for triggering event like jQuery.trigger().
280 * @name dBlazy.trigger
282 * @param {Element} elm
284 * @param {String} eventName
285 * The event name to trigger.
286 * @param {Object} custom
287 * The optional object passed into a custom event.
288 * @param {String} type
289 * Default to MouseEvents, can be either:
290 * MouseEvents: click, mousedown, mouseup.
291 * HTMLEvents: focus, change, blur, select.
293 * @see https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
294 * @todo: See if any consistent way for both custom and native events.
296 dBlazy.trigger = function (elm, eventName, custom, type) {
298 custom = custom || {};
299 type = type || 'MouseEvents';
301 var addEvent = function (eventName, data) {
303 // @todo: Use Event constructor, pending as not supported by all IEs.
304 event = document.createEvent(data && typeof data === 'object' ? 'Event' : type);
305 event.initEvent(eventName, true, true, data);
310 // IE >= 9 compat, else SCRIPT445: Object doesn't support this action.
311 // https://msdn.microsoft.com/library/ff975299(v=vs.85).aspx
313 event = custom ? new CustomEvent(eventName, {detail: custom}) : addEvent(eventName);
316 event = addEvent(eventName, custom);
319 elm.dispatchEvent(event);