closes #440, In-page highlighting of snapshots
closes #441, In-page annotations of snapshots testers: please check pages containing both annotations and highlights to make sure everything works correctly
This commit is contained in:
parent
09920275ac
commit
70d06e02f2
|
@ -5,6 +5,5 @@ locale zotero de-DE chrome/locale/de-DE/zotero/
|
|||
skin zotero default chrome/skin/default/zotero/
|
||||
|
||||
overlay chrome://browser/content/browser.xul chrome://zotero/content/overlay.xul
|
||||
overlay chrome://browser/content/browser.xul chrome://zotero/content/ingester/browser.xul
|
||||
style chrome://browser/content/browser.xul chrome://zotero/skin/zotero.css
|
||||
style chrome://global/content/customizeToolbar.xul chrome://zotero/skin/zotero.css
|
766
chrome/content/zotero/browser.js
Normal file
766
chrome/content/zotero/browser.js
Normal file
|
@ -0,0 +1,766 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (c) 2006 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://chnm.gmu.edu
|
||||
|
||||
Licensed under the Educational Community License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.opensource.org/licenses/ecl1.php
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
Based on code from Greasemonkey and PiggyBank
|
||||
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
//
|
||||
// Zotero Ingester Browser Functions
|
||||
//
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Zotero_Browser
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Class to interface with the browser when ingesting data
|
||||
|
||||
var Zotero_Browser = new function() {
|
||||
this.init = init;
|
||||
this.scrapeThisPage = scrapeThisPage;
|
||||
this.annotateThisPage = annotateThisPage;
|
||||
this.toggleAnnotateMode = toggleAnnotateMode;
|
||||
this.toggleHighlightMode = toggleHighlightMode;
|
||||
this.chromeLoad = chromeLoad;
|
||||
this.chromeUnload = chromeUnload;
|
||||
this.contentLoad = contentLoad;
|
||||
this.contentHide = contentHide;
|
||||
this.tabSelect = tabSelect;
|
||||
this.tabClose = tabClose;
|
||||
this.resize = resize;
|
||||
|
||||
this.tabbrowser = null;
|
||||
this.appcontent = null;
|
||||
this.statusImage = null;
|
||||
|
||||
var _scrapePopupShowing = false;
|
||||
var _annotateNextLoad = false;
|
||||
var _browserData = new Object();
|
||||
|
||||
var _blacklist = [
|
||||
"googlesyndication.com",
|
||||
"doubleclick.net",
|
||||
"questionmarket.com",
|
||||
"atdmt.com"
|
||||
];
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Public Zotero_Browser methods
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/*
|
||||
* Initialize some variables and prepare event listeners for when chrome is done
|
||||
* loading
|
||||
*/
|
||||
function init() {
|
||||
Zotero_Browser.browserData = new Object();
|
||||
Zotero_Browser._scrapePopupShowing = false;
|
||||
Zotero.Ingester.ProxyMonitor.init();
|
||||
Zotero.Ingester.MIMEHandler.init();
|
||||
Zotero.Translate.init();
|
||||
|
||||
window.addEventListener("load",
|
||||
function(e) { Zotero_Browser.chromeLoad(e) }, false);
|
||||
window.addEventListener("unload",
|
||||
function(e) { Zotero_Browser.chromeUnload(e) }, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scrapes a page (called when the capture icon is clicked); takes a collection
|
||||
* ID as the argument
|
||||
*/
|
||||
function scrapeThisPage(saveLocation) {
|
||||
_getTabObject(this.tabbrowser.selectedBrowser).translate(saveLocation);
|
||||
}
|
||||
|
||||
/*
|
||||
* flags a page for annotation
|
||||
*/
|
||||
function annotateThisPage(id) {
|
||||
var tab = _getTabObject(this.tabbrowser.selectedBrowser);
|
||||
tab.annotateNextLoad = true;
|
||||
tab.annotateID = id;
|
||||
}
|
||||
|
||||
/*
|
||||
* toggles the "add annotation" button
|
||||
*/
|
||||
function toggleAnnotateMode() {
|
||||
if(document.getElementById('zotero-annotate-tb-highlight').getAttribute("tool-active")) {
|
||||
toggleHighlightMode();
|
||||
}
|
||||
|
||||
var body = Zotero_Browser.tabbrowser.selectedBrowser.contentDocument.getElementsByTagName("body")[0];
|
||||
var addElement = document.getElementById('zotero-annotate-tb-add');
|
||||
|
||||
if(addElement.getAttribute("tool-active")) {
|
||||
body.style.cursor = "auto";
|
||||
addElement.removeAttribute("tool-active");
|
||||
Zotero_Browser.tabbrowser.selectedBrowser.removeEventListener("click", _addAnnotation, true);
|
||||
} else {
|
||||
body.style.cursor = "pointer";
|
||||
addElement.setAttribute("tool-active", "true");
|
||||
Zotero_Browser.tabbrowser.selectedBrowser.addEventListener("click", _addAnnotation, true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* toggles the "higlight" button
|
||||
*/
|
||||
function toggleHighlightMode() {
|
||||
if(document.getElementById('zotero-annotate-tb-add').getAttribute("tool-active")) {
|
||||
toggleAnnotateMode();
|
||||
}
|
||||
|
||||
var body = Zotero_Browser.tabbrowser.selectedBrowser.contentDocument.getElementsByTagName("body")[0];
|
||||
var addElement = document.getElementById('zotero-annotate-tb-highlight');
|
||||
|
||||
if(addElement.getAttribute("tool-active")) {
|
||||
body.style.cursor = "auto";
|
||||
addElement.removeAttribute("tool-active");
|
||||
Zotero_Browser.tabbrowser.selectedBrowser.removeEventListener("mouseup", _addHighlight, true);
|
||||
} else {
|
||||
body.style.cursor = "text";
|
||||
addElement.setAttribute("tool-active", "true");
|
||||
Zotero_Browser.tabbrowser.selectedBrowser.addEventListener("mouseup", _addHighlight, true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* called to hide the collection selection popup
|
||||
*/
|
||||
function hidePopup(collectionID) {
|
||||
_scrapePopupShowing = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* called to show the collection selection popup
|
||||
*/
|
||||
function showPopup(collectionID, parentElement) {
|
||||
if(_scrapePopupShowing && parentElement.hasChildNodes()) {
|
||||
return false; // Don't dynamically reload popups that are already showing
|
||||
}
|
||||
_scrapePopupShowing = true;
|
||||
parentElement.removeAllItems();
|
||||
|
||||
if(collectionID == null) { // show library
|
||||
var newItem = document.createElement("menuitem");
|
||||
newItem.setAttribute("label", Zotero.getString("pane.collections.library"));
|
||||
newItem.setAttribute("class", "menuitem-iconic zotero-scrape-popup-library");
|
||||
newItem.setAttribute("oncommand", 'Zotero_Browser.scrapeThisPage()');
|
||||
parentElement.appendChild(newItem);
|
||||
}
|
||||
|
||||
var childrenList = Zotero.getCollections(collectionID);
|
||||
for(var i = 0; i < childrenList.length; i++) {
|
||||
if(childrenList[i].hasChildCollections()) {
|
||||
var newItem = document.createElement("menu");
|
||||
var subMenu = document.createElement("menupopup");
|
||||
subMenu.setAttribute("onpopupshowing", 'Zotero_Browser.showPopup("'+childrenList[i].getID()+'", this)');
|
||||
newItem.setAttribute("class", "menu-iconic zotero-scrape-popup-collection");
|
||||
newItem.appendChild(subMenu);
|
||||
} else {
|
||||
var newItem = document.createElement("menuitem");
|
||||
newItem.setAttribute("class", "menuitem-iconic zotero-scrape-popup-collection");
|
||||
}
|
||||
newItem.setAttribute("label", childrenList[i].getName());
|
||||
newItem.setAttribute("oncommand", 'Zotero_Browser.scrapeThisPage("'+childrenList[i].getID()+'")');
|
||||
|
||||
parentElement.appendChild(newItem);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* When chrome loads, register our event handlers with the appropriate interfaces
|
||||
*/
|
||||
function chromeLoad() {
|
||||
this.tabbrowser = document.getElementById("content");
|
||||
this.appcontent = document.getElementById("appcontent");
|
||||
this.statusImage = document.getElementById("zotero-status-image");
|
||||
|
||||
// this gives us onLocationChange, for updating when tabs are switched/created
|
||||
this.tabbrowser.addEventListener("TabClose",
|
||||
function(e) { Zotero_Browser.tabClose(e) }, false);
|
||||
this.tabbrowser.addEventListener("TabSelect",
|
||||
function(e) { Zotero_Browser.tabSelect(e) }, false);
|
||||
// this is for pageshow, for updating the status of the book icon
|
||||
this.appcontent.addEventListener("pageshow",
|
||||
function(e) { Zotero_Browser.contentLoad(e) }, true);
|
||||
// this is for turning off the book icon when a user navigates away from a page
|
||||
this.appcontent.addEventListener("pagehide",
|
||||
function(e) { Zotero_Browser.contentHide(e) }, true);
|
||||
this.tabbrowser.addEventListener("resize",
|
||||
function(e) { Zotero_Browser.resize(e) }, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* When chrome unloads, delete our document objects
|
||||
*/
|
||||
function chromeUnload() {
|
||||
delete Zotero_Browser.browserData;
|
||||
}
|
||||
|
||||
/*
|
||||
* An event handler called when a new document is loaded. Creates a new document
|
||||
* object, and updates the status of the capture icon
|
||||
*/
|
||||
function contentLoad(event) {
|
||||
var isHTML = event.originalTarget instanceof HTMLDocument;
|
||||
|
||||
if(isHTML) {
|
||||
var doc = event.originalTarget;
|
||||
var rootDoc = doc;
|
||||
|
||||
// get the appropriate root document to check which browser we're on
|
||||
while(rootDoc.defaultView.frameElement) {
|
||||
rootDoc = rootDoc.defaultView.frameElement.ownerDocument;
|
||||
}
|
||||
|
||||
// ignore blacklisted domains
|
||||
if(doc.domain) {
|
||||
for each(var blacklistedURL in _blacklist) {
|
||||
if(doc.domain.substr(doc.domain.length-blacklistedURL.length) == blacklistedURL) {
|
||||
Zotero.debug("Ignoring blacklisted URL "+doc.location);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out what browser this contentDocument is associated with
|
||||
var browser;
|
||||
for(var i=0; i<this.tabbrowser.browsers.length; i++) {
|
||||
if(rootDoc == this.tabbrowser.browsers[i].contentDocument) {
|
||||
browser = this.tabbrowser.browsers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!browser) return;
|
||||
|
||||
// get data object
|
||||
var tab = _getTabObject(browser);
|
||||
|
||||
if(isHTML) {
|
||||
if(tab.annotateNextLoad) {
|
||||
// enable annotation
|
||||
tab.page.annotations = new Zotero.Annotations(browser, tab.annotateID);
|
||||
}
|
||||
|
||||
// detect translators
|
||||
tab.detectTranslators(rootDoc, doc);
|
||||
|
||||
// update status
|
||||
if(this.tabbrowser.selectedBrowser == browser) {
|
||||
_updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
// clear annotateNextLoad
|
||||
if(tab.annotateNextLoad) {
|
||||
tab.annotateNextLoad = tab.annotateID = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* called to unregister Zotero icon, etc.
|
||||
*/
|
||||
function contentHide(event) {
|
||||
if(event.originalTarget instanceof HTMLDocument && !event.originalTarget.defaultView.frameElement) {
|
||||
var doc = event.originalTarget;
|
||||
|
||||
// Figure out what browser this contentDocument is associated with
|
||||
var browser;
|
||||
for(var i=0; i<this.tabbrowser.browsers.length; i++) {
|
||||
if(doc == this.tabbrowser.browsers[i].contentDocument) {
|
||||
browser = this.tabbrowser.browsers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// clear data object
|
||||
var tab = _getTabObject(browser);
|
||||
|
||||
// save annotations
|
||||
if(tab.page.annotations) tab.page.annotations.save();
|
||||
|
||||
tab.clear();
|
||||
|
||||
// update status
|
||||
if(this.tabbrowser.selectedBrowser == browser) {
|
||||
_updateStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* called when a tab is closed
|
||||
*/
|
||||
function tabClose(event) {
|
||||
// To execute if document object does not exist
|
||||
_deleteTabObject(event.target.linkedBrowser);
|
||||
_deselectTools();
|
||||
}
|
||||
|
||||
/*
|
||||
* called when a tab is switched
|
||||
*/
|
||||
function tabSelect(event) {
|
||||
_updateStatus();
|
||||
}
|
||||
|
||||
/*
|
||||
* called when the window is resized
|
||||
*/
|
||||
function resize(event) {
|
||||
var tab = _getTabObject(this.tabbrowser.selectedBrowser);
|
||||
if(!tab.page.annotations) return;
|
||||
|
||||
tab.page.annotations.refresh();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Private Zotero_Browser methods
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Gets a data object given a browser window object
|
||||
*
|
||||
* NOTE: Browser objects are associated with document objects via keys generated
|
||||
* from the time the browser object is opened. I'm not sure if this is the
|
||||
* appropriate mechanism for handling this, but it's what PiggyBank used and it
|
||||
* appears to work.
|
||||
*
|
||||
* Currently, the data object contains only one property: "translators," which
|
||||
* is an array of translators that should work with the given page as returned
|
||||
* from Zotero.Translate.getTranslator()
|
||||
*/
|
||||
function _getTabObject(browser) {
|
||||
if(!browser) return false;
|
||||
try {
|
||||
var key = browser.getAttribute("zotero-key");
|
||||
if(_browserData[key]) {
|
||||
return _browserData[key];
|
||||
}
|
||||
} finally {
|
||||
if(!key) {
|
||||
var key = (new Date()).getTime();
|
||||
browser.setAttribute("zotero-key", key);
|
||||
return (_browserData[key] = new Zotero_Browser.Tab(browser));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes the document object associated with a given browser window object
|
||||
*/
|
||||
function _deleteTabObject(browser) {
|
||||
if(!browser) return false;
|
||||
try {
|
||||
var key = browser.getAttribute("zotero-key");
|
||||
if(_browserData[key]) {
|
||||
delete _browserData[key];
|
||||
return true;
|
||||
}
|
||||
} finally {}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the status of the capture icon to reflect the scrapability or lack
|
||||
* thereof of the current page
|
||||
*/
|
||||
function _updateStatus() {
|
||||
var tab = _getTabObject(Zotero_Browser.tabbrowser.selectedBrowser);
|
||||
|
||||
var captureIcon = tab.getCaptureIcon();
|
||||
if(captureIcon) {
|
||||
Zotero_Browser.statusImage.src = captureIcon;
|
||||
Zotero_Browser.statusImage.hidden = false;
|
||||
} else {
|
||||
Zotero_Browser.statusImage.hidden = true;
|
||||
}
|
||||
|
||||
// set annotation bar status
|
||||
if(tab.page.annotations) {
|
||||
document.getElementById('zotero-annotate-tb').hidden = false;
|
||||
_deselectTools();
|
||||
} else {
|
||||
document.getElementById('zotero-annotate-tb').hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Deselects annotation tools
|
||||
*/
|
||||
function _deselectTools() {
|
||||
if(document.getElementById('zotero-annotate-tb-add').getAttribute("tool-active")) {
|
||||
toggleAnnotateMode();
|
||||
}
|
||||
if(document.getElementById('zotero-annotate-tb-highlight').getAttribute("tool-active")) {
|
||||
toggleHighlightMode();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* adds an annotation
|
||||
*/
|
||||
function _addAnnotation(e) {
|
||||
var tab = _getTabObject(Zotero_Browser.tabbrowser.selectedBrowser);
|
||||
|
||||
// ignore click if it's on an existing annotation
|
||||
if(e.target.getAttribute("zotero-annotation")) return;
|
||||
|
||||
var annotation = tab.page.annotations.createAnnotation();
|
||||
annotation.initWithEvent(e);
|
||||
|
||||
// stop propagation
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// disable add mode, now that we've used it
|
||||
toggleAnnotateMode();
|
||||
}
|
||||
|
||||
/*
|
||||
* adds a highlight
|
||||
*/
|
||||
function _addHighlight(e) {
|
||||
var tab = _getTabObject(Zotero_Browser.tabbrowser.selectedBrowser);
|
||||
|
||||
try {
|
||||
var selection = Zotero_Browser.tabbrowser.selectedBrowser.contentWindow.getSelection();
|
||||
} catch(err) {
|
||||
return;
|
||||
}
|
||||
if(selection.isCollapsed) return;
|
||||
|
||||
tab.page.annotations.createHighlight(selection.getRangeAt(0));
|
||||
selection.removeAllRanges();
|
||||
|
||||
// stop propagation
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Zotero_Browser.Tab
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Zotero_Browser.Tab = function(browser) {
|
||||
this.browser = browser;
|
||||
this.page = new Object();
|
||||
}
|
||||
|
||||
/*
|
||||
* clears page-specific information
|
||||
*/
|
||||
Zotero_Browser.Tab.prototype.clear = function() {
|
||||
delete this.page;
|
||||
this.page = new Object();
|
||||
}
|
||||
|
||||
/*
|
||||
* detects translators for this browser object
|
||||
*/
|
||||
Zotero_Browser.Tab.prototype.detectTranslators = function(rootDoc, doc) {
|
||||
// if there's already a scrapable page in the browser window, and it's
|
||||
// still there, ensure it is actually part of the page, then return
|
||||
if(this.page.translators && this.page.translators.length && this.page.document.location) {
|
||||
if(this._searchFrames(rootDoc, this.page.document)) {
|
||||
return;
|
||||
} else {
|
||||
this.page.document = null;
|
||||
}
|
||||
}
|
||||
|
||||
// get translators
|
||||
this.page.translate = new Zotero.Translate("web");
|
||||
this.page.translate.setDocument(doc);
|
||||
this.page.translators = this.page.translate.getTranslators();
|
||||
// add document
|
||||
if(this.page.translators && this.page.translators.length) {
|
||||
this.page.document = doc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* searches for a document in all of the frames of a given document
|
||||
*/
|
||||
Zotero_Browser.Tab.prototype._searchFrames = function(rootDoc, searchDoc) {
|
||||
var frames = rootDoc.getElementsByTagName("frame");
|
||||
for each(var frame in frames) {
|
||||
if(frame.contentDocument &&
|
||||
(frame.contentDocument == searchDoc ||
|
||||
this._searchFrames(frame.contentDocument, searchDoc))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* translate a page, saving in saveLocation
|
||||
*/
|
||||
Zotero_Browser.Tab.prototype.translate = function(saveLocation) {
|
||||
if(this.page.translators && this.page.translators.length) {
|
||||
Zotero_Browser.Progress.show();
|
||||
|
||||
if(saveLocation) {
|
||||
saveLocation = Zotero.Collections.get(saveLocation);
|
||||
} else { // save to currently selected collection, if a collection is selected
|
||||
try {
|
||||
saveLocation = ZoteroPane.getSelectedCollection();
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
var me = this;
|
||||
|
||||
// use first translator available
|
||||
this.page.translate.setTranslator(this.page.translators[0]);
|
||||
this.page.translate.setHandler("select", me._selectItems);
|
||||
this.page.translate.setHandler("itemDone", function(obj, item) { me._itemDone(obj, item, saveLocation) });
|
||||
this.page.translate.setHandler("done", function(obj, item) { me._finishScraping(obj, item, saveLocation) });
|
||||
this.page.translate.translate();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback to be executed when an item has been finished
|
||||
*/
|
||||
Zotero_Browser.Tab.prototype._itemDone = function(obj, item, collection) {
|
||||
var title = item.getField("title");
|
||||
var icon = item.getImageSrc();
|
||||
Zotero_Browser.Progress.addLines([title], [icon]);
|
||||
|
||||
// add item to collection, if one was specified
|
||||
if(collection) {
|
||||
Zotero.Notifier.disable();
|
||||
collection.addItem(item.getID());
|
||||
Zotero.Notifier.enable();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* called when a user is supposed to select items
|
||||
*/
|
||||
Zotero_Browser.Tab.prototype._selectItems = function(obj, itemList) {
|
||||
// this is kinda ugly, mozillazine made me do it! honest!
|
||||
var io = { dataIn:itemList, dataOut:null }
|
||||
var newDialog = window.openDialog("chrome://zotero/content/ingester/selectitems.xul",
|
||||
"_blank","chrome,modal,centerscreen,resizable=yes", io);
|
||||
|
||||
if(!io.dataOut) { // user selected no items, so kill the progress indicatior
|
||||
Zotero_Browser.Progress.kill();
|
||||
}
|
||||
|
||||
return io.dataOut;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback to be executed when scraping is complete
|
||||
*/
|
||||
Zotero_Browser.Tab.prototype._finishScraping = function(obj, returnValue, collection) {
|
||||
if(!returnValue) {
|
||||
Zotero_Browser.Progress.changeHeadline(Zotero.getString("ingester.scrapeError"));
|
||||
Zotero_Browser.Progress.addDescription(Zotero.getString("ingester.scrapeErrorDescription"));
|
||||
}
|
||||
|
||||
if(collection) {
|
||||
// notify about modified items
|
||||
Zotero.Notifier.trigger("modify", "collection", collection.getID());
|
||||
}
|
||||
|
||||
Zotero_Browser.Progress.fade();
|
||||
}
|
||||
|
||||
/*
|
||||
* returns the URL of the image representing the translator to be called on the
|
||||
* current page, or false if the page cannot be scraped
|
||||
*/
|
||||
Zotero_Browser.Tab.prototype.getCaptureIcon = function() {
|
||||
if(this.page.translators && this.page.translators.length) {
|
||||
var itemType = this.page.translators[0].itemType;
|
||||
if(itemType == "multiple") {
|
||||
// Use folder icon for multiple types, for now
|
||||
return "chrome://zotero/skin/treesource-collection.png";
|
||||
} else {
|
||||
return Zotero.ItemTypes.getImageSrc(itemType);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Zotero_Browser.Progress
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Handles the display of a div showing progress in scraping
|
||||
Zotero_Browser.Progress = new function() {
|
||||
var _windowLoaded = false;
|
||||
var _windowLoading = false;
|
||||
// keep track of all of these things in case they're called before we're
|
||||
// done loading the progress window
|
||||
var _loadDescription = null;
|
||||
var _loadLines = new Array();
|
||||
var _loadIcons = new Array();
|
||||
var _loadHeadline = Zotero.getString("ingester.scraping");
|
||||
|
||||
this.show = show;
|
||||
this.changeHeadline = changeHeadline;
|
||||
this.addLines = addLines;
|
||||
this.addDescription = addDescription;
|
||||
this.fade = fade;
|
||||
this.kill = kill;
|
||||
|
||||
function show() {
|
||||
if(_windowLoading || _windowLoaded) { // already loading or loaded
|
||||
return false;
|
||||
}
|
||||
_progressWindow = window.openDialog("chrome://zotero/chrome/ingester/progress.xul",
|
||||
"", "chrome,dialog=no,titlebar=no,popup=yes");
|
||||
_progressWindow.addEventListener("load", _onWindowLoaded, false);
|
||||
_windowLoading = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function changeHeadline(headline) {
|
||||
if(_windowLoaded) {
|
||||
_progressWindow.document.getElementById("zotero-progress-text-headline").value = headline;
|
||||
} else {
|
||||
_loadHeadline = headline;
|
||||
}
|
||||
}
|
||||
|
||||
function addLines(label, icon) {
|
||||
if(_windowLoaded) {
|
||||
for(i in label) {
|
||||
var newLabel = _progressWindow.document.createElement("label");
|
||||
newLabel.setAttribute("class", "zotero-progress-item-label");
|
||||
newLabel.setAttribute("crop", "end");
|
||||
newLabel.setAttribute("value", label[i]);
|
||||
|
||||
var newImage = _progressWindow.document.createElement("image");
|
||||
newImage.setAttribute("class", "zotero-progress-item-icon");
|
||||
newImage.setAttribute("src", icon[i]);
|
||||
|
||||
var newHB = _progressWindow.document.createElement("hbox");
|
||||
newHB.setAttribute("class", "zotero-progress-item-hbox");
|
||||
newHB.setAttribute("valign", "center");
|
||||
newHB.appendChild(newImage);
|
||||
newHB.appendChild(newLabel);
|
||||
|
||||
_progressWindow.document.getElementById("zotero-progress-text-box").appendChild(newHB);
|
||||
}
|
||||
|
||||
_move();
|
||||
} else {
|
||||
_loadLines = _loadLines.concat(label);
|
||||
_loadIcons = _loadIcons.concat(icon);
|
||||
}
|
||||
}
|
||||
|
||||
function addDescription(text) {
|
||||
if(_windowLoaded) {
|
||||
var newHB = _progressWindow.document.createElement("hbox");
|
||||
newHB.setAttribute("class", "zotero-progress-item-hbox");
|
||||
var newDescription = _progressWindow.document.createElement("description");
|
||||
newDescription.setAttribute("class", "zotero-progress-description");
|
||||
var newText = _progressWindow.document.createTextNode(text);
|
||||
|
||||
newDescription.appendChild(newText);
|
||||
newHB.appendChild(newDescription);
|
||||
_progressWindow.document.getElementById("zotero-progress-text-box").appendChild(newHB);
|
||||
|
||||
_move();
|
||||
} else {
|
||||
_loadDescription = text;
|
||||
}
|
||||
}
|
||||
|
||||
function fade() {
|
||||
if(_windowLoaded || _windowLoading) {
|
||||
setTimeout(_timeout, 2500);
|
||||
}
|
||||
}
|
||||
|
||||
function kill() {
|
||||
_windowLoaded = false;
|
||||
_windowLoading = false;
|
||||
try {
|
||||
_progressWindow.close();
|
||||
} catch(ex) {}
|
||||
}
|
||||
|
||||
function _onWindowLoaded() {
|
||||
_windowLoading = false;
|
||||
_windowLoaded = true;
|
||||
|
||||
_move();
|
||||
// do things we delayed because the window was loading
|
||||
changeHeadline(_loadHeadline);
|
||||
addLines(_loadLines, _loadIcons);
|
||||
if(_loadDescription) {
|
||||
addDescription(_loadDescription);
|
||||
}
|
||||
|
||||
// reset parameters
|
||||
_loadDescription = null;
|
||||
_loadLines = new Array();
|
||||
_loadIcons = new Array();
|
||||
_loadHeadline = Zotero.getString("ingester.scraping")
|
||||
}
|
||||
|
||||
function _move() {
|
||||
_progressWindow.sizeToContent();
|
||||
_progressWindow.moveTo(
|
||||
window.screenX + window.innerWidth - _progressWindow.outerWidth - 30,
|
||||
window.screenY + window.innerHeight - _progressWindow.outerHeight - 10
|
||||
);
|
||||
}
|
||||
|
||||
function _timeout() {
|
||||
kill(); // could check to see if we're really supposed to fade yet
|
||||
// (in case multiple scrapers are operating at once)
|
||||
}
|
||||
}
|
||||
|
||||
Zotero_Browser.init();
|
|
@ -1,548 +0,0 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (c) 2006 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://chnm.gmu.edu
|
||||
|
||||
Licensed under the Educational Community License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.opensource.org/licenses/ecl1.php
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
Based on code from Greasemonkey and PiggyBank
|
||||
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
//
|
||||
// Zotero Ingester Browser Functions
|
||||
//
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Zotero_Ingester_Interface
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Class to interface with the browser when ingesting data
|
||||
|
||||
var Zotero_Ingester_Interface = function() {}
|
||||
|
||||
Zotero_Ingester_Interface.blacklist = [
|
||||
"googlesyndication.com",
|
||||
"doubleclick.net",
|
||||
"questionmarket.com",
|
||||
"atdmt.com"
|
||||
];
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Public Zotero_Ingester_Interface methods
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Initialize some variables and prepare event listeners for when chrome is done
|
||||
* loading
|
||||
*/
|
||||
Zotero_Ingester_Interface.init = function() {
|
||||
Zotero_Ingester_Interface.browserData = new Object();
|
||||
Zotero_Ingester_Interface._scrapePopupShowing = false;
|
||||
Zotero.Ingester.ProxyMonitor.init();
|
||||
Zotero.Ingester.MIMEHandler.init();
|
||||
Zotero.Translate.init();
|
||||
|
||||
window.addEventListener("load", Zotero_Ingester_Interface.chromeLoad, false);
|
||||
window.addEventListener("unload", Zotero_Ingester_Interface.chromeUnload, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* When chrome loads, register our event handlers with the appropriate interfaces
|
||||
*/
|
||||
Zotero_Ingester_Interface.chromeLoad = function() {
|
||||
Zotero_Ingester_Interface.tabBrowser = document.getElementById("content");
|
||||
Zotero_Ingester_Interface.appContent = document.getElementById("appcontent");
|
||||
Zotero_Ingester_Interface.statusImage = document.getElementById("zotero-status-image");
|
||||
|
||||
// this gives us onLocationChange, for updating when tabs are switched/created
|
||||
Zotero_Ingester_Interface.tabBrowser.addEventListener("TabClose",
|
||||
Zotero_Ingester_Interface.tabClose, false);
|
||||
Zotero_Ingester_Interface.tabBrowser.addEventListener("TabSelect",
|
||||
Zotero_Ingester_Interface.tabSelect, false);
|
||||
// this is for pageshow, for updating the status of the book icon
|
||||
Zotero_Ingester_Interface.appContent.addEventListener("pageshow",
|
||||
Zotero_Ingester_Interface.contentLoad, true);
|
||||
// this is for turning off the book icon when a user navigates away from a page
|
||||
Zotero_Ingester_Interface.appContent.addEventListener("pagehide",
|
||||
Zotero_Ingester_Interface.contentHide, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* When chrome unloads, delete our document objects and remove our listeners
|
||||
*/
|
||||
Zotero_Ingester_Interface.chromeUnload = function() {
|
||||
delete Zotero_Ingester_Interface.browserData;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scrapes a page (called when the capture icon is clicked); takes a collection
|
||||
* ID as the argument
|
||||
*/
|
||||
Zotero_Ingester_Interface.scrapeThisPage = function(saveLocation) {
|
||||
var browser = Zotero_Ingester_Interface.tabBrowser.selectedBrowser;
|
||||
var data = Zotero_Ingester_Interface._getData(browser);
|
||||
|
||||
if(data.translators && data.translators.length) {
|
||||
Zotero_Ingester_Interface.Progress.show();
|
||||
|
||||
if(saveLocation) {
|
||||
saveLocation = Zotero.Collections.get(saveLocation);
|
||||
} else { // save to currently selected collection, if a collection is selected
|
||||
try {
|
||||
saveLocation = ZoteroPane.getSelectedCollection();
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
var translate = new Zotero.Translate("web");
|
||||
translate.setDocument(data.document);
|
||||
// use first translator available
|
||||
translate.setTranslator(data.translators[0]);
|
||||
translate.setHandler("select", Zotero_Ingester_Interface._selectItems);
|
||||
translate.setHandler("itemDone", function(obj, item) { Zotero_Ingester_Interface._itemDone(obj, item, saveLocation) });
|
||||
translate.setHandler("done", function(obj, item) { Zotero_Ingester_Interface._finishScraping(obj, item, saveLocation) });
|
||||
translate.translate();
|
||||
}
|
||||
}
|
||||
|
||||
Zotero_Ingester_Interface.searchFrames = function(rootDoc, searchDoc) {
|
||||
var frames = rootDoc.getElementsByTagName("frame");
|
||||
for each(var frame in frames) {
|
||||
if(frame.contentDocument &&
|
||||
(frame.contentDocument == searchDoc ||
|
||||
Zotero_Ingester_Interface.searchFrames(frame.contentDocument, searchDoc))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* An event handler called when a new document is loaded. Creates a new document
|
||||
* object, and updates the status of the capture icon
|
||||
*/
|
||||
Zotero_Ingester_Interface.contentLoad = function(event) {
|
||||
if(event.originalTarget instanceof HTMLDocument) {
|
||||
var doc = event.originalTarget;
|
||||
var rootDoc = doc;
|
||||
|
||||
try {
|
||||
if (!doc.domain) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get the appropriate root document to check which browser we're on
|
||||
while(rootDoc.defaultView.frameElement) {
|
||||
rootDoc = rootDoc.defaultView.frameElement.ownerDocument;
|
||||
}
|
||||
|
||||
// Figure out what browser this contentDocument is associated with
|
||||
var browser;
|
||||
for(var i=0; i<Zotero_Ingester_Interface.tabBrowser.browsers.length; i++) {
|
||||
if(rootDoc == Zotero_Ingester_Interface.tabBrowser.browsers[i].contentDocument) {
|
||||
browser = Zotero_Ingester_Interface.tabBrowser.browsers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!browser) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get data object
|
||||
var data = Zotero_Ingester_Interface._getData(browser);
|
||||
|
||||
// if there's already a scrapable page in the browser window, and it's
|
||||
// still there, ensure it is actually part of the page, then return
|
||||
if(data.translators && data.translators.length && data.document.location) {
|
||||
if(Zotero_Ingester_Interface.searchFrames(rootDoc, data.document)) {
|
||||
return;
|
||||
} else {
|
||||
data.document = null;
|
||||
}
|
||||
}
|
||||
|
||||
for each(var blacklistedURL in Zotero_Ingester_Interface.blacklist) {
|
||||
if(doc.domain.substr(doc.domain.length-blacklistedURL.length) == blacklistedURL) {
|
||||
Zotero.debug("Ignoring blacklisted URL "+doc.location);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// get translators
|
||||
var translate = new Zotero.Translate("web");
|
||||
translate.setDocument(doc);
|
||||
data.translators = translate.getTranslators();
|
||||
// update status
|
||||
if(Zotero_Ingester_Interface.tabBrowser.selectedBrowser == browser) {
|
||||
Zotero_Ingester_Interface._updateStatus(data);
|
||||
}
|
||||
// add document
|
||||
if(data.translators && data.translators.length) {
|
||||
data.document = doc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* called to unregister Zotero icon, etc.
|
||||
*/
|
||||
Zotero_Ingester_Interface.contentHide = function(event) {
|
||||
if(event.originalTarget instanceof HTMLDocument && !event.originalTarget.defaultView.frameElement) {
|
||||
var doc = event.originalTarget;
|
||||
|
||||
// Figure out what browser this contentDocument is associated with
|
||||
var browser;
|
||||
for(var i=0; i<Zotero_Ingester_Interface.tabBrowser.browsers.length; i++) {
|
||||
if(doc == Zotero_Ingester_Interface.tabBrowser.browsers[i].contentDocument) {
|
||||
browser = Zotero_Ingester_Interface.tabBrowser.browsers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// get data object
|
||||
var data = Zotero_Ingester_Interface._getData(browser);
|
||||
|
||||
data.translators = false;
|
||||
data.document = false;
|
||||
// update status
|
||||
if(Zotero_Ingester_Interface.tabBrowser.selectedBrowser == browser) {
|
||||
Zotero_Ingester_Interface._updateStatus(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* called when a tab is closed
|
||||
*/
|
||||
Zotero_Ingester_Interface.tabClose = function(event) {
|
||||
// To execute if document object does not exist
|
||||
Zotero_Ingester_Interface._deleteData(event.target.linkedBrowser);
|
||||
}
|
||||
|
||||
/*
|
||||
* called when a tab is switched
|
||||
*/
|
||||
Zotero_Ingester_Interface.tabSelect = function(event) {
|
||||
var data = Zotero_Ingester_Interface._getData(Zotero_Ingester_Interface.tabBrowser.selectedBrowser);
|
||||
Zotero_Ingester_Interface._updateStatus(data);
|
||||
}
|
||||
|
||||
Zotero_Ingester_Interface.hidePopup = function(collectionID) {
|
||||
Zotero_Ingester_Interface._scrapePopupShowing = false;
|
||||
}
|
||||
|
||||
Zotero_Ingester_Interface.showPopup = function(collectionID, parentElement) {
|
||||
if(Zotero_Ingester_Interface._scrapePopupShowing && parentElement.hasChildNodes()) {
|
||||
return false; // Don't dynamically reload popups that are already showing
|
||||
}
|
||||
Zotero_Ingester_Interface._scrapePopupShowing = true;
|
||||
parentElement.removeAllItems();
|
||||
|
||||
if(collectionID == null) { // show library
|
||||
var newItem = document.createElement("menuitem");
|
||||
newItem.setAttribute("label", Zotero.getString("pane.collections.library"));
|
||||
newItem.setAttribute("class", "menuitem-iconic zotero-scrape-popup-library");
|
||||
newItem.setAttribute("oncommand", 'Zotero_Ingester_Interface.scrapeThisPage()');
|
||||
parentElement.appendChild(newItem);
|
||||
}
|
||||
|
||||
var childrenList = Zotero.getCollections(collectionID);
|
||||
for(var i = 0; i < childrenList.length; i++) {
|
||||
if(childrenList[i].hasChildCollections()) {
|
||||
var newItem = document.createElement("menu");
|
||||
var subMenu = document.createElement("menupopup");
|
||||
subMenu.setAttribute("onpopupshowing", 'Zotero_Ingester_Interface.showPopup("'+childrenList[i].getID()+'", this)');
|
||||
newItem.setAttribute("class", "menu-iconic zotero-scrape-popup-collection");
|
||||
newItem.appendChild(subMenu);
|
||||
} else {
|
||||
var newItem = document.createElement("menuitem");
|
||||
newItem.setAttribute("class", "menuitem-iconic zotero-scrape-popup-collection");
|
||||
}
|
||||
newItem.setAttribute("label", childrenList[i].getName());
|
||||
newItem.setAttribute("oncommand", 'Zotero_Ingester_Interface.scrapeThisPage("'+childrenList[i].getID()+'")');
|
||||
|
||||
parentElement.appendChild(newItem);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Private Zotero_Ingester_Interface methods
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Gets a data object given a browser window object
|
||||
*
|
||||
* NOTE: Browser objects are associated with document objects via keys generated
|
||||
* from the time the browser object is opened. I'm not sure if this is the
|
||||
* appropriate mechanism for handling this, but it's what PiggyBank used and it
|
||||
* appears to work.
|
||||
*
|
||||
* Currently, the data object contains only one property: "translators," which
|
||||
* is an array of translators that should work with the given page as returned
|
||||
* from Zotero.Translate.getTranslator()
|
||||
*/
|
||||
Zotero_Ingester_Interface._getData = function(browser) {
|
||||
if(!browser) return false;
|
||||
try {
|
||||
var key = browser.getAttribute("zotero-key");
|
||||
if(Zotero_Ingester_Interface.browserData[key]) {
|
||||
return Zotero_Ingester_Interface.browserData[key];
|
||||
}
|
||||
} finally {
|
||||
if(!key) {
|
||||
var key = (new Date()).getTime();
|
||||
browser.setAttribute("zotero-key", key);
|
||||
Zotero_Ingester_Interface.browserData[key] = new Array();
|
||||
return Zotero_Ingester_Interface.browserData[key];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes the document object associated with a given browser window object
|
||||
*/
|
||||
Zotero_Ingester_Interface._deleteData = function(browser) {
|
||||
if(!browser) return false;
|
||||
try {
|
||||
var key = browser.getAttribute("zotero-key");
|
||||
if(Zotero_Ingester_Interface.browserData[key]) {
|
||||
delete Zotero_Ingester_Interface.browserData[key];
|
||||
return true;
|
||||
}
|
||||
} finally {}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the status of the capture icon to reflect the scrapability or lack
|
||||
* thereof of the current page
|
||||
*/
|
||||
Zotero_Ingester_Interface._updateStatus = function(data) {
|
||||
if(data.translators && data.translators.length) {
|
||||
var itemType = data.translators[0].itemType;
|
||||
if(itemType == "multiple") {
|
||||
// Use folder icon for multiple types, for now
|
||||
Zotero_Ingester_Interface.statusImage.src = "chrome://zotero/skin/treesource-collection.png";
|
||||
} else {
|
||||
Zotero_Ingester_Interface.statusImage.src = Zotero.ItemTypes.getImageSrc(itemType);
|
||||
}
|
||||
Zotero_Ingester_Interface.statusImage.hidden = false;
|
||||
} else {
|
||||
Zotero_Ingester_Interface.statusImage.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback to be executed when an item has been finished
|
||||
*/
|
||||
Zotero_Ingester_Interface._itemDone = function(obj, item, collection) {
|
||||
var title = item.getField("title");
|
||||
var icon = item.getImageSrc();
|
||||
Zotero_Ingester_Interface.Progress.addLines([title], [icon]);
|
||||
|
||||
// add item to collection, if one was specified
|
||||
if(collection) {
|
||||
Zotero.Notifier.disable();
|
||||
collection.addItem(item.getID());
|
||||
Zotero.Notifier.enable();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* called when a user is supposed to select items
|
||||
*/
|
||||
Zotero_Ingester_Interface._selectItems = function(obj, itemList) {
|
||||
// this is kinda ugly, mozillazine made me do it! honest!
|
||||
var io = { dataIn:itemList, dataOut:null }
|
||||
var newDialog = window.openDialog("chrome://zotero/content/ingester/selectitems.xul",
|
||||
"_blank","chrome,modal,centerscreen,resizable=yes", io);
|
||||
|
||||
if(!io.dataOut) { // user selected no items, so kill the progress indicatior
|
||||
Zotero_Ingester_Interface.Progress.kill();
|
||||
}
|
||||
|
||||
return io.dataOut;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback to be executed when scraping is complete
|
||||
*/
|
||||
Zotero_Ingester_Interface._finishScraping = function(obj, returnValue, collection) {
|
||||
if(!returnValue) {
|
||||
Zotero_Ingester_Interface.Progress.changeHeadline(Zotero.getString("ingester.scrapeError"));
|
||||
Zotero_Ingester_Interface.Progress.addDescription(Zotero.getString("ingester.scrapeErrorDescription"));
|
||||
}
|
||||
|
||||
if(collection) {
|
||||
// notify about modified items
|
||||
Zotero.Notifier.trigger("modify", "collection", collection.getID());
|
||||
}
|
||||
|
||||
Zotero_Ingester_Interface.Progress.fade();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Zotero.Ingester.Progress
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Handles the display of a div showing progress in scraping
|
||||
Zotero_Ingester_Interface.Progress = new function() {
|
||||
var _windowLoaded = false;
|
||||
var _windowLoading = false;
|
||||
// keep track of all of these things in case they're called before we're
|
||||
// done loading the progress window
|
||||
var _loadDescription = null;
|
||||
var _loadLines = new Array();
|
||||
var _loadIcons = new Array();
|
||||
var _loadHeadline = Zotero.getString("ingester.scraping");
|
||||
|
||||
this.show = show;
|
||||
this.changeHeadline = changeHeadline;
|
||||
this.addLines = addLines;
|
||||
this.addDescription = addDescription;
|
||||
this.fade = fade;
|
||||
this.kill = kill;
|
||||
|
||||
function show() {
|
||||
if(_windowLoading || _windowLoaded) { // already loading or loaded
|
||||
return false;
|
||||
}
|
||||
_progressWindow = window.openDialog("chrome://zotero/chrome/ingester/progress.xul",
|
||||
"", "chrome,dialog=no,titlebar=no,popup=yes");
|
||||
_progressWindow.addEventListener("load", _onWindowLoaded, false);
|
||||
_windowLoading = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function changeHeadline(headline) {
|
||||
if(_windowLoaded) {
|
||||
_progressWindow.document.getElementById("zotero-progress-text-headline").value = headline;
|
||||
} else {
|
||||
_loadHeadline = headline;
|
||||
}
|
||||
}
|
||||
|
||||
function addLines(label, icon) {
|
||||
if(_windowLoaded) {
|
||||
for(i in label) {
|
||||
var newLabel = _progressWindow.document.createElement("label");
|
||||
newLabel.setAttribute("class", "zotero-progress-item-label");
|
||||
newLabel.setAttribute("crop", "end");
|
||||
newLabel.setAttribute("value", label[i]);
|
||||
|
||||
var newImage = _progressWindow.document.createElement("image");
|
||||
newImage.setAttribute("class", "zotero-progress-item-icon");
|
||||
newImage.setAttribute("src", icon[i]);
|
||||
|
||||
var newHB = _progressWindow.document.createElement("hbox");
|
||||
newHB.setAttribute("class", "zotero-progress-item-hbox");
|
||||
newHB.setAttribute("valign", "center");
|
||||
newHB.appendChild(newImage);
|
||||
newHB.appendChild(newLabel);
|
||||
|
||||
_progressWindow.document.getElementById("zotero-progress-text-box").appendChild(newHB);
|
||||
}
|
||||
|
||||
_move();
|
||||
} else {
|
||||
_loadLines = _loadLines.concat(label);
|
||||
_loadIcons = _loadIcons.concat(icon);
|
||||
}
|
||||
}
|
||||
|
||||
function addDescription(text) {
|
||||
if(_windowLoaded) {
|
||||
var newHB = _progressWindow.document.createElement("hbox");
|
||||
newHB.setAttribute("class", "zotero-progress-item-hbox");
|
||||
var newDescription = _progressWindow.document.createElement("description");
|
||||
newDescription.setAttribute("class", "zotero-progress-description");
|
||||
var newText = _progressWindow.document.createTextNode(text);
|
||||
|
||||
newDescription.appendChild(newText);
|
||||
newHB.appendChild(newDescription);
|
||||
_progressWindow.document.getElementById("zotero-progress-text-box").appendChild(newHB);
|
||||
|
||||
_move();
|
||||
} else {
|
||||
_loadDescription = text;
|
||||
}
|
||||
}
|
||||
|
||||
function fade() {
|
||||
if(_windowLoaded || _windowLoading) {
|
||||
setTimeout(_timeout, 2500);
|
||||
}
|
||||
}
|
||||
|
||||
function kill() {
|
||||
_windowLoaded = false;
|
||||
_windowLoading = false;
|
||||
try {
|
||||
_progressWindow.close();
|
||||
} catch(ex) {}
|
||||
}
|
||||
|
||||
function _onWindowLoaded() {
|
||||
_windowLoading = false;
|
||||
_windowLoaded = true;
|
||||
|
||||
_move();
|
||||
// do things we delayed because the window was loading
|
||||
changeHeadline(_loadHeadline);
|
||||
addLines(_loadLines, _loadIcons);
|
||||
if(_loadDescription) {
|
||||
addDescription(_loadDescription);
|
||||
}
|
||||
|
||||
// reset parameters
|
||||
_loadDescription = null;
|
||||
_loadLines = new Array();
|
||||
_loadIcons = new Array();
|
||||
_loadHeadline = Zotero.getString("ingester.scraping")
|
||||
}
|
||||
|
||||
function _move() {
|
||||
_progressWindow.sizeToContent();
|
||||
_progressWindow.moveTo(
|
||||
window.screenX + window.innerWidth - _progressWindow.outerWidth - 30,
|
||||
window.screenY + window.innerHeight - _progressWindow.outerHeight - 10
|
||||
);
|
||||
}
|
||||
|
||||
function _timeout() {
|
||||
kill(); // could check to see if we're really supposed to fade yet
|
||||
// (in case multiple scrapers are operating at once)
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (c) 2006 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://chnm.gmu.edu
|
||||
|
||||
Licensed under the Educational Community License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.opensource.org/licenses/ecl1.php
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
-->
|
||||
<overlay id="zotero-ingester-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script src="../include.js"/>
|
||||
<script src="browser.js"/>
|
||||
|
||||
<script type="application/x-javascript">
|
||||
Zotero_Ingester_Interface.init();
|
||||
</script>
|
||||
|
||||
<hbox id="urlbar-icons">
|
||||
<popupset>
|
||||
<popup id="zotero-scrape-popup" onpopupshowing="Zotero_Ingester_Interface.showPopup(null, this)" onpopuphidden="Zotero_Ingester_Interface.hidePopup(null, this)">
|
||||
</popup>
|
||||
</popupset>
|
||||
|
||||
<image src="chrome://zotero/skin/treeitem-book.png" id="zotero-status-image" onclick="Zotero_Ingester_Interface.scrapeThisPage()" position="1" hidden="true" context="zotero-scrape-popup" />
|
||||
</hbox>
|
||||
</overlay>
|
|
@ -42,7 +42,6 @@ var Zotero_Ingester_Interface_SelectItems = function() {}
|
|||
*/
|
||||
Zotero_Ingester_Interface_SelectItems.init = function() {
|
||||
this.io = window.arguments[0];
|
||||
this.Zotero_Ingester_Interface = window.arguments[1];
|
||||
var listbox = document.getElementById("zotero-selectitems-links");
|
||||
|
||||
for(i in this.io.dataIn) { // we could use a tree for this if we wanted to
|
||||
|
|
|
@ -1285,6 +1285,8 @@ var ZoteroPane = new function()
|
|||
|
||||
if (internal || Zotero.MIME.fileHasInternalHandler(file))
|
||||
{
|
||||
// enable annotation
|
||||
Zotero_Browser.annotateThisPage(attachment.getID());
|
||||
window.loadURI(fileURL);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -26,13 +26,14 @@
|
|||
<overlay id="zotero"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<?xul-overlay href="itemPane.xul" ?>
|
||||
|
||||
|
||||
<!-- Include the global XPCOM object -->
|
||||
<script src="include.js"/>
|
||||
|
||||
<script src="overlay.js"/>
|
||||
<script src="fileInterface.js"/>
|
||||
<script src="reportInterface.js"/>
|
||||
<script src="browser.js"/>
|
||||
|
||||
<commandset id="mainCommandSet">
|
||||
<command id="cmd_zotero_search" oncommand="ZoteroPane.search();"/>
|
||||
|
@ -268,8 +269,24 @@
|
|||
</groupbox>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
<!-- Annotation Toolbar -->
|
||||
<toolbar id="zotero-annotate-tb" crop="end" insertbefore="content" hidden="true">
|
||||
<toolbarbutton id="zotero-annotate-tb-add" tooltiptext="&zotero.annotate.toolbar.add.label;" oncommand="Zotero_Browser.toggleAnnotateMode();"/>
|
||||
<toolbarbutton id="zotero-annotate-tb-highlight" tooltiptext="&zotero.annotate.toolbar.highlight.label;" oncommand="Zotero_Browser.toggleHighlightMode();"/>
|
||||
</toolbar>
|
||||
</vbox>
|
||||
|
||||
<!-- Scrape Code -->
|
||||
<hbox id="urlbar-icons">
|
||||
<popupset>
|
||||
<popup id="zotero-scrape-popup" onpopupshowing="Zotero_Browser.showPopup(null, this)" onpopuphidden="Zotero_Browser.hidePopup(null, this)">
|
||||
</popup>
|
||||
</popupset>
|
||||
|
||||
<image src="chrome://zotero/skin/treeitem-book.png" id="zotero-status-image" onclick="Zotero_Browser.scrapeThisPage()" position="1" hidden="true" context="zotero-scrape-popup" />
|
||||
</hbox>
|
||||
|
||||
<statusbar id="status-bar">
|
||||
<statusbarpanel id="zotero-status-bar-icon" hidden="true"
|
||||
class="statusbarpanel-iconic" onclick="ZoteroPane.toggleDisplay();"/>
|
||||
|
|
822
chrome/content/zotero/xpcom/annotate.js
Normal file
822
chrome/content/zotero/xpcom/annotate.js
Normal file
|
@ -0,0 +1,822 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (c) 2006 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://chnm.gmu.edu
|
||||
|
||||
Licensed under the Educational Community License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.opensource.org/licenses/ecl1.php
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Zotero.Annotate
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// general purpose annotation/highlighting methods
|
||||
|
||||
Zotero.Annotate = new function() {
|
||||
this.annotationColor = "#fff580";
|
||||
this.annotationBarColor = "#c0b860";
|
||||
this.annotationBorderColor = "#878244";
|
||||
this.highlightColor = "#fff580";
|
||||
|
||||
this.getPathForPoint = getPathForPoint;
|
||||
this.getPointForPath = getPointForPath;
|
||||
this.getPixelOffset = getPixelOffset;
|
||||
|
||||
var textType = Components.interfaces.nsIDOMNode.TEXT_NODE;
|
||||
|
||||
/*
|
||||
* gets a path object, comprising an XPath, text node index, and offset, for
|
||||
* a given node.
|
||||
*/
|
||||
function getPathForPoint(node, offset) {
|
||||
Zotero.debug("have node of offset "+offset);
|
||||
|
||||
var path = {parent:"", textNode:null, offset:(offset ? offset : null)};
|
||||
|
||||
var lastWasTextNode = node.nodeType == textType;
|
||||
|
||||
// if the selected point is inside a highlight node
|
||||
if(node.parentNode.getAttribute && node.parentNode.getAttribute("zotero")) {
|
||||
// add offsets of preceding text nodes in this zotero node
|
||||
var sibling = node.previousSibling;
|
||||
while(sibling) {
|
||||
if(child.nodeType == textType) path.offset += child.nodeValue.length;
|
||||
sibling = sibling.previousSibling;
|
||||
}
|
||||
|
||||
// use parent node for future purposes
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
if(lastWasTextNode) {
|
||||
path.textNode = 1;
|
||||
var sibling = node.previousSibling;
|
||||
var first = true;
|
||||
|
||||
while(sibling) {
|
||||
var isZotero = undefined;
|
||||
if(sibling.getAttribute) isZotero = sibling.getAttribute("zotero");
|
||||
|
||||
if(sibling.nodeType == textType ||
|
||||
(isZotero == "highlight")) {
|
||||
// is a text node
|
||||
if(first == true) {
|
||||
// is still part of the first text node
|
||||
if(sibling.getAttribute) {
|
||||
// get offset of all child nodes
|
||||
for each(var child in sibling.childNodes) {
|
||||
if(child.nodeType == textType) path.offset += child.nodeValue.length;
|
||||
}
|
||||
} else {
|
||||
path.offset += sibling.nodeValue.length;
|
||||
}
|
||||
} else if(!lastWasTextNode) {
|
||||
// is part of another text node
|
||||
path.textNode++;
|
||||
lastWasTextNode = true;
|
||||
}
|
||||
} else if(!isZotero) { // skip over annotation marker nodes
|
||||
// is not a text node
|
||||
lastWasTextNode = first = false;
|
||||
}
|
||||
|
||||
sibling = sibling.previousSibling;
|
||||
}
|
||||
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
var doc = node.ownerDocument;
|
||||
|
||||
while(node && node != doc) {
|
||||
var number = 1;
|
||||
var sibling = node.previousSibling;
|
||||
while(sibling) {
|
||||
if(sibling.tagName == node.tagName) number++;
|
||||
sibling = sibling.previousSibling;
|
||||
}
|
||||
|
||||
// don't add highlight nodes
|
||||
var tag = node.tagName.toLowerCase();
|
||||
if(tag == "span") {
|
||||
tag += "[not(@zotero)]";
|
||||
}
|
||||
|
||||
path.parent = "/"+tag+"["+number+"]"+path.parent;
|
||||
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
Zotero.debug("Annotate: got path "+path.parent+", "+path.textNode+", "+path.offset);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
function getPointForPath(parent, textNode, offset, document, nsResolver) {
|
||||
var point = {offset:0};
|
||||
|
||||
// try to evaluate parent
|
||||
try {
|
||||
point.node = document.evaluate(parent, document, nsResolver,
|
||||
Components.interfaces.nsIDOMXPathResult.ANY_TYPE, null).iterateNext();
|
||||
} catch(e) {
|
||||
Zotero.debug("Annotate: could not find XPath "+parent+" in getPointForPath");
|
||||
return false;
|
||||
}
|
||||
|
||||
// don't do further processing if this path does not refer to a text node
|
||||
if(!textNode) return point;
|
||||
|
||||
// parent node must have children if we have a text node index
|
||||
if(!point.node.firstChild) {
|
||||
Zotero.debug("Annotate: node "+parent+" has no children in getPointForPath");
|
||||
return false;
|
||||
}
|
||||
|
||||
point.node = point.node.firstChild;
|
||||
point.offset = offset;
|
||||
var lastWasTextNode = false;
|
||||
var number = 0;
|
||||
|
||||
// find text node
|
||||
while(true) {
|
||||
var isZotero = undefined;
|
||||
if(point.node.getAttribute) isZotero = point.node.getAttribute("zotero");
|
||||
|
||||
if(point.node.nodeType == textType ||
|
||||
isZotero == "highlight") {
|
||||
if(!lastWasTextNode) {
|
||||
number++;
|
||||
|
||||
// if we found the node we're looking for, break
|
||||
if(number == textNode) break;
|
||||
|
||||
lastWasTextNode = true;
|
||||
}
|
||||
} else if(!isZotero) {
|
||||
lastWasTextNode = false;
|
||||
}
|
||||
|
||||
point.node = point.node.nextSibling;
|
||||
// if there's no node, this point is invalid
|
||||
if(!point.node) {
|
||||
Zotero.debug("Annotate: reached end of node list while searching for text node "+textNode+" of "+parent);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// find point.offset
|
||||
while(true) {
|
||||
// get length of enclosed text node
|
||||
if(point.node.getAttribute) {
|
||||
// this is a highlighted node; loop through and subtract all
|
||||
// offsets, breaking if we reach the end
|
||||
var parentNode = point.node;
|
||||
point.node = point.node.firstChild;
|
||||
while(point.node) {
|
||||
if(point.node.nodeType == textType) {
|
||||
// break if end condition reached
|
||||
if(point.node.nodeValue.length >= point.offset) return point;
|
||||
// otherwise, continue subtracting offsets
|
||||
point.offset -= point.node.nodeValue.length;
|
||||
}
|
||||
point.node = point.node.nextSibling;
|
||||
}
|
||||
// restore parent node
|
||||
point.node = parentNode;
|
||||
} else {
|
||||
// this is not a highlighted node; use simple node length
|
||||
if(point.node.nodeValue.length >= point.offset) return point;
|
||||
point.offset -= point.node.nodeValue.length;
|
||||
}
|
||||
|
||||
// get next node
|
||||
point.node = point.node.nextSibling;
|
||||
// if next node does not exist or is not a text node, this
|
||||
// point is invalid
|
||||
if(!point.node || (point.node.nodeType != textType &&
|
||||
(!point.node.getAttribute || !point.node.getAttribute("zotero")))) {
|
||||
Zotero.debug("Annotate: could not find point.offset "+point.offset+" for text node "+textNode+" of "+parent);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* gets the pixel offset of an item from the top left of a page. the
|
||||
* optional "offset" argument specifies a text offset.
|
||||
*/
|
||||
function getPixelOffset(node, offset) {
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
|
||||
do {
|
||||
x += node.offsetLeft;
|
||||
y += node.offsetTop;
|
||||
node = node.offsetParent;
|
||||
} while(node);
|
||||
|
||||
return [x, y];
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Zotero.Annotations
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// a set of annotations to correspond to a given page
|
||||
|
||||
Zotero.Annotations = function(browser, itemID) {
|
||||
this.browser = browser;
|
||||
this.document = browser.contentDocument;
|
||||
this.window = browser.contentWindow;
|
||||
this.nsResolver = this.document.createNSResolver(this.document.documentElement);
|
||||
|
||||
this.itemID = itemID;
|
||||
|
||||
this.annotations = new Array();
|
||||
this.highlights = new Array();
|
||||
|
||||
this.zIndex = 100;
|
||||
|
||||
this.load();
|
||||
}
|
||||
|
||||
Zotero.Annotations.prototype.createAnnotation = function() {
|
||||
var annotation = new Zotero.Annotation(this);
|
||||
this.annotations.push(annotation);
|
||||
return annotation;
|
||||
}
|
||||
|
||||
Zotero.Annotations.prototype.createHighlight = function(selectedRange) {
|
||||
var deleteHighlights = new Array();
|
||||
var startIn = false, endIn = false;
|
||||
|
||||
// first, see if part of this range is already covered
|
||||
for(var i in this.highlights) {
|
||||
var compareHighlight = this.highlights[i];
|
||||
var compareRange = compareHighlight.range;
|
||||
|
||||
var startToStart = compareRange.compareBoundaryPoints(Components.interfaces.nsIDOMRange.START_TO_START, selectedRange);
|
||||
var endToEnd = compareRange.compareBoundaryPoints(Components.interfaces.nsIDOMRange.END_TO_END, selectedRange);
|
||||
if(startToStart != 1 && endToEnd != -1) {
|
||||
// if the selected range is inside this one
|
||||
return compareHighlight;
|
||||
} else if(startToStart != -1 && endToEnd != 1) {
|
||||
// if this range is inside selected range, delete
|
||||
this.highlights[i] = undefined;
|
||||
delete this.highlights[i];
|
||||
} else {
|
||||
var endToStart = compareRange.compareBoundaryPoints(Components.interfaces.nsIDOMRange.END_TO_START, selectedRange);
|
||||
if(endToStart != 1 && endToEnd != -1) {
|
||||
// if the end of the selected range is between the start and
|
||||
// end of this range
|
||||
var endIn = i;
|
||||
} else {
|
||||
var startToEnd = compareRange.compareBoundaryPoints(Components.interfaces.nsIDOMRange.START_TO_END, selectedRange);
|
||||
if(startToEnd != -1 && startToStart != 1) {
|
||||
// if the start of the selected range is between the
|
||||
// start and end of this range
|
||||
var startIn = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(startIn !== false && endIn !== false) {
|
||||
selectedRange.setStart(this.highlights[startIn].range.endContainer,
|
||||
this.highlights[startIn].range.endOffset);
|
||||
selectedRange.setEnd(this.highlights[endIn].range.endContainer,
|
||||
this.highlights[endIn].range.endOffset);
|
||||
this.highlights[startIn].initWithRange(selectedRange);
|
||||
|
||||
// delete end range
|
||||
this.highlights[endIn] = undefined;
|
||||
delete this.highlights[endIn];
|
||||
|
||||
return startIn;
|
||||
} else if(startIn !== false) {
|
||||
selectedRange.setStart(this.highlights[startIn].range.startContainer,
|
||||
this.highlights[startIn].range.startOffset);
|
||||
this.highlights[startIn].initWithRange(selectedRange);
|
||||
return this.highlights[startIn];
|
||||
} else if(endIn != false) {
|
||||
selectedRange.setEnd(this.highlights[endIn].range.endContainer,
|
||||
this.highlights[endIn].range.endOffset);
|
||||
this.highlights[endIn].initWithRange(selectedRange);
|
||||
return this.highlights[endIn];
|
||||
}
|
||||
|
||||
var highlight = new Zotero.Highlight(this);
|
||||
highlight.initWithRange(selectedRange);
|
||||
this.highlights.push(highlight);
|
||||
return highlight;
|
||||
}
|
||||
|
||||
Zotero.Annotations.prototype.refresh = function() {
|
||||
for each(var annotation in this.annotations) {
|
||||
annotation.display();
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.Annotations.prototype.save = function() {
|
||||
Zotero.DB.beginTransaction();
|
||||
try {
|
||||
Zotero.DB.query("DELETE FROM highlights WHERE itemID = ?", [this.itemID]);
|
||||
|
||||
// save highlights
|
||||
for each(var highlight in this.highlights) {
|
||||
if(highlight) highlight.save();
|
||||
}
|
||||
|
||||
// save annotations
|
||||
for each(var annotation in this.annotations) {
|
||||
annotation.save();
|
||||
}
|
||||
Zotero.DB.commitTransaction();
|
||||
} catch(e) {
|
||||
Zotero.DB.rollbackTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.Annotations.prototype.load = function() {
|
||||
// load annotations
|
||||
var rows = Zotero.DB.query("SELECT * FROM annotations WHERE itemID = ?", [this.itemID]);
|
||||
for each(var row in rows) {
|
||||
var annotation = this.createAnnotation();
|
||||
annotation.initWithDBRow(row);
|
||||
}
|
||||
|
||||
// load highlights
|
||||
var rows = Zotero.DB.query("SELECT * FROM highlights WHERE itemID = ?", [this.itemID]);
|
||||
for each(var row in rows) {
|
||||
var highlight = new Zotero.Highlight(this);
|
||||
highlight.initWithDBRow(row);
|
||||
this.highlights.push(highlight);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Zotero.Annotation
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// an annotation (usually generated using Zotero.Annotations.createAnnotation())
|
||||
|
||||
Zotero.Annotation = function(annotationsObj) {
|
||||
this.annotationsObj = annotationsObj;
|
||||
this.window = annotationsObj.browser.contentWindow;
|
||||
this.document = annotationsObj.browser.contentDocument;
|
||||
this.nsResolver = annotationsObj.nsResolver;
|
||||
}
|
||||
|
||||
Zotero.Annotation.prototype.initWithEvent = function(e) {
|
||||
var maxOffset = false;
|
||||
|
||||
try {
|
||||
var range = this.window.getSelection().getRangeAt(0);
|
||||
this.node = range.startContainer;
|
||||
var offset = range.startOffset;
|
||||
if(this.node.nodeValue) maxOffset = this.node.nodeValue.length;
|
||||
} catch(err) {
|
||||
this.node = e.target;
|
||||
var offset = 0;
|
||||
}
|
||||
|
||||
var clickX = this.window.pageXOffset + e.clientX;
|
||||
var clickY = this.window.pageYOffset + e.clientY;
|
||||
|
||||
var isTextNode = (this.node.nodeType == Components.interfaces.nsIDOMNode.TEXT_NODE);
|
||||
|
||||
if(offset == 0 || !isTextNode) {
|
||||
// tag by this.offset from parent this.node, rather than text
|
||||
if(isTextNode) this.node = this.node.parentNode;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if(offset) this._generateMarker(offset);
|
||||
|
||||
var pixelOffset = Zotero.Annotate.getPixelOffset(this.node);
|
||||
this.x = clickX - pixelOffset[0];
|
||||
this.y = clickY - pixelOffset[1];
|
||||
this.editable = true;
|
||||
|
||||
Zotero.debug("Annotate: added new annotation");
|
||||
|
||||
|
||||
this.displayWithAbsoluteCoordinates(clickX, clickY);
|
||||
}
|
||||
|
||||
Zotero.Annotation.prototype.initWithDBRow = function(row) {
|
||||
var point = Zotero.Annotate.getPointForPath(row.parent, row.textNode,
|
||||
row.offset, this.document, this.nsResolver);
|
||||
if(!point) {
|
||||
Zotero.debug("Annotate: could not load annotation "+row.annotationID+" from DB");
|
||||
return;
|
||||
}
|
||||
this.node = point.node;
|
||||
|
||||
if(point.offset) this._generateMarker(point.offset);
|
||||
|
||||
this.x = row.x;
|
||||
this.y = row.y;
|
||||
this.annotationID = row.annotationID;
|
||||
this.editable = true;
|
||||
|
||||
this.display();
|
||||
|
||||
this.textarea.value = row.text;
|
||||
}
|
||||
|
||||
Zotero.Annotation.prototype.save = function() {
|
||||
var text = this.textarea.value;
|
||||
|
||||
if(this.annotationID) {
|
||||
// already in the DB; all we need to do is update the text
|
||||
var query = "UPDATE annotations SET text = ? WHERE annotationID = ?";
|
||||
var parameters = [
|
||||
text,
|
||||
this.annotationID
|
||||
];
|
||||
} else {
|
||||
// fetch marker location
|
||||
if(this.node.getAttribute && this.node.getAttribute("zotero") == "annotation-marker") {
|
||||
var node = this.node.previousSibling;
|
||||
|
||||
if(node.nodeType != Components.interfaces.nsIDOMNode.TEXT_NODE) {
|
||||
// someone added a highlight around this annotation
|
||||
node = node.lastChild;
|
||||
}
|
||||
var offset = node.nodeValue.length;
|
||||
} else {
|
||||
var node = this.node;
|
||||
var offset = 0;
|
||||
}
|
||||
|
||||
// fetch path to node
|
||||
var path = Zotero.Annotate.getPathForPoint(node, offset);
|
||||
|
||||
var query = "INSERT INTO annotations VALUES (NULL, ?, ?, ?, ?, ?, ?, ?)";
|
||||
var parameters = [
|
||||
this.annotationsObj.itemID, // itemID
|
||||
path.parent, // parent
|
||||
path.textNode, // textNode
|
||||
path.offset, // offset
|
||||
this.x, // x
|
||||
this.y, // y
|
||||
text // text
|
||||
];
|
||||
}
|
||||
|
||||
Zotero.DB.query(query, parameters);
|
||||
}
|
||||
|
||||
Zotero.Annotation.prototype.display = function() {
|
||||
if(!this.node) throw "Annotation not initialized!";
|
||||
|
||||
var x = 0, y = 0;
|
||||
|
||||
// first fetch the coordinates
|
||||
var pixelOffset = Zotero.Annotate.getPixelOffset(this.node);
|
||||
|
||||
var x = pixelOffset[0] + this.x;
|
||||
var y = pixelOffset[1] + this.y;
|
||||
|
||||
// then display
|
||||
this.displayWithAbsoluteCoordinates(x, y);
|
||||
}
|
||||
|
||||
Zotero.Annotation.prototype.displayWithAbsoluteCoordinates = function(absX, absY) {
|
||||
if(!this.node) throw "Annotation not initialized!";
|
||||
|
||||
var startScroll = this.window.scrollMaxX;
|
||||
|
||||
if(!this.div) {
|
||||
this.div = this.document.createElement("div");
|
||||
this.div.setAttribute("zotero", "annotation");
|
||||
this.document.getElementsByTagName("body")[0].appendChild(this.div);
|
||||
this.div.style.backgroundColor = Zotero.Annotate.annotationColor;
|
||||
this.div.style.padding = "0";
|
||||
this.div.style.display = "block";
|
||||
this.div.style.position = "absolute";
|
||||
this.div.style.border = "1px solid";
|
||||
this.div.style.borderColor = Zotero.Annotate.annotationBorderColor;
|
||||
this.div.style.MozOpacity = 0.9;
|
||||
this.div.style.zIndex = this.annotationsObj.zIndex;
|
||||
var me = this;
|
||||
this.div.addEventListener("click", function() { me._click() }, false);
|
||||
|
||||
this._addChildElements();
|
||||
}
|
||||
this.div.style.display = "block";
|
||||
this.div.style.left = absX+"px";
|
||||
this.div.style.top = absY+"px";
|
||||
|
||||
// move to the left if we're making things scroll
|
||||
if(absX + this.div.scrollWidth > this.window.innerWidth) {
|
||||
this.div.style.left = (absX-this.div.scrollWidth)+"px";
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.Annotation.prototype._generateMarker = function(offset) {
|
||||
// first, we create a new span at the correct offset in the node
|
||||
var range = this.document.createRange();
|
||||
range.setStart(this.node, offset);
|
||||
range.setEnd(this.node, offset);
|
||||
|
||||
// next, we insert a span
|
||||
this.node = this.document.createElement("span");
|
||||
this.node.setAttribute("zotero", "annotation-marker");
|
||||
range.insertNode(this.node);
|
||||
}
|
||||
|
||||
Zotero.Annotation.prototype._addChildElements = function() {
|
||||
var me = this;
|
||||
|
||||
if(this.editable) {
|
||||
var div = this.document.createElement("div");
|
||||
div.style.display = "block";
|
||||
div.style.textAlign = "left";
|
||||
div.style.backgroundColor = Zotero.Annotate.annotationBarColor;
|
||||
div.style.paddingRight = "0";
|
||||
div.style.paddingLeft = div.style.paddingTop = div.style.paddingBottom = "1px";
|
||||
div.style.borderBottom = "1px solid";
|
||||
div.style.borderColor = Zotero.Annotate.annotationBorderColor;
|
||||
|
||||
var img = this.document.createElement("img");
|
||||
img.src = "chrome://zotero/skin/annotation-close.png";
|
||||
img.addEventListener("click", function() { me._delete() }, false);
|
||||
div.appendChild(img);
|
||||
|
||||
this.textarea = this.document.createElement("textarea");
|
||||
this.textarea.setAttribute("zotero", "annotation");
|
||||
this.textarea.setAttribute("cols", "30");
|
||||
this.textarea.setAttribute("rows", "5");
|
||||
this.textarea.setAttribute("wrap", "soft");
|
||||
this.textarea.style.fontFamily = "Arial, Lucida Grande, FreeSans, sans";
|
||||
this.textarea.style.fontSize = "12px";
|
||||
this.textarea.style.backgroundColor = Zotero.Annotate.annotationColor;
|
||||
this.textarea.style.border = "none";
|
||||
this.textarea.style.margin = "3px";
|
||||
this.div.appendChild(div);
|
||||
this.div.appendChild(this.textarea);
|
||||
var me = this;
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.Annotation.prototype._click = function() {
|
||||
this.annotationsObj.zIndex++
|
||||
this.div.style.zIndex = this.annotationsObj.zIndex;
|
||||
}
|
||||
|
||||
Zotero.Annotation.prototype._delete = function() {
|
||||
if(this.annotationID) {
|
||||
Zotero.DB.query("DELETE FROM annotations WHERE annotationID = ?", [this.annotationID]);
|
||||
}
|
||||
|
||||
// hide div
|
||||
this.div.parentNode.removeChild(this.div);
|
||||
// delete from list
|
||||
for(var i in this.annotationsObj.annotations) {
|
||||
if(this.annotationsObj.annotations[i] == this) {
|
||||
this.annotationsObj.annotations.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Zotero.Highlight
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// a highlight (usually generated using Zotero.Annotations.createHighlight())
|
||||
|
||||
Zotero.Highlight = function(annotationsObj) {
|
||||
this.annotationsObj = annotationsObj;
|
||||
this.window = annotationsObj.browser.contentWindow;
|
||||
this.document = annotationsObj.browser.contentDocument;
|
||||
this.nsResolver = annotationsObj.nsResolver;
|
||||
|
||||
this.spans = new Array();
|
||||
}
|
||||
|
||||
Zotero.Highlight.prototype.initWithDBRow = function(row) {
|
||||
Zotero.debug(row.startParent);
|
||||
var start = Zotero.Annotate.getPointForPath(row.startParent, row.startTextNode,
|
||||
row.startOffset, this.document, this.nsResolver);
|
||||
var end = Zotero.Annotate.getPointForPath(row.endParent, row.endTextNode,
|
||||
row.endOffset, this.document, this.nsResolver);
|
||||
if(!start || !end) {
|
||||
Zotero.debug("Highlight: could not initialize from DB row");
|
||||
return false;
|
||||
}
|
||||
|
||||
this.range = this.document.createRange();
|
||||
this.range.setStart(start.node, start.offset);
|
||||
this.range.setEnd(end.node, end.offset);
|
||||
|
||||
this._highlight();
|
||||
}
|
||||
|
||||
Zotero.Highlight.prototype.initWithRange = function(range) {
|
||||
this.range = range;
|
||||
this._highlight();
|
||||
}
|
||||
|
||||
Zotero.Highlight.prototype.save = function(index) {
|
||||
var textType = Components.interfaces.nsIDOMNode.TEXT_NODE;
|
||||
|
||||
var start = Zotero.Annotate.getPathForPoint(this.range.startContainer, this.range.startOffset);
|
||||
var end = Zotero.Annotate.getPathForPoint(this.range.endContainer, this.range.endOffset);
|
||||
|
||||
var query = "INSERT INTO highlights VALUES (NULL, ?, ?, ?, ?, ?, ?, ?)";
|
||||
var parameters = [
|
||||
this.annotationsObj.itemID, // itemID
|
||||
start.parent, // startParent
|
||||
start.textNode, // startTextNode
|
||||
start.offset, // startOffset
|
||||
end.parent, // endParent
|
||||
end.textNode, // endTextNode
|
||||
end.offset // endOffset
|
||||
];
|
||||
|
||||
Zotero.DB.query(query, parameters);
|
||||
}
|
||||
|
||||
Zotero.Highlight.prototype.remove = function() {
|
||||
var textType = Components.interfaces.nsIDOMNode.TEXT_NODE;
|
||||
|
||||
for each(var span in this.spans) {
|
||||
var parentNode = span.parentNode;
|
||||
|
||||
// deal with split text nodes
|
||||
if(span.childNodes.length == 1 && span.previousSibling && span.nextSibling &&
|
||||
textType == span.previousSibling.nodeType == span.firstChild.nodeType == span.nextSibling.nodeType) {
|
||||
span.previousSibling.nodeValue += span.firstChild.nodeValue + span.nextSibling.nodeValue;
|
||||
span.removeChild(span.firstChild);
|
||||
parentNode.removeChild(span.nextSibling);
|
||||
} else if(span.previousSibling &&
|
||||
textType == span.firstChild.nodeType == span.previousSibling.nodeType) {
|
||||
span.previousSibling.nodeValue += span.firstChild.nodeValue;
|
||||
span.removeChild(span.firstChild);
|
||||
} else if(span.nextSibling &&
|
||||
textType == span.lastChild.nodeType == span.nextSibling.nodeType) {
|
||||
span.nextSibling.nodeValue = span.lastChild.nodeValue + span.nextSibling.nodeValue;
|
||||
span.removeChild(span.lastChild);
|
||||
}
|
||||
|
||||
// attach child nodes before
|
||||
while(span.firstChild) {
|
||||
var child = span.firstChild;
|
||||
span.removeChild(child);
|
||||
parentNode.insertBefore(child, span);
|
||||
}
|
||||
|
||||
// remove span from tree
|
||||
parentNode.removeChild(span);
|
||||
}
|
||||
|
||||
this.spans = new Array();
|
||||
}
|
||||
|
||||
Zotero.Highlight.prototype._highlight = function() {
|
||||
var startNode = this.range.startContainer;
|
||||
var endNode = this.range.endContainer;
|
||||
|
||||
var ancestor = this.range.commonAncestorContainer;
|
||||
|
||||
var onlyOneNode = startNode.isSameNode(endNode);
|
||||
|
||||
if(!onlyOneNode) {
|
||||
// highlight nodes after start node in the DOM hierarchy not at ancestor level
|
||||
while(!startNode.parentNode.isSameNode(ancestor)) {
|
||||
if(startNode.nextSibling) {
|
||||
this._highlightSpaceBetween(startNode.nextSibling, startNode.parentNode.lastChild);
|
||||
}
|
||||
|
||||
startNode = startNode.parentNode;
|
||||
}
|
||||
// highlight nodes after end node in the DOM hierarchy not at ancestor level
|
||||
while(!endNode.parentNode.isSameNode(ancestor)) {
|
||||
if(endNode.previousSibling) {
|
||||
this._highlightSpaceBetween(endNode.parentNode.firstChild, endNode.previousSibling);
|
||||
}
|
||||
|
||||
endNode = endNode.parentNode;
|
||||
}
|
||||
// highlight nodes between start node and end node at ancestor level
|
||||
if(!startNode.isSameNode(endNode.previousSibling)) {
|
||||
this._highlightSpaceBetween(startNode.nextSibling, endNode.previousSibling);
|
||||
}
|
||||
}
|
||||
|
||||
// split the end off the existing node
|
||||
if(this.range.endContainer.nodeType == Components.interfaces.nsIDOMNode.TEXT_NODE && this.range.endOffset != 0) {
|
||||
if(this.range.endOffset != this.range.endContainer.nodeValue) {
|
||||
var textNode = this.range.endContainer.splitText(this.range.endOffset);
|
||||
}
|
||||
if(!onlyOneNode) {
|
||||
this._highlightTextNode(this.range.endContainer);
|
||||
}
|
||||
if(textNode) this.range.setEnd(textNode, 0);
|
||||
}
|
||||
|
||||
// split the start off of the first node
|
||||
if(this.range.startContainer.nodeType == Components.interfaces.nsIDOMNode.TEXT_NODE) {
|
||||
if(this.range.startOffset == 0) {
|
||||
var highlightNode = this.range.startContainer;
|
||||
} else {
|
||||
var highlightNode = this.range.startContainer.splitText(this.range.startOffset);
|
||||
}
|
||||
var span = this._highlightTextNode(highlightNode);
|
||||
this.range.setStart(span.firstChild, 0);
|
||||
} else {
|
||||
this._highlightSpaceBetween(this.range.startContainer, this.range.startContainer);
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.Highlight.prototype._highlightTextNode = function(textNode) {
|
||||
var parent = textNode.parentNode;
|
||||
if(parent.getAttribute("zotero") == "highlight") {
|
||||
// already highlighted
|
||||
return parent;
|
||||
}
|
||||
|
||||
var nextSibling = textNode.nextSibling;
|
||||
if(nextSibling && nextSibling.getAttribute &&
|
||||
nextSibling.getAttribute("zotero") == "highlight") {
|
||||
// next node is highlighted
|
||||
parent.removeChild(textNode);
|
||||
nextSibling.firstChild.nodeValue = textNode.nodeValue + nextSibling.firstChild.nodeValue;
|
||||
return nextSibling;
|
||||
}
|
||||
|
||||
var previousSibling = textNode.previousSibling;
|
||||
if(previousSibling && previousSibling.getAttribute &&
|
||||
previousSibling.getAttribute("zotero") == "highlight") {
|
||||
// previous node is highlighted
|
||||
parent.removeChild(textNode);
|
||||
previousSibling.firstChild.nodeValue += textNode.nodeValue;
|
||||
return previousSibling;
|
||||
}
|
||||
|
||||
var span = this.document.createElement("span");
|
||||
span.setAttribute("zotero", "highlight");
|
||||
span.style.display = "inline";
|
||||
span.style.backgroundColor = Zotero.Annotate.highlightColor;
|
||||
|
||||
parent.removeChild(textNode);
|
||||
span.appendChild(textNode);
|
||||
parent.insertBefore(span, (nextSibling ? nextSibling : null));
|
||||
|
||||
this.spans.push(span);
|
||||
|
||||
return span;
|
||||
}
|
||||
|
||||
Zotero.Highlight.prototype._highlightSpaceBetween = function(start, end) {
|
||||
var meaningfulRe = /[^\s\r\n]/;
|
||||
|
||||
var node = start;
|
||||
var text;
|
||||
|
||||
while(node) {
|
||||
// process nodes
|
||||
if(node.nodeType == Components.interfaces.nsIDOMNode.TEXT_NODE) {
|
||||
var textArray = [node];
|
||||
} else {
|
||||
var texts = this.document.evaluate('.//text()', node, this.nsResolver,
|
||||
Components.interfaces.nsIDOMXPathResult.ANY_TYPE, null);
|
||||
var textArray = new Array()
|
||||
while(text = texts.iterateNext()) textArray.push(text);
|
||||
}
|
||||
|
||||
// do this in the middle, after we're finished with node but before we
|
||||
// add any spans
|
||||
if(node.isSameNode(end)) {
|
||||
node = false;
|
||||
} else {
|
||||
node = node.nextSibling;
|
||||
}
|
||||
|
||||
for each(var textNode in textArray) {
|
||||
this._highlightTextNode(textNode);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -697,7 +697,7 @@ Zotero.Ingester.MIMEHandler.StreamListener = function(request, contentType) {
|
|||
var windowWatcher = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Components.interfaces.nsIWindowWatcher);
|
||||
this._frontWindow = windowWatcher.activeWindow;
|
||||
this._frontWindow.Zotero_Ingester_Interface.Progress.show();
|
||||
this._frontWindow.Zotero_Browser.Progress.show();
|
||||
|
||||
Zotero.debug("EndNote prepared to grab content type "+contentType);
|
||||
}
|
||||
|
@ -749,15 +749,15 @@ Zotero.Ingester.MIMEHandler.StreamListener.prototype.onStopRequest = function(ch
|
|||
try {
|
||||
saveLocation = frontWindow.ZoteroPane.getSelectedCollection();
|
||||
} catch(e) {}
|
||||
translation.setHandler("itemDone", function(obj, item) { frontWindow.Zotero_Ingester_Interface._itemDone(obj, item, saveLocation) });
|
||||
translation.setHandler("done", function(obj, item) { frontWindow.Zotero_Ingester_Interface._finishScraping(obj, item, saveLocation) });
|
||||
translation.setHandler("itemDone", function(obj, item) { frontWindow.Zotero_Browser._itemDone(obj, item, saveLocation) });
|
||||
translation.setHandler("done", function(obj, item) { frontWindow.Zotero_Browser._finishScraping(obj, item, saveLocation) });
|
||||
|
||||
// attempt to retrieve translators
|
||||
var translators = translation.getTranslators();
|
||||
if(!translators.length) {
|
||||
// we lied. we can't really translate this file. call
|
||||
// nsIExternalHelperAppService with the data
|
||||
this._frontWindow.Zotero_Ingester_Interface.Progress.kill();
|
||||
this._frontWindow.Zotero_Browser.Progress.kill();
|
||||
|
||||
var streamListener;
|
||||
if(streamListener = externalHelperAppService.doContent(this._contentType, this._request, this._frontWindow)) {
|
||||
|
|
|
@ -86,4 +86,7 @@
|
|||
|
||||
<!ENTITY zotero.citation.page "Page">
|
||||
<!ENTITY zotero.citation.paragraph "Paragraph">
|
||||
<!ENTITY zotero.citation.line "Line">
|
||||
<!ENTITY zotero.citation.line "Line">
|
||||
|
||||
<!ENTITY zotero.annotate.toolbar.add.label "Add Annotation">
|
||||
<!ENTITY zotero.annotate.toolbar.highlight.label "Highlight Text">
|
BIN
chrome/skin/default/zotero/annotate-add-selected.png
Normal file
BIN
chrome/skin/default/zotero/annotate-add-selected.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 771 B |
BIN
chrome/skin/default/zotero/annotate-add.png
Normal file
BIN
chrome/skin/default/zotero/annotate-add.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 682 B |
BIN
chrome/skin/default/zotero/annotate-highlight-selected.png
Normal file
BIN
chrome/skin/default/zotero/annotate-highlight-selected.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
BIN
chrome/skin/default/zotero/annotate-highlight.png
Normal file
BIN
chrome/skin/default/zotero/annotate-highlight.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 987 B |
BIN
chrome/skin/default/zotero/annotation-close.png
Normal file
BIN
chrome/skin/default/zotero/annotation-close.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 187 B |
|
@ -225,4 +225,24 @@
|
|||
min-height: 4px;
|
||||
max-height: 4px;
|
||||
background: #f5f5f5 !important;
|
||||
}
|
||||
|
||||
#zotero-annotate-tb-add
|
||||
{
|
||||
list-style-image: url('chrome://zotero/skin/annotate-add.png');
|
||||
}
|
||||
|
||||
#zotero-annotate-tb-highlight
|
||||
{
|
||||
list-style-image: url('chrome://zotero/skin/annotate-highlight.png');
|
||||
}
|
||||
|
||||
#zotero-annotate-tb-add[tool-active=true]
|
||||
{
|
||||
list-style-image: url('chrome://zotero/skin/annotate-add-selected.png');
|
||||
}
|
||||
|
||||
#zotero-annotate-tb-highlight[tool-active=true]
|
||||
{
|
||||
list-style-image: url('chrome://zotero/skin/annotate-highlight-selected.png');
|
||||
}
|
|
@ -94,6 +94,10 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
|||
.getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://zotero/content/xpcom/progressWindow.js");
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://zotero/content/xpcom/annotate.js");
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://global/content/nsTransferable.js");
|
||||
|
|
28
userdata.sql
28
userdata.sql
|
@ -1,4 +1,4 @@
|
|||
-- 13
|
||||
-- 14
|
||||
|
||||
-- This file creates tables containing user-specific data -- any changes
|
||||
-- to existing tables made here must be mirrored in transition steps in
|
||||
|
@ -222,3 +222,29 @@ CREATE TABLE IF NOT EXISTS csl (
|
|||
title TEXT,
|
||||
csl TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS annotations (
|
||||
annotationID INTEGER PRIMARY KEY,
|
||||
itemID INT,
|
||||
parent TEXT,
|
||||
textNode INT,
|
||||
offset INT,
|
||||
x INT,
|
||||
y INT,
|
||||
text TEXT,
|
||||
FOREIGN KEY (itemID) REFERENCES itemAttachments(itemID)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS annotations_itemID ON annotations(itemID);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS highlights (
|
||||
highlightID INTEGER PRIMARY KEY,
|
||||
itemID INTEGER,
|
||||
startParent TEXT,
|
||||
startTextNode INT,
|
||||
startOffset INT,
|
||||
endParent TEXT,
|
||||
endTextNode INT,
|
||||
endOffset INT,
|
||||
FOREIGN KEY (itemID) REFERENCES itemAttachments(itemID)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS highlights_itemID ON highlights(itemID);
|
Loading…
Reference in New Issue
Block a user