diff --git a/chrome/content/zotero/itemTree.jsx b/chrome/content/zotero/itemTree.jsx
index 479a350c43..3efa466069 100644
--- a/chrome/content/zotero/itemTree.jsx
+++ b/chrome/content/zotero/itemTree.jsx
@@ -41,6 +41,7 @@ const CHILD_INDENT = 12;
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 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 {
static async init(domEl, opts={}) {
@@ -130,7 +131,7 @@ var ItemTree = class ItemTree extends LibraryTree {
this._itemTreeLoadingDeferred.resolve();
// 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
- 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.height = "2000px";
elem.style.position = "absolute";
@@ -2553,15 +2554,105 @@ var ItemTree = class ItemTree extends LibraryTree {
//
// //////////////////////////////////////////////////////////////////////////////
+ _titleMarkup = {
+ '': {
+ beginsTag: 'i',
+ inverseStyle: { fontStyle: 'normal' }
+ },
+ '': {
+ endsTag: 'i'
+ },
+ '': {
+ beginsTag: 'b',
+ inverseStyle: { fontWeight: 'normal' }
+ },
+ '': {
+ endsTag: 'b'
+ },
+ '': {
+ beginsTag: 'sub'
+ },
+ '': {
+ endsTag: 'sub'
+ },
+ '': {
+ beginsTag: 'sup'
+ },
+ '': {
+ endsTag: 'sup'
+ },
+ '': {
+ beginsTag: 'span',
+ style: { fontVariant: 'small-caps' }
+ },
+ '': {
+ // No effect in item tree
+ beginsTag: '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) {
- let span = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
+ let span = document.createElementNS(HTML_NS, 'span');
span.className = `cell ${column.className}`;
span.classList.add('primary');
// Add twisty, icon, tag swatches and retraction indicator
let twisty;
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");
}
else {
@@ -2607,15 +2698,13 @@ var ItemTree = class ItemTree extends LibraryTree {
Zotero.debug(e, 1);
}
- let textWithFullStop = data;
+ let textSpan = document.createElementNS(HTML_NS, 'span');
+ let textWithFullStop = this._renderItemTitle(data, textSpan);
if (!textWithFullStop.match(/\.$/)) {
textWithFullStop += '.';
}
let textSpanAriaLabel = [textWithFullStop, itemTypeAriaLabel, tagAriaLabel, retractedAriaLabel].join(' ');
-
- let textSpan = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
textSpan.className = "cell-text";
- textSpan.innerText = data;
textSpan.setAttribute('aria-label', textSpanAriaLabel);
span.append(twisty, icon, retracted, ...tagSpans, textSpan);
@@ -2632,7 +2721,7 @@ var ItemTree = class ItemTree extends LibraryTree {
}
_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}`;
if (this.collectionTreeRow.isTrash()) return span;
@@ -2757,7 +2846,7 @@ var ItemTree = class ItemTree extends LibraryTree {
div.innerHTML = "";
}
else {
- div = document.createElementNS("http://www.w3.org/1999/xhtml", 'div');
+ div = document.createElementNS(HTML_NS, 'div');
div.className = "row";
}
@@ -2769,7 +2858,7 @@ var ItemTree = class ItemTree extends LibraryTree {
if (this._dropRow == index) {
let span;
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";
div.appendChild(span);
} else {
@@ -3699,7 +3788,7 @@ var ItemTree = class ItemTree extends LibraryTree {
var icon = getDOMElement(iconClsName);
if (!icon) {
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;
}
@@ -3710,7 +3799,7 @@ var ItemTree = class ItemTree extends LibraryTree {
}
_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';
// If only emoji, display directly
//
diff --git a/chrome/content/zotero/xpcom/data/items.js b/chrome/content/zotero/xpcom/data/items.js
index c87c05c8f8..9db9fac948 100644
--- a/chrome/content/zotero/xpcom/data/items.js
+++ b/chrome/content/zotero/xpcom/data/items.js
@@ -1688,15 +1688,30 @@ Zotero.Items = function() {
}
- this.getSortTitle = function(title) {
- if (title === false || title === undefined || title == null) {
+ this.getSortTitle = function (title) {
+ if (!title) {
return '';
}
+
if (typeof title == 'number') {
- return title + '';
+ return title.toString();
}
- return title.replace(/^[\[\'\"](.*)[\'\"\]]?$/, '$1')
- }
+
+ let toRemove = [
+ '?i>',
+ '?b>',
+ '?sub>',
+ '?sup>',
+ '',
+ '',
+ '',
+ '\\p{P}'
+ ].map(re => new RegExp(re, 'g'));
+ for (let re of toRemove) {
+ title = title.replace(re, '');
+ }
+ return title;
+ };
Zotero.DataObjects.call(this);