mami/src/mami.js/settings.js

872 lines
26 KiB
JavaScript

#include common.js
#include emotes.js
#include txtrigs.js
#include utility.js
#include weeb.js
#include ui/emotes.js
#include ui/view.js
#include sound/sndpacks.js
#include sound/umisound.js
#include sound/osukeys.js
// Add anything you use Umi.Settings with here, lookups should probably be restricted or something to make sure
const UmiSettings = {
categories: [
{
id: 'interface',
name: 'Interface',
},
{
id: 'text',
name: 'Text',
},
{
id: 'notification',
name: 'Notification',
},
{
id: 'sounds',
name: 'Sound',
},
{
id: 'misc',
name: 'Misc',
},
{
id: 'settings',
name: 'Settings',
},
{
id: 'debug',
name: 'Debug',
collapse: true,
warning: "Only touch these settings if you're ABSOLUTELY sure you know what you're doing, you're on your own if you break something.",
}
],
settings: [
{
id: 'style',
name: 'Style',
category: 'interface',
type: 'select',
data: function() { return Umi.UI.View.AccentColours; },
dataType: 'call',
mutable: true,
default: 'dark',
watcher: function(v, n, i) {
if(!i) Umi.UI.View.AccentReload();
},
},
{
id: 'compactView',
name: 'Compact view',
category: 'interface',
type: 'checkbox',
mutable: true,
default: false,
watcher: function(v, n, i) {
if(!i) Umi.UI.View.AccentReload();
},
},
{
id: 'autoScroll',
name: 'Scroll to latest message',
category: 'interface',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'closeTabConfirm',
name: 'Confirm tab close',
category: 'interface',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'showChannelList',
name: 'Show channel list',
category: 'interface',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'fancyInfo',
name: 'Fancy server messages',
category: 'interface',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'autoCloseUserContext',
name: 'Auto-close user menus',
category: 'interface',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'enableParser',
name: 'Parse markup',
category: 'text',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'enableEmoticons',
name: 'Parse emoticons',
category: 'text',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'autoParseUrls',
name: 'Auto detect links',
category: 'text',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'preventOverflow',
name: 'Prevent overflow',
category: 'text',
type: 'checkbox',
mutable: true,
default: false,
watcher: function(v) {
document.body.classList[v ? 'add' : 'remove']('prevent-overflow');
},
},
{
id: 'expandTextBox',
name: 'Grow input box',
category: 'text',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'eepromAutoInsert',
name: 'Auto-insert uploads',
category: 'text',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'autoEmbedV1',
name: 'Auto-embed media',
category: 'text',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'soundEnable',
name: 'Enable sound',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: true,
//virtual: true, only when no autoplay
watcher: function(v, n, i) {
if(v && !mami.hasSound()) {
mami.initSound();
Umi.Settings.touch('soundVolume');
Umi.Settings.touch('soundPack', true);
}
if(mami.hasAudio())
mami.getAudio().setMuted(!v);
},
},
{
id: 'soundPack',
name: 'Sound pack',
category: 'sounds',
type: 'select',
data: function() {
const packs = {};
mami.getSoundPacks().forEachPack(function(pack) {
packs[pack.getName()] = pack.getTitle();
});
return packs;
},
dataType: 'call',
mutable: true,
default: 'ajax-chat',
watcher: function(v, n, i) {
const packs = mami.getSoundPacks();
if(!packs.hasPack(v)) {
Umi.Settings.remove(n);
return;
}
const player = mami.getSoundPackPlayer();
if(player !== null) {
player.loadPack(packs.getPack(v));
if(!i) player.playEvent('server');
}
},
},
{
id: 'soundVolume',
name: 'Sound volume',
category: 'sounds',
type: 'range',
mutable: true,
default: 80,
watcher: function(v, n, i) {
if(mami.hasAudio())
mami.getAudio().setVolume(v / 100);
},
},
{
id: 'soundEnableJoin',
name: 'Play join sound',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'soundEnableLeave',
name: 'Play leave sound',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'soundEnableError',
name: 'Play error sound',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'soundEnableServer',
name: 'Play server message sound',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'soundEnableIncoming',
name: 'Play receive message sound',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'onlySoundOnMention',
name: 'Only play receive sound on mention',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'soundEnableOutgoing',
name: 'Play send message sound',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'soundEnablePrivate',
name: 'Play private message sound',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'soundEnableForceLeave',
name: 'Play kick sound',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'minecraft',
name: 'Minecraft',
category: 'sounds',
type: 'select',
mutable: true,
dataType: 'object',
data: {
'no': 'No Minecraft',
'yes': 'Yes Minecraft',
'old': 'Old Minecraft',
},
default: 'no',
watcher: function(v, n, i) {
if(!i) Umi.Sound.Play('join');
},
},
{
id: 'playSoundOnConnect',
name: 'Play join sound on connect',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'windowsLiveMessenger',
name: 'Windows Live Messenger',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'seinfeld',
name: 'Seinfeld',
category: 'sounds',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'flashTitle',
name: 'Strobe title on new message',
category: 'notification',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'showServerMsgInTitle',
name: 'Show server message in title',
category: 'notification',
type: 'checkbox',
mutable: true,
default: true,
},
{
id: 'enableNotifications',
name: 'Show notifications',
category: 'notification',
type: 'checkbox',
mutable: true,
default: false,
watcher: function(v, n, i) {
if(!v) return;
if(Notification.permission === 'granted' && Notification.permission !== 'denied')
return;
Notification.requestPermission()
.then(perm => {
if(perm !== 'granted')
Umi.Settings.set('enableNotifications', false);
});
},
},
{
id: 'notificationShowMessage',
name: 'Show contents of message',
category: 'notification',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'notificationTriggers',
name: 'Triggers',
category: 'notification',
type: 'text',
mutable: true,
default: '',
},
{
id: 'playJokeSounds',
name: 'Run joke triggers',
category: 'misc',
type: 'checkbox',
mutable: true,
default: true,
watcher: function(v, n, i) {
if(v) {
const triggers = mami.getTextTriggers();
if(!triggers.hasTriggers())
futami.getJson('texttriggers').then(trigInfos => triggers.addTriggers(trigInfos));
}
},
},
{
id: 'weeaboo',
name: 'Weeaboo',
category: 'misc',
type: 'checkbox',
mutable: true,
default: false,
watcher: function(v, n, i) {
if(v) Weeaboo.init();
},
},
{
id: 'motivationalImages',
name: 'Make images motivational',
category: 'misc',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'motivationalVideos',
name: 'Make videos motivational',
category: 'misc',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'osuKeysV2',
name: 'osu! keyboard sounds',
category: 'misc',
type: 'select',
mutable: true,
dataType: 'object',
data: {
'no': 'Off',
'yes': 'On',
'rng': 'On, random pitch',
},
default: 'no',
watcher: function(v, n, i) {
// migrate old value
if(i && Umi.Settings.has('osuKeys')) {
Umi.Settings.set('osuKeysV2', Umi.Settings.get('osuKeys') ? 'yes' : 'no');
Umi.Settings.remove('osuKeys');
return;
}
OsuKeys.setEnable(v !== 'no');
OsuKeys.setRandomRate(v === 'rng');
},
},
{
id: 'explosionRadius',
name: 'Messages to keep on clear',
category: 'misc',
type: 'number',
mutable: true,
default: 20,
},
{
id: 'reloadEmoticons',
name: 'Reload emoticons',
category: 'misc',
type: 'button',
mutable: true,
dataType: 'void',
click: function() {
const emotes = futami.get('emotes');
setTimeout(function() {
this.disabled = true;
futami.getJson('emotes', true)
.then(emotes => {
MamiEmotes.clear();
MamiEmotes.loadLegacy(emotes);
})
.finally(() => {
Umi.UI.Emoticons.Init();
this.disabled = false;
});
}, 200);
},
},
{
id: 'reloadJokeTriggers',
name: 'Reload joke triggers',
category: 'misc',
type: 'button',
mutable: true,
dataType: 'void',
click: function() {
this.disabled = true;
const triggers = mami.getTextTriggers();
triggers.clearTriggers();
if(Umi.Settings.get('playJokeSounds'))
futami.getJson('texttriggers', true)
.then(trigInfos => triggers.addTriggers(trigInfos))
.finally(() => this.disabled = false);
},
},
{
id: 'dumpPackets',
name: 'Dump packets to console',
category: 'debug',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'openLegacyChat',
name: 'Open compatibility client',
category: 'misc',
type: 'button',
mutable: true,
dataType: 'void',
click: function() {
const meow = $e('a', { href: window.AMI_URL, target: '_blank', style: { display: 'none' } });
document.body.appendChild(meow);
meow.click();
$r(meow);
},
},
{
id: 'neverUseWorker',
name: 'Never use Worker for connection',
category: 'debug',
type: 'checkbox',
mutable: true,
default: false,
emergencyReset: true,
confirm: "If you're here it likely means that you mistakenly believe that your browser doesn't suck. You may go ahead but if disabling this causes any annoyances for other users you will be expunged.",
},
{
id: 'forceUseWorker',
name: 'Always use Worker for connection',
category: 'debug',
type: 'checkbox',
mutable: true,
default: false,
emergencyReset: true,
},
{
id: 'marqueeAllNames',
name: 'Apply marquee on everyone',
category: 'debug',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'resolveUrls',
name: 'Resolve URL meta data',
category: 'debug',
type: 'checkbox',
mutable: true,
default: false,
},
{
id: 'tmpDisableOldThemeSys',
name: 'Disable Old Theme System',
category: 'debug',
type: 'checkbox',
mutable: true,
default: false,
emergencyReset: true,
watcher: function(v, n, i) {
if(!i) Umi.UI.View.AccentReload();
},
},
{
id: 'tmpSkipDomainPopUpThing',
name: 'Skip domain pop up thing',
category: 'debug',
type: 'checkbox',
mutable: true,
default: false,
emergencyReset: true,
},
{
id: 'settingsImport',
name: 'Import settings',
category: 'settings',
type: 'button',
mutable: true,
dataType: 'void',
click: function() {
$ri('-mami-settings-import-field');
imp = $e('input', {
id: '-mami-settings-import-field',
type: 'file',
accept: '.mami',
style: { display: 'none' },
});
imp.addEventListener('change', function() {
if(imp.files.length > 0)
Umi.Settings.importFile(imp.files[0]);
$r(imp);
});
document.body.appendChild(imp);
imp.click();
},
},
{
id: 'settingsExport',
name: 'Export settings',
category: 'settings',
type: 'button',
mutable: true,
dataType: 'void',
click: function() {
const data = {
a: 'Mami Settings Export',
v: 1,
d: [],
};
for(const setting of UmiSettings.settings)
if(setting.mutable && setting.type !== 'button')
data.d.push({
i: setting.id,
v: Umi.Settings.get(setting.id)
});
const user = Umi.User.getCurrentUser();
let fileName = 'settings.mami';
if(user !== null)
fileName = user.getName() + '\'s settings.mami';
const exp = $e('a', {
href: URL.createObjectURL(new Blob(
[btoa(JSON.stringify(data))],
{ type: 'application/octet-stream' }
)),
download: fileName,
target: '_blank',
style: { display: 'none' }
});
document.body.appendChild(exp);
exp.click();
$r(exp);
},
},
{
id: 'settingsReset',
name: 'Reset settings',
category: 'settings',
type: 'button',
mutable: true,
dataType: 'void',
click: function() {
if(!confirm('This will reset all your settings to their defaults values. Are you sure you want to do this?'))
return;
for(const setting of UmiSettings.settings)
if(setting.mutable)
Umi.Settings.remove(setting.id);
},
},
{
id: 'resetAudioContext',
name: 'Reset audio context',
category: 'debug',
type: 'button',
mutable: true,
dataType: 'void',
click: function() {
if(mami.hasAudio())
mami.getAudio().resetContext();
if(mami.hasSound())
mami.getSound().clear();
},
},
],
};
Umi.Settings = function(metaData) {
let getRaw = null, setRaw = null, removeRaw = null;
const valid = [], mutable = [], locked = [], virtual = [];
const watchers = new Map, defaults = new Map, virtuals = new Map;
const prefix = 'umi-';
for(const setting of metaData) {
valid.push(setting.id);
if(setting.mutable && setting.dataType !== 'void')
mutable.push(setting.id);
if(setting.virtual)
virtual.push(setting.id);
if(setting.default)
defaults.set(setting.id, setting.default);
}
getRaw = function(name) {
let value = null;
if(virtual.includes(name))
value = virtuals.get(name);
else
value = localStorage.getItem(prefix + name);
return value === undefined ? null : JSON.parse(value);
};
setRaw = function(name, value) {
value = JSON.stringify(value);
if(virtual.includes(name))
virtuals.set(name, value);
else
localStorage.setItem(prefix + name, value);
};
removeRaw = function(name) {
virtuals.delete(name);
localStorage.removeItem(prefix + name);
};
removeRaw('cookiesMigrated');
const hasValue = function(name) {
if(!mutable.includes(name))
return false;
const value = getRaw(name);
return value !== null && value !== undefined;
};
const getValue = function(name) {
if(!valid.includes(name))
return null;
const value = mutable.includes(name) ? getRaw(name) : null;
if(value === null || value === undefined)
return defaults.get(name) || null;
return value;
};
const setValue = function(name, value) {
if(!mutable.includes(name))
return;
if(locked.includes(name))
return;
locked.push(name);
if(getValue(name) !== value) {
if(value === defaults.get(name))
removeRaw(name);
else
setRaw(name, value);
callWatcher(name);
}
$ari(locked, name);
};
const callWatcher = function(name, initial) {
if(watchers.has(name)) {
const w = watchers.get(name),
v = getValue(name);
initial = !!initial;
for(const f of w)
f(v, name, initial);
}
};
return {
has: hasValue,
get: getValue,
set: setValue,
remove: function(name) {
if(!mutable.includes(name))
return;
if(locked.includes(name))
return;
locked.push(name);
removeRaw(name);
callWatcher(name);
$ari(locked, name);
},
toggle: function(name) {
setValue(name, !getValue(name));
},
touch: callWatcher,
watch: function(name, callback) {
if(!mutable.includes(name))
return;
if(!watchers.has(name))
watchers.set(name, []);
const callbacks = watchers.get(name);
if(!callbacks.includes(callback))
callbacks.push(callback);
callback(getValue(name), name, true);
},
unwatch: function(name, callback) {
if(!watchers.get(name))
return;
$ari(watchers.get(name), callback);
},
virtualise: function(name) {
if(mutable.includes(name) && !virtual.includes(name)) {
const value = getRaw(name);
virtual.push(name);
if(value !== null && value !== undefined)
setRaw(name, value);
}
},
importFile: function(file) {
const reader = new FileReader;
reader.addEventListener('load', function() {
let data = atob(reader.result);
if(!data) {
alert('This is not a settings export. (1)');
return;
}
data = JSON.parse(data);
if(!data) {
alert('This is not a settings export. (2)');
return;
}
if(!data.a || !data.v || data.a !== 'Mami Settings Export') {
alert('This is not a settings export. (3)');
return;
}
if(data.v < 1) {
alert('Version of this settings export cannot be interpreted.');
return;
}
if(data.v > 1) {
alert('This settings export is not compatible with this version of the chat client.');
return;
}
if(!Array.isArray(data.d)) {
alert('Settings export contains invalid data.');
return;
}
if(confirm('Your current settings will be replaced with the ones in the export. Are you sure you want to continue?')) {
const settings = {};
for(const setting of data.d)
if(setting.i)
settings[setting.i] = setting.v;
for(const setting of UmiSettings.settings)
if(setting.mutable && setting.type !== 'button' && setting.id in settings)
setValue(setting.id, settings[setting.id]);
}
});
reader.readAsText(file);
},
};
};