| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- /*globals jQuery,Window,HTMLElement,HTMLDocument,HTMLCollection,NodeList,MutationObserver */
- /*exported Arrive*/
- /*jshint latedef:false */
- /*
- * arrive.js
- * v2.4.1
- * https://github.com/uzairfarooq/arrive
- * MIT licensed
- *
- * Copyright (c) 2014-2017 Uzair Farooq
- */
- var Arrive = (function(window, $, undefined) {
- "use strict";
- if(!window.MutationObserver || typeof HTMLElement === 'undefined'){
- return; //for unsupported browsers
- }
- var arriveUniqueId = 0;
- var utils = (function() {
- var matches = HTMLElement.prototype.matches || HTMLElement.prototype.webkitMatchesSelector || HTMLElement.prototype.mozMatchesSelector
- || HTMLElement.prototype.msMatchesSelector;
- return {
- matchesSelector: function(elem, selector) {
- return elem instanceof HTMLElement && matches.call(elem, selector);
- },
- // to enable function overloading - By John Resig (MIT Licensed)
- addMethod: function (object, name, fn) {
- var old = object[ name ];
- object[ name ] = function(){
- if ( fn.length == arguments.length ) {
- return fn.apply( this, arguments );
- }
- else if ( typeof old == 'function' ) {
- return old.apply( this, arguments );
- }
- };
- },
- callCallbacks: function(callbacksToBeCalled, registrationData) {
- if (registrationData && registrationData.options.onceOnly && registrationData.firedElems.length == 1) {
- // as onlyOnce param is true, make sure we fire the event for only one item
- callbacksToBeCalled = [callbacksToBeCalled[0]];
- }
- for (var i = 0, cb; (cb = callbacksToBeCalled[i]); i++) {
- if (cb && cb.callback) {
- cb.callback.call(cb.elem, cb.elem);
- }
- }
- if (registrationData && registrationData.options.onceOnly && registrationData.firedElems.length == 1) {
- // unbind event after first callback as onceOnly is true.
- registrationData.me.unbindEventWithSelectorAndCallback.call(
- registrationData.target, registrationData.selector, registrationData.callback);
- }
- },
- // traverse through all descendants of a node to check if event should be fired for any descendant
- checkChildNodesRecursively: function(nodes, registrationData, matchFunc, callbacksToBeCalled) {
- // check each new node if it matches the selector
- for (var i=0, node; (node = nodes[i]); i++) {
- if (matchFunc(node, registrationData, callbacksToBeCalled)) {
- callbacksToBeCalled.push({ callback: registrationData.callback, elem: node });
- }
- if (node.childNodes.length > 0) {
- utils.checkChildNodesRecursively(node.childNodes, registrationData, matchFunc, callbacksToBeCalled);
- }
- }
- },
- mergeArrays: function(firstArr, secondArr){
- // Overwrites default options with user-defined options.
- var options = {},
- attrName;
- for (attrName in firstArr) {
- if (firstArr.hasOwnProperty(attrName)) {
- options[attrName] = firstArr[attrName];
- }
- }
- for (attrName in secondArr) {
- if (secondArr.hasOwnProperty(attrName)) {
- options[attrName] = secondArr[attrName];
- }
- }
- return options;
- },
- toElementsArray: function (elements) {
- // check if object is an array (or array like object)
- // Note: window object has .length property but it's not array of elements so don't consider it an array
- if (typeof elements !== "undefined" && (typeof elements.length !== "number" || elements === window)) {
- elements = [elements];
- }
- return elements;
- }
- };
- })();
- // Class to maintain state of all registered events of a single type
- var EventsBucket = (function() {
- var EventsBucket = function() {
- // holds all the events
- this._eventsBucket = [];
- // function to be called while adding an event, the function should do the event initialization/registration
- this._beforeAdding = null;
- // function to be called while removing an event, the function should do the event destruction
- this._beforeRemoving = null;
- };
- EventsBucket.prototype.addEvent = function(target, selector, options, callback) {
- var newEvent = {
- target: target,
- selector: selector,
- options: options,
- callback: callback,
- firedElems: []
- };
- if (this._beforeAdding) {
- this._beforeAdding(newEvent);
- }
- this._eventsBucket.push(newEvent);
- return newEvent;
- };
- EventsBucket.prototype.removeEvent = function(compareFunction) {
- for (var i=this._eventsBucket.length - 1, registeredEvent; (registeredEvent = this._eventsBucket[i]); i--) {
- if (compareFunction(registeredEvent)) {
- if (this._beforeRemoving) {
- this._beforeRemoving(registeredEvent);
- }
- // mark callback as null so that even if an event mutation was already triggered it does not call callback
- var removedEvents = this._eventsBucket.splice(i, 1);
- if (removedEvents && removedEvents.length) {
- removedEvents[0].callback = null;
- }
- }
- }
- };
- EventsBucket.prototype.beforeAdding = function(beforeAdding) {
- this._beforeAdding = beforeAdding;
- };
- EventsBucket.prototype.beforeRemoving = function(beforeRemoving) {
- this._beforeRemoving = beforeRemoving;
- };
- return EventsBucket;
- })();
- /**
- * @constructor
- * General class for binding/unbinding arrive and leave events
- */
- var MutationEvents = function(getObserverConfig, onMutation) {
- var eventsBucket = new EventsBucket(),
- me = this;
- var defaultOptions = {
- fireOnAttributesModification: false
- };
- // actual event registration before adding it to bucket
- eventsBucket.beforeAdding(function(registrationData) {
- var
- target = registrationData.target,
- observer;
- // mutation observer does not work on window or document
- if (target === window.document || target === window) {
- target = document.getElementsByTagName("html")[0];
- }
- // Create an observer instance
- observer = new MutationObserver(function(e) {
- onMutation.call(this, e, registrationData);
- });
- var config = getObserverConfig(registrationData.options);
- observer.observe(target, config);
- registrationData.observer = observer;
- registrationData.me = me;
- });
- // cleanup/unregister before removing an event
- eventsBucket.beforeRemoving(function (eventData) {
- eventData.observer.disconnect();
- });
- this.bindEvent = function(selector, options, callback) {
- options = utils.mergeArrays(defaultOptions, options);
- var elements = utils.toElementsArray(this);
- for (var i = 0; i < elements.length; i++) {
- eventsBucket.addEvent(elements[i], selector, options, callback);
- }
- };
- this.unbindEvent = function() {
- var elements = utils.toElementsArray(this);
- eventsBucket.removeEvent(function(eventObj) {
- for (var i = 0; i < elements.length; i++) {
- if (this === undefined || eventObj.target === elements[i]) {
- return true;
- }
- }
- return false;
- });
- };
- this.unbindEventWithSelectorOrCallback = function(selector) {
- var elements = utils.toElementsArray(this),
- callback = selector,
- compareFunction;
- if (typeof selector === "function") {
- compareFunction = function(eventObj) {
- for (var i = 0; i < elements.length; i++) {
- if ((this === undefined || eventObj.target === elements[i]) && eventObj.callback === callback) {
- return true;
- }
- }
- return false;
- };
- }
- else {
- compareFunction = function(eventObj) {
- for (var i = 0; i < elements.length; i++) {
- if ((this === undefined || eventObj.target === elements[i]) && eventObj.selector === selector) {
- return true;
- }
- }
- return false;
- };
- }
- eventsBucket.removeEvent(compareFunction);
- };
- this.unbindEventWithSelectorAndCallback = function(selector, callback) {
- var elements = utils.toElementsArray(this);
- eventsBucket.removeEvent(function(eventObj) {
- for (var i = 0; i < elements.length; i++) {
- if ((this === undefined || eventObj.target === elements[i]) && eventObj.selector === selector && eventObj.callback === callback) {
- return true;
- }
- }
- return false;
- });
- };
- return this;
- };
- /**
- * @constructor
- * Processes 'arrive' events
- */
- var ArriveEvents = function() {
- // Default options for 'arrive' event
- var arriveDefaultOptions = {
- fireOnAttributesModification: false,
- onceOnly: false,
- existing: false
- };
- function getArriveObserverConfig(options) {
- var config = {
- attributes: false,
- childList: true,
- subtree: true
- };
- if (options.fireOnAttributesModification) {
- config.attributes = true;
- }
- return config;
- }
- function onArriveMutation(mutations, registrationData) {
- mutations.forEach(function( mutation ) {
- var newNodes = mutation.addedNodes,
- targetNode = mutation.target,
- callbacksToBeCalled = [],
- node;
- // If new nodes are added
- if( newNodes !== null && newNodes.length > 0 ) {
- utils.checkChildNodesRecursively(newNodes, registrationData, nodeMatchFunc, callbacksToBeCalled);
- }
- else if (mutation.type === "attributes") {
- if (nodeMatchFunc(targetNode, registrationData, callbacksToBeCalled)) {
- callbacksToBeCalled.push({ callback: registrationData.callback, elem: targetNode });
- }
- }
- utils.callCallbacks(callbacksToBeCalled, registrationData);
- });
- }
- function nodeMatchFunc(node, registrationData, callbacksToBeCalled) {
- // check a single node to see if it matches the selector
- if (utils.matchesSelector(node, registrationData.selector)) {
- if(node._id === undefined) {
- node._id = arriveUniqueId++;
- }
- // make sure the arrive event is not already fired for the element
- if (registrationData.firedElems.indexOf(node._id) == -1) {
- registrationData.firedElems.push(node._id);
- return true;
- }
- }
- return false;
- }
- arriveEvents = new MutationEvents(getArriveObserverConfig, onArriveMutation);
- var mutationBindEvent = arriveEvents.bindEvent;
- // override bindEvent function
- arriveEvents.bindEvent = function(selector, options, callback) {
- if (typeof callback === "undefined") {
- callback = options;
- options = arriveDefaultOptions;
- } else {
- options = utils.mergeArrays(arriveDefaultOptions, options);
- }
- var elements = utils.toElementsArray(this);
- if (options.existing) {
- var existing = [];
- for (var i = 0; i < elements.length; i++) {
- var nodes = elements[i].querySelectorAll(selector);
- for (var j = 0; j < nodes.length; j++) {
- existing.push({ callback: callback, elem: nodes[j] });
- }
- }
- // no need to bind event if the callback has to be fired only once and we have already found the element
- if (options.onceOnly && existing.length) {
- return callback.call(existing[0].elem, existing[0].elem);
- }
- setTimeout(utils.callCallbacks, 1, existing);
- }
- mutationBindEvent.call(this, selector, options, callback);
- };
- return arriveEvents;
- };
- /**
- * @constructor
- * Processes 'leave' events
- */
- var LeaveEvents = function() {
- // Default options for 'leave' event
- var leaveDefaultOptions = {};
- function getLeaveObserverConfig() {
- var config = {
- childList: true,
- subtree: true
- };
- return config;
- }
- function onLeaveMutation(mutations, registrationData) {
- mutations.forEach(function( mutation ) {
- var removedNodes = mutation.removedNodes,
- callbacksToBeCalled = [];
- if( removedNodes !== null && removedNodes.length > 0 ) {
- utils.checkChildNodesRecursively(removedNodes, registrationData, nodeMatchFunc, callbacksToBeCalled);
- }
- utils.callCallbacks(callbacksToBeCalled, registrationData);
- });
- }
- function nodeMatchFunc(node, registrationData) {
- return utils.matchesSelector(node, registrationData.selector);
- }
- leaveEvents = new MutationEvents(getLeaveObserverConfig, onLeaveMutation);
- var mutationBindEvent = leaveEvents.bindEvent;
- // override bindEvent function
- leaveEvents.bindEvent = function(selector, options, callback) {
- if (typeof callback === "undefined") {
- callback = options;
- options = leaveDefaultOptions;
- } else {
- options = utils.mergeArrays(leaveDefaultOptions, options);
- }
- mutationBindEvent.call(this, selector, options, callback);
- };
- return leaveEvents;
- };
- var arriveEvents = new ArriveEvents(),
- leaveEvents = new LeaveEvents();
- function exposeUnbindApi(eventObj, exposeTo, funcName) {
- // expose unbind function with function overriding
- utils.addMethod(exposeTo, funcName, eventObj.unbindEvent);
- utils.addMethod(exposeTo, funcName, eventObj.unbindEventWithSelectorOrCallback);
- utils.addMethod(exposeTo, funcName, eventObj.unbindEventWithSelectorAndCallback);
- }
- /*** expose APIs ***/
- function exposeApi(exposeTo) {
- exposeTo.arrive = arriveEvents.bindEvent;
- exposeUnbindApi(arriveEvents, exposeTo, "unbindArrive");
- exposeTo.leave = leaveEvents.bindEvent;
- exposeUnbindApi(leaveEvents, exposeTo, "unbindLeave");
- }
- if ($) {
- exposeApi($.fn);
- }
- exposeApi(HTMLElement.prototype);
- exposeApi(NodeList.prototype);
- exposeApi(HTMLCollection.prototype);
- exposeApi(HTMLDocument.prototype);
- exposeApi(Window.prototype);
- var Arrive = {};
- // expose functions to unbind all arrive/leave events
- exposeUnbindApi(arriveEvents, Arrive, "unbindAllArrive");
- exposeUnbindApi(leaveEvents, Arrive, "unbindAllLeave");
- return Arrive;
- })(window, typeof jQuery === 'undefined' ? null : jQuery, undefined);
|