From d071a2672fc953ebd5982d3aace84d018eef4c1e Mon Sep 17 00:00:00 2001 From: Artur Date: Mon, 13 Oct 2025 19:26:13 +0300 Subject: [PATCH] citation doc service --- sdkjs-plugins/content/zotero/index.html | 2 + .../zotero/scripts/citation-doc-service.js | 155 ++++++++++++++++++ sdkjs-plugins/content/zotero/scripts/code.js | 111 +++++-------- .../zotero/scripts/csl/citation/citation.js | 10 ++ .../zotero/scripts/csl/citation/storage.js | 3 + .../zotero/scripts/csl/styles/storage.js | 2 - .../scripts/csl/styles/styles-manager.js | 3 +- 7 files changed, 211 insertions(+), 75 deletions(-) create mode 100644 sdkjs-plugins/content/zotero/scripts/citation-doc-service.js diff --git a/sdkjs-plugins/content/zotero/index.html b/sdkjs-plugins/content/zotero/index.html index 6f2156b4..9d77e0a6 100644 --- a/sdkjs-plugins/content/zotero/index.html +++ b/sdkjs-plugins/content/zotero/index.html @@ -46,6 +46,8 @@ + + diff --git a/sdkjs-plugins/content/zotero/scripts/citation-doc-service.js b/sdkjs-plugins/content/zotero/scripts/citation-doc-service.js new file mode 100644 index 00000000..cf4a8578 --- /dev/null +++ b/sdkjs-plugins/content/zotero/scripts/citation-doc-service.js @@ -0,0 +1,155 @@ +// @ts-check + +/** + * @typedef {Object} CustomField + * @property {string} Value + * @property {string} Content + * @property {string} [FieldId] + */ + +/** + * @param {string} citPrefix + * @param {string} citSuffix + * @param {string} bibPrefix + * @param {string} bibSuffix + * @param {StyleFormat} styleFormat + */ +function CitationDocService( + citPrefix, + citSuffix, + bibPrefix, + bibSuffix, + styleFormat +) { + this._citPrefixOld = "ZOTERO_CITATION"; + this._bibPrefixOld = "ZOTERO_BIBLIOGRAPHY"; + + this._citPrefix = citPrefix; + this._citSuffix = citSuffix; + this._bibPrefix = bibPrefix; + this._bibSuffix = bibSuffix; + this._styleFormat = styleFormat; + + /** @type {number} */ + this._repeatTimeout; + /** + * @type {{ updateItems: (arg0: string[]) => void; makeCitationCluster: (arg0: SuppressAuthor[]) => string; makeBibliography: () => any[][]; } | null} + */ + this._formatter = null; +} + +/** + * @param {string} text + * @param {string} value + */ +CitationDocService.prototype.addBibliography = function (text, value) { + /** @type {CustomField} */ + const field = { + Value: this._bibPrefix + value + this._bibSuffix, + Content: text, + }; + return new Promise(function (resolve) { + window.Asc.plugin.executeMethod("AddAddinField", [field], resolve); + }); +}; + +/** + * @param {string} text + * @param {string} value + * @returns + */ +CitationDocService.prototype.addCitation = function (text, value) { + /** @type {CustomField} */ + const field = { + Value: this._citPrefix + " " + this._citSuffix + value, + Content: text, + }; + if (["note", "note-ibid"].indexOf(this._styleFormat) !== -1) { + window.Asc.plugin.callCommand(function () { + const oDocument = Api.GetDocument(); + oDocument.AddFootnote(); + }); + } + return new Promise(function (resolve, reject) { + window.Asc.plugin.executeMethod("AddAddinField", [field], resolve); + }); +}; + +/** + * @returns {Promise>} + */ +CitationDocService.prototype.getAllAddinFields = function () { + const self = this; + return new Promise(function (resolve, reject) { + window.Asc.plugin.executeMethod("GetAllAddinFields", null, resolve); + }); +}; + +/** + * @returns {Promise>} + */ +CitationDocService.prototype.getAddinZoteroFields = function () { + const self = this; + return new Promise(function (resolve, reject) { + self.getAllAddinFields().then(function (arrFields) { + try { + if (arrFields.length) { + arrFields = arrFields.filter(function (field) { + return ( + field.Value.indexOf(self._citPrefix) !== -1 || + field.Value.indexOf(self._bibPrefix) !== -1 || + field.Value.indexOf(self._citPrefixOld) !== -1 || + field.Value.indexOf(self._bibPrefixOld) !== -1 + ); + }); + } + } catch (e) { + reject(e); + } + resolve(arrFields); + }); + }); +}; + +/** + * @returns {Promise} + */ +CitationDocService.prototype.saveAsText = function () { + // TODO потом добавить ещё форматы, пока только как текст + return this.getAddinZoteroFields().then(function (arrFields) { + let count = arrFields.length; + if (!count) { + window.Asc.plugin.executeCommand("close", ""); + return false; + } + + return new Promise(function (resolve) { + arrFields.forEach(function (field) { + window.Asc.plugin.executeMethod( + "RemoveFieldWrapper", + [field.FieldId], + function () { + count--; + if (!count) { + resolve(true); + window.Asc.plugin.executeCommand("close", ""); + } + } + ); + }); + }); + }); +}; + +/** + * @param {StyleFormat} styleFormat + */ +CitationDocService.prototype.setStyleFormat = function (styleFormat) { + this._styleFormat = styleFormat; +}; + +CitationDocService.prototype.updateAddinFields = function (fields) { + return new Promise(function (resolve, reject) { + window.Asc.plugin.executeMethod("UpdateAddinFields", [fields], resolve); + }); +}; diff --git a/sdkjs-plugins/content/zotero/scripts/code.js b/sdkjs-plugins/content/zotero/scripts/code.js index 2d064c05..79289891 100644 --- a/sdkjs-plugins/content/zotero/scripts/code.js +++ b/sdkjs-plugins/content/zotero/scripts/code.js @@ -66,6 +66,7 @@ var sdk = null; var cslStylesManager = null; + var citationDocService = null; var lastSearch = { text: "", @@ -121,7 +122,7 @@ window.Asc.plugin.init = function () { showLoader(true); setTimeout(function () { searchField.focus(); },100); - updateCslItems(true, false, false, false); + sdk = window.Asc.plugin.zotero.api({}); initSdkApis().then(function (availableApis) { @@ -130,6 +131,15 @@ if (availableApis.online || availableApis.desktop) { loadStyles(); } + citationDocService = new CitationDocService( + citPrefixNew, + citSuffixNew, + bibPrefixNew, + bibSuffixNew, + cslStylesManager.getLastUsedFormat() + ); + + updateCslItems(true, false, false, false); }); window.Asc.plugin.onTranslate = applyTranslations; @@ -143,14 +153,6 @@ elements.styleSelectList.onopen = function () { elements.styleSelectList.style.width = (elements.styleWrapper.clientWidth - 2) + "px"; } - - Asc.scope.text = "note"; - /*window.Asc.plugin.callCommand(function () { - var oDocument = Api.GetDocument(); - oDocument.AddFootnote(); - let footnotesFirstParagraphs = oDocument.GetFootnotesFirstParagraphs(); - footnotesFirstParagraphs[0].AddText(Asc.scope.text); - }, true);*/ }; window.Asc.plugin.onThemeChanged = function(theme) @@ -384,7 +386,7 @@ elements.saveAsTextBtn.onclick = function() { showLoader(true); - saveAs(); + citationDocService.saveAsText(); } elements.styleSelect.oninput = function (e, filter) { var input = elements.styleSelect; @@ -405,7 +407,9 @@ showLoader(true); getStyle(val) .then(function(style) { - bNumFormat = (style.indexOf('citation-format="numeric"') !== -1); + let styleFormat = cslStylesManager.getLastUsedFormat(); + citationDocService.setStyleFormat(styleFormat); + bNumFormat = styleFormat == 'numeric'; if (isClick) updateCslItems(true, true, false, false); }) @@ -965,7 +969,7 @@ showError(getMessage("Language is not selected")); return; } - window.Asc.plugin.executeMethod("GetAllAddinFields", null, function(arrFields) { + citationDocService.getAllAddinFields().then(function(arrFields) { if (!arrFields.length) { showLoader(false); return; @@ -1012,28 +1016,25 @@ updatedFields.push(field); } else if (field.Value.indexOf(bibPrefix) !== -1 || field.Value.indexOf(bibPrefixNew) !== -1) { bibField = field; + bibField["Content"] = bibliography; if (typeof citationObject === "object" && Object.keys(citationObject).length > 0) { bibFieldValue = JSON.stringify(citationObject); } } }); if (bibField) { - bibField["Content"] = bibliography; updatedFields.push(bibField); } else if (bPastBib) { - bibField = { - "Value" : bibPrefixNew + bibFieldValue + bibSuffixNew, - "Content" : bibliography - }; - window.Asc.plugin.executeMethod("AddAddinField", [bibField], function() { - if (!updatedFields.length) { - showLoader(false); - } - }); + citationDocService.addBibliography(bibliography, bibFieldValue) + .then(function() { + if (!updatedFields.length) { + showLoader(false); + } + }); } if (updatedFields.length) { - window.Asc.plugin.executeMethod("UpdateAddinFields", [updatedFields], function() { + citationDocService.updateAddinFields(updatedFields).then(function() { showLoader(false); }); } @@ -1083,7 +1084,8 @@ // Refresh (1,1,0,0) function updateCslItems(bUpdadeFormatter, bUpadteAll, bPastBib, bPastLink) { CSLCitationStorage.clear(); - window.Asc.plugin.executeMethod("GetAllAddinFields", null, function(arrFields) { + + return citationDocService.getAllAddinFields().then(function(arrFields) { if (arrFields.length) { var numOfItems = 0; var bibField = null; @@ -1118,18 +1120,18 @@ // нет смысла ещё раз искать поле библиографии bUpdadeFormatter = false; bibField["Content"] = getMessage(bibPlaceholder); - window.Asc.plugin.executeMethod("UpdateAddinFields", [[bibField]], function() { - showLoader(false); - }); + citationDocService.updateAddinFields([bibField]).then(function() { + showLoader(false); + }); } } else if (bUpdadeFormatter && bPastBib) { - bibField = { - "Value" : bibPrefixNew + bibFieldValue + bibSuffixNew, - "Content" : getMessage(bibPlaceholder) - }; - window.Asc.plugin.executeMethod("AddAddinField", [bibField], function() { - showLoader(false); - }); + + citationDocService.addBibliography( + getMessage(bibPlaceholder), + bibFieldValue + ).then(function() { + showLoader(false); + }); } if (bUpdadeFormatter) updateFormatter(bUpadteAll, bPastBib, bPastLink, false); @@ -1176,18 +1178,10 @@ // TODO может ещё очистить поиск (подумать над этим) elements.tempDiv.innerHTML = formatter.makeCitationCluster(keysL[0]); - - var field = { - "Value" : citPrefixNew + ' ' + citSuffixNew + JSON.stringify(obj.toJSON()), - "Content" : elements.tempDiv.innerText - }; - if (['note', 'note-ibid'].indexOf(cslStylesManager.getLastUsedFormat()) !== -1) { - window.Asc.plugin.callCommand(function () { - var oDocument = Api.GetDocument(); - oDocument.AddFootnote(); - }); - } - window.Asc.plugin.executeMethod("AddAddinField", [field], function() { + citationDocService.addCitation( + elements.tempDiv.innerHTML, + JSON.stringify(obj.toJSON()) + ).then(function() { showLoader(false); // TODO есть проблема, что в плагине мы индексы обновили, а вот в документе нет (по идее надо обновить и индексы в документе перед вставкой) // но тогда у нас уедет селект и новое поле вставится не там, поэтому пока обновлять приходится в конце @@ -1221,31 +1215,6 @@ }; - function saveAs() { - // TODO потом добавить ещё форматы, пока только как текст - window.Asc.plugin.executeMethod("GetAllAddinFields", null, function(arrFields) { - let count = 0; - arrFields.forEach(function(field) { - if ( - ( field.Value.indexOf(bibPrefix) !== -1 ) || - ( field.Value.indexOf(citPrefix) !== -1 ) || - ( field.Value.indexOf(bibPrefixNew) !== -1 ) || - ( field.Value.indexOf(citPrefixNew) !== -1 ) - ) { - count++; - window.Asc.plugin.executeMethod("RemoveFieldWrapper", [field.FieldId], function() { - count--; - if (!count) - window.Asc.plugin.executeCommand("close", ""); - }); - } - }); - - if (!arrFields.length) - window.Asc.plugin.executeCommand("close", ""); - }); - }; - function synchronizeData() { // form an array for request (one array for user and other for groups) // todo now we should make full update (because when we make refresh, we check fields into the document). Fix it in new version (when we change refreshing and updating processes) diff --git a/sdkjs-plugins/content/zotero/scripts/csl/citation/citation.js b/sdkjs-plugins/content/zotero/scripts/csl/citation/citation.js index 4882891d..3483c4a5 100644 --- a/sdkjs-plugins/content/zotero/scripts/csl/citation/citation.js +++ b/sdkjs-plugins/content/zotero/scripts/csl/citation/citation.js @@ -1,3 +1,9 @@ +/** + * @typedef {Object} SuppressAuthor + * @property {string} id + * @property {boolean} "suppress-author" + */ + /** * @param {string} citationID * @param {number} [itemsStartIndex] @@ -16,6 +22,10 @@ function CSLCitation(citationID, itemsStartIndex) { "https://raw.githubusercontent.com/citation-style-language/schema/master/schemas/input/csl-citation.json"; } +/** + * + * @returns {Array} + */ CSLCitation.prototype.getSuppressAuthors = function () { return this._citationItems.map(function (item) { return { diff --git a/sdkjs-plugins/content/zotero/scripts/csl/citation/storage.js b/sdkjs-plugins/content/zotero/scripts/csl/citation/storage.js index b8be09f7..7c0f6413 100644 --- a/sdkjs-plugins/content/zotero/scripts/csl/citation/storage.js +++ b/sdkjs-plugins/content/zotero/scripts/csl/citation/storage.js @@ -1,7 +1,10 @@ var CSLCitationStorage = { + /** @type {Array} */ _items: [], + /** @type {Array} */ _ids: [], size: 0, + /** @returns {CSLCitation} */ get: function (id) { var id = this._ids.indexOf(id); if (id >= 0) return this._items[id]; diff --git a/sdkjs-plugins/content/zotero/scripts/csl/styles/storage.js b/sdkjs-plugins/content/zotero/scripts/csl/styles/storage.js index ea08f3aa..6f1d65c0 100644 --- a/sdkjs-plugins/content/zotero/scripts/csl/styles/storage.js +++ b/sdkjs-plugins/content/zotero/scripts/csl/styles/storage.js @@ -1,8 +1,6 @@ // @ts-check function CslStylesStorage() { - this._dbName = "ZoteroStylesDB"; - this._storeName = "cslFiles"; this._customStyleNamesKey = "zoteroCustomStyleNames"; this._customStylesKey = "zoteroCustomStyles"; } diff --git a/sdkjs-plugins/content/zotero/scripts/csl/styles/styles-manager.js b/sdkjs-plugins/content/zotero/scripts/csl/styles/styles-manager.js index e3870c7b..5191e942 100644 --- a/sdkjs-plugins/content/zotero/scripts/csl/styles/styles-manager.js +++ b/sdkjs-plugins/content/zotero/scripts/csl/styles/styles-manager.js @@ -107,8 +107,7 @@ CslStylesManager.prototype.getStyle = function (styleName) { } const customStyleNames = self._customStylesStorage.getStyleNames(); if (customStyleNames.indexOf(styleName) !== -1) { - return self._customStylesStorage - .getStyle(styleName); + return self._customStylesStorage.getStyle(styleName); } let url = self._STYLES_LOCAL + styleName + ".csl"; if (self._isOnlineAvailable) {