diff --git a/sdkjs-plugins/content/zotero/index.html b/sdkjs-plugins/content/zotero/index.html
index cac79464..1083efbf 100644
--- a/sdkjs-plugins/content/zotero/index.html
+++ b/sdkjs-plugins/content/zotero/index.html
@@ -58,7 +58,8 @@
-
+
+
@@ -134,12 +135,12 @@
0 selected
- Cancel selection
+ Cancel selection
-
+
Configure how citations will be formatted in your document.
@@ -170,69 +171,10 @@
-
-
-
-
-
- Afrikaans
- Arabic
- Bulgarian
- Catalan
- Czech
- Welsh
- Danish
- German (Austria)
- German (Switzerland)
- German (Germany)
- Greek
- English (UK)
- English (US)
- Spanish (Chile)
- Spanish (Spain)
- Spanish (Mexico)
- Estonian
- Basque
- Persian
- Finnish
- French (Canada)
- French (France)
- Hebrew
- Croatian
- Hungarian
- Indonesian
- Icelandic
- Italian
- Japanese
- Khmer
- Korean
- Latin
- Lithuanian
- Latvian
- Mongolian
- Norwegian (Bokmål)
- Dutch
- Norwegian (Nynorsk)
- Polish
- Portuguese (Brazil)
- Portuguese (Portugal)
- Romanian
- Russian
- Slovak
- Slovenian
- Serbian
- Swedish
- Thai
- Turkish
- Ukrainian
- Vietnamese
- Chinese (PRC)
- Chinese (Taiwan)
-
-
-
+
+
Display citations as:
@@ -243,6 +185,8 @@
+
+
diff --git a/sdkjs-plugins/content/zotero/resources/css/components.css b/sdkjs-plugins/content/zotero/resources/css/components.css
index f2484c5f..0793545f 100644
--- a/sdkjs-plugins/content/zotero/resources/css/components.css
+++ b/sdkjs-plugins/content/zotero/resources/css/components.css
@@ -59,6 +59,7 @@ html {
.custom-button {
position: relative;
display: inline-flex;
+ width: 100%;
align-items: center;
justify-content: center;
gap: 8px;
@@ -419,6 +420,7 @@ body.theme-dark .custom-button-container {
font-size: 22px;
line-height: 0;
transition: all 0.2s ease;
+ outline: none;
}
/* Counter */
diff --git a/sdkjs-plugins/content/zotero/resources/css/plugin_style.css b/sdkjs-plugins/content/zotero/resources/css/plugin_style.css
index 519b4ba6..ed7f2520 100644
--- a/sdkjs-plugins/content/zotero/resources/css/plugin_style.css
+++ b/sdkjs-plugins/content/zotero/resources/css/plugin_style.css
@@ -136,22 +136,17 @@ input[type="text"] {
#searchWrapper {
display: flex;
+ gap: 4px;
justify-content: space-between;
position: relative;
margin-top: 13px;
}
- #searchWrapper .input-field-container-searchField {
- width: 207px;
- }
#searchWrapper .selectbox-container {
position: absolute;
}
#searchWrapper .selectbox-container .selectbox-header {
opacity: 0;
}
-#searchField {
- width: 207px;
-}
#controlsHolder {
padding-top: 4px;
@@ -160,8 +155,8 @@ input[type="text"] {
#controlsHolder button {
margin-top: 8px;
}
- #controlsHolder .custom-button-primary {
- width: 207px;
+ #controlsHolder .custom-button-container:first-child {
+ width: calc(100% - 28px);
}
#controlsHolder .custom-button-secondary {
width: 100%;
@@ -256,11 +251,6 @@ input[type="text"] {
transform: rotateZ(-45deg);
}
-
-#styleLangList {
- right: 0px;
-}
-
.selectList {
display: flex;
flex-direction: column;
@@ -342,7 +332,7 @@ input[type="text"] {
box-sizing: border-box;
margin-top: 6px;
padding: 4px 6px 6px 6px;
- height: 51px;
+ max-height: 51px;
overflow: hidden;
}
.page0 > .doc:first-child {
@@ -390,8 +380,7 @@ input[type="text"] {
}
.doc.doc-open {
- height: auto;
- overflow: visible;
+ max-height: none;
}
.doc-open .selectbox-arrow {
transform: rotate(180deg);
diff --git a/sdkjs-plugins/content/zotero/scripts/code.js b/sdkjs-plugins/content/zotero/scripts/code.js
index c93287b9..413aecc1 100644
--- a/sdkjs-plugins/content/zotero/scripts/code.js
+++ b/sdkjs-plugins/content/zotero/scripts/code.js
@@ -23,26 +23,19 @@
///
///
///
-///
-///
///
///
///
///
-///
-///
///
-///
+///
+///
(function () {
var counter = 0; // счетчик отправленных запросов (используется чтобы знать показывать "not found" или нет)
var displayNoneClass = "hidden";
var blurClass = "blur";
- //var loadingStyle = false;
- //var loadingLocale = false;
- var bNumFormat = false;
-
// TODO добавить ещё обработку событий (удаление линков) их не нужно удалять
// из библиографии автоматически (это делать только при обновлении библиографии
// или refresh), но их точно нужно удалить из formatter!
@@ -52,7 +45,7 @@
// TODO сейчас всегда делаем полный refresh при каждом действии
// (обновлении, вставке линков, вставке библиографии), потому что мы не знаем
// что поменялось без событий (потом добавить ещё сравнение контента)
- // TODO ms меняет линки (если стиль с нумерацией bNumFormat) делает их по порядку
+ // TODO ms меняет линки (если стиль с нумерацией settings._bNumFormat) делает их по порядку
// как документе (для этого нужно знать где именно в документе мы вставляем цитату,
// какая цитата сверху и снизу от текущего курсора)
@@ -60,12 +53,10 @@
var router;
/** @type {ZoteroSdk} */
var sdk;
- /** @type {ConnectingToApi} */
- var connectingToApi;
- /** @type {CslStylesManager} */
- var cslStylesManager;
- /** @type {LocalesManager} */
- var localesManager;
+
+ /** @type {SettingsPage} */
+ var settings;
+
/** @type {CitationService} */
var citationService;
@@ -86,8 +77,6 @@
/** @type {Button} */
var insertLinkBtn;
/** @type {Button} */
- var settingsBtn;
- /** @type {Button} */
var insertBibBtn;
/** @type {Button} */
var refreshBtn;
@@ -111,62 +100,16 @@
throw new Error("contentHolder not found");
}
- const loginState = document.getElementById("loginState");
- if (!loginState) {
- throw new Error("loginState not found");
- }
const mainState = document.getElementById("mainState");
if (!mainState) {
throw new Error("mainState not found");
}
- const styleWrapper = document.getElementById("styleWrapper");
- if (!styleWrapper) {
- throw new Error("styleWrapper not found");
- }
- const styleSelectList = document.getElementById("styleSelectList");
- if (!styleSelectList) {
- throw new Error("styleSelectList not found");
- }
- const styleSelectListOther = document.getElementById(
- "styleSelectedListOther"
- );
- if (!styleSelectListOther) {
- throw new Error("styleSelectListOther not found");
- }
- const styleSelect = document.getElementById("styleSelect");
- if (!styleSelect) {
- throw new Error("styleSelect not found");
- }
- const styleLang = document.getElementById("styleLang");
- if (!styleLang) {
- throw new Error("styleLang not found");
- }
- const styleLangList = document.getElementById("styleLangList");
- if (!styleLangList) {
- throw new Error("styleLangList not found");
- }
- const notesStyleWrapper = document.getElementById("notesStyle");
- if (!notesStyleWrapper) {
- throw new Error("notesStyleWrapper not found");
- }
- const footNotes = document.getElementById("footNotes");
- if (!footNotes) {
- throw new Error("footNotes not found");
- }
- const endNotes = document.getElementById("endNotes");
- if (!endNotes) {
- throw new Error("endNotes not found");
- }
const checkOmitAuthor = document.getElementById("omitAuthor");
if (!checkOmitAuthor) {
throw new Error("checkOmitAuthor not found");
}
- const cslFileInput = document.getElementById("cslFileInput");
- if (!cslFileInput) {
- throw new Error("cslFileInput not found");
- }
searchFilter = new SearchFilterComponents();
selectCitation = new SelectCitationsComponent(
displayNoneClass,
@@ -179,10 +122,6 @@
insertLinkBtn = new Button("insertLinkBtn", {
disabled: true,
});
- settingsBtn = new Button("settingsBtn", {
- variant: "icon-only",
- size: "small",
- });
insertBibBtn = new Button("insertBibBtn", {
variant: "secondary",
});
@@ -195,22 +134,10 @@
error: error,
contentHolder: contentHolder,
- loginState: loginState,
mainState: mainState,
- styleWrapper: styleWrapper,
- styleSelectList: styleSelectList,
- styleSelectListOther: styleSelectListOther,
- styleSelect: styleSelect,
- styleLang: styleLang,
- styleLangList: styleLangList,
- notesStyleWrapper: notesStyleWrapper,
- footNotes: footNotes,
- endNotes: endNotes,
-
checkOmitAuthor: checkOmitAuthor,
- cslFileInput: cslFileInput,
};
}
@@ -220,116 +147,36 @@
router = new Router();
sdk = new ZoteroSdk();
- connectingToApi = new ConnectingToApi(router, sdk);
- cslStylesManager = new CslStylesManager();
- localesManager = new LocalesManager();
+ const loginPage = new LoginPage(router, sdk);
+ settings = new SettingsPage(router, displayNoneClass);
citationService = new CitationService(
- localesManager,
- cslStylesManager,
+ settings.getLocalesManager(),
+ settings.getStyleManager(),
sdk
);
addEventListeners();
- initSelectBoxes();
- const connector = connectingToApi.init();
- connector.onOpen(function () {
- showLoader(false);
- });
- connector.onChangeState(function (apis) {
- cslStylesManager.setDesktopApiAvailable(apis.desktop);
- cslStylesManager.setRestApiAvailable(apis.online);
- localesManager.setDesktopApiAvailable(apis.desktop);
- localesManager.setRestApiAvailable(apis.online);
- });
- connector.onAuthorized(function (apis) {
- showLoader(true);
-
- Promise.all([
- loadGroups(),
- loadStyles(),
- preloadLastStyle(),
- initLanguageSelect(),
- ]).then(function () {
+ loginPage
+ .init()
+ .onOpen(function () {
showLoader(false);
- });
+ })
+ .onChangeState(function (apis) {
+ settings.setDesktopApiAvailable(apis.desktop);
+ settings.setRestApiAvailable(apis.online);
+ })
+ .onAuthorized(function (apis) {
+ showLoader(true);
- addStylesEventListeners();
- });
+ Promise.all([loadGroups(), settings.init()]).then(function () {
+ showLoader(false);
+ });
+ });
window.Asc.plugin.onTranslate = applyTranslations;
};
- function preloadLastStyle() {
- var lastStyle = cslStylesManager.getLastUsedStyleId() || "ieee";
- return cslStylesManager.getStyle(lastStyle);
- }
-
- /**
- * @returns {Promise}
- */
- function initLanguageSelect() {
- const savedLang = localesManager.getLastUsedLanguage();
- const option = elements.styleLangList.querySelector(
- '[data-value="' + savedLang + '"]'
- );
- if (!option || !(elements.styleLang instanceof HTMLInputElement)) {
- console.error("initLanguageSelect: no option");
- return Promise.resolve(null);
- }
- option.setAttribute("selected", "");
- elements.styleLang.value = option.textContent;
- elements.styleLang.setAttribute("data-value", savedLang);
- return localesManager.loadLocale(savedLang);
- }
-
- /** @returns {Promise} */
- function loadStyles() {
- return cslStylesManager
- .getStylesInfo()
- .then(
- /** @param {Array} stylesInfo*/ function (
- stylesInfo
- ) {
- var openOtherStyleList = function (
- /** @type {HTMLElement} */ list
- ) {
- return function (/** @type {MouseEvent} */ ev) {
- elements.styleSelectListOther.style.width =
- elements.styleWrapper.clientWidth - 2 + "px";
- ev.stopPropagation();
- openList(list);
- };
- };
-
- addStylesToList(stylesInfo);
-
- const el = document.createElement("hr");
- elements.styleSelectList.appendChild(el);
-
- if (elements.styleSelectListOther.children.length > 0) {
- var other = document.createElement("span");
- other.textContent = "More Styles...";
- elements.styleSelectList.appendChild(other);
- other.onclick = openOtherStyleList(
- elements.styleSelectListOther
- );
- }
-
- var custom = document.createElement("span");
- custom.setAttribute("class", "select-file");
- var label = document.createElement("label");
- label.setAttribute("for", "cslFileInput");
- label.textContent = "Add custom style...";
- custom.appendChild(label);
- elements.styleSelectList.appendChild(custom);
- }
- )
- .catch(function (err) {
- console.error(err);
- });
- }
-
/** @returns {Promise} */
function loadGroups() {
return sdk
@@ -339,121 +186,9 @@
});
}
- /**
- * @param {Array} stylesInfo
- */
- function addStylesToList(stylesInfo) {
- var lastStyle = cslStylesManager.getLastUsedStyleIdOrDefault();
- const styleSelect = elements.styleSelect;
- if (styleSelect instanceof HTMLInputElement === false) {
- console.error("styleSelect is not an input element");
- return;
- }
-
- /**
- * @param {HTMLElement} list - the list of styles where the element is added.
- * @param {HTMLElement} other - the list of styles where the element is removed.
- */
- var onStyleSelectOther = function (list, other) {
- return function (/** @type {MouseEvent} */ ev) {
- let tmpEl = list.removeChild(
- list.children[list.children.length - 3]
- );
- var newEl = document.createElement("span");
- newEl.setAttribute(
- "data-value",
- String(tmpEl.getAttribute("data-value"))
- );
- newEl.textContent = tmpEl.textContent;
- other.appendChild(newEl);
- newEl.onclick = onStyleSelectOther(
- elements.styleSelectList,
- elements.styleSelectListOther
- );
-
- if (ev.target instanceof HTMLElement === false) {
- console.error("ev.target is not an HTMLElement");
- return;
- }
- tmpEl = other.removeChild(ev.target);
- newEl = document.createElement("span");
- newEl.setAttribute(
- "data-value",
- String(tmpEl.getAttribute("data-value"))
- );
- newEl.textContent = tmpEl.textContent;
- list.insertBefore(newEl, list.firstElementChild);
- newEl.onclick = onClickListElement(
- elements.styleSelectList,
- styleSelect
- );
- var event = new Event("click");
- newEl.dispatchEvent(event);
- closeList();
- };
- };
-
- for (var i = 0; i < stylesInfo.length; i++) {
- var el = document.createElement("span");
- el.setAttribute("data-value", stylesInfo[i].name);
- el.textContent = stylesInfo[i].title;
- if (
- cslStylesManager.isStyleDefault(stylesInfo[i].name) ||
- stylesInfo[i].name == lastStyle
- ) {
- if (stylesInfo.length == 1)
- elements.styleSelectList.insertBefore(
- el,
- elements.styleSelectList.firstElementChild
- );
- else elements.styleSelectList.appendChild(el);
- el.onclick = onClickListElement(
- elements.styleSelectList,
- styleSelect
- );
- } else {
- elements.styleSelectListOther.appendChild(el);
- el.onclick = onStyleSelectOther(
- elements.styleSelectList,
- elements.styleSelectListOther
- );
- }
- if (stylesInfo[i].name == lastStyle) {
- el.setAttribute("selected", "");
- selectInput(styleSelect, el, elements.styleSelectList, false);
- }
- }
- }
-
function addEventListeners() {
selectCitation.subscribe(checkSelected);
- elements.cslFileInput.onchange = function (e) {
- if (!(e.target instanceof HTMLInputElement)) return;
- /** @type {HTMLInputElement} */
- const target = e.target;
- if (!target.files) return;
- var file = target.files[0];
- if (!file) {
- console.error("No file selected");
- return;
- }
- //showLoader(true);
-
- cslStylesManager
- .addCustomStyle(file)
- .then(function (styleValue) {
- addStylesToList([styleValue]);
- })
- .catch(function (error) {
- console.error(error);
- showError(translate("Failed to upload file"));
- })
- .finally(function () {
- showLoader(false);
- });
- };
-
/**
* @param {string} text
* @param {Array} selectedGroups
@@ -538,11 +273,11 @@
if (event.type !== "button:click") {
return;
}
- if (!cslStylesManager.getLastUsedStyleId()) {
+ if (!settings.getLastUsedStyleId()) {
showError(translate("Style is not selected"));
return;
}
- if (!localesManager.getLocale()) {
+ if (!settings.getLocale()) {
showError(translate("Language is not selected"));
return;
}
@@ -566,11 +301,11 @@
if (event.type !== "button:click") {
return;
}
- if (!cslStylesManager.getLastUsedStyleId()) {
+ if (!settings.getLastUsedStyleId()) {
showError(translate("Style is not selected"));
return;
}
- if (!localesManager.getLocale()) {
+ if (!settings.getLocale()) {
showError(translate("Language is not selected"));
return;
}
@@ -596,11 +331,11 @@
if (event.type !== "button:click") {
return;
}
- if (!cslStylesManager.getLastUsedStyleId()) {
+ if (!settings.getLastUsedStyleId()) {
showError(translate("Style is not selected"));
return;
}
- if (!localesManager.getLocale()) {
+ if (!settings.getLocale()) {
showError(translate("Language is not selected"));
return;
}
@@ -643,91 +378,12 @@
showLoader(false);
});
});
- }
- function addStylesEventListeners() {
- /**
- * @param {Event|null} e - The input event.
- * @param {String} [filter] - The filter to apply on the style options.
- */
- elements.styleSelect.oninput = function (e, filter) {
- var input = elements.styleSelect;
- if (!(input instanceof HTMLInputElement)) return;
- filter = filter !== undefined ? filter : input.value.toLowerCase();
- var list = elements.styleSelectList.classList.contains(
- displayNoneClass
- )
- ? elements.styleSelectListOther
- : elements.styleSelectList;
-
- for (var i = 0; i < list.children.length; i++) {
- const child = list.children[i];
- if (child instanceof HTMLElement === false) {
- continue;
- }
- var text = child.textContent || child.innerText;
- var hide = true;
- if (!filter || text.toLowerCase().indexOf(filter) > -1) {
- hide = false;
- }
-
- switchClass(child, displayNoneClass, hide);
- }
- };
-
- /**
- * @param {Event} inp - The input event.
- * @param {String} styleName - The name of the selected style.
- * @param {Boolean} isClick - Whether the style was selected manually or not.
- */
- elements.styleSelect.onselectchange = function (
- inp,
- styleName,
- isClick
- ) {
- isClick && showLoader(true);
- elements.styleSelect.oninput(inp, "");
-
- return cslStylesManager
- .getStyle(styleName)
- .then(function (style) {
- onStyleChange();
- if (isClick) {
- return citationService.updateCslItems(
- true,
- true,
- false
- );
- }
- })
- .catch(function (err) {
- console.error(err);
- if (typeof err === "string") {
- showError(err);
- }
- })
- .finally(function () {
- isClick && showLoader(false);
- });
- };
-
- /**
- * @param {Event} inp - The select change event.
- * @param {String} val - The value of the selected language.
- * @param {Boolean} isClick - Whether the language was selected manually or not.
- */
- elements.styleLang.onselectchange = function (inp, val, isClick) {
+ settings.onChangeState(function (/** @type {Promise} */ promise) {
showLoader(true);
- localesManager.saveLastUsedLanguage(val);
- localesManager
- .loadLocale(val)
+ promise
.then(function () {
- if (isClick)
- return citationService.updateCslItems(
- true,
- true,
- false
- );
+ return citationService.updateCslItems(true, true, false);
})
.catch(function (error) {
console.error(error);
@@ -738,27 +394,6 @@
.finally(function () {
showLoader(false);
});
- };
-
- elements.styleSelectList.onopen = function () {
- elements.styleSelectList.style.width =
- elements.styleWrapper.clientWidth - 2 + "px";
- };
-
- [elements.footNotes, elements.endNotes].forEach(function (el) {
- el.addEventListener("change", function (event) {
- if (
- event.target instanceof HTMLInputElement &&
- event.target.checked
- ) {
- const value = event.target.value;
- if (value === "endnotes" || value === "footnotes") {
- cslStylesManager.saveLastUsedNotesStyle(value);
- } else {
- console.error("Unknown notes style: " + value);
- }
- }
- });
});
}
@@ -827,160 +462,6 @@
body.classList.add("theme-" + themeType);
};
- /** @type {HTMLElement[]} */
- var selectLists = [];
- function initSelectBoxes() {
- var select = document.getElementsByClassName("control select");
- for (var i = 0; i < select.length; i++) {
- var input = select[i];
- var holder = input.parentElement;
- if (!(input instanceof HTMLInputElement) || !holder) {
- console.error("initSelectBoxes: no input or holder");
- continue;
- }
-
- var arrow = document.createElement("span");
- arrow.classList.add("selectArrow");
- arrow.appendChild(document.createElement("span"));
- arrow.appendChild(document.createElement("span"));
- holder.appendChild(arrow);
-
- for (
- var k = 0;
- k < holder.getElementsByClassName("selectList").length;
- k++
- ) {
- var list = holder.getElementsByClassName("selectList")[k];
- if (list.children.length > 0) {
- for (var j = 0; j < list.children.length; j++) {
- const child = list.children[j];
- if (child instanceof HTMLElement === false) {
- continue;
- }
- child.onclick = onClickListElement(list, input);
- }
- // selectInput(input, list.children[0], list, false);
- }
-
- /**
- * @param {HTMLElement} list
- * @param {HTMLElement} input
- * @returns {function}
- */
- var fOpen = function (list, input) {
- return function (/** @type {MouseEvent} */ ev) {
- ev.stopPropagation();
- if (
- !elements.styleSelectListOther.classList.contains(
- displayNoneClass
- )
- )
- return true;
-
- if (list.onopen) {
- list.onopen();
- }
- if (!input.hasAttribute("readonly")) {
- input.select();
- }
- openList(list);
-
- return true;
- };
- };
-
- if (k !== 1) {
- input.onclick = fOpen(list, input);
- arrow.onclick = fOpen(list, input);
- }
- selectLists.push(list);
- }
- }
- }
-
- /**
- * @param {HTMLElement} el
- */
- function openList(el) {
- switchClass(el, displayNoneClass, false);
- window.addEventListener("click", closeList);
- }
-
- function closeList() {
- window.removeEventListener("click", closeList);
- for (var i = 0; i < selectLists.length; i++) {
- if (selectLists[i] === elements.styleSelectList)
- elements.styleSelect.oninput(null, "");
- switchClass(selectLists[i], displayNoneClass, true);
- }
- }
-
- /**
- * @param {HTMLInputElement} input
- * @param {HTMLElement} el
- * @param {HTMLElement} list
- * @param {boolean} isClick
- */
- function selectInput(input, el, list, isClick) {
- input.value = el.textContent;
- var val = el.getAttribute("data-value") || "";
- input.setAttribute("data-value", val);
- input.setAttribute("title", el.textContent);
- if (input.onselectchange) {
- input.onselectchange(input, val, isClick);
- }
- switchClass(list, displayNoneClass, true);
- }
-
- /**
- * @param {Element} list
- * @param {HTMLInputElement} input
- */
- function onClickListElement(list, input) {
- return function (/** @type {MouseEvent} */ ev) {
- if (!ev.target || !(ev.target instanceof HTMLElement)) {
- console.error("onClickListElement: no target");
- return;
- }
- var sel = ev.target.getAttribute("data-value");
- for (var i = 0; i < list.children.length; i++) {
- const temp = list.children[i];
- if (temp instanceof HTMLElement === false) continue;
- /** @type {HTMLElement} */
- const child = temp;
- if (list.children[i].getAttribute("data-value") == sel) {
- list.children[i].setAttribute("selected", "");
-
- selectInput(input, child, list, true);
- } else {
- if (list.children[i].hasAttribute("selected")) {
- list.children[i].attributes.removeNamedItem("selected");
- }
- }
- }
- };
- }
-
- function onStyleChange() {
- let styleFormat = cslStylesManager.getLastUsedFormat();
- citationService.setStyleFormat(styleFormat);
- bNumFormat = styleFormat == "numeric";
- if ("note" === styleFormat) {
- elements.notesStyleWrapper.classList.remove(displayNoneClass);
- } else {
- elements.notesStyleWrapper.classList.add(displayNoneClass);
- }
-
- let notesStyle = cslStylesManager.getLastUsedNotesStyle();
- citationService.setNotesStyle(notesStyle);
- const notesAs = elements.notesStyleWrapper.querySelector(
- 'input[name="notesAs"][value="' + notesStyle + '"]'
- );
- if (notesAs && notesAs instanceof HTMLInputElement) {
- notesAs.checked = true;
- }
- }
-
function applyTranslations() {
var elements = document.getElementsByClassName("i18n");
diff --git a/sdkjs-plugins/content/zotero/scripts/connection.js b/sdkjs-plugins/content/zotero/scripts/login.js
similarity index 93%
rename from sdkjs-plugins/content/zotero/scripts/connection.js
rename to sdkjs-plugins/content/zotero/scripts/login.js
index 611d3c4d..abe61440 100644
--- a/sdkjs-plugins/content/zotero/scripts/connection.js
+++ b/sdkjs-plugins/content/zotero/scripts/login.js
@@ -13,7 +13,7 @@
* @param {Router} router
* @param {ZoteroSdk} sdk
*/
-function ConnectingToApi(router, sdk) {
+function LoginPage(router, sdk) {
this._router = router;
this._sdk = sdk;
this._apiKeyLoginField = new InputField("apiKeyField", {
@@ -48,7 +48,7 @@ function ConnectingToApi(router, sdk) {
this._onOpen = function () {};
}
-ConnectingToApi.prototype.init = function () {
+LoginPage.prototype.init = function () {
const self = this;
this._addEventListeners();
let hasFirstAnswer = false;
@@ -90,29 +90,34 @@ ConnectingToApi.prototype.init = function () {
}
});
- return {
+ const triggers = {
/**
- * @param {function(AvailableApis): void} callbackFn
+ * @param {function(): void} callbackFn
*/
- onAuthorized: function (callbackFn) {
- self._onAuthorized = callbackFn;
+ onOpen: function (callbackFn) {
+ self._onOpen = callbackFn;
+ return triggers;
},
/**
* @param {function(AvailableApis): void} callbackFn
*/
onChangeState: function (callbackFn) {
self._onChangeState = callbackFn;
+ return triggers;
},
/**
- * @param {function(): void} callbackFn
+ * @param {function(AvailableApis): void} callbackFn
*/
- onOpen: function (callbackFn) {
- self._onOpen = callbackFn;
+ onAuthorized: function (callbackFn) {
+ self._onAuthorized = callbackFn;
+ return triggers;
},
};
+
+ return triggers;
};
-ConnectingToApi.prototype._addEventListeners = function () {
+LoginPage.prototype._addEventListeners = function () {
const self = this;
this._apiKeyLoginField.subscribe(function (event) {
if (event.type !== "inputfield:input") {
@@ -176,22 +181,19 @@ ConnectingToApi.prototype._addEventListeners = function () {
};
};
-ConnectingToApi.prototype._hideAllMessages = function () {
+LoginPage.prototype._hideAllMessages = function () {
this._apiKeyMessage.close();
};
-/** @param {string} apiKey */
-ConnectingToApi.prototype._onClickSave = function (apiKey) {};
-
/** @param {boolean} [bShowLogoutLink] */
-ConnectingToApi.prototype._hide = function (bShowLogoutLink) {
+LoginPage.prototype._hide = function (bShowLogoutLink) {
this._router.openMain();
if (bShowLogoutLink) {
this._logoutLink.classList.remove("hidden");
}
};
-ConnectingToApi.prototype._show = function () {
+LoginPage.prototype._show = function () {
this._router.openLogin();
this._logoutLink.classList.add("hidden");
};
diff --git a/sdkjs-plugins/content/zotero/scripts/router.js b/sdkjs-plugins/content/zotero/scripts/router.js
index 5233d6af..e0a38c3e 100644
--- a/sdkjs-plugins/content/zotero/scripts/router.js
+++ b/sdkjs-plugins/content/zotero/scripts/router.js
@@ -1,9 +1,9 @@
// @ts-check
function Router() {
- this._states = ["mainState", "loginState"];
- this._routes = ["main", "login"];
- /** @type {"main"|"login"} */
+ this._states = ["mainState", "loginState", "settingsState"];
+ this._routes = ["main", "login", "settings"];
+ /** @type {"main"|"login"|"settings"} */
this._currentRoute = "login";
this._currentRouteIndex = 1;
this._containers = this._states.map(function (route) {
@@ -13,12 +13,12 @@ function Router() {
});
}
-/** @returns {"main"|"login"} */
+/** @returns {"main"|"login"|"settings"} */
Router.prototype.getRoute = function () {
return this._currentRoute;
};
-/** @param {"main"|"login"} route */
+/** @param {"main"|"login"|"settings"} route */
Router.prototype._setCurrentRoute = function (route) {
this._containers[this._currentRouteIndex].classList.add("hidden");
this._currentRoute = route;
@@ -33,3 +33,7 @@ Router.prototype.openMain = function () {
Router.prototype.openLogin = function () {
this._setCurrentRoute("login");
};
+
+Router.prototype.openSettings = function () {
+ this._setCurrentRoute("settings");
+};
diff --git a/sdkjs-plugins/content/zotero/scripts/settings.js b/sdkjs-plugins/content/zotero/scripts/settings.js
new file mode 100644
index 00000000..275420e8
--- /dev/null
+++ b/sdkjs-plugins/content/zotero/scripts/settings.js
@@ -0,0 +1,669 @@
+// @ts-check
+
+///
+///
+///
+///
+///
+///
+///
+///
+
+/**
+ * @param {Router} router
+ * @param {string} displayNoneClass
+ */
+function SettingsPage(router, displayNoneClass) {
+ this._router = router;
+ this._displayNoneClass = displayNoneClass;
+
+ this._openSettingsBtn = new Button("settingsBtn", {
+ variant: "icon-only",
+ size: "small",
+ });
+ this._saveBtn = new Button("saveSettingsBtn", {
+ variant: "primary",
+ });
+ this._cancelBtn = new Button("cancelBtn", {
+ variant: "secondary",
+ });
+
+ this._styleWrapper = document.getElementById("styleWrapper");
+ if (!this._styleWrapper) {
+ throw new Error("styleWrapper not found");
+ }
+ this._styleSelectList = document.getElementById("styleSelectList");
+ if (!this._styleSelectList) {
+ throw new Error("styleSelectList not found");
+ }
+ this._styleSelectListOther = document.getElementById(
+ "styleSelectedListOther"
+ );
+ if (!this._styleSelectListOther) {
+ throw new Error("styleSelectListOther not found");
+ }
+ this._styleSelect = document.getElementById("styleSelect");
+ if (!this._styleSelect) {
+ throw new Error("styleSelect not found");
+ }
+ this._notesStyleWrapper = document.getElementById("notesStyle");
+ if (!this._notesStyleWrapper) {
+ throw new Error("notesStyleWrapper not found");
+ }
+ this._footNotes = document.getElementById("footNotes");
+ if (!this._footNotes) {
+ throw new Error("footNotes not found");
+ }
+ this._endNotes = document.getElementById("endNotes");
+ if (!this._endNotes) {
+ throw new Error("endNotes not found");
+ }
+
+ this._cslFileInput = document.getElementById("cslFileInput");
+ if (!this._cslFileInput) {
+ throw new Error("cslFileInput not found");
+ }
+
+ this._languageSelect = new SelectBox("styleLangList", {
+ placeholder: "Select language",
+ });
+
+ this._cslStylesManager = new CslStylesManager();
+ this._localesManager = new LocalesManager();
+
+ /** @type {HTMLElement[]} */
+ this._selectLists = [];
+ /**
+ * @param {Promise} promise
+ */
+ this._onChangeState = function (promise) {};
+ this._apiKeyMessage = new Message("apiKeyMessage", { type: "error" });
+ /** @type {Array<[string, string]>} */
+ this._LANGUAGES = [
+ ["af-ZA", "Afrikaans"],
+ ["ar", "Arabic"],
+ ["bg-BG", "Bulgarian"],
+ ["ca-AD", "Catalan"],
+ ["cs-CZ", "Czech"],
+ ["cy-GB", "Welsh"],
+ ["da-DK", "Danish"],
+ ["de-AT", "German (Austria)"],
+ ["de-CH", "German (Switzerland)"],
+ ["de-DE", "German (Germany)"],
+ ["el-GR", "Greek"],
+ ["en-GB", "English (UK)"],
+ ["en-US", "English (US)"],
+ ["es-CL", "Spanish (Chile)"],
+ ["es-ES", "Spanish (Spain)"],
+ ["es-MX", "Spanish (Mexico)"],
+ ["et-EE", "Estonian"],
+ ["eu", "Basque"],
+ ["fa-IR", "Persian"],
+ ["fi-FI", "Finnish"],
+ ["fr-CA", "French (Canada)"],
+ ["fr-FR", "French (France)"],
+ ["he-IL", "Hebrew"],
+ ["hr-HR", "Croatian"],
+ ["hu-HU", "Hungarian"],
+ ["id-ID", "Indonesian"],
+ ["is-IS", "Icelandic"],
+ ["it-IT", "Italian"],
+ ["ja-JP", "Japanese"],
+ ["km-KH", "Khmer"],
+ ["ko-KR", "Korean"],
+ ["la", "Latin"],
+ ["lt-LT", "Lithuanian"],
+ ["lv-LV", "Latvian"],
+ ["mn-MN", "Mongolian"],
+ ["nb-NO", "Norwegian (Bokmål)"],
+ ["nl-NL", "Dutch"],
+ ["nn-NO", "Norwegian (Nynorsk)"],
+ ["pl-PL", "Polish"],
+ ["pt-BR", "Portuguese (Brazil)"],
+ ["pt-PT", "Portuguese (Portugal)"],
+ ["ro-RO", "Romanian"],
+ ["ru-RU", "Russian"],
+ ["sk-SK", "Slovak"],
+ ["sl-SI", "Slovenian"],
+ ["sr-RS", "Serbian"],
+ ["sv-SE", "Swedish"],
+ ["th-TH", "Thai"],
+ ["tr-TR", "Turkish"],
+ ["uk-UA", "Ukrainian"],
+ ["vi-VN", "Vietnamese"],
+ ["zh-CN", "Chinese (PRC)"],
+ ["zh-TW", "Chinese (Taiwan)"],
+ ];
+
+ this._bNumFormat = false;
+
+ this._initSelectBoxes();
+}
+
+/**
+ * @returns {LocalesManager}
+ */
+SettingsPage.prototype.getLocalesManager = function () {
+ return this._localesManager;
+};
+
+/**
+ * @returns {CslStylesManager}
+ */
+SettingsPage.prototype.getStyleManager = function () {
+ return this._cslStylesManager;
+};
+
+/**
+ * @returns {string|null}
+ */
+SettingsPage.prototype.getLocale = function () {
+ return this._localesManager.getLocale();
+};
+
+/**
+ * @returns {string|null}
+ */
+SettingsPage.prototype.getLastUsedStyleId = function () {
+ return this._cslStylesManager.getLastUsedStyleId();
+};
+
+/**
+ * @returns
+ */
+SettingsPage.prototype.init = function () {
+ const self = this;
+ var lastStyle = this._cslStylesManager.getLastUsedStyleId() || "ieee";
+ const savedLang = this._localesManager.getLastUsedLanguage();
+ this._addEventListeners();
+ this._languageSelect.addItems(this._LANGUAGES, savedLang);
+
+ const promises = [
+ this._cslStylesManager.getStyle(lastStyle),
+ this._localesManager.loadLocale(savedLang),
+ this._loadStyles(),
+ ];
+
+ return Promise.all(promises);
+};
+
+/**
+ * @param {function(Promise): void} callbackFn
+ */
+SettingsPage.prototype.onChangeState = function (callbackFn) {
+ this._onChangeState = callbackFn;
+};
+
+/**
+ * @param {boolean} isAvailable
+ */
+SettingsPage.prototype.setDesktopApiAvailable = function (isAvailable) {
+ this._localesManager.setDesktopApiAvailable(isAvailable);
+ this._cslStylesManager.setDesktopApiAvailable(isAvailable);
+};
+
+/**
+ * @param {boolean} isAvailable
+ */
+SettingsPage.prototype.setRestApiAvailable = function (isAvailable) {
+ this._localesManager.setRestApiAvailable(isAvailable);
+ this._cslStylesManager.setRestApiAvailable(isAvailable);
+};
+
+SettingsPage.prototype._addEventListeners = function () {
+ const self = this;
+
+ this._openSettingsBtn.subscribe(function (event) {
+ if (event.type !== "button:click") {
+ return;
+ }
+ self._show();
+ });
+ this._saveBtn.subscribe(function (event) {
+ if (event.type !== "button:click") {
+ return;
+ }
+ const selectedLang = self._languageSelect.getSelectedValue();
+ if (selectedLang === null) {
+ console.error("No language selected");
+ return;
+ }
+ const promises = [];
+ if (self._localesManager.getLastUsedLanguage() !== selectedLang) {
+ self._localesManager.saveLastUsedLanguage(selectedLang);
+ promises.push(self._localesManager.loadLocale(selectedLang));
+ }
+
+ if (promises.length) {
+ self._onChangeState(
+ Promise.all(promises).then(function () {
+ self._hide();
+ })
+ );
+ }
+ });
+ this._cancelBtn.subscribe(function (event) {
+ if (event.type !== "button:click") {
+ return;
+ }
+ self._hide();
+ });
+
+ this._cslFileInput.onchange = function (e) {
+ if (!(e.target instanceof HTMLInputElement)) return;
+ /** @type {HTMLInputElement} */
+ const target = e.target;
+ if (!target.files) return;
+ var file = target.files[0];
+ if (!file) {
+ console.error("No file selected");
+ return;
+ }
+ //showLoader(true);
+
+ self._cslStylesManager
+ .addCustomStyle(file)
+ .then(function (styleValue) {
+ self._addStylesToList([styleValue]);
+ })
+ .catch(function (error) {
+ console.error(error);
+ showError(translate("Failed to upload file"));
+ })
+ .finally(function () {
+ showLoader(false);
+ });
+ };
+
+ /**
+ * @param {Event|null} e - The input event.
+ * @param {String} [filter] - The filter to apply on the style options.
+ */
+ this._styleSelect.oninput = function (e, filter) {
+ var input = self._styleSelect;
+ if (!(input instanceof HTMLInputElement)) return;
+ filter = filter !== undefined ? filter : input.value.toLowerCase();
+ var list = self._styleSelectList.classList.contains(
+ self._displayNoneClass
+ )
+ ? self._styleSelectListOther
+ : self._styleSelectList;
+
+ for (var i = 0; i < list.children.length; i++) {
+ const child = list.children[i];
+ if (child instanceof HTMLElement === false) {
+ continue;
+ }
+ var text = child.textContent || child.innerText;
+ if (!filter || text.toLowerCase().indexOf(filter) > -1) {
+ child.classList.remove(self._displayNoneClass);
+ } else {
+ child.classList.add(self._displayNoneClass);
+ }
+ }
+ };
+
+ /**
+ * @param {Event} inp - The input event.
+ * @param {String} styleName - The name of the selected style.
+ * @param {Boolean} isClick - Whether the style was selected manually or not.
+ */
+ this._styleSelect.onselectchange = function (inp, styleName, isClick) {
+ isClick && self._showLoader(true);
+ self._styleSelect.oninput(inp, "");
+
+ return self._cslStylesManager
+ .getStyle(styleName)
+ .then(function (style) {
+ self._onStyleChange();
+ if (isClick) {
+ return self._citationService.updateCslItems(
+ true,
+ true,
+ false
+ );
+ }
+ })
+ .catch(function (err) {
+ console.error(err);
+ if (typeof err === "string") {
+ self._showError(err);
+ }
+ })
+ .finally(function () {
+ isClick && self._showLoader(false);
+ });
+ };
+
+ this._styleSelectList.onopen = function () {
+ self._styleSelectList.style.width =
+ self._styleWrapper.clientWidth - 2 + "px";
+ };
+
+ [this._footNotes, this._endNotes].forEach(function (el) {
+ el.addEventListener("change", function (event) {
+ if (
+ event.target instanceof HTMLInputElement &&
+ event.target.checked
+ ) {
+ const value = event.target.value;
+ if (value === "endnotes" || value === "footnotes") {
+ self._cslStylesManager.saveLastUsedNotesStyle(value);
+ } else {
+ console.error("Unknown notes style: " + value);
+ }
+ }
+ });
+ });
+};
+
+SettingsPage.prototype._hideAllMessages = function () {
+ this._apiKeyMessage.close();
+};
+
+SettingsPage.prototype._hide = function () {
+ this._router.openMain();
+};
+
+SettingsPage.prototype._show = function () {
+ this._router.openSettings();
+};
+
+/** @returns {Promise} */
+SettingsPage.prototype._loadStyles = function () {
+ const self = this;
+ return this._cslStylesManager
+ .getStylesInfo()
+ .then(
+ /** @param {Array} stylesInfo*/ function (stylesInfo) {
+ var openOtherStyleList = function (
+ /** @type {HTMLElement} */ list
+ ) {
+ return function (/** @type {MouseEvent} */ ev) {
+ self._styleSelectListOther.style.width =
+ self._styleWrapper.clientWidth - 2 + "px";
+ ev.stopPropagation();
+ self._openList(list);
+ };
+ };
+
+ self._addStylesToList(stylesInfo);
+
+ const el = document.createElement("hr");
+ self._styleSelectList.appendChild(el);
+
+ if (self._styleSelectListOther.children.length > 0) {
+ var other = document.createElement("span");
+ other.textContent = "More Styles...";
+ self._styleSelectList.appendChild(other);
+ other.onclick = openOtherStyleList(
+ self._styleSelectListOther
+ );
+ }
+
+ var custom = document.createElement("span");
+ custom.setAttribute("class", "select-file");
+ var label = document.createElement("label");
+ label.setAttribute("for", "cslFileInput");
+ label.textContent = "Add custom style...";
+ custom.appendChild(label);
+ self._styleSelectList.appendChild(custom);
+ }
+ )
+ .catch(function (err) {
+ console.error(err);
+ });
+};
+
+/**
+ * @param {Array} stylesInfo
+ */
+SettingsPage.prototype._addStylesToList = function (stylesInfo) {
+ const self = this;
+ var lastStyle = this._cslStylesManager.getLastUsedStyleIdOrDefault();
+ const styleSelect = this._styleSelect;
+ if (styleSelect instanceof HTMLInputElement === false) {
+ console.error("styleSelect is not an input element");
+ return;
+ }
+
+ /**
+ * @param {HTMLElement} list - the list of styles where the element is added.
+ * @param {HTMLElement} other - the list of styles where the element is removed.
+ */
+ var onStyleSelectOther = function (list, other) {
+ return function (/** @type {MouseEvent} */ ev) {
+ let tmpEl = list.removeChild(
+ list.children[list.children.length - 3]
+ );
+ var newEl = document.createElement("span");
+ newEl.setAttribute(
+ "data-value",
+ String(tmpEl.getAttribute("data-value"))
+ );
+ newEl.textContent = tmpEl.textContent;
+ other.appendChild(newEl);
+ newEl.onclick = onStyleSelectOther(
+ self._styleSelectList,
+ self._styleSelectListOther
+ );
+
+ if (ev.target instanceof HTMLElement === false) {
+ console.error("ev.target is not an HTMLElement");
+ return;
+ }
+ tmpEl = other.removeChild(ev.target);
+ newEl = document.createElement("span");
+ newEl.setAttribute(
+ "data-value",
+ String(tmpEl.getAttribute("data-value"))
+ );
+ newEl.textContent = tmpEl.textContent;
+ list.insertBefore(newEl, list.firstElementChild);
+ newEl.onclick = self._onClickListElement(
+ self._styleSelectList,
+ styleSelect
+ );
+ var event = new Event("click");
+ newEl.dispatchEvent(event);
+ self._closeList();
+ };
+ };
+
+ for (var i = 0; i < stylesInfo.length; i++) {
+ var el = document.createElement("span");
+ el.setAttribute("data-value", stylesInfo[i].name);
+ el.textContent = stylesInfo[i].title;
+ if (
+ self._cslStylesManager.isStyleDefault(stylesInfo[i].name) ||
+ stylesInfo[i].name == lastStyle
+ ) {
+ if (stylesInfo.length == 1)
+ self._styleSelectList.insertBefore(
+ el,
+ self._styleSelectList.firstElementChild
+ );
+ else self._styleSelectList.appendChild(el);
+ el.onclick = self._onClickListElement(
+ self._styleSelectList,
+ styleSelect
+ );
+ } else {
+ self._styleSelectListOther.appendChild(el);
+ el.onclick = onStyleSelectOther(
+ self._styleSelectList,
+ self._styleSelectListOther
+ );
+ }
+ if (stylesInfo[i].name == lastStyle) {
+ el.setAttribute("selected", "");
+ self._selectInput(styleSelect, el, self._styleSelectList, false);
+ }
+ }
+};
+
+SettingsPage.prototype._onStyleChange = function () {
+ let styleFormat = this._cslStylesManager.getLastUsedFormat();
+ this._citationService.setStyleFormat(styleFormat);
+ this._bNumFormat = styleFormat == "numeric";
+ if ("note" === styleFormat) {
+ this._notesStyleWrapper.classList.remove(this._displayNoneClass);
+ } else {
+ this._notesStyleWrapper.classList.add(this._displayNoneClass);
+ }
+
+ let notesStyle = this._cslStylesManager.getLastUsedNotesStyle();
+ this._citationService.setNotesStyle(notesStyle);
+ const notesAs = this._notesStyleWrapper.querySelector(
+ 'input[name="notesAs"][value="' + notesStyle + '"]'
+ );
+ if (notesAs && notesAs instanceof HTMLInputElement) {
+ notesAs.checked = true;
+ }
+};
+
+/**
+ * @param {HTMLElement} el
+ */
+SettingsPage.prototype._openList = function (el) {
+ const self = this;
+ el.classList.remove(this._displayNoneClass);
+ const f = function () {
+ self._closeList();
+ window.removeEventListener("click", f);
+ };
+ window.addEventListener("click", f);
+};
+
+SettingsPage.prototype._closeList = function () {
+ for (var i = 0; i < this._selectLists.length; i++) {
+ if (this._selectLists[i] === this._styleSelectList)
+ this._styleSelect.oninput(null, "");
+ this._selectLists[i].classList.add(this._displayNoneClass);
+ }
+};
+
+SettingsPage.prototype._initSelectBoxes = function () {
+ const self = this;
+ var select = document.getElementsByClassName("control select");
+ for (var i = 0; i < select.length; i++) {
+ var input = select[i];
+ var holder = input.parentElement;
+ if (!(input instanceof HTMLInputElement) || !holder) {
+ console.error("initSelectBoxes: no input or holder");
+ continue;
+ }
+
+ var arrow = document.createElement("span");
+ arrow.classList.add("selectArrow");
+ arrow.appendChild(document.createElement("span"));
+ arrow.appendChild(document.createElement("span"));
+ holder.appendChild(arrow);
+
+ const holderElement = holder.getElementsByClassName("selectList");
+
+ for (var k = 0; k < holderElement.length; k++) {
+ var temp = holderElement[k];
+ var list;
+ if (temp instanceof HTMLElement) {
+ list = temp;
+ } else {
+ console.error(
+ "initSelectBoxes: holderElement is not HTMLElement"
+ );
+ continue;
+ }
+
+ if (list.children.length > 0) {
+ for (var j = 0; j < list.children.length; j++) {
+ const child = list.children[j];
+ if (child instanceof HTMLElement === false) {
+ continue;
+ }
+ child.onclick = self._onClickListElement(list, input);
+ }
+ // selectInput(input, list.children[0], list, false);
+ }
+
+ /**
+ * @param {HTMLElement} list
+ * @param {HTMLElement} input
+ * @returns {function}
+ */
+ var fOpen = function (list, input) {
+ return function (/** @type {MouseEvent} */ ev) {
+ ev.stopPropagation();
+ if (
+ !self._styleSelectListOther.classList.contains(
+ self._displayNoneClass
+ )
+ )
+ return true;
+
+ if (list.onopen) {
+ list.onopen();
+ }
+ if (!input.hasAttribute("readonly")) {
+ input.select();
+ }
+ self._openList(list);
+
+ return true;
+ };
+ };
+
+ if (k !== 1) {
+ input.onclick = fOpen(list, input);
+ arrow.onclick = fOpen(list, input);
+ }
+ self._selectLists.push(list);
+ }
+ }
+};
+
+/**
+ * @param {HTMLElement} list
+ * @param {HTMLInputElement} input
+ */
+SettingsPage.prototype._onClickListElement = function (list, input) {
+ const self = this;
+ return function (/** @type {MouseEvent} */ ev) {
+ if (!ev.target || !(ev.target instanceof HTMLElement)) {
+ console.error("onClickListElement: no target");
+ return;
+ }
+ var sel = ev.target.getAttribute("data-value");
+ for (var i = 0; i < list.children.length; i++) {
+ const temp = list.children[i];
+ if (temp instanceof HTMLElement === false) continue;
+ /** @type {HTMLElement} */
+ const child = temp;
+ if (list.children[i].getAttribute("data-value") == sel) {
+ list.children[i].setAttribute("selected", "");
+
+ self._selectInput(input, child, list, true);
+ } else {
+ if (list.children[i].hasAttribute("selected")) {
+ list.children[i].attributes.removeNamedItem("selected");
+ }
+ }
+ }
+ };
+};
+
+/**
+ * @param {HTMLInputElement} input
+ * @param {HTMLElement} el
+ * @param {HTMLElement} list
+ * @param {boolean} isClick
+ */
+SettingsPage.prototype._selectInput = function (input, el, list, isClick) {
+ input.value = el.textContent;
+ var val = el.getAttribute("data-value") || "";
+ input.setAttribute("data-value", val);
+ input.setAttribute("title", el.textContent);
+ if (input.onselectchange) {
+ input.onselectchange(input, val, isClick);
+ }
+ list.classList.add(this._displayNoneClass);
+};
diff --git a/sdkjs-plugins/content/zotero/scripts/shared/components/select-citation.js b/sdkjs-plugins/content/zotero/scripts/shared/components/select-citation.js
index 27cebda1..440c57ef 100644
--- a/sdkjs-plugins/content/zotero/scripts/shared/components/select-citation.js
+++ b/sdkjs-plugins/content/zotero/scripts/shared/components/select-citation.js
@@ -51,7 +51,7 @@ function SelectCitationsComponent(
["volume", "Volume"],
];
- this._cancelBtn = document.getElementById("cancelBtn");
+ this._cancelSelectBtn = document.getElementById("cancelSelectBtn");
this._docsHolder = document.getElementById("docsHolder");
this._docsThumb = document.getElementById("docsThumb");
@@ -89,8 +89,8 @@ function SelectCitationsComponent(
SelectCitationsComponent.prototype._init = function () {
const self = this;
- if (this._cancelBtn) {
- this._cancelBtn.onclick = function (e) {
+ if (this._cancelSelectBtn) {
+ this._cancelSelectBtn.onclick = function (e) {
var ids = [];
for (var id in self._items) {
ids.push(id);
diff --git a/sdkjs-plugins/content/zotero/scripts/shared/ui/selectbox.js b/sdkjs-plugins/content/zotero/scripts/shared/ui/selectbox.js
index e9087fc0..3aa43367 100644
--- a/sdkjs-plugins/content/zotero/scripts/shared/ui/selectbox.js
+++ b/sdkjs-plugins/content/zotero/scripts/shared/ui/selectbox.js
@@ -538,6 +538,36 @@ SelectBox.prototype.addItem = function (value, text, selected) {
this._updateSelectedText();
};
+/**
+ * @param {Array<[string,string]>} values
+ * @param {string} [selectedValue]
+ */
+SelectBox.prototype.addItems = function (values, selectedValue) {
+ const self = this;
+ values.forEach(function (pair, index) {
+ const isSelected = selectedValue
+ ? pair[0] === selectedValue
+ : index === 0;
+
+ if (isSelected) {
+ if (self._options.multiple) {
+ self._selectedValues.add(pair[0]);
+ } else {
+ self._selectedValues.clear();
+ self._selectedValues.add(pair[0]);
+ }
+ }
+
+ self._items.push({
+ value: pair[0],
+ text: pair[1],
+ selected: isSelected,
+ });
+ }, this);
+
+ this._updateSelectedText();
+};
+
SelectBox.prototype.addSeparator = function () {
this._items.push(null);
};
@@ -556,6 +586,21 @@ SelectBox.prototype.removeItem = function (value) {
this._updateSelectedText();
};
+/**
+ * @return {null | string}
+ */
+SelectBox.prototype.getSelectedValue = function () {
+ if (this._options.multiple) {
+ console.error(
+ "Method getSelectedValue is only available for single-select boxes."
+ );
+ return null;
+ } else {
+ var values = Array.from(this._selectedValues);
+ return values.length > 0 ? values[0] : null;
+ }
+};
+
/**
* @return {null | string | Array}
*/