Skip to content

Instantly share code, notes, and snippets.

@smirea
Last active December 19, 2015 14:39
Show Gist options
  • Save smirea/5970480 to your computer and use it in GitHub Desktop.
Save smirea/5970480 to your computer and use it in GitHub Desktop.
adds color coding to diff headers in Bugzilla, enables color coding of code blocks with hotkey navigation and more (check first comment)
// ==UserScript==
// @name Mozilla Bugzilla Comment Enhancer
// @namespace http://code4fun.de
// @description read the name
// @include https://bugzilla.mozilla.org/*
// @grant GM_addStyle
// @version 1
// ==/UserScript==
unsafeWindow.addEventListener ("load", init, false);
let swatches = ['red', 'orange', 'lightgreen', '#48BBE8'];
let regexps = {
file_path: /^\s*:::.*$/gim,
line_number: /^\s*@@.*@@.*$/gim
};
let last_navigated = null;
function init () {
add_style();
let comments = document.querySelectorAll('.bz_comment_text');
for (let comment of comments) {
for (let [name, regexp] of Iterator(regexps)) {
let match;
while (match = regexp.exec(comment.innerHTML)) {
if (match[0].length == 0) {
continue;
}
let content = comment.innerHTML.slice(match.index, match.index+match[0].length);
content = content.replace("\n", '');
comment.innerHTML = comment.innerHTML.slice(0, match.index)+
'<span class="'+name+'">'+
content+
'</span>'+
comment.innerHTML.slice(match.index+match[0].length);
}
}
}
add_events();
load_state();
}
let listeners = {
'h': function navigate_highlighted (event) {
navigate_elements('.highlighted', event);
},
'q': function navigate_codes (event) {
navigate_elements('.quote', event);
},
't': function toggle_highlighted (event) {
if (!last_navigated) {
return;
}
toggle_highlighted_quote.call(last_navigated.elem);
save_state();
},
's': function change_color (event) {
if (!last_navigated) {
return;
}
let index = last_navigated.elem.getAttribute('swatch-index');
if (event.shiftKey) {
index = index !== null ? Number(index) : 0;
index = --index >= 0 ? index : swatches.length - 1;
index = index % swatches.length;
} else {
index = index !== null ? Number(index) : swatches.length - 1;
index = (index + 1) % swatches.length;
}
last_navigated.elem.querySelectorAll('.swatch')[index].click();
},
'd': function remove_highlighted (event) {
let highlighted = get_quotes('.highlighted');
let msg = 'Are you sure you want to remove `'+highlighted+'` highlighted items?';
if (confirm(msg)) {
for (let h of highlighted) {
h.classList.remove('highlighted');
save_state();
}
}
}
};
function navigate_elements (selector, event) {
let elems = document.querySelectorAll(selector);
if (elems.length == 0) {
console.warn('No elements found for `'+selector+'`');
return;
}
let container = document.documentElement;
let vals = [];
for (let elem of elems) {
let offset = get_offset(elem);
let diff = offset - container.scrollTop;
vals.push({elem:elem, diff:diff, offset:offset});
}
vals = proper_sort(vals, !event.shiftKey);
let scroll_into_view = (index) => {
vals[index || 0].elem.scrollIntoView(true);
container.scrollTop = container.scrollTop - 120;
}
let old_scrollTop = container.scrollTop;
scroll_into_view(0);
if (old_scrollTop == container.scrollTop) {
vals.push(vals.shift());
scroll_into_view(0);
}
last_navigated = vals[0];
}
function proper_sort (arr, invert) {
let arr = arr.slice(0);
arr.sort((a, b) => { return a.diff >= b.diff; });
let index = 0;
for (index; index < arr.length && arr[index].diff <= 0; ++index) {}
index = index < arr.length ? index : arr.length - 1;
let order = arr.slice(0, index).reverse().concat(arr.slice(index).reverse());
if (invert) {
order = order.reverse();
order.push(order.shift());
}
return order;
}
function add_events () {
let quotes = get_quotes();
for (let quote of quotes) {
quote.style.backgroundColor = '';
quote.addEventListener('click', function (event) {
toggle_highlighted_quote.call(this);
save_state();
}, false);
}
document.body.addEventListener('keydown', function (event) {
if (event.altKey || event.ctrlKey || event.metaKey) {
return;
}
let ignore_tags = 'textarea input'.split(' ');
if (ignore_tags.indexOf(event.target.nodeName.toLowerCase()) > -1) {
return;
}
let code = event.keyCode;
let char = String.fromCharCode(code).toLowerCase();
if (listeners[char]) {
event.preventDefault();
listeners[char](event);
}
}, false);
}
function toggle_highlighted_quote () {
if (this.classList.contains('highlighted')) {
this.removeChild(this.querySelector('.info'));
this.classList.remove('highlighted');
} else {
let number = document.createElement('span');
number.classList.add('number');
let color = document.createElement('span');
color.classList.add('color');
add_swatches(color, this);
let info = document.createElement('div');
info.classList.add('info');
info.appendChild(number);
info.appendChild(color);
this.appendChild(info);
this.classList.add('highlighted');
let si = this.getAttribute('swatch-index');
si = si !== null ? Number(si) : 0;
this.querySelectorAll('.swatch')[si].click();
}
refresh_code_numbers();
}
function add_swatches (elem, target) {
swatches.forEach((color, index) => {
let swatch = document.createElement('span');
swatch.classList.add('swatch');
swatch.style.backgroundColor = color;
swatch.setAttribute('color', color);
swatch.setAttribute('index', index);
swatch.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
for (let i=0; i<swatches.length; ++i) {
target.classList.remove(get_swatch_name(i));
}
target.classList.add(get_swatch_name(index));
target.setAttribute('swatch-index', index);
save_state();
}, false);
elem.appendChild(swatch);
});
}
function get_swatch_name (index) {
return 'swatch-' + index;
}
function save_state () {
let highlighted = get_quotes('.highlighted');
let quotes = Array.prototype.slice.call(get_quotes());
let list = [];
for (let h of highlighted) {
let si = h.getAttribute('swatch-index');
list.push({
index: quotes.indexOf(h),
swatch: si !== null ? parseInt(si) : null
});
}
window.localStorage[get_bug_id()] = JSON.stringify({
highlighted: list
});
}
function load_state (state) {
let state = state || window.localStorage[get_bug_id()];
if (!state) {
return;
}
state = JSON.parse(state);
let quotes = get_quotes();
for (let h of state.highlighted) {
toggle_highlighted_quote.call(quotes[h.index]);
if (h.swatch !== null) {
quotes[h.index].querySelectorAll('.swatch')[h.swatch].click();
}
}
}
function refresh_code_numbers () {
let quotes = get_quotes('.highlighted');
for (let i=0; i<quotes.length; ++i) {
quotes[i].querySelector('.number').innerHTML = (i+1) + '/' + quotes.length;
}
}
function get_offset (elem) {
let count = 0;
let p = elem;
while (p) {
count += p.offsetTop;
p = p.offsetParent;
}
return count;
}
function get_quote_index (elem, nodeList) {
return Array.prototype.slice.call(get_quotes()).indexOf(elem);
}
function get_quotes (extra_class) {
return document.querySelectorAll('.quote'+(extra_class || ''));
}
function get_index (elem) {
let index = 0;
let p = elem;
while (p.previousSibling) {
if (p.previousSibling.nodeType === 1) ++index;
p = p.previousSibling;
}
return index;
}
function get_bug_id () {
return decodeURIComponent(window.location.search.slice(1)).match(/id=([0-9]+)/)[1];
}
function add_style () {
let generated = [];
swatches.forEach((color, index) => {
let name = get_swatch_name(index);
generated.push('.highlighted.'+name+" {border-color:"+color+";}");
generated.push('.highlighted.'+name+" .number {background-color:"+color+";}");
});
generated = generated.join("\n");
GM_addStyle(
".quote {cursor:pointer}\n"+
".file_path {color:crimson;}\n"+
".line_number {color:green!important;}\n"+
".highlighted {position:relative; border:5px solid red;}\n"+
".highlighted .info {position:absolute; right:0; top:0;}\n"+
".highlighted .color {display:inline-block; float:right;}\n"+
".swatch {display:inline-block; opacity:0.6; background-color:#000; width:10px; height:10px; margin:1px;}\n"+
".swatch:hover {opacity:1;}\n"+
".highlighted .number {display:inline-block; float:right; background:red; padding:1px 1px 5px 5px; font-weight:bold; color:#fff;}\n"+
generated
);
}
@smirea
Copy link
Author

smirea commented Jul 19, 2013

  • added code block highlighting. click on one to toggle, or just press t
  • use h, H hotkeys to navigate highlighted
  • use d to delete all code blocks
  • added general code block navigation, use q, Q hotkeys to navigate
  • added color swatches and color hoykeys, use s,S hotkeys to change color
  • added persistent highlight storage
  • disable hotkey actions when in input/textarea fields (duh!)
  • disable hotkey actions if any of the ctrl, alt or meta keys are pressed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment