|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /*
- * Copyright (C) 2007-2018 Diego Perini
- * All rights reserved.
- *
- * Caching/memoization module for NWMatcher
- *
- * Added capabilities:
- *
- * - Mutation Events are feature tested and used safely
- * - handle caching different document types HTML/XML/SVG
- * - store result sets for different selectors / contexts
- * - simultaneously control mutation on multiple documents
- *
- */
-
- (function(global) {
-
- // export the public API for CommonJS implementations,
- // for headless JS engines or for standard web browsers
- var Dom =
- // as CommonJS/NodeJS module
- typeof exports == 'object' ? exports :
- // create or extend NW namespace
- ((global.NW || (global.NW = { })) &&
- (global.NW.Dom || (global.NW.Dom = { }))),
-
- Contexts = { },
- Results = { },
-
- isEnabled = false,
- isExpired = true,
- isPaused = false,
-
- context = global.document,
- root = context.documentElement,
-
- // timing pauses
- now = 0,
-
- // last time cache initialization was called
- lastCalled = 0,
-
- // minimum time allowed between calls to the cache initialization
- minCacheRest = 15, //ms
-
- mutationTest =
- function(type, callback) {
- var isSupported = false,
- root = document.documentElement,
- div = document.createElement('div'),
- handler = function() { isSupported = true; };
- root.insertBefore(div, root.firstChild);
- div.addEventListener(type, handler, true);
- if (callback && callback.call) callback(div);
- div.removeEventListener(type, handler, true);
- root.removeChild(div);
- return isSupported;
- },
-
- // check for Mutation Events, DOMAttrModified should be
- // enough to ensure DOMNodeInserted/DOMNodeRemoved exist
- HACKED_MUTATION_EVENTS = false,
-
- NATIVE_MUTATION_EVENTS = root.addEventListener ?
- mutationTest('DOMAttrModified', function(e) { e.setAttribute('id', 'nw'); }) : false,
-
- loadResults =
- function(selector, from, doc, root) {
- if (isEnabled && !isPaused) {
- if (!isExpired) {
- if (Results[selector] && Contexts[selector] === from) {
- return Results[selector];
- }
- } else {
- // pause caching while we are getting
- // hammered by dom mutations (jdalton)
- now = (new Date).getTime();
- if ((now - lastCalled) < minCacheRest) {
- isPaused = isExpired = true;
- setTimeout(function() { isPaused = false; }, minCacheRest);
- } else setCache(true, doc);
- lastCalled = now;
- }
- }
- return undefined;
- },
-
- saveResults =
- function(selector, from, doc, data) {
- Contexts[selector] = from;
- Results[selector] = data;
- return;
- },
-
- /*-------------------------------- CACHING ---------------------------------*/
-
- // invoked by mutation events to expire cached parts
- mutationWrapper =
- function(event) {
- var d = event.target.ownerDocument || event.target;
- stopMutation(d);
- expireCache(d);
- },
-
- // append mutation events
- startMutation =
- function(d) {
- if (!d.isCaching && d.addEventListener) {
- // FireFox/Opera/Safari/KHTML have support for Mutation Events
- d.addEventListener('DOMAttrModified', mutationWrapper, true);
- d.addEventListener('DOMNodeInserted', mutationWrapper, true);
- d.addEventListener('DOMNodeRemoved', mutationWrapper, true);
- d.isCaching = true;
- }
- },
-
- // remove mutation events
- stopMutation =
- function(d) {
- if (d.isCaching && d.removeEventListener) {
- d.removeEventListener('DOMAttrModified', mutationWrapper, true);
- d.removeEventListener('DOMNodeInserted', mutationWrapper, true);
- d.removeEventListener('DOMNodeRemoved', mutationWrapper, true);
- d.isCaching = false;
- }
- },
-
- // enable/disable context caching system
- // @d optional document context (iframe, xml document)
- // script loading context will be used as default context
- setCache =
- function(enable, d) {
- if (!!enable) {
- isExpired = false;
- startMutation(d);
- } else {
- isExpired = true;
- stopMutation(d);
- }
- isEnabled = !!enable;
- },
-
- // expire complete cache
- // can be invoked by Mutation Events or
- // programmatically by other code/scripts
- // document context is mandatory no checks
- expireCache =
- function(d) {
- isExpired = true;
- Contexts = { };
- Results = { };
- };
-
- if (!NATIVE_MUTATION_EVENTS && root.addEventListener && Element && Element.prototype) {
- if (mutationTest('DOMNodeInserted', function(e) { e.appendChild(document.createElement('div')); }) &&
- mutationTest('DOMNodeRemoved', function(e) { e.removeChild(e.appendChild(document.createElement('div'))); })) {
- HACKED_MUTATION_EVENTS = true;
- Element.prototype._setAttribute = Element.prototype.setAttribute;
- Element.prototype.setAttribute =
- function(name, val) {
- this._setAttribute(name, val);
- mutationWrapper({
- target: this,
- type: 'DOMAttrModified',
- attrName: name,
- attrValue: val });
- };
- }
- }
-
- isEnabled = NATIVE_MUTATION_EVENTS || HACKED_MUTATION_EVENTS;
-
- /*------------------------------- PUBLIC API -------------------------------*/
-
- // save results into cache
- Dom.saveResults = saveResults;
-
- // load results from cache
- Dom.loadResults = loadResults;
-
- // expire DOM tree cache
- Dom.expireCache = expireCache;
-
- // enable/disable cache
- Dom.setCache = setCache;
-
- })(this);
|