// ==UserScript==
// @namespace http://openuserjs.org/users/Francute
// @name Kongregate - AutoCompletar nombres
// @description En el chat de la página, simplemente escribir parte de un nombre de usuario y autocompletar con Tab. Shif + Tab para volver a la recomendación anterior.
// @copyright Francute
// @license MIT
// @version 0.15
// @include http://www.kongregate.com/games/*/*
// ==/UserScript==
// ==OpenUserJS==
// @author Francute
// ==/OpenUserJS==
(function() {
'use strict';
class Chat {
constructor(selectorContenedorDelChat, selectorContenedorDeNodosUsuarios, selectorTextArea) {
this.pestanasChat = [];
this.nodoContenedorDeChats = document.querySelector(selectorContenedorDelChat);
this.selectorContenedorDeUsuarios = selectorContenedorDeNodosUsuarios;
this.selectorEntrada = selectorTextArea;
this.cargarPestanas();
}
cargarPestanas() {
let self = this;
new MutationObserver(function(mutaciones) {
mutaciones.forEach(function(mutacion) {
if (seAgregaronChats(mutacion)) {
mutacion.addedNodes.forEach(nodoChatNuevo => this.cargarNuevaPestana(nodoChatNuevo));
} else if (seRemovieronChats(mutacion)) {
mutacion.removedNodes.forEach(nodoChatEliminado => this.eliminarPestana(nodoChatEliminado));
}
}, self);
}).observe(this.nodoContenedorDeChats, {
childList: true
});
function seAgregaronChats(mutacion) {
return mutacion.addedNodes.length > 0;
}
function seRemovieronChats(mutacion) {
return mutacion.removedNodes.length > 0;
}
}
cargarNuevaPestana(nodoChat) {
this.pestanasChat.push(new ComponentePestana(nodoChat, this.selectorContenedorDeUsuarios, this.selectorEntrada));
}
eliminarPestana(nodoChat) {
let indicePestanaEncontrada;
let seEncontroPestana = this.pestanasChat.some(function(pestana, indice) {
indicePestanaEncontrada = indice;
return pestana.nodoChat == nodoChat;
});
if (seEncontroPestana) {
this.pestanasChat.splice(indicePestanaEncontrada, 1);
}
}
cargarPestanasChats(selectorContenedorDeNodosUsuarios, selectorTextArea) {
this.pestanasChat = [];
Array.from(this.nodoContenedorDeChats.children)
.forEach(nodoChat => this.pestanasChat.push(new ComponentePestana(nodoChat, selectorContenedorDeNodosUsuarios, selectorTextArea)));
}
}
class ComponentePestana {
constructor(nodo, selectorContenedorDeNodosUsuarios, selectorTextArea) {
this.nodoChat = nodo;
this.usuarios = new ComponenteUsuarios(nodo.querySelector(selectorContenedorDeNodosUsuarios));
this.nodoEntrada = new ComponenteEntrada(this.usuarios, nodo.querySelector(selectorTextArea));
}
}
class ComponenteUsuarios {
constructor(nodoUsuarios) {
this.posiblesUsuarios = [];
this.posicionUsuarioActual = -1;
this.nodoUsuarios = nodoUsuarios;
this.actualizarListaDeUsuarios();
}
actualizarListaDeUsuarios() {
this.usuarios = Array.from(this.nodoUsuarios.querySelectorAll("span > span.username"))
.map(nodo => nodo.innerHTML)
.sort((a, b) => (a.toLowerCase() > b.toLowerCase()) ? 1 : -1);
}
buscarPosiblesUsuarios(parteDelNombre) {
this.actualizarListaDeUsuarios();
this.posiblesUsuarios =
this.usuarios.filter(usuario => usuario.search(new RegExp(parteDelNombre + "\\w*$", "i")) === 0)
.sort((a, b) => (a.toLowerCase() > b.toLowerCase()) ? 1 : -1);
this.posicionUsuarioActual = -1;
return this.posiblesUsuarios;
}
esUnUsuarioYaDevuelto(nombreUsuario) {
return (this.posiblesUsuarios.indexOf(nombreUsuario) != -1);
}
existenUsuariosQueConcuerdenCon(parteDelNombreDeUsuario) {
return this.buscarPosiblesUsuarios(parteDelNombreDeUsuario).length > 0;
}
siguiente() {
return this.posiblesUsuarios[(++this.posicionUsuarioActual) % this.posiblesUsuarios.length];
}
anterior() {
if (this.posicionUsuarioActual <= 0) {
this.posicionUsuarioActual = this.posiblesUsuarios.length;
}
return this.posiblesUsuarios[--this.posicionUsuarioActual % this.posiblesUsuarios.length];
}
finalizarBusqueda() {
if (this.posiblesUsuarios.length > 0) this.posiblesUsuarios = [];
}
}
class ComponenteEntrada {
constructor(usuarios, nodoEntrada) {
this.usuarios = usuarios;
this.entrada = nodoEntrada;
this.separador = " ";
this.activarEventos();
}
activarEventos() {
/* Bugs pronosticados:
Escenario 1:
Busco por un usuario cuyo nombre comience con "la", y encuentra a "ladillaFeliz" y "LaEraWakeista".
Se conecta un usuario "laxvio".
Si continúo haciendo Tab sin buscar de nuevo, no podré encontrar a "laxvio".
-¿Posible solución? Dejar Kong.
Escenario 2:
Busco un usuario que justo se desconectó, no lo voy a poder encontrar. ¿Será un bug realmente?
Escenario 3:
Busco por un usuario cuyo nombre comience con "la", y encuentra a "ladillaFeliz" y "laxvio".
Selecciono alguno de los 2 y sigo escribiendo en el chat con normalidad.
Escribo luego "laxvio" y al tocar Tab debería cambiarla por "ladillaFeliz".
- Se puede resolver fácil actualizando cuando toque algo diferente de Shift o Tab, pero...
Doy posibilidades a bugs que se desprenden de este?
*/
this.entrada.addEventListener("keydown", function(evento) {
if (evento.code === 'Tab') {
evento.preventDefault();
if (this.usuarios.esUnUsuarioYaDevuelto(this.traerUltimaPalabra())) {
this.borrarUltimaPalabra();
if (seEstaPresionandoLaTeclaShift()) {
this.agregarAlMensaje(this.usuarios.anterior());
} else {
this.agregarAlMensaje(this.usuarios.siguiente());
}
} else {
if (this.usuarios.existenUsuariosQueConcuerdenCon(this.traerUltimaPalabra())) {
this.borrarUltimaPalabra();
this.agregarAlMensaje(this.usuarios.siguiente());
}
}
}
function seEstaPresionandoLaTeclaShift() {
return evento.shiftKey;
}
}.bind(this));
}
traerPalabrasActualmenteIngresadas() {
this.modeloTextArea = this.entrada.value.trim().split(this.separador);
return this.modeloTextArea;
}
traerUltimaPalabra() {
let palabrasIngresadas = this.traerPalabrasActualmenteIngresadas();
return palabrasIngresadas[palabrasIngresadas.length - 1];
}
borrarUltimaPalabra() {
this.modeloTextArea.pop();
}
agregarAlMensaje(texto) {
this.entrada.value = (
this.modeloTextArea.join(this.separador) +
this.separador +
texto
).trim();
}
}
document.observe('holodeck:ready', function() {
new Chat("#chat_rooms_container", ".users_in_room", ".chat_input");
});
})();