Compare commits

..

No commits in common. "683749748b62b2015701e3e5712b31facc9359b7" and "3086c5ef41a12703f523a23b531b8fc9bfbfed66" have entirely different histories.

5 changed files with 131 additions and 247 deletions

View file

@ -1,6 +1,6 @@
/** /**
* Unchecker - Simple extension letting you uncheck all checkboxes on a page * Unchecker - Simple extension letting you uncheck all checkboxes on a page
* Copyright (c) Ad5001 2021-2023 * Copyright (c) Ad5001 2021
* *
* This Source Code Form is subject to the terms of the Mozilla Public License, * This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can * v. 2.0. If a copy of the MPL was not distributed with this file, You can
@ -9,67 +9,52 @@
// This file handles all clicking automaticly all similar buttons (e.g : same text & element name) // This file handles all clicking automaticly all similar buttons (e.g : same text & element name)
const clickAllScript = (targetElementId) => { const CLICK_ALL_SCRIPT = `
let bound, selectedElements, clickedButton, buttonText, classes, query, sameElements var bound, selectedElements, clickedButton, buttonText, classes, query, sameElements;
// Get the potential selected button // Get the potential selected button
bound = browser.menus.getTargetElement(targetElementId).getBoundingClientRect() bound = browser.menus.getTargetElement(targetElementId).getBoundingClientRect();
selectedElements = document.elementsFromPoint(bound.x+bound.width/2, bound.y+bound.height/2) selectedElements = document.elementsFromPoint(bound.x+bound.width/2, bound.y+bound.height/2)
// Leftover debug for positioning // Leftover debug for positioning
//console.log(bound, browser.menus.getTargetElement(targetElementId), selectedElements) //console.log(bound, browser.menus.getTargetElement(targetElementId), selectedElements)
//var div = document.createElement("div"); div.style.background = "black"; div.style.position = "absolute"; //var div = document.createElement("div"); div.style.background = "black"; div.style.position = "absolute";
//div.style.left = bound.x+bound.width/2-5 +"px"; div.style.top = bound.y+bound.height/2-5 + "px"; //div.style.left = bound.x+bound.width/2-5 +"px"; div.style.top = bound.y+bound.height/2-5 + "px";
//div.style.width = "10px"; div.style.height = "10px"; //div.style.width = "10px"; div.style.height = "10px";
//document.body.appendChild(div); //document.body.appendChild(div);
selectedElements = selectedElements.filter(x => ["BUTTON","A","INPUT"].indexOf(x.tagName) > -1) selectedElements = selectedElements.filter(x => ["BUTTON","A","INPUT"].indexOf(x.tagName) > -1)
// If a button is selected // If a button is selected
if(selectedElements.length > 0) { if(selectedElements.length > 0) {
clickedButton = selectedElements[0] clickedButton = selectedElements[0]
// Gather element that will be used in similar buttons (same text content). // Gather element that will be used in similar buttons (same text content).
buttonText = clickedButton.textContent.trim() buttonText = clickedButton.textContent.trim()
// Find the similar buttons // Find the similar buttons
query = clickedButton.localName + (clickedButton.tagName == "INPUT" ? "[type=" + clickedButton.type + "]" : "") query = clickedButton.localName + (clickedButton.tagName == "INPUT" ? "[type=" + clickedButton.type + "]" : "")
sameElements = document.querySelectorAll(query) sameElements = document.querySelectorAll(query)
sameElements = Array.from(sameElements).filter(btn => btn.textContent.trim() == buttonText) sameElements = Array.from(sameElements).filter(btn => btn.textContent.trim() == buttonText)
// Click them automaticly. // Click them automaticly.
sameElements.forEach(btn => { sameElements.forEach(btn => {
btn.click() btn.click()
}) })
}
} }
// Requires targetElementId to be defined beforehands ` // Requires targetElementId to be defined beforehands
const CLICK_ALL_TITLE = "Click all similar buttons" const CLICK_ALL_TITLE = "Click all similar buttons";
const CLICK_ALL_MENU_CONTEXTS = ["all"] const CLICK_ALL_MENU_CONTEXTS = ["editable", "image", "link", "page"];
function clickAllSimilarButtons(info, tab) { function clickAllSimilarButtons(info, tab) {
browser.scripting.executeScript({ browser.tabs.executeScript(tab.id, {
target: { frameId: info.frameId,
tabId: tab.id, code: `var targetElementId = ${info.targetElementId};${CLICK_ALL_SCRIPT}`,
frameIds: [ info.frameId ],
},
func: clickAllScript,
args: [ info.targetElementId ]
})
}
/**
* Creating menu.
*/
if(browser.menus) { // Not supported on Firefox for Android
browser.menus.create({
id: "unchecker-clickall",
title: CLICK_ALL_TITLE,
icons: {
"16": "icons/click.svg",
"32": "icons/click.svg"
},
contexts: CLICK_ALL_MENU_CONTEXTS,
}); });
browser.menus.onClicked.addListener((info, tab) => {
if(info.menuItemId == "unchecker-clickall")
clickAllSimilarButtons(info, tab)
})
} }
browser.menus.create({
id: "unchecker-clickall",
title: CLICK_ALL_TITLE,
icons: {
"16": "icons/click.svg",
"32": "icons/click.svg"
},
contexts: CLICK_ALL_MENU_CONTEXTS,
onclick(info, tab) { clickAllSimilarButtons(info, tab) }
});

View file

@ -1,21 +1,18 @@
{ {
"description": "Simple extension letting you uncheck all checkboxes on a page.", "description": "Simple extension letting you uncheck all checkboxes on a page.",
"manifest_version": 3, "manifest_version": 2,
"name": "unchecker", "name": "unchecker",
"version": "1.2.2", "version": "1.1",
"author": "Ad5001", "author": "Ad5001",
"developer": { "developer": {
"name": "Ad5001", "name": "Ad5001",
"url": "https://ad5001.eu" "url": "https://ad5001.eu"
}, },
"browser_specific_settings": { "applications": {
"gecko": { "gecko": {
"id": "unchecker@ad5001.eu", "id": "unchecker@ad5001.eu",
"strict_min_version": "113.0" "strict_min_version": "60.0"
},
"gecko_android": {
"strict_min_version": "113.0"
} }
}, },
"homepage_url": "https://apps.ad5001.eu/unchecker/", "homepage_url": "https://apps.ad5001.eu/unchecker/",
@ -24,9 +21,9 @@
"scripts": ["uncheck.js", "clickall.js"] "scripts": ["uncheck.js", "clickall.js"]
}, },
"action": { "browser_action": {
"default_icon": "icons/on.svg", "default_icon": "icons/on.svg",
"default_title": "Uncheck all checkboxes" "browser_style": true
}, },
"icons": { "icons": {
"48": "icons/off.svg", "48": "icons/off.svg",
@ -35,8 +32,7 @@
"permissions": [ "permissions": [
"activeTab", "activeTab",
"tabs", "tabs",
"menus", "menus"
"scripting"
] ]
} }

View file

@ -1,86 +0,0 @@
<!--
* Unchecker - Simple extension letting you uncheck all checkboxes on a page
* Copyright (c) Ad5001 2021-2023
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Unchecker tests in iframe</title>
<style>
legend {
background-color: #000;
color: #fff;
padding: 3px 6px;
}
.toggled {
border-color: blue;
}
input {
margin: .4rem;
}
</style>
<script src="test.js"></script>
</head>
<body>
<fieldset>
<legend>Example radio list</legend>
<input type="radio" id="choice-value1" name="choice-value">
<label for="choice-value">Example, irrelevant choice value 1</label><br/>
<input type="radio" id="choice-value2" name="choice-value">
<label for="choice-value">Example, irrelevant choice value 2</label><br/>
</fieldset>
<br>
<fieldset>
<legend>Third party partners example</legend>
<input type="checkbox" id="ga-choice" checked="true">
<label for="ga-choice">Google Analytics</label><br/>
<input type="checkbox" id="facebook-ads" checked="true">
<label for="facebook-ads">Facebook Partnering</label><br/>
<input type="checkbox" id="amazon-sales" checked="true">
<label for="amazon-sales">Amazon</label><br/>
<input type="checkbox" id="salesforce" checked="true">
<label for="salesforce">Salesforce</label><br/>
</fieldset>
<br>
<fieldset>
<legend>Third party partners second</legend>
<table>
<tr>
<td>Google Analytics</td>
<td><button role="toggle" class="toggled" name="ga-choice">On</button></td>
<td><button role="toggle" name="ga-choice">Off</button></td>
</tr>
<tr>
<td>Facebook Partnering</td>
<td><button role="toggle" class="toggled" name="facebook-ads">On</button></td>
<td><button role="toggle" name="facebook-ads">Off</button></td>
</tr>
<tr>
<td>Amazon</td>
<td><button role="toggle" class="toggled" name="amazon-sales">On</button></td>
<td><button role="toggle" name="amazon-sales">Off</button></td>
</tr>
<tr>
<td>Salesforce</td>
<td><button role="toggle" class="toggled" name="salesforce">On</button></td>
<td><button role="toggle" name="salesforce">Off</button></td>
</tr>
</table>
</fieldset>
</body>
</html>

View file

@ -1,6 +1,6 @@
<!-- <!--
* Unchecker - Simple extension letting you uncheck all checkboxes on a page * Unchecker - Simple extension letting you uncheck all checkboxes on a page
* Copyright (c) Ad5001 2021-2023 * Copyright (c) Ad5001 2021
* *
* This Source Code Form is subject to the terms of the Mozilla Public License, * This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can * v. 2.0. If a copy of the MPL was not distributed with this file, You can
@ -25,11 +25,6 @@
input { input {
margin: .4rem; margin: .4rem;
} }
iframe {
width: 100%;
height: 500px;
}
</style> </style>
<script src="test.js"></script> <script src="test.js"></script>
</head> </head>
@ -86,9 +81,5 @@
</tr> </tr>
</table> </table>
</fieldset> </fieldset>
<fieldset>
<legend>IFramed tests</legend>
<iframe src="./frame.html"></iframe>
</body> </body>
</html> </html>

View file

@ -1,148 +1,146 @@
/** /**
* Unchecker - Simple extension letting you uncheck all checkboxes on a page * Unchecker - Simple extension letting you uncheck all checkboxes on a page
* Copyright (c) Ad5001 2021-2023 * Copyright (c) Ad5001 2021
* *
* This Source Code Form is subject to the terms of the Mozilla Public License, * This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can * v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. * obtain one at http://mozilla.org/MPL/2.0/.
**/ **/
const uncheckAll = () => { const UNCHK_SCRIPT = `
function disable_checkboxes(element){
document.body.querySelectorAll('input[type="checkbox"]').forEach(function(el){ document.body.querySelectorAll('input[type="checkbox"]').forEach(function(el){
el.checked = false el.checked = false
}) })
} }
disable_checkboxes(document.body)
const checkAll = () => { document.body.querySelectorAll('iframe').forEach(function(el){disable_checkboxes(el.contentDocument || el.contentWindow.document)})
`
const CHK_SCRIPT = `
function check_checkboxes(element){
document.body.querySelectorAll('input[type="checkbox"]').forEach(function(el){ document.body.querySelectorAll('input[type="checkbox"]').forEach(function(el){
el.checked = true el.checked = true
}) })
} }
const TITLE_UNCHECK = "Uncheck all checkboxes" check_checkboxes(document.body)
const TITLE_CHECK = "Check all checkboxes" document.body.querySelectorAll('iframe').forEach(function(el){check_checkboxes(el.contentDocument || el.contentWindow.document)})
const APPLICABLE_PROTOCOLS = ["http:", "https:"] `
const MENU_CONTEXTS = ["all"] const TITLE_APPLY = "Uncheck all checkboxes";
const TITLE_REMOVE = "Check all checkboxes";
const APPLICABLE_PROTOCOLS = ["http:", "https:"];
const MENU_CONTEXTS = ["editable", "image", "link", "page"];
/** /**
* Toggle Script: based on the current title, insert or remove the Script. * Toggle Script: based on the current title, insert or remove the Script.
* Update the page actions title and icon, aswell as the context menu item to reflect its state. * Update the page action's title and icon, aswell as the context menu item to reflect its state.
**/ **/
function toggleScript(tab) { function toggleScript(tab) {
function gotTitle(title) { function gotTitle(title) {
console.log((title === TITLE_UNCHECK ? "Unchecking all checkboxes..." : "Checking all checkboxes...") + " at tab " + tab.id) if (title === TITLE_APPLY) {
if(title === TITLE_UNCHECK) { browser.browserAction.setIcon({tabId: tab.id, path: "icons/off.svg"});
browser.action.setIcon({tabId: tab.id, path: "icons/off.svg"}) browser.browserAction.setTitle({tabId: tab.id, title: TITLE_REMOVE});
browser.action.setTitle({tabId: tab.id, title: TITLE_CHECK}) browser.menus.remove("unchecker-main").then(function() {
browser.scripting.executeScript({ browser.menus.create({
func: uncheckAll, id: "unchecker-main",
target: { title: TITLE_REMOVE,
tabId: tab.id,
allFrames: true
}
})
if(browser.menus)
browser.menus.update("unchecker-main", {
title: TITLE_CHECK,
icons: { icons: {
"16": "icons/off.svg" "16": "icons/off.svg"
} },
}) contexts: MENU_CONTEXTS,
onclick(info,tab) { toggleScript(tab) }
});
}, function(){})
browser.tabs.executeScript({
code: UNCHK_SCRIPT
});
} else { } else {
browser.action.setIcon({tabId: tab.id, path: "icons/on.svg"}) browser.browserAction.setIcon({tabId: tab.id, path: "icons/on.svg"});
browser.action.setTitle({tabId: tab.id, title: TITLE_UNCHECK}) browser.browserAction.setTitle({tabId: tab.id, title: TITLE_APPLY});
browser.scripting.executeScript({ browser.menus.remove("unchecker-main").then(function() {
func: checkAll, browser.menus.create({
target: { id: "unchecker-main",
tabId: tab.id, title: TITLE_APPLY,
allFrames: true,
}
})
if(browser.menus)
browser.menus.update("unchecker-main", {
title: TITLE_UNCHECK,
icons: { icons: {
"16": "icons/on.svg" "16": "icons/on.svg"
} },
}) contexts: MENU_CONTEXTS,
onclick(info,tab) { toggleScript(tab) }
});
}, function(){})
browser.tabs.executeScript({
code: CHK_SCRIPT
});
} }
} }
browser.action.getTitle({tabId: tab.id}).then(gotTitle) var gettingTitle = browser.browserAction.getTitle({tabId: tab.id});
gettingTitle.then(gotTitle);
} }
/** /**
* Returns true only if the URL's protocol is in APPLICABLE_PROTOCOLS. * Returns true only if the URL's protocol is in APPLICABLE_PROTOCOLS.
**/ **/
function protocolIsApplicable(url) { function protocolIsApplicable(url) {
let anchor = document.createElement('a') let anchor = document.createElement('a');
anchor.href = url anchor.href = url;
return APPLICABLE_PROTOCOLS.includes(anchor.protocol) return APPLICABLE_PROTOCOLS.includes(anchor.protocol);
} }
/** /**
* Initialize the page action set icon and title, then show. * Initialize the page action: set icon and title, then show.
* Only operates on tabs whose URL's protocol is applicable. * Only operates on tabs whose URL's protocol is applicable.
**/ **/
function initializebrowserAction(tab, createMenu) { function initializebrowserAction(tab, createMenu) {
// console.log("Initializing browser action for tab", tab, createMenu)
if(protocolIsApplicable(tab.url)) { if(protocolIsApplicable(tab.url)) {
browser.action.setIcon({tabId: tab.id, path: "icons/on.svg"}) browser.browserAction.setIcon({tabId: tab.id, path: "icons/on.svg"});
browser.action.setTitle({tabId: tab.id, title: TITLE_UNCHECK}) browser.browserAction.setTitle({tabId: tab.id, title: TITLE_APPLY});
browser.browserAction.show(tab.id);
} }
if(createMenu && browser.menus) { if(createMenu) {
browser.menus.remove("unchecker-main").then(function() { browser.menus.remove("unchecker-main").then(function() {
browser.menus.create({ browser.menus.create({
id: "unchecker-main", id: "unchecker-main",
title: TITLE_UNCHECK, title: TITLE_APPLY,
icons: { icons: {
"16": "icons/on.svg" "16": "icons/on.svg"
}, },
contexts: MENU_CONTEXTS contexts: MENU_CONTEXTS,
}) onclick(info,tab) { toggleScript(tab) }
}) });
}, function(){})
} }
} }
/**
* Create the default context menu for the main function.
*/
if(browser.menus) { // Not supported on Firefox for Android
browser.menus.create({
id: "unchecker-main",
title: TITLE_UNCHECK,
icons: {
"16": "icons/on.svg"
},
contexts: MENU_CONTEXTS
})
browser.menus.onClicked.addListener((info, tab) => {
if(info.menuItemId == "unchecker-main")
toggleScript(tab)
})
}
/** /**
* When first loaded, initialize the page action for all tabs. * When first loaded, initialize the page action for all tabs.
**/ **/
browser.menus.create({
browser.tabs.query({}).then((tabs) => { id: "unchecker-main",
for(let tab of tabs) { title: TITLE_APPLY,
initializebrowserAction(tab, false) icons: {
"16": "icons/on.svg"
},
contexts: MENU_CONTEXTS,
onclick(info,tab) { toggleScript(tab) }
});
let gettingAllTabs = browser.tabs.query({});
gettingAllTabs.then((tabs) => {
for (let tab of tabs) {
initializebrowserAction(tab, false);
} }
}); });
/** /**
* Each time a tab is updated, reset the page browser.action.for that tab. * Each time a tab is updated, reset the page action for that tab.
**/ **/
browser.tabs.onUpdated.addListener((id, changeInfo, tab) => { browser.tabs.onUpdated.addListener((id, changeInfo, tab) => {
initializebrowserAction(tab, true) initializebrowserAction(tab, true);
}); });
/** /**
* Toggle Script when the page browser.action.is clicked. * Toggle Script when the page action is clicked.
**/ **/
browser.action.onClicked.addListener(toggleScript) browser.browserAction.onClicked.addListener(toggleScript);