mirror of
https://github.com/ONLYOFFICE/onlyoffice.github.io.git
synced 2026-04-07 14:04:30 +08:00
component for selecting a citation from search results
This commit is contained in:
@ -51,6 +51,8 @@
|
||||
<script defer src="scripts/shared/ui/selectbox.js"></script>
|
||||
<script defer src="scripts/shared/ui/button.js"></script>
|
||||
<script defer src="scripts/shared/ui/message.js"></script>
|
||||
<script defer src="scripts/shared/components/search-filter.js"></script>
|
||||
<script defer src="scripts/shared/components/select-citation.js"></script>
|
||||
|
||||
<script defer src="scripts/zotero/zotero-environment.js"></script>
|
||||
<script defer src="scripts/zotero/zotero-api-checker.js"></script>
|
||||
@ -118,6 +120,10 @@
|
||||
<div id="selectedWrapper">
|
||||
<div class="flexCol flexSize flexCenter">
|
||||
<div id="selectedHolder"></div>
|
||||
<div id="selectedInfo" class="hidden">
|
||||
<span id="selectedCount">0 selected</span>
|
||||
<span id="cancelBtn" class="i18n link">Cancel selection</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="selectedThumb" class="scrollThumb hidden"></div>
|
||||
</div>
|
||||
@ -136,7 +142,6 @@
|
||||
<button id="insertLinkBtn" class="button control i18n btn-text-default"
|
||||
style="flex: 1; margin-left: 8px; margin-right: 0;" disabled>Insert Citation</button>
|
||||
</div>
|
||||
<button id="cancelBtn" class="button control i18n btn-text-default" disabled>Cancel select</button>
|
||||
</div>
|
||||
<div id="insertBibDiv" class="flex">
|
||||
<button id="insertBibBtn" class="button control i18n btn-text-default">Insert Bibliography</button>
|
||||
|
||||
@ -148,6 +148,12 @@ input[type="text"] {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
#selectedInfo:not(.hidden) {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#searchWrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@ -27,6 +27,8 @@
|
||||
/// <reference path="./csl/locales/locales-manager.js" />
|
||||
/// <reference path="./services/translate-service.js" />
|
||||
/// <reference path="./services/citation-service.js" />
|
||||
/// <reference path="./shared/components/search-filter.js" />
|
||||
/// <reference path="./shared/components/select-citation.js" />
|
||||
/// <reference path="./shared/ui/input.js" />
|
||||
/// <reference path="./shared/ui/selectbox.js" />
|
||||
/// <reference path="./shared/ui/button.js" />
|
||||
@ -37,20 +39,11 @@
|
||||
* @property {Function} onscroll
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Selected
|
||||
* @property {Object<string|number, SearchResultItem>} items
|
||||
* @property {Object<string|number, HTMLElement>} html
|
||||
* @property {Object<string|number, HTMLInputElement>} checks
|
||||
* @property {function(): number} count
|
||||
*/
|
||||
|
||||
(function () {
|
||||
var counter = 0; // счетчик отправленных запросов (используется чтобы знать показывать "not found" или нет)
|
||||
var displayNoneClass = "hidden";
|
||||
var blurClass = "blur";
|
||||
/** @type {number} */
|
||||
var loadTimeout;
|
||||
|
||||
//var loadingStyle = false;
|
||||
//var loadingLocale = false;
|
||||
var bNumFormat = false;
|
||||
@ -68,18 +61,6 @@
|
||||
// как документе (для этого нужно знать где именно в документе мы вставляем цитату,
|
||||
// какая цитата сверху и снизу от текущего курсора)
|
||||
|
||||
/** @type {Selected} */
|
||||
var selected = {
|
||||
items: {},
|
||||
html: {},
|
||||
checks: {},
|
||||
count: function () {
|
||||
var k = 0;
|
||||
for (var i in selected.items) k++;
|
||||
return k;
|
||||
},
|
||||
};
|
||||
|
||||
/** @type {Router} */
|
||||
var router;
|
||||
/** @type {ZoteroSdk} */
|
||||
@ -101,8 +82,10 @@
|
||||
groupsHash: "",
|
||||
};
|
||||
|
||||
/** @type {SearchFilter} */
|
||||
/** @type {SearchFilterComponents} */
|
||||
var searchFilter;
|
||||
/** @type {SelectCitationsComponent} */
|
||||
var selectCitation;
|
||||
/** @type {Button} */
|
||||
var saveAsTextBtn;
|
||||
/** @type {Object.<string, HTMLElement | HTMLInputElement>} */
|
||||
@ -124,14 +107,7 @@
|
||||
if (!contentHolder) {
|
||||
throw new Error("contentHolder not found");
|
||||
}
|
||||
const docsHolder = document.getElementById("docsHolder");
|
||||
if (!docsHolder) {
|
||||
throw new Error("docsHolder not found");
|
||||
}
|
||||
const docsThumb = document.getElementById("docsThumb");
|
||||
if (!docsThumb) {
|
||||
throw new Error("docsThumb not found");
|
||||
}
|
||||
|
||||
const configState = document.getElementById("configState");
|
||||
if (!configState) {
|
||||
throw new Error("configState not found");
|
||||
@ -140,18 +116,6 @@
|
||||
if (!mainState) {
|
||||
throw new Error("mainState not found");
|
||||
}
|
||||
const selectedWrapper = document.getElementById("selectedWrapper");
|
||||
if (!selectedWrapper) {
|
||||
throw new Error("selectedWrapper not found");
|
||||
}
|
||||
const selectedHolder = document.getElementById("selectedHolder");
|
||||
if (!selectedHolder) {
|
||||
throw new Error("selectedHolder not found");
|
||||
}
|
||||
const selectedThumb = document.getElementById("selectedThumb");
|
||||
if (!selectedThumb) {
|
||||
throw new Error("selectedThumb not found");
|
||||
}
|
||||
const buttonsWrapper = document.getElementById("buttonsWrapper");
|
||||
if (!buttonsWrapper) {
|
||||
throw new Error("buttonsWrapper not found");
|
||||
@ -210,10 +174,6 @@
|
||||
if (!insertLinkBtn) {
|
||||
throw new Error("insertLinkBtn not found");
|
||||
}
|
||||
const cancelBtn = document.getElementById("cancelBtn");
|
||||
if (!cancelBtn) {
|
||||
throw new Error("cancelBtn not found");
|
||||
}
|
||||
const refreshBtn = document.getElementById("refreshBtn");
|
||||
if (!refreshBtn) {
|
||||
throw new Error("refreshBtn not found");
|
||||
@ -227,7 +187,12 @@
|
||||
if (!cslFileInput) {
|
||||
throw new Error("cslFileInput not found");
|
||||
}
|
||||
searchFilter = new SearchFilter();
|
||||
searchFilter = new SearchFilterComponents();
|
||||
selectCitation = new SelectCitationsComponent(
|
||||
displayNoneClass,
|
||||
loadMore,
|
||||
shouldLoadMore
|
||||
);
|
||||
saveAsTextBtn = new Button("saveAsTextBtn");
|
||||
|
||||
elements = {
|
||||
@ -236,16 +201,10 @@
|
||||
error: error,
|
||||
|
||||
contentHolder: contentHolder,
|
||||
docsHolder: docsHolder,
|
||||
docsThumb: docsThumb,
|
||||
|
||||
configState: configState,
|
||||
|
||||
mainState: mainState,
|
||||
|
||||
selectedWrapper: selectedWrapper,
|
||||
selectedHolder: selectedHolder,
|
||||
selectedThumb: selectedThumb,
|
||||
buttonsWrapper: buttonsWrapper,
|
||||
|
||||
locatorLabel: locatorLabel,
|
||||
@ -263,7 +222,6 @@
|
||||
|
||||
insertBibBtn: insertBibBtn,
|
||||
insertLinkBtn: insertLinkBtn,
|
||||
cancelBtn: cancelBtn,
|
||||
refreshBtn: refreshBtn,
|
||||
|
||||
checkOmitAuthor: checkOmitAuthor,
|
||||
@ -271,11 +229,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
/** @type {Scroller} */
|
||||
var selectedScroller;
|
||||
/** @type {Scroller} */
|
||||
var docsScroller;
|
||||
|
||||
window.Asc.plugin.init = function () {
|
||||
initElements();
|
||||
showLoader(true);
|
||||
@ -321,16 +274,6 @@
|
||||
});
|
||||
|
||||
window.Asc.plugin.onTranslate = applyTranslations;
|
||||
|
||||
selectedScroller = initScrollBox(
|
||||
elements.selectedHolder,
|
||||
elements.selectedThumb
|
||||
);
|
||||
docsScroller = initScrollBox(
|
||||
elements.docsHolder,
|
||||
elements.docsThumb,
|
||||
checkDocsScroll
|
||||
);
|
||||
};
|
||||
|
||||
function preloadLastStyle() {
|
||||
@ -516,6 +459,8 @@
|
||||
}
|
||||
|
||||
function addEventListeners() {
|
||||
selectCitation.subscribe(checkSelected);
|
||||
|
||||
elements.cslFileInput.onchange = function (e) {
|
||||
if (!(e.target instanceof HTMLInputElement)) return;
|
||||
/** @type {HTMLInputElement} */
|
||||
@ -559,9 +504,9 @@
|
||||
)
|
||||
return Promise.resolve();
|
||||
|
||||
clearLibrary();
|
||||
selectCitation.clearLibrary();
|
||||
|
||||
/** @type {Array<Promise<void>>} */
|
||||
/** @type {Array<Promise<boolean>>} */
|
||||
const promises = [];
|
||||
|
||||
return sdk
|
||||
@ -622,16 +567,6 @@
|
||||
searchFor(text, selectedGroups);
|
||||
});
|
||||
|
||||
elements.cancelBtn.onclick = function (e) {
|
||||
var ids = [];
|
||||
for (var id in selected.items) {
|
||||
ids.push(id);
|
||||
}
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
removeSelected(ids[i]);
|
||||
}
|
||||
};
|
||||
|
||||
elements.refreshBtn.onclick = function () {
|
||||
if (!cslStylesManager.getLastUsedStyleId()) {
|
||||
showError(translate("Style is not selected"));
|
||||
@ -706,7 +641,7 @@
|
||||
}
|
||||
|
||||
return citationService.insertSelectedCitations(
|
||||
selected.items,
|
||||
selectCitation.getItems(),
|
||||
prefix,
|
||||
suffix,
|
||||
locatorInfo,
|
||||
@ -714,9 +649,7 @@
|
||||
);
|
||||
})
|
||||
.then(function (keys) {
|
||||
keys.forEach(function (key) {
|
||||
removeSelected(key);
|
||||
});
|
||||
selectCitation.removeItems(keys);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error(error);
|
||||
@ -1232,57 +1165,35 @@
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} holder - The element that contains the document list.
|
||||
* @param {HTMLElement} [thumb]
|
||||
*/
|
||||
function checkDocsScroll(holder, thumb) {
|
||||
if (shouldLoadMore(holder)) {
|
||||
if (loadTimeout) {
|
||||
clearTimeout(loadTimeout);
|
||||
}
|
||||
function loadMore() {
|
||||
console.warn("Loading more...");
|
||||
if (lastSearch.obj && lastSearch.obj.next) {
|
||||
loadLibrary(
|
||||
lastSearch.obj.next(),
|
||||
true,
|
||||
true,
|
||||
!lastSearch.groups.length,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!lastSearch.obj &&
|
||||
!lastSearch.text.trim() &&
|
||||
!lastSearch.groups.length
|
||||
)
|
||||
return;
|
||||
|
||||
loadTimeout = setTimeout(function () {
|
||||
if (shouldLoadMore(holder)) {
|
||||
console.warn("Loading more...");
|
||||
if (lastSearch.obj && lastSearch.obj.next) {
|
||||
loadLibrary(
|
||||
lastSearch.obj.next(),
|
||||
true,
|
||||
true,
|
||||
!lastSearch.groups.length,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
for (
|
||||
var i = 0;
|
||||
i < lastSearch.groups.length &&
|
||||
lastSearch.groups[i].next;
|
||||
i++
|
||||
) {
|
||||
loadLibrary(
|
||||
sdk.getGroupItems(
|
||||
lastSearch.groups[i].next(),
|
||||
lastSearch.groups[i].id
|
||||
),
|
||||
true,
|
||||
false,
|
||||
i == lastSearch.groups.length - 1,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
for (
|
||||
var i = 0;
|
||||
i < lastSearch.groups.length && lastSearch.groups[i].next;
|
||||
i++
|
||||
) {
|
||||
loadLibrary(
|
||||
sdk.getGroupItems(
|
||||
lastSearch.groups[i].next(),
|
||||
lastSearch.groups[i].id
|
||||
),
|
||||
true,
|
||||
false,
|
||||
i == lastSearch.groups.length - 1,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1301,19 +1212,16 @@
|
||||
if (el.next) flag = false;
|
||||
});
|
||||
if (!lastSearch.obj || !lastSearch.obj.next || !flag) return false;
|
||||
if (
|
||||
!lastSearch.obj &&
|
||||
!lastSearch.text.trim() &&
|
||||
!lastSearch.groups.length
|
||||
)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function clearLibrary() {
|
||||
var holder = elements.docsHolder;
|
||||
while (holder.lastChild) {
|
||||
holder.removeChild(holder.lastChild);
|
||||
}
|
||||
holder.scrollTop = 0;
|
||||
docsScroller.onscroll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Promise<SearchResult>} promise
|
||||
* @param {boolean} append
|
||||
@ -1321,7 +1229,7 @@
|
||||
* @param {boolean} hideLoader
|
||||
* @param {boolean} isGroup
|
||||
* @param {boolean} bCount
|
||||
* @returns {Promise<void>}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
function loadLibrary(
|
||||
promise,
|
||||
@ -1335,9 +1243,9 @@
|
||||
if (bCount) counter++;
|
||||
return promise
|
||||
.then(function (res) {
|
||||
console.log(res);
|
||||
console.warn(res);
|
||||
if (bCount) counter--;
|
||||
displaySearchItems(
|
||||
return displaySearchItems(
|
||||
append,
|
||||
res,
|
||||
null,
|
||||
@ -1351,7 +1259,7 @@
|
||||
if (err.message) {
|
||||
showError(translate(err.message));
|
||||
}
|
||||
displaySearchItems(
|
||||
return displaySearchItems(
|
||||
append,
|
||||
null,
|
||||
err,
|
||||
@ -1427,12 +1335,6 @@
|
||||
* @param {boolean} showNotFound
|
||||
*/
|
||||
function displaySearchItems(append, res, err, isGroup, showNotFound) {
|
||||
var holder = elements.docsHolder;
|
||||
|
||||
if (!append) {
|
||||
clearLibrary();
|
||||
}
|
||||
|
||||
var first = false;
|
||||
if (!lastSearch.obj && res && res.items && !res.items.length)
|
||||
first = true;
|
||||
@ -1446,192 +1348,31 @@
|
||||
if (isGroup && res && res.next) lastSearch.groups.push(res);
|
||||
else lastSearch.obj = res && res.items.length ? res : null;
|
||||
}
|
||||
|
||||
var page = document.createElement("div");
|
||||
page.classList.add("page" + holder.children.length);
|
||||
if (res && res.items && res.items.length > 0) {
|
||||
for (let index = 0; index < res.items.length; index++) {
|
||||
let item = res.items[index];
|
||||
item[isGroup ? "groupID" : "userID"] = res.id;
|
||||
citationService.fillUrisFromId(item);
|
||||
|
||||
page.appendChild(buildDocElement(item));
|
||||
}
|
||||
} else if (err || first) {
|
||||
if (err) {
|
||||
showError(err);
|
||||
} else if (showNotFound) {
|
||||
var notFound = document.createElement("div");
|
||||
notFound.textContent = translate("Nothing found");
|
||||
notFound.classList.add("searchInfo");
|
||||
page.appendChild(notFound);
|
||||
}
|
||||
}
|
||||
holder.appendChild(page);
|
||||
|
||||
docsScroller.onscroll();
|
||||
return selectCitation.displaySearchItems(
|
||||
append,
|
||||
res,
|
||||
err,
|
||||
showNotFound,
|
||||
first
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SearchResultItem} item
|
||||
* @returns {HTMLElement}
|
||||
* @param {number} numOfSelected
|
||||
*/
|
||||
function buildDocElement(item) {
|
||||
var root = document.createElement("div");
|
||||
root.classList.add("doc");
|
||||
|
||||
var checkHolder = document.createElement("div");
|
||||
var checkWrapper = document.createElement("div");
|
||||
checkWrapper.classList.add("checkbox");
|
||||
var check = document.createElement("input");
|
||||
check.setAttribute("type", "checkbox");
|
||||
if (selected.items[item.id]) {
|
||||
check.checked = true;
|
||||
selected.checks[item.id] = check;
|
||||
}
|
||||
checkWrapper.appendChild(check);
|
||||
checkWrapper.appendChild(document.createElement("span"));
|
||||
checkHolder.appendChild(checkWrapper);
|
||||
|
||||
var docInfo = document.createElement("div");
|
||||
docInfo.classList.add("docInfo");
|
||||
|
||||
var title = document.createElement("div");
|
||||
title.textContent = item.title;
|
||||
title.classList.add("truncate-text");
|
||||
docInfo.appendChild(title);
|
||||
|
||||
if (item.author && item.author.length > 0) {
|
||||
var authors = document.createElement("div");
|
||||
authors.textContent = item.author
|
||||
.map(function (a) {
|
||||
return a.family + ", " + a.given;
|
||||
})
|
||||
.join("; ");
|
||||
authors.setAttribute("title", authors.textContent);
|
||||
authors.classList.add("secondary-text");
|
||||
authors.classList.add("truncate-text");
|
||||
authors.classList.add("nowrap");
|
||||
docInfo.appendChild(authors);
|
||||
}
|
||||
|
||||
var source = document.createElement("div");
|
||||
if (item.publisher || item["publisher-place"]) {
|
||||
source.textContent =
|
||||
item.publisher || item["publisher-place"] || "";
|
||||
}
|
||||
if (item.issued && item.issued["date-parts"]) {
|
||||
var date = item.issued["date-parts"][0];
|
||||
if (source.textContent) {
|
||||
source.textContent += " (" + date.join("-") + ")";
|
||||
} else {
|
||||
source.textContent = date.join("-");
|
||||
}
|
||||
}
|
||||
source.setAttribute("title", source.textContent);
|
||||
source.classList.add("secondary-text");
|
||||
source.classList.add("truncate-text");
|
||||
source.classList.add("nowrap");
|
||||
docInfo.appendChild(source);
|
||||
|
||||
root.appendChild(checkHolder);
|
||||
root.appendChild(docInfo);
|
||||
|
||||
/**
|
||||
* @param {HTMLInputElement} input
|
||||
* @param {SearchResultItem} item
|
||||
* @returns
|
||||
*/
|
||||
function selectItem(input, item) {
|
||||
return function () {
|
||||
input.checked = !input.checked;
|
||||
if (input.checked) {
|
||||
addSelected(item, input);
|
||||
} else {
|
||||
removeSelected(item.id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var f = selectItem(check, item);
|
||||
checkWrapper.onclick = f;
|
||||
docInfo.onclick = f;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SearchResultItem} item
|
||||
* @param {HTMLInputElement} input
|
||||
*/
|
||||
function addSelected(item, input) {
|
||||
/** @type {HTMLElement} */
|
||||
var el = buildSelectedElement(item);
|
||||
selected.items[item.id] = item;
|
||||
selected.html[item.id] = el;
|
||||
selected.checks[item.id] = input;
|
||||
elements.selectedHolder.appendChild(el);
|
||||
docsScroller.onscroll();
|
||||
selectedScroller.onscroll();
|
||||
checkSelected();
|
||||
}
|
||||
|
||||
/** @param {string|number} id */
|
||||
function removeSelected(id) {
|
||||
var el = selected.html[id];
|
||||
delete selected.items[id];
|
||||
delete selected.html[id];
|
||||
if (selected.checks[id]) {
|
||||
selected.checks[id].checked = false;
|
||||
delete selected.checks[id];
|
||||
}
|
||||
elements.selectedHolder.removeChild(el);
|
||||
docsScroller.onscroll();
|
||||
selectedScroller.onscroll();
|
||||
checkSelected();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SearchResultItem} item
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
function buildSelectedElement(item) {
|
||||
var root = document.createElement("div");
|
||||
root.classList.add("selDoc");
|
||||
|
||||
var name = document.createElement("span");
|
||||
name.textContent = item.title;
|
||||
name.setAttribute("title", item.title);
|
||||
|
||||
var year = document.createElement("span");
|
||||
if (item.issued && item.issued["date-parts"]) {
|
||||
year.textContent = item.issued["date-parts"][0].join("-");
|
||||
}
|
||||
|
||||
var remove = document.createElement("span");
|
||||
remove.onclick = function () {
|
||||
removeSelected(item.id);
|
||||
};
|
||||
remove.textContent = "×";
|
||||
|
||||
root.appendChild(name);
|
||||
root.appendChild(year);
|
||||
root.appendChild(remove);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
function checkSelected() {
|
||||
if (selected.count() <= 0) {
|
||||
if (elements.insertLinkBtn)
|
||||
elements.insertLinkBtn.setAttribute("disabled", "");
|
||||
if (elements.cancelBtn)
|
||||
elements.cancelBtn.setAttribute("disabled", "");
|
||||
function checkSelected(numOfSelected) {
|
||||
if (numOfSelected <= 0) {
|
||||
elements.insertLinkBtn.setAttribute("disabled", "");
|
||||
} else {
|
||||
if (elements.insertLinkBtn)
|
||||
elements.insertLinkBtn.removeAttribute("disabled");
|
||||
if (elements.cancelBtn)
|
||||
elements.cancelBtn.removeAttribute("disabled");
|
||||
elements.insertLinkBtn.removeAttribute("disabled");
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
/// <reference path="../ui/selectbox.js" />
|
||||
/// <reference path="../ui/button.js" />
|
||||
|
||||
function SearchFilter() {
|
||||
function SearchFilterComponents() {
|
||||
this._searchField = new InputField("searchField", {
|
||||
type: "text",
|
||||
autofocus: true,
|
||||
@ -22,9 +22,11 @@ function SearchFilter() {
|
||||
});
|
||||
/** @type {Function[]} */
|
||||
this._subscribers = [];
|
||||
|
||||
this._addEventListeners();
|
||||
}
|
||||
|
||||
SearchFilter.prototype._addEventListeners = function () {
|
||||
SearchFilterComponents.prototype._addEventListeners = function () {
|
||||
const self = this;
|
||||
this._searchField.subscribe(function (e) {
|
||||
if (e.type === "inputfield:blur" || e.type === "inputfield:submit") {
|
||||
@ -49,7 +51,7 @@ SearchFilter.prototype._addEventListeners = function () {
|
||||
/**
|
||||
* @param {Array<UserGroupInfo>} groups
|
||||
*/
|
||||
SearchFilter.prototype.addGroups = function (groups) {
|
||||
SearchFilterComponents.prototype.addGroups = function (groups) {
|
||||
const self = this;
|
||||
let selectedItem = localStorage.getItem("selectedGroup");
|
||||
let hasSelected = false;
|
||||
@ -114,7 +116,7 @@ SearchFilter.prototype.addGroups = function (groups) {
|
||||
/**
|
||||
* @return {Array<string|"my_library"|"group_libraries">}
|
||||
*/
|
||||
SearchFilter.prototype._getSelectedGroups = function () {
|
||||
SearchFilterComponents.prototype._getSelectedGroups = function () {
|
||||
const self = this;
|
||||
const ids = this._librarySelectList.getSelectedValues();
|
||||
if (Array.isArray(ids) === false || ids.length === 0) {
|
||||
@ -132,7 +134,7 @@ SearchFilter.prototype._getSelectedGroups = function () {
|
||||
* @param {function(string, Array<string|"my_library"|"group_libraries">): void} callback
|
||||
* @returns {Object}
|
||||
*/
|
||||
SearchFilter.prototype.subscribe = function (callback) {
|
||||
SearchFilterComponents.prototype.subscribe = function (callback) {
|
||||
var self = this;
|
||||
this._subscribers.push(callback);
|
||||
|
||||
@ -150,7 +152,7 @@ SearchFilter.prototype.subscribe = function (callback) {
|
||||
* @param {Array<UserGroupInfo>} groups
|
||||
* @returns
|
||||
*/
|
||||
SearchFilter.prototype._selectedGroupsWatcher = function (
|
||||
SearchFilterComponents.prototype._selectedGroupsWatcher = function (
|
||||
customGroups,
|
||||
groups
|
||||
) {
|
||||
|
||||
@ -0,0 +1,444 @@
|
||||
// @ts-check
|
||||
|
||||
/// <reference path="../../types-global.js" />
|
||||
|
||||
/**
|
||||
* @param {string} displayNoneClass
|
||||
* @param {function(): void} fLoadMore
|
||||
* @param {function(HTMLElement): boolean} fShouldLoadMore
|
||||
*/
|
||||
function SelectCitationsComponent(
|
||||
displayNoneClass,
|
||||
fLoadMore,
|
||||
fShouldLoadMore
|
||||
) {
|
||||
/** @type {Object<string|number, SearchResultItem>} */
|
||||
this._items = {};
|
||||
/** @type {Object<string|number, HTMLElement>} */
|
||||
this._html = {};
|
||||
/** @type {Object<string|number, HTMLInputElement>} */
|
||||
this._checks = {};
|
||||
|
||||
this._cancelBtn = document.getElementById("cancelBtn");
|
||||
|
||||
this._docsHolder = document.getElementById("docsHolder");
|
||||
this._docsThumb = document.getElementById("docsThumb");
|
||||
this._selectedWrapper = document.getElementById("selectedWrapper");
|
||||
this._selectedHolder = document.getElementById("selectedHolder");
|
||||
this._selectedInfo = document.getElementById("selectedInfo");
|
||||
this._selectedCount = document.getElementById("selectedCount");
|
||||
this._selectedThumb = document.getElementById("selectedThumb");
|
||||
|
||||
if (this._selectedHolder && this._selectedThumb) {
|
||||
/** @type {Scroller} */
|
||||
this._selectedScroller = this._initScrollBox(
|
||||
this._selectedHolder,
|
||||
this._selectedThumb
|
||||
);
|
||||
}
|
||||
if (this._docsHolder && this._docsThumb) {
|
||||
/** @type {Scroller} */
|
||||
this._docsScroller = this._initScrollBox(
|
||||
this._docsHolder,
|
||||
this._docsThumb,
|
||||
this._checkDocsScroll.bind(this)
|
||||
);
|
||||
}
|
||||
/** @type {Function[]} */
|
||||
this._subscribers = [];
|
||||
this._displayNoneClass = displayNoneClass;
|
||||
this._fShouldLoadMore = fShouldLoadMore;
|
||||
this._fLoadMore = fLoadMore;
|
||||
/** @type {number} */
|
||||
this._loadTimeout;
|
||||
this._init();
|
||||
}
|
||||
|
||||
SelectCitationsComponent.prototype._init = function () {
|
||||
const self = this;
|
||||
if (this._cancelBtn) {
|
||||
this._cancelBtn.onclick = function (e) {
|
||||
var ids = [];
|
||||
for (var id in self._items) {
|
||||
ids.push(id);
|
||||
}
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
self._removeSelected(ids[i]);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
SelectCitationsComponent.prototype.clearLibrary = function () {
|
||||
var holder = this._docsHolder;
|
||||
while (holder && holder.lastChild) {
|
||||
holder.removeChild(holder.lastChild);
|
||||
}
|
||||
if (holder) holder.scrollTop = 0;
|
||||
this._docsScroller.onscroll();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean} append
|
||||
* @param {SearchResult | null} res
|
||||
* @param {Error | null} err
|
||||
* @param {boolean} showNotFound
|
||||
* @param {boolean} first
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
SelectCitationsComponent.prototype.displaySearchItems = function (
|
||||
append,
|
||||
res,
|
||||
err,
|
||||
showNotFound,
|
||||
first
|
||||
) {
|
||||
const self = this;
|
||||
var holder = this._docsHolder;
|
||||
|
||||
if (!append) {
|
||||
this.clearLibrary();
|
||||
}
|
||||
|
||||
var page = document.createElement("div");
|
||||
if (holder) page.classList.add("page" + holder.children.length);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (res && res.items && res.items.length > 0) {
|
||||
for (let index = 0; index < res.items.length; index++) {
|
||||
let item = res.items[index];
|
||||
page.appendChild(self._buildDocElement(item));
|
||||
}
|
||||
} else if (err || first) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else if (showNotFound) {
|
||||
var notFound = document.createElement("div");
|
||||
notFound.textContent = translate("Nothing found");
|
||||
notFound.classList.add("searchInfo");
|
||||
page.appendChild(notFound);
|
||||
}
|
||||
}
|
||||
if (holder) holder.appendChild(page);
|
||||
|
||||
this._docsScroller.onscroll();
|
||||
resolve(true);
|
||||
});
|
||||
};
|
||||
|
||||
/** @returns {Object<string|number, SearchResultItem>} */
|
||||
SelectCitationsComponent.prototype.getItems = function () {
|
||||
return this._items;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Array<string|number>} keys
|
||||
*/
|
||||
SelectCitationsComponent.prototype.removeItems = function (keys) {
|
||||
const self = this;
|
||||
keys.forEach(function (key) {
|
||||
self._removeSelected(key);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {function(number): void} callback
|
||||
* @returns {Object}
|
||||
*/
|
||||
SelectCitationsComponent.prototype.subscribe = function (callback) {
|
||||
var self = this;
|
||||
this._subscribers.push(callback);
|
||||
|
||||
return {
|
||||
unsubscribe: function () {
|
||||
self._subscribers = self._subscribers.filter(function (cb) {
|
||||
return cb !== callback;
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {SearchResultItem} item
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
SelectCitationsComponent.prototype._buildDocElement = function (item) {
|
||||
const self = this;
|
||||
var root = document.createElement("div");
|
||||
root.classList.add("doc");
|
||||
|
||||
var checkHolder = document.createElement("div");
|
||||
var checkWrapper = document.createElement("div");
|
||||
checkWrapper.classList.add("checkbox");
|
||||
var check = document.createElement("input");
|
||||
check.setAttribute("type", "checkbox");
|
||||
if (this._items[item.id]) {
|
||||
check.checked = true;
|
||||
this._checks[item.id] = check;
|
||||
}
|
||||
checkWrapper.appendChild(check);
|
||||
checkWrapper.appendChild(document.createElement("span"));
|
||||
checkHolder.appendChild(checkWrapper);
|
||||
|
||||
var docInfo = document.createElement("div");
|
||||
docInfo.classList.add("docInfo");
|
||||
|
||||
var title = document.createElement("div");
|
||||
title.textContent = item.title;
|
||||
title.classList.add("truncate-text");
|
||||
docInfo.appendChild(title);
|
||||
|
||||
if (item.author && item.author.length > 0) {
|
||||
var authors = document.createElement("div");
|
||||
authors.textContent = item.author
|
||||
.map(function (a) {
|
||||
return a.family + ", " + a.given;
|
||||
})
|
||||
.join("; ");
|
||||
authors.setAttribute("title", authors.textContent);
|
||||
authors.classList.add("secondary-text");
|
||||
authors.classList.add("truncate-text");
|
||||
authors.classList.add("nowrap");
|
||||
docInfo.appendChild(authors);
|
||||
}
|
||||
|
||||
var source = document.createElement("div");
|
||||
if (item.publisher || item["publisher-place"]) {
|
||||
source.textContent = item.publisher || item["publisher-place"] || "";
|
||||
}
|
||||
if (item.issued && item.issued["date-parts"]) {
|
||||
var date = item.issued["date-parts"][0];
|
||||
if (source.textContent) {
|
||||
source.textContent += " (" + date.join("-") + ")";
|
||||
} else {
|
||||
source.textContent = date.join("-");
|
||||
}
|
||||
}
|
||||
source.setAttribute("title", source.textContent);
|
||||
source.classList.add("secondary-text");
|
||||
source.classList.add("truncate-text");
|
||||
source.classList.add("nowrap");
|
||||
docInfo.appendChild(source);
|
||||
|
||||
root.appendChild(checkHolder);
|
||||
root.appendChild(docInfo);
|
||||
|
||||
/**
|
||||
* @param {HTMLInputElement} input
|
||||
* @param {SearchResultItem} item
|
||||
* @returns
|
||||
*/
|
||||
function selectItem(input, item) {
|
||||
return function () {
|
||||
input.checked = !input.checked;
|
||||
if (input.checked) {
|
||||
self._addSelected(item, input);
|
||||
} else {
|
||||
self._removeSelected(item.id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var f = selectItem(check, item);
|
||||
checkWrapper.onclick = f;
|
||||
docInfo.onclick = f;
|
||||
|
||||
return root;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {SearchResultItem} item
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
SelectCitationsComponent.prototype._buildSelectedElement = function (item) {
|
||||
const self = this;
|
||||
var root = document.createElement("div");
|
||||
root.classList.add("selDoc");
|
||||
|
||||
var name = document.createElement("span");
|
||||
name.textContent = item.title;
|
||||
name.setAttribute("title", item.title);
|
||||
|
||||
var year = document.createElement("span");
|
||||
if (item.issued && item.issued["date-parts"]) {
|
||||
year.textContent = item.issued["date-parts"][0].join("-");
|
||||
}
|
||||
|
||||
var remove = document.createElement("span");
|
||||
remove.onclick = function () {
|
||||
self._removeSelected(item.id);
|
||||
};
|
||||
remove.textContent = "×";
|
||||
|
||||
root.appendChild(name);
|
||||
root.appendChild(year);
|
||||
root.appendChild(remove);
|
||||
|
||||
return root;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {SearchResultItem} item
|
||||
* @param {HTMLInputElement} input
|
||||
*/
|
||||
SelectCitationsComponent.prototype._addSelected = function (item, input) {
|
||||
/** @type {HTMLElement} */
|
||||
var el = this._buildSelectedElement(item);
|
||||
this._items[item.id] = item;
|
||||
this._html[item.id] = el;
|
||||
this._checks[item.id] = input;
|
||||
if (this._selectedHolder) {
|
||||
this._selectedHolder.appendChild(el);
|
||||
}
|
||||
this._docsScroller.onscroll();
|
||||
this._selectedScroller.onscroll();
|
||||
this._checkSelected();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} holder - The element that contains the document list.
|
||||
* @param {HTMLElement} [thumb]
|
||||
*/
|
||||
SelectCitationsComponent.prototype._checkDocsScroll = function (holder, thumb) {
|
||||
const self = this;
|
||||
if (this._fShouldLoadMore(holder)) {
|
||||
if (this._loadTimeout) {
|
||||
clearTimeout(this._loadTimeout);
|
||||
}
|
||||
|
||||
if (
|
||||
!lastSearch.obj &&
|
||||
!lastSearch.text.trim() &&
|
||||
!lastSearch.groups.length
|
||||
)
|
||||
return;
|
||||
|
||||
this._loadTimeout = setTimeout(function () {
|
||||
if (self._fShouldLoadMore(holder)) {
|
||||
self._fLoadMore();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} holder
|
||||
* @param {HTMLElement} thumb
|
||||
* @param {function(HTMLElement): void} [onscroll]
|
||||
* @returns {Scroller}
|
||||
*/
|
||||
SelectCitationsComponent.prototype._initScrollBox = function (
|
||||
holder,
|
||||
thumb,
|
||||
onscroll
|
||||
) {
|
||||
var scroller = {};
|
||||
scroller.onscroll = this._checkScroll(holder, thumb, onscroll);
|
||||
|
||||
holder.onwheel = function (e) {
|
||||
holder.scrollTop +=
|
||||
e.deltaY > 10 || e.deltaY < -10 ? e.deltaY : e.deltaY * 20;
|
||||
scroller.onscroll();
|
||||
};
|
||||
|
||||
thumb.onmousedown = function (e) {
|
||||
thumb.classList.add("scrolling");
|
||||
var y = e.clientY;
|
||||
var initialPos = holder.scrollTop;
|
||||
|
||||
window.onmouseup = function (e) {
|
||||
thumb.classList.remove("scrolling");
|
||||
window.onmouseup = null;
|
||||
window.onmousemove = null;
|
||||
};
|
||||
window.onmousemove = function (e) {
|
||||
var delta = e.clientY - y;
|
||||
|
||||
var percMoved = delta / holder.clientHeight;
|
||||
var deltaScroll = holder.scrollHeight * percMoved;
|
||||
holder.scrollTop = initialPos + deltaScroll;
|
||||
|
||||
scroller.onscroll();
|
||||
};
|
||||
};
|
||||
|
||||
document.body.addEventListener("resize", function () {
|
||||
scroller.onscroll();
|
||||
});
|
||||
|
||||
return scroller;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} holder
|
||||
* @param {HTMLElement} thumb
|
||||
* @param {function} [func] - an optional function to be called with the holder and thumb as arguments.
|
||||
* @returns {function} - a function that checks the scroll state and updates the thumb accordingly.
|
||||
* */
|
||||
SelectCitationsComponent.prototype._checkScroll = function (
|
||||
holder,
|
||||
thumb,
|
||||
func
|
||||
) {
|
||||
const displayNoneClass = this._displayNoneClass;
|
||||
return function () {
|
||||
if (holder.scrollHeight <= holder.clientHeight) {
|
||||
thumb.classList.add(displayNoneClass);
|
||||
} else {
|
||||
thumb.classList.remove(displayNoneClass);
|
||||
var height =
|
||||
(holder.clientHeight / holder.scrollHeight) *
|
||||
holder.clientHeight;
|
||||
height = height < 40 ? 40 : height;
|
||||
thumb.style.height = height + "px";
|
||||
|
||||
var scroll = holder.scrollHeight - holder.clientHeight;
|
||||
var percScrolled = holder.scrollTop / scroll;
|
||||
|
||||
var margin = percScrolled * (holder.clientHeight - height);
|
||||
thumb.style.marginTop = margin + "px";
|
||||
}
|
||||
|
||||
if (func) func(holder, thumb);
|
||||
};
|
||||
};
|
||||
|
||||
/** @param {string|number} id */
|
||||
SelectCitationsComponent.prototype._removeSelected = function (id) {
|
||||
var el = this._html[id];
|
||||
delete this._items[id];
|
||||
delete this._html[id];
|
||||
if (this._checks[id]) {
|
||||
this._checks[id].checked = false;
|
||||
delete this._checks[id];
|
||||
}
|
||||
if (this._selectedHolder) {
|
||||
this._selectedHolder.removeChild(el);
|
||||
}
|
||||
this._docsScroller.onscroll();
|
||||
this._selectedScroller.onscroll();
|
||||
this._checkSelected();
|
||||
};
|
||||
|
||||
SelectCitationsComponent.prototype._checkSelected = function () {
|
||||
const numOfSelected = this._count();
|
||||
if (!this._selectedInfo || !this._selectedCount) {
|
||||
return;
|
||||
}
|
||||
if (numOfSelected <= 0) {
|
||||
this._selectedInfo.classList.add(this._displayNoneClass);
|
||||
} else {
|
||||
this._selectedInfo.classList.remove(this._displayNoneClass);
|
||||
this._selectedCount.textContent =
|
||||
numOfSelected + " " + translate("selected");
|
||||
}
|
||||
this._subscribers.forEach(function (cb) {
|
||||
cb(numOfSelected);
|
||||
});
|
||||
};
|
||||
|
||||
SelectCitationsComponent.prototype._count = function () {
|
||||
var k = 0;
|
||||
for (var i in this._items) k++;
|
||||
return k;
|
||||
};
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "Více informací se dozvíte zde.",
|
||||
"Save": "Uložit",
|
||||
"Insert Citation": "Vložit Citaci",
|
||||
"Cancel select": "Zrušit vybrat",
|
||||
"Cancel selection": "Zrušit vybrat",
|
||||
"Refresh": "Aktualizovat",
|
||||
"Unlink citations": "Odpojit citace",
|
||||
"Please insert some citation into the document.": "Vložte prosím do dokumentu nějakou citaci.",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "Erfahren Sie mehr hier.",
|
||||
"Save": "Speichern",
|
||||
"Insert Citation": "Zitat einfügen",
|
||||
"Cancel select": "Abbrechen auswählen",
|
||||
"Cancel selection": "Abbrechen auswählen",
|
||||
"Refresh": "Aktualisieren",
|
||||
"Unlink citations": "Zitate trennen",
|
||||
"Please insert some citation into the document.": "Bitte fügen Sie ein Zitat in das Dokument ein.",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "Aprenda más aquí.",
|
||||
"Save": "Salvar",
|
||||
"Insert Citation": "Insertar Cita",
|
||||
"Cancel select": "Cancelar seleccionar",
|
||||
"Cancel selection": "Cancelar seleccionar",
|
||||
"Refresh": "Actualizar",
|
||||
"Unlink citations": "Desvincular citas",
|
||||
"Please insert some citation into the document.": "Por favor, inserte alguna cita en el documento",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "En savoir plus ici.",
|
||||
"Save": "Sauvegarder",
|
||||
"Insert Citation": "Insérer une Citation",
|
||||
"Cancel select": "Annuler sélectionner",
|
||||
"Cancel selection": "Annuler sélectionner",
|
||||
"Refresh": "Rafraîchir",
|
||||
"Unlink citations": "Dissocier les citations",
|
||||
"Please insert some citation into the document.": "Veuillez insérer une citation dans le document.",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "Ulteriori informazioni qui.",
|
||||
"Save": "Salva",
|
||||
"Insert Citation": "Inserisci citazione",
|
||||
"Cancel select": "Annulla seleziona",
|
||||
"Cancel selection": "Annulla seleziona",
|
||||
"Refresh": "Aggiornare",
|
||||
"Unlink citations": "Disconnetti citazioni",
|
||||
"Please insert some citation into the document.": "Si prega di inserire qualche citazione nel documento.",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "詳しくはこちら",
|
||||
"Save": "保存",
|
||||
"Insert Citation": "引用を挿入",
|
||||
"Cancel select": "キャンセル選択",
|
||||
"Cancel selection": "キャンセル選択",
|
||||
"Refresh": "リフレッシュ",
|
||||
"Unlink citations": "引用を外す",
|
||||
"Please insert some citation into the document.": "文書にいくつかの引用を挿入してください。",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "Lees hier meer.",
|
||||
"Save": "Opslaan",
|
||||
"Insert Citation": "Citaat Invoegen",
|
||||
"Cancel select": "Annuleren selecteren",
|
||||
"Cancel selection": "Annuleren selecteren",
|
||||
"Refresh": "Vernieuwen",
|
||||
"Unlink citations": "Citaat Verwijderen",
|
||||
"Please insert some citation into the document.": "Voeg een citaat toe aan het document.",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "Saiba mais aqui.",
|
||||
"Save": "Salvar",
|
||||
"Insert Citation": "Inserir citação",
|
||||
"Cancel select": "Cancelar seleção",
|
||||
"Cancel selection": "Cancelar seleção",
|
||||
"Refresh": "Atualizar",
|
||||
"Unlink citations": "Desligar citações",
|
||||
"Please insert some citation into the document.": "Por favor insira alguma citação no documento.",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "Saiba mais aqui.",
|
||||
"Save": "Salve",
|
||||
"Insert Citation": "Inserir Citação",
|
||||
"Cancel select": "Cancelar seleccionar",
|
||||
"Cancel selection": "Cancelar seleccionar",
|
||||
"Refresh": "Actualizar",
|
||||
"Unlink citations": "Desligar citações",
|
||||
"Please insert some citation into the document.": "Por favor, insira alguma citação no documento.",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "Подробности здесь.",
|
||||
"Save": "Сохранить",
|
||||
"Insert Citation": "Вставить цитату",
|
||||
"Cancel select": "Отменить выделение",
|
||||
"Cancel selection": "Отменить выделение",
|
||||
"Refresh": "Обновить",
|
||||
"Unlink citations": "Открепить цитаты",
|
||||
"Please insert some citation into the document.": "Пожалуйста, вставьте цитату в документ.",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "Mësoni më shumë këtu",
|
||||
"Save": "Ruani",
|
||||
"Insert Citation": "Shtoni citimin",
|
||||
"Cancel select": "Anuloni selektimin",
|
||||
"Cancel selection": "Anuloni selektimin",
|
||||
"Refresh": "Rifreskoni",
|
||||
"Unlink citations": "Hiqni citimet",
|
||||
"Please insert some citation into the document.": "Ju lutem shtoni ndonjë citim në dokument",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "Сазнајте више овде.",
|
||||
"Save": "Сачувај",
|
||||
"Insert Citation": "Убаци цитат",
|
||||
"Cancel select": "Откажи избор",
|
||||
"Cancel selection": "Откажи избор",
|
||||
"Refresh": "Освежи",
|
||||
"Unlink citations": "Откажи цитате",
|
||||
"Please insert some citation into the document.": "Молимо убаците неки цитат у документ.",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "Saznajte više ovde.",
|
||||
"Save": "Sačuvaj",
|
||||
"Insert Citation": "Ubaci citat",
|
||||
"Cancel select": "Otkaži izbor",
|
||||
"Cancel selection": "Otkaži izbor",
|
||||
"Refresh": "Osveži",
|
||||
"Unlink citations": "Otkazi citat",
|
||||
"Please insert some citation into the document.": "Molimo ubacite neki citat u dokument.",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"Learn more here.": "点击此处了解更多信息。",
|
||||
"Save": "保存",
|
||||
"Insert Citation": "插入引用",
|
||||
"Cancel select": "取消选择",
|
||||
"Cancel selection": "取消选择",
|
||||
"Refresh": "刷新",
|
||||
"Unlink citations": "取消引用",
|
||||
"Please insert some citation into the document.": "请插入一些引用到文档中。",
|
||||
|
||||
Reference in New Issue
Block a user