edce4612f5b2d6b1f5da3eaaae95b0d8c20b0eac
[yaffs-website] / web / modules / contrib / blazy / js / dblazy.js
1 /**
2  * @file
3  * Cherries by @toddmotto, @cferdinandi, @adamfschwartz, @daniellmb.
4  *
5  * @todo: Use Cash or Underscore when jQuery is dropped by supported plugins.
6  */
7
8 /* global window, document, define, module */
9 (function (root, factory) {
10
11   'use strict';
12
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.
16     define([], factory);
17   }
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();
22   }
23   else {
24     // Browser globals (root is window).
25     root.dBlazy = factory();
26   }
27 })(this, function () {
28
29   'use strict';
30
31   /**
32    * Object for public APIs where dBlazy stands for drupalBlazy.
33    *
34    * @namespace
35    */
36   var dBlazy = {};
37
38   /**
39    * Check if the given element matches the selector.
40    *
41    * @name dBlazy.matches
42    *
43    * @param {Element} elem
44    *   The current element.
45    * @param {String} selector
46    *   Selector to match against (class, ID, data attribute, or tag).
47    *
48    * @return {Boolean}
49    *   Returns true if found, else false.
50    *
51    * @see http://caniuse.com/#feat=matchesselector
52    * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
53    */
54   dBlazy.matches = function (elem, selector) {
55     // Element.matches() polyfill.
56     var p = Element.prototype;
57     if (!p.matches) {
58       p.matches =
59         p.matchesSelector ||
60         p.mozMatchesSelector ||
61         p.msMatchesSelector ||
62         p.oMatchesSelector ||
63         p.webkitMatchesSelector ||
64         function (s) {
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.
69           }
70           return i > -1;
71         };
72     }
73
74     // Check if matches.
75     if (elem.matches(selector)) {
76       return true;
77     }
78
79     return false;
80   };
81
82   /**
83    * Get the closest matching element up the DOM tree.
84    *
85    * Inspired by Chris Ferdinandi, http://github.com/cferdinandi/smooth-scroll.
86    *
87    * @name dBlazy.closest
88    *
89    * @param {Element} elem
90    *   Starting element.
91    * @param {String} selector
92    *   Selector to match against (class, ID, data attribute, or tag).
93    *
94    * @return {Boolean|Element}
95    *   Returns null if not match found.
96    *
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
100    */
101   dBlazy.closest = function (elem, selector) {
102     for (; elem && elem !== document; elem = elem.parentNode) {
103       if (dBlazy.matches(elem, selector)) {
104         return elem;
105       }
106     }
107
108     return null;
109   };
110
111   /**
112    * Returns a new object after merging two, or more objects.
113    *
114    * Or use https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/
115    * Global_Objects/Object/assign.
116    *
117    * @name dBlazy.extend
118    *
119    * Inspired by @adamfschwartz, @zackbloom, http://youmightnotneedjquery.com.
120    *
121    * @param {Object} out
122    *   The objects to merge together.
123    *
124    * @return {Object}
125    *   Merged values of defaults and options.
126    */
127   dBlazy.extend = Object.assign || function (out) {
128     out = out || {};
129
130     for (var i = 1, len = arguments.length; i < len; i++) {
131       if (!arguments[i]) {
132         continue;
133       }
134
135       for (var key in arguments[i]) {
136         if (arguments[i].hasOwnProperty(key)) {
137           out[key] = arguments[i][key];
138         }
139       }
140     }
141
142     return out;
143   };
144
145   /**
146    * A simple forEach() implementation for Arrays, Objects and NodeLists.
147    *
148    * @name dBlazy.forEach
149    *
150    * @author Todd Motto
151    * @link https://github.com/toddmotto/foreach
152    *
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`).
159    */
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);
166         }
167       }
168     }
169     else {
170       for (var i = 0, len = collection.length; i < len; i++) {
171         callback.call(scope, collection[i], i, collection);
172       }
173     }
174   };
175
176   /**
177    * A simple wrapper for event delegation like jQuery.on().
178    *
179    * Inspired by http://stackoverflow.com/questions/30880757/
180    * javascript-equivalent-to-on.
181    *
182    * @name dBlazy.on
183    *
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.
192    */
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);
199         }
200         t = t.parentNode;
201       }
202     });
203   };
204
205   /**
206    * Executes a function once.
207    *
208    * @name dBlazy.once
209    *
210    * @author Daniel Lamb <dlamb.open.source@gmail.com>
211    * @link https://github.com/daniellmb/once.js
212    *
213    * @param {Function} fn
214    *   The executed function.
215    *
216    * @return {Object}
217    *   The function result.
218    */
219   dBlazy.once = function (fn) {
220     var result;
221     var ran = false;
222     return function proxy() {
223       if (ran) {
224         return result;
225       }
226       ran = true;
227       result = fn.apply(this, arguments);
228       // For garbage collection.
229       fn = null;
230       return result;
231     };
232   };
233
234   /**
235    * A simple wrapper for JSON.parse() for string within data-* attributes.
236    *
237    * @name dBlazy.parse
238    *
239    * @param {String} str
240    *   The string to convert into JSON object.
241    *
242    * @return {Object|Boolean}
243    *   The JSON object, or false in case invalid.
244    */
245   dBlazy.parse = function (str) {
246     try {
247       return JSON.parse(str);
248     }
249     catch (e) {
250       return false;
251     }
252   };
253
254   /**
255    * A simple wrapper to delay callback function on window resize.
256    *
257    * @name dBlazy.resize
258    *
259    * @link https://github.com/louisremi/jquery-smartresize
260    *
261    * @param {Function} c
262    *   The callback function.
263    * @param {Int} t
264    *   The timeout.
265    *
266    * @return {Function}
267    *   The callback function.
268    */
269   dBlazy.resize = function (c, t) {
270     window.onresize = function () {
271       window.clearTimeout(t);
272       t = window.setTimeout(c, 200);
273     };
274     return c;
275   };
276
277   /**
278    * A simple wrapper for triggering event like jQuery.trigger().
279    *
280    * @name dBlazy.trigger
281    *
282    * @param {Element} elm
283    *   The HTML element.
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.
292    *
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.
295    */
296   dBlazy.trigger = function (elm, eventName, custom, type) {
297     var event;
298     custom = custom || {};
299     type = type || 'MouseEvents';
300
301     var addEvent = function (eventName, data) {
302       data = 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);
306
307       return event;
308     };
309
310     // IE >= 9 compat, else SCRIPT445: Object doesn't support this action.
311     // https://msdn.microsoft.com/library/ff975299(v=vs.85).aspx
312     try {
313       event = custom ? new CustomEvent(eventName, {detail: custom}) : addEvent(eventName);
314     }
315     catch (e) {
316       event = addEvent(eventName, custom);
317     }
318
319     elm.dispatchEvent(event);
320   };
321
322   return dBlazy;
323
324 });