Skip to content

Instantly share code, notes, and snippets.

@qgustavor
Last active September 3, 2015 15:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qgustavor/fc66dc1aa54c2c3a9970 to your computer and use it in GitHub Desktop.
Save qgustavor/fc66dc1aa54c2c3a9970 to your computer and use it in GitHub Desktop.
Sugestões de Mudança para a Shoutbox
// ==UserScript==
// @name Sugestões de Mudança para a Shoutbox
// @version 1.5.17
// @grant none
// @include data:text/plain,sistema_implementado,_userscript_desativado
// @updateURL https://rawgit.com/qgustavor/fc66dc1aa54c2c3a9970/raw/shoutbox.js
// @downloadURL https://rawgit.com/qgustavor/fc66dc1aa54c2c3a9970/raw/shoutbox.js
// ==/UserScript==
// Coisas a fazer:
// - Reescrever código usando melhor as IDs
// - Reescrever evitando leituras no documento.
;(function(){
// Coloque `true` para voltar a forma antiga de apagar mensagens:
var APAGAR_USANDO_CONFIRM = false;
// Adicione esse código CSS em um arquivo
// O código da animação veio do css-spinners.com
// então processado no AutoPrefixer e finalmente Devilo.us
$('<style>', {
html: '@-webkit-keyframes spinner-loader{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-loader{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinning{-webkit-animation:spinner-loader 1500ms infinite linear;animation:spinner-loader 1500ms infinite linear}.message-alert{position:absolute;text-align:center;background:#bbdefb ;left:0;top:0;right:0;padding:1em}.shoutbox_contain{height:245px/* valor padrão para navegadores antigos */;height:calc(100vh - 45px)}@-webkit-keyframes new-message{0%{background:#bbdefb}100%{background:#f4f4f4!important}}@keyframes new-message{0%{background:#bbdefb}100%{background:#f4f4f4!important}}.highlight{-webkit-animation:new-message 3s ease-in 1;animation:new-message 3s ease-in 1}.history-link-row{text-align:center;background:#f4f4f4}.history-link-row td{padding:1em}.shoutboxform_input{margin-left:10px;margin-right:150px}.shoutboxform_input input{width:100%}.shoutboxform_links{float:right;width:100px;text-align:center;padding:6px 0}.shoutboxform_links a{margin:0 5px}td:only-child a{word-break:break-all}form{margin-top:5px}'
}).appendTo('head');
// Substitua o formulário baseado em tabelas com um baseado em classes:
$('form').html('<div class="shoutboxform_links"><a href="#" class="emoticon-link"><i title="Emoticons" class="icon-heart"></i></small></a><a href="?"><i title="Atualizar" class="icon-refresh"></i></a><a href="shoutbox.php?history=1" target="_top"><i title="Histórico" class="icon-book"></i></a></div><div class="shoutboxform_input input-append"><input class="shoutbox_msgbox" placeholder="→ CAMPO DE CHAT ←" type="text" name="message"><button class="btn" type="submit" name="submit">Enviar</button></div>');
var DEFAULT_TITLE = top.document.title;
var container = $('.shoutbox_contain');
var containerBody = $('.shoutbox_contain > table > tbody');
var refreshIcon = $('.icon-refresh').click(loadAjax);
var unreadMessages = $();
var timeBetweenReloads;
var timer;
// Retorna a função de emoticons:
$('.emoticon-link').on('click', function(evt) {
evt.preventDefault();
PopMoreSmiles('shoutboxform', 'message');
});
// Adiciona um link para ver o histórico de mensagens no final
// Se for adicionado no HTML é melhor
containerBody.append('<tr><td></td><td></td><td></td><tr>'); // As <tr> são sempre aos pares
$('<tr>', {'class': 'history-link-row'}).appendTo(containerBody)
.append($('<td>',{colspan:3}).append(
$('<a>', {
href: 'shoutbox.php?history=1',
target: '_top', // _blank = nova janela, _top = na janela atual
text: 'Ver histórico de mensagens' // ou 'Ver mensagens anteriores'
})));
setRefreshTimer();
setMessageDeleteHandlers();
function setMessageDeleteHandlers() {
$('a[href*="shoutbox.php?del="][onclick]')
.removeAttr('onclick')
.on('click', handleMessageDelete);
}
function handleMessageDelete(evt) {
var $this = $(this);
var deleteHref = $this.attr('href').match(/\?(del=\d+)/)[1];
evt.preventDefault();
// Caso queira voltar a forma antiga só
// alterar o valor dessa constante
if (APAGAR_USANDO_CONFIRM) {
if (confirm('Tem certeza que quer deletar esta mensagem?')) {
loadAjax({ data: deleteHref });
}
return;
}
// Ao invés de confirm, precisa de alguns ajustes...
showMessage('Você tem certeza? Clique nessa mensagem para confirmar', function() {
loadAjax({
data: deleteHref
})
.fail(function () {
showMessage('Não foi possível apagar a mensagem.');
});
});
}
$('form').on('submit', handleMessageSent);
function handleMessageSent(evt) {
// Por praticidade (i.e. não ter que lembrar da documentação)
// copiei isso do http://ginpen.com/2013/05/07/jquery-ajax-form/
var $form = $(this),
$input = $form.find('.shoutbox_msgbox');
evt.preventDefault();
loadAjax({
url: $form.attr('action'),
type: $form.attr('method'),
data: $form.serialize()
})
.fail(function () {
showMessage('A mensagem não foi enviada.');
$input.removeAttr('disabled');
})
.done(function () {
$input.removeAttr('disabled').val('');
});
$input.attr('disabled', true);
}
// As duas funções abaixo chamam uma a outra
// para que seja possível controlar o timer
// e assim pará-lo quando clicar no .icon-refresh
function refreshLoop() {
loadAjax(setRefreshTimer);
}
function setRefreshTimer() {
// Usar valor padrão de 30, caso não tenha sido definido
timer = setTimeout(refreshLoop, timeBetweenReloads || 30e3);
}
// Carregar as novas mensagens usando $.ajax
// Além disso adicionar uma animação de carregando
// e parar o timer, evitando que a página
// seja carregada várias vezes seguidas
function loadAjax(_opt) {
var opt = _opt || {};
// Se a função for chamada por um evento:
if (opt instanceof $.Event) {
opt.preventDefault();
opt = {};
}
refreshIcon.addClass('spinning');
clearTimeout(timer);
opt.timeout = 10e3; // 10 segundos de timeout
return $.ajax('shoutbox.php', opt)
.done(gotNewMessages)
.fail(failedGettingMessages)
.always(afterChatLoaded);
}
// Independente se carrregou ou não
// parar a animação e agendar para carregar novamente
function afterChatLoaded() {
refreshIcon.removeClass('spinning');
setRefreshTimer();
}
// Avisar caso as mensagens não carreguem e voltar
// o tempo entre recarregamentos ao padrão
function failedGettingMessages() {
showMessage('Não foi possível carregar novas mensagens');
timeBetweenReloads = null;
}
// Caso a página tenha sido carregada então processá-la
function gotNewMessages(returnedHtml) {
// Amarzena estado anterior de scroll antes de aplicar as modificações:
var firstOldMessage = containerBody.children().first();
var oldPosition = firstOldMessage.offset().top;
var scrolledLength = container.scrollTop();
// Usa os ids para determinar as mensagens novas
var currentMessages = $('tr td:only-child');
var currentMessagesId = currentMessages.map(function (){
return $(this).parent().data('id');
}).get();
var returnedMessages = $('<div>', {html: returnedHtml})
.find('.shoutbox_contain tr td:only-child');
var lastIndex = -1;
var newMessages = $();
returnedMessages.each(function () {
var $this = $(this);
var elementData = $this.parent().data('id');
// O segundo argumento do indexOf garante a ordem das mensagens:
var index = currentMessagesId.indexOf(elementData, lastIndex + 1);
// A mensagem já tinha sido registrada:
if (index !== -1) {
// Elimina mensagens apagadas:
if(index - lastIndex > 1) {
currentMessages.slice(lastIndex + 1, index).each(function (n) {
// Mais informação sobre essa corrente nas linhas abaixo
var removedElements = $(this).parent().prev().addBack();
// Remover da lista de mensagens mostradas:
removedElements.fadeOut(1200, function(){
$(this).remove();
});
});
}
lastIndex = index;
// A mensagem não tinha sido
// registrada então adicioná-la
} else {
// $this é o <td> no segundo <tr>
// logo para pegar toda a mensagem
// seleciona o <tr> e o anterior a ele.
var newMessagesElements = $this.parent().prev().addBack();
currentMessages.eq(lastIndex + 1).parent().prev().before(newMessagesElements);
// Só considerar mensagens novas aquelas que forem adicionadas anteriormente as já existentes:
// Por algum motivo está considerando menos mensagens do que deveria
if (lastIndex + 1 <= newMessages.length) {
newMessages = newMessages.add(newMessagesElements.prevAll().andSelf());
}
}
});
// Se estiver houvendo uma conversa ativa então
// carregar mensagens em um intervalo mais rápido
// se não aumentar o intervalo para aliviar o servidor
// ajuste os valores se necessário
timeBetweenReloads = Math.max(
10, // Intervalo mínimo de 10 segundos
60 - newMessages.length * 15 // Cada nova mensagem reduz o intervalo em 15 segundos
) * 1000; // Finalmente converte para milisegundos
if (newMessages.length === 0) { return; }
setMessageDeleteHandlers();
// Se não estiver na página então deixar
// um aviso pelo título da página
if (document.hidden) {
// unreadMessages amarzena os elementos das novas mensagens
unreadMessages = unreadMessages.add(newMessages);
// Como cada mensagem é composta de dois elementos logo se divide por dois
top.document.title = '(' + (unreadMessages.length / 2) + ') ' + DEFAULT_TITLE;
// Se for a primeira vez que isso acontece:
if (unreadMessages.length === newMessages.length) {
// O código para detectar quando voltou a página é simplificado,
// a versão completa dele está nessa página:
// https://developer.mozilla.org/en-US/docs/Web/Guide/User_experience/Using_the_Page_Visibility_API
$(document).one('visibilitychange', function () {
// volta o título ao padrão:
top.document.title = DEFAULT_TITLE;
// Seleciona a ultima mensagem e alinha o fundo do scroll com ela:
scrollToElement(unreadMessages.last(), function () {
unreadMessages.addClass('highlight');
// Limpa a fila de mensagens não lidas:
unreadMessages = $();
});
});
}
// Se estiver lendo mensagens anteriores
} else if (scrolledLength !== 0) {
// Manter o scroll no mesmo ponto,
// para não interromper a leitura:
container.scrollTop(scrolledLength + firstOldMessage.offset().top - oldPosition);
// Avisar que há novas mensagens por
// meio de um aviso no topo da página
showMessage(newMessages.length === 2 ? 'Há uma mensagem nova' :
('Há ' + (newMessages.length / 2) + ' novas mensagens'), function () {
scrollToElement(newMessages.last(), function () {
newMessages.addClass('highlight');
});
});
} else {
newMessages.addClass('highlight');
}
}
// Cria uma mensagem de aviso no topo da página
// Aceita uma função no segundo argumento
// que é chamada caso a mensagem seja clicada
function showMessage(message, callback) {
var el = $('<div>', {
text: message,
'class': 'message-alert'
})
.appendTo('body')
.on('mousedown touchstart', function handler(evt) {
// Evita ser chamado mais de uma vez
el.off('mousedown touchstart', handler);
// Chama o callback, se definido
if (typeof callback == 'function') {
callback();
}
// Faz a animação de fadeout
el.stop(true).fadeOut(300, function () {
el.remove();
});
})
// Usando as animações do jQuery
// Há CSS3, mas nesse caso tudo bem
.hide().fadeIn(1000)
.delay(5e3 /* 5 segundos*/ )
.fadeOut(1000, function () {
el.remove();
});
}
// Coloca o fundo do scroll no elemento
// Se não for possível irá o mais próximo
function scrollToElement(el, callback) {
container.animate({
scrollTop: el.position().top - container.height() + container.scrollTop() + el.height()
}, 600, 'swing', callback || $.noop);
}
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment