Files
onlyoffice.github.io/sdkjs-plugins/content/zotero/scripts/code.js

1342 lines
48 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
*
* (c) Copyright Ascensio System SIA 2020
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
(function () {
var counter = 0; // счетчик отправленных запросов (используется чтобы знать показывать "not found" или нет)
var displayNoneClass = "display-none";
var blurClass = "blur";
var formatter = null;
var cslItems = {count: 0};
var citPrefix = "ZOTERO_CITATION ";
var bibPrefix = "ZOTERO_BIBLIOGRAPHY";
var repeatTimeout = null;
var loadTimeout = null;
var loadingStyle = false;
var loadingLocale = false;
var bNumFormat = false;
let bibPlaceholder = "Please insert some citation into the document.";
var bUserItemsUpdated = false;
var bGroupsItemsUpdated = false;
// TODO добавить варианты сохранения для совместимости с другими редакторами
// (ms, libre, google, мой офис), пока есть вариант сохранить как текст
// TODO добавить ещё обработку событий (удаление линков) их не нужно удалять
// из библиографии автоматически (это делать только при обновлении библиографии
// или refresh), но их точно нужно удалить из formatter!
// TODO добавить ещё обработку события (изменения линков), предлать пользователю
// обновить их или сохранить ручное форматирование (при ручном форматировании
// не меняем внешний вид цитаты при refresh (да и вообще не меняем))
// TODO сейчас всегда делаем полный refresh при каждом действии
// (обновлении, вставке линков, вставке библиографии), потому что мы не знаем
// что поменялось без событий (потом добавить ещё сравнение контента)
// TODO ms меняет линки (если стиль с нумерацией bNumFormat) делает их по порядку
// как документе (для этого нужно знать где именно в документе мы вставляем цитату,
// какая цитата сверху и снизу от текущего курсора)
var selected = {
items: {},
html: {},
checks: {},
count: function () {
var k = 0;
for (var i in selected.items) k++;
return k;
}
};
var locales = {};
var selectedLocale;
var selectedStyle;
var sdk = null;
var cslStylesManager = null;
var lastSearch = {
text: "",
obj: null,
groups: []
};
var elements = {
loader: document.getElementById("loader"),
libLoader: document.getElementById("libLoader"),
error: document.getElementById("errorWrapper"),
contentHolder: document.getElementById("content"),
docsHolder: document.getElementById("docsHolder"),
docsThumb: document.getElementById("docsThumb"),
configState: document.getElementById("configState"),
apiKeyConfigField: document.getElementById("apiKeyField"),
saveConfigBtn: document.getElementById("saveConfigBtn"),
mainState: document.getElementById("mainState"),
logoutLink: document.getElementById("logoutLink"),
selectedWrapper: document.getElementById("selectedWrapper"),
selectedHolder: document.getElementById("selectedHolder"),
selectedThumb: document.getElementById("selectedThumb"),
buttonsWrapper: document.getElementById("buttonsWrapper"),
searchLabel: document.getElementById("searchLabel"),
searchClear: document.getElementById("searchClear"),
searchField: document.getElementById("searchField"),
styleWrapper: document.getElementById("styleWrapper"),
styleSelectList: document.getElementById("styleSelectList"),
styleSelectListOther: document.getElementById("styleSelectedListOther"),
styleSelect: document.getElementById("styleSelect"),
styleLang: document.getElementById("styleLang"),
notesStyleWrapper: document.querySelector(".notesStyle"),
notesStyleRadios: document.querySelectorAll(".notesStyle input[name=notesAs]"),
insertBibBtn: document.getElementById("insertBibBtn"),
insertLinkBtn: document.getElementById("insertLinkBtn"),
cancelBtn: document.getElementById("cancelBtn"),
tempDiv: document.getElementById('div_temp'),
refreshBtn: document.getElementById('refreshBtn'),
saveAsTextBtn: document.getElementById('saveAsTextBtn'),
synchronizeBtn: document.getElementById('synchronizeBtn'),
checkOmitAuthor: document.getElementById('omitAuthor'),
useDesktopApp: document.getElementById("useDesktopApp"),
cslFileInput: document.getElementById("cslFileInput"),
};
var selectedScroller;
var docsScroller;
window.Asc.plugin.init = function () {
showLoader(true);
sdk = window.Asc.plugin.zotero.api({});
cslStylesManager = new CslStylesManager();
addEventListeners();
initSdkApis().then(function (availableApis) {
showLoader(true);
loadStyles();
updateCslItems(true, false, false, false);
addStylesEventListeners();
initSelectBoxes();
});
window.Asc.plugin.onTranslate = applyTranslations;
selectedScroller = initScrollBox(elements.selectedHolder, elements.selectedThumb);
docsScroller = initScrollBox(elements.docsHolder, elements.docsThumb, checkDocsScroll);
};
function initSdkApis() {
return new Promise(function (resolve) {
let hasFirstAnswer = false;
let onlineZoteroElements =
document.querySelectorAll(".for-zotero-online");
ZoteroApiChecker.runApisChecker(sdk).subscribe(function (
/** @type {AvailableApis} */ apis
) {
cslStylesManager.setDesktopApiAvailable(apis.desktop);
cslStylesManager.setRestApiAvailable(apis.online);
if (!hasFirstAnswer) {
console.log("apis", apis);
hasFirstAnswer = true;
if (!apis.desktopVersion) {
elements.useDesktopApp.classList.add("display-none");
}
showLoader(false);
switchAuthState("config");
}
if (apis.online) {
onlineZoteroElements.forEach(function (element) {
element.classList.remove("display-none");
});
} else {
onlineZoteroElements.forEach(function (element) {
element.classList.add("display-none");
});
}
if (apis.online && apis.hasKey) {
window.Asc.plugin.zotero.isOnlineAvailable = true;
switchAuthState("main");
resolve(apis);
return;
} else if (apis.desktop && apis.hasPermission) {
window.Asc.plugin.zotero.isOnlineAvailable = false;
elements.logoutLink.style.display = "none";
switchAuthState("main");
showError(false);
resolve(apis);
return;
}
});
});
}
function loadStyles() {
cslStylesManager.getStylesInfo()
.then(function (stylesInfo) {
var openOtherStyleList = function (list) {
return function (ev) {
elements.styleSelectListOther.style.width = (elements.styleWrapper.clientWidth - 2) + "px";
ev.stopPropagation();
openList(list);
}
};
addStylesToList(stylesInfo);
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);
});
}
/**
* @param {object} stylesInfo
*/
function addStylesToList(stylesInfo) {
var found = false;
var lastStyle = cslStylesManager.getLastUsedStyle();
var onStyleSelectOther = function (list, other) {
return function (ev) {
var tmpEl = list.removeChild(list.children[list.children.length - 3]);
var newEl = document.createElement("span");
newEl.setAttribute("data-value", tmpEl.getAttribute("data-value"));
newEl.textContent = tmpEl.textContent;
other.appendChild(newEl);
newEl.onclick = onStyleSelectOther(elements.styleSelectList, elements.styleSelectListOther);
tmpEl = other.removeChild(ev.target);
newEl = document.createElement("span");
newEl.setAttribute("data-value", tmpEl.getAttribute("data-value"));
newEl.textContent = tmpEl.textContent;
list.insertBefore(newEl, list.firstElementChild);
newEl.onclick = onClickListElement(elements.styleSelectList, elements.styleSelect);
var event = new Event("click");
newEl.dispatchEvent(event);
openList(null);
}
};
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, elements.styleSelect);
} else {
elements.styleSelectListOther.appendChild(el);
el.onclick = onStyleSelectOther(elements.styleSelectList, elements.styleSelectListOther);
}
if (stylesInfo[i].name == lastStyle) {
el.setAttribute("selected", "");
selectInput(elements.styleSelect, el, elements.styleSelectList, false);
}
}
}
function addEventListeners() {
elements.cslFileInput.onchange = function (e) {
var file = e.target.files[0];
if (!file) return;
//showLoader(true);
cslStylesManager.addCustomStyle(file).then(function (styleValue) {
addStylesToList([styleValue]);
}).catch(function (error) {
console.error(error);
showError(getMessage("Failed to upload file"));
}).finally(function () {
showLoader(false);
});
};
elements.useDesktopApp.onclick = function () {
ZoteroApiChecker.checkStatus(sdk).then(function (/** @type {AvailableApis} */ apis) {
if (apis.desktop && apis.hasPermission) {
window.Asc.plugin.zotero.isOnlineAvailable = false;
elements.logoutLink.style.display = "none";
switchAuthState("main");
showError(false);
} else if (apis.desktop && !apis.hasPermission) {
const errorMessage =
"Connection to Zotero failed. " +
"Please enable external connections in Zotero: " +
'Edit → Settings → Advanced → Check "Allow other ' +
'applications on this computer to communicate with Zotero"';
showError(getMessage(errorMessage));
} else if (!apis.desktop) {
showError(
getMessage(
"Connection to Zotero failed. Make sure Zotero is running."
)
);
}
});
};
elements.logoutLink.onclick = function (e) {
sdk.clearSettings();
switchAuthState('config');
return true;
};
function searchFor(text) {
if (elements.mainState.classList.contains(displayNoneClass)) return;
text = text.trim();
if (!text) return;
if (text == lastSearch.text) return;
lastSearch.text = text;
lastSearch.obj = null;
lastSearch.groups = [];
clearLibrary();
var groups = sdk.getUserGroups();
loadLibrary(sdk.getItems(text), true, true, !groups.length, false, true);
if (groups.length) {
for (var i = 0; i < groups.length; i++) {
loadLibrary(sdk.groups(lastSearch.text, groups[i]), true, false, (i == groups.length -1), true, true );
}
}
};
elements.searchField.onkeypress = function (e) {
if (e.keyCode == 13) searchFor(e.target.value);
};
elements.searchField.onblur = function (e) {
setTimeout(function () { searchFor(e.target.value); }, 500);
};
elements.searchField.onkeyup = function (e) {
switchClass(elements.searchClear, displayNoneClass, !e.target.value);
};
elements.searchClear.onclick = function (e) {
if (e.target.classList.contains(displayNoneClass)) return true;
switchClass(elements.searchClear, displayNoneClass, true);
elements.searchField.value = "";
lastSearch.text = "";
clearLibrary();
return true;
};
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.saveConfigBtn.onclick = function (e) {
var apikey = elements.apiKeyConfigField.value.trim();
if (apikey) {
sdk.setApiKey(apikey)
.then(function () {
ZoteroApiChecker.stopApisChecker();
switchAuthState("main");
})
.catch(function (err) {
console.error(err);
showError(getMessage("Invalid API key"));
});
}
};
elements.refreshBtn.onclick = function() {
showLoader(true);
updateCslItems(true, true, false, false);
};
elements.synchronizeBtn.onclick = function() {
synchronizeData();
};
elements.insertBibBtn.onclick = function() {
showLoader(true);
// TODO #there
// updateCslItems(true, false, true, false);
updateCslItems(true, true, true, false);
};
elements.insertLinkBtn.onclick = function() {
showLoader(true);
updateCslItems(true, false, false, true);
};
elements.saveAsTextBtn.onclick = function() {
showLoader(true);
citationDocService.saveAsText();
}
}
function addStylesEventListeners() {
elements.styleSelect.oninput = function (e, filter) {
var input = elements.styleSelect;
var 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++) {
var text = list.children[i].textContent || list.children[i].innerText;
var hide = true;
if (!filter || text.toLowerCase().indexOf(filter) > -1) {
hide = false;
}
switchClass(list.children[i], displayNoneClass, hide);
}
};
elements.styleSelect.onselectchange = function (inp, val, isClick) {
showLoader(true);
getStyle(val)
.then(function(style) {
selectedStyle = val;
onStyleChange();
if (isClick)
updateCslItems(true, true, false, false);
})
.catch(function (err) {
console.error(err);
})
.finally(function() {
if (
locales[selectedLocale] &&
cslStylesManager.cached(selectedStyle)
)
showLoader(false);
});
elements.styleSelect.oninput(null, '');
};
elements.styleLang.onselectchange = function (inp, val, isClick) {
showLoader(true);
saveLanguage(val);
getLocale(val)
.then(function() {
if (isClick)
updateCslItems(true, true, false, false);
})
.catch(function (error) {
console.error(error);
})
.finally(function() {
if (locales[selectedLocale] && cslStylesManager.cached(selectedStyle)) showLoader(false);
});
selectedLocale = val;
};
elements.styleSelectList.onopen = function () {
elements.styleSelectList.style.width = (elements.styleWrapper.clientWidth - 2) + "px";
}
elements.notesStyleRadios.forEach(radio => {
radio.addEventListener('change', function(event) {
if (event.target.checked) {
cslStylesManager.saveLastUsedNotesStyle(event.target.value);
}
});
});
}
window.Asc.plugin.onThemeChanged = function(theme)
{
window.Asc.plugin.onThemeChangedBase(theme);
var rules = '.selectArrow > span { background-color: ' + window.Asc.plugin.theme['text-normal'] + '}\n';
rules += '.link { color : ' + window.Asc.plugin.theme['text-normal'] + ';}\n';
rules += '.control.select { background-color : ' + window.Asc.plugin.theme['background-normal'] + ';}\n';
rules += '.control { color : ' + window.Asc.plugin.theme['text-normal'] + '; border-color : ' + window.Asc.plugin.theme['border-regular-control'] + '}\n';
rules += '.selectList > span { background-color: ' + window.Asc.plugin.theme['background-normal'] + '; ';
rules += 'color : ' + window.Asc.plugin.theme['text-normal'] + '; }\n';
rules += '.selectList > span:hover { background-color : ' + window.Asc.plugin.theme['highlight-button-hover'] + '; color : ' + window.Asc.plugin.theme['text-normal'] + '}\n';
rules += '.selectList > span[selected=""] { background-color : ' + window.Asc.plugin.theme['highlight-button-pressed'] + ';' + '; color : ' + window.Asc.plugin.theme['text-normal'] + '}';
var styleTheme = document.createElement('style');
styleTheme.type = 'text/css';
styleTheme.innerHTML = rules;
document.getElementsByTagName('head')[0].appendChild(styleTheme);
};
var scrollBoxes = [];
function initScrollBox(holder, thumb, onscroll) {
var scroller = {};
scroller.onscroll = 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) {
switchClass(thumb, "scrolling", true);
var y = e.clientY;
var initialPos = holder.scrollTop;
window.onmouseup = function (e) {
switchClass(thumb, "scrolling", false);
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();
}
}
scrollBoxes.push(scroller);
document.body.onresize = function () {
for (var i = 0; i < scrollBoxes.length; i++) {
scrollBoxes[i].onscroll();
}
}
return scroller;
}
var selectLists = [];
function initSelectBoxes() {
var select = document.getElementsByClassName("control select");
var savedLang = restoreLanguage();
for (var i = 0; i < select.length; i++) {
var input = select[i];
var holder = input.parentElement;
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) {
var def = false;
for (var j = 0; j < list.children.length; j++) {
if (list.children[j].getAttribute("data-value") == savedLang) {
list.children[j].setAttribute("selected", "");
selectInput(input, list.children[j], list, false);
def = true;
}
list.children[j].onclick = onClickListElement(list, input);
}
if (!def) {
selectInput(input, list.children[0], list, false);
}
}
var f = function (list, input) {
return function (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 = f(list, input);
arrow.onclick = f(list, input);
}
selectLists.push(list);
}
}
window.onclick = function () {
openList(null);
}
};
function openList(el) {
for (var i = 0; i < selectLists.length; i++) {
var close = true;
if (selectLists[i] === el) {
close = false;
}
if (close && selectLists[i] === elements.styleSelectList)
elements.styleSelect.oninput(null, '');
switchClass(selectLists[i], displayNoneClass, close);
}
};
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);
};
function onClickListElement(list, input) {
return function (ev) {
var sel = ev.target.getAttribute("data-value");
for (var i = 0; i < list.children.length; i++) {
if (list.children[i].getAttribute("data-value") == sel) {
list.children[i].setAttribute("selected", "");
selectInput(input, list.children[i], list, true);
} else {
if (list.children[i].hasAttribute("selected")) {
list.children[i].attributes.removeNamedItem("selected");
}
}
}
};
};
function onStyleChange() {
let styleFormat = cslStylesManager.getLastUsedFormat();
bNumFormat = styleFormat == 'numeric';
if (["note", "note-ibid"].indexOf(styleFormat) !== -1) {
// elements.notesStyleWrapper.classList.remove(displayNoneClass);
} else {
// elements.notesStyleWrapper.classList.add(displayNoneClass);
}
let notesStyle = cslStylesManager.getLastUsedNotesStyle();
elements.notesStyleWrapper.querySelector('input[name="notesAs"][value="' + notesStyle + '"]').checked = true;
}
function applyTranslations() {
var elements = document.getElementsByClassName("i18n");
for (var i = 0; i < elements.length; i++) {
var el = elements[i];
if (el.attributes["placeholder"]) el.attributes["placeholder"].value = getMessage(el.attributes["placeholder"].value);
if (el.attributes["title"]) el.attributes["title"].value = getMessage(el.attributes["title"].value);
if (el.innerText) el.innerText = getMessage(el.innerText);
}
};
function getMessage(key) {
return window.Asc.plugin.tr(key);
};
function saveLanguage(id) {
localStorage.setItem("zoteroLang", id);
};
function restoreLanguage() {
return localStorage.getItem("zoteroLang") || "en-US";
};
function showError(message) {
if (message) {
switchClass(elements.error, displayNoneClass, false);
elements.error.textContent = message;
setTimeout(function () { window.onclick = function () { showError(); }; }, 100);
} else {
switchClass(elements.error, displayNoneClass, true);
elements.error.textContent = "";
window.onclick = null;
}
};
function showLoader(show) {
switchClass(elements.loader, displayNoneClass, !show);
switchClass(elements.contentHolder, blurClass, show);
};
function showLibLoader(show) {
switchClass(elements.libLoader, displayNoneClass, !show);
};
function switchClass(el, className, add) {
if (add) {
el.classList.add(className);
} else {
el.classList.remove(className);
}
};
function configState(hide) {
switchClass(elements.configState, displayNoneClass, hide);
};
function mainState(hide) {
switchClass(elements.mainState, displayNoneClass, hide);
switchClass(elements.logoutLink, displayNoneClass, hide);
};
var currentAuthState;
function switchAuthState(state) {
currentAuthState = state;
configState(true);
mainState(true);
switch (state) {
case 'config':
configState(false);
break;
case 'main':
mainState(false);
break;
}
};
function checkScroll(holder, thumb, func) {
return function () {
if (holder.scrollHeight <= holder.clientHeight) {
switchClass(thumb, displayNoneClass, true);
} else {
switchClass(thumb, displayNoneClass, false);
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);
}
};
function checkDocsScroll(holder) {
if (shouldLoadMore(holder)) {
if (loadTimeout) {
clearTimeout(loadTimeout);
}
if (!lastSearch.obj && !lastSearch.text.trim() && !lastSearch.groups.length) return;
loadTimeout = setTimeout(function () {
if (shouldLoadMore(holder)) {
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.groups(lastSearch.groups[i].next()), true, false, ( i == (lastSearch.groups.length -1) ), true, false, lastSearch.groups[i] );
}
} else {
}
}, 500);
}
};
function shouldLoadMore(holder) {
if (currentAuthState != "main") return false;
if (holder.scrollTop + holder.clientHeight < holder.scrollHeight) return false;
var flag = true;
lastSearch.groups.forEach(function(el) {
if (el.next) flag = false;
});
if (!lastSearch.obj || !lastSearch.obj.next || !flag) return false;
return true;
};
function clearLibrary() {
var holder = elements.docsHolder;
while (holder.lastChild) {
holder.removeChild(holder.lastChild);
}
holder.scrollTop = 0;
docsScroller.onscroll();
};
function loadLibrary(promise, append, showLoader, hideLoader, isGroup, bCount) {
if (showLoader) showLibLoader(true);
if (bCount) counter++;
promise
.then(function (res) {
if (bCount) counter--;
displaySearchItems(append, res, null, hideLoader, isGroup, (bCount && !counter) );
})
.catch(function (err) {
if (bCount) counter--;
console.error(err);
displaySearchItems(append, {}, err.message, hideLoader, isGroup, (bCount && !counter) );
})
.finally(function () {
if (hideLoader) {
showLibLoader(false);
}
});
};
function getStyle(styleName) {
return new Promise(function (res, rej) {
loadingStyle = true;
cslStylesManager.getStyle(styleName)
.then(function (text) {
res(text); loadingStyle = false;
})
.catch(function (err) { rej(err); loadingStyle = false; });
});
};
function getLocale(langTag) {
return new Promise(function (res, rej) {
if (locales[langTag] != null) {
res(locales[langTag]);
} else {
loadingLocale = true;
sdk.getLocale(langTag)
.then(function (text) { locales[langTag] = text; res(text); loadingLocale = false; })
.catch(function (err) { rej(err); loadingLocale = false; });
}
});
};
function displaySearchItems(append, res, err, hideLoader, isGroup, showNotFound) {
var holder = elements.docsHolder;
if (!append) {
clearLibrary();
}
var first = false;
if (!lastSearch.obj && (res && res.items.items && !res.items.items.length) ) first = true;
if (err) {
if (first) {
lastSearch.obj = null;
lastSearch.groups = [];
}
lastSearch.obj.next = null;
} else {
if (isGroup && res && res.next)
lastSearch.groups.push(res);
else
lastSearch.obj = (res && res.items.items.length ? res : null);
}
var page = document.createElement("div");
page.classList.add("page" + holder.children.length);
if (res && res.items.items.length > 0) {
for (let index = 0; index < res.items.items.length; index++) {
let item = res.items.items[index];
item[ (isGroup ? "groupID" : "userID") ] = res.id;
var pos = item.id.indexOf("/") + 1;
if (pos)
item.id = item.id.substring(pos)
page.appendChild(buildDocElement(item));
}
} else if (err || first) {
if (err) {
showError(err);
} else if (showNotFound) {
var notFound = document.createElement("div");
notFound.textContent = getMessage("Nothing found");
notFound.classList.add("searchInfo");
page.appendChild(notFound);
}
}
holder.appendChild(page);
docsScroller.onscroll();
};
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);
function selectItem(input, item) {
return function (e) {
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;
};
function addSelected(item, input) {
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();
};
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();
};
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) {
elements.insertLinkBtn.setAttribute('disabled', '');
elements.cancelBtn.setAttribute('disabled', '');
} else {
elements.insertLinkBtn.removeAttribute('disabled', '');
elements.cancelBtn.removeAttribute('disabled', '');
}
};
function updateAllOrAddBib(bUpadteAll, bPastBib, bSyncronize) {
if (!selectedStyle) {
showError(getMessage("Style is not selected"));
return;
}
if (!selectedLocale) {
showError(getMessage("Language is not selected"));
return;
}
window.Asc.plugin.executeMethod("GetAllAddinFields", null, function(arrFields) {
if (arrFields.length) {
var updatedFields = [];
var bibField = null;
elements.tempDiv.innerHTML = formatter.makeBibliography()[1].join('');
var bibliography = elements.tempDiv.innerText;
arrFields.forEach(function(field) {
if (bUpadteAll && ( field.Value.indexOf(citPrefix) !== -1 ) ) {
var citationItems = JSON.parse(field.Value.slice(citPrefix.length)).citationItems;
var keysL = [];
citationItems = citationItems.map(function(item) {
keysL.push({id:item.id, "suppress-author":item["suppress-author"]});
return cslItems[item.id];
});
elements.tempDiv.innerHTML = formatter.makeCitationCluster(keysL);
field["Content"] = elements.tempDiv.innerText;
if (bSyncronize) {
// if we make synchronization we must update value too
field['Value'] = citPrefix + JSON.stringify( { citationItems: citationItems } );
}
updatedFields.push(field);
} else if (field.Value.indexOf(bibPrefix) !== -1) {
bibField = field;
}
});
if (bibField) {
bibField["Content"] = bibliography;
updatedFields.push(bibField);
} else if (bPastBib) {
bibField = {
"Value" : bibPrefix,
"Content" : bibliography
};
window.Asc.plugin.executeMethod("AddAddinField", [bibField], function() {
if (!updatedFields.length) {
showLoader(false);
}
});
}
if (updatedFields.length) {
window.Asc.plugin.executeMethod("UpdateAddinFields", [updatedFields], function() {
showLoader(false);
});
}
} else {
showLoader(false);
}
});
};
function updateFormatter(bUpadteAll, bPastBib, bPastLink, bSyncronize) {
clearTimeout(repeatTimeout);
if (loadingStyle || loadingLocale || !cslStylesManager.cached(selectedStyle) || !locales[selectedLocale]) {
repeatTimeout = setTimeout( function() {
updateFormatter(bUpadteAll, bPastBib, bPastLink, bSyncronize);
}, 100);
return;
}
var arrItems = [];
for (var item in cslItems) {
if (item !== "count")
arrItems.push(item);
}
formatter = new CSL.Engine({ retrieveLocale: function (id) { return locales[id]; }, retrieveItem: function (id) { return cslItems[id]; } }, cslStylesManager.cached(selectedStyle), selectedLocale, true);
if (arrItems.length) {
formatter.updateItems(arrItems);
}
if (bUpadteAll || bPastBib)
updateAllOrAddBib(bUpadteAll, bPastBib, bSyncronize);
if (bPastLink) {
formatInsertLink();
}
};
function updateCslItems(bUpdadeFormatter, bUpadteAll, bPastBib, bPastLink) {
cslItems = {count: 0};
window.Asc.plugin.executeMethod("GetAllAddinFields", null, function(arrFields) {
if (arrFields.length) {
var arrItems = [];
var tmpObj = {};
var bibField = null;
arrFields.forEach(function(field) {
if (field.Value.indexOf(citPrefix) !== -1) {
var citationItems = JSON.parse(field.Value.slice(citPrefix.length)).citationItems;
citationItems.forEach(function(item) {
if (!tmpObj[item.id]) {
tmpObj[item.id] = 1;
arrItems.push(item);
}
});
} else if(field.Value.indexOf(bibPrefix) !== -1) {
bibField = field;
}
});
if (arrItems.length) {
arrItems.sort( function(itemA, itemB) { return (itemA.index > itemB.index ? 1 : -1) } );
arrItems.forEach(function(item) {
item.index = ++cslItems.count;
cslItems[item.id] = item;
});
} else if (bUpdadeFormatter && bibField && bUpadteAll) {
// нет смысла ещё раз искать поле библиографии
bUpdadeFormatter = false;
bibField["Content"] = getMessage(bibPlaceholder);
window.Asc.plugin.executeMethod("UpdateAddinFields", [[bibField]], function() {
showLoader(false);
});
}
} else if (bUpdadeFormatter && bPastBib) {
bibField = {
"Value" : bibPrefix,
"Content" : getMessage(bibPlaceholder)
};
window.Asc.plugin.executeMethod("AddAddinField", [bibField], function() {
showLoader(false);
});
}
if (bUpdadeFormatter)
updateFormatter(bUpadteAll, bPastBib, bPastLink, false);
});
};
function formatInsertLink() {
if (!selectedStyle) {
showError(getMessage("Style is not selected"));
return;
}
if (!selectedLocale) {
showError(getMessage("Language is not selected"));
return;
}
var bUpdateItems = false;
var keys = [];
var keysL = [];
for (var item in selected.items) {
if (!cslItems[item]) {
cslItems.count++;
cslItems[item] = convertToCSL(selected.items[item]);
bUpdateItems = true;
}
keys.push(item);
keysL.push({id:item, "suppress-author": cslItems[item]["suppress-author"]});
}
try {
if (bUpdateItems) {
var arrItems = [];
for (var item in cslItems) {
if (item !== "count")
arrItems.push(item);
}
formatter.updateItems(arrItems);
}
var obj = {
citationItems : []
};
keys.forEach(function(element) {
removeSelected(element);
obj.citationItems.push(cslItems[element]);
});
// TODO может ещё очистить поиск (подумать над этим)
elements.tempDiv.innerHTML = formatter.makeCitationCluster(keysL);
var field = {
"Value" : citPrefix + JSON.stringify(obj),
"Content" : elements.tempDiv.innerText
};
window.Asc.plugin.executeMethod("AddAddinField", [field], function() {
showLoader(false);
// TODO есть проблема, что в плагине мы индексы обновили, а вот в документе нет (по идее надо обновить и индексы в документе перед вставкой)
// но тогда у нас уедет селект и новое поле вставится не там, поэтому пока обновлять приходитсяя в конце
// такая же проблем с вставкой библиографии (при обнолении индексов в плагине надо бы их обновлять и в документе тоже)
updateCslItems(true, true, false, false);
});
} catch (e) {
showError(e);
console.error(e);
}
};
function convertToCSL(item) {
var cslData = item;
cslData.index = cslItems.count;
cslData["short-title"] = item.shortTitle;
cslData["title-short"] = item.shortTitle;
cslData["suppress-author"] = elements.checkOmitAuthor.checked;
return cslData;
};
function syncronizeCSLItem(item) {
var pos = item.id.indexOf("/") + 1;
if (pos)
item.id = item.id.substring(pos);
var cslItem = cslItems[item.id];
item["short-title"] = item.shortTitle;
item["title-short"] = item.shortTitle;
for (var key in item) {
if (Object.hasOwnProperty.call(item, key)) {
cslItem[key] = item[key];
}
}
};
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 ) ) {
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)
if (!cslItems.count)
return;
showLoader(true);
bGroupsItemsUpdated = false;
bUserItemsUpdated = false;
var bHasGroupsItems = false;
var arrUsrItems = [];
var arrGroupsItems = {};
for (var id in cslItems) {
if (Object.hasOwnProperty.call(cslItems, id)) {
if (id !== 'count') {
var item = cslItems[id];
if (item.userID) {
arrUsrItems.push(item.id)
} else if (item.groupID) {
if (!arrGroupsItems[item.groupID])
arrGroupsItems[item.groupID] = [];
arrGroupsItems[item.groupID].push(item.id);
}
}
}
}
if (arrUsrItems.length) {
sdk.getItems(null, arrUsrItems)
.then(function(res) {
var items = ( (res.items ? res.items.items : []) || [] );
items.forEach(function(item) {
syncronizeCSLItem(item);
});
})
.catch(function(err) {
console.error(err);
})
.finally(function() {
bUserItemsUpdated = true;
if (bGroupsItemsUpdated)
updateAfterSync();
});
} else {
bUserItemsUpdated = true;
}
for (var groupID in arrGroupsItems) {
if (Object.hasOwnProperty.call(arrGroupsItems, groupID)) {
bHasGroupsItems = true;
sdk.groups(null, groupID, arrGroupsItems[groupID])
.then(function(res) {
var items = ( (res.items ? res.items.items : []) || [] );
items.forEach(function(item) {
syncronizeCSLItem(item);
})
})
.catch(function(err) {
console.error(err);
})
.finally(function() {
bGroupsItemsUpdated = true;
if (bUserItemsUpdated)
updateAfterSync();
});
}
}
if (!bHasGroupsItems) {
bGroupsItemsUpdated = true;
}
if (bGroupsItemsUpdated && bUserItemsUpdated)
updateAfterSync();
};
function updateAfterSync() {
// 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)
updateFormatter(true, false, false, true);
};
})();