You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
6.6 KiB
157 lines
6.6 KiB
/*! |
|
* https://github.com/adampietrasiak/jquery.initialize |
|
* |
|
* Copyright (c) 2015-2016 Adam Pietrasiak |
|
* Released under the MIT license |
|
* https://github.com/timpler/jquery.initialize/blob/master/LICENSE |
|
* |
|
* This is based on MutationObserver |
|
* https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver |
|
*/ |
|
;(function ($) { |
|
|
|
"use strict"; |
|
|
|
var combinators = [' ', '>', '+', '~']; // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors#Combinators |
|
var fraternisers = ['+', '~']; // These combinators involve siblings. |
|
var complexTypes = ['ATTR', 'PSEUDO', 'ID', 'CLASS']; // These selectors are based upon attributes. |
|
|
|
//Check if browser supports "matches" function |
|
if (!Element.prototype.matches) { |
|
Element.prototype.matches = Element.prototype.matchesSelector || |
|
Element.prototype.webkitMatchesSelector || |
|
Element.prototype.mozMatchesSelector || |
|
Element.prototype.msMatchesSelector; |
|
} |
|
|
|
// Understand what kind of selector the initializer is based upon. |
|
function grok(msobserver) { |
|
if (!$.find.tokenize) { |
|
// This is an old version of jQuery, so cannot parse the selector. |
|
// Therefore we must assume the worst case scenario. That is, that |
|
// this is a complicated selector. This feature was available in: |
|
// https://github.com/jquery/sizzle/issues/242 |
|
msobserver.isCombinatorial = true; |
|
msobserver.isFraternal = true; |
|
msobserver.isComplex = true; |
|
return; |
|
} |
|
|
|
// Parse the selector. |
|
msobserver.isCombinatorial = false; |
|
msobserver.isFraternal = false; |
|
msobserver.isComplex = false; |
|
var token = $.find.tokenize(msobserver.selector); |
|
for (var i = 0; i < token.length; i++) { |
|
for (var j = 0; j < token[i].length; j++) { |
|
if (combinators.indexOf(token[i][j].type) != -1) |
|
msobserver.isCombinatorial = true; // This selector uses combinators. |
|
|
|
if (fraternisers.indexOf(token[i][j].type) != -1) |
|
msobserver.isFraternal = true; // This selector uses sibling combinators. |
|
|
|
if (complexTypes.indexOf(token[i][j].type) != -1) |
|
msobserver.isComplex = true; // This selector is based on attributes. |
|
} |
|
} |
|
} |
|
|
|
// MutationSelectorObserver represents a selector and it's associated initialization callback. |
|
var MutationSelectorObserver = function (selector, callback, options) { |
|
this.selector = selector.trim(); |
|
this.callback = callback; |
|
this.options = options; |
|
|
|
grok(this); |
|
}; |
|
|
|
// List of MutationSelectorObservers. |
|
var msobservers = []; |
|
msobservers.initialize = function (selector, callback, options) { |
|
|
|
// Wrap the callback so that we can ensure that it is only |
|
// called once per element. |
|
var seen = []; |
|
var callbackOnce = function () { |
|
if (seen.indexOf(this) == -1) { |
|
seen.push(this); |
|
$(this).each(callback); |
|
} |
|
}; |
|
|
|
// See if the selector matches any elements already on the page. |
|
$(options.target).find(selector).each(callbackOnce); |
|
|
|
// Then, add it to the list of selector observers. |
|
var msobserver = new MutationSelectorObserver(selector, callbackOnce, options) |
|
this.push(msobserver); |
|
|
|
// The MutationObserver watches for when new elements are added to the DOM. |
|
var observer = new MutationObserver(function (mutations) { |
|
var matches = []; |
|
|
|
// For each mutation. |
|
for (var m = 0; m < mutations.length; m++) { |
|
|
|
// If this is an attributes mutation, then the target is the node upon which the mutation occurred. |
|
if (mutations[m].type == 'attributes') { |
|
// Check if the mutated node matchs. |
|
if (mutations[m].target.matches(msobserver.selector)) |
|
matches.push(mutations[m].target); |
|
|
|
// If the selector is fraternal, query siblings of the mutated node for matches. |
|
if (msobserver.isFraternal) |
|
matches.push.apply(matches, mutations[m].target.parentElement.querySelectorAll(msobserver.selector)); |
|
else |
|
matches.push.apply(matches, mutations[m].target.querySelectorAll(msobserver.selector)); |
|
} |
|
|
|
// If this is an childList mutation, then inspect added nodes. |
|
if (mutations[m].type == 'childList') { |
|
// Search added nodes for matching selectors. |
|
for (var n = 0; n < mutations[m].addedNodes.length; n++) { |
|
if (!(mutations[m].addedNodes[n] instanceof Element)) continue; |
|
|
|
// Check if the added node matches the selector |
|
if (mutations[m].addedNodes[n].matches(msobserver.selector)) |
|
matches.push(mutations[m].addedNodes[n]); |
|
|
|
// If the selector is fraternal, query siblings for matches. |
|
if (msobserver.isFraternal) |
|
matches.push.apply(matches, mutations[m].addedNodes[n].parentElement.querySelectorAll(msobserver.selector)); |
|
else |
|
matches.push.apply(matches, mutations[m].addedNodes[n].querySelectorAll(msobserver.selector)); |
|
} |
|
} |
|
} |
|
|
|
// For each match, call the callback using jQuery.each() to initialize the element (once only.) |
|
for (var i = 0; i < matches.length; i++) { |
|
$(matches[i]).each(msobserver.callback); |
|
} |
|
}); |
|
|
|
// Observe the target element. |
|
var defaultObeserverOpts = { childList: true, subtree: true, attributes: msobserver.isComplex }; |
|
observer.observe(options.target, options.observer || defaultObeserverOpts ); |
|
|
|
return observer; |
|
}; |
|
|
|
// Deprecated API (does not work with jQuery >= 3.1.1): |
|
$.fn.initialize = function (callback, options) { |
|
return msobservers.initialize(this.selector, callback, $.extend({}, $.initialize.defaults, options)); |
|
}; |
|
|
|
// Supported API |
|
$.initialize = function (selector, callback, options) { |
|
return msobservers.initialize(selector, callback, $.extend({}, $.initialize.defaults, options)); |
|
}; |
|
|
|
// Options |
|
$.initialize.defaults = { |
|
target: document.documentElement, // Defaults to observe the entire document. |
|
observer: null // MutationObserverInit: Defaults to internal configuration if not provided. |
|
} |
|
|
|
})(jQuery); |