From d4fafae5d6d6111a93f0be578a8c6d1454e60ad2 Mon Sep 17 00:00:00 2001 From: MuiseDestiny <51939531+MuiseDestiny@users.noreply.github.com> Date: Mon, 20 Feb 2023 11:14:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=AF=E5=88=A0style?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=EF=BC=8C=E5=A2=9E=E5=8A=A0=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E4=B8=BB=E5=8F=A5=E5=AF=B9=E7=85=A7[=E5=AE=9E=E9=AA=8C]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addon/chrome/content/style.css | 15 -- package.json | 2 +- src/modules/item.ts | 22 +- src/modules/views.ts | 457 +++++++++++++++++++++------------ update.json | 4 +- update.rdf | 2 +- 6 files changed, 305 insertions(+), 197 deletions(-) diff --git a/addon/chrome/content/style.css b/addon/chrome/content/style.css index 4d71871..e69de29 100644 --- a/addon/chrome/content/style.css +++ b/addon/chrome/content/style.css @@ -1,15 +0,0 @@ -.tag-box .tag-swatch { - position: absolute; - display: inline-block; - height: .9em; - width: .9em; - border-radius: 100%; - font-size: 1em; -} - -.tag-box { - display: inline-block; - position: relative; - height: 1em; - line-height: 1em; -} \ No newline at end of file diff --git a/package.json b/package.json index aa59ebd..3014976 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zotero-style", - "version": "2.2.0", + "version": "2.2.1", "description": "让你的Zotero看起来更有趣", "config": { "addonName": "Zotero Style", diff --git a/src/modules/item.ts b/src/modules/item.ts index fc3a51f..2ea14c7 100644 --- a/src/modules/item.ts +++ b/src/modules/item.ts @@ -1,6 +1,7 @@ const log = console.log export default class AddonItem { public item!: _ZoteroItem; + public title = "Addon Item" public prefKey = "Zotero.AddonItem.key"; public cache: {[key: string]: any} = {}; constructor() { @@ -26,7 +27,7 @@ export default class AddonItem { } } let s = new Zotero.Search(); - s.addCondition("title", "contains", "Addon Item"); + s.addCondition("title", "contains", this.title); var ids = await s.search(); let items = await Zotero.Items.getAsync(ids); console.log(items) @@ -37,7 +38,7 @@ export default class AddonItem { } else { // @ts-ignore item = new Zotero.Item('computerProgram'); - item.setField('title', 'Addon Item'); + item.setField('title', this.title); await item.saveTx() log("From new") } @@ -128,20 +129,21 @@ export default class AddonItem { public hiddenNotes() { const excludeKey = this.item.key const search = Zotero.Search.prototype.search; + const itemTitle = this.title Zotero.Search.prototype.search = async function () { let ids = await search.apply(this, arguments); - // log("hook ids", ids) - // return ids.filter((id: number) => { - // return Zotero.Items.get(id).parentKey != excludeKey - // }) // 只有在搜索结果是笔记时才过滤 if ( - Zotero.Items.get(ids[0]).itemTypeID == 26 - && Zotero.Items.get(ids.slice(-1)[0]).itemTypeID == 26 + Zotero.Items.get(ids[0]).itemTypeID == 26 && + Zotero.Items.get(ids.slice(-1)[0]).itemTypeID == 26 ) { - log("hook ids", ids) + log("hook ids", ids.length) return ids.filter((id: number) => { - return Zotero.Items.get(id).parentKey != excludeKey + const parentID = Zotero.Items.get(id).parentID + if (!parentID) { return true } + const parentItem = Zotero.Items.get(parentID) + if (!parentItem) { return true } + return parentItem.key != excludeKey && parentItem.getField("title") != itemTitle }) } else { return ids diff --git a/src/modules/views.ts b/src/modules/views.ts index fc1599b..236b5f4 100644 --- a/src/modules/views.ts +++ b/src/modules/views.ts @@ -37,6 +37,20 @@ export default class Views { [id^=item-tree-main-default-row]:nth-child(even) { background-color: ${Zotero.Prefs.get(`${config.addonRef}.titleColumn.even`) as string}; } + .tag-box .tag-swatch { + position: absolute; + display: inline-block; + height: .9em; + width: .9em; + border-radius: 100%; + font-size: 1em; + } + .tag-box { + display: inline-block; + position: relative; + height: 1em; + line-height: 1em; + } ` }, }); @@ -1546,7 +1560,7 @@ export default class Views { dialog({ attributes: { buttonlabelaccept: "Set", - title: colKey + title: colKey.charAt(0).toUpperCase() + colKey.slice(1) }, element: element, hooks: { accept } @@ -1800,6 +1814,279 @@ export default class Views { * 注册Prompt命令 */ public async registerCommands() { + // 注册搜索文库 + ztoolkit.Prompt.register([{ + id: "search", + callback: async (prompt) => { + // https://github.com/zotero/zotero/blob/7262465109c21919b56a7ab214f7c7a8e1e63909/chrome/content/zotero/integration/quickFormat.js#L589 + function getItemDescription(item: Zotero.Item) { + var nodes = []; + var str = ""; + var author, authorDate = ""; + if (item.firstCreator) { author = authorDate = item.firstCreator; } + var date = item.getField("date", true, true) as string; + if (date && (date = date.substr(0, 4)) !== "0000") { + authorDate += " (" + parseInt(date) + ")"; + } + authorDate = authorDate.trim(); + if (authorDate) nodes.push(authorDate); + + var publicationTitle = item.getField("publicationTitle", false, true); + if (publicationTitle) { + nodes.push(`${publicationTitle}`); + } + var volumeIssue = item.getField("volume"); + var issue = item.getField("issue"); + if (issue) volumeIssue += "(" + issue + ")"; + if (volumeIssue) nodes.push(volumeIssue); + + var publisherPlace = [], field; + if ((field = item.getField("publisher"))) publisherPlace.push(field); + if ((field = item.getField("place"))) publisherPlace.push(field); + if (publisherPlace.length) nodes.push(publisherPlace.join(": ")); + + var pages = item.getField("pages"); + if (pages) nodes.push(pages); + + if (!nodes.length) { + var url = item.getField("url"); + if (url) nodes.push(url); + } + + // compile everything together + for (var i = 0, n = nodes.length; i < n; i++) { + var node = nodes[i]; + + if (i != 0) str += ", "; + + if (typeof node === "object") { + var label = document.createElement("label"); + label.setAttribute("value", str); + label.setAttribute("crop", "end"); + str = ""; + } else { + str += node; + } + } + str.length && (str += ".") + return str + }; + function filter(ids: number[]) { + ids = ids.filter(async (id) => { + const item = await Zotero.Items.getAsync(id) + return item.isRegularItem() && !item.isFeedItem + }) + return ids + } + const text = prompt.inputNode.value; + prompt.showTip("Searching...") + const s = new Zotero.Search(); + s.addCondition("quicksearch-titleCreatorYear", "contains", text); + s.addCondition("itemType", "isNot", "attachment"); + let ids = await s.search(); + // prompt.exit will remove current container element. + // @ts-ignore + prompt.exit(); + const container = prompt.createCommandsContainer(); + container.classList.add("suggestions"); + ids = filter(ids) + console.log(ids.length) + if (ids.length == 0) { + const s = new Zotero.Search(); + const operators = ['is', 'isNot', 'true', 'false', 'isInTheLast', 'isBefore', 'isAfter', 'contains', 'doesNotContain', 'beginsWith']; + let hasValidCondition = false + let joinMode: string = "all" + if (/\s*\|\|\s*/.test(text)) { + joinMode = "any" + } + text.split(/\s*(&&|\|\|)\s*/g).forEach((conditinString: string) => { + let conditions = conditinString.split(/\s+/g); + if (conditions.length == 3 && operators.indexOf(conditions[1]) != -1) { + hasValidCondition = true + s.addCondition("joinMode", joinMode); + s.addCondition( + conditions[0] as string, + conditions[1] as Zotero.Search.Operator, + conditions[2] as string + ); + } + }) + if (hasValidCondition) { + ids = await s.search(); + } + } + ids = filter(ids) + console.log(ids.length) + if (ids.length > 0) { + ids.forEach((id: number) => { + const item = Zotero.Items.get(id) + const title = item.getField("title") + const ele = ztoolkit.UI.createElement(document, "div", { + namespace: "html", + classList: ["command"], + listeners: [ + { + type: "mousemove", + listener: function () { + // @ts-ignore + prompt.selectItem(this) + } + }, + { + type: "click", + listener: () => { + prompt.promptNode.style.display = "none" + Zotero_Tabs.select('zotero-pane'); + ZoteroPane.selectItem(item.id); + } + } + ], + styles: { + display: "flex", + flexDirection: "column", + justifyContent: "start", + }, + children: [ + { + tag: "span", + styles: { + fontWeight: "bold", + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap" + + }, + properties: { + innerText: title + } + }, + { + tag: "span", + styles: { + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap" + }, + properties: { + innerHTML: getItemDescription(item) + } + } + ] + }) + container.appendChild(ele) + }) + } else { + // @ts-ignore + prompt.exit() + prompt.showTip("Not Found.") + } + } + }]) + /** + * 逐句翻译对照 + */ + ztoolkit.Prompt.register([{ + name: "逐句对照翻译", + label: "Translate", + when: () => { + const selection = ztoolkit.Reader.getSelectedText( + Zotero.Reader.getByTabID(Zotero_Tabs.selectedID) + ); + ztoolkit.log(selection) + return selection.length > 0 && Zotero.PDFTranslate + }, + callback: (prompt) => { + const selection = ztoolkit.Reader.getSelectedText( + Zotero.Reader.getByTabID(Zotero_Tabs.selectedID) + ); + const container = prompt.createCommandsContainer() as HTMLDivElement + container.style = ` + padding: .5em; + display: flex; + flex-direction: row; + justify-content: space-between; + ` + const queue = Zotero.PDFTranslate.data.translate.queue + const task = queue.find((i: any) => i.raw == selection) || queue.slice(-1)[0] + if (!task) { + prompt.showTip("Task is Null.") + } + const rawText = task.raw, resultText = task.result; + const props = { + styles: { + width: "49%", + height: "20em", + border: "1px solid #eee", + textAlign: "justify", + padding: ".5em", + fontSize: "1em", + lineHeight: "1.5em", + overflowY: "auto", + }, + } + // TODO: 首选项自行配置,不同语言分隔符,比如英文点,中文句号,这里默认英文中文翻译 + let addSentences = (node: HTMLElement, text: string, sep: string) => { + let sentences = text.match(new RegExp(`.+?${sep}\\s*`, "g"))! as string[] + console.log(sentences) + // 把错误断开的连接起来Fig. Table.等,只针对英文,其它分隔符以下代码将无效 + let i = 0 + while (i < sentences.length - 1) { + if ( + // Fig. 5形式 + /(table|fig|figure)\.\s*$/i.test(sentences[i]) || + // 2.33形式 + (/\d\.$/i.test(sentences[i]) && /^\d/i.test(sentences[i+1])) + ) { + console.log(sentences[i], "-", sentences[i + 1]) + sentences[i] = sentences[i] + sentences[i + 1] + sentences.splice(i+1, 1) + } else { + i += 1 + } + } + sentences = sentences.filter(s => s.length > 0) + console.log(sentences) + for (let i = 0; i < sentences.length; i++) { + console.log(sentences[i]) + node.appendChild(ztoolkit.UI.createElement(document, "span", { + id: `sentence-${i}`, + properties: { + innerHTML: sentences[i] + }, + listeners: [ + { + type: "mousemove", + listener: function () { + const hightlightColor = "#fee972" + // @ts-ignore + const span = this as HTMLSpanElement + const parentNode = span.parentNode as HTMLDivElement + parentNode?.querySelectorAll("span").forEach(e => e.style.backgroundColor="") + span.style.backgroundColor = hightlightColor + const siblingNode = (parentNode?.previousSibling || parentNode?.nextSibling) as HTMLDivElement + siblingNode?.querySelectorAll("span").forEach(e => e.style.backgroundColor = ""); + const twinSpan = siblingNode.querySelector(`span[id=sentence-${i}]`) as HTMLSpanElement + twinSpan.style.backgroundColor = hightlightColor; + // 滚轮滚动,让译文/原文在中间显示 + siblingNode.scrollTo(0, twinSpan.offsetTop - siblingNode.offsetHeight * .5); + } + } + ] + })) + } + } + // 原文 + const rawDiv = ztoolkit.UI.createElement(document, "div", { + ...props + }) + addSentences(rawDiv, rawText, "[\\.;]") + const resultDiv = ztoolkit.UI.createElement(document, "div", { + ...props, + }) + addSentences(resultDiv, resultText, "[。;;]") + container.append(rawDiv, resultDiv) + } + }]) // Prompt // 旧版数据迁移 let getItem = () => { @@ -2668,6 +2955,7 @@ export default class Views { } } ]) + return // 所有快捷键 let commands: Command[] = [] let getLable = (keyOptions: any) => { @@ -2733,174 +3021,7 @@ export default class Views { } // ztoolkit.Prompt.register(commands) - // 注册搜索文库 - ztoolkit.Prompt.register([{ - id: "search", - callback: async (prompt) => { - // https://github.com/zotero/zotero/blob/7262465109c21919b56a7ab214f7c7a8e1e63909/chrome/content/zotero/integration/quickFormat.js#L589 - function getItemDescription(item: Zotero.Item) { - var nodes = []; - var str = ""; - var author, authorDate = ""; - if (item.firstCreator) { author = authorDate = item.firstCreator; } - var date = item.getField("date", true, true) as string; - if (date && (date = date.substr(0, 4)) !== "0000") { - authorDate += " (" + parseInt(date) + ")"; - } - authorDate = authorDate.trim(); - if (authorDate) nodes.push(authorDate); - - var publicationTitle = item.getField("publicationTitle", false, true); - if (publicationTitle) { - nodes.push(`${publicationTitle}`); - } - var volumeIssue = item.getField("volume"); - var issue = item.getField("issue"); - if (issue) volumeIssue += "(" + issue + ")"; - if (volumeIssue) nodes.push(volumeIssue); - - var publisherPlace = [], field; - if ((field = item.getField("publisher"))) publisherPlace.push(field); - if ((field = item.getField("place"))) publisherPlace.push(field); - if (publisherPlace.length) nodes.push(publisherPlace.join(": ")); - - var pages = item.getField("pages"); - if (pages) nodes.push(pages); - - if (!nodes.length) { - var url = item.getField("url"); - if (url) nodes.push(url); - } - - // compile everything together - for (var i = 0, n = nodes.length; i < n; i++) { - var node = nodes[i]; - - if (i != 0) str += ", "; - - if (typeof node === "object") { - var label = document.createElement("label"); - label.setAttribute("value", str); - label.setAttribute("crop", "end"); - str = ""; - } else { - str += node; - } - } - str.length && (str += ".") - return str - }; - function filter(ids: number[]) { - ids = ids.filter(async (id) => { - const item = await Zotero.Items.getAsync(id) - return item.isRegularItem() && !item.isFeedItem - }) - return ids - } - const text = prompt.inputNode.value; - prompt.showTip("Searching...") - const s = new Zotero.Search(); - s.addCondition("quicksearch-titleCreatorYear", "contains", text); - s.addCondition("itemType", "isNot", "attachment"); - let ids = await s.search(); - // prompt.exit will remove current container element. - // @ts-ignore - prompt.exit(); - const container = prompt.createCommandsContainer(); - container.classList.add("suggestions"); - ids = filter(ids) - console.log(ids.length) - if (ids.length == 0) { - const s = new Zotero.Search(); - const operators = ['is', 'isNot', 'true', 'false', 'isInTheLast', 'isBefore', 'isAfter', 'contains', 'doesNotContain', 'beginsWith']; - let hasValidCondition = false - let joinMode: string = "all" - if (/\s*\|\|\s*/.test(text)) { - joinMode = "any" - } - text.split(/\s*(&&|\|\|)\s*/g).forEach((conditinString: string) => { - let conditions = conditinString.split(/\s+/g); - if (conditions.length == 3 && operators.indexOf(conditions[1]) != -1) { - hasValidCondition = true - s.addCondition("joinMode", joinMode); - s.addCondition( - conditions[0] as string, - conditions[1] as Zotero.Search.Operator, - conditions[2] as string - ); - } - }) - if (hasValidCondition) { - ids = await s.search(); - } - } - ids = filter(ids) - console.log(ids.length) - if (ids.length > 0) { - ids.forEach((id: number) => { - const item = Zotero.Items.get(id) - const title = item.getField("title") - const ele = ztoolkit.UI.createElement(document, "div", { - namespace: "html", - classList: ["command"], - listeners: [ - { - type: "mousemove", - listener: function () { - // @ts-ignore - prompt.selectItem(this) - } - }, - { - type: "click", - listener: () => { - prompt.promptNode.style.display = "none" - Zotero_Tabs.select('zotero-pane'); - ZoteroPane.selectItem(item.id); - } - } - ], - styles: { - display: "flex", - flexDirection: "column", - justifyContent: "start", - }, - children: [ - { - tag: "span", - styles: { - fontWeight: "bold", - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap" - }, - properties: { - innerText: title - } - }, - { - tag: "span", - styles: { - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap" - }, - properties: { - innerHTML: getItemDescription(item) - } - } - ] - }) - container.appendChild(ele) - }) - } else { - // @ts-ignore - prompt.exit() - prompt.showTip("Not Found.") - } - } - }]) } /** diff --git a/update.json b/update.json index dee6496..8b5019b 100644 --- a/update.json +++ b/update.json @@ -3,7 +3,7 @@ "zoterostyle@polygon.org": { "updates": [ { - "version": "2.2.0", + "version": "2.2.1", "update_link": "https://github.com/muisedestiny/zotero-style/releases/latest/download/zotero-style.xpi", "applications": { "gecko": { @@ -12,7 +12,7 @@ } }, { - "version": "2.2.0", + "version": "2.2.1", "update_link": "https://github.com/muisedestiny/zotero-style/releases/latest/download/zotero-style.xpi", "applications": { "zotero": { diff --git a/update.rdf b/update.rdf index 5114afd..7b97903 100644 --- a/update.rdf +++ b/update.rdf @@ -5,7 +5,7 @@ - 2.2.0 + 2.2.1 zotero@chnm.gmu.edu