MediaWiki:Functions-list.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
(function () {
'use strict';
// For now only opens in https://www.wikifunctions.org/wiki/Special:Blankpage/functions-list?withJS=MediaWiki:Functions-list.js
if (mw.config.get('wgCanonicalSpecialPageName') !== 'Blankpage' ||
mw.config.get('wgPageName').split('/')[1] !== 'functions-list') {
return;
}
const api = new mw.Api({ ajax: { headers: { 'Api-User-Agent': 'functions-list/0.1' } } });
async function* allFunctions() {
let result = { 'continue': { apcontinue: '' } };
while (Object.hasOwn(result, 'continue')) {
result = await api.get({
action: 'query',
list: 'allpages',
apnamespace: 0,
aplimit: 500,
apcontinue: result.continue.apcontinue
});
yield* result.query.allpages;
}
}
async function wikiLambdaMultiFetch(zIds) {
try {
return await api.post({ 'action': 'wikilambda_fetch', zids: zIds.join('|') });
} catch (_) {
return await api.post({ 'action': 'wikilambda_fetch', zids: zIds.join('|') });
}
}
async function wikiLambdaFetch(zId) {
const result = await api.get({ 'action': 'wikilambda_fetch', zids: zId });
return JSON.parse(Object.values(result)[0].wikilambda_fetch);
}
function init(require) {
const Vue = require('vue');
const { CdxButton, CdxAccordion, CdxProgressBar } = require('@wikimedia/codex');
Vue.createMwApp({
components: { CdxButton, CdxAccordion, CdxProgressBar },
data() {
const functions = Vue.ref([]);
const fetchAllIsInProgress = Vue.ref(false);
const fetchAllIsDoneOpenAttribute = Vue.ref('close');
const longestShortDescription = Vue.ref({});
const details = Vue.ref({});
const excludeList = Vue.ref([]);
const codeStore = Vue.ref({});
(async function () {
const list = [];
for await (let item of allFunctions()) {
list.push(item.title);
}
list.sort((x, y) => +x.slice(1) - +y.slice(1));
functions.value = list;
}());
async function fetchDetails(item) {
if (Object.hasOwn(details.value, item)) return;
details.value[item] = await wikiLambdaFetch(item);
}
async function fetchAll() {
fetchAllIsInProgress.value = true;
const chunkSize = mw.config.get('wgUserGroups').includes('sysop') ? 500 : 50;
for (let i = 0; i < functions.value.length; i += chunkSize) {
const result = await wikiLambdaMultiFetch(functions.value.slice(i, i + chunkSize));
for (const [key, value] of Object.entries(result)) {
details.value[key] = JSON.parse(value.wikilambda_fetch);
}
}
fetchAllIsInProgress.value = false;
fetchAllIsDoneOpenAttribute.value = 'open';
}
function sortByLongestShortDescription() {
const sizes = Object.fromEntries(functions.value.map(zId => {
return [zId, Math.max(...[0].concat(
details.value[zId]?.Z2K5?.Z12K1?.slice(1)
?.map(x => [...x.Z11K2].length) ?? 0
))];
}));
functions.value = functions.value.toSorted((x, y) => {
return sizes[y] - sizes[x];
});
longestShortDescription.value = sizes;
}
function showOnlyItemsWithCode() {
excludeList.value = functions.value
.filter(x => !Object.hasOwn(details.value[x]?.Z2K2 ?? {}, 'Z14K3'));
codeStore.value = Object.fromEntries(
functions.value
.filter(x => Object.hasOwn(details.value[x]?.Z2K2 ?? {}, 'Z14K3'))
.map(x => [x, details.value[x].Z2K2.Z14K3.Z16K2])
);
}
return {
functions,
details,
fetchDetails,
fetchAll,
fetchAllIsInProgress,
fetchAllIsDoneOpenAttribute,
longestShortDescription,
getUrl: mw.util.getUrl,
sortByLongestShortDescription,
showOnlyItemsWithCode,
excludeList,
codeStore,
};
},
// available components, https://doc.wikimedia.org/codex/latest/components/demos/
template: `
<cdx-button v-if="functions.length !== Object.keys(details).length && !fetchAllIsInProgress" @click="fetchAll()">Fetch All ({{ functions.length }})</cdx-button>
<cdx-button v-if="functions.length !== 0 && functions.length === Object.keys(details).length" @click="sortByLongestShortDescription()">Sort by longest short description</cdx-button>
<cdx-button v-if="functions.length !== 0 && functions.length === Object.keys(details).length" @click="showOnlyItemsWithCode()">Show only items with code</cdx-button>
<cdx-progress-bar v-if="!functions.length || fetchAllIsInProgress" />
<!--progress v-if="fetchAllIsInProgress" :value="Object.keys(details).length" :max="functions.length" /-->
<div v-for="item in functions" :key="item">
<details @toggle="fetchDetails(item)" :[fetchAllIsDoneOpenAttribute]="true" v-if="!excludeList.includes(item)">
<summary style="padding: .25em"><a :href="getUrl(item)" target="_blank">{{ item }}</a></summary>
<p v-if="longestShortDescription[item] !== undefined">Longest Description Size: {{ longestShortDescription[item] ?? '' }}</p>
<p v-if="!Object.hasOwn(codeStore, item)">{{ details[item] ?? '' }}</p>
<p v-if="Object.hasOwn(codeStore, item)" style="white-space: pre; font-family: monospace">{{ codeStore[item] }}</p>
<cdx-progress-bar inline v-if="details[item] === undefined" />
</details>
</div>
`,
}).mount($('<div>').appendTo($('#mw-content-text').empty())[0]);
document.title = 'Functions List';
document.querySelector('h1').innerText = 'Functions List';
}
$(() => mw.loader.using(['vue', '@wikimedia/codex']).then(init));
}());