// ==UserScript==
// @name cytube user highlight
// @namespace http://tampermonkey.net/
// @version 1.0
// @description highlight users in chat
// @author unrealfag
// @match https://cytu.be/r/*
// @grant none
// ==/UserScript==
'use strict';
const defaultColor = '#2c902c99';
const toggleOnClass = 'glyphicon glyphicon-heart';
const toggleOffClass = 'glyphicon glyphicon-heart-empty';
const storageId = 'unrealfags-usercolor-menu';
const openMessage = `<i class="glyphicon glyphicon-list"></i> Highlights `;
const closeMessage = `<i class="glyphicon glyphicon-floppy-disk"></i> Save! `;
const storage = (name, value) => {
if (typeof value === 'undefined') {
return localStorage[name] ? JSON.parse(localStorage[name]) : undefined;
}
localStorage[name] = JSON.stringify(value);
};
const createElement = (tag, inner, style) => {
const el = document.createElement(tag);
if (style) Object.keys(style).forEach(key => { el.style[key] = style[key]; });
if (typeof inner === 'object' && inner !== null) el.appendChild(inner);
else el.innerHTML = inner || '';
return el;
};
const Menu = createElement('ul', null, {
display: 'none',
'white-space': 'nowrap',
padding: '5px',
});
const MenuHelpers = {
MenuHandlers: [],
getNameListInput: (nameList) => {
const NamesInput = createElement('input', null, {
height: '11px',
display: 'inline-block',
width: 'initial',
});
NamesInput.type = 'text';
NamesInput.className = 'form-control';
NamesInput.value = nameList;
NamesInput.placeholder = 'user1 user2 userName...';
return NamesInput;
},
getColorInput: (color) => {
const ColorInput = createElement('input', null, {
height: '11px',
display: 'inline-block',
color: 'white',
width: '100px',
'font-family': 'monospace',
margin: '0px 5px',
});
ColorInput.addEventListener('input', MenuHelpers.onColorInputChanged);
ColorInput.className = 'form-control';
ColorInput.value = color || defaultColor;
ColorInput.style.background = color || defaultColor;
return ColorInput;
},
onColorInputChanged: (target) => {
const color = target.target.value.match(/#[a-fA-F0-9]{8}/g);
if (color) {
target.target.style.background = color;
target.target.style['border-color'] = 'initial';
} else {
target.target.style.background = 'transparent';
target.target.style['border-color'] = 'red';
}
},
getToggleButton: (isOn) => {
const ToggleButton = document.createElement('i');
ToggleButton.addEventListener('click', MenuHelpers.toggleRow);
ToggleButton.className = isOn ? toggleOnClass : toggleOffClass;
return ToggleButton;
},
toggleRow: (target) => {
const isOn = target.target.className === toggleOnClass;
target.target.className = isOn ? toggleOffClass : toggleOnClass;
},
getMenuRow: (isOn, color, nameList) => {
const ToggleButton = MenuHelpers.getToggleButton(isOn);
const ColorInput = MenuHelpers.getColorInput(color);
const NamesInput = MenuHelpers.getNameListInput(nameList);
const MenuRow = createElement('div');
MenuRow.appendChild(ToggleButton);
MenuRow.appendChild(ColorInput);
MenuRow.appendChild(NamesInput);
const MenuItem = createElement('li', MenuRow);
return {
element: MenuItem,
isEnabled: () => ToggleButton.className === toggleOnClass,
getColor: () => ColorInput.value || defaultColor,
getNameList: () => NamesInput.value || '',
};
},
openMenu: () => {
const menuItems = storage(storageId) || [];
menuItems.push({
isOn: true,
color: defaultColor,
names: '',
});
menuItems.forEach(item => {
const itemData = MenuHelpers.getMenuRow(item.isOn, item.color, item.names);
MenuHelpers.MenuHandlers.push(itemData);
Menu.appendChild(itemData.element);
});
MenuButton.innerHTML = closeMessage;
Menu.style.display = 'block';
},
closeMenu: () => {
const listData = MenuHelpers.MenuHandlers.map(menuData => {
const isOn = menuData.isEnabled();
const color = menuData.getColor();
const names = menuData.getNameList();
if (names && color) {
return { isOn, color, names };
}
return null;
}).filter(i => i !== null);
storage(storageId, listData);
MenuHelpers.MenuHandlers.length = 0;
while (Menu.firstChild) { Menu.removeChild(Menu.firstChild); }
MenuButton.innerHTML = openMessage;
Menu.style.display = 'none';
applyColors();
}
};
Menu.className = 'dropdown-menu';
const MenuButton = createElement('button', openMessage, {
padding: '2px 5px 2px 5px',
'font-size': '11px',
height: '20px',
'vertical-align': 'top',
});
MenuButton.addEventListener('click', () => {
const isClosed = Menu.style.display === 'none';
if (isClosed) MenuHelpers.openMenu();
else MenuHelpers.closeMenu();
});
MenuButton.className = 'btn btn-primary';
const Container = createElement('div');
Container.className = 'dropdown pull-right';
Container.appendChild(MenuButton);
Container.appendChild(Menu);
const applyColors = () => {
if (!head) { return; }
const styleClass = `${storageId}_style`;
var oldStyles = head.getElementsByClassName(styleClass);
while (oldStyles[0]) {
head.removeChild(oldStyles[0]);
}
const userColorData = storage(storageId);
userColorData.forEach(colorData => {
if (colorData.isOn) {
const names = colorData.names.split(' ');
const style = createElement('style', `\
${names.map(name => `.chat-msg-${name}>:not(:last-of-type)`).join(', ')} {\
display: inline-block;
vertical-align: middle;
line-height: 20px;
background-color: ${colorData.color};\
}`);
style.type = 'text/css';
style.className = styleClass;
head.appendChild(style);
}
});
};
const ChatHeader = document.getElementById('chatheader');
ChatHeader.appendChild(Container);
const head = document.getElementsByTagName('head')[0];
applyColors();