Created
July 23, 2014 18:23
-
-
Save theY4Kman/2d95080b392dba0780a5 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 Hive passwords utilities | |
| // @description Highlights common passwords and copies to clipboard upon Reveal | |
| // @match https://hive.hivelocity.net/admin/passwords/password/* | |
| // @require http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js | |
| // @require http://github.com/bartaz/sandbox.js/raw/master/jquery.highlight.js | |
| // @grant GM_setClipboard | |
| // @copyright 2013, Zach "theY4Kman" Kanzler | |
| // ==/UserScript== | |
| $('input[type=password]').siblings('a').click(function() { | |
| var $this = $(this); | |
| var $input = $this.siblings('input'); | |
| if ($input.attr('type') == 'text') { | |
| GM_setClipboard($input.val()); | |
| } | |
| }); | |
| var IMPORTANT_ENTRIES_KEY = 'hive-important-entries'; | |
| var important_entries; | |
| var unimportant_entries; | |
| var important_entries_reverse = {}; | |
| var colors = ['#ff0037', '#00fff2', '#ffc300']; | |
| function css_rgb_to_obj(css_rgb) { | |
| var s_rgb = css_rgb.substr(4).slice(0, -1).split(', '); | |
| return {r: parseInt(s_rgb[0]), g: parseInt(s_rgb[1]), b: parseInt(s_rgb[2])}; | |
| } | |
| function rgb_obj_to_css(rgb) { | |
| return 'rgb(' + [rgb.r, rgb.g, rgb.b].join(', ') + ')'; | |
| } | |
| function get_readable_text_color(css_rgb) { | |
| var rgb = css_rgb_to_obj(css_rgb); | |
| var hsv = RGB2HSV(rgb); | |
| hsv.hue = HueShift(hsv.hue, 180.0); | |
| return rgb_obj_to_css(HSV2RGB(hsv)); | |
| } | |
| function RGB2HSV(rgb) { | |
| hsv = new Object(); | |
| max=max3(rgb.r,rgb.g,rgb.b); | |
| dif=max-min3(rgb.r,rgb.g,rgb.b); | |
| hsv.saturation=(max==0.0)?0:(100*dif/max); | |
| if (hsv.saturation==0) hsv.hue=0; | |
| else if (rgb.r==max) hsv.hue=60.0*(rgb.g-rgb.b)/dif; | |
| else if (rgb.g==max) hsv.hue=120.0+60.0*(rgb.b-rgb.r)/dif; | |
| else if (rgb.b==max) hsv.hue=240.0+60.0*(rgb.r-rgb.g)/dif; | |
| if (hsv.hue<0.0) hsv.hue+=360.0; | |
| hsv.value=Math.round(max*100/255); | |
| hsv.hue=Math.round(hsv.hue); | |
| hsv.saturation=Math.round(hsv.saturation); | |
| return hsv; | |
| } | |
| // RGB2HSV and HSV2RGB are based on Color Match Remix [http://color.twysted.net/] | |
| // which is based on or copied from ColorMatch 5K [http://colormatch.dk/] | |
| function HSV2RGB(hsv) { | |
| var rgb=new Object(); | |
| if (hsv.saturation==0) { | |
| rgb.r=rgb.g=rgb.b=Math.round(hsv.value*2.55); | |
| } else { | |
| hsv.hue/=60; | |
| hsv.saturation/=100; | |
| hsv.value/=100; | |
| i=Math.floor(hsv.hue); | |
| f=hsv.hue-i; | |
| p=hsv.value*(1-hsv.saturation); | |
| q=hsv.value*(1-hsv.saturation*f); | |
| t=hsv.value*(1-hsv.saturation*(1-f)); | |
| switch(i) { | |
| case 0: rgb.r=hsv.value; rgb.g=t; rgb.b=p; break; | |
| case 1: rgb.r=q; rgb.g=hsv.value; rgb.b=p; break; | |
| case 2: rgb.r=p; rgb.g=hsv.value; rgb.b=t; break; | |
| case 3: rgb.r=p; rgb.g=q; rgb.b=hsv.value; break; | |
| case 4: rgb.r=t; rgb.g=p; rgb.b=hsv.value; break; | |
| default: rgb.r=hsv.value; rgb.g=p; rgb.b=q; | |
| } | |
| rgb.r=Math.round(rgb.r*255); | |
| rgb.g=Math.round(rgb.g*255); | |
| rgb.b=Math.round(rgb.b*255); | |
| } | |
| return rgb; | |
| } | |
| function min3(a,b,c) { | |
| return (a<b)?((a<c)?a:c):((b<c)?b:c); | |
| } | |
| function max3(a,b,c) { | |
| return (a>b)?((a>c)?a:c):((b>c)?b:c); | |
| } | |
| function HueShift(h,s) { | |
| h+=s; while (h>=360.0) h-=360.0; | |
| while (h<0.0) h+=360.0; | |
| return h; | |
| } | |
| function is_important_entry(id) { | |
| return important_entries_reverse.hasOwnProperty(id); | |
| } | |
| function add_important_entry(id) { | |
| if (is_important_entry(id)) | |
| return; | |
| important_entries.push(id); | |
| important_entries_reverse[id] = true; | |
| unimportant_entries.splice(unimportant_entries.indexOf(id), 1); | |
| save_important_entries(); | |
| } | |
| function remove_important_entry(id) { | |
| if (!is_important_entry(id)) | |
| return; | |
| var index = important_entries.indexOf(id); | |
| // This should not occur if is_important_entry(id) is false | |
| // BUT I CHECK ANYWAY CUZ IM HARDCORE | |
| if (index !== -1) { | |
| important_entries.splice(index, 1); | |
| delete important_entries_reverse[id]; | |
| unimportant_entries.push(id); | |
| unimportant_entries.sort(function(a,b) { return a-b; }); | |
| save_important_entries(); | |
| } | |
| } | |
| function save_important_entries() { | |
| window.localStorage[IMPORTANT_ENTRIES_KEY] = JSON.stringify(important_entries); | |
| } | |
| function load_important_entries() { | |
| important_entries = _retrieve_important_entries() || []; | |
| sync_reverse_important_entries(); | |
| } | |
| function sync_reverse_important_entries() { | |
| for (i=0; i<important_entries.length; i++) | |
| important_entries_reverse[important_entries[i]] = true; | |
| } | |
| function _retrieve_important_entries() { | |
| var saved = window.localStorage[IMPORTANT_ENTRIES_KEY]; | |
| if (saved) | |
| return JSON.parse(saved); | |
| } | |
| Array.prototype.binaryClosest = function(searchElement) { | |
| if (this.length == 0) | |
| return undefined; | |
| if (searchElement < this[0]) | |
| return null; | |
| var minIndex = 0; | |
| var maxIndex = this.length - 1; | |
| var currentIndex; | |
| var currentElement; | |
| while (minIndex <= maxIndex) { | |
| currentIndex = (minIndex + maxIndex) / 2 | 0; | |
| currentElement = this[currentIndex]; | |
| if (currentElement < searchElement) { | |
| minIndex = currentIndex + 1; | |
| } | |
| else if (currentElement > searchElement) { | |
| maxIndex = currentIndex - 1; | |
| if (minIndex > maxIndex) { | |
| currentIndex--; | |
| break; | |
| } | |
| } else { | |
| return this[currentIndex]; | |
| } | |
| } | |
| return this[currentIndex]; | |
| } | |
| $(function() { | |
| var $result_list = $('#result_list'); | |
| var $tbody = $result_list.children('tbody'); | |
| // Build an array of the IDs on the page, which will later be binary searched | |
| // to place an unstarred row back in its original position | |
| unimportant_entries = $tbody.find('.action-select').map(function() { return parseInt($(this).val()); }).toArray(); | |
| function fix_striping() { | |
| $tbody.children('.row1:visible, .row2:visible').removeClass('row1 row2').each(function(idx) { | |
| this.setAttribute('class', 'row' + (idx % 2 + 1)); | |
| }); | |
| } | |
| function highlight_important_entries() { | |
| for (var i=0; i<important_entries.length; i++) { | |
| var value = important_entries[i]; | |
| var $tr = $('.action-select[value="' + value + '"]').parents('tr'); | |
| if ($tr.length == 0) | |
| continue; | |
| $tr.addClass('important').removeClass('row1 row2'); | |
| $tr.prependTo($tbody); | |
| var color = colors[i % colors.length]; | |
| // Get the RGB colour of the background, so we can set a readable text colour | |
| var color_test = document.createElement('span') | |
| color_test.style.color = color; | |
| var rgb_bg = $(color_test).css('color'); | |
| var text_color = get_readable_text_color(rgb_bg); | |
| // Don't know why .css('background', color) doesn't work | |
| $tr[0].setAttribute('style', 'background: ' + color + '; color: ' + text_color + ';'); | |
| $tr.find(':not(input,select,option)').css('color', text_color); | |
| } | |
| } | |
| function unhighlight_important_entry(id) { | |
| var $tr = $('.action-select[value="' + id + '"]').parents('tr'); | |
| $tr.removeClass('important').addClass('row1'); | |
| $tr[0].setAttribute('style', null); | |
| $tr.find(':not(input,select,option)').css('color', ''); | |
| $tr.remove(); | |
| // This all assumes no sorting of columns, so the sort is based on index. All I care about. | |
| var closest_id = unimportant_entries.binaryClosest(id); | |
| if (closest_id == null) | |
| $tbody.children('tr:last-child').after($tr); | |
| else | |
| $('.action-select[value="' + closest_id + '"]').parents('tr').before($tr); | |
| } | |
| $tbody.on('change', '.star', function() { | |
| var $this = $(this); | |
| var id = parseInt($this.parents('tr').find('.action-select').val()); | |
| if ($this.prop('checked')) { | |
| add_important_entry(id); | |
| highlight_important_entries(); | |
| } else { | |
| unhighlight_important_entry(id); | |
| remove_important_entry(id); | |
| } | |
| fix_striping(); | |
| }); | |
| // Initialize important entries | |
| load_important_entries(); | |
| // Remove from unimportant_entries | |
| for (i=0; i<important_entries.length; i++) | |
| unimportant_entries.splice(unimportant_entries.indexOf(important_entries[i]), 1); | |
| unimportant_entries.sort(function(a,b) { return a-b; }); | |
| // Highlight them | |
| highlight_important_entries(); | |
| // Then redo the row striping | |
| fix_striping(); | |
| // Add column for starring rows (must be after load_important_entries, in order to pre-check starred boxes) | |
| var $thead = $result_list.children('thead'); | |
| $thead.find('tr th:nth-child(1)').after($('<th><img src="https://i.imgur.com/EtaQ0pP.png" /></th>')); | |
| $tbody.find('tr > td:nth-child(1)').each(function() { | |
| var $this = $(this); | |
| var td = document.createElement('td'); | |
| var checkbox = document.createElement('input'); | |
| checkbox.className = 'star'; | |
| checkbox.type = 'checkbox'; | |
| if (is_important_entry(parseInt($this.children('.action-select').val()))) | |
| checkbox.checked = 'checked'; | |
| td.appendChild(checkbox); | |
| $this.after(td); | |
| }); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment