137 lines
5.3 KiB
TypeScript
137 lines
5.3 KiB
TypeScript
const {stringToHtml} = (window as any).fwdekker;
|
|
import {InterlangNetwork, LinkVerdict, Page, PageVerdict} from "./MediaWiki";
|
|
|
|
|
|
/**
|
|
* A network of interlanguage links.
|
|
*/
|
|
export class InterlangTable {
|
|
/**
|
|
* Generates an icon element with the given title and additional classes.
|
|
*
|
|
* @param icon the name of the icon to display, or `null` if an empty span should be returned
|
|
* @param title the title of the icon, used for the `title` attribute
|
|
* @param classes the additional classes to apply to the icon
|
|
* @return an icon element with the given title and additional classes
|
|
* @private
|
|
*/
|
|
private static createIcon(icon: string | null, title: string, classes: string[]): string {
|
|
if (icon === null) return `<span></span>`;
|
|
|
|
return `<i class="fa fa-${icon} ${(classes || []).join(" ")}" title="${title}"></i>`;
|
|
}
|
|
|
|
/**
|
|
* Returns an appropriate label for the given page.
|
|
*
|
|
* 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 pages a list of all pages
|
|
* @param page the page to generate a label of
|
|
* @return an appropriate label with icons for the given page
|
|
* @private
|
|
*/
|
|
private generateLabel(pages: Page[], page: Page): string {
|
|
const labelText = pages.some(it => it.link.lang === page.link.lang && !it.link.equals(page.link))
|
|
? page.link.toString()
|
|
: page.link.lang;
|
|
|
|
return "" +
|
|
`<span class="${page.exists ? "" : "red-link"}">` +
|
|
/**/`<a href="${page.url}" target="_blank" title="${page.link}">${labelText}</a>` +
|
|
/**/`<span> </span>` +
|
|
/**/`<a href="${page.url}?action=edit" target="_blank" title="Edit"><i class="fa fa-pencil"></i></a>` +
|
|
/**/`<span> </span>` +
|
|
/**/`<a title="Copy"><i class="fa fa-clipboard copy-icon" data-clipboarddata="${page.link}"></i></a>` +
|
|
`</span>`;
|
|
}
|
|
|
|
/**
|
|
* Generates the head of the table generated by `#toTable`.
|
|
*
|
|
* @param network the network to generate the head for
|
|
* @return the head of the table generated by `#toTable`
|
|
* @private
|
|
*/
|
|
private generateTableHead(network: InterlangNetwork): string {
|
|
return "" +
|
|
`<thead>` +
|
|
/**/`<tr>` +
|
|
/****/`<th rowspan="2"></th>` +
|
|
/****/`<th class="source-label" rowspan="2">Source</th>` +
|
|
/****/`<th colspan="${network.pages.length}">Destination</th>` +
|
|
/**/`</tr>` +
|
|
/**/`<tr>${network.pages.map(page => `<th>${this.generateLabel(network.pages, page)}</th>`)}</tr>` +
|
|
`</thead>`;
|
|
}
|
|
|
|
/**
|
|
* Generates the body of the table generated by `#toTable`.
|
|
*
|
|
* @param network the network to generate the body for
|
|
* @return the body of the table generated by `#toTable`
|
|
* @private
|
|
*/
|
|
private generateTableBody(network: InterlangNetwork): string {
|
|
const rows = network.pages.map(srcPage => {
|
|
const {self: selfVerdict, links: linkVerdicts} = network.getPageVerdict(srcPage);
|
|
|
|
const icons = selfVerdict
|
|
.map(state => {
|
|
const props = PageVerdict.props[state];
|
|
return InterlangTable.createIcon(props.icon, props.message, props.style);
|
|
})
|
|
.map(it => `${it}<span> </span>`);
|
|
const label = this.generateLabel(network.pages, srcPage);
|
|
const cells = network.pages.map(dstPage => {
|
|
const linkState = linkVerdicts.get(dstPage.link)!;
|
|
const props = LinkVerdict.props[linkState];
|
|
return InterlangTable.createIcon(props.icon, props.message, props.style);
|
|
});
|
|
|
|
return "" +
|
|
`<tr>` +
|
|
/**/`<th>${icons}</th>` +
|
|
/**/`<th class="source-label">${label}</th>` +
|
|
/**/cells.map(it => `<td>${it}</td>`) +
|
|
`</tr>`;
|
|
});
|
|
|
|
return `<tbody>${rows}</tbody>`;
|
|
}
|
|
|
|
/**
|
|
* Renders the table describing the interlanguage network.
|
|
*
|
|
* @param id the ID to assign to the table element
|
|
* @param network the network of pages to render
|
|
* @return the generated table
|
|
*/
|
|
render(id: string, network: InterlangNetwork): HTMLElement {
|
|
const table = stringToHtml(
|
|
`<table id="${id}" role="grid">` +
|
|
/**/this.generateTableHead(network) +
|
|
/**/this.generateTableBody(network) +
|
|
`</table>`,
|
|
"table"
|
|
) as HTMLElement;
|
|
|
|
// Add event handlers
|
|
table.querySelectorAll(".copy-icon").forEach(icon => {
|
|
if (!(icon instanceof HTMLElement)) return;
|
|
|
|
icon.addEventListener("click", () => {
|
|
// noinspection JSIgnoredPromiseFromCall
|
|
navigator.clipboard.writeText(`[[${icon.dataset.clipboarddata}]]`);
|
|
|
|
icon.classList.replace("fa-clipboard", "fa-check");
|
|
setTimeout(() => icon.classList.replace("fa-check", "fa-clipboard"), 1000);
|
|
});
|
|
});
|
|
|
|
return table;
|
|
}
|
|
}
|