User:HearthRaven/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:EmoticonsWindow/code.js',        'u:dev:EscapeEmoticons.js',        'u:dev:ExtendedPrivateMessaging/code.js'        'u:dev:FaviconNotifier/code.js',        'u:dev:IsTyping.js',        'u:dev:MessageBlocker/code.js',        'u:dev:PrivateHistory/code.js',        'u:dev:PrivateMessageAlert/code.js',        'u:dev:Tabinsert.js',         // WTF WHY ISNT IT TabInsert.js???    ] }); /* Pings */ window.PING_REGEX = /(Hearth|Raven|\bHearth-san\b|\bHealth\b|\bHearthRaven|Heart)\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', $.debounce(0, function(e) {            var user = mainRoom.model.users.findByName(e.name);            $('#UserStatsMenu img').attr('src', function(_, src) { if (!user) return src; 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 });       }));    });    /* 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);    }); });