react / wstein / node_modules / jest-cli / node_modules / jsdom / node_modules / nwmatcher / src / modules / nwmatcher-cache.js
81144 views/*1* Copyright (C) 2007-2015 Diego Perini2* All rights reserved.3*4* Caching/memoization module for NWMatcher5*6* Added capabilities:7*8* - Mutation Events are feature tested and used safely9* - handle caching different document types HTML/XML/SVG10* - store result sets for different selectors / contexts11* - simultaneously control mutation on multiple documents12*13*/1415(function(global) {1617// export the public API for CommonJS implementations,18// for headless JS engines or for standard web browsers19var Dom =20// as CommonJS/NodeJS module21typeof exports == 'object' ? exports :22// create or extend NW namespace23((global.NW || (global.NW = global.Object())) &&24(global.NW.Dom || (global.NW.Dom = global.Object()))),2526Contexts = global.Object(),27Results = global.Object(),2829isEnabled = false,30isExpired = true,31isPaused = false,3233context = global.document,34root = context.documentElement,3536// last time cache initialization was called37lastCalled = 0,3839// minimum time allowed between calls to the cache initialization40minCacheRest = 15, //ms4142mutationTest =43function(type, callback) {44var isSupported = false,45root = document.documentElement,46div = document.createElement('div'),47handler = function() { isSupported = true; };48root.insertBefore(div, root.firstChild);49div.addEventListener(type, handler, true);50if (callback && callback.call) callback(div);51div.removeEventListener(type, handler, true);52root.removeChild(div);53return isSupported;54},5556// check for Mutation Events, DOMAttrModified should be57// enough to ensure DOMNodeInserted/DOMNodeRemoved exist58HACKED_MUTATION_EVENTS = false,5960NATIVE_MUTATION_EVENTS = root.addEventListener ?61mutationTest('DOMAttrModified', function(e) { e.setAttribute('id', 'nw'); }) : false,6263loadResults =64function(selector, from, doc, root) {65if (isEnabled && !isPaused) {66if (!isExpired) {67if (Results[selector] && Contexts[selector] === from) {68return Results[selector];69}70} else {71// pause caching while we are getting72// hammered by dom mutations (jdalton)73now = (new global.Date).getTime();74if ((now - lastCalled) < minCacheRest) {75isPaused = isExpired = true;76global.setTimeout(function() { isPaused = false; }, minCacheRest);77} else setCache(true, doc);78lastCalled = now;79}80}81return undefined;82},8384saveResults =85function(selector, from, doc, data) {86Contexts[selector] = from;87Results[selector] = data;88return;89},9091/*-------------------------------- CACHING ---------------------------------*/9293// invoked by mutation events to expire cached parts94mutationWrapper =95function(event) {96var d = event.target.ownerDocument || event.target;97stopMutation(d);98expireCache(d);99},100101// append mutation events102startMutation =103function(d) {104if (!d.isCaching && d.addEventListener) {105// FireFox/Opera/Safari/KHTML have support for Mutation Events106d.addEventListener('DOMAttrModified', mutationWrapper, true);107d.addEventListener('DOMNodeInserted', mutationWrapper, true);108d.addEventListener('DOMNodeRemoved', mutationWrapper, true);109d.isCaching = true;110}111},112113// remove mutation events114stopMutation =115function(d) {116if (d.isCaching && d.removeEventListener) {117d.removeEventListener('DOMAttrModified', mutationWrapper, true);118d.removeEventListener('DOMNodeInserted', mutationWrapper, true);119d.removeEventListener('DOMNodeRemoved', mutationWrapper, true);120d.isCaching = false;121}122},123124// enable/disable context caching system125// @d optional document context (iframe, xml document)126// script loading context will be used as default context127setCache =128function(enable, d) {129if (!!enable) {130isExpired = false;131startMutation(d);132} else {133isExpired = true;134stopMutation(d);135}136isEnabled = !!enable;137},138139// expire complete cache140// can be invoked by Mutation Events or141// programmatically by other code/scripts142// document context is mandatory no checks143expireCache =144function(d) {145isExpired = true;146Contexts = global.Object();147Results = global.Object();148};149150if (!NATIVE_MUTATION_EVENTS && root.addEventListener && Element && Element.prototype) {151if (mutationTest('DOMNodeInserted', function(e) { e.appendChild(document.createElement('div')); }) &&152mutationTest('DOMNodeRemoved', function(e) { e.removeChild(e.appendChild(document.createElement('div'))); })) {153HACKED_MUTATION_EVENTS = true;154Element.prototype._setAttribute = Element.prototype.setAttribute;155Element.prototype.setAttribute =156function(name, val) {157this._setAttribute(name, val);158mutationWrapper({159target: this,160type: 'DOMAttrModified',161attrName: name,162attrValue: val });163};164}165}166167isEnabled = NATIVE_MUTATION_EVENTS || HACKED_MUTATION_EVENTS;168169/*------------------------------- PUBLIC API -------------------------------*/170171// save results into cache172Dom.saveResults = saveResults;173174// load results from cache175Dom.loadResults = loadResults;176177// expire DOM tree cache178Dom.expireCache = expireCache;179180// enable/disable cache181Dom.setCache = setCache;182183})(this);184185186