Remember array after the popup has closed in a Chrome extension - javascript

I have some programming knowledge, however I didn't work extensively in Javascript or dealt with Chrome extensions before. I'm looking to build a Chrome extension that opens up the selected links in new tabs (that is the initial goal I want to achieve, as my final goal is to open the links in frames of the same tab - if possible -, in order to save the images from those links, similar to what the Save Images extension for Firefox is doing).
I have a problem though: the selected links from my popup are not being "remembered" in order to open them using a delay, after the popup window is closed (due to opening the new tabs). I guess this is related to not making a background script to perform the operation, instead of popup.js? Perhaps some message passing between those two Javascipt files? Just guessing here...
Here is my code so far:
manifest.json
{
"name": "Get links",
"version": "1.0",
"manifest_version": 2,
"description": "Get links from a page",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": ["tabs", "downloads", "<all_urls>"]
}
popup.html
<!DOCTYPE html>
<html>
<head>
<script src="popup.js"></script>
</head>
<body style="width:400px;">
<form id="linksform">
<p>
<label for="matchregex" style="margin-left: 7px;">RegEx Match: </label>
<input id="regextext" type="text" style="width:90px;" name="matchregex" value="" />
<input id="togglematches" type="button" value="Toggle Matches" />
<input id="openselected" type="button" value="Open Selected" />
</p>
<table id='links'>
<th></th>
</table>
</form>
</body>
</html>
popup.js
var alllinks = [];
var visiblelinks = [];
var selectedlinks = [];
var delay = 1000;
// Display all visible links.
function showlinks()
{
var togglematchesbutton = document.getElementById('togglematches');
var openselectedbutton = document.getElementById('openselected');
var linkstable = document.getElementById('links');
while (linkstable.children.length > 1)
{
linkstable.removeChild(linkstable.children[linkstable.children.length - 1])
}
for (var i = 0; i < visiblelinks.length; ++i)
{
var row = document.createElement('tr');
var col0 = document.createElement('td');
var col1 = document.createElement('td');
var checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = 'check' + i;
checkbox.checked = false;
checkbox.value = visiblelinks[i];
col0.appendChild(checkbox);
col1.innerText = visiblelinks[i];
col1.style.whiteSpace = 'nowrap';
col1.onclick = function()
{
checkbox.checked = !checkbox.checked;
}
row.appendChild(col0);
row.appendChild(col1);
linkstable.appendChild(row);
}
togglematchesbutton.onclick = function()
{
var regex = new RegExp(document.getElementById("regextext").value);
var getinputs = document.getElementsByTagName("input");
for (var i = 0, max = getinputs.length; i < max; i++)
{
if ((getinputs[i].type === 'checkbox') && (regex.test(getinputs[i].value))) getinputs[i].checked = !getinputs[i].checked;
}
}
openselectedbutton.onclick = function()
{
var getinputs = document.getElementsByTagName("input");
for (var i = 0, max = getinputs.length; i < max; i++)
{
if ((getinputs[i].type === 'checkbox') && (getinputs[i].checked))
{
selectedlinks.push(getinputs[i].value);
}
}
for (var i = 0, max = selectedlinks.length; i < max; i++)
{
window.setTimeout(function() {chrome.tabs.create({url: selectedlinks[i]});}, delay);
//chrome.tabs.create({url: selectedlinks[i]});
}
}
}
// Add links to alllinks and visiblelinks, sort and show them. Sendlinks.js is
// injected into all frames of the active tab, so this listener may be called
// multiple times.
chrome.extension.onMessage.addListener
(
function(links)
{
for (var index in links)
{
alllinks.push(links[index]);
}
alllinks.sort();
visiblelinks = alllinks;
//console.log(links);
showlinks();
}
);
// Set up event handlers and inject sendlinks.js into all frames in the active
// tab.
window.onload = function()
{
chrome.windows.getCurrent
(
function (currentWindow)
{
chrome.tabs.query
(
{active: true, windowId: currentWindow.id},
function(activeTabs)
{
chrome.tabs.executeScript(activeTabs[0].id, {file: 'sendlinks.js', allFrames: true});
}
);
}
);
};
sendlinks.js
// Send back to the popup a sorted deduped list of valid link URLs on this page.
// The popup injects this script into all frames in the active tab.
console.log("Injected");
var links = [].slice.apply(document.getElementsByTagName('a'));
console.log(links);
links = links.map
(
function(element)
{
// Return an anchor's href attribute, stripping any URL fragment (hash '#').
// If the html specifies a relative path, chrome converts it to an absolute
// URL.
var href = element.href;
var hashIndex = href.indexOf('#');
if (hashIndex >= 0)
{
href = href.substr(0, hashIndex);
}
return href;
}
);
links.sort();
// Remove duplicates and invalid URLs.
var kBadPrefix = 'javascript';
for (var i = 0; i < links.length;)
{
if (((i > 0) && (links[i] == links[i - 1])) || (links[i] == '') || (kBadPrefix == links[i].toLowerCase().substr(0, kBadPrefix.length)))
{
links.splice(i, 1);
}
else
{
++i;
}
}
console.log(links);
chrome.extension.sendMessage(links);
Note: The most part of this code is taken from somewhere else, and has been modified to suit to my needs.
The main issue is in this code snippet from popup.js:
for (var i = 0, max = selectedlinks.length; i < max; i++)
{
window.setTimeout(function() {chrome.tabs.create({url: selectedlinks[i]});}, delay);
//chrome.tabs.create({url: selectedlinks[i]});
}
If I comment the setTimeout line and uncomment the following line, it works (e.g. the extension opens the tabs successfully), but it doesn't use a delay between opening successive tabs - which is required for avoiding 'Too many requests' error on some sites. If I let this as it is, it opens the number of tabs that it is supposed to open (using the specified delay), but the urls of those tabs don't match the selected values (basically, they're blank). What I want is the latter to happen, but opening the selected links instead of blank tabs.
Could you please point where my mistake is, and suggest the code modifications to make this work? If doable, by keeping the existing code as close as possible to the posted version (aka perfoming only minor modifications to the source). Thank you.

Nevermind, I solved the issue. It turns out that, for some reason, Chrome didn't remember the i variable inside the for loop (maybe because of settimeout() working asynchronously compared to the for loop, due to the delay?), so it couldn't reference the correct array element.
All I had to do was replace the above
window.setTimeout(function() {chrome.tabs.create({url: selectedlinks[i]});}, delay);
with
window.setTimeout(function() {chrome.tabs.create({url: selectedlinks.shift()});}, delay);
and it worked flawlessly. Basically, instead of referencing the array element using the i variable, the updated code is always referencing the first array element, while removing it at the same time, thus "navigating" through the whole array and eliminating the elements for which tabs have been created.

Related

Tampermonkey userscript to scroll to first image with alt tag

I use a training site, that has a lot of courses. It is rather annoying that it does not remember the scroll position of your last course. I am trying to write a tampermonkey userscript to scroll to the first image with an alt tag of "Not Started" but it does not seem to work.
Here is what I have tried so far
var myList = document.getElementsByTagName("img");
for(var i=0;i<myList.length;i++)
{
if(myList[i].alt == "Not Started")
{
var pos = myList[i].offsetTop;
}
}
window.scrollTo(0,pos);
Two things here:
var pos
if you declare (mention for the first time) the variable pos in a for loop, it won't work. You have to declare it before where the rest of the function can read it.
window.addEventListener('load', (event) => {
i'm not sure why but at least in the snippet below this was needed. Perhaps the js starts running before the window loads and therefore cannot set window.scrollTo. If you have any questions leave it in the comments.
window.addEventListener('load', (event) => {
var myList = document.querySelectorAll('img');
var pos;
for (var i = 0; i < myList.length; i++) {
if (myList[i].alt == "Not Started") {
pos = myList[i].offsetTop;
break;
}
}
window.scrollTo(0, pos);
});
<img src='https://placekitten.com/800/800'>
<img src='https://placekitten.com/800/800' alt='Not Started'>
<img src='https://placekitten.com/700/800' alt='Not Started'>

JS script fires everytime an element is loaded in aspx page

I am trying to get my little js script to run once the entire dom or content of the aspx is loaded.
I have loaded the below:
JavaScript that executes after page load
and they still fire everytime an element is loaded.
I also tried
document.addEventListener("DOMContentLoaded", function () {
//Me Script!
});
This kinda works it loades the entire page before running the script for the number of times per element.
Or if there is a work around for my script so that it still forces what I want. Which is just add two slashes to my chosen links. ie file://///jobs/year/etc
if (isFirefox || isChrome) {
//You can use what ever to detect links, you can do it by tag and get every link.
var linkmodify = document.getElementsByClassName("fa-folder-open");
for (var i = 0; i < linkmodify.length; i++) {
var replacementLink = "";
link = linkmodify[i].href;
for (var j = 0; j < link.length; j++) {
if (j == 5) {
replacementLink += '//';
}
replacementLink += link[j];
}
linkmodify[i].href = replacementLink;
}
}
Currently the links only have the standard three slashes.
var haveIDoneMyStuff = false;
var myDOMLoadHandler = function(){
if (!haveIDoneMyStuff){
doMyStuff();
haveIDoneMyStuff = true;
}
}
document.addEventListener('DOMContentLoaded', myDOMLoadHandler);

Hiding `AdSense` `div` by accessing site from referral

I'm really a newbie to everything around programming ok. So...
I want to hide an AdSense div when accessing from a specific source (for example: ?utm_source=www.test.com) for the time a user is still moving around on my site or 120 seconds after that.
PS: I'm in a partnership with another advertising company and I have to show their ads when visitors come through this referral.
I have an AdSense code:
<div class="td-all-devices">
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- Test -->
<ins class="adsbygoogle"
style="display:inline-block;width:300px;height:600px"
data-ad-client="ca-pub-0000000000000"
data-ad-slot="000000"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
I tried this, but AdSense keep showing in footer:
function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++)
{
var pair = vars[i].split("=");
if (pair[0] == variable)
{
return pair[1];
}
}
return -1; //not found
}
if ( getQueryVariable('utm_source') == 'www.test.com' )
{
im.getAds(pozice);
var ads = 1;
document.getElementsByClassName("td-all-devices").style.display = 'none';
}
else
{
var ads = 0;
}
Can anyone help?
In here:
if ( getQueryVariable('utm_source') == 'www.test.com' )
{
im.getAds(pozice);
var ads = 1;
document.getElementsByClassName("td-all-devices").style.display = 'none';
}
document.getElementsByClassName returns an array of objects. Therefore you need to iterate through it to change the value of something in it. Well, you don't HAVE TO ITERATE through it... as long as you can specify which index of the collection you want to play with.
Something like this:
if ( getQueryVariable('utm_source') == 'www.test.com' )
{
im.getAds(pozice);
var ads = 1;
var theElements = document.getElementsByClassName("td-all-devices");
for(var i = 0; i < theElements.length; i++)
{
theElements[i].style.display = 'none';
}
}
else
{
var ads = 0;
}
document.getElementById() returns 1 element you could have modified using this
document.getElementById("whatever unique id").style.display = 'none';
document.getElementsByClassName() returns a collection of elements you could have modified like this
document.getElementsByClassName("whatever class name")[index position].style.display = 'none';
Since you are a self proclaimed newbie, I will invite you to discover the many Developer tools your preferred browser likely has to offer you. Most browsers have a console you can look at. The console will help you tremendously in troubleshooting your code. In console, you would have picked your problem almost instantly because you would have seen an entry like this:
TypeError: document.getElementsByClassName(...).style is undefined
In Firefox, with unmodified keyboard shortcuts, you can bring up the developers palette using ctrl + shift + s.

Unable to retrieve value from chrome.storage.sync.get

For a simple Chrome extension I've made that finds and replaces a text string I'm trying to add an option to add an asterisk after the replaced text. I have a bloolean variable showAsterisk that gets set in options.js from the options.html dialogue. I can see from the console that the value is being set properly, however it is not being retrieved with chrome.storage.sync.get. Not sure what I'm doing wrong. Here is the relevant code:
manifest.json
"permissions": ["storage"],
"options_ui": {
"page": "options.html",
"chrome_style": true
},
options.html
<h1>Options</h1>
<p><input type="checkbox" id="showAsterisk"> Add an asterisk after each instance of replaced text.</p>
<button id="save">Save</button>
<script src="options.js"></script>
options.js
function save_options() {
var showAsterisk = document.getElementById('showAsterisk').checked;
chrome.storage.sync.set({ showAsterisk: showAsterisk }, function() {
window.close();
});
}
function restore_options() {
chrome.storage.sync.get({ showAsterisk: false }, function(items) {
document.getElementById('showAsterisk').checked = items.showAsterisk;
});
}
document.addEventListener('DOMContentLoaded', restore_options);
document.getElementById('save').addEventListener('click', save_options);
content.js
var elements = document.getElementsByTagName("*");
var showAsterisk = false;
chrome.storage.sync.get("showAsterisk", function(items) {
if(typeof items.showAsterisk !== "undefined") showAsterisk = items.showAsterisk;
})
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
for (var j = 0; j < element.childNodes.length; j++) {
var node = element.childNodes[j];
if (node.nodeType === 3) {
var text = node.nodeValue;
if (showAsterisk === true) {
var replacedText = text.replace(/Moda Center/gi, "Rose Garden*");
}
if (showAsterisk === false) {
var replacedText = text.replace(/Moda Center/gi, "Rose Garden");
}
if (replacedText !== text) {
element.replaceChild(document.createTextNode(replacedText), node);
}
}
}
}
I am having no problem with the extension working properly.
In the developer tools you want to make sure you're opening the frame that the options page runs in, otherwise you'll be asking the extensions webpage, rather than the options page when debugging and testing things.
Running the following gives me false when I hit "Save" with the box uncheck in the options:
chrome.storage.sync.get({'showAsterisk' : '0'}, function(items){console.log(items.showAsterisk)});
And when I run this again but instead with it checked I get true.
It seems to be getting the data as intended, just when you're testing with the developer tools you may be in the wrong frame. Even running this on a random page like a google search result I can get the data. However I will get an error if I don't switch the frame the console is showing. See the image below for what I am talking about:
You want this:
Not this default frame:
Edit:
Referencing why you are not seeing the asterisks. I believe it is because your extension was not actually injecting onto any pages. I could not see it in the list when on a web page. However, I altered the permissions line in your manifest to the following:
"permissions": ["storage","http://*/*","https://*/*"],
Now it loads on webpages:

script for offsite links in new window, does not collect all links from page

Hi I'm total beginner and this is my first question.
I recently setup a site and have some external links to it. All are set to open in same window by default, but wanted to give visitors choice to toggle external links to open in new window.
I found great script from: http://www.dynamicdrive.com/dynamicindex8/newwindow2.htm
edit
Here is the script from dynamicdrive:
//Open offsite links in new window script- http://www.dynamicdrive.com/
//Created: August 28th, 2007'
var ddwindowlinks={
//1)Enter domains to be EXCLUDED from opening in new window:
excludedomains: ["dynamicdrive.com", "google.com"],
//2) Target for links that should open in a new window (ie: "_blank", "secwin" etc):
linktarget: "_blank",
//3) Specify operating mode ("auto" or "manual"):
mode: "manual",
//4) If mode is "manual", customize checkbox HTML to show to users (Preserve id attribute):
toggleHTML: '<form><input type="checkbox" id="targetcheckbox" checked="checked" /><label for="targetcheckbox">Open off-site links in new window?</label></form>',
//5) If mode is "manual", enable user persistence so the state of the checkbox is remembered?
persist: true,
assigntarget:function(){
var rexcludedomains=new RegExp(this.excludedomains.join("|"), "i")
var all_links=document.getElementsByTagName("a")
if (this.mode=="auto" || (this.mode=="manual" && this.togglebox.checked)){
for (var i=0; i<=(all_links.length-1); i++){
if (all_links[i].hostname.search(rexcludedomains)==-1 && all_links[i].href.indexOf("http:")!=-1)
all_links[i].target=ddwindowlinks.linktarget
}
}
else{
for (var i=0; i<=(all_links.length-1); i++)
all_links[i].target=""
}
if (this.mode=="manual" && this.persist)
this.setCookie("dlinktarget", (this.togglebox.checked)? "yes" : "no", 30) //remember user setting for 30 days (set to -1 then reload page to erase cookie)
},
init:function(){
if (document.getElementById && this.mode=="manual"){
document.write(this.toggleHTML)
this.togglebox=document.getElementById("targetcheckbox")
this.togglebox.onclick=function(){ddwindowlinks.assigntarget()}
if (this.persist && this.getCookie("dlinktarget")!="")
this.togglebox.checked=(this.getCookie("dlinktarget")=="yes")? true : false
}
if (window.addEventListener)
window.addEventListener("load", function(){ddwindowlinks.assigntarget()}, false)
else if (window.attachEvent)
window.attachEvent("onload", function(){ddwindowlinks.assigntarget()})
},
getCookie:function(Name){
var re=new RegExp(Name+"=[^;]+", "i"); //construct RE to search for target name/value pair
if (document.cookie.match(re)) //if cookie found
return document.cookie.match(re)[0].split("=")[1] //return its value
return ""
},
setCookie:function(name, value, days){
var expireDate = new Date()
//set "expstring" to either an explicit date (past or future)
var expstring=expireDate.setDate(expireDate.getDate()+parseInt(days))
document.cookie = name+"="+value+"; expires="+expireDate.toGMTString()+"; path=/"
}
}
ddwindowlinks.init()
end edit
The script works excelent for links that are on text.
Example
<div class="Artistic-Body-P">
<span class="Artistic-Body-C">
test
</span>
</div>
but it's not working for links that are within div rollover.
example:
<div id="div_popup_roll_13" style="position:absolute;left:109px;top:259px;width:76px;height:76px;">
<a href="http://externallink.com" rev="image1.png~#~-14~#~-15~#~text~#~148~#~-119~#~#949393~#~Tahoma~#~30~#~0~#~1~#~transparent~#~165">
<img src="image2.png" border="0" width="76" height="76" id="popup_roll_13" alt="">
</a>
</div>
I have been busting my head for two days how to fix this, but guess that the fact that I'm beginner, doesn't help so I decided to post here.
thnx for help in advance
edit
here is the code from poproll:
(function($){jQuery.fn.poproll=function(settings){var m_bHovering=false;var m_nDivId=0;
var $m_ImageDiv=null;var $m_TextDiv=null;
var eOptions={Img:0,ImgPosX:1,ImgPosY:2,Txt:3,TxtPosX:4,TxtPosY:5,TxtCol:6,TxtFont:7,TxtSize:8,TxtItallic:9,TxtBold:10,TxtBkgrndCol:11,TxtWidth:12};
function ClosePopup(){if($m_ImageDiv!==null){$m_ImageDiv.remove();
$m_ImageDiv=null;if($m_TextDiv!==null){$m_TextDiv.remove();
$m_TextDiv=null}}}function HoverOver(div){m_bHovering=true;var nDivId=$(div).attr('id');
if(nDivId!==m_nDivId){m_nDivId=nDivId;ClosePopup()}if($m_ImageDiv===null){var anchor=$(div).find('a');
var optionArray=$(anchor).attr('rev').split('~#~');
var href=$(anchor).attr('href');
if(href===undefined){$(div).append('<div id="poproll_img" style="position:absolute; left:'+optionArray[eOptions.ImgPosX]+'px; top:'+optionArray[eOptions.ImgPosY]+'px; z-index:100;"><img src="'+optionArray[eOptions.Img]+'" name="popup_roll_2" alt="" style="position:absolute;left:0px;top:0px;"></div>')}else{$(div).append('<div id="poproll_img" style="position:absolute; left:'+optionArray[eOptions.ImgPosX]+'px; top:'+optionArray[eOptions.ImgPosY]+'px; z-index:100;"></div>')}$m_ImageDiv=$('#poproll_img');
if(optionArray[eOptions.Txt].length>0){var fontStyle=optionArray[eOptions.TxtItallic]>0?'italic':'normal';
var fontWeight=optionArray[eOptions.TxtBold]>0?'bold':'normal';$(div).append('<div id="poproll_txt" style="position:absolute; left:'+optionArray[eOptions.TxtPosX]+'px; top:'+optionArray[eOptions.TxtPosY]+'px; width:'+optionArray[eOptions.TxtWidth]+'px; color:'+optionArray[eOptions.TxtCol]+'; font-size:'+optionArray[eOptions.TxtSize]+'; font-family:'+optionArray[eOptions.TxtFont]+'; font-style: '+fontStyle+'; font-weight:'+fontWeight+'; background-color:'+optionArray[eOptions.TxtBkgrndCol]+'; z-index:100;">'+optionArray[eOptions.Txt]+'</div>');$m_TextDiv=$('#poproll_txt')}}}function HoverOut(){m_bHovering=false;window.setTimeout(function(){if(!m_bHovering){ClosePopup()}},100)}this.hover(function(){HoverOver(this)},function(){HoverOut()})}})(jQuery);
It might just be cleaner to write the code yourself. Try the following:
// Grab every <a> tag in the document.
var allTheTags = document.getElementsByTagName('a');
function parseTags(tags) {
var size = tags.length; // cache the size;
for (var i = 0; i < size; i++) {
var tag = tags[i];
var href = tag.getAttribute('href');
// Do we have a target attribute? (and, of course, an href attribute)
if (href && !tag.getAttribute('target')) {
var ourHostName = window.location.hostname;
href = href.split('://');
// Is there a protocol?
if (href.length > 1) {
href = href[1].split('/')[0]; // Get everything before the first /
if (href != window.location.hostname &&
href != 'www' + window.location.hostname) {
// Sometimes, hostname does not have www in it.
tag.setAttribute('target', '_blank');
}
}
}
}
};
// Call our function.
parseTags(allTheTags);
Our variable "allTheTags" is a Nodelist, which will update when the DOM updates, so we can always re-run our parseTags function passing in our allTheTags object each time. This way, we skip querying the DOM if there's an instance where a tags are added dynamically.
EDIT
In the case of accounting for poproll functionality, you'll have to listen for the same roll-over event you pass to your poproll function. So, right after you call poproll on the element you selected, listen for a hover on that same element.
$(someElementYouUseForPoproll).hover(function () {
parseTags(allTheTags);
}, function() { });
This way, after our markup has been injected, this hover event is next in-line to be executed and will now see the tag the poproll generated in the DOM.
I solved my problem, by editing the code for the poprol, changed the href to:
<a href="'+href+'" target="_blank">

Categories