home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 April / PCWorld_2008-04_cd.bin / v cisle / ozo / zotero-1.0.3.xpi / chrome / zotero.jar / content / zotero / browser.js < prev    next >
Encoding:
JavaScript  |  2007-11-26  |  22.0 KB  |  746 lines

  1. /*
  2.     ***** BEGIN LICENSE BLOCK *****
  3.     
  4.     Copyright (c) 2006  Center for History and New Media
  5.                         George Mason University, Fairfax, Virginia, USA
  6.                         http://chnm.gmu.edu
  7.     
  8.     Licensed under the Educational Community License, Version 1.0 (the "License");
  9.     you may not use this file except in compliance with the License.
  10.     You may obtain a copy of the License at
  11.     
  12.     http://www.opensource.org/licenses/ecl1.php
  13.     
  14.     Unless required by applicable law or agreed to in writing, software
  15.     distributed under the License is distributed on an "AS IS" BASIS,
  16.     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17.     See the License for the specific language governing permissions and
  18.     limitations under the License.
  19.     
  20.     
  21.     Based on code from Greasemonkey and PiggyBank
  22.     
  23.     
  24.     ***** END LICENSE BLOCK *****
  25. */
  26.  
  27. //
  28. // Zotero Ingester Browser Functions
  29. //
  30.  
  31. //////////////////////////////////////////////////////////////////////////////
  32. //
  33. // Zotero_Browser
  34. //
  35. //////////////////////////////////////////////////////////////////////////////
  36.  
  37. // Class to interface with the browser when ingesting data
  38.  
  39. var Zotero_Browser = new function() {
  40.     this.init = init;
  41.     this.scrapeThisPage = scrapeThisPage;
  42.     this.annotatePage = annotatePage;
  43.     this.toggleMode = toggleMode;
  44.     this.setCollapsed = setCollapsed;
  45.     this.chromeLoad = chromeLoad;
  46.     this.chromeUnload = chromeUnload;
  47.     this.contentLoad = contentLoad;
  48.     this.contentHide = contentHide;
  49.     this.tabClose = tabClose;
  50.     this.resize = resize;
  51.     this.updateStatus = updateStatus;
  52.     this.finishScraping = finishScraping;
  53.     this.itemDone = itemDone;
  54.     
  55.     this.tabbrowser = null;
  56.     this.appcontent = null;
  57.     this.statusImage = null;
  58.     
  59.     var _scrapePopupShowing = false;
  60.     var _browserData = new Object();
  61.     
  62.     var _blacklist = [
  63.         "googlesyndication.com",
  64.         "doubleclick.net",
  65.         "questionmarket.com",
  66.         "atdmt.com",
  67.         "aggregateknowledge.com"
  68.     ];
  69.     
  70.     var tools = {
  71.         'zotero-annotate-tb-add':{
  72.             cursor:"pointer",
  73.             event:"click",
  74.             callback:function(e) { _add("annotation", e) }
  75.         },
  76.         'zotero-annotate-tb-highlight':{
  77.             cursor:"text",
  78.             event:"mouseup",
  79.             callback:function(e) { _add("highlight", e) }
  80.         },
  81.         'zotero-annotate-tb-unhighlight':{
  82.             cursor:"text",
  83.             event:"mouseup",
  84.             callback:function(e) { _add("unhighlight", e) }
  85.         }
  86.     };
  87.  
  88.     //////////////////////////////////////////////////////////////////////////////
  89.     //
  90.     // Public Zotero_Browser methods
  91.     //
  92.     //////////////////////////////////////////////////////////////////////////////
  93.     
  94.     
  95.     /*
  96.      * Initialize some variables and prepare event listeners for when chrome is done
  97.      * loading
  98.      */
  99.     function init() {
  100.         if (!Zotero || !Zotero.initialized) {
  101.             return;
  102.         }
  103.         
  104.         Zotero_Browser.browserData = new Object();
  105.         Zotero_Browser._scrapePopupShowing = false;
  106.         Zotero.Ingester.ProxyMonitor.init();
  107.         Zotero.Ingester.MIMEHandler.init();
  108.         Zotero.Cite.MIMEHandler.init();
  109.         Zotero.Translate.init();
  110.         
  111.         window.addEventListener("load",
  112.             function(e) { Zotero_Browser.chromeLoad(e) }, false);
  113.         window.addEventListener("unload",
  114.             function(e) { Zotero_Browser.chromeUnload(e) }, false);
  115.     }
  116.     
  117.     /*
  118.      * Scrapes a page (called when the capture icon is clicked); takes a collection
  119.      * ID as the argument
  120.      */
  121.     function scrapeThisPage(saveLocation) {
  122.         if (!Zotero.stateCheck()) {
  123.             Zotero_Browser.progress.changeHeadline(Zotero.getString("ingester.scrapeError"));
  124.             var desc = Zotero.getString("ingester.scrapeError.transactionInProgress.previousError")
  125.                 + ' ' + Zotero.getString("general.restartFirefoxAndTryAgain");
  126.             Zotero_Browser.progress.addDescription(desc);
  127.             Zotero_Browser.progress.show();
  128.             Zotero_Browser.progress.startCloseTimer(8000);
  129.             return;
  130.         }
  131.         _getTabObject(this.tabbrowser.selectedBrowser).translate(saveLocation);
  132.     }
  133.     
  134.     /*
  135.      * flags a page for annotation
  136.      */
  137.     function annotatePage(id, browser) {
  138.         if (browser) {
  139.             var tab = _getTabObject(browser);
  140.         }
  141.         else {
  142.             var tab = _getTabObject(this.tabbrowser.selectedBrowser);
  143.         }
  144.         tab.annotateNextLoad = true;
  145.         tab.annotateID = id;
  146.     }
  147.     
  148.     /*
  149.      * toggles a tool on/off
  150.      */
  151.     function toggleMode(toggleTool, ignoreOtherTools) {
  152.         // make sure other tools are turned off
  153.         if(!ignoreOtherTools) {
  154.             for(var tool in tools) {
  155.                 if(tool != toggleTool && document.getElementById(tool).getAttribute("tool-active")) {
  156.                     toggleMode(tool, true);
  157.                 }
  158.             }
  159.         }
  160.         
  161.         // make sure annotation action is toggled
  162.         var tab = _getTabObject(Zotero_Browser.tabbrowser.selectedBrowser);
  163.         if(tab.page && tab.page.annotations && tab.page.annotations.clearAction) tab.page.annotations.clearAction();
  164.         
  165.         if(!toggleTool) return;
  166.         
  167.         var body = Zotero_Browser.tabbrowser.selectedBrowser.contentDocument.getElementsByTagName("body")[0];
  168.         var addElement = document.getElementById(toggleTool);
  169.         
  170.         if(addElement.getAttribute("tool-active")) {
  171.             // turn off
  172.             body.style.cursor = "auto";
  173.             addElement.removeAttribute("tool-active");
  174.             Zotero_Browser.tabbrowser.selectedBrowser.removeEventListener(tools[toggleTool].event, tools[toggleTool].callback, true);
  175.         } else {
  176.             body.style.cursor = tools[toggleTool].cursor;
  177.             addElement.setAttribute("tool-active", "true");
  178.             Zotero_Browser.tabbrowser.selectedBrowser.addEventListener(tools[toggleTool].event, tools[toggleTool].callback, true);
  179.         }
  180.     }
  181.     
  182.     /*
  183.      * expands all annotations
  184.      */
  185.     function setCollapsed(status) {
  186.         var tab = _getTabObject(Zotero_Browser.tabbrowser.selectedBrowser);
  187.         tab.page.annotations.setCollapsed(status);
  188.     }
  189.     
  190.     /*
  191.      * called to hide the collection selection popup
  192.      */
  193.     function hidePopup(collectionID) {
  194.         _scrapePopupShowing = false;
  195.     }
  196.  
  197.     /*
  198.      * called to show the collection selection popup
  199.      */
  200.     function showPopup(collectionID, parentElement) {
  201.         if(_scrapePopupShowing && parentElement.hasChildNodes()) {
  202.             return false;    // Don't dynamically reload popups that are already showing
  203.         }
  204.         _scrapePopupShowing = true;
  205.         parentElement.removeAllItems();
  206.         
  207.         if(collectionID == null) {    // show library
  208.             var newItem = document.createElement("menuitem");
  209.             newItem.setAttribute("label", Zotero.getString("pane.collections.library"));
  210.             newItem.setAttribute("class", "menuitem-iconic zotero-scrape-popup-library");
  211.             newItem.setAttribute("oncommand", 'Zotero_Browser.scrapeThisPage()');
  212.             parentElement.appendChild(newItem);
  213.         }
  214.         
  215.         var childrenList = Zotero.getCollections(collectionID);
  216.         for(var i = 0; i < childrenList.length; i++) {
  217.             if(childrenList[i].hasChildCollections()) {
  218.                 var newItem = document.createElement("menu");
  219.                 var subMenu = document.createElement("menupopup");
  220.                 subMenu.setAttribute("onpopupshowing", 'Zotero_Browser.showPopup("'+childrenList[i].getID()+'", this)');
  221.                 newItem.setAttribute("class", "menu-iconic zotero-scrape-popup-collection");
  222.                 newItem.appendChild(subMenu);
  223.             } else {
  224.                 var newItem = document.createElement("menuitem");
  225.                 newItem.setAttribute("class", "menuitem-iconic zotero-scrape-popup-collection");
  226.             }
  227.             newItem.setAttribute("label", childrenList[i].getName());
  228.             newItem.setAttribute("oncommand", 'Zotero_Browser.scrapeThisPage("'+childrenList[i].getID()+'")');
  229.             
  230.             parentElement.appendChild(newItem);
  231.         }
  232.         
  233.         return true;
  234.     }
  235.     
  236.     /*
  237.      * When chrome loads, register our event handlers with the appropriate interfaces
  238.      */
  239.     function chromeLoad() {
  240.         this.tabbrowser = document.getElementById("content");
  241.         this.appcontent = document.getElementById("appcontent");
  242.         this.statusImage = document.getElementById("zotero-status-image");
  243.         
  244.         // this gives us onLocationChange, for updating when tabs are switched/created
  245.         this.tabbrowser.addEventListener("TabClose",
  246.             function(e) {
  247.                 //Zotero.debug("TabClose");
  248.                 Zotero_Browser.tabClose(e);
  249.             }, false);
  250.         this.tabbrowser.addEventListener("TabSelect",
  251.             function(e) {
  252.                 //Zotero.debug("TabSelect");
  253.                 Zotero_Browser.updateStatus();
  254.             }, false);
  255.         // this is for pageshow, for updating the status of the book icon
  256.         this.appcontent.addEventListener("pageshow",
  257.             function(e) {
  258.                 //Zotero.debug("pageshow");
  259.                 Zotero_Browser.contentLoad(e);
  260.             }, true);
  261.         // this is for turning off the book icon when a user navigates away from a page
  262.         this.appcontent.addEventListener("pagehide",
  263.             function(e) {
  264.                 //Zotero.debug("pagehide");
  265.                 Zotero_Browser.contentHide(e);
  266.             }, true);
  267.         
  268.         this.tabbrowser.addEventListener("resize",
  269.             function(e) { Zotero_Browser.resize(e) }, false);
  270.         // Resize on text zoom changes
  271.         document.getElementById('cmd_textZoomReduce').addEventListener("command",
  272.             function(e) { Zotero_Browser.resize(e) }, false);
  273.         document.getElementById('cmd_textZoomEnlarge').addEventListener("command",
  274.             function(e) { Zotero_Browser.resize(e) }, false);
  275.         document.getElementById('cmd_textZoomReset').addEventListener("command",
  276.             function(e) { Zotero_Browser.resize(e) }, false);
  277.     }
  278.     
  279.     /*
  280.      * When chrome unloads, delete our document objects
  281.      */
  282.     function chromeUnload() {
  283.         delete Zotero_Browser.browserData;
  284.     }
  285.     
  286.     /*
  287.      * An event handler called when a new document is loaded. Creates a new document
  288.      * object, and updates the status of the capture icon
  289.      */
  290.     function contentLoad(event) {
  291.         var isHTML = event.originalTarget instanceof HTMLDocument;
  292.         var doc = event.originalTarget;
  293.         var rootDoc = doc;
  294.         
  295.         if(isHTML) {
  296.             // get the appropriate root document to check which browser we're on
  297.             while(rootDoc.defaultView.frameElement) {
  298.                 rootDoc = rootDoc.defaultView.frameElement.ownerDocument;
  299.             }
  300.             
  301.             // ignore blacklisted domains
  302.             try {
  303.                 if(doc.domain) {
  304.                     for each(var blacklistedURL in _blacklist) {
  305.                         if(doc.domain.substr(doc.domain.length-blacklistedURL.length) == blacklistedURL) {
  306.                             Zotero.debug("Ignoring blacklisted URL "+doc.location);
  307.                             return;
  308.                         }
  309.                     }
  310.                 }
  311.             }
  312.             catch (e) {}
  313.         }
  314.         
  315.         // Figure out what browser this contentDocument is associated with
  316.         var browser;
  317.         for(var i=0; i<this.tabbrowser.browsers.length; i++) {
  318.             if(rootDoc == this.tabbrowser.browsers[i].contentDocument) {
  319.                 browser = this.tabbrowser.browsers[i];
  320.                 break;
  321.             }
  322.         }
  323.         if(!browser) return;
  324.         
  325.         // get data object
  326.         var tab = _getTabObject(browser);
  327.         
  328.         if(isHTML) {
  329.             if(tab.annotateNextLoad) {                
  330.                 if(Zotero.Annotate.isAnnotated(tab.annotateID)) {
  331.                     window.alert(Zotero.getString("annotations.oneWindowWarning"));
  332.                 } else {        
  333.                     // enable annotation
  334.                     tab.page.annotations = new Zotero.Annotations(this, browser, tab.annotateID);
  335.                     Zotero.Annotate.setAnnotated(tab.annotateID, true);
  336.                     browser.contentWindow.addEventListener('beforeunload', function() {            
  337.                         // save annotations
  338.                         try {
  339.                             tab.page.annotations.save();
  340.                         } catch(e) {
  341.                             throw(e);
  342.                         } finally {
  343.                             Zotero.Annotate.setAnnotated(tab.page.annotations.itemID, false);
  344.                         }
  345.                     }, false);
  346.                 }
  347.             }
  348.         }
  349.         
  350.         // detect translators
  351.         tab.detectTranslators(rootDoc, doc);
  352.         
  353.         // clear annotateNextLoad
  354.         if(tab.annotateNextLoad) {
  355.             tab.annotateNextLoad = tab.annotateID = undefined;
  356.         }
  357.     }
  358.  
  359.     /*
  360.      * called to unregister Zotero icon, etc.
  361.      */
  362.     function contentHide(event) {
  363.         if(event.originalTarget instanceof HTMLDocument && !event.originalTarget.defaultView.frameElement) {
  364.             var doc = event.originalTarget;
  365.             
  366.             // Figure out what browser this contentDocument is associated with
  367.             var browser;
  368.             for(var i=0; i<this.tabbrowser.browsers.length; i++) {
  369.                 if(doc == this.tabbrowser.browsers[i].contentDocument) {
  370.                     browser = this.tabbrowser.browsers[i];
  371.                     break;
  372.                 }
  373.             }
  374.             
  375.             // clear data object
  376.             var tab = _getTabObject(browser);
  377.             if(!tab) return;
  378.             tab.clear();
  379.             
  380.             // update status
  381.             if(this.tabbrowser.selectedBrowser == browser) {
  382.                 updateStatus();
  383.             }
  384.         }
  385.     }
  386.     
  387.     /*
  388.      * called when a tab is closed
  389.      */
  390.     function tabClose(event) {
  391.         // Save annotations when closing a tab, since the browser is already
  392.         // gone from tabbrowser by the time contentHide() gets called
  393.         var tab = _getTabObject(event.target);
  394.         if(tab.page && tab.page.annotations) tab.page.annotations.save();
  395.         tab.clear();
  396.         
  397.         // To execute if document object does not exist
  398.         _deleteTabObject(event.target.linkedBrowser);
  399.         toggleMode(null);
  400.     }
  401.     
  402.     
  403.     /*
  404.      * called when the window is resized
  405.      */
  406.     function resize(event) {
  407.         var tab = _getTabObject(this.tabbrowser.selectedBrowser);
  408.         if(!tab.page.annotations) return;
  409.         
  410.         tab.page.annotations.refresh();
  411.     }
  412.     
  413.     /*
  414.      * Updates the status of the capture icon to reflect the scrapability or lack
  415.      * thereof of the current page
  416.      */
  417.     function updateStatus() {
  418.         var tab = _getTabObject(Zotero_Browser.tabbrowser.selectedBrowser);
  419.         
  420.         var captureIcon = tab.getCaptureIcon();
  421.         if(captureIcon) {
  422.             Zotero_Browser.statusImage.src = captureIcon;
  423.             Zotero_Browser.statusImage.tooltipText = tab.getCaptureTooltip();
  424.             Zotero_Browser.statusImage.hidden = false;
  425.         } else {
  426.             Zotero_Browser.statusImage.hidden = true;
  427.         }
  428.         
  429.         // set annotation bar status
  430.         if(tab.page.annotations) {
  431.             document.getElementById('zotero-annotate-tb').hidden = false;
  432.             toggleMode();
  433.         } else {
  434.             document.getElementById('zotero-annotate-tb').hidden = true;
  435.         }
  436.     }
  437.     
  438.     /*
  439.      * Callback to be executed when scraping is complete
  440.      */
  441.     function finishScraping(obj, returnValue) {
  442.         if(!returnValue) {
  443.             Zotero_Browser.progress.changeHeadline(Zotero.getString("ingester.scrapeError"));
  444.             // Include link to Known Translator Issues page
  445.             var url = "http://www.zotero.org/documentation/known_translator_issues";
  446.             var linkText = '<a href="' + url + '" tooltiptext="' + url + '">'
  447.                 + Zotero.getString('ingester.scrapeErrorDescription.linkText') + '</a>';
  448.             Zotero_Browser.progress.addDescription(Zotero.getString("ingester.scrapeErrorDescription", linkText));
  449.         }
  450.         Zotero_Browser.progress.startCloseTimer();
  451.     }
  452.     
  453.     
  454.     /*
  455.      * Callback to be executed when an item has been finished
  456.      */
  457.     function itemDone(obj, item, collection) {
  458.         var title = item.getField("title");
  459.         var icon = item.getImageSrc();
  460.         Zotero_Browser.progress.changeHeadline(Zotero.getString("ingester.scraping"));
  461.         Zotero_Browser.progress.addLines([title], [icon]);
  462.         
  463.         // add item to collection, if one was specified
  464.         if(collection) {
  465.             collection.addItem(item.getID());
  466.         }
  467.         
  468.         Zotero_Browser.progress.startCloseTimer();
  469.     }
  470.     
  471.     //////////////////////////////////////////////////////////////////////////////
  472.     //
  473.     // Private Zotero_Browser methods
  474.     //
  475.     //////////////////////////////////////////////////////////////////////////////
  476.     
  477.     /*
  478.      * Gets a data object given a browser window object
  479.      * 
  480.      * NOTE: Browser objects are associated with document objects via keys generated
  481.      * from the time the browser object is opened. I'm not sure if this is the
  482.      * appropriate mechanism for handling this, but it's what PiggyBank used and it
  483.      * appears to work.
  484.      *
  485.      * Currently, the data object contains only one property: "translators," which
  486.      * is an array of translators that should work with the given page as returned
  487.      * from Zotero.Translate.getTranslator()
  488.      */
  489.     function _getTabObject(browser) {
  490.         if(!browser) return false;
  491.         try {
  492.             var key = browser.getAttribute("zotero-key");
  493.             if(_browserData[key]) {
  494.                 return _browserData[key];
  495.             }
  496.         } finally {
  497.             if(!key) {
  498.                 var key = (new Date()).getTime();
  499.                 browser.setAttribute("zotero-key", key);
  500.                 return (_browserData[key] = new Zotero_Browser.Tab(browser));
  501.             }
  502.         }
  503.         return false;
  504.     }
  505.     
  506.     /*
  507.      * Deletes the document object associated with a given browser window object
  508.      */
  509.     function _deleteTabObject(browser) {
  510.         if(!browser) return false;
  511.         try {
  512.             var key = browser.getAttribute("zotero-key");
  513.             if(_browserData[key]) {
  514.                 delete _browserData[key];
  515.                 return true;
  516.             }
  517.         } finally {}
  518.         return false;
  519.     }
  520.     
  521.     /*
  522.      * adds an annotation
  523.      */
  524.      function _add(type, e) {
  525.         var tab = _getTabObject(Zotero_Browser.tabbrowser.selectedBrowser);
  526.         
  527.         if(type == "annotation") {
  528.             // ignore click if it's on an existing annotation
  529.             if(e.target.getAttribute("zotero-annotation")) return;
  530.             
  531.             var annotation = tab.page.annotations.createAnnotation();
  532.             annotation.initWithEvent(e);
  533.             
  534.             // disable add mode, now that we've used it
  535.             toggleMode();
  536.         } else {
  537.             try {
  538.                 var selection = Zotero_Browser.tabbrowser.selectedBrowser.contentWindow.getSelection();
  539.             } catch(err) {
  540.                 return;
  541.             }
  542.             if(selection.isCollapsed) return;
  543.             
  544.             if(type == "highlight") {
  545.                  tab.page.annotations.createHighlight(selection.getRangeAt(0));
  546.             } else if(type == "unhighlight") {
  547.                  tab.page.annotations.unhighlight(selection.getRangeAt(0));
  548.             }
  549.             
  550.             selection.removeAllRanges();
  551.         }
  552.         
  553.         // stop propagation
  554.         e.stopPropagation();
  555.         e.preventDefault();
  556.      }
  557. }
  558.  
  559.  
  560. //////////////////////////////////////////////////////////////////////////////
  561. //
  562. // Zotero_Browser.Tab
  563. //
  564. //////////////////////////////////////////////////////////////////////////////
  565.  
  566. Zotero_Browser.Tab = function(browser) {
  567.     this.browser = browser;
  568.     this.page = new Object();
  569. }
  570.  
  571. /*
  572.  * clears page-specific information
  573.  */
  574. Zotero_Browser.Tab.prototype.clear = function() {
  575.     delete this.page;
  576.     this.page = new Object();
  577. }
  578.  
  579. /*
  580.  * detects translators for this browser object
  581.  */
  582. Zotero_Browser.Tab.prototype.detectTranslators = function(rootDoc, doc) {
  583.     // if there's already a scrapable page in the browser window, and it's
  584.     // still there, ensure it is actually part of the page, then return
  585.     if(this.page.translators && this.page.translators.length && this.page.document.location) {
  586.         if(this._searchFrames(rootDoc, this.page.document)) {
  587.             return;
  588.         } else {
  589.             this.page.document = null;
  590.         }
  591.     }
  592.     
  593.     if(doc instanceof HTMLDocument) {
  594.         // get translators
  595.         var me = this;
  596.         
  597.         var translate = new Zotero.Translate("web");
  598.         translate.setDocument(doc);
  599.         translate.setHandler("translators", function(obj, item) { me._translatorsAvailable(obj, item) });
  600.         translate.getTranslators();
  601.     } else if(doc.documentURI.length > 7 && doc.documentURI.substr(0, 7) == "file://") {
  602.         this._attemptLocalFileImport(doc);
  603.     }
  604. }
  605.  
  606.  
  607. /*
  608.  * searches for a document in all of the frames of a given document
  609.  */
  610. Zotero_Browser.Tab.prototype._searchFrames = function(rootDoc, searchDoc) {
  611.     var frames = rootDoc.getElementsByTagName("frame");
  612.     for each(var frame in frames) {
  613.         if(frame.contentDocument &&
  614.           (frame.contentDocument == searchDoc ||
  615.           this._searchFrames(frame.contentDocument, searchDoc))) {
  616.             return true;
  617.         }
  618.     }
  619.     
  620.     return false;
  621. }
  622.  
  623. /*
  624.  * Attempts import of a file; to be run on local files only
  625.  */
  626. Zotero_Browser.Tab.prototype._attemptLocalFileImport = function(doc) {
  627.     if(doc.documentURI.substr(doc.documentURI.length-4).toLowerCase() == ".csl") {
  628.         // read CSL string
  629.         var csl = Zotero.File.getContentsFromURL(doc.documentURI);
  630.         if(csl.indexOf("http://purl.org/net/xbiblio/csl") != -1) {
  631.             // looks like a CSL; try to import
  632.             Zotero.Cite.installStyle(csl, doc.documentURI);
  633.         }
  634.     } else {
  635.         // see if we can import this file
  636.         var file = Components.classes["@mozilla.org/network/protocol;1?name=file"]
  637.                                     .getService(Components.interfaces.nsIFileProtocolHandler)
  638.                                     .getFileFromURLSpec(doc.documentURI);
  639.         
  640.         var me = this;
  641.         var translate = new Zotero.Translate("import");
  642.         translate.setLocation(file);
  643.         translate.setHandler("translators", function(obj, item) { me._translatorsAvailable(obj, item) });
  644.         translate.getTranslators();
  645.     }
  646. }
  647.  
  648. /*
  649.  * translate a page, saving in saveLocation
  650.  */
  651. Zotero_Browser.Tab.prototype.translate = function(saveLocation) {
  652.     if(this.page.translators && this.page.translators.length) {
  653.         Zotero_Browser.progress.show();
  654.         
  655.         if(saveLocation) {
  656.             saveLocation = Zotero.Collections.get(saveLocation);
  657.         } else { // save to currently selected collection, if a collection is selected
  658.             try {
  659.                 saveLocation = ZoteroPane.getSelectedCollection();
  660.             } catch(e) {}
  661.         }
  662.         
  663.         var me = this;
  664.         
  665.         if(!this.page.hasBeenTranslated) {
  666.             // use first translator available
  667.             this.page.translate.setTranslator(this.page.translators[0]);
  668.             this.page.translate.setHandler("select", me._selectItems);
  669.             this.page.translate.setHandler("done", function(obj, item) { Zotero_Browser.finishScraping(obj, item) });
  670.             this.page.hasBeenTranslated = true;
  671.         }
  672.         this.page.translate.clearHandlers("itemDone");
  673.         this.page.translate.setHandler("itemDone", function(obj, item) { Zotero_Browser.itemDone(obj, item, saveLocation) });
  674.         
  675.         this.page.translate.translate();
  676.     }
  677. }
  678.  
  679. /*
  680.  * returns the URL of the image representing the translator to be called on the
  681.  * current page, or false if the page cannot be scraped
  682.  */
  683. Zotero_Browser.Tab.prototype.getCaptureIcon = function() {
  684.     if(this.page.translators && this.page.translators.length) {
  685.         var itemType = this.page.translators[0].itemType;
  686.         if(itemType == "multiple") {
  687.             // Use folder icon for multiple types, for now
  688.             return "chrome://zotero/skin/treesource-collection.png";
  689.         } else {
  690.             return Zotero.ItemTypes.getImageSrc(itemType);
  691.         }
  692.     }
  693.     
  694.     return false;
  695. }
  696.  
  697. Zotero_Browser.Tab.prototype.getCaptureTooltip = function() {
  698.     if (this.page.translators && this.page.translators.length) {
  699.         var arr = [Zotero.getString('ingester.saveToZotero')];
  700.         if (this.page.translators[0].itemType == 'multiple') {
  701.             arr.push('...');
  702.         }
  703.         arr.push (' ' , '(' + this.page.translators[0].label + ')');
  704.         return Zotero.localeJoin(arr, '');
  705.     }
  706.     return '';
  707. }
  708.  
  709.  
  710. /**********CALLBACKS**********/
  711.  
  712. /*
  713.  * called when a user is supposed to select items
  714.  */
  715. Zotero_Browser.Tab.prototype._selectItems = function(obj, itemList) {
  716.     // this is kinda ugly, mozillazine made me do it! honest!
  717.     var io = { dataIn:itemList, dataOut:null }
  718.     var newDialog = window.openDialog("chrome://zotero/content/ingester/selectitems.xul",
  719.         "_blank","chrome,modal,centerscreen,resizable=yes", io);
  720.     
  721.     if(!io.dataOut) {    // user selected no items, so close the progress indicatior
  722.         Zotero_Browser.progress.close();
  723.     }
  724.     
  725.     return io.dataOut;
  726. }
  727.  
  728. /*
  729.  * called when translators are available
  730.  */
  731. Zotero_Browser.Tab.prototype._translatorsAvailable = function(translate, translators) {
  732.     if(translators && translators.length) {
  733.         this.page.translate = translate;
  734.         this.page.translators = translators;
  735.         this.page.document = translate.document;
  736.     } else if(translate.type != "import" && translate.document.documentURI.length > 7
  737.             && translate.document.documentURI.substr(0, 7) == "file://") {
  738.         this._attemptLocalFileImport(translate.document);
  739.     }
  740.     Zotero_Browser.updateStatus();
  741. }
  742.  
  743. // Handles the display of a div showing progress in scraping
  744. Zotero_Browser.progress = new Zotero.ProgressWindow();
  745.  
  746. Zotero_Browser.init();