Render markup in item tree, strip when sorting (#2355)
This commit is contained in:
parent
a45da8df17
commit
13f48ec5c7
2 changed files with 121 additions and 17 deletions
|
@ -41,6 +41,7 @@ const CHILD_INDENT = 12;
|
||||||
const COLORED_TAGS_RE = new RegExp("^[0-" + Zotero.Tags.MAX_COLORED_TAGS + "]{1}$");
|
const COLORED_TAGS_RE = new RegExp("^[0-" + Zotero.Tags.MAX_COLORED_TAGS + "]{1}$");
|
||||||
const COLUMN_PREFS_FILEPATH = OS.Path.join(Zotero.Profile.dir, "treePrefs.json");
|
const COLUMN_PREFS_FILEPATH = OS.Path.join(Zotero.Profile.dir, "treePrefs.json");
|
||||||
const EMOJI_RE = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu;
|
const EMOJI_RE = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu;
|
||||||
|
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||||
|
|
||||||
var ItemTree = class ItemTree extends LibraryTree {
|
var ItemTree = class ItemTree extends LibraryTree {
|
||||||
static async init(domEl, opts={}) {
|
static async init(domEl, opts={}) {
|
||||||
|
@ -130,7 +131,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
this._itemTreeLoadingDeferred.resolve();
|
this._itemTreeLoadingDeferred.resolve();
|
||||||
// Create an element where we can create drag images to be displayed next to the cursor while dragging
|
// Create an element where we can create drag images to be displayed next to the cursor while dragging
|
||||||
// since for multiple item drags we need to display all the elements
|
// since for multiple item drags we need to display all the elements
|
||||||
let elem = this._dragImageContainer = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
|
let elem = this._dragImageContainer = document.createElementNS(HTML_NS, "div");
|
||||||
elem.style.width = "100%";
|
elem.style.width = "100%";
|
||||||
elem.style.height = "2000px";
|
elem.style.height = "2000px";
|
||||||
elem.style.position = "absolute";
|
elem.style.position = "absolute";
|
||||||
|
@ -2553,15 +2554,105 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
//
|
//
|
||||||
// //////////////////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
_titleMarkup = {
|
||||||
|
'<i>': {
|
||||||
|
beginsTag: 'i',
|
||||||
|
inverseStyle: { fontStyle: 'normal' }
|
||||||
|
},
|
||||||
|
'</i>': {
|
||||||
|
endsTag: 'i'
|
||||||
|
},
|
||||||
|
'<b>': {
|
||||||
|
beginsTag: 'b',
|
||||||
|
inverseStyle: { fontWeight: 'normal' }
|
||||||
|
},
|
||||||
|
'</b>': {
|
||||||
|
endsTag: 'b'
|
||||||
|
},
|
||||||
|
'<sub>': {
|
||||||
|
beginsTag: 'sub'
|
||||||
|
},
|
||||||
|
'</sub>': {
|
||||||
|
endsTag: 'sub'
|
||||||
|
},
|
||||||
|
'<sup>': {
|
||||||
|
beginsTag: 'sup'
|
||||||
|
},
|
||||||
|
'</sup>': {
|
||||||
|
endsTag: 'sup'
|
||||||
|
},
|
||||||
|
'<span style="font-variant:small-caps;">': {
|
||||||
|
beginsTag: 'span',
|
||||||
|
style: { fontVariant: 'small-caps' }
|
||||||
|
},
|
||||||
|
'<span class="nocase">': {
|
||||||
|
// No effect in item tree
|
||||||
|
beginsTag: 'span'
|
||||||
|
},
|
||||||
|
'</span>': {
|
||||||
|
endsTag: 'span'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_renderItemTitle(title, targetNode) {
|
||||||
|
let markupStack = [];
|
||||||
|
let nodeStack = [targetNode];
|
||||||
|
let textContent = '';
|
||||||
|
|
||||||
|
for (let token of title.split(/(<[^>]+>)/)) {
|
||||||
|
if (this._titleMarkup.hasOwnProperty(token)) {
|
||||||
|
let markup = this._titleMarkup[token];
|
||||||
|
if (markup.beginsTag) {
|
||||||
|
let node = document.createElementNS(HTML_NS, markup.beginsTag);
|
||||||
|
if (markup.style) {
|
||||||
|
Object.assign(node.style, markup.style);
|
||||||
|
}
|
||||||
|
if (markup.inverseStyle && markupStack.some(otherMarkup => otherMarkup.beginsTag === markup.beginsTag)) {
|
||||||
|
Object.assign(node.style, markup.inverseStyle);
|
||||||
|
}
|
||||||
|
markupStack.push({ ...markup, token });
|
||||||
|
nodeStack.push(node);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (markup.endsTag && markupStack.some(otherMarkup => otherMarkup.beginsTag === markup.endsTag)) {
|
||||||
|
while (markupStack.length) {
|
||||||
|
let discardedMarkup = markupStack.pop();
|
||||||
|
let discardedNode = nodeStack.pop();
|
||||||
|
if (discardedMarkup.beginsTag === markup.endsTag) {
|
||||||
|
nodeStack[nodeStack.length - 1].append(discardedNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nodeStack[nodeStack.length - 1].append(discardedMarkup.token, ...discardedNode.childNodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeStack[nodeStack.length - 1].append(token);
|
||||||
|
textContent += token;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (markupStack.length) {
|
||||||
|
let discardedMarkup = markupStack.pop();
|
||||||
|
let discardedNode = nodeStack.pop();
|
||||||
|
nodeStack[0].append(discardedMarkup.token, ...discardedNode.childNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return textContent;
|
||||||
|
}
|
||||||
|
|
||||||
_renderPrimaryCell(index, data, column) {
|
_renderPrimaryCell(index, data, column) {
|
||||||
let span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
let span = document.createElementNS(HTML_NS, 'span');
|
||||||
span.className = `cell ${column.className}`;
|
span.className = `cell ${column.className}`;
|
||||||
span.classList.add('primary');
|
span.classList.add('primary');
|
||||||
|
|
||||||
// Add twisty, icon, tag swatches and retraction indicator
|
// Add twisty, icon, tag swatches and retraction indicator
|
||||||
let twisty;
|
let twisty;
|
||||||
if (this.isContainerEmpty(index)) {
|
if (this.isContainerEmpty(index)) {
|
||||||
twisty = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
twisty = document.createElementNS(HTML_NS, 'span');
|
||||||
twisty.classList.add("spacer-twisty");
|
twisty.classList.add("spacer-twisty");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2607,15 +2698,13 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
Zotero.debug(e, 1);
|
Zotero.debug(e, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let textWithFullStop = data;
|
let textSpan = document.createElementNS(HTML_NS, 'span');
|
||||||
|
let textWithFullStop = this._renderItemTitle(data, textSpan);
|
||||||
if (!textWithFullStop.match(/\.$/)) {
|
if (!textWithFullStop.match(/\.$/)) {
|
||||||
textWithFullStop += '.';
|
textWithFullStop += '.';
|
||||||
}
|
}
|
||||||
let textSpanAriaLabel = [textWithFullStop, itemTypeAriaLabel, tagAriaLabel, retractedAriaLabel].join(' ');
|
let textSpanAriaLabel = [textWithFullStop, itemTypeAriaLabel, tagAriaLabel, retractedAriaLabel].join(' ');
|
||||||
|
|
||||||
let textSpan = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
|
||||||
textSpan.className = "cell-text";
|
textSpan.className = "cell-text";
|
||||||
textSpan.innerText = data;
|
|
||||||
textSpan.setAttribute('aria-label', textSpanAriaLabel);
|
textSpan.setAttribute('aria-label', textSpanAriaLabel);
|
||||||
|
|
||||||
span.append(twisty, icon, retracted, ...tagSpans, textSpan);
|
span.append(twisty, icon, retracted, ...tagSpans, textSpan);
|
||||||
|
@ -2632,7 +2721,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderHasAttachmentCell(index, data, column) {
|
_renderHasAttachmentCell(index, data, column) {
|
||||||
let span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
let span = document.createElementNS(HTML_NS, 'span');
|
||||||
span.className = `cell ${column.className}`;
|
span.className = `cell ${column.className}`;
|
||||||
|
|
||||||
if (this.collectionTreeRow.isTrash()) return span;
|
if (this.collectionTreeRow.isTrash()) return span;
|
||||||
|
@ -2757,7 +2846,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
div.innerHTML = "";
|
div.innerHTML = "";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
div = document.createElementNS("http://www.w3.org/1999/xhtml", 'div');
|
div = document.createElementNS(HTML_NS, 'div');
|
||||||
div.className = "row";
|
div.className = "row";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2769,7 +2858,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
if (this._dropRow == index) {
|
if (this._dropRow == index) {
|
||||||
let span;
|
let span;
|
||||||
if (Zotero.DragDrop.currentOrientation != 0) {
|
if (Zotero.DragDrop.currentOrientation != 0) {
|
||||||
span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
span = document.createElementNS(HTML_NS, 'span');
|
||||||
span.className = Zotero.DragDrop.currentOrientation < 0 ? "drop-before" : "drop-after";
|
span.className = Zotero.DragDrop.currentOrientation < 0 ? "drop-before" : "drop-after";
|
||||||
div.appendChild(span);
|
div.appendChild(span);
|
||||||
} else {
|
} else {
|
||||||
|
@ -3699,7 +3788,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
var icon = getDOMElement(iconClsName);
|
var icon = getDOMElement(iconClsName);
|
||||||
if (!icon) {
|
if (!icon) {
|
||||||
Zotero.debug('Could not find tree icon for "' + itemType + '"');
|
Zotero.debug('Could not find tree icon for "' + itemType + '"');
|
||||||
return document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
return document.createElementNS(HTML_NS, 'span');
|
||||||
}
|
}
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
@ -3710,7 +3799,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
_getTagSwatch(tag, color) {
|
_getTagSwatch(tag, color) {
|
||||||
let span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
|
let span = document.createElementNS(HTML_NS, 'span');
|
||||||
span.className = 'tag-swatch';
|
span.className = 'tag-swatch';
|
||||||
// If only emoji, display directly
|
// If only emoji, display directly
|
||||||
//
|
//
|
||||||
|
|
|
@ -1688,15 +1688,30 @@ Zotero.Items = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.getSortTitle = function(title) {
|
this.getSortTitle = function (title) {
|
||||||
if (title === false || title === undefined || title == null) {
|
if (!title) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof title == 'number') {
|
if (typeof title == 'number') {
|
||||||
return title + '';
|
return title.toString();
|
||||||
}
|
}
|
||||||
return title.replace(/^[\[\'\"](.*)[\'\"\]]?$/, '$1')
|
|
||||||
}
|
let toRemove = [
|
||||||
|
'</?i>',
|
||||||
|
'</?b>',
|
||||||
|
'</?sub>',
|
||||||
|
'</?sup>',
|
||||||
|
'<span style="font-variant:small-caps;">',
|
||||||
|
'<span class="nocase">',
|
||||||
|
'</span>',
|
||||||
|
'\\p{P}'
|
||||||
|
].map(re => new RegExp(re, 'g'));
|
||||||
|
for (let re of toRemove) {
|
||||||
|
title = title.replace(re, '');
|
||||||
|
}
|
||||||
|
return title;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Zotero.DataObjects.call(this);
|
Zotero.DataObjects.call(this);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue