mirror of
https://github.com/ONLYOFFICE/onlyoffice.github.io.git
synced 2026-04-07 14:04:30 +08:00
variable typing
This commit is contained in:
@ -31,7 +31,7 @@
|
|||||||
<script src="scripts/citeproc/citeproc_commonjs.js"></script>
|
<script src="scripts/citeproc/citeproc_commonjs.js"></script>
|
||||||
|
|
||||||
<script src="scripts/csl/citation/citation-item-data.js"></script>
|
<script src="scripts/csl/citation/citation-item-data.js"></script>
|
||||||
<script src="scripts/csl/citation/citation-tem.js"></script>
|
<script src="scripts/csl/citation/citation-item.js"></script>
|
||||||
<script src="scripts/csl/citation/citation.js"></script>
|
<script src="scripts/csl/citation/citation.js"></script>
|
||||||
<script src="scripts/csl/citation/storage.js"></script>
|
<script src="scripts/csl/citation/storage.js"></script>
|
||||||
|
|
||||||
@ -74,7 +74,7 @@
|
|||||||
<div class="selectHolder">
|
<div class="selectHolder">
|
||||||
<input id="library" readonly class="control select" type="text">
|
<input id="library" readonly class="control select" type="text">
|
||||||
<div id="searchLibrary" class="selectList display-none">
|
<div id="searchLibrary" class="selectList display-none">
|
||||||
<span data-value="all" class="i18n">All groups</span>
|
<span data-value="all" class="i18n" selected>All groups</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<label id="searchLabel">
|
<label id="searchLabel">
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
/// <reference path="./types-global.js" />
|
||||||
|
/// <reference path="./csl/citation/citation.js" />
|
||||||
|
/// <reference path="./csl/styles/style-parser.js" />
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} CustomField
|
* @typedef {Object} CustomField
|
||||||
* @property {string} Value
|
* @property {string} Value
|
||||||
@ -7,15 +11,6 @@
|
|||||||
* @property {string} [FieldId]
|
* @property {string} [FieldId]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} SupSubPositions
|
|
||||||
* @property {'sup'|'sub'} type
|
|
||||||
* @property {number} start
|
|
||||||
* @property {number} end
|
|
||||||
* @property {string} content
|
|
||||||
* @property {string} originalMatch
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} citPrefix
|
* @param {string} citPrefix
|
||||||
* @param {string} citSuffix
|
* @param {string} citSuffix
|
||||||
|
|||||||
@ -15,6 +15,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
/// <reference path="./zotero.js" />
|
||||||
|
/// <reference path="./csl/citation/citation.js" />
|
||||||
|
/// <reference path="./csl/styles/styles-manager.js" />
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
var counter = 0; // счетчик отправленных запросов (используется чтобы знать показывать "not found" или нет)
|
var counter = 0; // счетчик отправленных запросов (используется чтобы знать показывать "not found" или нет)
|
||||||
var displayNoneClass = "display-none";
|
var displayNoneClass = "display-none";
|
||||||
@ -64,6 +68,7 @@
|
|||||||
var selectedLocale;
|
var selectedLocale;
|
||||||
var selectedStyle;
|
var selectedStyle;
|
||||||
|
|
||||||
|
/** @type {ZoteroSdk} */
|
||||||
var sdk = null;
|
var sdk = null;
|
||||||
var cslStylesManager = null;
|
var cslStylesManager = null;
|
||||||
var citationDocService = null;
|
var citationDocService = null;
|
||||||
@ -126,7 +131,7 @@
|
|||||||
showLoader(true);
|
showLoader(true);
|
||||||
setTimeout(function () { searchField.focus(); },100);
|
setTimeout(function () { searchField.focus(); },100);
|
||||||
|
|
||||||
sdk = ZoteroSdk();
|
sdk = new ZoteroSdk();
|
||||||
cslStylesManager = new CslStylesManager();
|
cslStylesManager = new CslStylesManager();
|
||||||
addEventListeners();
|
addEventListeners();
|
||||||
|
|
||||||
@ -826,6 +831,15 @@
|
|||||||
docsScroller.onscroll();
|
docsScroller.onscroll();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Promise} promise
|
||||||
|
* @param {boolean} append
|
||||||
|
* @param {boolean} showLoader
|
||||||
|
* @param {boolean} hideLoader
|
||||||
|
* @param {boolean} isGroup
|
||||||
|
* @param {boolean} bCount
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
function loadLibrary(promise, append, showLoader, hideLoader, isGroup, bCount) {
|
function loadLibrary(promise, append, showLoader, hideLoader, isGroup, bCount) {
|
||||||
if (showLoader) showLibLoader(true);
|
if (showLoader) showLibLoader(true);
|
||||||
if (bCount) counter++;
|
if (bCount) counter++;
|
||||||
|
|||||||
@ -127,11 +127,18 @@ CitationItemData.prototype._addCustomProperty = function (key, value) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
CitationItemData.prototype.getCustomProperty = function (key) {
|
CitationItemData.prototype.getCustomProperty = function (key) {
|
||||||
if (Object.hasOwnProperty.call(this._custom, key)) return this._custom[key];
|
if (Object.hasOwnProperty.call(this._custom, key)) return this._custom[key];
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} itemDataObject
|
||||||
|
*/
|
||||||
CitationItemData.prototype.fillFromObject = function (itemDataObject) {
|
CitationItemData.prototype.fillFromObject = function (itemDataObject) {
|
||||||
if (Object.hasOwnProperty.call(itemDataObject, "type")) {
|
if (Object.hasOwnProperty.call(itemDataObject, "type")) {
|
||||||
this._type = itemDataObject.type;
|
this._type = itemDataObject.type;
|
||||||
@ -448,7 +455,10 @@ CitationItemData.prototype.fillFromObject = function (itemDataObject) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Object.hasOwnProperty.call(itemDataObject, "creators")) {
|
if (Object.hasOwnProperty.call(itemDataObject, "creators")) {
|
||||||
itemDataObject.creators.forEach(function (creator) {
|
const self = this;
|
||||||
|
itemDataObject.creators.forEach(function (
|
||||||
|
/** @type {{firstName: string, lastName: string}}} */ creator
|
||||||
|
) {
|
||||||
let author = {};
|
let author = {};
|
||||||
if (creator.firstName) {
|
if (creator.firstName) {
|
||||||
author.given = creator.firstName;
|
author.given = creator.firstName;
|
||||||
@ -456,8 +466,9 @@ CitationItemData.prototype.fillFromObject = function (itemDataObject) {
|
|||||||
if (creator.lastName) {
|
if (creator.lastName) {
|
||||||
author.family = creator.lastName;
|
author.family = creator.lastName;
|
||||||
}
|
}
|
||||||
this._author.push(author);
|
self._author.push(author);
|
||||||
}, this);
|
},
|
||||||
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.hasOwnProperty.call(itemDataObject, "libraryCatalog")) {
|
if (Object.hasOwnProperty.call(itemDataObject, "libraryCatalog")) {
|
||||||
|
|||||||
@ -1,3 +1,16 @@
|
|||||||
|
// @ts-check
|
||||||
|
/// <reference path="./citation-item-data.js" />
|
||||||
|
/**
|
||||||
|
* @typedef {Object} OldCitationItem - before version 1.0.5
|
||||||
|
* @property {string|number} id
|
||||||
|
* @property {string} [type]
|
||||||
|
* @property {string} [title]
|
||||||
|
* @property {string} [userID]
|
||||||
|
* @property {string} [groupID]
|
||||||
|
* @property {number} index
|
||||||
|
* @property {boolean} [`suppress-author`]
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string|number} id
|
* @param {string|number} id
|
||||||
*/
|
*/
|
||||||
@ -17,7 +30,11 @@ function CitationItem(id) {
|
|||||||
this._uris = new Array();
|
this._uris = new Array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} itemObject
|
||||||
|
*/
|
||||||
CitationItem.prototype.fillFromObject = function (itemObject) {
|
CitationItem.prototype.fillFromObject = function (itemObject) {
|
||||||
|
const self = this;
|
||||||
if (
|
if (
|
||||||
Object.hasOwnProperty.call(itemObject, "version") &&
|
Object.hasOwnProperty.call(itemObject, "version") &&
|
||||||
Object.hasOwnProperty.call(itemObject, "library")
|
Object.hasOwnProperty.call(itemObject, "library")
|
||||||
@ -50,12 +67,15 @@ CitationItem.prototype.fillFromObject = function (itemObject) {
|
|||||||
if (Object.hasOwnProperty.call(itemObject, "author-only"))
|
if (Object.hasOwnProperty.call(itemObject, "author-only"))
|
||||||
this._authorOnly = itemObject["author-only"];
|
this._authorOnly = itemObject["author-only"];
|
||||||
if (Object.hasOwnProperty.call(itemObject, "uris")) {
|
if (Object.hasOwnProperty.call(itemObject, "uris")) {
|
||||||
itemObject.uris.forEach(function (uri) {
|
itemObject.uris.forEach(function (/** @type {string} */ uri) {
|
||||||
this.addUri(uri);
|
self.addUri(uri);
|
||||||
}, this);
|
}, this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {SuppressAuthor}
|
||||||
|
*/
|
||||||
CitationItem.prototype.getSuppressAuthor = function () {
|
CitationItem.prototype.getSuppressAuthor = function () {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
@ -209,7 +229,12 @@ CitationItem.prototype.toJSON = function () {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} index
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
CitationItem.prototype.toFlatJSON = function (index) {
|
CitationItem.prototype.toFlatJSON = function (index) {
|
||||||
|
/** @type {OldCitationItem} */
|
||||||
var oldItem = {
|
var oldItem = {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
index: index,
|
index: index,
|
||||||
@ -221,11 +246,17 @@ CitationItem.prototype.toFlatJSON = function (index) {
|
|||||||
let itemDataObject = this._itemData.toJSON();
|
let itemDataObject = this._itemData.toJSON();
|
||||||
Object.assign(oldItem, itemDataObject);
|
Object.assign(oldItem, itemDataObject);
|
||||||
|
|
||||||
if (this._itemData.getCustomProperty("userID") !== null) {
|
if (
|
||||||
oldItem.userID = this._itemData.getCustomProperty("userID");
|
typeof this._itemData.getCustomProperty("userID") !== "undefined" &&
|
||||||
|
this._itemData.getCustomProperty("userID") !== null
|
||||||
|
) {
|
||||||
|
oldItem.userID = String(this._itemData.getCustomProperty("userID"));
|
||||||
}
|
}
|
||||||
if (this._itemData.getCustomProperty("groupID") !== null) {
|
if (
|
||||||
oldItem.groupID = this._itemData.getCustomProperty("groupID");
|
typeof this._itemData.getCustomProperty("groupID") !== "undefined" &&
|
||||||
|
this._itemData.getCustomProperty("groupID") !== null
|
||||||
|
) {
|
||||||
|
oldItem.groupID = String(this._itemData.getCustomProperty("groupID"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return oldItem;
|
return oldItem;
|
||||||
@ -1,7 +1,10 @@
|
|||||||
|
// @ts-check
|
||||||
|
/// <reference path="./citation-item.js" />
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} SuppressAuthor
|
* @typedef {Object} SuppressAuthor
|
||||||
* @property {string} id
|
* @property {string|number} id
|
||||||
* @property {boolean} "suppress-author"
|
* @property {boolean} `suppress-author`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,6 +43,7 @@ function CSLCitation(itemsStartIndex, citationID) {
|
|||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
this.citationID = citationID;
|
this.citationID = citationID;
|
||||||
this._itemsStartIndex = itemsStartIndex;
|
this._itemsStartIndex = itemsStartIndex;
|
||||||
|
/** @type {Array<CitationItem>} */
|
||||||
this._citationItems = new Array();
|
this._citationItems = new Array();
|
||||||
this._properties = new Object();
|
this._properties = new Object();
|
||||||
|
|
||||||
@ -48,7 +52,7 @@ function CSLCitation(itemsStartIndex, citationID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {*} citationObject
|
* @param {any} citationObject
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
CSLCitation.prototype.fillFromObject = function (citationObject) {
|
CSLCitation.prototype.fillFromObject = function (citationObject) {
|
||||||
@ -69,7 +73,12 @@ CSLCitation.prototype.fillFromObject = function (citationObject) {
|
|||||||
return this._fillFromCslJson(citationObject);
|
return this._fillFromCslJson(citationObject);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} citationObject
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
CSLCitation.prototype._fillFromCitationObject = function (citationObject) {
|
CSLCitation.prototype._fillFromCitationObject = function (citationObject) {
|
||||||
|
const self = this;
|
||||||
if (Object.hasOwnProperty.call(citationObject, "schema")) {
|
if (Object.hasOwnProperty.call(citationObject, "schema")) {
|
||||||
// this._setSchema(citationObject.schema);
|
// this._setSchema(citationObject.schema);
|
||||||
}
|
}
|
||||||
@ -86,11 +95,13 @@ CSLCitation.prototype._fillFromCitationObject = function (citationObject) {
|
|||||||
return item.id;
|
return item.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
citationObject.citationItems.forEach(function (item) {
|
citationObject.citationItems.forEach(function (
|
||||||
|
/** @type {CitationItem} */ item
|
||||||
|
) {
|
||||||
let id = item.id;
|
let id = item.id;
|
||||||
let citationItem;
|
let citationItem;
|
||||||
if (existingIds.indexOf(id) >= 0) {
|
if (existingIds.indexOf(id) >= 0) {
|
||||||
citationItem = this._citationItems[existingIds.indexOf(id)];
|
citationItem = self._citationItems[existingIds.indexOf(id)];
|
||||||
} else {
|
} else {
|
||||||
citationItem = new CitationItem(id);
|
citationItem = new CitationItem(id);
|
||||||
existingIds.push(id);
|
existingIds.push(id);
|
||||||
@ -98,21 +109,23 @@ CSLCitation.prototype._fillFromCitationObject = function (citationObject) {
|
|||||||
|
|
||||||
if (typeof id === "number") {
|
if (typeof id === "number") {
|
||||||
// Word 365 or wps
|
// Word 365 or wps
|
||||||
id = this._extractIdFromWord365Citation(item);
|
id = self._extractIdFromWord365Citation(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
citationItem.fillFromObject(item);
|
citationItem.fillFromObject(item);
|
||||||
|
|
||||||
this._addCitationItem(citationItem);
|
self._addCitationItem(citationItem);
|
||||||
}, this);
|
},
|
||||||
|
this);
|
||||||
return existingIds.length;
|
return existingIds.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{citationObject: Array<{id: string, index: number, "suppress-author": boolean, title: string, type: string, userID: string, groupID: string}>}} citationObject
|
* @param {{citationItems: OldCitationItem[]}} citationObject
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
CSLCitation.prototype._fillFromFlatCitationObject = function (citationObject) {
|
CSLCitation.prototype._fillFromFlatCitationObject = function (citationObject) {
|
||||||
|
const self = this;
|
||||||
if (citationObject.citationItems.length === 0) {
|
if (citationObject.citationItems.length === 0) {
|
||||||
console.error("CSLCitation.citationItems: citationItems is empty");
|
console.error("CSLCitation.citationItems: citationItems is empty");
|
||||||
return 0;
|
return 0;
|
||||||
@ -123,14 +136,14 @@ CSLCitation.prototype._fillFromFlatCitationObject = function (citationObject) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
citationObject.citationItems.forEach(function (itemObject) {
|
citationObject.citationItems.forEach(function (itemObject) {
|
||||||
this._fillFromCslJson(itemObject);
|
self._fillFromCslJson(itemObject);
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} citationObject
|
* @param {any} itemObject
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
CSLCitation.prototype._fillFromCslJson = function (itemObject) {
|
CSLCitation.prototype._fillFromCslJson = function (itemObject) {
|
||||||
@ -155,7 +168,7 @@ CSLCitation.prototype._fillFromCslJson = function (itemObject) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{key: string, version: number, library: Object, links: Object, meta: Object, data: CitationJsonData}} citationObject
|
* @param {{key: string, version: number, library: Object, links: Object, meta: Object, data: CitationJsonData}} itemObject
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
CSLCitation.prototype._fillFromJson = function (itemObject) {
|
CSLCitation.prototype._fillFromJson = function (itemObject) {
|
||||||
@ -195,6 +208,10 @@ CSLCitation.prototype.getSuppressAuthors = function () {
|
|||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
CSLCitation.prototype.getProperty = function (key) {
|
CSLCitation.prototype.getProperty = function (key) {
|
||||||
let items = this._citationItems;
|
let items = this._citationItems;
|
||||||
for (var i = 0; i < items.length; i++) {
|
for (var i = 0; i < items.length; i++) {
|
||||||
@ -207,6 +224,10 @@ CSLCitation.prototype.getProperty = function (key) {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {CitationItem} item
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
CSLCitation.prototype._addCitationItem = function (item) {
|
CSLCitation.prototype._addCitationItem = function (item) {
|
||||||
const existingIds = this._citationItems.map(function (item) {
|
const existingIds = this._citationItems.map(function (item) {
|
||||||
return item.id;
|
return item.id;
|
||||||
@ -219,17 +240,26 @@ CSLCitation.prototype._addCitationItem = function (item) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} properties
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
CSLCitation.prototype._setProperties = function (properties) {
|
CSLCitation.prototype._setProperties = function (properties) {
|
||||||
this._properties = properties;
|
this._properties = properties;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} schema
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
CSLCitation.prototype._setSchema = function (schema) {
|
CSLCitation.prototype._setSchema = function (schema) {
|
||||||
this._schema = schema;
|
this._schema = schema;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {any} item
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
CSLCitation.prototype._extractIdFromWord365Citation = function (item) {
|
CSLCitation.prototype._extractIdFromWord365Citation = function (item) {
|
||||||
@ -265,9 +295,10 @@ CSLCitation.prototype.validate = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
CSLCitation.prototype.toJSON = function () {
|
CSLCitation.prototype.toJSON = function () {
|
||||||
var result = {
|
var result = /** @type {any} */ ({
|
||||||
citationID: this.citationID,
|
citationID: this.citationID,
|
||||||
};
|
schema: this._schema,
|
||||||
|
});
|
||||||
|
|
||||||
if (this._properties && Object.keys(this._properties).length > 0) {
|
if (this._properties && Object.keys(this._properties).length > 0) {
|
||||||
result.properties = this._properties;
|
result.properties = this._properties;
|
||||||
@ -275,11 +306,9 @@ CSLCitation.prototype.toJSON = function () {
|
|||||||
|
|
||||||
if (this._citationItems && this._citationItems.length > 0) {
|
if (this._citationItems && this._citationItems.length > 0) {
|
||||||
result.citationItems = this._citationItems.map(function (item) {
|
result.citationItems = this._citationItems.map(function (item) {
|
||||||
return item.toJSON ? item.toJSON() : item;
|
return item.toJSON();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
result.schema = this._schema;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
/// <reference path="./storage.js" />
|
||||||
|
/// <reference path="./style-parser.js" />
|
||||||
|
|
||||||
function CslStylesManager() {
|
function CslStylesManager() {
|
||||||
this._isOnlineAvailable = false;
|
this._isOnlineAvailable = false;
|
||||||
|
|||||||
127
sdkjs-plugins/content/zotero/scripts/types-global.js
Normal file
127
sdkjs-plugins/content/zotero/scripts/types-global.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} AscSimpleRequestParams
|
||||||
|
* @property {string} url
|
||||||
|
* @property {"GET"|"POST"} method
|
||||||
|
* @property {object} headers
|
||||||
|
* @property {string} [body]
|
||||||
|
* @property {function(AscSimpleResponse, string): void} complete
|
||||||
|
* @property {function(AscSimpleResponse, string, Error): void} error
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} AscSimpleRequest
|
||||||
|
* @property {function(AscSimpleRequestParams): void} createRequest
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} AscSimpleResponse
|
||||||
|
* @property {number} responseStatus
|
||||||
|
* @property {string} responseText
|
||||||
|
* @property {string} [message]
|
||||||
|
* @property {string} status
|
||||||
|
* @property {number} statusCode
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {AscSimpleRequest} */
|
||||||
|
var AscSimpleRequest = window.AscSimpleRequest;
|
||||||
|
|
||||||
|
/** ------------------------------------------------ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SupSubPositions
|
||||||
|
* @property {'sup'|'sub'} type
|
||||||
|
* @property {number} start
|
||||||
|
* @property {number} end
|
||||||
|
* @property {string} content
|
||||||
|
* @property {string} originalMatch
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} AscPluginTheme
|
||||||
|
* @property {string} `text-normal`
|
||||||
|
* @property {string} `background-normal`
|
||||||
|
* @property {string} `highlight-button-hover`
|
||||||
|
* @property {string} `highlight-button-pressed`
|
||||||
|
* @property {string} `border-regular-control`
|
||||||
|
* @property {string} `border-toolbar`
|
||||||
|
* @property {string} `border-divider`
|
||||||
|
* @property {string} `background-toolbar`
|
||||||
|
* @property {string} RulerLight
|
||||||
|
* @property {string} Color - text color
|
||||||
|
* @property {string} type - light/dark
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback ExecuteCommandCallback
|
||||||
|
* @param {string} command
|
||||||
|
* @param {any} [value]
|
||||||
|
* @param {function} [callback]
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback CallCommandCallback
|
||||||
|
* @param {function} command
|
||||||
|
* @param {boolean} [isClose]
|
||||||
|
* @param {boolean} [isCalc]
|
||||||
|
* @param {function} [callback]
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} AscPlugin
|
||||||
|
* @property {function(string, Array<any>|null, function(any): void): void} executeMethod
|
||||||
|
* @property {ExecuteCommandCallback} executeCommand
|
||||||
|
* @property {CallCommandCallback} callCommand
|
||||||
|
* @property {function(): void} init
|
||||||
|
* @property {object} info
|
||||||
|
* @property {function(string): void} sendToPlugin
|
||||||
|
* @property {function} onTranslate
|
||||||
|
* @property {function(string, function): void} attachEvent
|
||||||
|
* @property {string} onThemeChanged
|
||||||
|
* @property {function(string): void} onThemeChangedBase
|
||||||
|
* @property {AscPluginTheme} theme
|
||||||
|
* @property {function(string): string} tr
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Asc
|
||||||
|
* @property {AscPlugin} plugin
|
||||||
|
* @property {{positions: Array<SupSubPositions>}} scope
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {Asc} */
|
||||||
|
var Asc = window.Asc;
|
||||||
|
|
||||||
|
/** ------------------------------------------------ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Api
|
||||||
|
* @property {function(): any} GetDocument
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {Api} */
|
||||||
|
var Api = window.Api;
|
||||||
|
|
||||||
|
/** ------------------------------------------------ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} FetchResponse
|
||||||
|
* @property {function(): Promise<ArrayBuffer>} arrayBuffer
|
||||||
|
* @property {function(): Promise<Blob>} blob
|
||||||
|
* @property {function(): Promise<Object>} json
|
||||||
|
* @property {function(): Promise<string>} text
|
||||||
|
* @property {boolean} ok
|
||||||
|
* @property {number} status
|
||||||
|
* @property {string} statusText
|
||||||
|
* @property {Headers} headers
|
||||||
|
* @property {string} type
|
||||||
|
* @property {string} url
|
||||||
|
* @property {boolean} redirected
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Promise<FetchResponse>} FetchPromise
|
||||||
|
*/
|
||||||
@ -1,3 +1,8 @@
|
|||||||
|
// @ts-check
|
||||||
|
/// <reference path="./types-global.js" />
|
||||||
|
/// <reference path="./zotero.js" />
|
||||||
|
/// <reference path="./zotero-environment.js" />
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} AvailableApis
|
* @typedef {Object} AvailableApis
|
||||||
* @property {boolean} desktop
|
* @property {boolean} desktop
|
||||||
@ -14,7 +19,8 @@ var ZoteroApiChecker = {
|
|||||||
_online: false,
|
_online: false,
|
||||||
_hasKey: false,
|
_hasKey: false,
|
||||||
_timeout: 1000, // 1 second
|
_timeout: 1000, // 1 second
|
||||||
_callback: function () {},
|
/** @type {function(AvailableApis): void} */
|
||||||
|
_callback: function (e) {},
|
||||||
_desktopVersion: (function () {
|
_desktopVersion: (function () {
|
||||||
if (
|
if (
|
||||||
window.navigator &&
|
window.navigator &&
|
||||||
@ -24,15 +30,17 @@ var ZoteroApiChecker = {
|
|||||||
)
|
)
|
||||||
return false;
|
return false;
|
||||||
if (window.location && window.location.protocol == "file:") return true;
|
if (window.location && window.location.protocol == "file:") return true;
|
||||||
if (
|
const src = window.document.currentScript
|
||||||
window.document &&
|
? window.document.currentScript.getAttribute("src")
|
||||||
window.document.currentScript &&
|
: "";
|
||||||
0 == window.document.currentScript.src.indexOf("file:///")
|
if (src && 0 == src.indexOf("file:///")) return true;
|
||||||
)
|
|
||||||
return true;
|
|
||||||
return false;
|
return false;
|
||||||
})(),
|
})(),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ZoteroSdk} sdk
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
runApisChecker: function (sdk) {
|
runApisChecker: function (sdk) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
@ -55,15 +63,21 @@ var ZoteroApiChecker = {
|
|||||||
attemptCheck();
|
attemptCheck();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe: function (callbackFn) {
|
subscribe: function (
|
||||||
|
/** @type {function(AvailableApis): void} */ callbackFn
|
||||||
|
) {
|
||||||
self._callback = callbackFn;
|
self._callback = callbackFn;
|
||||||
},
|
},
|
||||||
unsubscribe: function () {
|
unsubscribe: function () {
|
||||||
done = true;
|
self._done = true;
|
||||||
callback = function () {};
|
self._callback = function () {};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @param {ZoteroSdk} sdk
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
checkStatus: function (sdk) {
|
checkStatus: function (sdk) {
|
||||||
return this._checkApiAvailable(sdk);
|
return this._checkApiAvailable(sdk);
|
||||||
},
|
},
|
||||||
@ -77,7 +91,10 @@ var ZoteroApiChecker = {
|
|||||||
desktopVersion: this._desktopVersion,
|
desktopVersion: this._desktopVersion,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @param {ZoteroSdk} sdk
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
_checkApiAvailable: function (sdk) {
|
_checkApiAvailable: function (sdk) {
|
||||||
const self = this;
|
const self = this;
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function (resolve) {
|
||||||
@ -115,6 +132,10 @@ var ZoteroApiChecker = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @param {string} url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
_sendDesktopRequest: function (url) {
|
_sendDesktopRequest: function (url) {
|
||||||
const self = this;
|
const self = this;
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
@ -132,7 +153,8 @@ var ZoteroApiChecker = {
|
|||||||
"Zotero-API-Version": "3",
|
"Zotero-API-Version": "3",
|
||||||
"User-Agent": "AscDesktopEditor",
|
"User-Agent": "AscDesktopEditor",
|
||||||
},
|
},
|
||||||
complete: function (e) {
|
complete: function (/** @type {AscSimpleResponse} */ e) {
|
||||||
|
console.warn(e);
|
||||||
let hasPermission = false;
|
let hasPermission = false;
|
||||||
let isZoteroRunning = false;
|
let isZoteroRunning = false;
|
||||||
if (e.responseStatus == 403) {
|
if (e.responseStatus == 403) {
|
||||||
@ -147,7 +169,7 @@ var ZoteroApiChecker = {
|
|||||||
isZoteroRunning: isZoteroRunning,
|
isZoteroRunning: isZoteroRunning,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
error: function (e) {
|
error: function (/** @type {AscSimpleResponse} */ e) {
|
||||||
if (e.statusCode == -102) e.statusCode = 404;
|
if (e.statusCode == -102) e.statusCode = 404;
|
||||||
reject(e);
|
reject(e);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -16,315 +16,509 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
/// <reference path="./types-global.js" />
|
||||||
|
/// <reference path="./zotero-environment.js" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ZoteroGroupInfo
|
||||||
|
* @property {number} id
|
||||||
|
* @property {number} version
|
||||||
|
* @property {{alternate: {href: string, type: string}, self: {href: string, type: string}}} meta
|
||||||
|
* @property {{created: string, lastModified: string, numItems: number}} links
|
||||||
|
* @property {{name: string, description: string, id: number, owner: number, type: string}} data
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} UserGroupInfo
|
||||||
|
* @property {number} id
|
||||||
|
* @property {string} name
|
||||||
|
*/
|
||||||
|
|
||||||
const ZoteroSdk = function () {
|
const ZoteroSdk = function () {
|
||||||
var apiKey;
|
this._apiKey = null;
|
||||||
var userId = 0;
|
this._userId = 0;
|
||||||
var userGroups = [];
|
/** @type {Array<UserGroupInfo>}} */
|
||||||
var isOnlineAvailable = true;
|
this._userGroups = [];
|
||||||
|
this._isOnlineAvailable = true;
|
||||||
|
};
|
||||||
|
|
||||||
function getRequestWithOfflineSupport(url) {
|
// Constants
|
||||||
if (isOnlineAvailable) {
|
ZoteroSdk.prototype.ZOTERO_API_VERSION = "3";
|
||||||
return getRequest(url);
|
ZoteroSdk.prototype.USER_AGENT = "AscDesktopEditor";
|
||||||
} else {
|
ZoteroSdk.prototype.DEFAULT_FORMAT = "csljson";
|
||||||
return getDesktopRequest(url.href);
|
ZoteroSdk.prototype.STORAGE_KEYS = {
|
||||||
}
|
USER_ID: "zoteroUserId",
|
||||||
}
|
API_KEY: "zoteroApiKey",
|
||||||
|
};
|
||||||
|
ZoteroSdk.prototype.API_PATHS = {
|
||||||
|
USERS: "users",
|
||||||
|
GROUPS: "groups",
|
||||||
|
ITEMS: "items",
|
||||||
|
KEYS: "keys",
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a GET request to the local Zotero API.
|
* Get appropriate base URL based on online/offline mode
|
||||||
* @param {string} url - URL of the request.
|
*/
|
||||||
* @returns {Promise<{responseStatus: number, responseText: string, status: string, statusCode: number}>}
|
ZoteroSdk.prototype._getBaseUrl = function () {
|
||||||
*/
|
return this._isOnlineAvailable
|
||||||
function getDesktopRequest(url) {
|
? zoteroEnvironment.restApiUrl
|
||||||
return new Promise(function (resolve, reject) {
|
: zoteroEnvironment.desktopApiUrl;
|
||||||
window.AscSimpleRequest.createRequest({
|
};
|
||||||
url: url,
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Zotero-API-Version": "3",
|
|
||||||
"User-Agent": "AscDesktopEditor",
|
|
||||||
},
|
|
||||||
complete: function(e) {
|
|
||||||
resolve(e);
|
|
||||||
},
|
|
||||||
error: function(e) {
|
|
||||||
if ( e.statusCode == -102 ) {
|
|
||||||
e.statusCode = 404;
|
|
||||||
e.message = "Connection to Zotero failed. Make sure Zotero is running";
|
|
||||||
}
|
|
||||||
reject(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a GET request to the online Zotero API.
|
* Get locales URL based on online/offline mode
|
||||||
* @param {string} url - URL of the request.
|
*/
|
||||||
* @returns {Promise<{body: ReadableStream, bodyUsed: boolean, headers: Headers, ok: boolean, redirected: boolean, status: string, statusText: string, type: string, url: string}>}
|
ZoteroSdk.prototype._getLocalesUrl = function () {
|
||||||
*/
|
return this._isOnlineAvailable
|
||||||
function getRequest(url) {
|
? zoteroEnvironment.localesUrl
|
||||||
return new Promise(function (resolve, reject) {
|
: zoteroEnvironment.localesPath;
|
||||||
var headers = {
|
};
|
||||||
"Zotero-API-Version": "3"
|
|
||||||
};
|
|
||||||
if (apiKey) headers["Zotero-API-Key"] = apiKey;
|
|
||||||
fetch(url, {
|
|
||||||
headers: headers
|
|
||||||
}).then(function (res) {
|
|
||||||
if (!res.ok) {
|
|
||||||
reject(new Error(res.status + " " + res.statusText));
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
resolve(res);
|
|
||||||
}).catch(function (err) {
|
|
||||||
if (typeof err === "object") {
|
|
||||||
console.error(err.message);
|
|
||||||
err.message = "Connection to Zotero failed";
|
|
||||||
}
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildGetRequest(path, query) {
|
/**
|
||||||
var url = new URL(path, zoteroEnvironment.restApiUrl);
|
* Make a GET request to the local Zotero API (offline mode)
|
||||||
if (!isOnlineAvailable) {
|
* @param {string} url
|
||||||
url = new URL(path, zoteroEnvironment.desktopApiUrl);
|
* @returns {Promise<AscSimpleResponse>}
|
||||||
}
|
*/
|
||||||
for (var key in query) url.searchParams.append(key, query[key]);
|
ZoteroSdk.prototype._getDesktopRequest = function (url) {
|
||||||
return getRequestWithOfflineSupport(url);
|
var self = this;
|
||||||
}
|
return new Promise(function (resolve, reject) {
|
||||||
|
window.AscSimpleRequest.createRequest({
|
||||||
/**
|
url: url,
|
||||||
* Retrieves items from the Zotero API.
|
method: "GET",
|
||||||
* @param {string} [search] - Search query.
|
headers: {
|
||||||
* @param {string[]} [itemsID] - IDs of items to retrieve.
|
"Zotero-API-Version": self.ZOTERO_API_VERSION,
|
||||||
* @param {string} [format]
|
"User-Agent": self.USER_AGENT,
|
||||||
* @returns {Promise<{body: ReadableStream, bodyUsed: boolean, headers: Headers, ok: boolean, redirected: boolean, status: string, statusText: string, type: string, url: string}>}
|
},
|
||||||
*/
|
complete: resolve,
|
||||||
function getItems(search, itemsID, format) {
|
error: function (/** @type {AscSimpleResponse} */ error) {
|
||||||
return new Promise(function (resolve, reject) {
|
if (error.statusCode === -102) {
|
||||||
format = format || "csljson";
|
error.statusCode = 404;
|
||||||
var props = {
|
error.message =
|
||||||
format: format
|
"Connection to Zotero failed. Make sure Zotero is running";
|
||||||
};
|
|
||||||
if (search) {
|
|
||||||
props.q = search;
|
|
||||||
} else if (itemsID) {
|
|
||||||
props.itemKey = itemsID.join(',');
|
|
||||||
}
|
|
||||||
if (isOnlineAvailable) {
|
|
||||||
parseItemsResponse(buildGetRequest("users/" + userId + "/items", props), resolve, reject, userId);
|
|
||||||
} else {
|
|
||||||
parseDesktopItemsResponse(buildGetRequest("users/" + userId + "/items", props), resolve, reject, userId);
|
|
||||||
}
|
}
|
||||||
});
|
reject(error);
|
||||||
}
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function getGroupItems(search, groupId, itemsID, format) {
|
/**
|
||||||
return new Promise(function (resolve, reject) {
|
* Make a GET request to the online Zotero API
|
||||||
format = format || "csljson";
|
* @param {URL} url
|
||||||
var props = {
|
* @returns {Promise<FetchResponse>}
|
||||||
format: format
|
*/
|
||||||
};
|
ZoteroSdk.prototype._getOnlineRequest = function (url) {
|
||||||
if (search) {
|
var self = this;
|
||||||
props.q = search;
|
var headers = {
|
||||||
} else if (itemsID) {
|
"Zotero-API-Version": self.ZOTERO_API_VERSION,
|
||||||
props.itemKey = itemsID.join(',');
|
"Zotero-API-Key": self._apiKey || "",
|
||||||
}
|
};
|
||||||
if (isOnlineAvailable) {
|
|
||||||
parseItemsResponse(buildGetRequest("groups/" + groupId + "/items", props), resolve, reject, groupId);
|
|
||||||
} else {
|
|
||||||
parseDesktopItemsResponse(buildGetRequest("groups/" + groupId + "/items", props), resolve, reject, groupId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLocale(langTag) {
|
return fetch(url, { headers: headers })
|
||||||
let url = zoteroEnvironment.localesPath;
|
.then(function (response) {
|
||||||
if (isOnlineAvailable) {
|
if (!response.ok) {
|
||||||
url = zoteroEnvironment.localesUrl;
|
throw new Error(response.status + " " + response.statusText);
|
||||||
}
|
}
|
||||||
return fetch(url + "locales-" + langTag + ".xml")
|
return response;
|
||||||
.then(function (resp) { return resp.text(); });
|
})
|
||||||
}
|
.catch(function (error) {
|
||||||
|
console.error("Zotero API request failed:", error.message);
|
||||||
|
if (typeof error === "object") {
|
||||||
|
error.message = "Connection to Zotero failed";
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function getUserGroups() {
|
/**
|
||||||
return new Promise(function (resolve, reject) {
|
* Universal request handler with offline support
|
||||||
if (userGroups.length > 0) {
|
* @param {URL} url
|
||||||
resolve(userGroups);
|
* @returns {Promise<AscSimpleResponse | FetchResponse>}
|
||||||
} else {
|
*/
|
||||||
buildGetRequest("users/" + userId + "/groups").then(function (res) {
|
ZoteroSdk.prototype._getRequestWithOfflineSupport = function (url) {
|
||||||
if (isOnlineAvailable) {
|
return this._isOnlineAvailable
|
||||||
if (!res.ok)
|
? this._getOnlineRequest(url)
|
||||||
throw new Error(res.status + " " + res.statusText);
|
: this._getDesktopRequest(url.href);
|
||||||
return res.json();
|
};
|
||||||
}
|
|
||||||
return JSON.parse(res.responseText);
|
/**
|
||||||
}).then(function (res) {
|
* Build URL and make GET request
|
||||||
res.forEach(function(el) {
|
* @param {string} path
|
||||||
userGroups.push({
|
* @param {*} [queryParams]
|
||||||
id: el.id,
|
* @returns {Promise<AscSimpleResponse | FetchResponse>}
|
||||||
name: el.data.name
|
*/
|
||||||
});
|
ZoteroSdk.prototype._buildGetRequest = function (path, queryParams) {
|
||||||
});
|
queryParams = queryParams || {};
|
||||||
resolve(userGroups);
|
var url = new URL(path, this._getBaseUrl());
|
||||||
}).catch(function (err) {
|
|
||||||
reject(err);
|
Object.keys(queryParams).forEach(function (key) {
|
||||||
|
if (queryParams[key] !== undefined && queryParams[key] !== null) {
|
||||||
|
url.searchParams.append(key, queryParams[key]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this._getRequestWithOfflineSupport(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse link header for pagination
|
||||||
|
* @param {string} headerValue
|
||||||
|
* @returns {{[key: string]: string}}
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype._parseLinkHeader = function (headerValue) {
|
||||||
|
/** @type {{[key: string]: string}} */
|
||||||
|
var links = {};
|
||||||
|
var linkHeaderRegex = /<(.*?)>; rel="(.*?)"/g;
|
||||||
|
|
||||||
|
if (!headerValue) return links;
|
||||||
|
|
||||||
|
var match;
|
||||||
|
while ((match = linkHeaderRegex.exec(headerValue.trim())) !== null) {
|
||||||
|
links[match[2]] = match[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return links;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse response for desktop (offline) mode
|
||||||
|
* @param {Promise<AscSimpleResponse>} promise
|
||||||
|
* @param {function(any): void} resolve
|
||||||
|
* @param {function(any): void} reject
|
||||||
|
* @param {number} id
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype._parseDesktopItemsResponse = function (
|
||||||
|
promise,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
id
|
||||||
|
) {
|
||||||
|
var self = this;
|
||||||
|
return promise
|
||||||
|
.then(function (response) {
|
||||||
|
return {
|
||||||
|
items: { items: JSON.parse(response.responseText) },
|
||||||
|
id: id,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.then(resolve)
|
||||||
|
.catch(reject);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse response for online mode with pagination support
|
||||||
|
* @param {Promise<FetchResponse>} promise
|
||||||
|
* @param {function(any): void} resolve
|
||||||
|
* @param {function(any): void} reject
|
||||||
|
* @param {number} id
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype._parseItemsResponse = function (
|
||||||
|
promise,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
id
|
||||||
|
) {
|
||||||
|
var self = this;
|
||||||
|
return promise
|
||||||
|
.then(function (response) {
|
||||||
|
return Promise.all([response.json(), response]);
|
||||||
|
})
|
||||||
|
.then(function (results) {
|
||||||
|
var json = results[0];
|
||||||
|
var response = results[1];
|
||||||
|
var links = self._parseLinkHeader(
|
||||||
|
response.headers.get("Link") || ""
|
||||||
|
);
|
||||||
|
/** @type {{items: any, id: number, next?: function(): Promise<void>}} */
|
||||||
|
var result = {
|
||||||
|
items: json,
|
||||||
|
id: id,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (links.next) {
|
||||||
|
result.next = function () {
|
||||||
|
return new Promise(function (rs, rj) {
|
||||||
|
self._parseItemsResponse(
|
||||||
|
self._getOnlineRequest(new URL(links.next)),
|
||||||
|
rs,
|
||||||
|
rj,
|
||||||
|
id
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getUserId() {
|
|
||||||
return userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function format(ids, key, style, locale) {
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
var request;
|
|
||||||
if (key) {
|
|
||||||
request = buildGetRequest("groups/" + key + "/items", {
|
|
||||||
format: "bib",
|
|
||||||
style: style,
|
|
||||||
locale: locale,
|
|
||||||
itemKey: ids.join(",")
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
request = buildGetRequest("users/" + userId + "/items", {
|
|
||||||
format: "bib",
|
|
||||||
style: style,
|
|
||||||
locale: locale,
|
|
||||||
itemKey: ids.join(",")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
request.then(function (res) {
|
|
||||||
resolve(res.text());
|
|
||||||
}).catch(function (err) {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setApiKey(key) {
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
buildGetRequest("keys/" + key)
|
|
||||||
.then(function (res) {
|
|
||||||
if (!res.ok) throw new Error(res.status + " " + res.statusText);
|
|
||||||
return res.json();
|
|
||||||
}).then(function (res) {
|
|
||||||
saveSettings(res.userID, key);
|
|
||||||
resolve(true);
|
|
||||||
}).catch(function (err) {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function applySettings(id, key) {
|
|
||||||
userId = id;
|
|
||||||
apiKey = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveSettings(id, key) {
|
|
||||||
applySettings(id, key);
|
|
||||||
localStorage.setItem("zoteroUserId", id);
|
|
||||||
localStorage.setItem("zoteroApiKey", key);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSettings() {
|
|
||||||
var uid = localStorage.getItem("zoteroUserId");
|
|
||||||
var key = localStorage.getItem("zoteroApiKey");
|
|
||||||
|
|
||||||
var configured = !(!uid || !key);
|
|
||||||
if (configured) applySettings(uid, key);
|
|
||||||
return configured;
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearSettings() {
|
|
||||||
localStorage.removeItem("zoteroUserId");
|
|
||||||
localStorage.removeItem("zoteroApiKey");
|
|
||||||
userGroups = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Promise} promise - promise from items request
|
|
||||||
* @param {function} resolve - resolve function for returned promise
|
|
||||||
* @param {function} reject - reject function for returned promise
|
|
||||||
* @param {string} id - id of request
|
|
||||||
* @returns {Promise<{items: {items: Array<Object>}, id: string}}>} promise with items and optional next function
|
|
||||||
*/
|
|
||||||
function parseDesktopItemsResponse(promise, resolve, reject, id) {
|
|
||||||
promise.then(function (res) {
|
|
||||||
var obj = {
|
|
||||||
items: {items: JSON.parse(res.responseText)},
|
|
||||||
id: id
|
|
||||||
};
|
};
|
||||||
resolve(obj);
|
|
||||||
}).catch(function (err) {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Promise} promise - promise from items request
|
|
||||||
* @param {function} resolve - resolve function for returned promise
|
|
||||||
* @param {function} reject - reject function for returned promise
|
|
||||||
* @param {string} id - id of request
|
|
||||||
* @returns {Promise<{items: {items: Array<Object>}, id: string}}>} promise with items and optional next function
|
|
||||||
*/
|
|
||||||
function parseItemsResponse(promise, resolve, reject, id) {
|
|
||||||
promise.then(function (res) {
|
|
||||||
return res.json().then(function (json) {
|
|
||||||
var links = parseLinkHeader(res.headers.get("Link"));
|
|
||||||
var obj = {
|
|
||||||
items: json,
|
|
||||||
id: id
|
|
||||||
};
|
|
||||||
if (links.next) {
|
|
||||||
obj.next = function () {
|
|
||||||
return new Promise(function (rs, rj) {
|
|
||||||
parseItemsResponse(getRequest(links.next), rs, rj, id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve(obj);
|
|
||||||
});
|
|
||||||
}).catch(function (err) {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var linkHeaderRegex = /<(.*?)>; rel="(.*?)"/g;
|
|
||||||
function parseLinkHeader(headerValue) {
|
|
||||||
var links = {};
|
|
||||||
if (!headerValue) return links;
|
|
||||||
headerValue = headerValue.trim();
|
|
||||||
if (!headerValue) return links;
|
|
||||||
|
|
||||||
var match;
|
|
||||||
while ((match = linkHeaderRegex.exec(headerValue)) !== null) {
|
|
||||||
links[match[2]] = match[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return links;
|
resolve(result);
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Universal items response parser
|
||||||
|
* @param {Promise<AscSimpleResponse | FetchResponse>} promise
|
||||||
|
* @param {function(any): void} resolve
|
||||||
|
* @param {function(any): void} reject
|
||||||
|
* @param {number} id
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype._parseResponse = function (promise, resolve, reject, id) {
|
||||||
|
if (this._isOnlineAvailable) {
|
||||||
|
const fetchPromise = /** @type {Promise<FetchResponse>} */ (promise);
|
||||||
|
this._parseItemsResponse(fetchPromise, resolve, reject, id);
|
||||||
|
} else {
|
||||||
|
const ascSimplePromise = /** @type {Promise<AscSimpleResponse>} */ (
|
||||||
|
promise
|
||||||
|
);
|
||||||
|
this._parseDesktopItemsResponse(
|
||||||
|
/** @type {Promise<AscSimpleResponse>} */ ascSimplePromise,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get items from user library
|
||||||
|
* @param {string} search
|
||||||
|
* @param {string[]} itemsID
|
||||||
|
* @param {"csljson"|"json"} format
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype.getItems = function (search, itemsID, format) {
|
||||||
|
var self = this;
|
||||||
|
format = format || self.DEFAULT_FORMAT;
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var queryParams =
|
||||||
|
/** @type {{format: string, q?: string, itemKey?: string}} */ ({
|
||||||
|
format: format,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
queryParams.q = search;
|
||||||
|
} else if (itemsID) {
|
||||||
|
queryParams.itemKey = itemsID.join(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
function setIsOnlineAvailable(isOnline) {
|
var path =
|
||||||
isOnlineAvailable = isOnline;
|
self.API_PATHS.USERS +
|
||||||
|
"/" +
|
||||||
|
self._userId +
|
||||||
|
"/" +
|
||||||
|
self.API_PATHS.ITEMS;
|
||||||
|
var request = self._buildGetRequest(path, queryParams);
|
||||||
|
|
||||||
|
self._parseResponse(request, resolve, reject, self._userId);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get items from group library
|
||||||
|
* @param {string} search
|
||||||
|
* @param {number} groupId
|
||||||
|
* @param {string[]} itemsID
|
||||||
|
* @param {"csljson"|"json"} format
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype.getGroupItems = function (
|
||||||
|
search,
|
||||||
|
groupId,
|
||||||
|
itemsID,
|
||||||
|
format
|
||||||
|
) {
|
||||||
|
var self = this;
|
||||||
|
format = format || self.DEFAULT_FORMAT;
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var queryParams =
|
||||||
|
/** @type {{format: string, q?: string, itemKey?: string}} */ ({
|
||||||
|
format: format,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
queryParams.q = search;
|
||||||
|
} else if (itemsID) {
|
||||||
|
queryParams.itemKey = itemsID.join(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
var path =
|
||||||
getItems: getItems,
|
self.API_PATHS.GROUPS + "/" + groupId + "/" + self.API_PATHS.ITEMS;
|
||||||
getGroupItems: getGroupItems,
|
var request = self._buildGetRequest(path, queryParams);
|
||||||
getUserGroups: getUserGroups,
|
|
||||||
format: format,
|
self._parseResponse(request, resolve, reject, groupId);
|
||||||
hasSettings: getSettings,
|
});
|
||||||
clearSettings: clearSettings,
|
};
|
||||||
setApiKey: setApiKey,
|
|
||||||
getUserId: getUserId,
|
/**
|
||||||
getLocale: getLocale,
|
* Get user groups
|
||||||
setIsOnlineAvailable: setIsOnlineAvailable
|
* @returns {Promise<Array<UserGroupInfo>>}
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype.getUserGroups = function () {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
if (self._userGroups.length > 0) {
|
||||||
|
resolve(self._userGroups);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
var path = self.API_PATHS.USERS + "/" + self._userId + "/groups";
|
||||||
|
|
||||||
|
self._buildGetRequest(path)
|
||||||
|
.then(function (
|
||||||
|
/** @type {FetchResponse | AscSimpleResponse} */ response
|
||||||
|
) {
|
||||||
|
if (self._isOnlineAvailable) {
|
||||||
|
var fetchResponse = /** @type {FetchResponse} */ (response);
|
||||||
|
if (!fetchResponse.ok) {
|
||||||
|
throw new Error(
|
||||||
|
fetchResponse.status +
|
||||||
|
" " +
|
||||||
|
fetchResponse.statusText
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return fetchResponse.json();
|
||||||
|
}
|
||||||
|
var ascSimpleResponse = /** @type {AscSimpleResponse} */ (
|
||||||
|
response
|
||||||
|
);
|
||||||
|
return JSON.parse(ascSimpleResponse.responseText);
|
||||||
|
})
|
||||||
|
.then(function (groups) {
|
||||||
|
self._userGroups = groups.map(function (
|
||||||
|
/** @type {ZoteroGroupInfo} */ group
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
id: group.id,
|
||||||
|
name: group.data.name,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
resolve(self._userGroups);
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format citations
|
||||||
|
*/
|
||||||
|
/*ZoteroSdk.prototype.format = function (ids, groupKey, style, locale) {
|
||||||
|
var queryParams = {
|
||||||
|
format: "bib",
|
||||||
|
style: style,
|
||||||
|
locale: locale,
|
||||||
|
itemKey: ids.join(","),
|
||||||
|
};
|
||||||
|
|
||||||
|
var path = groupKey
|
||||||
|
? this.API_PATHS.GROUPS + "/" + groupKey + "/" + this.API_PATHS.ITEMS
|
||||||
|
: this.API_PATHS.USERS +
|
||||||
|
"/" +
|
||||||
|
this._userId +
|
||||||
|
"/" +
|
||||||
|
this.API_PATHS.ITEMS;
|
||||||
|
|
||||||
|
return this._buildGetRequest(path, queryParams).then(function (response) {
|
||||||
|
return response.text();
|
||||||
|
});
|
||||||
|
};*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get locale data
|
||||||
|
* @param {string} langTag
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype.getLocale = function (langTag) {
|
||||||
|
var url = this._getLocalesUrl() + "locales-" + langTag + ".xml";
|
||||||
|
return fetch(url).then(function (response) {
|
||||||
|
return response.text();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set API key and validate it
|
||||||
|
* @param {string} key
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype.setApiKey = function (key) {
|
||||||
|
var self = this;
|
||||||
|
var path = this.API_PATHS.KEYS + "/" + key;
|
||||||
|
|
||||||
|
return this._buildGetRequest(path)
|
||||||
|
.then(function (response) {
|
||||||
|
var fetchResponse = /** @type {FetchResponse} */ (response);
|
||||||
|
if (!fetchResponse.ok) {
|
||||||
|
throw new Error(
|
||||||
|
fetchResponse.status + " " + fetchResponse.statusText
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return fetchResponse.json();
|
||||||
|
})
|
||||||
|
.then(function (/** @type {any} */ keyData) {
|
||||||
|
self._saveSettings(keyData.userID, key);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply settings to current session
|
||||||
|
* @param {number} userId
|
||||||
|
* @param {string} apiKey
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype._applySettings = function (userId, apiKey) {
|
||||||
|
this._userId = userId;
|
||||||
|
this._apiKey = apiKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save settings to localStorage
|
||||||
|
* @param {number} userId
|
||||||
|
* @param {string} apiKey
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype._saveSettings = function (userId, apiKey) {
|
||||||
|
this._applySettings(userId, apiKey);
|
||||||
|
localStorage.setItem(this.STORAGE_KEYS.USER_ID, String(userId));
|
||||||
|
localStorage.setItem(this.STORAGE_KEYS.API_KEY, apiKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load settings from localStorage
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype.hasSettings = function () {
|
||||||
|
var userId = localStorage.getItem(this.STORAGE_KEYS.USER_ID);
|
||||||
|
var apiKey = localStorage.getItem(this.STORAGE_KEYS.API_KEY);
|
||||||
|
|
||||||
|
if (userId && apiKey) {
|
||||||
|
this._applySettings(Number(userId), apiKey);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear stored settings
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype.clearSettings = function () {
|
||||||
|
localStorage.removeItem(this.STORAGE_KEYS.USER_ID);
|
||||||
|
localStorage.removeItem(this.STORAGE_KEYS.API_KEY);
|
||||||
|
this._userGroups = [];
|
||||||
|
this._userId = 0;
|
||||||
|
this._apiKey = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user ID
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype.getUserId = function () {
|
||||||
|
return this._userId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set online availability
|
||||||
|
* @param {boolean} isOnline
|
||||||
|
*/
|
||||||
|
ZoteroSdk.prototype.setIsOnlineAvailable = function (isOnline) {
|
||||||
|
this._isOnlineAvailable = isOnline;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user