Separate Link from Page
This commit is contained in:
parent
4ea257780d
commit
8345c69c60
|
@ -1,6 +1,7 @@
|
|||
:root {
|
||||
--error-color: red;
|
||||
--success-color: green;
|
||||
--fandom-redlink: #ba0000;
|
||||
}
|
||||
|
||||
|
||||
|
@ -67,6 +68,14 @@
|
|||
font-weight: normal;
|
||||
}
|
||||
|
||||
#networkTable a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.redLink a {
|
||||
color: var(--fandom-redlink);
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* Loading icon
|
||||
|
@ -120,7 +129,3 @@ input[data-entered=true]:invalid {
|
|||
border-color: var(--error-color);
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.redLink a {
|
||||
color: #ba0000;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@ import {html} from "htm/preact";
|
|||
/**
|
||||
* A data class for combining a language and page title to identify a page.
|
||||
*
|
||||
* This is only an _identifier_ of a page, not the page itself. For information on the page such as the links it
|
||||
* contains, whether it's a redirect, etc., see the `Page` class.
|
||||
*
|
||||
* @property lang {string} the language of the wiki this page is of
|
||||
* @property title {string} the title of the page
|
||||
*/
|
||||
|
@ -105,6 +108,31 @@ export class InterwikiMap {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a page, i.e. what you get if you follow an `InterlangLink`.
|
||||
*
|
||||
* @property url {URL} the full URL at which this page is located
|
||||
* @property link {InterlangLink} the interlanguage link describing the location of the page
|
||||
* @property linksTo {InterlangLink[]} the interlanguage links contained in this page
|
||||
* @property exists {boolean} `true` if and only if this page exists
|
||||
*/
|
||||
export class Page {
|
||||
/**
|
||||
* Constructs a new `Page`.
|
||||
*
|
||||
* @param url {URL} the full URL at which this page is located
|
||||
* @param link {InterlangLink} the interlanguage link describing the location of the page
|
||||
* @param linksTo {InterlangLink[]} the interlanguage links contained in this page
|
||||
* @param exists {boolean} `true` if and only if this page exists
|
||||
*/
|
||||
constructor(url, link, linksTo, exists) {
|
||||
this.url = url;
|
||||
this.link = link;
|
||||
this.linksTo = linksTo;
|
||||
this.exists = exists;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interacts with the API in an asynchronous manner.
|
||||
|
@ -276,66 +304,58 @@ export class MediaWikiManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the article paths of all `MediaWiki`s known to this manager.
|
||||
* Returns the URL to the given article.
|
||||
*
|
||||
* @returns {Object.<string, string>} the article paths of all languages known to this manager
|
||||
* @param link {InterlangLink} the link to return the URL of
|
||||
* @returns {URL} the URL to the given article
|
||||
*/
|
||||
getArticlePaths() {
|
||||
return Object.keys(this.mws).reduce((paths, lang) => {
|
||||
paths[lang] = this._iwMap.getUrl(lang).slice(0, -2);
|
||||
return paths;
|
||||
}, {});
|
||||
getArticlePath(link) {
|
||||
return new URL(this._iwMap.getUrl(link.lang).replace("$1", link.title));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A network of interlanguage links.
|
||||
*
|
||||
* @property pages {Page[]} the pages in the network}
|
||||
*/
|
||||
export class InterlangNetwork {
|
||||
/**
|
||||
* Constructs a new `InterlangNetwork`.
|
||||
*
|
||||
* @param mappings {Object.<string, InterlangLink[]>} a mapping from source page to all pages it links to
|
||||
* @param missing {InterlangLink[]} list of articles that do not exist
|
||||
* @param articlePaths = {Object.<string, string>} the article paths of each language
|
||||
* @param pages {Page[]} the pages in the network
|
||||
*/
|
||||
constructor(mappings, missing, articlePaths) {
|
||||
this._mappings = mappings;
|
||||
this._missing = missing;
|
||||
this._articlePaths = articlePaths;
|
||||
constructor(pages) {
|
||||
this.pages = pages.sort((a, b) => b.link.toString() - a.link.toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an appropriate label for the given link.
|
||||
* Returns an appropriate label for the given page.
|
||||
*
|
||||
* The label itself is an `a` `HTMLElement` linking to the editor for the given link.
|
||||
* The text in the label is just the language of the link if the given link is the only link with that language
|
||||
* in this network, or the entire link otherwise.
|
||||
* The label contains a link to the page and a few buttons to help the user interact with that page. The label's
|
||||
* appearance and contents depend both on the properties of the page (e.g. whether it exists and whether it's a
|
||||
* redirect page) and on the other pages in this network (e.g. whether it's the only page in its language).
|
||||
*
|
||||
* @param linkStr {string} the link to generate a label of
|
||||
* @return {VNode} an appropriate label with icons for the given link
|
||||
* @param page {Page} the page to generate a label of
|
||||
* @return {VNode} an appropriate label with icons for the given page
|
||||
*/
|
||||
_generateLabel(linkStr) {
|
||||
const link = InterlangLink.fromString(linkStr);
|
||||
const url = this._articlePaths[link.lang] + link.title;
|
||||
const isMissing = this._missing.some(it => it.equals(link));
|
||||
const labelText =
|
||||
Object.keys(this._mappings).filter(it => link.lang === InterlangLink.fromString(it).lang).length > 1
|
||||
? linkStr
|
||||
: link.lang;
|
||||
_generateLabel(page) {
|
||||
const labelText = this.pages.some(it => it.link.lang === page.link.lang && !it.link.equals(page.link))
|
||||
? page.link.toString()
|
||||
: page.link.lang;
|
||||
|
||||
const onClickCopy = (event) => {
|
||||
navigator.clipboard.writeText(`[[${linkStr}]]`);
|
||||
navigator.clipboard.writeText(`[[${page.link}]]`);
|
||||
event.target.classList.replace("fa-clipboard", "fa-check");
|
||||
setTimeout(() => event.target.classList.replace("fa-check", "fa-clipboard"), 1000);
|
||||
};
|
||||
|
||||
return html`
|
||||
<span class="${isMissing ? "redLink" : ""}">
|
||||
<a href="${url}" target="_blank" title="${linkStr}">${labelText}</a>
|
||||
<span class="${page.exists ? "" : "redLink"}">
|
||||
<a href="${page.url}" target="_blank" title="${page.link}">${labelText}</a>
|
||||
<span> </span>
|
||||
<a href="${url}?action=edit" target="_blank" title="Edit"><i class="fa fa-pencil"></i></a>
|
||||
<a href="${page.url}?action=edit" target="_blank" title="Edit"><i class="fa fa-pencil"></i></a>
|
||||
<a title="Copy"><i class="fa fa-clipboard" onclick="${onClickCopy}"></i></a>
|
||||
</span>
|
||||
`;
|
||||
|
@ -350,13 +370,13 @@ export class InterlangNetwork {
|
|||
const topRow = html`
|
||||
<tr>
|
||||
<th class="sourceLabel" rowspan="2">Source</th>
|
||||
<th colspan="${Object.keys(this._mappings).length}">Destination</th>
|
||||
<th colspan="${this.pages.length}">Destination</th>
|
||||
</tr>
|
||||
`;
|
||||
const bottomRow = html`
|
||||
<tr>
|
||||
${Object.keys(this._mappings).sort().map(key => html`<th>${this._generateLabel(key)}</th>`)}
|
||||
</tr>
|
||||
${this.pages.map(page => html`<th>${this._generateLabel(page)}</th>`)}
|
||||
</tr>
|
||||
`;
|
||||
|
||||
return html`<thead>${topRow}${bottomRow}</thead>`;
|
||||
|
@ -368,16 +388,15 @@ export class InterlangNetwork {
|
|||
* @return {HTMLElement} the body of the table generated by `#toTable`
|
||||
*/
|
||||
_generateTableBody() {
|
||||
const rows = Object.keys(this._mappings).sort().map(srcKey => {
|
||||
const label = html`<th class="sourceLabel">${this._generateLabel(srcKey)}</th>`;
|
||||
const rows = this.pages.map(srcPage => {
|
||||
const label = html`<th class="sourceLabel">${this._generateLabel(srcPage)}</th>`;
|
||||
|
||||
const cells = Object.keys(this._mappings).sort().map(dstKey => {
|
||||
if (srcKey !== dstKey) {
|
||||
const isPresent = this._mappings[srcKey].some(it => it.equals(InterlangLink.fromString(dstKey)));
|
||||
return html`<td class="${isPresent ? "correct" : "incorrect"}">${isPresent ? "✓" : "✗"}</td>`;
|
||||
} else {
|
||||
const cells = this.pages.map(dstPage => {
|
||||
if (srcPage.link.equals(dstPage.link))
|
||||
return html`<td></td>`;
|
||||
}
|
||||
|
||||
const isPresent = srcPage.linksTo.some(it => it.equals(dstPage.link));
|
||||
return html`<td class="${isPresent ? "correct" : "incorrect"}">${isPresent ? "✓" : "✗"}</td>`;
|
||||
});
|
||||
|
||||
return html`<tr>${label}${cells}</tr>`;
|
||||
|
@ -406,8 +425,7 @@ export class InterlangNetwork {
|
|||
* @returns {Promise<InterlangNetwork>} a network of interlanguage links
|
||||
*/
|
||||
static async discoverNetwork(mwm, title, progressCb) {
|
||||
const mappings = {};
|
||||
const missing = [];
|
||||
const pages = [];
|
||||
|
||||
const history = [];
|
||||
const queue = [new InterlangLink(mwm.baseLang, title)];
|
||||
|
@ -423,19 +441,21 @@ export class InterlangNetwork {
|
|||
.getLangLinks([next.title])
|
||||
.then(langlinks => langlinks[next.title])
|
||||
.then(langlinks => {
|
||||
mappings[next.toString()] = [];
|
||||
const url = mwm.getArticlePath(next);
|
||||
if (langlinks === undefined) {
|
||||
missing.push(next);
|
||||
pages.push(new Page(url, next, [], false));
|
||||
return;
|
||||
}
|
||||
if (langlinks.length === 0)
|
||||
if (langlinks.length === 0) {
|
||||
pages.push(new Page(url, next, [], true));
|
||||
return;
|
||||
}
|
||||
|
||||
mappings[next.toString()] = langlinks;
|
||||
pages.push(new Page(url, next, langlinks, true));
|
||||
queue.push(...langlinks);
|
||||
});
|
||||
}
|
||||
|
||||
return new InterlangNetwork(mappings, missing, mwm.getArticlePaths());
|
||||
return new InterlangNetwork(pages);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue