MediaWiki:Common.js: Difference between revisions
Jump to navigation
Jump to search
try data.player_count |
RobertWesner (talk | contribs) Add online-status badge. |
||
| Line 1: | Line 1: | ||
/* Any JavaScript here will be loaded for all users on every page load. */ | /* Any JavaScript here will be loaded for all users on every page load. */ | ||
/** | |||
* Reusable XSS-safe html-builder DSL. | |||
* Taken from Roberts userscripts so we can avoid insecure innerHtml nonsense. | |||
* | |||
* Example: | |||
* $builder('div') | |||
* .id('foo') | |||
* .className('a b c') | |||
* .onBuildText('<b>Test</b>') | |||
* .build() | |||
* | |||
* Result: | |||
* ``` | |||
* <div id="foo" class="a b c"> | |||
* <b>Test</b> | |||
* </div>; | |||
* ``` | |||
* | |||
* @license MIT | |||
* @author Robert Wesner | |||
* @see https://github.com/RobertWesner/YouTube-Play-All/blob/main/script.user.js | |||
*/ | |||
const { $builder, $style } = (()=>{const $builder=t=>(t=>{const e=t.match(/^[a-zA-Z0-9-]+/);if(!e)throw"Invalid tag supplied to parseQuery.";const n=e[0],r=(t=>{let e=[];const getOperations=()=>{const t=[...e.flat()];return e=[],t},n=new Proxy(t,{get(r,a,s){const P=t=>(...e)=>(t(...e),n),PBO=t=>P(((...n)=>e.push((()=>t(...n)))));switch(a){case"build":return()=>(getOperations().forEach((e=>e(t))),t);case"buildWithSync":return async()=>{for(const e of getOperations()){const n=e(t);n&&"function"==typeof n.then&&await n}return t};case"addClass":return P((e=>t.classList.add(e)));case"onBuild":return P((t=>e.push(t)));case"onBuildAppend":return PBO(((...e)=>t.append(...e)));case"onBuildText":return PBO((e=>t.textContent=e));case"on":return P(((e,n)=>t.addEventListener(e,n)))}const c=["hidden","style"];return e=>(!c.includes(a)&&a in t?t[a]=e:t.setAttribute(a.replaceAll("_","-"),e),n)}});return n})(document.createElement(n));if(!t.match(/^[a-zA-Z0-9-]+$/)){const e=(t=>t.match(/^[a-zA-Z0-9-]+((?:[#.][a-zA-Z0-9-]+)+)?((?:\[[a-zA-Z0-9-]+(?:=".*?")?])+)?/))(t),n=e[1]??"",a=e[2]??"";n.matchAll(/([#.])([a-zA-Z0-9-]+)/g).forEach((([t,e,n])=>{({"#":r.id,".":r.addClass})[e](n)})),a.matchAll(/\[([a-zA-Z0-9-]+)(?:="(.*?)")?]/g).forEach((([t,e,n])=>{r[e](n)}))}return r})(t);return{$builder,$style:(t,e)=>document.head.insertAdjacentElement("beforeend",$builder(`style#${t}`).onBuild((t=>t.textContent=e)).build())}})(); | |||
$style('clam-components', ` | |||
.clam-component---online-badge { | |||
} | & .cc-ob-online { | ||
background-color: #2c303633; | |||
color: white; | |||
padding: 0.12em 0.4em; | |||
margin-left: 0.6em; | |||
border-radius: 0.32em; | |||
&[data-online="true"] { | |||
color: lime; | |||
&::after { content: '● online'; } | |||
} | |||
&[data-online="false"] { | |||
color: red; | |||
&::after { content: '● offline'; } | |||
} | |||
} | |||
} | } | ||
`) | |||
// TODO: caching so its reusable across widgets | |||
const statsClient = (() => ({ | |||
player: name => fetch(`https://api.betamc.org/api/v1/player/${name}`).then(r => r.json()), | |||
}))(); | |||
document. | (async () => { | ||
const userPathMatch = location.pathname.match(/\/index.php\/User:([^\/]+)/); | |||
if (userPathMatch) { | |||
const stats = await statsClient.player(userPathMatch[1]); | |||
document.querySelectorAll('br.cc.-online').forEach(element => { | |||
console.log(element, stats); | |||
element.replaceWith( | |||
$builder('span.clam-component---online-badge') | |||
.onBuildAppend( | |||
$builder('span.cc-ob-name').onBuildText(stats.name).build(), | |||
$builder('span.cc-ob-online').data_online(stats.online).build(), | |||
) | |||
.build(), | |||
); | |||
}); | |||
} | |||
})(); | |||
Revision as of 11:07, 8 October 2026
/* Any JavaScript here will be loaded for all users on every page load. */
/**
* Reusable XSS-safe html-builder DSL.
* Taken from Roberts userscripts so we can avoid insecure innerHtml nonsense.
*
* Example:
* $builder('div')
* .id('foo')
* .className('a b c')
* .onBuildText('<b>Test</b>')
* .build()
*
* Result:
* ```
* <div id="foo" class="a b c">
* <b>Test</b>
* </div>;
* ```
*
* @license MIT
* @author Robert Wesner
* @see https://github.com/RobertWesner/YouTube-Play-All/blob/main/script.user.js
*/
const { $builder, $style } = (()=>{const $builder=t=>(t=>{const e=t.match(/^[a-zA-Z0-9-]+/);if(!e)throw"Invalid tag supplied to parseQuery.";const n=e[0],r=(t=>{let e=[];const getOperations=()=>{const t=[...e.flat()];return e=[],t},n=new Proxy(t,{get(r,a,s){const P=t=>(...e)=>(t(...e),n),PBO=t=>P(((...n)=>e.push((()=>t(...n)))));switch(a){case"build":return()=>(getOperations().forEach((e=>e(t))),t);case"buildWithSync":return async()=>{for(const e of getOperations()){const n=e(t);n&&"function"==typeof n.then&&await n}return t};case"addClass":return P((e=>t.classList.add(e)));case"onBuild":return P((t=>e.push(t)));case"onBuildAppend":return PBO(((...e)=>t.append(...e)));case"onBuildText":return PBO((e=>t.textContent=e));case"on":return P(((e,n)=>t.addEventListener(e,n)))}const c=["hidden","style"];return e=>(!c.includes(a)&&a in t?t[a]=e:t.setAttribute(a.replaceAll("_","-"),e),n)}});return n})(document.createElement(n));if(!t.match(/^[a-zA-Z0-9-]+$/)){const e=(t=>t.match(/^[a-zA-Z0-9-]+((?:[#.][a-zA-Z0-9-]+)+)?((?:\[[a-zA-Z0-9-]+(?:=".*?")?])+)?/))(t),n=e[1]??"",a=e[2]??"";n.matchAll(/([#.])([a-zA-Z0-9-]+)/g).forEach((([t,e,n])=>{({"#":r.id,".":r.addClass})[e](n)})),a.matchAll(/\[([a-zA-Z0-9-]+)(?:="(.*?)")?]/g).forEach((([t,e,n])=>{r[e](n)}))}return r})(t);return{$builder,$style:(t,e)=>document.head.insertAdjacentElement("beforeend",$builder(`style#${t}`).onBuild((t=>t.textContent=e)).build())}})();
$style('clam-components', `
.clam-component---online-badge {
& .cc-ob-online {
background-color: #2c303633;
color: white;
padding: 0.12em 0.4em;
margin-left: 0.6em;
border-radius: 0.32em;
&[data-online="true"] {
color: lime;
&::after { content: '● online'; }
}
&[data-online="false"] {
color: red;
&::after { content: '● offline'; }
}
}
}
`)
// TODO: caching so its reusable across widgets
const statsClient = (() => ({
player: name => fetch(`https://api.betamc.org/api/v1/player/${name}`).then(r => r.json()),
}))();
(async () => {
const userPathMatch = location.pathname.match(/\/index.php\/User:([^\/]+)/);
if (userPathMatch) {
const stats = await statsClient.player(userPathMatch[1]);
document.querySelectorAll('br.cc.-online').forEach(element => {
console.log(element, stats);
element.replaceWith(
$builder('span.clam-component---online-badge')
.onBuildAppend(
$builder('span.cc-ob-name').onBuildText(stats.name).build(),
$builder('span.cc-ob-online').data_online(stats.online).build(),
)
.build(),
);
});
}
})();