addreses #526, highlight/unhighlight bugs (Sean, let me know if you're still having problems; a site and a list of procedures to follow to reproduce is probably the most useful thing, although some bugs can be very difficult to troubleshoot)
closes #531, error when closing tabs
This commit is contained in:
parent
00ff46d068
commit
f756d16db0
|
@ -306,6 +306,7 @@ var Zotero_Browser = new function() {
|
||||||
|
|
||||||
// clear data object
|
// clear data object
|
||||||
var tab = _getTabObject(browser);
|
var tab = _getTabObject(browser);
|
||||||
|
if(!tab) return;
|
||||||
|
|
||||||
// save annotations
|
// save annotations
|
||||||
if(tab.page && tab.page.annotations) tab.page.annotations.save();
|
if(tab.page && tab.page.annotations) tab.page.annotations.save();
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const TEXT_TYPE = Components.interfaces.nsIDOMNode.TEXT_NODE;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Zotero.Annotate
|
// Zotero.Annotate
|
||||||
|
@ -36,8 +38,7 @@ Zotero.Annotate = new function() {
|
||||||
this.getPathForPoint = getPathForPoint;
|
this.getPathForPoint = getPathForPoint;
|
||||||
this.getPointForPath = getPointForPath;
|
this.getPointForPath = getPointForPath;
|
||||||
this.getPixelOffset = getPixelOffset;
|
this.getPixelOffset = getPixelOffset;
|
||||||
|
this.normalizeRange = normalizeRange;
|
||||||
var textType = Components.interfaces.nsIDOMNode.TEXT_NODE;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* gets a path object, comprising an XPath, text node index, and offset, for
|
* gets a path object, comprising an XPath, text node index, and offset, for
|
||||||
|
@ -48,14 +49,14 @@ Zotero.Annotate = new function() {
|
||||||
|
|
||||||
var path = {parent:"", textNode:null, offset:(offset ? offset : null)};
|
var path = {parent:"", textNode:null, offset:(offset ? offset : null)};
|
||||||
|
|
||||||
var lastWasTextNode = node.nodeType == textType;
|
var lastWasTextNode = node.nodeType == TEXT_TYPE;
|
||||||
|
|
||||||
if(node.parentNode.getAttribute && node.parentNode.getAttribute("zotero")) {
|
if(node.parentNode.getAttribute && node.parentNode.getAttribute("zotero")) {
|
||||||
// if the selected point is inside a highlight node, add offsets of
|
// if the selected point is inside a highlight node, add offsets of
|
||||||
// preceding text nodes in this zotero node
|
// preceding text nodes in this zotero node
|
||||||
var sibling = node.previousSibling;
|
var sibling = node.previousSibling;
|
||||||
while(sibling) {
|
while(sibling) {
|
||||||
if(sibling.nodeType == textType) path.offset += sibling.nodeValue.length;
|
if(sibling.nodeType == TEXT_TYPE) path.offset += sibling.nodeValue.length;
|
||||||
sibling = sibling.previousSibling;
|
sibling = sibling.previousSibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,13 +66,16 @@ Zotero.Annotate = new function() {
|
||||||
// if selected point is a zotero node, move it to the last character
|
// if selected point is a zotero node, move it to the last character
|
||||||
// of the previous node
|
// of the previous node
|
||||||
node = node.previousSibling;
|
node = node.previousSibling;
|
||||||
if(node.nodeType == textType) {
|
if(node.nodeType == TEXT_TYPE) {
|
||||||
offset = node.nodeValue.length;
|
path.offset = node.nodeValue.length;
|
||||||
|
lastWasTextNode = true;
|
||||||
} else {
|
} else {
|
||||||
offset = 0;
|
path.offset = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastWasTextNode = lastWasTextNode || node.nodeType == TEXT_TYPE;
|
||||||
|
|
||||||
if(lastWasTextNode) {
|
if(lastWasTextNode) {
|
||||||
path.textNode = 1;
|
path.textNode = 1;
|
||||||
var sibling = node.previousSibling;
|
var sibling = node.previousSibling;
|
||||||
|
@ -81,7 +85,7 @@ Zotero.Annotate = new function() {
|
||||||
var isZotero = undefined;
|
var isZotero = undefined;
|
||||||
if(sibling.getAttribute) isZotero = sibling.getAttribute("zotero");
|
if(sibling.getAttribute) isZotero = sibling.getAttribute("zotero");
|
||||||
|
|
||||||
if(sibling.nodeType == textType ||
|
if(sibling.nodeType == TEXT_TYPE ||
|
||||||
(isZotero == "highlight")) {
|
(isZotero == "highlight")) {
|
||||||
// is a text node
|
// is a text node
|
||||||
if(first == true) {
|
if(first == true) {
|
||||||
|
@ -89,7 +93,9 @@ Zotero.Annotate = new function() {
|
||||||
if(sibling.getAttribute) {
|
if(sibling.getAttribute) {
|
||||||
// get offset of all child nodes
|
// get offset of all child nodes
|
||||||
for each(var child in sibling.childNodes) {
|
for each(var child in sibling.childNodes) {
|
||||||
if(child.nodeType == textType) path.offset += child.nodeValue.length;
|
if(child && child.nodeType == TEXT_TYPE) {
|
||||||
|
path.offset += child.nodeValue.length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
path.offset += sibling.nodeValue.length;
|
path.offset += sibling.nodeValue.length;
|
||||||
|
@ -108,6 +114,10 @@ Zotero.Annotate = new function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node.parentNode;
|
node = node.parentNode;
|
||||||
|
} else if(path.offset != 0) {
|
||||||
|
for(; path.offset > 0 && node.nextSibling; path.offset--) {
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var doc = node.ownerDocument;
|
var doc = node.ownerDocument;
|
||||||
|
@ -121,10 +131,14 @@ Zotero.Annotate = new function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't add highlight nodes
|
// don't add highlight nodes
|
||||||
|
if(node.tagName) {
|
||||||
var tag = node.tagName.toLowerCase();
|
var tag = node.tagName.toLowerCase();
|
||||||
if(tag == "span") {
|
if(tag == "span") {
|
||||||
tag += "[not(@zotero)]";
|
tag += "[not(@zotero)]";
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
var tag = node.nodeType;
|
||||||
|
}
|
||||||
|
|
||||||
path.parent = "/"+tag+"["+number+"]"+path.parent;
|
path.parent = "/"+tag+"["+number+"]"+path.parent;
|
||||||
|
|
||||||
|
@ -167,7 +181,7 @@ Zotero.Annotate = new function() {
|
||||||
var isZotero = undefined;
|
var isZotero = undefined;
|
||||||
if(point.node.getAttribute) isZotero = point.node.getAttribute("zotero");
|
if(point.node.getAttribute) isZotero = point.node.getAttribute("zotero");
|
||||||
|
|
||||||
if(point.node.nodeType == textType ||
|
if(point.node.nodeType == TEXT_TYPE ||
|
||||||
isZotero == "highlight") {
|
isZotero == "highlight") {
|
||||||
if(!lastWasTextNode) {
|
if(!lastWasTextNode) {
|
||||||
number++;
|
number++;
|
||||||
|
@ -198,7 +212,7 @@ Zotero.Annotate = new function() {
|
||||||
var parentNode = point.node;
|
var parentNode = point.node;
|
||||||
point.node = point.node.firstChild;
|
point.node = point.node.firstChild;
|
||||||
while(point.node) {
|
while(point.node) {
|
||||||
if(point.node.nodeType == textType) {
|
if(point.node.nodeType == TEXT_TYPE) {
|
||||||
// break if end condition reached
|
// break if end condition reached
|
||||||
if(point.node.nodeValue.length >= point.offset) return point;
|
if(point.node.nodeValue.length >= point.offset) return point;
|
||||||
// otherwise, continue subtracting offsets
|
// otherwise, continue subtracting offsets
|
||||||
|
@ -218,7 +232,7 @@ Zotero.Annotate = new function() {
|
||||||
point.node = point.node.nextSibling;
|
point.node = point.node.nextSibling;
|
||||||
// if next node does not exist or is not a text node, this
|
// if next node does not exist or is not a text node, this
|
||||||
// point is invalid
|
// point is invalid
|
||||||
if(!point.node || (point.node.nodeType != textType &&
|
if(!point.node || (point.node.nodeType != TEXT_TYPE &&
|
||||||
(!point.node.getAttribute || !point.node.getAttribute("zotero")))) {
|
(!point.node.getAttribute || !point.node.getAttribute("zotero")))) {
|
||||||
Zotero.debug("Annotate: could not find point.offset "+point.offset+" for text node "+textNode+" of "+parent);
|
Zotero.debug("Annotate: could not find point.offset "+point.offset+" for text node "+textNode+" of "+parent);
|
||||||
return false;
|
return false;
|
||||||
|
@ -242,6 +256,30 @@ Zotero.Annotate = new function() {
|
||||||
|
|
||||||
return [x, y];
|
return [x, y];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sometimes, Firefox stupidly decides to use node offsets to reference
|
||||||
|
* nodes. This is a terrible idea, because Firefox then can't figure out
|
||||||
|
* when the node boundary matches another. This fixes it.
|
||||||
|
*/
|
||||||
|
function normalizeRange(range) {
|
||||||
|
for each(var type in ["start", "end"]) {
|
||||||
|
var container = range[type+"Container"];
|
||||||
|
var offset = range[type+"Offset"];
|
||||||
|
|
||||||
|
if(container.nodeType != TEXT_TYPE && offset) {
|
||||||
|
for(; offset > 0 && container.nextSibling; offset--) {
|
||||||
|
container = container.nextSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type == "start") {
|
||||||
|
range.setStart(container, offset);
|
||||||
|
} else {
|
||||||
|
range.setEnd(container, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -332,6 +370,7 @@ Zotero.Annotations.prototype.createHighlight = function(selectedRange) {
|
||||||
return this.highlights[endIn];
|
return this.highlights[endIn];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Zotero.Annotate.normalizeRange(selectedRange);
|
||||||
var highlight = new Zotero.Highlight(this);
|
var highlight = new Zotero.Highlight(this);
|
||||||
highlight.initWithRange(selectedRange);
|
highlight.initWithRange(selectedRange);
|
||||||
this.highlights.push(highlight);
|
this.highlights.push(highlight);
|
||||||
|
@ -344,6 +383,8 @@ Zotero.Annotations.prototype.unhighlight = function(selectedRange) {
|
||||||
var compareHighlight = this.highlights[i];
|
var compareHighlight = this.highlights[i];
|
||||||
var compareRange = compareHighlight.range;
|
var compareRange = compareHighlight.range;
|
||||||
|
|
||||||
|
Zotero.Annotate.normalizeRange(compareRange);
|
||||||
|
|
||||||
var startToStart = compareRange.compareBoundaryPoints(Components.interfaces.nsIDOMRange.START_TO_START, selectedRange);
|
var startToStart = compareRange.compareBoundaryPoints(Components.interfaces.nsIDOMRange.START_TO_START, selectedRange);
|
||||||
var endToEnd = compareRange.compareBoundaryPoints(Components.interfaces.nsIDOMRange.END_TO_END, selectedRange);
|
var endToEnd = compareRange.compareBoundaryPoints(Components.interfaces.nsIDOMRange.END_TO_END, selectedRange);
|
||||||
|
|
||||||
|
@ -366,11 +407,14 @@ Zotero.Annotations.prototype.unhighlight = function(selectedRange) {
|
||||||
selectEndPoint.offset == compareEndPoint.offset) {
|
selectEndPoint.offset == compareEndPoint.offset) {
|
||||||
endToEnd = 0;
|
endToEnd = 0;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// this will unhighlight the entire end
|
// this will unhighlight the entire end
|
||||||
compareHighlight.unhighlight(selectedRange.startContainer, selectedRange.startOffset, 2);
|
compareHighlight.unhighlight(selectedRange.startContainer, selectedRange.startOffset, 2);
|
||||||
|
|
||||||
// need to use point references because they disregard highlights
|
|
||||||
var newRange = this.document.createRange();
|
var newRange = this.document.createRange();
|
||||||
|
|
||||||
|
// need to use point references because they disregard highlights
|
||||||
|
|
||||||
var startPoint = Zotero.Annotate.getPointForPath(selectEndPoint.parent, selectEndPoint.textNode, selectEndPoint.offset,
|
var startPoint = Zotero.Annotate.getPointForPath(selectEndPoint.parent, selectEndPoint.textNode, selectEndPoint.offset,
|
||||||
this.document, this.nsResolver);
|
this.document, this.nsResolver);
|
||||||
var endPoint = Zotero.Annotate.getPointForPath(compareEndPoint.parent, compareEndPoint.textNode, compareEndPoint.offset,
|
var endPoint = Zotero.Annotate.getPointForPath(compareEndPoint.parent, compareEndPoint.textNode, compareEndPoint.offset,
|
||||||
|
@ -456,9 +500,13 @@ Zotero.Annotations.prototype.load = function() {
|
||||||
// load highlights
|
// load highlights
|
||||||
var rows = Zotero.DB.query("SELECT * FROM highlights WHERE itemID = ?", [this.itemID]);
|
var rows = Zotero.DB.query("SELECT * FROM highlights WHERE itemID = ?", [this.itemID]);
|
||||||
for each(var row in rows) {
|
for each(var row in rows) {
|
||||||
|
try {
|
||||||
var highlight = new Zotero.Highlight(this);
|
var highlight = new Zotero.Highlight(this);
|
||||||
highlight.initWithDBRow(row);
|
highlight.initWithDBRow(row);
|
||||||
this.highlights.push(highlight);
|
this.highlights.push(highlight);
|
||||||
|
} catch(e) {
|
||||||
|
Zotero.debug("Annotate: could not load highlight");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -737,7 +785,6 @@ Zotero.Highlight = function(annotationsObj) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Highlight.prototype.initWithDBRow = function(row) {
|
Zotero.Highlight.prototype.initWithDBRow = function(row) {
|
||||||
Zotero.debug(row.startParent);
|
|
||||||
var start = Zotero.Annotate.getPointForPath(row.startParent, row.startTextNode,
|
var start = Zotero.Annotate.getPointForPath(row.startParent, row.startTextNode,
|
||||||
row.startOffset, this.document, this.nsResolver);
|
row.startOffset, this.document, this.nsResolver);
|
||||||
var end = Zotero.Annotate.getPointForPath(row.endParent, row.endTextNode,
|
var end = Zotero.Annotate.getPointForPath(row.endParent, row.endTextNode,
|
||||||
|
@ -796,7 +843,7 @@ Zotero.Highlight.prototype.unhighlight = function(container, offset, mode) {
|
||||||
this.range.setEnd(container, offset);
|
this.range.setEnd(container, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(var i in this.spans) {
|
for(var i=0; i<this.spans.length; i++) {
|
||||||
var span = this.spans[i];
|
var span = this.spans[i];
|
||||||
var parentNode = span.parentNode;
|
var parentNode = span.parentNode;
|
||||||
|
|
||||||
|
@ -842,24 +889,28 @@ Zotero.Highlight.prototype.unhighlight = function(container, offset, mode) {
|
||||||
|
|
||||||
this.range.setEnd(textNode, 0);
|
this.range.setEnd(textNode, 0);
|
||||||
}
|
}
|
||||||
} else if(mode == 0 || !this.range.isPointInRange(span, 1)) {
|
} else if(mode == 0 || !this.range.isPointInRange(span, 1) && parentNode) {
|
||||||
Zotero.debug("point is in range");
|
Zotero.debug("point is in range");
|
||||||
|
|
||||||
// attach child nodes before
|
// attach child nodes before
|
||||||
while(span.hasChildNodes()) {
|
while(span.hasChildNodes()) {
|
||||||
Zotero.debug("moving "+span.firstChild.textContent);
|
Zotero.debug("moving "+span.firstChild.textContent);
|
||||||
span.parentNode.insertBefore(span.removeChild(span.firstChild), span);
|
parentNode.insertBefore(span.removeChild(span.firstChild), span);
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove span from DOM
|
// remove span from DOM
|
||||||
span.parentNode.removeChild(span);
|
parentNode.removeChild(span);
|
||||||
}
|
|
||||||
|
|
||||||
parentNode.normalize();
|
// remove span from list
|
||||||
|
this.spans.splice(i, 1);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Highlight.prototype._highlight = function() {
|
Zotero.Highlight.prototype._highlight = function() {
|
||||||
|
Zotero.Annotate.normalizeRange(this.range);
|
||||||
|
|
||||||
var startNode = this.range.startContainer;
|
var startNode = this.range.startContainer;
|
||||||
var endNode = this.range.endContainer;
|
var endNode = this.range.endContainer;
|
||||||
|
|
||||||
|
@ -869,7 +920,7 @@ Zotero.Highlight.prototype._highlight = function() {
|
||||||
|
|
||||||
if(!onlyOneNode) {
|
if(!onlyOneNode) {
|
||||||
// highlight nodes after start node in the DOM hierarchy not at ancestor level
|
// highlight nodes after start node in the DOM hierarchy not at ancestor level
|
||||||
while(!startNode.parentNode.isSameNode(ancestor)) {
|
while(startNode.parentNode && !startNode.parentNode.isSameNode(ancestor)) {
|
||||||
if(startNode.nextSibling) {
|
if(startNode.nextSibling) {
|
||||||
this._highlightSpaceBetween(startNode.nextSibling, startNode.parentNode.lastChild);
|
this._highlightSpaceBetween(startNode.nextSibling, startNode.parentNode.lastChild);
|
||||||
}
|
}
|
||||||
|
@ -877,7 +928,7 @@ Zotero.Highlight.prototype._highlight = function() {
|
||||||
startNode = startNode.parentNode;
|
startNode = startNode.parentNode;
|
||||||
}
|
}
|
||||||
// highlight nodes after end node in the DOM hierarchy not at ancestor level
|
// highlight nodes after end node in the DOM hierarchy not at ancestor level
|
||||||
while(!endNode.parentNode.isSameNode(ancestor)) {
|
while(endNode.parentNode && !endNode.parentNode.isSameNode(ancestor)) {
|
||||||
if(endNode.previousSibling) {
|
if(endNode.previousSibling) {
|
||||||
this._highlightSpaceBetween(endNode.parentNode.firstChild, endNode.previousSibling);
|
this._highlightSpaceBetween(endNode.parentNode.firstChild, endNode.previousSibling);
|
||||||
}
|
}
|
||||||
|
@ -892,8 +943,10 @@ Zotero.Highlight.prototype._highlight = function() {
|
||||||
|
|
||||||
// split the end off the existing node
|
// split the end off the existing node
|
||||||
if(this.range.endContainer.nodeType == Components.interfaces.nsIDOMNode.TEXT_NODE && this.range.endOffset != 0) {
|
if(this.range.endContainer.nodeType == Components.interfaces.nsIDOMNode.TEXT_NODE && this.range.endOffset != 0) {
|
||||||
|
Zotero.debug("using end range");
|
||||||
if(this.range.endOffset != this.range.endContainer.nodeValue) {
|
if(this.range.endOffset != this.range.endContainer.nodeValue) {
|
||||||
var textNode = this.range.endContainer.splitText(this.range.endOffset);
|
var textNode = this.range.endContainer.splitText(this.range.endOffset);
|
||||||
|
Zotero.debug("split along "+textNode.textContent);
|
||||||
}
|
}
|
||||||
if(!onlyOneNode) {
|
if(!onlyOneNode) {
|
||||||
this._highlightTextNode(this.range.endContainer);
|
this._highlightTextNode(this.range.endContainer);
|
||||||
|
@ -927,7 +980,7 @@ Zotero.Highlight.prototype._highlightTextNode = function(textNode) {
|
||||||
nextSibling.getAttribute("zotero") == "highlight") {
|
nextSibling.getAttribute("zotero") == "highlight") {
|
||||||
// next node is highlighted
|
// next node is highlighted
|
||||||
parent.removeChild(textNode);
|
parent.removeChild(textNode);
|
||||||
nextSibling.firstChild.nodeValue = textNode.nodeValue + nextSibling.firstChild.nodeValue;
|
nextSibling.insertBefore(textNode, nextSibling.firstChild);
|
||||||
return nextSibling;
|
return nextSibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -936,7 +989,7 @@ Zotero.Highlight.prototype._highlightTextNode = function(textNode) {
|
||||||
previousSibling.getAttribute("zotero") == "highlight") {
|
previousSibling.getAttribute("zotero") == "highlight") {
|
||||||
// previous node is highlighted
|
// previous node is highlighted
|
||||||
parent.removeChild(textNode);
|
parent.removeChild(textNode);
|
||||||
previousSibling.firstChild.nodeValue += textNode.nodeValue;
|
previousSibling.appendChild(textNode);
|
||||||
return previousSibling;
|
return previousSibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user