CURRENT MISSION: Conclude the 2026 soyjak.party hack page, aswell as any pages relating to it, such as the Summer 2026 Crisis page.
Update Quotecord with any relevant info. See the blackboard for more info.
User:Tar/common.js: Difference between revisions
From Soyjak Wiki, the free ensoyclopedia
< User:Tar
No edit summary |
No edit summary Tag: Reverted |
||
| Line 164: | Line 164: | ||
})(); | })(); | ||
(function () { | |||
'use strict'; | |||
if (!['edit', 'submit'].includes(mw.config.get('wgAction'))) { | |||
return; | |||
} | |||
const config = { | |||
minimumCharacters: 2, | |||
maximumResults: 5, | |||
searchDelay: 200, | |||
usernameInsertFormat: '@[[User:$1]] ', | |||
emojiInsertFormat: '{{e|$1}} ' | |||
}; | |||
const emojis = [ | |||
'abdul', 'ack', 'alien', 'angel', 'brainlet', 'caca', 'calm', | |||
'cat', 'chang', 'cheeky', 'cheers', 'clittycel', 'clown', | |||
'coal', 'cold', 'concerned', 'cool', 'coomer', 'coping', | |||
'disgusted', 'diva', 'dog', 'dreamy', 'esl', 'euro', 'fact', | |||
'false', 'feelsgoodman', 'flushed', 'fruity', 'fuming', 'gem', | |||
'giga', 'glowie', 'heartbroken', 'hearts', 'holdit', 'horny', | |||
'hot', 'hunk', 'imp', 'informative', 'irritated', 'itsover', | |||
'jamming', 'jarty', 'jsid', 'kaching', 'kek', 'keyed', 'kumar', | |||
'locked', 'looking', 'marge', 'meds', 'mexi', 'mutt', | |||
'nailbiter', 'neutral', 'nophono', 'omgsisa', 'pleading', | |||
'poon', 'projecting', 'rage', 'reddit', 'robot', 'sad', | |||
'schizo', 'serious', 'shush', 'dough', 'sick', 'skull', | |||
'sleeping', 'slf', 'smart', 'smoking', 'smug', 'smugjak', | |||
'snca', 'sob', 'soyboy', 'soytan', 'spade', 'steiner', | |||
'surprised', 'suspicious', 'tendie', 'thinking', 'though', | |||
'thumbsdown', 'thumbsup', 'tired', 'tism', 'true', 'tsmt', | |||
'turk', 'tyrone', 'unc', 'uncomfortable', 'wholesome', | |||
'xitter', 'yum', 'zany', 'dodis', 'arrow', 'ijgs', 'genius' | |||
]; | |||
let codeMirror; | |||
let activeCompletion; | |||
let results = []; | |||
let selectedIndex = 0; | |||
let debounceTimer; | |||
let apiPromise = null; | |||
const menu = document.createElement('div'); | |||
menu.className = 'username-autocomplete'; | |||
menu.hidden = true; | |||
document.body.appendChild(menu); | |||
function getApi() { | |||
if (!apiPromise) { | |||
apiPromise = mw.loader | |||
.using('mediawiki.api') | |||
.then(function () { | |||
return new mw.Api(); | |||
}); | |||
} | |||
return apiPromise; | |||
} | |||
function getCodeMirrorFromEvent(event) { | |||
const wrapper = event.target instanceof Element && event.target.closest('.CodeMirror'); | |||
return wrapper?.CodeMirror || null; | |||
} | |||
function getCompletion(cm) { | |||
const cursor = cm.getCursor(); | |||
const before = cm.getLine(cursor.line).slice(0, cursor.ch); | |||
let match = before.match( | |||
/(?:^|[\s([{"'])@([^@\n\r\t<>{}\[\]|#/:;,!?]*)$/ | |||
); | |||
let type = 'username'; | |||
if (!match) { | |||
match = before.match( | |||
/(?:^|[\s([{"']):([a-zA-Z0-9_-]*)$/ | |||
); | |||
type = 'emoji'; | |||
} | |||
if (!match) { | |||
return null; | |||
} | |||
const query = match[1]; | |||
return { | |||
type, | |||
query, | |||
start: { | |||
line: cursor.line, | |||
ch: cursor.ch - query.length - 1 | |||
}, | |||
end: cursor | |||
}; | |||
} | |||
function completionsMatch(a, b) { | |||
return ( | |||
a && | |||
b && | |||
a.type === b.type && | |||
a.query === b.query && | |||
a.start.line === b.start.line && | |||
a.start.ch === b.start.ch && | |||
a.end.line === b.end.line && | |||
a.end.ch === b.end.ch | |||
); | |||
} | |||
function hideMenu() { | |||
menu.hidden = true; | |||
menu.replaceChildren(); | |||
activeCompletion = null; | |||
results = []; | |||
selectedIndex = 0; | |||
} | |||
function positionMenu() { | |||
if (!codeMirror) { | |||
return; | |||
} | |||
const pos = codeMirror.cursorCoords(null, 'page'); | |||
menu.style.left = pos.left + 'px'; | |||
menu.style.top = pos.bottom + 'px'; | |||
} | |||
function setSelectedIndex(index) { | |||
const items = menu.querySelectorAll( | |||
'.username-autocomplete-result' | |||
); | |||
if (!items.length) { | |||
return; | |||
} | |||
selectedIndex = (index + items.length) % items.length; | |||
items.forEach((item, i) => { | |||
item.classList.toggle( | |||
'selected', | |||
i === selectedIndex | |||
); | |||
}); | |||
} | |||
function insertResult(value) { | |||
if (!codeMirror || !activeCompletion) { | |||
return; | |||
} | |||
const format = | |||
activeCompletion.type === 'emoji' | |||
? config.emojiInsertFormat | |||
: config.usernameInsertFormat; | |||
const insertion = format.replaceAll('$1', value); | |||
codeMirror.replaceRange( | |||
insertion, | |||
activeCompletion.start, | |||
activeCompletion.end, | |||
'+autocomplete' | |||
); | |||
codeMirror.setCursor({ | |||
line: activeCompletion.start.line, | |||
ch: activeCompletion.start.ch + insertion.length | |||
}); | |||
codeMirror.focus(); | |||
hideMenu(); | |||
} | |||
function renderResults(values) { | |||
menu.replaceChildren(); | |||
results = values; | |||
selectedIndex = 0; | |||
if (!values.length) { | |||
const empty = document.createElement('div'); | |||
empty.className = 'username-autocomplete-empty'; | |||
empty.textContent = | |||
activeCompletion.type === 'emoji' | |||
? 'No matching emojis' | |||
: 'No matching users'; | |||
menu.appendChild(empty); | |||
} else { | |||
values.forEach((value, index) => { | |||
const item = document.createElement('div'); | |||
item.className = 'username-autocomplete-result'; | |||
if (index === 0) { | |||
item.classList.add('selected'); | |||
} | |||
if (activeCompletion.type === 'emoji') { | |||
const img = document.createElement('img'); | |||
img.src = | |||
mw.config.get('wgScriptPath') + | |||
'/index.php?title=Special:Redirect/file/' + | |||
encodeURIComponent( 'Nuuru ' + value.toLowerCase() + '.png') | |||
img.alt = value; | |||
item.append(img, document.createTextNode(' ' + value)); | |||
} else { | |||
item.textContent = value; | |||
} | |||
item.addEventListener( | |||
'mouseenter', | |||
() => setSelectedIndex(index) | |||
); | |||
item.addEventListener( | |||
'mousedown', | |||
event => { | |||
event.preventDefault(); | |||
insertResult(value); | |||
} | |||
); | |||
menu.appendChild(item); | |||
}); | |||
} | |||
menu.hidden = false; | |||
positionMenu(); | |||
} | |||
function searchUsers(cm, completion) { | |||
getApi() | |||
.then(function (api) { | |||
return api.get({ | |||
action: 'query', | |||
list: 'allusers', | |||
auprefix: completion.query, | |||
aulimit: config.maximumResults, | |||
formatversion: 2 | |||
}); | |||
}) | |||
.then(function (data) { | |||
const current = getCompletion(cm); | |||
if (!completionsMatch(current, completion)) { | |||
return; | |||
} | |||
codeMirror = cm; | |||
activeCompletion = current; | |||
renderResults( | |||
(data.query.allusers || []).map( | |||
user => user.name | |||
) | |||
); | |||
}) | |||
.catch(function (error) { | |||
console.error( | |||
'Username autocomplete API error:', | |||
error | |||
); | |||
hideMenu(); | |||
}); | |||
} | |||
function searchEmojis(cm, completion) { | |||
const current = getCompletion(cm); | |||
if (!completionsMatch(current, completion)) { | |||
return; | |||
} | |||
codeMirror = cm; | |||
activeCompletion = current; | |||
const query = completion.query.toLowerCase(); | |||
renderResults( | |||
emojis | |||
.filter(emoji => | |||
emoji.startsWith(query) | |||
) | |||
.slice(0, config.maximumResults) | |||
); | |||
} | |||
function processEditor(cm) { | |||
const completion = getCompletion(cm); | |||
if ( | |||
!completion || | |||
completion.query.length < | |||
config.minimumCharacters | |||
) { | |||
clearTimeout(debounceTimer); | |||
hideMenu(); | |||
return; | |||
} | |||
codeMirror = cm; | |||
activeCompletion = completion; | |||
clearTimeout(debounceTimer); | |||
if (completion.type === 'emoji') { | |||
searchEmojis(cm, completion); | |||
return; | |||
} | |||
debounceTimer = setTimeout( | |||
() => searchUsers(cm, completion), | |||
config.searchDelay | |||
); | |||
} | |||
document.addEventListener( | |||
'keyup', | |||
event => { | |||
const cm = getCodeMirrorFromEvent(event); | |||
if ( | |||
!cm || | |||
[ | |||
'ArrowUp', | |||
'ArrowDown', | |||
'Enter', | |||
'Tab', | |||
'Escape' | |||
].includes(event.key) | |||
) { | |||
return; | |||
} | |||
processEditor(cm); | |||
}, | |||
true | |||
); | |||
document.addEventListener( | |||
'keydown', | |||
event => { | |||
if ( | |||
menu.hidden || | |||
getCodeMirrorFromEvent(event) !== codeMirror | |||
) { | |||
return; | |||
} | |||
switch (event.key) { | |||
case 'ArrowDown': | |||
event.preventDefault(); | |||
event.stopPropagation(); | |||
setSelectedIndex(selectedIndex + 1); | |||
break; | |||
case 'ArrowUp': | |||
event.preventDefault(); | |||
event.stopPropagation(); | |||
setSelectedIndex(selectedIndex - 1); | |||
break; | |||
case 'Enter': | |||
case 'Tab': | |||
if (results.length) { | |||
event.preventDefault(); | |||
event.stopPropagation(); | |||
insertResult( | |||
results[selectedIndex] | |||
); | |||
} | |||
break; | |||
case 'Escape': | |||
event.preventDefault(); | |||
event.stopPropagation(); | |||
hideMenu(); | |||
break; | |||
} | |||
}, | |||
true | |||
); | |||
document.addEventListener('mousedown', event => { | |||
if ( | |||
!menu.contains(event.target) && | |||
!( | |||
event.target instanceof Element && | |||
event.target.closest('.CodeMirror') | |||
) | |||
) { | |||
hideMenu(); | |||
} | |||
}); | |||
window.addEventListener('resize', () => { | |||
if (!menu.hidden) { | |||
positionMenu(); | |||
} | |||
}); | |||
}()); | |||
Revision as of 19:00, 8 August 2026
(function () {
'use strict';
function isOnFilePage() {
const path = location.pathname;
const params = new URLSearchParams(location.search);
return (
/^\/File:/.test(path) ||
/^\/Special:Moderation/i.test(path) ||
/^\/Special:Moderation/i.test(path) ||
(
params.get("title") === "Special:Moderation" &&
params.get("modaction") === "show"
)
);
}
function extractImageData(img, callback) {
const tmpImg = new Image();
tmpImg.crossOrigin = 'anonymous';
tmpImg.onload = function () {
const canvas = document.createElement('canvas');
canvas.width = tmpImg.width;
canvas.height = tmpImg.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(tmpImg, 0, 0);
callback(ctx, canvas);
};
tmpImg.src = img.dataset.originalSrc;
}
function revealLSB(imageData, bits) {
const data = imageData.data;
const maskShift = 8 - bits;
for (let i = 0; i < data.length; i += 4) {
data[i] = (data[i] << maskShift) & 0xff;
data[i + 1] = (data[i + 1] << maskShift) & 0xff;
data[i + 2] = (data[i + 2] << maskShift) & 0xff;
}
}
function decodeLSB(imageData, maxChars = 5000) {
const pixels = imageData.data;
const bits = [];
for (let i = 0; i < pixels.length; i += 4) {
bits.push(pixels[i] & 1, pixels[i + 1] & 1, pixels[i + 2] & 1);
}
let output = '';
for (let i = 0; i + 7 < bits.length && output.length < maxChars; i += 8) {
let c = 0;
for (let j = 0; j < 8; j++) c = (c << 1) | bits[i + j];
if (c === 0) break;
output += String.fromCharCode(c);
}
return output;
}
function addStegUIForImg(img) {
if (!img || img.dataset.stegscanInit) return;
img.dataset.stegscanInit = '1';
img.dataset.originalSrc = img.dataset.originalSrc || img.src;
const container = document.createElement('div');
Object.assign(container.style, {
margin: '6px 0 12px',
display: 'block',
maxWidth: img.width ? img.width + 'px' : '422px'
});
const label = document.createElement('div');
label.textContent = 'Steganography Scan:';
Object.assign(label.style, {
fontSize: '12px',
fontWeight: '600',
marginBottom: '4px'
});
const slider = document.createElement('input');
Object.assign(slider, { type: 'range', min: 1, max: 8, value: 1 });
slider.style.width = '99%';
const buttonWrapper = document.createElement('div');
Object.assign(buttonWrapper.style, {
display: 'flex',
justifyContent: 'center',
marginTop: '6px'
});
const decodeBtn = document.createElement('button');
decodeBtn.textContent = 'Decode';
Object.assign(decodeBtn.style, {
cursor: 'pointer',
padding: '4px 12px',
});
buttonWrapper.appendChild(decodeBtn);
container.appendChild(label);
container.appendChild(slider);
container.appendChild(buttonWrapper);
const parent = img.parentNode;
(parent.tagName.toLowerCase() === 'a' ? parent : img)
.insertAdjacentElement('afterend', container);
let lastBits = 1;
slider.addEventListener('input', () => {
const bits = Number(slider.value);
if (bits === lastBits) return;
lastBits = bits;
if (bits <= 1) {
img.src = img.dataset.originalSrc;
return;
}
extractImageData(img, (ctx, canvas) => {
const data = ctx.getImageData(0, 0, canvas.width, canvas.height);
revealLSB(data, bits - 1);
img.removeAttribute('srcset');
ctx.putImageData(data, 0, 0);
img.src = canvas.toDataURL();
});
});
decodeBtn.addEventListener('click', () => {
decodeBtn.disabled = true;
decodeBtn.textContent = 'Decoding...';
extractImageData(img, (ctx, canvas) => {
const data = ctx.getImageData(0, 0, canvas.width, canvas.height);
const message = decodeLSB(data);
decodeBtn.textContent = 'Decode';
decodeBtn.disabled = false;
if (message) {
navigator.clipboard.writeText(message)
.then(() => alert('Decoded message copied to clipboard'))
.catch(() => prompt('Decoded message:', message));
} else {
alert('No message detected.');
}
});
});
}
function scanAllImages() {
if (!isOnFilePage()) return; // Only run on File pages or media viewer
const content = document.querySelector('#content');
if (!content) return;
content.querySelectorAll('img').forEach(img => {
// Skip file history thumbnails
if (img.closest('.filehistory')) return;
addStegUIForImg(img);
});
}
scanAllImages();
const observer = new MutationObserver(() => {
if (isOnFilePage()) scanAllImages();
else observer.disconnect();
});
observer.observe(document.body, { childList: true, subtree: true });
})();
(function () {
'use strict';
if (!['edit', 'submit'].includes(mw.config.get('wgAction'))) {
return;
}
const config = {
minimumCharacters: 2,
maximumResults: 5,
searchDelay: 200,
usernameInsertFormat: '@[[User:$1]] ',
emojiInsertFormat: '{{e|$1}} '
};
const emojis = [
'abdul', 'ack', 'alien', 'angel', 'brainlet', 'caca', 'calm',
'cat', 'chang', 'cheeky', 'cheers', 'clittycel', 'clown',
'coal', 'cold', 'concerned', 'cool', 'coomer', 'coping',
'disgusted', 'diva', 'dog', 'dreamy', 'esl', 'euro', 'fact',
'false', 'feelsgoodman', 'flushed', 'fruity', 'fuming', 'gem',
'giga', 'glowie', 'heartbroken', 'hearts', 'holdit', 'horny',
'hot', 'hunk', 'imp', 'informative', 'irritated', 'itsover',
'jamming', 'jarty', 'jsid', 'kaching', 'kek', 'keyed', 'kumar',
'locked', 'looking', 'marge', 'meds', 'mexi', 'mutt',
'nailbiter', 'neutral', 'nophono', 'omgsisa', 'pleading',
'poon', 'projecting', 'rage', 'reddit', 'robot', 'sad',
'schizo', 'serious', 'shush', 'dough', 'sick', 'skull',
'sleeping', 'slf', 'smart', 'smoking', 'smug', 'smugjak',
'snca', 'sob', 'soyboy', 'soytan', 'spade', 'steiner',
'surprised', 'suspicious', 'tendie', 'thinking', 'though',
'thumbsdown', 'thumbsup', 'tired', 'tism', 'true', 'tsmt',
'turk', 'tyrone', 'unc', 'uncomfortable', 'wholesome',
'xitter', 'yum', 'zany', 'dodis', 'arrow', 'ijgs', 'genius'
];
let codeMirror;
let activeCompletion;
let results = [];
let selectedIndex = 0;
let debounceTimer;
let apiPromise = null;
const menu = document.createElement('div');
menu.className = 'username-autocomplete';
menu.hidden = true;
document.body.appendChild(menu);
function getApi() {
if (!apiPromise) {
apiPromise = mw.loader
.using('mediawiki.api')
.then(function () {
return new mw.Api();
});
}
return apiPromise;
}
function getCodeMirrorFromEvent(event) {
const wrapper = event.target instanceof Element && event.target.closest('.CodeMirror');
return wrapper?.CodeMirror || null;
}
function getCompletion(cm) {
const cursor = cm.getCursor();
const before = cm.getLine(cursor.line).slice(0, cursor.ch);
let match = before.match(
/(?:^|[\s([{"'])@([^@\n\r\t<>{}\[\]|#/:;,!?]*)$/
);
let type = 'username';
if (!match) {
match = before.match(
/(?:^|[\s([{"']):([a-zA-Z0-9_-]*)$/
);
type = 'emoji';
}
if (!match) {
return null;
}
const query = match[1];
return {
type,
query,
start: {
line: cursor.line,
ch: cursor.ch - query.length - 1
},
end: cursor
};
}
function completionsMatch(a, b) {
return (
a &&
b &&
a.type === b.type &&
a.query === b.query &&
a.start.line === b.start.line &&
a.start.ch === b.start.ch &&
a.end.line === b.end.line &&
a.end.ch === b.end.ch
);
}
function hideMenu() {
menu.hidden = true;
menu.replaceChildren();
activeCompletion = null;
results = [];
selectedIndex = 0;
}
function positionMenu() {
if (!codeMirror) {
return;
}
const pos = codeMirror.cursorCoords(null, 'page');
menu.style.left = pos.left + 'px';
menu.style.top = pos.bottom + 'px';
}
function setSelectedIndex(index) {
const items = menu.querySelectorAll(
'.username-autocomplete-result'
);
if (!items.length) {
return;
}
selectedIndex = (index + items.length) % items.length;
items.forEach((item, i) => {
item.classList.toggle(
'selected',
i === selectedIndex
);
});
}
function insertResult(value) {
if (!codeMirror || !activeCompletion) {
return;
}
const format =
activeCompletion.type === 'emoji'
? config.emojiInsertFormat
: config.usernameInsertFormat;
const insertion = format.replaceAll('$1', value);
codeMirror.replaceRange(
insertion,
activeCompletion.start,
activeCompletion.end,
'+autocomplete'
);
codeMirror.setCursor({
line: activeCompletion.start.line,
ch: activeCompletion.start.ch + insertion.length
});
codeMirror.focus();
hideMenu();
}
function renderResults(values) {
menu.replaceChildren();
results = values;
selectedIndex = 0;
if (!values.length) {
const empty = document.createElement('div');
empty.className = 'username-autocomplete-empty';
empty.textContent =
activeCompletion.type === 'emoji'
? 'No matching emojis'
: 'No matching users';
menu.appendChild(empty);
} else {
values.forEach((value, index) => {
const item = document.createElement('div');
item.className = 'username-autocomplete-result';
if (index === 0) {
item.classList.add('selected');
}
if (activeCompletion.type === 'emoji') {
const img = document.createElement('img');
img.src =
mw.config.get('wgScriptPath') +
'/index.php?title=Special:Redirect/file/' +
encodeURIComponent( 'Nuuru ' + value.toLowerCase() + '.png')
img.alt = value;
item.append(img, document.createTextNode(' ' + value));
} else {
item.textContent = value;
}
item.addEventListener(
'mouseenter',
() => setSelectedIndex(index)
);
item.addEventListener(
'mousedown',
event => {
event.preventDefault();
insertResult(value);
}
);
menu.appendChild(item);
});
}
menu.hidden = false;
positionMenu();
}
function searchUsers(cm, completion) {
getApi()
.then(function (api) {
return api.get({
action: 'query',
list: 'allusers',
auprefix: completion.query,
aulimit: config.maximumResults,
formatversion: 2
});
})
.then(function (data) {
const current = getCompletion(cm);
if (!completionsMatch(current, completion)) {
return;
}
codeMirror = cm;
activeCompletion = current;
renderResults(
(data.query.allusers || []).map(
user => user.name
)
);
})
.catch(function (error) {
console.error(
'Username autocomplete API error:',
error
);
hideMenu();
});
}
function searchEmojis(cm, completion) {
const current = getCompletion(cm);
if (!completionsMatch(current, completion)) {
return;
}
codeMirror = cm;
activeCompletion = current;
const query = completion.query.toLowerCase();
renderResults(
emojis
.filter(emoji =>
emoji.startsWith(query)
)
.slice(0, config.maximumResults)
);
}
function processEditor(cm) {
const completion = getCompletion(cm);
if (
!completion ||
completion.query.length <
config.minimumCharacters
) {
clearTimeout(debounceTimer);
hideMenu();
return;
}
codeMirror = cm;
activeCompletion = completion;
clearTimeout(debounceTimer);
if (completion.type === 'emoji') {
searchEmojis(cm, completion);
return;
}
debounceTimer = setTimeout(
() => searchUsers(cm, completion),
config.searchDelay
);
}
document.addEventListener(
'keyup',
event => {
const cm = getCodeMirrorFromEvent(event);
if (
!cm ||
[
'ArrowUp',
'ArrowDown',
'Enter',
'Tab',
'Escape'
].includes(event.key)
) {
return;
}
processEditor(cm);
},
true
);
document.addEventListener(
'keydown',
event => {
if (
menu.hidden ||
getCodeMirrorFromEvent(event) !== codeMirror
) {
return;
}
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
event.stopPropagation();
setSelectedIndex(selectedIndex + 1);
break;
case 'ArrowUp':
event.preventDefault();
event.stopPropagation();
setSelectedIndex(selectedIndex - 1);
break;
case 'Enter':
case 'Tab':
if (results.length) {
event.preventDefault();
event.stopPropagation();
insertResult(
results[selectedIndex]
);
}
break;
case 'Escape':
event.preventDefault();
event.stopPropagation();
hideMenu();
break;
}
},
true
);
document.addEventListener('mousedown', event => {
if (
!menu.contains(event.target) &&
!(
event.target instanceof Element &&
event.target.closest('.CodeMirror')
)
) {
hideMenu();
}
});
window.addEventListener('resize', () => {
if (!menu.hidden) {
positionMenu();
}
});
}());