Last active
August 29, 2015 14:01
-
-
Save kui/9c37177a61933158737f to your computer and use it in GitHub Desktop.
Hipchat のブラウザクライアントで、右カラムのルーム一覧の中で、赤字のルームと選択中のルームを上に持ってくるやつ。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Hipchat Room Sort by Alert | |
// @include https://*.hipchat.com/chat | |
// ==/UserScript== | |
'use strict'; | |
function main() { | |
console.log('# Hipchat Room Sort by Alert'); | |
startAlertOserver(); | |
} | |
// Callbacks which is called when the classes is added or removed | |
// to the "class" attr of the room node. | |
var CLASS_MUTATION_CALLBACKS = [ | |
function(addedClassNames, removedClassNames, room) { | |
if (addedClassNames.indexOf('alert') < 0) return; | |
moveToAlerteds(room); | |
}, | |
function(addedClassNames, removedClassNames, room) { | |
if (addedClassNames.indexOf('selected') < 0 | |
&& removedClassNames.indexOf('alert') < 0) return; | |
moveToNonAlerteds(room); | |
} | |
]; | |
function startAlertOserver() { | |
var observer = new MutationObserver(function (mutations) { | |
var isChild = haveChildNode.bind(null, tabs()); | |
handleMutations( | |
mutations | |
.filter(function (m) { return m.type === 'attributes'; }) | |
.filter(function (m) { return m.attributeName === 'class'; }) | |
.filter(function (m) { return isChild(m.target); })); | |
}); | |
function start() { | |
var t = tabs(); | |
if (!t || !lobby()) retry(start); | |
observer.observe( | |
t, | |
{ attributes: true, | |
subtree: true, | |
attributeOldValue: true, | |
attributeFilter: ['class'] }); | |
} | |
start(); | |
} | |
function retry(f) { | |
console.log('retry: %o', f); | |
requestAnimationFrame(f); | |
} | |
function handleMutations(mutations) { | |
var map = {}; | |
mutations.forEach(function(m) { | |
var name = m.target.getAttribute('name'); | |
if (map.hasOwnProperty(name)) { | |
var o = map[name]; | |
map[name] = { oldValue: o.oldValue, node: m.target }; | |
} else { | |
map[name] = { oldValue: m.oldValue, node: m.target }; | |
} | |
}); | |
for (var p in map) { | |
if (!map.hasOwnProperty(p)) continue; | |
var a = map[p]; | |
handleMutation(a.node, a.oldValue); | |
} | |
} | |
function handleMutation(node, oldValue) { | |
var newClassList = (node.classList) ? toArray(node.classList) : []; | |
var oldClassList = (oldValue) ? removeEmpties(oldValue.split(/\s+/)) : []; | |
if (newClassList.length === 0 && oldClassList.length === 0) return; | |
console.log('%o => %o', oldClassList, newClassList); | |
var addedClassList = []; | |
newClassList.forEach(function(c) { | |
if (oldClassList.indexOf(c) < 0) addedClassList.push(c); | |
}); | |
var removedClassList = []; | |
oldClassList.forEach(function(c) { | |
if (newClassList.indexOf(c) < 0) removedClassList.push(c); | |
}); | |
CLASS_MUTATION_CALLBACKS.forEach(function(f) { | |
f(addedClassList, removedClassList, node); | |
}); | |
} | |
function removeEmpties(list) { | |
return list.filter(function(e) { return e.length != 0; }); | |
} | |
function haveClass(node, className) { | |
if (!node || !node.classList) return false; | |
return node.classList.contains(className); | |
} | |
function moveToAlerteds(node) { | |
console.log('move to alerteds: %o', node); | |
insertAfter(lobby(), node); | |
} | |
function moveToNonAlerteds(node) { | |
console.log('move to non-alerteds: %o', node); | |
var headNonAlerted = find(toArray(rooms()), function(e) { | |
return node !== e && !haveClass(e, 'alert'); | |
}); | |
insertBefore(headNonAlerted, node); | |
} | |
function find(arr, condition) { | |
for (var i = 0; i < arr.length; i++) { | |
var e = arr[i]; | |
if (condition(e)) | |
return e; | |
} | |
} | |
function haveChildNode(parent, child) { | |
if (!parent || !child) return false; | |
var children = toArray(parent.childNodes); | |
if (!children || children.length === 0) return false; | |
return children.indexOf(child) >= 0; | |
} | |
function lobby() { | |
return querySelector('#tab_lobby'); | |
} | |
function tabs() { | |
return querySelector('#tabs'); | |
} | |
function rooms() { | |
return querySelectorAll('#tab_lobby ~ li'); | |
} | |
var querySelector = document.querySelector.bind(document); | |
var querySelectorAll = document.querySelectorAll.bind(document); | |
function toArray(a) { | |
return Array.prototype.slice.call(a, 0); | |
} | |
function insertAfter(node, newNode) { | |
node.parentNode.insertBefore(newNode, node.nextSibling); | |
} | |
function insertBefore(node, newNode) { | |
node.parentNode.insertBefore(newNode, node); | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment