Camamba Chat Tweaks

tweaks layout of the chat

目前为 2021-04-01 提交的版本。查看 最新版本

// ==UserScript==
// @name            Camamba Chat Tweaks
// @namespace       dannysaurus.camamba
// @version         0.5.6
// @description     tweaks layout of the chat
// @license         MIT License
//
// @include         https://www.camamba.com/chat/
// @include         https://www.de.camamba.com/chat/
//
// @require         https://greasyfork.org/scripts/423722-camamba-chat-helpers/code/Camamba%20Chat%20Helpers.js?version=916211
//
// @grant           GM.getValue
// @grant           GM.setValue
// ==/UserScript==

/* jslint esnext: true */
/* globals knownUsers, me */

(function() {
    'use strict';
    // --- initial sizes ---
    const SIZES = {
        FONT_EM: {
            userList: 1.2,
            chatBox: 1.8,
        },
        WIDTH_EM: {
            sidebarLeft: 10,
            sidebarRight: 14,
        },
    };

    // --- HTML Selector Helpers ---
    const SELECTORS = {
        ID: {
            // original
            userList: 'userList',
            chatBox: 'chatBox',
            chatWindow: 'chatWindow',
            mediaContainer1 : 'mediaContainer1',
            mediaContainer2 : 'mediaContainer2',
            mediaContainer3 : 'mediaContainer3',
            mediaContainer4 : 'mediaContainer4',
            mediaContainer5 : 'mediaContainer5',
            mediaContainer6 : 'mediaContainer6',
            mediaContainer7 : 'mediaContainer7',
            mediaContainer8 : 'mediaContainer8',

            // script
            cbCamslots: 'cb-camslots',
            spinnerUserlistFont: 'spinner-userlist-font',
            spinnerChatFont: 'spinner-chat-font',
        },
        CLASS: {
            noTextSelect: 'noTextSelect',
            borderBox: 'borderBox',
            camBox: 'camBox'
        }
    };

    const containers = (() => {
        let userList, chatBox, sidebars, camslots;

        return {
            get userList() {
                if (typeof userList === "undefined") {
                    userList = document.getElementById(SELECTORS.ID.userList);
                }
                return userList;
            },

            get chatBox() {
                if (typeof chatBox === "undefined") {
                    chatBox = document.getElementById(SELECTORS.ID.chatBox);
                }
                return chatBox;
            },

            get sidebars() {
                if (typeof sidebars === "undefined") {
                    sidebars = document.getElementById(SELECTORS.ID.chatWindow).querySelectorAll(`.${SELECTORS.CLASS.noTextSelect}`);
                }
                return sidebars;
            },

            get sidebarLeft() {
                return this.sidebars[0];
            },

            get sidebarTop() {
                return this.sidebars[1];
            },

            get sidebarRight() {
                return this.sidebars[2];
            },

            get camslots() {
                 if (typeof camslots === "undefined") {
                   const parentContainers = [
                       SELECTORS.ID.mediaContainer1,
                       SELECTORS.ID.mediaContainer2,
                       SELECTORS.ID.mediaContainer3,
                       SELECTORS.ID.mediaContainer4,
                       SELECTORS.ID.mediaContainer5,
                       SELECTORS.ID.mediaContainer6,
                       SELECTORS.ID.mediaContainer7,
                       SELECTORS.ID.mediaContainer8,
                    ]
                        .map(id => document.getElementById(id))
                        .filter(el => el !== null)
                        .map(el => el.parentNode);

                     camslots = [ ...new Set(parentContainers)];
                }
                return camslots;
            }
        };
    })();

    const layoutPatcher = new class {
        constructor() {
            this.historyCamslotsRemoved = [];
        }

        patchSizes() {
            // this.setWidthOfSidebarLeft(`${SIZES.WIDTH_EM.sidebarLeft}em`);
            this.setWidthOfSidebarRight(`${SIZES.WIDTH_EM.sidebarRight}em`);
            return this;
        }

        setFontSizeOfUserList(fontSize) {
            containers.userList.style.fontSize = fontSize;
            return this;
        }

        setFontSizeOfChat(fontSize) {
            containers.chatBox.style.fontSize = fontSize;
            return this;
        }

        setWidthOfSidebarLeft(width) {
            containers.sidebarLeft.style.width = width;
            return this;
        }

        setWidthOfSidebarRight(width) {
            containers.sidebarLeft.style.width = width;
            return this;
        }

        showCamslots() {
            for (let i = 0; i < this.historyCamslotsRemoved.length; i++) {
                const { parent, index, element } = this.historyCamslotsRemoved.pop();
                parent.insertBefore(element, parent.children[index]);
            }
            return this;
        }

        hideCamslots() {
            for (let element of containers.camslots) {
                const parent = element.parentNode;
                if (parent) {
                    let index = Array.from(parent.children).indexOf(element);
                    parent.removeChild(element);

                    this.historyCamslotsRemoved.push({ parent, index, element });
                }
            }
            return this;
        }
    }();


    const controls = (() => {
        // --- HTML Create Element Helpers ---
        const createInput = ({
            id,
            parentElement = null,
            type = 'text',
            defaultValue = '',
            labelText = null,
            onValueChange = null,
            propertyNameValue = 'value',
            eventNameValueChange = 'input',
        }) => {
            const div = document.createElement('div');

            const input = div.appendChild(document.createElement('input'));
            input.type = type;
            input.id = id;
            input.style.backgroundColor = 'rgba(39,62,77,1)';

            if (labelText) {
                const label = div.appendChild(document.createElement('label'));
                label.htmlFor = id;
                label.appendChild(document.createTextNode(labelText));
            }

            if (onValueChange) {
                let oldValue;

                input.addEventListener(eventNameValueChange, () => {
                    const newValue = input[propertyNameValue];
                    if (oldValue !== newValue) {
                        oldValue = newValue;

                        onValueChange(newValue);
                    }
                });
            }

            if (parentElement) {
                parentElement.appendChild(div);
            }
            return input;
        };

        const createInputPersistent = ({
            id,
            parentElement = null,
            type = 'text',
            defaultValue = '',
            labelText = null,
            onValueChange = null,
            propertyNameValue = 'value',
            eventNameValueChange = 'input',
        }) => {
            const input = createInput({
                parentElement, type, id, defaultValue, labelText, propertyNameValue, eventNameValueChange,
                onValueChange: value => {
                    GM.setValue(id, value);
                    if (onValueChange) {
                        onValueChange(value);
                    }
                }
            });

            input.setValue = value => {
                GM.setValue(id, value);
                input[propertyNameValue] = value;
                onValueChange(value);
            };

            input.updateValue = () => GM.getValue(id, defaultValue).then(value => {
                input[propertyNameValue] = value;
                if (onValueChange) {
                    onValueChange(value);
                }
            });

            return input;
        };

        const createCheckbox = ({
            id,
            parentElement = null,
            initialChecked = false,
            labelText = null,
            onValueChange = null,
        }) => {
            const checkbox = createInputPersistent({
                parentElement, id, labelText, onValueChange,
                defaultValue: !!initialChecked,
                type: 'checkbox',
                propertyNameValue: 'checked',
                eventNameValueChange: 'click',
            });
            return checkbox;
        };

        const createSpinner = ({
            id, min, max, step,
            parentElement = null,
            defaultValue = 0,
            labelText = null,
            onValueChange = null,
        }) => {
            const spinner = createInputPersistent({
                parentElement, id, defaultValue, labelText, onValueChange,
                type: 'number',
            });
            spinner.min = min;
            spinner.max = max;
            spinner.step = step;

            const buttonDec = spinner.parentNode.insertBefore(document.createElement('button'), spinner);
            buttonDec.type = 'button';
            buttonDec.innerHTML = '-';
            buttonDec.addEventListener('click', () => {
                spinner.stepDown();
                spinner.setValue(spinner.value);
            });

            const buttonInc = spinner.parentNode.insertBefore(document.createElement('button'), spinner.nextSibling);
            buttonInc.type = 'button';
            buttonInc.innerHTML = '+';
            buttonInc.addEventListener('click', () => {
                spinner.stepUp();
                spinner.setValue(spinner.value);
            });

            return spinner;
        };

        const sidebarLeftCenter = containers.sidebarLeft.children[1];
        sidebarLeftCenter.innerHTML = "";
        const container = sidebarLeftCenter.appendChild(document.createElement('div'));

        // checkbox camslots on/off
        const cbCamslots = createCheckbox({
            parentElement: container,
            id: SELECTORS.ID.cbCamslots,
            initialChecked: true,
            labelText: 'camslots',
            onValueChange: value => {
                if (value) {
                    layoutPatcher.showCamslots();
                } else {
                    layoutPatcher.hideCamslots();
                }
            },
        });

        // spinner userlist font
        const spinnerUserlistFont = createSpinner({
            parentElement: container,
            id: SELECTORS.ID.spinnerUserlistFont,
            defaultValue: SIZES.FONT_EM.userList,
            min: 1.0,
            max: 2.2,
            step: 0.1,
            labelText: 'users',
            onValueChange: value => {
                const fontSize = `${value}em`;
                layoutPatcher.setFontSizeOfUserList(fontSize);
            },
        });

        // spinner chat font
        const spinnerChatFont = createSpinner({
            parentElement: container,
            id: SELECTORS.ID.spinnerChatFont,
            defaultValue: SIZES.FONT_EM.chatBox,
            min: 1.0,
            max: 2.5,
            step: 0.1,
            labelText: 'chat',
            onValueChange: value => {
                const fontSize = `${value}em`;
                layoutPatcher.setFontSizeOfChat(fontSize);
            },
        });

        const buttonKickFromCam = container.appendChild(document.createElement('button'));
        buttonKickFromCam.type = 'button';
        buttonKickFromCam.innerHTML = 'Kick from cam';
        buttonKickFromCam.addEventListener('click', () => {
            knownUsers.bySelected().stopViewing();
        });

        if (me.admin) {
            const buttonRepeatBan = container.appendChild(document.createElement('button'));
            buttonRepeatBan.type = 'button';
            buttonRepeatBan.innerHTML = 'repeat last ban';
            buttonRepeatBan.addEventListener('click', () => {
                if (lastBanData && lastBanData.userId) {
                    let { text, time, isPerma } = lastBanData;

                    knownUsers.byId(lastBanData.userId).ban(text, time, { isPerma });
                }
            });
        }

        return {
            cbCamslots,
            spinnerUserlistFont,
            spinnerChatFont,
        };
    })();

 
    const patchObject = ({ getExpected, doPatch, confirmAvailable = null, timeOutRetryMillis = 200, maxPeriodTryMillis = 5000 }) => {
        const expected = getExpected();
        const isAvailable = confirmAvailable ? confirmAvailable(expected) : !!expected;
        
        if (!isAvailable) {
            if (timeOutRetryMillis <= maxPeriodTryMillis) {
 
                setTimeout(() => {
                    maxPeriodTryMillis -= timeOutRetryMillis;
                    patchObject({ getExpected, doPatch, confirmAvailable, timeOutRetryMillis, maxPeriodTryMillis });
 
                }, timeOutRetryMillis);
            }
 
            return;
        }
 
        doPatch(expected);
    };

    /* eslint-disable no-undef */
    patchObject({
        getExpected: () => initSettings,

        doPatch: (original) => {
            initSettings = () => {
                original();

                // Breite von Userliste anpassen
                layoutPatcher.patchSizes();

                // weiterere Einstellungen überschreiben, bzw übernehmen
                for (let control of [ controls.cbCamslots, controls.spinnerUserlistFont, controls.spinnerChatFont ]) {
                    control.updateValue();
                }
            };
        },
    });

    let lastBanData = { userId : 0, text: '', time: 0, isPerma: false };

    patchObject({
        getExpected: () => adminExec,

        doPatch: (original) => {
            original();

            if (currentAdminAction == "ban") {
                let userId, text, time, isPerma;

                text = byId('adminMessageInput').value;
                if (!text || text.length <= 3 && byId('adminMessageSelect').selectedIndex) {
                    text = adminMessages[currentAdminAction][byId('adminMessageSelect').value];
                }

                userId = currentAdminTarget;
                time = parseInt(byId('banTime').value);
                isPerma = byId('permaBan') && byId('permaBan').checked;

                if (userId && text > 3 && time) {

                     lastBanData = { userId, text, time, isPerma };
                }
            }
        }
    });
    /* eslint-enable no-undef */
})();