|
@@ -0,0 +1,157 @@
|
|
|
+/*!
|
|
|
+ * 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);
|