| @@ -0,0 +1,10 @@ | |||
| chrome.runtime.onInstalled.addListener(() => { | |||
| chrome.declarativeContent.onPageChanged.removeRules(undefined, () => { | |||
| chrome.declarativeContent.onPageChanged.addRules([{ | |||
| conditions: [ new chrome.declarativeContent.PageStateMatcher({ | |||
| pageUrl: { }, | |||
| }) | |||
| ], actions: [ new chrome.declarativeContent.ShowPageAction() ] | |||
| }]); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,25 @@ | |||
| /****************/ | |||
| /* FONT AWESOME */ | |||
| /****************/ | |||
| @font-face { | |||
| font-family: 'Font Awesome'; | |||
| src: url('fa-light-300.ttf') format('truetype'); | |||
| font-weight: 300; | |||
| font-style: normal; | |||
| } | |||
| @font-face { | |||
| font-family: 'Font Awesome'; | |||
| src: url('fa-regular-400.ttf') format('truetype'); | |||
| font-weight: 400; | |||
| font-style: normal; | |||
| } | |||
| @font-face { | |||
| font-family: 'Font Awesome'; | |||
| src: url('fa-solid-900.ttf') format('truetype'); | |||
| font-weight: 900; | |||
| font-style: normal; | |||
| } | |||
| @@ -4,16 +4,22 @@ | |||
| "version": "0.3", | |||
| "description": "Get rid of sticky elements on websites - permanently!", | |||
| "author": "Said Achmiz", | |||
| "homepage_url": "https://wiki.obormot.net/Main/AlwaysKillSticky", | |||
| "permissions": [ "activeTab", "storage" ], | |||
| "homepage_url": "https://git.sr.ht/~achmizs/AlwaysKillSticky.git", | |||
| "permissions": [ "declarativeContent", "activeTab", "storage" ], | |||
| "background": { | |||
| "scripts": ["background.js"], | |||
| "persistent": false | |||
| }, | |||
| "content_scripts": [ | |||
| { | |||
| "matches": [ "http://*/*", "https://*/*" ], | |||
| "run_at": "document_end", | |||
| "js": [ "contentScript.js" ] | |||
| "js": [ "functions.js", "contentScript.js" ] | |||
| } | |||
| ], | |||
| "page_action": { | |||
| "default_popup": "popup.html", | |||
| "default_title": "AlwaysKillSticky - Click the icon to control sticky-killing on this site!", | |||
| "default_icon": { | |||
| "16": "images/ASK16.png", | |||
| "32": "images/ASK32.png", | |||
| @@ -0,0 +1,173 @@ | |||
| html { | |||
| box-sizing: border-box; | |||
| font-size: 16px; | |||
| } | |||
| *, *::before, *::after { | |||
| box-sizing: inherit; | |||
| } | |||
| body { | |||
| margin: 0 auto; | |||
| display: flex; | |||
| flex-flow: column; | |||
| max-width: 640px; | |||
| min-height: 100vh; | |||
| padding: 10px; | |||
| font-size: 1rem; | |||
| } | |||
| body > div { | |||
| border: 1px solid #ddd; | |||
| padding: 30px 15px 15px 15px; | |||
| position: relative; | |||
| } | |||
| body > div + div { | |||
| margin: 2.5em 0 0 0; | |||
| } | |||
| h1 { | |||
| border-bottom: 1px solid #ddd; | |||
| margin: 0.5em 0; | |||
| } | |||
| h2 { | |||
| margin: 0; | |||
| background-color: #fff; | |||
| padding: 10px 15px; | |||
| position: absolute; | |||
| top: calc(-1 * (0.625em + 10px)); | |||
| left: 10px; | |||
| border: inherit; | |||
| } | |||
| textarea { | |||
| border: 1px solid #aaa; | |||
| font-family: Inconsolata, Courier, monospace; | |||
| font-size: 1.125rem; | |||
| width: 100%; | |||
| display: block; | |||
| } | |||
| #matchingPatterns textarea { | |||
| min-height: 300px; | |||
| } | |||
| #exclusionPatterns textarea { | |||
| min-height: 150px; | |||
| } | |||
| form { | |||
| text-align: right; | |||
| margin: 0 0 2.5em 0; | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| form .buttons { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: flex-end; | |||
| flex: 1 1 0; | |||
| } | |||
| button { | |||
| -webkit-appearance: none; | |||
| -moz-appearance: none; | |||
| border: 1px solid #bbb; | |||
| color: #fff; | |||
| padding: 10px 16px; | |||
| cursor: default; | |||
| margin: 0 0 0 0.5em; | |||
| } | |||
| button.save-button { | |||
| background-color: #16e; | |||
| font-size: 1.5rem; | |||
| } | |||
| button.reset-button { | |||
| background-color: #d3453d; | |||
| font-size: 1rem; | |||
| } | |||
| button:active { | |||
| transform: scale(0.95); | |||
| } | |||
| button:focus:active { | |||
| outline: none; | |||
| } | |||
| button:disabled { | |||
| background-color: #777; | |||
| opacity: 0.3; | |||
| } | |||
| input#whitelist-mode { | |||
| display: block; | |||
| -webkit-appearance: none; | |||
| -moz-appearance: none; | |||
| border: 1px solid #000; | |||
| font-size: 1rem; | |||
| height: 2em; | |||
| width: 4em; | |||
| margin: 0 6px; | |||
| background-color: #000; | |||
| box-shadow: | |||
| -2em 0 0 1px #fff inset, | |||
| 0 0 0 3px #fff inset; | |||
| } | |||
| input#whitelist-mode:checked { | |||
| border: 1px solid #fff; | |||
| background-color: #fff; | |||
| box-shadow: | |||
| 2em 0 0 1px #000 inset, | |||
| 0 0 0 3px #000 inset; | |||
| } | |||
| input#whitelist-mode, | |||
| .mode-select-container span { | |||
| cursor: pointer; | |||
| } | |||
| .mode-select-container { | |||
| white-space: nowrap; | |||
| display: flex; | |||
| align-items: center; | |||
| align-self: flex-start; | |||
| border: 1px solid #ddd; | |||
| padding: 8px 10px; | |||
| cursor: default; | |||
| background-color: #fff; | |||
| } | |||
| .mode-select-container.whitelist { | |||
| background-color: #000; | |||
| color: #fff; | |||
| } | |||
| .mode-select-container:not(.whitelist) .blacklist-mode-label, | |||
| .mode-select-container.whitelist .whitelist-mode-label { | |||
| border-bottom: 1px dotted currentColor; | |||
| } | |||
| .mode-select-container span::selection { | |||
| background-color: transparent; | |||
| } | |||
| .mode-select-container span.disabled { | |||
| pointer-events: none; | |||
| } | |||
| .mode-select-container span:not(.disabled):hover { | |||
| border-bottom: 1px solid currentColor; | |||
| } | |||
| form button:not(:disabled):hover { | |||
| text-shadow: | |||
| 0 0 1px #fff, | |||
| 0 0 3px #fff, | |||
| 0 0 5px #fff; | |||
| cursor: pointer; | |||
| } | |||
| #matchingPatterns.disabled h2 { | |||
| color: rgba(0,0,0,0.15); | |||
| } | |||
| #matchingPatterns.disabled p { | |||
| opacity: 0.2; | |||
| } | |||
| #matchingPatterns.disabled .textarea-container { | |||
| position: relative; | |||
| } | |||
| #matchingPatterns.disabled .textarea-container::before { | |||
| content: ".*"; | |||
| display: flex; | |||
| position: absolute; | |||
| background-color: rgba(232,232,232,0.9); | |||
| width: 100%; | |||
| height: 100%; | |||
| z-index: 1; | |||
| justify-content: center; | |||
| align-items: center; | |||
| font-family: Inconsolata, Courier, monospace; | |||
| font-size: 4em; | |||
| } | |||
| body > p { | |||
| padding: 0 15px; | |||
| } | |||
| @@ -3,181 +3,7 @@ | |||
| <head> | |||
| <title>Always Kill Sticky - Options</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"/> | |||
| <style> | |||
| html { | |||
| box-sizing: border-box; | |||
| font-size: 16px; | |||
| } | |||
| *, *::before, *::after { | |||
| box-sizing: inherit; | |||
| } | |||
| body { | |||
| margin: 0 auto; | |||
| display: flex; | |||
| flex-flow: column; | |||
| max-width: 640px; | |||
| min-height: 100vh; | |||
| padding: 10px; | |||
| font-size: 1rem; | |||
| } | |||
| body > div { | |||
| border: 1px solid #ddd; | |||
| padding: 30px 15px 15px 15px; | |||
| position: relative; | |||
| } | |||
| body > div + div { | |||
| margin: 2.5em 0 0 0; | |||
| } | |||
| h1 { | |||
| border-bottom: 1px solid #ddd; | |||
| margin: 0.5em 0; | |||
| } | |||
| h2 { | |||
| margin: 0; | |||
| background-color: #fff; | |||
| padding: 10px 15px; | |||
| position: absolute; | |||
| top: calc(-1 * (0.625em + 10px)); | |||
| left: 10px; | |||
| border: inherit; | |||
| } | |||
| textarea { | |||
| border: 1px solid #aaa; | |||
| font-family: Inconsolata, Courier, monospace; | |||
| font-size: 1.125rem; | |||
| width: 100%; | |||
| display: block; | |||
| } | |||
| #matchingPatterns textarea { | |||
| min-height: 300px; | |||
| } | |||
| #exclusionPatterns textarea { | |||
| min-height: 150px; | |||
| } | |||
| form { | |||
| text-align: right; | |||
| margin: 0 0 2.5em 0; | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| form .buttons { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: flex-end; | |||
| flex: 1 1 0; | |||
| } | |||
| button { | |||
| -webkit-appearance: none; | |||
| -moz-appearance: none; | |||
| border: 1px solid #bbb; | |||
| color: #fff; | |||
| padding: 10px 16px; | |||
| cursor: default; | |||
| margin: 0 0 0 0.5em; | |||
| } | |||
| button.save-button { | |||
| background-color: #16e; | |||
| font-size: 1.5rem; | |||
| } | |||
| button.reset-button { | |||
| background-color: #d3453d; | |||
| font-size: 1rem; | |||
| } | |||
| button:active { | |||
| transform: scale(0.95); | |||
| } | |||
| button:focus:active { | |||
| outline: none; | |||
| } | |||
| button:disabled { | |||
| background-color: #777; | |||
| opacity: 0.3; | |||
| } | |||
| input#whitelist-mode { | |||
| display: block; | |||
| -webkit-appearance: none; | |||
| -moz-appearance: none; | |||
| border: 1px solid #000; | |||
| font-size: 1rem; | |||
| height: 2em; | |||
| width: 4em; | |||
| margin: 0 6px; | |||
| background-color: #000; | |||
| box-shadow: | |||
| -2em 0 0 1px #fff inset, | |||
| 0 0 0 3px #fff inset; | |||
| } | |||
| input#whitelist-mode:checked { | |||
| border: 1px solid #fff; | |||
| background-color: #fff; | |||
| box-shadow: | |||
| 2em 0 0 1px #000 inset, | |||
| 0 0 0 3px #000 inset; | |||
| } | |||
| input#whitelist-mode, | |||
| .mode-select-container span { | |||
| cursor: pointer; | |||
| } | |||
| .mode-select-container { | |||
| white-space: nowrap; | |||
| display: flex; | |||
| align-items: center; | |||
| align-self: flex-start; | |||
| border: 1px solid #ddd; | |||
| padding: 8px 10px; | |||
| cursor: default; | |||
| background-color: #fff; | |||
| } | |||
| .mode-select-container.whitelist { | |||
| background-color: #000; | |||
| color: #fff; | |||
| } | |||
| .mode-select-container:not(.whitelist) .blacklist-mode-label, | |||
| .mode-select-container.whitelist .whitelist-mode-label { | |||
| border-bottom: 1px dotted currentColor; | |||
| } | |||
| .mode-select-container span::selection { | |||
| background-color: transparent; | |||
| } | |||
| .mode-select-container span.disabled { | |||
| pointer-events: none; | |||
| } | |||
| .mode-select-container span:not(.disabled):hover { | |||
| border-bottom: 1px solid currentColor; | |||
| } | |||
| form button:not(:disabled):hover { | |||
| text-shadow: | |||
| 0 0 1px #fff, | |||
| 0 0 3px #fff, | |||
| 0 0 5px #fff; | |||
| cursor: pointer; | |||
| } | |||
| #matchingPatterns.disabled h2 { | |||
| color: rgba(0,0,0,0.15); | |||
| } | |||
| #matchingPatterns.disabled p { | |||
| opacity: 0.2; | |||
| } | |||
| #matchingPatterns.disabled .textarea-container { | |||
| position: relative; | |||
| } | |||
| #matchingPatterns.disabled .textarea-container::before { | |||
| content: ".*"; | |||
| display: flex; | |||
| position: absolute; | |||
| background-color: rgba(232,232,232,0.9); | |||
| width: 100%; | |||
| height: 100%; | |||
| z-index: 1; | |||
| justify-content: center; | |||
| align-items: center; | |||
| font-family: Inconsolata, Courier, monospace; | |||
| font-size: 4em; | |||
| } | |||
| body > p { | |||
| padding: 0 15px; | |||
| } | |||
| </style> | |||
| <link rel='stylesheet' type='text/css' href='options.css' /> | |||
| </head> | |||
| <body> | |||
| <h1>Always Kill Sticky</h1> | |||
| @@ -0,0 +1,93 @@ | |||
| html { | |||
| font-size: 16px; | |||
| box-sizing: border-box; | |||
| } | |||
| *, *::before, *::after { | |||
| box-sizing: inherit; | |||
| } | |||
| body { | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| button { | |||
| -webkit-appearance: none; | |||
| -moz-appearance: none; | |||
| background-color: #fff; | |||
| border: none; | |||
| cursor: pointer; | |||
| } | |||
| button:active { | |||
| transform: scale(0.95); | |||
| } | |||
| button:focus { | |||
| outline: none; | |||
| } | |||
| button::selection { | |||
| background-color: transparent; | |||
| } | |||
| #main-button-container { | |||
| display: flex; | |||
| flex-flow: column; | |||
| } | |||
| #main-button-container button { | |||
| padding: 10px; | |||
| font-size: 6rem; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| height: 150px; | |||
| width: 150px; | |||
| color: #ccc; | |||
| font-family: Font Awesome, sans-serif; | |||
| } | |||
| #main-button-container button.active { | |||
| color: #000; | |||
| } | |||
| #main-button-container button:hover { | |||
| text-shadow: | |||
| 0 0 1px #fff, | |||
| 0 0 3px #fff, | |||
| 0 0 5px #fff; | |||
| } | |||
| #main-button-container button:hover { | |||
| color: #999; | |||
| } | |||
| #main-button-container button::before { | |||
| content: "\F08D"; | |||
| font-weight: 900; | |||
| position: relative; | |||
| top: 3px; | |||
| } | |||
| #main-button-container button::after { | |||
| content: "\F05E"; | |||
| position: absolute; | |||
| font-weight: 300; | |||
| color: #c00; | |||
| opacity: 0.0; | |||
| transform: scaleX(-1) scale(1.5); | |||
| } | |||
| #main-button-container button.active::after { | |||
| opacity: 1.0; | |||
| } | |||
| #aux-button-container { | |||
| display: flex; | |||
| border-top: 1px solid #ddd; | |||
| } | |||
| .options-button { | |||
| font-size: 1.125rem; | |||
| padding: 10px; | |||
| color: #777; | |||
| width: 100%; | |||
| } | |||
| .options-button:hover { | |||
| color: #000; | |||
| } | |||
| .options-button::before { | |||
| content: "\F1DE"; | |||
| font-family: Font Awesome; | |||
| font-size: 0.875em; | |||
| margin: 0 5px 0 0; | |||
| } | |||
| .options-button::after { | |||
| content: "Options"; | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| <html> | |||
| <head> | |||
| <title>AlwaysKillSticky</title> | |||
| <link rel='stylesheet' type='text/css' href='popup.css' /> | |||
| <link rel='stylesheet' type='text/css' href='fonts/font-awesome.css' /> | |||
| </head> | |||
| <body> | |||
| <div id='main-button-container'> | |||
| <button type='button'></button> | |||
| </div> | |||
| <div id='aux-button-container'> | |||
| <button type='button' class='options-button'></button> | |||
| </div> | |||
| </body> | |||
| <script src="functions.js"></script> | |||
| <script src="popup.js"></script> | |||
| </html> | |||
| @@ -0,0 +1,147 @@ | |||
| /*******************/ | |||
| /* EVENT LISTENERS */ | |||
| /*******************/ | |||
| /***********/ | |||
| /* HELPERS */ | |||
| /***********/ | |||
| /* Toggle the current state, as represented in the ASK object. (Nothing actually | |||
| happens until the UI state is updated, killSticky() is called (if needed), | |||
| and the new settings are saved in storage.) | |||
| What “toggle the current state” actually means depends on the current mode. | |||
| [1] In blacklist mode, toggleState() does one of the following: | |||
| (a) (if stickies are not being killed on the current page) adds a matching | |||
| pattern (if one is not already there), and removes all applicable | |||
| exclusion patterns; or, | |||
| (b) (if stickies are being killed on the current page) removes all | |||
| applicable matching patterns. | |||
| [2] In whitelist mode, toggleState() does one of the following: | |||
| (a) (if stickies are not being killed on the current page) removes all | |||
| applicable exclusion patterns; or, | |||
| (b) (if stickies are being killed on the current page) adds an exclusion | |||
| pattern. | |||
| */ | |||
| function toggleState() { | |||
| if (ASK.mode == "blacklist") { | |||
| if (!ASK.pageMatched || ASK.pageExcluded) { | |||
| /* In this case, stickies are NOT being killed. We must add a matching | |||
| pattern, and remove all applicable exclusion patterns. */ | |||
| if (!ASK.pageMatched) { | |||
| ASK.matchingPatterns.push(regExpForCurrentTab()); | |||
| } | |||
| if (ASK.pageExcluded) { | |||
| ASK.exclusionPatterns = ASK.exclusionPatterns.filter(pattern => | |||
| !ASK.activeTabLocation.match(new RegExp(pattern)) | |||
| ); | |||
| } | |||
| } else { | |||
| /* In this case, stickies ARE being killed. We must remove all | |||
| applicable matching patterns. */ | |||
| ASK.matchingPatterns = ASK.matchingPatterns.filter(pattern => | |||
| !ASK.activeTabLocation.match(new RegExp(pattern)) | |||
| ); | |||
| } | |||
| } else { // if whitelist mode | |||
| // TODO: code this! | |||
| } | |||
| recalculatePatternEffects(); | |||
| } | |||
| function regExpForCurrentTab() { | |||
| let a = document.createElement("A"); | |||
| a.href = ASK.activeTabLocation; | |||
| return ".*" + a.host.replace(/\./g, "\\.") + ".*"; | |||
| } | |||
| function recalculatePatternEffects() { | |||
| ASK.pageMatched = false; | |||
| ASK.pageExcluded = false; | |||
| for (let pattern of ASK.matchingPatterns) { | |||
| if (ASK.activeTabLocation.match(new RegExp(pattern))) { | |||
| ASK.pageMatched = true; | |||
| break; | |||
| } | |||
| } | |||
| for (let pattern of ASK.exclusionPatterns) { | |||
| if (ASK.activeTabLocation.match(new RegExp(pattern))) { | |||
| ASK.pageExcluded = true; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| function updateState(result) { | |||
| ASK.mode = result.mode || "blacklist"; | |||
| ASK.matchingPatterns = (ASK.mode == "whitelist") ? | |||
| [ ".*" ] : | |||
| result.matchingPatterns.split("\n"); | |||
| ASK.exclusionPatterns = (typeof result.exclusionPatterns != "undefined" && | |||
| result.exclusionPatterns != "") ? | |||
| result.exclusionPatterns.split("\n") : | |||
| [ ]; | |||
| recalculatePatternEffects(); | |||
| } | |||
| function updateUIState() { | |||
| let button = document.querySelector("#main-button-container button"); | |||
| if (ASK.mode == "blacklist" && ASK.pageMatched && !ASK.pageExcluded) { | |||
| button.classList.toggle("active", true); | |||
| // button.innerHTML = "X"; | |||
| } else if (ASK.mode == "blacklist" && (!ASK.pageMatched || ASK.pageExcluded)) { | |||
| button.classList.toggle("active", false); | |||
| // button.innerHTML = "O"; | |||
| } else if (ASK.mode == "whitelist" && !ASK.pageExcluded) { | |||
| button.classList.toggle("active", false); | |||
| // button.innerHTML = "X"; | |||
| } else if (ASK.mode == "whitelist" && ASK.pageExcluded) { | |||
| button.classList.toggle("active", true); | |||
| // button.innerHTML = "O"; | |||
| } | |||
| } | |||
| /******************/ | |||
| /* INITIALIZATION */ | |||
| /******************/ | |||
| function initialize() { | |||
| window.ASK = { }; | |||
| chrome.tabs.query({currentWindow: true, active: true}, (tabs) => { | |||
| ASK.activeTabLocation = tabs[0].url; | |||
| chrome.storage.sync.get([ "matchingPatterns", "exclusionPatterns", "mode" ], (result) => { | |||
| updateState(result); | |||
| updateUIState(); | |||
| }); | |||
| }); | |||
| document.querySelector("#main-button-container button").addActivateEvent((event) => { | |||
| toggleState(); | |||
| let matchingPatterns = ASK.matchingPatterns.join("\n"); | |||
| let exclusionPatterns = ASK.exclusionPatterns.join("\n"); | |||
| let mode = ASK.mode; | |||
| chrome.storage.sync.set({ | |||
| "matchingPatterns": matchingPatterns, | |||
| "exclusionPatterns": exclusionPatterns, | |||
| "mode": mode | |||
| }, () => { | |||
| updateUIState(); | |||
| if (ASK.pageMatched && !ASK.pageExcluded) | |||
| chrome.tabs.executeScript(null, { code: 'killSticky()' }); | |||
| }); | |||
| }); | |||
| document.querySelector("#aux-button-container button").addActivateEvent((event) => { | |||
| chrome.runtime.openOptionsPage(null); | |||
| }); | |||
| } | |||
| initialize(); | |||