I am trying to use a google extension to keep name of people that I have already seen on Linkedin
Form this picture when I click the next page or change the page I am trying to get the google extension to keep the track of the name of person in this page.
This is my Manifest:
{
"manifest_version": 2,
"name": "One test",
"description": "test.",
"version": "1.0",
"background": {"scripts": ["test.js"]},
"page_action":
{
"default_icon": "pic1.png"
},
"permissions": ["tabs"],
"content_security_policy": "script-src 'self' https://www.jbhired.com; object-src 'self' "
}
And these are the functions I am trying to use to keep track of the data:
function check(tab_id, data, tab) {
if (tab.url.indexOf('https://www.linkedin.com/search/results/people/') > -1) {
chrome.pageAction.show(tab_id);
}
};
chrome.tabs.onUpdated.addListener(check);
// _______________________________________________________________________
var data = (function(){
return {
bigArr: [],
}
})();
var interfaceCtrl = (function() {
var selector = document.querySelector('.page-list');
return {
selectTwo: selector.childNodes[1].childNodes[5],
cons: document.querySelector('.nav-item__profile-member-photo').alt,
nextClass: document.querySelector('.next'),
selectOne: selector.childNodes[1].childNodes[2].classList.value
}
})();
var listeners = (function(arr, ui){
document.querySelector('.results-paginator').addEventListener('click',function(event){
if(event.target.innerText === ui.selectTwo.innerText || ( event.target.classList.value === 'next-text' && ui.selectOne == 'active')) {
arr.bigArr.push({
consultant: ui.cons,
search: window.location.href,
time: Date.now()
});
}
});
})(data,interfaceCtrl);
I am trying try but it's still not working. I'm really a newbie on both javascript and google extensions. help me please :(
Related
I'm trying to add DOM elements to the page https://anilist.co/user/diskxo/animelist
var divEntryRow = document.getElementsByClassName("entry row")[x]
var playbutton = document.createElement("DIV");
playbutton.className = "playbutton";
var aInput = document.createElement("A");
var img = document.createElement("IMG");
aInput.appendChild(img);
playbutton.appendChild(aInput);
divEntryRow.appendChild(playbutton);
, but not the whole page is shown on loading, so my extension doesn't
add the elements in some places (play buttons on right)
also, when I move between the tabs of the site, the extension is not reloaded, since updates are made in the background only by ajax. I've been looking for ways to detect changes and reload the extension, including these:
$.ajax({
processData: false,
contentType: false,
success: function() {
doThings();
},
});
Or:
function DOMModificationHandler(){
$(this).unbind('DOMSubtreeModified.event1');
setTimeout(function(){
doThings();
$('#ContentContainer').bind('DOMSubtreeModified.event1',DOMModificationHandler);
},1000);
}
//after document-load
$('#ContentContainer').bind('DOMSubtreeModified.event1',DOMModificationHandler);
I included jquery library in my Extension. This is my manifest.json file:
{
"manifest_version": 2,
"name": "JiyuMe",
"version": "1.0",
"description": "Your next anime streaming website... it's not a streaming website!",
"content_scripts": [{
"matches": ["*://*.anilist.co/*"],
"js": ["js/jquery-3.6.0.min.js", "js/client.js"]
}],
"background": {
"scripts": ["js/background.js"]
},
"browser_action": {
"default_popup": "popup.html"
},
"permissions": [
"*://anilist.co/*",
"nativeMessaging",
"tabs",
"activeTab"
],
"content_security_policy": "script-src 'self' https://apis.google.com; object-src 'self'"
}
Do you have any ideas to understand how to fix this problem?
For the second problem I resolved with a simple function:
//Sleep function
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
//Wait for Ajax site load
$.ajax({
processData: false,
contentType: false,
success: async function() {
onPageChange("1");
},
});
async function onPageChange(oldPage_val) {
var currentPage = window.location.href;
if (currentPage != oldPage_val) {
oldPage_val = currentPage;
pageScan();
await sleep(500);
onPageChange(oldPage_val);
} else {
await sleep(500);
onPageChange(oldPage_val);
}
}
I wait for the load of the page, and then I start onPageChange() function, that check every 500 ms if the link of the page changes.
compare the old link with the new one thanks to the variable: oldPage_val
Before you read this, it may be related to
How can a Chrome extension get a user's permission to use user's computer's microphone?
I've added an answer below, including code and my manifest, if that helps.
I am writing a minimal Chrome Extension (using Chrome 75.0.3770.90 on MacOS 10.14.5) to implement a 'listen' button for my accessibility project. I've written an HTML version with the JavaScript which works the microphone.
However, when I lift that code into the Extension background.js file, the text-to-speech works, but not the speech-to-text. The code runs, but the flashing mic never appears in the tab.
The code which works is:
<!DOCTYPE html>
<html>
<body>
<h2>All-in-one JavaScript Example</h2>
<button onclick="myCode();">Listen</button>
<script>
window.SpeechRecognition = window.webkitSpeechRecognition
|| window.SpeechRecognition;
function myCode() {
recognition = new SpeechRecognition();
recognition.start();
recognition.onresult = function(event) {
if (event.results[0].isFinal) {
response = event.results[0][0].transcript;
synth = window.speechSynthesis;
synth.speak( new SpeechSynthesisUtterance(
"i don't understand, "+response
));
} }
alert( "all-in-one: we're done!" );
}
</script>
</body>
</html>
Minimal reproducible example:
{
"name": "myName",
"description": "Press to talk",
"version": "0.97",
"manifest_version": 2,
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": ["contentSettings","desktopCapture","*://*/*","tabCapture","tabs","tts","ttsEngine"],
"browser_action": {
"default_icon": "images/myIcon512.png",
"default_title": "Press Ctrl(Win)/Command(Mac)+Shift+ Down to speak"
},
"commands": {
"myCommand": {
"suggested_key": {
"default": "Ctrl+Shift+Down",
"mac": "Command+Shift+Down"
},
"description": "Start speaking"
}
},
"icons": {
"512": "images/myIcon512.png"
}
}
My background JavaScript is:
window.SpeechRecognition = window.webkitSpeechRecognition || window.SpeechRecognition;
function myCode() {
var recognition = new SpeechRecognition();
recognition.onresult = function(event) {
if (event.results[0].isFinal) {
var synth = window.speechSynthesis;
synth.speak( new SpeechSynthesisUtterance(
"sorry, I don't understand."
)
);
}
}
recognition.start();
alert( "extension: we're done!" );
}
chrome.commands.onCommand.addListener(function(command) {
if (command === 'myCommand')
myCode();
});
I've also noticed that the code only runs once - I can keep on clicking in the listen button, but the Extension command only runs once (putting in an alert at the beginning of the function only gets displayed the first time around)
The default on my browser is that it should ask (once) which it does on the HTML version.
Thanks, just for reading this far! I've put an answer, with code, below.
The problem I had is that the mic seems to be a background task, whereas I'm trying to interact with tab contents. I don't think this is usual, and have ended up with a generic 'matches' value (*://*/*) in my manifest (in full):
{ "name": "Enguage(TM) - Let's all talk to the Web",
"short_name" : "Enguage",
"description": "A vocal Web interface",
"version": "0.98",
"manifest_version": 2,
"content_security_policy": "script-src 'self'; object-src 'self'",
"background": {
"scripts": ["kbTx.js"],
"persistent": false
},
"content_scripts": [
{ "matches" : ["*://*/*"],
"js": ["tabRx.js", "interp.js"]
} ],
"permissions": [
"activeTab",
"contentSettings",
"desktopCapture",
"tabCapture",
"tabs",
"tts"
],
"browser_action": {
"default_icon": "images/lbolt512.png",
"default_title": "Press Ctrl(Win)/Command(Mac)+Shift+ Space and speak"
},
"commands": {
"enguage": {
"suggested_key": {
"default": "Ctrl+Shift+Space",
"mac": "Command+Shift+Space"
},
"description": "single utterance"
} },
"icons": {
"16": "images/lbolt16.png",
"48": "images/lbolt48.png",
"128": "images/lbolt128.png",
"512": "images/lbolt512.png"
} }
I think Google might not like this! Anyway, I have put in a keyboard listener in my background code (kbTx.js):
chrome.commands.onCommand.addListener(function(command) {
if (command === 'enguage') {
// find the active tab...
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
//send it a message...
chrome.tabs.sendMessage(
tabs[0].id, // index not always 0?
null, // message sent - none required?
null // response callback - none expected!
//function(response) {console.log("done" /*response.farewell*/);}
);
});
}
});
And I've put in a context script which listens for this message (tabRx.js):
window.SpeechRecognition = window.webkitSpeechRecognition ||
window.SpeechRecognition;
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
var recognition = new SpeechRecognition();
recognition.start();
recognition.continuous = false;
recognition.onresult = function(event) {
if (event.results[0].isFinal) {
window.speechSynthesis.speak(
new SpeechSynthesisUtterance(
interp( event.results[0][0].transcript )
) );
} } }
);
The message listener essentially contains the code in the allInOne.html example above.
There may be other ways of doing this, but this works and seems reasonably lightweight.
Hope this helps.
Please feel free to add comments to this, if you think I can improve my code!
Basically what I am trying to do is send a message from the popup.js by grabbing values from the input fields, and onMessage in the content_script to assign those values from that local scope to the global scope. I can't manage to do it! I can close the whole code in the onMessage function, but then to get to next function of the code I have to keep clicking save on the chrome extension. If someone knows this way better than I do with chrome extensions and what not please review my code and guide me to progress. I have been on this for like a week and my head is about to expload!
I have tried enclosing all the code in the onMessage function in the content_script. I have tried flipping which .js files are sending the message and trying to send the input fields from the extension as a response. I have tried to exclude the sendMessage out of the .click DOM function. Reviewed Messaging API on Google Chrome Extension and im puzzled.
content.js file
chrome.runtime.onMessage.addListener (
function(request) {
item_name = request.name;
item_color = request.color;
item_category = request.category;
item_size = request.size;
console.log(item_name);
console.log(item_color);
console.log(item_category);
console.log(item_size);
});
var url = window.location.href;
var i;
var item_category;
var item_name;
var item_color;
var item_size;
console.log(item_name);
console.log(item_color);
console.log(item_category);
console.log(item_size);
popup.js file
document.getElementById("save_input").onclick = function() {
var item_name_save = document.getElementById("item_name_save").value;
var item_color_save = document.getElementById("item_color_save").value;
var item_category_save = document.getElementById("item_category_save").value;
var item_size_save = document.getElementById("item_size_save").value;
chrome.storage.sync.set({'item_name_save' : item_name_save}, function() {
console.log('Value is set to ' + item_name_save);
});
chrome.storage.sync.set({'item_color_save' : item_color_save}, function() {
console.log('Value is set to ' + item_color_save);
});
chrome.storage.sync.set({'item_category_save' : item_category_save}, function() {
console.log('Value is set to ' + item_category_save);
});
chrome.storage.sync.set({'item_size_save' : item_size_save}, function() {
console.log('Value is set to ' + item_size_save);
});
const msg = { name: item_name_save,
color: item_color_save,
category: item_category_save,
size: item_size_save
};
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, msg, function() {
});
});
location.reload();
// window.close();
}
manifest.json
{
"manifest_version": 2,
"name": "Supreme bot",
"description": "A bot for Supreme",
"version": "1.0",
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html",
"default_title": "Supreme bot"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["bot-functionallity.js"]
}
],
"permissions": [
"activeTab",
"tabs",
"storage",
"tabs"
]
}
(When all code is in the onMessage function, works but not as intended)
The Incorrect Functionality
I basically want the content.js to NOT be all inside of the onMessage function because that doesn't work properly, and have it assign values in the global scope from the onMessage function. I just want that info to be sent 1 time from the popup.js to the content.js.
I just set the value with chrome.storage.sync.set and use chrome.storage.sync.get in the other file...
I'm writing my first Chrome extension and have hit a brick wall when it comes to setting and using "options." I've used Google's documentation to learn how to set up an options page and have opted to set it as my default popup in the extension.
Here's my manifest for reference:
{
"manifest_version": 2,
"name": "MyExtension",
"description": "MyDescription",
"version": "0.0",
"options_page": "options.html",
"browser_action": {
"default_icon": "on.png",
"default_popup": "options.html",
"default_title": "Manage Tools!"
},
"permissions": [
"storage",
"tabs",
"activeTab",
"https://ajax.googleapis.com/"
],
"content_scripts": [{
"matches": ["specialURL.com*"],
"js": ["jquery-3.1.1.min.js", "content.js"]
}],
"web_accessible_resources": [
"script.js"
],
"background": {
"scripts": ["background.js"]
}
}
My content.js page contains the following:
var s = document.createElement('script');
s.src = chrome.extension.getURL('script.js');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
which loads my script file (script.js). Inside of script.js there are two methods
function foo() { -code- }
function bar() { -code- }
options.js:
function save_options() {
var alltoggle = document.getElementById('alltoggle').checked;
var footoggle = document.getElementById('footoggle').checked;
var bartoggle = document.getElementById('bartoggle').checked;
chrome.storage.sync.set({
allsetting: alltoggle,
foosetting: footoggle,
barsetting: bartoggle
}, function () {
// Update status to let user know options were saved.
var status = document.getElementById('status');
status.textContent = 'Options saved.';
setTimeout(function () {
status.textContent = '';
}, 750);
});
}
// Restores select box and checkbox state using the preferences
// stored in chrome.storage.
function restore_options() {
// Default to true
chrome.storage.sync.get({
allsetting: true,
foosetting: true,
barsetting: true
}, function (items) {
document.getElementById('alltoggle').checked = items.allsetting;
document.getElementById('footoggle').checked = items.foosetting;
document.getElementById('bartoggle').checked = items.barsetting;
});
}
document.addEventListener('DOMContentLoaded', restore_options);
document.getElementById('save').addEventListener('click', save_options);
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('#alltoggle').addEventListener('change', allHandler);
// Turn on/off all features
function allHandler() {
$("input:checkbox").prop('checked', $(this).prop("checked"));
}
});
The problem comes in when I try to load settings back and apply them to my content scripts. Specifically, I can't find how to do that anywhere.
Solved
At first I thought this was a messages issue, but it wasn't. By adding the following to content.js I was able to check storage for my settings and execute code from there.
var fooON;
chrome.storage.sync.get("foosetting", function(result) {
fooON = result.foosetting;
//confirm
console.log(result.foosetting);
});
var barON;
chrome.storage.sync.get("barsetting", function(result) {
barON = result.barsetting;
//confirm
console.log(result.barsetting);
});
Then, by separating foo() and bar() into two scripts I could make the script injection in content.js selective by adding if(fooOn){-inject foo()-} etc.
For others facing similar issues
You can access options saved to storage.sync by using the chrome.storage.sync.get() API call in your content scripts.
var yourSetting;
chrome.storage.sync.get("yourSetting", function(result) {
yourSetting = result.yourSetting;
});
Hi I've written the following extension for our company machines that should in practice remove any numbers from a list that is held on our webserver.
Whilst this works in it dramatically slows down loading of medium/webpages and chrome appears to look through the list of numbers (the favicon for example refreshes whilst this happens).
Any help or guidance for a better way of achieving the same result would be immensely appreciated.
[highlight.js]
// This array will all of the numbers to highlight
var numberArray = [''];
var UPDATE_INTERVAL = 57600; // Update after 1 Minute
// Retrieve script from storage
chrome.storage.local.get({
lastUpdated: 0,
code: ''
}, function(items) {
if (Date.now() - items.lastUpdated > UPDATE_INTERVAL) {
// Get updated file, and if found, save it.
get('http://webaddress/highlight.js', function(code) {
if (!code) return;
chrome.storage.local.set({lastUpdated: Date.now(), code: code});
});
}
if (items.code) // Cached script is available, use it
execute(items.code);
else // No cached version yet. Load from extension
get(chrome.extension.getURL('highlight.js'), execute);
});
//
function execute(code) {
try { window.eval(code); } catch (e) { console.error(e); }
// Run number replacement.
numberArray.forEach(function(v){
var number = v;
ve = new RegExp(number, "g");
document.body.innerHTML = document.body.innerHTML.replace(ve,"DO-NOT-CALL");
});
}
function get(url, callback) {
var x = new XMLHttpRequest();
x.onload = x.onerror = function() { callback(x.responseText); };
x.open('GET', url);
x.send();
}
[manifest.json]
{
"background": {
},
"content_scripts": [ {
"js": [ "highlight.js"],
"run_at" : "document_idle",
"matches": [ "http://*/*", "https://*/*" ],
"css": [ "style.css" ]
} ],
"content_security_policy": "script-src 'self' 'unsafe-eval' https://ssl.google-analytics.com; object-src 'self'",
"description": "Detect telephone numbers and remove blocked numbers.",
"icons": {
"128": "icon_128.png",
"16": "icon_16.png",
"32": "icon_32.png",
"48": "icon_48.png"
},
"manifest_version": 2,
"name": "CLS Call Bar",
"permissions": [ "tabs", "storage", "http://*/*", "https://*/*" ],
"version": "1.0.0"
}
You are replacing innerHTML of whole document for every number which seems highly inefficient
document.body.innerHTML = document.body.innerHTML.replace(ve,"DO-NOT-CALL");
Instead you can use numbers in their immediate parent id/class as identifier like so
<span id="number-holder-{{number}}">{{number}}</span>
( it might already be like this as it is a common templating practice )
and in your content script do something like
numberArray.forEach(function(v){
document.getElementById("number-holder-"+v).innerHTML = "DO-NOT-CALL"
}
If changing the HTML is not an option then do the regex replace on a temporary variable with document.body.innerHTML as initial value and assign it back after the loop.
I've managed to solve this with thanks to Aman Verma for pointing out my replacing the whole innerHTML I've changed the following and its now very efficient...
From
numberArray.forEach(function(v){
var number = v;
ve = new RegExp(number, "g");
document.body.innerHTML = document.body.innerHTML.replace(ve,"DO-NOT-CALL");
});
To
numberArray.forEach(function(v){
var treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT),
textNode;
while(textNode = treeWalker.nextNode()) {
if(textNode.parentElement.tagName !== 'SCRIPT') {
textNode.nodeValue = textNode.nodeValue.replace(v, "DO NOT CALL THIS NUMBER");
}
}
});