Compare commits
4 commits
834f5cfd4b
...
f539c46954
Author | SHA1 | Date | |
---|---|---|---|
f539c46954 | |||
5514c8fdc6 | |||
1c797565b7 | |||
7564765317 |
22 changed files with 1197 additions and 1277 deletions
|
@ -1,26 +1,32 @@
|
|||
#include server.js
|
||||
|
||||
// backwards compat for scripts
|
||||
if(!Umi) window.Umi = {};
|
||||
const MamiCompat = (current, path, handler) => {
|
||||
if(typeof path !== 'string')
|
||||
throw 'path must be a string';
|
||||
if(typeof handler !== 'function')
|
||||
throw 'handler must be a function';
|
||||
|
||||
if(!Umi.Server) Umi.Server = {};
|
||||
if(!Umi.Server.sendMessage) Umi.Server.sendMessage = function() { console.log('Umi.Server.sendMessage called'); };
|
||||
if(!Umi.Server.SendMessage) Umi.Server.SendMessage = Umi.Server.sendMessage.bind(Umi.Server);
|
||||
path = path.split('.');
|
||||
|
||||
if(!Umi.Protocol) Umi.Protocol = {};
|
||||
if(!Umi.Protocol.SockLegacy) Umi.Protocol.SockLegacy = {};
|
||||
if(!Umi.Protocol.SockLegacy.Protocol) Umi.Protocol.SockLegacy.Protocol = {};
|
||||
if(!Umi.Protocol.SockLegacy.Protocol.Instance) Umi.Protocol.SockLegacy.Protocol.Instance = {};
|
||||
if(!Umi.Protocol.SockLegacy.Protocol.Instance.SendMessage) Umi.Protocol.SockLegacy.Protocol.Instance.SendMessage = Umi.Server.sendMessage.bind(Umi.Server);
|
||||
const final = path.pop();
|
||||
if(final === undefined)
|
||||
throw 'invalid path';
|
||||
|
||||
if(!Umi.Parser) Umi.Parser = {};
|
||||
if(!Umi.Parser.SockChatBBcode) Umi.Parser.SockChatBBcode = {};
|
||||
if(!Umi.Parser.SockChatBBcode.EmbedStub) Umi.Parser.SockChatBBcode.EmbedStub = function() {};
|
||||
for(const part of path) {
|
||||
if(!(part in current))
|
||||
current[part] = {};
|
||||
current = current[part];
|
||||
}
|
||||
|
||||
if(!Umi.UI) Umi.UI = {};
|
||||
if(!Umi.UI.View) Umi.UI.View = {};
|
||||
if(!Umi.UI.View.SetText) Umi.UI.View.SetText = function() { console.log('Umi.UI.View.SetText called'); };
|
||||
if(!(final in current))
|
||||
current[final] = handler;
|
||||
};
|
||||
|
||||
if(!Umi.UI.Menus) Umi.UI.Menus = {};
|
||||
if(!Umi.UI.Menus.Add) Umi.UI.Menus.Add = function() { console.log('Umi.UI.Menus.Add called'); };
|
||||
if(!Umi.UI.Menus.Get) Umi.UI.Menus.Get = function() { console.log('Umi.UI.Menus.Get called'); };
|
||||
// Backwards compat for scripts
|
||||
// Keep in sync with <https://fii.moe/fp/13176> for as long as possible
|
||||
MamiCompat(Umi, 'Server.SendMessage', text => Umi.Server.sendMessage(text));
|
||||
MamiCompat(Umi, 'Protocol.SockLegacy.Protocol.Instance.SendMessage', text => Umi.Server.sendMessage(text));
|
||||
MamiCompat(Umi, 'Parser.SockChatBBcode.EmbedStub', () => {}); // intentionally a no-op
|
||||
MamiCompat(Umi, 'UI.View.SetText', text => console.log(`Umi.UI.View.SetText(text: ${text})`));
|
||||
MamiCompat(Umi, 'UI.Menus.Add', (baseId, title, initiallyHidden) => console.log(`Umi.UI.Menus.Add(baseId: ${baseId}, title: ${title}, initiallyHidden: ${initiallyHidden})`));
|
||||
MamiCompat(Umi, 'UI.Menus.Get', (baseId, icon) => console.log(`Umi.UI.Menus.Get(baseId: ${baseId}, icon: ${icon})`));
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
const MamiContext = function(targetBody) {
|
||||
const pub = {};
|
||||
|
||||
let settings;
|
||||
pub.setSettings = inst => settings = inst;
|
||||
pub.getSettings = () => settings;
|
||||
|
||||
const viewsCtx = new MamiViewsControl({ body: targetBody });
|
||||
pub.getViews = () => viewsCtx;
|
||||
|
||||
|
|
|
@ -7,8 +7,14 @@ const Umi = { UI: {} };
|
|||
#include messages.js
|
||||
#include mszauth.js
|
||||
#include server.js
|
||||
#include settings.js
|
||||
#include utility.js
|
||||
#include weeb.js
|
||||
#include audio/context.js
|
||||
#include eeprom/eeprom.js
|
||||
#include settings/backup.js
|
||||
#include settings/settings.js
|
||||
#include sound/osukeys.js
|
||||
#include sound/umisound.js
|
||||
#include ui/chat-layout.js
|
||||
#include ui/domaintrans.jsx
|
||||
#include ui/elems.js
|
||||
|
@ -18,12 +24,10 @@ const Umi = { UI: {} };
|
|||
#include ui/markup.js
|
||||
#include ui/menus.js
|
||||
#include ui/view.js
|
||||
#include ui/settings.js
|
||||
#include ui/settings.jsx
|
||||
#include ui/title.js
|
||||
#include ui/toggles.js
|
||||
#include ui/uploads.js
|
||||
#include audio/context.js
|
||||
#include eeprom/eeprom.js
|
||||
|
||||
(async () => {
|
||||
const ctx = new MamiContext(document.body),
|
||||
|
@ -98,6 +102,63 @@ const Umi = { UI: {} };
|
|||
Umi.UI.Elements.MessageMenus = $i('umi-msg-menu');
|
||||
|
||||
|
||||
lo.setMessage('Loading settings...');
|
||||
|
||||
const settings = new MamiSettings('umi-');
|
||||
ctx.setSettings(settings);
|
||||
|
||||
settings.define('style', 'string', 'dark');
|
||||
settings.define('compactView', 'boolean', false);
|
||||
settings.define('autoScroll', 'boolean', true);
|
||||
settings.define('closeTabConfirm', 'boolean', false);
|
||||
settings.define('showChannelList', 'boolean', false);
|
||||
settings.define('fancyInfo', 'boolean', true);
|
||||
settings.define('autoCloseUserContext', 'boolean', true);
|
||||
settings.define('enableParser', 'boolean', true);
|
||||
settings.define('enableEmoticons', 'boolean', true);
|
||||
settings.define('autoParseUrls', 'boolean', true);
|
||||
settings.define('preventOverflow', 'boolean', false);
|
||||
settings.define('expandTextBox', 'boolean', false);
|
||||
settings.define('eepromAutoInsert', 'boolean', true);
|
||||
settings.define('autoEmbedV1', 'boolean', false);
|
||||
settings.define('soundEnable', 'boolean', true, false, true);
|
||||
settings.define('soundPack', 'string', 'ajax-chat');
|
||||
settings.define('soundVolume', 'number', 80);
|
||||
settings.define('soundEnableJoin', 'boolean', true);
|
||||
settings.define('soundEnableLeave', 'boolean', true);
|
||||
settings.define('soundEnableError', 'boolean', true);
|
||||
settings.define('soundEnableServer', 'boolean', true);
|
||||
settings.define('soundEnableIncoming', 'boolean', true);
|
||||
settings.define('onlySoundOnMention', 'boolean', true);
|
||||
settings.define('soundEnableOutgoing', 'boolean', false);
|
||||
settings.define('soundEnablePrivate', 'boolean', true);
|
||||
settings.define('soundEnableForceLeave', 'boolean', true);
|
||||
settings.define('minecraft', ['no', 'yes', 'old'], 'no');
|
||||
settings.define('playSoundOnConnect', 'boolean', false);
|
||||
settings.define('windowsLiveMessenger', 'boolean', false);
|
||||
settings.define('seinfeld', 'boolean', false);
|
||||
settings.define('flashTitle', 'boolean', true);
|
||||
settings.define('showServerMsgInTitle', 'boolean', true);
|
||||
settings.define('playJokeSounds', 'boolean', true);
|
||||
settings.define('weeaboo', 'boolean', false);
|
||||
settings.define('motivationalImages', 'boolean', false);
|
||||
settings.define('motivationalVideos', 'boolean', false);
|
||||
settings.define('osuKeys', 'boolean', false);
|
||||
settings.define('osuKeysV2', ['no', 'yes', 'rng'], 'no');
|
||||
settings.define('explosionRadius', 'number', 20);
|
||||
settings.define('dumpPackets', 'boolean', FUTAMI_DEBUG);
|
||||
settings.define('neverUseWorker', 'boolean', false, false, true);
|
||||
settings.define('forceUseWorker', 'boolean', false, false, true);
|
||||
settings.define('marqueeAllNames', 'boolean', false);
|
||||
settings.define('tmpDisableOldThemeSys', 'boolean', false, false, true);
|
||||
settings.define('tmpSkipDomainPopUpThing', 'boolean', false, false, true);
|
||||
|
||||
const noNotifSupport = !('Notification' in window);
|
||||
settings.define('enableNotifications', 'boolean', false, noNotifSupport, true);
|
||||
settings.define('notificationShowMessage', 'boolean', false, noNotifSupport);
|
||||
settings.define('notificationTriggers', 'string', '', noNotifSupport);
|
||||
|
||||
|
||||
lo.setMessage('Loading sounds...');
|
||||
try {
|
||||
const sounds = await futami.getJson('sounds2');
|
||||
|
@ -109,6 +170,46 @@ const Umi = { UI: {} };
|
|||
console.error(ex);
|
||||
}
|
||||
|
||||
if(!await MamiDetectAutoPlay()) {
|
||||
settings.set('soundEnable', false);
|
||||
settings.virtualise('soundEnable');
|
||||
}
|
||||
|
||||
settings.watch('soundEnable', (v, n, i) => {
|
||||
if(v && !mami.hasSound()) {
|
||||
mami.initSound();
|
||||
|
||||
settings.touch('soundVolume');
|
||||
settings.touch('soundPack');
|
||||
|
||||
const player = mami.getSoundPackPlayer();
|
||||
if(player !== null)
|
||||
player.playEvent('server');
|
||||
}
|
||||
|
||||
if(mami.hasAudio())
|
||||
mami.getAudio().setMuted(!v);
|
||||
});
|
||||
|
||||
settings.watch('soundPack', (v, n, i) => {
|
||||
const packs = mami.getSoundPacks();
|
||||
if(!packs.hasPack(v)) {
|
||||
settings.delete(n);
|
||||
return;
|
||||
}
|
||||
|
||||
const player = mami.getSoundPackPlayer();
|
||||
if(player !== null) {
|
||||
player.loadPack(packs.getPack(v));
|
||||
if(!i) player.playEvent('server');
|
||||
}
|
||||
});
|
||||
|
||||
settings.watch('soundVolume', v => {
|
||||
if(mami.hasAudio())
|
||||
mami.getAudio().setVolume(v / 100);
|
||||
})
|
||||
|
||||
|
||||
lo.setMessage('Loading emoticons...');
|
||||
try {
|
||||
|
@ -119,32 +220,13 @@ const Umi = { UI: {} };
|
|||
}
|
||||
|
||||
|
||||
lo.setMessage('Loading settings...');
|
||||
|
||||
Umi.Settings = new Umi.Settings(UmiSettings.settings);
|
||||
|
||||
if(!await MamiDetectAutoPlay()) {
|
||||
Umi.Settings.set('soundEnable', false);
|
||||
Umi.Settings.virtualise('soundEnable');
|
||||
}
|
||||
|
||||
const meta = UmiSettings.settings;
|
||||
for(const setting of UmiSettings.settings)
|
||||
if(setting.watcher)
|
||||
Umi.Settings.watch(setting.id, setting.watcher);
|
||||
|
||||
|
||||
if(!Umi.Settings.get('tmpSkipDomainPopUpThing'))
|
||||
if(!settings.get('tmpSkipDomainPopUpThing'))
|
||||
await (() => {
|
||||
return new Promise((resolve) => {
|
||||
views.push(new MamiDomainTransition(() => {
|
||||
for(const setting of UmiSettings.settings)
|
||||
if(setting.id === 'settingsImport') {
|
||||
setting.click();
|
||||
break;
|
||||
}
|
||||
(new MamiSettingsBackup(settings)).importUpload(document.body);
|
||||
}, () => {
|
||||
Umi.Settings.set('tmpSkipDomainPopUpThing', true);
|
||||
settings.set('tmpSkipDomainPopUpThing', true);
|
||||
views.pop();
|
||||
resolve();
|
||||
}));
|
||||
|
@ -154,10 +236,7 @@ const Umi = { UI: {} };
|
|||
|
||||
const onHashChange = () => {
|
||||
if(location.hash === '#reset') {
|
||||
for(const setting of UmiSettings.settings)
|
||||
if(setting.emergencyReset)
|
||||
Umi.Settings.remove(setting.id);
|
||||
|
||||
settings.clear(true);
|
||||
location.assign('/');
|
||||
}
|
||||
};
|
||||
|
@ -182,13 +261,61 @@ const Umi = { UI: {} };
|
|||
Umi.UI.Hooks.AddChannelHooks();
|
||||
Umi.UI.Hooks.AddTextHooks();
|
||||
|
||||
settings.watch('style', (v, n, i) => { if(!i) Umi.UI.View.AccentReload(); });
|
||||
settings.watch('compactView', (v, n, i) => { if(!i) Umi.UI.View.AccentReload(); });
|
||||
settings.watch('preventOverflow', v => document.body.classList.toggle('prevent-overflow', v));
|
||||
settings.watch('tmpDisableOldThemeSys', (v, n, i) => { if(!i) Umi.UI.View.AccentReload(); });
|
||||
|
||||
const mcPortalSnd = 'minecraft:nether:enter';
|
||||
if(Umi.Settings.get('minecraft') !== 'no' && ctx.hasSound() && sndLib.hasSound(mcPortalSnd))
|
||||
if(settings.get('minecraft') !== 'no' && ctx.hasSound() && sndLib.hasSound(mcPortalSnd))
|
||||
ctx.getSound().load(mcPortalSnd, sndLib.getSound(mcPortalSnd).getSources(), function(success, buffer) {
|
||||
if(success)
|
||||
buffer.createSource().play();
|
||||
});
|
||||
|
||||
settings.watch('minecraft', (v, n, i) => {
|
||||
if(!i)
|
||||
Umi.Sound.Play('join');
|
||||
});
|
||||
|
||||
settings.watch('enableNotifications', v => {
|
||||
if(!v || !('Notification' in window)
|
||||
|| (Notification.permission === 'granted' && Notification.permission !== 'denied'))
|
||||
return;
|
||||
|
||||
Notification.requestPermission()
|
||||
.then(perm => {
|
||||
if(perm !== 'granted')
|
||||
settings.set('enableNotifications', false);
|
||||
});
|
||||
});
|
||||
|
||||
settings.watch('playJokeSounds', v => {
|
||||
if(!v) return;
|
||||
|
||||
const triggers = mami.getTextTriggers();
|
||||
if(!triggers.hasTriggers())
|
||||
futami.getJson('texttriggers').then(trigInfos => triggers.addTriggers(trigInfos));
|
||||
});
|
||||
|
||||
settings.watch('weeaboo', v => {
|
||||
if(v) Weeaboo.init();
|
||||
});
|
||||
|
||||
settings.watch('osuKeysV2', (v, n, i) => {
|
||||
// migrate old value
|
||||
if(i) {
|
||||
if(settings.has('osuKeys')) {
|
||||
settings.set('osuKeysV2', settings.get('osuKeys') ? 'yes' : 'no');
|
||||
settings.delete('osuKeys');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
OsuKeys.setEnable(v !== 'no');
|
||||
OsuKeys.setRandomRate(v === 'rng');
|
||||
});
|
||||
|
||||
|
||||
lo.setMessage('Loading EEPROM...');
|
||||
try {
|
||||
|
@ -205,7 +332,7 @@ const Umi = { UI: {} };
|
|||
lo.setMessage('Building menus...');
|
||||
|
||||
Umi.UI.Menus.Add('users', 'Users');
|
||||
Umi.UI.Menus.Add('channels', 'Channels', !Umi.Settings.get('showChannelList'));
|
||||
Umi.UI.Menus.Add('channels', 'Channels', !settings.get('showChannelList'));
|
||||
Umi.UI.Menus.Add('settings', 'Settings');
|
||||
|
||||
let sidebarAnimation = null;
|
||||
|
@ -254,10 +381,10 @@ const Umi = { UI: {} };
|
|||
|
||||
Umi.UI.Toggles.Add('scroll', {
|
||||
'click': function() {
|
||||
Umi.Settings.toggle('autoScroll');
|
||||
settings.toggle('autoScroll');
|
||||
}
|
||||
}, 'Autoscroll');
|
||||
Umi.Settings.watch('autoScroll', function(value) {
|
||||
settings.watch('autoScroll', function(value) {
|
||||
Umi.UI.Toggles.Get('scroll').classList[value ? 'remove' : 'add']('sidebar__selector-mode--scroll-off');
|
||||
});
|
||||
|
||||
|
@ -266,10 +393,10 @@ const Umi = { UI: {} };
|
|||
|
||||
Umi.UI.Toggles.Add('audio', {
|
||||
'click': function() {
|
||||
Umi.Settings.toggle('soundEnable');
|
||||
settings.toggle('soundEnable');
|
||||
}
|
||||
}, 'Sounds');
|
||||
Umi.Settings.watch('soundEnable', function(value) {
|
||||
settings.watch('soundEnable', function(value) {
|
||||
Umi.UI.Toggles.Get('audio').classList[value ? 'remove' : 'add']('sidebar__selector-mode--audio-off');
|
||||
});
|
||||
|
||||
|
@ -284,7 +411,7 @@ const Umi = { UI: {} };
|
|||
Umi.UI.Toggles.Add('clear', {
|
||||
'click': function() {
|
||||
if(confirm('ARE YOU SURE ABOUT THAT???')) {
|
||||
const limit = Umi.Settings.get('explosionRadius');
|
||||
const limit = settings.get('explosionRadius');
|
||||
const explode = $e({
|
||||
tag: 'img',
|
||||
attrs: {
|
||||
|
@ -402,7 +529,7 @@ const Umi = { UI: {} };
|
|||
}
|
||||
|
||||
|
||||
if(Umi.Settings.get('eepromAutoInsert'))
|
||||
if(settings.get('eepromAutoInsert'))
|
||||
Umi.UI.Markup.InsertRaw(insertText, '');
|
||||
};
|
||||
|
||||
|
@ -457,8 +584,8 @@ const Umi = { UI: {} };
|
|||
if(ev.dataTransfer && ev.dataTransfer.files.length > 0)
|
||||
for(const file of ev.dataTransfer.files) {
|
||||
if(file.name.slice(-5) === '.mami'
|
||||
&& confirm('This file appears to be a settings export. Do you want to import it?')) {
|
||||
Umi.Settings.importFile(file);
|
||||
&& confirm('This file appears to be a settings export. Do you want to import it? This will overwrite your existing settings!')) {
|
||||
(new MamiSettingsBackup(settings)).importFile(file);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -471,7 +598,7 @@ const Umi = { UI: {} };
|
|||
Umi.UI.InputMenus.Add('emotes', 'Emoticons');
|
||||
|
||||
window.addEventListener('beforeunload', function(ev) {
|
||||
if(Umi.Settings.get('closeTabConfirm')) {
|
||||
if(settings.get('closeTabConfirm')) {
|
||||
ev.preventDefault();
|
||||
return ev.returnValue = 'Are you sure you want to close the tab?';
|
||||
}
|
||||
|
@ -479,7 +606,7 @@ const Umi = { UI: {} };
|
|||
|
||||
|
||||
lo.setMessage('Connecting...');
|
||||
Umi.Server.open(views);
|
||||
Umi.Server.open(views, settings);
|
||||
|
||||
if(window.dispatchEvent)
|
||||
window.dispatchEvent(new Event('umi:connect'));
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include messages.js
|
||||
#include settings.js
|
||||
#include utility.js
|
||||
#include ui/markup.js
|
||||
|
||||
|
@ -226,7 +225,7 @@ Umi.Parsing = (function() {
|
|||
},
|
||||
});
|
||||
|
||||
if(Umi.Settings.get('motivationalImages'))
|
||||
if(mami.getSettings().get('motivationalImages'))
|
||||
html = motivFrame(
|
||||
extractMotiv(element),
|
||||
html
|
||||
|
@ -307,7 +306,7 @@ Umi.Parsing = (function() {
|
|||
},
|
||||
});
|
||||
|
||||
if(Umi.Settings.get('motivationalVideos'))
|
||||
if(mami.getSettings().get('motivationalVideos'))
|
||||
html = motivFrame(
|
||||
extractMotiv(element),
|
||||
html
|
||||
|
|
|
@ -1,870 +0,0 @@
|
|||
#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: (() => 'Notification' in window)(),
|
||||
default: false,
|
||||
watcher: function(v, n, i) {
|
||||
if(!v || !('Notification' in window)
|
||||
|| (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: (() => 'Notification' in window)(),
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
id: 'notificationTriggers',
|
||||
name: 'Triggers',
|
||||
category: 'notification',
|
||||
type: 'text',
|
||||
mutable: (() => 'Notification' in window)(),
|
||||
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: FUTAMI_DEBUG,
|
||||
},
|
||||
{
|
||||
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);
|
||||
},
|
||||
};
|
||||
};
|
126
src/mami.js/settings/backup.js
Normal file
126
src/mami.js/settings/backup.js
Normal file
|
@ -0,0 +1,126 @@
|
|||
#include utility.js
|
||||
|
||||
const MamiSettingsBackup = function(settings) {
|
||||
const header = 'Mami Settings Export';
|
||||
const version = 1;
|
||||
const minVersion = 1;
|
||||
const maxVersion = 1;
|
||||
|
||||
const exportData = () => {
|
||||
const names = settings.names();
|
||||
const data = { a: header, v: version, d: [] };
|
||||
|
||||
for(const name of names) {
|
||||
const info = settings.info(name);
|
||||
if(info.immutable)
|
||||
continue;
|
||||
|
||||
data.d.push({
|
||||
i: info.name,
|
||||
v: settings.get(info.name),
|
||||
});
|
||||
}
|
||||
|
||||
return btoa(JSON.stringify(data));
|
||||
};
|
||||
|
||||
const importData = (data, clear) => {
|
||||
try {
|
||||
data = JSON.parse(atob(data));
|
||||
} catch(ex) {
|
||||
throw 'Settings export data is invalid.';
|
||||
}
|
||||
|
||||
if(typeof data !== 'object')
|
||||
throw 'Settings export data is not an object.';
|
||||
|
||||
if(data.a !== header)
|
||||
throw 'Provided data is not settings export.';
|
||||
|
||||
if(data.v < minVersion)
|
||||
throw 'Settings export is too old and no longer compatible with this version of the chat client.';
|
||||
if(data.v > maxVersion)
|
||||
throw 'Settings export is too new and not compatible with this version of the chat client.';
|
||||
|
||||
if(!Array.isArray(data.d))
|
||||
throw 'Values are missing from settings export.';
|
||||
|
||||
if(clear === true)
|
||||
settings.clear();
|
||||
|
||||
let success = true;
|
||||
|
||||
for(const exportInfo of data.d) {
|
||||
if(typeof exportInfo.i !== 'string')
|
||||
continue;
|
||||
|
||||
try {
|
||||
settings.set(exportInfo.i, exportInfo.v);
|
||||
} catch(ex) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
};
|
||||
|
||||
const importFile = file => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader;
|
||||
reader.onerror = () => reject();
|
||||
reader.onload = () => {
|
||||
try {
|
||||
resolve(importData(reader.result));
|
||||
} catch(ex) {
|
||||
reject(ex);
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
export: exportData,
|
||||
exportDownload: async (target, fileName) => {
|
||||
if(!(target instanceof Element))
|
||||
throw 'target must be an instance of Element';
|
||||
if(typeof fileName !== 'string')
|
||||
fileName = 'settings.mami';
|
||||
|
||||
const data = exportData();
|
||||
const html = $e('a', {
|
||||
href: URL.createObjectURL(new Blob([data], { type: 'application/octet-stream' })),
|
||||
download: fileName,
|
||||
target: '_blank',
|
||||
style: { display: 'none' },
|
||||
});
|
||||
target.appendChild(html);
|
||||
html.click();
|
||||
target.removeChild(html);
|
||||
},
|
||||
import: importData,
|
||||
importFile: importFile,
|
||||
importUpload: (target) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const html = $e('input', {
|
||||
type: 'file',
|
||||
accept: '.mami',
|
||||
style: { display: 'none' },
|
||||
});
|
||||
html.addEventListener('change', () => {
|
||||
if(html.files.length > 0) {
|
||||
importFile(html.files[0])
|
||||
.then(result => resolve(result))
|
||||
.catch(ex => reject(ex))
|
||||
.finally(() => target.removeChild(html));
|
||||
} else {
|
||||
target.removeChild(html);
|
||||
reject();
|
||||
}
|
||||
});
|
||||
target.appendChild(html);
|
||||
html.click();
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
37
src/mami.js/settings/scoped.js
Normal file
37
src/mami.js/settings/scoped.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
const MamiSettingsScoped = function(settings, prefix) {
|
||||
if(typeof settings !== 'object')
|
||||
throw 'settings must be an object';
|
||||
if(typeof prefix !== 'string')
|
||||
throw 'prefix must be a string';
|
||||
if(prefix.length < 1)
|
||||
throw 'prefix may not be empty';
|
||||
|
||||
if(!prefix.endsWith(':'))
|
||||
prefix += ':';
|
||||
|
||||
return {
|
||||
define: (name, type, fallback, immutable, critical) => settings.define(prefix + name, type, fallback, immutable, critical),
|
||||
info: name => settings.info(prefix + name),
|
||||
names: () => {
|
||||
const filtered = [];
|
||||
const names = settings.names();
|
||||
|
||||
for(const name in names)
|
||||
if(name.startsWith(prefix))
|
||||
filtered.push(name.substring(prefix.length));
|
||||
|
||||
return filtered;
|
||||
},
|
||||
has: name => settings.has(prefix + name),
|
||||
get: name => settings.get(prefix + name),
|
||||
set: (name, value) => settings.set(prefix + name, value),
|
||||
delete: name => settings.delete(prefix + name),
|
||||
toggle: name => settings.toggle(prefix + name),
|
||||
touch: name => settings.touch(prefix + name),
|
||||
clear: (criticalOnly, pfx) => settings.clear(criticalOnly, prefix + pfx),
|
||||
watch: (name, handler) => settings.watch(prefix + name, handler),
|
||||
unwatch: (name, handler) => settings.unwatch(prefix + name, handler),
|
||||
virtualise: name => settings.virtualise(prefix + name),
|
||||
scope: name => settings.scope(prefix + name),
|
||||
};
|
||||
};
|
151
src/mami.js/settings/settings.js
Normal file
151
src/mami.js/settings/settings.js
Normal file
|
@ -0,0 +1,151 @@
|
|||
#include watcher.js
|
||||
#include settings/scoped.js
|
||||
#include settings/virtual.js
|
||||
#include settings/webstorage.js
|
||||
|
||||
const MamiSettings = function(storageOrPrefix) {
|
||||
if(typeof storageOrPrefix === 'string')
|
||||
storageOrPrefix = new MamiSettingsWebStorage(window.localStorage, storageOrPrefix);
|
||||
else if(typeof storageOrPrefix !== 'object')
|
||||
throw 'storageOrPrefix must be a prefix string or an object';
|
||||
|
||||
if(typeof storageOrPrefix.get !== 'function'
|
||||
|| typeof storageOrPrefix.set !== 'function'
|
||||
|| typeof storageOrPrefix.delete !== 'function')
|
||||
throw 'required methods do not exist in storageOrPrefix object';
|
||||
|
||||
const storage = new MamiSettingsVirtualStorage(storageOrPrefix),
|
||||
watchers = new MamiWatchers,
|
||||
settings = new Map;
|
||||
|
||||
const getSetting = name => {
|
||||
const setting = settings.get(name);
|
||||
if(setting === undefined)
|
||||
throw `setting ${name} is undefined`;
|
||||
return setting;
|
||||
};
|
||||
|
||||
const getValue = setting => {
|
||||
if(setting.immutable)
|
||||
return setting.fallback;
|
||||
|
||||
const value = storage.get(setting.name);
|
||||
return value === null ? setting.fallback : value;
|
||||
};
|
||||
|
||||
const deleteValue = setting => {
|
||||
if(setting.immutable)
|
||||
return;
|
||||
|
||||
storage.delete(setting.name);
|
||||
};
|
||||
|
||||
const setValue = (setting, value) => {
|
||||
if(value !== null) {
|
||||
if(value === undefined)
|
||||
value = null;
|
||||
else if('type' in setting) {
|
||||
if(Array.isArray(setting.type)) {
|
||||
if(!setting.type.includes(value))
|
||||
throw `setting ${setting.name} must match an enum value`;
|
||||
} else {
|
||||
const type = typeof value;
|
||||
let resolved = false;
|
||||
|
||||
if(type !== setting.type) {
|
||||
if(type === 'string') {
|
||||
if(setting.type === 'number') {
|
||||
value = parseFloat(value);
|
||||
resolved = true;
|
||||
} else if(setting.type === 'boolean') {
|
||||
value = !!value;
|
||||
resolved = true;
|
||||
}
|
||||
} else if(setting.type === 'string') {
|
||||
value = value.toString();
|
||||
resolved = true;
|
||||
}
|
||||
} else resolved = true;
|
||||
|
||||
if(!resolved)
|
||||
throw `setting ${setting.name} must be of type ${setting.type}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(setting.immutable)
|
||||
return;
|
||||
|
||||
if(value === null || value === setting.fallback) {
|
||||
value = setting.fallback;
|
||||
storage.delete(setting.name);
|
||||
} else
|
||||
storage.set(setting.name, value);
|
||||
|
||||
watchers.call(setting.name, value, setting.name);
|
||||
};
|
||||
|
||||
const pub = {
|
||||
define: (name, type, fallback, immutable, critical, virtual) => {
|
||||
if(typeof name !== 'string')
|
||||
throw 'setting name must be a string';
|
||||
if(typeof type !== 'string' && !Array.isArray(type))
|
||||
throw 'type must be a javascript type or array of valid string values.';
|
||||
if(settings.has(name))
|
||||
throw `setting ${name} has already been defined`;
|
||||
|
||||
settings.set(name, Object.freeze({
|
||||
name: name,
|
||||
type: type === null ? undefined : type,
|
||||
fallback: fallback === undefined ? null : fallback,
|
||||
immutable: immutable === true,
|
||||
critical: critical === true,
|
||||
}));
|
||||
|
||||
if(virtual === true)
|
||||
storage.virtualise(name);
|
||||
|
||||
watchers.define(name);
|
||||
},
|
||||
info: name => getSetting(name),
|
||||
names: () => Array.from(settings.keys()),
|
||||
has: name => {
|
||||
const setting = settings.get(name);
|
||||
return setting !== undefined
|
||||
&& !setting.immutable
|
||||
&& storage.get(setting.name) !== null;
|
||||
},
|
||||
get: name => getValue(getSetting(name)),
|
||||
set: (name, value) => setValue(getSetting(name), value),
|
||||
delete: name => {
|
||||
const setting = getSetting(name);
|
||||
if(!setting.immutable)
|
||||
storage.delete(setting.name)
|
||||
},
|
||||
toggle: name => {
|
||||
const setting = getSetting(name);
|
||||
if(!setting.immutable)
|
||||
setValue(setting, !getValue(setting));
|
||||
},
|
||||
touch: name => {
|
||||
const setting = getSetting(name);
|
||||
watchers.call(setting.name, getValue(setting), setting.name);
|
||||
},
|
||||
clear: (criticalOnly, prefix) => {
|
||||
for(const setting of settings.values())
|
||||
if((prefix === undefined || setting.name.startsWith(prefix)) && (!criticalOnly || setting.critical))
|
||||
storage.delete(setting.name);
|
||||
},
|
||||
watch: (name, handler) => {
|
||||
const setting = getSetting(name);
|
||||
watchers.watch(setting.name, handler, getValue(setting), setting.name);
|
||||
},
|
||||
unwatch: (name, handler) => {
|
||||
watchers.unwatch(getSetting(name).name, handler);
|
||||
},
|
||||
virtualise: name => storage.virtualise(getSetting(name).name),
|
||||
scope: name => new MamiSettingsScoped(pub, name),
|
||||
};
|
||||
|
||||
return pub;
|
||||
};
|
29
src/mami.js/settings/virtual.js
Normal file
29
src/mami.js/settings/virtual.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
const MamiSettingsVirtualStorage = function(storage) {
|
||||
const virtuals = new Map;
|
||||
|
||||
return {
|
||||
virtualise: name => virtuals.set(name, storage.get(name)),
|
||||
get: name => {
|
||||
if(virtuals.has(name))
|
||||
try {
|
||||
return JSON.parse(virtuals.get(name));
|
||||
} catch(ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return storage.get(name);
|
||||
},
|
||||
delete: name => {
|
||||
if(virtuals.has(name))
|
||||
virtuals.set(name, null);
|
||||
else
|
||||
storage.delete(name);
|
||||
},
|
||||
set: (name, value) => {
|
||||
if(virtuals.has(name))
|
||||
virtuals.set(name, value === undefined ? null : JSON.stringify(value));
|
||||
else
|
||||
storage.set(name, value);
|
||||
},
|
||||
};
|
||||
};
|
18
src/mami.js/settings/webstorage.js
Normal file
18
src/mami.js/settings/webstorage.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
const MamiSettingsWebStorage = function(storage, prefix) {
|
||||
if(!(storage instanceof Storage))
|
||||
throw 'storage must be an instance of window.Storage';
|
||||
if(typeof prefix !== 'string')
|
||||
prefix = '';
|
||||
|
||||
return {
|
||||
delete: name => storage.removeItem(prefix + name),
|
||||
set: (name, value) => storage.setItem(prefix + name, value === undefined ? null : JSON.stringify(value)),
|
||||
get: name => {
|
||||
try {
|
||||
return JSON.parse(storage.getItem(prefix + name));
|
||||
} catch(ex) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
|
@ -8,7 +8,6 @@
|
|||
#include users.js
|
||||
#include parsing.js
|
||||
#include servers.js
|
||||
#include settings.js
|
||||
#include txtrigs.js
|
||||
#include websock.js
|
||||
#include ui/emotes.js
|
||||
|
@ -19,12 +18,11 @@
|
|||
#include ui/loading-overlay.jsx
|
||||
#include sound/umisound.js
|
||||
|
||||
if(!Umi) window.Umi = {};
|
||||
if(!Umi.Protocol) Umi.Protocol = {};
|
||||
if(!Umi.Protocol.SockChat) Umi.Protocol.SockChat = {};
|
||||
if(!Umi.Protocol.SockChat.Protocol) Umi.Protocol.SockChat.Protocol = {};
|
||||
|
||||
Umi.Protocol.SockChat.Protocol = function(views) {
|
||||
Umi.Protocol.SockChat.Protocol = function(views, settings) {
|
||||
const pub = {};
|
||||
Umi.Protocol.SockChat.Protocol.Instance = pub;
|
||||
|
||||
|
@ -45,7 +43,7 @@ Umi.Protocol.SockChat.Protocol = function(views) {
|
|||
if(!sock) return;
|
||||
let msg = opcode;
|
||||
if(data) msg += "\t" + data.join("\t");
|
||||
if(Umi.Settings.get('dumpPackets'))
|
||||
if(settings.get('dumpPackets'))
|
||||
console.log(msg);
|
||||
sock.send(msg);
|
||||
};
|
||||
|
@ -147,7 +145,7 @@ Umi.Protocol.SockChat.Protocol = function(views) {
|
|||
};
|
||||
|
||||
const onOpen = function(ev) {
|
||||
if(Umi.Settings.get('dumpPackets'))
|
||||
if(settings.get('dumpPackets'))
|
||||
console.log(ev);
|
||||
|
||||
wasKicked = false;
|
||||
|
@ -176,7 +174,7 @@ Umi.Protocol.SockChat.Protocol = function(views) {
|
|||
};
|
||||
|
||||
const onClose = function(ev) {
|
||||
if(Umi.Settings.get('dumpPackets'))
|
||||
if(settings.get('dumpPackets'))
|
||||
console.log(ev);
|
||||
|
||||
userId = null;
|
||||
|
@ -437,7 +435,7 @@ Umi.Protocol.SockChat.Protocol = function(views) {
|
|||
const onMessage = function(ev) {
|
||||
const data = ev.data.split("\t");
|
||||
|
||||
if(Umi.Settings.get('dumpPackets'))
|
||||
if(settings.get('dumpPackets'))
|
||||
console.log(data);
|
||||
|
||||
switch(data[0]) {
|
||||
|
@ -471,7 +469,7 @@ Umi.Protocol.SockChat.Protocol = function(views) {
|
|||
|
||||
startKeepAlive();
|
||||
|
||||
if(Umi.Settings.get('playSoundOnConnect'))
|
||||
if(settings.get('playSoundOnConnect'))
|
||||
Umi.Sound.Play('join');
|
||||
} else {
|
||||
switch (data[2]) {
|
||||
|
@ -590,7 +588,7 @@ Umi.Protocol.SockChat.Protocol = function(views) {
|
|||
if(muser.getId() === userId)
|
||||
sound = 'outgoing';
|
||||
|
||||
if(Umi.Settings.get('playJokeSounds'))
|
||||
if(settings.get('playJokeSounds'))
|
||||
try {
|
||||
const trigger = mami.getTextTriggers().getTrigger(text);
|
||||
if(trigger.isSoundType()) {
|
||||
|
@ -615,7 +613,7 @@ Umi.Protocol.SockChat.Protocol = function(views) {
|
|||
isAction
|
||||
));
|
||||
|
||||
if(!Umi.Settings.get('onlySoundOnMention') && sound !== '')
|
||||
if(!settings.get('onlySoundOnMention') && sound !== '')
|
||||
Umi.Sound.Play(sound);
|
||||
break;
|
||||
|
||||
|
@ -922,7 +920,7 @@ Umi.Protocol.SockChat.Protocol = function(views) {
|
|||
getLoadingOverlay('spinner', 'Loading...', str);
|
||||
|
||||
UmiServers.getServer(function(server) {
|
||||
if(Umi.Settings.get('dumpPackets'))
|
||||
if(settings.get('dumpPackets'))
|
||||
console.log('Connecting to ' + server);
|
||||
|
||||
sock = new UmiWebSocket(server, function(ev) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#include settings.js
|
||||
#include sound/sndlibrary.js
|
||||
|
||||
const MamiSoundPack = function(name, isReadOnly, title, events) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#include settings.js
|
||||
#include audio/context.js
|
||||
#include sound/sndpacks.js
|
||||
#include sound/seinfeld.js
|
||||
|
@ -9,19 +8,20 @@ Umi.Sound = (function() {
|
|||
if(!sound || sound === 'none' || !mami.hasSound())
|
||||
return;
|
||||
|
||||
const settings = mami.getSettings();
|
||||
const sndPackPlay = mami.getSoundPackPlayer();
|
||||
|
||||
switch(sound) {
|
||||
case 'join':
|
||||
if(!Umi.Settings.get('soundEnableJoin'))
|
||||
if(!settings.get('soundEnableJoin'))
|
||||
return;
|
||||
|
||||
if(Umi.Settings.get('seinfeld')) {
|
||||
if(settings.get('seinfeld')) {
|
||||
mami.playUrlSound(Seinfeld.getRandom());
|
||||
break;
|
||||
}
|
||||
|
||||
switch(Umi.Settings.get('minecraft')) {
|
||||
switch(settings.get('minecraft')) {
|
||||
case 'yes':
|
||||
mami.playLibrarySound('minecraft:door:open');
|
||||
break;
|
||||
|
@ -37,10 +37,10 @@ Umi.Sound = (function() {
|
|||
break;
|
||||
|
||||
case 'leave':
|
||||
if(!Umi.Settings.get('soundEnableLeave'))
|
||||
if(!settings.get('soundEnableLeave'))
|
||||
return;
|
||||
|
||||
switch(Umi.Settings.get('minecraft')) {
|
||||
switch(settings.get('minecraft')) {
|
||||
case 'yes':
|
||||
mami.playLibrarySound('minecraft:door:close');
|
||||
break;
|
||||
|
@ -56,31 +56,31 @@ Umi.Sound = (function() {
|
|||
break;
|
||||
|
||||
case 'error':
|
||||
if(!Umi.Settings.get('soundEnableError'))
|
||||
if(!settings.get('soundEnableError'))
|
||||
return;
|
||||
|
||||
sndPackPlay.playEvent('error');
|
||||
break;
|
||||
|
||||
case 'server':
|
||||
if(!Umi.Settings.get('soundEnableServer'))
|
||||
if(!settings.get('soundEnableServer'))
|
||||
return;
|
||||
|
||||
sndPackPlay.playEvent('server');
|
||||
break;
|
||||
|
||||
case 'unban':
|
||||
if(!Umi.Settings.get('soundEnableServer'))
|
||||
if(!settings.get('soundEnableServer'))
|
||||
return;
|
||||
|
||||
sndPackPlay.playEvent(['unban', 'server']);
|
||||
break;
|
||||
|
||||
case 'incoming':
|
||||
if(!Umi.Settings.get('soundEnableIncoming'))
|
||||
if(!settings.get('soundEnableIncoming'))
|
||||
return;
|
||||
|
||||
if(Umi.Settings.get('windowsLiveMessenger')) {
|
||||
if(settings.get('windowsLiveMessenger')) {
|
||||
mami.playLibrarySound('msn:incoming');
|
||||
} else {
|
||||
sndPackPlay.playEvent('incoming');
|
||||
|
@ -88,7 +88,7 @@ Umi.Sound = (function() {
|
|||
break;
|
||||
|
||||
case 'outgoing':
|
||||
if(!Umi.Settings.get('soundEnableOutgoing'))
|
||||
if(!settings.get('soundEnableOutgoing'))
|
||||
return;
|
||||
|
||||
sndPackPlay.playEvent('outgoing');
|
||||
|
@ -96,21 +96,21 @@ Umi.Sound = (function() {
|
|||
|
||||
case 'private':
|
||||
case 'incoming-priv':
|
||||
if(!Umi.Settings.get('soundEnablePrivate'))
|
||||
if(!settings.get('soundEnablePrivate'))
|
||||
return;
|
||||
|
||||
sndPackPlay.playEvent(['incoming-priv', 'incoming']);
|
||||
break;
|
||||
|
||||
case 'flood':
|
||||
if(!Umi.Settings.get('soundEnableForceLeave'))
|
||||
if(!settings.get('soundEnableForceLeave'))
|
||||
return;
|
||||
|
||||
sndPackPlay.playEvent(['flood', 'kick', 'leave']);
|
||||
break;
|
||||
|
||||
case 'timeout':
|
||||
if(!Umi.Settings.get('soundEnableForceLeave'))
|
||||
if(!settings.get('soundEnableForceLeave'))
|
||||
return;
|
||||
|
||||
sndPackPlay.playEvent(['timeout', 'leave']);
|
||||
|
@ -118,7 +118,7 @@ Umi.Sound = (function() {
|
|||
|
||||
case 'kick':
|
||||
case 'forceLeave':
|
||||
if(!Umi.Settings.get('soundEnableForceLeave'))
|
||||
if(!settings.get('soundEnableForceLeave'))
|
||||
return;
|
||||
|
||||
sndPackPlay.playEvent(['kick', 'leave']);
|
||||
|
|
|
@ -33,7 +33,7 @@ Umi.UI.Emoticons = (function() {
|
|||
});
|
||||
},
|
||||
Parse: function(element, message) {
|
||||
if(!Umi.Settings.get('enableEmoticons'))
|
||||
if(!mami.getSettings().get('enableEmoticons'))
|
||||
return element;
|
||||
|
||||
let inner = element.innerHTML;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include channels.js
|
||||
#include common.js
|
||||
#include settings.js
|
||||
#include user.js
|
||||
#include sound/umisound.js
|
||||
#include ui/channels.js
|
||||
|
@ -33,12 +32,14 @@ Umi.UI.Hooks = (function() {
|
|||
Umi.UI.Channels.Unread(msg.getChannel());
|
||||
Umi.UI.Messages.Add(msg);
|
||||
|
||||
if(!document.hidden && Umi.Settings.get('flashTitle'))
|
||||
const settings = mami.getSettings();
|
||||
|
||||
if(!document.hidden && settings.get('flashTitle'))
|
||||
return;
|
||||
|
||||
let title = ' ' + msg.getUser().getName(),
|
||||
channel = Umi.Channels.Current() || null;
|
||||
if(msg.getUser().isBot() && Umi.Settings.get('showServerMsgInTitle'))
|
||||
if(msg.getUser().isBot() && settings.get('showServerMsgInTitle'))
|
||||
title = ' ' + msg.getText();
|
||||
|
||||
if(channel !== null && channel.getName() !== msg.getChannel())
|
||||
|
@ -46,14 +47,14 @@ Umi.UI.Hooks = (function() {
|
|||
|
||||
Umi.UI.Title.Flash(['[ @]' + title, '[@ ]' + title]);
|
||||
|
||||
if(Umi.Settings.get('enableNotifications') && Umi.User.getCurrentUser() !== null) {
|
||||
const triggers = (Umi.Settings.get('notificationTriggers') || '').toLowerCase().split(' '),
|
||||
if(settings.get('enableNotifications') && Umi.User.getCurrentUser() !== null) {
|
||||
const triggers = (settings.get('notificationTriggers') || '').toLowerCase().split(' '),
|
||||
options = {};
|
||||
|
||||
triggers.push((Umi.User.getCurrentUser() || { getName: function() { return ''; } }).getName().toLowerCase());
|
||||
options.body = 'Click here to see what they said.';
|
||||
|
||||
if(Umi.Settings.get('notificationShowMessage'))
|
||||
if(settings.get('notificationShowMessage'))
|
||||
options.body += "\n" + msg.getText();
|
||||
|
||||
const avatarUrl = futami.get('avatar');
|
||||
|
@ -68,7 +69,7 @@ Umi.UI.Hooks = (function() {
|
|||
|
||||
if(message.toLowerCase().indexOf(' ' + trigger + ' ') >= 0) {
|
||||
new Notification('{0} mentioned you!'.replace('{0}', msg.getUser().getName()), options);
|
||||
if(Umi.Settings.get('onlySoundOnMention'))
|
||||
if(settings.get('onlySoundOnMention'))
|
||||
Umi.Sound.Play('incoming');
|
||||
break;
|
||||
}
|
||||
|
@ -128,7 +129,7 @@ Umi.UI.Hooks = (function() {
|
|||
const elemInput = Umi.UI.Elements.MessageInput, elemParent = elemInput.parentNode;
|
||||
let height = 40;
|
||||
|
||||
if(Umi.Settings.get('expandTextBox') && elemInput.scrollHeight > elemInput.clientHeight) {
|
||||
if(mami.getSettings().get('expandTextBox') && elemInput.scrollHeight > elemInput.clientHeight) {
|
||||
/*const cols = Math.floor(elemInput.clientWidth / 8),
|
||||
rows = Math.floor(elemInput.textLength / cols);
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include channels.js
|
||||
#include common.js
|
||||
#include parsing.js
|
||||
#include settings.js
|
||||
#include url.js
|
||||
#include users.js
|
||||
#include utility.js
|
||||
|
@ -30,6 +29,7 @@ Umi.UI.Messages = (function() {
|
|||
eMeta = null,
|
||||
eUser = null;
|
||||
|
||||
const settings = mami.getSettings();
|
||||
const sender = msg.getUser();
|
||||
let avatarUser = sender,
|
||||
avatarSize = '80';
|
||||
|
@ -61,7 +61,7 @@ Umi.UI.Messages = (function() {
|
|||
+ ':' + msgDateTimeObj.getMinutes().toString().padStart(2, '0')
|
||||
+ ':' + msgDateTimeObj.getSeconds().toString().padStart(2, '0');
|
||||
|
||||
if(sender.isBot() && Umi.Settings.get('fancyInfo')) {
|
||||
if(sender.isBot() && settings.get('fancyInfo')) {
|
||||
const botInfo = msg.getBotInfo();
|
||||
|
||||
if(botInfo) {
|
||||
|
@ -153,7 +153,7 @@ Umi.UI.Messages = (function() {
|
|||
|
||||
const urls = [];
|
||||
|
||||
if(Umi.Settings.get('autoParseUrls')) {
|
||||
if(settings.get('autoParseUrls')) {
|
||||
const textSplit = eText.innerText.split(' ');
|
||||
for(const textPart of textSplit) {
|
||||
const uri = Umi.URI.Parse(textPart);
|
||||
|
@ -177,11 +177,7 @@ Umi.UI.Messages = (function() {
|
|||
}
|
||||
}
|
||||
|
||||
if(Umi.Settings.get('resolveUrls')) {
|
||||
// todo: resolve urls
|
||||
}
|
||||
|
||||
if(Umi.Settings.get('weeaboo')) {
|
||||
if(settings.get('weeaboo')) {
|
||||
eText.appendChild($t(Weeaboo.getTextSuffix(sender)));
|
||||
|
||||
const kaomoji = Weeaboo.getRandomKaomoji(true, msg);
|
||||
|
@ -191,7 +187,7 @@ Umi.UI.Messages = (function() {
|
|||
}
|
||||
}
|
||||
|
||||
if(Umi.Settings.get('weeaboo'))
|
||||
if(settings.get('weeaboo'))
|
||||
eUser.appendChild($t(Weeaboo.getNameSuffix(sender)));
|
||||
}
|
||||
|
||||
|
@ -212,14 +208,14 @@ Umi.UI.Messages = (function() {
|
|||
lastMsgUser = sender.getId();
|
||||
lastMsgChannel = msg.getChannel();
|
||||
|
||||
if(Umi.Settings.get('autoEmbedV1')) {
|
||||
if(settings.get('autoEmbedV1')) {
|
||||
const callEmbedOn = eBase.querySelectorAll('a[onclick^="Umi.Parser.SockChatBBcode.Embed"]');
|
||||
for(const embedElem of callEmbedOn)
|
||||
if(embedElem.dataset.embed !== '1')
|
||||
embedElem.click();
|
||||
}
|
||||
|
||||
if(Umi.Settings.get('autoScroll'))
|
||||
if(settings.get('autoScroll'))
|
||||
Umi.UI.Elements.Messages.scrollTop = Umi.UI.Elements.Messages.scrollHeight;
|
||||
|
||||
if(window.CustomEvent)
|
||||
|
|
|
@ -1,280 +0,0 @@
|
|||
#include settings.js
|
||||
#include utility.js
|
||||
#include ui/menus.js
|
||||
|
||||
Umi.UI.Settings = (function() {
|
||||
let copyright = null;
|
||||
const createCopyright = function() {
|
||||
if(copyright !== null)
|
||||
return;
|
||||
|
||||
copyright = $e({
|
||||
attrs: {
|
||||
className: 'mami-copyright',
|
||||
},
|
||||
child: [
|
||||
'Mami',
|
||||
' © ',
|
||||
{
|
||||
tag: 'a',
|
||||
child: 'flash.moe',
|
||||
attrs: {
|
||||
href: '//flash.moe',
|
||||
target: '_blank',
|
||||
},
|
||||
},
|
||||
{ tag: 'br', },
|
||||
{
|
||||
tag: 'a',
|
||||
child: 'Sock Chat Documentation',
|
||||
attrs: {
|
||||
href: '//railgun.sh/sockchat',
|
||||
target: '_blank',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Umi.UI.Menus.Get('settings').appendChild(copyright);
|
||||
};
|
||||
|
||||
const addCategory = function(category) {
|
||||
const catBody = $e({
|
||||
attrs: {
|
||||
id: Umi.UI.Menus.Get('settings').id + '-category-' + category.id,
|
||||
classList: ['setting__category', 'setting__category--' + category.id],
|
||||
style: { overflow: 'hidden' },
|
||||
},
|
||||
}),
|
||||
catHeader = $e({
|
||||
attrs: {
|
||||
classList: ['setting__category-title', 'setting__category-title--' + category.id],
|
||||
style: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
onclick: function() {
|
||||
if(catBody.dataset.mamiClosed) {
|
||||
delete catBody.dataset.mamiClosed;
|
||||
catBody.style.maxHeight = null;
|
||||
const meow = catBody.clientHeight;
|
||||
catBody.style.maxHeight = '0';
|
||||
setTimeout(function() {
|
||||
catBody.style.maxHeight = meow.toString() + 'px';
|
||||
}, 50);
|
||||
} else {
|
||||
catBody.dataset.mamiClosed = 1;
|
||||
if(!catBody.style.maxHeight) {
|
||||
catBody.style.maxHeight = catBody.clientHeight.toString() + 'px';
|
||||
setTimeout(function() {
|
||||
catBody.style.maxHeight = '0';
|
||||
}, 50);
|
||||
} else catBody.style.maxHeight = '0';
|
||||
}
|
||||
},
|
||||
},
|
||||
child: category.name,
|
||||
});
|
||||
|
||||
$ib(copyright, catHeader);
|
||||
$ib(copyright, catBody);
|
||||
|
||||
if(category.collapse) {
|
||||
catBody.dataset.mamiClosed = 1;
|
||||
catBody.style.maxHeight = '0';
|
||||
}
|
||||
|
||||
if(category.warning)
|
||||
catBody.appendChild($e({
|
||||
attrs: {
|
||||
style: {
|
||||
fontSize: '.9em',
|
||||
lineHeight: '1.4em',
|
||||
margin: '5px',
|
||||
padding: '5px',
|
||||
backgroundColor: 'darkred',
|
||||
border: '2px solid red',
|
||||
borderRadius: '5px',
|
||||
},
|
||||
},
|
||||
child: category.warning,
|
||||
}));
|
||||
};
|
||||
|
||||
const addSetting = function(setting) {
|
||||
if(!setting.category)
|
||||
return;
|
||||
|
||||
let entry = null;
|
||||
const settingsHtml = Umi.UI.Menus.Get('settings'),
|
||||
typeAlias = setting.type === 'url' ? 'text' : setting.type,
|
||||
container = $e({
|
||||
attrs: {
|
||||
'class': 'setting__container setting__container--' + typeAlias,
|
||||
id: settingsHtml.id + '-' + setting.id
|
||||
}
|
||||
});
|
||||
|
||||
switch(setting.type) {
|
||||
case 'select':
|
||||
entry = $e({ tag: 'select', attrs: { 'class': 'setting__input' } });
|
||||
entry.disabled = !setting.mutable;
|
||||
|
||||
let data = {};
|
||||
const dataType = setting.dataType || 'setting';
|
||||
|
||||
switch(dataType) {
|
||||
case 'object':
|
||||
data = setting.data;
|
||||
break;
|
||||
case 'call':
|
||||
data = setting.data();
|
||||
break;
|
||||
}
|
||||
|
||||
for(const _i in data) {
|
||||
const _j = data[_i],
|
||||
_x = $e({ tag: 'option', attrs: { 'class': 'setting__style setting__style--' + setting.id + '-' + _i } });
|
||||
_x.value = _i;
|
||||
_x.appendChild($t(_j));
|
||||
entry.appendChild(_x);
|
||||
}
|
||||
|
||||
Umi.Settings.watch(setting.id, function(v) {
|
||||
const keys = Object.keys(data);
|
||||
for(let i = 0; i < keys.length; ++i) {
|
||||
if(keys[i] === v) {
|
||||
entry.selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
entry.addEventListener('change', function(ev) {
|
||||
Umi.Settings.set(setting.id, entry.value);
|
||||
});
|
||||
|
||||
container.appendChild($e({
|
||||
tag: 'label',
|
||||
attrs: {
|
||||
className: 'setting__label',
|
||||
},
|
||||
child: [
|
||||
{ child: setting.name },
|
||||
entry,
|
||||
],
|
||||
}));
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
entry = $e({ tag: 'input', attrs: { type: 'checkbox', 'class': 'setting__input' } });
|
||||
entry.disabled = !setting.mutable;
|
||||
|
||||
entry.addEventListener('click', function(ev) {
|
||||
if(setting.confirm && !confirm(setting.confirm)) {
|
||||
entry.checked = !entry.checked;
|
||||
return;
|
||||
}
|
||||
|
||||
Umi.Settings.toggle(setting.id);
|
||||
});
|
||||
|
||||
Umi.Settings.watch(setting.id, function(v) {
|
||||
entry.checked = v;
|
||||
});
|
||||
|
||||
container.appendChild($e({
|
||||
tag: 'label',
|
||||
attrs: {
|
||||
className: 'setting__label',
|
||||
},
|
||||
child: [
|
||||
entry,
|
||||
{ child: setting.name },
|
||||
],
|
||||
}));
|
||||
break;
|
||||
|
||||
case 'button': // this really shouldn't probably be here i think
|
||||
entry = $e({ tag: 'input', attrs: { 'class': 'setting__input' } });
|
||||
entry.disabled = !setting.mutable;
|
||||
entry.value = setting.name;
|
||||
entry.type = setting.type;
|
||||
entry.addEventListener('click', setting.click);
|
||||
|
||||
container.appendChild($e({
|
||||
tag: 'label',
|
||||
attrs: {
|
||||
className: 'setting__label',
|
||||
},
|
||||
child: entry,
|
||||
}));
|
||||
break;
|
||||
|
||||
case 'url':
|
||||
case 'text':
|
||||
case 'number':
|
||||
entry = $e({ tag: 'input', attrs: { 'class': 'setting__input' } });
|
||||
entry.disabled = !setting.mutable;
|
||||
entry.type = setting.type;
|
||||
|
||||
Umi.Settings.watch(setting.id, function(v) {
|
||||
if(entry.value !== v)
|
||||
entry.value = v;
|
||||
});
|
||||
entry.addEventListener('keyup', function(ev) {
|
||||
Umi.Settings.set(setting.id, entry.value);
|
||||
});
|
||||
|
||||
container.appendChild($e({
|
||||
tag: 'label',
|
||||
attrs: {
|
||||
className: 'setting__label',
|
||||
},
|
||||
child: [
|
||||
{ child: setting.name },
|
||||
entry,
|
||||
],
|
||||
}));
|
||||
break;
|
||||
|
||||
case 'range':
|
||||
entry = $e({ tag: 'input', attrs: { 'class': 'setting__input' } });
|
||||
entry.disabled = !setting.mutable;
|
||||
entry.type = setting.type;
|
||||
|
||||
Umi.Settings.watch(setting.id, function(v) {
|
||||
entry.value = v;
|
||||
});
|
||||
entry.addEventListener('change', function(ev) {
|
||||
Umi.Settings.set(setting.id, entry.value);
|
||||
});
|
||||
|
||||
container.appendChild($e({
|
||||
tag: 'label',
|
||||
attrs: {
|
||||
className: 'setting__label',
|
||||
},
|
||||
child: [
|
||||
{ child: setting.name },
|
||||
entry,
|
||||
],
|
||||
}));
|
||||
break;
|
||||
}
|
||||
|
||||
$i(settingsHtml.id + '-category-' + setting.category).appendChild(container);
|
||||
};
|
||||
|
||||
return {
|
||||
Init: function() {
|
||||
createCopyright();
|
||||
|
||||
for(const category of UmiSettings.categories)
|
||||
addCategory(category);
|
||||
for(const setting of UmiSettings.settings)
|
||||
addSetting(setting);
|
||||
},
|
||||
Add: addSetting,
|
||||
AddCat: addCategory,
|
||||
};
|
||||
})();
|
515
src/mami.js/ui/settings.jsx
Normal file
515
src/mami.js/ui/settings.jsx
Normal file
|
@ -0,0 +1,515 @@
|
|||
#include common.js
|
||||
#include emotes.js
|
||||
#include utility.js
|
||||
#include settings/backup.js
|
||||
#include ui/emotes.js
|
||||
#include ui/menus.js
|
||||
#include ui/view.js
|
||||
|
||||
Umi.UI.Settings = (function() {
|
||||
const items = [
|
||||
{
|
||||
name: 'interface',
|
||||
title: 'Interface',
|
||||
items: [
|
||||
{
|
||||
name: 'style',
|
||||
title: 'Style',
|
||||
type: 'select',
|
||||
options: () => Umi.UI.View.AccentColours,
|
||||
},
|
||||
{
|
||||
name: 'compactView',
|
||||
title: 'Compact view',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'autoScroll',
|
||||
title: 'Scroll to latest message',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'closeTabConfirm',
|
||||
title: 'Confirm tab close',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'showChannelList',
|
||||
title: 'Show channel list',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'fancyInfo',
|
||||
title: 'Fancy server messages',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'autoCloseUserContext',
|
||||
title: 'Auto-close user menus',
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'text',
|
||||
title: 'Text',
|
||||
items: [
|
||||
{
|
||||
name: 'enableParser',
|
||||
title: 'Parse markup',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'enableEmoticons',
|
||||
title: 'Parse emoticons',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'autoParseUrls',
|
||||
title: 'Auto detect links',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'preventOverflow',
|
||||
title: 'Prevent overflow',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'expandTextBox',
|
||||
title: 'Grow input box',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'eepromAutoInsert',
|
||||
title: 'Auto-insert uploads',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'autoEmbedV1',
|
||||
title: 'Auto-embed media',
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'notification',
|
||||
title: 'Notification',
|
||||
items: [
|
||||
{
|
||||
name: 'flashTitle',
|
||||
title: 'Strobe title on new message',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'showServerMsgInTitle',
|
||||
title: 'Show server message in title',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'enableNotifications',
|
||||
title: 'Show notifications',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'notificationShowMessage',
|
||||
title: 'Show contents of message',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'notificationTriggers',
|
||||
title: 'Triggers',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'sounds',
|
||||
title: 'Sound',
|
||||
items: [
|
||||
{
|
||||
name: 'soundEnable',
|
||||
title: 'Enable sound',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'soundPack',
|
||||
title: 'Sound pack',
|
||||
type: 'select',
|
||||
options: () => {
|
||||
const packs = {};
|
||||
mami.getSoundPacks().forEachPack(function(pack) {
|
||||
packs[pack.getName()] = pack.getTitle();
|
||||
});
|
||||
return packs;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'soundVolume',
|
||||
title: 'Sound volume',
|
||||
type: 'range',
|
||||
},
|
||||
{
|
||||
name: 'soundEnableJoin',
|
||||
title: 'Play join sound',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'soundEnableLeave',
|
||||
title: 'Play leave sound',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'soundEnableError',
|
||||
title: 'Play error sound',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'soundEnableServer',
|
||||
title: 'Play server message sound',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'soundEnableIncoming',
|
||||
title: 'Play receive message sound',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'onlySoundOnMention',
|
||||
title: 'Only play receive sound on mention',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'soundEnableOutgoing',
|
||||
title: 'Play send message sound',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'soundEnablePrivate',
|
||||
title: 'Play private message sound',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'soundEnableForceLeave',
|
||||
title: 'Play kick sound',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'minecraft',
|
||||
title: 'Minecraft',
|
||||
type: 'select',
|
||||
options: () => {
|
||||
return {
|
||||
'no': 'No Minecraft',
|
||||
'yes': 'Yes Minecraft',
|
||||
'old': 'Old Minecraft',
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'playSoundOnConnect',
|
||||
title: 'Play join sound on connect',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'windowsLiveMessenger',
|
||||
title: 'Windows Live Messenger',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'seinfeld',
|
||||
title: 'Seinfeld',
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'misc',
|
||||
title: 'Misc',
|
||||
items: [
|
||||
{
|
||||
name: 'playJokeSounds',
|
||||
title: 'Run joke triggers',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'weeaboo',
|
||||
title: 'Weeaboo',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'motivationalImages',
|
||||
title: 'Make images motivational',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'motivationalVideos',
|
||||
title: 'Make videos motivational',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'osuKeysV2',
|
||||
title: 'osu! keyboard sounds',
|
||||
type: 'select',
|
||||
options: () => {
|
||||
return {
|
||||
'no': 'Off',
|
||||
'yes': 'On',
|
||||
'rng': 'On, random pitch',
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'explosionRadius',
|
||||
title: 'Messages to keep on clear',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
title: 'Reload emoticons',
|
||||
type: 'button',
|
||||
invoke: button => {
|
||||
const emotes = futami.get('emotes');
|
||||
setTimeout(() => {
|
||||
button.disabled = true;
|
||||
|
||||
futami.getJson('emotes', true)
|
||||
.then(emotes => {
|
||||
MamiEmotes.clear();
|
||||
MamiEmotes.loadLegacy(emotes);
|
||||
})
|
||||
.finally(() => {
|
||||
Umi.UI.Emoticons.Init();
|
||||
button.disabled = false;
|
||||
});
|
||||
}, 200);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Reload joke triggers',
|
||||
type: 'button',
|
||||
invoke: button => {
|
||||
button.disabled = true;
|
||||
|
||||
const triggers = mami.getTextTriggers();
|
||||
triggers.clearTriggers();
|
||||
|
||||
if(mami.getSettings().get('playJokeSounds'))
|
||||
futami.getJson('texttriggers', true)
|
||||
.then(trigInfos => triggers.addTriggers(trigInfos))
|
||||
.finally(() => button.disabled = false);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Open compatibility client',
|
||||
type: 'button',
|
||||
invoke: () => {
|
||||
const meow = $e('a', { href: window.AMI_URL, target: '_blank', style: { display: 'none' } });
|
||||
document.body.appendChild(meow);
|
||||
meow.click();
|
||||
$r(meow);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'settings',
|
||||
title: 'Settings',
|
||||
items: [
|
||||
{
|
||||
title: 'Import settings',
|
||||
type: 'button',
|
||||
invoke: () => {
|
||||
if(!confirm('Your current settings will be replaced with the ones in the export. Are you sure you want to continue?'))
|
||||
return;
|
||||
|
||||
(new MamiSettingsBackup(mami.getSettings())).importUpload(document.body);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Export settings',
|
||||
type: 'button',
|
||||
invoke: () => {
|
||||
const user = Umi.User.getCurrentUser();
|
||||
let fileName;
|
||||
if(user !== null)
|
||||
fileName = `${user.getName()}'s settings.mami`;
|
||||
|
||||
(new MamiSettingsBackup(mami.getSettings())).exportDownload(document.body, fileName);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Reset settings',
|
||||
type: 'button',
|
||||
invoke: () => {
|
||||
if(!confirm('This will reset all your settings to their defaults values. Are you sure you want to do this?'))
|
||||
return;
|
||||
|
||||
mami.getSettings().clear();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'debug',
|
||||
title: '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.",
|
||||
items: [
|
||||
{
|
||||
name: 'dumpPackets',
|
||||
title: 'Dump packets to console',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'neverUseWorker',
|
||||
title: 'Never use Worker for connection',
|
||||
type: 'checkbox',
|
||||
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.",
|
||||
},
|
||||
{
|
||||
name: 'forceUseWorker',
|
||||
title: 'Always use Worker for connection',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'marqueeAllNames',
|
||||
title: 'Apply marquee on everyone',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'tmpDisableOldThemeSys',
|
||||
title: 'Disable Old Theme System',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'tmpSkipDomainPopUpThing',
|
||||
title: 'Skip domain pop up thing',
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
}
|
||||
];
|
||||
|
||||
const createCopyright = function() {
|
||||
return <div class="mami-copyright">
|
||||
<a href="//patchii.net/flashii/mami" target="_blank">Mami</a> © <a href="//flash.moe" target="_blank">flash.moe</a><br/>
|
||||
<a href="//railgun.sh/sockchat" target="_blank">Sock Chat documentation</a>
|
||||
</div>;
|
||||
};
|
||||
|
||||
const createSetting = function(display) {
|
||||
const settings = mami.getSettings();
|
||||
let setting;
|
||||
|
||||
if('name' in display)
|
||||
setting = settings.info(display.name);
|
||||
|
||||
let input = display.type === 'select'
|
||||
? <select class="setting__input"/>
|
||||
: <input type={display.type} class="setting__input"/>;
|
||||
|
||||
if(display.disabled === true)
|
||||
input.disabled = true;
|
||||
|
||||
if(display.type === 'select') {
|
||||
const options = display.options();
|
||||
for(const name in options)
|
||||
input.appendChild(<option class={['setting__style', `setting__style--${display.name}-${name}`]} value={name}>
|
||||
{options[name]}
|
||||
</option>);
|
||||
} else if(display.type === 'button') {
|
||||
input.value = display.title;
|
||||
input.addEventListener('click', () => display.invoke(input));
|
||||
}
|
||||
|
||||
if(setting !== undefined) {
|
||||
if(!input.disabled && setting.immutable)
|
||||
input.disabled = true;
|
||||
|
||||
if(display.type === 'checkbox') {
|
||||
settings.watch(setting.name, v => input.checked = v);
|
||||
input.addEventListener('change', () => {
|
||||
if(display.confirm !== undefined && input.checked !== setting.fallback && !confirm(display.confirm)) {
|
||||
input.checked = setting.fallback;
|
||||
return;
|
||||
}
|
||||
|
||||
settings.toggle(setting.name);
|
||||
});
|
||||
} else {
|
||||
settings.watch(setting.name, v => input.value = v);
|
||||
input.addEventListener('change', () => settings.set(setting.name, input.value));
|
||||
}
|
||||
}
|
||||
|
||||
let label = input;
|
||||
if(display.type === 'checkbox') {
|
||||
label = <label class="setting__label">
|
||||
{input}
|
||||
<div>{display.title}</div>
|
||||
</label>;
|
||||
} else if(display.type === 'button') {
|
||||
label = <label class="setting__label">{input}</label>;
|
||||
} else {
|
||||
label = <label class="setting__label">
|
||||
<div>{display.title}</div>
|
||||
{input}
|
||||
</label>;
|
||||
}
|
||||
|
||||
return <div class={['setting__container', `setting__container--${display.type}`]}>{label}</div>;
|
||||
};
|
||||
|
||||
const createCategory = function(category) {
|
||||
const catHeader = <div class={['setting__category-title', `setting__category-title--${category.name}`]} style={{ cursor: 'pointer' }}>{category.title}</div>;
|
||||
const catBody = <div class={['setting__category', `setting__category--${category.name}`]} style={{ overflow: 'hidden' }}/>;
|
||||
|
||||
catHeader.onclick = () => {
|
||||
if(catBody.dataset.mamiClosed) {
|
||||
delete catBody.dataset.mamiClosed;
|
||||
catBody.style.maxHeight = null;
|
||||
const meow = catBody.clientHeight;
|
||||
catBody.style.maxHeight = '0';
|
||||
setTimeout(function() {
|
||||
catBody.style.maxHeight = meow.toString() + 'px';
|
||||
}, 50);
|
||||
} else {
|
||||
catBody.dataset.mamiClosed = 1;
|
||||
if(!catBody.style.maxHeight) {
|
||||
catBody.style.maxHeight = catBody.clientHeight.toString() + 'px';
|
||||
setTimeout(function() {
|
||||
catBody.style.maxHeight = '0';
|
||||
}, 50);
|
||||
} else catBody.style.maxHeight = '0';
|
||||
}
|
||||
};
|
||||
|
||||
if(category.collapse) {
|
||||
catBody.dataset.mamiClosed = 1;
|
||||
catBody.style.maxHeight = '0';
|
||||
}
|
||||
|
||||
if(category.warning)
|
||||
catBody.appendChild(<div style={{ fontSize: '.9em', lineHeight: '1.4em', margin: '5px', padding: '5px', backgroundColor: 'darkred', border: '2px solid red', borderRadius: '5px' }}>{category.warning}</div>);
|
||||
|
||||
if(category.items)
|
||||
for(const item of category.items)
|
||||
catBody.appendChild(createSetting(item));
|
||||
|
||||
return <div>
|
||||
{catHeader}
|
||||
{catBody}
|
||||
</div>;
|
||||
};
|
||||
|
||||
return {
|
||||
Init: function() {
|
||||
const html = Umi.UI.Menus.Get('settings');
|
||||
$rc(html);
|
||||
|
||||
for(const category of items)
|
||||
html.appendChild(createCategory(category));
|
||||
|
||||
html.appendChild(createCopyright());
|
||||
},
|
||||
};
|
||||
})();
|
|
@ -1,6 +1,5 @@
|
|||
#include animate.js
|
||||
#include common.js
|
||||
#include settings.js
|
||||
#include user.js
|
||||
#include utility.js
|
||||
#include ui/menus.js
|
||||
|
@ -24,9 +23,9 @@ Umi.UI.Users = (function() {
|
|||
}
|
||||
|
||||
if(isClosed) {
|
||||
if(Umi.Settings.get('autoCloseUserContext'))
|
||||
if(mami.getSettings().get('autoCloseUserContext'))
|
||||
toggleTimeouts[prefix] = setTimeout(function() {
|
||||
if(Umi.Settings.get('autoCloseUserContext'))
|
||||
if(mami.getSettings().get('autoCloseUserContext'))
|
||||
toggleUser(id);
|
||||
}, 300000);
|
||||
|
||||
|
@ -146,7 +145,7 @@ Umi.UI.Users = (function() {
|
|||
if(isAFK)
|
||||
uName.appendChild($e({ attrs: { 'class': 'user-sidebar-afk' }, child: afkText }));
|
||||
|
||||
if(sbUserName.length > 16 || Umi.Settings.get('marqueeAllNames')) {
|
||||
if(sbUserName.length > 16 || mami.getSettings().get('marqueeAllNames')) {
|
||||
uName.appendChild($e({
|
||||
tag: 'marquee',
|
||||
attrs: {
|
||||
|
@ -196,7 +195,7 @@ Umi.UI.Users = (function() {
|
|||
if(isAFK)
|
||||
uName.appendChild($e({ attrs: { 'class': 'user-sidebar-afk' }, child: afkText }));
|
||||
|
||||
if(sbUserName.length > 16 || Umi.Settings.get('marqueeAllNames')) {
|
||||
if(sbUserName.length > 16 || mami.getSettings().get('marqueeAllNames')) {
|
||||
uName.appendChild($e({
|
||||
tag: 'marquee',
|
||||
attrs: {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#include settings.js
|
||||
#include themes.js
|
||||
#include ui/elems.js
|
||||
|
||||
|
@ -28,8 +27,9 @@ Umi.UI.View = (function() {
|
|||
return {
|
||||
AccentColours: accentColours,
|
||||
AccentReload: function() {
|
||||
const available = Object.keys(accentColours),
|
||||
name = Umi.Settings.get('style'),
|
||||
const settings = mami.getSettings(),
|
||||
available = Object.keys(accentColours),
|
||||
name = settings.get('style'),
|
||||
compact = 'chat--compact',
|
||||
classes = ['umi'];
|
||||
|
||||
|
@ -40,7 +40,7 @@ Umi.UI.View = (function() {
|
|||
// the entire AccentReload function should probably be axed
|
||||
UmiThemeApply(name);
|
||||
|
||||
if(!Umi.Settings.get('tmpDisableOldThemeSys'))
|
||||
if(!settings.get('tmpDisableOldThemeSys'))
|
||||
classes.push('umi--' + name);
|
||||
|
||||
if(Umi.UI.Elements.Chat.className.indexOf('hidden') >= 0)
|
||||
|
@ -48,13 +48,13 @@ Umi.UI.View = (function() {
|
|||
|
||||
Umi.UI.Elements.Chat.className = '';
|
||||
Umi.UI.Elements.Chat.classList.add.apply(Umi.UI.Elements.Chat.classList, classes);
|
||||
if(Umi.Settings.get('compactView')) {
|
||||
if(settings.get('compactView')) {
|
||||
if(Umi.UI.Elements.Chat.className.indexOf(compact) < 0)
|
||||
Umi.UI.Elements.Messages.classList.add(compact);
|
||||
} else
|
||||
Umi.UI.Elements.Messages.classList.remove(compact);
|
||||
|
||||
if(Umi.Settings.get('autoScroll'))
|
||||
if(settings.get('autoScroll'))
|
||||
Umi.UI.Elements.Messages.scrollTop = Umi.UI.Elements.Messages.scrollHeight;
|
||||
},
|
||||
Focus: function() {
|
||||
|
|
67
src/mami.js/watcher.js
Normal file
67
src/mami.js/watcher.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include utility.js
|
||||
|
||||
const MamiWatcher = function() {
|
||||
const handlers = [];
|
||||
|
||||
const watch = (handler, ...args) => {
|
||||
if(typeof handler !== 'function')
|
||||
throw 'handler must be a function';
|
||||
if(handlers.includes(handler))
|
||||
throw 'handler already registered';
|
||||
|
||||
handlers.push(handler);
|
||||
args.push(true);
|
||||
handler(...args);
|
||||
};
|
||||
|
||||
const unwatch = handler => {
|
||||
$ari(handlers, handler);
|
||||
};
|
||||
|
||||
return {
|
||||
watch: watch,
|
||||
unwatch: unwatch,
|
||||
call: (...args) => {
|
||||
args.push(false);
|
||||
|
||||
for(const handler of handlers)
|
||||
handler(...args);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const MamiWatchers = function() {
|
||||
const watchers = new Map;
|
||||
|
||||
const getWatcher = name => {
|
||||
const watcher = watchers.get(name);
|
||||
if(watcher === undefined)
|
||||
throw 'undefined watcher name';
|
||||
return watcher;
|
||||
};
|
||||
|
||||
const watch = (name, handler, ...args) => {
|
||||
getWatcher(name).watch(handler, ...args);
|
||||
};
|
||||
|
||||
const unwatch = (name, handler) => {
|
||||
getWatcher(name).unwatch(handler);
|
||||
};
|
||||
|
||||
return {
|
||||
watch: watch,
|
||||
unwatch: unwatch,
|
||||
define: names => {
|
||||
if(typeof names === 'string')
|
||||
watchers.set(names, new MamiWatcher);
|
||||
else if(Array.isArray(names))
|
||||
for(const name of names)
|
||||
watchers.set(name, new MamiWatcher);
|
||||
else
|
||||
throw 'names must be an array of names or a single name';
|
||||
},
|
||||
call: (name, ...args) => {
|
||||
getWatcher(name).call(...args);
|
||||
},
|
||||
};
|
||||
};
|
|
@ -1,12 +1,10 @@
|
|||
#include settings.js
|
||||
|
||||
const UmiWebSocket = function(server, message, useWorker) {
|
||||
if(typeof useWorker === 'undefined')
|
||||
useWorker = (function() {
|
||||
// Overrides
|
||||
if(Umi.Settings.get('neverUseWorker'))
|
||||
if(mami.getSettings().get('neverUseWorker'))
|
||||
return false;
|
||||
if(Umi.Settings.get('forceUseWorker'))
|
||||
if(mami.getSettings().get('forceUseWorker'))
|
||||
return true;
|
||||
|
||||
// Detect chromosomes
|
||||
|
|
Loading…
Reference in a new issue