├── .gitignore ├── icons ├── nukem-128.png ├── nukem-16.png ├── nukem-19.png ├── nukem-48.png └── nukem-19-disabled.png ├── popup.js ├── README.md ├── nukem.js ├── popup.html ├── manifest.json ├── options.html ├── options.css ├── engine.js ├── background.js ├── options.js └── jquery-3.1.1.slim.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | -------------------------------------------------------------------------------- /icons/nukem-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gruntfuggly/nukem/HEAD/icons/nukem-128.png -------------------------------------------------------------------------------- /icons/nukem-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gruntfuggly/nukem/HEAD/icons/nukem-16.png -------------------------------------------------------------------------------- /icons/nukem-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gruntfuggly/nukem/HEAD/icons/nukem-19.png -------------------------------------------------------------------------------- /icons/nukem-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gruntfuggly/nukem/HEAD/icons/nukem-48.png -------------------------------------------------------------------------------- /icons/nukem-19-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gruntfuggly/nukem/HEAD/icons/nukem-19-disabled.png -------------------------------------------------------------------------------- /popup.js: -------------------------------------------------------------------------------- 1 | addEventListener( "unload", function( event ) 2 | { 3 | chrome.extension.getBackgroundPage().toggleEnabled(); 4 | }, true ); 5 | 6 | $( document ).ready( function() 7 | { 8 | $( "#start" ).click( function() 9 | { 10 | self.close(); 11 | } ); 12 | } ); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nukem 2 | 3 | There are plenty of Chrome extensions that allow you to remove 4 | elements from the page, but they all seem to be temporary. This 5 | one remembers whats been removed, and removes it whenever the 6 | page is loaded again. 7 | 8 | Wildcards can be used to make it work on multiple pages from 9 | the same site (for example) and an optional delay can be set in 10 | case the element you want to remove doesn't get displayed right 11 | away. 12 | 13 | [https://github.com/Gruntfuggly/nukem](https://github.com/Gruntfuggly/nukem) -------------------------------------------------------------------------------- /nukem.js: -------------------------------------------------------------------------------- 1 | function remove( selector, method ) 2 | { 3 | if( method === "Hide" ) 4 | { 5 | $( selector ).remove(); 6 | } 7 | else 8 | { 9 | $( selector ).css( "visibility", "hidden" ); 10 | } 11 | chrome.extension.sendRequest( { 12 | method: "elementNuked", 13 | }); 14 | } 15 | 16 | var url = document.location.href; 17 | 18 | chrome.extension.sendRequest( { 19 | method: "getElements", 20 | url: url 21 | }, 22 | function( response ) 23 | { 24 | response.elements.map( function( element ) 25 | { 26 | setTimeout( function() 27 | { 28 | remove( element.selector, element.method ); 29 | }, parseInt( element.delay ) ); 30 | }); 31 | } 32 | ); -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

Move your mouse to hover over the element you want to remove, and click.

10 |

When you've removed everything you want to, click the menu button again to go to the options page where you'll see what you've nuked.

11 |

From the options page you can add a delay and change the method that's used to hide the element. You can also edit the URL to make it less specific, or add wildcards.

12 |

Once you're happy, just close the options page. Next time the page is loaded, the elements will get nuked again...

13 |
14 |

P.S. You can always visit the options page by right clicking the icon again.

15 | 16 | 17 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Nukem", 4 | "short_name": "Nukem", 5 | "description": "Remove elements from web pages permanently", 6 | "version": "0.6", 7 | "author": "Gruntfuggly", 8 | "permissions": [ 9 | "chrome://favicon/", 10 | "activeTab", 11 | "storage" 12 | ], 13 | "icons": { 14 | "16": "icons/nukem-16.png", 15 | "48": "icons/nukem-48.png", 16 | "128": "icons/nukem-128.png" 17 | }, 18 | "background": { 19 | "scripts": [ 20 | "background.js" 21 | ] 22 | }, 23 | "browser_action": { 24 | "default_icon": "icons/nukem-19-disabled.png", 25 | "default_title": "Start nukin'...", 26 | "default_popup": "popup.html" 27 | }, 28 | "options_page": "options.html", 29 | "content_scripts": [ 30 | { 31 | "matches": [ 32 | "*://*/*" 33 | ], 34 | "js": [ 35 | "jquery-3.1.1.slim.min.js", 36 | "engine.js" 37 | ] 38 | }, 39 | { 40 | "matches": [ 41 | "*://*/*" 42 | ], 43 | "js": [ 44 | "jquery-3.1.1.slim.min.js", 45 | "nukem.js" 46 | ], 47 | "run_at": "document_end" 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Nukem: Options 5 | 6 | 7 | 8 | 9 | 13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 24 | 25 | 27 | 34 | 35 |
26 | 28 | 29 | 30 | 31 | 32 | 33 |
36 |
37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /options.css: -------------------------------------------------------------------------------- 1 | html 2 | { 3 | height: 100%; 4 | } 5 | 6 | body 7 | { 8 | font-family: Helvetica; 9 | cursor: default; 10 | font-size: 13px; 11 | font-family: sans-serif; 12 | background-color: #ebeff9; 13 | overflow: hidden; 14 | margin: -20px -20px -20px 200px; 15 | user-select: none; 16 | } 17 | 18 | td 19 | { 20 | user-select: contain; 21 | white-space: nowrap; 22 | } 23 | 24 | .options 25 | { 26 | z-index: 2; 27 | position: absolute; 28 | left: 200px; 29 | top: 0; 30 | bottom: 4em; 31 | right: 0; 32 | padding: 0 25px; 33 | overflow-y: auto; 34 | overflow-x: hidden; 35 | } 36 | 37 | .section 38 | { 39 | height: 100%; 40 | } 41 | 42 | .background 43 | { 44 | border-left: 1px solid #C6C9CE; 45 | box-shadow: 0 0 20px #cdcdcd; 46 | width: 100%; 47 | height: 100%; 48 | background-color: #FFF; 49 | } 50 | 51 | select 52 | { 53 | border-radius: 2px; 54 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 55 | background: -webkit-linear-gradient(#FAFAFA, #F4F4F4 40%, #E5E5E5); 56 | border: 1px solid #AAA; 57 | color: #444; 58 | font-size: inherit; 59 | margin-bottom: 0; 60 | margin-top: 2px; 61 | min-width: 4em; 62 | padding: 3px 12px; 63 | height: 26px; 64 | } 65 | 66 | button 67 | { 68 | border-radius: 2px; 69 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 70 | background: -webkit-linear-gradient(#FAFAFA, #F4F4F4 40%, #E5E5E5); 71 | border: 1px solid #AAA; 72 | color: #444; 73 | font-size: inherit; 74 | margin-bottom: 0; 75 | margin-top: 2px; 76 | padding: 3px 12px; 77 | } 78 | 79 | button:hover 80 | { 81 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); 82 | background: #EBEBEB -webkit-linear-gradient(#FEFEFE, #F8F8F8 40%, #E9E9E9); 83 | border-color: #999; 84 | color: #222; 85 | } 86 | 87 | button:active 88 | { 89 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2); 90 | background: #EBEBEB -webkit-linear-gradient(#F4F4F4, #EFEFEF 40%, #DCDCDC); 91 | color: #333; 92 | } 93 | 94 | h1 95 | { 96 | color: #53637d; 97 | font-size: 2em; 98 | font-weight: normal; 99 | margin: 0; 100 | padding: 13px 24px 0 4px; 101 | text-shadow: #FFF 0 1px 2px; 102 | } 103 | 104 | .navigate 105 | { 106 | left: 0; 107 | top: 0; 108 | height: 50px; 109 | width: 200px; 110 | position: absolute; 111 | z-index: -1; 112 | } 113 | 114 | .navigate h1 115 | { 116 | border-bottom: none; 117 | text-align: right; 118 | } 119 | 120 | .listContainer 121 | { 122 | border: 1px solid #D9D9D9; 123 | border-radius: 2px; 124 | margin-top: 10px; 125 | } 126 | 127 | #content 128 | { 129 | clear: both; 130 | position: relative; 131 | height: 100%; 132 | } 133 | 134 | .controls 135 | { 136 | position: fixed; 137 | bottom: 0; 138 | right: 0; 139 | padding: .55rem; 140 | } 141 | 142 | #elementsTable 143 | { 144 | width: 100%; 145 | margin: .25rem 0; 146 | padding: 0 .25rem; 147 | } 148 | 149 | #elementsTable tbody 150 | { 151 | overflow-y: auto; 152 | } 153 | 154 | #elementsTable button 155 | { 156 | margin-left: .15em; 157 | margin-right: .15em; 158 | } 159 | 160 | #elementsTable button.reduceScope 161 | { 162 | width: 100%; 163 | font-size: 13px; 164 | } 165 | 166 | #elementsTable td.url 167 | { 168 | padding-left: 26px; 169 | background-repeat: no-repeat; 170 | background-position: 5px 50%; 171 | } 172 | 173 | #elementsTable td.remove 174 | { 175 | padding-left: 13px; 176 | } 177 | 178 | #elementsTable th 179 | { 180 | text-align: left; 181 | } 182 | 183 | #elementsTable th:nth-child(1) 184 | { 185 | padding-left: 26px; 186 | } 187 | 188 | #elementsTable input 189 | { 190 | width: 100%; 191 | margin: 0 auto; 192 | } 193 | 194 | #options input 195 | { 196 | border-radius: 2px; 197 | border: 1px solid #AAA; 198 | font-size: inherit; 199 | padding: 3px; 200 | margin-top: 2px; 201 | } 202 | 203 | #importexport 204 | { 205 | width: 100%; 206 | } 207 | 208 | #controls button 209 | { 210 | min-width: 4em; 211 | } 212 | -------------------------------------------------------------------------------- /engine.js: -------------------------------------------------------------------------------- 1 | var enabled = false; 2 | var elementsNuked = 0; 3 | 4 | function getDomPath( el ) 5 | { 6 | var stack = []; 7 | while( el.parentNode !== null ) 8 | { 9 | var sibCount = 0; 10 | var sibIndex = 0; 11 | for( var i = 0; i < el.parentNode.childNodes.length; i++ ) 12 | { 13 | var sib = el.parentNode.childNodes[ i ]; 14 | if( sib.nodeName == el.nodeName ) 15 | { 16 | if( sib === el ) 17 | { 18 | sibIndex = sibCount; 19 | } 20 | sibCount++; 21 | } 22 | } 23 | if( el.hasAttribute( 'id' ) && el.id !== '' ) 24 | { 25 | stack.unshift( el.nodeName.toLowerCase() + '#' + el.id ); 26 | } 27 | else if( sibCount > 1 ) 28 | { 29 | stack.unshift( el.nodeName.toLowerCase() + ':eq(' + sibIndex + ')' ); 30 | } 31 | else 32 | { 33 | stack.unshift( el.nodeName.toLowerCase() ); 34 | } 35 | el = el.parentNode; 36 | } 37 | 38 | return stack.slice( 1 ); // removes the html element 39 | } 40 | 41 | var box = $( "
" ).css( 42 | { 43 | display: "none", 44 | position: "absolute", 45 | zIndex: 65000, 46 | background: "rgba(255, 0, 0, .3)" 47 | }).appendTo( "body" ); 48 | 49 | var mouseX, mouseY, target, lastTarget; 50 | 51 | window.requestAnimationFrame( function frame() 52 | { 53 | window.requestAnimationFrame( frame ); 54 | 55 | if( target === undefined ) 56 | { 57 | box.hide(); 58 | return; 59 | } 60 | 61 | if( target && target.className === "outer" ) 62 | { 63 | box.hide(); 64 | target = document.elementFromPoint( mouseX, mouseY ); 65 | } 66 | 67 | box.show(); 68 | 69 | if( target === lastTarget ) return; 70 | 71 | lastTarget = target; 72 | 73 | if( target ) 74 | { 75 | var $target = $( target ); 76 | var offset = $target.offset(); 77 | 78 | box.css( { 79 | width: $target.outerWidth() - 1, 80 | height: $target.outerHeight() - 1, 81 | left: offset.left, 82 | top: offset.top 83 | }); 84 | } 85 | }); 86 | 87 | function setEnabled( enable ) 88 | { 89 | enabled = enable; 90 | 91 | if( enabled ) 92 | { 93 | elementsNuked = 0; 94 | 95 | $( "body" ).on( "mousemove.nukem", function( e ) 96 | { 97 | mouseX = e.clientX; 98 | mouseY = e.clientY; 99 | target = e.target; 100 | }); 101 | $( "body" ).on( "click.nukem", function( e ) 102 | { 103 | var selector = "#" + $( target ).prop( "id" ); 104 | 105 | if( selector.trim() === "#" ) 106 | { 107 | selector = getDomPath( target ).join( " > " ); 108 | var lastHash = selector.lastIndexOf( "#" ); 109 | if( lastHash > 0 ) 110 | { 111 | selector = selector.substr( lastHash ); 112 | } 113 | } 114 | 115 | elementsNuked++; 116 | 117 | chrome.extension.sendRequest( { 118 | method: "remove", 119 | selector: selector, 120 | url: window.location.protocol + "//" + window.location.hostname + window.location.pathname 121 | }, function( response ) { } 122 | ); 123 | $( selector ).remove(); 124 | }); 125 | } 126 | else 127 | { 128 | $( "body" ).off( "mousemove.nukem" ); 129 | $( "body" ).off( "click.nukem" ); 130 | 131 | target = undefined; 132 | } 133 | } 134 | 135 | chrome.extension.onRequest.addListener( 136 | function( request, sender, sendResponse ) 137 | { 138 | if( request.method == "toggle-enabled" ) 139 | { 140 | setEnabled( !enabled ); 141 | if( !enabled && elementsNuked > 0 ) 142 | { 143 | chrome.extension.sendRequest( { 144 | method: "options", 145 | }, function( response ) { } 146 | ); 147 | } 148 | sendResponse( { 149 | enabled: enabled 150 | }); 151 | } 152 | } 153 | ); 154 | 155 | chrome.extension.onMessage.addListener( 156 | function( request, sender, sendResponse ) 157 | { 158 | if( request.method == "stop" ) 159 | { 160 | setEnabled( false ); 161 | sendResponse( { 162 | enabled: false 163 | }); 164 | } 165 | } 166 | ); -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | // if( ! localStorage['firstRun'] ) 2 | // { 3 | // chrome.tabs.create( { url : "http://zaonce.com/projects/nukem.shtml" } ); 4 | // localStorage['firstRun'] = 'true'; 5 | // } 6 | var currentTab; 7 | var elementsNuked = 0; 8 | 9 | function getDefaultEntry() 10 | { 11 | return { 12 | url: "", 13 | selector: "", 14 | delay: 0, 15 | method: "Blank" 16 | }; 17 | } 18 | 19 | function setIcon( enabled ) 20 | { 21 | chrome.browserAction.setIcon( { 22 | path: "icons/nukem-" + ( enabled % 2 != 0 ? "19" : "19-disabled" ) + ".png" 23 | }); 24 | chrome.browserAction.setTitle( { 25 | title: ( enabled % 2 != 0 ? "Stop" : "Start" ) + " nukin'..." 26 | }); 27 | } 28 | 29 | function setBadge( count ) 30 | { 31 | chrome.browserAction.setBadgeText( { 32 | text: ( count > 0 ) ? count.toString() : "" 33 | }); 34 | } 35 | 36 | function toggleEnabled() 37 | { 38 | chrome.tabs.getSelected( 39 | null, 40 | function( tab ) 41 | { 42 | chrome.tabs.sendRequest( 43 | tab.id, 44 | { 45 | method: "toggle-enabled" 46 | }, 47 | function( response ) 48 | { 49 | setIcon( response.enabled ); 50 | if( response.enabled ) 51 | { 52 | chrome.tabs.query( { 53 | active: true, 54 | currentWindow: true 55 | }, function( tabs ) 56 | { 57 | currentTab = tabs[ 0 ].id; 58 | }); 59 | } 60 | } 61 | ); 62 | } 63 | ); 64 | 65 | chrome.browserAction.setPopup( { 66 | popup: "" 67 | }); 68 | } 69 | 70 | function addSite( url, selector ) 71 | { 72 | chrome.storage.sync.get( "settings", function( stored ) 73 | { 74 | var settings = stored.settings === undefined ? [] : stored.settings; 75 | 76 | settings.push( { 77 | url: url, 78 | selector: selector, 79 | delay: "0", 80 | method: "Hide" 81 | }); 82 | 83 | chrome.storage.sync.set( { settings: settings }, function() 84 | { 85 | console.log( "Failed to store settings: " + runtime.lastError ); 86 | }); 87 | }); 88 | } 89 | 90 | function pageMatches( url, value ) 91 | { 92 | var pattern = "^" + value; 93 | pattern = pattern.replace( /\//g, "\\/" ); 94 | pattern = pattern.replace( /\?/g, "\\?" ); 95 | pattern = pattern.replace( /\./g, "\\." ); 96 | pattern = pattern.replace( /\*/g, ".+" ); 97 | var regex = new RegExp( pattern ); 98 | var matched = url.search( regex ) != -1; 99 | 100 | return matched; 101 | } 102 | 103 | function getElements( url, callback ) 104 | { 105 | var selectors = []; 106 | 107 | chrome.storage.sync.get( "settings", function( stored ) 108 | { 109 | var settings = stored.settings === undefined ? [] : stored.settings; 110 | 111 | settings.map( function( entry ) 112 | { 113 | if( pageMatches( url, entry.url ) ) 114 | { 115 | selectors.push( entry ); 116 | } 117 | }); 118 | 119 | callback( { 120 | elements: selectors 121 | }); 122 | }); 123 | } 124 | 125 | chrome.browserAction.onClicked.addListener( toggleEnabled ); 126 | 127 | chrome.tabs.onActivated.addListener( function( tabId, changeInfo, tab ) 128 | { 129 | setIcon( false ); 130 | setBadge( 0 ); 131 | 132 | if( currentTab ) 133 | { 134 | chrome.tabs.sendMessage( currentTab, { 135 | method: "stop" 136 | }, function( response ) { }); 137 | } 138 | }); 139 | 140 | chrome.extension.onRequest.addListener( 141 | function( request, sender, sendResponse ) 142 | { 143 | if( request.method === "remove" ) 144 | { 145 | addSite( request.url, request.selector ); 146 | } 147 | else if( request.method === "options" ) 148 | { 149 | chrome.runtime.openOptionsPage( function() { }); 150 | } 151 | else if( request.method === "getElements" ) 152 | { 153 | elementsNuked = 0; 154 | setBadge( 0 ); 155 | getElements( request.url, sendResponse ); 156 | } 157 | else if( request.method === "elementNuked" ) 158 | { 159 | setBadge( ++elementsNuked ); 160 | } 161 | else 162 | { 163 | sendResponse( 164 | {} 165 | ); // snub them. 166 | } 167 | } 168 | ); 169 | -------------------------------------------------------------------------------- /options.js: -------------------------------------------------------------------------------- 1 | var storedSettings = []; 2 | 3 | const defaultEntry = chrome.extension.getBackgroundPage().getDefaultEntry(); 4 | 5 | function setIcon( cell, url ) 6 | { 7 | if( url && url.indexOf( "*" ) !== -1 ) 8 | { 9 | url = url.match( /^([\w-]+:\/*\[?[\w\.:-]+)\]?(?::\d+)?/ )[ 1 ]; 10 | } 11 | $( cell ).css( "background-image", "url(\'chrome://favicon/" + url + "\')" ); 12 | } 13 | 14 | function addRow( entry ) 15 | { 16 | function refreshIcon( field ) 17 | { 18 | setIcon( $( field ).parent(), field.value ); 19 | } 20 | 21 | function removeRow( button ) 22 | { 23 | $( button ).closest( "tr" ).remove(); 24 | if( $( "#elementsTable tbody tr" ).length === 0 ) 25 | { 26 | addRow( defaultEntry ); 27 | } 28 | } 29 | 30 | function reduceScope( button ) 31 | { 32 | var selectorField = $( button ).closest( "tr" ).find( "input[name='selector']" ); 33 | var selectorElements = selectorField.val().split( " > " ); 34 | if( selectorElements.length > 1 ) 35 | { 36 | selectorElements.pop(); 37 | } 38 | selectorField.val( selectorElements.join( " > " ) ); 39 | } 40 | 41 | $( "#elementsTable tbody" ) 42 | .append( $( "" ) 43 | .append( $( "" ) 44 | .append( $( "" ) 45 | .on( "blur", function() { refreshIcon( this ); }) ) ) 46 | .append( $( "" ) 47 | .append( $( "" ) ) ) 48 | .append( $( "" ) 49 | .append( $( "