forked from pie6k/jquery.initialize
-
Notifications
You must be signed in to change notification settings - Fork 0
/
jquery.initialize.js
156 lines (129 loc) · 6.64 KB
/
jquery.initialize.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*!
* 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);