Wikisumber
mswikisource
https://ms.wikisource.org/wiki/Laman_Utama
MediaWiki 1.47.0-wmf.6
first-letter
Media
Khas
Perbincangan
Pengguna
Perbincangan pengguna
Wikisumber
Perbincangan Wikisumber
Fail
Perbincangan fail
MediaWiki
Perbincangan MediaWiki
Templat
Perbincangan templat
Bantuan
Perbincangan bantuan
Kategori
Perbincangan kategori
Gerbang
Perbincangan gerbang
Pengarang
Perbincangan pengarang
Karya
Perbincangan karya
Terjemahan
Perbincangan terjemahan
Laman
Perbincangan laman
Indeks
Perbincangan indeks
TimedText
TimedText talk
Modul
Perbincangan modul
Acara
Perbincangan acara
MediaWiki:Gadgets-definition
8
2226
23398
22409
2026-06-09T12:22:44Z
Hakimi97
9
Mengemas kini mengikut padanan Wikipedia bahasa Cina ([[:w:zh:MediaWiki:Gadgets-definition]])
23398
wikitext
text/x-wiki
== editing-tools ==
* charinsert[ResourceLoader|default]|charinsert.js
* charinsert-core[ResourceLoader|hidden|dependencies=jquery.textSelection,user,mediawiki.storage]|charinsert-core.js|charinsert-core.css
* RegexMenuFramework[ResourceLoader|dependencies=mediawiki.util]|RegexMenuFramework.js
* HotCat[ResourceLoader|rights=edit]|HotCat.js
* TemplatePreloader[ResourceLoader|actions=edit]|TemplatePreloader.js
* refToolbar[ResourceLoader|dependencies=user.options]|refToolbar.js
== editing-tools-for-Index-namespace ==
* transclusion-check[ResourceLoader|default|dependencies=mediawiki.util,mediawiki.api|actions=view|namespaces=252]|transclusion-check.js|transclusion-check.css
== editing-tools-for-Page-namespace ==
* Easy_LST[ResourceLoader|default]|Easy_LST.js
* proofreadpage-customization[ResourceLoader|default]|proofreadpage-customization.js
* ocr[ResourceLoader|external]|ocr.js
== interface ==
* PageNumbers[ResourceLoader|default]|PageNumbers.js
* PageNumbers-core[ResourceLoader|hidden|dependencies=mediawiki.util,user,mediawiki.cookie]|PageNumbers-core.js|PageNumbers-core.css
* AdvancedSiteNotices[ResourceLoader|default|dependencies=mediawiki.api,mediawiki.cookie,mediawiki.Title,mediawiki.user,mediawiki.util,ext.centralNotice.geoIP]|AdvancedSiteNotices.js|AdvancedSiteNotices.css
* PurgeTab[ResourceLoader|dependencies=mediawiki.util,mediawiki.api|default]|PurgeTab.js
* Visibility[ResourceLoader|dependencies=mediawiki.cookie]|Visibility.js
* interwiki-transclusion[ResourceLoader|default|dependencies=mediawiki.util,mediawiki.api]|interwiki-transclusion.js
== development ==
* WE-framework WS[ResourceLoader]|WE-framework WS.js
* WikidataInfo [ResourceLoader|dependencies=mediawiki.jqueryMsg] | WikidataInfo.js|WikidataInfo.css
kwjnt0je1zccngy51o4jz55bg20gu64
23401
23398
2026-06-10T02:48:34Z
Hakimi97
9
Kemas kini gajet AdvancedSiteNotices
23401
wikitext
text/x-wiki
== editing-tools ==
* charinsert[ResourceLoader|default]|charinsert.js
* charinsert-core[ResourceLoader|hidden|dependencies=jquery.textSelection,user,mediawiki.storage]|charinsert-core.js|charinsert-core.css
* RegexMenuFramework[ResourceLoader|dependencies=mediawiki.util]|RegexMenuFramework.js
* HotCat[ResourceLoader|rights=edit]|HotCat.js
* TemplatePreloader[ResourceLoader|actions=edit]|TemplatePreloader.js
* refToolbar[ResourceLoader|dependencies=user.options]|refToolbar.js
== editing-tools-for-Index-namespace ==
* transclusion-check[ResourceLoader|default|dependencies=mediawiki.util,mediawiki.api|actions=view|namespaces=252]|transclusion-check.js|transclusion-check.css
== editing-tools-for-Page-namespace ==
* Easy_LST[ResourceLoader|default]|Easy_LST.js
* proofreadpage-customization[ResourceLoader|default]|proofreadpage-customization.js
* ocr[ResourceLoader|external]|ocr.js
== interface ==
* PageNumbers[ResourceLoader|default]|PageNumbers.js
* PageNumbers-core[ResourceLoader|hidden|dependencies=mediawiki.util,user,mediawiki.cookie]|PageNumbers-core.js|PageNumbers-core.css
* AdvancedSiteNotices[ResourceLoader|default|dependencies=mediawiki.Title,mediawiki.api,mediawiki.cookie,mediawiki.util,mediawiki.user]|AdvancedSiteNotices.js|AdvancedSiteNotices.css
* PurgeTab[ResourceLoader|dependencies=mediawiki.util,mediawiki.api|default]|PurgeTab.js
* Visibility[ResourceLoader|dependencies=mediawiki.cookie]|Visibility.js
* interwiki-transclusion[ResourceLoader|default|dependencies=mediawiki.util,mediawiki.api]|interwiki-transclusion.js
== development ==
* WE-framework WS[ResourceLoader]|WE-framework WS.js
* WikidataInfo [ResourceLoader|dependencies=mediawiki.jqueryMsg] | WikidataInfo.js|WikidataInfo.css
lzd54bl5cgyrkhqw2zhj4r08dp2yzph
Templat:AdvancedSiteNotices
10
2368
23405
21800
2026-06-10T02:49:02Z
Hakimi97
9
23405
wikitext
text/x-wiki
<noinclude>{{Shortcut|WP:ASN}}'''Perhatian''': Mohon hanya tambahkan maklumat yang penting untuk diketahui komuniti, misalnya tentang perubahan besar, penyelenggaraan pelayan (''server''), pemilihan pengurus, mengumpul dana, dan aktiviti global lain. Jangan ulangi mesej yang sudah tertampil melalui sistem CentralNotice. Untuk mencadangkan pengumuman baru, sila ke [[{{TALKPAGENAME}}|laman perbincangan]].</noinclude>
{{AdvancedSiteNotices/core|<!--
---ALL---
-->{{AdvancedSiteNotices/item|{{Ucapan Peristiwa}}}}<!--
-->{{AdvancedSiteNotices/item|{{Ucapan Wiki}}}}<!--
---ONLY ANON---
-->{{AdvancedSiteNotices/item|{{Pautan|1=Ingin mengenali apa itu Wikisumber? Sila lawati [[Wikisumber:Apa itu Wikisumber?|laman pengenalan Wikisumber]], [{{FULLURL:Khas:Buka_akaun}} dan jadilah sukarelawan Wikisumber Bahasa Melayu!]}}|criteria=is_anon()}}<!--
---ONLY LOGGED IN USER---
-->{{AdvancedSiteNotices/item|{{Senarai perbincangan}}|criteria=is_named()}}<!--
-->
<!--
---Media sosial Wikisumber bahasa Melayu---
-->{{AdvancedSiteNotices/item|Ikuti media sosial [[File:Wikisource-logo.svg|20x20px|link=|Wikisumber Bahasa Melayu]] '''Wikisumber Bahasa Melayu''' di [[Fail:Instagram_simple_icon.svg|15x14px|link=https://www.instagram.com/wikisumber]] {{Pautan|alt=Perkongsian maklumat maklumat berkenaan Wikisumber BM|1=[https://www.instagram.com/wikisumber '''Instagram''']}} {{·}} [[File:X logo 2023.svg|15x14px|link=https://twitter.com/wikisumber]] {{Pautan|alt=Perkongsian maklumat maklumat berkenaan Wikisumber BM|1=[https://twitter.com/wikisumber '''Twitter''']}}<br/><!--
---Media sosial Wikimedia Malaysia---
-->Ikuti media sosial [[Fail:Wikimedia-logo.svg|20x20px]] '''Wikimedia Malaysia''' di [[Fail:Facebook logo 36x36.svg|15x14px]] {{Pautan|alt=Perkongsian maklumat maklumat berkenaan Wikimedia Malaysia|1=[https://fb.com/wikimediamalaysia/ '''Facebook''']}} {{·}} [[Fail:Instagram_simple_icon.svg|15x14px]] {{Pautan|alt=Perkongsian maklumat maklumat berkenaan Wikimedia Malaysia|1=[https://www.instagram.com/wikimediamy '''Instagram''']}} {{·}} [[File:X logo 2023.svg|15x14px]] {{Pautan|alt=Perkongsian maklumat maklumat berkenaan Wikimedia Malaysia|1=[https://www.x.com/wikimediamy '''Twitter''']}} {{·}} [[Fail:Telegram logo.svg|15x14px]] {{Pautan|alt=[[Telegram]] untuk Wikimedia Malaysia|1=[https://t.me/wikimediamy '''Telegram''' (saluran)]}} {{·}} {{Pautan|alt=[[Telegram]] untuk Wikimedia Malaysia|1=[https://t.me/wikimediamalaysia (laman sembang)]}} {{·}} [[Fail:WhatsApp.svg|20px]] {{Pautan|alt=[[WhatsApp|Saluran WhatsApp]] untuk Wikimedia Malaysia|1=[https://whatsapp.com/channel/0029Va64yHRKLaHjj57QHR3i '''WhatsApp''' (saluran)]}}}}<!--
-->{{AdvancedSiteNotices/item|<big>Anda diundang untuk menyunting di projek Wikimedia yang lain juga:</big><br/>[[Fail:Wikipedia-logo.svg|15x14px|link=w:]] [[:w:|Wikipedia]]{{·}}[[Fail:Wiktionary-logo.svg|15x14px|link=wikt:]] [[wikt:|Wikikamus]]{{·}}[[Fail:Wikibooks-logo.svg|15x14px|link=b:]] [[b:|Wikibuku]] {{!}} [[Fail:Wikinews-logo.svg|15x14px|link={{fullurle:Incubator:Wn/ms/Laman Utama|uselang=ms}}]] [{{fullurle:Incubator:Wn/ms/Laman Utama|uselang=ms}} ''Wikiberita'']{{·}}[[Fail:Wikivoyage-logo.svg|15x14px|link={{fullurle:incubator:Wy/ms/Laman Utama|uselang=ms}}]] [{{fullurle:incubator:Wy/ms/Laman Utama|uselang=ms}} ''Wikilayar'']{{·}}[[Fail:Wikiquote-logo.svg|15x14px|link={{fullurle:Incubator:Wq/ms/Laman Utama|uselang=ms}}]] [{{fullurle:Incubator:Wq/ms/Laman Utama|uselang=ms}} ''Wikipetik'']{{·}}[[Fail:Wikiversity-logo.svg|15x14px|link={{fullurle:BetaWikiversity:Laman Utama|uselang=ms}}|Wikiversiti]] [{{fullurle:BetaWikiversity:Laman Utama|uselang=ms}} ''Wikiversiti'' Beta] {{!}} [[Fail:Wikidata-logo.svg|15x14px|link={{fullurle:d:Wikidata:Laman Utama|uselang=ms}}]] [{{fullurle:d:Wikidata:Laman Utama|uselang=ms}} Wikidata]{{·}}[[Fail:Wikispecies-logo.svg|15x14px|link={{fullurle:Wikispecies:Laman Utama|uselang=ms}}|Wikispesies]] [{{fullurle:Wikispecies:Laman Utama|uselang=ms}} Wikispesies]{{·}}[[Fail:Commons-logo.svg|15x14px|link={{fullurle:commons:Laman Utama|uselang=ms}}]] [{{fullurle:commons:Laman Utama|uselang=ms}} Wikimedia Commons]|plainlinks}}<!--
--><!--{{AdvancedSiteNotices/item|{{Sasaran rencana}}}}--><!--
---ONLY SYSOP---
--><!--{{AdvancedSiteNotices/item|Penyelia, sila selalu memeriksa '''[[WP:PPP]]'''!|criteria=in_group('sysop')}}
-->{{#ifexpr: {{PAGESINCATEGORY:Rencana_untuk_dipadamkan}} > 0 | {{AdvancedSiteNotices/item|Penyelia, terdapat [[:Kategori:Rencana_untuk_dipadamkan|'''{{PAGESINCATEGORY:Rencana_untuk_dipadamkan}}''' halaman yang layak untuk dihapuskan]]|criteria=in_group('sysop')}} }}<!--
-->}}
mfeeemr7d1udrf0rcgdcyj11eyrvfkk
Templat:AdvancedSiteNotices/core
10
2377
23400
18156
2026-06-10T02:38:18Z
Hakimi97
9
Kemas kini kod
23400
wikitext
text/x-wiki
<ul class="sitents" data-asn-version="{{REVISIONID:Templat:AdvancedSiteNotices|R}}">{{{1|}}}</ul><noinclude>{{documentation}}</noinclude>
nmcnpi7m3bv6ey9qhsoqxtf8x1wfdas
Templat:AdvancedSiteNotices/item
10
2378
23404
18155
2026-06-10T02:48:57Z
Hakimi97
9
23404
wikitext
text/x-wiki
{{#if:{{{1|}}}|<li {{#if:{{{2|}}}|class="{{{2}}}"}} {{#if:{{{criteria|}}}|data-asn-criteria="{{urlencode:{{{criteria|}}}}}"}} {{#if:{{{style|}}}|data-asn-style="{{urlencode:{{{style|}}}}}"}} {{#if:{{{html|}}}|data-asn-html="{{urlencode:{{{html|}}}}}"}}>{{{1}}}</li>}}{{#ifeq:{{FULLPAGENAME}}|Templat:AdvancedSiteNotices/viewer|{{#if:{{{2|}}}{{{criteria|}}}|({{{2|}}}{{#if:{{{2|}}}|{{#if:{{{criteria|}}}|, }}}}{{{criteria|}}})}}{{#if:{{{html|}}}|{{#tag:pre|{{#tag:nowiki|{{{html|}}}}}}}}}{{#if:{{{style|}}}|{{#tag:pre|{{#tag:nowiki|{{{style|}}}}}}}}}}}<noinclude></noinclude>
gqqxpuaawcdvh4232pb25i5j7xgm80s
MediaWiki:Gadget-AdvancedSiteNotices.js
8
5403
23396
21804
2026-06-09T12:22:39Z
Hakimi97
9
Mengemas kini mengikut padanan Wikipedia bahasa Cina ([[:w:zh:MediaWiki:Gadget-AdvancedSiteNotices.js]])
23396
javascript
text/javascript
// <nowiki>
/*
* Advanced Site Notices for Malay Wikipedia
* Based on Chinese Wikipedia's MediaWiki:Gadget-AdvancedSiteNotices.js,
* with HanAssist and Chinese variant support removed.
*
* Please make sure [[Template:AdvancedSiteNotices/ajax]] is properly protected.
*/
(function () {
"use strict";
var modules = [
"mediawiki.api",
"mediawiki.cookie",
"mediawiki.Title",
"mediawiki.user",
"mediawiki.util"
];
mw.loader.using(modules).then(function () {
return Promise.all([
$.ready,
mw.geoIP && typeof mw.geoIP.getPromise === "function" ? mw.geoIP.getPromise().catch(function () {}) : Promise.resolve()
]);
}).then(function (results) {
var geo = results[1];
if (
!$("#siteNotice").length ||
window.closeASNForever ||
mw.config.get("wgAction") === "edit" ||
mw.config.get("wgAction") === "submit"
) {
return;
}
var customASNInterval = window.customASNInterval || 15;
var COOKIE_NAME = "dismissASN";
var cookieVal = Number.parseInt(mw.cookie.get(COOKIE_NAME) || "-1", 10);
var revisionId = 0;
var timeoutId = null;
var $asnRoot = $("<div>", {
id: "asn-dismissable-notice"
});
var $asnBody = $("<div>", {
id: "advancedSiteNotices",
class: "mw-parser-output"
});
var $asnClose = $("<button>", {
title: "Tutup",
"aria-label": "Tutup",
class: "asn-close-button",
type: "button"
});
$asnRoot.append($asnBody, $asnClose);
$asnClose.on("click", function () {
$asnClose.prop("disabled", true);
mw.cookie.set(COOKIE_NAME, revisionId, {
expires: 60 * 60 * 24 * 30,
path: "/",
secure: true
});
clearTimeout(timeoutId);
$asnRoot.fadeOut(function () {
$asnRoot.remove();
});
});
/**
* Criteria parser. Only a small safe subset of JavaScript-like expressions is supported.
*/
class CriteriaExecutor {
constructor(functions) {
this.functions = functions || {};
}
_tokenizeExpression(expression) {
expression = expression.trim();
if (!expression) {
return [];
}
const result = [];
const status = {
expression,
startTokenRe: /\|\||&&|\b(?:true|false)\b|[("'!\s]/g,
index: 0,
append(item) {
result.push(item);
}
};
const skipSpace = () => {
while (/\s/.test(expression[status.index] || "")) {
status.index++;
}
};
let match = status.startTokenRe.exec(expression);
while (match) {
const token = match[0];
if (status.startTokenRe.lastIndex - token.length !== status.index) {
if (token === "(") {
const parsed = this._handleFunctionCall(status);
if (!parsed) {
break;
}
} else {
break;
}
} else {
let parsed = false;
switch (token) {
case "(":
parsed = this._handleExpressionStatement(status);
break;
case "||":
case "&&":
parsed = this._handleLogicalOperator(status, token);
break;
case "!":
parsed = this._handleUnaryExpression(status, token);
break;
case "'":
case '"':
parsed = this._handleStringLiteral(status, token);
break;
case "true":
case "false":
parsed = this._handleBooleanLiteral(status, token);
break;
}
if (!parsed) {
break;
}
}
skipSpace();
status.startTokenRe.lastIndex = status.index;
match = status.startTokenRe.exec(expression);
}
if (status.index < expression.length - 1) {
throw new SyntaxError("Unexpected token.");
}
return result;
}
_handleFunctionCall(status) {
const functionName = status.expression
.slice(status.index, status.startTokenRe.lastIndex - 1)
.trim();
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(functionName)) {
return false;
}
const argsStartIndex = status.startTokenRe.lastIndex - 1;
const endIndex = this._findNextBalanceItem(
status.expression,
"(",
")",
argsStartIndex
);
if (endIndex === -1) {
throw new SyntaxError("Unbalanced parentheses in function call.");
}
const rawArgs = status.expression
.slice(argsStartIndex + 1, endIndex)
.trim();
const args = !rawArgs.length ? [] : this._balanceSplit(rawArgs, ",", "(", ")");
status.append({
type: "FunctionCall",
functionName,
args: args.map((arg) => this._tokenizeExpression(arg))
});
status.index = endIndex + 1;
return true;
}
_handleLogicalOperator(status, token) {
status.append({
type: "LogicalExpression",
operator: token
});
status.index = status.startTokenRe.lastIndex;
return true;
}
_handleUnaryExpression(status, token) {
const item = {
type: "UnaryExpression",
operator: token,
argument: undefined
};
status.append(item);
const origAppend = status.append.__orig__ || status.append;
status.append = function (nextItem) {
if (nextItem.type === "LogicalExpression") {
throw new SyntaxError("Unexpected LogicalExpression.");
}
item.argument = nextItem;
status.append = origAppend;
};
status.append.__orig__ = origAppend;
status.index = status.startTokenRe.lastIndex;
return true;
}
_handleBooleanLiteral(status, token) {
status.append({
type: "BooleanLiteral",
value: token === "true"
});
status.index = status.startTokenRe.lastIndex;
return true;
}
_handleExpressionStatement(status) {
const endIndex = this._findNextBalanceItem(
status.expression,
"(",
")",
status.index
);
if (endIndex === -1) {
throw new SyntaxError("Unbalanced parentheses in expression.");
}
status.append({
type: "ExpressionStatement",
expression: this._tokenizeExpression(
status.expression.slice(status.index + 1, endIndex)
)
});
status.index = endIndex + 1;
return true;
}
_handleStringLiteral(status, delimiter) {
const endIndex = this._findNextTokenWithoutEscape(
status.expression,
delimiter,
status.index + 1
);
if (endIndex === -1) {
throw new SyntaxError("Unterminated string literal.");
}
status.append({
type: "StringLiteral",
value: this._parseString(
status.expression.slice(status.index + 1, endIndex)
)
});
status.index = endIndex + 1;
return true;
}
_findNextBalanceItem(expression, startToken, endToken, currentIndex) {
let count = 1;
while (++currentIndex < expression.length) {
if (expression[currentIndex] === startToken) {
count++;
} else if (expression[currentIndex] === endToken) {
count--;
}
if (count === 0) {
return currentIndex;
}
}
return -1;
}
_balanceSplit(expression, splitToken, startToken, endToken) {
const result = [];
let balance = 0;
let current = "";
for (const char of expression) {
if (char === startToken) {
balance++;
} else if (char === endToken) {
balance--;
}
if (char === splitToken && balance === 0) {
result.push(current.trim());
current = "";
} else {
current += char;
}
}
if (current) {
result.push(current.trim());
}
return result;
}
_findNextTokenWithoutEscape(expression, token, currentIndex) {
while (currentIndex < expression.length) {
const foundIndex = expression.indexOf(token, currentIndex);
if (foundIndex === -1) {
return -1;
} else if (expression[foundIndex - 1] !== "\\") {
return foundIndex;
}
currentIndex = foundIndex + 1;
}
return -1;
}
_parseString(input) {
return input.replace(
/\\(n|t|r|b|f|x[0-9A-Fa-f]{2}|u\{[0-9A-Fa-f]+\}|u[0-9A-Fa-f]{4}|.)/g,
function (_, esc) {
switch (esc[0]) {
case "n":
return "\n";
case "t":
return "\t";
case "r":
return "\r";
case "b":
return "\b";
case "f":
return "\f";
case "x":
if (esc === "x") {
throw new SyntaxError("Invalid hexadecimal escape sequence.");
}
return String.fromCharCode(parseInt(esc.slice(1), 16));
case "u":
if (esc === "u") {
throw new SyntaxError("Invalid Unicode escape sequence.");
} else if (esc[1] === "{") {
const codePoint = Number.parseInt(esc.slice(2, -1), 16);
if (codePoint > 0x10ffff) {
throw new SyntaxError(
"Undefined Unicode code-point: \\" + esc + "."
);
}
return String.fromCodePoint(codePoint);
}
return String.fromCharCode(Number.parseInt(esc.slice(1), 16));
default:
return esc;
}
}
);
}
_tokensToAst(tokens) {
if (!tokens.length) {
throw new TypeError("Token list is empty.");
}
let logicalItemIndex = tokens.findIndex(
(t) => t.type === "LogicalExpression" && t.operator === "&&"
);
if (logicalItemIndex === -1) {
logicalItemIndex = tokens.findIndex(
(t) => t.type === "LogicalExpression" && t.operator === "||"
);
}
if (logicalItemIndex === -1) {
if (tokens.length === 1) {
return this._tokenToAst(tokens[0]);
}
throw new SyntaxError("Unexpected " + tokens[1].type + ".");
} else if (logicalItemIndex === 0) {
throw new SyntaxError("Unexpected LogicalExpression.");
}
return {
type: "LogicalExpression",
operator: tokens[logicalItemIndex].operator,
left: this._tokensToAst(tokens.slice(0, logicalItemIndex)),
right: this._tokensToAst(tokens.slice(logicalItemIndex + 1))
};
}
_tokenToAst(token) {
if (!token) {
throw new TypeError("token is undefined or null.");
}
const result = Object.assign({}, token);
if (token.type === "UnaryExpression") {
if (!result.argument) {
throw new SyntaxError("Unexpected UnaryExpression.");
}
result.argument = this._tokenToAst(token.argument);
} else if (token.type === "FunctionCall") {
result.args = token.args.map((argument) => this._tokensToAst(argument));
} else if (token.type === "ExpressionStatement") {
result.expression = this._tokensToAst(token.expression);
}
return result;
}
toAst(expression) {
const tokenizes = this._tokenizeExpression(expression);
return this._tokensToAst(tokenizes);
}
_executeFunction(functionName, args) {
if (!Object.prototype.hasOwnProperty.call(this.functions, functionName)) {
throw new Error("Function " + functionName + " is not allowed.");
}
return this.functions[functionName](...args);
}
evaluate(ast) {
let left;
let returnValue;
switch (ast.type) {
case "ExpressionStatement":
return this.evaluate(ast.expression);
case "UnaryExpression":
if (ast.operator === "!") {
return !this.evaluate(ast.argument);
}
throw new Error("Unknown UnaryExpression operator: " + ast.operator);
case "LogicalExpression":
if (ast.operator === "&&" || ast.operator === "||") {
left = this.evaluate(ast.left);
if (ast.operator === "&&" && !left) {
return false;
} else if (ast.operator === "||" && left) {
return true;
}
return this.evaluate(ast.right);
}
throw new Error("Unknown LogicalExpression operator: " + ast.operator);
case "FunctionCall":
returnValue = this._executeFunction(
ast.functionName,
ast.args.map((arg) => this.evaluate(arg))
);
if (typeof returnValue === "undefined" || returnValue === null) {
returnValue = false;
} else if (typeof returnValue !== "string" && typeof returnValue !== "boolean") {
mw.log.warn(
"[AdvancedSiteNotices]: The return type " +
typeof returnValue +
" of the function " +
ast.functionName +
"() is unsafe and has been forcibly converted to a string."
);
returnValue = String(returnValue);
}
return returnValue;
case "StringLiteral":
case "BooleanLiteral":
return ast.value;
default:
throw new Error("Unknown AST node type: " + ast.type);
}
}
}
var functions = {};
if (geo) {
functions.in_country = function (...countries) {
return countries.includes(geo.country);
};
functions.in_region = function (...regions) {
return regions.includes(geo.region);
};
functions.in_city = function (...cities) {
return cities.includes(geo.city);
};
} else {
functions.in_country = functions.in_region = functions.in_city = function (...args) {
return args.length ? true : false;
};
}
var configs = mw.config.get(["wgUserGroups", "wgUserLanguage"]);
functions.in_group = function (...groups) {
return groups.some(function (group) {
return configs.wgUserGroups.includes(group);
});
};
functions.in_group_every = function (...groups) {
return groups.every(function (group) {
return configs.wgUserGroups.includes(group);
});
};
functions.in_lang = function (...userLangs) {
return userLangs.includes(configs.wgUserLanguage);
};
functions.is_anon = function () {
return !!mw.user.isAnon();
};
functions.is_temp = function () {
return typeof mw.user.isTemp === "function" ? !!mw.user.isTemp() : false;
};
functions.is_named = function () {
return typeof mw.user.isNamed === "function" ? !!mw.user.isNamed() : !mw.user.isAnon();
};
var parser = new CriteriaExecutor(functions);
var cache = new WeakMap();
function getCache($element, key) {
var element = $element.get(0);
if (cache.has(element)) {
return cache.get(element)[key];
}
}
function setCache($element, key, value) {
var element = $element.get(0);
if (cache.has(element)) {
cache.get(element)[key] = value;
} else {
cache.set(element, {
[key]: value
});
}
}
function matchCriteria($noticeItem) {
var cachedResult = getCache($noticeItem, "asn-cache");
var criteria;
var result;
var testList;
if (cachedResult !== undefined) {
return cachedResult;
}
criteria = $noticeItem.attr("data-asn-criteria");
if (criteria !== undefined) {
if (criteria === "") {
result = true;
} else {
try {
criteria = decodeURIComponent(criteria.replace(/\+/g, "%20")).trim();
result = !!parser.evaluate(parser.toAst(criteria));
} catch (error) {
mw.log.warn(
"[AdvancedSiteNotices]: Fail to parse or evaluate criteria \"" + criteria + "\":",
error
);
result = false;
}
}
} else {
testList = [];
if ($noticeItem.hasClass("only_sysop")) {
testList.push(functions.in_group("sysop"));
}
if (
$noticeItem.hasClass("only_logged_in") ||
$noticeItem.hasClass("only_logged") ||
$noticeItem.hasClass("is_named")
) {
testList.push(functions.is_named());
}
if (
$noticeItem.hasClass("only_logged_out") ||
$noticeItem.hasClass("only_anon")
) {
testList.push(!functions.is_named());
}
if ($noticeItem.hasClass("is_temp")) {
testList.push(functions.is_temp());
}
if ($noticeItem.hasClass("is_anon")) {
testList.push(functions.is_anon());
}
result = !testList.length || testList.every(function (value) {
return !!value;
});
}
setCache($noticeItem, "asn-cache", result);
return result;
}
function getNoticeElement($noticeItem) {
var $cachedElement = getCache($noticeItem, "asn-element");
if ($cachedElement !== undefined) {
return $cachedElement;
}
$cachedElement = $("<div>").append($noticeItem.contents().clone());
setCache($noticeItem, "asn-element", $cachedElement);
return $cachedElement;
}
var isSetMinHeightCalled = false;
function setMinHeight($noticeList) {
var minHeight = -1;
$noticeList.each(function (_, nt) {
var $nt = $(nt);
$asnBody.replaceWith($nt);
minHeight = Math.max(minHeight, $nt.height());
$nt.replaceWith($asnBody);
});
$asnRoot.css("min-height", minHeight + "px");
if (!isSetMinHeightCalled) {
isSetMinHeightCalled = true;
window.addEventListener(
"resize",
mw.util.debounce(function () {
setMinHeight($noticeList);
}, 300)
);
}
}
function loadNotice($noticeList, pos) {
var $noticeItem = $noticeList.eq(pos);
var nextPos = pos + 1;
var $noticeElement = getNoticeElement($noticeItem);
if (nextPos === $noticeList.length) {
nextPos = 0;
}
if ($asnBody.children().length) {
$asnBody.stop().fadeOut(function () {
$asnBody.empty().append($noticeElement);
try {
$asnBody.fadeIn();
} catch (_) {}
});
} else {
$asnBody.append($noticeElement).fadeIn();
}
if ($noticeList.length > 1) {
timeoutId = setTimeout(function () {
loadNotice($noticeList, nextPos);
}, customASNInterval * 1000);
}
}
function initialNotices($noticeList) {
if (!$asnRoot.length || !$noticeList.length || revisionId === cookieVal) {
return;
}
mw.cookie.set(COOKIE_NAME, null, {
path: "/"
});
$asnRoot.appendTo($("#siteNotice"));
setMinHeight($noticeList);
loadNotice($noticeList, Math.floor(Math.random() * $noticeList.length));
}
new mw.Api({
ajax: {
headers: {
"Api-User-Agent": "s:ms:MediaWiki:Gadget-AdvancedSiteNotices.js"
}
}
}).parse(new mw.Title("Template:AdvancedSiteNotices/ajax"), {
maxage: 3600,
smaxage: 3600
}).then(function (html) {
var $json = $("ul.sitents", $.parseHTML(html));
var $noticeList = $("li", $json).filter(function (_, li) {
return matchCriteria($(li));
});
revisionId = Number.parseInt($json.data("asn-version") || "0", 10);
initialNotices($noticeList);
}).catch(function (error) {
mw.log.error("[AdvancedSiteNotices]: error", error);
});
}).catch(function (error) {
mw.log.error("[AdvancedSiteNotices]: dependency loading error", error);
});
}());
// </nowiki>
emqb1o1cha6vpycsdrg3qsjakghbgdt
23399
23396
2026-06-09T12:29:31Z
Hakimi97
9
23399
javascript
text/javascript
// <nowiki>
/*
* ****************************************************************************
* * >>>>> TO GADGET IMPORTERS: READ BEFORE IMPORING <<<<< *
* * Please MAKE SURE the notice page ([[Template:AdvancedSiteNotices/ajax]]) *
* * is properly protected on your wiki. *
* * Albeit efforts are made to handle JavaScript expressions correctly, *
* * they are not exhaustively battle-tested. *
* ****************************************************************************
*
* __________________________________________________________________________
* | |
* | === WARNING: GLOBAL GADGET FILE === |
* | Changes to this page affect many users. |
* | Please discuss changes on the talk page or on [[WP:VPT]] before editing. |
* |__________________________________________________________________________|
*
* Advanced Site Notices for Malay Wikipedia
* Based on Chinese Wikipedia's MediaWiki:Gadget-AdvancedSiteNotices.js,
* with HanAssist and Chinese variant support removed.
* Allow to custom dynamic site notices
* Maintainer: [[User:PhiLiP]], [[User:Diskdance]], [[User:SunAfterRain]], [[User:Hakimi97]]
*/
(function () {
"use strict";
var modules = [
"mediawiki.api",
"mediawiki.cookie",
"mediawiki.Title",
"mediawiki.user",
"mediawiki.util"
];
mw.loader.using(modules).then(function () {
return Promise.all([
$.ready,
mw.geoIP && typeof mw.geoIP.getPromise === "function" ? mw.geoIP.getPromise().catch(function () {}) : Promise.resolve()
]);
}).then(function (results) {
var geo = results[1];
if (
!$("#siteNotice").length ||
window.closeASNForever ||
mw.config.get("wgAction") === "edit" ||
mw.config.get("wgAction") === "submit"
) {
return;
}
var customASNInterval = window.customASNInterval || 15;
var COOKIE_NAME = "dismissASN";
var cookieVal = Number.parseInt(mw.cookie.get(COOKIE_NAME) || "-1", 10);
var revisionId = 0;
var timeoutId = null;
var $asnRoot = $("<div>", {
id: "asn-dismissable-notice"
});
var $asnBody = $("<div>", {
id: "advancedSiteNotices",
class: "mw-parser-output"
});
var $asnClose = $("<button>", {
title: "Tutup",
"aria-label": "Tutup",
class: "asn-close-button",
type: "button"
});
$asnRoot.append($asnBody, $asnClose);
$asnClose.on("click", function () {
$asnClose.prop("disabled", true);
mw.cookie.set(COOKIE_NAME, revisionId, {
expires: 60 * 60 * 24 * 30,
path: "/",
secure: true
});
clearTimeout(timeoutId);
$asnRoot.fadeOut(function () {
$asnRoot.remove();
});
});
/**
* Criteria parser. Only a small safe subset of JavaScript-like expressions is supported.
*/
class CriteriaExecutor {
constructor(functions) {
this.functions = functions || {};
}
_tokenizeExpression(expression) {
expression = expression.trim();
if (!expression) {
return [];
}
const result = [];
const status = {
expression,
startTokenRe: /\|\||&&|\b(?:true|false)\b|[("'!\s]/g,
index: 0,
append(item) {
result.push(item);
}
};
const skipSpace = () => {
while (/\s/.test(expression[status.index] || "")) {
status.index++;
}
};
let match = status.startTokenRe.exec(expression);
while (match) {
const token = match[0];
if (status.startTokenRe.lastIndex - token.length !== status.index) {
if (token === "(") {
const parsed = this._handleFunctionCall(status);
if (!parsed) {
break;
}
} else {
break;
}
} else {
let parsed = false;
switch (token) {
case "(":
parsed = this._handleExpressionStatement(status);
break;
case "||":
case "&&":
parsed = this._handleLogicalOperator(status, token);
break;
case "!":
parsed = this._handleUnaryExpression(status, token);
break;
case "'":
case '"':
parsed = this._handleStringLiteral(status, token);
break;
case "true":
case "false":
parsed = this._handleBooleanLiteral(status, token);
break;
}
if (!parsed) {
break;
}
}
skipSpace();
status.startTokenRe.lastIndex = status.index;
match = status.startTokenRe.exec(expression);
}
if (status.index < expression.length - 1) {
throw new SyntaxError("Unexpected token.");
}
return result;
}
_handleFunctionCall(status) {
const functionName = status.expression
.slice(status.index, status.startTokenRe.lastIndex - 1)
.trim();
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(functionName)) {
return false;
}
const argsStartIndex = status.startTokenRe.lastIndex - 1;
const endIndex = this._findNextBalanceItem(
status.expression,
"(",
")",
argsStartIndex
);
if (endIndex === -1) {
throw new SyntaxError("Unbalanced parentheses in function call.");
}
const rawArgs = status.expression
.slice(argsStartIndex + 1, endIndex)
.trim();
const args = !rawArgs.length ? [] : this._balanceSplit(rawArgs, ",", "(", ")");
status.append({
type: "FunctionCall",
functionName,
args: args.map((arg) => this._tokenizeExpression(arg))
});
status.index = endIndex + 1;
return true;
}
_handleLogicalOperator(status, token) {
status.append({
type: "LogicalExpression",
operator: token
});
status.index = status.startTokenRe.lastIndex;
return true;
}
_handleUnaryExpression(status, token) {
const item = {
type: "UnaryExpression",
operator: token,
argument: undefined
};
status.append(item);
const origAppend = status.append.__orig__ || status.append;
status.append = function (nextItem) {
if (nextItem.type === "LogicalExpression") {
throw new SyntaxError("Unexpected LogicalExpression.");
}
item.argument = nextItem;
status.append = origAppend;
};
status.append.__orig__ = origAppend;
status.index = status.startTokenRe.lastIndex;
return true;
}
_handleBooleanLiteral(status, token) {
status.append({
type: "BooleanLiteral",
value: token === "true"
});
status.index = status.startTokenRe.lastIndex;
return true;
}
_handleExpressionStatement(status) {
const endIndex = this._findNextBalanceItem(
status.expression,
"(",
")",
status.index
);
if (endIndex === -1) {
throw new SyntaxError("Unbalanced parentheses in expression.");
}
status.append({
type: "ExpressionStatement",
expression: this._tokenizeExpression(
status.expression.slice(status.index + 1, endIndex)
)
});
status.index = endIndex + 1;
return true;
}
_handleStringLiteral(status, delimiter) {
const endIndex = this._findNextTokenWithoutEscape(
status.expression,
delimiter,
status.index + 1
);
if (endIndex === -1) {
throw new SyntaxError("Unterminated string literal.");
}
status.append({
type: "StringLiteral",
value: this._parseString(
status.expression.slice(status.index + 1, endIndex)
)
});
status.index = endIndex + 1;
return true;
}
_findNextBalanceItem(expression, startToken, endToken, currentIndex) {
let count = 1;
while (++currentIndex < expression.length) {
if (expression[currentIndex] === startToken) {
count++;
} else if (expression[currentIndex] === endToken) {
count--;
}
if (count === 0) {
return currentIndex;
}
}
return -1;
}
_balanceSplit(expression, splitToken, startToken, endToken) {
const result = [];
let balance = 0;
let current = "";
for (const char of expression) {
if (char === startToken) {
balance++;
} else if (char === endToken) {
balance--;
}
if (char === splitToken && balance === 0) {
result.push(current.trim());
current = "";
} else {
current += char;
}
}
if (current) {
result.push(current.trim());
}
return result;
}
_findNextTokenWithoutEscape(expression, token, currentIndex) {
while (currentIndex < expression.length) {
const foundIndex = expression.indexOf(token, currentIndex);
if (foundIndex === -1) {
return -1;
} else if (expression[foundIndex - 1] !== "\\") {
return foundIndex;
}
currentIndex = foundIndex + 1;
}
return -1;
}
_parseString(input) {
return input.replace(
/\\(n|t|r|b|f|x[0-9A-Fa-f]{2}|u\{[0-9A-Fa-f]+\}|u[0-9A-Fa-f]{4}|.)/g,
function (_, esc) {
switch (esc[0]) {
case "n":
return "\n";
case "t":
return "\t";
case "r":
return "\r";
case "b":
return "\b";
case "f":
return "\f";
case "x":
if (esc === "x") {
throw new SyntaxError("Invalid hexadecimal escape sequence.");
}
return String.fromCharCode(parseInt(esc.slice(1), 16));
case "u":
if (esc === "u") {
throw new SyntaxError("Invalid Unicode escape sequence.");
} else if (esc[1] === "{") {
const codePoint = Number.parseInt(esc.slice(2, -1), 16);
if (codePoint > 0x10ffff) {
throw new SyntaxError(
"Undefined Unicode code-point: \\" + esc + "."
);
}
return String.fromCodePoint(codePoint);
}
return String.fromCharCode(Number.parseInt(esc.slice(1), 16));
default:
return esc;
}
}
);
}
_tokensToAst(tokens) {
if (!tokens.length) {
throw new TypeError("Token list is empty.");
}
let logicalItemIndex = tokens.findIndex(
(t) => t.type === "LogicalExpression" && t.operator === "&&"
);
if (logicalItemIndex === -1) {
logicalItemIndex = tokens.findIndex(
(t) => t.type === "LogicalExpression" && t.operator === "||"
);
}
if (logicalItemIndex === -1) {
if (tokens.length === 1) {
return this._tokenToAst(tokens[0]);
}
throw new SyntaxError("Unexpected " + tokens[1].type + ".");
} else if (logicalItemIndex === 0) {
throw new SyntaxError("Unexpected LogicalExpression.");
}
return {
type: "LogicalExpression",
operator: tokens[logicalItemIndex].operator,
left: this._tokensToAst(tokens.slice(0, logicalItemIndex)),
right: this._tokensToAst(tokens.slice(logicalItemIndex + 1))
};
}
_tokenToAst(token) {
if (!token) {
throw new TypeError("token is undefined or null.");
}
const result = Object.assign({}, token);
if (token.type === "UnaryExpression") {
if (!result.argument) {
throw new SyntaxError("Unexpected UnaryExpression.");
}
result.argument = this._tokenToAst(token.argument);
} else if (token.type === "FunctionCall") {
result.args = token.args.map((argument) => this._tokensToAst(argument));
} else if (token.type === "ExpressionStatement") {
result.expression = this._tokensToAst(token.expression);
}
return result;
}
toAst(expression) {
const tokenizes = this._tokenizeExpression(expression);
return this._tokensToAst(tokenizes);
}
_executeFunction(functionName, args) {
if (!Object.prototype.hasOwnProperty.call(this.functions, functionName)) {
throw new Error("Function " + functionName + " is not allowed.");
}
return this.functions[functionName](...args);
}
evaluate(ast) {
let left;
let returnValue;
switch (ast.type) {
case "ExpressionStatement":
return this.evaluate(ast.expression);
case "UnaryExpression":
if (ast.operator === "!") {
return !this.evaluate(ast.argument);
}
throw new Error("Unknown UnaryExpression operator: " + ast.operator);
case "LogicalExpression":
if (ast.operator === "&&" || ast.operator === "||") {
left = this.evaluate(ast.left);
if (ast.operator === "&&" && !left) {
return false;
} else if (ast.operator === "||" && left) {
return true;
}
return this.evaluate(ast.right);
}
throw new Error("Unknown LogicalExpression operator: " + ast.operator);
case "FunctionCall":
returnValue = this._executeFunction(
ast.functionName,
ast.args.map((arg) => this.evaluate(arg))
);
if (typeof returnValue === "undefined" || returnValue === null) {
returnValue = false;
} else if (typeof returnValue !== "string" && typeof returnValue !== "boolean") {
mw.log.warn(
"[AdvancedSiteNotices]: The return type " +
typeof returnValue +
" of the function " +
ast.functionName +
"() is unsafe and has been forcibly converted to a string."
);
returnValue = String(returnValue);
}
return returnValue;
case "StringLiteral":
case "BooleanLiteral":
return ast.value;
default:
throw new Error("Unknown AST node type: " + ast.type);
}
}
}
var functions = {};
if (geo) {
functions.in_country = function (...countries) {
return countries.includes(geo.country);
};
functions.in_region = function (...regions) {
return regions.includes(geo.region);
};
functions.in_city = function (...cities) {
return cities.includes(geo.city);
};
} else {
functions.in_country = functions.in_region = functions.in_city = function (...args) {
return args.length ? true : false;
};
}
var configs = mw.config.get(["wgUserGroups", "wgUserLanguage"]);
functions.in_group = function (...groups) {
return groups.some(function (group) {
return configs.wgUserGroups.includes(group);
});
};
functions.in_group_every = function (...groups) {
return groups.every(function (group) {
return configs.wgUserGroups.includes(group);
});
};
functions.in_lang = function (...userLangs) {
return userLangs.includes(configs.wgUserLanguage);
};
functions.is_anon = function () {
return !!mw.user.isAnon();
};
functions.is_temp = function () {
return typeof mw.user.isTemp === "function" ? !!mw.user.isTemp() : false;
};
functions.is_named = function () {
return typeof mw.user.isNamed === "function" ? !!mw.user.isNamed() : !mw.user.isAnon();
};
var parser = new CriteriaExecutor(functions);
var cache = new WeakMap();
function getCache($element, key) {
var element = $element.get(0);
if (cache.has(element)) {
return cache.get(element)[key];
}
}
function setCache($element, key, value) {
var element = $element.get(0);
if (cache.has(element)) {
cache.get(element)[key] = value;
} else {
cache.set(element, {
[key]: value
});
}
}
function matchCriteria($noticeItem) {
var cachedResult = getCache($noticeItem, "asn-cache");
var criteria;
var result;
var testList;
if (cachedResult !== undefined) {
return cachedResult;
}
criteria = $noticeItem.attr("data-asn-criteria");
if (criteria !== undefined) {
if (criteria === "") {
result = true;
} else {
try {
criteria = decodeURIComponent(criteria.replace(/\+/g, "%20")).trim();
result = !!parser.evaluate(parser.toAst(criteria));
} catch (error) {
mw.log.warn(
"[AdvancedSiteNotices]: Fail to parse or evaluate criteria \"" + criteria + "\":",
error
);
result = false;
}
}
} else {
testList = [];
if ($noticeItem.hasClass("only_sysop")) {
testList.push(functions.in_group("sysop"));
}
if (
$noticeItem.hasClass("only_logged_in") ||
$noticeItem.hasClass("only_logged") ||
$noticeItem.hasClass("is_named")
) {
testList.push(functions.is_named());
}
if (
$noticeItem.hasClass("only_logged_out") ||
$noticeItem.hasClass("only_anon")
) {
testList.push(!functions.is_named());
}
if ($noticeItem.hasClass("is_temp")) {
testList.push(functions.is_temp());
}
if ($noticeItem.hasClass("is_anon")) {
testList.push(functions.is_anon());
}
result = !testList.length || testList.every(function (value) {
return !!value;
});
}
setCache($noticeItem, "asn-cache", result);
return result;
}
function getNoticeElement($noticeItem) {
var $cachedElement = getCache($noticeItem, "asn-element");
if ($cachedElement !== undefined) {
return $cachedElement;
}
$cachedElement = $("<div>").append($noticeItem.contents().clone());
setCache($noticeItem, "asn-element", $cachedElement);
return $cachedElement;
}
var isSetMinHeightCalled = false;
function setMinHeight($noticeList) {
var minHeight = -1;
$noticeList.each(function (_, nt) {
var $nt = $(nt);
$asnBody.replaceWith($nt);
minHeight = Math.max(minHeight, $nt.height());
$nt.replaceWith($asnBody);
});
$asnRoot.css("min-height", minHeight + "px");
if (!isSetMinHeightCalled) {
isSetMinHeightCalled = true;
window.addEventListener(
"resize",
mw.util.debounce(function () {
setMinHeight($noticeList);
}, 300)
);
}
}
function loadNotice($noticeList, pos) {
var $noticeItem = $noticeList.eq(pos);
var nextPos = pos + 1;
var $noticeElement = getNoticeElement($noticeItem);
if (nextPos === $noticeList.length) {
nextPos = 0;
}
if ($asnBody.children().length) {
$asnBody.stop().fadeOut(function () {
$asnBody.empty().append($noticeElement);
try {
$asnBody.fadeIn();
} catch (_) {}
});
} else {
$asnBody.append($noticeElement).fadeIn();
}
if ($noticeList.length > 1) {
timeoutId = setTimeout(function () {
loadNotice($noticeList, nextPos);
}, customASNInterval * 1000);
}
}
function initialNotices($noticeList) {
if (!$asnRoot.length || !$noticeList.length || revisionId === cookieVal) {
return;
}
mw.cookie.set(COOKIE_NAME, null, {
path: "/"
});
$asnRoot.appendTo($("#siteNotice"));
setMinHeight($noticeList);
loadNotice($noticeList, Math.floor(Math.random() * $noticeList.length));
}
new mw.Api({
ajax: {
headers: {
"Api-User-Agent": "s:ms:MediaWiki:Gadget-AdvancedSiteNotices.js"
}
}
}).parse(new mw.Title("Template:AdvancedSiteNotices/ajax"), {
maxage: 3600,
smaxage: 3600
}).then(function (html) {
var $json = $("ul.sitents", $.parseHTML(html));
var $noticeList = $("li", $json).filter(function (_, li) {
return matchCriteria($(li));
});
revisionId = Number.parseInt($json.data("asn-version") || "0", 10);
initialNotices($noticeList);
}).catch(function (error) {
mw.log.error("[AdvancedSiteNotices]: error", error);
});
}).catch(function (error) {
mw.log.error("[AdvancedSiteNotices]: dependency loading error", error);
});
}());
// </nowiki>
qrqgkh4seuph94heiunppht85m4hjzy
23402
23399
2026-06-10T02:48:37Z
Hakimi97
9
Mengemas kini mengikut padanan Wikipedia bahasa Cina ([[w:zh:MediaWiki:Gadget-AdvancedSiteNotices.js]])
23402
javascript
text/javascript
// <nowiki>
/*
* ****************************************************************************
* * >>>>> TO GADGET IMPORTERS: READ BEFORE IMPORING <<<<< *
* * Please MAKE SURE the notice page ([[Template:AdvancedSiteNotices/ajax]]) *
* * is properly protected on your wiki. *
* * Albeit efforts are made to handle JavaScript expressions correctly, *
* * they are not exhaustively battle-tested. *
* ****************************************************************************
*
* __________________________________________________________________________
* | |
* | === WARNING: GLOBAL GADGET FILE === |
* | Changes to this page affect many users. |
* | Please discuss changes on the talk page or on [[WP:VPT]] before editing. |
* |__________________________________________________________________________|
*
* Advanced Site Notices for Malay Wikipedia
* Based on Chinese Wikipedia's MediaWiki:Gadget-AdvancedSiteNotices.js,
* but without:
* - geo-targeting: no in_country(), in_region(), in_city()
* - Chinese variant support: no HanAssist, no wgUserVariant parsing
*
* Supported criteria functions:
* - in_group('sysop')
* - in_group_every('sysop', 'interface-admin')
* - in_lang('ms')
* - is_anon()
* - is_temp()
* - is_named()
*
* Contributors: [[zh:User:PhiLiP]], [[zh:User:Diskdance]], [[zh:User:SunAfterRain]], [[User:Hakimi97]]
*/
Promise.all([
$.ready,
]).then(() => {
if (
$("#siteNotice").length <= 0 ||
mw.config.get("wgAction") === "edit" ||
mw.config.get("wgAction") === "submit"
) {
return;
}
let customASNInterval = window.customASNInterval || 15;
const COOKIE_NAME = "dismissASN";
let cookieVal = Number.parseInt(mw.cookie.get(COOKIE_NAME) || "-1", 10);
let revisionId = 0;
let timeoutId = null;
let $asnRoot = $("<div>", { id: "asn-dismissable-notice" });
let $asnBody = $("<div>", {
id: "advancedSiteNotices",
class: "mw-parser-output",
});
let $asnClose = $("<button>", {
title: "Tutup",
"aria-label": "Tutup",
class: "asn-close-button",
});
$asnRoot.append($asnBody, $asnClose);
$asnClose.click(() => {
$asnClose.prop("disabled", true);
mw.cookie.set(COOKIE_NAME, revisionId, {
expires: 60 * 60 * 24 * 30,
path: "/",
secure: true,
});
clearTimeout(timeoutId);
$asnRoot.fadeOut(() => {
$asnRoot.remove();
});
});
/**
* @typedef {Object} TokenizeStatus
* @property {string} expression
* @property {RegExp} startTokenRe
* @property {number} index
* @property {((item: any) => void) & { __orig__?: (item: any) => void }} append
*/
/**
* Criteria parser. Only a small subset of JavaScript is supported.
*/
class CriteriaExecutor {
/**
* @param {Record<string, (...args: any[]) => any>} functions
*/
constructor(functions = {}) {
this.functions = functions;
}
/**
* Parse expressions into tokens.
*
* @param {string} expression
* @return {any[]}
*/
_tokenizeExpression(expression) {
expression = expression.trim();
if (!expression) {
return [];
}
const result = [];
/** @type {TokenizeStatus} */
const status = {
expression,
startTokenRe: /\|\||&&|\b(?:true|false)\b|[("'!\s]/g,
index: 0,
append(item) {
result.push(item);
},
};
let match = status.startTokenRe.exec(expression);
const skipSpace = () => {
while (/\s/.test(expression[status.index] || "")) {
status.index++;
}
};
while (match) {
const token = match[0];
if (status.startTokenRe.lastIndex - token.length !== status.index) {
if (token === "(") {
const parsed = this._handleFunctionCall(status);
if (!parsed) {
break;
}
} else {
break;
}
} else {
let parsed = false;
switch (token) {
case "(":
parsed = this._handleExpressionStatement(status);
break;
case "||":
case "&&":
parsed = this._handleLogicalOperator(status, token);
break;
case "!":
parsed = this._handleUnaryExpression(status, token);
break;
case "'":
case '"':
parsed = this._handleStringLiteral(status, token);
break;
case "true":
case "false":
parsed = this._handleBooleanLiteral(status, token);
break;
}
if (!parsed) {
break;
}
}
skipSpace();
status.startTokenRe.lastIndex = status.index;
match = status.startTokenRe.exec(expression);
}
if (status.index < expression.length - 1) {
throw new SyntaxError("Unexpected token.");
}
return result;
}
/**
* @param {TokenizeStatus} status
* @return {boolean}
*/
_handleFunctionCall(status) {
const functionName = status.expression
.slice(status.index, status.startTokenRe.lastIndex - 1)
.trim();
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(functionName)) {
return false;
}
const argsStartIndex = status.startTokenRe.lastIndex - 1;
const endIndex = this._findNextBalanceItem(
status.expression,
"(",
")",
argsStartIndex
);
if (endIndex === -1) {
throw new SyntaxError("Unbalanced parentheses in function call");
}
const rawArgs = status.expression
.slice(argsStartIndex + 1, endIndex)
.trim();
const args = !rawArgs.length ? [] : this._balanceSplit(rawArgs, ",", "(", ")");
status.append({
type: "FunctionCall",
functionName,
args: args.map((arg) => this._tokenizeExpression(arg)),
});
status.index = endIndex + 1;
return true;
}
/**
* @param {TokenizeStatus} status
* @param {string} token
* @return {boolean}
*/
_handleLogicalOperator(status, token) {
status.append({
type: "LogicalExpression",
operator: token,
});
status.index = status.startTokenRe.lastIndex;
return true;
}
/**
* @param {TokenizeStatus} status
* @param {string} token
* @return {boolean}
*/
_handleUnaryExpression(status, token) {
const item = {
type: "UnaryExpression",
operator: token,
argument: undefined,
};
status.append(item);
const origAppend = status.append.__orig__ || status.append;
status.append = function (nextItem) {
if (nextItem.type === "LogicalExpression") {
throw new SyntaxError("Unexpected LogicalExpression");
}
item.argument = nextItem;
status.append = origAppend;
};
status.append.__orig__ = origAppend;
status.index = status.startTokenRe.lastIndex;
return true;
}
/**
* @param {TokenizeStatus} status
* @param {string} token
* @return {boolean}
*/
_handleBooleanLiteral(status, token) {
status.append({
type: "BooleanLiteral",
value: token === "true",
});
status.index = status.startTokenRe.lastIndex;
return true;
}
/**
* @param {TokenizeStatus} status
* @return {boolean}
*/
_handleExpressionStatement(status) {
const endIndex = this._findNextBalanceItem(
status.expression,
"(",
")",
status.index
);
if (endIndex === -1) {
throw new SyntaxError("Unbalanced parentheses in expression.");
}
status.append({
type: "ExpressionStatement",
expression: this._tokenizeExpression(
status.expression.slice(status.index + 1, endIndex)
),
});
status.index = endIndex + 1;
return true;
}
/**
* @param {TokenizeStatus} status
* @param {string} delimiter
* @return {boolean}
*/
_handleStringLiteral(status, delimiter) {
const endIndex = this._findNextTokenWithoutEscape(
status.expression,
delimiter,
status.index + 1
);
if (endIndex === -1) {
throw new SyntaxError("Unterminated string literal.");
}
status.append({
type: "StringLiteral",
value: this._parseString(
status.expression.slice(status.index + 1, endIndex)
),
});
status.index = endIndex + 1;
return true;
}
/**
* @param {string} expression
* @param {string} startToken
* @param {string} endToken
* @param {number} currentIndex
* @return {number}
*/
_findNextBalanceItem(expression, startToken, endToken, currentIndex) {
let count = 1;
while (++currentIndex < expression.length) {
if (expression[currentIndex] === startToken) {
count++;
} else if (expression[currentIndex] === endToken) {
count--;
}
if (count === 0) {
return currentIndex;
}
}
return -1;
}
/**
* @param {string} expression
* @param {string} splitToken
* @param {string} startToken
* @param {string} endToken
* @return {string[]}
*/
_balanceSplit(expression, splitToken, startToken, endToken) {
const result = [];
let balance = 0;
let current = "";
for (const char of expression) {
if (char === startToken) {
balance++;
} else if (char === endToken) {
balance--;
}
if (char === splitToken && balance === 0) {
result.push(current.trim());
current = "";
} else {
current += char;
}
}
if (current) {
result.push(current.trim());
}
return result;
}
/**
* @param {string} expression
* @param {string} token
* @param {number} currentIndex
* @return {number}
*/
_findNextTokenWithoutEscape(expression, token, currentIndex) {
while (currentIndex < expression.length) {
const foundIndex = expression.indexOf(token, currentIndex);
if (foundIndex === -1) {
return -1;
} else if (expression[foundIndex - 1] !== "\\") {
return foundIndex;
}
currentIndex = foundIndex + 1;
}
return -1;
}
/**
* @param {string} input
* @return {string}
*/
_parseString(input) {
return input.replace(
/\\(n|t|r|b|f|x[0-9A-Fa-f]{2}|u\{[0-9A-Fa-f]+\}|u[0-9A-Fa-f]{4}|.)/g,
(_, esc) => {
switch (esc[0]) {
case "n":
return "\n";
case "t":
return "\t";
case "r":
return "\r";
case "b":
return "\b";
case "f":
return "\f";
case "x":
if (esc === "x") {
throw new SyntaxError("Invalid hexadecimal escape sequence.");
}
return String.fromCharCode(parseInt(esc.slice(1), 16));
case "u":
if (esc === "u") {
throw new SyntaxError("Invalid Unicode escape sequence.");
} else if (esc[1] === "{") {
const codePoint = Number.parseInt(esc.slice(2, -1), 16);
if (codePoint > 0x10ffff) {
throw new SyntaxError(
`Undefined Unicode code-point: \\${esc}.`
);
}
return String.fromCodePoint(codePoint);
}
return String.fromCharCode(Number.parseInt(esc.slice(1), 16));
default:
return esc;
}
}
);
}
/**
* @param {any[]} tokens
*/
_tokensToAst(tokens) {
if (!tokens.length) {
throw new TypeError("Token list is empty.");
}
let logicalItemIndex = tokens.findIndex((t) => t.type === "LogicalExpression" && t.operator === "&&");
if (logicalItemIndex === -1) {
logicalItemIndex = tokens.findIndex((t) => t.type === "LogicalExpression" && t.operator === "||");
}
if (logicalItemIndex === -1) {
if (tokens.length === 1) {
return this._tokenToAst(tokens[0]);
}
throw new SyntaxError(`Unexpected ${tokens[1].type}.`);
} else if (logicalItemIndex === 0) {
throw new SyntaxError("Unexpected LogicalExpression");
} else {
const left = this._tokensToAst(tokens.slice(0, logicalItemIndex));
const right = this._tokensToAst(tokens.slice(logicalItemIndex + 1));
return {
type: "LogicalExpression",
operator: tokens[logicalItemIndex].operator,
left,
right,
};
}
}
/**
* @param {any} token
*/
_tokenToAst(token) {
if (!token) {
throw new TypeError("token is undefined or null.");
}
const result = Object.assign({}, token);
if (token.type === "UnaryExpression") {
if (!result.argument) {
throw new SyntaxError("Unexpected UnaryExpression.");
}
result.argument = this._tokenToAst(token.argument);
} else if (token.type === "FunctionCall") {
result.args = token.args.map((argument) =>
this._tokensToAst(argument)
);
} else if (token.type === "ExpressionStatement") {
result.expression = this._tokensToAst(token.expression);
}
return result;
}
/**
* @param {string} expression
*/
toAst(expression) {
const tokenizes = this._tokenizeExpression(expression);
return this._tokensToAst(tokenizes);
}
/**
* @param {string} functionName
* @param {any[]} args
*/
_executeFunction(functionName, args) {
if (!Object.prototype.hasOwnProperty.call(this.functions, functionName)) {
throw new Error(`Function ${functionName} is not allowed.`);
}
return this.functions[functionName](...args);
}
/**
* @param {any} ast
*/
evaluate(ast) {
switch (ast.type) {
case "ExpressionStatement":
return this.evaluate(ast.expression);
case "UnaryExpression":
if (ast.operator === "!") {
return !this.evaluate(ast.argument);
}
throw new Error(`Unknown UnaryExpression operator: ${ast.operator}`);
case "LogicalExpression":
if (ast.operator === "&&" || ast.operator === "||") {
const left = this.evaluate(ast.left);
if (ast.operator === "&&" && !left) {
return false;
} else if (ast.operator === "||" && left) {
return true;
}
return this.evaluate(ast.right);
}
throw new Error(`Unknown LogicalExpression operator: ${ast.operator}`);
case "FunctionCall": {
const args = ast.args.map((arg) => this.evaluate(arg));
let returnValue = this._executeFunction(ast.functionName, args);
if (typeof returnValue === "undefined" || returnValue === null) {
returnValue = false;
} else if (typeof returnValue !== "string" && typeof returnValue !== "boolean") {
console.warn(
"[AdvancedSiteNotices]: The return type %s of the function %s() is unsafe and has been forcibly converted to a string.",
typeof returnValue,
ast.functionName
);
returnValue = String(returnValue);
}
return returnValue;
}
case "StringLiteral":
case "BooleanLiteral":
return ast.value;
default:
throw new Error(`Unknown AST node type: ${ast.type}`);
}
}
}
const functions = {};
const configs = mw.config.get(["wgUserGroups", "wgUserLanguage"]);
functions.in_group = (...groups) => groups.some((group) => configs.wgUserGroups.includes(group));
functions.in_group_every = (...groups) => groups.every((group) => configs.wgUserGroups.includes(group));
functions.in_lang = (...useLangs) => useLangs.includes(configs.wgUserLanguage);
functions.is_anon = () => !!mw.user.isAnon();
functions.is_temp = () => typeof mw.user.isTemp === "function" ? !!mw.user.isTemp() : false;
functions.is_named = () => typeof mw.user.isNamed === "function" ? !!mw.user.isNamed() : !mw.user.isAnon();
const parser = new CriteriaExecutor(functions);
const cache = new WeakMap();
function getCache($element, key) {
const element = $element.get(0);
if (cache.has(element)) {
return cache.get(element)[key];
}
}
function setCache($element, key, value) {
const element = $element.get(0);
if (cache.has(element)) {
cache.get(element)[key] = value;
} else {
cache.set(element, {
[key]: value,
});
}
}
function matchCriteria($noticeItem) {
let cache = getCache($noticeItem, "asn-cache");
if (cache !== undefined) {
return cache;
}
let criteria = $noticeItem.attr("data-asn-criteria");
let result;
if (criteria !== undefined) {
if (criteria === "") {
result = true;
} else {
try {
criteria = decodeURIComponent(criteria.replace(/\+/g, "%20")).trim();
const ast = parser.toAst(criteria);
result = !!parser.evaluate(ast);
} catch (error) {
console.warn(
"[AdvancedSiteNotices]: Fail to parse or evaluate criteria \"%s\":",
criteria,
error
);
result = false;
}
}
} else {
const testList = [];
result = !testList.length || testList.every((v) => !!v);
}
setCache($noticeItem, "asn-cache", result);
return result;
}
function getNoticeElement($noticeItem) {
let $cache = getCache($noticeItem, "asn-element");
if ($cache !== undefined) {
return $cache;
}
$cache = $("<div>").append($noticeItem.contents().clone());
setCache($noticeItem, "asn-element", $cache);
return $cache;
}
let isSetMinHeightCalled = false;
function setMinHeight($noticeList) {
let minHeight = -1;
$noticeList.each((_, nt) => {
let $nt = $(nt);
$asnBody.replaceWith($nt);
minHeight = Math.max(minHeight, $nt.height());
$nt.replaceWith($asnBody);
});
$asnRoot.css("min-height", `${minHeight}px`);
if (!isSetMinHeightCalled) {
isSetMinHeightCalled = true;
window.addEventListener(
"resize",
mw.util.debounce(() => setMinHeight($noticeList), 300)
);
}
}
function loadNotice($noticeList, pos) {
const $noticeItem = $noticeList.eq(pos);
let nextPos = pos + 1;
if (nextPos === $noticeList.length) {
nextPos = 0;
}
const $noticeElement = getNoticeElement($noticeItem);
if ($asnBody.children().length) {
$asnBody.stop().fadeOut(() => {
$asnBody.empty().append($noticeElement);
try {
$asnBody.fadeIn();
} catch (_) { }
});
} else {
$asnBody.append($noticeElement).fadeIn();
}
if ($noticeList.length > 1) {
timeoutId = setTimeout(() => {
loadNotice($noticeList, nextPos);
}, customASNInterval * 1000);
}
}
function initialNotices($noticeList) {
if (!$asnRoot.length || !$noticeList.length || revisionId === cookieVal) {
return;
}
mw.cookie.set(COOKIE_NAME, null);
$asnRoot.appendTo($("#siteNotice"));
setMinHeight($noticeList);
loadNotice($noticeList, Math.floor(Math.random() * $noticeList.length));
}
new mw.Api({
ajax: {
headers: {
"Api-User-Agent": "w:ms:MediaWiki:Gadget-AdvancedSiteNotices.js",
},
},
})
.parse(new mw.Title("Templat:AdvancedSiteNotices/ajax"), {
maxage: 3600,
smaxage: 3600,
})
.then((html) => {
let $json = $("ul.sitents", $.parseHTML(html));
let $noticeList = $("li", $json).filter((_, li) => matchCriteria($(li)));
revisionId = $json.data("asn-version");
initialNotices($noticeList);
})
.catch((e) => {
console.error("[AdvancedSiteNotices]: error ", e);
});
});
// </nowiki>
pa21l5shzvzfk0l9ex55289cmyodguf
23406
23402
2026-06-10T03:28:57Z
Hakimi97
9
23406
javascript
text/javascript
// <nowiki>
/*
* ****************************************************************************
* * >>>>> TO GADGET IMPORTERS: READ BEFORE IMPORING <<<<< *
* * Please MAKE SURE the notice page ([[Template:AdvancedSiteNotices/ajax]]) *
* * is properly protected on your wiki. *
* * Albeit efforts are made to handle JavaScript expressions correctly, *
* * they are not exhaustively battle-tested. *
* ****************************************************************************
*
* __________________________________________________________________________
* | |
* | === WARNING: GLOBAL GADGET FILE === |
* | Changes to this page affect many users. |
* | Please discuss changes on the talk page or on [[WP:VPT]] before editing. |
* |__________________________________________________________________________|
*
* Advanced Site Notices for Malay Wikipedia
* Based on Chinese Wikipedia's MediaWiki:Gadget-AdvancedSiteNotices.js,
* but without:
* - geo-targeting: no in_country(), in_region(), in_city()
* - Chinese variant support: no HanAssist, no wgUserVariant parsing
*
* Supported criteria functions:
* - in_group('sysop')
* - in_group_every('sysop', 'interface-admin')
* - in_lang('ms')
* - is_anon()
* - is_temp()
* - is_named()
*
* Contributors: [[zh:User:PhiLiP]], [[zh:User:Diskdance]], [[zh:User:SunAfterRain]], [[User:Hakimi97]]
*/
Promise.all([
$.ready,
]).then(() => {
if (
$("#siteNotice").length <= 0 ||
mw.config.get("wgAction") === "edit" ||
mw.config.get("wgAction") === "submit"
) {
return;
}
let customASNInterval = window.customASNInterval || 15;
const COOKIE_NAME = "dismissASN";
let cookieVal = Number.parseInt(mw.cookie.get(COOKIE_NAME) || "-1", 10);
let revisionId = 0;
let timeoutId = null;
let $asnRoot = $("<div>", { id: "asn-dismissable-notice" });
let $asnBody = $("<div>", {
id: "advancedSiteNotices",
class: "mw-parser-output",
});
let $asnClose = $("<button>", {
title: "Tutup",
"aria-label": "Tutup",
class: "asn-close-button",
});
$asnRoot.append($asnBody, $asnClose);
$asnClose.click(() => {
$asnClose.prop("disabled", true);
mw.cookie.set(COOKIE_NAME, revisionId, {
expires: 60 * 60 * 24 * 30,
path: "/",
secure: true,
});
clearTimeout(timeoutId);
$asnRoot.fadeOut(() => {
$asnRoot.remove();
});
});
/**
* @typedef {Object} TokenizeStatus
* @property {string} expression
* @property {RegExp} startTokenRe
* @property {number} index
* @property {((item: any) => void) & { __orig__?: (item: any) => void }} append
*/
/**
* Criteria parser. Only a small subset of JavaScript is supported.
*/
class CriteriaExecutor {
/**
* @param {Record<string, (...args: any[]) => any>} functions
*/
constructor(functions = {}) {
this.functions = functions;
}
/**
* Parse expressions into tokens.
*
* @param {string} expression
* @return {any[]}
*/
_tokenizeExpression(expression) {
expression = expression.trim();
if (!expression) {
return [];
}
const result = [];
/** @type {TokenizeStatus} */
const status = {
expression,
startTokenRe: /\|\||&&|\b(?:true|false)\b|[("'!\s]/g,
index: 0,
append(item) {
result.push(item);
},
};
let match = status.startTokenRe.exec(expression);
const skipSpace = () => {
while (/\s/.test(expression[status.index] || "")) {
status.index++;
}
};
while (match) {
const token = match[0];
if (status.startTokenRe.lastIndex - token.length !== status.index) {
if (token === "(") {
const parsed = this._handleFunctionCall(status);
if (!parsed) {
break;
}
} else {
break;
}
} else {
let parsed = false;
switch (token) {
case "(":
parsed = this._handleExpressionStatement(status);
break;
case "||":
case "&&":
parsed = this._handleLogicalOperator(status, token);
break;
case "!":
parsed = this._handleUnaryExpression(status, token);
break;
case "'":
case '"':
parsed = this._handleStringLiteral(status, token);
break;
case "true":
case "false":
parsed = this._handleBooleanLiteral(status, token);
break;
}
if (!parsed) {
break;
}
}
skipSpace();
status.startTokenRe.lastIndex = status.index;
match = status.startTokenRe.exec(expression);
}
if (status.index < expression.length - 1) {
throw new SyntaxError("Unexpected token.");
}
return result;
}
/**
* @param {TokenizeStatus} status
* @return {boolean}
*/
_handleFunctionCall(status) {
const functionName = status.expression
.slice(status.index, status.startTokenRe.lastIndex - 1)
.trim();
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(functionName)) {
return false;
}
const argsStartIndex = status.startTokenRe.lastIndex - 1;
const endIndex = this._findNextBalanceItem(
status.expression,
"(",
")",
argsStartIndex
);
if (endIndex === -1) {
throw new SyntaxError("Unbalanced parentheses in function call");
}
const rawArgs = status.expression
.slice(argsStartIndex + 1, endIndex)
.trim();
const args = !rawArgs.length ? [] : this._balanceSplit(rawArgs, ",", "(", ")");
status.append({
type: "FunctionCall",
functionName,
args: args.map((arg) => this._tokenizeExpression(arg)),
});
status.index = endIndex + 1;
return true;
}
/**
* @param {TokenizeStatus} status
* @param {string} token
* @return {boolean}
*/
_handleLogicalOperator(status, token) {
status.append({
type: "LogicalExpression",
operator: token,
});
status.index = status.startTokenRe.lastIndex;
return true;
}
/**
* @param {TokenizeStatus} status
* @param {string} token
* @return {boolean}
*/
_handleUnaryExpression(status, token) {
const item = {
type: "UnaryExpression",
operator: token,
argument: undefined,
};
status.append(item);
const origAppend = status.append.__orig__ || status.append;
status.append = function (nextItem) {
if (nextItem.type === "LogicalExpression") {
throw new SyntaxError("Unexpected LogicalExpression");
}
item.argument = nextItem;
status.append = origAppend;
};
status.append.__orig__ = origAppend;
status.index = status.startTokenRe.lastIndex;
return true;
}
/**
* @param {TokenizeStatus} status
* @param {string} token
* @return {boolean}
*/
_handleBooleanLiteral(status, token) {
status.append({
type: "BooleanLiteral",
value: token === "true",
});
status.index = status.startTokenRe.lastIndex;
return true;
}
/**
* @param {TokenizeStatus} status
* @return {boolean}
*/
_handleExpressionStatement(status) {
const endIndex = this._findNextBalanceItem(
status.expression,
"(",
")",
status.index
);
if (endIndex === -1) {
throw new SyntaxError("Unbalanced parentheses in expression.");
}
status.append({
type: "ExpressionStatement",
expression: this._tokenizeExpression(
status.expression.slice(status.index + 1, endIndex)
),
});
status.index = endIndex + 1;
return true;
}
/**
* @param {TokenizeStatus} status
* @param {string} delimiter
* @return {boolean}
*/
_handleStringLiteral(status, delimiter) {
const endIndex = this._findNextTokenWithoutEscape(
status.expression,
delimiter,
status.index + 1
);
if (endIndex === -1) {
throw new SyntaxError("Unterminated string literal.");
}
status.append({
type: "StringLiteral",
value: this._parseString(
status.expression.slice(status.index + 1, endIndex)
),
});
status.index = endIndex + 1;
return true;
}
/**
* @param {string} expression
* @param {string} startToken
* @param {string} endToken
* @param {number} currentIndex
* @return {number}
*/
_findNextBalanceItem(expression, startToken, endToken, currentIndex) {
let count = 1;
while (++currentIndex < expression.length) {
if (expression[currentIndex] === startToken) {
count++;
} else if (expression[currentIndex] === endToken) {
count--;
}
if (count === 0) {
return currentIndex;
}
}
return -1;
}
/**
* @param {string} expression
* @param {string} splitToken
* @param {string} startToken
* @param {string} endToken
* @return {string[]}
*/
_balanceSplit(expression, splitToken, startToken, endToken) {
const result = [];
let balance = 0;
let current = "";
for (const char of expression) {
if (char === startToken) {
balance++;
} else if (char === endToken) {
balance--;
}
if (char === splitToken && balance === 0) {
result.push(current.trim());
current = "";
} else {
current += char;
}
}
if (current) {
result.push(current.trim());
}
return result;
}
/**
* @param {string} expression
* @param {string} token
* @param {number} currentIndex
* @return {number}
*/
_findNextTokenWithoutEscape(expression, token, currentIndex) {
while (currentIndex < expression.length) {
const foundIndex = expression.indexOf(token, currentIndex);
if (foundIndex === -1) {
return -1;
} else if (expression[foundIndex - 1] !== "\\") {
return foundIndex;
}
currentIndex = foundIndex + 1;
}
return -1;
}
/**
* @param {string} input
* @return {string}
*/
_parseString(input) {
return input.replace(
/\\(n|t|r|b|f|x[0-9A-Fa-f]{2}|u\{[0-9A-Fa-f]+\}|u[0-9A-Fa-f]{4}|.)/g,
(_, esc) => {
switch (esc[0]) {
case "n":
return "\n";
case "t":
return "\t";
case "r":
return "\r";
case "b":
return "\b";
case "f":
return "\f";
case "x":
if (esc === "x") {
throw new SyntaxError("Invalid hexadecimal escape sequence.");
}
return String.fromCharCode(parseInt(esc.slice(1), 16));
case "u":
if (esc === "u") {
throw new SyntaxError("Invalid Unicode escape sequence.");
} else if (esc[1] === "{") {
const codePoint = Number.parseInt(esc.slice(2, -1), 16);
if (codePoint > 0x10ffff) {
throw new SyntaxError(
`Undefined Unicode code-point: \\${esc}.`
);
}
return String.fromCodePoint(codePoint);
}
return String.fromCharCode(Number.parseInt(esc.slice(1), 16));
default:
return esc;
}
}
);
}
/**
* @param {any[]} tokens
*/
_tokensToAst(tokens) {
if (!tokens.length) {
throw new TypeError("Token list is empty.");
}
let logicalItemIndex = tokens.findIndex((t) => t.type === "LogicalExpression" && t.operator === "&&");
if (logicalItemIndex === -1) {
logicalItemIndex = tokens.findIndex((t) => t.type === "LogicalExpression" && t.operator === "||");
}
if (logicalItemIndex === -1) {
if (tokens.length === 1) {
return this._tokenToAst(tokens[0]);
}
throw new SyntaxError(`Unexpected ${tokens[1].type}.`);
} else if (logicalItemIndex === 0) {
throw new SyntaxError("Unexpected LogicalExpression");
} else {
const left = this._tokensToAst(tokens.slice(0, logicalItemIndex));
const right = this._tokensToAst(tokens.slice(logicalItemIndex + 1));
return {
type: "LogicalExpression",
operator: tokens[logicalItemIndex].operator,
left,
right,
};
}
}
/**
* @param {any} token
*/
_tokenToAst(token) {
if (!token) {
throw new TypeError("token is undefined or null.");
}
const result = Object.assign({}, token);
if (token.type === "UnaryExpression") {
if (!result.argument) {
throw new SyntaxError("Unexpected UnaryExpression.");
}
result.argument = this._tokenToAst(token.argument);
} else if (token.type === "FunctionCall") {
result.args = token.args.map((argument) =>
this._tokensToAst(argument)
);
} else if (token.type === "ExpressionStatement") {
result.expression = this._tokensToAst(token.expression);
}
return result;
}
/**
* @param {string} expression
*/
toAst(expression) {
const tokenizes = this._tokenizeExpression(expression);
return this._tokensToAst(tokenizes);
}
/**
* @param {string} functionName
* @param {any[]} args
*/
_executeFunction(functionName, args) {
if (!Object.prototype.hasOwnProperty.call(this.functions, functionName)) {
throw new Error(`Function ${functionName} is not allowed.`);
}
return this.functions[functionName](...args);
}
/**
* @param {any} ast
*/
evaluate(ast) {
switch (ast.type) {
case "ExpressionStatement":
return this.evaluate(ast.expression);
case "UnaryExpression":
if (ast.operator === "!") {
return !this.evaluate(ast.argument);
}
throw new Error(`Unknown UnaryExpression operator: ${ast.operator}`);
case "LogicalExpression":
if (ast.operator === "&&" || ast.operator === "||") {
const left = this.evaluate(ast.left);
if (ast.operator === "&&" && !left) {
return false;
} else if (ast.operator === "||" && left) {
return true;
}
return this.evaluate(ast.right);
}
throw new Error(`Unknown LogicalExpression operator: ${ast.operator}`);
case "FunctionCall": {
const args = ast.args.map((arg) => this.evaluate(arg));
let returnValue = this._executeFunction(ast.functionName, args);
if (typeof returnValue === "undefined" || returnValue === null) {
returnValue = false;
} else if (typeof returnValue !== "string" && typeof returnValue !== "boolean") {
console.warn(
"[AdvancedSiteNotices]: The return type %s of the function %s() is unsafe and has been forcibly converted to a string.",
typeof returnValue,
ast.functionName
);
returnValue = String(returnValue);
}
return returnValue;
}
case "StringLiteral":
case "BooleanLiteral":
return ast.value;
default:
throw new Error(`Unknown AST node type: ${ast.type}`);
}
}
}
const functions = {};
const configs = mw.config.get(["wgUserGroups", "wgUserLanguage"]);
functions.in_group = (...groups) => groups.some((group) => configs.wgUserGroups.includes(group));
functions.in_group_every = (...groups) => groups.every((group) => configs.wgUserGroups.includes(group));
functions.in_lang = (...useLangs) => useLangs.includes(configs.wgUserLanguage);
functions.is_anon = () => !!mw.user.isAnon();
functions.is_temp = () => typeof mw.user.isTemp === "function" ? !!mw.user.isTemp() : false;
functions.is_named = () => typeof mw.user.isNamed === "function" ? !!mw.user.isNamed() : !mw.user.isAnon();
const parser = new CriteriaExecutor(functions);
const cache = new WeakMap();
function getCache($element, key) {
const element = $element.get(0);
if (cache.has(element)) {
return cache.get(element)[key];
}
}
function setCache($element, key, value) {
const element = $element.get(0);
if (cache.has(element)) {
cache.get(element)[key] = value;
} else {
cache.set(element, {
[key]: value,
});
}
}
function matchCriteria($noticeItem) {
let cache = getCache($noticeItem, "asn-cache");
if (cache !== undefined) {
return cache;
}
let criteria = $noticeItem.attr("data-asn-criteria");
let result;
if (criteria !== undefined) {
if (criteria === "") {
result = true;
} else {
try {
criteria = decodeURIComponent(criteria.replace(/\+/g, "%20")).trim();
const ast = parser.toAst(criteria);
result = !!parser.evaluate(ast);
} catch (error) {
console.warn(
"[AdvancedSiteNotices]: Fail to parse or evaluate criteria \"%s\":",
criteria,
error
);
result = false;
}
}
} else {
const testList = [];
if ($noticeItem.hasClass("only_sysop")) {
testList.push(functions.in_group("sysop"));
}
if (
$noticeItem.hasClass("only_logged_in") ||
$noticeItem.hasClass("only_logged") ||
$noticeItem.hasClass("is_named")
) {
testList.push(functions.is_named());
}
if (
$noticeItem.hasClass("only_logged_out") ||
$noticeItem.hasClass("only_anon")
) {
testList.push(!functions.is_named());
}
if ($noticeItem.hasClass("is_temp")) {
testList.push(functions.is_temp());
}
if ($noticeItem.hasClass("is_anon")) {
testList.push(functions.is_anon());
}
result = !testList.length || testList.every((v) => !!v);
}
setCache($noticeItem, "asn-cache", result);
return result;
}
function getNoticeElement($noticeItem) {
let $cache = getCache($noticeItem, "asn-element");
if ($cache !== undefined) {
return $cache;
}
$cache = $("<div>").append($noticeItem.contents().clone());
setCache($noticeItem, "asn-element", $cache);
return $cache;
}
let isSetMinHeightCalled = false;
function setMinHeight($noticeList) {
let minHeight = -1;
$noticeList.each((_, nt) => {
let $nt = $(nt);
$asnBody.replaceWith($nt);
minHeight = Math.max(minHeight, $nt.height());
$nt.replaceWith($asnBody);
});
$asnRoot.css("min-height", `${minHeight}px`);
if (!isSetMinHeightCalled) {
isSetMinHeightCalled = true;
window.addEventListener(
"resize",
mw.util.debounce(() => setMinHeight($noticeList), 300)
);
}
}
function loadNotice($noticeList, pos) {
const $noticeItem = $noticeList.eq(pos);
let nextPos = pos + 1;
if (nextPos === $noticeList.length) {
nextPos = 0;
}
const $noticeElement = getNoticeElement($noticeItem);
if ($asnBody.children().length) {
$asnBody.stop().fadeOut(() => {
$asnBody.empty().append($noticeElement);
try {
$asnBody.fadeIn();
} catch (_) { }
});
} else {
$asnBody.append($noticeElement).fadeIn();
}
if ($noticeList.length > 1) {
timeoutId = setTimeout(() => {
loadNotice($noticeList, nextPos);
}, customASNInterval * 1000);
}
}
function initialNotices($noticeList) {
if (!$asnRoot.length || !$noticeList.length || revisionId === cookieVal) {
return;
}
mw.cookie.set(COOKIE_NAME, null);
$asnRoot.appendTo($("#siteNotice"));
setMinHeight($noticeList);
loadNotice($noticeList, Math.floor(Math.random() * $noticeList.length));
}
new mw.Api({
ajax: {
headers: {
"Api-User-Agent": "w:ms:MediaWiki:Gadget-AdvancedSiteNotices.js",
},
},
})
.parse(new mw.Title("Templat:AdvancedSiteNotices/ajax"), {
maxage: 3600,
smaxage: 3600,
})
.then((html) => {
let $json = $("ul.sitents", $.parseHTML(html));
let $noticeList = $("li", $json).filter((_, li) => matchCriteria($(li)));
revisionId = $json.data("asn-version");
initialNotices($noticeList);
})
.catch((e) => {
console.error("[AdvancedSiteNotices]: error ", e);
});
});
// </nowiki>
8jclwh7xpfuunoijy75urtcld71gk8t
Templat:Ucapan Wiki
10
5405
23409
20860
2026-06-10T04:00:03Z
Hakimi97
9
23409
wikitext
text/x-wiki
<big><big>{{#switch:{{CURRENTDAY}} {{CURRENTMONTHNAME}}
<!--Projek Wikimedia--><!--
-->|24 November=[[Fail:Balloons-aj.png|link=|60px]] Selamat hari jadi ke-{{#expr:{{CURRENTYEAR}}-2003}} [[:w:Wikisumber]]<!--
-->|15 Januari=[[Fail:Balloons-aj.png|link=|60px]] Selamat hari jadi ke-{{#expr:{{CURRENTYEAR}}-2001}} [[:w:Wikipedia]]<!--
-->|19 Jun|20 Jun|21 Jun=[[Fail:Balloons-aj.png|link=|60px]] Selamat hari jadi ke-{{#expr:{{CURRENTYEAR}}-2003}} [[:w:Yayasan Wikimedia]]<!--
-->|27 Jun=[[Fail:Balloons-aj.png|link=|60px]] Selamat hari jadi [[wikiquote:|Wikipetik]]<!--
-->|29 Oktober=[[Fail:Balloons-aj.png|link=|60px]] Selamat hari jadi [[wikidata:|Wikidata]]<!--
-->|12 Disember=[[Fail:Balloons-aj.png|link=|60px]] Selamat hari jadi [[wiktionary:|Wikikamus]]<!--
--><!--Wikisumber Bahasa Melayu, ditayangkan 3 hari--><!--
-->|23 April |24 April |25 April=[[Fail:Balloons-aj.png|link=|60px]] <big>Selamat hari jadi ke-{{#expr:{{CURRENTYEAR}}-2024}} [[:w:Wikisumber|Wikisumber Bahasa Melayu]]!</big> [[File:Old Wikisource logo used until 2006.jpg|link=|60px]]<!--
-->|24 Mei=Selamat hari jadi ke-{{#expr:{{CURRENTYEAR}}-2004}} [[:w:Wikikamus|Wikikamus Bahasa Melayu]]<!--
-->|24 Ogos=Selamat hari jadi ke-{{#expr:{{CURRENTYEAR}}-2004}} [[:w:Wikibuku|Wikibuku Bahasa Melayu]]<!--
-->|26 Oktober=Selamat Hari Jadi ke-{{#expr:{{CURRENTYEAR}}-2002}}, [[:w:Wikipedia Bahasa Melayu]]<!--
--><!--Wikisumber bahasa-bahasa yang dituturkan di Brunei, Malaysia dan Singapura--><!--
-->
}}</big></big><noinclude>
{{Pendokumenan}}</noinclude>
d3jolc1s8kutkrbh7xkcxgg8wc6n2va
Templat:Ucapan Peristiwa
10
5406
23407
22251
2026-06-10T03:57:25Z
Hakimi97
9
23407
wikitext
text/x-wiki
<big><big>{{#switch:{{CURRENTDAY}} {{CURRENTMONTHNAME}}
<!--Tahun Baharu, ditampilkan 7 hari--><!--
-->|26 Disember|27 Disember|28 Disember|29 Disember|30 Disember|31 Disember|1 Januari|2 Januari={{UP Tahun Baru Masihi}}<!--
--><!--Hari kelepasan negara di Brunei, Malaysia, Singapura--><!--
-->|20 Februari=Selamat memperingati [[w:Pengisytiharan Kemerdekaan Persekutuan Tanah Melayu|pengisytiharan kemerdekaan Persekutuan Tanah Melayu]]<!--
-->|23 Februari={{UP Hari Kebangsaan Brunei}}<!--
-->|22 Julai=Selamat Hari [[w:Sarawak|Sarawak]]<!--
-->|9 Ogos={{UP Hari Kebangsaan Singapura}}<!--
-->|17 Ogos={{UP Hari Kemerdekaan Indonesia}}<!--
-->|31 Ogos={{UP Hari Kebangsaan Malaysia}}<!--
-->|31 Ogos=Selamat Hari [[w:Sabah|Sabah]]<!--
-->|16 September=Selamat [[w:Hari Malaysia|Hari Malaysia]]<!--
--><!--Hari bangsa-bangsa di Brunei, Malaysia, Singapura--><!--
-->|16 Februari|17 Februari={{UP Tahun Baru Cina}}<!--
-->|13 April|14 April|15 April=Selamat menyambut perayaan [[w:Songkran|Songkran]]<!--
-->|30 Mei|31 Mei=Selamat menyambut [[w:Tadau Kaamatan|Hari Kaamatan]]<!--
-->|1 Jun|2 Jun=Selamat menyambut [[w:Hari Gawai|Hari Gawai Dayak]]<!--
-->|10 Jun=Percubaan sahaja {{UP Tahun Baru Hijrah}}<!--
-->|17 Jun={{UP Tahun Baru Hijrah}}<!--
--><!--Hari agama-agama di Brunei, Malaysia, Singapura--><!--
-->|17 Januari=Selamat memperingati [[w:Israk dan Mikraj|Israk dan Mikraj]]<!--
-->|1 Februari={{UP Thaipusam}}<!--
-->|18 Februari=Selamat beribadat di [[w:Rabu Abu|Rabu Abu]] kepada masyarakat Kristian<!--
-->|19 Februari|20 Februari|21 Februari|22 Februari|23 Februari|24 Februari|25 Februari|26 Februari=Selamat berpuasa sepanjang [[w:Ramadan (bulan Islam)|Ramadan]]<!--
-->|27 Februari|28 Februari|1 Mac|2 Mac|3 Mac|4 Mac|5 Mac|6 Mac=Selamat berpuasa sepanjang [[w:Ramadan (bulan Islam)|Ramadan]]<!--
-->|8 Mac|9 Mac|10 Mac|11 Mac|12 Mac|13 Mac|14 Mac|15 Mac=Selamat berpuasa sepanjang [[w:Ramadan (bulan Islam)|Ramadan]]<!--
-->|7 Mac=Selamat memperingati [[w:Nuzul Quran|penurunan al-Quran]]<!--
-->|16 Mac|17 Mac|18 Mac|19 Mac=Selamat berpuasa sepanjang [[w:Ramadan (bulan Islam)|Ramadan]]<!--
-->|20 Mac|21 Mac|22 Mac|23 Mac={{UP Aidilfitri}}<!--
-->|3 April=Selamat Menyambut [[w:Jumaat Agung|Hari Jumaat Agung]]<!--
-->|5 April=Selamat [[w:Hari Paskah Besar|Hari Paskah]]<!--
-->|31 Mei=Selamat [[w:Hari Wesak|Hari Wesak]]<!--
-->|27 Mei|28 Mei={{UP Aidiladha}}<!--
-->|25 Ogos={{UP Maulid}}<!--
-->|20 Oktober={{UP Deepavali}}<!--
-->|1 November=Selamat menyambut [[w:Hari Para Orang Kudus|Hari Para Orang Kudus]]<!--
-->|2 November=Selamat menyambut [[w:Hari Para Mendiang|Hari Semua Jiwa]]<!--
-->|23 Disember|24 Disember|25 Disember={{UP Krismas}}<!--
--><!--Hari keputeraan raja Melayu di Brunei dan Malaysia--><!--
-->|14 Januari=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Yang di-Pertuan Besar Negeri Sembilan|Yang di-Pertuan Besar Negeri Sembilan]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|23 Mac=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Johor|Sultan Johor]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|26 April=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Terengganu|Sultan Terengganu]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|17 Mei=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Raja Perlis|Raja Perlis]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|22 Jun=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Kedah|Sultan Kedah]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|15 Julai=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Brunei|Sultan Brunei]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|30 Julai=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Pahang|Sultan Pahang]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|29 September=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Kelantan|Sultan Kelantan]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|7 November=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Perak|Sultan Perak]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|11 Disember=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Selangor|Sultan Selangor]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.}}</big></big>
<noinclude>{{Pendokumenan}}</noinclude>
57p16qqs2b6jjkf00ybe0lzn3wbpzsk
23408
23407
2026-06-10T03:59:09Z
Hakimi97
9
23408
wikitext
text/x-wiki
<big><big>{{#switch:{{CURRENTDAY}} {{CURRENTMONTHNAME}}
<!--Tahun Baharu, ditampilkan 7 hari--><!--
-->|26 Disember|27 Disember|28 Disember|29 Disember|30 Disember|31 Disember|1 Januari|2 Januari={{UP Tahun Baru Masihi}}<!--
--><!--Hari kelepasan negara di Brunei, Malaysia, Singapura--><!--
-->|20 Februari=Selamat memperingati [[w:Pengisytiharan Kemerdekaan Persekutuan Tanah Melayu|pengisytiharan kemerdekaan Persekutuan Tanah Melayu]]<!--
-->|23 Februari={{UP Hari Kebangsaan Brunei}}<!--
-->|22 Julai=Selamat Hari [[w:Sarawak|Sarawak]]<!--
-->|9 Ogos={{UP Hari Kebangsaan Singapura}}<!--
-->|17 Ogos={{UP Hari Kemerdekaan Indonesia}}<!--
-->|31 Ogos={{UP Hari Kebangsaan Malaysia}}<!--
-->|31 Ogos=Selamat Hari [[w:Sabah|Sabah]]<!--
-->|16 September=Selamat [[w:Hari Malaysia|Hari Malaysia]]<!--
--><!--Hari bangsa-bangsa di Brunei, Malaysia, Singapura--><!--
-->|16 Februari|17 Februari={{UP Tahun Baru Cina}}<!--
-->|13 April|14 April|15 April=Selamat menyambut perayaan [[w:Songkran|Songkran]]<!--
-->|30 Mei|31 Mei=Selamat menyambut [[w:Tadau Kaamatan|Hari Kaamatan]]<!--
-->|1 Jun|2 Jun=Selamat menyambut [[w:Hari Gawai|Hari Gawai Dayak]]<!--
-->|17 Jun={{UP Tahun Baru Hijrah}}<!--
--><!--Hari agama-agama di Brunei, Malaysia, Singapura--><!--
-->|17 Januari=Selamat memperingati [[w:Israk dan Mikraj|Israk dan Mikraj]]<!--
-->|1 Februari={{UP Thaipusam}}<!--
-->|18 Februari=Selamat beribadat di [[w:Rabu Abu|Rabu Abu]] kepada masyarakat Kristian<!--
-->|19 Februari|20 Februari|21 Februari|22 Februari|23 Februari|24 Februari|25 Februari|26 Februari=Selamat berpuasa sepanjang [[w:Ramadan (bulan Islam)|Ramadan]]<!--
-->|27 Februari|28 Februari|1 Mac|2 Mac|3 Mac|4 Mac|5 Mac|6 Mac=Selamat berpuasa sepanjang [[w:Ramadan (bulan Islam)|Ramadan]]<!--
-->|8 Mac|9 Mac|10 Mac|11 Mac|12 Mac|13 Mac|14 Mac|15 Mac=Selamat berpuasa sepanjang [[w:Ramadan (bulan Islam)|Ramadan]]<!--
-->|7 Mac=Selamat memperingati [[w:Nuzul Quran|penurunan al-Quran]]<!--
-->|16 Mac|17 Mac|18 Mac|19 Mac=Selamat berpuasa sepanjang [[w:Ramadan (bulan Islam)|Ramadan]]<!--
-->|20 Mac|21 Mac|22 Mac|23 Mac={{UP Aidilfitri}}<!--
-->|3 April=Selamat Menyambut [[w:Jumaat Agung|Hari Jumaat Agung]]<!--
-->|5 April=Selamat [[w:Hari Paskah Besar|Hari Paskah]]<!--
-->|31 Mei=Selamat [[w:Hari Wesak|Hari Wesak]]<!--
-->|27 Mei|28 Mei={{UP Aidiladha}}<!--
-->|25 Ogos={{UP Maulid}}<!--
-->|20 Oktober={{UP Deepavali}}<!--
-->|1 November=Selamat menyambut [[w:Hari Para Orang Kudus|Hari Para Orang Kudus]]<!--
-->|2 November=Selamat menyambut [[w:Hari Para Mendiang|Hari Semua Jiwa]]<!--
-->|23 Disember|24 Disember|25 Disember={{UP Krismas}}<!--
--><!--Hari keputeraan raja Melayu di Brunei dan Malaysia--><!--
-->|14 Januari=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Yang di-Pertuan Besar Negeri Sembilan|Yang di-Pertuan Besar Negeri Sembilan]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|23 Mac=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Johor|Sultan Johor]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|26 April=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Terengganu|Sultan Terengganu]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|17 Mei=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Raja Perlis|Raja Perlis]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|22 Jun=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Kedah|Sultan Kedah]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|15 Julai=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Brunei|Sultan Brunei]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|30 Julai=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Pahang|Sultan Pahang]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|29 September=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Kelantan|Sultan Kelantan]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|7 November=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Perak|Sultan Perak]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.<!--
-->|11 Disember=Merafak sembah ucapan setinggi-tinggi tahniah kepada [[w:Sultan Selangor|Sultan Selangor]]<br>sempena hari keputeraan tahun {{CURRENTYEAR}} ini.}}</big></big>
<noinclude>{{Pendokumenan}}</noinclude>
fvji41vizwhn5if151xpw117sf0r21o
MediaWiki:Gadget-AdvancedSiteNotices.css
8
8302
23397
2026-06-09T12:22:42Z
Hakimi97
9
Mengemas kini mengikut padanan Wikipedia bahasa Cina ([[:w:zh:MediaWiki:Gadget-AdvancedSiteNotices.css]])
23397
css
text/css
#siteNotice #asn-dismissable-notice {
margin-top: 12px;
display: flex;
align-items: center;
gap: 6px;
}
/* Align on Minerva/mobile */
@media screen and (max-width: 992px) {
.skin-minerva #siteNotice #asn-dismissable-notice {
margin-left: 16px;
margin-right: 16px;
}
}
#advancedSiteNotices {
flex: 1;
}
.asn-close-button {
margin-right: -6px;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
max-width: 28rem;
padding: 4px;
border-width: 1px;
border-style: solid;
border-radius: 2px;
overflow: hidden;
white-space: nowrap;
text-transform: none;
transition-property: background-color, color, border-color, box-shadow;
transition-duration: 0.1s;
color: var(--color-base, #202122);
background-color: var(--background-color-transparent, transparent);
border-color: var(--border-color-transparent, transparent);
cursor: pointer;
}
.asn-close-button:hover {
background-color: var(--background-color-interactive-subtle--hover, #eaecf0);
mix-blend-mode: var(--mix-blend-mode-blend, multiply);
}
.asn-close-button:active {
background-color: var(--background-color-interactive-subtle--active, #dadde3);
}
.asn-close-button:focus:not(:active) {
border-color: var(--border-color-progressive--focus, #36c);
box-shadow: inset 0 0 0 1px var(--box-shadow-color-progressive--focus, #36c);
}
.asn-close-button::before {
content: "";
width: 20px;
height: 20px;
background-color: currentColor;
mask: url(https://upload.wikimedia.org/wikipedia/commons/8/82/Codex_icon_close.svg) no-repeat center / contain;
-webkit-mask: url(https://upload.wikimedia.org/wikipedia/commons/8/82/Codex_icon_close.svg) no-repeat center / contain;
}
4dffsj3pla7clx7gwuxbbbdat49gup6
23403
23397
2026-06-10T02:48:53Z
Hakimi97
9
Kemas kini (guna tab bukannya aksara jarak)
23403
css
text/css
#siteNotice #asn-dismissable-notice {
margin-top: 12px;
display: flex;
align-items: center;
gap: 6px;
}
/* Align on Minerva/mobile */
@media screen and (max-width: 992px) {
.skin-minerva #siteNotice #asn-dismissable-notice {
margin-left: 16px;
margin-right: 16px;
}
}
#advancedSiteNotices {
flex: 1;
}
.asn-close-button {
margin-right: -6px;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
max-width: 28rem;
padding: 4px;
border-width: 1px;
border-style: solid;
border-radius: 2px;
overflow: hidden;
white-space: nowrap;
text-transform: none;
transition-property: background-color, color, border-color, box-shadow;
transition-duration: 0.1s;
color: var(--color-base, #202122);
background-color: var(--background-color-transparent, transparent);
border-color: var(--border-color-transparent, transparent);
cursor: pointer;
}
.asn-close-button:hover {
background-color: var(--background-color-interactive-subtle--hover, #eaecf0);
mix-blend-mode: var(--mix-blend-mode-blend, multiply);
}
.asn-close-button:active {
background-color: var(--background-color-interactive-subtle--active, #dadde3);
}
.asn-close-button:focus:not(:active) {
border-color: var(--border-color-progressive--focus, #36c);
box-shadow: inset 0 0 0 1px var(--box-shadow-color-progressive--focus, #36c);
}
.asn-close-button::before {
content: "";
width: 20px;
height: 20px;
background-color: currentColor;
mask: url(https://upload.wikimedia.org/wikipedia/commons/8/82/Codex_icon_close.svg) no-repeat center / contain;
-webkit-mask: url(https://upload.wikimedia.org/wikipedia/commons/8/82/Codex_icon_close.svg) no-repeat center / contain;
}
ddm33erabl85uclkqahh02ebj7vg7kq