Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81144 views
1
/*
2
* Copyright (C) 2007-2015 Diego Perini
3
* All rights reserved.
4
*
5
* Caching/memoization module for NWMatcher
6
*
7
* Added capabilities:
8
*
9
* - Mutation Events are feature tested and used safely
10
* - handle caching different document types HTML/XML/SVG
11
* - store result sets for different selectors / contexts
12
* - simultaneously control mutation on multiple documents
13
*
14
*/
15
16
(function(global) {
17
18
// export the public API for CommonJS implementations,
19
// for headless JS engines or for standard web browsers
20
var Dom =
21
// as CommonJS/NodeJS module
22
typeof exports == 'object' ? exports :
23
// create or extend NW namespace
24
((global.NW || (global.NW = global.Object())) &&
25
(global.NW.Dom || (global.NW.Dom = global.Object()))),
26
27
Contexts = global.Object(),
28
Results = global.Object(),
29
30
isEnabled = false,
31
isExpired = true,
32
isPaused = false,
33
34
context = global.document,
35
root = context.documentElement,
36
37
// last time cache initialization was called
38
lastCalled = 0,
39
40
// minimum time allowed between calls to the cache initialization
41
minCacheRest = 15, //ms
42
43
mutationTest =
44
function(type, callback) {
45
var isSupported = false,
46
root = document.documentElement,
47
div = document.createElement('div'),
48
handler = function() { isSupported = true; };
49
root.insertBefore(div, root.firstChild);
50
div.addEventListener(type, handler, true);
51
if (callback && callback.call) callback(div);
52
div.removeEventListener(type, handler, true);
53
root.removeChild(div);
54
return isSupported;
55
},
56
57
// check for Mutation Events, DOMAttrModified should be
58
// enough to ensure DOMNodeInserted/DOMNodeRemoved exist
59
HACKED_MUTATION_EVENTS = false,
60
61
NATIVE_MUTATION_EVENTS = root.addEventListener ?
62
mutationTest('DOMAttrModified', function(e) { e.setAttribute('id', 'nw'); }) : false,
63
64
loadResults =
65
function(selector, from, doc, root) {
66
if (isEnabled && !isPaused) {
67
if (!isExpired) {
68
if (Results[selector] && Contexts[selector] === from) {
69
return Results[selector];
70
}
71
} else {
72
// pause caching while we are getting
73
// hammered by dom mutations (jdalton)
74
now = (new global.Date).getTime();
75
if ((now - lastCalled) < minCacheRest) {
76
isPaused = isExpired = true;
77
global.setTimeout(function() { isPaused = false; }, minCacheRest);
78
} else setCache(true, doc);
79
lastCalled = now;
80
}
81
}
82
return undefined;
83
},
84
85
saveResults =
86
function(selector, from, doc, data) {
87
Contexts[selector] = from;
88
Results[selector] = data;
89
return;
90
},
91
92
/*-------------------------------- CACHING ---------------------------------*/
93
94
// invoked by mutation events to expire cached parts
95
mutationWrapper =
96
function(event) {
97
var d = event.target.ownerDocument || event.target;
98
stopMutation(d);
99
expireCache(d);
100
},
101
102
// append mutation events
103
startMutation =
104
function(d) {
105
if (!d.isCaching && d.addEventListener) {
106
// FireFox/Opera/Safari/KHTML have support for Mutation Events
107
d.addEventListener('DOMAttrModified', mutationWrapper, true);
108
d.addEventListener('DOMNodeInserted', mutationWrapper, true);
109
d.addEventListener('DOMNodeRemoved', mutationWrapper, true);
110
d.isCaching = true;
111
}
112
},
113
114
// remove mutation events
115
stopMutation =
116
function(d) {
117
if (d.isCaching && d.removeEventListener) {
118
d.removeEventListener('DOMAttrModified', mutationWrapper, true);
119
d.removeEventListener('DOMNodeInserted', mutationWrapper, true);
120
d.removeEventListener('DOMNodeRemoved', mutationWrapper, true);
121
d.isCaching = false;
122
}
123
},
124
125
// enable/disable context caching system
126
// @d optional document context (iframe, xml document)
127
// script loading context will be used as default context
128
setCache =
129
function(enable, d) {
130
if (!!enable) {
131
isExpired = false;
132
startMutation(d);
133
} else {
134
isExpired = true;
135
stopMutation(d);
136
}
137
isEnabled = !!enable;
138
},
139
140
// expire complete cache
141
// can be invoked by Mutation Events or
142
// programmatically by other code/scripts
143
// document context is mandatory no checks
144
expireCache =
145
function(d) {
146
isExpired = true;
147
Contexts = global.Object();
148
Results = global.Object();
149
};
150
151
if (!NATIVE_MUTATION_EVENTS && root.addEventListener && Element && Element.prototype) {
152
if (mutationTest('DOMNodeInserted', function(e) { e.appendChild(document.createElement('div')); }) &&
153
mutationTest('DOMNodeRemoved', function(e) { e.removeChild(e.appendChild(document.createElement('div'))); })) {
154
HACKED_MUTATION_EVENTS = true;
155
Element.prototype._setAttribute = Element.prototype.setAttribute;
156
Element.prototype.setAttribute =
157
function(name, val) {
158
this._setAttribute(name, val);
159
mutationWrapper({
160
target: this,
161
type: 'DOMAttrModified',
162
attrName: name,
163
attrValue: val });
164
};
165
}
166
}
167
168
isEnabled = NATIVE_MUTATION_EVENTS || HACKED_MUTATION_EVENTS;
169
170
/*------------------------------- PUBLIC API -------------------------------*/
171
172
// save results into cache
173
Dom.saveResults = saveResults;
174
175
// load results from cache
176
Dom.loadResults = loadResults;
177
178
// expire DOM tree cache
179
Dom.expireCache = expireCache;
180
181
// enable/disable cache
182
Dom.setCache = setCache;
183
184
})(this);
185
186