Moved common JS and CSS to separate file.

This commit is contained in:
flash 2025-02-02 21:27:31 +00:00
parent 584546a8f7
commit 2d6f9d0f1b
36 changed files with 204 additions and 564 deletions

View file

@ -1 +1 @@
20250202.2
20250202.3

View file

@ -0,0 +1,22 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
position: relative;
}
html, body {
width: 100%;
height: 100%;
}
[hidden],
.hidden {
display: none !important;
visibility: hidden !important;
}
:root {
--font-regular: Verdana, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif;
--font-monospace: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
}

27
assets/common.js/array.js Normal file
View file

@ -0,0 +1,27 @@
const $arrayRemoveAt = function(array, index) {
array.splice(index, 1);
};
const $arrayRemoveValue = function(array, item) {
let index;
while(array.length > 0 && (index = array.indexOf(item)) >= 0)
$arrayRemoveAt(array, index);
};
const $arrayRemoveAny = function(array, predicate) {
let index;
while(array.length > 0 && (index = array.findIndex(predicate)) >= 0)
$arrayRemoveAt(array, index);
};
const $arrayShuffle = function(array) {
if(array.length < 2)
return;
for(let i = array.length - 1; i > 0; --i) {
const j = Math.floor(Math.random() * (i + 1));
const tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
};

View file

@ -1,10 +1,10 @@
#include utility.js
#include html.js
const MszCSRF = (() => {
const $csrf = (() => {
let elem;
const getElement = () => {
if(elem === undefined)
elem = $q('meta[name="csrf-token"]');
elem = $query('meta[name="csrf-token"]');
return elem;
};

View file

@ -1,32 +1,20 @@
const $i = document.getElementById.bind(document);
const $c = document.getElementsByClassName.bind(document);
const $q = document.querySelector.bind(document);
const $qa = document.querySelectorAll.bind(document);
const $t = document.createTextNode.bind(document);
const $id = document.getElementById.bind(document);
const $query = document.querySelector.bind(document);
const $queryAll = document.querySelectorAll.bind(document);
const $text = document.createTextNode.bind(document);
const $r = function(element) {
if(element && element.parentNode)
element.parentNode.removeChild(element);
};
const $ri = function(name) {
$r($i(name));
};
const $rq = function(query) {
$r($q(query));
};
const $ib = function(ref, elem) {
const $insertBefore = function(ref, elem) {
ref.parentNode.insertBefore(elem, ref);
};
const $rc = function(element) {
while(element.lastChild)
element.removeChild(element.lastChild);
const $removeChildren = function(element) {
while(element.firstChild)
element.firstChild.remove();
};
const $e = function(info, attrs, child, created) {
const $jsx = (type, props, ...children) => $create({ tag: type, attrs: props, child: children });
const $create = function(info, attrs, child, created) {
info = info || {};
if(typeof info === 'string') {
@ -117,15 +105,15 @@ const $e = function(info, attrs, child, created) {
if(childElem instanceof Element)
elem.appendChild(childElem);
else
elem.appendChild($e(child));
elem.appendChild($create(child));
} else if('getElement' in child) {
const childElem = child.getElement();
if(childElem instanceof Element)
elem.appendChild(childElem);
else
elem.appendChild($e(child));
elem.appendChild($create(child));
} else {
elem.appendChild($e(child));
elem.appendChild($create(child));
}
break;
@ -141,30 +129,3 @@ const $e = function(info, attrs, child, created) {
return elem;
};
const $er = (type, props, ...children) => $e({ tag: type, attrs: props, child: children });
const $ar = function(array, index) {
array.splice(index, 1);
};
const $ari = function(array, item) {
let index;
while(array.length > 0 && (index = array.indexOf(item)) >= 0)
$ar(array, index);
};
const $arf = function(array, predicate) {
let index;
while(array.length > 0 && (index = array.findIndex(predicate)) >= 0)
$ar(array, index);
};
const $as = function(array) {
if(array.length < 2)
return;
for(let i = array.length - 1; i > 0; --i) {
let j = Math.floor(Math.random() * (i + 1)),
tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
};

5
assets/common.js/main.js Normal file
View file

@ -0,0 +1,5 @@
#include array.js
#include csrf.js
#include html.js
#include uniqstr.js
#include xhr.js

View file

@ -1,4 +1,4 @@
const MszRandomInt = function(min, max) {
const $rngi = function(min, max) {
let ret = 0;
const range = max - min;
@ -21,18 +21,18 @@ const MszRandomInt = function(min, max) {
ret &= mask;
if(ret >= range)
return MszRandomInt(min, max);
return $rngi(min, max);
return min + ret;
};
const MszUniqueStr = (function() {
const $rngs = (function() {
const chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789';
return function(length) {
let str = '';
for(let i = 0; i < length; ++i)
str += chars[MszRandomInt(0, chars.length)];
str += chars[$rngi(0, chars.length)];
return str;
};
})();

View file

@ -1,6 +1,6 @@
#include csrf.js
const $x = (function() {
const $xhr = (function() {
const send = function(method, url, options, body) {
if(options === undefined)
options = {};
@ -18,7 +18,7 @@ const $x = (function() {
requestHeaders.set(name.toLowerCase(), options.headers[name]);
if(options.csrf)
requestHeaders.set('x-csrf-token', MszCSRF.token);
requestHeaders.set('x-csrf-token', $csrf.token);
if(typeof options.download === 'function') {
xhr.onloadstart = ev => options.download(ev);
@ -81,7 +81,7 @@ const $x = (function() {
})(xhr.getAllResponseHeaders());
if(options.csrf && headers.has('x-csrf-token'))
MszCSRF.token = headers.get('x-csrf-token');
$csrf.token = headers.get('x-csrf-token');
resolve({
get ev() { return ev; },

View file

@ -1,27 +1,6 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
position: relative;
}
html,
body {
width: 100%;
height: 100%;
}
[hidden],
.hidden {
display: none !important;
visibility: hidden !important;
}
:root {
--font-size: 12px;
--line-height: 20px;
--font-regular: Verdana, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif;
--font-monospace: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
--site-max-width: 1200px;
--site-mobile-width: 800px;

View file

@ -1,4 +1,3 @@
#include utility.js
#include watcher.js
const MszAudioEmbedPlayerEvents = function() {
@ -10,7 +9,7 @@ const MszAudioEmbedPlayerEvents = function() {
};
const MszAudioEmbed = function(player) {
const elem = $e({
const elem = $create({
attrs: {
classList: ['aembed', 'aembed-' + player.getType()],
},
@ -25,14 +24,14 @@ const MszAudioEmbed = function(player) {
target.appendChild(elem);
},
insertBefore: function(ref) {
$ib(ref, elem);
$insertBefore(ref, elem);
},
nuke: function() {
$r(elem);
elem.remove();
},
replaceElement(target) {
$ib(target, elem);
$r(target);
$insertBefore(target, elem);
target.remove();
},
getPlayer: function() {
return player;
@ -59,7 +58,7 @@ const MszAudioEmbedPlayer = function(metadata, options) {
const watchers = new MszWatchers;
watchers.define(MszAudioEmbedPlayerEvents());
const player = $e({
const player = $create({
tag: 'audio',
attrs: playerAttrs,
});
@ -72,14 +71,14 @@ const MszAudioEmbedPlayer = function(metadata, options) {
target.appendChild(player);
},
insertBefore: function(ref) {
$ib(ref, player);
$insertBefore(ref, player);
},
nuke: function() {
$r(player);
player.remove();
},
replaceElement(target) {
$ib(target, player);
$r(target);
$insertBefore(target, player);
target.remove();
},
getType: function() { return 'external'; },
};
@ -236,7 +235,7 @@ const MszAudioEmbedPlaceholder = function(metadata, options) {
if(typeof metadata.color !== 'undefined')
style.push('--aembedph-colour: ' + metadata.color);
const coverBackground = $e({
const coverBackground = $create({
attrs: {
className: 'aembedph-bg',
},
@ -252,7 +251,7 @@ const MszAudioEmbedPlaceholder = function(metadata, options) {
},
});
const coverPreview = $e({
const coverPreview = $create({
attrs: {
className: 'aembedph-info-cover',
},
@ -270,7 +269,7 @@ const MszAudioEmbedPlaceholder = function(metadata, options) {
const pub = {};
const elem = $e({
const elem = $create({
attrs: {
className: ('aembedph aembedph-' + (options.type || 'external')),
style: style.join(';'),
@ -355,13 +354,13 @@ const MszAudioEmbedPlaceholder = function(metadata, options) {
pub.getElement = function() { return elem; };
pub.appendTo = function(target) { target.appendChild(elem); };
pub.insertBefore = function(ref) { $ib(ref, elem); };
pub.insertBefore = function(ref) { $insertBefore(ref, elem); };
pub.nuke = function() {
$r(elem);
elem.remove();
};
pub.replaceElement = function(target) {
$ib(target, elem);
$r(target);
$insertBefore(target, elem);
target.remove();
};
return pub;

View file

@ -1,4 +1,3 @@
#include utility.js
#include embed/audio.js
#include embed/image.js
#include embed/video.js
@ -30,8 +29,8 @@ const MszEmbed = (function() {
continue;
}
$rc(target);
target.appendChild($e({
$removeChildren(target);
target.appendChild($create({
tag: 'i',
attrs: {
className: 'fas fa-2x fa-spinner fa-pulse',
@ -52,7 +51,7 @@ const MszEmbed = (function() {
const replaceWithUrl = function(targets, url) {
for(const target of targets) {
let body = $e({
let body = $create({
tag: 'a',
attrs: {
className: 'link',
@ -62,8 +61,8 @@ const MszEmbed = (function() {
},
child: url
});
$ib(target, body);
$r(target);
$insertBefore(target, body);
target.remove();
}
};

View file

@ -1,9 +1,7 @@
#include utility.js
const MszImageEmbed = function(metadata, options, target) {
options = options || {};
const image = $e({
const image = $create({
tag: 'img',
attrs: {
alt: target.dataset.mszEmbedAlt || '',
@ -19,14 +17,14 @@ const MszImageEmbed = function(metadata, options, target) {
target.appendChild(image);
},
insertBefore: function(ref) {
$ib(ref, image);
$insertBefore(ref, image);
},
nuke: function() {
$r(image);
image.remove();
},
replaceElement(target) {
$ib(target, image);
$r(target);
$insertBefore(target, image);
target.remove();
},
getType: function() { return 'external'; },
};

View file

@ -1,5 +1,3 @@
#include utility.js
#include uniqstr.js
#include watcher.js
const MszVideoEmbedPlayerEvents = function() {
@ -49,7 +47,7 @@ const MszVideoEmbed = function(playerOrFrame) {
const frame = playerOrFrame;
const player = 'getPlayer' in frame ? frame.getPlayer() : frame;
const elem = $e({
const elem = $create({
attrs: {
classList: ['embed', 'embed-' + player.getType()],
},
@ -64,14 +62,14 @@ const MszVideoEmbed = function(playerOrFrame) {
target.appendChild(elem);
},
insertBefore: function(ref) {
$ib(ref, elem);
$insertBefore(ref, elem);
},
nuke: function() {
$r(elem);
elem.remove();
},
replaceElement(target) {
$ib(target, elem);
$r(target);
$insertBefore(target, elem);
target.remove();
},
getFrame: function() {
return frame;
@ -93,7 +91,7 @@ const MszVideoEmbedFrame = function(player, options) {
icoVolQuiet = 'fa-volume-down',
icoVolLoud = 'fa-volume-up';
const btnPlayPause = $e({
const btnPlayPause = $create({
attrs: {},
child: {
tag: 'i',
@ -103,7 +101,7 @@ const MszVideoEmbedFrame = function(player, options) {
}
});
const btnStop = $e({
const btnStop = $create({
attrs: {},
child: {
tag: 'i',
@ -113,20 +111,20 @@ const MszVideoEmbedFrame = function(player, options) {
},
});
const numCurrentTime = $e({
const numCurrentTime = $create({
attrs: {},
});
const sldProgress = $e({
const sldProgress = $create({
attrs: {},
child: [],
});
const numDurationRemaining = $e({
const numDurationRemaining = $create({
attrs: {},
});
const btnVolMute = $e({
const btnVolMute = $create({
attrs: {},
child: {
tag: 'i',
@ -140,7 +138,7 @@ const MszVideoEmbedFrame = function(player, options) {
},
});
const elem = $e({
const elem = $create({
attrs: {
className: 'embedvf',
style: {
@ -185,14 +183,14 @@ const MszVideoEmbedFrame = function(player, options) {
target.appendChild(elem);
},
insertBefore: function(ref) {
$ib(ref, elem);
$insertBefore(ref, elem);
},
nuke: function() {
$r(elem);
elem.remove();
},
replaceElement(target) {
$ib(target, elem);
$r(target);
$insertBefore(target, elem);
target.remove();
},
getPlayer: function() {
return player;
@ -232,7 +230,7 @@ const MszVideoEmbedPlayer = function(metadata, options) {
const watchers = new MszWatchers;
watchers.define(MszVideoEmbedPlayerEvents());
const player = $e({
const player = $create({
tag: 'video',
attrs: videoAttrs,
});
@ -251,14 +249,14 @@ const MszVideoEmbedPlayer = function(metadata, options) {
target.appendChild(player);
},
insertBefore: function(ref) {
$ib(ref, player);
$insertBefore(ref, player);
},
nuke: function() {
$r(player);
player.remove();
},
replaceElement(target) {
$ib(target, player);
$r(target);
$insertBefore(target, player);
target.remove();
},
getType: function() { return 'external'; },
getWidth: function() { return width; },
@ -350,7 +348,7 @@ const MszVideoEmbedYouTube = function(metadata, options) {
options = options || {};
const ytOrigin = 'https://www.youtube.com',
playerId = 'yt-' + MszUniqueStr(8),
playerId = 'yt-' + $rngs(8),
shouldAutoplay = options.autoplay === undefined || options.autoplay;
let embedUrl = 'https://www.youtube.com/embed/' + metadata.youtube_video_id + '?enablejsapi=1';
@ -378,7 +376,7 @@ const MszVideoEmbedYouTube = function(metadata, options) {
const watchers = new MszWatchers;
watchers.define(MszVideoEmbedPlayerEvents());
const player = $e({
const player = $create({
tag: 'iframe',
attrs: {
frameborder: 0,
@ -396,14 +394,14 @@ const MszVideoEmbedYouTube = function(metadata, options) {
target.appendChild(player);
},
insertBefore: function(ref) {
$ib(ref, player);
$insertBefore(ref, player);
},
nuke: function() {
$r(player);
player.remove();
},
replaceElement(target) {
$ib(target, player);
$r(target);
$insertBefore(target, player);
target.remove();
},
getType: function() { return 'youtube'; },
getWidth: function() { return 560; },
@ -562,7 +560,7 @@ const MszVideoEmbedNicoNico = function(metadata, options) {
options = options || {};
const nndOrigin = 'https://embed.nicovideo.jp',
playerId = 'nnd-' + MszUniqueStr(8),
playerId = 'nnd-' + $rngs(8),
shouldAutoplay = options.autoplay === undefined || options.autoplay;
let embedUrl = 'https://embed.nicovideo.jp/watch/' + metadata.nicovideo_video_id + '?jsapi=1&playerId=' + playerId;
@ -579,7 +577,7 @@ const MszVideoEmbedNicoNico = function(metadata, options) {
const watchers = new MszWatchers;
watchers.define(MszVideoEmbedPlayerEvents());
const player = $e({
const player = $create({
tag: 'iframe',
attrs: {
frameborder: 0,
@ -597,14 +595,14 @@ const MszVideoEmbedNicoNico = function(metadata, options) {
target.appendChild(player);
},
insertBefore: function(ref) {
$ib(ref, player);
$insertBefore(ref, player);
},
nuke: function() {
$r(player);
player.remove();
},
replaceElement(target) {
$ib(target, player);
$r(target);
$insertBefore(target, player);
target.remove();
},
getType: function() { return 'nicovideo'; },
getWidth: function() { return 640; },
@ -792,7 +790,7 @@ const MszVideoEmbedPlaceholder = function(metadata, options) {
const pub = {};
const elem = $e({
const elem = $create({
attrs: {
className: ('embedph embedph-' + (options.type || 'external')),
style: style.join(';'),
@ -894,13 +892,13 @@ const MszVideoEmbedPlaceholder = function(metadata, options) {
pub.getElement = function() { return elem; };
pub.appendTo = function(target) { target.appendChild(elem); };
pub.insertBefore = function(ref) { $ib(ref, elem); };
pub.insertBefore = function(ref) { $insertBefore(ref, elem); };
pub.nuke = function() {
$r(elem);
elem.remove();
};
pub.replaceElement = function(target) {
$ib(target, elem);
$r(target);
$insertBefore(target, elem);
target.remove();
};
return pub;

View file

@ -1,5 +1,3 @@
#include utility.js
const MszChristmas2019EventInfo = function() {
return {
isActive: () => {
@ -16,8 +14,8 @@ const MszChristmas2019EventInfo = function() {
const MszChristmas2019Event = function() {
const propName = 'msz-christmas-' + (new Date).getFullYear().toString();
const headerBg = $q('.header__background');
const menuBgs = Array.from($qa('.header__desktop__submenu__background'));
const headerBg = $query('.header__background');
const menuBgs = Array.from($queryAll('.header__desktop__submenu__background'));
if(!localStorage.getItem(propName))
localStorage.setItem(propName, '0');

View file

@ -1,5 +1,3 @@
#include xhr.js
const MszEEPROM = function(appId, endPoint) {
if(typeof appId !== 'string')
throw 'appId must be a string';
@ -44,7 +42,7 @@ const MszEEPROM = function(appId, endPoint) {
formData.append('src', appId);
formData.append('file', fileInput);
const { status, body } = await $x.post(`${endPoint}/uploads`, {
const { status, body } = await $xhr.post(`${endPoint}/uploads`, {
type: 'json',
authed: true,
upload: reportProgress,
@ -79,7 +77,7 @@ const MszEEPROM = function(appId, endPoint) {
if(typeof fileInfo.urlf !== 'string')
throw 'fileInfo.urlf must be a string';
const { status, body } = await $x.delete(fileInfo.urlf, {
const { status, body } = await $xhr.delete(fileInfo.urlf, {
type: 'json',
authed: true,
});

View file

@ -1,5 +1,3 @@
#include xhr.js
const MszUiharu = function(apiUrl) {
const maxBatchSize = 4;
const lookupOneUrl = apiUrl + '/metadata';
@ -9,7 +7,7 @@ const MszUiharu = function(apiUrl) {
if(typeof targetUrl !== 'string')
throw 'targetUrl must be a string';
return $x.post(lookupOneUrl, { type: 'json' }, targetUrl);
return $xhr.post(lookupOneUrl, { type: 'json' }, targetUrl);
},
};
};

View file

@ -1,7 +1,6 @@
#include msgbox.jsx
#include parsing.js
#include utility.js
#include xhr.js
#include ext/eeprom.js
let MszForumEditorAllowClose = false;
@ -38,12 +37,9 @@ const MszForumEditor = function(form) {
</div>
</div>;
if(eepromHistory.children.length > 0)
$ib(eepromHistory.firstChild, uploadElem);
else
eepromHistory.appendChild(uploadElem);
eepromHistory.insertAdjacentElement('afterbegin', uploadElem);
const explodeUploadElem = () => $r(uploadElem);
const explodeUploadElem = () => { uploadElem.remove(); };
const uploadTask = eepromClient.create(file);
uploadTask.onProgress(prog => {
@ -79,7 +75,7 @@ const MszForumEditor = function(form) {
};
uploadElemProgressText.appendChild(<a href="javascript:void(0)" onclick={() => insertTheLinkIntoTheBoxEx2()}>Insert</a>);
uploadElemProgressText.appendChild($t(' '));
uploadElemProgressText.appendChild($text(' '));
uploadElemProgressText.appendChild(<a href="javascript:void(0)" onclick={() => {
eepromClient.delete(fileInfo)
.then(() => explodeUploadElem())
@ -172,7 +168,7 @@ const MszForumEditor = function(form) {
});
const switchButtons = parser => {
$rc(markupActs);
$removeChildren(markupActs);
const tags = MszParsing.getTagsFor(parser);
for(const tag of tags)
@ -191,7 +187,7 @@ const MszForumEditor = function(form) {
formData.append('post[text]', text);
formData.append('post[parser]', parseInt(parser));
return (await $x.post('/forum/posting.php', { authed: true }, formData)).body;
return (await $xhr.post('/forum/posting.php', { authed: true }, formData)).body;
};
const previewBtn = <button class="input__button" type="button" value="preview">Preview</button>;
@ -233,7 +229,7 @@ const MszForumEditor = function(form) {
lastPostParser = postParser;
previewElem.innerHTML = body;
MszEmbed.handle($qa('.js-msz-embed-media'));
MszEmbed.handle($queryAll('.js-msz-embed-media'));
previewElem.removeAttribute('hidden');
textElem.setAttribute('hidden', 'hidden');
@ -276,7 +272,7 @@ const MszForumEditor = function(form) {
lastPostParser = postParser;
previewElem.innerHTML = body;
MszEmbed.handle($qa('.js-msz-embed-media'));
MszEmbed.handle($queryAll('.js-msz-embed-media'));
previewBtn.removeAttribute('disabled');
parserElem.removeAttribute('disabled');

View file

@ -1,6 +1,4 @@
#include msgbox.jsx
#include utility.js
#include xhr.js
#include embed/embed.js
#include events/christmas2019.js
#include events/events.js
@ -10,7 +8,7 @@
(async () => {
const initLoginPage = async () => {
const forms = Array.from($qa('.js-login-form'));
const forms = Array.from($queryAll('.js-login-form'));
if(forms.length < 1)
return;
@ -18,7 +16,7 @@
if(!(avatar instanceof Element) || !(userName instanceof Element))
return;
const { body } = await $x.get(`/auth/login.php?resolve=1&name=${encodeURIComponent(userName.value)}`, { type: 'json' });
const { body } = await $xhr.get(`/auth/login.php?resolve=1&name=${encodeURIComponent(userName.value)}`, { type: 'json' });
avatar.src = body.avatar;
if(body.name.length > 0)
@ -48,7 +46,7 @@
};
const initQuickSubmit = () => {
const elems = Array.from($qa('.js-quick-submit, .js-ctrl-enter-submit'));
const elems = Array.from($queryAll('.js-quick-submit, .js-ctrl-enter-submit'));
if(elems.length < 1)
return;
@ -66,7 +64,7 @@
};
const initXhrActions = () => {
const targets = Array.from($qa('a[data-url], button[data-url]'));
const targets = Array.from($queryAll('a[data-url], button[data-url]'));
for(const target of targets) {
target.onclick = async () => {
if(target.disabled)
@ -89,7 +87,7 @@
if(target.dataset.confirm && !await MszShowConfirmBox(target.dataset.confirm, 'Are you sure?'))
return;
const { status, body } = await $x.send(
const { status, body } = await $xhr.send(
target.dataset.method ?? 'GET',
url,
{
@ -125,7 +123,7 @@
};
try {
MszSakuya.trackElements($qa('time'));
MszSakuya.trackElements($queryAll('time'));
hljs.highlightAll();
MszEmbed.init(`${location.protocol}//uiharu.${location.host}`);
@ -134,7 +132,7 @@
// only used by the forum posting form
initQuickSubmit();
const forumPostingForm = $q('.js-forum-posting');
const forumPostingForm = $query('.js-forum-posting');
if(forumPostingForm !== null)
MszForumEditor(forumPostingForm);
@ -146,7 +144,7 @@
MszMessages();
MszEmbed.handle($qa('.js-msz-embed-media'));
MszEmbed.handle($queryAll('.js-msz-embed-media'));
} catch(ex) {
console.error(ex);
}

View file

@ -1,4 +1,3 @@
#include utility.js
#include watcher.js
const MsgMessagesList = function(list) {
@ -55,8 +54,8 @@ const MsgMessagesList = function(list) {
return selected;
},
removeItem: item => {
$ari(items, item);
$r(item.getElement());
$arrayRemoveValue(items, item);
item.getElement().remove();
recountSelected();
watchers.call('select', selectedCount, items.length);
},

View file

@ -1,6 +1,4 @@
#include msgbox.jsx
#include utility.js
#include xhr.js
#include messages/actbtn.js
#include messages/list.js
#include messages/recipient.js
@ -40,7 +38,7 @@ const MszMessages = () => {
formData.append('recipient', recipient);
formData.append('reply', replyTo);
const { body } = await $x.post('/messages/create', { type: 'json', csrf: true }, formData);
const { body } = await $xhr.post('/messages/create', { type: 'json', csrf: true }, formData);
if(body.error !== undefined)
throw body.error;
@ -54,7 +52,7 @@ const MszMessages = () => {
formData.append('parser', parser);
formData.append('draft', draft);
const { body } = await $x.post(`/messages/${encodeURIComponent(messageId)}`, { type: 'json', csrf: true }, formData);
const { body } = await $xhr.post(`/messages/${encodeURIComponent(messageId)}`, { type: 'json', csrf: true }, formData);
if(body.error !== undefined)
throw body.error;
@ -62,7 +60,7 @@ const MszMessages = () => {
};
const msgsMark = async (msgs, state) => {
const { body } = await $x.post('/messages/mark', { type: 'json', csrf: true }, {
const { body } = await $xhr.post('/messages/mark', { type: 'json', csrf: true }, {
type: state,
messages: msgs.map(extractMsgIds).join(','),
});
@ -73,7 +71,7 @@ const MszMessages = () => {
};
const msgsDelete = async msgs => {
const { body } = await $x.post('/messages/delete', { type: 'json', csrf: true }, {
const { body } = await $xhr.post('/messages/delete', { type: 'json', csrf: true }, {
messages: msgs.map(extractMsgIds).join(','),
});
if(body.error !== undefined)
@ -83,7 +81,7 @@ const MszMessages = () => {
};
const msgsRestore = async msgs => {
const { body } = await $x.post('/messages/restore', { type: 'json', csrf: true }, {
const { body } = await $xhr.post('/messages/restore', { type: 'json', csrf: true }, {
messages: msgs.map(extractMsgIds).join(','),
});
if(body.error !== undefined)
@ -93,7 +91,7 @@ const MszMessages = () => {
};
const msgsNuke = async msgs => {
const { body } = await $x.post('/messages/nuke', { type: 'json', csrf: true }, {
const { body } = await $xhr.post('/messages/nuke', { type: 'json', csrf: true }, {
messages: msgs.map(extractMsgIds).join(','),
});
if(body.error !== undefined)
@ -102,28 +100,28 @@ const MszMessages = () => {
return true;
};
const msgsUserBtns = Array.from($qa('.js-header-pms-button'));
const msgsUserBtns = Array.from($queryAll('.js-header-pms-button'));
if(msgsUserBtns.length > 0)
$x.get('/messages/stats', { type: 'json' }).then(result => {
$xhr.get('/messages/stats', { type: 'json' }).then(result => {
const body = result.body;
if(typeof body === 'object' && typeof body.unread === 'number')
if(body.unread > 0)
for(const msgsUserBtn of msgsUserBtns)
msgsUserBtn.append($e({ child: body.unread.toLocaleString(), attrs: { className: 'header__desktop__user__button__count' } }));
msgsUserBtn.append($create({ child: body.unread.toLocaleString(), attrs: { className: 'header__desktop__user__button__count' } }));
});
const msgsListElem = $q('.js-messages-list');
const msgsListElem = $query('.js-messages-list');
const msgsList = msgsListElem instanceof Element ? new MsgMessagesList(msgsListElem) : undefined;
const msgsListEmptyNotice = $q('.js-messages-folder-empty');
const msgsListEmptyNotice = $query('.js-messages-folder-empty');
const msgsThreadElem = $q('.js-messages-thread');
const msgsThreadElem = $query('.js-messages-thread');
const msgsThread = msgsThreadElem instanceof Element ? new MszMessagesThread(msgsThreadElem) : undefined;
const msgsRecipientElem = $q('.js-messages-recipient');
const msgsRecipientElem = $query('.js-messages-recipient');
const msgsRecipient = msgsRecipientElem instanceof Element ? new MszMessagesRecipient(msgsRecipientElem) : undefined;
const msgsReplyElem = $q('.js-messages-reply');
const msgsReplyElem = $query('.js-messages-reply');
const msgsReply = msgsReplyElem instanceof Element ? new MszMessagesReply(msgsReplyElem) : undefined;
if(msgsReply !== undefined) {
@ -165,7 +163,7 @@ const MszMessages = () => {
let actSelectAll, actMarkRead, actMoveTrash, actNuke;
const actSelectAllBtn = $q('.js-messages-actions-select-all');
const actSelectAllBtn = $query('.js-messages-actions-select-all');
if(actSelectAllBtn instanceof Element) {
actSelectAll = new MszMessagesActionButton(actSelectAllBtn);
@ -181,7 +179,7 @@ const MszMessages = () => {
}
}
const actMarkReadBtn = $q('.js-messages-actions-mark-read');
const actMarkReadBtn = $query('.js-messages-actions-mark-read');
if(actMarkReadBtn instanceof Element) {
actMarkRead = new MszMessagesActionButton(actMarkReadBtn);
@ -241,7 +239,7 @@ const MszMessages = () => {
}
}
const actMoveTrashBtn = $q('.js-messages-actions-move-trash');
const actMoveTrashBtn = $query('.js-messages-actions-move-trash');
if(actMoveTrashBtn instanceof Element) {
actMoveTrash = new MszMessagesActionButton(actMoveTrashBtn);
@ -303,7 +301,7 @@ const MszMessages = () => {
}
}
const actNukeBtn = $q('.js-messages-actions-nuke');
const actNukeBtn = $query('.js-messages-actions-nuke');
if(actNukeBtn instanceof Element) {
actNuke = new MszMessagesActionButton(actNukeBtn, true);

View file

@ -1,5 +1,3 @@
#include xhr.js
const MszMessagesRecipient = function(element) {
if(!(element instanceof Element))
throw 'element must be an instance of Element';
@ -9,7 +7,7 @@ const MszMessagesRecipient = function(element) {
let updateHandler = undefined;
const update = async () => {
const { body } = await $x.post(element.dataset.msgLookup, { type: 'json', csrf: true }, {
const { body } = await $xhr.post(element.dataset.msgLookup, { type: 'json', csrf: true }, {
name: nameInput.value,
});

View file

@ -1,4 +1,5 @@
#include parsing.js
#include utility.js
#include ext/eeprom.js
const MszMessagesReply = function(element) {
@ -46,7 +47,7 @@ const MszMessagesReply = function(element) {
});
const switchButtons = parser => {
$rc(actsElem);
$removeChildren(actsElem);
const tags = MszParsing.getTagsFor(parser);
actsElem.hidden = tags.length < 1;

View file

@ -1,5 +1,3 @@
#include utility.js
const MszShowConfirmBox = async (text, title, target) => {
let result = false;

View file

@ -1,174 +1,3 @@
const $i = document.getElementById.bind(document);
const $c = document.getElementsByClassName.bind(document);
const $q = document.querySelector.bind(document);
const $qa = document.querySelectorAll.bind(document);
const $t = document.createTextNode.bind(document);
const $r = function(element) {
if(element && element.parentNode)
element.parentNode.removeChild(element);
};
const $ri = function(name) {
$r($i(name));
};
const $rq = function(query) {
$r($q(query));
};
const $ib = function(ref, elem) {
ref.parentNode.insertBefore(elem, ref);
};
const $rc = function(element) {
while(element.lastChild)
element.removeChild(element.lastChild);
};
const $e = function(info, attrs, child, created) {
info = info || {};
if(typeof info === 'string') {
info = {tag: info};
if(attrs)
info.attrs = attrs;
if(child)
info.child = child;
if(created)
info.created = created;
}
const elem = document.createElement(info.tag || 'div');
if(info.attrs) {
const attrs = info.attrs;
for(let key in attrs) {
const attr = attrs[key];
if(attr === undefined || attr === null)
continue;
switch(typeof attr) {
case 'function':
if(key.substring(0, 2) === 'on')
key = key.substring(2).toLowerCase();
elem.addEventListener(key, attr);
break;
case 'object':
if(attr instanceof Array) {
if(key === 'class')
key = 'classList';
const prop = elem[key];
let addFunc = null;
if(prop instanceof Array)
addFunc = prop.push.bind(prop);
else if(prop instanceof DOMTokenList)
addFunc = prop.add.bind(prop);
if(addFunc !== null) {
for(let j = 0; j < attr.length; ++j)
addFunc(attr[j]);
} else {
if(key === 'classList')
key = 'class';
elem.setAttribute(key, attr.toString());
}
} else {
for(const attrKey in attr)
elem[key][attrKey] = attr[attrKey];
}
break;
case 'boolean':
if(attr)
elem.setAttribute(key, '');
break;
default:
if(key === 'className')
key = 'class';
elem.setAttribute(key, attr.toString());
break;
}
}
}
if(info.child) {
let children = info.child;
if(!Array.isArray(children))
children = [children];
for(const child of children) {
switch(typeof child) {
case 'string':
elem.appendChild(document.createTextNode(child));
break;
case 'object':
if(child instanceof Element) {
elem.appendChild(child);
} else if('element' in child) {
const childElem = child.element;
if(childElem instanceof Element)
elem.appendChild(childElem);
else
elem.appendChild($e(child));
} else if('getElement' in child) {
const childElem = child.getElement();
if(childElem instanceof Element)
elem.appendChild(childElem);
else
elem.appendChild($e(child));
} else {
elem.appendChild($e(child));
}
break;
default:
elem.appendChild(document.createTextNode(child.toString()));
break;
}
}
}
if(info.created)
info.created(elem);
return elem;
};
const $er = (type, props, ...children) => $e({ tag: type, attrs: props, child: children });
const $ar = function(array, index) {
array.splice(index, 1);
};
const $ari = function(array, item) {
let index;
while(array.length > 0 && (index = array.indexOf(item)) >= 0)
$ar(array, index);
};
const $arf = function(array, predicate) {
let index;
while(array.length > 0 && (index = array.findIndex(predicate)) >= 0)
$ar(array, index);
};
const $as = function(array) {
if(array.length < 2)
return;
for(let i = array.length - 1; i > 0; --i) {
let j = Math.floor(Math.random() * (i + 1)),
tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
};
const $insertTags = function(target, tagOpen, tagClose) {
tagOpen = tagOpen || '';
tagClose = tagClose || '';

View file

@ -13,7 +13,7 @@ const MszWatcher = function() {
};
const unwatch = handler => {
$ari(handlers, handler);
$arrayRemoveValue(handlers, handler);
};
return {

View file

@ -1,117 +0,0 @@
#include csrf.js
const $x = (function() {
const send = function(method, url, options, body) {
if(options === undefined)
options = {};
else if(typeof options !== 'object')
throw 'options must be undefined or an object';
Object.freeze(options);
const xhr = new XMLHttpRequest;
const requestHeaders = new Map;
if('headers' in options && typeof options.headers === 'object')
for(const name in options.headers)
if(options.headers.hasOwnProperty(name))
requestHeaders.set(name.toLowerCase(), options.headers[name]);
if(options.csrf)
requestHeaders.set('x-csrf-token', MszCSRF.token);
if(typeof options.download === 'function') {
xhr.onloadstart = ev => options.download(ev);
xhr.onprogress = ev => options.download(ev);
xhr.onloadend = ev => options.download(ev);
}
if(typeof options.upload === 'function') {
xhr.upload.onloadstart = ev => options.upload(ev);
xhr.upload.onprogress = ev => options.upload(ev);
xhr.upload.onloadend = ev => options.upload(ev);
}
if(options.authed)
xhr.withCredentials = true;
if(typeof options.timeout === 'number')
xhr.timeout = options.timeout;
if(typeof options.type === 'string')
xhr.responseType = options.type;
if(typeof options.abort === 'function')
options.abort(() => xhr.abort());
if(typeof options.xhr === 'function')
options.xhr(() => xhr);
if(typeof body === 'object') {
if(body instanceof URLSearchParams) {
requestHeaders.set('content-type', 'application/x-www-form-urlencoded');
} else if(body instanceof FormData) {
// content-type is implicitly set
} else if(body instanceof Blob || body instanceof ArrayBuffer || body instanceof DataView) {
if(!requestHeaders.has('content-type'))
requestHeaders.set('content-type', 'application/octet-stream');
} else if(!requestHeaders.has('content-type')) {
const bodyParts = [];
for(const name in body)
if(body.hasOwnProperty(name))
bodyParts.push(encodeURIComponent(name) + '=' + encodeURIComponent(body[name]));
body = bodyParts.join('&');
requestHeaders.set('content-type', 'application/x-www-form-urlencoded');
}
}
return new Promise((resolve, reject) => {
xhr.onload = ev => {
const headers = (headersString => {
const headers = new Map;
const raw = headersString.trim().split(/[\r\n]+/);
for(const name in raw)
if(raw.hasOwnProperty(name)) {
const parts = raw[name].split(': ');
headers.set(parts.shift(), parts.join(': '));
}
return headers;
})(xhr.getAllResponseHeaders());
if(options.csrf && headers.has('x-csrf-token'))
MszCSRF.token = headers.get('x-csrf-token');
resolve({
get ev() { return ev; },
get xhr() { return xhr; },
get status() { return xhr.status; },
get headers() { return headers; },
get body() { return xhr.response; },
get text() { return xhr.responseText; },
});
};
xhr.onerror = ev => reject({
xhr: xhr,
ev: ev,
});
xhr.open(method, url);
for(const [name, value] of requestHeaders)
xhr.setRequestHeader(name, value);
xhr.send(body);
});
};
return {
send: send,
get: (url, options, body) => send('GET', url, options, body),
post: (url, options, body) => send('POST', url, options, body),
delete: (url, options, body) => send('DELETE', url, options, body),
patch: (url, options, body) => send('PATCH', url, options, body),
put: (url, options, body) => send('PUT', url, options, body),
};
})();

View file

@ -1,25 +1,3 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
position: relative;
}
html, body {
width: 100%;
height: 100%;
}
[hidden],
.hidden {
display: none !important;
}
:root {
--font-regular: Verdana, Geneva, 'Dejavu Sans', Arial, Helvetica, sans-serif;
--font-monospace: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
}
body {
background-color: #111;
color: #fff;

View file

@ -1,5 +1,4 @@
#include loading.jsx
#include xhr.js
#include app/info.jsx
#include app/scope.jsx
#include header/header.js
@ -158,7 +157,7 @@ const MszOAuth2Authorise = async () => {
}
try {
const { body } = await $x.get(`/oauth2/resolve-authorise-app?${resolveParams}`, { authed: true, csrf: true, type: 'json' });
const { body } = await $xhr.get(`/oauth2/resolve-authorise-app?${resolveParams}`, { authed: true, csrf: true, type: 'json' });
if(!body)
throw 'authorisation resolve failed';
if(typeof body.error === 'string') {
@ -185,7 +184,7 @@ const MszOAuth2Authorise = async () => {
params.scope = scope;
try {
const { body } = await $x.post('/oauth2/authorise', { authed: true, csrf: true, type: 'json' }, params);
const { body } = await $xhr.post('/oauth2/authorise', { authed: true, csrf: true, type: 'json' }, params);
if(!body)
throw 'authorisation failed';
if(typeof body.error === 'string')

View file

@ -1,24 +0,0 @@
#include utility.js
const MszCSRF = (() => {
let elem;
const getElement = () => {
if(elem === undefined)
elem = $q('meta[name="csrf-token"]');
return elem;
};
return {
get token() {
return getElement()?.content ?? '';
},
set token(token) {
if(typeof token !== 'string')
throw 'token must be a string';
const elem = getElement();
if(elem instanceof HTMLMetaElement)
elem.content = token;
},
};
})();

View file

@ -1,4 +1,3 @@
#include utility.js
#include authorise.js
#include verify.js

View file

@ -1,5 +1,4 @@
#include loading.jsx
#include xhr.js
#include app/info.jsx
#include app/scope.jsx
#include header/header.js
@ -22,7 +21,7 @@ const MszOAuth2Verify = () => {
const verifyAuthsRequest = async approve => {
try {
const { body } = await $x.post('/oauth2/verify', { authed: true, csrf: true, type: 'json' }, {
const { body } = await $xhr.post('/oauth2/verify', { authed: true, csrf: true, type: 'json' }, {
code: userCode,
approve: approve === true ? 'yes' : 'no',
});
@ -91,7 +90,7 @@ const MszOAuth2Verify = () => {
fCode.classList.add('hidden');
userCode = encodeURIComponent(eUserCode.value);
$x.get(`/oauth2/resolve-verify?code=${userCode}`, { authed: true, csrf: true, type: 'json' })
$xhr.get(`/oauth2/resolve-verify?code=${userCode}`, { authed: true, csrf: true, type: 'json' })
.then(result => {
const body = result.body;

View file

@ -12,11 +12,13 @@ const fs = require('fs');
debug: isDebug,
swc: {
es: 'es2021',
jsx: '$jsx',
},
};
const tasks = {
js: [
{ source: 'common.js', target: '/assets', name: 'common.{hash}.js', },
{ source: 'misuzu.js', target: '/assets', name: 'misuzu.{hash}.js', },
{ source: 'oauth2.js', target: '/assets', name: 'oauth2.{hash}.js', },
{ source: 'redir-bsky.js', target: '/assets', name: 'redir-bsky.{hash}.js', },
@ -24,6 +26,7 @@ const fs = require('fs');
],
css: [
{ source: 'errors.css', target: '/', name: 'errors.css', },
{ source: 'common.css', target: '/assets', name: 'common.{hash}.css', },
{ source: 'misuzu.css', target: '/assets', name: 'misuzu.{hash}.css', },
{ source: 'oauth2.css', target: '/assets', name: 'oauth2.{hash}.css', },
],

View file

@ -7,7 +7,7 @@
{% block html_head %}
<meta name="description" content="{{ error_blerb }}">
<link href="/vendor/fontawesome/css/all.min.css" type="text/css" rel="stylesheet">
<link href="{{ asset('errors.css') }}" rel="stylesheet">
<link href="/errors.css" rel="stylesheet">
<style>
:root {
--error-colour: {{ error_colour }};

View file

@ -51,10 +51,10 @@
<script>
window.addEventListener('load', function() {
const ubExpires = $i('ub_expires'),
ubExpiresCustom = $i('ub_expires_custom'),
ubSeverity = $i('ub_severity'),
ubSeverityDisplay = $i('ub_severity_display');
const ubExpires = $id('ub_expires'),
ubExpiresCustom = $id('ub_expires_custom'),
ubSeverity = $id('ub_severity'),
ubSeverityDisplay = $id('ub_severity_display');
const updateExpires = function() {
ubExpiresCustom.parentNode.classList.toggle('manage__ban__duration__value__custom--hidden', ubExpires.value != '-2');

View file

@ -3,6 +3,7 @@
{% block html_head %}
{% include '_layout/meta.twig' %}
<link href="/vendor/fontawesome/css/all.min.css" type="text/css" rel="stylesheet">
<link href="{{ asset('common.css') }}" type="text/css" rel="stylesheet">
<link href="{{ asset('misuzu.css') }}" type="text/css" rel="stylesheet">
{% if site_background is defined %}
<style>
@ -62,5 +63,6 @@
{% endblock %}
<script src="/vendor/highlightjs/highlight.min.js" type="text/javascript"></script>
<script src="{{ asset('common.js') }}" type="text/javascript"></script>
<script src="{{ asset('misuzu.js') }}" type="text/javascript"></script>
{% endblock %}

View file

@ -3,6 +3,7 @@
{% set html_title = (title is defined ? (title ~ ' :: ') : '') ~ globals.site_info.name %}
{% block html_head %}
<link href="{{ asset('common.css') }}" rel="stylesheet">
<link href="{{ asset('oauth2.css') }}" rel="stylesheet">
{% endblock %}
@ -35,5 +36,6 @@
</div>
</div>
</div>
<script src="{{ asset('common.js') }}"></script>
<script src="{{ asset('oauth2.js') }}"></script>
{% endblock %}