Skip to content

Commit

Permalink
Uses a browser action to communicate with the content script and extr…
Browse files Browse the repository at this point in the history
…act and print a list, with ability to copy text.

Don't think I actually need the browser action though!
  • Loading branch information
jeremyestein committed Dec 30, 2023
1 parent 6708187 commit 1551fd6
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 8 deletions.
106 changes: 106 additions & 0 deletions content_scripts/linkex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
(function() {
/**
* Check and set a global guard variable.
* If this content script is injected into the same page again,
* it will do nothing next time.
*/
if (window.hasRun) {
console.log('has already run, do nothing');

return;
}
window.hasRun = true;
console.log('running for first time...');

/**
* Given a URL to a beast image, remove all existing beasts, then
* create and style an IMG node pointing to
* that image, then insert the node into the document.
*/
function insertBeast(beastURL) {
removeExistingBeasts();
let beastImage = document.createElement("img");
beastImage.setAttribute("src", beastURL);
beastImage.style.height = "100vh";
beastImage.className = "beastify-image";
document.body.appendChild(beastImage);
}

/**
* Remove every beast from the page.
*/
function removeExistingBeasts() {
let existingBeasts = document.querySelectorAll(".beastify-image");
for (let beast of existingBeasts) {
beast.remove();
}
}

function extractLinks() {
// shopping basket items are in li tags with class "basket-item"

//var all_links = document.getElementsByTagName('a');
var basket_items = document.querySelectorAll('li.basket-item');
var basket_items_links = document.querySelectorAll('li.basket-item > a');
//var basket_items = document.getElementsByClassName('basket-item');
console.log('basket_items: ')
console.log(basket_items)
console.log('basket_items_links')
console.log(basket_items_links)
var all_items = []
for (let bi of basket_items) {
var link_in_title = bi.querySelector('.basket-item__title a');
var abs_url = link_in_title.href;
var wine_desc = link_in_title.text;
var stock_status_span = bi.querySelector('.badge-label');
try {
var quantity_value_sel = bi.querySelector('.basket-item__quantity-select select');
var quantity_value = quantity_value_sel.value;
var quantity_unit_sel = bi.querySelector('.basket-item__type select');
var quantity_unit = quantity_unit_sel.value;
} catch (err) {
var quantity_value = '?';
var quantity_unit = '?';
}
//var links_in_item = bi.querySelectorAll('a');
//var links_in_item = bi.getElementsByTagName('a');
var item = {
wine: wine_desc,
url: abs_url,
stock: stock_status_span.text,
qty_val: quantity_value,
qty_unit: quantity_unit
};
all_items.push(item);
//if (links_in_item.length > 0) {
// all_items.push(links_in_item[0]);
//} else {
// console.log('No links in LI: ' + bi);
//}
}
return all_items;
//return [
//"flub", "flib"
//];
}

/**
* Listen for messages from the background script.
* Call "beastify()" or "reset()".
*/
browser.runtime.onMessage.addListener((message) => {
if (message.command === "beastify") {
insertBeast(message.beastURL);
} else if (message.command === "reset") {
removeExistingBeasts();
} else if (message.command === "extractLinks") {
links = extractLinks();

// will want to send this back as a message, but just log for now...
console.log('links = ', links);
return Promise.resolve(links);
}
});

})();

12 changes: 6 additions & 6 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ document.body.style.border = "5px solid #ff0080";
console.log('main.js says hello');


browser.runtime.onMessage.addListener(request => {
document.body.style.border = "15px solid #ff0080";
console.log("Message from the background script:");
console.log(request.greeting);
return Promise.resolve({response: "Hi from content script"});
});
//browser.runtime.onMessage.addListener(request => {
//document.body.style.border = "15px solid #ff0080";
//console.log("Message from the background script:");
//console.log(request.greeting);
//return Promise.resolve({response: "Hi from content script"});
//});
8 changes: 6 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"version":"1.0",
"name":"Wine Society link extractor",
"description": "Extracts tabular data from shopping basket, wishlist, etc",

"permissions": [
"activeTab"
],
"icons": {
"64": "icons/wine64.png",
"32": "icons/wine32.png"
Expand All @@ -15,8 +19,8 @@
],
"browser_action": {
"default_icon": "icons/wine32.png",
"default_title": "Beastify",
"default_popup": "menu/actions.html"
"default_title": "Copy wine links",
"default_popup": "popup/copy_wine.html"
}

}
Expand Down
30 changes: 30 additions & 0 deletions popup/copy_wine.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
html, body {
width: 800px;
}

.hidden {
display: none;
}

button {
border: none;
width: 100%;
margin: 3% auto;
padding: 4px;
text-align: center;
font-size: 1.5em;
cursor: pointer;
background-color: #E5F2F2;
}

button:hover {
background-color: #CFF2F2;
}

button[type="reset"] {
background-color: #FBFBC9;
}

button[type="reset"]:hover {
background-color: #EAEA9D;
}
20 changes: 20 additions & 0 deletions popup/copy_wine.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>

<html>
<head>
<meta charset="utf-8">
<!-- why?? -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self';"/>
<link rel="stylesheet" href="copy_wine.css"/>
</head>

<body>
<div id="link-content">
</div>
<div id="error-content" class="hidden">
<p>Couldn't find any links</p>
</div>
<script src="copy_wine.js"></script>
</body>

</html>
130 changes: 130 additions & 0 deletions popup/copy_wine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"use strict";

/**
* CSS to hide everything on the page,
* except for elements that have the "beastify-image" class.
*/
const hidePage = `body > :not(.beastify-image) {
display: none;
}`;

/**
* Listen for clicks on the buttons, and send the appropriate message to
* the content script in the page.
*/
function listenForClicks() {
console.log('listenForClicks addingeventlistener');

/**
* Given the name of a beast, get the URL to the corresponding image.
*/
function beastNameToURL(beastName) {
switch (beastName) {
case "Frog":
return browser.runtime.getURL("beasts/frog.jpg");
case "Snake":
return browser.runtime.getURL("beasts/snake.jpg");
case "Turtle":
return browser.runtime.getURL("beasts/turtle.jpg");
}
}

/**
* Insert the page-hiding CSS into the active tab,
* then get the beast URL and
* send a "beastify" message to the content script in the active tab.
*/
function beastify(tabs) {
browser.tabs.insertCSS({code: hidePage}).then(() => {
const url = beastNameToURL(e.target.textContent);
browser.tabs.sendMessage(tabs[0].id, {
command: "beastify",
beastURL: url
});
});
}

function requestLinks(tabs) {
var promise = browser.tabs.sendMessage(tabs[0].id, {
command: "extractLinks"
});
promise.then((all_wines) => {
console.log('got data back!' + all_wines);

var table = document.createElement("TABLE");

for(var i = 0; i < all_wines.length; i++) {
var trow = table.insertRow(i);
var butt = document.createElement('BUTTON');
var copyString = 'yada yada ' + all_wines[i].wine;
butt.onclick = () => {
navigator.clipboard.writeText(copyString);
}

butt.innerHTML = 'Copy';
trow.insertCell().appendChild(butt);
trow.insertCell().innerHTML = all_wines[i].wine;
trow.insertCell().innerHTML = all_wines[i].url;
trow.insertCell().innerHTML = all_wines[i].stock;
trow.insertCell().innerHTML = 'val ' + all_wines[i].qty_val;
trow.insertCell().innerHTML = 'unit ' + all_wines[i].qty_unit;
}

var theadRow = table.createTHead().insertRow(0);
var headers = ['copy', 'wine', 'url', 'stock', 'qty_val', 'qty_unit'];
for(var i = 0; i < headers.length; i++) {
theadRow.insertCell(i).innerHTML = headers[i];
}

var table_place = document.getElementById('link-content');
table_place.appendChild(table);
});
}

/**
* Remove the page-hiding CSS from the active tab,
* send a "reset" message to the content script in the active tab.
*/
function reset(tabs) {
browser.tabs.removeCSS({code: hidePage}).then(() => {
browser.tabs.sendMessage(tabs[0].id, {
command: "reset",
});
});
}

/**
* Just log the error to the console.
*/
function reportError(error) {
console.error(`Could not beastify: ${error}`);
}

/**
* Get the active tab and request it to extract links and send them
* to us.
*/
browser.tabs.query({active: true, currentWindow: true})
.then(requestLinks)
.catch(reportError);
}

/**
* There was an error executing the script.
* Display the popup's error message, and hide the normal UI.
*/
function reportExecuteScriptError(error) {
document.querySelector("#popup-content").classList.add("hidden");
document.querySelector("#error-content").classList.remove("hidden");
console.error(`Failed to execute beastify content script: ${error.message}`);
}

/**
* When the popup loads, inject a content script into the active tab,
* and add a click handler.
* If we couldn't inject the script, handle the error.
*/
console.log('inject content script');
browser.tabs.executeScript({file: "/content_scripts/linkex.js"})
.then(listenForClicks)
.catch(reportExecuteScriptError);

0 comments on commit 1551fd6

Please sign in to comment.