User:Dorumin/chat.js

/* Short header */ mw.messages.set('chat-private-headline', '$1');

/* Settings */ window.IsTyping = { ignore: ['Mendes2'] };

window.PrivateMessageAlert = { beepSound: 'https://soundbible.com/grab.php?id=1645&type=mp3', message: '$1 sent you a message!' };

importArticles({   type: 'script',    articles: [        'u:dev:IsTyping.js',        'u:dev:Tabinsert.js',         // WTF WHY ISNT IT TabInsert.js???        'u:dev:EscapeEmoticons.js',        'u:dev:PrivateHistory/code.js',        'u:dev:FaviconNotifier/code.js',        'u:dev:EmoticonsWindow/code.js',        'u:dev:PrivateMessageAlert/code.js',        'u:dev:ExtendedPrivateMessaging/code.js'    ] });

/* Pings */ window.PING_REGEX = /(doru|dorium|\bdor\b|\bdory\b|\bdrumi|betterthancube)\w*/i;

mw.hook('dev.chat.render').add(function {   /* AFK Button */    var button = new dev.chat.Button({ name: 'AFKButton', attr: { text: 'AFK', click: toggleAway }   }).el;    mainRoom.model.chats.bind('afteradd', function(model) { var userMain = mainRoom.model.users.findByName(wgUserName); if (model.attributes.name == wgUserName && userMain && userMain.attributes.statusState == 'away') { setBack; }   });    var sb = mainRoom.setBack;    function toggleAway {        var current = mainRoom.model.users.findByName(wgUserName).attributes.statusState; // No, mainRoom.userMain doesn't work        if (current == 'away') {            setBack;        } else {            setAway;        }    }    function setAway {        mainRoom.setAway;        clearTimeout(mainRoom.activityTimeout);        mainRoom.setBack = $.noop;        button.textContent = 'Back';    }    function setBack {        mainRoom.setBack;        mainRoom.activityTimer = setTimeout($.proxy(mainRoom.setAway, mainRoom), 5 * 60 * 1000);        mainRoom.setBack = sb;        mainRoom.setBack;        button.textContent = 'AFK';    }    mainRoom.socket.on("updateUser", function(msg) { var data = JSON.parse(msg.data).attrs, type = data.statusMessage, status = data.statusState, user = data.name; if (user == wgUserName && status != 'away') { mainRoom.setBack = sb; button.textContent = 'AFK'; }   });    /* Pings */    var avatars = {};

if (Notification.permission === 'default') { Notification.requestPermission; }

function parseInlineAlertRegex(elem) { // We could probably make it prettier, but as long as it's abstracted inside this function we probably won't need to deal with it again var msg = mw.messages.get(elem) .replace(/[-[\]{}*+?.,\\^$|#\s]/g, '\\$&') //escape regex .replace(/(\\\$\d)(?!.*\1)/g, '(.+?)') //place capturing groups .replace(/\\\$\d/g, '.+?'); //replace remaining $Ns

return new RegExp(msg); }

function notify(title, text, icon) {

if (Notification.permission !== 'granted' || document.hasFocus && mainRoom.active) { return; }       var notification = new Notification(title, {            body: text,            icon: icon        }); notification.onclick = function { window.focus; notification.close; };   }

function formatAvi(avatar) { // Aaanyways, this regex matches only the last occurrence of 28 in the string // (?!) means a negative lookahead, so it looks ahead of the match for any occurrences of (any character) times 0 to infinity // immediately followed by 28 return avatar.replace(/28(?!.*28)/, '150'); }

function highlight($elem, match) { var $message = $elem, html = $message.html, lastIndex = match.index + match[0].length, text = html.slice(match.index, lastIndex), span = $(' ', {               id: 'ping',                text: text,                css: {                    color: 'red'                }            }).get(0);

html = html.slice(0, match.index) + span.outerHTML + html.slice(lastIndex); $message.html(html); }

function saveAvatar(user) { avatars[user.attributes.name] = formatAvi(user.attributes.avatarSrc); }

var kick_regex = parseInlineAlertRegex('chat-user-was-kicked'), ban_regex = parseInlineAlertRegex('chat-user-was-banned');

function afterChat(chat) { var $elem = $('#entry-' + chat.cid).find('.message'), html = $elem.html, text = chat.attributes.text, name = chat.attributes.name, icon = formatAvi(chat.attributes.avatarSrc), inline = chat.attributes.isInlineAlert || false, match;

if (name !== mw.config.get('wgUserName') && !inline) { if (match = html.match(PING_REGEX)) { notify(name + ' pinged you!', text, icon); highlight($elem, match); }       } else if (inline) { if (match = text.match(kick_regex)) { notify(match[1] + ' was kicked!', '', avatars[match[1]]); } else if (match = text.match(ban_regex)) { notify(match[1] + ' was banned!', '', avatars[match[1]]); }       }    }    mainRoom.model.chats.bind('afteradd', afterChat); mainRoom.model.users.models.forEach(saveAvatar); mainRoom.model.users.bind('add', saveAvatar); ['main', 'private'].forEach(function(scope) {       mainRoom.viewUsers.bind(scope + 'ListClick', function(e) { var user = mainRoom.model.users.findByName(e.name); $('#UserStatsMenu img').attr('src', function(_, src) {               if (!user) return src.replace('/scale-to-width-down/28', '/scale-to-width-down/256');                return user.attributes.avatarSrc.replace('/scale-to-width-down/28', '/scale-to-width-down/256');            }); /* Fixed chat header PMs */ var menu = document.getElementById('UserStatsMenu'); if (scope == 'private') { menu.classList.add('private'); } else { menu.classList.remove('private'); }			if (!user) return; // wtf $('#UserStatsMenu .info').append(' '); user.attributes.groups.forEach(function(group) {				$('#UserStatsMenu .groups').append($(' ', {					'class': 'group',					'data-group': group               }));            }); $('#UserStatsMenu').offset({               top: $('#UserStatsMenu').offset.top - $('#UserStatsMenu .groups').height - 4            }); });   });    mainRoom.viewUsers.bind('privateListClick', function(e) {        var menu = document.getElementById('UserStatsMenu');        if (menu.style.display == 'none') return;        var right = $(window).width - (e.target.offset.left + e.target.outerWidth * .5) - menu.clientWidth / 2;        menu.style.top = '56px';        menu.style.right = right + 'px';    }); /* FIXME: Dirty hack */ $(window).click($.debounce(0, function { var menu = document.getElementById('UserStatsMenu'); if (menu.style.display == 'none' && menu.classList.contains('private')) { menu.classList.remove('private'); }   }));    function get_room { return mainRoom.chats.privates[mainRoom.activeRoom] || mainRoom; }   function bind_messages(fn) { mainRoom.model.chats.bind('afteradd', fn); mainRoom.model.privateUsers.bind('add', function(u) {           mainRoom.chats.privates[u.attributes.roomId].model.chats.bind('afteradd', fn);        }); }   /* Scroll fix */ bind_messages(function {       var vd = get_room.viewDiscussion,        div = vd.chatDiv.get(0);        if (div.scrollHeight - div.scrollTop - div.clientHeight < 200) {            vd.scrollToBottom;        }    }); /* Add seconds */ bind_messages(function(model) {       if (model.attributes.isInlineAlert) return;        var seconds = document.createElement('span');        seconds.className = 'seconds';        seconds.textContent = ':' + new Date(model.attributes.timeStamp).getSeconds.toString.padStart(2, '0');        document            .getElementById('entry-' + model.cid)            .querySelector('.time')                .appendChild(seconds);    }); });