Files
onlyoffice.github.io/sdkjs-plugins/content/zotero/scripts/code.js
2025-10-08 15:52:45 +03:00

1325 lines
49 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 citPrefixNew = "ZOTERO_ITEM";
var citSuffixNew = "CSL_CITATION";
var citPrefix = "ZOTERO_CITATION";
var bibPrefixNew = "ZOTERO_BIBL";
var bibSuffixNew = "CSL_BIBLIOGRAPHY";
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 styles = {};
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"),
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'),
fileInput: document.getElementById('cslFileInput')
};
var selectedScroller;
var docsScroller;
window.Asc.plugin.init = function () {
showLoader(true);
setTimeout(function () { searchField.focus(); },100);
updateCslItems(true, false, false, false);
sdk = window.Asc.plugin.zotero.api({});
initSdkApis().then(function (availableApis) {
cslStylesManager = new CslStylesManager(availableApis.online, availableApis.desktop);
if (availableApis.online || availableApis.desktop) {
loadStyles();
}
});
window.Asc.plugin.onTranslate = applyTranslations;
addEventListeners();
selectedScroller = initScrollBox(elements.selectedHolder, elements.selectedThumb);
docsScroller = initScrollBox(elements.docsHolder, elements.docsThumb, checkDocsScroll);
initSelectBoxes();
elements.styleSelectList.onopen = function () {
elements.styleSelectList.style.width = (elements.styleWrapper.clientWidth - 2) + "px";
}
};
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);
};
function initSdkApis() {
return sdk.isApiAvailable().then(function(availableApis) {
console.log('apis', availableApis);
if (availableApis.desktop) {
switchAuthState('main');
} else if (availableApis.online) {
if (sdk.hasSettings()) {
switchAuthState('main');
} else {
switchAuthState('config');
}
} else if (availableApis.permissionNeeded) {
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 {
showError(getMessage('Connection to Zotero failed. Make sure Zotero is running.'));
}
return availableApis;
});
}
function loadStyles() {
cslStylesManager.getStyles()
.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 onStyleSelect = function (f) {
return function (ev) {
var sel = ev.target.getAttribute("data-value");
cslStylesManager.saveLastUsedStyle(sel);
f(ev);
}
};
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 = onStyleSelect(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[i].name == lastStyle)
elements.styleSelectList.insertBefore(el, elements.styleSelectList.firstElementChild);
else
elements.styleSelectList.appendChild(el);
el.onclick = onStyleSelect(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);
found = true;
}
}
if (!found) {
var first = elements.styleSelectList.children[0];
first.setAttribute("selected", "");
selectInput(elements.styleSelect, first, elements.styleSelectList, false);
}
}
function addEventListeners() {
elements.fileInput.onchange = function (e) {
var file = e.target.files[0];
if (!file) return;
//showLoader(true);
cslStylesManager.addCustomStyle(file).then(function (styleValue) {
elements.styleSelectList.querySelector('[selected]').removeAttribute("selected");
addStylesToList([styleValue]);
}).catch(function (error) {
console.error(error);
showError(getMessage("Failed to upload file"));
}).finally(function () {
showLoader(false);
});
};
elements.useDesktopApp.onclick = function() {
showLoader(true);
initSdkApis().finally(function() {
showLoader(false);
});
};
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.items(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 () {
switchAuthState("main");
}).catch(function () {
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);
saveAs();
}
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) {
bNumFormat = (style.indexOf('citation-format="numeric"') !== -1);
if (isClick)
updateCslItems(true, true, false, false);
})
.catch(function () { })
.finally(function() {
if (locales[selectedLocale] && styles[selectedStyle]) showLoader(false);
});
selectedStyle = val;
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 () { })
.finally(function() {
if (locales[selectedLocale] && styles[selectedStyle]) showLoader(false);
});
selectedLocale = val;
};
}
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 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) {
if (styles[styleName] != null) {
res(styles[styleName]);
} else {
loadingStyle = true;
cslStylesManager.getStyle(styleName)
.then(function (text) {
styles[styleName] = 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) {
showLoader(false);
return;
}
var updatedFields = [];
var bibField = null;
var bibFieldValue = ' ';
try {
elements.tempDiv.innerHTML = formatter.makeBibliography()[1].join('');
} catch (e) {
console.error(e);
showError(getMessage("Failed to apply this style."));
showLoader(false);
return;
}
var bibliography = elements.tempDiv.innerText;
arrFields.forEach(function(field) {
var citationObject;
var citationStartIndex = field.Value.indexOf("{");
if (citationStartIndex !== -1) {
var citationString = field.Value.slice(citationStartIndex);
citationObject = JSON.parse(citationString);
}
var keysL = [];
var cslCitation;
if (bUpadteAll && ( field.Value.indexOf(citPrefixNew) !== -1 || field.Value.indexOf(citPrefix) !== -1 ) ) {
var citationID = citationObject.citationID;
if (field.Value.indexOf(citPrefix) !== -1) {
citationID = citationObject.citationItems[0].id;
}
cslCitation = new CSLCitation(citationID, keysL.length);
cslCitation.fillFromObject(citationObject);
keysL = keysL.concat(cslCitation.getSuppressAuthors());
elements.tempDiv.innerHTML = formatter.makeCitationCluster(keysL);
field["Content"] = elements.tempDiv.innerText;
if (bSyncronize && cslCitation) {
// if we make synchronization we must update value too
field["Value"] = citPrefixNew + ' ' + citSuffixNew + JSON.stringify(cslCitation.toJSON());
}
updatedFields.push(field);
} else if (field.Value.indexOf(bibPrefix) !== -1 || field.Value.indexOf(bibPrefixNew) !== -1) {
bibField = field;
if (typeof citationObject === "object" && Object.keys(citationObject).length > 0) {
bibFieldValue = JSON.stringify(citationObject);
}
}
});
if (bibField) {
bibField["Content"] = bibliography;
updatedFields.push(bibField);
} else if (bPastBib) {
bibField = {
"Value" : bibPrefixNew + bibFieldValue + bibSuffixNew,
"Content" : bibliography
};
window.Asc.plugin.executeMethod("AddAddinField", [bibField], function() {
if (!updatedFields.length) {
showLoader(false);
}
});
}
if (updatedFields.length) {
window.Asc.plugin.executeMethod("UpdateAddinFields", [updatedFields], function() {
showLoader(false);
});
}
});
};
function updateFormatter(bUpadteAll, bPastBib, bPastLink, bSyncronize) {
clearTimeout(repeatTimeout);
if (loadingStyle || loadingLocale || !styles[selectedStyle] || !locales[selectedLocale]) {
repeatTimeout = setTimeout( function() {
updateFormatter(bUpadteAll, bPastBib, bPastLink, bSyncronize);
}, 100);
return;
}
var arrIds = [];
CSLCitationStorage.forEach(function(item, id) {
arrIds.push(id);
});
formatter = new CSL.Engine(
{
retrieveLocale: function (id) { return locales[id]; },
retrieveItem: function (id) {
var item = CSLCitationStorage.get(id);
return item.toOldJSON();
}
},
styles[selectedStyle],
selectedLocale,
true
);
if (arrIds.length) {
formatter.updateItems(arrIds);
}
if (bUpadteAll || bPastBib)
updateAllOrAddBib(bUpadteAll, bPastBib, bSyncronize);
if (bPastLink) {
formatInsertLink();
}
};
// onInit (1,0,0,0)
// Insert Citation (1,0,0,1)
// Insert Bibliography (1,1,1,0)
// Refresh (1,1,0,0)
function updateCslItems(bUpdadeFormatter, bUpadteAll, bPastBib, bPastLink) {
CSLCitationStorage.clear();
window.Asc.plugin.executeMethod("GetAllAddinFields", null, function(arrFields) {
if (arrFields.length) {
var numOfItems = 0;
var bibField = null;
var bibFieldValue = ' ';
arrFields.forEach(function(field) {
var citationObject;
var citationStartIndex = field.Value.indexOf("{");
if (citationStartIndex !== -1) {
var citationString = field.Value.slice(citationStartIndex);
citationObject = JSON.parse(citationString);
}
if (field.Value.indexOf(citPrefix) !== -1 || field.Value.indexOf(citPrefixNew) !== -1) {
var citationID = citationObject.citationID;
if (field.Value.indexOf(citPrefix) !== -1) {
citationID = citationObject.citationItems[0].id;
}
var cslCitation = new CSLCitation(citationID, numOfItems);
CSLCitationStorage.set(citationID, cslCitation);
numOfItems += cslCitation.fillFromObject(citationObject);
} else if (field.Value.indexOf(bibPrefix) !== -1 || field.Value.indexOf(bibPrefixNew) !== -1) {
bibField = field;
if (typeof citationObject === "object" && Object.keys(citationObject).length > 0) {
bibFieldValue = JSON.stringify(citationObject);
}
}
});
if (numOfItems) {
// sort?
} 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" : bibPrefixNew + bibFieldValue + bibSuffixNew,
"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 citationID in selected.items) {
if (!CSLCitationStorage.has(citationID)) {
var cslCitation = new CSLCitation(citationID, CSLCitationStorage.size);
var item = convertToCSL(selected.items[citationID]);
cslCitation.fillFromObject(item);
CSLCitationStorage.set(citationID, cslCitation);
bUpdateItems = true;
}
keys.push(citationID);
keysL.push(CSLCitationStorage.get(citationID).getSuppressAuthors());
}
try {
if (bUpdateItems) {
var arrIds = [];
CSLCitationStorage.forEach(function(item, id) {
arrIds.push(id);
});
formatter.updateItems(arrIds);
}
var element = keys[0];
var obj = CSLCitationStorage.get(element);
removeSelected(element);
// TODO может ещё очистить поиск (подумать над этим)
elements.tempDiv.innerHTML = formatter.makeCitationCluster(keysL[0]);
var field = {
"Value" : citPrefixNew + ' ' + citSuffixNew + JSON.stringify(obj.toJSON()),
"Content" : elements.tempDiv.innerText
};
window.Asc.plugin.executeMethod("AddAddinField", [field], function() {
showLoader(false);
// TODO есть проблема, что в плагине мы индексы обновили, а вот в документе нет (по идее надо обновить и индексы в документе перед вставкой)
// но тогда у нас уедет селект и новое поле вставится не там, поэтому пока обновлять приходится в конце
// такая же проблем с вставкой библиографии (при обнолении индексов в плагине надо бы их обновлять и в документе тоже)
updateCslItems(true, true, false, false);
});
} catch (e) {
showError(e);
}
};
function convertToCSL(item) {
var cslData = item;
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 = CSLCitationStorage.get(item.id);
item["short-title"] = item.shortTitle;
item["title-short"] = item.shortTitle;
cslItem.fillFromObject(item);
};
function saveAs() {
// TODO потом добавить ещё форматы, пока только как текст
window.Asc.plugin.executeMethod("GetAllAddinFields", null, function(arrFields) {
let count = 0;
arrFields.forEach(function(field) {
if (
( field.Value.indexOf(bibPrefix) !== -1 ) ||
( field.Value.indexOf(citPrefix) !== -1 ) ||
( field.Value.indexOf(bibPrefixNew) !== -1 ) ||
( field.Value.indexOf(citPrefixNew) !== -1 )
) {
count++;
window.Asc.plugin.executeMethod("RemoveFieldWrapper", [field.FieldId], function() {
count--;
if (!count)
window.Asc.plugin.executeCommand("close", "");
});
}
});
if (!arrFields.length)
window.Asc.plugin.executeCommand("close", "");
});
};
function synchronizeData() {
// form an array for request (one array for user and other for groups)
// todo now we should make full update (because when we make refresh, we check fields into the document). Fix it in new version (when we change refreshing and updating processes)
if (!CSLCitationStorage.size)
return;
showLoader(true);
bGroupsItemsUpdated = false;
bUserItemsUpdated = false;
var bHasGroupsItems = false;
var arrUsrItems = [];
var arrGroupsItems = {};
CSLCitationStorage.forEach(function(item, id) {
var userID = item.getProperty("userID");
var groupID = item.getProperty("groupID");
if (userID) {
arrUsrItems.push(id)
} else if (groupID) {
if (!arrGroupsItems[groupID])
arrGroupsItems[groupID] = [];
arrGroupsItems[groupID].push(id);
}
})
if (arrUsrItems.length) {
sdk.items(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);
};
})();