Resolve language links from relative sites
This commit is contained in:
parent
c8d4925841
commit
89a7224816
210
index.html
210
index.html
|
@ -70,8 +70,14 @@
|
|||
<script src="https://static.fwdekker.com/js/common.js" crossorigin="anonymous"></script>
|
||||
<script src="MediawikiJS.js" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
|
||||
/**
|
||||
* A data class for combining a language and page title to identify a page.
|
||||
*
|
||||
* @property lang {string} the language of the wiki this page is of
|
||||
* @property title {string} the title of the page
|
||||
*/
|
||||
class InterlangLink {
|
||||
/**
|
||||
|
@ -117,10 +123,67 @@
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of interwiki links.
|
||||
*
|
||||
* @property map {Object.<string, string>} maps interwiki abbreviations to URLs
|
||||
*/
|
||||
class InterwikiMap {
|
||||
/**
|
||||
* Constructs a new interwiki map.
|
||||
*
|
||||
* @param map {Object.<string, string>} the mapping from interwiki abbreviations to URLs to store in this map
|
||||
*/
|
||||
constructor(map) {
|
||||
this.map = Object.assign({}, map);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the URL for the given abbreviation, or `undefined` if the abbreviation could not be found.
|
||||
*
|
||||
* @param abbr {string} the abbreviation to return the URL of
|
||||
* @returns {string} the URL for the given abbreviation, or `undefined` if the abbreviation could not be found
|
||||
*/
|
||||
getUrl(abbr) {
|
||||
return this.map[abbr];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if and only if this map has a URL for the given abbreviation.
|
||||
*
|
||||
* @param abbr {string} the abbreviation to check for
|
||||
* @returns {boolean} `true` if and only if this map has a URL for the given abbreviation
|
||||
*/
|
||||
hasUrl(abbr) {
|
||||
return this.map[abbr] !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new `InterwikiMap` with all entries from both this map and the given map.
|
||||
*
|
||||
* @param other {InterwikiMap} the map to merge with
|
||||
* @return a new `InterwikiMap` with all entries from both this map and the given map
|
||||
*/
|
||||
mergeWith(other) {
|
||||
const conflicts = Object.keys(this.map)
|
||||
.filter(key => other.map.hasOwnProperty(key))
|
||||
.filter(key => this.map[key] !== other.map[key]);
|
||||
if (conflicts.length !== 0)
|
||||
console.warn("iw map merge conflict(s)", conflicts);
|
||||
|
||||
return new InterwikiMap({...this.map, ...other.map});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interacts with the API in an asynchronous manner.
|
||||
*
|
||||
* Wraps around the MediawikiJS library.
|
||||
*
|
||||
* @property baseUrl {string} the origin of the wiki's API
|
||||
* @property apiPath {string} the path relative to the wiki's API
|
||||
*/
|
||||
class MediaWiki {
|
||||
/**
|
||||
|
@ -130,7 +193,9 @@
|
|||
*/
|
||||
constructor(baseUrl) {
|
||||
const urlObj = new URL(baseUrl);
|
||||
this.mwjs = MediaWikiJS({baseURL: urlObj.origin, apiPath: urlObj.pathname});
|
||||
this._mwjs = MediaWikiJS({baseURL: urlObj.origin, apiPath: urlObj.pathname});
|
||||
this.baseUrl = this._mwjs.baseURL;
|
||||
this.apiPath = this._mwjs.apiPath;
|
||||
}
|
||||
|
||||
|
||||
|
@ -141,7 +206,8 @@
|
|||
* @return {Promise<Object>} the API's response
|
||||
*/
|
||||
request(params) {
|
||||
return new Promise(resolve => this.mwjs.send(params, it => resolve(it)));
|
||||
console.debug(`Requesting from ${this.baseUrl}/${this.apiPath} with param`, params);
|
||||
return new Promise(resolve => this._mwjs.send(params, it => resolve(it)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,7 +236,7 @@
|
|||
/**
|
||||
* Requests all interwiki abbreviations available on this wiki.
|
||||
*
|
||||
* @return {Promise<Object.<string, string>>} a mapping from
|
||||
* @return {Promise<InterwikiMap>} a mapping from
|
||||
*/
|
||||
getIwMap() {
|
||||
return this
|
||||
|
@ -180,40 +246,61 @@
|
|||
map[mapping["prefix"]] = mapping["url"];
|
||||
return map;
|
||||
}, {})
|
||||
);
|
||||
)
|
||||
.then(map => new InterwikiMap(map));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages a `MediaWiki` instance for different languages, caching retrieved information for re-use.
|
||||
*
|
||||
* @property mws {Object.<string, MediaWiki>} the cached `MediaWiki` instances
|
||||
*/
|
||||
class MediaWikiManager {
|
||||
/**
|
||||
* Constructs a new `MediaWikiManager`.
|
||||
*
|
||||
* The `#init` method **must** be called before invoking any other function. Behavior is undefined otherwise.
|
||||
*/
|
||||
constructor() {
|
||||
this.mws = {};
|
||||
this.iwMaps = {};
|
||||
this._iwMap = new InterwikiMap({});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stores the given `MediaWiki` in this manager.
|
||||
* Initializes this `MediaWikiManager`.
|
||||
*
|
||||
* @param lang {string} the language of the `MediaWiki`
|
||||
* @param mw {MediaWiki} the `MediaWiki` to store
|
||||
* @param baseLang {string} the language for the `MediaWiki` that is used as a starting point
|
||||
* @param baseMw {MediaWiki} the `MediaWiki` that is used as a starting point
|
||||
* @return {MediaWikiManager} this `MediaWikiManager`
|
||||
*/
|
||||
addMw(lang, mw) {
|
||||
this.mws[lang] = mw;
|
||||
async init(baseLang, baseMw) {
|
||||
this.apiPath = baseMw.apiPath;
|
||||
this.mws[baseLang] = baseMw;
|
||||
await this._importIwMap(baseMw);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the `MediaWiki` for the given language, or `undefined` if it has not been stored.
|
||||
* Returns the `MediaWiki` for the given language, creating it if necessary, or `undefined` if it it could not
|
||||
* be created.
|
||||
*
|
||||
* @param lang {string} the language of the `MediaWiki` to return
|
||||
* @returns {MediaWiki} the `MediaWiki` for the given language, or `undefined` if it has not been stored
|
||||
* @returns {MediaWiki} the `MediaWiki` for the given language, or `undefined` if it could not be created
|
||||
*/
|
||||
getMw(lang) {
|
||||
async getMw(lang) {
|
||||
if (this.hasMw(lang))
|
||||
return this.mws[lang];
|
||||
|
||||
if (!this._iwMap.hasUrl(lang))
|
||||
return undefined;
|
||||
|
||||
const url = this._iwMap.getUrl(lang);
|
||||
this.mws[lang] = new MediaWiki(url.slice(0, -7) + "/" + this.apiPath);
|
||||
await this._importIwMap(this.mws[lang]);
|
||||
|
||||
return this.mws[lang];
|
||||
}
|
||||
|
||||
|
@ -224,33 +311,17 @@
|
|||
* @returns {boolean} `true` if and only if this manager has a `MediaWiki` for the given language
|
||||
*/
|
||||
hasMw(lang) {
|
||||
return this.getMw(lang) !== undefined;
|
||||
return this.mws[lang] !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interwiki map for the given language, fetching it from the server if necessary.
|
||||
* Imports the interwiki map from the given wiki, fetching it from the server and merging it into this manager's
|
||||
* main interwiki map.
|
||||
*
|
||||
* @param lang {string} the language to return the interwiki map for
|
||||
* @return {Promise<Object.<string, number>>} the interwiki map for the given language
|
||||
* @param mw {MediaWiki} the `MediaWiki` to import the interwiki map of
|
||||
*/
|
||||
getIwMap(lang) {
|
||||
if (!this.hasMw(lang))
|
||||
return null;
|
||||
|
||||
if (!this.hasIwMap(lang))
|
||||
this.iwMaps[lang] = this.getMw(lang).getIwMap();
|
||||
|
||||
return this.iwMaps[lang];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if and only if this manager has an interwiki table for the given language.
|
||||
*
|
||||
* @param lang {string} the language of the interwiki table to check presence of
|
||||
* @returns {boolean} `true` if and only if this manager has an interwiki table for the given language
|
||||
*/
|
||||
hasIwMap(lang) {
|
||||
return this.iwMaps[lang] !== undefined;
|
||||
async _importIwMap(mw) {
|
||||
this._iwMap = this._iwMap.mergeWith(await mw.getIwMap());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,65 +332,54 @@
|
|||
/**
|
||||
* Constructs a new `InterlangNetwork`.
|
||||
*
|
||||
* @param mappings {Object.<InterlangLink, InterlangLink[]>} a mapping from source page to all pages it links to
|
||||
* @param mappings {Object.<string, InterlangLink[]>} a mapping from source page to all pages it links to
|
||||
*/
|
||||
constructor(mappings) {
|
||||
this.mappings = mappings || {};
|
||||
}
|
||||
|
||||
|
||||
static async discoverNetwork(mw, title) {
|
||||
static async discoverNetwork(mwm, link) {
|
||||
const mappings = {};
|
||||
const mwm = new MediaWikiManager();
|
||||
mwm.addMw("en", mw); // TODO Support non-English `mw`
|
||||
|
||||
return mwm.getIwMap("en").then(async map => {
|
||||
const entries = [];
|
||||
const history = [];
|
||||
const queue = [link];
|
||||
while (queue.length > 0) {
|
||||
const next = queue.pop();
|
||||
if (history.some(it => it.equals(next)))
|
||||
continue;
|
||||
|
||||
const queue = [new InterlangLink("en", title)];
|
||||
while (queue.length > 0) {
|
||||
const next = queue.pop();
|
||||
if (entries.some(it => it.equals(next)))
|
||||
continue;
|
||||
history.push(next);
|
||||
const nextMw = await mwm.getMw(next.lang);
|
||||
await nextMw
|
||||
.getLangLinks([next.title], undefined)
|
||||
.then(langlinks => langlinks[next.title])
|
||||
.then(langlinks => {
|
||||
if (langlinks === undefined)
|
||||
return;
|
||||
|
||||
entries.push(next);
|
||||
if (!mwm.hasMw(next.lang)) {
|
||||
if (!map.hasOwnProperty(next.lang))
|
||||
continue;
|
||||
mappings[next.toString()] = langlinks;
|
||||
queue.push(...langlinks);
|
||||
});
|
||||
}
|
||||
|
||||
mwm.addMw(next.lang, new MediaWiki(map[next.lang].slice(0, -7) + "/api.php"));
|
||||
}
|
||||
const nextMw = mwm.getMw(next.lang);
|
||||
await nextMw.getLangLinks([next.title], undefined)
|
||||
.then(langlinks => langlinks[next.title])
|
||||
.then(langlinks => {
|
||||
if (langlinks === undefined)
|
||||
return;
|
||||
|
||||
mappings[next.toString()] = langlinks;
|
||||
queue.push(...(langlinks));
|
||||
});
|
||||
}
|
||||
|
||||
return new InterlangNetwork(mappings);
|
||||
});
|
||||
return new InterlangNetwork(mappings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const url = "https://fallout.fandom.com/api.php";
|
||||
const mw = new MediaWiki(url);
|
||||
|
||||
InterlangNetwork.discoverNetwork(mw, "Felix").then(it => console.log(it));
|
||||
|
||||
|
||||
// noinspection JSUnresolvedFunction Defined in `common.js`
|
||||
doAfterLoad(() => {
|
||||
doAfterLoad(async () => {
|
||||
const url = "https://fallout.fandom.com/api.php";
|
||||
const mw = new MediaWiki(url);
|
||||
const mwm = await new MediaWikiManager().init("en", mw);
|
||||
|
||||
const pageInput = $("#page");
|
||||
const checkButton = $("#check");
|
||||
|
||||
const submit = () => {
|
||||
window.alert("SUBMIT");
|
||||
InterlangNetwork.discoverNetwork(mwm, new InterlangLink("en", pageInput.value))
|
||||
.then(it => console.log(it));
|
||||
};
|
||||
|
||||
pageInput.addEventListener("keypress", (event) => {
|
||||
|
|
Loading…
Reference in New Issue