From b919d27d1e3b43d5bb488400890aa5fda55e95b8 Mon Sep 17 00:00:00 2001 From: aurimasv Date: Tue, 29 Jan 2013 03:25:05 -0600 Subject: [PATCH 1/5] Allow web translators to monitor DOM nodes for changes in order to re-trigger detectWeb --- chrome/content/zotero/browser.js | 17 +++--- .../zotero/xpcom/translation/translate.js | 54 ++++++++++++++++++- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/chrome/content/zotero/browser.js b/chrome/content/zotero/browser.js index 8d12f08691..b293b875a0 100644 --- a/chrome/content/zotero/browser.js +++ b/chrome/content/zotero/browser.js @@ -46,6 +46,7 @@ var Zotero_Browser = new function() { this.toggleCollapsed = toggleCollapsed; this.chromeLoad = chromeLoad; this.contentLoad = contentLoad; + this.itemUpdated = itemUpdated; this.contentHide = contentHide; this.tabClose = tabClose; this.resize = resize; @@ -344,7 +345,7 @@ var Zotero_Browser = new function() { if(isHTML) { var contentWin = doc.defaultView; if(!contentWin.haveZoteroEventListener) { - contentWin.addEventListener("ZoteroItemUpdated", itemUpdated, false); + contentWin.addEventListener("ZoteroItemUpdated", function(event) { itemUpdated(event.originalTarget) }, false); contentWin.haveZoteroEventListener = true; } } @@ -380,14 +381,13 @@ var Zotero_Browser = new function() { /** * Called when item should be updated due to a DOM event */ - function itemUpdated(event) { + function itemUpdated(doc) { try { - var doc = event.originalTarget; - var rootDoc = (doc instanceof HTMLDocument ? doc.defaultView.top.document : doc); - var browser = Zotero_Browser.tabbrowser.getBrowserForDocument(rootDoc); - var tab = _getTabObject(browser); - if(doc == tab.page.document || doc == rootDoc) tab.clear(); - tab.detectTranslators(rootDoc, doc); + var rootDoc = (doc instanceof HTMLDocument ? doc.defaultView.top.document : doc); + var browser = Zotero_Browser.tabbrowser.getBrowserForDocument(rootDoc); + var tab = _getTabObject(browser); + if(doc == tab.page.document || doc == rootDoc) tab.clear(); + tab.detectTranslators(rootDoc, doc); } catch(e) { Zotero.debug(e); } @@ -649,6 +649,7 @@ Zotero_Browser.Tab.prototype.detectTranslators = function(rootDoc, doc) { var translate = new Zotero.Translate.Web(); translate.setDocument(doc); translate.setHandler("translators", function(obj, item) { me._translatorsAvailable(obj, item) }); + translate.setHandler("pageModified", function(translate, doc) { Zotero_Browser.itemUpdated(doc) }); translate.getTranslators(true); } else if(doc.documentURI.substr(0, 7) == "file://") { this._attemptLocalFileImport(doc); diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js index 12c883c37f..cf3ab76327 100644 --- a/chrome/content/zotero/xpcom/translation/translate.js +++ b/chrome/content/zotero/xpcom/translation/translate.js @@ -635,6 +635,52 @@ Zotero.Translate.Sandbox = { // call super Zotero.Translate.Sandbox.Base._itemDone(translate, item); + }, + + /** + * Tells Zotero to monitor changes to the DOM and re-trigger detectWeb + * Can only be set during the detectWeb call + * @param {DOMNode} target Document node to monitor for changes + * @param {MutationObserverInit} [config] specifies which DOM mutations should be reported + */ + "monitorDOMChanges":function(translate, target, config) { + if(translate._currentState != "detect") { + Zotero.debug("Translate: monitorDOMChanges can only be called during the 'detect' stage"); + return; + } + + var window = translate.document.defaultView + var mutationObserver = window && ( window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver ); + if(!mutationObserver) { + Zotero.debug("Translate: This browser does not support mutation observers."); + return; + } + + var translator = translate._potentialTranslators[0]; + if(!translate._registeredDOMObservers[translator.translatorID]) + translate._registeredDOMObservers[translator.translatorID] = []; + var obs = translate._registeredDOMObservers[translator.translatorID]; + + //do not re-register observer by the same translator for the same node + if(obs.indexOf(target) != -1) { + Zotero.debug("Translate: Already monitoring this node"); + return; + } + + obs.push(target); + + var observer = new mutationObserver(function(mutations, observer) { + obs.splice(obs.indexOf(target),1); + observer.disconnect(); + + Zotero.debug("Translate: Page modified."); + //we don't really care what got updated + var doc = mutations[0].target.ownerDocument; + translate._runHandler("pageModified", doc); + }); + + observer.observe(target, config || {childList: true, subtree: true}); + Zotero.debug("Translate: Mutation observer registered on <" + target.nodeName + "> node"); } }, @@ -843,6 +889,11 @@ Zotero.Translate.Base.prototype = { * complete * passed: an array of appropriate translators * returns: N/A + * pageModified + * valid: web + * called: when a web page has been modified + * passed: the document object for the modified page + * returns: N/A * @param {Function} handler Callback function. All handlers will be passed the current * translate instance as the first argument. The second argument is dependent on the handler. */ @@ -909,7 +960,7 @@ Zotero.Translate.Base.prototype = { if(this._handlers[type]) { // compile list of arguments if(this._parentTranslator) { - // if there is a parent translator, make sure we don't the Zotero.Translate + // if there is a parent translator, make sure we don't pass the Zotero.Translate // object, since it could open a security hole var args = [null]; } else { @@ -1528,6 +1579,7 @@ Zotero.Translate.Base.prototype = { * this Translate instance. */ Zotero.Translate.Web = function() { + this._registeredDOMObservers = {} this.init(); } Zotero.Translate.Web.prototype = new Zotero.Translate.Base(); From 72d3b8b711f267212644f5f81f5c51d8b5418c6b Mon Sep 17 00:00:00 2001 From: aurimasv Date: Mon, 18 Feb 2013 00:16:44 -0600 Subject: [PATCH 2/5] Set this.translator for "detect" calls so that parentTranslator is set correctly --- chrome/content/zotero/xpcom/translation/translate.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js index 5c46ba2e72..5b013255d3 100644 --- a/chrome/content/zotero/xpcom/translation/translate.js +++ b/chrome/content/zotero/xpcom/translation/translate.js @@ -1310,17 +1310,19 @@ Zotero.Translate.Base.prototype = { var me = this; this._loadTranslator(this._potentialTranslators[0], - function() { me._detectTranslatorLoaded() }); + function() { me._detectTranslatorLoaded(me._potentialTranslators[0]) }); }, /** * Runs detect code for a translator */ - "_detectTranslatorLoaded":function() { + "_detectTranslatorLoaded":function(translator) { this._prepareDetection(); this.incrementAsyncProcesses("Zotero.Translate#getTranslators"); + this.translator = [translator]; + try { var returnValue = this._sandboxManager.sandbox["detect"+this._entryFunctionSuffix].apply(null, this._getParameters()); } catch(e) { From db8b8a96621b0812148e49dffa64c6dc823cc505 Mon Sep 17 00:00:00 2001 From: aurimasv Date: Mon, 18 Feb 2013 01:42:08 -0600 Subject: [PATCH 3/5] Do not overwrite set translators --- chrome/content/zotero/xpcom/translation/translate.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js index 5b013255d3..07d6e81aea 100644 --- a/chrome/content/zotero/xpcom/translation/translate.js +++ b/chrome/content/zotero/xpcom/translation/translate.js @@ -1167,6 +1167,8 @@ Zotero.Translate.Base.prototype = { if(!returnValue && error) errorString = this._generateErrorString(error); if(oldState === "detect") { + if(this._clearTranslator) delete this.translator; + if(this._potentialTranslators.length) { var lastTranslator = this._potentialTranslators.shift(); var lastProperToProxyFunction = this._properToProxyFunctions ? this._properToProxyFunctions.shift() : null; @@ -1321,7 +1323,10 @@ Zotero.Translate.Base.prototype = { this.incrementAsyncProcesses("Zotero.Translate#getTranslators"); - this.translator = [translator]; + if(!this.translator) { + this.translator = [translator]; + this._clearTranslator = true; + } try { var returnValue = this._sandboxManager.sandbox["detect"+this._entryFunctionSuffix].apply(null, this._getParameters()); From 63b9c9fd36ca9bac39933993228f0b25aad15058 Mon Sep 17 00:00:00 2001 From: aurimasv Date: Mon, 18 Feb 2013 01:47:45 -0600 Subject: [PATCH 4/5] And also don't leave behind unnecessary properties. --- chrome/content/zotero/xpcom/translation/translate.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js index 07d6e81aea..52cfdf6051 100644 --- a/chrome/content/zotero/xpcom/translation/translate.js +++ b/chrome/content/zotero/xpcom/translation/translate.js @@ -1167,7 +1167,10 @@ Zotero.Translate.Base.prototype = { if(!returnValue && error) errorString = this._generateErrorString(error); if(oldState === "detect") { - if(this._clearTranslator) delete this.translator; + if(this._clearTranslator) { + delete this.translator; + delete this._clearTranslator; + } if(this._potentialTranslators.length) { var lastTranslator = this._potentialTranslators.shift(); From c4f008a97d6596b3d2dbdc4f6959c23ded920b39 Mon Sep 17 00:00:00 2001 From: aurimasv Date: Fri, 1 Mar 2013 03:29:11 -0600 Subject: [PATCH 5/5] Keep track of currently loaded translator --- .../zotero/xpcom/translation/translate.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/chrome/content/zotero/xpcom/translation/translate.js b/chrome/content/zotero/xpcom/translation/translate.js index 52cfdf6051..df3b687e38 100644 --- a/chrome/content/zotero/xpcom/translation/translate.js +++ b/chrome/content/zotero/xpcom/translation/translate.js @@ -1167,11 +1167,6 @@ Zotero.Translate.Base.prototype = { if(!returnValue && error) errorString = this._generateErrorString(error); if(oldState === "detect") { - if(this._clearTranslator) { - delete this.translator; - delete this._clearTranslator; - } - if(this._potentialTranslators.length) { var lastTranslator = this._potentialTranslators.shift(); var lastProperToProxyFunction = this._properToProxyFunctions ? this._properToProxyFunctions.shift() : null; @@ -1315,22 +1310,17 @@ Zotero.Translate.Base.prototype = { var me = this; this._loadTranslator(this._potentialTranslators[0], - function() { me._detectTranslatorLoaded(me._potentialTranslators[0]) }); + function() { me._detectTranslatorLoaded() }); }, /** * Runs detect code for a translator */ - "_detectTranslatorLoaded":function(translator) { + "_detectTranslatorLoaded":function() { this._prepareDetection(); this.incrementAsyncProcesses("Zotero.Translate#getTranslators"); - if(!this.translator) { - this.translator = [translator]; - this._clearTranslator = true; - } - try { var returnValue = this._sandboxManager.sandbox["detect"+this._entryFunctionSuffix].apply(null, this._getParameters()); } catch(e) { @@ -1363,6 +1353,7 @@ Zotero.Translate.Base.prototype = { this._generateSandbox(); } + this._currentTranslator = translator; this._runningAsyncProcesses = 0; this._returnValue = undefined; this._aborted = false; @@ -1450,8 +1441,8 @@ Zotero.Translate.Base.prototype = { this._sandboxManager.sandbox.Zotero.isConnector = Zotero.isConnector || false; this._sandboxManager.sandbox.Zotero.isServer = Zotero.isServer || false; this._sandboxManager.sandbox.Zotero.parentTranslator = this._parentTranslator - && this._parentTranslator.translator && this._parentTranslator.translator[0] ? - this._parentTranslator.translator[0].translatorID : null; + && this._parentTranslator._currentTranslator ? + this._parentTranslator._currentTranslator.translatorID : null; // create shortcuts this._sandboxManager.sandbox.Z = this._sandboxManager.sandbox.Zotero;