interlanguage-checker/src/main/js/InterlangTable.ts

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;
}
}